Compare commits
No commits in common. "601ebca93e12aa839758a4cdae1de8fc37913075" and "6fbfd5364e83e5d739499ff11acde582d4ee8c6e" have entirely different histories.
601ebca93e
...
6fbfd5364e
5 changed files with 34 additions and 57 deletions
|
|
@ -6,7 +6,6 @@
|
||||||
"name": "app",
|
"name": "app",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@azure-rest/ai-inference": "^1.0.0-beta.6",
|
"@azure-rest/ai-inference": "^1.0.0-beta.6",
|
||||||
"@google/generative-ai": "^0.24.1",
|
|
||||||
"@imnyang/comcigan.ts": "^0.3.0",
|
"@imnyang/comcigan.ts": "^0.3.0",
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|
@ -36,8 +35,6 @@
|
||||||
|
|
||||||
"@azure/logger": ["@azure/logger@1.3.0", "", { "dependencies": { "@typespec/ts-http-runtime": "^0.3.0", "tslib": "^2.6.2" } }, "sha512-fCqPIfOcLE+CGqGPd66c8bZpwAji98tZ4JI9i/mlTNTlsIWslCfpg48s/ypyLxZTump5sypjrKn2/kY7q8oAbA=="],
|
"@azure/logger": ["@azure/logger@1.3.0", "", { "dependencies": { "@typespec/ts-http-runtime": "^0.3.0", "tslib": "^2.6.2" } }, "sha512-fCqPIfOcLE+CGqGPd66c8bZpwAji98tZ4JI9i/mlTNTlsIWslCfpg48s/ypyLxZTump5sypjrKn2/kY7q8oAbA=="],
|
||||||
|
|
||||||
"@google/generative-ai": ["@google/generative-ai@0.24.1", "", {}, "sha512-MqO+MLfM6kjxcKoy0p1wRzG3b4ZZXtPI+z2IE26UogS2Cm/XHO+7gGRBh6gcJsOiIVoH93UwKvW4HdgiOZCy9Q=="],
|
|
||||||
|
|
||||||
"@imnyang/comcigan.ts": ["@imnyang/comcigan.ts@0.3.0", "", { "dependencies": { "iconv-lite": "^0.6.3", "undici": "^6.23.0" } }, "sha512-IqOoqsrziSOZe0vUBvVjCysv8Ydz5uYBoSBiBtX3eopR2b+Em5W1C7mRJwrMa/9Tt2bgZxn30F3yq7w5yJzEdg=="],
|
"@imnyang/comcigan.ts": ["@imnyang/comcigan.ts@0.3.0", "", { "dependencies": { "iconv-lite": "^0.6.3", "undici": "^6.23.0" } }, "sha512-IqOoqsrziSOZe0vUBvVjCysv8Ydz5uYBoSBiBtX3eopR2b+Em5W1C7mRJwrMa/9Tt2bgZxn30F3yq7w5yJzEdg=="],
|
||||||
|
|
||||||
"@types/bun": ["@types/bun@1.3.10", "", { "dependencies": { "bun-types": "1.3.10" } }, "sha512-0+rlrUrOrTSskibryHbvQkDOWRJwJZqZlxrUs1u4oOoTln8+WIXBPmAuCF35SWB2z4Zl3E84Nl/D0P7803nigQ=="],
|
"@types/bun": ["@types/bun@1.3.10", "", { "dependencies": { "bun-types": "1.3.10" } }, "sha512-0+rlrUrOrTSskibryHbvQkDOWRJwJZqZlxrUs1u4oOoTln8+WIXBPmAuCF35SWB2z4Zl3E84Nl/D0P7803nigQ=="],
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,7 @@ async function main() {
|
||||||
const YYYY = now.getFullYear();
|
const YYYY = now.getFullYear();
|
||||||
const MM = String(now.getMonth() + 1).padStart(2, '0');
|
const MM = String(now.getMonth() + 1).padStart(2, '0');
|
||||||
const DD = String(now.getDate()).padStart(2, '0');
|
const DD = String(now.getDate()).padStart(2, '0');
|
||||||
// const YYMMDD = `${YYYY}${MM}${DD}`;
|
const YYMMDD = `${YYYY}${MM}${DD}`;
|
||||||
const YYMMDD = `20260403`;
|
|
||||||
|
|
||||||
// getDay(): 일(0) ~ 토(6)
|
// getDay(): 일(0) ~ 토(6)
|
||||||
// comcigan.ts 라이브러리 기준에 맞춰 weekday 설정 필요
|
// comcigan.ts 라이브러리 기준에 맞춰 weekday 설정 필요
|
||||||
|
|
|
||||||
|
|
@ -40,44 +40,22 @@ export async function Meal({ MLSV_YMD, ATPT_OFCDC_SC_CODE, SD_SCHUL_CODE, userna
|
||||||
|
|
||||||
|
|
||||||
export async function Timetable({ schoolId, grade, classNum, weekday, WEBHOOK_URL }: { schoolId: number, grade: number, classNum: number, weekday: number, WEBHOOK_URL: string }) {
|
export async function Timetable({ schoolId, grade, classNum, weekday, WEBHOOK_URL }: { schoolId: number, grade: number, classNum: number, weekday: number, WEBHOOK_URL: string }) {
|
||||||
// 필수 값 검증: undefined/null 이면 전송 안 함
|
|
||||||
if ([schoolId, grade, classNum, weekday, WEBHOOK_URL].some((v) => v === undefined || v === null)) {
|
|
||||||
console.warn("⚠️ | Undefined input detected. Skip sending webhook.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const weekdayText = ["월", "화", "수", "목", "금"][weekday - 1];
|
|
||||||
if (!weekdayText) {
|
|
||||||
console.warn("⚠️ | Invalid weekday. Skip sending webhook.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const timetableInfo = await getTimetable({ schoolId, grade, classNum, weekday });
|
const timetableInfo = await getTimetable({ schoolId, grade, classNum, weekday });
|
||||||
|
|
||||||
console.log("🏓 | Timetable Info Retrieved", timetableInfo);
|
console.log("🏓 | Timetable Info Retrieved", timetableInfo);
|
||||||
|
|
||||||
// 받아온 시간표 값이 없거나(undefined) 항목에 undefined 값이 있으면 전송 안 함
|
|
||||||
if (
|
|
||||||
!timetableInfo ||
|
|
||||||
timetableInfo.length === 0 ||
|
|
||||||
timetableInfo.some((item) => !item || item.subject === undefined || item.teacher === undefined)
|
|
||||||
) {
|
|
||||||
console.warn("⚠️ | Timetable contains undefined/empty data. Skip sending webhook.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = {
|
const data = {
|
||||||
content: `📅 | ${grade}학년 ${classNum}반 시간표 정보`,
|
content: `📅 | ${grade}학년 ${classNum}반 시간표 정보`,
|
||||||
embeds: [
|
embeds: [
|
||||||
{
|
{
|
||||||
title: `🏫 | 학교 : 선린인터넷고등학교`,
|
title: `🏫 | 학교 : 선린인터넷고등학교`,
|
||||||
fields: timetableInfo.map((item) => ({
|
fields: (timetableInfo ?? []).map((item) => ({
|
||||||
name: `${item.subject}${item.changed ? " *" : ""}`,
|
name: `${item.subject}${item.changed ? (" *") : ""}`,
|
||||||
value: `${item.teacher}${item.subject in room ? ` | ${room[item.subject as keyof typeof room]}` : ""}`,
|
value: `${item.teacher}${item.subject in room ? ` | ${room[item.subject as keyof typeof room]}` : ""}`,
|
||||||
inline: false,
|
inline: false,
|
||||||
})),
|
})),
|
||||||
footer: {
|
footer: {
|
||||||
text: `${weekdayText}요일 시간표 정보`,
|
text: `${["월", "화", "수", "목", "금"][weekday - 1]}요일 시간표 정보`
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import { GoogleGenerativeAI } from "@google/generative-ai";
|
import ModelClient, { isUnexpected } from "@azure-rest/ai-inference";
|
||||||
|
import { AzureKeyCredential } from "@azure/core-auth";
|
||||||
|
|
||||||
const KEY = process.env.NEIS_API_KEY;
|
const KEY = process.env.NEIS_API_KEY;
|
||||||
|
|
||||||
|
|
@ -44,14 +45,19 @@ export async function getMealInfo(MLSV_YMD: string, ATPT_OFCDC_SC_CODE: string,
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
export async function NameToEmoji(name: string): Promise<string | undefined> {
|
|
||||||
const apiKey = process.env.GOOGLE_API_KEY;
|
|
||||||
if (!apiKey) {
|
|
||||||
throw new Error("GOOGLE_API_KEY environment variable is not set.");
|
|
||||||
}
|
|
||||||
|
|
||||||
const genAI = new GoogleGenerativeAI(apiKey);
|
export async function NameToEmoji(name: string): Promise<string> {
|
||||||
const model = genAI.getGenerativeModel({ model: "gemini-3.1-flash-lite-preview" });
|
const token = process.env.GITHUB_TOKEN;
|
||||||
|
if (!token) {
|
||||||
|
throw new Error("GITHUB_TOKEN environment variable is not set.");
|
||||||
|
}
|
||||||
|
const endpoint = "https://models.github.ai/inference";
|
||||||
|
const model = "openai/gpt-5-mini";
|
||||||
|
|
||||||
|
const client = ModelClient(
|
||||||
|
endpoint,
|
||||||
|
new AzureKeyCredential(token),
|
||||||
|
);
|
||||||
|
|
||||||
const systemPrompt = `⚠️ 중요한 지침: 당신은 오직 이모지로만 응답하는 AI입니다. 다음 규칙을 예외 없이 철저히 준수해야 합니다. ⚠️
|
const systemPrompt = `⚠️ 중요한 지침: 당신은 오직 이모지로만 응답하는 AI입니다. 다음 규칙을 예외 없이 철저히 준수해야 합니다. ⚠️
|
||||||
|
|
||||||
|
|
@ -99,26 +105,24 @@ A: 🥰,☮️,🗽
|
||||||
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const response = await model.generateContent({
|
const response = await client.path("/chat/completions").post({
|
||||||
contents: [
|
body: {
|
||||||
{ role: "user", parts: [{ text: systemPrompt + "\n\nUser input: " + name }] }
|
messages: [
|
||||||
],
|
{ role: "system", content: systemPrompt },
|
||||||
generationConfig: {
|
{ role: "user", content: name }
|
||||||
|
],
|
||||||
temperature: 1.0,
|
temperature: 1.0,
|
||||||
topP: 1.0,
|
top_p: 1.0,
|
||||||
candidateCount: 1,
|
model: model
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
|
if (isUnexpected(response)) {
|
||||||
|
throw response.body.error;
|
||||||
|
}
|
||||||
|
|
||||||
const result = response.response;
|
const choices = response.body?.choices;
|
||||||
if (!result.candidates || !result.candidates[0]) {
|
if (!choices || !choices[0]?.message?.content) {
|
||||||
throw new Error("No valid response from the model.");
|
throw new Error("No valid response from the model.");
|
||||||
}
|
}
|
||||||
|
return choices[0].message.content as string;
|
||||||
const textContent = result.candidates[0].content.parts[0];
|
|
||||||
if (!textContent) {
|
|
||||||
throw new Error("Invalid response format from the model.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return textContent.text;
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,6 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@azure-rest/ai-inference": "^1.0.0-beta.6",
|
"@azure-rest/ai-inference": "^1.0.0-beta.6",
|
||||||
"@google/generative-ai": "^0.24.1",
|
|
||||||
"@imnyang/comcigan.ts": "^0.3.0"
|
"@imnyang/comcigan.ts": "^0.3.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue