[英]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 將顯示所選產品的類型等等。
如何在用戶和機器人之間創建鏈式對話? 如何讓機器人記住以前選擇的命令並在需要時重置它們? 我是否需要保留在我自己的數據庫中才能做到這一點?
你可以用不同的方式做到這一點。
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.