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

Telegram Bot - Process response from user based on last bot question

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

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

我想过两种方法:

有没有更好的方法?我正在使用 Java 和 telegrambots library

通常要具体说明您发送给电报机器人的内容,您会使用命令与机器人通信,(电报命令以 / 开头,并且每个命令在服务器端都有一个特定的命令处理程序),不幸的是,到目前为止,无法将额外的参数连同命令一起发送到电报机器人,您可以使用以下对话中提到的解决方法之一:

如果您不熟悉使用 java 的 bot 命令,请参考以下示例: https://www.programcreek.com/java-api-examples/?api=org.telegram.telegrambots.bots.AbsSender

希望对您有所帮助。

开发电报机器人时最流行的任务之一。
有一个解决方案,您需要安装 shapoapps/multibot_driver 包 (composer require shapoapps/multibot_driver) 对于 Laravel。
这个包有一个会话管理器,类似于网页用户的会话。在每个用户消息之后,您将用户输入记录到服务器端的会话中。当收到每个新请求(消息)时,您从会话中读取数据并构建逻辑。
这是文档 - https://github.com/shapoapps/multibot_driver

由于很难找到可以引导我找到解决方案的想法(在 Java),我将在这里分享我的想法未来 Java 位谷歌员工。我正在使用 telegrambots library 和 Spring Boot/Data.

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

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

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

@Entity
@Table(name = "user_bot")
public class UserBot implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    @Column(name = "chat_id", unique = true, nullable = false, length = 255)
    private String chatId;

    @Column(name = "bot_verification_code", length = 6)
    private String botVerificationCode;

    @Enumerated
    @Column(name = "last_bot_state", columnDefinition = "SMALLINT DEFAULT NULL")
    private BotState lastBotState;

    @Column(columnDefinition = "TINYINT(1)")
    private boolean verified;

    @JoinColumn(name = "user_id", referencedColumnName = "id")
    @ManyToOne(fetch = FetchType.EAGER)
    private User user;
}

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

public enum BotState {
    // Respostas do bot que representam estados
    AUTH_STEP_1("Muito bem. Qual é o seu e-mail no sistema?"), AUTH_STEP_2("Enviei um código para o seu e-mail. Por favor, digite-o aqui."),
    NO_STATE("");

    private final String state;

    private BotState(String state) {
        this.state = state;
    }

    @Override
    public String toString() {
        return this.state;
    }
}

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

@Service
public class TelegramBotService extends TelegramLongPollingBot {

    @Autowired
    private CodeUtils codeUtils;

    @Autowired
    private UserBotRepository userBotRepository;

    @Autowired
    private UserRepository userRepository;

    @Value("${telegram.bot.username}")
    private String botUsername;

    @Value("${telegram.bot.token}")
    private String botToken;

    @PostConstruct
    public void registerBot() {
        TelegramBotsApi botsApi = new TelegramBotsApi();
        try {
            botsApi.registerBot(this);
        } catch (TelegramApiException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onUpdateReceived(Update update) {
        if (update.hasMessage() && update.getMessage().hasText()) {
            String receivedMessage = update.getMessage().getText();
            SendMessage sendMessage = null;

            // TODO: futuramente, tratar casos onde um usuário chama um comando sem ainda estar autenticado
            switch (receivedMessage) {
                case "/autenticar":
                    sendMessage = handleAuthentication(update);
                    break;
                default:
                    // Quando nenhum comando atender, será um texto a ser checado de acordo com o estado anterior
                    sendMessage = checkState(update);
            }

            try {
                execute(sendMessage);
            } catch (TelegramApiException e) {
                codeUtils.log(e.getMessage(), this);
            }
        }
    }

    private SendMessage handleAuthentication(Update update) {
        SendMessage sendMessage = new SendMessage()
                .setChatId(update.getMessage().getChatId())
                .setText(BotState.AUTH_STEP_1.toString());

        UserBot userBot = userBotRepository.findByChatId(update.getMessage().getChatId().toString());

        if (userBot == null) {
            userBot = new UserBot();
            userBot.setChatId(update.getMessage().getChatId().toString());
            userBot.setLastBotState(BotState.AUTH_STEP_1);
        } else if (userBot.isVerified()) {
            // Um texto simples enviado no sendMessage indica o fim de um fluxo
            sendMessage.setText("Este aparelho já está autenticado no sistema.");
            userBot.setLastBotState(null);
        }

        userBotRepository.save(userBot);
        return sendMessage;
    }

    // Checa o estado anterior do bot em relação ao chatId recebido
    private SendMessage checkState(Update update) {
        UserBot userBot = userBotRepository.findByChatId(update.getMessage().getChatId().toString());
        SendMessage sendMessage = null;

        if (userBot == null || userBot.getLastBotState() == null)
            return sendDefaultMessage(update);

        switch (Optional.ofNullable(userBot.getLastBotState()).orElse(BotState.NO_STATE)) {
            case AUTH_STEP_1:
                sendMessage = sendCode(update);
                break;
            case AUTH_STEP_2:
                sendMessage = validateCode(update);
                break;
            default:
                sendMessage = sendDefaultMessage(update);
        }

        return sendMessage;
    }

    // Grava o código no banco e envia para o e-mail do usuário
    private SendMessage sendCode(Update update) {
        User user = userRepository.findByEmail(update.getMessage().getText().toLowerCase());
        SendMessage sendMessage = new SendMessage(update.getMessage().getChatId(), "");

        if (user == null)
            sendMessage.setText("Não encontrei nenhum usuário no sistema com este e-mail :(");
        else {
            UserBot userBot = userBotRepository.findByChatId(update.getMessage().getChatId().toString());

            String verificationCode = Integer.toString(new Random().nextInt(899999) + 100000);
            String text = "Este é um e-mail automático de verificação de identidade. Informe este código para o bot do Telegram: " + verificationCode;
            codeUtils.sendEmail(new String[]{user.getEmail()}, "CCR Laudos - Código de Verificação", text);

            // Associa a conversação ao usuário, mas a validade depende da flag verified
            userBot.setUser(user);
            userBot.setBotVerificationCode(verificationCode);
            userBot.setLastBotState(BotState.AUTH_STEP_2);
            userBotRepository.save(userBot);

            sendMessage.setText(BotState.AUTH_STEP_2.toString());
        }

        return sendMessage;
    }

    // Checa se o código informado foi o mesmo passado por e-mail para o usuário a fim de autenticá-lo
    private SendMessage validateCode(Update update) {
        UserBot userBot = userBotRepository.findByChatId(update.getMessage().getChatId().toString());
        SendMessage sendMessage = new SendMessage(update.getMessage().getChatId(), "");

        if (update.getMessage().getText().equals(userBot.getBotVerificationCode())) {
            userBot.setVerified(true);
            sendMessage.setText("O aparelho foi autenticado com sucesso. Você passará a receber notificações do sistema.");
        } else {
            userBot.setUser(null);
            sendMessage.setText("Código inválido.");
        }

        userBotRepository.save(userBot);
        return sendMessage;
    }

    private SendMessage sendDefaultMessage(Update update) {
        String markdownMessage = "Não entendi \ud83e\udd14 \n"
                + "Que tal tentar um comando digitando */* ?";
        return new SendMessage(update.getMessage().getChatId(), markdownMessage).setParseMode(ParseMode.MARKDOWN);
    }

    @Override
    public String getBotUsername() {
        return this.botUsername;
    }

    @Override
    public String getBotToken() {
        return this.botToken;
    }
}

实现的流程是:

  1. 用户发送/验证。

  2. 系统对设备一无所知,因此存储聊天 ID 和最后状态。 最后的状态将是对用户的响应。系统要求用户e-mail。

  3. 用户发送他的e-mail。

  4. 该文本未被识别为命令,因此系统检查是否存在与该聊天 ID 相关的最后状态。如果存在先前状态,则使用传入文本作为此状态方法的参数。系统向用户的 e-mail 发送代码并请求它。

  5. 用户发送代码。

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

就是这样!希望对大家有帮助。