├── .dockerignore ├── requirements.txt ├── Dockerfile ├── main.py ├── .github └── workflows │ └── docker-image.yml ├── templates └── chat.html ├── static ├── script.js └── style.css └── README.md /.dockerignore: -------------------------------------------------------------------------------- 1 | .git 2 | Dockerfile 3 | README.md 4 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | revChatGPT 2 | flask 3 | gunicorn 4 | gevent 5 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:slim 2 | WORKDIR /chatgpt-html 3 | COPY . . 4 | RUN pip install --no-cache-dir -r requirements.txt 5 | 6 | CMD ["gunicorn", "-b", "0.0.0.0:8088", "main:server", "--timeout", "200", "--worker-class", "gevent"] 7 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import json 2 | from revChatGPT.V1 import Chatbot 3 | from flask import Flask, request, render_template 4 | 5 | server = Flask(__name__) 6 | 7 | # get config 8 | with open("config.json", "r") as f: config = json.load(f) 9 | 10 | # init chatbot 11 | chatbot = Chatbot(config) 12 | 13 | def generate_response(prompt): 14 | try: 15 | for data in chatbot.ask(prompt): 16 | response = data["message"] 17 | return response 18 | except BaseException as e: 19 | return str(e) 20 | 21 | @server.route("/") 22 | def home(): 23 | chatbot.reset_chat() 24 | return render_template("chat.html") 25 | 26 | @server.route("/get") 27 | def get_bot_response(): 28 | user_text = request.args.get('msg') 29 | return str(generate_response(user_text)) 30 | 31 | if __name__ == '__main__': 32 | server.run(debug=False, host='0.0.0.0', port=8088) 33 | -------------------------------------------------------------------------------- /.github/workflows/docker-image.yml: -------------------------------------------------------------------------------- 1 | name: ci 2 | 3 | on: 4 | push: 5 | branches: 6 | - "main" 7 | 8 | jobs: 9 | docker: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - 13 | name: Checkout 14 | uses: actions/checkout@v3 15 | - 16 | name: Set up QEMU 17 | uses: docker/setup-qemu-action@v2 18 | - 19 | name: Set up Docker Buildx 20 | uses: docker/setup-buildx-action@v2 21 | - 22 | name: Login to Docker Hub 23 | uses: docker/login-action@v2 24 | with: 25 | username: ${{ secrets.DOCKERHUB_USERNAME }} 26 | password: ${{ secrets.DOCKERHUB_TOKEN }} 27 | - 28 | name: Build and push 29 | uses: docker/build-push-action@v4 30 | with: 31 | context: . 32 | platforms: linux/amd64, linux/arm64 33 | push: true 34 | tags: sheepgreen/chatgpt-html:latest 35 | -------------------------------------------------------------------------------- /templates/chat.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | ChatGPT 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 |
18 |
19 |
ChatGPT
20 |
21 | 26 |
27 | 28 | 29 |
30 |
31 |
32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /static/script.js: -------------------------------------------------------------------------------- 1 | // 加入回车提交支持,shift+回车换行 2 | var input = document.getElementById("chatinput"); 3 | input.addEventListener("keydown", function (event) { 4 | if (event.keyCode === 13 && !event.shiftKey) { 5 | event.preventDefault(); 6 | document.getElementById("sendbutton").click(); 7 | } 8 | }); 9 | 10 | // Add your JavaScript here 11 | document.getElementById("sendbutton").addEventListener("click", function () { 12 | // Get the user's message from the input field 13 | var message = document.getElementById("chatinput").value; 14 | var chatlog = document.getElementById("chatlog"); 15 | var response = document.createElement("div"); 16 | if (message.length < 1) { 17 | response.innerHTML = "🤔
🤖
Message cannot be null\n问题不能为空"; 18 | // 给response添加一个动画类 19 | response.classList.add("animate__animated", "animate__lightSpeedInLeft", "dark"); 20 | chatlog.appendChild(response); 21 | response.scrollIntoView({ behavior: 'smooth', block: 'end' }); 22 | } else { 23 | // Clear the input field 24 | document.getElementById("chatinput").value = ""; 25 | // Send the message to the chatbot 26 | var xhr = new XMLHttpRequest(); 27 | xhr.open("GET", "/get?msg=" + message); 28 | xhr.send(); 29 | // Display "typing" message while the bot is thinking 30 | var typingMessage = document.createElement("div"); 31 | // 新增一个小圆点元素,添加typing类 32 | var dot = document.createElement("div"); 33 | dot.classList.add("typing"); 34 | typingMessage.appendChild(dot); 35 | chatlog.appendChild(typingMessage); 36 | typingMessage.scrollIntoView({ behavior: 'smooth', block: 'end' }); 37 | xhr.onload = function () { 38 | // Append the chatbot's response to the chatlog 39 | chatlog.removeChild(typingMessage); 40 | response.innerHTML = "🤔
" + message + "
🤖" + marked.parse(xhr.responseText); 41 | // 给response添加一个动画类 42 | response.classList.add("animate__animated", "animate__lightSpeedInLeft", "dark"); 43 | chatlog.appendChild(response); 44 | response.scrollIntoView({ behavior: 'smooth', block: 'end' }); 45 | } 46 | } 47 | }); 48 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # chatgpt-html 2 | ### 使用[acheong08](https://github.com/acheong08/ChatGPT)对接官方ChatGPT接口,实现简单HTML网页版在线聊天 3 | > 该版本基于`ChatGPT`网页端代理开发(免费),想使用ChatGPT API KEY(付费)的请访问[chatgpt-web](https://github.com/slippersheepig/chatgpt-web) 4 | ### 注意 5 | > 使用该项目的前提是你的服务器IP没有被BAN,由于我自己的服务器IP已BAN,无法测试项目是否可用,请自行尝试 6 | ## 特性 7 | - 文件结构简单,主要面向小白用户 8 | - 功能不多,但核心的连续对话、多用户会话隔离、markdown格式输出都具备 9 | ## 部署 10 | #### 使用Docker Compose 11 | > 以下所有文件放同一目录 12 | - 新建`config.json`文件,粘贴以下代码并保存 13 | ```bash 14 | { 15 | "__comment01__": "邮箱、session_token和access_token三选一", 16 | "__comment02__": "邮箱认证(暂仅支持普通方式注册的账号,不支持谷歌或微软快捷登录)", 17 | "email": "", 18 | "password": "", 19 | "__comment03__": "session_token认证(不受账号注册方式影响)", 20 | "session_token": "", 21 | "__comment04__": "access_token认证(不受账号注册方式影响)", 22 | "access_token": "", 23 | 24 | "__comment05__": "以下为选填字段", 25 | "__comment06__": "通过代理连接代理端(作者服务器被墙过,代理好像只能用无密码认证的socks5或者http,请自行测试)", 26 | "proxy": "", 27 | "__comment07__": "使用付费openai账号(官方称速度更快,无频率限制,将false改为true)", 28 | "paid": false 29 | } 30 | ``` 31 | - session_token获取方法(随时过期) 32 | 1. Go to https://chat.openai.com/chat and open the developer tools by `F12`. 33 | 2. Find the `__Secure-next-auth.session-token` cookie in `Application` > `Storage` > `Cookies` > `https://chat.openai.com`. 34 | 3. Copy the value in the `Cookie Value` field. 35 | - access_token获取方法(据说可以持续2周不过期) 36 | 37 | 登录ChatGPT官方网页版后再打开https://chat.openai.com/api/auth/session 38 | 39 | - 新建`docker-compose.yml`配置文件,粘贴以下内容并保存 40 | ```bash 41 | services: 42 | chatgpt: 43 | image: sheepgreen/chatgpt-html 44 | container_name: htmchat 45 | # environment: 46 | # - CHATGPT_BASE_URL=你的代理服务端地址(不填默认使用作者服务器,目前偶尔会不可用) 47 | volumes: 48 | - ./config.json:/chatgpt-html/config.json 49 | # - ./chat.html:/chatgpt-html/templates/chat.html #默认内置我的UI,如需替换自用网页请取消注释 50 | ports: 51 | - "9999:8088" #8088为容器内端口,不可更换;9999为外部端口,可自行更换 52 | restart: always 53 | ``` 54 | - 输入`docker-compose up -d`即启动成功 55 | ## 注意事项 56 | - 访问地址为http://ip:port 57 | - 修改`chat.html`文件后,需要docker restart htmchat才能生效 58 | ## 其他相关 59 | - [ChatGPT电报机器人](https://github.com/slippersheepig/chatgpt-telegram-bot),[ChatGPT企业微信应用机器人](https://github.com/slippersheepig/chatgpt-bizwechat-bot),[ChatGPT的QQ频道机器人DOCKER版](https://github.com/slippersheepig/QQChannelChatGPT),[微软BING电报机器人DOCKER版](https://github.com/slippersheepig/BingChatBot),[谷歌BARD网页版](https://github.com/slippersheepig/bard-web),[谷歌BARD电报机器人](https://github.com/slippersheepig/bard-telegram-bot) 60 | - 出于玩玩bing的chatgpt心态,按[waylaidwanderer](https://github.com/waylaidwanderer/PandoraAI)搞了一套[测试站](https://ai.sheepig.top)(需要先点击聊天框左边的图标切换模型,默认模型是API,我的KEY没额度了),`Bing`就是GPT-4,`Sydney`是“破解”过的Bing(没有每轮对话最多15次和每天对话最多150次的限制,但是智商差一点)。另外此项目代码也有bug需要完善(如果你去体验会发现的),不做详细介绍。 61 | ![RIVG68}0DCNFD)8MH@OO%W2](https://user-images.githubusercontent.com/58287293/225894449-34e4fde8-8add-4674-8231-c78c6025a913.png) 62 | ![~@M18}0M{LXG6$`5{ZDG{XU](https://user-images.githubusercontent.com/58287293/225894846-a5cb608a-3f1f-4740-ac86-c3601b1a3ad5.png) 63 | - 基于[pandora](https://github.com/pengzhile/pandora)制作了官方OPENAI CHATGPT[高仿站](https://v.sheepig.top) 64 | -------------------------------------------------------------------------------- /static/style.css: -------------------------------------------------------------------------------- 1 | * { 2 | box-sizing: border-box; 3 | margin: 0; 4 | padding: 0; 5 | } 6 | 7 | body { 8 | background-color: #edeff2; 9 | font-family: "Calibri", "Roboto", sans-serif; 10 | } 11 | 12 | @media (prefers-color-scheme: dark) { 13 | body { 14 | background: black; 15 | color: white; 16 | } 17 | } 18 | 19 | .chat_window { 20 | position: absolute; 21 | width: calc(100% - 20px); 22 | max-width: 800px; 23 | border-radius: 10px; 24 | background-color: #f8f8f8; 25 | left: 50%; 26 | top: 50%; 27 | transform: translate(-50%, -50%); 28 | box-shadow: 0 10px 20px rgba(0, 0, 0, 0.15); 29 | overflow: hidden; 30 | } 31 | 32 | .top_menu { 33 | background-color: #fff; 34 | width: 100%; 35 | padding: 20px 0 15px; 36 | box-shadow: 0 1px 30px rgba(0, 0, 0, 0.1); 37 | } 38 | 39 | .top_menu .title { 40 | text-align: center; 41 | color: #bcbdc0; 42 | font-size: 20px; 43 | } 44 | 45 | .messages { 46 | position: relative; 47 | list-style: none; 48 | padding: 20px 10px 0; 49 | overflow-x: hidden; 50 | overflow-y: auto; 51 | } 52 | 53 | .messages div { 54 | text-align: left; 55 | color: green; 56 | } 57 | 58 | .messages pre { 59 | white-space: pre-wrap; 60 | word-wrap: break-word; 61 | } 62 | 63 | .bottom_wrapper { 64 | clear: both; 65 | position: relative; 66 | width: 100%; 67 | background-color: #f6f6f6; 68 | padding: 20px; 69 | border-radius: 0 0 10px 10px; 70 | } 71 | 72 | .bottom_wrapper textarea { 73 | width: calc(100% - 80px); 74 | border: none; 75 | padding: 10px; 76 | border-radius: 3px; 77 | resize: none; 78 | font-size: 16px; 79 | height: 40px; /* 设置输入框的高度 */ 80 | vertical-align: middle; /* 垂直居中 */ 81 | } 82 | 83 | .bottom_wrapper button { 84 | width: 60px; 85 | height: 40px; 86 | margin-left: 10px; /* 增加左边距 */ 87 | font-size: 16px; 88 | border: none; 89 | border-radius: 3px; 90 | background-color: #4CAF50; 91 | color: white; 92 | cursor: pointer; 93 | vertical-align: middle; /* 垂直居中 */ 94 | } 95 | 96 | .bottom_wrapper button:hover { 97 | background-color: #3e8e41; 98 | } 99 | 100 | .bottom_wrapper button:focus { 101 | outline: none; 102 | } 103 | 104 | /* 新增一个typing类,用于显示动画效果 */ 105 | .typing { 106 | width: 20px; 107 | height: 20px; 108 | border-radius: 50%; 109 | background-color: green; 110 | position: relative; 111 | animation: typing 1.5s infinite; 112 | } 113 | 114 | .dark { 115 | color: #fff; 116 | } 117 | 118 | @media (prefers-color-scheme: dark) { 119 | .chat_window { 120 | background-color: #333; 121 | box-shadow: 0 10px 20px rgba(255, 255, 255, 0.1); 122 | } 123 | 124 | .top_menu { 125 | background-color: #222; 126 | box-shadow: 0 1px 30px rgba(255, 255, 255, 0.1); 127 | } 128 | 129 | .top_menu .title { 130 | color: #ccc; 131 | } 132 | 133 | .bottom_wrapper { 134 | background-color: #222; 135 | box-shadow: 0 1px 30px; 136 | rgba(255, 255, 255, 0.1); 137 | } 138 | 139 | .bottom_wrapper textarea { 140 | background-color: #333; 141 | color: #fff; 142 | } 143 | 144 | .bottom_wrapper button { 145 | background-color: #4CAF50; 146 | } 147 | 148 | .bottom_wrapper button:hover { 149 | background-color: #3e8e41; 150 | } 151 | } 152 | 153 | /* 定义typing动画,让小圆点在水平方向上移动 */ 154 | @keyframes typing { 155 | 0% { 156 | left: 0; 157 | } 158 | 50% { 159 | left: calc(100% - 20px); 160 | } 161 | 100% { 162 | left: 0; 163 | } 164 | } 165 | --------------------------------------------------------------------------------