작은 스타트업에 근무하면서 회사에서는 자율적으로 재택근무를 선택해서 할 수 있는 혜택을 주었다.
하지만 막상 출근했을때 누가 출근했고 누가 재택근무를 하는지 확인하는지를 체크하기가 불편해서 항상 Flex(HR 근태관리솔루션)에 들어가서 확인해야했다.
매번 들어가서 확인하는것이 생각보다 불편해서 슬랙에 데일리 스케쥴러를 만들어보았다 :)
결과물은 다음과 같다.
휴무일을 제외한 매일 10시에 특정 슬랙 채널에 이러한 형태로 알람이 간다.
결과물은 간단하지만 생각보다 다양한 기술들을 사용했다.
다행히 크롤링은 따로 사용하지 않았다.
전체적인 로직은 다음과 같다.
1) GCP Cloud Scheduler 에서 설정한 시간(매일 10시)에 GCP Function을 호출한다.
Cloud Scheduler는 cron job schedule로, 매일 10시에 실행시키기 위해 빈도를 다음과 같이 설정했다.
0 10 * * * (Asia/Seoul)
2) GCP Function 내에 Node.js 코드를 실행한다.
- Flex API를 이용하여 로그인(JWT) 및 근태정보를 획득한다.
- 근태정보와 회사 구성원 정보를 조합하여 팀별로 분류한다.
- 2에서 얻은 데이터를 이용하여 Slack를 제작한다.
- Slack Webhook을 이용하여 메세지를 보내고 종료한다.
전체 코드
const axios = require("axios");
const departmentsID = "departmentsID";
const loginURL = "https://flex.team/actions/login";
const upcomingURL = `https://flex.team/actions/api/v1/departments/${departmentsID}/upcoming-events?countLimitType=DAY&limitCount=1`;
const userListURL = "https://flex.team/actions/people/list?size=50&page=0";
const webhookURL = "https://hooks.slack.com/services/...";
exports.helloWorld = async (req, res) => {
timetable().then();
res.status(200).send("Hello World!");
};
const timetable = async () => {
try {
const isWeekend = new Date().getDay() === 0 || new Date().getDay() === 6;
if (isWeekend) return; // 주말 예외 처리
const { accessToken, refreshToken } = await axios
.post(loginURL, {
email: "Flex-Email",
password: "Flex-Password",
})
.then((res) => res.data.credentials);
const events = await axios
.get(upcomingURL, {
headers: { cookie: `AID=${accessToken}; RID=${refreshToken}` },
})
.then((res) => res.data.data.events[0].events);
// 휴일인 경우 종료
if (events.findIndex((event) => event.eventType === "CUSTOMER_HOLIDAY") > 0)
return;
const users = await axios
.get(userListURL, {
headers: { cookie: `AID=${accessToken}; RID=${refreshToken}` },
})
.then((res) => res.data.users);
events.forEach((event) => {
const uid = event.eventCta.link.replace("/feed?uid=", "");
const user = users.find((user) => user.uuid === uid);
if (user) event.departmentName = user.departments[0].name;
});
const groupByDepartment = events.reduce((acc, obj) => {
const key = obj.departmentName;
if (!acc[key]) acc[key] = [];
acc[key].push(obj);
return acc;
}, {});
const today = formatDate();
let message = `*${today}*\n`;
Object.entries(groupByDepartment).forEach(([departmentName, events]) => {
message += `>${departmentName}\n`;
events.forEach((event) => {
message += `:${event.eventEmoji}: ${event.eventName} \`${event.eventDescription}\`\n`;
});
});
await axios.post(webhookURL, { text: message });
} catch (error) {
throw error;
}
};
function formatDate(date = new Date()) {
const d = date instanceof Date ? date : new Date();
let month = "" + (d.getMonth() + 1);
let day = "" + d.getDate();
const year = d.getFullYear();
if (month.length < 2) month = "0" + month;
if (day.length < 2) day = "0" + day;
return [year, month, day].join(".");
}
코드 제작하면서 신경쓴 점
느낀점
여러 사람들에게 편리한 기능을 만들어 제공해서 뿌듯했다. 약 1년 정도 사용하면서 나름 편리했던 나만의 슬랙 봇이라고 생각한다 :-) 다음에 또 이런 것을 만들 기회가 있다면 즐겁게 만들어야지~
좋은 봇이네요! notion API 를 사용해서 notion 인라인 테이블 뷰로 표기해보시는것도 좋을 것 같아요 ㅎㅎ
좋은 글 감사합니다!