当前位置: 首页 > 知识库问答 >
问题:

电报机器人--基于最后一个机器人问题处理用户的响应

鲍理
2023-03-14

我想让我的电报机器人根据机器人问的最后一个问题来处理用户输入。基本上,这就是流程:

  • 用户调用/authenticate命令
  • 机器人请求电子邮件
  • 用户发送电子邮件
  • bot会回答一条关于将代码发送到用户电子邮件以进行确认的消息,并要求用户在聊天中键入代码
  • 用户键入代码
  • 机器人验证用户代码,用户通过身份验证并开始接收通知

问题是:我如何知道用户在这个流中回答的是特定的bot问题?

将最后一个bot消息存储在某个地方,然后当消息到达时,检查最后一个bot消息是什么,并假设用户消息是响应。

有更好的办法吗?我使用Java与telegrambots库。

共有1个答案

连志义
2023-03-14

由于很难找到能够引导我找到解决方案的想法(用Java),所以为了将来的Java Googleer们,我将在这里分享我的想法。我正在使用telegrambots库和Spring boot/data。

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

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

@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;
    }
}

系统对设备一无所知,所以存储聊天id和最后状态。最后一个状态将是对用户的响应。系统询问用户的电子邮件。

用户发送他的电子邮件。

文本不能被识别为命令,因此系统检查是否存在与此聊天ID相关的最后状态。如果存在以前的状态,请使用传入的文本作为此状态方法的参数。系统发送一个代码到用户的电子邮件并要求它。

 类似资料:
  • 这不是重复的:| 我添加了一个用于管理goup的新机器人。通过此信息: 我的机器人是管理员 我的机器人隐私被禁用 “启用”-您的机器人只会接收以“/”符号开头或通过用户名提及机器人的消息。“禁用”-您的机器人将接收人们发送到组的所有消息。当前状态是:禁用成功!新状态是:禁用 bot可以读取除其他bot消息外的所有成员!但可以在回复中看到信息。 我的tg api是:https://github.co

  • 我无法理解电报机器人api的Reply_to_message方法。以下是我的代码: 这里,当用户发送/启动机器人发送hello world文本。我想让用户回复发送hello world的消息。我的意思是当用户发送/启动机器人回复信息的文本“你好,世界!” 我使用网络钩子。

  • 1、接口声明 在调用接口时必须在https请求的header中携带"token"参数。 token是智齿客服接口开放平台全局唯一的接口调用凭据。 开发者在调用各业务接口时都需使用token,开发者需要进行妥善保存。token的存储至少要保留32个字符空间。token的有效期目前为24个小时,需定时刷新,或根据接口返回的token失效提示,进行重新获取。请求token接口,无论token是否存在,都

  • 我在电报中创建了一个频道,并添加了一个机器人作为该频道的管理员。当我发送消息到频道时,机器人没有回答,为什么? 我可以用/sendmessage发送消息 https://api.telegram.org/bot[键]/发送消息?聊天室id=@MyChannelID 我正在使用:https://github.com/Eleirbag89/TelegramBotPHP 这是一个简单的bot测试代码:

  • 我通过GitHub将我的电报机器人部署到Heroku(我使用Webhook),机器人正在运行,但由于某种原因,它没有通过Webhook接收消息。 代码如下: 文件:

  • 我正在使用laravel-inentation-通道/电报,我已经为我的用户和我的机器人实现了欢迎通知,没有问题地发送该消息,我实现的第二部分是将这些用户(我已经发送了欢迎消息)添加到我的通道中。 我的机器人是我频道的管理员,可以添加用户。 < li >电报用户可以注册(完成) < li >获取电报用户数据并发送欢迎消息(完成) < li >通过bot将电报用户添加到我的频道。(需要帮助) < c