Зависает Telegram бот на обработке CallbackQuery
Создаю сообщение с InlineKeyboardButton.WithCallbackData. Далее надо обработать нажатие на кнопку и обработку. Сейчас код просто выводит выбранный результат. Сама проблема заключается в том, что при нажатии на эту кнопку бот просто зависает и не реагирует ни на что, до тех пор пока его не перезапустишь.
Самой обработки тоже не проиходит. Ничего никуда не выводиться, а просто часть с case UpdateType.CallbackQuery: просто пропускается. Ошибок никаких не выдает, просто намертво зависает для этого пользователя.
private void startBtn_Click(object sender, EventArgs e)
{
cts = new CancellationTokenSource();
botClient.StartReceiving(Update, Error, cancellationToken: cts.Token);
}
private async Task Update(ITelegramBotClient client, Update update, CancellationToken c_token)
{
var message = update.Message;
logsListBox.Items.Add($"{message.Date}, {message.Chat.Id}: {message.Text}");
switch (update.Type)
{
case UpdateType.Message:
if (message.Text == "/start")
{
_ = await botClient.SendTextMessageAsync(
chatId: message.Chat.Id,
text: "Trying *all the parameters* of `sendMessage` method",
parseMode: ParseMode.MarkdownV2,
disableNotification: false,
replyToMessageId: message.MessageId,
replyMarkup: new InlineKeyboardMarkup(new[]
{
InlineKeyboardButton.WithCallbackData("EN", "en"),
InlineKeyboardButton.WithCallbackData("DE", "de"),
}),
cancellationToken: c_token);
};
break;
case UpdateType.CallbackQuery:
var pressedButtonID = update.CallbackQuery.Data;
logsListBox.Items.Add($"{pressedButtonID}");
_ = await botClient.SendTextMessageAsync(
chatId: message.Chat.Id,
text: pressedButtonID,
parseMode: ParseMode.MarkdownV2,
cancellationToken: c_token);
break;
default:
break;
}
}
Вот пример работы. Все работало и реагировало, до тех пор пока не нажал на кнопку "EN", после этого программа даже не видит новых сообщений.
Ответы (1 шт):
С UI элементами надо работать в UI потоке.
Например так.
this.Invoke(() => logsListBox.Items.Add($"{pressedButtonID}"));
А еще лучше заведите метод
private void Log(string message)
{
Action action = () => logsListBox.Items.Add(message);
if (this.InvokeRequired)
this.Invoke(action);
else
action();
}
И теперь можно не думая о потоках писать Log($"{pressedButtonID}");
Есть кросс-платформенный метод сделать то же самое с помощью класса Progress<T>. Предыдущий будет работать только в WinForms.
Просто заведите поле
private readonly IProgress<string> log;
В конструкторе формы допишите после InitializeComponent();
log = new Progress<string>(m => logsListBox.Items.Add(m));
Этот конструктор захватит контекст синхронизации при создании экземпляра.
Теперь можно писать так: log.Report($"{pressedButtonID}"); всё так же из любого потока.
Не забудьте применить выбранный подход для всех вызовов из сторонних потоков, а не только в одном месте, с листбоксом нельзя работать из стороннего потока вообще. Именно по этой причине вы могли нарваться на рассинхрон и дедлок (зависание).
Также возможно вы не обрабатываете ошибки в Error. Покажите обработчик, дополню ответ, если с ним что-то не так.
И последнее. Я не уверен, что бот приспособлен работать в однопоточном контексте синхронизации. Рекомендую явный запуск бота на пуле потоков, это даст гарантию, что не будет странностей из-за UI контекста синхронизации.
_ = Task.Run(() => botClient.StartReceiving(Update, Error, cancellationToken: cts.Token));