using OpenAI; using OpenAI.Chat; using System.ClientModel; using Telegram.Bot; using Telegram.Bot.Types; using Telegram.Bot.Types.Enums; using File = System.IO.File; string GetEnv(string name) => Environment.GetEnvironmentVariable(name) ?? throw new Exception($"Environment variable {name} is not set"); string baseUrl = GetEnv("OPENAI_BASE_URL"); string model = GetEnv("OPENAI_MODEL"); string apiKey = GetEnv("OPENAI_API_KEY"); Console.WriteLine("Starting the bot..."); Console.WriteLine( $""" Base URL: {baseUrl} Model: {model} API Key: {apiKey} """); string nemesisPrompt = File.ReadAllText($"prompt/{GetEnv("NEMESIS_PROMPT_FILE")}"); Dictionary> oaiChats = new(); var options = new OpenAIClientOptions() { Endpoint = new(baseUrl), NetworkTimeout = new TimeSpan(0, 0, 30) }; var openAiApiKey = new ApiKeyCredential(apiKey); var openAiClient = new OpenAIClient(openAiApiKey, options); var chatClient = openAiClient.GetChatClient(model); string token = GetEnv("TELEGRAM_BOT_TOKEN") ?? string.Empty; Console.WriteLine("OpenAI Chat Client created"); using var cts = new CancellationTokenSource(); var bot = new TelegramBotClient(token, cancellationToken:cts.Token); await bot.DropPendingUpdates(); var me = bot.GetMe(); bot.OnMessage += OnMessage; Console.WriteLine("Bot running"); Thread.Sleep(Timeout.Infinite); cts.Cancel(); // stop the bot async Task OnMessage(Message msg, UpdateType type) { //Discard any message that is not a text message if (msg.Type != MessageType.Text) return; //Check if the message contains the bot's username or a reply to a message sent by the bot or a private chat if (msg.Text!.Contains(me.Result.FirstName!, StringComparison.OrdinalIgnoreCase) || msg.ReplyToMessage != null && msg.ReplyToMessage.From!.Id == me.Result.Id || msg.Chat.Type == ChatType.Private) { Console.WriteLine( $""" Received message from {msg.Chat.Id} Type: {type} Message: {msg.Text} """); var chatid = msg.Chat.Id; //Check if the message is a reset command if (msg.Text.StartsWith("/reset")) { ResetChat(chatid); await bot.SendMessage(chatid, "Chat context has been reset"); return; } // Otherwise process it normally await AnswerChat(chatid, msg.Text); } } async Task AnswerChat(long chatId, string input) { //Check if the chat is already in the dictionary if (!oaiChats.ContainsKey(chatId)) AddChatToDictionary(chatId); string text = input; //Limit the message to 1024 characters to avoid out of context jump if (input.Length > 1024) text = input.Substring(0, 1024); //Add the current message to the chat //oaiChats[chatId].Add(new UserChatMessage(text)); ChatMessageRotate(chatId, new UserChatMessage(text)); //fetch existing messages history var messages = oaiChats[chatId]; //Fetch the response from the model var result = chatClient.CompleteChat(messages).Value.Content[0].Text; //Add the response to the chat Console.WriteLine("Replying with: " + result); oaiChats[chatId].Add(new AssistantChatMessage(result)); //Send the response to the user await bot.SendMessage(chatId, result); } void AddChatToDictionary(long id) { //Create a new chat object var chat = new List(); chat.Add(new SystemChatMessage(nemesisPrompt)); //add the entry to the dictionary oaiChats.Add(id, chat); } void ChatMessageRotate(long chatId, ChatMessage message){ //Remove the first message from the chat if the chat has more than 5 couples of messages (0 is our prompt) if (oaiChats[chatId].Count > 10) { oaiChats[chatId].RemoveAt(1); oaiChats[chatId].RemoveAt(2); } //Add the new message to the chat oaiChats[chatId].Add(message); } void ResetChat(long chatId) { //Remove the chat from the dictionary oaiChats.Remove(chatId); //Add the chat back to the dictionary AddChatToDictionary(chatId); }