Compare commits

...

63 Commits

Author SHA1 Message Date
Rock Chin
a716f071be Release v2.2.0 2023-03-12 20:48:15 +08:00
Rock Chin
3450a91824 Merge pull request #262 from chordfish-k/json_scenario
[Feat] 情景预设(人格)完善
2023-03-12 20:40:20 +08:00
Rock Chin
d2c2b457e5 fix: !list指令显示的是机器人第一次回复 (#264) 2023-03-12 20:31:28 +08:00
Rock Chin
9cd7e49804 feat: 分离储存会话情景预设和对话内容 2023-03-11 23:44:22 +08:00
Rock Chin
e9155e836f feat: 允许通过前缀指定使用的JSON情景 2023-03-10 23:49:41 +08:00
Rock Chin
ed248539c7 doc: 标记二群已满 2023-03-10 23:33:54 +08:00
Rock Chin
54cc75506f feat: 使用模板储存默认的json格式的情景预设 2023-03-10 23:26:36 +08:00
Rock Chin
4269c7927e chore: typo 2023-03-10 23:14:32 +08:00
Rock Chin
064ac7f603 feat: 添加窗口处于暂停模式的提示 2023-03-10 22:50:07 +08:00
Rock Chin
48ccf15273 Merge pull request #263 from RockChinQ/history-deletion
[Feat] 支持删除指定当前会话的指定或全部历史记录 (#239)
2023-03-10 22:40:19 +08:00
Rock Chin
b920ced6d4 feat: !delhst 指令支持管理员删除会话历史记录 2023-03-10 21:20:19 +08:00
Rock Chin
69610a674c perf: 更改help中指令信息帮助 2023-03-10 21:11:55 +08:00
Rock Chin
1828e34190 feat: 支持删除指定当前会话的指定或全部历史记录 (#239) 2023-03-10 21:04:20 +08:00
chordfish
d53f4e3917 adjust:修改,去除neko.json,以及一些占位的变量等 2023-03-10 19:37:29 +08:00
chordfish
01706d5b4e Delete mesugaki.json 2023-03-10 16:17:47 +08:00
chordfish
8916b8a450 Update manager.py 2023-03-10 16:17:07 +08:00
chordfish
ed33af5638 Update README.md 2023-03-10 14:10:54 +08:00
chordfish
c94a9e1ae6 bug:修复上次更新后不响应的问题 2023-03-10 13:55:56 +08:00
chordfish
e2e93afd06 bug:修复上次更新后不响应的问题 2023-03-10 13:03:25 +08:00
chordfish
a810158d5b bug:修复上次更新后不响应的问题 2023-03-10 12:43:07 +08:00
chordfish
5a5ebb95fc bug:修复上次更新后不响应的问题 2023-03-10 12:35:58 +08:00
chordfish
61dd9e29c0 Merge branch 'master' into json_scenario 2023-03-10 10:20:18 +08:00
chordfish
ac65d81ba1 adjust:整理代码,仅添加json方式的prompt读取 2023-03-10 10:13:40 +08:00
chordfish
7288d3cb15 删除一部分注释和调试信息 2023-03-09 21:20:59 +08:00
chordfish
7477c7c67f 删除一部分注释和调试内容 2023-03-09 21:16:15 +08:00
chordfish
453952859e Merge branch 'full_scenario' 2023-03-09 21:08:47 +08:00
chordfish
85d46089e3 已按要求修改 2023-03-09 19:53:31 +08:00
chordfish
3b55f706de 修正防人格否定的一个Bug 2023-03-09 19:22:37 +08:00
chordfish
f448276423 Merge commit '830ee704da0903a8922dc757381cdf6fd68870a3' 2023-03-09 18:34:03 +08:00
chordfish
830ee704da Bug修复 2023-03-09 18:32:39 +08:00
chordfish
393369e446 Merge branch 'master' of https://github.com/chordfish-k/QChatGPT 2023-03-09 18:29:37 +08:00
chordfish
2cc6a09905 Bug修复 2023-03-09 18:29:31 +08:00
chordfish
d7d9d88e16 适配线程版本 2023-03-09 17:56:57 +08:00
chordfish
357d6aaf75 更新配置文件 2023-03-09 15:52:18 +08:00
chordfish
8059c422e3 Update README.md 2023-03-09 14:48:21 +08:00
chordfish
b336e1334d Update README.md 2023-03-09 14:47:39 +08:00
chordfish
12a0942ddb 初步追加通过json导入messages数组的方式进行情景预设 2023-03-09 14:44:33 +08:00
Rock Chin
7e5a77f77e doc: 添加致谢https://github.com/qq255204159 2023-03-08 16:16:41 +08:00
Rock Chin
2933d4843f Release v2.1.4 2023-03-07 08:50:43 +08:00
Rock Chin
c5de978098 Merge pull request #236 from RockChinQ/fix-234
[Fix] !reload 重新加载以后首次对话报错
2023-03-07 08:47:53 +08:00
Rock Chin
8b9cfab072 doc(main.py): 优化注释 2023-03-07 08:46:20 +08:00
Rock Chin
ea5f3c222f fix: 修改主线程main流程以初步修复 2023-03-06 20:53:40 +08:00
Rock Chin
36bcbca15b Merge pull request #233 from RockChinQ/respond-rule
[Feat] 支持设置不响应群内at消息及随机响应
2023-03-06 17:55:19 +08:00
Rock Chin
2b2060e71b feat: 支持设置不响应群内at消息;支持设置随机响应概率 2023-03-06 17:50:34 +08:00
Rock Chin
451688f2df Merge pull request #232 from RockChinQ/sensitive-mask
[Feat] 支持更换敏感词的掩盖字符
2023-03-06 15:27:15 +08:00
Rock Chin
d993852de7 feat: 支持将敏感词替换成整个字符串 2023-03-06 15:26:06 +08:00
Rock Chin
9d73770a4e feat: 支持更换敏感词的掩盖字符 2023-03-06 15:07:10 +08:00
Rock Chin
2541acf9d2 fix: 赞赏码base64值错误 2023-03-06 14:16:25 +08:00
Rock Chin
a1bfbad24e Release v2.1.3 2023-03-06 12:41:35 +08:00
Rock Chin
8af4918048 Merge pull request #230 from LINSTCL/config_integrity_check
添加配置文件完整性校验
2023-03-06 12:35:59 +08:00
Rock Chin
49f4ab0ec8 perf: 完整性检查忽略__开头的属性 2023-03-06 12:34:08 +08:00
LINSTCL
85c623fb0f 修改提示逻辑 2023-03-06 11:27:16 +08:00
Rock Chin
9e28298250 perf: 完善未启动情况下的自动更新 2023-03-06 11:18:31 +08:00
Rock Chin
7a04ef0985 feat: 未启动状态下的自动更新 (#223) 2023-03-06 11:04:25 +08:00
LINSTCL
83005e9ba9 添加配置文件完整性校验 2023-03-06 09:40:33 +08:00
Rock Chin
f0c78f0529 Merge pull request #222 from LINSTCL/threadpool-optimization
使用线程池控制线程数量,防止高并发崩溃
2023-03-06 08:51:47 +08:00
Rock Chin
3f638adcf9 perf(qqbot/manager.py): 优化控制台日志显示 2023-03-06 08:50:28 +08:00
Rock Chin
d9405d8d5d fix: main.py的字段版本兼容性问题 2023-03-06 08:48:50 +08:00
Rock Chin
606713a418 Merge pull request #228 from yichuxue/patch-1
启动时,更新openai和pillow库超时问题
2023-03-06 08:44:29 +08:00
Rock Chin
52102f0d0a feat(deps): trusted-host参数 2023-03-06 08:43:51 +08:00
依初雪
df30931aad 启动openai和pillow库超时问题
主要改动如下:
1、在ensure_dependencies函数更更新包时,出现超时的情况,指定更新源 https://pypi.douban.com/simple/
2023-03-06 00:32:46 +08:00
LINSTCL
59877bf71d 添加日志输出 2023-03-05 16:47:07 +08:00
LINSTCL
d2800ac58b 使用线程池控制线程数量,防止高并发崩溃 2023-03-05 16:41:12 +08:00
15 changed files with 332 additions and 123 deletions

2
.gitignore vendored
View File

@@ -12,3 +12,5 @@ logs/
sensitive.json sensitive.json
temp/ temp/
current_tag current_tag
scenario/
!scenario/default-template.json

View File

@@ -6,7 +6,7 @@
- 到[项目Wiki](https://github.com/RockChinQ/QChatGPT/wiki)可了解项目详细信息 - 到[项目Wiki](https://github.com/RockChinQ/QChatGPT/wiki)可了解项目详细信息
- 由bilibili TheLazy制作的[视频教程](https://www.bilibili.com/video/BV15v4y1X7aP) - 由bilibili TheLazy制作的[视频教程](https://www.bilibili.com/video/BV15v4y1X7aP)
- 交流、答疑群: ~~204785790~~已满、691226829、656285629 - 交流、答疑群: ~~204785790~~(已满)、~~691226829~~(已满)、656285629
- **进群提问前请您`确保`已经找遍文档和issue均无法解决** - **进群提问前请您`确保`已经找遍文档和issue均无法解决**
- QQ频道机器人见[QQChannelChatGPT](https://github.com/Soulter/QQChannelChatGPT) - QQ频道机器人见[QQChannelChatGPT](https://github.com/Soulter/QQChannelChatGPT)
@@ -227,6 +227,7 @@ python3 main.py
- [@hissincn](https://github.com/hissincn) 本项目贡献者 - [@hissincn](https://github.com/hissincn) 本项目贡献者
- [@LINSTCL](https://github.com/LINSTCL) GPT-3.5官方模型适配贡献者 - [@LINSTCL](https://github.com/LINSTCL) GPT-3.5官方模型适配贡献者
- [@Haibersut](https://github.com/Haibersut) 本项目贡献者 - [@Haibersut](https://github.com/Haibersut) 本项目贡献者
- [@万神的星空](https://github.com/qq255204159) 整合包发行
以及其他所有为本项目提供支持的朋友们。 以及其他所有为本项目提供支持的朋友们。

View File

@@ -79,15 +79,23 @@ default_prompt = {
"default": "如果我之后想获取帮助,请你说“输入!help获取帮助”", "default": "如果我之后想获取帮助,请你说“输入!help获取帮助”",
} }
# 实验性设置项: JSON完整情景导入
# 预设prompt模式
# 参考值旧版本方式default | 完整情景full_scenario
preset_mode = "default"
# 群内响应规则 # 群内响应规则
# 符合此消息的群内消息即使不包含at机器人也会响应 # 符合此消息的群内消息即使不包含at机器人也会响应
# 支持消息前缀匹配及正则表达式匹配 # 支持消息前缀匹配及正则表达式匹配
# 支持设置是否响应at消息、随机响应概率
# 注意:由消息前缀(prefix)匹配的消息中将会删除此前缀,正则表达式(regexp)匹配的消息不会删除匹配的部分 # 注意:由消息前缀(prefix)匹配的消息中将会删除此前缀,正则表达式(regexp)匹配的消息不会删除匹配的部分
# 前缀匹配优先级高于正则表达式匹配 # 前缀匹配优先级高于正则表达式匹配
# 正则表达式简明教程https://www.runoob.com/regexp/regexp-tutorial.html # 正则表达式简明教程https://www.runoob.com/regexp/regexp-tutorial.html
response_rules = { response_rules = {
"at": True, # 是否响应at机器人的消息
"prefix": ["/ai", "!ai", "ai", "ai"], "prefix": ["/ai", "!ai", "ai", "ai"],
"regexp": [] # "为什么.*", "怎么?样.*", "怎么.*", "如何.*", "[Hh]ow to.*", "[Ww]hy not.*", "[Ww]hat is.*", ".*怎么办", ".*咋办" "regexp": [], # "为什么.*", "怎么?样.*", "怎么.*", "如何.*", "[Hh]ow to.*", "[Ww]hy not.*", "[Ww]hat is.*", ".*怎么办", ".*咋办"
"random_rate": 0.0, # 随机响应概率0.0-1.00.0为不随机响应1.0为响应所有消息, 仅在前几项判断不通过时生效
} }
# 消息忽略规则 # 消息忽略规则
@@ -202,6 +210,11 @@ hide_exce_info_to_user = False
# 设置为空字符串时,不发送提示信息 # 设置为空字符串时,不发送提示信息
alter_tip_message = '出错了,请稍后再试' alter_tip_message = '出错了,请稍后再试'
# 机器人线程池大小
# 该参数决定机器人可以同时处理几个人的消息,超出线程池数量的请求会被阻塞,不会被丢弃
# 如果你不清楚该参数的意义,请不要更改
pool_num = 10
# 每个会话的过期时间,单位为秒 # 每个会话的过期时间,单位为秒
# 默认值20分钟 # 默认值20分钟
session_expire_time = 60 * 20 session_expire_time = 60 * 20
@@ -241,11 +254,4 @@ help_message = """此机器人通过调用OpenAI的GPT-3大型语言模型生成
每次会话最后一次交互后{}分钟后会自动结束,结束后将开启新会话,如需继续前一次会话请发送 !last 重新开启 每次会话最后一次交互后{}分钟后会自动结束,结束后将开启新会话,如需继续前一次会话请发送 !last 重新开启
欢迎到github.com/RockChinQ/QChatGPT 给个star 欢迎到github.com/RockChinQ/QChatGPT 给个star
帮助信息 指令帮助信息请查看: https://github.com/RockChinQ/QChatGPT/wiki/%E5%8A%9F%E8%83%BD%E4%BD%BF%E7%94%A8#%E6%9C%BA%E5%99%A8%E4%BA%BA%E6%8C%87%E4%BB%A4""".format(session_expire_time // 60)
!help - 显示帮助
!reset - 重置会话
!last - 切换到前一次的对话
!next - 切换到后一次的对话
!prompt - 显示当前对话所有内容
!list - 列出所有历史会话
!usage - 列出各个api-key的使用量""".format(session_expire_time // 60)

82
main.py
View File

@@ -45,7 +45,9 @@ def init_db():
def ensure_dependencies(): def ensure_dependencies():
import pkg.utils.pkgmgr as pkgmgr import pkg.utils.pkgmgr as pkgmgr
pkgmgr.run_pip(["install", "openai", "Pillow", "--upgrade"]) pkgmgr.run_pip(["install", "openai", "Pillow", "--upgrade",
"-i", "https://pypi.douban.com/simple/",
"--trusted-host", "pypi.douban.com"])
known_exception_caught = False known_exception_caught = False
@@ -105,6 +107,8 @@ def reset_logging():
def main(first_time_init=False): def main(first_time_init=False):
"""启动流程reload之后会被执行"""
global known_exception_caught global known_exception_caught
import config import config
@@ -127,13 +131,26 @@ def main(first_time_init=False):
config = importlib.import_module('config') config = importlib.import_module('config')
import pkg.utils.context
pkg.utils.context.set_config(config)
init_runtime_log_file() init_runtime_log_file()
sh = reset_logging() sh = reset_logging()
# 配置完整性校验
is_integrity = True
config_template = importlib.import_module('config-template')
for key in dir(config_template):
if not key.startswith("__") and not hasattr(config, key):
setattr(config, key, getattr(config_template, key))
logging.warning("[{}]不存在".format(key))
is_integrity = False
if not is_integrity:
logging.warning("配置文件不完整请依据config-template.py检查config.py")
logging.warning("以上配置已被设为默认值将在5秒后继续启动... ")
time.sleep(5)
import pkg.utils.context
pkg.utils.context.set_config(config)
# 检查是否设置了管理员 # 检查是否设置了管理员
if not (hasattr(config, 'admin_qq') and config.admin_qq != 0): if not (hasattr(config, 'admin_qq') and config.admin_qq != 0):
# logging.warning("未设置管理员QQ,管理员权限指令及运行告警将无法使用,如需设置请修改config.py中的admin_qq字段") # logging.warning("未设置管理员QQ,管理员权限指令及运行告警将无法使用,如需设置请修改config.py中的admin_qq字段")
@@ -165,6 +182,7 @@ def main(first_time_init=False):
import pkg.openai.dprompt import pkg.openai.dprompt
pkg.openai.dprompt.read_prompt_from_file() pkg.openai.dprompt.read_prompt_from_file()
pkg.openai.dprompt.read_scenario_from_file()
pkg.utils.context.context['logger_handler'] = sh pkg.utils.context.context['logger_handler'] = sh
# 主启动流程 # 主启动流程
@@ -180,7 +198,7 @@ def main(first_time_init=False):
# 初始化qq机器人 # 初始化qq机器人
qqbot = pkg.qqbot.manager.QQBotManager(mirai_http_api_config=config.mirai_http_api_config, qqbot = pkg.qqbot.manager.QQBotManager(mirai_http_api_config=config.mirai_http_api_config,
timeout=config.process_message_timeout, retry=config.retry_times, timeout=config.process_message_timeout, retry=config.retry_times,
first_time_init=first_time_init) first_time_init=first_time_init, pool_num=config.pool_num)
# 加载插件 # 加载插件
import pkg.plugin.host import pkg.plugin.host
@@ -188,7 +206,7 @@ def main(first_time_init=False):
pkg.plugin.host.initialize_plugins() pkg.plugin.host.initialize_plugins()
if first_time_init: # 不是热重载之后的启动,则启动新的bot线程 if first_time_init: # 不是热重载之后的启动,则启动新的bot线程
import mirai.exceptions import mirai.exceptions
@@ -238,6 +256,11 @@ def main(first_time_init=False):
qq_bot_thread = threading.Thread(target=run_bot_wrapper, args=(), daemon=True) qq_bot_thread = threading.Thread(target=run_bot_wrapper, args=(), daemon=True)
qq_bot_thread.start() qq_bot_thread.start()
finally: finally:
# 判断若是Windows输出选择模式可能会暂停程序的警告
if os.name == 'nt':
time.sleep(2)
logging.info("您正在使用Windows系统若命令行窗口处于“选择”模式程序可能会被暂停此时请右键点击窗口空白区域使其取消选择模式。")
time.sleep(12) time.sleep(12)
if first_time_init: if first_time_init:
if not known_exception_caught: if not known_exception_caught:
@@ -277,17 +300,7 @@ def main(first_time_init=False):
except Exception as e: except Exception as e:
logging.warning("检查更新失败:{}".format(e)) logging.warning("检查更新失败:{}".format(e))
while True: return qqbot
try:
time.sleep(10)
if qqbot != pkg.utils.context.get_qqbot_manager(): # 已经reload了
logging.info("以前的main流程由于reload退出")
break
except KeyboardInterrupt:
stop()
print("程序退出")
sys.exit(0)
def stop(): def stop():
@@ -325,6 +338,10 @@ if __name__ == '__main__':
if not os.path.exists("sensitive.json"): if not os.path.exists("sensitive.json"):
shutil.copy("sensitive-template.json", "sensitive.json") shutil.copy("sensitive-template.json", "sensitive.json")
# 检查是否有scenario/default.json
if not os.path.exists("scenario/default.json"):
shutil.copy("scenario/default-template.json", "scenario/default.json")
# 检查temp目录 # 检查temp目录
if not os.path.exists("temp/"): if not os.path.exists("temp/"):
os.mkdir("temp/") os.mkdir("temp/")
@@ -340,24 +357,21 @@ if __name__ == '__main__':
sys.exit(0) sys.exit(0)
elif len(sys.argv) > 1 and sys.argv[1] == 'update': elif len(sys.argv) > 1 and sys.argv[1] == 'update':
try: print("正在进行程序更新...")
try: import pkg.utils.updater as updater
import pkg.utils.pkgmgr updater.update_all(cli=True)
pkg.utils.pkgmgr.ensure_dulwich()
except:
pass
from dulwich import porcelain
repo = porcelain.open_repo('.')
porcelain.pull(repo)
except ModuleNotFoundError:
print("dulwich模块未安装,请查看 https://github.com/RockChinQ/QChatGPT/issues/77")
sys.exit(0) sys.exit(0)
# import pkg.utils.configmgr
#
# pkg.utils.configmgr.set_config_and_reload("quote_origin", False)
requests.packages.urllib3.disable_warnings(InsecureRequestWarning) requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
main(True) qqbot = main(True)
import pkg.utils.context
while True:
try:
time.sleep(10)
except KeyboardInterrupt:
stop()
print("程序退出")
sys.exit(0)

View File

@@ -35,6 +35,7 @@ class DatabaseManager:
def __execute__(self, *args, **kwargs) -> Cursor: def __execute__(self, *args, **kwargs) -> Cursor:
# logging.debug('SQL: {}'.format(sql)) # logging.debug('SQL: {}'.format(sql))
logging.debug('SQL: {}'.format(args))
c = self.cursor.execute(*args, **kwargs) c = self.cursor.execute(*args, **kwargs)
self.conn.commit() self.conn.commit()
return c return c
@@ -52,10 +53,23 @@ class DatabaseManager:
`create_timestamp` bigint not null, `create_timestamp` bigint not null,
`last_interact_timestamp` bigint not null, `last_interact_timestamp` bigint not null,
`status` varchar(255) not null default 'on_going', `status` varchar(255) not null default 'on_going',
`default_prompt` text not null default '',
`prompt` text not null `prompt` text not null
) )
""") """)
# 检查sessions表是否存在`default_prompt`字段
self.__execute__("PRAGMA table_info('sessions')")
columns = self.cursor.fetchall()
has_default_prompt = False
for field in columns:
if field[1] == 'default_prompt':
has_default_prompt = True
break
if not has_default_prompt:
self.__execute__("alter table `sessions` add column `default_prompt` text not null default ''")
self.__execute__(""" self.__execute__("""
create table if not exists `account_fee`( create table if not exists `account_fee`(
`id` INTEGER PRIMARY KEY AUTOINCREMENT, `id` INTEGER PRIMARY KEY AUTOINCREMENT,
@@ -75,7 +89,7 @@ class DatabaseManager:
# session持久化 # session持久化
def persistence_session(self, subject_type: str, subject_number: int, create_timestamp: int, def persistence_session(self, subject_type: str, subject_number: int, create_timestamp: int,
last_interact_timestamp: int, prompt: str): last_interact_timestamp: int, prompt: str, default_prompt: str = ''):
"""持久化指定session""" """持久化指定session"""
# 检查是否已经有了此name和create_timestamp的session # 检查是否已经有了此name和create_timestamp的session
@@ -88,13 +102,13 @@ class DatabaseManager:
if count == 0: if count == 0:
sql = """ sql = """
insert into `sessions` (`name`, `type`, `number`, `create_timestamp`, `last_interact_timestamp`, `prompt`) insert into `sessions` (`name`, `type`, `number`, `create_timestamp`, `last_interact_timestamp`, `prompt`, `default_prompt`)
values (?, ?, ?, ?, ?, ?) values (?, ?, ?, ?, ?, ?, ?)
""" """
self.__execute__(sql, self.__execute__(sql,
("{}_{}".format(subject_type, subject_number), subject_type, subject_number, create_timestamp, ("{}_{}".format(subject_type, subject_number), subject_type, subject_number, create_timestamp,
last_interact_timestamp, prompt)) last_interact_timestamp, prompt, default_prompt))
else: else:
sql = """ sql = """
update `sessions` set `last_interact_timestamp` = ?, `prompt` = ? update `sessions` set `last_interact_timestamp` = ?, `prompt` = ?
@@ -126,7 +140,7 @@ class DatabaseManager:
# 从数据库中加载所有还没过期的session # 从数据库中加载所有还没过期的session
config = pkg.utils.context.get_config() config = pkg.utils.context.get_config()
self.__execute__(""" self.__execute__("""
select `name`, `type`, `number`, `create_timestamp`, `last_interact_timestamp`, `prompt`, `status` select `name`, `type`, `number`, `create_timestamp`, `last_interact_timestamp`, `prompt`, `status`, `default_prompt`
from `sessions` where `last_interact_timestamp` > {} from `sessions` where `last_interact_timestamp` > {}
""".format(int(time.time()) - config.session_expire_time)) """.format(int(time.time()) - config.session_expire_time))
results = self.cursor.fetchall() results = self.cursor.fetchall()
@@ -139,6 +153,7 @@ class DatabaseManager:
last_interact_timestamp = result[4] last_interact_timestamp = result[4]
prompt = result[5] prompt = result[5]
status = result[6] status = result[6]
default_prompt = result[7]
# 当且仅当最后一个该对象的会话是on_going状态时才会被加载 # 当且仅当最后一个该对象的会话是on_going状态时才会被加载
if status == 'on_going': if status == 'on_going':
@@ -147,7 +162,8 @@ class DatabaseManager:
'subject_number': subject_number, 'subject_number': subject_number,
'create_timestamp': create_timestamp, 'create_timestamp': create_timestamp,
'last_interact_timestamp': last_interact_timestamp, 'last_interact_timestamp': last_interact_timestamp,
'prompt': prompt 'prompt': prompt,
'default_prompt': default_prompt
} }
else: else:
if session_name in sessions: if session_name in sessions:
@@ -159,7 +175,7 @@ class DatabaseManager:
def last_session(self, session_name: str, cursor_timestamp: int): def last_session(self, session_name: str, cursor_timestamp: int):
self.__execute__(""" self.__execute__("""
select `name`, `type`, `number`, `create_timestamp`, `last_interact_timestamp`, `prompt`, `status` select `name`, `type`, `number`, `create_timestamp`, `last_interact_timestamp`, `prompt`, `status`, `default_prompt`
from `sessions` where `name` = '{}' and `last_interact_timestamp` < {} order by `last_interact_timestamp` desc from `sessions` where `name` = '{}' and `last_interact_timestamp` < {} order by `last_interact_timestamp` desc
limit 1 limit 1
""".format(session_name, cursor_timestamp)) """.format(session_name, cursor_timestamp))
@@ -175,20 +191,22 @@ class DatabaseManager:
last_interact_timestamp = result[4] last_interact_timestamp = result[4]
prompt = result[5] prompt = result[5]
status = result[6] status = result[6]
default_prompt = result[7]
return { return {
'subject_type': subject_type, 'subject_type': subject_type,
'subject_number': subject_number, 'subject_number': subject_number,
'create_timestamp': create_timestamp, 'create_timestamp': create_timestamp,
'last_interact_timestamp': last_interact_timestamp, 'last_interact_timestamp': last_interact_timestamp,
'prompt': prompt 'prompt': prompt,
'default_prompt': default_prompt
} }
# 获取此session_name后一个session的数据 # 获取此session_name后一个session的数据
def next_session(self, session_name: str, cursor_timestamp: int): def next_session(self, session_name: str, cursor_timestamp: int):
self.__execute__(""" self.__execute__("""
select `name`, `type`, `number`, `create_timestamp`, `last_interact_timestamp`, `prompt`, `status` select `name`, `type`, `number`, `create_timestamp`, `last_interact_timestamp`, `prompt`, `status`, `default_prompt`
from `sessions` where `name` = '{}' and `last_interact_timestamp` > {} order by `last_interact_timestamp` asc from `sessions` where `name` = '{}' and `last_interact_timestamp` > {} order by `last_interact_timestamp` asc
limit 1 limit 1
""".format(session_name, cursor_timestamp)) """.format(session_name, cursor_timestamp))
@@ -204,19 +222,21 @@ class DatabaseManager:
last_interact_timestamp = result[4] last_interact_timestamp = result[4]
prompt = result[5] prompt = result[5]
status = result[6] status = result[6]
default_prompt = result[7]
return { return {
'subject_type': subject_type, 'subject_type': subject_type,
'subject_number': subject_number, 'subject_number': subject_number,
'create_timestamp': create_timestamp, 'create_timestamp': create_timestamp,
'last_interact_timestamp': last_interact_timestamp, 'last_interact_timestamp': last_interact_timestamp,
'prompt': prompt 'prompt': prompt,
'default_prompt': default_prompt
} }
# 列出与某个对象的所有对话session # 列出与某个对象的所有对话session
def list_history(self, session_name: str, capacity: int, page: int): def list_history(self, session_name: str, capacity: int, page: int):
self.__execute__(""" self.__execute__("""
select `name`, `type`, `number`, `create_timestamp`, `last_interact_timestamp`, `prompt`, `status` select `name`, `type`, `number`, `create_timestamp`, `last_interact_timestamp`, `prompt`, `status`, `default_prompt`
from `sessions` where `name` = '{}' order by `last_interact_timestamp` desc limit {} offset {} from `sessions` where `name` = '{}' order by `last_interact_timestamp` desc limit {} offset {}
""".format(session_name, capacity, capacity * page)) """.format(session_name, capacity, capacity * page))
results = self.cursor.fetchall() results = self.cursor.fetchall()
@@ -229,17 +249,40 @@ class DatabaseManager:
last_interact_timestamp = result[4] last_interact_timestamp = result[4]
prompt = result[5] prompt = result[5]
status = result[6] status = result[6]
default_prompt = result[7]
sessions.append({ sessions.append({
'subject_type': subject_type, 'subject_type': subject_type,
'subject_number': subject_number, 'subject_number': subject_number,
'create_timestamp': create_timestamp, 'create_timestamp': create_timestamp,
'last_interact_timestamp': last_interact_timestamp, 'last_interact_timestamp': last_interact_timestamp,
'prompt': prompt 'prompt': prompt,
'default_prompt': default_prompt
}) })
return sessions return sessions
def delete_history(self, session_name: str, index: int) -> bool:
# 删除倒序第index个session
# 查找其id再删除
self.__execute__("""
delete from `sessions` where `id` in (select `id` from `sessions` where `name` = '{}' order by `last_interact_timestamp` desc limit 1 offset {})
""".format(session_name, index))
return self.cursor.rowcount == 1
def delete_all_history(self, session_name: str) -> bool:
self.__execute__("""
delete from `sessions` where `name` = '{}'
""".format(session_name))
return self.cursor.rowcount > 0
def delete_all_session_history(self) -> bool:
self.__execute__("""
delete from `sessions`
""")
return self.cursor.rowcount > 0
# 将apikey的使用量存进数据库 # 将apikey的使用量存进数据库
def dump_api_key_usage(self, api_keys: dict, usage: dict): def dump_api_key_usage(self, api_keys: dict, usage: dict):
logging.debug('dumping api key usage...') logging.debug('dumping api key usage...')

View File

@@ -1,4 +1,6 @@
# 多情景预设值管理 # 多情景预设值管理
import json
import logging
__current__ = "default" __current__ = "default"
"""当前默认使用的情景预设的名称 """当前默认使用的情景预设的名称
@@ -9,8 +11,10 @@ __current__ = "default"
__prompts_from_files__ = {} __prompts_from_files__ = {}
"""从文件中读取的情景预设值""" """从文件中读取的情景预设值"""
__scenario_from_files__ = {}
def read_prompt_from_file() -> str:
def read_prompt_from_file():
"""从文件读取预设值""" """从文件读取预设值"""
# 读取prompts/目录下的所有文件,以文件名为键,文件内容为值 # 读取prompts/目录下的所有文件,以文件名为键,文件内容为值
# 保存在__prompts_from_files__中 # 保存在__prompts_from_files__中
@@ -23,6 +27,19 @@ def read_prompt_from_file() -> str:
__prompts_from_files__[file] = f.read() __prompts_from_files__[file] = f.read()
def read_scenario_from_file():
"""从JSON文件读取情景预设"""
global __scenario_from_files__
import os
__scenario_from_files__ = {}
for file in os.listdir("scenario"):
if file == "default-template.json":
continue
with open(os.path.join("scenario", file), encoding="utf-8") as f:
__scenario_from_files__[file] = json.load(f)
def get_prompt_dict() -> dict: def get_prompt_dict() -> dict:
"""获取预设值字典""" """获取预设值字典"""
import config import config
@@ -65,15 +82,40 @@ def set_to_default():
__current__ = list(default_dict.keys())[0] __current__ = list(default_dict.keys())[0]
def get_prompt(name: str = None) -> str: def get_prompt(name: str = None) -> list:
global __scenario_from_files__
import config
preset_mode = config.preset_mode
"""获取预设值""" """获取预设值"""
if name is None: if name is None:
name = get_current() name = get_current()
default_dict = get_prompt_dict() # JSON预设方式
if preset_mode == 'full_scenario':
import os
for key in default_dict: for key in __scenario_from_files__:
if key.lower().startswith(name.lower()): if key.lower().startswith(name.lower()):
return default_dict[key] logging.debug('成功加载情景预设从JSON文件: {}'.format(key))
return __scenario_from_files__[key]['prompt']
raise KeyError("未找到情景预设: " + name) # 默认预设方式
elif preset_mode == 'default':
default_dict = get_prompt_dict()
for key in default_dict:
if key.lower().startswith(name.lower()):
return [
{
"role": "user",
"content": default_dict[key]
},
{
"role": "assistant",
"content": "好的。"
}
]
raise KeyError("未找到默认情景预设: " + name)

View File

@@ -40,7 +40,7 @@ def reset_session_prompt(session_name, prompt):
prompt = [ prompt = [
{ {
'role': 'system', 'role': 'system',
'content': config.default_prompt['default'] 'content': config.default_prompt['default'] if type(config.default_prompt) == dict else config.default_prompt
} }
] ]
# 警告 # 警告
@@ -75,6 +75,8 @@ def load_sessions():
except Exception: except Exception:
temp_session.prompt = reset_session_prompt(session_name, session_data[session_name]['prompt']) temp_session.prompt = reset_session_prompt(session_name, session_data[session_name]['prompt'])
temp_session.persistence() temp_session.persistence()
temp_session.default_prompt = json.loads(session_data[session_name]['default_prompt']) if \
session_data[session_name]['default_prompt'] else []
sessions[session_name] = temp_session sessions[session_name] = temp_session
@@ -104,6 +106,9 @@ class Session:
prompt = [] prompt = []
"""使用list来保存会话中的回合""" """使用list来保存会话中的回合"""
default_prompt = []
"""本session的默认prompt"""
create_timestamp = 0 create_timestamp = 0
"""会话创建时间""" """会话创建时间"""
@@ -129,24 +134,13 @@ class Session:
# 从配置文件获取会话预设信息 # 从配置文件获取会话预设信息
def get_default_prompt(self, use_default: str = None): def get_default_prompt(self, use_default: str = None):
config = pkg.utils.context.get_config()
import pkg.openai.dprompt as dprompt import pkg.openai.dprompt as dprompt
if use_default is None: if use_default is None:
current_default_prompt = dprompt.get_prompt(dprompt.get_current()) use_default = dprompt.get_current()
else:
current_default_prompt = dprompt.get_prompt(use_default)
return [ current_default_prompt = dprompt.get_prompt(use_default)
{ return current_default_prompt
'role': 'user',
'content': current_default_prompt
}, {
'role': 'assistant',
'content': 'ok'
}
]
def __init__(self, name: str): def __init__(self, name: str):
self.name = name self.name = name
@@ -155,7 +149,9 @@ class Session:
self.schedule() self.schedule()
self.response_lock = threading.Lock() self.response_lock = threading.Lock()
self.prompt = self.get_default_prompt()
self.default_prompt = self.get_default_prompt()
logging.debug("prompt is: {}".format(self.default_prompt))
# 设定检查session最后一次对话是否超过过期时间的计时器 # 设定检查session最后一次对话是否超过过期时间的计时器
def schedule(self): def schedule(self):
@@ -199,11 +195,11 @@ class Session:
self.last_interact_timestamp = int(time.time()) self.last_interact_timestamp = int(time.time())
# 触发插件事件 # 触发插件事件
if self.prompt == self.get_default_prompt(): if not self.prompt:
args = { args = {
'session_name': self.name, 'session_name': self.name,
'session': self, 'session': self,
'default_prompt': self.prompt, 'default_prompt': self.default_prompt,
} }
event = pkg.plugin.host.emit(plugin_models.SessionFirstMessageReceived, **args) event = pkg.plugin.host.emit(plugin_models.SessionFirstMessageReceived, **args)
@@ -256,25 +252,29 @@ class Session:
def cut_out(self, msg: str, max_tokens: int) -> list: def cut_out(self, msg: str, max_tokens: int) -> list:
"""将现有prompt进行切割处理使得新的prompt长度不超过max_tokens""" """将现有prompt进行切割处理使得新的prompt长度不超过max_tokens"""
# 如果用户消息长度超过max_tokens直接返回 # 如果用户消息长度超过max_tokens直接返回
temp_prompt: list = []
temp_prompt = [ temp_prompt += self.default_prompt
temp_prompt.append(
{ {
'role': 'user', 'role': 'user',
'content': msg 'content': msg
} }
] )
token_count = 0
for item in temp_prompt:
token_count += len(item['content'])
token_count = len(msg)
# 倒序遍历prompt # 倒序遍历prompt
for i in range(len(self.prompt) - 1, -1, -1): for i in range(len(self.prompt) - 1, -1, -1):
if token_count >= max_tokens: if token_count >= max_tokens:
break break
# 将prompt加到temp_prompt头部 # 将prompt加到temp_prompt倒数第二个位置
temp_prompt.insert(0, self.prompt[i]) temp_prompt.insert(len(self.default_prompt), self.prompt[i])
token_count += len(self.prompt[i]['content']) token_count += len(self.prompt[i]['content'])
logging.debug('cut_out: {}'.format(str(temp_prompt))) logging.debug('cut_out: {}'.format(json.dumps(temp_prompt, ensure_ascii=False, indent=4)))
return temp_prompt return temp_prompt
@@ -291,11 +291,11 @@ class Session:
subject_number = int(name_spt[1]) subject_number = int(name_spt[1])
db_inst.persistence_session(subject_type, subject_number, self.create_timestamp, self.last_interact_timestamp, db_inst.persistence_session(subject_type, subject_number, self.create_timestamp, self.last_interact_timestamp,
json.dumps(self.prompt)) json.dumps(self.prompt), json.dumps(self.default_prompt))
# 重置session # 重置session
def reset(self, explicit: bool = False, expired: bool = False, schedule_new: bool = True, use_prompt: str = None): def reset(self, explicit: bool = False, expired: bool = False, schedule_new: bool = True, use_prompt: str = None):
if self.prompt[-1]['role'] != "system": if self.prompt:
self.persistence() self.persistence()
if explicit: if explicit:
# 触发插件事件 # 触发插件事件
@@ -311,7 +311,9 @@ class Session:
if expired: if expired:
pkg.utils.context.get_database_manager().set_session_expired(self.name, self.create_timestamp) pkg.utils.context.get_database_manager().set_session_expired(self.name, self.create_timestamp)
self.prompt = self.get_default_prompt(use_prompt)
self.default_prompt = self.get_default_prompt(use_prompt)
self.prompt = []
self.create_timestamp = int(time.time()) self.create_timestamp = int(time.time())
self.last_interact_timestamp = int(time.time()) self.last_interact_timestamp = int(time.time())
self.just_switched_to_exist_session = False self.just_switched_to_exist_session = False
@@ -340,6 +342,7 @@ class Session:
except json.decoder.JSONDecodeError: except json.decoder.JSONDecodeError:
self.prompt = reset_session_prompt(self.name, last_one['prompt']) self.prompt = reset_session_prompt(self.name, last_one['prompt'])
self.persistence() self.persistence()
self.default_prompt = json.loads(last_one['default_prompt']) if last_one['default_prompt'] else []
self.just_switched_to_exist_session = True self.just_switched_to_exist_session = True
return self return self
@@ -359,6 +362,7 @@ class Session:
except json.decoder.JSONDecodeError: except json.decoder.JSONDecodeError:
self.prompt = reset_session_prompt(self.name, next_one['prompt']) self.prompt = reset_session_prompt(self.name, next_one['prompt'])
self.persistence() self.persistence()
self.default_prompt = json.loads(next_one['default_prompt']) if next_one['default_prompt'] else []
self.just_switched_to_exist_session = True self.just_switched_to_exist_session = True
return self return self
@@ -366,5 +370,11 @@ class Session:
def list_history(self, capacity: int = 10, page: int = 0): def list_history(self, capacity: int = 10, page: int = 0):
return pkg.utils.context.get_database_manager().list_history(self.name, capacity, page) return pkg.utils.context.get_database_manager().list_history(self.name, capacity, page)
def delete_history(self, index: int) -> bool:
return pkg.utils.context.get_database_manager().delete_history(self.name, index)
def delete_all_history(self) -> bool:
return pkg.utils.context.get_database_manager().delete_all_history(self.name)
def draw_image(self, prompt: str): def draw_image(self, prompt: str):
return pkg.utils.context.get_openai_manager().request_image(prompt) return pkg.utils.context.get_openai_manager().request_image(prompt)

View File

@@ -234,7 +234,7 @@ def process_command(session_name: str, text_message: str, mgr, config,
if len(msg) >= 2: if len(msg) >= 2:
reply_str += "#{} 创建:{} {}\n".format(i + page * 10, reply_str += "#{} 创建:{} {}\n".format(i + page * 10,
datetime_obj.strftime("%Y-%m-%d %H:%M:%S"), datetime_obj.strftime("%Y-%m-%d %H:%M:%S"),
msg[1]['content']) msg[0]['content'])
else: else:
reply_str += "#{} 创建:{} {}\n".format(i + page * 10, reply_str += "#{} 创建:{} {}\n".format(i + page * 10,
datetime_obj.strftime("%Y-%m-%d %H:%M:%S"), datetime_obj.strftime("%Y-%m-%d %H:%M:%S"),
@@ -256,6 +256,20 @@ def process_command(session_name: str, text_message: str, mgr, config,
reply = pkg.qqbot.message.process_normal_message(to_send, mgr, config, reply = pkg.qqbot.message.process_normal_message(to_send, mgr, config,
launcher_type, launcher_id, sender_id) launcher_type, launcher_id, sender_id)
elif cmd == 'del': # 删除指定会话历史记录
if len(params) == 0:
reply = ["[bot]参数不足, 格式: !del <序号>\n可以通过!list查看序号"]
else:
if params[0] == 'all':
pkg.openai.session.get_session(session_name).delete_all_history()
reply = ["[bot]已删除所有历史会话"]
elif params[0].isdigit():
if pkg.openai.session.get_session(session_name).delete_history(int(params[0])):
reply = ["[bot]已删除历史会话 #{}".format(params[0])]
else:
reply = ["[bot]没有历史会话 #{}".format(params[0])]
else:
reply = ["[bot]参数错误, 格式: !del <序号>\n可以通过!list查看序号"]
elif cmd == 'usage': elif cmd == 'usage':
reply_str = "[bot]各api-key使用情况:\n\n" reply_str = "[bot]各api-key使用情况:\n\n"
@@ -322,6 +336,18 @@ def process_command(session_name: str, text_message: str, mgr, config,
reply = ["[bot]err: 未找到情景预设:{}".format(params[0])] reply = ["[bot]err: 未找到情景预设:{}".format(params[0])]
else: else:
reply = ["[bot]err: 仅管理员可设置默认情景预设"] reply = ["[bot]err: 仅管理员可设置默认情景预设"]
elif cmd == "delhst" and is_admin:
if len(params) == 0:
reply = ["[bot]err:请输入要删除的会话名: group_<群号> 或者 person_<QQ号>, 或使用 !delhst all 删除所有会话的历史记录"]
else:
if params[0] == "all":
pkg.utils.context.get_database_manager().delete_all_session_history()
reply = ["[bot]已删除所有会话的历史记录"]
else:
if pkg.utils.context.get_database_manager().delete_all_history(params[0]):
reply = ["[bot]已删除会话 {} 的所有历史记录".format(params[0])]
else:
reply = ["[bot]未找到会话 {} 的历史记录".format(params[0])]
elif cmd == 'reload' and is_admin: elif cmd == 'reload' and is_admin:
def reload_task(): def reload_task():
pkg.utils.reloader.reload_all() pkg.utils.reloader.reload_all()

View File

@@ -7,6 +7,8 @@ import logging
class ReplyFilter: class ReplyFilter:
sensitive_words = [] sensitive_words = []
mask = "*"
mask_word = ""
# 默认值( 兼容性考虑 ) # 默认值( 兼容性考虑 )
baidu_check = False baidu_check = False
@@ -14,8 +16,10 @@ class ReplyFilter:
baidu_secret_key = "" baidu_secret_key = ""
inappropriate_message_tips = "[百度云]请珍惜机器人,当前返回内容不合规" inappropriate_message_tips = "[百度云]请珍惜机器人,当前返回内容不合规"
def __init__(self, sensitive_words: list): def __init__(self, sensitive_words: list, mask: str = "*", mask_word: str = ""):
self.sensitive_words = sensitive_words self.sensitive_words = sensitive_words
self.mask = mask
self.mask_word = mask_word
import config import config
if hasattr(config, 'baidu_check') and hasattr(config, 'baidu_api_key') and hasattr(config, 'baidu_secret_key'): if hasattr(config, 'baidu_check') and hasattr(config, 'baidu_api_key') and hasattr(config, 'baidu_secret_key'):
self.baidu_check = config.baidu_check self.baidu_check = config.baidu_check
@@ -36,7 +40,10 @@ class ReplyFilter:
match = re.findall(word, message) match = re.findall(word, message)
if len(match) > 0: if len(match) > 0:
for i in range(len(match)): for i in range(len(match)):
message = message.replace(match[i], "*" * len(match[i])) if self.mask_word == "":
message = message.replace(match[i], self.mask * len(match[i]))
else:
message = message.replace(match[i], self.mask_word)
# 百度云审核 # 百度云审核
if self.baidu_check: if self.baidu_check:

View File

@@ -2,6 +2,7 @@ import asyncio
import json import json
import os import os
import threading import threading
from concurrent.futures import ThreadPoolExecutor
import mirai.models.bus import mirai.models.bus
from mirai import At, GroupMessage, MessageEvent, Mirai, StrangerMessage, WebSocketAdapter, HTTPAdapter, \ from mirai import At, GroupMessage, MessageEvent, Mirai, StrangerMessage, WebSocketAdapter, HTTPAdapter, \
@@ -21,12 +22,6 @@ import pkg.plugin.host as plugin_host
import pkg.plugin.models as plugin_models import pkg.plugin.models as plugin_models
# 并行运行
def go(func, args=()):
thread = threading.Thread(target=func, args=args, daemon=True)
thread.start()
# 检查消息是否符合泛响应匹配机制 # 检查消息是否符合泛响应匹配机制
def check_response_rule(text: str): def check_response_rule(text: str):
config = pkg.utils.context.get_config() config = pkg.utils.context.get_config()
@@ -51,10 +46,29 @@ def check_response_rule(text: str):
return False, "" return False, ""
def response_at():
config = pkg.utils.context.get_config()
if 'at' not in config.response_rules:
return True
return config.response_rules['at']
def random_responding():
config = pkg.utils.context.get_config()
if 'random_rate' in config.response_rules:
import random
return random.random() < config.response_rules['random_rate']
return False
# 控制QQ消息输入输出的类 # 控制QQ消息输入输出的类
class QQBotManager: class QQBotManager:
retry = 3 retry = 3
#线程池控制
pool = None
bot: Mirai = None bot: Mirai = None
reply_filter = None reply_filter = None
@@ -64,11 +78,14 @@ class QQBotManager:
ban_person = [] ban_person = []
ban_group = [] ban_group = []
def __init__(self, mirai_http_api_config: dict, timeout: int = 60, retry: int = 3, first_time_init=True): def __init__(self, mirai_http_api_config: dict, timeout: int = 60, retry: int = 3, pool_num: int = 10, first_time_init=True):
self.timeout = timeout self.timeout = timeout
self.retry = retry self.retry = retry
self.pool_num = pool_num
self.pool = ThreadPoolExecutor(max_workers=self.pool_num)
logging.debug("Registered thread pool Size:{}".format(pool_num))
# 加载禁用列表 # 加载禁用列表
if os.path.exists("banlist.py"): if os.path.exists("banlist.py"):
import banlist import banlist
@@ -82,7 +99,12 @@ class QQBotManager:
and config.sensitive_word_filter is not None \ and config.sensitive_word_filter is not None \
and config.sensitive_word_filter: and config.sensitive_word_filter:
with open("sensitive.json", "r", encoding="utf-8") as f: with open("sensitive.json", "r", encoding="utf-8") as f:
self.reply_filter = pkg.qqbot.filter.ReplyFilter(json.load(f)['words']) sensitive_json = json.load(f)
self.reply_filter = pkg.qqbot.filter.ReplyFilter(
sensitive_words=sensitive_json['words'],
mask=sensitive_json['mask'] if 'mask' in sensitive_json else '*',
mask_word=sensitive_json['mask_word'] if 'mask_word' in sensitive_json else ''
)
else: else:
self.reply_filter = pkg.qqbot.filter.ReplyFilter([]) self.reply_filter = pkg.qqbot.filter.ReplyFilter([])
@@ -116,7 +138,7 @@ class QQBotManager:
self.on_person_message(event) self.on_person_message(event)
go(friend_message_handler, (event,)) self.go(friend_message_handler, event)
@self.bot.on(StrangerMessage) @self.bot.on(StrangerMessage)
async def on_stranger_message(event: StrangerMessage): async def on_stranger_message(event: StrangerMessage):
@@ -136,7 +158,7 @@ class QQBotManager:
self.on_person_message(event) self.on_person_message(event)
go(stranger_message_handler, (event,)) self.go(stranger_message_handler, event)
@self.bot.on(GroupMessage) @self.bot.on(GroupMessage)
async def on_group_message(event: GroupMessage): async def on_group_message(event: GroupMessage):
@@ -156,7 +178,7 @@ class QQBotManager:
self.on_group_message(event) self.on_group_message(event)
go(group_message_handler, (event,)) self.go(group_message_handler, event)
def unsubscribe_all(): def unsubscribe_all():
"""取消所有订阅 """取消所有订阅
@@ -173,6 +195,9 @@ class QQBotManager:
self.unsubscribe_all = unsubscribe_all self.unsubscribe_all = unsubscribe_all
def go(self, func, *args, **kwargs):
self.pool.submit(func, *args, **kwargs)
def first_time_init(self, mirai_http_api_config: dict): def first_time_init(self, mirai_http_api_config: dict):
"""热重载后不再运行此函数""" """热重载后不再运行此函数"""
@@ -288,14 +313,19 @@ class QQBotManager:
if Image in event.message_chain: if Image in event.message_chain:
pass pass
elif At(self.bot.qq) not in event.message_chain:
check, result = check_response_rule(str(event.message_chain).strip())
if check:
reply = process(result.strip())
else: else:
# 直接调用 if At(self.bot.qq) in event.message_chain and response_at():
reply = process() # 直接调用
reply = process()
else:
check, result = check_response_rule(str(event.message_chain).strip())
if check:
reply = process(result.strip())
# 检查是否随机响应
elif random_responding():
logging.info("随机响应group_{}消息".format(event.group.id))
reply = process()
if reply: if reply:
return self.send(event, reply) return self.send(event, reply)

File diff suppressed because one or more lines are too long

View File

@@ -54,7 +54,7 @@ def get_current_tag() -> str:
return current_tag return current_tag
def update_all() -> bool: def update_all(cli: bool = False) -> bool:
"""检查更新并下载源码""" """检查更新并下载源码"""
current_tag = get_current_tag() current_tag = get_current_tag()
@@ -69,12 +69,19 @@ def update_all() -> bool:
if latest_rls == {}: if latest_rls == {}:
latest_rls = rls latest_rls = rls
logging.info("更新日志: {}".format(rls_notes)) if not cli:
logging.info("更新日志: {}".format(rls_notes))
else:
print("更新日志: {}".format(rls_notes))
if latest_rls == {}: # 没有新版本 if latest_rls == {}: # 没有新版本
return False return False
# 下载最新版本的zip到temp目录 # 下载最新版本的zip到temp目录
logging.info("开始下载最新版本: {}".format(latest_rls['zipball_url'])) if not cli:
logging.info("开始下载最新版本: {}".format(latest_rls['zipball_url']))
else:
print("开始下载最新版本: {}".format(latest_rls['zipball_url']))
zip_url = latest_rls['zipball_url'] zip_url = latest_rls['zipball_url']
zip_resp = requests.get(url=zip_url) zip_resp = requests.get(url=zip_url)
zip_data = zip_resp.content zip_data = zip_resp.content
@@ -87,7 +94,10 @@ def update_all() -> bool:
with open("temp/updater/{}.zip".format(latest_rls['tag_name']), "wb") as f: with open("temp/updater/{}.zip".format(latest_rls['tag_name']), "wb") as f:
f.write(zip_data) f.write(zip_data)
logging.info("下载最新版本完成: {}".format("temp/updater/{}.zip".format(latest_rls['tag_name']))) if not cli:
logging.info("下载最新版本完成: {}".format("temp/updater/{}.zip".format(latest_rls['tag_name'])))
else:
print("下载最新版本完成: {}".format("temp/updater/{}.zip".format(latest_rls['tag_name'])))
# 解压zip到temp/updater/<tag_name>/ # 解压zip到temp/updater/<tag_name>/
import zipfile import zipfile
@@ -124,8 +134,11 @@ def update_all() -> bool:
f.write(current_tag) f.write(current_tag)
# 通知管理员 # 通知管理员
import pkg.utils.context if not cli:
pkg.utils.context.get_qqbot_manager().notify_admin("已更新到最新版本: {}\n更新日志:\n{}\n新功能通常可以在config-template.py中看到完整的更新日志请前往 https://github.com/RockChinQ/QChatGPT/releases 查看".format(current_tag, "\n".join(rls_notes))) import pkg.utils.context
pkg.utils.context.get_qqbot_manager().notify_admin("已更新到最新版本: {}\n更新日志:\n{}\n新功能通常可以在config-template.py中看到完整的更新日志请前往 https://github.com/RockChinQ/QChatGPT/releases 查看".format(current_tag, "\n".join(rls_notes)))
else:
print("已更新到最新版本: {}\n更新日志:\n{}\n新功能通常可以在config-template.py中看到完整的更新日志请前往 https://github.com/RockChinQ/QChatGPT/releases 查看".format(current_tag, "\n".join(rls_notes)))
return True return True

View File

@@ -0,0 +1,12 @@
{
"prompt": [
{
"role": "system",
"content": "You are a helpful assistant. 如果我需要帮助,你要说“输入!help获得帮助”"
},
{
"role": "assistant",
"content": "好的我是一个能干的AI助手。 如果你需要帮助,我会说“输入!help获得帮助”"
}
]
}

View File

@@ -1,4 +1,7 @@
{ {
"说明": "mask将替换敏感词中的每一个字若mask_word值不为空则将敏感词整个替换为mask_word的值",
"mask": "*",
"mask_word": "",
"words": [ "words": [
"习近平", "习近平",
"胡锦涛", "胡锦涛",