Skip to content

Commit

Permalink
feat: add temporary services
Browse files Browse the repository at this point in the history
  • Loading branch information
KKardd committed Feb 24, 2025
1 parent e600a48 commit 881a104
Show file tree
Hide file tree
Showing 5 changed files with 208 additions and 0 deletions.
46 changes: 46 additions & 0 deletions src/handlers/check-til.handlers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import {registeredUsers} from "../common/registered-user";
import SlackService from "../service/slack.service";
import NotionService from "../service/notion.service";
import RedisService from "../service/redis.service";

const slackService = new SlackService();
const notionService = new NotionService();

export const handler = async (event: any) => {
try {
// 어제 작성된 TIL 확인
const yesterdayEntries = await notionService.getYesterdayTILWriter();
const writtenUsers = new Set(yesterdayEntries.flatMap((entry) => entry.user));

// Redis 클라이언트 가져오기
const redis = RedisService.getInstance();

// Redis에 저장된 휴식 중인 사용자 확인
const restingKeysPattern = "rest:*";
const restingKeys = await redis.keys(restingKeysPattern);
const restingUserIds = restingKeys.map((key) => key.split(":")[1]);

// 휴식 중이 아니면서 작성하지 않은 사용자 필터링
const missingUsers = Object.entries(registeredUsers)
.filter(([email, slackId]) => {
return !writtenUsers.has(email) && !restingUserIds.includes(slackId);
})
.map(([_, slackId]) => `<@${slackId}>`);

if (missingUsers.length > 0) {
const message = `🚨 안쓰고 뭐하셨어요! : ${missingUsers.join(", ")}님!`;
await slackService.sendTILNotification(message);
}

return {
statusCode: 200,
body: JSON.stringify({message: "Check completed"}),
};
} catch (error) {
console.error("Error:", error);
return {
statusCode: 500,
body: JSON.stringify({error: "Internal server error"}),
};
}
};
66 changes: 66 additions & 0 deletions src/handlers/rest-til.handlers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import RedisService from "../service/redis.service";
import SlackService from "../service/slack.service";

const slackService = new SlackService();

export const handler = async (event: any) => {
try {
// API Gateway로부터 받은 이벤트 파싱
const body = JSON.parse(event.body);

// Slack의 이벤트 검증
if (body.type === "url_verification") {
return {
statusCode: 200,
body: JSON.stringify({challenge: body.challenge}),
};
}

// 메시지 이벤트 처리
if (body.event && body.event.type === "message" && body.event.channel_type === "im") {
const message = body.event.text.toLowerCase().trim();
const userId = body.event.user;

// "rest {number}" 형식의 명령어 처리
const match = message.match(/^rest\s+(\d+)$/);
if (match) {
const restDays = parseInt(match[1]);

if (restDays > 0 && restDays <= 30) {
// 최대 30일로 제한
// Redis 클라이언트 가져오기
const redis = RedisService.getInstance();

// 현재 시간부터 N일 후의 Unix 타임스탬프 계산
const now = Math.floor(Date.now() / 1000);
const expiryTime = now + restDays * 24 * 60 * 60;

// Redis에 저장 (사용자 ID를 키로 사용)
const key = `rest:${userId}`;
await redis.set(key, "true");
await redis.expire(key, restDays * 24 * 60 * 60); // 자동 만료 설정

// 사용자에게 확인 메시지 전송
const endDate = new Date(expiryTime * 1000).toLocaleDateString();
await slackService.sendMessage(
body.event.channel,
`✅ ${restDays}일 동안의 휴식이 설정되었습니다. (${endDate} 까지)`
);
} else {
await slackService.sendMessage(body.event.channel, "❌ 휴식 기간은 1-30일 사이로 설정해주세요.");
}
}
}

return {
statusCode: 200,
body: JSON.stringify({message: "OK"}),
};
} catch (error) {
console.error("Error:", error);
return {
statusCode: 500,
body: JSON.stringify({error: "Internal server error"}),
};
}
};
40 changes: 40 additions & 0 deletions src/service/notion.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import {Client} from "@notionhq/client";
import Configuration from "../config/configuration";

export default class NotionService {
private notionClient: Client;
private databaseId: string;

constructor() {
this.notionClient = new Client({auth: Configuration.NOTION_API_KEY});
this.databaseId = Configuration.TIL_NOTION_DATABASE_ID;
}

async getYesterdayTILWriter(): Promise<{id: string; user: string[]}[]> {
try {
const yesterday = this.getYesterday();

const response = await this.notionClient.databases.query({
database_id: this.databaseId,
filter: {
property: "날짜",
date: {equals: yesterday},
},
});

return response.results.map((page: any) => ({
id: page.id,
user: page.properties.사람?.people?.map((user: any) => user.person.email) || [],
}));
} catch (error) {
console.error("Notion 데이터 조회 실패:", error);
throw error;
}
}

private getYesterday(): string {
const now = new Date();
now.setDate(now.getDate() - 1);
return now.toISOString().split("T")[0];
}
}
21 changes: 21 additions & 0 deletions src/service/redis.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import Redis from "ioredis";
import Configuration from "../config/configuration";

export default class RedisService {
private static instance: Redis;

private constructor() {}

static getInstance(): Redis {
if (!this.instance) {
this.instance = new Redis({
host: Configuration.REDIS_HOST,
port: Number(Configuration.REDIS_PORT),
});

this.instance.on("error", (err) => console.error("Redis Client Error", err));
}

return this.instance;
}
}
35 changes: 35 additions & 0 deletions src/service/slack.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import {WebClient} from "@slack/web-api";
import axios from "axios";
import Configuration from "../config/configuration";
import {Channel} from "../common/channels";

export default class SlackService {
private slackClient: WebClient;
private webhookUrl: string;

constructor() {
this.slackClient = new WebClient(Configuration.SLACK_BOT_TOKEN);
this.webhookUrl = Configuration.TIL_SLACK_WEBHOOK_URL;
}

async sendMessage(channel: Channel, text: string): Promise<void> {
try {
await this.slackClient.chat.postMessage({
channel,
text,
});
} catch (error) {
console.error("Slack 메시지 전송 실패:", error);
throw error;
}
}

async sendTILNotification(message: string): Promise<void> {
try {
await axios.post(this.webhookUrl, {text: message});
} catch (error) {
console.error("TIL Slack Webhook 전송 실패:", error);
throw error;
}
}
}

0 comments on commit 881a104

Please sign in to comment.