diff --git a/apps/web-app/.env.example b/apps/web-app/.env.example index 68c0dcf6..abdc3b3d 100644 --- a/apps/web-app/.env.example +++ b/apps/web-app/.env.example @@ -24,6 +24,7 @@ NUXT_AI_MODEL= NUXT_AI_MODEL_PRO= NUXT_AI_SERVICE_TOKEN= NUXT_AI_DAILY_REPORT_PROMPT= +NUXT_AI_WEEKLY_REPORT_PROMPT= # Telegram NUXT_TELEGRAM_LOCAL_BOT_API_SERVER_URL= diff --git a/apps/web-app/nuxt.config.ts b/apps/web-app/nuxt.config.ts index 4cdfc003..15eadab7 100644 --- a/apps/web-app/nuxt.config.ts +++ b/apps/web-app/nuxt.config.ts @@ -19,6 +19,7 @@ export default defineNuxtConfig({ apiKey: '', serviceToken: '', dailyReportPrompt: '', + weeklyReportPrompt: '', }, telegram: { localBotApiServerUrl: '', @@ -81,6 +82,7 @@ export default defineNuxtConfig({ '0 * * * *': ['kitchen:revenue-update'], // Every hour '0 0 * * *': ['kitchen:rating-update'], // Every day '0 17 * * 1-5': ['ai:daily-report'], // Mon-Fri 17:00 + '30 17 * * 5': ['ai:weekly-report'], // Friday 17:30 }, }, tiptap: { diff --git a/apps/web-app/server/tasks/ai/daily-report.ts b/apps/web-app/server/tasks/ai/daily-report.ts index 0ac801ee..ca8a023d 100644 --- a/apps/web-app/server/tasks/ai/daily-report.ts +++ b/apps/web-app/server/tasks/ai/daily-report.ts @@ -9,7 +9,7 @@ const logger = useLogger('task:ai:daily-report') export default defineTask({ meta: { name: 'ai:daily-report', - description: 'Prepare and post daily report to Telegram group', + description: 'Prepare and post daily report', }, async run() { if (process.env.NODE_ENV !== 'production') { @@ -31,6 +31,8 @@ export default defineTask({ const client = new OpenAI({ apiKey: ai.apiKey, baseURL: ai.baseUrl, + timeout: 120000, + maxRetries: 2, }) const response = await client.chat.completions.create({ diff --git a/apps/web-app/server/tasks/ai/weekly-report.ts b/apps/web-app/server/tasks/ai/weekly-report.ts new file mode 100644 index 00000000..b80c8015 --- /dev/null +++ b/apps/web-app/server/tasks/ai/weekly-report.ts @@ -0,0 +1,91 @@ +import process from 'node:process' +import { repository } from '@roll-stack/database' +import { format } from 'date-fns' +import { ru } from 'date-fns/locale/ru' +import OpenAI from 'openai' + +const logger = useLogger('task:ai:weekly-report') + +export default defineTask({ + meta: { + name: 'ai:weekly-report', + description: 'Prepare and post weekly report', + }, + async run() { + if (process.env.NODE_ENV !== 'production') { + logger.info('Skipping task in non-production environment') + return { result: true } + } + + try { + const { ai } = useRuntimeConfig() + + const tasks = await repository.task.listCompletedThisWeek() + const staff = await repository.user.findStaff() + + function preparePerformer(performerId: string | null) { + const user = staff.find((user) => user.id === performerId) + if (!user) { + return null + } + + return { + name: user?.name, + surname: user?.surname, + caption: user?.caption, + completedTasksCount: tasks.filter((task) => task.performerId === user?.id).length, + } + } + + const preparedTasks = tasks.map((task) => { + const performer = preparePerformer(task.performerId) + + return { + completedAt: task.completedAt, + name: task.name, + description: task.description, + report: task.report, + performer, + } + }) + + const client = new OpenAI({ + apiKey: ai.apiKey, + baseURL: ai.baseUrl, + timeout: 120000, + maxRetries: 2, + }) + + const response = await client.chat.completions.create({ + model: ai.modelPro, + messages: [ + { + role: 'system', + content: ai.weeklyReportPrompt, + }, + { + role: 'user', + content: JSON.stringify(preparedTasks), + }, + ], + }) + + const finalMessage = response.choices[0].message.content + if (!finalMessage) { + return { result: true } + } + + // Flow item + const week = format(new Date(), 'w', { locale: ru }) + await repository.flow.createItem({ + type: 'weekly_task_report', + title: `Задачи за неделю ${week}`, + description: finalMessage, + }) + } catch (error) { + errorResolver(error) + } + + return { result: true } + }, +}) diff --git a/packages/database/src/repository/task.ts b/packages/database/src/repository/task.ts index ad24813b..93654ee0 100644 --- a/packages/database/src/repository/task.ts +++ b/packages/database/src/repository/task.ts @@ -74,6 +74,13 @@ export class Task { }) } + static async listCompletedThisWeek() { + return useDatabase().query.tasks.findMany({ + where: (tasks, { gte }) => gte(tasks.completedAt, sql`now() - interval '6 day'`), + orderBy: (tasks, { desc }) => desc(tasks.updatedAt), + }) + } + static async autoCreatorsList() { return useDatabase().query.taskAutoCreators.findMany() } diff --git a/packages/database/src/types.ts b/packages/database/src/types.ts index bc404bdb..0900782e 100644 --- a/packages/database/src/types.ts +++ b/packages/database/src/types.ts @@ -36,7 +36,7 @@ export type TimeZone = '+00:00' | '+11:00' | '+12:00' -export type FlowItemType = 'daily_task_report' +export type FlowItemType = 'daily_task_report' | 'weekly_task_report' export type Permission = InferSelectModel export type PermissionDraft = InferInsertModel