mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-06-07 14:26:03 +00:00
Compare commits
24 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a1bfbad24e | ||
|
|
8af4918048 | ||
|
|
49f4ab0ec8 | ||
|
|
85c623fb0f | ||
|
|
9e28298250 | ||
|
|
7a04ef0985 | ||
|
|
83005e9ba9 | ||
|
|
f0c78f0529 | ||
|
|
3f638adcf9 | ||
|
|
d9405d8d5d | ||
|
|
606713a418 | ||
|
|
52102f0d0a | ||
|
|
61c29829ed | ||
|
|
df30931aad | ||
|
|
5afcc03e8b | ||
|
|
fbeb4673f4 | ||
|
|
4aba319560 | ||
|
|
74f79e002c | ||
|
|
2668ef2b3f | ||
|
|
74c018e271 | ||
|
|
64776fd601 | ||
|
|
59877bf71d | ||
|
|
d2800ac58b | ||
|
|
ffef944119 |
@@ -115,10 +115,9 @@
|
|||||||
|
|
||||||
### - 注册OpenAI账号
|
### - 注册OpenAI账号
|
||||||
|
|
||||||
**可以直接进群找群主购买**
|
参考以下文章自行注册
|
||||||
或参考以下文章自行注册
|
|
||||||
|
|
||||||
> ~~[只需 1 元搞定 ChatGPT 注册](https://zhuanlan.zhihu.com/p/589470082)~~(已失效)
|
> [国内注册ChatGPT的方法(100%可用)](https://www.pythonthree.com/register-openai-chatgpt/)
|
||||||
> [手把手教你如何注册ChatGPT,超级详细](https://guxiaobei.com/51461)
|
> [手把手教你如何注册ChatGPT,超级详细](https://guxiaobei.com/51461)
|
||||||
|
|
||||||
注册成功后请前往[个人中心查看](https://beta.openai.com/account/api-keys)api_key
|
注册成功后请前往[个人中心查看](https://beta.openai.com/account/api-keys)api_key
|
||||||
|
|||||||
@@ -183,6 +183,12 @@ blob_message_threshold = 256
|
|||||||
# - "forward": 将长消息转换为转发消息组件发送
|
# - "forward": 将长消息转换为转发消息组件发送
|
||||||
blob_message_strategy = "forward"
|
blob_message_strategy = "forward"
|
||||||
|
|
||||||
|
# 文字转图片时使用的字体文件路径
|
||||||
|
# 当策略为"image"时生效
|
||||||
|
# 若在Windows系统下,程序会自动使用Windows自带的微软雅黑字体
|
||||||
|
# 若未填写或不存在且不是Windows,将禁用文字转图片功能,改为使用转发消息组件
|
||||||
|
font_path = ""
|
||||||
|
|
||||||
# 消息处理超时重试次数
|
# 消息处理超时重试次数
|
||||||
retry_times = 3
|
retry_times = 3
|
||||||
|
|
||||||
@@ -196,6 +202,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
|
||||||
|
|||||||
41
main.py
41
main.py
@@ -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
|
||||||
@@ -127,13 +129,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字段")
|
||||||
@@ -180,7 +195,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
|
||||||
@@ -340,19 +355,9 @@ 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
|
# import pkg.utils.configmgr
|
||||||
|
|||||||
@@ -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, \
|
||||||
@@ -20,13 +21,6 @@ import pkg.utils.context
|
|||||||
import pkg.plugin.host as plugin_host
|
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()
|
||||||
@@ -55,6 +49,9 @@ def check_response_rule(text: str):
|
|||||||
class QQBotManager:
|
class QQBotManager:
|
||||||
retry = 3
|
retry = 3
|
||||||
|
|
||||||
|
#线程池控制
|
||||||
|
pool = None
|
||||||
|
|
||||||
bot: Mirai = None
|
bot: Mirai = None
|
||||||
|
|
||||||
reply_filter = None
|
reply_filter = None
|
||||||
@@ -64,11 +61,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
|
||||||
@@ -116,7 +116,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 +136,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 +156,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 +173,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):
|
||||||
"""热重载后不再运行此函数"""
|
"""热重载后不再运行此函数"""
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -7,13 +7,15 @@ import pkg.utils.context
|
|||||||
import pkg.plugin.host
|
import pkg.plugin.host
|
||||||
|
|
||||||
|
|
||||||
def walk(module, prefix=''):
|
def walk(module, prefix='', path_prefix=''):
|
||||||
"""遍历并重载所有模块"""
|
"""遍历并重载所有模块"""
|
||||||
for item in pkgutil.iter_modules(module.__path__):
|
for item in pkgutil.iter_modules(module.__path__):
|
||||||
if item.ispkg:
|
if item.ispkg:
|
||||||
walk(__import__(module.__name__ + '.' + item.name, fromlist=['']), prefix + item.name + '.')
|
|
||||||
|
walk(__import__(module.__name__ + '.' + item.name, fromlist=['']), prefix + item.name + '.', path_prefix + item.name + '/')
|
||||||
else:
|
else:
|
||||||
logging.info('reload module: {}'.format(prefix + item.name))
|
logging.info('reload module: {}, path: {}'.format(prefix + item.name, path_prefix + item.name + '.py'))
|
||||||
|
pkg.plugin.host.__current_module_path__ = "plugins/" + path_prefix + item.name + '.py'
|
||||||
importlib.reload(__import__(module.__name__ + '.' + item.name, fromlist=['']))
|
importlib.reload(__import__(module.__name__ + '.' + item.name, fromlist=['']))
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,37 @@
|
|||||||
|
import logging
|
||||||
|
|
||||||
from PIL import Image, ImageDraw, ImageFont
|
from PIL import Image, ImageDraw, ImageFont
|
||||||
import re
|
import re
|
||||||
import os
|
import os
|
||||||
|
import config
|
||||||
|
import traceback
|
||||||
|
|
||||||
text_render_font = ImageFont.truetype("res/simhei.ttf", 32, encoding="utf-8")
|
text_render_font: ImageFont = None
|
||||||
|
|
||||||
|
if hasattr(config, "blob_message_strategy") and config.blob_message_strategy == "image": # 仅在启用了image时才加载字体
|
||||||
|
use_font = config.font_path if hasattr(config, "font_path") else ""
|
||||||
|
try:
|
||||||
|
|
||||||
|
# 检查是否存在
|
||||||
|
if not os.path.exists(use_font):
|
||||||
|
# 若是windows系统,使用微软雅黑
|
||||||
|
if os.name == "nt":
|
||||||
|
use_font = "C:/Windows/Fonts/msyh.ttc"
|
||||||
|
if not os.path.exists(use_font):
|
||||||
|
logging.warn("未找到字体文件,且无法使用Windows自带字体,更换为转发消息组件以发送长消息,您可以在config.py中调整相关设置。")
|
||||||
|
config.blob_message_strategy = "forward"
|
||||||
|
else:
|
||||||
|
logging.info("使用Windows自带字体:" + use_font)
|
||||||
|
text_render_font = ImageFont.truetype(use_font, 32, encoding="utf-8")
|
||||||
|
else:
|
||||||
|
logging.warn("未找到字体文件,且无法使用Windows自带字体,更换为转发消息组件以发送长消息,您可以在config.py中调整相关设置。")
|
||||||
|
config.blob_message_strategy = "forward"
|
||||||
|
else:
|
||||||
|
text_render_font = ImageFont.truetype(use_font, 32, encoding="utf-8")
|
||||||
|
except:
|
||||||
|
traceback.print_exc()
|
||||||
|
logging.error("加载字体文件失败({}),更换为转发消息组件以发送长消息,您可以在config.py中调整相关设置。".format(use_font))
|
||||||
|
config.blob_message_strategy = "forward"
|
||||||
|
|
||||||
|
|
||||||
def indexNumber(path=''):
|
def indexNumber(path=''):
|
||||||
@@ -123,7 +152,7 @@ def text_to_image(text_str: str, save_as="temp.png", width=800):
|
|||||||
else:
|
else:
|
||||||
continue
|
continue
|
||||||
# 准备画布
|
# 准备画布
|
||||||
img = Image.new('RGBA', (width, max(280, len(final_lines) * 35 + 45)), (255, 255, 255, 255))
|
img = Image.new('RGBA', (width, max(280, len(final_lines) * 35 + 65)), (255, 255, 255, 255))
|
||||||
draw = ImageDraw.Draw(img, mode='RGBA')
|
draw = ImageDraw.Draw(img, mode='RGBA')
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|
||||||
@@ -152,24 +165,12 @@ def get_remote_url(repo_path: str) -> str:
|
|||||||
|
|
||||||
def get_current_version_info() -> str:
|
def get_current_version_info() -> str:
|
||||||
"""获取当前版本信息"""
|
"""获取当前版本信息"""
|
||||||
check_dulwich_closure()
|
rls_list = get_release_list()
|
||||||
|
current_tag = get_current_tag()
|
||||||
from dulwich import porcelain
|
for rls in rls_list:
|
||||||
|
if rls['tag_name'] == current_tag:
|
||||||
repo = porcelain.open_repo('.')
|
return rls['name'] + "\n" + rls['body']
|
||||||
|
return "未知版本"
|
||||||
version_str = ""
|
|
||||||
|
|
||||||
for entry in repo.get_walker():
|
|
||||||
version_str += "提交编号: "+str(entry.commit.id)[2:9] + "\n"
|
|
||||||
tz = datetime.timezone(datetime.timedelta(hours=entry.commit.commit_timezone // 3600))
|
|
||||||
dt = datetime.datetime.fromtimestamp(entry.commit.commit_time, tz)
|
|
||||||
version_str += "时间: "+dt.strftime('%m-%d %H:%M:%S') + "\n"
|
|
||||||
version_str += "说明: "+str(entry.commit.message, encoding="utf-8").strip() + "\n"
|
|
||||||
version_str += "提交作者: '" + str(entry.commit.author)[2:-1] + "'"
|
|
||||||
break
|
|
||||||
|
|
||||||
return version_str
|
|
||||||
|
|
||||||
|
|
||||||
def get_commit_id_and_time_and_msg() -> str:
|
def get_commit_id_and_time_and_msg() -> str:
|
||||||
|
|||||||
BIN
res/simhei.ttf
BIN
res/simhei.ttf
Binary file not shown.
Reference in New Issue
Block a user