commit f173af8104987dcaf0bb1be14740e122ca169ce6 Author: linghaihui <75124771@qq.com> Date: Wed Mar 22 15:25:55 2023 +0800 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..24c8763 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +venv/ +__pycache__/ +cookie*.json +env +config.json diff --git a/README.md b/README.md new file mode 100644 index 0000000..f19ec99 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +本项目实现了[New Bing 聊天的接口](./new-bing),并做了一个简单的[小程序](./bingchat),同时将`chatgpt 3.5`和`New Bing`集成到[微信](./wechatbot)。 diff --git a/bingchat/README.md b/bingchat/README.md new file mode 100644 index 0000000..e4fcab0 --- /dev/null +++ b/bingchat/README.md @@ -0,0 +1,3 @@ +# New Bing Bot + + diff --git a/bingchat/app.js b/bingchat/app.js new file mode 100644 index 0000000..2c8b352 --- /dev/null +++ b/bingchat/app.js @@ -0,0 +1,35 @@ +import SERVER_HOST from '../../config'; + +App({ + onShow: function () {}, + onLaunch: function () { + this.getSid(); + }, + globalData: {}, + getSid: function () { + var that = this; + if (!this.globalData.sid) { + var sid = wx.getStorageSync('sid1'); + if (!sid) { + wx.login({ + success: (res) => { + wx.request({ + url: SERVER_HOST + '/bing/openid', + data: { + code: res.code, + }, + success: (res) => { + that.globalData.sid = res.data.data.openid; + wx.setStorageSync('sid1', that.globalData.sid); + }, + }); + }, + }); + } else { + this.globalData.sid = sid; + wx.setStorageSync('sid1', this.globalData.sid); + } + } + return this.globalData.sid ? this.globalData.sid : ''; + }, +}); diff --git a/bingchat/app.json b/bingchat/app.json new file mode 100644 index 0000000..1998d22 --- /dev/null +++ b/bingchat/app.json @@ -0,0 +1,21 @@ +{ + "pages": [ + "pages/index/index" + ], + "useExtendedLib": { + "weui":true + }, + "usingComponents": { + "chat-box":"components/chatbox/index" + }, + "window": { + "backgroundTextStyle": "light", + "navigationBarBackgroundColor": "#ffffff", + "navigationBarTitleText": "New Bing Bot 🤖", + "navigationBarTextStyle": "black" + }, + "sitemapLocation": "sitemap.json", + "networkTimeout": { + "request": 1800000 + } +} diff --git a/bingchat/app.wxss b/bingchat/app.wxss new file mode 100644 index 0000000..67efa87 --- /dev/null +++ b/bingchat/app.wxss @@ -0,0 +1,4 @@ +page { + background-color: #fff; + font-size: 28rpx; +} diff --git a/bingchat/components/chatbox/index.js b/bingchat/components/chatbox/index.js new file mode 100644 index 0000000..d314c6d --- /dev/null +++ b/bingchat/components/chatbox/index.js @@ -0,0 +1,94 @@ +const app = getApp(); + +Component({ + options: { + addGlobalClass: true, + multipleSlots: true, + }, + properties: {}, + pageLifetimes: { + show: function () { + this.initMessageHistory(); + }, + }, + lifetimes: { + attached() { + var that = this; + app.globalData.cht = that; + //that.initMessageHistory(); + wx.getSystemInfo({ + success: function (res) { + that.setData({ + systemInfo: res, + }); + }, + }); + }, + detached() { + try { + } catch (error) {} + }, + }, + data: { + chatList: [], + }, + methods: { + initMessageHistory() { + 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, + }); + } + }, + clearChat: function (e) { + var that = this; + var index = e.currentTarget.dataset.index; + var data = this.data.chatList; + wx.showModal({ + content: "是否删除该条记录?", + complete: (res) => { + if (res.confirm) { + data.splice(index, 1); + that.setData({ + chatList: data, + }); + wx.setStorage({ + key: "chatList", + data: data, + }); + } + }, + }); + }, + copyContent: function (e) { + var index = e.currentTarget.dataset.index; + var content = this.data.chatList[index].originContent; + wx.setClipboardData({ + data: content, + success: function () { + wx.showToast({ + title: "复制成功", + }); + }, + }); + }, + suggestSubmit: function (e) { + var suggest = e.currentTarget.dataset.suggest; + this.triggerEvent( + "suggestSubmit", + { + suggest, + }, + {} + ); + }, + }, +}); diff --git a/bingchat/components/chatbox/index.json b/bingchat/components/chatbox/index.json new file mode 100644 index 0000000..a89ef4d --- /dev/null +++ b/bingchat/components/chatbox/index.json @@ -0,0 +1,4 @@ +{ + "component": true, + "usingComponents": {} +} diff --git a/bingchat/components/chatbox/index.wxml b/bingchat/components/chatbox/index.wxml new file mode 100644 index 0000000..ebe8ad0 --- /dev/null +++ b/bingchat/components/chatbox/index.wxml @@ -0,0 +1,22 @@ +输入问题开始和New Bing聊天吧~ + + + + + + {{item.dt}} + {{item.originContent}} + + {{suggest}} + + + + + + {{item.dt}} + {{item.originContent}} + + + + + \ No newline at end of file diff --git a/bingchat/components/chatbox/index.wxss b/bingchat/components/chatbox/index.wxss new file mode 100644 index 0000000..b98de25 --- /dev/null +++ b/bingchat/components/chatbox/index.wxss @@ -0,0 +1,77 @@ +.chat { + display: flex; + flex-direction: column; +} + +.chat-item { + display: flex; + padding: 10rpx 10rpx 35rpx; + flex-direction: row; +} + +.left { + justify-content: flex-start; +} + +.right { + justify-content: flex-end; +} + +.avatar { + width: 80rpx; + height: 80rpx; + border-radius: 50%; + background-color: rgb(224, 226, 232) +} + +.dt { + font-size: 24rpx; + color: rgb(180, 187, 196) +} + +.content { + border-radius: 16rpx; + padding: 10rpx 16rpx 10rpx 16rpx; + word-break: break-all; + white-space: pre-wrap; +} + +.bg-green { + background-color: #d2f9d1; +} + +.bg-white { + background-color: #f4f6f8; +} + +.chat-box { + display: flex; + flex-direction: column; + max-width: 84%; +} + +.suggest { + display: flex; + justify-content: flex-start; + margin-top: 8rpx; + flex-wrap: wrap; +} +.suggest-item{ + background-color: #f4f6f8; + display: inline-block; + padding: 5rpx 10rpx 5rpx 10rpx; + border-radius: 10rpx; + margin-right: 16rpx; + color: rgb(180, 187, 196); + margin-bottom: 10rpx; + font-size: 25rpx; +} +@keyframes blink { + from { display: 1.0; } + to { opacity: 0.0; } +} + +.blinking { + animation: blink 1.5s ease-in-out infinite; + display: block; +} \ No newline at end of file diff --git a/bingchat/config.js b/bingchat/config.js new file mode 100644 index 0000000..b742d7e --- /dev/null +++ b/bingchat/config.js @@ -0,0 +1 @@ +export default SERVER_HOST = 'https://example.com'; diff --git a/bingchat/image/bing-avatar.png b/bingchat/image/bing-avatar.png new file mode 100644 index 0000000..a7db7d2 Binary files /dev/null and b/bingchat/image/bing-avatar.png differ diff --git a/bingchat/image/person.jpeg b/bingchat/image/person.jpeg new file mode 100644 index 0000000..9be9587 Binary files /dev/null and b/bingchat/image/person.jpeg differ diff --git a/bingchat/pages/index/index.js b/bingchat/pages/index/index.js new file mode 100644 index 0000000..4d1967f --- /dev/null +++ b/bingchat/pages/index/index.js @@ -0,0 +1,222 @@ +import SERVER_HOST from "../../config"; + +Date.prototype.format = function (fmt) { + var o = { + "M+": this.getMonth() + 1, //月份 + "d+": this.getDate(), //日 + "h+": this.getHours(), //小时 + "m+": this.getMinutes(), //分 + "s+": this.getSeconds(), //秒 + "q+": Math.floor((this.getMonth() + 3) / 3), //季度 + S: this.getMilliseconds(), //毫秒 + }; + if (/(y+)/.test(fmt)) { + fmt = fmt.replace( + RegExp.$1, + (this.getFullYear() + "").substr(4 - RegExp.$1.length) + ); + } + for (var k in o) { + if (new RegExp("(" + k + ")").test(fmt)) { + fmt = fmt.replace( + RegExp.$1, + RegExp.$1.length == 1 ? o[k] : ("00" + o[k]).substr(("" + o[k]).length) + ); + } + } + return fmt; +}; + +function getNow() { + return new Date().format("yyyy-MM-dd hh:mm:ss"); +} + +const app = getApp(); +const robAvatar = "../../image/bing-avatar.png"; +const personAvatar = "../../image/person.jpeg"; + +Page({ + data: { + InputBottom: 24, + content: "", + }, + InputFocus(e) { + this.setData({ + InputBottom: e.detail.height + 2, + }); + }, + InputBlur(e) { + this.setData({ + InputBottom: 24, + }); + }, + processContent(content) { + return content.replace(/\\n/g, "\n"); + }, + resetConversation: function () { + wx.request({ + url: SERVER_HOST + "/bing/reset", + data: { + t: new Date().getTime(), + sid: app.getSid(), + }, + enableHttp2: true, + }); + }, + onShow() { + const cht = app.globalData.cht; + if (cht.data.chatList.length > 1) { + cht.setData({ + scrollId: "item" + (cht.data.chatList.length - 2), + }); + } + }, + submitContent: function (content) { + var that = this; + const cht = app.globalData.cht; + content = this.processContent(content); + cht.data.chatList.push({ + type: "man", + avatarUrl: personAvatar, + dt: getNow(), + originContent: content, + }); + cht.setData({ + chatList: cht.data.chatList, + }); + that.setData({ + content: "", + }); + if (content == "重新对话!") { + this.resetConversation(); + cht.data.chatList.push({ + type: "rob", + avatarUrl: robAvatar, + dt: getNow(), + originContent: "现在我们可以开始新的对话😊", + suggests: [], + }); + cht.setData({ + chatList: cht.data.chatList, + }); + that.scrollTo(cht); + return; + } else { + cht.data.chatList.push({ + type: "rob", + avatarUrl: robAvatar, + dt: getNow(), + originContent: "搜索中🔍...", + suggests: [], + blink: true, + }); + cht.setData({ + chatList: cht.data.chatList, + }); + } + wx.request({ + url: SERVER_HOST + "/bing/chat", + method: "GET", + data: { + q: content, + t: new Date().getTime(), + sid: app.getSid(), + }, + enableHttp2: true, + success(res) { + try { + var robContent = ""; + var suggests = []; + if (res.statusCode != 200) { + robContent = + "抱歉😭,网络异常,请稍后重试 [" + res.statusCode + "]"; + suggests.push(content); + } else { + robContent = res.data["data"]["status"]; + if (robContent == "Success") { + robContent = res.data["data"]["text"]; + suggests = res.data["data"]["suggests"]; + if (robContent.indexOf("New topic") != -1) { + robContent += "\n发送“重新对话!”开始新的对话"; + suggests.push("重新对话!"); + suggests.push(content); + } + } else { + if (robContent == "Throttled") { + robContent = "这真是愉快,但你已达到每日限制。是否明天再聊?"; + suggests.push("重新对话!"); + suggests.push(content); + } else { + var msg = res.data["data"]["message"]; + if (msg.indexOf("has expired") != -1) { + that.resetConversation(); + robContent = "本轮对话已过期,请重新开始。"; + suggests.push(content); + } else { + robContent = "抱歉😭,发生错误:" + msg + ",请重试"; + suggests.push(content); + } + } + } + } + cht.data.chatList.pop(); + cht.data.chatList.push({ + type: "rob", + avatarUrl: robAvatar, + dt: getNow(), + originContent: that.processContent(robContent), + suggests: suggests, + }); + cht.setData({ + chatList: cht.data.chatList, + }); + wx.setStorage({ + key: "chatList", + data: cht.data.chatList, + }); + } catch (error) { + console.log(error); + wx.showToast({ + title: "fatal error", + }); + } + }, + fail(e) { + console.log(e); + cht.data.chatList.pop(); + cht.data.chatList.push({ + type: "rob", + avatarUrl: robAvatar, + dt: getNow(), + originContent: e.errMsg, + suggests: [], + }); + }, + }); + that.scrollTo(cht); + }, + scrollTo: function (cht) { + setTimeout(() => { + cht.setData({ + scrollId: "item" + (cht.data.chatList.length - 1), + }); + }, 50); + }, + submit() { + var content = this.data.content; + if (content.length == 0 || content == null || content.trim().length == 0) { + return; + } + this.submitContent(content); + }, + onShareAppMessage() { + return { + title: "New Bing Bot 🤖", + path: "/pages/index/index", + }; + }, + onSuggestSubmit: function (e) { + var suggest = e.detail.suggest; + this.submitContent(suggest); + }, +}); diff --git a/bingchat/pages/index/index.json b/bingchat/pages/index/index.json new file mode 100644 index 0000000..c77dc7a --- /dev/null +++ b/bingchat/pages/index/index.json @@ -0,0 +1,4 @@ +{ + "usingComponents": { + } +} diff --git a/bingchat/pages/index/index.wxml b/bingchat/pages/index/index.wxml new file mode 100644 index 0000000..fe97059 --- /dev/null +++ b/bingchat/pages/index/index.wxml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/bingchat/pages/index/index.wxss b/bingchat/pages/index/index.wxss new file mode 100644 index 0000000..e69de29 diff --git a/bingchat/project.config.json b/bingchat/project.config.json new file mode 100644 index 0000000..0a6d485 --- /dev/null +++ b/bingchat/project.config.json @@ -0,0 +1,27 @@ +{ + "appid": "wxee7496be5b68b740", + "compileType": "miniprogram", + "libVersion": "2.12.3", + "packOptions": { + "ignore": [], + "include": [] + }, + "setting": { + "es6": true, + "postcss": true, + "minified": true, + "enhance": true, + "packNpmRelationList": [], + "babelSetting": { + "ignore": [], + "disablePlugins": [], + "outputPath": "" + }, + "uglifyFileName": false + }, + "condition": {}, + "editorSetting": { + "tabIndent": "tab", + "tabSize": 2 + } +} diff --git a/bingchat/project.private.config.json b/bingchat/project.private.config.json new file mode 100644 index 0000000..3d11a51 --- /dev/null +++ b/bingchat/project.private.config.json @@ -0,0 +1,9 @@ +{ + "description": "项目私有配置文件。此文件中的内容将覆盖 project.config.json 中的相同字段。项目的改动优先同步到此文件中。详见文档:https://developers.weixin.qq.com/miniprogram/dev/devtools/projectconfig.html", + "projectname": "bingchat", + "setting": { + "compileHotReLoad": true, + "urlCheck": false + }, + "libVersion": "2.15.0" +} diff --git a/bingchat/sitemap.json b/bingchat/sitemap.json new file mode 100644 index 0000000..ff91049 --- /dev/null +++ b/bingchat/sitemap.json @@ -0,0 +1,7 @@ +{ + "desc": "关于本文件的更多信息,请参考文档 https://developers.weixin.qq.com/miniprogram/dev/framework/sitemap.html", + "rules": [{ + "action": "disallow", + "page": "*" + }] +} \ No newline at end of file diff --git a/bingchat/snapshoot.png b/bingchat/snapshoot.png new file mode 100644 index 0000000..e9c1857 Binary files /dev/null and b/bingchat/snapshoot.png differ diff --git a/new-bing/Dockerfile b/new-bing/Dockerfile new file mode 100644 index 0000000..d6369df --- /dev/null +++ b/new-bing/Dockerfile @@ -0,0 +1,13 @@ +FROM sanicframework/sanic:3.11-latest + +WORKDIR /sanic + +COPY app.py EdgeGPT.py cookie.json cookie1.json cookie2.json requirements.txt /sanic/ + +RUN pip install -r requirements.txt + +RUN rm requirements.txt + +EXPOSE 8000 + +CMD ["python", "app.py"] diff --git a/new-bing/EdgeGPT.py b/new-bing/EdgeGPT.py new file mode 100644 index 0000000..baef5bd --- /dev/null +++ b/new-bing/EdgeGPT.py @@ -0,0 +1,336 @@ +""" +Main.py +""" +from __future__ import annotations + +import asyncio +import json +import os +import random +import ssl +import uuid +from enum import Enum +from typing import Generator +from typing import Literal +from typing import Optional +from typing import Union + +import certifi +import httpx +import websockets.client as websockets + +DELIMITER = "\x1e" + +# Generate random IP between range 13.104.0.0/14 +FORWARDED_IP = (f"13.{random.randint(104, 107)}.{random.randint(0, 255)}.{random.randint(0, 255)}") + +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-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-mobile": "?0", + "sec-ch-ua-model": "", + "sec-ch-ua-platform": '"Windows"', + "sec-ch-ua-platform-version": '"15.0.0"', + "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", + "Referer": "https://www.bing.com/search?q=Bing+AI&showconv=1&FORM=hpcodx", + "Referrer-Policy": "origin-when-cross-origin", + "x-forwarded-for": FORWARDED_IP, +} + +HEADERS_INIT_CONVER = { + "authority": "edgeservices.bing.com", + "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-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-mobile": "?0", + "sec-ch-ua-model": '""', + "sec-ch-ua-platform": '"Windows"', + "sec-ch-ua-platform-version": '"15.0.0"', + "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", + "x-edge-shopping-flag": "1", +} + +ssl_context = ssl.create_default_context() +ssl_context.load_verify_locations(certifi.where()) + + +class NotAllowedToAccess(Exception): + pass + + +class ConversationStyle(Enum): + creative = "h3relaxedimg" + balanced = "galileo" + precise = "h3precise" + + +CONVERSATION_STYLE_TYPE = Optional[Union[ConversationStyle, Literal["creative", "balanced", "precise"]]] + + +def append_identifier(msg: dict) -> str: + """ + Appends special character to end of message to identify end of message + """ + # Convert dict to json string + return json.dumps(msg) + DELIMITER + + +class ChatHubRequest: + """ + Request object for ChatHub + """ + + def __init__( + self, + conversation_signature: str, + client_id: str, + conversation_id: str, + invocation_id: int = 0, + ) -> None: + self.struct: dict = {} + + self.client_id: str = client_id + self.conversation_id: str = conversation_id + self.conversation_signature: str = conversation_signature + self.invocation_id: int = invocation_id + + def update( + self, + prompt: str, + conversation_style: CONVERSATION_STYLE_TYPE, + options: list | None = None, + ) -> None: + """ + Updates request object + """ + if options is None: + options = [ + "deepleo", + "enable_debug_commands", + "disable_emoji_spoken_text", + "enablemm", + ] + if conversation_style: + if not isinstance(conversation_style, ConversationStyle): + conversation_style = getattr(ConversationStyle, conversation_style) + options = [ + "deepleo", + "enable_debug_commands", + "disable_emoji_spoken_text", + "enablemm", + conversation_style.value, + ] + self.struct = { + "arguments": [ + { + "source": "cib", + "optionsSets": options, + "isStartOfSession": self.invocation_id == 0, + "message": { + "author": "user", + "inputMethod": "Keyboard", + "text": prompt, + "messageType": "Chat", + }, + "conversationSignature": self.conversation_signature, + "participant": { + "id": self.client_id, + }, + "conversationId": self.conversation_id, + }, + ], + "invocationId": str(self.invocation_id), + "target": "chat", + "type": 4, + } + self.invocation_id += 1 + + +class Conversation: + """ + Conversation API + """ + + def __init__(self, cookiePath: str = "", cookies: dict | None = None) -> None: + self.struct: dict = { + "conversationId": None, + "clientId": None, + "conversationSignature": None, + "result": { + "value": "Success", + "message": None + }, + } + self.session = httpx.Client() + self.session.headers.update({ + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36", + }, ) + if cookies is not None: + cookie_file = cookies + else: + f = ( + open(cookiePath, encoding="utf8").read() + if cookiePath else open(os.environ.get("COOKIE_FILE"), encoding="utf-8").read() + ) + cookie_file = json.loads(f) + for cookie in cookie_file: + self.session.cookies.set(cookie["name"], cookie["value"]) + url = "https://edgeservices.bing.com/edgesvc/turing/conversation/create" + # Send GET request + response = self.session.get( + url, + timeout=1800, + headers=HEADERS_INIT_CONVER, + ) + if response.status_code != 200: + print(f"Status code: {response.status_code}") + print(response.text) + raise Exception("Authentication failed") + try: + self.struct = response.json() + if self.struct["result"]["value"] == "UnauthorizedRequest": + raise NotAllowedToAccess(self.struct["result"]["message"]) + except (json.decoder.JSONDecodeError, NotAllowedToAccess) as exc: + raise Exception("Authentication failed. You have not been accepted into the beta.", ) from exc + + +class ChatHub: + """ + Chat API + """ + + def __init__(self, conversation: Conversation) -> None: + self.wss: websockets.WebSocketClientProtocol | None = None + self.request: ChatHubRequest + self.loop: bool + self.task: asyncio.Task + self.request = ChatHubRequest( + conversation_signature=conversation.struct["conversationSignature"], + client_id=conversation.struct["clientId"], + conversation_id=conversation.struct["conversationId"], + ) + + async def ask_stream( + self, + prompt: str, + conversation_style: CONVERSATION_STYLE_TYPE = None, + ) -> Generator[str, None, None]: + """ + Ask a question to the bot + """ + if self.wss: + if not self.wss.closed: + await self.wss.close() + # Check if websocket is closed + self.wss = await websockets.connect( + "wss://sydney.bing.com/sydney/ChatHub", + extra_headers=HEADERS, + max_size=None, + ssl=ssl_context, + open_timeout=30, + ) + await self.__initial_handshake() + # Construct a ChatHub request + self.request.update(prompt=prompt, conversation_style=conversation_style) + # Send request + await self.wss.send(append_identifier(self.request.struct)) + final = False + while not final: + objects = str(await self.wss.recv()).split(DELIMITER) + for obj in objects: + if obj is None or obj == "": + continue + response = json.loads(obj) + if response.get("type") == 1 and response["arguments"][0].get("messages", ): + yield False, response["arguments"][0]["messages"][0]["adaptiveCards"][0]["body"][0].get("text") + elif response.get("type") == 2: + final = True + yield True, response + + async def __initial_handshake(self): + await self.wss.send(append_identifier({ + "protocol": "json", + "version": 1 + })) + await self.wss.recv() + + async def close(self): + """ + Close the connection + """ + if self.wss and not self.wss.closed: + await self.wss.close() + + +class Chatbot: + """ + Combines everything to make it seamless + """ + + def __init__(self, cookiePath: str = "", cookies: dict | None = None) -> None: + self.cookiePath: str = cookiePath + self.cookies: dict | None = cookies + self.chat_hub: ChatHub = ChatHub(Conversation(self.cookiePath, self.cookies)) + + async def ask( + self, + prompt: str, + conversation_style: CONVERSATION_STYLE_TYPE = None, + ) -> dict: + """ + Ask a question to the bot + """ + async for final, response in self.chat_hub.ask_stream( + prompt=prompt, + conversation_style=conversation_style, + ): + if final: + return response + self.chat_hub.wss.close() + + async def ask_stream( + self, + prompt: str, + conversation_style: CONVERSATION_STYLE_TYPE = None, + ) -> Generator[str, None, None]: + """ + Ask a question to the bot + """ + async for response in self.chat_hub.ask_stream( + prompt=prompt, + conversation_style=conversation_style, + ): + yield response + + async def close(self): + """ + Close the connection + """ + await self.chat_hub.close() + + async def reset(self): + """ + Reset the conversation + """ + await self.close() + self.chat_hub = ChatHub(Conversation()) diff --git a/new-bing/Makefile b/new-bing/Makefile new file mode 100644 index 0000000..b9de292 --- /dev/null +++ b/new-bing/Makefile @@ -0,0 +1,6 @@ +build: + docker build -t yy194131/new-bing:$(version) . + docker push yy194131/new-bing:$(version) + +release:build + ssh roselle 'bash /home/linghaihui/bingchat/start.sh $(version)' diff --git a/new-bing/README.md b/new-bing/README.md new file mode 100644 index 0000000..13262c7 --- /dev/null +++ b/new-bing/README.md @@ -0,0 +1,3 @@ +[登录 bing 账号](https://login.live.com/), 使用[Cookie-Editor 扩展](https://chrome.google.com/webstore/detail/cookie-editor/hlkenndednhfkekhgcdicdfddnkalmdm)将 cookie 以 json 格式导出,保存为`cookie.json`文件,同时将`COOKIE_FILE`环境变量的值指向该文件。 + +`EdgeGPT.py`文件 fork 至[https://github.com/acheong08/EdgeGPT](https://github.com/acheong08/EdgeGPT),并做了些许修改,在此表示感谢! diff --git a/new-bing/app.py b/new-bing/app.py new file mode 100644 index 0000000..d1fda9d --- /dev/null +++ b/new-bing/app.py @@ -0,0 +1,108 @@ +# coding=utf-8 + +import os +import re +import threading + +import requests +from sanic import Sanic +from sanic.response import json + +from EdgeGPT import Chatbot, ConversationStyle + +APPID = os.environ.get('WXAPPID') +APPSECRET = os.environ.get('WXAPPSECRET') +WX_URL = 'https://api.weixin.qq.com/sns/jscode2session?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code' +# 备用cookie +COOKIE = os.environ.get('COOKIE_FILE', '') +BAK_COOKIE = os.environ.get('COOKIE_FILE1', '') +BAK_COOKIE1 = os.environ.get('COOKIE_FILE2', '') + +LOCK = threading.Lock() + + +def reset_cookie(): + if not LOCK.acquire(blocking=False): + return + cookie = os.environ.get('COOKIE_FILE') + if cookie == COOKIE: + os.environ['COOKIE_FILE'] = BAK_COOKIE + elif cookie == BAK_COOKIE: + os.environ['COOKIE_FILE'] = BAK_COOKIE1 + elif cookie == BAK_COOKIE1: + os.environ['COOKIE_FILE'] = COOKIE + LOCK.release() + + +app = Sanic('new-bing') +app.config.REQUEST_TIMEOUT = 900 +app.config.RESPONSE_TIMEOUT = 900 +bots = {} + + +def get_bot(sid): + if sid in bots: + return bots[sid] + bot = Chatbot() + bots[sid] = bot + return bot + + +async def do_chat(request): + return await get_bot(request.args.get('sid')).ask( + request.args.get('q'), conversation_style=ConversationStyle.balanced + ) + + +@app.route('/chat') +async def chat(request): + res = await do_chat(request) + auto_reset = request.args.get('auto_reset', '') + status = res['item']['result']['value'] + if status == 'Throttled': + reset_cookie() + await get_bot(request.args.get('sid')).reset() + res = await do_chat(request) + status = res['item']['result']['value'] + text = '' + suggests = [] + if status == 'Success': + if len(res['item']['messages']) >= 2: + text = res['item']['messages'][1]['text'] + if re.match(r'.*\[\^\d+\^\].*', text): + text = res['item']['messages'][1]['adaptiveCards'][0]['body'][0]['text'] + text = re.sub(r'\[\^\d+\^\]', '', text) + suggests = [x['text'] for x in res['item']['messages'][1]['suggestedResponses']] + else: + text = '抱歉,未搜索到结果,请重试。' + suggests = [request.args.get('q')] + msg = res['item']['result']['message'] if 'message' in res['item']['result'] else '' + # 自动reset + if auto_reset and ('New topic' in text or 'has expired' in msg): + await get_bot(request.args.get('sid')).reset() + return json({ + 'data': { + 'status': status, + 'text': text, + 'suggests': suggests, + 'message': msg, + }, + 'cookie': os.environ.get('COOKIE_FILE') + }) + + +@app.route('/reset') +async def reset(request): + await get_bot(request.args.get('sid')).reset() + return json({'data': ''}) + + +@app.route('/openid') +async def openid(request): + code = request.args.get('code') + url = WX_URL % (APPID, APPSECRET, code) + return json({'data': requests.get(url).json()}) + + +if __name__ == '__main__': + app.run(host='0.0.0.0', port=8000, workers=4) diff --git a/new-bing/env.example b/new-bing/env.example new file mode 100644 index 0000000..e304a2e --- /dev/null +++ b/new-bing/env.example @@ -0,0 +1,5 @@ +WXAPPID= +WXAPPSECRET= +COOKIE_FILE=/sanic/cookie.json +COOKIE_FILE1=/sanic/cookie1.json +COOKIE_FILE2=/sanic/cookie2.json diff --git a/new-bing/pyrightconfig.json b/new-bing/pyrightconfig.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/new-bing/pyrightconfig.json @@ -0,0 +1 @@ +{} diff --git a/new-bing/requirements.txt b/new-bing/requirements.txt new file mode 100644 index 0000000..8ca718d --- /dev/null +++ b/new-bing/requirements.txt @@ -0,0 +1,5 @@ +sanic==22.12.0 +requests==2.28.2 +asyncio==3.4.3 +websockets==10.4 +httpx==0.23.3 diff --git a/new-bing/start.sh b/new-bing/start.sh new file mode 100644 index 0000000..a56de19 --- /dev/null +++ b/new-bing/start.sh @@ -0,0 +1,3 @@ +docker pull yy194131/new-bing:$1 +docker stop new-bing && docker rm new-bing +docker run --restart always --name new-bing --env-file $(pwd)/env -p 8000:8000 -d yy194131/new-bing:$1 diff --git a/wechatbot/.gitignore b/wechatbot/.gitignore new file mode 100644 index 0000000..4983c0e --- /dev/null +++ b/wechatbot/.gitignore @@ -0,0 +1 @@ +storage.json diff --git a/wechatbot/Dockerfile b/wechatbot/Dockerfile new file mode 100644 index 0000000..01b821c --- /dev/null +++ b/wechatbot/Dockerfile @@ -0,0 +1,9 @@ +FROM golang:1.16 as builder +ENV GOPROXY=https://goproxy.cn,direct +WORKDIR /app +COPY . . +RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o chatgpt +FROM alpine:3.17.2 +WORKDIR /app +COPY --from=builder /app/chatgpt . +ENTRYPOINT ["./chatgpt", ">>", "chatgpt.log", "2>&1"] diff --git a/wechatbot/Makefile b/wechatbot/Makefile new file mode 100644 index 0000000..a3cb9a3 --- /dev/null +++ b/wechatbot/Makefile @@ -0,0 +1,3 @@ +build: + docker build -t yy194131/chatgpt:$(version) . + docker push yy194131/chatgpt:$(version) diff --git a/wechatbot/README.md b/wechatbot/README.md new file mode 100644 index 0000000..d68c46b --- /dev/null +++ b/wechatbot/README.md @@ -0,0 +1,31 @@ +# wechatbot +最近chatGPT异常火爆,想到将其接入到个人微信是件比较有趣的事,所以有了这个项目。项目基于[openwechat](https://github.com/eatmoreapple/openwechat)开发,支持chatGPT和New Bing[依赖于new-bing](../new-bing) + +### 目前实现了以下功能 + + 群聊@回复 + + 私聊回复 + + 自动通过回复 + +# 注册openai +chatGPT注册可以参考[这里](https://juejin.cn/post/7173447848292253704) + +# 安装使用 + +``` +# 获取项目 +git clone https://github.com/bujnlc8/gptbing + +# 进入项目目录 +cd gptbing/wechatbot + +# 复制配置文件 +copy config.dev.json config.json + +# 启动项目 +go run main.go + +``` + +本项目fork至[https://github.com/djun/wechatbot](https://github.com/djun/wechatbot),在此致谢! + +**⚠️ 有一定的几率导致被微信封号,请谨慎使用,由此导致的封号,本人概不负责** diff --git a/wechatbot/bootstrap/bootstrap.go b/wechatbot/bootstrap/bootstrap.go new file mode 100644 index 0000000..bd59cd1 --- /dev/null +++ b/wechatbot/bootstrap/bootstrap.go @@ -0,0 +1,31 @@ +package bootstrap + +import ( + "log" + + "github.com/bujnlc8/wechatbot/handlers" + "github.com/eatmoreapple/openwechat" +) + +func Run() { + //bot := openwechat.DefaultBot() + bot := openwechat.DefaultBot(openwechat.Desktop) // 桌面模式,上面登录不上的可以尝试切换这种模式 + + // 注册消息处理函数 + bot.MessageHandler = handlers.Handler + // 注册登陆二维码回调 + bot.UUIDCallback = openwechat.PrintlnQrcodeUrl + + // 创建热存储容器对象 + reloadStorage := openwechat.NewJsonFileHotReloadStorage("storage.json") + // 执行热登录 + err := bot.HotLogin(reloadStorage) + if err != nil { + if err = bot.Login(); err != nil { + log.Printf("login error: %v \n", err) + return + } + } + // 阻塞主goroutine, 直到发生异常或者用户主动退出 + bot.Block() +} diff --git a/wechatbot/config.json.example b/wechatbot/config.json.example new file mode 100644 index 0000000..e105e78 --- /dev/null +++ b/wechatbot/config.json.example @@ -0,0 +1,5 @@ +{ + "api_key": "", + "auto_pass": true, + "bing_chat_url": "" +} diff --git a/wechatbot/config/config.go b/wechatbot/config/config.go new file mode 100644 index 0000000..8142465 --- /dev/null +++ b/wechatbot/config/config.go @@ -0,0 +1,57 @@ +package config + +import ( + "encoding/json" + "log" + "os" + "sync" +) + +// Configuration 项目配置 +type Configuration struct { + // gpt apikey + ApiKey string `json:"api_key"` + // 自动通过好友 + AutoPass bool `json:"auto_pass"` + + // bing 聊天接口 + BingChatUrl string `json:"bing_chat_url"` +} + +var config *Configuration +var once sync.Once + +// LoadConfig 加载配置 +func LoadConfig() *Configuration { + once.Do(func() { + // 从文件中读取 + config = &Configuration{} + f, err := os.Open("config.json") + if err != nil { + log.Fatalf("open config err: %v", err) + return + } + defer f.Close() + encoder := json.NewDecoder(f) + err = encoder.Decode(config) + if err != nil { + log.Fatalf("decode config err: %v", err) + return + } + + // 如果环境变量有配置,读取环境变量 + ApiKey := os.Getenv("ApiKey") + AutoPass := os.Getenv("AutoPass") + BingChatUrl := os.Getenv("BingChatUrl") + if ApiKey != "" { + config.ApiKey = ApiKey + } + if AutoPass == "true" { + config.AutoPass = true + } + if BingChatUrl != "" { + config.BingChatUrl = BingChatUrl + } + }) + return config +} diff --git a/wechatbot/go.mod b/wechatbot/go.mod new file mode 100644 index 0000000..2563054 --- /dev/null +++ b/wechatbot/go.mod @@ -0,0 +1,5 @@ +module github.com/bujnlc8/wechatbot + +go 1.16 + +require github.com/eatmoreapple/openwechat v1.4.2-0.20230321053318-1c102bdea8d7 diff --git a/wechatbot/go.sum b/wechatbot/go.sum new file mode 100644 index 0000000..d2750a9 --- /dev/null +++ b/wechatbot/go.sum @@ -0,0 +1,2 @@ +github.com/eatmoreapple/openwechat v1.4.2-0.20230321053318-1c102bdea8d7 h1:5vryo6dsmZZn1pYo8TrcqEUJq075QMpdDwLZz+oMfLs= +github.com/eatmoreapple/openwechat v1.4.2-0.20230321053318-1c102bdea8d7/go.mod h1:ZxMcq7IpVWVU9JG7ERjExnm5M8/AQ6yZTtX30K3rwRQ= diff --git a/wechatbot/gpt/bing.go b/wechatbot/gpt/bing.go new file mode 100644 index 0000000..c9e4f08 --- /dev/null +++ b/wechatbot/gpt/bing.go @@ -0,0 +1,85 @@ +package gpt + +import ( + "encoding/json" + "io/ioutil" + "log" + "net/http" + "net/url" + "strings" + + "github.com/bujnlc8/wechatbot/config" +) + +type BingQuery struct { + Q string `json:"q"` + SID string `json:"sid"` +} + +type BingResponse struct { + Data BingResponseData `json:"data"` + Cookie string `json:"cookie"` +} + +type BingResponseData struct { + Suggests []string `json:"suggests"` + Status string `json:"status"` + Text string `json:"text"` + Message string `json:"message"` +} + +// const BingChatUrl = "http://127.0.0.1:8000/chat" + +const Referer = "https://servicewechat.com/wxee7496be5b68b740" + +func BingSearch(msg string, nickName string) (string, error) { + params := url.Values{} + params.Add("q", msg) + params.Add("sid", nickName) + params.Add("auto_reset", "1") + log.Printf("request bing query string : %v", params) + BingChatUrl := config.LoadConfig().BingChatUrl + req, err := http.NewRequest("GET", BingChatUrl+"?"+params.Encode(), nil) + if err != nil { + return "", err + } + req.Header.Set("Referer", Referer) + client := &http.Client{} + response, err := client.Do(req) + if err != nil { + return "非常抱歉😭,网络异常,请稍后重试", err + } + if response.StatusCode != 200 { + return "非常抱歉😭,网络异常,请稍后重试 [" + string(rune(response.StatusCode)) + "]", nil + } + defer response.Body.Close() + + body, err := ioutil.ReadAll(response.Body) + if err != nil { + return "响应异常,请稍后再试", err + } + + bingResponse := &BingResponse{} + log.Println(string(body)) + err = json.Unmarshal(body, bingResponse) + if err != nil { + return "", err + } + if bingResponse.Data.Status == "Success" { + if strings.Contains(bingResponse.Data.Text, "New topic") { + return bingResponse.Data.Text + "\n请重新开始对话", nil + } + return bingResponse.Data.Text, nil + + } else { + if bingResponse.Data.Status == "Throttled" { + return "这真是愉快,但你已达到每日限制。是否明天再聊?", nil + } else { + if strings.Contains(bingResponse.Data.Message, "has expired") { + return "本轮对话已过期,请重新开始。", nil + } else { + return "抱歉😭,发生错误:" + bingResponse.Data.Message + ",请重试", nil + } + } + } +} diff --git a/wechatbot/gpt/bing_test.go b/wechatbot/gpt/bing_test.go new file mode 100644 index 0000000..2b59c2b --- /dev/null +++ b/wechatbot/gpt/bing_test.go @@ -0,0 +1,15 @@ +package gpt + +import ( + "fmt" + "testing" +) + +func TestBing(t *testing.T) { + reply, err := BingSearch("今天北京的天气怎么样", "nickname") + if err != nil{ + t.Error(err) + } + fmt.Printf("%+v\n", reply) + +} diff --git a/wechatbot/gpt/gpt.go b/wechatbot/gpt/gpt.go new file mode 100644 index 0000000..6308ee5 --- /dev/null +++ b/wechatbot/gpt/gpt.go @@ -0,0 +1,92 @@ +package gpt + +import ( + "bytes" + "encoding/json" + "io/ioutil" + "log" + "net/http" + + "github.com/bujnlc8/wechatbot/config" +) + +const BASEURL = "https://api.openai.com/v1/chat/" + +type Message struct { + Role string `json:"role"` + Content string `json:"content"` +} + +// ChatGPTResponseBody 请求体 +type ChatGPTResponseBody struct { + ID string `json:"id"` + Object string `json:"object"` + Created int `json:"created"` + Model string `json:"model"` + Choices []ChoiceItem `json:"choices"` + Usage map[string]interface{} `json:"usage"` +} + +type ChoiceItem struct { + Message Message `json:"message"` + FinishReason string `json:"finish_reason"` +} + +// ChatGPTRequestBody 响应体 +type ChatGPTRequestBody struct { + Model string `json:"model"` + MaxTokens int `json:"max_tokens"` + Temperature float32 `json:"temperature"` + Messages []Message `json:"messages"` +} + +func Completions(msg string) (string, error) { + message := Message{Role: "user", Content: msg} + requestBody := ChatGPTRequestBody{ + Model: "gpt-3.5-turbo", + MaxTokens: 2048, + Temperature: 0.2, + Messages: []Message{message}, + } + requestData, err := json.Marshal(requestBody) + + if err != nil { + return "", err + } + log.Printf("request gpt json string : %v", string(requestData)) + req, err := http.NewRequest("POST", BASEURL+"completions", bytes.NewBuffer(requestData)) + if err != nil { + return "", err + } + + apiKey := config.LoadConfig().ApiKey + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Authorization", "Bearer "+apiKey) + client := &http.Client{} + response, err := client.Do(req) + if err != nil { + return "", err + } + defer response.Body.Close() + + body, err := ioutil.ReadAll(response.Body) + if err != nil { + return "", err + } + + gptResponseBody := &ChatGPTResponseBody{} + log.Println(string(body)) + err = json.Unmarshal(body, gptResponseBody) + if err != nil { + return "", err + } + var reply string + if len(gptResponseBody.Choices) > 0 { + for _, v := range gptResponseBody.Choices { + reply = v.Message.Content + break + } + } + log.Printf("gpt response text: %s \n", reply) + return reply, nil +} diff --git a/wechatbot/handlers/group_msg_handler.go b/wechatbot/handlers/group_msg_handler.go new file mode 100644 index 0000000..554e580 --- /dev/null +++ b/wechatbot/handlers/group_msg_handler.go @@ -0,0 +1,78 @@ +package handlers + +import ( + "log" + "strings" + + "github.com/bujnlc8/wechatbot/gpt" + "github.com/eatmoreapple/openwechat" +) + +var _ MessageHandlerInterface = (*GroupMessageHandler)(nil) + +// GroupMessageHandler 群消息处理 +type GroupMessageHandler struct { +} + +// handle 处理消息 +func (g *GroupMessageHandler) handle(msg *openwechat.Message) error { + if msg.IsText() { + return g.ReplyText(msg) + } + msg.ReplyText("目前我只支持文字哦~") + return nil +} + +// NewGroupMessageHandler 创建群消息处理器 +func NewGroupMessageHandler() MessageHandlerInterface { + return &GroupMessageHandler{} +} + +// ReplyText 发送文本消息到群 +func (g *GroupMessageHandler) ReplyText(msg *openwechat.Message) error { + sender, err := msg.Sender() + group := openwechat.Group{User: sender} + log.Printf("Received Group %v Text Msg : %v", group.NickName, msg.Content) + + // @GPTBot 或者 @bing的消息才处理 + if !(strings.Contains(msg.Content, "@GPTBot") || strings.Contains(msg.Content, "@bing")) { + return nil + } + + requestText := strings.TrimSpace(strings.ReplaceAll(msg.Content, "@GPTBot", "")) + var reply = "" + if strings.Contains(msg.Content, "@bing") { + requestText = strings.TrimSpace(strings.ReplaceAll(msg.Content, "@bing", "")) + reply, err = gpt.BingSearch(requestText, sender.NickName) + } else { + + reply, err = gpt.Completions(requestText) + } + if err != nil { + log.Printf("gpt request error: %v \n", err) + msg.ReplyText("机器人神了,我一会发现了就去修。") + return err + } + if reply == "" { + msg.ReplyText("机器人响应为空") + return nil + } + + // 获取@我的用户 + groupSender, err := msg.SenderInGroup() + if err != nil { + log.Printf("get sender in group error :%v \n", err) + return err + } + + // 回复@我的用户 + reply = strings.TrimSpace(reply) + reply = strings.Trim(reply, "\n") + atText := "@" + groupSender.NickName + replyText := atText + " " + reply + _, err = msg.ReplyText(replyText) + if err != nil { + log.Printf("response group error: %v \n", err) + } + return err +} diff --git a/wechatbot/handlers/handler.go b/wechatbot/handlers/handler.go new file mode 100644 index 0000000..efb22e4 --- /dev/null +++ b/wechatbot/handlers/handler.go @@ -0,0 +1,54 @@ +package handlers + +import ( + "log" + + "github.com/bujnlc8/wechatbot/config" + "github.com/eatmoreapple/openwechat" +) + +// MessageHandlerInterface 消息处理接口 +type MessageHandlerInterface interface { + handle(*openwechat.Message) error + ReplyText(*openwechat.Message) error +} + +type HandlerType string + +const ( + GroupHandler = "group" + UserHandler = "user" +) + +// handlers 所有消息类型类型的处理器 +var handlers map[HandlerType]MessageHandlerInterface + +func init() { + handlers = make(map[HandlerType]MessageHandlerInterface) + handlers[GroupHandler] = NewGroupMessageHandler() + handlers[UserHandler] = NewUserMessageHandler() +} + +// Handler 全局处理入口 +func Handler(msg *openwechat.Message) { + log.Printf("hadler Received msg : %v", msg.Content) + // 处理群消息 + if msg.IsSendByGroup() { + handlers[GroupHandler].handle(msg) + return + } + + // 好友申请 + if msg.IsFriendAdd() { + if config.LoadConfig().AutoPass { + _, err := msg.Agree("你好我是基于chatGPT引擎开发的微信机器人,你可以向我提问任何问题。") + if err != nil { + log.Fatalf("add friend agree error : %v", err) + return + } + } + } + + // 私聊 + handlers[UserHandler].handle(msg) +} diff --git a/wechatbot/handlers/user_msg_handler.go b/wechatbot/handlers/user_msg_handler.go new file mode 100644 index 0000000..6a31626 --- /dev/null +++ b/wechatbot/handlers/user_msg_handler.go @@ -0,0 +1,65 @@ +package handlers + +import ( + "log" + "strings" + + "github.com/bujnlc8/wechatbot/gpt" + "github.com/eatmoreapple/openwechat" +) + +var _ MessageHandlerInterface = (*UserMessageHandler)(nil) + +// UserMessageHandler 私聊消息处理 +type UserMessageHandler struct { +} + +// handle 处理消息 +func (g *UserMessageHandler) handle(msg *openwechat.Message) error { + if msg.IsText() { + return g.ReplyText(msg) + } + msg.ReplyText("目前我只支持文字哦~") + return nil +} + +// NewUserMessageHandler 创建私聊处理器 +func NewUserMessageHandler() MessageHandlerInterface { + return &UserMessageHandler{} +} + +// ReplyText 发送文本消息到群 +func (g *UserMessageHandler) ReplyText(msg *openwechat.Message) error { + // 接收私聊消息 + sender, err := msg.Sender() + log.Printf("Received User %v Text Msg : %v", sender.NickName, msg.Content) + + requestText := strings.TrimSpace(msg.Content) + requestText = strings.Trim(msg.Content, "\n") + + var reply = "" + if strings.Contains(msg.Content, "@bing") { + requestText = strings.TrimSpace(strings.ReplaceAll(msg.Content, "@bing", "")) + reply, err = gpt.BingSearch(requestText, sender.NickName) + } else { + reply, err = gpt.Completions(requestText) + } + if err != nil { + log.Printf("gpt request error: %v \n", err) + msg.ReplyText("机器人神了,我一会发现了就去修。") + return err + } + if reply == "" { + msg.ReplyText("机器人响应为空") + return nil + } + + // 回复用户 + reply = strings.TrimSpace(reply) + reply = strings.Trim(reply, "\n") + _, err = msg.ReplyText(reply) + if err != nil { + log.Printf("response user error: %v \n", err) + } + return err +} diff --git a/wechatbot/main.go b/wechatbot/main.go new file mode 100644 index 0000000..4ae64b0 --- /dev/null +++ b/wechatbot/main.go @@ -0,0 +1,9 @@ +package main + +import ( + "github.com/bujnlc8/wechatbot/bootstrap" +) + +func main() { + bootstrap.Run() +}