From 0ceba57012e310e542ba13fa6d6fb6ebe946438f Mon Sep 17 00:00:00 2001 From: Yuu Ottosoka Date: Sat, 12 Jul 2025 17:15:32 +0300 Subject: [PATCH] feat(bot): implement conversation flow for start command add conversation handler for start command with name input and welcome message update locales for new conversation flow add @grammyjs/conversations dependency --- apps/bot/locales/en.ftl | 3 ++- apps/bot/locales/ru.ftl | 3 ++- apps/bot/package.json | 1 + apps/bot/src/conversations/consts.ts | 3 +++ apps/bot/src/conversations/index.ts | 2 ++ .../src/conversations/start.conversation.ts | 23 +++++++++++++++++++ .../bot/src/handlers/purchase-subscription.ts | 0 apps/bot/src/index.ts | 7 +++++- apps/bot/src/types.ts | 5 +++- package-lock.json | 14 +++++++++++ 10 files changed, 57 insertions(+), 4 deletions(-) create mode 100644 apps/bot/src/conversations/consts.ts create mode 100644 apps/bot/src/conversations/index.ts create mode 100644 apps/bot/src/conversations/start.conversation.ts delete mode 100644 apps/bot/src/handlers/purchase-subscription.ts diff --git a/apps/bot/locales/en.ftl b/apps/bot/locales/en.ftl index 406ecc9..c83a03a 100644 --- a/apps/bot/locales/en.ftl +++ b/apps/bot/locales/en.ftl @@ -1 +1,2 @@ -start = Hello, how can I help you \ No newline at end of file +start-ask-name = Hello, what is your name? +start-welcome = Welcome, { $user }! \ No newline at end of file diff --git a/apps/bot/locales/ru.ftl b/apps/bot/locales/ru.ftl index 4f62ecb..2659462 100644 --- a/apps/bot/locales/ru.ftl +++ b/apps/bot/locales/ru.ftl @@ -1 +1,2 @@ -start = Привет, как я могу тебе помочь \ No newline at end of file +start-ask-name = Привет, как тебя зовут +start-welcome = Привет, { $user }! \ No newline at end of file diff --git a/apps/bot/package.json b/apps/bot/package.json index c15e804..6cdfa83 100644 --- a/apps/bot/package.json +++ b/apps/bot/package.json @@ -11,6 +11,7 @@ "clean": "rm -rf dist" }, "dependencies": { + "@grammyjs/conversations": "^2.1.0", "@grammyjs/i18n": "^1.1.2", "@hono/node-server": "^1.15.0", "@repo/config": "*", diff --git a/apps/bot/src/conversations/consts.ts b/apps/bot/src/conversations/consts.ts new file mode 100644 index 0000000..e3a4106 --- /dev/null +++ b/apps/bot/src/conversations/consts.ts @@ -0,0 +1,3 @@ +export enum ConversationSlug { + START = 'start', +} diff --git a/apps/bot/src/conversations/index.ts b/apps/bot/src/conversations/index.ts new file mode 100644 index 0000000..cbdb949 --- /dev/null +++ b/apps/bot/src/conversations/index.ts @@ -0,0 +1,2 @@ +export * from './consts'; +export * from './start.conversation'; diff --git a/apps/bot/src/conversations/start.conversation.ts b/apps/bot/src/conversations/start.conversation.ts new file mode 100644 index 0000000..c45e4f2 --- /dev/null +++ b/apps/bot/src/conversations/start.conversation.ts @@ -0,0 +1,23 @@ +import { Conversation, createConversation } from '@grammyjs/conversations'; +import { BotContext } from '../types'; +import { ConversationSlug } from './consts'; + +async function enterConversation(conversation: Conversation, ctx: BotContext) { + const askNameText = await conversation.external((ctx: BotContext) => + ctx.t('start-ask-name') + ); + + await ctx.reply(askNameText); + + const { message } = await conversation.waitFor('message:text'); + const welcomeText = await conversation.external((ctx: BotContext) => + ctx.t('start-welcome', { user: message.text }) + ); + + await ctx.reply(welcomeText); +} + +export const startConversation = createConversation( + enterConversation, + ConversationSlug.START +); diff --git a/apps/bot/src/handlers/purchase-subscription.ts b/apps/bot/src/handlers/purchase-subscription.ts deleted file mode 100644 index e69de29..0000000 diff --git a/apps/bot/src/index.ts b/apps/bot/src/index.ts index 00da0c6..eba4fcd 100644 --- a/apps/bot/src/index.ts +++ b/apps/bot/src/index.ts @@ -8,6 +8,8 @@ import { BotContext } from './types'; import { upsertUser } from './middlewares/upsert-user'; import { onError } from './middlewares/on-error'; import { I18n } from '@grammyjs/i18n'; +import { conversations } from '@grammyjs/conversations'; +import { ConversationSlug, startConversation } from './conversations'; const bot = new Bot(config.BOT_TOKEN); const storage = new PrismaStorageAdapter(); @@ -20,6 +22,7 @@ const i18n = new I18n({ defaultLocale: 'ru', useSession: true, // whether to store user language in session directory: 'locales', // Load all translation files from locales/. + fluentBundleOptions: { useIsolating: false }, localeNegotiator: async (ctx) => { const session = await ctx.session; return session.__language_code ?? ctx.from?.language_code ?? 'ru'; @@ -27,9 +30,11 @@ const i18n = new I18n({ }); bot.use(i18n); +bot.use(conversations()); +bot.use(startConversation); bot.command('start', async (ctx) => { - await ctx.reply(ctx.t('start')); + await ctx.conversation.enter(ConversationSlug.START); }); if (config.MODE === 'webhook') { diff --git a/apps/bot/src/types.ts b/apps/bot/src/types.ts index 0ba6b01..fad5658 100644 --- a/apps/bot/src/types.ts +++ b/apps/bot/src/types.ts @@ -1,6 +1,9 @@ +import { ConversationFlavor } from '@grammyjs/conversations'; import { I18nFlavor } from '@grammyjs/i18n'; import { Context, LazySessionFlavor } from 'grammy'; // config your session data export type SessionData = { __language_code?: string }; -export type BotContext = Context & LazySessionFlavor & I18nFlavor; +export type BotContext = ConversationFlavor< + Context & LazySessionFlavor & I18nFlavor +>; diff --git a/package-lock.json b/package-lock.json index 96aafc5..66e4129 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26,6 +26,7 @@ "@hono/zod-openapi": "^0.9.0", "@hono/zod-validator": "^0.4.3", "@repo/config": "*", + "@repo/db": "*", "@repo/logger": "*", "@repo/typescript-config": "*", "dotenv": "^17.1.0", @@ -149,6 +150,7 @@ "apps/bot": { "version": "0.0.0", "dependencies": { + "@grammyjs/conversations": "^2.1.0", "@grammyjs/i18n": "^1.1.2", "@hono/node-server": "^1.15.0", "@repo/config": "*", @@ -894,6 +896,18 @@ "npm": ">=7.0.0" } }, + "node_modules/@grammyjs/conversations": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@grammyjs/conversations/-/conversations-2.1.0.tgz", + "integrity": "sha512-nYoHwFyXcQ2lkQejKReoINknkgFjeIZhyJNMBwHng5UHO/ewHKs1dV2XVaLrKkHTZ1gCZ+dKkjEp26wwHyQeGg==", + "license": "MIT", + "engines": { + "node": "^12.20.0 || >=14.13.1" + }, + "peerDependencies": { + "grammy": "^1.20.1" + } + }, "node_modules/@grammyjs/i18n": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@grammyjs/i18n/-/i18n-1.1.2.tgz",