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()
+}