From 68fd5a79d712bd9fc393e14d710275c79392043a Mon Sep 17 00:00:00 2001 From: linghaihui <75124771@qq.com> Date: Tue, 4 Apr 2023 22:21:42 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8C=81=E4=B9=85=E5=8C=96=E8=81=8A=E5=A4=A9?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E5=88=B0redis?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bingchat/app.js | 40 ++++++++++- bingchat/components/chatbox/index.js | 92 +++++++++++++++++++++----- bingchat/components/chatbox/index.wxml | 6 +- bingchat/components/chatbox/index.wxss | 2 +- bingchat/pages/index/index.js | 49 +++++++++----- bingchat/pages/index/index.wxml | 2 +- new-bing/Dockerfile | 2 +- new-bing/EdgeGPT.py | 27 ++++---- new-bing/app.py | 56 +++++++++++++--- new-bing/conversation_ctr.py | 58 ++++++++++++++++ new-bing/env.example | 3 + new-bing/requirements.txt | 2 + 12 files changed, 275 insertions(+), 64 deletions(-) create mode 100644 new-bing/conversation_ctr.py diff --git a/bingchat/app.js b/bingchat/app.js index d22b08d..af1bbb3 100644 --- a/bingchat/app.js +++ b/bingchat/app.js @@ -1,5 +1,6 @@ import { - doRequest + doRequest, + sid_prefix } from "./config" App({ @@ -8,8 +9,45 @@ App({ this.getSid(sid => { console.log(sid) }) + this.upload_conversation() + }, + onHide: function () { + this.upload_conversation() }, globalData: {}, + upload_cache_conversation: function (sid) { + wx.getStorage({ + key: "chatList", + success: function (res) { + var data = res.data + if (data && data.length > 0) { + doRequest("/save", "POST", { + "sid": sid_prefix + sid, + "conversations": data + }).then(res => { + console.log("upload " + data.length + " conversations success!") + }) + } + } + }) + }, + upload_conversation: function (conversations = []) { + var that = this + if (conversations.length == 0) { + that.getSid(sid => { + that.upload_cache_conversation(sid) + }) + } else { + that.getSid(sid => { + doRequest("/save", "POST", { + "sid": sid_prefix + sid, + "conversations": conversations, + }).then(res => { + console.log("upload " + conversations.length + " conversations success!") + }) + }) + } + }, getSid: function (callback) { var that = this if (!this.globalData.sid) { diff --git a/bingchat/components/chatbox/index.js b/bingchat/components/chatbox/index.js index cff7fd2..baa13f2 100644 --- a/bingchat/components/chatbox/index.js +++ b/bingchat/components/chatbox/index.js @@ -1,5 +1,11 @@ const app = getApp() +import { + doRequest, + sid_prefix, + systemInfo +} from "../../config" + var closeShareOnCopy = false try { if (wx.getStorageSync("closeShareOnCopy")) { @@ -22,14 +28,14 @@ Component({ }, pageLifetimes: { show: function () { - this.initMessageHistory() + // this.initMessageHistory() }, }, lifetimes: { attached() { var that = this app.globalData.cht = that - //that.initMessageHistory() + that.initMessageHistory() wx.getSystemInfo({ success: function (res) { that.setData({ @@ -48,22 +54,69 @@ Component({ autoIncrConversation: 1, closeShareOnCopy: closeShareOnCopy, showShare: false, + loadingData: false, + height: systemInfo.windowHeight - parseInt(100 / 750 * systemInfo.windowWidth) - ((systemInfo.platform == "ios" || systemInfo.platform == "android") ? 22 : 5) }, methods: { - initMessageHistory() { + bindscrolltoupper: function (e) { var that = this - var data = wx.getStorageSync("chatList") - data = data ? data : [] - data.forEach((v) => { - if (v["suggests"] === undefined) { - v["suggests"] = [] - } - }) - if (data.length > 0) { - that.setData({ - chatList: data, - }) + if (that.data.loadingData) { + return } + that.setData({ + loadingData: true + }) + wx.showLoading({ + title: "加载历史记录...", + }) + app.getSid(sid => { + var page = 1 + if (that.data.chatList.length > 0) { + page = Math.ceil((that.data.chatList.length + 1) / 10) + } + doRequest("/query", "GET", { + "sid": sid_prefix + sid, + "page": page, + "size": 10, + }).then(res => { + var data = res.data["data"] + data.reverse() + var oldData = that.data.chatList + var filterData = [] + if (oldData.length > 0) { + data.forEach(k => { + if (k["dt"] < oldData[0]["dt"]) { + filterData.push(k) + } + }) + } else { + filterData = data + } + var newData = filterData.concat(oldData) + that.setData({ + chatList: newData, + loadingData: false + }, () => { + if (filterData.length == 0 && e) { + setTimeout(() => { + wx.showToast({ + title: "已加载完成" + }) + }, 300) + } else { + setTimeout(() => { + wx.hideLoading() + }, 300) + } + }) + }).catch(res => { + wx.hideLoading() + console.log(res) + }) + }) + }, + initMessageHistory() { + this.bindscrolltoupper() }, clearChat: function (e) { var that = this @@ -73,13 +126,18 @@ Component({ content: "是否删除该条聊天?", complete: (res) => { if (res.confirm) { + var deleteData = data[index] data.splice(index, 1) that.setData({ chatList: data, }) - wx.setStorage({ - key: "chatList", - data: data, + app.getSid(sid => { + doRequest("/delete", "POST", { + "sid": sid_prefix + sid, + "conversation": deleteData + }).then(res => { + console.log(res) + }) }) } }, diff --git a/bingchat/components/chatbox/index.wxml b/bingchat/components/chatbox/index.wxml index 430b5a7..fa8a1c1 100644 --- a/bingchat/components/chatbox/index.wxml +++ b/bingchat/components/chatbox/index.wxml @@ -1,11 +1,11 @@ 输入问题开始和{{chatType == "bing" ? "New Bing" : "ChatGPT"}}聊天吧~ - + - {{item.dt}} + {{item.dt}} {{item.num_in_conversation}} @@ -28,5 +28,5 @@ - + \ No newline at end of file diff --git a/bingchat/components/chatbox/index.wxss b/bingchat/components/chatbox/index.wxss index e9a4a82..4d52e3f 100644 --- a/bingchat/components/chatbox/index.wxss +++ b/bingchat/components/chatbox/index.wxss @@ -102,4 +102,4 @@ font-size: 18rpx; line-height: 2em; font-weight: bold; -} \ No newline at end of file +} diff --git a/bingchat/pages/index/index.js b/bingchat/pages/index/index.js index 3cb3870..4afa42b 100644 --- a/bingchat/pages/index/index.js +++ b/bingchat/pages/index/index.js @@ -1,11 +1,10 @@ import { doRequest, - SERVER_WSS_HOST + SERVER_WSS_HOST, + systemInfo, + sid_prefix } from "../../config" -const systemInfo = wx.getSystemInfoSync() -// 各平台对话分离 -const sid_prefix = systemInfo.platform == "ios" || systemInfo.platform == "android" ? "" : systemInfo.platform const initHeight = inputPop() ? 22 : 5 // 是否使用websocket请求 var useWebsocket = true @@ -146,12 +145,6 @@ Page({ chatType: chatType, }) } - const cht = app.globalData.cht - if (cht.data.chatList.length > 1) { - cht.setData({ - scrollId: "item" + (cht.data.chatList.length - 2), - }) - } // 切换title this.switchTitle() }, @@ -166,7 +159,24 @@ Page({ }) } }, - onLoad() {}, + scrollBottom: function () { + const cht = app.globalData.cht + if (cht.data.chatList.length > 1 && !this.data.textareaFocus) { + cht.setData({ + scrollId: "item" + (cht.data.chatList.length - 2), + }) + } + }, + onLoad() { + const cht = app.globalData.cht + setTimeout(() => { + if (cht.data.chatList.length > 1) { + cht.setData({ + scrollId: "item" + (cht.data.chatList.length - 2), + }) + } + }, 1500) + }, processData: function (data, suggests, content) { var robContent = data["data"]["status"] if (robContent == "Success") { @@ -268,7 +278,7 @@ Page({ }) return } else { - that.pushStorageMessage(cht, "搜索中🔍...", "rob", [], true) + that.pushStorageMessage(cht, "搜索中🔍...", "rob", [], true, false, -1, false) } if (that.data.useWebsocket) { that.sendWSRequest(content) @@ -300,10 +310,14 @@ Page({ searching: false }) } + // 只保留最新的10条 wx.setStorage({ key: "chatList", - data: cht.data.chatList, + data: cht.data.chatList.slice(cht.data.chatList.length - 10), }) + if (final) { + app.upload_conversation(cht.data.chatList.slice(cht.data.chatList.length - 1)) + } setTimeout(() => { cht.setData({ scrollId: "item" + (autoIncrConversation + "9999"), @@ -537,9 +551,12 @@ Page({ cht.setData({ chatList: [], }) - wx.setStorage({ - key: "chatList", - data: [], + app.getSid(sid => { + doRequest("/delete_all", "POST", { + "sid": sid_prefix + sid + }).then(res => { + console.log("delete all") + }) }) } }, diff --git a/bingchat/pages/index/index.wxml b/bingchat/pages/index/index.wxml index a155cb4..c898428 100644 --- a/bingchat/pages/index/index.wxml +++ b/bingchat/pages/index/index.wxml @@ -1,6 +1,6 @@ - + 发送 \ No newline at end of file diff --git a/new-bing/Dockerfile b/new-bing/Dockerfile index 6ed2d80..244e960 100644 --- a/new-bing/Dockerfile +++ b/new-bing/Dockerfile @@ -2,7 +2,7 @@ FROM sanicframework/sanic:3.11-latest WORKDIR /sanic -COPY app.py EdgeGPT.py requirements.txt /sanic/ +COPY app.py EdgeGPT.py conversation_ctr.py requirements.txt /sanic/ RUN pip install -r requirements.txt diff --git a/new-bing/EdgeGPT.py b/new-bing/EdgeGPT.py index 6cfbbe4..ba25fd5 100644 --- a/new-bing/EdgeGPT.py +++ b/new-bing/EdgeGPT.py @@ -22,20 +22,20 @@ HEADERS = { "accept": "application/json", "accept-language": "en-US,en;q=0.9", "content-type": "application/json", - "sec-ch-ua": '"Not_A Brand";v="99", "Microsoft Edge";v="110", "Chromium";v="110"', + "sec-ch-ua": '"Microsoft Edge";v="111", "Not(A:Brand";v="8", "Chromium";v="111"', "sec-ch-ua-arch": '"x86"', "sec-ch-ua-bitness": '"64"', - "sec-ch-ua-full-version": '"109.0.1518.78"', - "sec-ch-ua-full-version-list": '"Chromium";v="110.0.5481.192", "Not A(Brand";v="24.0.0.0", "Microsoft Edge";v="110.0.1587.69"', + "sec-ch-ua-full-version": '"111.0.1661.43"', + "sec-ch-ua-full-version-list": '"Microsoft Edge";v="111.0.1661.43", "Not(A:Brand";v="8.0.0.0", "Chromium";v="111.0.5563.64"', "sec-ch-ua-mobile": "?0", "sec-ch-ua-model": "", - "sec-ch-ua-platform": '"Windows"', - "sec-ch-ua-platform-version": '"15.0.0"', + "sec-ch-ua-platform": '"macOS"', + "sec-ch-ua-platform-version": '"11.7.3"', "sec-fetch-dest": "empty", "sec-fetch-mode": "cors", "sec-fetch-site": "same-origin", "x-ms-client-request-id": str(uuid.uuid4()), - "x-ms-useragent": "azsdk-js-api-client-factory/1.0.0-beta.1 core-rest-pipeline/1.10.0 OS/Win32", + "x-ms-useragent": "azsdk-js-api-client-factory/1.0.0-beta.1 core-rest-pipeline/1.10.0 OS/MacIntel", "Referer": "https://www.bing.com/search?q=Bing+AI&showconv=1&FORM=hpcodx", "Referrer-Policy": "origin-when-cross-origin", "x-forwarded-for": FORWARDED_IP, @@ -46,21 +46,21 @@ HEADERS_INIT_CONVER = { "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7", "accept-language": "en-US,en;q=0.9", "cache-control": "max-age=0", - "sec-ch-ua": '"Chromium";v="110", "Not A(Brand";v="24", "Microsoft Edge";v="110"', + "sec-ch-ua": '"Microsoft Edge";v="111", "Not(A:Brand";v="8", "Chromium";v="111"', "sec-ch-ua-arch": '"x86"', "sec-ch-ua-bitness": '"64"', - "sec-ch-ua-full-version": '"110.0.1587.69"', - "sec-ch-ua-full-version-list": '"Chromium";v="110.0.5481.192", "Not A(Brand";v="24.0.0.0", "Microsoft Edge";v="110.0.1587.69"', + "sec-ch-ua-full-version": '"111.0.1661.43"', + "sec-ch-ua-full-version-list": '"Microsoft Edge";v="111.0.1661.43", "Not(A:Brand";v="8.0.0.0", "Chromium";v="111.0.5563.64"', "sec-ch-ua-mobile": "?0", "sec-ch-ua-model": '""', - "sec-ch-ua-platform": '"Windows"', - "sec-ch-ua-platform-version": '"15.0.0"', + "sec-ch-ua-platform": '"macOS"', + "sec-ch-ua-platform-version": '"11.7.3"', "sec-fetch-dest": "document", "sec-fetch-mode": "navigate", "sec-fetch-site": "none", "sec-fetch-user": "?1", "upgrade-insecure-requests": "1", - "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36 Edg/110.0.1587.69", + "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36 Edg/111.0.1661.43", "x-edge-shopping-flag": "1", "x-forwarded-for": "1.1.1.1", } @@ -288,8 +288,6 @@ class ChatHub: elif response.get("type") == 2: final = True yield True, response - else: - print(response) async def __initial_handshake(self): await self.wss.send(append_identifier({ @@ -367,4 +365,5 @@ class Chatbot: Reset the conversation """ await self.close() + HEADERS["x-ms-client-request-id"] = str(uuid.uuid4()) self.chat_hub = ChatHub(Conversation()) diff --git a/new-bing/app.py b/new-bing/app.py index 9ea921c..bbfeefa 100644 --- a/new-bing/app.py +++ b/new-bing/app.py @@ -13,6 +13,7 @@ from sanic import Sanic from sanic.log import logger from sanic.response import json +from conversation_ctr import conversation_ctr from EdgeGPT import Chatbot, ConversationStyle APPID = os.environ.get('WXAPPID') @@ -80,9 +81,10 @@ async def ws_chat(_, ws): while True: try: data = raw_json.loads(await ws.recv()) - logger.info('Websocket receive data: %s', data) + logger.info('[bing] Websocket receive data: %s', data) sid = data['sid'] q = data['q'] + index = 0 async for response in get_bot(sid).ask_stream(q, conversation_style=ConversationStyle.creative): final, res = response if final: @@ -96,10 +98,12 @@ async def ws_chat(_, ws): 'data': processed_data })) else: - await ws.send(raw_json.dumps({ - 'final': final, - 'data': res - })) + index += 1 + if index % 3 == 1: + await ws.send(raw_json.dumps({ + 'final': final, + 'data': res + })) except Exception as e: logger.error(e) await ws.send(raw_json.dumps({ @@ -127,7 +131,7 @@ async def reset_conversation(sid): async def do_chat(request): - logger.info('Http request payload: %s', request.json) + logger.info('[bing] Http request payload: %s', request.json) return await get_bot(request.json.get('sid')).ask( request.json.get('q'), conversation_style=ConversationStyle.creative ) @@ -157,9 +161,6 @@ async def process_data(res, q, sid, auto_reset=None): text = '抱歉,未搜索到结果。' logger.error('响应异常:%s', res) suggests = [q] - if res['type'] == 2: - await reset_conversation(sid) - text += '\n已结束本轮对话。' msg = res['item']['result']['message'] if 'message' in res['item']['result'] else '' if auto_reset and ('New topic' in text or 'has expired' in msg): await reset_conversation(sid) @@ -204,7 +205,7 @@ async def ws_openai_chat(_, ws): while True: try: data = raw_json.loads(await ws.recv()) - logger.info('Websocket receive data: %s', data) + logger.info('[openai] Websocket receive data: %s', data) sid = data['sid'] q = data['q'] # 保存30个对话 @@ -220,11 +221,14 @@ async def ws_openai_chat(_, ws): stream=True, ) chunks = [] + index = 0 for chunk in response: chunk_message = chunk['choices'][0]['delta'] if chunk_message: if 'content' in chunk_message: chunks.append(chunk_message['content']) + index += 1 + if index % 5 == 1: await ws.send( raw_json.dumps({ 'final': False, @@ -253,6 +257,7 @@ async def ws_openai_chat(_, ws): @app.post('/openai_chat') async def openai_chat(request): try: + logger.info('[openai] Http request payload: %s', request.json) sid = request.json.get('sid') q = request.json.get('q') history_conversation = OPENAI_CONVERSATION[sid][-30:] @@ -283,5 +288,36 @@ async def openai_chat(request): return json(make_response_data('Error', str(e), [], str(e))) +@app.route('/last_sync_time') +async def last_sync_time(request): + return json({'last_sync_time': conversation_ctr.get_last_sync_time(request.args.get('sid'))}) + + +@app.post('/save') +async def save(request): + conversation_ctr.save(request.json.get('sid'), request.json.get('conversations')) + return json({}) + + +@app.route('/query') +async def query(request): + data = conversation_ctr.get_by_page( + request.args.get('sid'), int(request.args.get('page', '1')), int(request.args.get('size', '20')) + ) + return json({'data': data}) + + +@app.post('/delete') +async def delete(request): + conversation_ctr.delete(request.json.get('sid'), request.json.get('conversation')) + return json({}) + + +@app.post('/delete_all') +async def delete_all(request): + conversation_ctr.delete_all(request.json.get('sid')) + return json({}) + + if __name__ == '__main__': app.run(host='0.0.0.0', port=8000) diff --git a/new-bing/conversation_ctr.py b/new-bing/conversation_ctr.py new file mode 100644 index 0000000..9eb6ade --- /dev/null +++ b/new-bing/conversation_ctr.py @@ -0,0 +1,58 @@ +# coding=utf-8 + +import json +import os + +import redis + +REDIS_HOST = os.environ.get('REDIS_HOST', '127.0.0.1') +REDIS_PORT = int(os.environ.get('REDIS_PORT', 6379)) +REDIS_PASSWD = os.environ.get('REDIS_PASSWD', '123456') +REDIS_DB = int(os.environ.get('REDIS_DB', 0)) + + +class ConversationCtr: + + LAST_SYNC_TIME_KEY = 'bing:last_sync_time:%s' + CONVERSATION_LIST_KEY = 'bing:conversation_list:%s' + + def __init__(self, client=None) -> None: + self.redis_client = client + if client is None: + self.init() + + def init(self): + if self.redis_client is not None: + return + pool = redis.ConnectionPool(host=REDIS_HOST, port=REDIS_PORT, db=REDIS_DB, password=REDIS_PASSWD) + self.redis_client = redis.Redis(connection_pool=pool) + + def get_last_sync_time(self, sid): + key = self.LAST_SYNC_TIME_KEY % sid + v = self.redis_client.get(key) + return v.decode() if v else '' + + def get_by_page(self, sid, page=1, size=20): + offset = size * (page - 1) if page > 0 else 0 + key = self.CONVERSATION_LIST_KEY % sid + return [json.loads(x) for x in self.redis_client.lrange(key, offset, offset + size - 1)] + + def save(self, sid, conversations): + _last_sync_time = self.get_last_sync_time(sid) + conversations = [x for x in conversations if x['dt'] > _last_sync_time] + if len(conversations) <= 0: + return + key = self.CONVERSATION_LIST_KEY % sid + self.redis_client.lpush(key, *[json.dumps(x) for x in conversations]) + self.redis_client.set(self.LAST_SYNC_TIME_KEY % sid, conversations[-1]['dt']) + + def delete(self, sid, conversation): + key = self.CONVERSATION_LIST_KEY % sid + self.redis_client.lrem(key, 0, json.dumps(conversation)) + + def delete_all(self, sid): + key = self.CONVERSATION_LIST_KEY % sid + self.redis_client.delete(key) + + +conversation_ctr = ConversationCtr() diff --git a/new-bing/env.example b/new-bing/env.example index ad8df90..681b4de 100644 --- a/new-bing/env.example +++ b/new-bing/env.example @@ -6,3 +6,6 @@ COOKIE_FILE2=/sanic/cookies/cookie2.json # 备用cookie2, 没有可以删掉 COOKIE_FILES=["/sanic/cookies/cookie.json", "/sanic/cookies/cookie1.json", "/sanic/cookies/cookie2.json"] #cookie列表,配置了此环境变量,会优先使用此变量,直接忽略上面的3个环境变量 https_proxy=http://127.0.0.1:1080 # 目前中国大陆的IP会返回404,所以最好能加个代理 OPENAI_API_KEY= +REDIS_HOST= +REDIS_PORT= +REDIS_PASSWD= diff --git a/new-bing/requirements.txt b/new-bing/requirements.txt index 0fe308c..9726486 100644 --- a/new-bing/requirements.txt +++ b/new-bing/requirements.txt @@ -4,3 +4,5 @@ asyncio==3.4.3 websockets==10.4 httpx==0.23.3 openai==0.27.2 +redis==4.5.1 +hiredis==2.2.2