-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
208 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"}), | ||
}; | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"}), | ||
}; | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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]; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
} | ||
} |