From bb65863f81c6be0af180d98d800c07dd318812f6 Mon Sep 17 00:00:00 2001 From: vchikalkin Date: Fri, 27 Jun 2025 13:24:34 +0300 Subject: [PATCH] order: notify to telegram messages --- apps/bot/package.json | 2 +- packages/graphql/api/notify.ts | 77 +++++++++++++++++++ packages/graphql/api/orders.ts | 9 +++ packages/graphql/config/env.ts | 1 + packages/graphql/operations/customers.graphql | 12 ++- packages/graphql/package.json | 3 +- .../graphql/types/operations.generated.ts | 3 +- packages/graphql/utils/notify.ts | 9 +++ packages/graphql/utils/telegram.ts | 4 + packages/utils/src/datetime-format.ts | 9 ++- packages/utils/src/environment.ts | 3 + pnpm-lock.yaml | 8 +- pnpm-workspace.yaml | 1 + 13 files changed, 132 insertions(+), 9 deletions(-) create mode 100644 packages/graphql/api/notify.ts create mode 100644 packages/graphql/utils/notify.ts create mode 100644 packages/graphql/utils/telegram.ts create mode 100644 packages/utils/src/environment.ts diff --git a/apps/bot/package.json b/apps/bot/package.json index 022d9f6..f3b4639 100644 --- a/apps/bot/package.json +++ b/apps/bot/package.json @@ -13,7 +13,7 @@ "lint-staged": "lint-staged" }, "dependencies": { - "telegraf": "^4.16.3", + "telegraf": "catalog:", "zod": "catalog:" }, "devDependencies": { diff --git a/packages/graphql/api/notify.ts b/packages/graphql/api/notify.ts new file mode 100644 index 0000000..b04f9f2 --- /dev/null +++ b/packages/graphql/api/notify.ts @@ -0,0 +1,77 @@ +import type * as GQL from '../types'; +import { notifyByTelegramId } from '../utils/notify'; +import { BaseService } from './base'; +import { CustomersService } from './customers'; +import { OrdersService } from './orders'; +import { ServicesService } from './services'; +import { SlotsService } from './slots'; +import { type VariablesOf } from '@graphql-typed-document-node/core'; +import { formatDate, formatTime, sumTime } from '@repo/utils/datetime-format'; + +export class NotifyService extends BaseService { + async orderCreated(variables: { + input: Omit['input'], 'time_end'>; + }) { + const customersService = new CustomersService(this.customer); + const slotsService = new SlotsService(this.customer); + const servicesService = new ServicesService(this.customer); + + const slotId = String(variables.input.slot ?? ''); + const serviceId = String(variables.input.services?.[0] ?? ''); + const timeStart = String(variables.input.time_start ?? ''); + const clientId = String(variables.input.client ?? ''); + + const { slot } = await slotsService.getSlot({ documentId: slotId }); + const { service } = await servicesService.getService({ documentId: serviceId }); + const { customer: master } = await customersService.getCustomer({ + documentId: slot?.master?.documentId ?? '', + }); + const { customer: client } = await customersService.getCustomer({ documentId: clientId }); + + const slotDate = formatDate(slot?.date).user(); + const timeStartString = formatTime(timeStart).user(); + const timeEndString = formatTime( + service?.duration ? sumTime(timeStart, service.duration) : timeStart, + ).user(); + + // Мастеру + if (master?.telegramId) { + const message = `✅ Запись создана!\nДата: ${slotDate}\nВремя: ${timeStartString} - ${timeEndString}\nКлиент: ${client?.name ?? '-'}\nУслуга: ${service?.name ?? '-'}`; + await notifyByTelegramId(String(master.telegramId), message); + } + + // Клиенту + if (client?.telegramId) { + const message = `✅ Запись создана!\nДата: ${slotDate}\nВремя: ${timeStartString} - ${timeEndString}\nМастер: ${master?.name ?? '-'}\nУслуга: ${service?.name ?? '-'}`; + await notifyByTelegramId(String(client.telegramId), message); + } + } + + async orderUpdated(variables: VariablesOf) { + // Получаем order через OrdersService + const ordersService = new OrdersService(this.customer); + const { order } = await ordersService.getOrder({ documentId: variables.documentId }); + if (!order) return; + + const slot = order.slot; + const service = order.services[0]; + const master = slot?.master; + const client = order.client; + + const slotDate = formatDate(slot?.date).user(); + const timeStartString = formatTime(order.time_start ?? '').user(); + const timeEndString = formatTime(order.time_end ?? '').user(); + + // Мастеру + if (master?.telegramId) { + const message = `✏️ Запись изменена!\nДата: ${slotDate}\nВремя: ${timeStartString} - ${timeEndString}\nКлиент: ${client?.name ?? '-'}\nУслуга: ${service?.name ?? '-'}`; + await notifyByTelegramId(String(master.telegramId), message); + } + + // Клиенту + if (client?.telegramId) { + const message = `✏️ Запись изменена!\nДата: ${slotDate}\nВремя: ${timeStartString} - ${timeEndString}\nМастер: ${master?.name ?? '-'}\nУслуга: ${service?.name ?? '-'}`; + await notifyByTelegramId(String(client.telegramId), message); + } + } +} diff --git a/packages/graphql/api/orders.ts b/packages/graphql/api/orders.ts index 7847640..c9fae88 100644 --- a/packages/graphql/api/orders.ts +++ b/packages/graphql/api/orders.ts @@ -4,6 +4,7 @@ import * as GQL from '../types'; import { Enum_Customer_Role, Enum_Slot_State } from '../types'; import { BaseService } from './base'; import { CustomersService } from './customers'; +import { NotifyService } from './notify'; import { ServicesService } from './services'; import { SlotsService } from './slots'; import { type VariablesOf } from '@graphql-typed-document-node/core'; @@ -90,6 +91,10 @@ export class OrdersService extends BaseService { const error = mutationResult.errors?.at(0); if (error) throw new Error(error.message); + // Уведомление об создании заказа + const notifyService = new NotifyService(this.customer); + notifyService.orderCreated(variables); + return mutationResult.data; } @@ -150,6 +155,10 @@ export class OrdersService extends BaseService { const error = mutationResult.errors?.at(0); if (error) throw new Error(error.message); + // Уведомление об изменении заказа + const notifyService = new NotifyService(this.customer); + notifyService.orderUpdated(variables); + return mutationResult.data; } } diff --git a/packages/graphql/config/env.ts b/packages/graphql/config/env.ts index 91482f2..688f1e8 100644 --- a/packages/graphql/config/env.ts +++ b/packages/graphql/config/env.ts @@ -2,6 +2,7 @@ import { z } from 'zod'; export const envSchema = z.object({ + BOT_TOKEN: z.string(), LOGIN_GRAPHQL: z.string(), PASSWORD_GRAPHQL: z.string(), URL_GRAPHQL: z.string(), diff --git a/packages/graphql/operations/customers.graphql b/packages/graphql/operations/customers.graphql index d45f566..3236735 100644 --- a/packages/graphql/operations/customers.graphql +++ b/packages/graphql/operations/customers.graphql @@ -14,8 +14,16 @@ mutation CreateCustomer($name: String!, $telegramId: Long, $phone: String) { } } -query GetCustomer($phone: String, $telegramId: Long) { - customers(filters: { or: [{ phone: { eq: $phone } }, { telegramId: { eq: $telegramId } }] }) { +query GetCustomer($phone: String, $telegramId: Long, $documentId: ID) { + customers( + filters: { + or: [ + { phone: { eq: $phone } } + { telegramId: { eq: $telegramId } } + { documentId: { eq: $documentId } } + ] + } + ) { ...CustomerFields } } diff --git a/packages/graphql/package.json b/packages/graphql/package.json index c7708f9..6679a8c 100644 --- a/packages/graphql/package.json +++ b/packages/graphql/package.json @@ -12,7 +12,8 @@ "@repo/utils": "workspace:", "dayjs": "catalog:", "jsonwebtoken": "catalog:", - "radashi": "catalog:", + "radashi": "catalog:", + "telegraf": "catalog:", "zod": "catalog:" }, "devDependencies": { diff --git a/packages/graphql/types/operations.generated.ts b/packages/graphql/types/operations.generated.ts index db2079b..b763eb2 100644 --- a/packages/graphql/types/operations.generated.ts +++ b/packages/graphql/types/operations.generated.ts @@ -664,6 +664,7 @@ export type CreateCustomerMutation = { __typename?: 'Mutation', createCustomer?: export type GetCustomerQueryVariables = Exact<{ phone?: InputMaybe; telegramId?: InputMaybe; + documentId?: InputMaybe; }>; @@ -794,7 +795,7 @@ export const SlotFieldsFragmentDoc = {"kind":"Document","definitions":[{"kind":" export const RegisterDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"Register"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"identifier"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"password"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"email"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"register"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"username"},"value":{"kind":"Variable","name":{"kind":"Name","value":"identifier"}}},{"kind":"ObjectField","name":{"kind":"Name","value":"password"},"value":{"kind":"Variable","name":{"kind":"Name","value":"password"}}},{"kind":"ObjectField","name":{"kind":"Name","value":"email"},"value":{"kind":"Variable","name":{"kind":"Name","value":"email"}}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"jwt"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"username"}}]}}]}}]}}]} as unknown as DocumentNode; export const LoginDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"Login"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"identifier"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"password"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"login"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"identifier"},"value":{"kind":"Variable","name":{"kind":"Name","value":"identifier"}}},{"kind":"ObjectField","name":{"kind":"Name","value":"password"},"value":{"kind":"Variable","name":{"kind":"Name","value":"password"}}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"jwt"}}]}}]}}]} as unknown as DocumentNode; export const CreateCustomerDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateCustomer"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"name"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"telegramId"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Long"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"phone"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createCustomer"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"data"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"name"},"value":{"kind":"Variable","name":{"kind":"Name","value":"name"}}},{"kind":"ObjectField","name":{"kind":"Name","value":"telegramId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"telegramId"}}},{"kind":"ObjectField","name":{"kind":"Name","value":"phone"},"value":{"kind":"Variable","name":{"kind":"Name","value":"phone"}}},{"kind":"ObjectField","name":{"kind":"Name","value":"role"},"value":{"kind":"EnumValue","value":"client"}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"documentId"}}]}}]}}]} as unknown as DocumentNode; -export const GetCustomerDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetCustomer"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"phone"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"telegramId"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Long"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"customers"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filters"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"or"},"value":{"kind":"ListValue","values":[{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"phone"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"eq"},"value":{"kind":"Variable","name":{"kind":"Name","value":"phone"}}}]}}]},{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"telegramId"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"eq"},"value":{"kind":"Variable","name":{"kind":"Name","value":"telegramId"}}}]}}]}]}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CustomerFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CustomerFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Customer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"active"}},{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"phone"}},{"kind":"Field","name":{"kind":"Name","value":"photoUrl"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"telegramId"}}]}}]} as unknown as DocumentNode; +export const GetCustomerDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetCustomer"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"phone"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"telegramId"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Long"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"documentId"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"customers"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filters"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"or"},"value":{"kind":"ListValue","values":[{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"phone"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"eq"},"value":{"kind":"Variable","name":{"kind":"Name","value":"phone"}}}]}}]},{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"telegramId"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"eq"},"value":{"kind":"Variable","name":{"kind":"Name","value":"telegramId"}}}]}}]},{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"documentId"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"eq"},"value":{"kind":"Variable","name":{"kind":"Name","value":"documentId"}}}]}}]}]}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CustomerFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CustomerFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Customer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"active"}},{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"phone"}},{"kind":"Field","name":{"kind":"Name","value":"photoUrl"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"telegramId"}}]}}]} as unknown as DocumentNode; export const GetMastersDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetMasters"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"phone"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"telegramId"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Long"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"documentId"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"customers"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filters"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"or"},"value":{"kind":"ListValue","values":[{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"phone"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"eq"},"value":{"kind":"Variable","name":{"kind":"Name","value":"phone"}}}]}}]},{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"telegramId"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"eq"},"value":{"kind":"Variable","name":{"kind":"Name","value":"telegramId"}}}]}}]},{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"documentId"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"eq"},"value":{"kind":"Variable","name":{"kind":"Name","value":"documentId"}}}]}}]}]}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"masters"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CustomerFields"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CustomerFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Customer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"active"}},{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"phone"}},{"kind":"Field","name":{"kind":"Name","value":"photoUrl"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"telegramId"}}]}}]} as unknown as DocumentNode; export const GetClientsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetClients"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"phone"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"telegramId"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Long"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"customers"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filters"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"or"},"value":{"kind":"ListValue","values":[{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"phone"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"eq"},"value":{"kind":"Variable","name":{"kind":"Name","value":"phone"}}}]}}]},{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"telegramId"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"eq"},"value":{"kind":"Variable","name":{"kind":"Name","value":"telegramId"}}}]}}]}]}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"clients"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CustomerFields"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CustomerFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Customer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"active"}},{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"phone"}},{"kind":"Field","name":{"kind":"Name","value":"photoUrl"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"telegramId"}}]}}]} as unknown as DocumentNode; export const UpdateCustomerDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateCustomer"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"documentId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"data"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"CustomerInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"updateCustomer"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"documentId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"documentId"}}},{"kind":"Argument","name":{"kind":"Name","value":"data"},"value":{"kind":"Variable","name":{"kind":"Name","value":"data"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CustomerFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CustomerFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Customer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"active"}},{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"phone"}},{"kind":"Field","name":{"kind":"Name","value":"photoUrl"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"telegramId"}}]}}]} as unknown as DocumentNode; diff --git a/packages/graphql/utils/notify.ts b/packages/graphql/utils/notify.ts new file mode 100644 index 0000000..c6394f4 --- /dev/null +++ b/packages/graphql/utils/notify.ts @@ -0,0 +1,9 @@ +import { bot } from './telegram'; + +export async function notifyByTelegramId(telegramId: string | undefined, message: string) { + if (!telegramId) return; + + await bot.telegram.sendMessage(telegramId, message, { + parse_mode: 'HTML', + }); +} diff --git a/packages/graphql/utils/telegram.ts b/packages/graphql/utils/telegram.ts new file mode 100644 index 0000000..02e7730 --- /dev/null +++ b/packages/graphql/utils/telegram.ts @@ -0,0 +1,4 @@ +import { env } from '@/config/env'; +import { Telegraf } from 'telegraf'; + +export const bot = new Telegraf(env.BOT_TOKEN); diff --git a/packages/utils/src/datetime-format.ts b/packages/utils/src/datetime-format.ts index d313cf7..8967dd6 100644 --- a/packages/utils/src/datetime-format.ts +++ b/packages/utils/src/datetime-format.ts @@ -1,6 +1,7 @@ /* eslint-disable import/no-unassigned-import */ -import dayjs from 'dayjs'; +import { isBrowser } from './environment'; import 'dayjs/locale/ru'; +import dayjs from 'dayjs'; import { noop } from 'radashi'; export function combineDateTime(date: Date, time: string) { @@ -20,9 +21,11 @@ export function formatDate(date: Date | string) { return { db: () => dayjs(date).format('YYYY-MM-DD'), - user: (template?: string) => { - const lang = document.documentElement.lang || 'ru'; + user: (template?: string, fallbackLang: string = 'ru') => { + const lang = isBrowser() ? document.documentElement.lang || fallbackLang : fallbackLang; + dayjs.locale(lang); + return dayjs(date).format(template || 'D MMMM YYYY'); }, }; diff --git a/packages/utils/src/environment.ts b/packages/utils/src/environment.ts new file mode 100644 index 0000000..92090db --- /dev/null +++ b/packages/utils/src/environment.ts @@ -0,0 +1,3 @@ +export function isBrowser() { + return typeof window !== 'undefined' && typeof document !== 'undefined'; +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 79e8422..22ff344 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -72,6 +72,9 @@ catalogs: tailwindcss: specifier: ^3.4.15 version: 3.4.15 + telegraf: + specifier: ^4.16.3 + version: 4.16.3 typescript: specifier: ^5.7 version: 5.7.2 @@ -105,7 +108,7 @@ importers: apps/bot: dependencies: telegraf: - specifier: ^4.16.3 + specifier: 'catalog:' version: 4.16.3 zod: specifier: 'catalog:' @@ -289,6 +292,9 @@ importers: radashi: specifier: 'catalog:' version: 12.5.1 + telegraf: + specifier: 'catalog:' + version: 4.16.3 zod: specifier: 'catalog:' version: 3.24.1 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 8e2e4ef..e1cb849 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -24,6 +24,7 @@ catalog: radashi: ^12.5.1 rimraf: ^6.0.1 tailwindcss: ^3.4.15 + telegraf: ^4.16.3 typescript: ^5.7 vite-tsconfig-paths: ^5.1.4 vitest: ^2.1.8