import type { Conversation } from '@grammyjs/conversations' import type { BotContext } from '../types.js' type BotConversation = Conversation import { getActiveCities, findUserByTelegramId } from '../../api/_lib/db.js' import { t, getLang } from '../i18n/index.js' import { createQuestFromOnboarding } from '../services/quest.service.js' const COMPANIONS_MAP: Record = { solo: 'solo', couple: 'couple', family: 'family', friends: 'friends', } const PACE_MAP: Record = { slow: 'slow', normal: 'normal', active: 'active', } export async function onboardingConversation(conversation: BotConversation, ctx: BotContext) { const lang = getLang(ctx.from?.language_code) // Step 1: City selection const cities = await conversation.external(() => getActiveCities()) const cityButtons = cities.map((city: { id: string; name: string }) => ([ { text: city.name, callback_data: `city_${city.id}` } ])) await ctx.reply(t(lang, 'choose_city'), { reply_markup: { inline_keyboard: cityButtons }, }) const cityResponse = await conversation.waitForCallbackQuery(/^city_/) const cityId = cityResponse.callbackQuery!.data!.replace('city_', '') const cityName = cities.find((c: { id: string }) => c.id === cityId)?.name || 'Unknown' await cityResponse.answerCallbackQuery() // Step 2: Number of days await ctx.reply(t(lang, 'choose_days')) let days: number = 0 while (true) { const daysResponse = await conversation.waitFor('message:text') const parsed = parseInt(daysResponse.message.text) if (parsed >= 1 && parsed <= 30) { days = parsed break } await ctx.reply(t(lang, 'invalid_days')) } // Step 3: Companions await ctx.reply(t(lang, 'choose_companions'), { reply_markup: { inline_keyboard: [ [ { text: t(lang, 'companions_solo'), callback_data: 'comp_solo' }, { text: t(lang, 'companions_couple'), callback_data: 'comp_couple' }, ], [ { text: t(lang, 'companions_family'), callback_data: 'comp_family' }, { text: t(lang, 'companions_friends'), callback_data: 'comp_friends' }, ], ], }, }) const compResponse = await conversation.waitForCallbackQuery(/^comp_/) const companions = COMPANIONS_MAP[compResponse.callbackQuery!.data!.replace('comp_', '')] || 'solo' await compResponse.answerCallbackQuery() // Step 4: Pace await ctx.reply(t(lang, 'choose_pace'), { reply_markup: { inline_keyboard: [ [{ text: t(lang, 'pace_slow'), callback_data: 'pace_slow' }], [{ text: t(lang, 'pace_normal'), callback_data: 'pace_normal' }], [{ text: t(lang, 'pace_active'), callback_data: 'pace_active' }], ], }, }) const paceResponse = await conversation.waitForCallbackQuery(/^pace_/) const pace = PACE_MAP[paceResponse.callbackQuery!.data!.replace('pace_', '')] || 'normal' await paceResponse.answerCallbackQuery() // Step 5: Wishes (optional) await ctx.reply(t(lang, 'add_wishes'), { reply_markup: { inline_keyboard: [ [{ text: t(lang, 'skip'), callback_data: 'wishes_skip' }], ], }, }) let userComment: string | undefined // Wait for either text or callback const wishesCtx = await conversation.wait() if (wishesCtx.callbackQuery?.data === 'wishes_skip') { await wishesCtx.answerCallbackQuery() userComment = undefined } else if (wishesCtx.message?.text) { userComment = wishesCtx.message.text } // Step 6: Confirmation const companionsLabel = t(lang, `companions_${companions}`) const paceLabel = t(lang, `pace_${pace}`) const wishesLine = userComment ? t(lang, 'wishes_line', { comment: userComment }) : '' await ctx.reply( t(lang, 'onboarding_summary', { city: cityName, days: days, companions: companionsLabel, pace: paceLabel, wishes: wishesLine, }), { reply_markup: { inline_keyboard: [ [ { text: t(lang, 'confirm_yes'), callback_data: 'onboarding_confirm' }, { text: t(lang, 'confirm_restart'), callback_data: 'onboarding_restart' }, ], ], }, } ) const confirmResponse = await conversation.waitForCallbackQuery(/^onboarding_/) await confirmResponse.answerCallbackQuery() if (confirmResponse.callbackQuery!.data === 'onboarding_restart') { // Restart the conversation await ctx.reply(t(lang, 'welcome')) return } // Step 7: Create quest await ctx.reply(t(lang, 'creating_quest')) const userId = await conversation.external(async () => { const user = await findUserByTelegramId(ctx.from!.id) return user?.id }) if (!userId) { await ctx.reply(t(lang, 'use_start')) return } const result = await conversation.external(() => createQuestFromOnboarding({ userId, cityId, cityName, days, companions, pace, userComment, lang, }) ) if (!result) { await ctx.reply('Something went wrong. Please try /start again.') return } const appUrl = process.env.APP_URL || 'https://guidly.example.com' const isHttps = appUrl.startsWith('https://') const questText = t(lang, 'quest_created', { title: result.title, description: result.description, }) + (isHttps ? '' : `\n\nšŸ”— ${appUrl}`) await ctx.reply( questText, isHttps ? { reply_markup: { inline_keyboard: [[ { text: t(lang, 'open_quest'), web_app: { url: appUrl } } ]], }, } : {} ) }