簡體   English   中英

Node Telegram bot api,在用戶之間進行鏈式對話

[英]Node Telegram bot api, make chain conversation between user

我已經開始使用這個模塊為電報編寫機器人邏輯

我可以像這樣創建簡單的問答邏輯:

bot.onText(/\/start/, function(msg, match){
  bot.sendMessage(msg.chat.id, "Hello this is great bot");
});

當用戶鍵入/start他將收到此消息。

我想在機器人和用戶之間創建類似鏈式對話的東西。 就像當用戶輸入/buy bot 時會顯示購買選項,在用戶輸入他想要購買的東西之后,bot 將顯示所選產品的類型等等。

如何在用戶和機器人之間創建鏈式對話? 如何讓機器人記住以前選擇的命令並在需要時重置它們? 我是否需要保留在我自己的數據庫中才能做到這一點?

你可以用不同的方式做到這一點。

  1. 您可以存儲用戶自己的“狀態”
  2. 你可以使用多個獨立運行的命令,但你只是給用戶一種被引導的感覺
  3. 您可以使用 Bot API 的ForceReply

好吧。 所以對於1.我會說你有一些好處。 當用戶處於不正確的狀態時,您實際上可以引導用戶並限制對某些命令的訪問。 因此,假設他想購買爆米花,但他在鞋店,您可以通過檢查保存的用戶狀態來禁止該命令。

對於2.你總是允許用戶使用/buy/buy_popcorn/buy_shoe 但是根據您的回答,您只需給他特定數量的可能選擇。

User: /buy

What do you want to buy? /shoes or /food :Bot

User: /food

How about some Popcorn? Use /buy_popcorn :Bot

User: /buy_shoe

Alright. Shoes added to cart :Bot

這是允許的,但用戶必須手動編寫/buy_shoe

3.可能的方法是使用ForceReply 用戶將自動獲得消息的answer to 因此,當他使用/buy_shoe他將回復機器人發送的最后一條消息。 您還將在來自 api 的消息中獲得用戶回答的消息。 您可以檢查用戶回答的消息是否是該命令的正確前提/正確消息,然后限制或允許該命令。

User: /buy

What do you want to buy? /shoes or /food :Bot

User: [Answer to: What do you...] /food

How about some Popcorn? Use /buy_popcorn :Bot

User: [Answer to: How about some...] /buy_shoe

Sorry, but you're currently in the Food Store :Bot

我想這取決於個人喜好。 但所有這些都有利有弊,您必須決定是否要在沒有先決條件的情況下允許特定命令。

此列表可能不完整。 可能還有其他方法,我沒想過。 但這三種方式是我所知道的。

我也遇到了這個問題,我需要我的機器人根據他對用戶的最后回答來回答問題,而且由於很難找到可以引導我找到解決方案的想法(在Java 中),我將在這里分享我的想法為了未來的 Java 谷歌員工。 我正在將電報機器人庫與 Spring Boot/Data 一起使用。

正如 Loki 所指出的,實現此流程的最佳方法是在數據庫中保存狀態。 為此,請使用消息唯一的聊天 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 和最后狀態。 最后一個狀態將是對用戶的響應。 系統要求提供用戶的電子郵件。

3 - 用戶發送他的電子郵件。

4 - 該文本未被識別為命令,因此系統會檢查是否存在與此聊天 ID 相關的最后狀態。 如果存在先前的狀態,則使用傳入文本作為此狀態方法的參數。 系統向用戶的電子郵件發送一個代碼並要求它。

5 - 用戶發送代碼。

6 - 系統再次檢查之前的狀態並驗證用戶(如果代碼正確)。

就是這樣! 希望它可以幫助某人。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM