feat(bot): implement add contact wizard scene and enhance contact handling logic
This commit is contained in:
parent
0bfebce1e3
commit
6eb421bfd4
@ -6,6 +6,7 @@ import {
|
||||
KEYBOARD_REMOVE,
|
||||
KEYBOARD_SHARE_BOT,
|
||||
KEYBOARD_SHARE_PHONE,
|
||||
MESSAGE_CANCEL,
|
||||
MESSAGE_NOT_MASTER,
|
||||
MESSAGE_SHARE_BOT,
|
||||
MSG_ALREADY_MASTER,
|
||||
@ -19,13 +20,91 @@ import {
|
||||
MSG_WELCOME,
|
||||
MSG_WELCOME_BACK,
|
||||
} from './message';
|
||||
import { isCustomerMaster } from './utils/customer';
|
||||
import { normalizePhoneNumber } from './utils/phone';
|
||||
import { CustomersService } from '@repo/graphql/api/customers';
|
||||
import { Enum_Customer_Role } from '@repo/graphql/types';
|
||||
import { Telegraf } from 'telegraf';
|
||||
import { Scenes, session, Telegraf, type Context as TelegrafContext } from 'telegraf';
|
||||
import { message } from 'telegraf/filters';
|
||||
import {
|
||||
type SceneContextScene,
|
||||
type SceneSession,
|
||||
type WizardContextWizard,
|
||||
type WizardSessionData,
|
||||
} from 'telegraf/typings/scenes';
|
||||
|
||||
const bot = new Telegraf(environment.BOT_TOKEN);
|
||||
type BotContext = TelegrafContext & {
|
||||
scene: SceneContextScene<BotContext, WizardSessionData>;
|
||||
session: SceneSession<WizardSessionData>;
|
||||
wizard: WizardContextWizard<BotContext>;
|
||||
};
|
||||
|
||||
const bot = new Telegraf<BotContext>(environment.BOT_TOKEN);
|
||||
|
||||
const stage = new Scenes.Stage<BotContext>();
|
||||
bot.use(session({ defaultSession: () => ({ __scenes: { cursor: 0, state: {} } }) }));
|
||||
bot.use(stage.middleware());
|
||||
|
||||
const addContactScene = new Scenes.WizardScene<BotContext>(
|
||||
'add-contact',
|
||||
async (context) => {
|
||||
await context.reply(MSG_SEND_CLIENT_CONTACT, { parse_mode: 'HTML' });
|
||||
return context.wizard.next();
|
||||
},
|
||||
async (context) => {
|
||||
if (!context.from) {
|
||||
await context.reply('Ошибка: не удалось определить пользователя');
|
||||
return context.scene.leave();
|
||||
}
|
||||
|
||||
if (context.message && 'text' in context.message && context.message.text === '/cancel') {
|
||||
await context.reply(MESSAGE_CANCEL + commandsList, { parse_mode: 'HTML' });
|
||||
return context.scene.leave();
|
||||
}
|
||||
|
||||
if (!('message' in context && context.message && 'contact' in context.message)) {
|
||||
await context.reply('Пожалуйста, отправьте контакт клиента через кнопку Telegram');
|
||||
return;
|
||||
}
|
||||
|
||||
const telegramId = context.from.id;
|
||||
const customerService = new CustomersService({ telegramId });
|
||||
const { customer } = await customerService.getCustomer({ telegramId });
|
||||
if (!customer || !isCustomerMaster(customer)) {
|
||||
await context.reply(MESSAGE_NOT_MASTER, { parse_mode: 'HTML' });
|
||||
return context.scene.leave();
|
||||
}
|
||||
|
||||
const { contact } = context.message;
|
||||
const name = (contact.first_name || '') + ' ' + (contact.last_name || '').trim();
|
||||
const phone = normalizePhoneNumber(contact.phone_number);
|
||||
|
||||
try {
|
||||
const { customer: existingCustomer } = await customerService.getCustomer({ phone });
|
||||
|
||||
let documentId = existingCustomer?.documentId;
|
||||
|
||||
if (!documentId) {
|
||||
const createCustomerResult = await customerService.createCustomer({ name, phone });
|
||||
documentId = createCustomerResult?.createCustomer?.documentId;
|
||||
if (!documentId) throw new Error('Customer not created');
|
||||
}
|
||||
|
||||
const masters = [customer.documentId];
|
||||
await customerService.addMasters({ data: { masters }, documentId });
|
||||
await context.reply(MSG_CONTACT_ADDED(name), { parse_mode: 'HTML' });
|
||||
await context.reply(MSG_CONTACT_FORWARD, { parse_mode: 'HTML' });
|
||||
await context.reply(MESSAGE_SHARE_BOT, { ...KEYBOARD_SHARE_BOT, parse_mode: 'HTML' });
|
||||
} catch (error) {
|
||||
await context.reply(MSG_ERROR(error), { parse_mode: 'HTML' });
|
||||
} finally {
|
||||
await context.reply(commandsList, { ...KEYBOARD_REMOVE, parse_mode: 'HTML' });
|
||||
context.scene.leave();
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
stage.register(addContactScene);
|
||||
|
||||
bot.start(async (context) => {
|
||||
const telegramId = context.from.id;
|
||||
@ -49,19 +128,17 @@ bot.command('help', async (context) => {
|
||||
|
||||
bot.command('addcontact', async (context) => {
|
||||
const telegramId = context.from.id;
|
||||
|
||||
const customerService = new CustomersService({ telegramId });
|
||||
const { customer } = await customerService.getCustomer({ telegramId });
|
||||
|
||||
if (!customer) {
|
||||
return context.reply(MSG_NEED_PHONE, { ...KEYBOARD_SHARE_PHONE, parse_mode: 'HTML' });
|
||||
}
|
||||
|
||||
if (customer.role !== Enum_Customer_Role.Master) {
|
||||
if (!isCustomerMaster(customer)) {
|
||||
return context.reply(MESSAGE_NOT_MASTER, { parse_mode: 'HTML' });
|
||||
}
|
||||
|
||||
return context.reply(MSG_SEND_CLIENT_CONTACT, { parse_mode: 'HTML' });
|
||||
return context.scene.enter('add-contact');
|
||||
});
|
||||
|
||||
bot.command('becomemaster', async (context) => {
|
||||
@ -74,7 +151,7 @@ bot.command('becomemaster', async (context) => {
|
||||
return context.reply(MSG_NEED_PHONE, { ...KEYBOARD_SHARE_PHONE, parse_mode: 'HTML' });
|
||||
}
|
||||
|
||||
if (customer.role === Enum_Customer_Role.Master) {
|
||||
if (isCustomerMaster(customer)) {
|
||||
return context.reply(MSG_ALREADY_MASTER, { parse_mode: 'HTML' });
|
||||
}
|
||||
|
||||
@ -100,60 +177,25 @@ bot.command('sharebot', async (context) => {
|
||||
|
||||
bot.on(message('contact'), async (context) => {
|
||||
const telegramId = context.from.id;
|
||||
|
||||
const customerService = new CustomersService({ telegramId });
|
||||
const { customer } = await customerService.getCustomer({ telegramId });
|
||||
|
||||
const isRegistration = !customer;
|
||||
if (!customer) {
|
||||
const { contact } = context.message;
|
||||
const name = (contact.first_name || '') + ' ' + (contact.last_name || '').trim();
|
||||
const phone = normalizePhoneNumber(contact.phone_number);
|
||||
|
||||
const { contact } = context.message;
|
||||
const name = (contact.first_name || '') + ' ' + (contact.last_name || '').trim();
|
||||
const phone = normalizePhoneNumber(contact.phone_number);
|
||||
|
||||
if (isRegistration) {
|
||||
const response = await customerService
|
||||
.createCustomer({
|
||||
name,
|
||||
phone,
|
||||
telegramId: context.from.id,
|
||||
})
|
||||
.createCustomer({ name, phone, telegramId: context.from.id })
|
||||
.catch((error) => {
|
||||
context.reply(MSG_ERROR(error), { parse_mode: 'HTML' });
|
||||
});
|
||||
|
||||
if (response) {
|
||||
return context.reply(MSG_PHONE_SAVED + commandsList, {
|
||||
...KEYBOARD_REMOVE,
|
||||
parse_mode: 'HTML',
|
||||
});
|
||||
}
|
||||
} else {
|
||||
if (customer.role !== Enum_Customer_Role.Master) {
|
||||
return context.reply(MESSAGE_NOT_MASTER, { parse_mode: 'HTML' });
|
||||
}
|
||||
|
||||
try {
|
||||
const createCustomerResult = await customerService.createCustomer({ name, phone });
|
||||
|
||||
const documentId = createCustomerResult?.createCustomer?.documentId;
|
||||
|
||||
if (!documentId) {
|
||||
throw new Error('Customer not created');
|
||||
}
|
||||
|
||||
const masters = [customer.documentId];
|
||||
|
||||
await customerService.addMasters({
|
||||
data: { masters },
|
||||
documentId,
|
||||
});
|
||||
|
||||
await context.reply(MSG_CONTACT_ADDED(name), { parse_mode: 'HTML' });
|
||||
await context.reply(MSG_CONTACT_FORWARD, { parse_mode: 'HTML' });
|
||||
await context.reply(MESSAGE_SHARE_BOT, { ...KEYBOARD_SHARE_BOT, parse_mode: 'HTML' });
|
||||
} catch (error) {
|
||||
context.reply(MSG_ERROR(error), { parse_mode: 'HTML' });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@ -43,7 +43,7 @@ export const KEYBOARD_SHARE_BOT = {
|
||||
};
|
||||
|
||||
export const MESSAGE_NOT_MASTER =
|
||||
'⛔️ <b>Только мастер может добавлять контакты.</b>\nСтать мастером можно на странице профиля в приложении или с помощью команды <b>/becomemaster</b>';
|
||||
'⛔️ <b>Только мастер может добавлять контакты</b>\nСтать мастером можно на странице профиля в приложении или с помощью команды <b>/becomemaster</b>';
|
||||
|
||||
export const MSG_WELCOME =
|
||||
'👋 <b>Добро пожаловать!</b>\nПожалуйста, поделитесь своим номером телефона для регистрации';
|
||||
@ -52,26 +52,28 @@ export const MSG_WELCOME_BACK = (name: string) =>
|
||||
`👋 <b>С возвращением, ${name}!</b>\nЧтобы воспользоваться сервисом, откройте приложение.\n`;
|
||||
|
||||
export const MSG_NEED_PHONE =
|
||||
'📱 <b>Чтобы добавить контакт, сначала поделитесь своим номером телефона.</b>';
|
||||
'📱 <b>Чтобы добавить контакт, сначала поделитесь своим номером телефона</b>';
|
||||
|
||||
export const MSG_SEND_CLIENT_CONTACT =
|
||||
'👤 <b>Отправьте контакт клиента, которого вы хотите добавить.</b>';
|
||||
'👤 <b>Отправьте контакт клиента, которого вы хотите добавить. \n<em>Для отмены операции используйте команду /cancel</em></b>';
|
||||
|
||||
export const MSG_ALREADY_MASTER = '🎉 <b>Вы уже являетесь мастером!</b>';
|
||||
|
||||
export const MSG_BECOME_MASTER = '🥳 <b>Поздравляем! Теперь вы мастер.</b>';
|
||||
export const MSG_BECOME_MASTER = '🥳 <b>Поздравляем! Теперь вы мастер</b>';
|
||||
|
||||
export const MSG_ERROR = (error?: unknown) =>
|
||||
`❌ <b>Произошла ошибка.</b>\n${error ? String(error) : ''}`;
|
||||
`❌ <b>Произошла ошибка</b>\n${error ? String(error) : ''}`;
|
||||
|
||||
export const MSG_PHONE_SAVED =
|
||||
'✅ <b>Спасибо! Мы сохранили ваш номер телефона.</b>\nТеперь вы можете открыть приложение или воспользоваться командами бота';
|
||||
'✅ <b>Спасибо! Мы сохранили ваш номер телефона</b>\nТеперь вы можете открыть приложение или воспользоваться командами бота';
|
||||
|
||||
export const MSG_CONTACT_ADDED = (name: string) =>
|
||||
`✅ <b>Добавили контакт:</b> <b>${name}</b>\nПригласите пользователя в приложение, чтобы вы могли добавлять записи с этим контактом`;
|
||||
`✅ <b>Добавили контакт в список ваших клиентов</b>\n\nИмя: <b>${name}</b>\n\nПригласите клиента в приложение, чтобы вы могли добавлять записи с этим контактом`;
|
||||
|
||||
export const MSG_CONTACT_FORWARD =
|
||||
'<em>Перешлите следующее сообщение, чтобы пользователь мог начать пользоваться ботом ⬇️</em>';
|
||||
'<em>Перешлите пользователю следующее сообщение, чтобы он мог начать пользоваться ботом ⬇️</em>';
|
||||
|
||||
export const MESSAGE_SHARE_BOT =
|
||||
'📅 <b>Воспользуйтесь этим ботом для записи к вашему мастеру!</b>\nНажмите кнопку ниже, чтобы начать';
|
||||
|
||||
export const MESSAGE_CANCEL = '<b>❌ Отменена операции</b>';
|
||||
|
||||
5
apps/bot/src/utils/customer.ts
Normal file
5
apps/bot/src/utils/customer.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import * as GQL from '@repo/graphql/types';
|
||||
|
||||
export function isCustomerMaster(customer: GQL.CustomerFieldsFragment) {
|
||||
return customer?.role === GQL.Enum_Customer_Role.Master;
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user