From c99422ebcf9a7a4423ede9a7cdd97c7a03836960 Mon Sep 17 00:00:00 2001 From: Roz Date: Sun, 21 Aug 2022 20:16:55 -0300 Subject: [PATCH] feat: implement /setinfo and /start commands --- package-lock.json | 52 ++++++++++++++ package.json | 5 +- src/bot.ts | 20 ++++-- src/commands/cancel.ts | 11 +-- src/commands/index.ts | 2 + src/commands/set-info.ts | 9 +++ src/commands/start.ts | 12 +--- src/conversations/index.ts | 1 + src/conversations/set-info.ts | 114 +++++++++++++++++++++++++++++++ src/util/pixCode.ts | 2 +- src/util/telegram/sendMessage.ts | 27 -------- src/util/telegram/wizard.ts | 41 ----------- src/util/types/Awaitable.ts | 1 - src/util/wizard.ts | 108 ----------------------------- src/wizards/set-info.ts | 87 ----------------------- 15 files changed, 202 insertions(+), 290 deletions(-) create mode 100644 src/commands/set-info.ts create mode 100644 src/conversations/index.ts create mode 100644 src/conversations/set-info.ts delete mode 100644 src/util/telegram/sendMessage.ts delete mode 100644 src/util/telegram/wizard.ts delete mode 100644 src/util/types/Awaitable.ts delete mode 100644 src/util/wizard.ts delete mode 100644 src/wizards/set-info.ts diff --git a/package-lock.json b/package-lock.json index 4e89416..5888683 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,8 +9,10 @@ "version": "1.2.0", "license": "GPL-3.0", "dependencies": { + "@grammyjs/conversations": "^1.0.2", "@grammyjs/router": "^2.0.0", "axios": "^0.21.1", + "common-tags": "^1.8.2", "dotenv": "^16.0.1", "grammy": "^1.10.1", "jimp": "^0.16.1", @@ -26,6 +28,7 @@ "@commitlint/config-conventional": "^12.1.1", "@semantic-release/changelog": "^5.0.1", "@semantic-release/git": "^9.0.1", + "@types/common-tags": "^1.8.1", "@types/mongodb": "^3.6.20", "@types/node": "^15.0.1", "@types/qrcode": "^1.4.0", @@ -454,6 +457,21 @@ "node": ">= 4" } }, + "node_modules/@grammyjs/conversations": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@grammyjs/conversations/-/conversations-1.0.2.tgz", + "integrity": "sha512-9VG0bDpDf2ndpIT8M6kJbiQ/OJ6JTBybEqhGEhEqnrMNsUTx/56sbkWyepvdIH6OPbOjO7iORdlHaStsGL0xSg==", + "dependencies": { + "@grammyjs/types": "^2.8.2", + "debug": "^4.3.4" + }, + "engines": { + "node": "^12.20.0 || >=14.13.1" + }, + "peerDependencies": { + "grammy": "^1.10.0" + } + }, "node_modules/@grammyjs/router": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@grammyjs/router/-/router-2.0.0.tgz", @@ -1399,6 +1417,12 @@ "bson": "*" } }, + "node_modules/@types/common-tags": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@types/common-tags/-/common-tags-1.8.1.tgz", + "integrity": "sha512-20R/mDpKSPWdJs5TOpz3e7zqbeCNuMCPhV7Yndk9KU2Rbij2r5W4RzwDPkzC+2lzUqXYu9rFzTktCBnDjHuNQg==", + "dev": true + }, "node_modules/@types/json-schema": { "version": "7.0.11", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", @@ -2691,6 +2715,14 @@ "node": ">= 12" } }, + "node_modules/common-tags": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz", + "integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==", + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/compare-func": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz", @@ -13237,6 +13269,15 @@ } } }, + "@grammyjs/conversations": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@grammyjs/conversations/-/conversations-1.0.2.tgz", + "integrity": "sha512-9VG0bDpDf2ndpIT8M6kJbiQ/OJ6JTBybEqhGEhEqnrMNsUTx/56sbkWyepvdIH6OPbOjO7iORdlHaStsGL0xSg==", + "requires": { + "@grammyjs/types": "^2.8.2", + "debug": "^4.3.4" + } + }, "@grammyjs/router": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@grammyjs/router/-/router-2.0.0.tgz", @@ -13993,6 +14034,12 @@ "bson": "*" } }, + "@types/common-tags": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@types/common-tags/-/common-tags-1.8.1.tgz", + "integrity": "sha512-20R/mDpKSPWdJs5TOpz3e7zqbeCNuMCPhV7Yndk9KU2Rbij2r5W4RzwDPkzC+2lzUqXYu9rFzTktCBnDjHuNQg==", + "dev": true + }, "@types/json-schema": { "version": "7.0.11", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", @@ -14964,6 +15011,11 @@ "strip-json-comments": "3.1.1" } }, + "common-tags": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz", + "integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==" + }, "compare-func": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz", diff --git a/package.json b/package.json index 29e1cda..f108b7d 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "@commitlint/config-conventional": "^12.1.1", "@semantic-release/changelog": "^5.0.1", "@semantic-release/git": "^9.0.1", + "@types/common-tags": "^1.8.1", "@types/mongodb": "^3.6.20", "@types/node": "^15.0.1", "@types/qrcode": "^1.4.0", @@ -57,8 +58,10 @@ } }, "dependencies": { + "@grammyjs/conversations": "^1.0.2", "@grammyjs/router": "^2.0.0", "axios": "^0.21.1", + "common-tags": "^1.8.2", "dotenv": "^16.0.1", "grammy": "^1.10.1", "jimp": "^0.16.1", @@ -92,4 +95,4 @@ "@semantic-release/git" ] } -} \ No newline at end of file +} diff --git a/src/bot.ts b/src/bot.ts index d839c29..5159208 100644 --- a/src/bot.ts +++ b/src/bot.ts @@ -1,9 +1,8 @@ +import { ConversationFlavor, conversations as grammyConversations } from '@grammyjs/conversations' import { Bot, Context, session, SessionFlavor } from 'grammy' -import { start } from './commands' -import { cancel } from './commands/cancel' +import { cancel, setInfo, start } from './commands' import { AppConfig } from './config' -import { WizardFlavor, wizards } from './util/wizard' -import setInfo from './wizards/set-info' +import * as conversations from './conversations' export type AppSession = { pixKey: string @@ -12,7 +11,7 @@ export type AppSession = { query?: string } -export type AppContext = Context & SessionFlavor & WizardFlavor +export type AppContext = Context & SessionFlavor & ConversationFlavor export async function getBot(config: AppConfig) { const bot = new Bot(config.telegram.token) @@ -28,10 +27,19 @@ export async function getBot(config: AppConfig) { }) ) - bot.use(...wizards([setInfo])) + bot.use(grammyConversations()) + /** Cancel Command */ bot.command(cancel.name, cancel.fn) + + /** Conversations */ + bot.use(conversations.setInfo) + + /** Regular commands */ bot.command(start.name, start.fn) + bot.command(setInfo.name, setInfo.fn) + + bot.catch(console.error) return bot } diff --git a/src/commands/cancel.ts b/src/commands/cancel.ts index 5c63877..84c4c60 100644 --- a/src/commands/cancel.ts +++ b/src/commands/cancel.ts @@ -3,14 +3,9 @@ import { AppContext } from '../bot' export const cancel = { name: 'cancel', helpText: 'Cancela a operação atual', - fn: (ctx: AppContext) => { - if (ctx.session.wizard?.id === 'setInfo') { - ctx.session.city = '' - ctx.session.name = '' - ctx.session.pixKey = '' - } - ctx.wizard.exit() + fn: async (ctx: AppContext) => { delete ctx.session.query - return ctx.reply('Ok, deixa pra lá') + await ctx.reply('Tudo bem. Deixa pra lá :)', { reply_markup: { remove_keyboard: true } }) + await ctx.conversation.exit() } } diff --git a/src/commands/index.ts b/src/commands/index.ts index be77bd3..83002bb 100644 --- a/src/commands/index.ts +++ b/src/commands/index.ts @@ -1 +1,3 @@ +export * from './cancel' +export * from './set-info' export * from './start' diff --git a/src/commands/set-info.ts b/src/commands/set-info.ts new file mode 100644 index 0000000..b722bd5 --- /dev/null +++ b/src/commands/set-info.ts @@ -0,0 +1,9 @@ +import { AppContext } from '../bot' + +export const setInfo = { + name: 'setinfo', + helpText: 'Define suas informações', + fn: async (ctx: AppContext) => { + return ctx.conversation.enter('setInfo') + } +} diff --git a/src/commands/start.ts b/src/commands/start.ts index 694abce..94f15fd 100644 --- a/src/commands/start.ts +++ b/src/commands/start.ts @@ -1,7 +1,7 @@ import { InlineKeyboard } from 'grammy' +import { evaluateQuery } from '../../old/handleInlineQuery' import { AppContext } from '../bot' import { User } from '../domain/User' -import { evaluateQuery } from '../old/handleInlineQuery' const KNOWN_MESSAGE = (user: User) => `Opa, tudo certo? Eu já tenho seus dados do Pix aqui, olha só: @@ -17,8 +17,6 @@ const KNOWN_MESSAGE_REQUESTED = (amount: string) => ` Para gerar um código de R$ ${amount} conforme solicitado, clique no botão abaixo. ` -const PRIVACY = `\n\n\\(Ao responder, você concorda com o armazenamento desses dados com o único propósito de gerar os códigos pix que você solicitar e de acordo com a [política de privacidade](https://github.com/roziscoding/amandapix-telegram-bot/blob/main/PRIVACY.md)\\)` - export const start = { name: 'start', helpText: 'Cria seu cadastro, caso ainda não exista', @@ -40,12 +38,6 @@ export const start = { } if (query) ctx.session.query = query - ctx.wizard.enter('setInfo', { step: 1 }) - - const text = query - ? `Opa, entendi que você quer gerar um código de ${query} mas, pra isso, primeiro me manda sua chave Pix:${PRIVACY}` - : `Opa, bora te cadastrar por aqui pra você poder gerar códigos Pix\\! Primeiro, me manda sua chave Pix:${PRIVACY}` - - return ctx.reply(text, { disable_web_page_preview: true, parse_mode: 'MarkdownV2' }) + return ctx.conversation.enter('setInfo') } } diff --git a/src/conversations/index.ts b/src/conversations/index.ts new file mode 100644 index 0000000..c0bf2c0 --- /dev/null +++ b/src/conversations/index.ts @@ -0,0 +1 @@ +export { default as setInfo } from './set-info' diff --git a/src/conversations/set-info.ts b/src/conversations/set-info.ts new file mode 100644 index 0000000..e949cfc --- /dev/null +++ b/src/conversations/set-info.ts @@ -0,0 +1,114 @@ +import { Conversation, createConversation } from '@grammyjs/conversations' +import { oneLine, safeHtml, stripIndents } from 'common-tags' +import { InlineKeyboard, Keyboard } from 'grammy' +import { evaluateQuery } from '../../old/handleInlineQuery' +import { AppContext } from '../bot' + +const PRIVACY_URL = 'https://github.com/roziscoding/amandapix-telegram-bot/blob/main/PRIVACY.md' + +const confirm = new Keyboard().text('Sim').text('Não').oneTime() +const cancellable = (fn: (ctx: AppContext) => any) => (ctx: AppContext) => ctx.hasCommand('cancel') ?? fn(ctx) + +const setInfo = async (conversation: Conversation, ctx: AppContext) => { + await ctx.reply( + oneLine` + Antes de começarmos, preciso que você leia minha [política de privacidade](${PRIVACY_URL})\\. + Você concorda com a política de privacidade? Você pode usar /cancel a qualquer momento pra encerrar a conversa\\. + `, + { parse_mode: 'MarkdownV2', disable_web_page_preview: true, reply_markup: confirm } + ) + + const privacyPolicy = await conversation.form.select( + ['Sim', 'Não'], + cancellable((ctx) => ctx.reply('Usa um dos botões pra me responder, por favor.')) + ) + + if (privacyPolicy === 'Não') { + await ctx.reply('Tudo bem. Deixa pra lá então.', { reply_markup: { remove_keyboard: true } }) + return ctx.reply( + 'Se quiser sugerir mudanças na minha política de privacidade, você pode abrir uma issue no meu repositório.', + { + reply_markup: new InlineKeyboard().url( + 'Abrir issue', + 'https://github.com/roziscoding/amandapix-telegram-bot/issues/new' + ) + } + ) + } + + await ctx.reply('Boa! Agora sim, podemos começar!', { reply_markup: { remove_keyboard: true } }) + + await ctx.reply('Primeiro, me envia sua chave pix.') + const pixKey = await conversation.form.text( + cancellable((ctx) => ctx.reply('Me manda sua chave pix como texto, por favor!')) + ) + + await ctx.reply('Show. Agora me manda sua cidade, por favor') + const city = await conversation.form.text( + cancellable((ctx) => ctx.reply('Pra continuarmos, preciso que me mande sua cidade como texto')) + ) + + await ctx.reply('Boa. Por último, me diz seu nome') + const name = await conversation.form.text( + cancellable((ctx) => ctx.reply('Ainda não sei seu nome... Pode me mandar como texto, por favor?')) + ) + + await ctx.reply( + stripIndents(safeHtml)` + Boa! Então me confirma se tá tudo certo. Esses são os dados que eu anotei: + + Chave PIX: ${pixKey} + Cidade: ${city} + Nome: ${name} + + Tá correto? + `, + { parse_mode: 'HTML', reply_markup: confirm.text('Cancelar Cadastro') } + ) + + const confirmation = await conversation.form.select( + ['Sim', 'Não', 'Cancelar Cadastro'], + cancellable((ctx) => ctx.reply('Não entendi... Por favor, usa os botões pra me responder :)')) + ) + + if (confirmation === 'Cancelar Cadastro') { + return ctx.reply('OK. Deixa pra lá então. Espero poder ajudar numa próxima :)', { + reply_markup: { remove_keyboard: true } + }) + } + + if (confirmation === 'Não') { + await ctx.reply('Putz. Bora do começo então', { reply_markup: { remove_keyboard: true } }) + return ctx.conversation.reenter('setInfo') + } + + // ctx.session.pixKey = pixKey + // ctx.session.city = city + // ctx.session.name = name + + await ctx.reply('Só mais um minuto...', { reply_markup: { remove_keyboard: true } }) + await conversation.sleep(1000) + + if (ctx.session.query) { + const amount = await evaluateQuery(ctx.session.query) + const keyboard = new InlineKeyboard().switchInline(`Gerar código Pix de R$ ${amount.toString()}`, ctx.session.query) + return ctx.reply(`Pronto! Agora tá tudo certo pra gerar o código Pix de ${amount} reais. É só clicar no botão:`, { + reply_markup: keyboard + }) + } + + const keyboard = new InlineKeyboard().switchInline(`Gerar código Pix`) + return ctx.reply( + stripIndents` + Pronto! Agora tá tudo certo pra gerar códigos Pix. Pra isso, é só clicar no botão aqui em baixo. + + Você também pode digitar, em qualquer chat, @amandapixbot 10, trocando "10" pelo valor desejado, ou por uma operação matemática. + `, + { + parse_mode: 'HTML', + reply_markup: keyboard + } + ) +} + +export default createConversation(setInfo) diff --git a/src/util/pixCode.ts b/src/util/pixCode.ts index aaf42d6..4ad7c0d 100644 --- a/src/util/pixCode.ts +++ b/src/util/pixCode.ts @@ -1,5 +1,5 @@ import { pix } from 'pix-me' -import { User } from '../old/domain/User' +import { User } from '../../old/domain/User' export function getPixCodeForUser(user: User, value: string | number) { return pix({ diff --git a/src/util/telegram/sendMessage.ts b/src/util/telegram/sendMessage.ts deleted file mode 100644 index 6868c95..0000000 --- a/src/util/telegram/sendMessage.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { ForceReply, InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove } from 'typegram' -import { Response } from '../../old/domain/Response' - -export const REMOVE_KEYBOARD: ReplyKeyboardRemove = { - remove_keyboard: true -} - -export const CONFIRM: ReplyKeyboardMarkup = { - keyboard: [[{ text: 'Sim' }], [{ text: 'Não' }]], - one_time_keyboard: true, - resize_keyboard: true -} - -export const CONFIRM_OR_CANCEL: ReplyKeyboardMarkup = { - ...CONFIRM, - keyboard: [...CONFIRM.keyboard, [{ text: 'Cancelar' }]] -} - -export type Markup = InlineKeyboardMarkup | ReplyKeyboardMarkup | ReplyKeyboardRemove | ForceReply - -export const sendMessage = (id: number, text: string, markdown = false, markup?: Markup): Response<'sendMessage'> => ({ - method: 'sendMessage', - chat_id: id, - text, - parse_mode: markdown ? 'Markdown' : undefined, - reply_markup: markup -}) diff --git a/src/util/telegram/wizard.ts b/src/util/telegram/wizard.ts deleted file mode 100644 index 2562b87..0000000 --- a/src/util/telegram/wizard.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { Telegram } from 'typegram' -import { Command, Context } from '../../old/domain/Command' -import { Response } from '../../old/domain/Response' -import { Awaitable } from '../types/Awaitable' - -type WizardContext = { - next: (extraData?: any) => Promise - previous: (extraData?: any) => Promise - setStep: (stepIndex: number) => Promise - exit: () => Promise -} - -type WizardFn = (ctx: Context, wizardCtx: WizardContext) => Awaitable | null> - -export const getWizard = - (commandName: string, steps: [WizardFn, ...WizardFn[]]): Command['fn'] => - (ctx) => { - const sessionData = ctx.user.session?.data - const stepIndex = sessionData?.step || 0 - const step = steps[stepIndex]! - - const setSession = async (stepNumber: number, extraData: any = {}) => { - const sessionData = ctx.user.session?.data || {} - await ctx.repository.setSesstion(ctx.user.telegramId, commandName, { - ...sessionData, - ...extraData, - step: stepNumber - }) - } - - const wizardContext: WizardContext = { - next: async (extraData = {}) => setSession(stepIndex + 1, extraData), - previous: async (extraData = {}) => setSession(stepIndex > 0 ? stepIndex - 1 : 0, extraData), - setStep: async (stepIndex) => setSession(stepIndex), - exit: async () => { - await ctx.repository.clearSession(ctx.user.telegramId) - } - } - - return step(ctx, wizardContext) - } diff --git a/src/util/types/Awaitable.ts b/src/util/types/Awaitable.ts deleted file mode 100644 index fe097b3..0000000 --- a/src/util/types/Awaitable.ts +++ /dev/null @@ -1 +0,0 @@ -export type Awaitable = Promise | T diff --git a/src/util/wizard.ts b/src/util/wizard.ts deleted file mode 100644 index 30b2052..0000000 --- a/src/util/wizard.ts +++ /dev/null @@ -1,108 +0,0 @@ -import { Router } from '@grammyjs/router' -import { Context, MiddlewareFn, SessionFlavor } from 'grammy' - -const internal = Symbol('wizard') - -type Internals = { - wizards: Map>> -} - -export type WizardSessionData = { - wizard?: { - id: string - step: number - } -} - -export type WizardStep = (wizard: Wizard, ctx: C) => unknown | Promise - -export type WizardFlavor = { - wizard: WizardControls -} & SessionFlavor - -export type WizardDefinition = { - id: string - handler: MiddlewareFn -} - -class Wizard { - constructor(private readonly ctx: C & WizardFlavor) { - if (!ctx.session.wizard) { - throw new Error('Wizard data not found in session.') - } - } - - next() { - this.ctx.session.wizard!.step += 1 - } - - previous() { - this.ctx.session.wizard!.step -= 1 - } - - setStep(step: number) { - this.ctx.session.wizard!.step = step - } - - exit() { - delete this.ctx.session.wizard - } -} - -class WizardControls { - readonly [internal]: Internals = { wizards: new Map() } - - constructor(private readonly session: WizardSessionData) {} - - enter(id: string, extra: { step?: number } = {}) { - const { step = 0 } = extra - this.session.wizard = { id, step } - } - - exit() { - delete this.session.wizard - } -} - -export function wizards( - wizards: Array> -): [MiddlewareFn, Router] { - // Why doesn't TypeScript accepts that `C` satisfies `Context`? - const router = new Router((ctx) => ctx.session.wizard?.id) - - for (const wizardDefinition of wizards) { - router.route(wizardDefinition.id, wizardDefinition.handler) - } - - return [ - async (ctx, next) => { - if (!('session' in ctx)) { - throw new Error('Cannot use wizards without session!') - } - - ctx.wizard ??= new WizardControls(ctx.session) - - await next() - }, - router - ] -} - -export function createWizard(id: string, steps: Array>): WizardDefinition { - const handler: MiddlewareFn = async (ctx, next) => { - const stepIndex = ctx.session.wizard?.step ?? 0 - - const step = steps[stepIndex] - if (!step) { - throw new Error(`Wizard ${id} does not have a step with index ${stepIndex}`) - } - - await step(new Wizard(ctx), ctx) - return next() - } - - return { - id, - handler - } -} diff --git a/src/wizards/set-info.ts b/src/wizards/set-info.ts deleted file mode 100644 index a6fa585..0000000 --- a/src/wizards/set-info.ts +++ /dev/null @@ -1,87 +0,0 @@ -import { InlineKeyboard, Keyboard } from 'grammy' -import { AppContext } from '../bot' -import { evaluateQuery } from '../old/handleInlineQuery' -import { createWizard } from '../util/wizard' - -export default createWizard('setInfo', [ - async (wizard, ctx) => { - wizard.next() - await ctx.reply('Bora ajeitar esses dados do Pix! Primeiro, me manda sua chave:') - }, - async (wizard, ctx) => { - const key = ctx.message?.text - - if (!key) { - await ctx.reply('Me manda sua chave Pix numa mensagem de texto, por favor :)') - return - } - - ctx.session.pixKey = key - wizard.next() - await ctx.reply('Show! Agora, me manda sua cidade:') - }, - async (wizard, ctx) => { - const city = ctx.message?.text - - if (!city) { - await ctx.reply('Me manda sua cidade numa mensagem de texto, por favor :)') - return - } - - ctx.session.city = city - wizard.next() - await ctx.reply('Boa! Por último, me diz seu nome:') - }, - async (wizard, ctx) => { - const name = ctx.message?.text - - if (!name) { - await ctx.reply('Me manda seu nome numa mensagem de texto, por favor :)') - return - } - - ctx.session.name = name - wizard.next() - - const { pixKey, city } = ctx.session - const message = `Bom, pelo que eu entendi, seus dados são:\n\n\`Chave: ${pixKey}\`\n\`Cidade: ${city}\`\n\`Nome: ${name}\`\n\nTá correto?` - - const keyboard = new Keyboard().text('Sim').row().text('Não').row().text('Cancelar') - - await ctx.reply(message, { parse_mode: 'MarkdownV2', reply_markup: keyboard }) - }, - async (wizard, ctx) => { - const text = ctx.message?.text - if (!text) { - return ctx.reply('Por favor, utilize um dos botões pra me responder :)') - } - - if (text === 'Cancelar') { - wizard.exit() - return ctx.reply('OK, deixa pra lá. Até mais!', { reply_markup: { remove_keyboard: true } }) - } - - if (text === 'Não') { - wizard.setStep(1) - - return ctx.reply('Ok, vamos do começo então. Me manda sua chave Pix:', { - reply_markup: { remove_keyboard: true } - }) - } - - if (text === 'Sim') { - await wizard.exit() - await ctx.reply('Entendi! Só mais um minuto...', { reply_markup: { remove_keyboard: true } }) - - const query = ctx.session.query - const amount = query ? await evaluateQuery(query) : 0 - - const buttonText = query ? `Gerar código de R$ ${amount.toFixed(2)}` : 'Gerar código Pix' - return ctx.reply('Tudo certo! Você já pode gerar códigos Pix! Pra isso, me chama inline, ou clica no botão :D', { - reply_markup: new InlineKeyboard().switchInline(buttonText) - }) - } - - return ctx.reply('Não entendi... Por favor, clique em um dos botões para responder') - } -])