diff --git a/.idea/OpenChatAiBot.iml b/.idea/OpenChatAiBot.iml index 74d515a..e0fc69a 100644 --- a/.idea/OpenChatAiBot.iml +++ b/.idea/OpenChatAiBot.iml @@ -4,7 +4,7 @@ - + \ No newline at end of file diff --git a/.idea/dataSources.xml b/.idea/dataSources.xml index 14ea0b9..349fd04 100644 --- a/.idea/dataSources.xml +++ b/.idea/dataSources.xml @@ -1,18 +1,11 @@ - + sqlite.xerial true - Содержит в себе список(tg_id) -пользователей бота с количеством -истраченных символов, репутацией -и лимитом символов для запросов. org.sqlite.JDBC - jdbc:sqlite:$PROJECT_DIR$/DataBase/UserList - - - + jdbc:sqlite:$PROJECT_DIR$/DataBase/OCAB_DB.db $ProjectFileDir$ diff --git a/src/OpenAI/GPT35turbo/OA_processing.py b/src/OpenAI/GPT35turbo/OA_processing.py new file mode 100644 index 0000000..e9b3250 --- /dev/null +++ b/src/OpenAI/GPT35turbo/OA_processing.py @@ -0,0 +1,134 @@ +import sqlite3 +import os +import configparser + +mother_path = os.path.dirname(os.path.dirname(os.getcwd())) + +config = configparser.ConfigParser() +config.read(os.path.join(mother_path, 'src/config.ini')) + +database = sqlite3.connect(os.path.join(mother_path, 'DataBase/OCAB_DB.db')) +cursor = database.cursor() + +# Импорт библиотек + +import openai +max_token_count = int(config['Openai']['max_token_count']) + +base_message_formated_text = [ + { + "role": "system", + "content": config['Openai']['story_model'] + } +] + + +def openai_response(message_formated_text): + # Запуск OpenAI + # Считаем размер полученного текста + print(message_formated_text) + count_length = 0 + for message in message_formated_text: + print(message["content"]) + count_length += len(message["content"]) + print(count_length) + response = openai.ChatCompletion.create( + model="gpt-3.5-turbo", + messages=message_formated_text, + max_tokens=max_token_count - count_length + ) + return response + +def sort_message_from_user(message_formated_text, message_id): + print(int(*( + cursor.execute("SELECT message_sender FROM message_list WHERE message_id = ?", (message_id,)).fetchone()))) + if int(*( + cursor.execute("SELECT message_sender FROM message_list WHERE message_id = ?", + (message_id,)).fetchone())) == 0: + message_formated_text.append({ + "role": "assistant", + "content": str(*(cursor.execute("SELECT message_text FROM message_list WHERE message_id = ?", + (message_id,)).fetchone())) + }) + else: + message_formated_text.append({ + "role": "user", + "content": str(*(cursor.execute("SELECT message_text FROM message_list WHERE message_id = ?", + (message_id,)).fetchone())) + }) + #Проверка что длина всех сообщений в кортеже не превышает max_token_count-500 + count_length = 0 + for message in message_formated_text: + count_length += len(message['content']) + if count_length > max_token_count-800: + message_formated_text.pop(1) + return message_formated_text + +def openai_collecting_message(message_id, message_formated_text): + # собирает цепочку сообщений для OpenAI длинной до max_token_count + # проверяем что сообщение отвечает на другое сообщение + if int(*(cursor.execute("SELECT answer_id FROM message_list WHERE message_id = ?", (message_id,)).fetchone())) not in (0, 643885, 476959, 1, 476977, 633077, 630664, 476966, 634567): + # Продолжаем искать ответы на сообщения + print(int(*(cursor.execute("SELECT answer_id FROM message_list WHERE message_id = ?", (message_id,)).fetchone()))) + message_formated_text = openai_collecting_message(int(*(cursor.execute("SELECT answer_id FROM message_list WHERE message_id = ?", (message_id,)).fetchone())), message_formated_text) + #Проверяем ID отправителя сообщения, если 0 то это сообщение от бота + sort_message_from_user(message_formated_text, message_id) + else: + # Проверяем ID отправителя сообщения, если 0 то это сообщение от бота + sort_message_from_user(message_formated_text, message_id) + return message_formated_text + + + + + + + + + + + + + + # if (((cursor.execute("SELECT answer_id FROM message_list WHERE message_id") is None)) or (cursor.execute("SELECT answer_id FROM message_list WHERE message_id") == 643885)): + # openai_collecting_message(cursor.execute("SELECT answer_id FROM message_list WHERE message_id")) + # # проверяем что с новым сообщение длина всех сообщений в цепочке не будет превышать max_token_count + # count_length = 0 + # for message in message_formated_text: + # count_length += len(message['content']) + # if count_length + len(cursor.execute("SELECT message_text FROM message_list WHERE message_id = ?", (message_id,)).fetchone()) > max_token_count: + # message_formated_text.pop(1) + # print(int(*(cursor.execute("SELECT message_sender FROM message_list WHERE message_id = ?", (message_id,)).fetchone()))) + # if int(*(cursor.execute("SELECT message_sender FROM message_list WHERE message_id = ?", (message_id,)).fetchone())) == 0: + # message_formated_text.append({ + # "role": "assistant", + # "content": str(*(cursor.execute("SELECT message_text FROM message_list WHERE message_id = ?", (message_id,)).fetchone())) + # }) + # else: + # message_formated_text.append({ + # "role": "user", + # "content": str(*(cursor.execute("SELECT message_text FROM message_list WHERE message_id = ?", (message_id,)).fetchone())) + # }) + + + + +def openai_message_processing(message_id): + #проверяем на наличие сообщения в базе данных + if cursor.execute("SELECT message_text FROM message_list WHERE message_id = ?", (message_id,)).fetchone() is None: + return None + else: + # проверяем на то что сообщение влезает в max_token_count с учётом message_formated_text + #print((len(str(cursor.execute("SELECT message_text FROM message_list WHERE message_id"))))) + #print(len(message_formated_text[0]['content'])) + #print(max_token_count) + #print(max_token_count - len(message_formated_text[0]['content'])) + message_formated_text = base_message_formated_text + if ((len(str(cursor.execute("SELECT message_text FROM message_list WHERE message_id")))) < (max_token_count - len(message_formated_text[0]['content']))): + message_formated_text = openai_collecting_message(message_id, message_formated_text) + response = openai_response(message_formated_text) + return response + else: + return f"Сообщение слишком длинное, максимальная длина сообщения \ + {max_token_count - len(message_formated_text[0]['content'])} символов, укоротите его на \ + {len(str(cursor.execute('SELECT message_text FROM message_list WHERE message_id'))) - max_token_count} символов" \ No newline at end of file diff --git a/src/TelegramBot/MessageHandler.py b/src/TelegramBot/MessageHandler.py index 5311712..e6b8759 100644 --- a/src/TelegramBot/MessageHandler.py +++ b/src/TelegramBot/MessageHandler.py @@ -3,12 +3,6 @@ from aiogram import types from src.TelegramBot.main import dp +from main import cursor, config -@dp.message_handler() -async def send(message: types.Message): - # Получение сообщений в чате, и запись их в базу данных - # Проверка на то, что сообщение не пустое и не отправлено в чате содержащим ChatType = 1 в базе данных chatlist - if (message.chat.type == "group" or message.chat.type == "supergroup") and \ - message.text != '' and message.text != ' ': return None - else: - # Проверка статуса ChatType в базе данных chatlist +#импортировать функцию для обработки сообщений из OpenAI diff --git a/src/TelegramBot/main.py b/src/TelegramBot/main.py index 9c981b0..c304449 100644 --- a/src/TelegramBot/main.py +++ b/src/TelegramBot/main.py @@ -1,23 +1,29 @@ -# Файл с инициализацией всех процессов телеграмм бота и запуском API OpenAI - # Импорт библиотек import os +from contextlib import suppress + import openai import configparser import sqlite3 +import asyncio from aiogram import Bot, Dispatcher, executor, types from aiogram.contrib.fsm_storage.memory import MemoryStorage +from aiogram.utils.exceptions import MessageCantBeDeleted, MessageToDeleteNotFound -from MessageHandler import * +mother_path = os.path.dirname(os.path.dirname(os.getcwd())) # Импорт переменных из файла .ini config = configparser.ConfigParser() -config.read(os.path.join(os.path.dirname(__file__), 'config.ini')) - +config.read(os.path.join(mother_path, 'src/config.ini')) TOKEN = config['Telegram']['token'] -OPENAI_API_KEY = config['OpenAI']['api_key'] +OPENAI_API_KEY = (config['Openai']['api_key']) +bot_trigger_front = (config['Telegram']['bot_trigger_front']).split('|') +bot_trigger_all = (config['Telegram']['bot_trigger_all']).split('|') +# удаление лишних элементов массивов +bot_trigger_front.remove('') +bot_trigger_all.remove('') # Инициализация бота @@ -29,8 +35,100 @@ dp = Dispatcher(bot, storage=MemoryStorage()) openai.api_key = OPENAI_API_KEY # Инициализация базы данных OCAB_DB в папке DataBase/OCAB_DB.db -database = sqlite3.connect(os.path.join(os.path.dirname(__file__), 'DataBase/OCAB_DB.db')) +# Создаём базу данных sqlite3 по пути /home/armatik/PycharmProjects/OpenChatAiBot/DataBase/OCAB_DB.db +database = sqlite3.connect(os.path.join(mother_path, 'DataBase/OCAB_DB.db')) cursor = database.cursor() +# Создаём таблицу chat_list +cursor.execute("""CREATE TABLE IF NOT EXISTS chat_list ( + chat_id INTEGER PRIMARY KEY, + chat_role INTEGER NOT NULL, + chat_stats INTEGER NOT NULL +)""") +# Создаём таблицу message_list +cursor.execute("""CREATE TABLE IF NOT EXISTS message_list ( + message_id INTEGER PRIMARY KEY, + message_text TEXT NOT NULL, + message_sender INTEGER NOT NULL, + answer_id INTEGER +)""") +# Создаём таблицу user_list +cursor.execute("""CREATE TABLE IF NOT EXISTS user_list ( + user_id INTEGER PRIMARY KEY, + user_role INTEGER, + user_stats INTEGER +)""") + +#запись информации о чате в базу данных +@dp.message_handler(commands=['start']) +async def start(message: types.Message): + chat_id = message.chat.id + chat_role = 0 + chat_stats = 1 + cursor.execute("INSERT INTO chat_list VALUES (?, ?, ?)", (chat_id, chat_role, chat_stats)) + database.commit() + await message.reply("Привет, я бот, который учится на основе модели GPT-3.5. " + "Я могу поговорить с тобой на любую тему, но пока что я не очень умный, поэтому не обижайся, " + "если я не пойму тебя. Чтобы начать общение со мной, напиши мне сообщение, начинающееся с " + "слова Арма, например: Арма, как дела?") + +from src.OpenAI.GPT35turbo.OA_processing import openai_message_processing + + +async def delete_message(message: types.Message, sleep_time: int = 0): + await asyncio.sleep(sleep_time) + with suppress(MessageCantBeDeleted, MessageToDeleteNotFound): + await message.delete() + +@dp.message_handler() +async def in_message(message: types.Message): + chat_id = message.chat.id + # Получение сообщений в чате, и запись их в базу данных + # Проверка на то, что сообщение не пустое и не отправлено в чате содержащим ChatType = 1 в базе данных chatlist + if (message.chat.type != "group" or message.chat.type != "supergroup") and \ + message.text != '' and message.text != ' ' and \ + (cursor.execute("SELECT chat_role FROM chat_list WHERE chat_id;") == 1): return None + else: + # Запись сообщения в базу данных + cursor.execute("INSERT INTO message_list VALUES (?, ?, ?, ?)", + (message.message_id, message.text, message.from_user.id, None)) + if message.reply_to_message is not None: + # Запись ответа на сообщение в базу данных + cursor.execute("UPDATE message_list SET answer_id = ? WHERE message_id = ?", + (message.reply_to_message.message_id, message.message_id)) + database.commit() + # Обработка сообщения OpenAI + send_answer = False + # импортируем массив триггеров из файла .ini + if message.reply_to_message and message.reply_to_message.from_user.id == (await bot.me).id: + send_answer = True + for trigger in bot_trigger_all: + if trigger.lower() in message.text.lower(): + send_answer = True + for trigger in bot_trigger_front: + if message.text.lower().startswith(trigger.lower()): + send_answer = True + + if send_answer: + your_id = message.from_id + your_name = message.from_user.username + temp_msg = await message.reply( + f"[{your_name}](tg://user?id={str(your_id)}), Подожди немного и я обязательно отвечу тебе!", + parse_mode="Markdown") + response = openai_message_processing(message.message_id) + if response is None: + bot_message_id = await message.reply("Я не понял тебя, попробуй перефразировать") + asyncio.create_task(delete_message(temp_msg, 0)) + #заносим сообщение в базу данных в качестве message_id пишем id сообщения которое отправил бот + cursor.execute("INSERT INTO message_list VALUES (?, ?, ?, ?)", + (bot_message_id, "Я не понял тебя, попробуй перефразировать", 0, message.message_id)) + else: + bot_message_id = await message.reply(response['choices'][0]['message']['content'], parse_mode="markdown") + asyncio.create_task(delete_message(temp_msg, 0)) + #заносим сообщение в базу данных в качестве message_id мы пишем id сообщения в bot_message_id + cursor.execute("INSERT INTO message_list VALUES (?, ?, ?, ?)", + (bot_message_id.message_id, response['choices'][0]['message']['content'], 0, message.message_id)) + database.commit() + if __name__ == '__main__': executor.start_polling(dp, skip_updates=True) \ No newline at end of file diff --git a/src/config.ini b/src/config.ini index c2ac29c..4625b23 100644 --- a/src/config.ini +++ b/src/config.ini @@ -1,7 +1,24 @@ [Telegram] -token="....." -admin_password="Test_pass" # ONLY FOR ADDING CHAT TO ADMIN LIST! +token=**** +admin_password="Test_pass" +# Пока не используется -[OpenAI] -token="....." -chat_model="....." \ No newline at end of file +# Массивы заполнять через запятую. Пример: test|test2 |test3, +bot_trigger_front= +# Живой пример: Арма |Армат |Арма, | +bot_trigger_all= +# Живой пример: @arma_ai_bot |помогите | + +[Openai] +api_key=**** +chat_model=gpt-3.5-turbo +story_model= +# Тут должен быть текст истории бота, но я его не показываю))) +max_token_count=4000 +# максимальное количество токенов в сообщении +min_token_for_answer=800 +# минимальное количество токенов в сообщении ответа бота (чем больше, тем более длинный ответ ГАРАНТИРОВАН) + +[AI_Dungeon] +use=openai +# "openai" or "YaGPT" Пока не используется \ No newline at end of file