Merge pull request #3 from RegimenArsenic/master

merge master
This commit is contained in:
RegimenArsenic
2023-03-22 00:06:54 +08:00
committed by GitHub
14 changed files with 277 additions and 40 deletions

View File

@@ -7,6 +7,7 @@
- [x] [ChatGPT (gpt-3.5)](https://github.com/zhayujie/bot-on-anything#1-chatgpt)
- [x] [GPT-3.0](https://github.com/zhayujie/bot-on-anything#2gpt-30)
- [x] 文心一言 (测试版)
- [x] [New Bing](https://github.com/zhayujie/bot-on-anything#4newbing)
**应用:**
@@ -140,6 +141,45 @@ pip3 install --upgrade openai
参考: [#154](https://github.com/zhayujie/bot-on-anything/issues/154)
### 4.NewBing
使用的是https://github.com/acheong08/EdgeGPT 网页版逆向API
#### (1) 安装依赖
```bash
pip3 install EdgeGPT --upgrade
```
#### (2) 配置项说明
```bash
{
"model": {
"type" : "bing",
"bing": {
"cookies":[] //edge登录https://www.bing.com/new 获取的json格式name为"_U"的cookies目前看cookies有效期应该为14天
}
}
cookie示例:
"cookies":[
{
"domain": ".bing.com",
"expirationDate": 1680372573.67057,
"hostOnly": false,
"httpOnly": false,
"name": "_U",
"path": "/",
"sameSite": "no_restriction",
"secure": true,
"session": false,
"storeId": null,
"value": ""
}
]
```
## 三、选择应用
### 1.命令行终端
@@ -162,6 +202,11 @@ pip3 install --upgrade openai
```
注:`itchat-uos`使用指定版本1.5.0.dev0`openai`使用最新版本需高于0.27.0。
**修复 itchat-uos bug**
···
bash fix-itchat.sh
···
**配置项说明:**
@@ -435,3 +480,8 @@ pip3 install PyJWT flask
本地运行:`python3 app.py`运行后访问 `http://127.0.0.1:80`
服务器运行:部署后访问 `http://公网域名或IP:端口`
**允许无密码访问**
```
bash ./allow-http-nopassword.sh
```

16
allow-http-nopassword.sh Normal file
View File

@@ -0,0 +1,16 @@
#!/usr/bin/env bash
set -x
# @see https://stackoverflow.com/questions/30003570/how-to-use-gnu-sed-on-mac-os-10-10-brew-install-default-names-no-longer-su
# @see https://www.cnblogs.com/fnlingnzb-learner/p/10657285.html
cmd=sed
if [ "$(uname)" == "Darwin" ];then
brew install gnu-sed
cmd=gsed
fi
echo "current sed command is: $cmd"
echo "allow http nopasword"
$cmd -i "s/\"http_auth_password\": \".*\"/\"http_auth_password\": \"\"/" config.json

6
app.py
View File

@@ -3,6 +3,7 @@
import config
from channel import channel_factory
from common import log
import os
from multiprocessing import Pool
def startProcess(channel_type):
@@ -21,6 +22,11 @@ if __name__ == '__main__':
# load config
config.load_config()
proxy = config.conf().get("model").get("openai").get("proxy")
if proxy:
os.environ['http_proxy'] = proxy
os.environ['https_proxy'] = proxy
model_type = config.conf().get("model").get("type")
channel_type = config.conf().get("channel").get("type")

View File

@@ -46,4 +46,4 @@ def create_channel(channel_type):
return HttpChannel()
else:
raise RuntimeError
raise RuntimeError("unknown channel_type in config.json: " + channel_type)

View File

@@ -83,6 +83,9 @@ def identify(request):
:return: list
"""
try:
authPassword = channel_conf(const.HTTP).get('http_auth_password')
if (not authPassword):
return True
if (request is None):
return False
authorization = request.cookies.get('Authorization')

View File

@@ -41,7 +41,7 @@ ConvState.prototype.sendMessage = function (msg) {
$.ajax({
url: "./chat",
type: "POST",
timeout:60000,
timeout:180000,
data: JSON.stringify({
"id": _this.id,
"msg": msg

View File

@@ -13,7 +13,6 @@ from common.log import logger
from common import const
from config import channel_conf_val
import requests
from urllib.parse import urlencode
from common.sensitive_word import SensitiveWord
@@ -41,11 +40,19 @@ class WechatChannel(Channel):
def startup(self):
# login by scan QRCode
itchat.auto_login(enableCmdQR=2, hotReload=True)
if (channel_conf_val(const.WECHAT, 'receive_qrcode_api')):
itchat.auto_login(enableCmdQR=2, hotReload=True, qrCallback=self.login)
else:
itchat.auto_login(enableCmdQR=2, hotReload=True)
# start message listener
itchat.run()
def login(self, uuid=None, status='0', qrcode=None):
print('uuid:', uuid)
print('status:', status)
# 请将链接转发到外部接口,并在外部自行通过二维码生成库将链接转换为二维码后展示,例如:将下方的 qrcode_link 通过草料二维码进行处理后,再通过手机端扫码登录微信小号
print('qrcode_link:', 'https://login.weixin.qq.com/l/'+uuid)
def handle(self, msg):
logger.debug("[WX]receive msg: " + json.dumps(msg, ensure_ascii=False))

View File

@@ -46,36 +46,49 @@ class WechatSubsribeAccount(Channel):
robot.config['HOST'] = '0.0.0.0'
robot.run()
def handle(self, msg, count=0):
def handle(self, msg, count=1):
if msg.content == "继续":
return self.get_un_send_content(msg.source)
context = dict()
context['from_user_id'] = msg.source
key = msg.source
key = msg.content + '|' + msg.source
res = cache.get(key)
if msg.content == "继续":
if not res or res.get("status") == "done":
return "目前不在等待回复状态,请输入对话"
if res.get("status") == "waiting":
return "还在处理中,请稍后再试"
elif res.get("status") == "success":
cache[key] = {"status":"done"}
return res.get("data")
else:
return "目前不在等待回复状态,请输入对话"
elif not res or res.get('status') == "done":
if not res:
cache[key] = {"status": "waiting", "req_times": 1}
thread_pool.submit(self._do_send, msg.content, context)
res = cache.get(key)
logger.info("count={}, res={}".format(count, res))
if res.get('status') == 'success':
res['status'] = "done"
cache.pop(key)
return res.get("data")
if cache.get(key)['req_times'] == 3 and count >= 4:
logger.info("微信超时3次")
return "已开始处理,请稍等片刻后输入\"继续\"查看回复"
else:
if res.get('status') == "done":
reply = res.get("data")
thread_pool.submit(self._do_send, msg.content, context)
return reply
else:
return "上一句对话正在处理中,请稍后输入\"继续\"查看回复"
if count <= 5:
time.sleep(1)
if count == 5:
# 第5秒不做返回防止消息发送出去了但是微信已经中断连接
return None
return self.handle(msg, count+1)
def _do_send(self, query, context):
key = context['from_user_id']
cache[key] = {"status": "waiting"}
key = query + '|' + context['from_user_id']
reply_text = super().build_reply_content(query, context)
logger.info('[WX_Public] reply content: {}'.format(reply_text))
cache[key]['status'] = "success"
cache[key]['data'] = reply_text
cache[key] = {"status": "success", "data": reply_text}
def get_un_send_content(self, from_user_id):
for key in cache:
if from_user_id in key:
value = cache[key]
if value.get('status') == "success":
cache.pop(key)
return value.get("data")
return "还在处理中,请稍后再试"
return "目前无等待回复信息,请输入对话"

View File

@@ -12,4 +12,5 @@ HTTP = "http"
# model
OPEN_AI = "openai"
CHATGPT = "chatgpt"
BAIDU = "baidu"
BAIDU = "baidu"
BING = "bing"

View File

@@ -11,9 +11,11 @@
"baidu": {
"acs_token": "YOUR ACS TOKEN",
"cookie": "YOUR COOKIE"
},
"bing":{
"cookies":[]
}
},
"channel": {
"type": ["http","wechat","terminal"],
"single_chat_prefix": ["bot", "@bot"],
@@ -26,6 +28,7 @@
},
"wechat": {
"receive_qrcode_api": ""
},
"wechat_mp": {

39
fix-itchat.sh Executable file
View File

@@ -0,0 +1,39 @@
#!/usr/bin/env bash
set -x
# @see https://stackoverflow.com/questions/30003570/how-to-use-gnu-sed-on-mac-os-10-10-brew-install-default-names-no-longer-su
# @see https://www.cnblogs.com/fnlingnzb-learner/p/10657285.html
cmd=sed
if [ "$(uname)" == "Darwin" ];then
brew install gnu-sed
cmd=gsed
fi
echo "current sed command is: $cmd"
pack_dir="$(pip3 show itchat-uos | grep "Location" | awk '{print $2}')"
file_name="${pack_dir}/itchat/components/login.py"
sleep15Code="time.sleep(15)"
cat $file_name | grep $sleep15Code
if [ "$?" != "0" ];then
echo "fix $sleep15Code"
$cmd -i "/while not isLoggedIn/i\ $sleep15Code" $file_name
else
echo "already fix $sleep15Code"
fi
sleep3Code="time.sleep(3)"
cat $file_name | grep $sleep3Code
if [ "$?" != "0" ];then
echo "fix $sleep3Code"
$cmd -i "s/elif status != '408'/elif status in ['408', '400']/" $file_name
$cmd -i "/if isLoggedIn:/i\ time.sleep(3)" $file_name
else
echo "already fix $sleep3Code"
fi

View File

@@ -3,10 +3,9 @@
from model.model import Model
from config import model_conf
from common import const
from common import log
from common.log import logger
import requests
import time
import json
sessions = {}
@@ -17,7 +16,9 @@ class YiyanModel(Model):
self.base_url = 'https://yiyan.baidu.com/eb'
def reply(self, query, context=None):
logger.info("[BAIDU] query={}".format(query))
user_id = context.get('session_id') or context.get('from_user_id')
context['query'] = query
# 1.create session
chat_session_id = sessions.get(user_id)
@@ -28,8 +29,9 @@ class YiyanModel(Model):
context['chat_session_id'] = chat_session_id
# 2.create chat
context['query'] = query
self.new_chat(context)
flag = self.new_chat(context)
if not flag:
return "创建会话失败,请稍后再试"
# 3.query
context['reply'] = ''
@@ -40,13 +42,14 @@ class YiyanModel(Model):
def new_session(self, context):
data = {
"sessionName": "test session",
"sessionName": context['query'],
"timestamp": int(time.time() * 1000),
"deviceType": "pc"
}
res = requests.post(url=self.base_url+'/session/new', headers=self._create_header(), json=data)
# print(res.headers)
context['chat_session_id'] = res.json()['data']['sessionId']
logger.info("[BAIDU] newSession: id={}".format(context['chat_session_id']))
def new_chat(self, context):
@@ -62,10 +65,13 @@ class YiyanModel(Model):
"code": 0,
"msg": ""
}
res = requests.post(url=self.base_url+'/chat/new', headers=headers, json=data)
print(res.text)
context['chat_id'] = res.json()['data']['botChat']['id']
context['parent_chat_id'] = res.json()['data']['botChat']['parent']
res = requests.post(url=self.base_url+'/chat/new', headers=headers, json=data).json()
if res['code'] != 0:
logger.error("[BAIDU] New chat error, msg={}", res['msg'])
return False
context['chat_id'] = res['data']['botChat']['id']
context['parent_chat_id'] = res['data']['botChat']['parent']
return True
def query(self, context, sentence_id, count):
@@ -79,10 +85,13 @@ class YiyanModel(Model):
"timestamp": 1679068791405,
"deviceType": "pc"
}
res = requests.post(url=self.base_url + '/chat/query', headers=headers, json=data).json()
res = requests.post(url=self.base_url + '/chat/query', headers=headers, json=data)
logger.debug("[BAIDU] query: sent_id={}, count={}, res={}".format(sentence_id, count, res.text))
res = res.json()
if res['data']['text'] != '':
context['reply'] += res['data']['text']
# logger.debug("[BAIDU] query: sent_id={}, reply={}".format(sentence_id, res['data']['text']))
if res['data']['is_end'] == 1:
return
@@ -90,9 +99,11 @@ class YiyanModel(Model):
if count > 10:
return
time.sleep(1)
if not res['data']['text']:
time.sleep(1)
return self.query(context, sentence_id, count+1)
else:
return self.query(context, sentence_id+1, count+1)
def _create_header(self):

View File

@@ -0,0 +1,84 @@
# encoding:utf-8
import asyncio
from model.model import Model
from config import model_conf_val
from common import log
from EdgeGPT import Chatbot, ConversationStyle
user_session = dict()
suggestion_session = dict()
# newBing对话模型逆向网页gitAPI
class BingModel(Model):
style = ConversationStyle.creative
bot: Chatbot = None
def __init__(self):
try:
self.bot = Chatbot(cookies=model_conf_val("bing", "cookies"))
except Exception as e:
log.exception(e)
def reply(self, query: str, context=None) -> tuple[str, dict]:
bot = user_session.get(context['from_user_id'], None)
if (bot == None):
bot = self.bot
else:
if (len(query) == 1 and query.isdigit() and query != "0"):
suggestion_dict = suggestion_session[context['from_user_id']]
if (suggestion_dict != None):
query = suggestion_dict[int(query)-1]
if (query == None):
return "输入的序号不在建议列表范围中"
else:
query = "在上面的基础上,"+query
log.info("[NewBing] query={}".format(query))
task = bot.ask(query, conversation_style=self.style)
answer = asyncio.run(task)
# 最新一条回复
reply = answer["item"]["messages"][-1]
reply_text = reply["text"]
reference = ""
if "sourceAttributions" in reply:
for i, attribution in enumerate(reply["sourceAttributions"]):
display_name = attribution["providerDisplayName"]
url = attribution["seeMoreUrl"]
reference += f"{i+1}、[{display_name}]({url})\n\n"
if len(reference) > 0:
reference = "***\n"+reference
suggestion = ""
if "suggestedResponses" in reply:
suggestion_dict = dict()
for i, attribution in enumerate(reply["suggestedResponses"]):
suggestion_dict[i] = attribution["text"]
suggestion += f">{i+1}{attribution['text']}\n\n"
suggestion_session[context['from_user_id']] = suggestion_dict
if len(suggestion) > 0:
suggestion = "***\n你可以通过输入序号快速追问我以下建议问题:\n\n"+suggestion
throttling = answer["item"]["throttling"]
throttling_str = ""
if throttling["numUserMessagesInConversation"] == throttling["maxNumUserMessagesInConversation"]:
self.reset_chat(context['from_user_id'])
throttling_str = "(对话轮次已达上限,本次聊天已结束,将开启新的对话)"
else:
throttling_str = f"对话轮次: {throttling['numUserMessagesInConversation']}/{throttling['maxNumUserMessagesInConversation']}\n"
response = f"{reply_text}\n{reference}\n{suggestion}\n***\n{throttling_str}"
log.info("[NewBing] reply={}", response)
user_session[context['from_user_id']] = bot
return response
else:
self.reset_chat(context['from_user_id'])
log.warn("[NewBing] reply={}", answer)
return "对话被接口拒绝,已开启新的一轮对话。"
def reset_chat(self, from_user_id):
asyncio.run(user_session.get(from_user_id, None).reset())

View File

@@ -25,5 +25,9 @@ def create_bot(model_type):
from model.baidu.yiyan_model import YiyanModel
return YiyanModel()
elif model_type == const.BING:
from model.bing.new_bing_model import BingModel
return BingModel()
raise RuntimeError