feat: add libphonenumber-js for phone number parsing and validation
- Integrated libphonenumber-js to handle phone number parsing and validation in the addContact and registration features. - Removed deprecated phone validation and normalization functions from utils. - Updated contact handling logic to ensure valid phone numbers are processed correctly.
This commit is contained in:
parent
b88ac07ba3
commit
6a21f5911e
@ -25,7 +25,7 @@ description =
|
|||||||
start =
|
start =
|
||||||
.description = Запуск бота
|
.description = Запуск бота
|
||||||
addcontact =
|
addcontact =
|
||||||
.description = Добавить контакт пользователя
|
.description = Добавить контакт
|
||||||
sharebot =
|
sharebot =
|
||||||
.description = Поделиться ботом
|
.description = Поделиться ботом
|
||||||
subscribe =
|
subscribe =
|
||||||
@ -36,7 +36,7 @@ help =
|
|||||||
.description = Список команд и поддержка
|
.description = Список команд и поддержка
|
||||||
commands-list =
|
commands-list =
|
||||||
📋 Доступные команды:
|
📋 Доступные команды:
|
||||||
• /addcontact — добавить контакт пользователя
|
• /addcontact — добавить контакт
|
||||||
• /sharebot — поделиться ботом
|
• /sharebot — поделиться ботом
|
||||||
• /subscribe — приобрести Pro доступ
|
• /subscribe — приобрести Pro доступ
|
||||||
• /pro — информация о вашем Pro доступе
|
• /pro — информация о вашем Pro доступе
|
||||||
|
|||||||
@ -29,6 +29,7 @@
|
|||||||
"dayjs": "catalog:",
|
"dayjs": "catalog:",
|
||||||
"grammy": "^1.38.1",
|
"grammy": "^1.38.1",
|
||||||
"ioredis": "^5.7.0",
|
"ioredis": "^5.7.0",
|
||||||
|
"libphonenumber-js": "^1.12.24",
|
||||||
"pino": "^9.9.0",
|
"pino": "^9.9.0",
|
||||||
"pino-pretty": "^13.1.1",
|
"pino-pretty": "^13.1.1",
|
||||||
"radashi": "catalog:",
|
"radashi": "catalog:",
|
||||||
|
|||||||
@ -5,10 +5,10 @@ import { env } from '@/config/env';
|
|||||||
import { KEYBOARD_SHARE_BOT, KEYBOARD_SHARE_PHONE } from '@/config/keyboards';
|
import { KEYBOARD_SHARE_BOT, KEYBOARD_SHARE_PHONE } from '@/config/keyboards';
|
||||||
import { parseContact } from '@/utils/contact';
|
import { parseContact } from '@/utils/contact';
|
||||||
import { combine } from '@/utils/messages';
|
import { combine } from '@/utils/messages';
|
||||||
import { isValidPhoneNumber, normalizePhoneNumber } from '@/utils/phone';
|
|
||||||
import { type Conversation } from '@grammyjs/conversations';
|
import { type Conversation } from '@grammyjs/conversations';
|
||||||
import { CustomersService } from '@repo/graphql/api/customers';
|
import { CustomersService } from '@repo/graphql/api/customers';
|
||||||
import { RegistrationService } from '@repo/graphql/api/registration';
|
import { RegistrationService } from '@repo/graphql/api/registration';
|
||||||
|
import parsePhoneNumber from 'libphonenumber-js';
|
||||||
|
|
||||||
export async function addContact(conversation: Conversation<Context, Context>, ctx: Context) {
|
export async function addContact(conversation: Conversation<Context, Context>, ctx: Context) {
|
||||||
// Все пользователи могут добавлять контакты
|
// Все пользователи могут добавлять контакты
|
||||||
@ -60,23 +60,36 @@ export async function addContact(conversation: Conversation<Context, Context>, c
|
|||||||
let phone = '';
|
let phone = '';
|
||||||
|
|
||||||
if (firstCtx.message?.contact) {
|
if (firstCtx.message?.contact) {
|
||||||
|
/**
|
||||||
|
* Отправлен контакт
|
||||||
|
*/
|
||||||
const { contact } = firstCtx.message;
|
const { contact } = firstCtx.message;
|
||||||
const parsedContact = parseContact(contact);
|
const parsedContact = parseContact(contact);
|
||||||
|
const parsedPhone = parsePhoneNumber(contact.phone_number, 'RU');
|
||||||
|
|
||||||
name = parsedContact.name;
|
name = parsedContact.name;
|
||||||
surname = parsedContact.surname;
|
surname = parsedContact.surname;
|
||||||
phone = normalizePhoneNumber(parsedContact.phone);
|
|
||||||
|
if (!parsedPhone?.isValid() || !parsedPhone.number) {
|
||||||
|
return ctx.reply(await conversation.external(({ t }) => t('msg-invalid-phone')));
|
||||||
|
}
|
||||||
|
|
||||||
|
phone = parsedPhone.number;
|
||||||
} else if (firstCtx.message?.text) {
|
} else if (firstCtx.message?.text) {
|
||||||
const typedPhone = normalizePhoneNumber(firstCtx.message.text);
|
/**
|
||||||
if (!isValidPhoneNumber(typedPhone)) {
|
* Номер в тексте сообщения
|
||||||
|
*/
|
||||||
|
const parsedPhone = parsePhoneNumber(firstCtx.message.text, 'RU');
|
||||||
|
if (!parsedPhone?.isValid() || !parsedPhone.number) {
|
||||||
return ctx.reply(await conversation.external(({ t }) => t('msg-invalid-phone')));
|
return ctx.reply(await conversation.external(({ t }) => t('msg-invalid-phone')));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Нельзя добавлять свой собственный номер телефона
|
// Нельзя добавлять свой собственный номер телефона
|
||||||
if (customer.phone && normalizePhoneNumber(customer.phone) === typedPhone) {
|
if (customer.phone && customer.phone === parsedPhone.number) {
|
||||||
return ctx.reply(await conversation.external(({ t }) => t('err-cannot-add-self')));
|
return ctx.reply(await conversation.external(({ t }) => t('err-cannot-add-self')));
|
||||||
}
|
}
|
||||||
|
|
||||||
phone = typedPhone;
|
phone = parsedPhone.number;
|
||||||
|
|
||||||
// Просим ввести имя клиента
|
// Просим ввести имя клиента
|
||||||
await ctx.reply(await conversation.external(({ t }) => t('msg-send-client-name')));
|
await ctx.reply(await conversation.external(({ t }) => t('msg-send-client-name')));
|
||||||
@ -101,11 +114,6 @@ export async function addContact(conversation: Conversation<Context, Context>, c
|
|||||||
return ctx.reply(await conversation.external(({ t }) => t('msg-send-client-contact-or-phone')));
|
return ctx.reply(await conversation.external(({ t }) => t('msg-send-client-contact-or-phone')));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Проверяем валидность номера телефона
|
|
||||||
if (!isValidPhoneNumber(phone)) {
|
|
||||||
return ctx.reply(await conversation.external(({ t }) => t('msg-invalid-phone')));
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Проверяем, есть ли клиент с таким номером
|
// Проверяем, есть ли клиент с таким номером
|
||||||
const { customer: existingCustomer } = await registrationService._NOCACHE_GetCustomer({
|
const { customer: existingCustomer } = await registrationService._NOCACHE_GetCustomer({
|
||||||
|
|||||||
@ -2,9 +2,9 @@ import { type Context } from '@/bot/context';
|
|||||||
import { logHandle } from '@/bot/helpers/logging';
|
import { logHandle } from '@/bot/helpers/logging';
|
||||||
import { KEYBOARD_REMOVE, mainMenu } from '@/config/keyboards';
|
import { KEYBOARD_REMOVE, mainMenu } from '@/config/keyboards';
|
||||||
import { parseContact } from '@/utils/contact';
|
import { parseContact } from '@/utils/contact';
|
||||||
import { isValidPhoneNumber, normalizePhoneNumber } from '@/utils/phone';
|
|
||||||
import { RegistrationService } from '@repo/graphql/api/registration';
|
import { RegistrationService } from '@repo/graphql/api/registration';
|
||||||
import { Composer } from 'grammy';
|
import { Composer } from 'grammy';
|
||||||
|
import parsePhoneNumber from 'libphonenumber-js';
|
||||||
|
|
||||||
const composer = new Composer<Context>();
|
const composer = new Composer<Context>();
|
||||||
|
|
||||||
@ -35,13 +35,15 @@ feature.on(':contact', logHandle('contact-registration'), async (ctx) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Нормализация и валидация номера
|
// Нормализация и валидация номера
|
||||||
const phone = normalizePhoneNumber(contact.phone_number);
|
const parsedPhone = parsePhoneNumber(contact.phone_number, 'RU');
|
||||||
if (!isValidPhoneNumber(phone)) {
|
if (!parsedPhone?.isValid() || !parsedPhone?.number) {
|
||||||
return ctx.reply(ctx.t('msg-invalid-phone'));
|
return ctx.reply(ctx.t('msg-invalid-phone'));
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { customer } = await registrationService._NOCACHE_GetCustomer({ phone });
|
const { customer } = await registrationService._NOCACHE_GetCustomer({
|
||||||
|
phone: parsedPhone.number,
|
||||||
|
});
|
||||||
|
|
||||||
if (customer && !customer.telegramId) {
|
if (customer && !customer.telegramId) {
|
||||||
// Пользователь добавлен ранее мастером — обновляем данные
|
// Пользователь добавлен ранее мастером — обновляем данные
|
||||||
@ -57,7 +59,7 @@ feature.on(':contact', logHandle('contact-registration'), async (ctx) => {
|
|||||||
|
|
||||||
// Новый пользователь — создаём и активируем
|
// Новый пользователь — создаём и активируем
|
||||||
const response = await registrationService.createCustomer({
|
const response = await registrationService.createCustomer({
|
||||||
data: { name, phone, surname, telegramId },
|
data: { name, phone: parsedPhone.number, surname, telegramId },
|
||||||
});
|
});
|
||||||
|
|
||||||
const documentId = response?.createCustomer?.documentId;
|
const documentId = response?.createCustomer?.documentId;
|
||||||
|
|||||||
@ -1,9 +0,0 @@
|
|||||||
export function isValidPhoneNumber(phone: string) {
|
|
||||||
return /^\+7\d{10}$/u.test(phone);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function normalizePhoneNumber(phone: string): string {
|
|
||||||
const digitsOnly = phone.replaceAll(/\D/gu, '');
|
|
||||||
|
|
||||||
return `+${digitsOnly}`;
|
|
||||||
}
|
|
||||||
8
pnpm-lock.yaml
generated
8
pnpm-lock.yaml
generated
@ -153,6 +153,9 @@ importers:
|
|||||||
ioredis:
|
ioredis:
|
||||||
specifier: ^5.7.0
|
specifier: ^5.7.0
|
||||||
version: 5.7.0
|
version: 5.7.0
|
||||||
|
libphonenumber-js:
|
||||||
|
specifier: ^1.12.24
|
||||||
|
version: 1.12.24
|
||||||
pino:
|
pino:
|
||||||
specifier: ^9.9.0
|
specifier: ^9.9.0
|
||||||
version: 9.9.0
|
version: 9.9.0
|
||||||
@ -6094,6 +6097,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
|
resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
|
||||||
engines: {node: '>= 0.8.0'}
|
engines: {node: '>= 0.8.0'}
|
||||||
|
|
||||||
|
libphonenumber-js@1.12.24:
|
||||||
|
resolution: {integrity: sha512-l5IlyL9AONj4voSd7q9xkuQOL4u8Ty44puTic7J88CmdXkxfGsRfoVLXHCxppwehgpb/Chdb80FFehHqjN3ItQ==}
|
||||||
|
|
||||||
light-my-request@5.14.0:
|
light-my-request@5.14.0:
|
||||||
resolution: {integrity: sha512-aORPWntbpH5esaYpGOOmri0OHDOe3wC5M2MQxZ9dvMLZm6DnaAn0kJlcbU9hwsQgLzmZyReKwFwwPkR+nHu5kA==}
|
resolution: {integrity: sha512-aORPWntbpH5esaYpGOOmri0OHDOe3wC5M2MQxZ9dvMLZm6DnaAn0kJlcbU9hwsQgLzmZyReKwFwwPkR+nHu5kA==}
|
||||||
|
|
||||||
@ -15291,6 +15297,8 @@ snapshots:
|
|||||||
prelude-ls: 1.2.1
|
prelude-ls: 1.2.1
|
||||||
type-check: 0.4.0
|
type-check: 0.4.0
|
||||||
|
|
||||||
|
libphonenumber-js@1.12.24: {}
|
||||||
|
|
||||||
light-my-request@5.14.0:
|
light-my-request@5.14.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
cookie: 0.7.2
|
cookie: 0.7.2
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user