Telegram Bot - 根据最后一个机器人问题处理用户响应

Die*_*sus 8 java telegram telegram-bot

我希望我的 Telegram 机器人根据机器人提出的最后一个问题来处理用户输入。基本上,流程是这样的:

  • 用户调用 /authenticate 命令
  • 机器人要求提供电子邮件
  • 用户发送他的电子邮件
  • 机器人会回复一条有关将代码发送到用户的电子邮件进行确认的消息,并要求用户在聊天中输入代码
  • 用户输入代码
  • 机器人验证用户代码并且用户已通过身份验证并开始接收通知

问题是:我如何知道用户正在回答此流程中的特定机器人问题?

我想了两种方法:

  • 发送带有强制回复选项的消息,以便用户必须回复机器人问题。这将向我发回用户正在响应的消息,以便我可以比较机器人消息字符串以查看答案是什么。

  • 将最后一条机器人消息存储在某处,然后当消息到达时,检查最后一条机器人消息是什么,并假设用户消息是响应。

有没有更好的办法?我正在使用 Java 和telegrambots 库

Die*_*sus 6

由于很难找到可以引导我找到解决方案的想法(在Java中),因此我将在这里分享我的想法,以供未来的 Java 谷歌搜索者参考。我将telegrambots 库与 Spring Boot/Data 一起使用。

\n

实现此流程的最佳方法是将状态保存在数据库中。为此,请使用消息唯一的聊天 ID 来区分聊天和其他聊天

\n

以下是 Java 实现的相关部分(逻辑几乎适用于任何语言):

\n

保存与系统用户相关的 Telegram 聊天信息的实体。

\n
@Entity\n@Table(name = "user_bot")\npublic class UserBot implements Serializable {\n\n    @Id\n    @GeneratedValue(strategy = GenerationType.IDENTITY)\n    private Integer id;\n\n    @Column(name = "chat_id", unique = true, nullable = false, length = 255)\n    private String chatId;\n\n    @Column(name = "bot_verification_code", length = 6)\n    private String botVerificationCode;\n\n    @Enumerated\n    @Column(name = "last_bot_state", columnDefinition = "SMALLINT DEFAULT NULL")\n    private BotState lastBotState;\n\n    @Column(columnDefinition = "TINYINT(1)")\n    private boolean verified;\n\n    @JoinColumn(name = "user_id", referencedColumnName = "id")\n    @ManyToOne(fetch = FetchType.EAGER)\n    private User user;\n}\n
Run Code Online (Sandbox Code Playgroud)\n

代表所有可能的机器人响应(状态)的枚举。

\n
public enum BotState {\n    // Respostas do bot que representam estados\n    AUTH_STEP_1("Muito bem. Qual \xc3\xa9 o seu e-mail no sistema?"), AUTH_STEP_2("Enviei um c\xc3\xb3digo para o seu e-mail. Por favor, digite-o aqui."),\n    NO_STATE("");\n\n    private final String state;\n\n    private BotState(String state) {\n        this.state = state;\n    }\n\n    @Override\n    public String toString() {\n        return this.state;\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

接收消息并做出相应响应的服务。

\n
@Service\npublic class TelegramBotService extends TelegramLongPollingBot {\n\n    @Autowired\n    private CodeUtils codeUtils;\n\n    @Autowired\n    private UserBotRepository userBotRepository;\n\n    @Autowired\n    private UserRepository userRepository;\n\n    @Value("${telegram.bot.username}")\n    private String botUsername;\n\n    @Value("${telegram.bot.token}")\n    private String botToken;\n\n    @PostConstruct\n    public void registerBot() {\n        TelegramBotsApi botsApi = new TelegramBotsApi();\n        try {\n            botsApi.registerBot(this);\n        } catch (TelegramApiException e) {\n            e.printStackTrace();\n        }\n    }\n\n    @Override\n    public void onUpdateReceived(Update update) {\n        if (update.hasMessage() && update.getMessage().hasText()) {\n            String receivedMessage = update.getMessage().getText();\n            SendMessage sendMessage = null;\n\n            // TODO: futuramente, tratar casos onde um usu\xc3\xa1rio chama um comando sem ainda estar autenticado\n            switch (receivedMessage) {\n                case "/autenticar":\n                    sendMessage = handleAuthentication(update);\n                    break;\n                default:\n                    // Quando nenhum comando atender, ser\xc3\xa1 um texto a ser checado de acordo com o estado anterior\n                    sendMessage = checkState(update);\n            }\n\n            try {\n                execute(sendMessage);\n            } catch (TelegramApiException e) {\n                codeUtils.log(e.getMessage(), this);\n            }\n        }\n    }\n\n    private SendMessage handleAuthentication(Update update) {\n        SendMessage sendMessage = new SendMessage()\n                .setChatId(update.getMessage().getChatId())\n                .setText(BotState.AUTH_STEP_1.toString());\n\n        UserBot userBot = userBotRepository.findByChatId(update.getMessage().getChatId().toString());\n\n        if (userBot == null) {\n            userBot = new UserBot();\n            userBot.setChatId(update.getMessage().getChatId().toString());\n            userBot.setLastBotState(BotState.AUTH_STEP_1);\n        } else if (userBot.isVerified()) {\n            // Um texto simples enviado no sendMessage indica o fim de um fluxo\n            sendMessage.setText("Este aparelho j\xc3\xa1 est\xc3\xa1 autenticado no sistema.");\n            userBot.setLastBotState(null);\n        }\n\n        userBotRepository.save(userBot);\n        return sendMessage;\n    }\n\n    // Checa o estado anterior do bot em rela\xc3\xa7\xc3\xa3o ao chatId recebido\n    private SendMessage checkState(Update update) {\n        UserBot userBot = userBotRepository.findByChatId(update.getMessage().getChatId().toString());\n        SendMessage sendMessage = null;\n\n        if (userBot == null || userBot.getLastBotState() == null)\n            return sendDefaultMessage(update);\n\n        switch (Optional.ofNullable(userBot.getLastBotState()).orElse(BotState.NO_STATE)) {\n            case AUTH_STEP_1:\n                sendMessage = sendCode(update);\n                break;\n            case AUTH_STEP_2:\n                sendMessage = validateCode(update);\n                break;\n            default:\n                sendMessage = sendDefaultMessage(update);\n        }\n\n        return sendMessage;\n    }\n\n    // Grava o c\xc3\xb3digo no banco e envia para o e-mail do usu\xc3\xa1rio\n    private SendMessage sendCode(Update update) {\n        User user = userRepository.findByEmail(update.getMessage().getText().toLowerCase());\n        SendMessage sendMessage = new SendMessage(update.getMessage().getChatId(), "");\n\n        if (user == null)\n            sendMessage.setText("N\xc3\xa3o encontrei nenhum usu\xc3\xa1rio no sistema com este e-mail :(");\n        else {\n            UserBot userBot = userBotRepository.findByChatId(update.getMessage().getChatId().toString());\n\n            String verificationCode = Integer.toString(new Random().nextInt(899999) + 100000);\n            String text = "Este \xc3\xa9 um e-mail autom\xc3\xa1tico de verifica\xc3\xa7\xc3\xa3o de identidade. Informe este c\xc3\xb3digo para o bot do Telegram: " + verificationCode;\n            codeUtils.sendEmail(new String[]{user.getEmail()}, "CCR Laudos - C\xc3\xb3digo de Verifica\xc3\xa7\xc3\xa3o", text);\n\n            // Associa a conversa\xc3\xa7\xc3\xa3o ao usu\xc3\xa1rio, mas a validade depende da flag verified\n            userBot.setUser(user);\n            userBot.setBotVerificationCode(verificationCode);\n            userBot.setLastBotState(BotState.AUTH_STEP_2);\n            userBotRepository.save(userBot);\n\n            sendMessage.setText(BotState.AUTH_STEP_2.toString());\n        }\n\n        return sendMessage;\n    }\n\n    // Checa se o c\xc3\xb3digo informado foi o mesmo passado por e-mail para o usu\xc3\xa1rio a fim de autentic\xc3\xa1-lo\n    private SendMessage validateCode(Update update) {\n        UserBot userBot = userBotRepository.findByChatId(update.getMessage().getChatId().toString());\n        SendMessage sendMessage = new SendMessage(update.getMessage().getChatId(), "");\n\n        if (update.getMessage().getText().equals(userBot.getBotVerificationCode())) {\n            userBot.setVerified(true);\n            sendMessage.setText("O aparelho foi autenticado com sucesso. Voc\xc3\xaa passar\xc3\xa1 a receber notifica\xc3\xa7\xc3\xb5es do sistema.");\n        } else {\n            userBot.setUser(null);\n            sendMessage.setText("C\xc3\xb3digo inv\xc3\xa1lido.");\n        }\n\n        userBotRepository.save(userBot);\n        return sendMessage;\n    }\n\n    private SendMessage sendDefaultMessage(Update update) {\n        String markdownMessage = "N\xc3\xa3o entendi \\ud83e\\udd14 \\n"\n                + "Que tal tentar um comando digitando */* ?";\n        return new SendMessage(update.getMessage().getChatId(), markdownMessage).setParseMode(ParseMode.MARKDOWN);\n    }\n\n    @Override\n    public String getBotUsername() {\n        return this.botUsername;\n    }\n\n    @Override\n    public String getBotToken() {\n        return this.botToken;\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

实现的流程是:

\n
    \n
  1. 用户发送/验证。

    \n
  2. \n
  3. 系统对设备一无所知,因此存储聊天 ID 和最后状态。最后的状态将是对用户的响应。系统询问用户的电子邮件。

    \n
  4. \n
  5. 用户发送他的电子邮件。

    \n
  6. \n
  7. 该文本未被识别为命令,因此系统检查是否存在与该聊天 ID 相关的最后状态。如果存在先前的状态,则使用传入的文本作为该状态的方法的参数。系统将代码发送到用户的电子邮件并要求提供。

    \n
  8. \n
  9. 用户发送代码。

    \n
  10. \n
  11. 系统再次检查之前的状态,如果代码正确,则对用户进行身份验证。

    \n
  12. \n
\n

就是这样!希望它能帮助某人。

\n