@@ -40,13 +40,16 @@ pip3 install itchat | |||||
配置文件在根目录的 `config.json` 中,示例文件及各配置项解析如下: (TODO) | 配置文件在根目录的 `config.json` 中,示例文件及各配置项解析如下: (TODO) | ||||
```json | |||||
{ | |||||
```bash | |||||
{ | |||||
"session_token": "YOUR SESSION TOKEN", # 从页面获取的token | |||||
"single_chat_prefix": ["bot", "@bot"], # 私聊触发自动回复的前缀 | |||||
"group_chat_prefix": ["@bot"], # 群聊触发自动回复的前缀 | |||||
"group_name_white_list": ["群名称1", "群名称2"] # 开启自动回复的群名称 | |||||
} | } | ||||
``` | ``` | ||||
其中 token的设置需要在openAI网页端获取: | |||||
其中 session_token 需要在openAI网页端获取: | |||||
- 打开 <https://chat.openai.com/chat> 并登录,可使用测试账号 (lgfo353p@linshiyouxiang.net, 密码yy123123),账号来源为该[文章](https://www.bilibili.com/read/cv20257021) | - 打开 <https://chat.openai.com/chat> 并登录,可使用测试账号 (lgfo353p@linshiyouxiang.net, 密码yy123123),账号来源为该[文章](https://www.bilibili.com/read/cv20257021) | ||||
- F12 进入开发者控制台 | - F12 进入开发者控制台 | ||||
@@ -1,10 +1,12 @@ | |||||
import config | |||||
from channel import channel_factory | from channel import channel_factory | ||||
if __name__ == '__main__': | if __name__ == '__main__': | ||||
# load config | |||||
config.load_config() | |||||
# create channel | # create channel | ||||
channel = channel_factory.create_channel("wx") | channel = channel_factory.create_channel("wx") | ||||
# startup channel | # startup channel | ||||
channel.startup() | channel.startup() | ||||
print("Hello bot") |
@@ -2,17 +2,19 @@ import time | |||||
from bot.bot import Bot | from bot.bot import Bot | ||||
from revChatGPT.revChatGPT import Chatbot | from revChatGPT.revChatGPT import Chatbot | ||||
from common.log import logger | from common.log import logger | ||||
from config import conf | |||||
config = { | |||||
"Authorization": "<Your Bearer Token Here>", # This is optional | |||||
"session_token": "eyJhbGciOiJkaXIiLCJlbmMiOiJBMjU2R0NNIn0..yUEdyIPaNgrKerHa.hQfnBM6Ry2npNvakpj1TL_4wr7fuMeLMWOmy-yOSzJxJw7DNyq5vwKeZBwOzthFBIuSu_CpHvYCK_SvRy2RW0gtjPh1XZMxoXejzwJ8VJaVrj3BjarIJdMKRaIHrFwYlRj6fdWa_nGWeueGf1EDE71aSHf4La-4YjoEX8Ou68XHXEsOYQqMuk06u8Wa_aRq5UAj3Clc99dEw3iHU7xvf8lMmB3T1G1LMaubH21niQj-76pUzlf1Kq278Yl8Q6fOGD_CA7mCvnA1LGYzo7u0P5A8dd-p7K3Oqbxw3Gn2TMyEkzZ2q_rTqSJwnbRG87SEYp5Y6HzYyfNoqM_Ew3OGQqk9PHbv8CjKN6sR53UMNRJeFxkW2owCsR0eCvc8kL-tc5RyHWF3zWVmlOxmzDaHZo_XlA0fgEpjlMZS1ClHCBT6_ZoQRvKan0dkFfhJEdp35aK_v9DLXs46Sfs2rqfN0Fdr698gv0UbGsLdeR00W7M9qMsvXoFDBW3-GnIqsxjjepDPlv4RInMKfSeVdISp4VPWW-GjGyzCB1ooWiyZybaGul1FsdXVSibMq6qsiGUQNr08uf_In3NUPFCKNxJ2iR6A_5-TEiIIjcK6ywbI87L13PFT3oCCXiFxRPjp4f1nUUTGxLcetGzYC_eYmQD004R5M5u1epQdWen4Of1Fzn7D0sOWibSHcl_J9xSxzzVt__b9NVDWieoaGYCO3MJCDVmucfFZ1UFPhIRwsr3nUnom8mnXJocDDSPlb0EWfZhCrMgjhPt7Iqcjg-uNB9QYZNjtHASAcQlUYx5GfP6IZs47UqqLPRlzUISsc65CPyQF6sFgwPO1GNy5Q7QcCUQreMmJdBUyYEUnrKCurZkfRWx2eEJ_1efnnprtUc_Upniar-5PJxAfsZ0Mw4QRweIriRB2CW7L24yZsLR7Q8xzm0vj4KeXeuZ7ZlJJw5f65xndTNwII0jS1-VriBsnKs1SDXJc3WEiviifG8Lx-IjirXoH8Q7RtcqPpRURJApu1aIaWtvSEw5mCGjynuINufN_GEu2r71i231_58IYSK9fBpmRKCkHmTZWkjJmiyhFaG2aYI8Z5UwXEUhOZoijb10ZGgcyW6cnSzuthWfa5VzcYFOa35tE69_xZ8W2A6YKuJeJlW01oXirYxtBazyG2o3dpg-mD_BD7hgU4_ONU8SBXubtbxtCzWqNzIg5F0d0e2pc5aNaDJH3yzK5X1y1nlBZe59l3vCmpmvBfgWzI2Q1pbM_me-1g5-w6ju_waQLvR-DPuUOown_EbiCZ7Zd7BAszVlPMgAMWIJ3AljMceIj6ned8YFldZztM_RdvM1qW_KohVurd_bt3vvIBD_c7gttJzAohod61TYBtu5esXNr-sHQNYfapPp8U8J6KZjJFJVEPdrNYeGFewVFVsgRCx_WfaEyIUTaoC9d_ZTDX_nFn_GJceUANqsJYB_FSbzz7aZLj2WKGK8WKw6ujkSMOrLpspt0meqohTWcV68aIMNDhLdOGS7R53vnTUoyrfGLi2HH9QyF5sjjy_YFz1Z9B1Pcv38c9XKBxHUCMNS9ws6IZlIMaA9z8F5_2s-LSKR88Atnb82gQy-BsRdffTI1IhnLLPeisPv_dVOChCdEVHmTMKDvkiYp9GobHW9V3WBm48K2mYDjR6eW459uJP1TVjGP00-O0FZDHTcBZ9L-pq93EbdhYv1VdT-S8UHWy_zoksV-D-iOmc8DKvEHU3CJcUlEf63vcRuBRzfJdeqvw2E4J5j9tt2tkAO814kng2fZaob8XUmjF3QKevYvY8NKAevp6cpoT7DIDZttT6mEkUy1pW02thh3FMqr_EfsCI5pR84BQCr-LIRDzhudnOxHxXOJXE_zuEt1QNNSAH0eqxA6Mx7N5p3WU7dU0ULGPMtEEXz1IwiGAJ21z8Z77EOrL3vOWUnq3Y1sAPD9PBzaPC8_wezBIXQqI6Ufa6D38xPdZnkoaxMd3PRFu91s9qMoYO4OZ1WfjbQJi51T4M76y1K73eBLGwVgWRguEs8Yqr2F-ctZ-BiSEa2RfOwYcmT5uRbFzZnQCtj32JTNMSIFGQ5It5bR0nPh5BK6LjK2_kbQny6dZ9d_KrBcl15REEKM9XhZOSGWRRwAmf_4iVsy6ceqXMuYMbEGL7xnw6tmBWzuuN21_1RnxY8JS8CtzjPC4LAIRgN7VE4M6LbEcQJiaw9hVsUzLueoP-CCtjBeqQ1ylsaz6C4rOpDISATG-jAQ66FE9P0YHXQXOAip4Bf7KO-IvSvZF_dKv2_RMdAE53dlpup5oCWqPk8qAVNgZXIT6LN8MiqRDVfObQDa-uElVtX8ll7ItOUXRoXUJfxabE6oW.bDrh_KN_-hbGsWTk_0z35g" | |||||
} | |||||
chatbot = Chatbot(config) | |||||
user_session = dict() | user_session = dict() | ||||
last_session_refresh = time.time() | last_session_refresh = time.time() | ||||
class ChatGPTBot(Bot): | class ChatGPTBot(Bot): | ||||
def __init__(self): | |||||
config = { | |||||
"Authorization": "<Your Bearer Token Here>", # This is optional | |||||
"session_token": conf().get("session_token") | |||||
} | |||||
self.chatbot = Chatbot(config) | |||||
def reply(self, query, context=None): | def reply(self, query, context=None): | ||||
from_user_id = context['from_user_id'] | from_user_id = context['from_user_id'] | ||||
@@ -22,22 +24,22 @@ class ChatGPTBot(Bot): | |||||
global last_session_refresh | global last_session_refresh | ||||
if now - last_session_refresh > 60 * 8: | if now - last_session_refresh > 60 * 8: | ||||
logger.info('[GPT]session refresh, now={}, last={}'.format(now, last_session_refresh)) | logger.info('[GPT]session refresh, now={}, last={}'.format(now, last_session_refresh)) | ||||
chatbot.refresh_session() | |||||
self.chatbot.refresh_session() | |||||
last_session_refresh = now | last_session_refresh = now | ||||
if from_user_id in user_session: | if from_user_id in user_session: | ||||
if time.time() - user_session[from_user_id]['last_reply_time'] < 60 * 5: | if time.time() - user_session[from_user_id]['last_reply_time'] < 60 * 5: | ||||
chatbot.conversation_id = user_session[from_user_id]['conversation_id'] | |||||
chatbot.parent_id = user_session[from_user_id]['parent_id'] | |||||
self.chatbot.conversation_id = user_session[from_user_id]['conversation_id'] | |||||
self.chatbot.parent_id = user_session[from_user_id]['parent_id'] | |||||
else: | else: | ||||
chatbot.reset_chat() | |||||
self.chatbot.reset_chat() | |||||
else: | else: | ||||
chatbot.reset_chat() | |||||
self.chatbot.reset_chat() | |||||
logger.info("[GPT]convId={}, parentId={}".format(chatbot.conversation_id, chatbot.parent_id)) | |||||
logger.info("[GPT]convId={}, parentId={}".format(self.chatbot.conversation_id, self.chatbot.parent_id)) | |||||
try: | try: | ||||
res = chatbot.get_chat_response(query, output="text") | |||||
res = self.chatbot.get_chat_response(query, output="text") | |||||
logger.info("[GPT]userId={}, res={}".format(from_user_id, res)) | logger.info("[GPT]userId={}, res={}".format(from_user_id, res)) | ||||
user_cache = dict() | user_cache = dict() | ||||
@@ -7,6 +7,7 @@ from itchat.content import * | |||||
from channel.channel import Channel | from channel.channel import Channel | ||||
from concurrent.futures import ThreadPoolExecutor | from concurrent.futures import ThreadPoolExecutor | ||||
from common.log import logger | from common.log import logger | ||||
from config import conf | |||||
thead_pool = ThreadPoolExecutor(max_workers=8) | thead_pool = ThreadPoolExecutor(max_workers=8) | ||||
@@ -21,9 +22,6 @@ def handler_group_msg(msg): | |||||
WechatChannel().handle_group(msg) | WechatChannel().handle_group(msg) | ||||
group_white_list = ['测试群1', '测试群2'] | |||||
class WechatChannel(Channel): | class WechatChannel(Channel): | ||||
def __init__(self): | def __init__(self): | ||||
pass | pass | ||||
@@ -40,19 +38,22 @@ class WechatChannel(Channel): | |||||
from_user_id = msg['FromUserName'] | from_user_id = msg['FromUserName'] | ||||
other_user_id = msg['User']['UserName'] | other_user_id = msg['User']['UserName'] | ||||
content = msg['Text'] | content = msg['Text'] | ||||
if from_user_id == other_user_id and (content.lower().startswith('bot') or content.lower().startswith('@bot')): | |||||
if from_user_id == other_user_id and \ | |||||
self.check_prefix(content, conf().get('group_chat_prefix')): | |||||
str_list = content.split('bot', 1) | str_list = content.split('bot', 1) | ||||
if len(str_list) == 2: | if len(str_list) == 2: | ||||
content = str_list[1].strip() | content = str_list[1].strip() | ||||
thead_pool.submit(self._do_send, content, from_user_id) | thead_pool.submit(self._do_send, content, from_user_id) | ||||
def handle_group(self, msg): | def handle_group(self, msg): | ||||
logger.info("[WX]receive group msg: " + json.dumps(msg, ensure_ascii=False)) | logger.info("[WX]receive group msg: " + json.dumps(msg, ensure_ascii=False)) | ||||
group_id = msg['User']['UserName'] | group_id = msg['User']['UserName'] | ||||
group_name = msg['User'].get('NickName', None) | group_name = msg['User'].get('NickName', None) | ||||
if not group_name: | if not group_name: | ||||
return "" | return "" | ||||
origin_content = msg['Content'] | |||||
origin_content = msg['Content'] | |||||
content = msg['Content'] | content = msg['Content'] | ||||
content_list = content.split(' ', 1) | content_list = content.split(' ', 1) | ||||
context_special_list = content.split('\u2005', 1) | context_special_list = content.split('\u2005', 1) | ||||
@@ -61,7 +62,9 @@ class WechatChannel(Channel): | |||||
elif len(content_list) == 2: | elif len(content_list) == 2: | ||||
content = content_list[1] | content = content_list[1] | ||||
if group_name in group_white_list and (msg['IsAt'] or origin_content.lower().startswith('@bot')): | |||||
config = conf() | |||||
if group_name in config.get('group_name_white_list') \ | |||||
and (msg['IsAt'] or self.check_prefix(origin_content, config.get('group_chat_prefix'))): | |||||
thead_pool.submit(self._do_send_group, content, msg) | thead_pool.submit(self._do_send_group, content, msg) | ||||
def send(self, msg, receiver): | def send(self, msg, receiver): | ||||
@@ -83,3 +86,10 @@ class WechatChannel(Channel): | |||||
reply_text = '@' + msg['ActualNickName'] + ' ' + reply_text | reply_text = '@' + msg['ActualNickName'] + ' ' + reply_text | ||||
if reply_text: | if reply_text: | ||||
self.send(reply_text, msg['User']['UserName']) | self.send(reply_text, msg['User']['UserName']) | ||||
def check_prefix(self, content, prefix_list): | |||||
for prefix in prefix_list: | |||||
if content.lower().startswith(prefix): | |||||
return True | |||||
return False | |||||
@@ -1,3 +1,6 @@ | |||||
{ | { | ||||
"bot_": "123" | |||||
} | |||||
"session_token": "eyJhbGciOiJkaXIiLCJlbmMiOiJBMjU2R0NNIn0..yUEdyIPaNgrKerHa.hQfnBM6Ry2npNvakpj1TL_4wr7fuMeLMWOmy-yOSzJxJw7DNyq5vwKeZBwOzthFBIuSu_CpHvYCK_SvRy2RW0gtjPh1XZMxoXejzwJ8VJaVrj3BjarIJdMKRaIHrFwYlRj6fdWa_nGWeueGf1EDE71aSHf4La-4YjoEX8Ou68XHXEsOYQqMuk06u8Wa_aRq5UAj3Clc99dEw3iHU7xvf8lMmB3T1G1LMaubH21niQj-76pUzlf1Kq278Yl8Q6fOGD_CA7mCvnA1LGYzo7u0P5A8dd-p7K3Oqbxw3Gn2TMyEkzZ2q_rTqSJwnbRG87SEYp5Y6HzYyfNoqM_Ew3OGQqk9PHbv8CjKN6sR53UMNRJeFxkW2owCsR0eCvc8kL-tc5RyHWF3zWVmlOxmzDaHZo_XlA0fgEpjlMZS1ClHCBT6_ZoQRvKan0dkFfhJEdp35aK_v9DLXs46Sfs2rqfN0Fdr698gv0UbGsLdeR00W7M9qMsvXoFDBW3-GnIqsxjjepDPlv4RInMKfSeVdISp4VPWW-GjGyzCB1ooWiyZybaGul1FsdXVSibMq6qsiGUQNr08uf_In3NUPFCKNxJ2iR6A_5-TEiIIjcK6ywbI87L13PFT3oCCXiFxRPjp4f1nUUTGxLcetGzYC_eYmQD004R5M5u1epQdWen4Of1Fzn7D0sOWibSHcl_J9xSxzzVt__b9NVDWieoaGYCO3MJCDVmucfFZ1UFPhIRwsr3nUnom8mnXJocDDSPlb0EWfZhCrMgjhPt7Iqcjg-uNB9QYZNjtHASAcQlUYx5GfP6IZs47UqqLPRlzUISsc65CPyQF6sFgwPO1GNy5Q7QcCUQreMmJdBUyYEUnrKCurZkfRWx2eEJ_1efnnprtUc_Upniar-5PJxAfsZ0Mw4QRweIriRB2CW7L24yZsLR7Q8xzm0vj4KeXeuZ7ZlJJw5f65xndTNwII0jS1-VriBsnKs1SDXJc3WEiviifG8Lx-IjirXoH8Q7RtcqPpRURJApu1aIaWtvSEw5mCGjynuINufN_GEu2r71i231_58IYSK9fBpmRKCkHmTZWkjJmiyhFaG2aYI8Z5UwXEUhOZoijb10ZGgcyW6cnSzuthWfa5VzcYFOa35tE69_xZ8W2A6YKuJeJlW01oXirYxtBazyG2o3dpg-mD_BD7hgU4_ONU8SBXubtbxtCzWqNzIg5F0d0e2pc5aNaDJH3yzK5X1y1nlBZe59l3vCmpmvBfgWzI2Q1pbM_me-1g5-w6ju_waQLvR-DPuUOown_EbiCZ7Zd7BAszVlPMgAMWIJ3AljMceIj6ned8YFldZztM_RdvM1qW_KohVurd_bt3vvIBD_c7gttJzAohod61TYBtu5esXNr-sHQNYfapPp8U8J6KZjJFJVEPdrNYeGFewVFVsgRCx_WfaEyIUTaoC9d_ZTDX_nFn_GJceUANqsJYB_FSbzz7aZLj2WKGK8WKw6ujkSMOrLpspt0meqohTWcV68aIMNDhLdOGS7R53vnTUoyrfGLi2HH9QyF5sjjy_YFz1Z9B1Pcv38c9XKBxHUCMNS9ws6IZlIMaA9z8F5_2s-LSKR88Atnb82gQy-BsRdffTI1IhnLLPeisPv_dVOChCdEVHmTMKDvkiYp9GobHW9V3WBm48K2mYDjR6eW459uJP1TVjGP00-O0FZDHTcBZ9L-pq93EbdhYv1VdT-S8UHWy_zoksV-D-iOmc8DKvEHU3CJcUlEf63vcRuBRzfJdeqvw2E4J5j9tt2tkAO814kng2fZaob8XUmjF3QKevYvY8NKAevp6cpoT7DIDZttT6mEkUy1pW02thh3FMqr_EfsCI5pR84BQCr-LIRDzhudnOxHxXOJXE_zuEt1QNNSAH0eqxA6Mx7N5p3WU7dU0ULGPMtEEXz1IwiGAJ21z8Z77EOrL3vOWUnq3Y1sAPD9PBzaPC8_wezBIXQqI6Ufa6D38xPdZnkoaxMd3PRFu91s9qMoYO4OZ1WfjbQJi51T4M76y1K73eBLGwVgWRguEs8Yqr2F-ctZ-BiSEa2RfOwYcmT5uRbFzZnQCtj32JTNMSIFGQ5It5bR0nPh5BK6LjK2_kbQny6dZ9d_KrBcl15REEKM9XhZOSGWRRwAmf_4iVsy6ceqXMuYMbEGL7xnw6tmBWzuuN21_1RnxY8JS8CtzjPC4LAIRgN7VE4M6LbEcQJiaw9hVsUzLueoP-CCtjBeqQ1ylsaz6C4rOpDISATG-jAQ66FE9P0YHXQXOAip4Bf7KO-IvSvZF_dKv2_RMdAE53dlpup5oCWqPk8qAVNgZXIT6LN8MiqRDVfObQDa-uElVtX8ll7ItOUXRoXUJfxabE6oW.bDrh_KN_-hbGsWTk_0z35g", | |||||
"single_chat_prefix": ["bot", "@bot"], | |||||
"group_chat_prefix": ["@bot"], | |||||
"group_name_white_list": ["群名称1", "群名称2"] | |||||
} |
@@ -0,0 +1,33 @@ | |||||
import json | |||||
import os | |||||
from common.log import logger | |||||
config = {} | |||||
def load_config(): | |||||
global config | |||||
config_path = "config.json" | |||||
try: | |||||
if not os.path.exists(config_path): | |||||
logger.error('配置文件路径不存在') | |||||
return | |||||
config_str = read_file(config_path) | |||||
# 将json字符串反序列化为dict类型 | |||||
config = json.loads(config_str) | |||||
logger.info("[INIT] load config: {}".format(config)) | |||||
except Exception as e: | |||||
logger.error(e) | |||||
def get_root(): | |||||
return os.path.dirname(os.path.abspath( __file__ )) | |||||
def read_file(path): | |||||
with open(path, 'r') as f: | |||||
return f.read() | |||||
def conf(): | |||||
return config |