슬랙 휴가자 멘션 알림

박경준·2023년 7월 18일
2

슬랙 앱 연동

목록 보기
1/2

구글 캘린더 - 휴가자 상태 변경 작업 이후 들어온 추가 요청.
5개월만에 착수.
1. 슬랙 내 모든 메시지에 훅을 걸 수 있어야한다.
2. 메시지에 태그가 있는지 확인한다.
3. 태그 걸린 유저가 휴가중인지 확인한다.
4. 태그 작성자에게 휴가자 멘션 알림을 보내준다.

  1. 슬랙 앱 설정 내 Event Subscriptions 에서 message.channels 이벤트 구독을 추가한다.

    Subscribe to bot events vs Subscribe to events on behalf of users
    왼쪽은 bot이 직접 구독하는 것이기 때문에 나의 경우 메시지를 구독하고 싶은 채널마다 app을 추가해줘야한다. 반면 오른쪽은 유저를 대신하여 즉, 유저가 메시지를 볼 수 있으면 앱에도 메시지가 구독된다. 왼쪽은 휴가자 멘션 알림을 bot이 해줄수 있지만, 오른쪽은 유저가 보낸것처럼 된다.
    각 장단점이 있지만 채널마다 app 추가하는것이 너무 귀찮아서 오른쪽 선택.

    이벤트를 구독하면 해당 이벤트가 발생할때마다 post 할 api 주소를 등록해야 한다.

    /mention/callback 을 등록했는데 특이하게 인증을 받으려면 challenge라는 reponse 값을 뱉어줘야한다.

  2. 메시지에 태그가 있는지 확인

const regex = /<@([A-Z0-9]+)>/g;
const matches = [];
let match;

while ((match = regex.exec(text)) !== null) {
  // 반복문을 도는 이유는 태그가 여러개일수도 있어서...
  const userId = match[1];
  matches.push(userId);
}
  1. 태그 걸린 유저가 휴가중인지 확인
if (matches.length) {
  matches.forEach(async (uid) => {
    // 태그 걸린 사람의 user profile 가져와서 status_text 확인
    // 휴가중인 사람은 '휴가중'
    const { data: vacationerData } = await axios.get(`https://slack.com/api/users.profile.get?user=${uid}&pretty=1`, {
      headers: {
        'Content-type': 'application/json',
        'Authorization': `Bearer ${slackToken}`,
      },
    });
    if (!vacationerData.ok) return;
    const vacationerRealName = vacationerData.profile.real_name;
    const statusExp = vacationerData.profile.status_expiration;
    const statusText = vacationerData.profile.status_text;
    const time = new Date().getTime();
  });
}
  1. 태그 작성자에게 태그 작성자 계정으로 휴가자 알림을 보내기
if (statusText === '휴가중' && time < (req.body.event_time + 4) * 1000) {
  // 작성자의 token을 db에서 가져오기 위해 작성자 user profile 가져오기
  const { data: writerData } = await axios.get(`https://slack.com/api/users.profile.get?user=${writerId}&pretty=1`, {
    headers: {
      'Content-type': 'application/json',
      'Authorization': `Bearer ${slackToken}`,
    },
  });
  if (!writerData.ok) return;
  const writerName = writerData.profile.real_name;

  const DBUser = await getDBUser(writerName);
  if (!DBUser.token) return;

  console.log(uid, vacationerRealName, time, req.body);

  // 작성자인척 휴가자 멘션 알림 보내기
  const slackBot = new WebClient(decrypt(DBUser.token));
  slackBot.chat.postMessage({
    channel,
    thread_ts: threadTs,
    text:
    statusExp > 0
    ? `${vacationerRealName}님은 ${format(new Date(statusExp * 1000), 'M/d H')}시까지 휴가입니다 :palm_tree:`
    : `${vacationerRealName}님은 휴가중입니다 :palm_tree:`,
  });
}
  1. 요상한 점

    이상하게 메시지 발생 한번에 hook이 두번~네번 발생함. 이유를 도저히 모르겠음.

  2. 위에 대한 대응

matches.forEach(async (uid) => {
  ...
  if (statusText === '휴가중' && time < (req.body.event_time + 4) * 1000) {
    ... 
  }
}
  • forEach와 async 콜백을 사용해서 matches의 각 element 간 실행은 비동기적으로, 루프 내의 동작은 동기적으로 실행.
  • 이렇게 실행하여 첫번째 hook은 실행되되, 두번째 hook은 실행되지 않는 시간차를 대충 4초 정도로 넣어서 수동으로 한번만 메시지 보내지게 설정. (매우 짜침)
  • 차라리 event_id와 member_id를 활용해서 db에 추가하고, db에 맵핑되지 않은 이벤트에만 알림을 보내도록 해야겠다. (시간 때문에 알림이 안가거나 두번 보내지는 일이 빈번해지면...)

profile
빠굥

0개의 댓글