2023-01-08 23:32:46 +01:00
|
|
|
|
using System.Reflection;
|
|
|
|
|
using Telegram.Bot;
|
2023-01-08 22:57:00 +01:00
|
|
|
|
using Telegram.Bot.Types;
|
|
|
|
|
using Telegram.Bot.Types.Enums;
|
|
|
|
|
|
|
|
|
|
var token = Environment.GetEnvironmentVariable("TELEGRAM_BOT_TOKEN")!;
|
2023-01-08 23:32:46 +01:00
|
|
|
|
var version = ((AssemblyInformationalVersionAttribute)Assembly.GetEntryAssembly()!.GetCustomAttributes(typeof(AssemblyInformationalVersionAttribute), false)[0])
|
|
|
|
|
.InformationalVersion[6..];
|
2023-01-08 22:57:00 +01:00
|
|
|
|
|
|
|
|
|
using var cts = new CancellationTokenSource();
|
|
|
|
|
|
2023-01-08 23:02:18 +01:00
|
|
|
|
var bot = new TelegramBotClient(token);
|
|
|
|
|
var me = await bot.GetMeAsync();
|
|
|
|
|
|
2023-01-08 22:57:00 +01:00
|
|
|
|
await bot.SetMyCommandsAsync(new[] {
|
|
|
|
|
new BotCommand { Command = "coinflip", Description = "Flips a coin" },
|
|
|
|
|
new BotCommand { Command = "shouldi", Description = "Tells you if you should or shouldn't do something" },
|
|
|
|
|
new BotCommand { Command = "yesno", Description = "Answes binary questions" },
|
|
|
|
|
new BotCommand { Command = "makedecision", Description = "Makes a decision. Expects a list of comma separated options." },
|
|
|
|
|
new BotCommand { Command = "choose", Description = "Chooses an item. Expects a list of comma separated options." },
|
2023-01-27 23:23:03 +01:00
|
|
|
|
new BotCommand { Command = "spinbottle", Description = "Spins a bottle (for truth or dare). Expects a list of users participating separated by a space." },
|
2023-01-08 23:02:18 +01:00
|
|
|
|
new BotCommand { Command = "help", Description = "Lists all commands" },
|
2023-01-08 22:57:00 +01:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
var iBotCommands = new List<string> {
|
|
|
|
|
"coinflip",
|
|
|
|
|
"shouldi",
|
|
|
|
|
"yesno",
|
|
|
|
|
"makedecision",
|
|
|
|
|
"choose",
|
2023-01-27 23:23:03 +01:00
|
|
|
|
"spinbottle",
|
2023-01-08 22:57:00 +01:00
|
|
|
|
"help",
|
|
|
|
|
"start"
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
bot.StartReceiving(HandleUpdateAsync, PollingErrorHandler, null, cts.Token);
|
|
|
|
|
Console.WriteLine($"Start listening for @{me.Username}");
|
2023-01-20 18:59:26 +01:00
|
|
|
|
|
|
|
|
|
var exitEvent = new ManualResetEvent(false);
|
|
|
|
|
Console.CancelKeyPress += (sender, eventArgs) => {
|
|
|
|
|
eventArgs.Cancel = true;
|
|
|
|
|
exitEvent.Set();
|
|
|
|
|
};
|
|
|
|
|
exitEvent.WaitOne();
|
2023-01-08 23:44:10 +01:00
|
|
|
|
|
2023-01-08 22:57:00 +01:00
|
|
|
|
cts.Cancel();
|
|
|
|
|
|
|
|
|
|
Task PollingErrorHandler(ITelegramBotClient ibot, Exception ex, CancellationToken ct) {
|
|
|
|
|
Console.WriteLine($"Exception while polling for updates: {ex}");
|
|
|
|
|
return Task.CompletedTask;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async Task HandleUpdateAsync(ITelegramBotClient ibot, Update update, CancellationToken ct) {
|
|
|
|
|
try {
|
2023-01-08 23:02:18 +01:00
|
|
|
|
if (update.Type != UpdateType.Message)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
await BotOnMessageReceived(ibot, update.Message!);
|
2023-01-08 22:57:00 +01:00
|
|
|
|
}
|
|
|
|
|
catch (Exception ex) {
|
|
|
|
|
Console.WriteLine($"Exception while handling {update.Type}: {ex}");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async Task BotOnMessageReceived(ITelegramBotClient botClient, Message message) {
|
|
|
|
|
var command = "";
|
|
|
|
|
var query = "";
|
|
|
|
|
try {
|
|
|
|
|
// if we didn't receive any text, return
|
|
|
|
|
if (string.IsNullOrWhiteSpace(message.Text))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// we need to figure out which way of sending commands is being used
|
|
|
|
|
// let's get the first word of the message
|
|
|
|
|
var firstword = message.Text!.Split(" ")[0];
|
|
|
|
|
|
|
|
|
|
// is it a slash command?
|
|
|
|
|
if (firstword.StartsWith("/")) {
|
|
|
|
|
// trim explicit username mention
|
|
|
|
|
command = firstword.Replace($"@{me.Username}", "").TrimStart('/');
|
|
|
|
|
// remove command (and optional username) from message text
|
|
|
|
|
query = message.Text?[firstword.Length..];
|
2023-01-09 15:08:10 +01:00
|
|
|
|
// ignore unknown slash commands in groups not explicitly directed at us
|
|
|
|
|
if (!iBotCommands.Contains(command) && message.Chat.Type != ChatType.Private && !firstword.Contains($"@{me.Username}"))
|
|
|
|
|
return;
|
2023-01-08 22:57:00 +01:00
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
// are we sure the bot is being asked?
|
|
|
|
|
if (message.Chat.Type == ChatType.Private || message.Text!.Contains($"@{me.Username}")) {
|
|
|
|
|
var iquery = message.Text.Replace($"@{me.Username}", "").Trim();
|
|
|
|
|
if (string.IsNullOrWhiteSpace(iquery))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// is the first word a command?
|
|
|
|
|
firstword = iquery.Split(" ")[0];
|
|
|
|
|
if (iBotCommands.Contains(firstword)) {
|
|
|
|
|
command = firstword;
|
|
|
|
|
query = iquery[firstword.Length..];
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
// try to guess what was meant
|
|
|
|
|
if (iquery.ToLowerInvariant().Contains(" or ") && !iquery.Contains(',')) {
|
|
|
|
|
command = "makedecision";
|
|
|
|
|
query = iquery.Replace(" or ", ",").TrimEnd('?');
|
|
|
|
|
}
|
|
|
|
|
else if (iquery.EndsWith("?")) {
|
|
|
|
|
command = "yesno";
|
|
|
|
|
}
|
|
|
|
|
else if (iquery.ToLowerInvariant().Contains("flip") && iquery.ToLowerInvariant().Contains("coin")) {
|
|
|
|
|
command = "coinflip";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-01-09 15:14:18 +01:00
|
|
|
|
else {
|
|
|
|
|
// likely not a message meant for us
|
|
|
|
|
return;
|
|
|
|
|
}
|
2023-01-08 22:57:00 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (command) {
|
|
|
|
|
case "coinflip":
|
|
|
|
|
await botClient.SendTextMessageAsync(message.Chat.Id, RandomOption(new[] { "Heads", "Tails" }), replyToMessageId: message.MessageId);
|
|
|
|
|
break;
|
|
|
|
|
case "shouldi":
|
|
|
|
|
await botClient.SendTextMessageAsync(message.Chat.Id, RandomOption(new[] { "Go for it!", "Can't hurt to try!", "Probably not.", "I wouldn't recommend it." }),
|
|
|
|
|
replyToMessageId: message.MessageId);
|
|
|
|
|
break;
|
|
|
|
|
case "yesno":
|
|
|
|
|
await botClient.SendTextMessageAsync(message.Chat.Id, RandomOption(new[] { "Yeah!", "Nah..." }), replyToMessageId: message.MessageId);
|
|
|
|
|
break;
|
|
|
|
|
case "makedecision":
|
|
|
|
|
case "choose":
|
|
|
|
|
var options = query?.Split(",").Where(p => !string.IsNullOrWhiteSpace(p)).ToList();
|
|
|
|
|
if (options == null || !options.Any())
|
|
|
|
|
options = new List<string> { "Syntax error. (Can't select from zero options)" };
|
|
|
|
|
await botClient.SendTextMessageAsync(message.Chat.Id, RandomOption(options).Trim(), replyToMessageId: message.MessageId);
|
|
|
|
|
break;
|
2023-01-27 23:23:03 +01:00
|
|
|
|
case "spinbottle":
|
|
|
|
|
var people = query?.Split(" ").Where(p => !string.IsNullOrWhiteSpace(p)).ToList();
|
2023-01-27 23:27:14 +01:00
|
|
|
|
if (people == null || !people.Any()) {
|
|
|
|
|
await botClient.SendTextMessageAsync(message.Chat.Id, "Syntax error. (Can't select from zero options)", replyToMessageId: message.MessageId);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-27 23:33:10 +01:00
|
|
|
|
await botClient.SendTextMessageAsync(message.Chat.Id, RandomOption(people).Trim().TrimEnd(',') + ", truth or dare?", replyToMessageId: message.MessageId);
|
2023-01-27 23:23:03 +01:00
|
|
|
|
break;
|
2023-01-08 22:57:00 +01:00
|
|
|
|
case "help":
|
2023-01-08 23:02:18 +01:00
|
|
|
|
case "start":
|
2023-01-08 23:32:46 +01:00
|
|
|
|
await botClient.SendTextMessageAsync(message.Chat.Id, $"""
|
2023-01-08 22:57:00 +01:00
|
|
|
|
Heya, I am a bot that can help you make decisions! Here's a list of things I can do:
|
|
|
|
|
|
|
|
|
|
/coinflip - flips a coin
|
|
|
|
|
/shouldi - when you are unsure if you should do something
|
|
|
|
|
/yesno - answers more generic yes/no questions
|
|
|
|
|
/makedecision - helps you make a decision (chooses from a comma separated list of options; aliased to /choose)
|
|
|
|
|
|
2023-01-08 23:49:09 +01:00
|
|
|
|
Feel free to add me to your group chats!
|
|
|
|
|
Be aware that in those contexts, I can't read (and therefore not respond to) any messages unless they use the syntax `/<command>@{me.Username}` except when I'm the only bot in the chat, in which case `/<command>` is enough.
|
2023-01-08 22:57:00 +01:00
|
|
|
|
|
2023-01-08 23:49:09 +01:00
|
|
|
|
In direct messages, commands also work without the / at the start.
|
2023-01-08 22:57:00 +01:00
|
|
|
|
|
2023-01-08 23:49:09 +01:00
|
|
|
|
If your message doesn't start with a recognized command, I'll do my best to figure out what you are asking.
|
2023-01-08 23:36:38 +01:00
|
|
|
|
|
2023-01-08 22:57:00 +01:00
|
|
|
|
This currently works with the following queries:
|
|
|
|
|
- `thing1 or thing2 or thing3` gets translated into `/makedecision thing1,thing2,thing3`
|
|
|
|
|
- `flip a coin` gets translated into `/coinflip`
|
|
|
|
|
- `this is a question?` gets translated to `/yesno this is a question`
|
|
|
|
|
|
2023-01-08 23:49:09 +01:00
|
|
|
|
If there is an error, please forward the stacktrace message to @zotan (my creator). You can also DM them for feature requests.
|
2023-01-08 22:57:00 +01:00
|
|
|
|
|
|
|
|
|
Enjoy!
|
|
|
|
|
|
2023-01-08 23:32:46 +01:00
|
|
|
|
I'm powered by [~zotan](https://zotan.pw)'s [Telegram.Bot.DecisionMaker](https://git.ztn.sh/zotan/Telegram.Bot.DecisionMaker) rev. [{version}](https://git.ztn.sh/zotan/Telegram.Bot.DecisionMaker/commit/{version.Replace("-dirty", "")})
|
2023-01-08 22:57:00 +01:00
|
|
|
|
""", replyToMessageId: message.MessageId, parseMode: ParseMode.Markdown, disableWebPagePreview: true);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
await botClient.SendTextMessageAsync(message.Chat.Id, $"""
|
|
|
|
|
Apologies, I can't figure out what you want me to do >w<
|
2023-01-08 23:27:01 +01:00
|
|
|
|
|
2023-01-08 22:57:00 +01:00
|
|
|
|
Message received: `{message.Text}`
|
|
|
|
|
Parsed command: `{command}`
|
|
|
|
|
Parsed query: `{query}`
|
2023-01-08 23:27:01 +01:00
|
|
|
|
|
|
|
|
|
Try /help for examples on how to help me understand your query!
|
2023-01-08 22:57:00 +01:00
|
|
|
|
""", replyToMessageId: message.MessageId, parseMode: ParseMode.Markdown);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (Exception e) {
|
|
|
|
|
await botClient.SendTextMessageAsync(message.Chat.Id, $"""
|
|
|
|
|
Error processing message.
|
2023-01-08 23:27:01 +01:00
|
|
|
|
|
2023-01-08 22:57:00 +01:00
|
|
|
|
Message content: `{message.Text}`
|
|
|
|
|
Parsed command: `{command}`
|
|
|
|
|
Parsed query: `{query}`
|
|
|
|
|
|
|
|
|
|
Exception: `{e.Message.Replace("`", "'")}`
|
|
|
|
|
Stacktrace: `{e.StackTrace?.Replace("`", "'")}`
|
|
|
|
|
""", replyToMessageId: message.MessageId, parseMode: ParseMode.Markdown);
|
|
|
|
|
throw;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static string RandomOption(IReadOnlyList<string> options) {
|
|
|
|
|
var random = new Random(Guid.NewGuid().GetHashCode());
|
|
|
|
|
var index = random.Next(options.Count);
|
|
|
|
|
return options[index];
|
|
|
|
|
}
|