From 452a49dcc630437488d79df233fc58e08dafdb10 Mon Sep 17 00:00:00 2001 From: F33RNI Date: Fri, 10 Mar 2023 07:39:31 +0300 Subject: [PATCH 1/4] Add revChatGPT V0, V1, V2, V3 + bugfix --- .gitignore | 2 + AIHandler.py | 222 ++++++++++++++++++++++++++------ Authenticator.py | 327 ++++++++++++++++++++++++++++++++--------------- BotHandler.py | 6 +- main.py | 2 +- requirements.txt | 2 +- settings.json | 95 +++++++------- 7 files changed, 466 insertions(+), 190 deletions(-) diff --git a/.gitignore b/.gitignore index 19e700c5..23bf0c8b 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,5 @@ /test.py /logs/ /chats.json +/conversations/ +/conversations.json diff --git a/AIHandler.py b/AIHandler.py index 011de4a2..1402c475 100644 --- a/AIHandler.py +++ b/AIHandler.py @@ -14,7 +14,9 @@ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ +import asyncio import logging +import os import queue import threading import time @@ -30,6 +32,8 @@ ERROR_CHATGPT_DISABLED = 'ChatGPT module is disabled in settings.json' ERROR_DALLE_DISABLED = 'DALL-E module is disabled in settings.json' +CONVERSATION_DIR_OR_FILE = 'conversations' + class AIHandler: def __init__(self, settings, chats_file, authenticator): @@ -124,6 +128,99 @@ def set_chat(self, chat_id: int, conversation_id=None, parent_id=None): chats = {} save_json(self.chats_file, chats) + def save_conversation(self, chatbot_, conversation_id) -> None: + """ + Saves conversation + :param chatbot_: + :param conversation_id: + :return: + """ + logging.info('Saving conversation ' + conversation_id) + # API type 0 + if int(self.settings['modules']['chatgpt_api_type']) == 0: + conversations_file = CONVERSATION_DIR_OR_FILE + '.json' + chatbot_.conversations.save(conversations_file) + + # API type 3 + elif int(self.settings['modules']['chatgpt_api_type']) == 3: + if not os.path.exists(CONVERSATION_DIR_OR_FILE): + os.makedirs(CONVERSATION_DIR_OR_FILE) + conversation_file = os.path.join(CONVERSATION_DIR_OR_FILE, conversation_id + '.json') + chatbot_.save(conversation_file, conversation_id) + + def load_conversation(self, chatbot_, conversation_id) -> None: + """ + Loads conversation + :param chatbot_: + :param conversation_id: + :return: + """ + logging.info('Loading conversation ' + conversation_id) + try: + # API type 0 + if int(self.settings['modules']['chatgpt_api_type']) == 0: + conversations_file = CONVERSATION_DIR_OR_FILE + '.json' + chatbot_.conversations.load(conversations_file) + + # API type 3 + elif int(self.settings['modules']['chatgpt_api_type']) == 3: + conversation_file = os.path.join(CONVERSATION_DIR_OR_FILE, conversation_id + '.json') + chatbot_.load(conversation_file, conversation_id) + except Exception as e: + logging.warning('Error loading conversation ' + conversation_id + ' ' + str(e)) + + def delete_conversation(self, conversation_id) -> None: + """ + Deletes conversation + :param conversation_id: + :return: + """ + logging.info('Deleting conversation ' + conversation_id) + try: + # API type 0 + if int(self.settings['modules']['chatgpt_api_type']) == 0: + self.authenticator.chatbot.reset() + try: + conversations_file = CONVERSATION_DIR_OR_FILE + '.json' + self.authenticator.chatbot.conversations.remove_conversation(conversations_file) + self.authenticator.chatbot.conversations.save(conversations_file) + except Exception as e: + logging.error('Error deleting conversation ' + conversation_id, e) + + # API type 1 + elif int(self.settings['modules']['chatgpt_api_type']) == 1: + self.authenticator.chatbot.reset_chat() + try: + self.authenticator.chatbot.delete_conversation(conversation_id) + except Exception as e: + logging.error('Error deleting conversation ' + conversation_id, e) + + # API type 2 + elif int(self.settings['modules']['chatgpt_api_type']) == 2: + self.authenticator.chatbot.reset_chat() + try: + self.authenticator.chatbot.conversations.remove(conversation_id) + except Exception as e: + logging.error('Error deleting conversation ' + conversation_id, e) + + # API type 3 + elif int(self.settings['modules']['chatgpt_api_type']) == 3: + self.authenticator.chatbot.reset(conversation_id) + + # Wrong API type + else: + raise Exception('Wrong chatgpt_api_type') + + # Delete conversation file if exists + try: + conversation_file = os.path.join(CONVERSATION_DIR_OR_FILE, conversation_id + '.json') + if os.path.exists(conversation_file): + os.remove(conversation_file) + except Exception as e: + logging.error('Error removing conversation file for conversation ' + conversation_id, e) + except Exception as e: + logging.warning('Error loading conversation ' + conversation_id + ' ' + str(e)) + def gpt_loop(self): """ Background loop for handling requests @@ -153,17 +250,46 @@ def gpt_loop(self): api_response = None raise Exception(ERROR_CHATGPT_DISABLED) - # API type 0 - if self.authenticator.api_type == 0: - # Get conversation_id - conversation_id, parent_id = self.get_chat(container.chat_id) + # Too many requests in 1 hour + if self.authenticator.chatbot_too_many_requests: + raise Exception(Authenticator.TOO_MANY_REQUESTS_MESSAGE) - # Get chatbot from Authenticator class + # Wait for chatbot + chatbot = self.authenticator.chatbot + while not self.authenticator.chatbot_working or chatbot is None: + time.sleep(1) chatbot = self.authenticator.chatbot - # Reset chat + # Too many requests in 1 hour + if self.authenticator.chatbot_too_many_requests: + raise Exception(Authenticator.TOO_MANY_REQUESTS_MESSAGE) + + # Lock chatbot + self.authenticator.chatbot_locked = True + + # Get conversation_id and parent_id + conversation_id, parent_id = self.get_chat(container.chat_id) + + # Log request + logging.info('Asking: ' + str(container.request) + + ', conversation_id: ' + str(conversation_id) + ', parent_id: ' + str(parent_id)) + + # API type 0 + if int(self.settings['modules']['chatgpt_api_type']) == 0: + # Reset current chat chatbot.reset() + # Try to load conversation + if conversation_id is not None: + self.load_conversation(chatbot, conversation_id) + + # Try to load conversation + if conversation_id is not None: + try: + chatbot.load_conversation(conversation_id) + except Exception as e: + logging.warning('Error loading conversation with id: ' + conversation_id + ' ' + str(e)) + # Ask for data in chatbot.ask_stream(str(container.request), conversation_id=conversation_id): # Initialize response @@ -178,6 +304,7 @@ def gpt_loop(self): if conversation_id is None: conversation_id = str(uuid.uuid4()) chatbot.save_conversation(conversation_id) + self.save_conversation(chatbot, conversation_id) self.set_chat(container.chat_id, conversation_id, parent_id) except Exception as e: logging.warning('Error saving conversation! ' + str(e)) @@ -186,32 +313,8 @@ def gpt_loop(self): api_response = api_response.replace('<|im_end|>', '').replace('<|im_start|>', '') # API type 1 - elif self.authenticator.api_type == 1: - # Too many requests in 1 hour - if self.authenticator.chatbot_too_many_requests: - raise Exception(Authenticator.TOO_MANY_REQUESTS_MESSAGE) - - # Wait for chatbot - chatbot = self.authenticator.chatbot - while not self.authenticator.chatbot_working or chatbot is None: - time.sleep(1) - chatbot = self.authenticator.chatbot - - # Too many requests in 1 hour - if self.authenticator.chatbot_too_many_requests: - raise Exception(Authenticator.TOO_MANY_REQUESTS_MESSAGE) - - # Lock chatbot - self.authenticator.chatbot_locked = True - - # Get conversation_id and parent_id - conversation_id, parent_id = self.get_chat(container.chat_id) - - # Log request - logging.info('Asking: ' + str(container.request) - + ', conversation_id: ' + str(conversation_id) + ', parent_id: ' + str(parent_id)) - - # Reset chat + elif int(self.settings['modules']['chatgpt_api_type']) == 1: + # Reset current chat chatbot.reset_chat() # Ask @@ -230,18 +333,65 @@ def gpt_loop(self): parent_id = data['parent_id'] # Log conversation id and parent id - logging.info('Current conversation_id: ' + conversation_id + ', parent_id: ' + parent_id) + logging.info('Current conversation_id: ' + str(conversation_id) + + ', parent_id: ' + str(parent_id)) + + # Save conversation id + self.set_chat(container.chat_id, conversation_id, parent_id) + + # API type 2 + elif int(self.settings['modules']['chatgpt_api_type']) == 2: + # Make async function sync + api_responses_ = [] + conversation_ids_ = [] + + async def ask_async(request_, conversation_id_): + async for data_ in chatbot.ask(request_, conversation_id=conversation_id_): + # Get last response + api_responses_.append(data_['message']) + + # Store conversation_id + if 'conversation_id' in data_ and data_['conversation_id'] is not None: + conversation_ids_.append(data_['conversation_id']) + + # Ask + loop = asyncio.new_event_loop() + loop.run_until_complete(ask_async(str(container.request), conversation_id)) + + # Log conversation id and parent id + logging.info('Current conversation_id: ' + str(conversation_id) + + ', parent_id: ' + str(parent_id)) # Save conversation id self.set_chat(container.chat_id, conversation_id, parent_id) + # API type 3 + elif int(self.settings['modules']['chatgpt_api_type']) == 3: + # Generate conversation ID + if conversation_id is None: + conversation_id = str(uuid.uuid4()) + + # Try to load conversation + else: + self.load_conversation(chatbot, conversation_id) + + # Ask + for data in chatbot.ask(str(container.request), convo_id=conversation_id): + # Initialize response + if api_response is None: + api_response = '' + + # Append response + api_response += str(data) + + # Save conversation id + self.save_conversation(chatbot, conversation_id) + self.set_chat(container.chat_id, conversation_id) + # Wrong api type else: raise Exception('Wrong chatgpt_api_type') - # Log response - logging.info(str(api_response)) - # DALL-E else: # Check if ChatGPT is enabled diff --git a/Authenticator.py b/Authenticator.py index 15529e86..d7382eff 100644 --- a/Authenticator.py +++ b/Authenticator.py @@ -14,6 +14,7 @@ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ +import asyncio import copy import logging import multiprocessing @@ -21,6 +22,7 @@ import random import threading import time +import uuid from urllib import request import useragents @@ -42,25 +44,53 @@ def kill_all_processes(processes_and_times): try: process_.kill() process_.join() - except: - logging.warning('Error killing process with PID: ' + str(process_.pid)) + except Exception as e: + logging.warning('Error killing process with PID: ' + str(process_.pid) + ' ' + str(e)) -def initialize_chatbot(base_url, proxy, config, chatbots_and_proxies_queue): +def initialize_chatbot(chatgpt_api_type, proxy, config, openai_api_key, engine, chatbots_and_proxies_queue): """ - Pops first proxy and tries to initialize chatbot + Tries to initialize chatbot in background process + :param chatgpt_api_type: int(self.settings['modules']['chatgpt_api_type']) + :param proxy: proxy prom list + :param config: self.get_chatbot_config() + :param openai_api_key: str(self.settings['chatgpt_auth']['api_key']) + :param engine: str(self.settings['chatgpt_auth']['engine']) + :param chatbots_and_proxies_queue: multiprocessing queue :return: """ try: - # Get config - config_ = copy.deepcopy(config) - config_['proxy'] = proxy + # API type 0 + if chatgpt_api_type == 0: + from revChatGPT.V0 import Chatbot + if engine is not None and len(engine) > 0: + chatbot = Chatbot(openai_api_key, engine=engine, proxy=proxy) + else: + chatbot = Chatbot(openai_api_key, proxy=proxy) + + # API type 1 + elif chatgpt_api_type == 1: + config_ = copy.deepcopy(config) + config_['proxy'] = proxy + from revChatGPT.V1 import Chatbot + chatbot = Chatbot(config=config_) + + # API type 2 + elif chatgpt_api_type == 2: + from revChatGPT.V2 import Chatbot + chatbot = Chatbot(openai_api_key, proxy=proxy) + + # API type 3 + elif chatgpt_api_type == 3: + from revChatGPT.V3 import Chatbot + if engine is not None and len(engine) > 0: + chatbot = Chatbot(openai_api_key, engine=engine, proxy=proxy) + else: + chatbot = Chatbot(openai_api_key, proxy=proxy) - # Initialize chatbot - if base_url is not None and len(str(base_url)) > 0: - os.environ['CHATGPT_BASE_URL'] = str(base_url) - from revChatGPT.V1 import Chatbot - chatbot = Chatbot(config=config_) + # Other api type + else: + chatbot = None # Append working chatbot and proxy if chatbot is not None: @@ -73,77 +103,113 @@ class Authenticator: def __init__(self, settings): self.settings = settings - self.api_type = 0 self.chatbot = None self.chatbot_locked = False self.chatbot_too_many_requests = False self.chatbot_working = False - self.chatbots_and_proxies_queue = multiprocessing.Queue(maxsize=int(self.settings['chatgpt_api_1']['proxy'] + self.chatbots_and_proxies_queue = multiprocessing.Queue(maxsize=int(self.settings['proxy'] ['max_number_of_processes']) * 2) self.current_proxy = None self.conversation_id = None self.proxy_list = [] self.check_loop_running = False + self.engine = None def start_chatbot(self): """ Initializes chatbot and starts background proxy checker thread if needed :return: """ - # Official API - if int(self.settings['modules']['chatgpt_api_type']) == 0: - self.api_type = 0 - # Proxy - proxy_ = str(self.settings['chatgpt_api_0']['proxy']) - if len(proxy_) > 0: - self.current_proxy = proxy_ - try: + # Set base url + base_url = None + if int(self.settings['modules']['chatgpt_api_type']) == 1: + base_url = str(self.settings['chatgpt_auth']['base_url']) + if base_url is not None and len(base_url) > 0: + os.environ['CHATGPT_BASE_URL'] = base_url + + # Set engine name + self.engine = str(self.settings['chatgpt_auth']['engine']) + if self.engine is not None and len(self.engine) <= 0: + self.engine = None + + # Initialize proxy + proxy_ = None + if self.settings['proxy']['enabled']: + logging.info('Proxy is set to enabled!') + + # Get proxy checks enabled flag + proxy_checks_enabled = self.settings['proxy']['proxy_checks_enabled'] + if int(self.settings['proxy']['check_interval_seconds']) <= 0: + proxy_checks_enabled = False + + # Start thread if proxy checks is set to true + if proxy_checks_enabled: + # Set flag + self.check_loop_running = True + + # Start thread + thread = threading.Thread(target=self.proxy_checker_loop) + thread.start() + logging.info('Proxy checking thread: ' + thread.name) + return + + # No checks + else: + # Auto proxy -> get first proxy + if self.settings['proxy']['auto']: + self.proxy_get() + proxy_ = self.proxy_list[0] + # Manual proxy + else: + proxy_ = str(self.settings['proxy']['manual_proxy']) + + # Initialize chatbot + try: + # API type 0 + if int(self.settings['modules']['chatgpt_api_type']) == 0: from revChatGPT.V0 import Chatbot - # Initialize chatbot - self.chatbot = Chatbot(str(self.settings['chatgpt_api_0']['open_ai_api_key']), - engine=str(self.settings['chatgpt_api_0']['engine']), - proxy=proxy_) + if self.engine is None: + self.chatbot = Chatbot(str(self.settings['chatgpt_auth']['api_key']), + proxy=proxy_) + else: + self.chatbot = Chatbot(str(self.settings['chatgpt_auth']['api_key']), + engine=self.engine, + proxy=proxy_) self.chatbot_working = True - # Error initializing chatbot - except Exception as e: - logging.warning('Error initializing chatbot!' + str(e)) - self.chatbot_working = False - - # revChatGPT API version 1 - elif int(self.settings['modules']['chatgpt_api_type']) == 1: - self.api_type = 1 - # No proxy - if int(self.settings['chatgpt_api_1']['proxy']['check_interval_seconds']) <= 0 \ - or not self.settings['chatgpt_api_1']['proxy']['enabled']: - logging.info('Proxy checks disabled. Initializing chatbot...') - if self.chatbot is None: - try: - if len(str(self.settings['chatgpt_api_1']['chatgpt_auth']['base_url'])) > 0: - os.environ['CHATGPT_BASE_URL'] \ - = str(self.settings['chatgpt_api_1']['chatgpt_auth']['base_url']) - from revChatGPT.V1 import Chatbot - self.chatbot = Chatbot(config=self.get_chatbot_config()) - self.chatbot_working = True - - # Error initializing chatbot - except Exception as e: - logging.warning('Error initializing chatbot!' + str(e)) - self.chatbot_working = False - return + # API type 1 + elif int(self.settings['modules']['chatgpt_api_type']) == 1: + from revChatGPT.V1 import Chatbot + self.chatbot = Chatbot(config=self.get_chatbot_config()) + self.chatbot_working = True - # Set flag - self.check_loop_running = True + # API type 2 + elif int(self.settings['modules']['chatgpt_api_type']) == 2: + from revChatGPT.V2 import Chatbot + self.chatbot = Chatbot(str(self.settings['chatgpt_auth']['api_key']), proxy=proxy_) + self.chatbot_working = True + + # API type 3 + elif int(self.settings['modules']['chatgpt_api_type']) == 3: + from revChatGPT.V3 import Chatbot + if self.engine is None: + self.chatbot = Chatbot(str(self.settings['chatgpt_auth']['api_key']), + proxy=proxy_) + else: + self.chatbot = Chatbot(str(self.settings['chatgpt_auth']['api_key']), + engine=self.engine, + proxy=proxy_) + self.chatbot_working = True - # Start thread - thread = threading.Thread(target=self.proxy_checker_loop) - thread.start() - logging.info('Checking thread: ' + thread.name) + # Other api type + else: + logging.error('Wrong chatgpt_api_type!') + raise Exception('Wrong chatgpt_api_type') - # Other than 0 or 1 - else: - logging.error('Wrong chatgpt_api_type!') - raise Exception('Wrong chatgpt_api_type') + # Error initializing chatbot + except Exception as e: + logging.error('Error initializing chatbot!', e) + self.chatbot_working = False def stop_chatbot(self): """ @@ -194,7 +260,7 @@ def proxy_get(self): # Check data and append to list if len(ip.split('.')) == 4 and len(port) > 1 and \ - (is_https or not self.settings['chatgpt_api_1']['proxy']['https_only']): + (is_https or not self.settings['proxy']['https_only']): # proxies_list.append(('https://' if is_https else 'http://') + ip + ':' + port) self.proxy_list.append('http://' + ip + ':' + port) except: @@ -225,28 +291,84 @@ def proxy_checker_loop(self): time.sleep(1) # Ask test message - logging.info('Asking test question: ' + str(self.settings['chatgpt_api_1'] - ['proxy']['check_message']).strip()) + logging.info('Asking test question: ' + str(self.settings['proxy']['check_message']).strip()) chatbot_response = '' - for data in self.chatbot.ask(str(self.settings['chatgpt_api_1']['proxy']['check_message']).strip(), - conversation_id=self.conversation_id, - timeout=int(self.settings['chatgpt_api_1'] - ['proxy']['check_message_timeout'])): - # Get response - chatbot_response = data['message'] - # Store conversation_id - if data['conversation_id'] is not None: - self.conversation_id = data['conversation_id'] + # API type 0 + if int(self.settings['modules']['chatgpt_api_type']) == 0: + # Reset current chat + self.chatbot.reset() + + # Ask + for data in self.chatbot.ask_stream(str(self.settings['proxy']['check_message']).strip()): + # Initialize response + if chatbot_response is None: + chatbot_response = '' + + # Append response + chatbot_response += str(data) + + # API type 1 + elif int(self.settings['modules']['chatgpt_api_type']) == 1: + # Reset current chat + self.chatbot.reset_chat() + + # Ask + for data in self.chatbot.ask(str(self.settings['proxy']['check_message']).strip(), + conversation_id=self.conversation_id): + # Get last response + chatbot_response = data['message'] + + # Store conversation_id + if 'conversation_id' in data and data['conversation_id'] is not None: + self.conversation_id = data['conversation_id'] + + # API type 2 + elif int(self.settings['modules']['chatgpt_api_type']) == 2: + # Make async function sync + api_responses_ = [] + conversation_ids_ = [] + + async def ask_async(request_, conversation_id_): + async for data_ in self.chatbot.ask(request_, conversation_id=conversation_id_): + # Get last response + api_responses_.append(data_['message']) + + # Store conversation_id + if 'conversation_id' in data_ and data_['conversation_id'] is not None: + conversation_ids_.append(data_['conversation_id']) + + # Ask + loop = asyncio.new_event_loop() + loop.run_until_complete(ask_async(str(self.settings['proxy']['check_message']).strip(), + self.conversation_id)) + + # API type 3 + elif int(self.settings['modules']['chatgpt_api_type']) == 3: + # Generate conversation ID + if self.conversation_id is None: + self.conversation_id = str(uuid.uuid4()) + + # Ask + for data in self.chatbot.ask(str(self.settings['proxy']['check_message']).strip(), + convo_id=self.conversation_id): + # Initialize response + if chatbot_response is None: + chatbot_response = '' + + # Append response + chatbot_response += str(data) + + # Wrong api type + else: + raise Exception('Wrong chatgpt_api_type') # Check response - if str(self.settings['chatgpt_api_1']['proxy']['check_reply_must_include']).strip() \ - in chatbot_response: + if str(self.settings['proxy']['check_reply_must_include']).strip() in chatbot_response: check_successful = True self.chatbot_too_many_requests = False else: - raise Exception('No ' + self.settings['chatgpt_api_1']['proxy']['check_reply_must_include'] - + ' in response!') + raise Exception('No ' + self.settings['proxy']['check_reply_must_include'] + ' in response!') except Exception as e: # Too many requests in 1 hour @@ -254,8 +376,7 @@ def proxy_checker_loop(self): logging.warning(str(e)) # Wait before next try - wait_seconds = \ - int(self.settings['chatgpt_api_1']['proxy']['too_many_requests_wait_time_seconds']) + wait_seconds = int(self.settings['proxy']['too_many_requests_wait_time_seconds']) logging.warning('Waiting ' + str(wait_seconds) + ' seconds...') self.chatbot_too_many_requests = True time.sleep(wait_seconds) @@ -263,7 +384,7 @@ def proxy_checker_loop(self): # Other error else: self.chatbot_too_many_requests = False - logging.warning('Error checking chatbot! ' + str(e)) + logging.error('Error checking chatbot! ' + str(e)) # Sleep for next cycle in check is successful if check_successful: @@ -275,8 +396,7 @@ def proxy_checker_loop(self): # Sleep and check for self.chatbot_working sleep_started_time = time.time() - while time.time() - sleep_started_time \ - < int(self.settings['chatgpt_api_1']['proxy']['check_interval_seconds']): + while time.time() - sleep_started_time < int(self.settings['proxy']['check_interval_seconds']): if not self.chatbot_working: logging.info('Sleep interrupted!') break @@ -289,15 +409,15 @@ def proxy_checker_loop(self): # Check is not successful else: # Get proxy - if self.settings['chatgpt_api_1']['proxy']['enabled']: + if self.settings['proxy']['enabled']: # Auto proxy - if self.settings['chatgpt_api_1']['proxy']['auto']: + if self.settings['proxy']['auto']: # Get new proxy list if len(self.proxy_list) <= 0: self.proxy_get() # Manual proxy else: - self.proxy_list = [self.settings['chatgpt_api_1']['proxy']['manual_proxy']] + self.proxy_list = [self.settings['proxy']['manual_proxy']] # Proxy disabled else: break @@ -311,7 +431,10 @@ def proxy_checker_loop(self): processes_and_times = [] # Get default config - default_config = self.get_chatbot_config() + if int(self.settings['modules']['chatgpt_api_type']) == 1: + default_config = self.get_chatbot_config() + else: + default_config = None while True: # Exit if thread stopped @@ -322,13 +445,14 @@ def proxy_checker_loop(self): # Create and start processes while len(self.proxy_list) > 0 \ and len(processes_and_times) \ - < int(self.settings['chatgpt_api_1']['proxy']['max_number_of_processes']): + < int(self.settings['proxy']['max_number_of_processes']): proxy = self.proxy_list.pop(0) process = multiprocessing.Process(target=initialize_chatbot, - args=(str(self.settings['chatgpt_api_1'] - ['chatgpt_auth']['base_url']), + args=(int(self.settings['modules']['chatgpt_api_type']), proxy, default_config, + str(self.settings['chatgpt_auth']['api_key']), + str(self.settings['chatgpt_auth']['engine']), self.chatbots_and_proxies_queue,)) process.start() time_started = time.time() @@ -345,7 +469,7 @@ def proxy_checker_loop(self): # No more proxies if len(self.proxy_list) == 0: # Get new proxy list in auto mode - if self.settings['chatgpt_api_1']['proxy']['auto']: + if self.settings['proxy']['auto']: self.proxy_get() # Exit if manual mode and no more processes @@ -378,8 +502,7 @@ def proxy_checker_loop(self): if process_ is not None and time_ is not None: # Kill on timeout or exit if time.time() - time_ \ - > int(self.settings['chatgpt_api_1']['proxy']['initialization_timeout']) \ - or not process_.is_alive(): + > int(self.settings['proxy']['initialization_timeout']) or not process_.is_alive(): if process_.is_alive(): logging.info('Killing process with PID: ' + str(process_.pid) + ' due to timeout') try: @@ -401,25 +524,25 @@ def proxy_checker_loop(self): def get_chatbot_config(self): """ - Constructs chatbot config + Constructs chatbot config for api type 1 See: https://github.com/acheong08/ChatGPT :return: """ config = {} # Use email/password - if len(self.settings['chatgpt_api_1']['chatgpt_auth']['email']) > 0 \ - and len(self.settings['chatgpt_api_1']['chatgpt_auth']['password']) > 0: - config['email'] = self.settings['chatgpt_api_1']['chatgpt_auth']['email'] - config['password'] = self.settings['chatgpt_api_1']['chatgpt_auth']['password'] + if len(self.settings['chatgpt_auth']['email']) > 0 \ + and len(self.settings['chatgpt_auth']['password']) > 0: + config['email'] = self.settings['chatgpt_auth']['email'] + config['password'] = self.settings['chatgpt_auth']['password'] # Use session_token - elif len(self.settings['chatgpt_api_1']['chatgpt_auth']['session_token']) > 0: - config['session_token'] = self.settings['chatgpt_api_1']['chatgpt_auth']['session_token'] + elif len(self.settings['chatgpt_auth']['session_token']) > 0: + config['session_token'] = self.settings['chatgpt_auth']['session_token'] # Use access_token - elif len(self.settings['chatgpt_api_1']['chatgpt_auth']['access_token']) > 0: - config['access_token'] = self.settings['chatgpt_api_1']['chatgpt_auth']['access_token'] + elif len(self.settings['chatgpt_auth']['access_token']) > 0: + config['access_token'] = self.settings['chatgpt_auth']['access_token'] # No credentials else: diff --git a/BotHandler.py b/BotHandler.py index efcbd138..70d980d3 100644 --- a/BotHandler.py +++ b/BotHandler.py @@ -180,13 +180,13 @@ async def bot_command_clear(self, update: Update, context: ContextTypes.DEFAULT_ logging.info('/clear command from user ' + str(user.full_name) + ' request: ' + ' '.join(context.args)) # Delete conversation - if self.ai_handler.authenticator.api_type == 1: + if int(self.settings['modules']['chatgpt_api_type']) == 1: conversation_id, _ = self.ai_handler.get_chat(chat_id) if conversation_id is not None and len(conversation_id) > 0: try: - self.ai_handler.authenticator.chatbot.delete_conversation(conversation_id) + self.ai_handler.delete_conversation(conversation_id) except Exception as e: - logging.warning('Error deleting conversation ' + str(conversation_id) + ' ' + str(e)) + logging.warning('Error removing conversation ' + str(conversation_id) + ' ' + str(e)) # Clear conversation ID and parent ID self.ai_handler.set_chat(chat_id, None, None) diff --git a/main.py b/main.py index ea1e166b..2827fdb7 100644 --- a/main.py +++ b/main.py @@ -29,7 +29,7 @@ import BotHandler from JSONReaderWriter import load_json -TELEGRAMUS_VERSION = 'beta_1.9.0' +TELEGRAMUS_VERSION = 'beta_2.0.0' # Logging level (INFO for debug, WARN for release) LOGGING_LEVEL = logging.INFO diff --git a/requirements.txt b/requirements.txt index 5d7ad368..4ff6124f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ psutil>=5.9.1 telegram~=0.0.1 python-telegram-bot~=20.1 -revChatGPT>=2.3.14 +revChatGPT==3.3.1 openai>=0.26.4 tiktoken>=0.2.0 OpenAIAuth>=0.3.2 \ No newline at end of file diff --git a/settings.json b/settings.json index cd247a4d..5e202134 100644 --- a/settings.json +++ b/settings.json @@ -1,66 +1,67 @@ { - "__comment01__": "SPECIFY WHAT MODULES WILL BE INCLUDED IN TELEGRAM BOT", - "__comment02__": "SPECIFY revChatGPT API TYPE: 0 USES OPENAI API KEY, 1 USES EMAIL/PASS BUT BETTER AND FREE", + "__comment01__": "SPECIFY WHAT MODULES WILL BE INCLUDED IN TELEGRAM BOT AND revChatGPT API TYPE", + "__comment02__": "0 - OFFICIAL CHATGPT API. MORE STUPID MODEL. AUTHORIZATION VIA OPENAI API KEY", + "__comment03__": "1 - (NOT TESTED) SAME AS OFFICIAL WEBSITE. FREE BUT LIMITED NUMBER OF REQUESTS. AUTHORIZATION VIA ACCESS_TOKEN, SESSION_TOKEN (NOT TESTED) OR EMAIL/PASS (NOT TESTED)", + "__comment04__": "2 - (MAY NOT WORK) FREE API FOR CHATGPT. AUTHORIZATION VIA OPENAI API KEY", + "__comment05__": "3 - (RECOMMENDED) OFFICIAL CHATGPT API. AUTHORIZATION VIA OPENAI API KEY", "modules": { "chatgpt": true, - "chatgpt_api_type": 1, + "chatgpt_api_type": 3, "dalle": true }, - "__comment03__": "API TYPE 0 USES 'OFFICIAL' API WITH engine IT WILL USE YOUR CREDITS (18USD FREE TRIAL)", - "__comment04__": "GO TO https://platform.openai.com/account/api-keys GENERATE NEW API KEY AND PASTE BELOW", - "chatgpt_api_0": { - "open_ai_api_key": "", - "engine": "text-davinci-003", - "proxy": "" - }, + "__comment06__": "AUTHORIZATION FOR CHATGPT", + "chatgpt_auth": { + "__comment07__": "API_KEY FROM https://platform.openai.com/account/api-keys. ONLY FOR API TYPES 0, 2 AND 3", + "api_key": "", - "__comment05__": "API TYPE 1 USES revChatGPT TO ACCESS SAME MODEL AS OFFICIAL WEBSITE AND ALSO IT'S FREE", - "chatgpt_api_1": { - "__comment06__": "PROVIDE YOUR OPENAI ACCOUNT DETAILS BELOW TO USE CHATGPT (IF ENABLED)", - "__comment07__": "GO TO https://chat.openai.com/api/auth/session AND PASTE accessToken BELOW", - "__comment08__": "OR SESSION_TOKEN (FROM COOKIES ON chat.openai.com AS __Secure-next-auth.session-token)", - "__comment09__": "OR YOU CAN USE EMAIL/PASSWORD (MAY NOT WORKING)", - "__comment10__": "PROVIDE BASE URL (LEAVE EMPTY FOR DEFAULT VALUE OR TRY https://apps.openai.com/)", - "chatgpt_auth": { - "email": "", - "password": "", - "session_token": "", - "access_token": "", - "base_url": "https://apps.openai.com/" - }, + "__comment08__": "MODEL NAME FOR API TYPE 0 AND 3", + "__comment09__": "DEFAULT FOR API TYPE 0: text-davinci-003", + "__comment10__": "DEFAULT FOR API TYPE 3: gpt-3.5-turbo", + "engine": "", + + "__comment11__": "FOR API TYPE 1 ACCESS TOKEN IS RECOMMENDED. GET IT FROM https://chat.openai.com/api/auth/session", + "__comment12__": "OR SESSION_TOKEN (FROM COOKIES ON chat.openai.com AS __Secure-next-auth.session-token", + "email": "", + "password": "", + "session_token": "", + "access_token": "", + + "__comment13__": "URL FOR API REQUESTS. ONLY FOR API TYPE 1. SPECIFY NOTHING TO USE DEFAULT VALUE", + "base_url": "" + }, - "__comment11__": "PROXIES TO BYPASS COUNTRY RESTRICTIONS", - "__comment12__": "IN AUTO MODE, PROXIES WILL BE REQUESTED FROM http://free-proxy-list.net/", - "__comment13__": "IF AUTO IS FALSE, SPECIFY PROXY IN THE http://IP:PORT FORMAT", - "__comment14__": "SPECIFY THE INTERVAL HOW LONG TO CHECK THE PROXY BY ASKING A QUESTION. OR SET 0 TO DISABLE CHECK", - "__comment15__": "SPECIFY THE QUESTION WHICH THE APP WILL ASK AND THE TEXT WHICH SHOULD BE IN THE ANSWER", - "proxy": { - "enabled": false, - "auto": true, - "https_only": true, - "manual_proxy": "http://IP:PORT", - "check_interval_seconds": 300, - "check_message": "1+1", - "check_message_timeout": 240, - "check_reply_must_include": "2", - "max_number_of_processes": 5, - "initialization_timeout": 60, - "too_many_requests_wait_time_seconds": 600 - } + "__comment14__": "PROXIES TO BYPASS COUNTRY RESTRICTIONS. TESTED ONLY ON API TYPE 0 and 3", + "__comment15__": "IN AUTO MODE PROXIES WILL BE REQUESTED FROM http://free-proxy-list.net/", + "__comment16__": "IF AUTO IS FALSE, SPECIFY PROXY IN THE http://IP:PORT FORMAT (SPECIFY HTTP EVEN IF IT IS HTTPS PROXY)", + "__comment17__": "SPECIFY THE INTERVAL HOW LONG TO CHECK THE PROXY BY ASKING A QUESTION. OR SET 0 TO DISABLE CHECK", + "__comment18__": "SPECIFY THE QUESTION WHICH THE APP WILL ASK AND THE TEXT WHICH SHOULD BE IN THE ANSWER", + "proxy": { + "enabled": false, + "auto": true, + "manual_proxy": "", + "https_only": true, + "proxy_checks_enabled": true, + "check_interval_seconds": 600, + "check_message": "1+1", + "check_message_timeout": 240, + "check_reply_must_include": "2", + "max_number_of_processes": 5, + "initialization_timeout": 60, + "too_many_requests_wait_time_seconds": 600 }, - "__comment16__": "PROVIDE YOUR API_KEY FROM https://platform.openai.com/account/api-keys FOR REQUESTS TO DALL-E", - "__comment17__": "SPECIFY FORMAT OF GENERATED IMAGES (256x256 or 512x512 or 1024x1024)", - "__comment18__": "SPECIFY IS PROXY NEEDED FOR DALLE (SAME PROXY AS FOR CHATGPT)", + "__comment19__": "FOR REQUESTS TO DALL-E PROVIDE YOUR API_KEY FROM https://platform.openai.com/account/api-keys", + "__comment20__": "SPECIFY FORMAT OF GENERATED IMAGES (256x256 or 512x512 or 1024x1024)", + "__comment21__": "SPECIFY IS PROXY NEEDED FOR DALLE (SAME PROXY AS FOR CHATGPT)", "dalle": { "open_ai_api_key": "", "image_size": "512x512", "use_proxy": true }, - "__comment19__": "PROVIDE YOUR BOT API KEY FROM https://t.me/BotFather", - "__comment20__": "SPECIFY THE SIZE OF THE REQUEST QUEUE AND WHETHER TO SHOW A MESSAGE ABOUT ADDING TO THE QUEUE", + "__comment22__": "PROVIDE YOUR BOT API KEY FROM https://t.me/BotFather", + "__comment23__": "SPECIFY THE SIZE OF THE REQUEST QUEUE AND WHETHER TO SHOW A MESSAGE ABOUT ADDING TO THE QUEUE", "telegram": { "api_key": "", "queue_max": 5, From 8d952b66ea09105edb1fbbef5e873baf6ea23f7f Mon Sep 17 00:00:00 2001 From: F33RNI Date: Fri, 10 Mar 2023 08:12:29 +0300 Subject: [PATCH 2/4] Move all history into chats dir --- .gitignore | 4 +--- AIHandler.py | 36 ++++++++++++++++++++++-------------- BotHandler.py | 16 +++++++--------- main.py | 8 ++++---- 4 files changed, 34 insertions(+), 30 deletions(-) diff --git a/.gitignore b/.gitignore index 23bf0c8b..6dd6b853 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,4 @@ /*.zip /test.py /logs/ -/chats.json -/conversations/ -/conversations.json +/chats/ diff --git a/AIHandler.py b/AIHandler.py index 1402c475..2c0ff0b7 100644 --- a/AIHandler.py +++ b/AIHandler.py @@ -36,9 +36,9 @@ class AIHandler: - def __init__(self, settings, chats_file, authenticator): + def __init__(self, settings, chats_dir, authenticator): self.settings = settings - self.chats_file = chats_file + self.chats_dir = chats_dir self.authenticator = authenticator # Loop running flag @@ -63,6 +63,10 @@ def thread_start(self): Starts background thread :return: """ + # Create chats directory if not exists + if not os.path.exists(self.chats_dir): + os.makedirs(self.chats_dir) + # Set flag self.loop_running = True @@ -78,7 +82,7 @@ def get_chat(self, chat_id: int): :return: (conversation_id, parent_id) """ logging.info('Loading conversation_id for chat_id ' + str(chat_id)) - chats = load_json(self.chats_file) + chats = load_json(os.path.join(self.chats_dir, 'chats.json')) if chats is not None and str(chat_id) in chats: chat = chats[str(chat_id)] conversation_id = None @@ -102,7 +106,7 @@ def set_chat(self, chat_id: int, conversation_id=None, parent_id=None): """ logging.info('Saving conversation_id ' + str(conversation_id) + ' and parent_id ' + str(parent_id) + ' for chat_id ' + str(chat_id)) - chats = load_json(self.chats_file) + chats = load_json(os.path.join(self.chats_dir, 'chats.json')) if chats is not None: if str(chat_id) in chats: # Save or delete conversation_id @@ -126,7 +130,7 @@ def set_chat(self, chat_id: int, conversation_id=None, parent_id=None): chats[str(chat_id)]['parent_id'] = parent_id else: chats = {} - save_json(self.chats_file, chats) + save_json(os.path.join(self.chats_dir, 'chats.json'), chats) def save_conversation(self, chatbot_, conversation_id) -> None: """ @@ -139,13 +143,13 @@ def save_conversation(self, chatbot_, conversation_id) -> None: # API type 0 if int(self.settings['modules']['chatgpt_api_type']) == 0: conversations_file = CONVERSATION_DIR_OR_FILE + '.json' - chatbot_.conversations.save(conversations_file) + chatbot_.conversations.save(os.path.join(self.chats_dir, conversations_file)) # API type 3 elif int(self.settings['modules']['chatgpt_api_type']) == 3: - if not os.path.exists(CONVERSATION_DIR_OR_FILE): - os.makedirs(CONVERSATION_DIR_OR_FILE) - conversation_file = os.path.join(CONVERSATION_DIR_OR_FILE, conversation_id + '.json') + if not os.path.exists(os.path.join(self.chats_dir, CONVERSATION_DIR_OR_FILE)): + os.makedirs(os.path.join(self.chats_dir, CONVERSATION_DIR_OR_FILE)) + conversation_file = os.path.join(self.chats_dir, CONVERSATION_DIR_OR_FILE, conversation_id + '.json') chatbot_.save(conversation_file, conversation_id) def load_conversation(self, chatbot_, conversation_id) -> None: @@ -160,12 +164,15 @@ def load_conversation(self, chatbot_, conversation_id) -> None: # API type 0 if int(self.settings['modules']['chatgpt_api_type']) == 0: conversations_file = CONVERSATION_DIR_OR_FILE + '.json' - chatbot_.conversations.load(conversations_file) + chatbot_.conversations.load(os.path.join(self.chats_dir, conversations_file)) # API type 3 elif int(self.settings['modules']['chatgpt_api_type']) == 3: - conversation_file = os.path.join(CONVERSATION_DIR_OR_FILE, conversation_id + '.json') - chatbot_.load(conversation_file, conversation_id) + conversation_file = os.path.join(self.chats_dir, CONVERSATION_DIR_OR_FILE, conversation_id + '.json') + if os.path.exists(conversation_file): + chatbot_.load(conversation_file, conversation_id) + else: + logging.warning('File ' + conversation_file + ' not exists!') except Exception as e: logging.warning('Error loading conversation ' + conversation_id + ' ' + str(e)) @@ -181,7 +188,7 @@ def delete_conversation(self, conversation_id) -> None: if int(self.settings['modules']['chatgpt_api_type']) == 0: self.authenticator.chatbot.reset() try: - conversations_file = CONVERSATION_DIR_OR_FILE + '.json' + conversations_file = os.path.join(self.chats_dir, CONVERSATION_DIR_OR_FILE + '.json') self.authenticator.chatbot.conversations.remove_conversation(conversations_file) self.authenticator.chatbot.conversations.save(conversations_file) except Exception as e: @@ -213,8 +220,9 @@ def delete_conversation(self, conversation_id) -> None: # Delete conversation file if exists try: - conversation_file = os.path.join(CONVERSATION_DIR_OR_FILE, conversation_id + '.json') + conversation_file = os.path.join(self.chats_dir, CONVERSATION_DIR_OR_FILE, conversation_id + '.json') if os.path.exists(conversation_file): + logging.info('Removing ' + conversation_file + ' file') os.remove(conversation_file) except Exception as e: logging.error('Error removing conversation file for conversation ' + conversation_id, e) diff --git a/BotHandler.py b/BotHandler.py index 70d980d3..8b69f7d7 100644 --- a/BotHandler.py +++ b/BotHandler.py @@ -41,10 +41,9 @@ class BotHandler: - def __init__(self, settings, messages, chats_file, ai_handler): + def __init__(self, settings, messages, ai_handler): self.settings = settings self.messages = messages - self.chats_file = chats_file self.ai_handler = ai_handler self.application = None self.event_loop = None @@ -180,13 +179,12 @@ async def bot_command_clear(self, update: Update, context: ContextTypes.DEFAULT_ logging.info('/clear command from user ' + str(user.full_name) + ' request: ' + ' '.join(context.args)) # Delete conversation - if int(self.settings['modules']['chatgpt_api_type']) == 1: - conversation_id, _ = self.ai_handler.get_chat(chat_id) - if conversation_id is not None and len(conversation_id) > 0: - try: - self.ai_handler.delete_conversation(conversation_id) - except Exception as e: - logging.warning('Error removing conversation ' + str(conversation_id) + ' ' + str(e)) + conversation_id, _ = self.ai_handler.get_chat(chat_id) + if conversation_id is not None and len(conversation_id) > 0: + try: + self.ai_handler.delete_conversation(conversation_id) + except Exception as e: + logging.warning('Error removing conversation ' + str(conversation_id) + ' ' + str(e)) # Clear conversation ID and parent ID self.ai_handler.set_chat(chat_id, None, None) diff --git a/main.py b/main.py index 2827fdb7..8a1c9ef9 100644 --- a/main.py +++ b/main.py @@ -37,7 +37,7 @@ # Files and directories SETTINGS_FILE = 'settings.json' MESSAGES_FILE = 'messages.json' -CHATS_FILE = 'chats.json' +CHATS_DIR = 'chats' LOGS_DIR = 'logs' @@ -96,8 +96,8 @@ def parse_args(): default=os.getenv('TELEGRAMUS_SETTINGS_FILE', SETTINGS_FILE)) parser.add_argument('--messages', type=str, help='messages.json file location', default=os.getenv('TELEGRAMUS_MESSAGES_FILE', MESSAGES_FILE)) - parser.add_argument('--chats', type=str, help='chats.json file location', - default=os.getenv('TELEGRAMUS_CHATS_FILE', CHATS_FILE)) + parser.add_argument('--chats', type=str, help='chats directory location', + default=os.getenv('TELEGRAMUS_CHATS_DIR', CHATS_DIR)) parser.add_argument('--version', action='version', version=TELEGRAMUS_VERSION) return parser.parse_args() @@ -121,7 +121,7 @@ def main(): # Initialize classes authenticator = Authenticator.Authenticator(settings) ai_handler = AIHandler.AIHandler(settings, args.chats, authenticator) - bot_handler = BotHandler.BotHandler(settings, messages, args.chats, ai_handler) + bot_handler = BotHandler.BotHandler(settings, messages, ai_handler) # Set requests_queue to ai_handler ai_handler.requests_queue = bot_handler.requests_queue From f6b784f798353d80dd5c3f7bc284b1531f79ea9a Mon Sep 17 00:00:00 2001 From: F33RNI Date: Fri, 10 Mar 2023 08:12:40 +0300 Subject: [PATCH 3/4] README updates --- README.md | 116 +++++++++++++++++++++++++++++------------------------- 1 file changed, 62 insertions(+), 54 deletions(-) diff --git a/README.md b/README.md index 1f2ae6f9..071c86f7 100644 --- a/README.md +++ b/README.md @@ -15,9 +15,9 @@

- - - + /start command + Screenshot of request to ChatGPT + Screenshot of request to DALL-E

@@ -49,63 +49,67 @@ Support the project by buying and listening to my music 🎵 1. Install Python and pip 2. Download source code 3. Install requirements `pip install -r requirements.txt --upgrade` -4. For **API type 1** (recommended) (revChatGPT API V1, free, uses same model as official website): +4. For **API type 3** (recommended) (revChatGPT API V3, official chatGPT API): + 1. Generate API Key https://platform.openai.com/account/api-keys and paste it into `api_key` in `chatgpt_auth` in `settings.json` file + 2. Configure proxy if needed in `proxy` in `settings.json` file +5. For **API type 0** (revChatGPT API V0, Official API, more "stupid" model, uses credits): + 1. Generate API Key https://platform.openai.com/account/api-keys and paste it into `api_key` in `chatgpt_auth` in `settings.json` file + 2. Configure proxy if needed in `proxy` in `settings.json` file +6. For **API type 1** (revChatGPT API V1, free, uses same model as official website): 1. Create account at OpenAI. Make sure you have access to https://chat.openai.com/ 2. Open https://chat.openai.com/api/auth/session 3. Copy value of `accessToken` into `access_token` in `chatgpt_auth` in `chatgpt_api_1` in `settings.json` file - 5. Configure proxy if needed -5. For **API type 0** (Official API, more "stupid" model, uses credits): - 1. Generate API Key https://platform.openai.com/account/api-keys and paste it into `open_ai_api_key` in `chatgpt_api_0` in `settings.json` file -6. For DALL-E, generate API Key https://platform.openai.com/account/api-keys -7. Type Generated OpenAI API Key into `open_ai_api_key` in `dalle` in `settings.json` file -8. Create bot at https://t.me/BotFather -9. Type Bot's token into `api_key` in `telegram` in `settings.json` file -10. Run main script `python main.py` + 4. Configure proxy if needed in `proxy` in `settings.json` file + +7. For DALL-E, generate API Key https://platform.openai.com/account/api-keys +8. Type Generated OpenAI API Key into `open_ai_api_key` in `dalle` in `settings.json` file +9. Create bot at https://t.me/BotFather +10. Type Bot's token into `api_key` in `telegram` in `settings.json` file +11. Run main script `python main.py` Example `settings.json`: ```json { "modules": { "chatgpt": true, - "chatgpt_api_type": 1, + "chatgpt_api_type": 3, "dalle": true }, - - "chatgpt_api_0": { - "open_ai_api_key": "sk-2xxxxxxXXxXXXxxXXXxxXXXXXXXXXXXxxxxxxxxxxxxxxxXX", - "engine": "text-davinci-003", - "proxy": "" + + "chatgpt_auth": { + "api_key": "sk-XxxxxxxXXxXXXxxXXXxxXXXXXXXXXXXxxxxxxxxxxxxxxxXX", + + "engine": "", + + "email": "", + "password": "", + "session_token": "", + "access_token": "", + + "base_url": "" }, - - "chatgpt_api_1": { - "chatgpt_auth": { - "email": "", - "password": "", - "session_token": "", - "access_token": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.........." - }, - - "proxy": { - "enabled": true, - "auto": true, - "https_only": true, - "manual_proxy": "http://111.222.123.111:443", - "check_interval_seconds": 300, - "check_message": "1+1", - "check_message_timeout": 240, - "check_reply_must_include": "2", - "max_number_of_processes": 5, - "initialization_timeout": 60, - "too_many_requests_wait_time_seconds": 600 - } + + "proxy": { + "enabled": false, + "auto": true, + "manual_proxy": "", + "https_only": true, + "proxy_checks_enabled": true, + "check_interval_seconds": 600, + "check_message": "1+1", + "check_message_timeout": 240, + "check_reply_must_include": "2", + "max_number_of_processes": 5, + "initialization_timeout": 60, + "too_many_requests_wait_time_seconds": 600 }, - + "dalle": { - "open_ai_api_key": "sk-2xxxxxxXXxXXXxxXXXxxXXXXXXXXXXXxxxxxxxxxxxxxxxXX", + "open_ai_api_key": "sk-XxxxxxxXXxXXXxxXXXxxXXXXXXXXXXXxxxxxxxxxxxxxxxXX", "image_size": "512x512", "use_proxy": true }, - + "telegram": { "api_key": "1234567890:XXXxXxxXxxXXxxXXX-XXXXXXXxXxxxXxXxX", "queue_max": 5, @@ -155,24 +159,22 @@ Note: make shure you don't delete argumensts `{0}` in message and please restart ## Running in Docker -**WARNING: not tested and not recommended** +**WARNING: not tested** 1. Install Docker 2. Clone repo 3. Build container - ``` + ```shell docker buildx build -t telegramus --load -f Dockerfile . ``` 4. Run the container - ``` - docker run -d -e TELEGRAMUS_SETTINGS_FILE=you_settings_file_location -e TELEGRAMUS_MESSAGES_FILE=you_messages_file_location --name gpt-telegramus --restart on-failure telegramus + ```shell + docker run -d --name gpt-telegramus --restart on-failure telegramus ``` -**Note:** You can specify settings, messages and chats file location. (default location is in project folder): -```dockerfile -ENV TELEGRAMUS_SETTINGS_FILE "settings.json" -ENV TELEGRAMUS_MESSAGES_FILE "messages.json" -ENV TELEGRAMUS_MESSAGES_FILE "chats.json" +**Note:** You can specify settings and messages files and chats folder location. (default location is in project folder): +```shell +docker run -d -e TELEGRAMUS_SETTINGS_FILE="PATH_TO_settings.json" -e TELEGRAMUS_MESSAGES_FILE="PATH_TO_messages.json" -e TELEGRAMUS_CHATS_DIR="PATH_TO_chats_DIRECTORY" --name gpt-telegramus --restart on-failure telegramus ``` ---------- @@ -195,6 +197,7 @@ If you have proxy that definitely works you can specify it in `manual_proxy` in - `auto` - Download proxies automatically. Otherwise, use `manual_proxy` - `https_only` - Don't include http proxies in list - `manual_proxy` - Manual proxy server. It must support HTTPS, but you need to type it in `http://IP:PORT` format +- `proxy_checks_enabled` - Enables automatic periodic proxy check by asking 1+1 question - `check_interval_seconds` - Automatic connection check interval (in seconds) - `check_message` - This message will be sent as a request - `check_message_timeout` - How long should a response take? @@ -219,9 +222,14 @@ If you have proxy that definitely works you can specify it in `manual_proxy` in ## Chat history -File `chats.json` saves `conversation_id` and `parent_id` for each telegram chat to prevent history collision +Chat histories for each telegram chat (to prevent history collision) are stored in `chats` directory. You can specify it with env variable `TELEGRAMUS_CHATS_DIR` + +- Conversation IDs will be saved into `chats.json` inside `chats` directory. + - File `chats.json` saves `conversation_id` and `parent_id` (for API V1) +- For `"chatgpt_api_type": 3` conversation histories will be saved in `conversations` directory inside `chats` directory. +- For `"chatgpt_api_type": 0` conversation histories will be saved in `conversations.json` file inside `chats` directory. -It only works properly if `chatgpt_api_type` is set to `1` +*p.s. Might not work properly =)* ---------- From 27488e734701314cc559bef6942ad5478e077a4c Mon Sep 17 00:00:00 2001 From: F33RNI Date: Fri, 10 Mar 2023 08:23:36 +0300 Subject: [PATCH 4/4] More README updates --- README.md | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 071c86f7..f0c46308 100644 --- a/README.md +++ b/README.md @@ -50,17 +50,19 @@ Support the project by buying and listening to my music 🎵 2. Download source code 3. Install requirements `pip install -r requirements.txt --upgrade` 4. For **API type 3** (recommended) (revChatGPT API V3, official chatGPT API): - 1. Generate API Key https://platform.openai.com/account/api-keys and paste it into `api_key` in `chatgpt_auth` in `settings.json` file - 2. Configure proxy if needed in `proxy` in `settings.json` file + 1. Set `chatgpt_api_type` to `3` in `modules` in `settings.json` file + 2. Generate API Key https://platform.openai.com/account/api-keys and paste it into `api_key` in `chatgpt_auth` in `settings.json` file + 3. Configure proxy if needed in `proxy` in `settings.json` file 5. For **API type 0** (revChatGPT API V0, Official API, more "stupid" model, uses credits): - 1. Generate API Key https://platform.openai.com/account/api-keys and paste it into `api_key` in `chatgpt_auth` in `settings.json` file - 2. Configure proxy if needed in `proxy` in `settings.json` file + 1. Set `chatgpt_api_type` to `0` in `modules` in `settings.json` file + 2. Generate API Key https://platform.openai.com/account/api-keys and paste it into `api_key` in `chatgpt_auth` in `settings.json` file + 3. Configure proxy if needed in `proxy` in `settings.json` file 6. For **API type 1** (revChatGPT API V1, free, uses same model as official website): - 1. Create account at OpenAI. Make sure you have access to https://chat.openai.com/ - 2. Open https://chat.openai.com/api/auth/session - 3. Copy value of `accessToken` into `access_token` in `chatgpt_auth` in `chatgpt_api_1` in `settings.json` file - 4. Configure proxy if needed in `proxy` in `settings.json` file - + 1. Set `chatgpt_api_type` to `1` in `modules` in `settings.json` file + 2. Make sure you have access to https://chat.openai.com/ + 3. Open https://chat.openai.com/api/auth/session + 4. Copy value of `accessToken` into `access_token` in `chatgpt_auth` in `chatgpt_api_1` in `settings.json` file + 5. Configure proxy if needed in `proxy` in `settings.json` file 7. For DALL-E, generate API Key https://platform.openai.com/account/api-keys 8. Type Generated OpenAI API Key into `open_ai_api_key` in `dalle` in `settings.json` file 9. Create bot at https://t.me/BotFather @@ -229,6 +231,8 @@ Chat histories for each telegram chat (to prevent history collision) are stored - For `"chatgpt_api_type": 3` conversation histories will be saved in `conversations` directory inside `chats` directory. - For `"chatgpt_api_type": 0` conversation histories will be saved in `conversations.json` file inside `chats` directory. +To remove conversation use `/clear` command in telegram chat + *p.s. Might not work properly =)* ----------