mirror of
https://github.com/zhayujie/bot-on-anything.git
synced 2026-03-03 16:45:10 +08:00
50
README.md
50
README.md
@@ -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
16
allow-http-nopassword.sh
Normal 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
6
app.py
@@ -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")
|
||||
|
||||
|
||||
@@ -46,4 +46,4 @@ def create_channel(channel_type):
|
||||
return HttpChannel()
|
||||
|
||||
else:
|
||||
raise RuntimeError
|
||||
raise RuntimeError("unknown channel_type in config.json: " + channel_type)
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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 "目前无等待回复信息,请输入对话"
|
||||
|
||||
@@ -12,4 +12,5 @@ HTTP = "http"
|
||||
# model
|
||||
OPEN_AI = "openai"
|
||||
CHATGPT = "chatgpt"
|
||||
BAIDU = "baidu"
|
||||
BAIDU = "baidu"
|
||||
BING = "bing"
|
||||
@@ -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
39
fix-itchat.sh
Executable 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
|
||||
@@ -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):
|
||||
|
||||
84
model/bing/new_bing_model.py
Normal file
84
model/bing/new_bing_model.py
Normal 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())
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user