[Slack] Slack daily bot을 만들어보자 (2)

들어가며

node.js와 slack client를 이용해 slack의 bot을 만들어보자.

Node.js와 slack client 사용법에 대해서는 Slack bot 만들기 (1) 을 참고 한다.

기존에 slack bot을 만들려고 할 때 message 이벤트를 이용해서 저장하려고 하였는데

해당 방식 보다 특정 시간에 메시지를 크롤링하여 저장하는 방식이 간단하고 다른 이벤트를 처리할 필요 없어 해당 방식으로 변경한다.

Work flow

  1. daily bot은 특정 시간에 메시지를 발송한다.
  2. daily bot이 작성한 메시지에 thread를 이용해 댓글 방식으로 자신의 daily를 작성한다.
  3. daily bot은 특정 시간에 daily message에 작성된 thread 댓글을 크롤링 하여 저장한다.

1. 특정 시간에 메시지 발송

node.js에서는 node-cron이라는 라이브러리가 있어 해당 cron 방식을 이용하기로 했다.

$ npm install --save node-cron

해당 라이브러리의 가장 좋은 점은 아래 2가지 이다.

  1. 월~금으로만 설정 가능
  2. timezone으로 설정 가능

휴일에 대해서는 어쩔 수 없지만 주말을 제외하고 알아서 글을 작성 및 저장해 줄 수 있는 옵션과 서버 시간이 한국시간이 아닌 경우 엉뚱한 시간으로 동작할 수 있어 타임존을 셋팅할 수 있는 부분이 마음에 들었다.

daily bot은 YYYY-MM-DD 포맷으로 메시지를 작성하며 해당 메시지에 id(YYYY-MM-DD), channel, ts에 대해서 저장한다.

  • channel과 ts를 저장하는 이유는 해당 channel에 ts를 통해서 thread 내용을 크롤링 할 수 있기 때문에 저장한다.
  • YYYY-MM-DD는 해당 일에 대해서 데이터 크롤링 id로 사용하기 위해 저장한다.
cron.schedule('0 0 9 * * 1-5', () => {
    (async () => {
      try {
        const rtmCallResult = await SlackClient.sendMessage(`${DateUtils.getTodayDateFormat()} daily`, channel);
        const newVar = await BotTask.createOne({
          id: DateUtils.getTodayDateFormat(),
          channel,
          ts: rtmCallResult.ts,
          temp: JSON.stringify(rtmCallResult)
        });
      } catch (e) {
        console.error(e);
      }
    })();
  }, {
    timezone
  });
  1. 해당 채널에 원하는 메시지 포맷으로 전송한다.
  2. 전송 후 id, channel, ts를 저장해 둔다.

추후 id를 통해서 해당 날의 daily thread를 저장하는 용도로 쓰이기 위해서 저장 해둔다.

2. 메시지 thread 크롤링

slack에서는 conversations.replies를 통해서 해당 thread에 작성된 메시지를 크롤링 할 수 있다.

크롤링을 하기 위해서는 token, channel, ts가 required기 때문에 앞에서 channel, ts를 저장했으며 token은 slack bok에 token을 이용하면 된다.

cron.schedule('0 0 19 * * 1-5', () => {
    (async () => {
      try {
        const botTask = await BotTask.findByPk(DateUtils.getTodayDateFormat());
        const ts = botTask.ts;
        const channel = botTask.channel;
        const date = botTask.id;

        const tasks = await SlackClient.conversionReplies({ts, channel});
        const {messages} = tasks;
        for (const task of messages) {
          if (task.bot_id) {
            continue;
          }
          const username = await SlackApi.getUsername(task.user);
          const taskInfo = {
            id: `${channel}-${task.ts}`,
            username,
            date: date,
            task: task.text
          };
          const saved = await Task.createOne(taskInfo);
        }
      } catch (error) {
        console.error(error);
      }
    })();
  }, {
    timezone
  });
  1. id를 통해서 해당 날의 daily 메시지 데이터를 조회한다. (YYYY-MM-DD 사용)
  2. channel, ts에 정보를 바탕으로 thread에 replies를 조회 한다.
  3. 해당 데이터 가공한 뒤 데이터를 저장한다.

마치며

처음에 message 이벤트를 처리하여 해결하려고 하니, message의 changed 이벤트와 message 이벤트의 response 포맷이 달라 매 번 업데이트를 해야 하나 고민했었는데 해당 방식처럼 크롤링으로 변경하니 사용자들이 업데이트를 하던지 안하던지 상관없이 최종 작성된 text로 데이터가 나와 이벤트 처리에 대한 고민을 해결할 수 있었다.

데이터 모델 관련해서는 sequelize 를 사용하였는데 ORM을 제공해주고 db table도 없으면 생성해줘서 별 다른 셋팅을 하지 않아도 되서 편했었다.

생각보다 사용법도 간단하여 다음에 포스팅을 할 예정이다.


Leave a comment