diff --git a/apps/bot/src/api.ts b/apps/bot/src/api.ts new file mode 100644 index 0000000..ed4ade7 --- /dev/null +++ b/apps/bot/src/api.ts @@ -0,0 +1,8 @@ +import * as api from '@repo/graphql/api'; + +export async function getCustomer(telegramId: number) { + const response = await api.getCustomer({ telegramId }); + const customer = response?.data?.customers?.at(0); + + return customer; +} diff --git a/apps/bot/src/index.ts b/apps/bot/src/index.ts index b789740..e8a2458 100644 --- a/apps/bot/src/index.ts +++ b/apps/bot/src/index.ts @@ -1,61 +1,79 @@ +/* eslint-disable consistent-return */ +import { getCustomer } from './api'; import { env as environment } from './config/env'; -import { createCustomer, getCustomer } from '@repo/graphql/api'; +import { commandsList, KEYBOARD_REMOVE, KEYBOARD_SHARE_PHONE } from './message'; +import { createClient, createCustomer } from '@repo/graphql/api'; import { Telegraf } from 'telegraf'; import { message } from 'telegraf/filters'; const bot = new Telegraf(environment.BOT_TOKEN); bot.start(async (context) => { - const { id: telegramId } = context.from; + const customer = await getCustomer(context.from.id); - const response = await getCustomer({ telegramId }); - const customer = response?.data?.customers?.at(0); - - if (customer && !response.error) { + if (customer) { return context.reply( - `Приветствуем снова, ${customer.name}. Чтобы воспользоваться сервисом, откройте приложение.`, - { - reply_markup: { - remove_keyboard: true, - }, - }, + `Приветствуем снова, ${customer.name} 👋. +Чтобы воспользоваться сервисом, откройте приложение.` + commandsList, + KEYBOARD_REMOVE, ); } - return context.reply('Добро пожаловать! Пожалуйста, поделитесь своим номером телефона.', { - reply_markup: { - keyboard: [ - [ - { - request_contact: true, - text: 'Отправить номер телефона', - }, - ], - ], - one_time_keyboard: true, - }, - }); + return context.reply( + 'Добро пожаловать! Пожалуйста, поделитесь своим номером телефона.', + KEYBOARD_SHARE_PHONE, + ); +}); + +bot.command('addcontact', async (context) => { + const customer = await getCustomer(context.from.id); + + if (!customer) { + return context.reply( + 'Чтобы добавить контакт, поделитесь своим номером телефона.', + KEYBOARD_SHARE_PHONE, + ); + } + + return context.reply('Отправьте контакт клиента, которого вы хотите добавить'); }); bot.on(message('contact'), async (context) => { - const response = await createCustomer({ - name: context.from.first_name, - phone: context.message.contact.phone_number, - telegramId: context.from.id, - }); + const customer = await getCustomer(context.from.id); - if (response.errors?.length) { - return context.reply('Произошла ошибка, попробуйте позже.'); + const isRegistration = !customer; + + if (isRegistration) { + const response = await createCustomer({ + name: context.from.first_name, + phone: context.message.contact.phone_number, + telegramId: context.from.id, + }).catch((error) => { + context.reply('Произошла ошибка, попробуйте позже.' + error); + }); + + if (response) { + return context.reply( + `Спасибо! Мы сохранили ваш номер телефона. Теперь можете открыть приложение или воспользоваться командами бота.` + + commandsList, + KEYBOARD_REMOVE, + ); + } + } else { + const { contact } = context.message; + const name = (contact.first_name || '') + ' ' + (contact.last_name || '').trim(); + const phone = contact.phone_number; + + const response = await createClient({ name, phone }).catch((error) => { + context.reply('Произошла ошибка, попробуйте позже.\n' + error); + }); + + if (response) { + return context.reply( + `Добавили контакт ${name}. Теперь вы можете добавить запись с ним в приложении.`, + ); + } } - - return context.reply( - 'Спасибо! Мы сохранили ваш номер телефона. Теперь можете открыть приложение.', - { - reply_markup: { - remove_keyboard: true, - }, - }, - ); }); bot.launch(); diff --git a/apps/bot/src/message.ts b/apps/bot/src/message.ts new file mode 100644 index 0000000..45f66c9 --- /dev/null +++ b/apps/bot/src/message.ts @@ -0,0 +1,26 @@ +import { type ReplyKeyboardRemove } from 'telegraf/types'; + +export const commandsList = ` +\nДоступные команды: + /addcontact - Добавить контакт клиента +`; + +export const KEYBOARD_SHARE_PHONE = { + reply_markup: { + keyboard: [ + [ + { + request_contact: true, + text: 'Отправить номер телефона', + }, + ], + ], + one_time_keyboard: true, + }, +}; + +export const KEYBOARD_REMOVE = { + reply_markup: { + remove_keyboard: true, + } as ReplyKeyboardRemove, +}; diff --git a/packages/graphql/api/customer.ts b/packages/graphql/api/customer.ts index a0bd9cb..556cfd4 100644 --- a/packages/graphql/api/customer.ts +++ b/packages/graphql/api/customer.ts @@ -1,4 +1,4 @@ -'use server'; +'use server';; import { getClientWithToken } from '../apollo/client'; import * as GQL from '../types'; @@ -11,6 +11,23 @@ export async function createCustomer(variables: GQL.CreateCustomerMutationVariab }); } +export async function createClient(variables: GQL.CreateClientMutationVariables) { + const { mutate } = await getClientWithToken(); + + const response = mutate({ + mutation: GQL.CreateClientDocument, + variables, + }).catch((error) => { + const isContactAlreadyExists = JSON.stringify(error).includes('This attribute must be unique'); + + if (isContactAlreadyExists) throw new Error('Contact already exists'); + + throw error; + }); + + return response; +} + export async function getCustomer(variables: GQL.GetCustomerQueryVariables) { const { query } = await getClientWithToken(); diff --git a/packages/graphql/operations/customer.graphql b/packages/graphql/operations/customer.graphql index d6e9f15..5b8fb4b 100644 --- a/packages/graphql/operations/customer.graphql +++ b/packages/graphql/operations/customer.graphql @@ -14,6 +14,12 @@ mutation CreateCustomer($name: String!, $telegramId: Long!, $phone: String!) { } } +mutation CreateClient($name: String!, $phone: String!) { + createCustomer(data: { name: $name, phone: $phone, role: client }) { + ...CustomerProfile + } +} + query GetCustomer($telegramId: Long!) { customers(filters: { telegramId: { eq: $telegramId } }) { ...CustomerProfile diff --git a/packages/graphql/types/operations.generated.ts b/packages/graphql/types/operations.generated.ts index caec0c5..5bc6aa2 100644 --- a/packages/graphql/types/operations.generated.ts +++ b/packages/graphql/types/operations.generated.ts @@ -578,6 +578,14 @@ export type CreateCustomerMutationVariables = Exact<{ export type CreateCustomerMutation = { __typename?: 'Mutation', createCustomer?: { __typename?: 'Customer', active?: boolean | null | undefined, documentId: string, name: string, phone: string, photoUrl?: string | null | undefined, role: Enum_Customer_Role, telegramId?: any | null | undefined } | null | undefined }; +export type CreateClientMutationVariables = Exact<{ + name: Scalars['String']['input']; + phone: Scalars['String']['input']; +}>; + + +export type CreateClientMutation = { __typename?: 'Mutation', createCustomer?: { __typename?: 'Customer', active?: boolean | null | undefined, documentId: string, name: string, phone: string, photoUrl?: string | null | undefined, role: Enum_Customer_Role, telegramId?: any | null | undefined } | null | undefined }; + export type GetCustomerQueryVariables = Exact<{ telegramId: Scalars['Long']['input']; }>; @@ -597,5 +605,6 @@ export const CustomerProfileFragmentDoc = {"kind":"Document","definitions":[{"ki 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":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Long"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"phone"}},"type":{"kind":"NonNullType","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":"FragmentSpread","name":{"kind":"Name","value":"CustomerProfile"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CustomerProfile"},"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 CreateClientDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateClient"},"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":"phone"}},"type":{"kind":"NonNullType","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":"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":"FragmentSpread","name":{"kind":"Name","value":"CustomerProfile"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CustomerProfile"},"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":"telegramId"}},"type":{"kind":"NonNullType","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":"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":"CustomerProfile"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CustomerProfile"},"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 UpdateCustomerProfileDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateCustomerProfile"},"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":"CustomerProfile"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CustomerProfile"},"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; \ No newline at end of file