diff --git a/.gitignore b/.gitignore index 3fcac9c..4a713f4 100644 --- a/.gitignore +++ b/.gitignore @@ -6,7 +6,7 @@ __pycache__/ *.log # Виртуальное окружение -# venv/ +.venv/ # env/ # IDE diff --git a/GPT_chat.code-workspace b/GPT_chat.code-workspace new file mode 100644 index 0000000..8dd0818 --- /dev/null +++ b/GPT_chat.code-workspace @@ -0,0 +1,13 @@ +{ + "folders": [ + { + "name": "GPT_chat", + "path": "." + }, + { + "name": "data", + "path": "../Free-GPT4-WEB-API/src/data" + } +], + "settings": {} +} diff --git a/data/cookies.json b/data/cookies.json new file mode 100644 index 0000000..1560bc1 --- /dev/null +++ b/data/cookies.json @@ -0,0 +1 @@ +{"field": "value"} \ No newline at end of file diff --git a/data/proxies.json b/data/proxies.json new file mode 100644 index 0000000..909cc00 --- /dev/null +++ b/data/proxies.json @@ -0,0 +1 @@ +[{"protocol": "socks5", "username": "usr", "password": "pass", "ip": "0.0.0.0", "port": "8080"}, {"protocol": "https", "username": "usr", "password": "pass", "ip": "0.0.0.1", "port": "443"}] \ No newline at end of file diff --git a/data/settings.db b/data/settings.db new file mode 100644 index 0000000..a075969 Binary files /dev/null and b/data/settings.db differ diff --git a/har_and_cookies/.nodriver_is_open b/har_and_cookies/.nodriver_is_open new file mode 100644 index 0000000..c0826ae --- /dev/null +++ b/har_and_cookies/.nodriver_is_open @@ -0,0 +1 @@ +1756455950.493793 \ No newline at end of file diff --git a/har_and_cookies/auth_Copilot.json b/har_and_cookies/auth_Copilot.json new file mode 100644 index 0000000..d8f9468 --- /dev/null +++ b/har_and_cookies/auth_Copilot.json @@ -0,0 +1 @@ +{"api_key": null, "cookies": {}} \ No newline at end of file diff --git a/har_and_cookies/auth_OpenaiChat.json b/har_and_cookies/auth_OpenaiChat.json new file mode 100644 index 0000000..542ae9c --- /dev/null +++ b/har_and_cookies/auth_OpenaiChat.json @@ -0,0 +1 @@ +{"api_key": null, "cookies": {"oai-sc": "0gAAAAABpbEtKmJIvZ_F3YOXS39ecZRVsrgMbuv8bTtyxVh3arzq5d7mOBGJsvHrpPFOukKLjVQyicK5SW_Af3_bIfJqoHBTtVBj4euUFy-3WO57tpejgw6zBdNysI0HEiSxT-6QZkIefAn0DjP4blmpWPKXK2ME7HRKKVKn2eKEhxSS5A8YAX93YdPP9HdaerMegBZI4BufWF7CGcNDDNvzSPbef7qWrnju-1Tygvc3jwMMDl38fpvw", "__Secure-next-auth.callback-url": "https%3A%2F%2Fchatgpt.com%2F", "__cflb": "0H28vzvP5FJafnkHxigzNzx1VHUBBoXPrHsSMD5CzAP", "cf_clearance": "3_zWdtXVy7ufElk0uu4wSj0saDo9B4uVt70wg4gg8ug-1768704824-1.2.1.1-eEq_M5epL8FLomcx_3L1KkoQR5mj1j.UKmb8hyuzqBX8QOrA_l1AuSgyC.3mwnRZM.ZmVgJffhT_Xz5djma0Gri38RSYq9i9v_CXj14qzjIHz4EXBosAfVoHYQTi4rl1uLN5_rING6Cm7v64TpMvLxqJd0R3Fi0hOpFx7c3od60sMlMDPwTyvJEOPW4xn1AJCsRVI6cavLI1RwvF6bS3JbDWCH.tzzKI0AerEcSygpg", "_dd_s": "aid=6db4d26f-2b95-420a-bce7-de0da89803df&rum=0&expire=1768705725278&logs=1&id=ea04dc8d-f365-42ff-92ac-5b1130eb021c&created=1768704825278", "__cf_bm": "rTETMFMJ7Xq.5M2k7yLgYQyFx88teBrejwY_DIYZqBA-1768704818-1.0.1.1-6gfHSDnI93_FAppbnb3AlR7OQH8oql_wX5eIQ6qO4dSQW2KmPj_cCfqjuJeYeG4GkFSfy8aPScIhwuBJh.HLOBNduxb5WHS.jp2RXICKyiQ", "_cfuvid": "UqEYVbbXtYY6BZT1g2Ry6WABj_grtROT55BSqdn9jWY-1768704818214-0.0.1.1-604800000", "oai-did": "cf3a6f3e-fe8b-4605-9a86-73f014dceb26", "oai-hm": "WHAT_ARE_YOU_WORKING_ON%20%7C%20GOOD_TO_SEE_YOU", "__Host-next-auth.csrf-token": "8c709bcbc4e9e04ec47adf47b4897593fd2a31db5b271d567fcacbfd2cf0554b%7Cebdd61b9c2b800b8de0bd2508c5aba7282434f07bb3a7d9c7b993e21a199f40c"}, "headers": {"upgrade-insecure-requests": "1", "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36 Edg/143.0.0.0", "sec-ch-ua": "\"Microsoft Edge\";v=\"143\", \"Chromium\";v=\"143\", \"Not A(Brand\";v=\"24\"", "sec-ch-ua-mobile": "?0", "sec-ch-ua-platform": "\"Windows\"", "cookie": "oai-sc=0gAAAAABpbEtKmJIvZ_F3YOXS39ecZRVsrgMbuv8bTtyxVh3arzq5d7mOBGJsvHrpPFOukKLjVQyicK5SW_Af3_bIfJqoHBTtVBj4euUFy-3WO57tpejgw6zBdNysI0HEiSxT-6QZkIefAn0DjP4blmpWPKXK2ME7HRKKVKn2eKEhxSS5A8YAX93YdPP9HdaerMegBZI4BufWF7CGcNDDNvzSPbef7qWrnju-1Tygvc3jwMMDl38fpvw; __Secure-next-auth.callback-url=https%3A%2F%2Fchatgpt.com%2F; __cflb=0H28vzvP5FJafnkHxigzNzx1VHUBBoXPrHsSMD5CzAP; cf_clearance=3_zWdtXVy7ufElk0uu4wSj0saDo9B4uVt70wg4gg8ug-1768704824-1.2.1.1-eEq_M5epL8FLomcx_3L1KkoQR5mj1j.UKmb8hyuzqBX8QOrA_l1AuSgyC.3mwnRZM.ZmVgJffhT_Xz5djma0Gri38RSYq9i9v_CXj14qzjIHz4EXBosAfVoHYQTi4rl1uLN5_rING6Cm7v64TpMvLxqJd0R3Fi0hOpFx7c3od60sMlMDPwTyvJEOPW4xn1AJCsRVI6cavLI1RwvF6bS3JbDWCH.tzzKI0AerEcSygpg; _dd_s=aid=6db4d26f-2b95-420a-bce7-de0da89803df&rum=0&expire=1768705725278&logs=1&id=ea04dc8d-f365-42ff-92ac-5b1130eb021c&created=1768704825278; __cf_bm=rTETMFMJ7Xq.5M2k7yLgYQyFx88teBrejwY_DIYZqBA-1768704818-1.0.1.1-6gfHSDnI93_FAppbnb3AlR7OQH8oql_wX5eIQ6qO4dSQW2KmPj_cCfqjuJeYeG4GkFSfy8aPScIhwuBJh.HLOBNduxb5WHS.jp2RXICKyiQ; _cfuvid=UqEYVbbXtYY6BZT1g2Ry6WABj_grtROT55BSqdn9jWY-1768704818214-0.0.1.1-604800000; oai-did=cf3a6f3e-fe8b-4605-9a86-73f014dceb26; oai-hm=WHAT_ARE_YOU_WORKING_ON%20%7C%20GOOD_TO_SEE_YOU; __Host-next-auth.csrf-token=8c709bcbc4e9e04ec47adf47b4897593fd2a31db5b271d567fcacbfd2cf0554b%7Cebdd61b9c2b800b8de0bd2508c5aba7282434f07bb3a7d9c7b993e21a199f40c"}, "expires": null, "proof_token": [3000, "Sun Jan 18 2026 12:53:47 GMT+1000 (\u0412\u043b\u0430\u0434\u0438\u0432\u043e\u0441\u0442\u043e\u043a, \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u043e\u0435 \u0432\u0440\u0435\u043c\u044f)", 2248146944, 18, "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36 Edg/143.0.0.0", null, "prod-db9ecebcc798846f5535282d71389bd2e15c025b", "ru", "ru,en,en-GB,en-US", 7, "webkitGetUserMedia\u2212function webkitGetUserMedia() { [native code] }", "_reactListening56vcyb4zlwt", "oncuechange", 6511, "2d53e54b-ac85-45d9-b136-7853488774d2", "", 4, 1768704820850.8], "turnstile_token": "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"} \ No newline at end of file diff --git a/main.py b/main.py new file mode 100644 index 0000000..572eb9a --- /dev/null +++ b/main.py @@ -0,0 +1,564 @@ +import re +import argparse +import json +import os +import random +import string +import sqlite3 +from uuid import uuid4 +import threading + +# GPT Library +import g4f +from g4f.api import run_api + +# Server +from flask import Flask, redirect, render_template +from flask import request +import getpass +from werkzeug.security import generate_password_hash, check_password_hash +from werkzeug.utils import secure_filename + +app = Flask(__name__) + +UPLOAD_FOLDER = 'data/' + +app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER +app.config['MAX_CONTENT_LENGTH'] = 16 * 1000 * 1000 # 16 MB + +# Settings file path +SETTINGS_FILE = "./data/settings.db" +PROXIES_FILE = "./data/proxies.json" + +# Available providers +PROVIDERS = { + "Auto": "", + # "Acytoo": g4f.Provider.Acytoo, + # "Aichat": g4f.Provider.Aichat, + # "Ails": g4f.Provider.Ails, + # "BlackBox": g4f.Provider.Blackbox, + # "Chatgpt4o": g4f.Provider.Chatgpt4o, + # "ChatGpt": g4f.Provider.ChatGpt, + # "ChatGptt" : g4f.Provider.ChatGptt, + # "DeepInfraChat": g4f.Provider.DeepInfraChat, + # "Glider": g4f.Provider.Glider, + # "H2o": g4f.Provider.H2o, + # "HuggingChat": g4f.Provider.HuggingChat, + # "Opchatgpts": g4f.Provider.Opchatgpts, + # "OpenAssistant": g4f.Provider.OpenAssistant, + # "OpenaiChat": g4f.Provider.OpenaiChat, + # "Raycast": g4f.Provider.Raycast, + # "Theb": g4f.Provider.Theb, + # "Wewordle": g4f.Provider.Wewordle, + # "You": g4f.Provider.You, + # "Yqcloud": g4f.Provider.Yqcloud, + # "Pizzagpt": g4f.Provider.Pizzagpt, + # "HuggingChat": g4f.Provider.HuggingChat, + # "HuggingFace": g4f.Provider.HuggingFace + +} + +GENERIC_MODELS = ["gpt-3.5-turbo", "gpt-4", "gpt-4o","gpt-4o-mini"] + + +print( + """ + FreeGPT4 Web API - A Web API for GPT-4 + Repo: github.com/aledipa/FreeGPT4-WEB-API + By: Alessandro Di Pasquale + + GPT4Free Credits: github.com/xtekky/gpt4free + """, +) +parser = argparse.ArgumentParser() +parser.add_argument( + "--remove-sources", + action='store_true', + required=False, + help="Remove the sources from the response", +) +parser.add_argument( + "--enable-gui", + action='store_true', + required=False, + help="Use a graphical interface for settings", +) +parser.add_argument( + "--private-mode", + action='store_true', + required=False, + help="Use a private token to access the API", +) +parser.add_argument( + "--enable-proxies", + action='store_true', + required=False, + help="Use one or more proxies to avoid being blocked or banned", +) +parser.add_argument( + "--enable-history", + action='store_true', + required=False, + help="Enable the history of the messages", +) +parser.add_argument( + "--password", + action='store', + required=False, + help="Set or change the password for the settings page [mandatory in docker envirtonment]", +) +parser.add_argument( + "--cookie-file", + action='store', + required=False, + type=str, + help="Use a cookie file", +) +parser.add_argument( + "--file-input", + action='store_true', + required=False, + help="Add the file as input support", +) +parser.add_argument( + "--port", + action='store', + required=False, + type=str, + help="Change the port (default: 5500)", +) +parser.add_argument( + "--model", + action='store', + required=False, + type=str, + help="Change the model (default: gpt_4)", +) +parser.add_argument( + "--provider", + action='store', + required=False, + type=str, + help="Change the provider (default: Bing)", +) +parser.add_argument( + "--keyword", + action='store', + required=False, + type=str, + help="Add the keyword support", +) +parser.add_argument( + "--system-prompt", + action='store', + required=False, + type=str, + help="Use a system prompt to 'customize' the answers", +) +parser.add_argument( + "--enable-fast-api", + action='store_true', + required=False, + help="Use the fast API standard (PORT 1337 - compatible with OpenAI integrations)", +) + +args, unknown = parser.parse_known_args() + +# Get the absolute path of the script +script_path = os.path.abspath(__file__) +# Get the directory of the script +script_dir = os.path.dirname(script_path) +# Change the current working directory +os.chdir(script_dir) + +# Loads the proxies from the file +if (args.enable_proxies and os.path.exists(PROXIES_FILE)): + proxies = json.load(open(PROXIES_FILE)) +else: + proxies = None + +# Crates the Fast API Thread +t = threading.Thread(target=run_api, name="fastapi") + +# Loads the settings from the file +def load_settings(file=SETTINGS_FILE): + conn = sqlite3.connect(file) + c = conn.cursor() + c.execute("SELECT * FROM settings") + data = c.fetchone() + conn.close() + # Converts the data to a dictionary + data = { + "keyword": data[1], + "file_input": data[2], + "port": data[3], + "provider": data[4], + "model": data[5], + "cookie_file": data[6], + "token": data[7], + "remove_sources": data[8], + "system_prompt": data[9], + "message_history": data[10], + "proxies": data[11], + "password": data[12], + "fast_api": data[13] + } + print(data) + return data + +def start_fast_api(): + print("[!] FAST API PORT: 1336") + t.daemon = True + t.start() + +# Updates the settings +data = load_settings() +if (args.keyword == None): + args.keyword = data["keyword"] + +if (args.file_input == False): + args.file_input = data["file_input"] + +if (args.port == None): + args.port = data["port"] + +if (args.provider == None): + args.provider = data["provider"] + +if (args.model == None): + args.model = data["model"] + +if (args.cookie_file == None): + args.cookie_file = data["cookie_file"] + +if (args.private_mode and data["token"] == ""): + token = str(uuid4()) + data["token"] = token +elif (data["token"] != ""): + token = data["token"] + args.private_mode = True + +if (args.remove_sources == False): + args.remove_sources = data["remove_sources"] + +if (args.system_prompt == None): + args.system_prompt = data["system_prompt"] + +if (args.enable_history == False): + args.enable_history = data["message_history"] + +if (args.enable_proxies == False): + args.enable_proxies = data["proxies"] + +if (args.enable_fast_api or data["fast_api"]): + start_fast_api() + + +# Initializes the message history +message_history = [{"role": "system", "content": args.system_prompt}] + +if (args.enable_gui): + # Asks for password to set to lock the settings page + # Checks if settings.db contains a password + try: + set_password = True + if (args.password != None): + password = args.password + confirm_password = password + else: + if (data["password"] == ""): + password = getpass.getpass("Settings page password:\n > ") + confirm_password = getpass.getpass("Confirm password:\n > ") + else: + set_password = False + + if (set_password): + if(password == "" or confirm_password == ""): + print("[X] Password cannot be empty") + exit() + + if ((password != confirm_password) and (data["password"] == "")): + print("[X] Passwords don't match") + exit() + else: + password = generate_password_hash(password) + confirm_password = generate_password_hash(confirm_password) + print("[V] Password set.") + try: + conn = sqlite3.connect(SETTINGS_FILE) + c = conn.cursor() + c.execute("UPDATE settings SET password = ?", (password,)) + conn.commit() + conn.close() + print("[V] Password saved.") + except Exception as error: + print("[X] Error:", error) + exit() + except Exception as error: + print("[X] Error:", error) + exit() +else: + print("[!] GUI disabled") + +# Saves the settings to the file +def save_settings(request, file): + conn = sqlite3.connect(file) + c = conn.cursor() + c.execute("UPDATE settings SET file_input = ?", (bool(request.form["file_input"] == "true"),)) + c.execute("UPDATE settings SET remove_sources = ?", (bool(request.form["remove_sources"] == "true"),)) + c.execute("UPDATE settings SET port = ?", (request.form["port"],)) + c.execute("UPDATE settings SET model = ?", (request.form["model"],)) + c.execute("UPDATE settings SET keyword = ?", (request.form["keyword"],)) + c.execute("UPDATE settings SET provider = ?", (request.form["provider"],)) + c.execute("UPDATE settings SET system_prompt = ?", (request.form["system_prompt"],)) + c.execute("UPDATE settings SET message_history = ?", (bool(request.form["message_history"] == "true"),)) + c.execute("UPDATE settings SET proxies = ?", (bool(request.form["proxies"] == "true"),)) + c.execute("UPDATE settings SET fast_api = ?", (bool(request.form["fast_api"] == "true"),)) + if (len(request.form["new_password"]) > 0): + c.execute("UPDATE settings SET password = ?", (generate_password_hash(request.form["new_password"]),)) + + file = request.files["cookie_file"] + if (bool(request.form["private_mode"] == "true")): + c.execute("UPDATE settings SET token = ?", (request.form["token"],)) + args.private_mode = True + else: + c.execute("UPDATE settings SET token = ''") + args.private_mode = False + #checks if the file is not empty + if file.filename != '': + #checks if the file is a json file + if file.filename.endswith('.json'): + #saves the file + filename = secure_filename(file.filename) + file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename)) + #updates the cookie_file + c.execute("UPDATE settings SET cookie_file = ?", (os.path.join(app.config['UPLOAD_FOLDER'], filename),)) + else: + print("The file is not a json file") + + if (args.enable_proxies or bool(request.form["proxies"] == "true")): + # Extracts the proxies from the form + proxies = [] + i = 1 + while True: + if (("proxy_" + str(i)) in request.form): + proxy = request.form["proxy_" + str(i)] + if (proxy != ""): + # Checks if the proxy syntax is correct + if (proxy.count(":") == 3 and proxy.count("@") == 1): + proxy = { + "protocol": proxy.split("://")[0], + "username": proxy.split("://")[1].split(":")[0], + "password": proxy.split("://")[1].split(":")[1].split("@")[0], + "ip": proxy.split("://")[1].split(":")[1].split("@")[1], + "port": proxy.split("://")[1].split(":")[2] + } + proxies.append(proxy) + i += 1 + else: + break + + # Saves the proxies to the file proxies.json + with open(PROXIES_FILE, "w") as pf: + json.dump(proxies, pf) + pf.close() + + if (bool(request.form["fast_api"] == "true") and not args.enable_fast_api): + start_fast_api() + + conn.commit() + conn.close() + return + +# Loads the settings from the file and updates the args +def apply_settings(file): + data = load_settings(file) + args.keyword = data["keyword"] + args.file_input = data["file_input"] + args.port = data["port"] + args.provider = data["provider"] + args.model = data["model"] + args.cookie_file = data["cookie_file"] + args.token = data["token"] + args.remove_sources = data["remove_sources"] + args.system_prompt = data["system_prompt"] + args.enable_history = data["message_history"] + args.enable_proxies = data["proxies"] + args.password = data["password"] + args.enable_fast_api = data["fast_api"] + return + + +@app.route("/", methods=["GET", "POST"]) +async def index() -> str: + """ + Main function + """ + + # Starts the bot and gets the input + print("Initializing...") + question = None + + print("start") + if request.method == "GET": + question = request.args.get(args.keyword) #text + print(args.private_mode) + if (args.private_mode and request.args.get("token") != data["token"]): + return "
Invalid token
" + print("get") + else: + file = request.files["file"] + text = file.read().decode("utf-8") + question = text + print("Post reading the file", question) + + print("ici") + if question is None: + return "Please enter a question
" + + # Gets the response from the bot + # print(PROVIDERS[args.provider].params) # supported args + print("\nCookies: " + str(len(args.cookie_file))) + print("\nInput: " + question) + if (len(args.cookie_file) != 0): + try: + cookies = json.load(open(args.cookie_file)) # Loads the cookies from the file + print("COOKIES: "+str(cookies)) + if (len(cookies) == 0): + cookies = {"a": "b"} # Dummy cookies + except Exception as error: + print("[X] Error:", error) + exit() + else: + cookies = {"a": "b"} # Dummy cookies + + + if (args.enable_history): + print("History enabled") + message_history.append({"role": "user", "content": question}) + else: + print("History disabled") + message_history.clear() + message_history.append({"role": "system", "content": args.system_prompt}) + message_history.append({"role": "user", "content": question}) + + proxy = None + if (args.enable_proxies): + # Extracts a proxy from the list + proxy = random.choice(proxies) + # Formats the proxy like https://user:password@host:port + proxy = f"{proxy['protocol']}://{proxy['username']}:{proxy['password']}@{proxy['ip']}:{proxy['port']}" + print("Proxy: " + proxy) + + if (args.provider == "Auto"): + response = ( + await g4f.ChatCompletion.create_async( + model=args.model, + messages=message_history, + cookies=cookies, + proxy=proxy + ) + ) + else: + response = ( + await g4f.ChatCompletion.create_async( + model=args.model, + provider=args.provider, + messages=message_history, + cookies=cookies, + proxy=proxy + ) + ) + + print(response) + + #Joins the response into a single string + resp_str = "" + for message in response: + resp_str += message + + # Cleans the response from the resources links + if (args.remove_sources): + if re.search(r"\[\^[0-9]+\^\]\[[0-9]+\]", resp_str): + resp_str = resp_str.split("\n\n") + if len(resp_str) > 1: + resp_str.pop(0) + resp_str = re.sub(r"\[\^[0-9]+\^\]\[[0-9]+\]", "", str(resp_str[0])) + # Returns the response + return resp_str + # return "" + resp + "
" # Uncomment if preferred + +def auth(): + # Reads the password from the data file + data = load_settings() + # Checks if the password is set + if (data["password"] != ""): + if (request.method == "POST"): + password = request.form["password"] + if (check_password_hash(data["password"], password)): + return True + else: + return False + else: + return False + else: + return True + +@app.route("/login", methods=["GET", "POST"]) +async def login(): + if (args.enable_gui): + return render_template("login.html", **locals()) + else: + return "The GUI is disabled. In order to enable it, use the --enable-gui argument." + +@app.route("/settings", methods=["GET", "POST"]) +async def settings(): + if (request.method == "GET"): + return redirect("/login", code=302) + if (auth()): + try: + providers=PROVIDERS + generic_models=GENERIC_MODELS + data = load_settings() + proxies = json.loads(open(PROXIES_FILE).read()) + return render_template("settings.html", **locals()) + except Exception as error: + print("[X] Error:", error) + return "Error: " + str(error) + else: + return render_template("login.html", **locals()) + +@app.route("/save", methods=["POST"]) +async def save(): + if (auth()): + try: + if (request.method == "POST"): + save_settings(request, SETTINGS_FILE) + apply_settings(SETTINGS_FILE) + + return "New settings saved and applied successfully!" + else: + return render_template("login.html", **locals()) + except Exception as error: + print("[X] Error:", error) + return "Error: " + str(error) + else: + return render_template("login.html", **locals()) + +@app.route("/models", methods=["GET"]) +async def get_models(): + provider = request.args.get("provider") + if (provider == "Auto"): + return GENERIC_MODELS + try: + return PROVIDERS[provider].models + except: + return ["default"] + +@app.route("/generatetoken", methods=["GET", "POST"]) +async def get_token(): + return str(uuid4()) + +if __name__ == "__main__": +# # Starts the server, change the port if needed + app.run("0.0.0.0", port=args.port, debug=False) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..eaac9b9 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,13 @@ +curl_cffi +aiohttp +aiohttp_socks +auth +Flask[async] +g4f +Werkzeug>=3.0.3 +aiohttp_socks +nodriver +# pysqlite3 +python-multipart +uvicorn +fastapi \ No newline at end of file diff --git a/static/css/style.css b/static/css/style.css new file mode 100644 index 0000000..3cd5a16 --- /dev/null +++ b/static/css/style.css @@ -0,0 +1,73 @@ +@import url("https://fonts.googleapis.com/css2?family=Inter:wght@100;200;300;400;500;600;700;800;900&display=swap"); +/* font-family: 'Inter', sans-serif; */ + +body { + margin: 0; + padding: 0; + font-family: "Inter", sans-serif; + background-color: #e4f2f4; +} + +input::placeholder { + font-style: italic; +} + +textarea::placeholder { + font-style: italic; +} + +.inter { + font-family: "Inter", sans-serif; +} + +.darkblue { + color: #034953; +} + +.blue { + color: #007080; +} + +.label_gray { + color: #c8d9de; +} + +.label_green { + color: #035306; +} + +.label_red { + color: #8b0000; +} + +.border_red { + border-color: #8b0000; +} + +.label_darkorange { + color: #8b7b00; +} + +.darkblue_bg { + background-color: #034953; +} + +.darkgreen_bg { + background-color: #035306; +} + +.darkred_bg { + background-color: #8b0000; +} + +.blue_bg { + background-color: #007080; +} + +.lightpink_bg { + background-color: #fff2f2; +} + +.fileholder { + background-color: white !important; +} diff --git a/static/img/copy(Gregor_Cresnar).png b/static/img/copy(Gregor_Cresnar).png new file mode 100644 index 0000000..a9368a6 Binary files /dev/null and b/static/img/copy(Gregor_Cresnar).png differ diff --git a/static/img/delete(Anggara).png b/static/img/delete(Anggara).png new file mode 100644 index 0000000..019c64e Binary files /dev/null and b/static/img/delete(Anggara).png differ diff --git a/static/img/favicon(Nicoladipa).png b/static/img/favicon(Nicoladipa).png new file mode 100644 index 0000000..6ed5488 Binary files /dev/null and b/static/img/favicon(Nicoladipa).png differ diff --git a/static/js/script.js b/static/js/script.js new file mode 100644 index 0000000..4577d64 --- /dev/null +++ b/static/js/script.js @@ -0,0 +1,252 @@ +// Turns on the button +function turnOn(idOn, idOff, set) { + document.getElementById(idOff).style.color = "#C8D9DE"; + document.getElementById(idOn).style.color = "#035306"; + document.getElementById(set).style.color = "#035306"; +} + +// Turns off the button +function turnOff(idOn, idOff, set) { + document.getElementById(idOff).style.color = "#8B0000"; + document.getElementById(idOn).style.color = "#C8D9DE"; + document.getElementById(set).style.color = "#8B0000"; +} + +// Changes the button text and color +function changeButton(buttonID) { + document.getElementById(buttonID).style.backgroundColor = "#035306"; + document.getElementById(buttonID).textContent = "Change"; +} + +// Hides an element and shows another +function replaceElement(idToHide, idToShow) { + document.getElementById(idToHide).hidden = true; + document.getElementById(idToShow).hidden = false; +} + +// Shows an element +function showElement(idToShow) { + element = document.getElementById(idToShow); + element.hidden = false; +} + +// Hides an element +function hideElement(idToHide) { + element = document.getElementById(idToHide); + element.hidden = true; +} + +// Hides a composed warning +function hideWarning(idToHide) { + document.getElementById(idToHide).classList.add("hidden"); +} + +// Shows a composed warning +function showWarning(idToShow) { + document.getElementById(idToShow).classList.remove("hidden"); +} + +// Copies the text in the given id to the clipboard +function copyTextToClipboardFromName(name) { + var copyText = document.getElementsByName(name)[0].value; + navigator.clipboard.writeText(copyText); +} + +// Replaces the element in the given ID with the given text +function replaceElementWithText(id, text) { + var element = document.getElementById(id); + var originalElement = element; + // Creates a new element + var newElement = document.createElement("p"); + // Adds some content to the new element + newElement.textContent = text; + newElement.classList.add("darkblue"); + newElement.classList.add("inline-block"); + newElement.id = id; + // Replaces the old element with the new one + element.parentNode.replaceChild(newElement, element); + + return originalElement; +} + +function copy(name, id, text) { + copyTextToClipboardFromName(name); + var oldElement = replaceElementWithText(id, text); + setTimeout( + (param) => { + var textElement = document.getElementById(id); + textElement.parentNode.replaceChild(param, textElement); + }, + 2000, + oldElement, + ); +} + +function createToken(targetID) { + $.ajax({ + url: "/generatetoken", + data: {}, + success: function (response) { + var tokenText = document.getElementById(targetID).innerText; + if (tokenText.length == 0) { + document.getElementById(targetID).innerText = response; + document.getElementById("token").value = response; + } + }, + }); +} + +function addProxy() { + // Gets the number of proxy input fields in 'proxy_list_div' + var proxyListDiv = document.getElementById("proxy_list_div"); + var proxyList = proxyListDiv.getElementsByTagName("input"); + + // Checks if there's already an empty proxy input field + for (var i = 0; i < proxyList.length; i++) { + if (proxyList[i].value.length == 0) { + return; + } + } + + // adds a new proxy input field + var newProxyDiv = document.createElement("div"); + newProxyDiv.classList.add("flex", "justify-start", "mb-3"); + + var newProxy = document.createElement("input"); + newProxy.type = "text"; + newProxy.name = "proxy_" + (proxyList.length + 1); + newProxy.id = newProxy.name; + newProxy.classList.add( + "input", + "outline-none", + "py-1", + "px-2", + "rounded-lg", + "inter", + "w-full", + ); + newProxy.placeholder = "protocol://user:password@host:port"; + newProxy.setAttribute("onchange", "checkAllProxies()"); + newProxyDiv.appendChild(newProxy); + + // adds the 'delete' image button to the new proxy input field + var deleteButton = document.createElement("img"); + deleteButton.src = "static/img/delete(Anggara).png"; + deleteButton.classList.add( + "inline-block", + "mx-2", + "p-1", + "hover:brightness-125", + ); + deleteButton.width = 32; + deleteButton.onclick = function () { + newProxyDiv.remove(); + }; + newProxyDiv.appendChild(deleteButton); + + document.getElementById("proxy_list_div").appendChild(newProxyDiv); +} + +function deleteElement(id) { + document.getElementById(id).remove(); +} + +// Checks if the proxy syntax is correct +function checkProxySyntax(proxy) { + var proxyRegex = + /^((http|https|socks4|socks5):\/\/)?([a-zA-Z0-9]+:[a-zA-Z0-9]+@)?([a-zA-Z0-9.-]+):([0-9]+)$/; + return proxyRegex.test(proxy); +} + +// Warns the user if the proxy syntax is incorrect +function warnProxySyntax(proxy, proxyID) { + if (checkProxySyntax(proxy)) { + document + .getElementById(proxyID) + .classList.remove("border_red", "label_red", "lightpink_bg"); + } else { + document + .getElementById(proxyID) + .classList.add("border_red", "border", "label_red", "lightpink_bg"); + } +} + +// Checks the syntax at every proxy input field at input change +function checkAllProxies() { + var proxyListDiv = document.getElementById("proxy_list_div"); + var proxyList = proxyListDiv.getElementsByTagName("input"); + for (var i = 0; i < proxyList.length; i++) { + warnProxySyntax(proxyList[i].value, proxyList[i].id); + } +} + +// Checks if new password and confirm password match +function checkPasswordMatch() { + var newPassword = document.getElementById("new_password").value; + var confirmPassword = document.getElementById("confirm_password").value; + if (newPassword == confirmPassword) { + if (newPassword.length > 0) { + replaceElement("error_password", "success_password"); + return true; + } + } else { + replaceElement("success_password", "error_password"); + } + return false; +} + +// Opens the update password form +function openPasswordUpdate() { + replaceElement("open_password_update", "cancel_password_update"); + showElement("password_update"); +} + +// Closes the update password form +function cancelPasswordUpdate() { + hideElement("password_update"); + hideElement("success_password"); + hideElement("error_password"); + replaceElement("cancel_password_update", "open_password_update"); + document.getElementById("new_password").value = ""; + document.getElementById("confirm_password").value = ""; +} + +// Enables the save button if the password is correct +function enableSaveButton() { + var pass_length = document.getElementById("password").value.length; + var isPasswordUpdateClosed = document.getElementById("password_update").hidden; + if ((checkPasswordMatch() || isPasswordUpdateClosed) && pass_length > 0) { + replaceElement('save_label_dummy', 'save_label'); + } else { + replaceElement('save_label', 'save_label_dummy'); + } +} + +// Gets available models for choosen A.I. Provider +$(document).ready(function () { + $("#provider").change(function () { + var inputValue = $(this).val(); + $.ajax({ + url: "/models", + data: { + provider: inputValue, + }, + success: function (response) { + var select = document.getElementById("model"); + + // Remove existing models + while (select.firstChild) { + select.removeChild(select.firstChild); + } + + // Add the new models + for (var i = 0; i < response.length; i++) { + var option = document.createElement("option"); + option.innerText = response[i]; + option.value = response[i]; + select.add(option); + } + }, + }); + }); +}); diff --git a/templates/login.html b/templates/login.html new file mode 100644 index 0000000..5cf6f6a --- /dev/null +++ b/templates/login.html @@ -0,0 +1,49 @@ + + + + + + + + + + + + +|
+ |
+
+ FreeGPT4+Server settings+ |
+
|
+ |
+
+ FreeGPT4+Server settings+ |
+