147 lines
4.6 KiB
C#
147 lines
4.6 KiB
C#
using OpenAI.Chat;
|
|
using Telegram.Bot;
|
|
using Telegram.Bot.Types;
|
|
using Telegram.Bot.Types.Enums;
|
|
using TelegramBot;
|
|
using File = System.IO.File;
|
|
|
|
string baseUrl = Env.Get("OPENAI_BASE_URL");
|
|
string apiKey = Env.Get("OPENAI_API_KEY");
|
|
string model = Env.Get("OPENAI_MODEL");
|
|
var oaiAgent = new OpenAiAgent(baseUrl, apiKey, model);
|
|
oaiAgent.ContextSize = Int32.Parse(Env.Get("CONTEXT_SIZE"));
|
|
|
|
Console.WriteLine("Starting the bot...");
|
|
|
|
string nemesisPrompt = File.ReadAllText($"prompt/{Env.Get("NEMESIS_PROMPT_FILE")}");
|
|
string krolikPrompt = File.ReadAllText($"prompt/{Env.Get("KROLIK_PROMPT_FILE")}");
|
|
|
|
//Ratelimit
|
|
TimeSpan rateLimit = new(0, 0, 0, 10);
|
|
Dictionary<long, DateTime> lastMessage = new();
|
|
HashSet<long> unlimitedChats = new();
|
|
|
|
bool IsRateLimited(long chatId) {
|
|
if (lastMessage.ContainsKey(chatId) && DateTime.Now - lastMessage[chatId] < rateLimit) return true;
|
|
|
|
lastMessage[chatId] = DateTime.Now;
|
|
return false;
|
|
}
|
|
|
|
#region TelegramBot Startup
|
|
|
|
string nemesisToken = Env.Get("NEMESIS_BOT_TOKEN");
|
|
string krolikToken = Env.Get("KROLIK_BOT_TOKEN");
|
|
|
|
using var nemcts = new CancellationTokenSource();
|
|
using var krocts = new CancellationTokenSource();
|
|
|
|
var nemesisBot = new TelegramBotClient(nemesisToken, cancellationToken:nemcts.Token);
|
|
var krolikBot = new TelegramBotClient(krolikToken, cancellationToken:krocts.Token);
|
|
|
|
var nemProfile = nemesisBot.GetMe();
|
|
Agent Nemesis = new(
|
|
Actor.Nemesis,
|
|
nemProfile.Result.Id,
|
|
nemProfile.Result.FirstName,
|
|
nemProfile.Result.Username!,
|
|
nemesisBot,
|
|
nemesisPrompt,
|
|
oaiAgent.GetTokenLenght(nemesisPrompt)
|
|
);
|
|
|
|
var kroProfile = krolikBot.GetMe();
|
|
Agent Krolik = new(
|
|
Actor.Krolik,
|
|
kroProfile.Result.Id,
|
|
kroProfile.Result.FirstName,
|
|
kroProfile.Result.Username!,
|
|
krolikBot,
|
|
krolikPrompt,
|
|
oaiAgent.GetTokenLenght(krolikPrompt)
|
|
);
|
|
|
|
nemesisBot.OnMessage += OnNemMessage;
|
|
krolikBot.OnMessage += OnKroMessage;
|
|
|
|
await nemesisBot.DropPendingUpdates();
|
|
Console.WriteLine("Nemesis Bot running");
|
|
await krolikBot.DropPendingUpdates();
|
|
Console.WriteLine("Krolik Bot running");
|
|
|
|
Thread.Sleep(Timeout.Infinite);
|
|
nemcts.Cancel(); // stop nembot
|
|
krocts.Cancel(); // stop krobot
|
|
|
|
#endregion
|
|
|
|
async Task OnNemMessage(Message msg, UpdateType type) {
|
|
//Discard any message that is not a text message
|
|
if (msg.Type != MessageType.Text) return;
|
|
|
|
await OnMessage(msg, Nemesis);
|
|
}
|
|
|
|
async Task OnKroMessage(Message msg, UpdateType type) {
|
|
//Discard any message that is not a text message
|
|
if (msg.Type != MessageType.Text) return;
|
|
|
|
await OnMessage(msg, Krolik);
|
|
}
|
|
|
|
//TODO: currently we only take in account private messages and messages directed to the bot/mentioning them.
|
|
// We should also take in account the last x messages in groups to add more context
|
|
async Task OnMessage(Message msg, Agent agent) {
|
|
var chatid = msg.Chat.Id;
|
|
|
|
//Check if the message is a reset command
|
|
if (msg.Text == "/reset" || msg.Text == "/reset@" + agent.Username) {
|
|
oaiAgent.ResetChat(chatid);
|
|
await agent.Bot.SendMessage(chatid, "Chat context has been reset");
|
|
return;
|
|
}
|
|
|
|
if (msg.Text == "/history @"+agent.Username) {
|
|
|
|
var history = oaiAgent.GetChatHistory(chatid, agent.Actor);
|
|
var historyText = string.Join("\n", history.Select(x => x.Item1.Content[0].Text));
|
|
await agent.Bot.SendMessage(chatid, historyText);
|
|
return;
|
|
}
|
|
|
|
var text = $"{msg.From?.FirstName} {msg.From?.LastName}: {msg.Text}";
|
|
var tokenlenght = oaiAgent.GetTokenLenght(msg.Text!);
|
|
Console.WriteLine(
|
|
$"""
|
|
{agent.Name} has received message from {chatid} TokenLenght: {tokenlenght}
|
|
Message: {msg.Text}
|
|
""");
|
|
//Add the message to the chat history
|
|
oaiAgent.ChatHistoryAppend(Actor.User, chatid, text);
|
|
|
|
|
|
//Check if the message contains the bot's username or a reply to a message sent by the bot or a private chat
|
|
// Otherwise process it normally
|
|
if (msg.Text!.Contains(agent.Name, StringComparison.OrdinalIgnoreCase) ||
|
|
msg.ReplyToMessage?.From?.Id == agent.TelegramId ||
|
|
msg.Chat.Type == ChatType.Private) {
|
|
//Check if the chat (group) is rate limited
|
|
/*if (IsRateLimited(chatid)) {
|
|
Console.WriteLine("No response due to ratelimit.");
|
|
return;
|
|
}
|
|
*/
|
|
await AnswerChat(chatid, agent);
|
|
}
|
|
}
|
|
|
|
async Task AnswerChat(long chatId, Agent agent) {
|
|
//Get the response from the OpenAI API
|
|
var result = oaiAgent.GetChatResponse(chatId, agent);
|
|
Console.WriteLine(
|
|
$"""
|
|
{agent.Name} has responded with: {result}
|
|
""");
|
|
//Send the response to the user
|
|
await agent.Bot.SendMessage(chatId, result);
|
|
} |