mirror of
https://github.com/zhenxun-org/zhenxun_bot.git
synced 2026-09-29 16:50:02 +08:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0ea740aa7c | ||
|
|
5f118471cb | ||
|
|
504f78dd49 | ||
|
|
72723c7d1f | ||
|
|
6a74053853 | ||
|
|
af0b4987a4 | ||
|
|
775140cf79 | ||
|
|
6a8a796add | ||
|
|
8e4bda3760 | ||
|
|
a36e8145a8 | ||
|
|
3cbd899daa | ||
|
|
30f356a2c7 | ||
|
|
1138a115ce | ||
|
|
c397da75e7 | ||
|
|
1d141c9313 | ||
|
|
a4cc365af8 | ||
|
|
4e551bec0d | ||
|
|
e03086062d | ||
|
|
270a5a5e20 | ||
|
|
bda9133ed6 | ||
|
|
749f475f2c | ||
|
|
e38394948c | ||
|
|
51fc4cbb6a | ||
|
|
9a976cda37 | ||
|
|
e42521f0a3 | ||
|
|
0756d39ac5 | ||
|
|
4595bdcee4 | ||
|
|
38160065cc | ||
|
|
3d04f013cd | ||
|
|
8e1a416be3 | ||
|
|
a98bc42f1a | ||
|
|
6c93769838 | ||
|
|
d2dccfebc5 | ||
|
|
82fe59e469 | ||
|
|
afc05e1731 | ||
|
|
0206b0a522 | ||
|
|
0e1e5a5ebe | ||
|
|
9ad088ed6a | ||
|
|
34da04bba5 | ||
|
|
09ce812faf | ||
|
|
1dc8cb2a98 | ||
|
|
e73c509823 | ||
|
|
e70a85400f | ||
|
|
f7aecf8eed | ||
|
|
cc2645aa9d | ||
|
|
52d23c4a8d | ||
|
|
1e57ddb9ee | ||
|
|
7ae883d921 | ||
|
|
27e97214bd | ||
|
|
6321a9e7e0 | ||
|
|
a9eadca46c | ||
|
|
76bef8a725 | ||
|
|
308b76dc7d | ||
|
|
31d4d29f3e | ||
|
|
016a7e87ea | ||
|
|
4ce88c7bfc | ||
|
|
f570bffcf7 | ||
|
|
3bb9aaf637 | ||
|
|
cf7a64c934 | ||
|
|
c466603193 | ||
|
|
bc53f329a9 | ||
|
|
4a5cc5153f | ||
|
|
e70d0df949 | ||
|
|
fcaa469edd | ||
|
|
51508182ef | ||
|
|
27e78c1626 | ||
|
|
0b4d37a0b6 | ||
|
|
ddbebba6f1 | ||
|
|
7f296348e4 | ||
|
|
ca78e32d97 | ||
|
|
4efc561f4d | ||
|
|
3c120ce602 | ||
|
|
6151dfb161 | ||
|
|
f05d4ec931 | ||
|
|
a1308fbdc9 | ||
|
|
946fbbec3c | ||
|
|
bd531c85b1 | ||
|
|
cda9aae8a3 | ||
|
|
a33e785043 |
@@ -159,3 +159,5 @@ resources/
|
||||
!/configs/config.yaml
|
||||
!/.env
|
||||
!/.env.dev
|
||||
!/plugins/csgo_server/
|
||||
!/plugins/activity/
|
||||
|
||||
@@ -252,6 +252,10 @@ PS: **ARM平台** 请使用全量版 同时 **如果你的机器 RAM < 1G 可能
|
||||
|
||||
### 感谢名单
|
||||
(可以告诉我你的 __github__ 地址,我偷偷换掉0v|)
|
||||
[腊条](https://afdian.net/u/f739c4d69eca11eba94b52540025c377)
|
||||
[ze roller](https://afdian.net/u/0e599e96257211ed805152540025c377)
|
||||
[爱发电用户_4jrf](https://afdian.net/u/6b2cdcc817c611ed949152540025c377)
|
||||
[爱发电用户_TBsd](https://afdian.net/u/db638b60217911ed9efd52540025c377)
|
||||
[烟寒若雨](https://afdian.net/u/067bd2161eec11eda62b52540025c377)
|
||||
[ln](https://afdian.net/u/b51914ba1c6611ed8a4e52540025c377)
|
||||
[爱发电用户_b9S4](https://afdian.net/u/3d8f30581a2911edba6d52540025c377)
|
||||
@@ -275,6 +279,55 @@ PS: **ARM平台** 请使用全量版 同时 **如果你的机器 RAM < 1G 可能
|
||||
|
||||
## 更新
|
||||
|
||||
### 2022/9/16
|
||||
|
||||
* fix: bilibili_sub, azur_draw_card [@pull/1090](https://github.com/HibiKier/zhenxun_bot/pull/1090)
|
||||
* 修复原神资源查询查询完毕后图片存储错误
|
||||
* b站订阅发送 与 b站订阅 使用相同开关,即:关闭b站订阅
|
||||
|
||||
### 2022/9/10
|
||||
|
||||
* 自定义群欢迎消息参数不完全时提示报错
|
||||
* 修改bt插件的url地址 [@pull/1067](https://github.com/HibiKier/zhenxun_bot/pull/1067)
|
||||
|
||||
### 2022/9/8
|
||||
|
||||
* 添加插件数据初始化判断
|
||||
|
||||
### 2022/9/4
|
||||
|
||||
* 旧词条提供图片迁移(需要重新获取old_model文件,并将数据库中user_qq为0的数据删除)
|
||||
|
||||
### 2022/9/3
|
||||
|
||||
* 原神玩家查询增加须弥地区 [@pull/1053](https://github.com/HibiKier/zhenxun_bot/pull/1053)
|
||||
* av号覆盖全面,且修复av号链接 [@pull/1033](https://github.com/HibiKier/zhenxun_bot/pull/1033)
|
||||
* 修复词条含有CQ回答的模糊匹配无法被解析
|
||||
* 禁言检测图片在内存中获取图片hash
|
||||
* B站订阅在群里中任意群管理员可以统一管理(原来为管理员1无法删除管理员2的订阅)
|
||||
* 修复原神资源查询地图api数据变更导致更新的地图不完全
|
||||
|
||||
### 2022/8/27
|
||||
|
||||
* 修复签到积分双倍后,日志记录获得积分变4倍问题 [@pull/1044](https://github.com/HibiKier/zhenxun_bot/pull/1044)
|
||||
|
||||
### 2022/8/26
|
||||
|
||||
* 修复群管理员无法添加词条
|
||||
* 修复词条关键词"问"前空格问题
|
||||
|
||||
### 2022/8/23
|
||||
|
||||
* 修了下模糊匹配 issue#1026 [@pull/1026](https://github.com/HibiKier/zhenxun_bot/pull/1026)
|
||||
|
||||
### 2022/8/22
|
||||
|
||||
* 修复首次安装时词条旧表出错(因为根本就没有这张表!)
|
||||
* 取消配置替换定时任务,统一存储
|
||||
* 对米游社cookie进行判断,整合米游社签到信息 [@pull/1014](https://github.com/HibiKier/zhenxun_bot/pull/1014)
|
||||
* 修正尘歌壶和质变仪图片获取地址 [@pull/1010](https://github.com/HibiKier/zhenxun_bot/pull/1010)
|
||||
* 修复词库问答 **很多** 问题[@pull/1012](https://github.com/HibiKier/zhenxun_bot/pull/1012)
|
||||
|
||||
### 2022/8/21 \[v0.1.6.3]
|
||||
|
||||
* 重构群词条,改为词库Plus,增加 精准|模糊|正则 问题匹配,问题与回答均支持at,image,face,超级用户额外提供 全局|私聊 词库设置,数据迁移目前只提供了问题和回答都是纯文本的词条
|
||||
@@ -787,7 +840,7 @@ PS: **ARM平台** 请使用全量版 同时 **如果你的机器 RAM < 1G 可能
|
||||
__..... 更多更新信息请查看文档__
|
||||
|
||||
## Todo
|
||||
- [ ] web管理
|
||||
- [x] web管理
|
||||
|
||||
## 感谢
|
||||
[botuniverse / onebot](https://github.com/botuniverse/onebot) :超棒的机器人协议
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
__version__: v0.1.6.3
|
||||
__version__: v0.1.6.3-fix1
|
||||
|
||||
@@ -36,11 +36,11 @@ custom_welcome = on_command(
|
||||
|
||||
@custom_welcome.handle()
|
||||
async def _(event: GroupMessageEvent, arg: Message = CommandArg()):
|
||||
msg = arg.extract_plain_text().strip()
|
||||
img = get_message_img(event.json())
|
||||
if not msg and not img:
|
||||
await custom_welcome.finish(__plugin_usage__)
|
||||
try:
|
||||
msg = arg.extract_plain_text().strip()
|
||||
img = get_message_img(event.json())
|
||||
if not msg and not img:
|
||||
await custom_welcome.finish(__plugin_usage__)
|
||||
await custom_welcome.send(
|
||||
await custom_group_welcome(msg, img, event.user_id, event.group_id),
|
||||
at_sender=True,
|
||||
|
||||
@@ -60,7 +60,7 @@ async def _():
|
||||
try:
|
||||
await bot.send_group_msg(group_id=g, message="[[_task|zwa]]早上好" + result)
|
||||
except ActionFailed:
|
||||
logger.warning(f"{g} 群被禁言中,无法发送早安")
|
||||
logger.warning(f"{g} 发送早安失败")
|
||||
except Exception as e:
|
||||
logger.error(f"早晚安错误 e:{e}")
|
||||
|
||||
@@ -83,7 +83,7 @@ async def _():
|
||||
group_id=g, message=f"[[_task|zwa]]{NICKNAME}要睡觉了,你们也要早点睡呀" + result
|
||||
)
|
||||
except ActionFailed:
|
||||
logger.warning(f"{g} 群被禁言中,无法发送晚安")
|
||||
logger.warning(f"{g} 发送晚安失败")
|
||||
except Exception as e:
|
||||
logger.error(f"早晚安错误 e:{e}")
|
||||
|
||||
|
||||
@@ -27,6 +27,8 @@ def init_plugins_config(data_path):
|
||||
# 优先使用 metadata 数据
|
||||
for matcher in _matchers:
|
||||
_plugin = matcher.plugin
|
||||
if not _plugin:
|
||||
continue
|
||||
metadata = _plugin.metadata
|
||||
try:
|
||||
_module = _plugin.module
|
||||
@@ -108,16 +110,16 @@ def init_plugins_config(data_path):
|
||||
_data, wf, indent=2, Dumper=yaml.RoundTripDumper, allow_unicode=True
|
||||
)
|
||||
user_config_file = Path() / "configs" / "config.yaml"
|
||||
if not user_config_file.exists():
|
||||
_replace_config()
|
||||
else:
|
||||
logger.info('五分钟后将进行配置数据替换,请注意...')
|
||||
scheduler.add_job(
|
||||
_replace_config,
|
||||
"date",
|
||||
run_date=datetime.now() + timedelta(minutes=5),
|
||||
id=f"_replace_config"
|
||||
)
|
||||
# if not user_config_file.exists():
|
||||
_replace_config()
|
||||
# else:
|
||||
# logger.info('五分钟后将进行配置数据替换,请注意...')
|
||||
# scheduler.add_job(
|
||||
# _replace_config,
|
||||
# "date",
|
||||
# run_date=datetime.now() + timedelta(minutes=5),
|
||||
# id=f"_replace_config"
|
||||
# )
|
||||
|
||||
|
||||
def _replace_config():
|
||||
|
||||
@@ -25,6 +25,8 @@ def init_plugins_data(data_path):
|
||||
_matchers = get_matchers(True)
|
||||
for matcher in _matchers:
|
||||
_plugin = matcher.plugin
|
||||
if not _plugin:
|
||||
continue
|
||||
metadata = _plugin.metadata
|
||||
try:
|
||||
_module = _plugin.module
|
||||
|
||||
@@ -34,82 +34,87 @@ def init_plugins_settings(data_path: str):
|
||||
logger.warning(f"配置文件 模块:{x} 获取 plugin_name 失败...{e}")
|
||||
_tmp_module[x] = ""
|
||||
for matcher in _matchers:
|
||||
if matcher.plugin_name not in plugins2settings_manager.keys():
|
||||
_plugin = matcher.plugin
|
||||
metadata = _plugin.metadata
|
||||
try:
|
||||
_module = _plugin.module
|
||||
except AttributeError:
|
||||
logger.warning(f"插件 {matcher.plugin_name} 加载失败...,插件控制未加载.")
|
||||
else:
|
||||
try:
|
||||
if matcher.plugin_name not in plugins2settings_manager.keys():
|
||||
_plugin = matcher.plugin
|
||||
if not _plugin:
|
||||
continue
|
||||
metadata = _plugin.metadata
|
||||
try:
|
||||
if metadata:
|
||||
plugin_name = metadata.name
|
||||
_module = _plugin.module
|
||||
except AttributeError:
|
||||
logger.warning(f"插件 {matcher.plugin_name} 加载失败...,插件控制未加载.")
|
||||
else:
|
||||
try:
|
||||
if metadata:
|
||||
plugin_name = metadata.name
|
||||
else:
|
||||
plugin_name = _module.__getattribute__("__zx_plugin_name__")
|
||||
if "[admin]" in plugin_name.lower():
|
||||
try:
|
||||
admin_settings = _module.__getattribute__(
|
||||
"__plugin_settings__"
|
||||
)
|
||||
level = admin_settings["admin_level"]
|
||||
cmd = admin_settings.get("cmd")
|
||||
except (AttributeError, KeyError):
|
||||
level = 5
|
||||
cmd = None
|
||||
if level is None:
|
||||
level = 5
|
||||
admin_manager.add_admin_plugin_settings(
|
||||
matcher.plugin_name, cmd, level
|
||||
)
|
||||
if (
|
||||
"[hidden]" in plugin_name.lower()
|
||||
or "[admin]" in plugin_name.lower()
|
||||
or "[superuser]" in plugin_name.lower()
|
||||
or matcher.plugin_name in plugins2settings_manager.keys()
|
||||
):
|
||||
continue
|
||||
except AttributeError:
|
||||
if matcher.plugin_name not in _tmp:
|
||||
logger.warning(
|
||||
f"获取插件 {matcher.plugin_name} __zx_plugin_name__ 失败...,插件控制未加载."
|
||||
)
|
||||
else:
|
||||
plugin_name = _module.__getattribute__("__zx_plugin_name__")
|
||||
if "[admin]" in plugin_name.lower():
|
||||
_tmp_module[matcher.plugin_name] = plugin_name
|
||||
try:
|
||||
admin_settings = _module.__getattribute__(
|
||||
plugin_settings = _module.__getattribute__(
|
||||
"__plugin_settings__"
|
||||
)
|
||||
level = admin_settings["admin_level"]
|
||||
cmd = admin_settings.get("cmd")
|
||||
except (AttributeError, KeyError):
|
||||
level = 5
|
||||
cmd = None
|
||||
if level is None:
|
||||
level = 5
|
||||
admin_manager.add_admin_plugin_settings(
|
||||
matcher.plugin_name, cmd, level
|
||||
)
|
||||
if (
|
||||
"[hidden]" in plugin_name.lower()
|
||||
or "[admin]" in plugin_name.lower()
|
||||
or "[superuser]" in plugin_name.lower()
|
||||
or matcher.plugin_name in plugins2settings_manager.keys()
|
||||
):
|
||||
continue
|
||||
except AttributeError:
|
||||
if matcher.plugin_name not in _tmp:
|
||||
logger.warning(
|
||||
f"获取插件 {matcher.plugin_name} __zx_plugin_name__ 失败...,插件控制未加载."
|
||||
)
|
||||
else:
|
||||
_tmp_module[matcher.plugin_name] = plugin_name
|
||||
try:
|
||||
plugin_settings = _module.__getattribute__(
|
||||
"__plugin_settings__"
|
||||
)
|
||||
except AttributeError:
|
||||
plugin_settings = {"cmd": [matcher.plugin_name, plugin_name]}
|
||||
if not plugin_settings.get("cost_gold"):
|
||||
plugin_settings["cost_gold"] = 0
|
||||
if (
|
||||
plugin_settings.get("cmd") is not None
|
||||
and plugin_name not in plugin_settings["cmd"]
|
||||
):
|
||||
plugin_settings["cmd"].append(plugin_name)
|
||||
if plugins2settings_manager.get(
|
||||
matcher.plugin_name
|
||||
) and plugins2settings_manager[matcher.plugin_name].get(
|
||||
"plugin_type"
|
||||
):
|
||||
plugin_type = tuple(
|
||||
plugins2settings_manager.get_plugin_data(
|
||||
matcher.plugin_name
|
||||
)["plugin_type"]
|
||||
)
|
||||
else:
|
||||
try:
|
||||
plugin_type = _module.__getattribute__("__plugin_type__")
|
||||
except AttributeError:
|
||||
plugin_type = ("normal",)
|
||||
if plugin_settings and matcher.plugin_name:
|
||||
plugins2settings_manager.add_plugin_settings(
|
||||
matcher.plugin_name,
|
||||
plugin_type=plugin_type,
|
||||
**plugin_settings,
|
||||
)
|
||||
plugin_settings = {"cmd": [matcher.plugin_name, plugin_name]}
|
||||
if not plugin_settings.get("cost_gold"):
|
||||
plugin_settings["cost_gold"] = 0
|
||||
if (
|
||||
plugin_settings.get("cmd") is not None
|
||||
and plugin_name not in plugin_settings["cmd"]
|
||||
):
|
||||
plugin_settings["cmd"].append(plugin_name)
|
||||
if plugins2settings_manager.get(
|
||||
matcher.plugin_name
|
||||
) and plugins2settings_manager[matcher.plugin_name].get(
|
||||
"plugin_type"
|
||||
):
|
||||
plugin_type = tuple(
|
||||
plugins2settings_manager.get_plugin_data(
|
||||
matcher.plugin_name
|
||||
)["plugin_type"]
|
||||
)
|
||||
else:
|
||||
try:
|
||||
plugin_type = _module.__getattribute__("__plugin_type__")
|
||||
except AttributeError:
|
||||
plugin_type = ("normal",)
|
||||
if plugin_settings and matcher.plugin_name:
|
||||
plugins2settings_manager.add_plugin_settings(
|
||||
matcher.plugin_name,
|
||||
plugin_type=plugin_type,
|
||||
**plugin_settings,
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f'{matcher.plugin_name} 初始化 plugin_settings 发生错误 {type(e)}:{e}')
|
||||
_tmp.append(matcher.plugin_name)
|
||||
_tmp_data = {"PluginSettings": plugins2settings_manager.get_data()}
|
||||
with open(plugins2settings_file, "w", encoding="utf8") as wf:
|
||||
|
||||
@@ -94,13 +94,14 @@ class ConfigsManager:
|
||||
del self._data[module]
|
||||
self.save()
|
||||
|
||||
def set_config(self, module: str, key: str, value: Any, save_simple_data: bool = False):
|
||||
def set_config(self, module: str, key: str, value: Any, auto_save: bool = False, save_simple_data: bool = True):
|
||||
"""
|
||||
设置配置值
|
||||
:param module: 模块名
|
||||
:param key: 配置名称
|
||||
:param value: 值
|
||||
:param save_simple_data: 同时保存至config.yaml
|
||||
:param auto_save: 自动保存
|
||||
:param save_simple_data: 保存至config.yaml
|
||||
"""
|
||||
if module in self._data.keys():
|
||||
if (
|
||||
@@ -109,6 +110,7 @@ class ConfigsManager:
|
||||
):
|
||||
self._data[module][key]["value"] = value
|
||||
self._simple_data[module][key] = value
|
||||
if auto_save:
|
||||
self.save(save_simple_data=save_simple_data)
|
||||
|
||||
def set_help(self, module: str, key: str, help_: str):
|
||||
@@ -145,18 +147,12 @@ class ConfigsManager:
|
||||
:param default: 没有key值内容的默认返回值
|
||||
"""
|
||||
key = key.upper()
|
||||
# 优先使用simple_data
|
||||
if module in self._simple_data.keys():
|
||||
if module in self._data.keys():
|
||||
for key in [key, f"{key} [LEVEL]"]:
|
||||
if self._simple_data[module].get(key) is not None:
|
||||
return self._simple_data[module][key]
|
||||
# if module in self._data.keys():
|
||||
# if module in self._data.keys():
|
||||
# for key in [key, f"{key} [LEVEL]"]:
|
||||
# if self._data[module].get(key) is not None:
|
||||
# if self._data[module][key]["value"] is None:
|
||||
# return self._data[module][key]["value"]
|
||||
# return self._data[module][key]["default_value"]
|
||||
if self._data[module].get(key) is not None:
|
||||
if self._data[module][key]["value"] is None:
|
||||
return self._data[module][key]["default_value"]
|
||||
return self._data[module][key]["value"]
|
||||
if default is not None:
|
||||
return default
|
||||
return None
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 694 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 694 KiB After Width: | Height: | Size: 326 KiB |
@@ -15,6 +15,7 @@ from .data_source import (
|
||||
BilibiliSub,
|
||||
)
|
||||
from models.level_user import LevelUser
|
||||
from utils.manager import group_manager
|
||||
from configs.config import Config
|
||||
from utils.utils import is_number, scheduler, get_bot
|
||||
from typing import Optional, Tuple, Any
|
||||
@@ -160,7 +161,7 @@ async def _(
|
||||
async def _(event: MessageEvent, reg_group: Tuple[Any, ...] = RegexGroup()):
|
||||
msg = reg_group[0]
|
||||
id_ = (
|
||||
f"{event.user_id}:{event.group_id}"
|
||||
f"{event.group_id}"
|
||||
if isinstance(event, GroupMessageEvent)
|
||||
else f"{event.user_id}"
|
||||
)
|
||||
@@ -267,7 +268,8 @@ async def send_sub_msg(rst: str, sub: BilibiliSub, bot: Bot):
|
||||
and Config.get_config("bilibili_sub", "UP_MSG_AT_ALL")
|
||||
):
|
||||
rst = "[CQ:at,qq=all]\n" + rst
|
||||
await bot.send_group_msg(group_id=group_id, message=Message(rst))
|
||||
if group_manager.get_plugin_status("bilibili_sub", group_id):
|
||||
await bot.send_group_msg(group_id=group_id, message=Message(rst))
|
||||
else:
|
||||
await bot.send_private_msg(user_id=int(x), message=Message(rst))
|
||||
except Exception as e:
|
||||
|
||||
@@ -208,7 +208,9 @@ async def get_sub_status(id_: int, sub_type: str) -> Optional[str]:
|
||||
elif sub_type == "season":
|
||||
return await _get_season_status(id_)
|
||||
except ResponseCodeError:
|
||||
return f"Id:{id_} 获取信息失败...请检查订阅Id是否存在或稍后再试..."
|
||||
logger.error(f"Id:{id_} 获取信息失败...请检查订阅Id是否存在或稍后再试...")
|
||||
return None
|
||||
# return f"Id:{id_} 获取信息失败...请检查订阅Id是否存在或稍后再试..."
|
||||
# except Exception as e:
|
||||
# logger.error(f"获取订阅状态发生预料之外的错误 id_:{id_} {type(e)}:{e}")
|
||||
# return "发生了预料之外的错误..请稍后再试或联系管理员....."
|
||||
|
||||
@@ -9,7 +9,7 @@ import platform
|
||||
# asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
|
||||
|
||||
|
||||
url = "http://www.eclzz.plus"
|
||||
url = "http://www.eclzz.cool"
|
||||
|
||||
|
||||
async def get_bt_info(keyword: str, page: int):
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import contextlib
|
||||
import random
|
||||
import dateparser
|
||||
from lxml import etree
|
||||
from typing import List, Optional
|
||||
from typing import List, Optional, Tuple
|
||||
from urllib.parse import unquote
|
||||
from pydantic import ValidationError
|
||||
from nonebot.log import logger
|
||||
@@ -12,7 +13,6 @@ from .base_handle import BaseHandle, BaseData, UpEvent as _UpEvent, UpChar as _U
|
||||
from ..config import draw_config
|
||||
from ..util import remove_prohibited_str, cn2py
|
||||
from utils.image_utils import BuildImage
|
||||
import asyncio
|
||||
|
||||
try:
|
||||
import ujson as json
|
||||
@@ -29,7 +29,7 @@ class AzurChar(BaseData):
|
||||
|
||||
|
||||
class UpChar(_UpChar):
|
||||
type_: str # 舰娘类型
|
||||
type_: str # 舰娘类型
|
||||
|
||||
|
||||
class UpEvent(_UpEvent):
|
||||
@@ -53,7 +53,9 @@ class AzurHandle(BaseHandle[AzurChar]):
|
||||
type_ = ["维修", "潜艇", "重巡", "轻航", "航母"]
|
||||
up_pool_flag = pool_name == "活动"
|
||||
# Up
|
||||
up_ship = [x for x in self.UP_EVENT.up_char if x.zoom > 0]
|
||||
up_ship = (
|
||||
[x for x in self.UP_EVENT.up_char if x.zoom > 0] if self.UP_EVENT else []
|
||||
)
|
||||
# print(up_ship)
|
||||
acquire_char = None
|
||||
if up_ship and up_pool_flag:
|
||||
@@ -61,19 +63,15 @@ class AzurHandle(BaseHandle[AzurChar]):
|
||||
# 初始化概率
|
||||
cur_ = up_ship[0].zoom / 100
|
||||
for i in range(len(up_ship)):
|
||||
try:
|
||||
up_zoom.append((cur_, cur_ + up_ship[i+1].zoom / 100))
|
||||
cur_ += up_ship[i+1].zoom / 100
|
||||
except IndexError:
|
||||
pass
|
||||
with contextlib.suppress(IndexError):
|
||||
up_zoom.append((cur_, cur_ + up_ship[i + 1].zoom / 100))
|
||||
cur_ += up_ship[i + 1].zoom / 100
|
||||
rand = random.random()
|
||||
# 抽取up
|
||||
for i, zoom in enumerate(up_zoom):
|
||||
if zoom[0] <= rand <= zoom[1]:
|
||||
try:
|
||||
with contextlib.suppress(IndexError):
|
||||
acquire_char = [x for x in self.ALL_CHAR if x.name == up_ship[i].name][0]
|
||||
except IndexError:
|
||||
pass
|
||||
# 没有up或者未抽取到up
|
||||
if not acquire_char:
|
||||
star = self.get_star(
|
||||
@@ -92,9 +90,6 @@ class AzurHandle(BaseHandle[AzurChar]):
|
||||
])
|
||||
return acquire_char
|
||||
|
||||
# async def draw(self, count: int, **kwargs) -> Message:
|
||||
# return await asyncio.get_event_loop().run_in_executor(None, self._draw, count)
|
||||
|
||||
async def draw(self, count: int, **kwargs) -> Message:
|
||||
index2card = self.get_cards(count, **kwargs)
|
||||
cards = [card[0] for card in index2card]
|
||||
@@ -162,22 +157,22 @@ class AzurHandle(BaseHandle[AzurChar]):
|
||||
return
|
||||
dom = etree.HTML(result, etree.HTMLParser())
|
||||
contents = dom.xpath(
|
||||
"//div[@class='resp-tabs-container']/div[@class='resp-tab-content']"
|
||||
"//div[@class='mw-body-content mw-content-ltr']/div[@class='mw-parser-output']"
|
||||
)
|
||||
for index, content in enumerate(contents):
|
||||
char_list = content.xpath("./table/tbody/tr[2]/td/div/div/div/div")
|
||||
char_list = content.xpath("./div[@id='CardSelectTr']/div")
|
||||
for char in char_list:
|
||||
try:
|
||||
name = char.xpath("./a/@title")[0]
|
||||
frame = char.xpath("./div/a/img/@alt")[0]
|
||||
avatar = char.xpath("./a/img/@srcset")[0]
|
||||
name = char.xpath("./div/a/@title")[0]
|
||||
frame = char.xpath("./div/div/a/img/@alt")[0]
|
||||
avatar = char.xpath("./div/a/img/@srcset")[0]
|
||||
except IndexError:
|
||||
continue
|
||||
member_dict = {
|
||||
"名称": remove_prohibited_str(name),
|
||||
"头像": unquote(str(avatar).split(" ")[-2]),
|
||||
"星级": self.parse_star(frame),
|
||||
"类型": self.parse_type(index),
|
||||
"类型": char.xpath("./@data-param1")[0].split(",")[1],
|
||||
}
|
||||
info[member_dict["名称"]] = member_dict
|
||||
# 更新额外信息
|
||||
@@ -225,48 +220,21 @@ class AzurHandle(BaseHandle[AzurChar]):
|
||||
|
||||
@staticmethod
|
||||
def parse_star(star: str) -> int:
|
||||
if star in ["舰娘头像外框普通.png", "舰娘头像外框白色.png"]:
|
||||
if star in {"舰娘头像外框普通.png", "舰娘头像外框白色.png"}:
|
||||
return 1
|
||||
elif star in ["舰娘头像外框稀有.png", "舰娘头像外框蓝色.png"]:
|
||||
elif star in {"舰娘头像外框稀有.png", "舰娘头像外框蓝色.png"}:
|
||||
return 2
|
||||
elif star in ["舰娘头像外框精锐.png", "舰娘头像外框紫色.png"]:
|
||||
elif star in {"舰娘头像外框精锐.png", "舰娘头像外框紫色.png"}:
|
||||
return 3
|
||||
elif star in ["舰娘头像外框超稀有.png", "舰娘头像外框金色.png"]:
|
||||
elif star in {"舰娘头像外框超稀有.png", "舰娘头像外框金色.png"}:
|
||||
return 4
|
||||
elif star in ["舰娘头像外框海上传奇.png", "舰娘头像外框彩色.png"]:
|
||||
elif star in {"舰娘头像外框海上传奇.png", "舰娘头像外框彩色.png"}:
|
||||
return 5
|
||||
elif star in [
|
||||
"舰娘头像外框最高方案.png",
|
||||
"舰娘头像外框决战方案.png",
|
||||
"舰娘头像外框超稀有META.png",
|
||||
"舰娘头像外框精锐META.png",
|
||||
]:
|
||||
elif star in {"舰娘头像外框最高方案.png", "舰娘头像外框决战方案.png", "舰娘头像外框超稀有META.png", "舰娘头像外框精锐META.png"}:
|
||||
return 6
|
||||
else:
|
||||
return 6
|
||||
|
||||
@staticmethod
|
||||
def parse_type(index: int) -> str:
|
||||
azur_types = [
|
||||
"驱逐",
|
||||
"轻巡",
|
||||
"重巡",
|
||||
"超巡",
|
||||
"战巡",
|
||||
"战列",
|
||||
"航母",
|
||||
"航站",
|
||||
"轻航",
|
||||
"重炮",
|
||||
"维修",
|
||||
"潜艇",
|
||||
"运输",
|
||||
]
|
||||
try:
|
||||
return azur_types[index]
|
||||
except IndexError:
|
||||
return azur_types[0]
|
||||
|
||||
async def update_up_char(self):
|
||||
url = "https://wiki.biligame.com/blhx/游戏活动表"
|
||||
result = await self.get_url(url)
|
||||
@@ -290,7 +258,7 @@ class AzurHandle(BaseHandle[AzurChar]):
|
||||
up_chars = []
|
||||
for ship in ships:
|
||||
name = ship.xpath("./tbody/tr/td[2]/p/a/@title")[0]
|
||||
type_ = ship.xpath("./tbody/tr/td[2]/p/small/text()")[0] # 舰船类型
|
||||
type_ = ship.xpath("./tbody/tr/td[2]/p/small/text()")[0] # 舰船类型
|
||||
try:
|
||||
p = float(str(ship.xpath(".//sup/text()")[0]).strip("%"))
|
||||
except (IndexError, ValueError):
|
||||
@@ -298,7 +266,9 @@ class AzurHandle(BaseHandle[AzurChar]):
|
||||
star = self.parse_star(
|
||||
ship.xpath("./tbody/tr/td[1]/div/div/div/a/img/@alt")[0]
|
||||
)
|
||||
up_chars.append(UpChar(name=name, star=star, limited=False, zoom=p, type_=type_))
|
||||
up_chars.append(
|
||||
UpChar(name=name, star=star, limited=False, zoom=p, type_=type_)
|
||||
)
|
||||
self.UP_EVENT = UpEvent(
|
||||
title=title,
|
||||
pool_img="",
|
||||
|
||||
+6
-15
@@ -1,6 +1,8 @@
|
||||
from io import BytesIO
|
||||
import imagehash
|
||||
from PIL import Image
|
||||
from nonebot.adapters.onebot.v11.permission import GROUP
|
||||
from configs.path_config import TEMP_PATH
|
||||
from utils.image_utils import get_img_hash
|
||||
import random
|
||||
from utils.message_builder import image
|
||||
from nonebot import on_message
|
||||
@@ -8,7 +10,6 @@ from utils.utils import get_message_img, get_message_text
|
||||
from nonebot.adapters.onebot.v11 import GroupMessageEvent
|
||||
from configs.config import Config
|
||||
from utils.http_utils import AsyncHttpx
|
||||
from services.log import logger
|
||||
from configs.config import NICKNAME
|
||||
|
||||
|
||||
@@ -90,7 +91,7 @@ async def _(event: GroupMessageEvent):
|
||||
if not img and not msg:
|
||||
return
|
||||
if img:
|
||||
img_hash = await get_fudu_img_hash(img[0], event.group_id)
|
||||
img_hash = await get_fudu_img_hash(img[0])
|
||||
else:
|
||||
img_hash = ""
|
||||
add_msg = msg + "|-|" + img_hash
|
||||
@@ -123,15 +124,5 @@ async def _(event: GroupMessageEvent):
|
||||
await fudu.finish("[[_task|fudu]]" + rst)
|
||||
|
||||
|
||||
async def get_fudu_img_hash(url, group_id):
|
||||
try:
|
||||
if await AsyncHttpx.download_file(
|
||||
url, TEMP_PATH / f"compare_{group_id}_img.jpg"
|
||||
):
|
||||
img_hash = get_img_hash(TEMP_PATH / f"compare_{group_id}_img.jpg")
|
||||
return str(img_hash)
|
||||
else:
|
||||
logger.warning(f"复读下载图片失败...")
|
||||
except Exception as e:
|
||||
logger.warning(f"复读读取图片Hash出错 {type(e)}:{e}")
|
||||
return ""
|
||||
async def get_fudu_img_hash(url):
|
||||
return str(imagehash.average_hash(Image.open(BytesIO((await AsyncHttpx.get(url)).content))))
|
||||
|
||||
@@ -47,7 +47,7 @@ __plugin_block_limit__ = {
|
||||
qr = on_command("原神资源查询", aliases={"原神资源查找"}, priority=5, block=True)
|
||||
qr_lst = on_command("原神资源列表", priority=5, block=True)
|
||||
rex_qr = on_regex(".*?(在哪|在哪里|哪有|哪里有).*?", priority=5, block=True)
|
||||
update_info = on_command("更新原神资源信息", permission=SUPERUSER, priority=1, block=True)
|
||||
update_info = on_regex("^更新原神资源信息$", permission=SUPERUSER, priority=1, block=True)
|
||||
|
||||
|
||||
@qr.handle()
|
||||
|
||||
@@ -118,7 +118,7 @@ class Map:
|
||||
self._generate_best_route()
|
||||
self.map.crop((min_width, min_height, max_width, max_height))
|
||||
rand = random.randint(1, 10000)
|
||||
self.map.save(f"{IMAGE_PATH}/temp/genshin_map_{rand}.png")
|
||||
self.map.save(f"{TEXT_PATH}/genshin_map_{rand}.png")
|
||||
return rand
|
||||
|
||||
# 资源数量
|
||||
|
||||
@@ -172,28 +172,29 @@ async def download_map_init(semaphore: Semaphore, flag: bool = False):
|
||||
if data["message"] == "OK":
|
||||
data = json.loads(data["data"]["info"]["detail"])
|
||||
CENTER_POINT = (data["origin"][0], data["origin"][1])
|
||||
if not _map.exists():
|
||||
if not _map.exists() or flag:
|
||||
data = data["slices"]
|
||||
idx = 0
|
||||
for _map_data in data[0]:
|
||||
map_url = _map_data["url"]
|
||||
await download_image(
|
||||
map_url,
|
||||
map_path / f"{idx}.png",
|
||||
semaphore,
|
||||
force_flag=flag,
|
||||
)
|
||||
BuildImage(
|
||||
0, 0, background=f"{map_path}/{idx}.png", ratio=MAP_RATIO
|
||||
).save()
|
||||
idx += 1
|
||||
_w, h = BuildImage(0, 0, background=f"{map_path}/0.png").size
|
||||
w = _w * len(os.listdir(map_path))
|
||||
map_file = BuildImage(w, h, _w, h, ratio=MAP_RATIO)
|
||||
w_len = len(data[0])
|
||||
h_len = len(data)
|
||||
for _map_data in data:
|
||||
for _map in _map_data:
|
||||
map_url = _map["url"]
|
||||
await download_image(
|
||||
map_url,
|
||||
map_path / f"{idx}.png",
|
||||
semaphore,
|
||||
force_flag=flag,
|
||||
)
|
||||
BuildImage(
|
||||
0, 0, background=f"{map_path}/{idx}.png", ratio=MAP_RATIO
|
||||
).save()
|
||||
idx += 1
|
||||
w, h = BuildImage(0, 0, background=f"{map_path}/0.png").size
|
||||
map_file = BuildImage(w * w_len, h * h_len, w, h, ratio=MAP_RATIO)
|
||||
for i in range(idx):
|
||||
map_file.paste(
|
||||
BuildImage(0, 0, background=f"{map_path}/{i}.png")
|
||||
)
|
||||
img = BuildImage(0, 0, background=f"{map_path}/{i}.png")
|
||||
await map_file.apaste(img)
|
||||
map_file.save(f"{map_path}/map.png")
|
||||
else:
|
||||
logger.warning(f'获取原神地图失败 msg: {data["message"]}')
|
||||
@@ -202,7 +203,7 @@ async def download_map_init(semaphore: Semaphore, flag: bool = False):
|
||||
except (TimeoutError, ConnectTimeout):
|
||||
logger.warning("下载原神地图数据超时....")
|
||||
except Exception as e:
|
||||
logger.error(f"下载原神地图数据超时 {type(e)}:{e}")
|
||||
logger.error(f"下载原神地图数据失败 {type(e)}:{e}")
|
||||
|
||||
|
||||
# 下载资源类型数据
|
||||
|
||||
@@ -93,6 +93,8 @@ async def _(event: MessageEvent, cmd: Tuple[str, ...] = Command(), arg: Message
|
||||
print(cookie)
|
||||
cookie_json = json.loads(cookie)
|
||||
print(cookie_json)
|
||||
if 'login_ticket' not in cookie_json:
|
||||
await bind.finish("请发送正确完整的cookie!")
|
||||
login_ticket = cookie_json['login_ticket']
|
||||
# try:
|
||||
res = await AsyncHttpx.get(url=bbs_Cookie_url.format(login_ticket))
|
||||
|
||||
@@ -44,6 +44,8 @@ async def _(event: MessageEvent, cmd: Tuple[str, ...] = Command()):
|
||||
uid = await Genshin.get_user_uid(event.user_id)
|
||||
if cmd == "查看我的cookie":
|
||||
my_cookie = await Genshin.get_user_cookie(uid, True)
|
||||
if isinstance(event, GroupMessageEvent):
|
||||
await genshin_matcher.finish("请私聊查看您的cookie!")
|
||||
await genshin_matcher.finish("您的cookie为" + my_cookie)
|
||||
if not uid or not await Genshin.get_user_cookie(uid, True):
|
||||
await genshin_matcher.finish("请先绑定uid和cookie!")
|
||||
@@ -51,9 +53,9 @@ async def _(event: MessageEvent, cmd: Tuple[str, ...] = Command()):
|
||||
# await genshin_matcher.finish("请更新cookie!")
|
||||
if cmd == "原神我硬签":
|
||||
try:
|
||||
await genshin_matcher.send("正在进行签到...", at_sender=True)
|
||||
msg = await genshin_sign(uid)
|
||||
return_data = await mihoyobbs_sign(event.user_id)
|
||||
await genshin_matcher.send(return_data)
|
||||
logger.info(
|
||||
f"(USER {event.user_id}, "
|
||||
f"GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'}) UID:{uid} 原神签到"
|
||||
@@ -70,7 +72,7 @@ async def _(event: MessageEvent, cmd: Tuple[str, ...] = Command()):
|
||||
await u.clear_sign_time(uid)
|
||||
next_date = await Genshin.random_sign_time(uid)
|
||||
add_job(event.user_id, uid, next_date)
|
||||
msg += f"\n因开启自动签到\n下一次签到时间为:{next_date.replace(microsecond=0)}"
|
||||
msg += f"\n{return_data}\n因开启自动签到\n下一次签到时间为:{next_date.replace(microsecond=0)}"
|
||||
except Exception as e:
|
||||
msg = "原神签到失败..请尝试检查cookie或报告至管理员!"
|
||||
logger.info(
|
||||
|
||||
@@ -38,16 +38,18 @@ mihoyobbs_matcher = on_command(
|
||||
|
||||
@mihoyobbs_matcher.handle()
|
||||
async def _(event: MessageEvent, cmd: Tuple[str, ...] = Command()):
|
||||
await mihoyobbs_matcher.send("提交米游社签到申请")
|
||||
await mihoyobbs_matcher.send("提交米游社签到申请", at_sender=True)
|
||||
return_data = await mihoyobbs_sign(event.user_id)
|
||||
if return_data:
|
||||
await mihoyobbs_matcher.finish(return_data)
|
||||
await mihoyobbs_matcher.finish(return_data, at_sender=True)
|
||||
else:
|
||||
await mihoyobbs_matcher.finish("米游社签到失败,请查看控制台输出")
|
||||
await mihoyobbs_matcher.finish("米游社签到失败,请查看控制台输出", at_sender=True)
|
||||
|
||||
|
||||
async def mihoyobbs_sign(user_id):
|
||||
uid = await Genshin.get_user_uid(user_id)
|
||||
if not uid or not await Genshin.get_user_cookie(uid, True):
|
||||
await mihoyobbs_matcher.finish("请先绑定uid和cookie!", at_sender=True)
|
||||
stuid = await Genshin.get_stuid(uid)
|
||||
stoken = await Genshin.get_stoken(uid)
|
||||
cookie = await Genshin.get_user_cookie(uid)
|
||||
|
||||
@@ -35,8 +35,8 @@ async def _():
|
||||
"20.png?x-oss-process=image//resize,s_600/quality,q_80/auto-orient,0/interlace,1/format,png",
|
||||
"https://patchwiki.biligame.com/images/ys/thumb/c/cc/6k6kuj1kte6m1n7hexqfrn92z6h4yhh.png/60px-委托任务logo.png",
|
||||
"https://patchwiki.biligame.com/images/ys/d/d9/t1hv6wpucbwucgkhjntmzroh90nmcdv.png",
|
||||
"https://s3.bmp.ovh/imgs/2022/07/24/28d9338c7da4bcb2.png",
|
||||
"https://genshin.honeyhunterworld.com/img/gadget/i_3016.png",
|
||||
"https://s3.bmp.ovh/imgs/2022/08/21/3a3b2e6c22e305ff.png",
|
||||
"https://s3.bmp.ovh/imgs/2022/08/21/c2d7ace21e1d46cf.png",
|
||||
],
|
||||
):
|
||||
file = memo_path / name
|
||||
|
||||
@@ -195,6 +195,7 @@ def parsed_data(
|
||||
"magic_chest_number": data["stats"]["magic_chest_number"], # 奇馈宝箱
|
||||
"common_chest_number": data["stats"]["common_chest_number"], # 普通宝箱
|
||||
"electroculus_number": data["stats"]["electroculus_number"], # 雷神瞳已收集
|
||||
"dendroculus_number": data["stats"]["dendroculus_number"], # 草神瞳已收集
|
||||
}
|
||||
world_data_dict = {}
|
||||
for world in data["world_explorations"]:
|
||||
|
||||
@@ -129,7 +129,7 @@ def get_user_data_image(
|
||||
"""
|
||||
if mys_data:
|
||||
nickname = [x["nickname"] for x in mys_data if x["game_id"] == 2][0]
|
||||
region = BuildImage(1440, 450, color="#E3DBD1", font="HYWenHei-85W.ttf")
|
||||
region = BuildImage(1440, 560, color="#E3DBD1", font="HYWenHei-85W.ttf")
|
||||
region.circle_corner(30)
|
||||
uname_img = BuildImage(
|
||||
0,
|
||||
@@ -163,36 +163,42 @@ def get_user_data_image(
|
||||
/ random.choice(os.listdir(image_path / "chars_ava")),
|
||||
)
|
||||
ava_bk.paste(ava_img, (12, 16), alpha=True)
|
||||
region.paste(uname_img, (int(170 + uid_img.w / 2 - uname_img.w / 2), 305), True)
|
||||
region.paste(uid_img, (170, 355), True)
|
||||
region.paste(ava_bk, (int(550 / 2 - ava_bk.w / 2), 40), True)
|
||||
region.paste(uname_img, (int(170 + uid_img.w / 2 - uname_img.w / 2), 365), True)
|
||||
region.paste(uid_img, (170, 415), True)
|
||||
region.paste(ava_bk, (int(550 / 2 - ava_bk.w / 2), 100), True)
|
||||
data_img = BuildImage(
|
||||
800, 400, color="#E3DBD1", font="HYWenHei-85W.ttf", font_size=40
|
||||
800, 510, color="#E3DBD1", font="HYWenHei-85W.ttf", font_size=40
|
||||
)
|
||||
_height = 0
|
||||
keys = [
|
||||
["活跃天数", "成就达成", "获得角色", "深境螺旋"],
|
||||
["华丽宝箱", "珍贵宝箱", "精致宝箱", "普通宝箱"],
|
||||
["奇馈宝箱", "风神瞳", "岩神瞳", "雷神瞳"],
|
||||
["活跃天数", "成就达成", "获得角色", "解锁传送"],
|
||||
["风神瞳", "岩神瞳", "雷神瞳", "草神瞳"],
|
||||
["解锁秘境", "深境螺旋", "华丽宝箱", "珍贵宝箱"],
|
||||
["精致宝箱", "普通宝箱", "奇馈宝箱",],
|
||||
]
|
||||
values = [
|
||||
[
|
||||
role_data["active_day_number"],
|
||||
role_data["achievement_number"],
|
||||
role_data["avatar_number"],
|
||||
role_data["spiral_abyss"],
|
||||
role_data["way_point_number"],
|
||||
],
|
||||
[
|
||||
role_data["luxurious_chest_number"],
|
||||
role_data["precious_chest_number"],
|
||||
role_data["exquisite_chest_number"],
|
||||
role_data["common_chest_number"],
|
||||
],
|
||||
[
|
||||
role_data["magic_chest_number"],
|
||||
role_data["anemoculus_number"],
|
||||
role_data["geoculus_number"],
|
||||
role_data["electroculus_number"],
|
||||
role_data["dendroculus_number"],
|
||||
],
|
||||
[
|
||||
role_data["domain_number"],
|
||||
role_data["spiral_abyss"],
|
||||
role_data["luxurious_chest_number"],
|
||||
role_data["precious_chest_number"],
|
||||
],
|
||||
[
|
||||
role_data["exquisite_chest_number"],
|
||||
role_data["common_chest_number"],
|
||||
role_data["magic_chest_number"],
|
||||
],
|
||||
]
|
||||
for key, value in zip(keys, values):
|
||||
@@ -215,7 +221,7 @@ def get_user_data_image(
|
||||
)
|
||||
tmp_.text((0, 0), str(v), center_type="by_width")
|
||||
tmp_.paste(t_, (0, 50), True, "by_width")
|
||||
_tmp_data_img.paste(tmp_, (_width if len(key) > 3 else _width + 15, 0))
|
||||
_tmp_data_img.paste(tmp_, ((_width + 15) if keys.index(key) == 1 else _width, 0))
|
||||
_width += 200
|
||||
data_img.paste(_tmp_data_img, (0, _height))
|
||||
_height += _tmp_data_img.h - 70
|
||||
@@ -228,7 +234,7 @@ def get_home_data_image(home_data_list: List[Dict]) -> BuildImage:
|
||||
画出家园数据
|
||||
:param home_data_list: 家园列表
|
||||
"""
|
||||
h = 130 + 300 * 4
|
||||
h = 130 + 340 * 4
|
||||
region = BuildImage(
|
||||
550, h, color="#E3DBD1", font="HYWenHei-85W.ttf", font_size=40
|
||||
)
|
||||
@@ -287,7 +293,7 @@ def get_home_data_image(home_data_list: List[Dict]) -> BuildImage:
|
||||
x.paste(black_img, alpha=True, center_type="center")
|
||||
x.circle_corner(50)
|
||||
region.paste(x, (0, height), True, "by_width")
|
||||
height += 300
|
||||
height += 340
|
||||
return region
|
||||
|
||||
|
||||
@@ -297,9 +303,9 @@ def get_country_data_image(world_data_dict: Dict) -> BuildImage:
|
||||
:param world_data_dict: 国家数据字典
|
||||
"""
|
||||
# 层岩巨渊 和 地下矿区 算一个
|
||||
region = BuildImage(790, 267 * (len(world_data_dict) - 1), color="#F9F6F2")
|
||||
region = BuildImage(790, 267 * ((len(world_data_dict) - 1) if world_data_dict.get("层岩巨渊·地下矿区") else len(world_data_dict)), color="#F9F6F2")
|
||||
height = 0
|
||||
for country in ["蒙德", "龙脊雪山", "璃月", "层岩巨渊", "稻妻", "渊下宫"]:
|
||||
for country in ["蒙德", "龙脊雪山", "璃月", "层岩巨渊", "稻妻", "渊下宫", "须弥"]:
|
||||
if not world_data_dict.get(country):
|
||||
continue
|
||||
x = BuildImage(790, 250, color="#3A4467")
|
||||
@@ -311,15 +317,15 @@ def get_country_data_image(world_data_dict: Dict) -> BuildImage:
|
||||
)
|
||||
content_bk.paste(logo, (50, 0), True, "by_height")
|
||||
if country in ["蒙德", "璃月"]:
|
||||
content_bk.text((300, 40), "探索", fill=(239, 211, 114))
|
||||
content_bk.text((300, 40), "蒙德探索" if country == "蒙德" else "璃月探索", fill=(239, 211, 114))
|
||||
content_bk.text(
|
||||
(450, 40),
|
||||
(500, 40),
|
||||
f"{world_data_dict[country]['exploration_percentage'] / 10}%",
|
||||
fill=(255, 255, 255),
|
||||
)
|
||||
content_bk.text((300, 120), "声望", fill=(239, 211, 114))
|
||||
content_bk.text((300, 120), "蒙德声望" if country == "蒙德" else "璃月声望", fill=(239, 211, 114))
|
||||
content_bk.text(
|
||||
(450, 120),
|
||||
(500, 120),
|
||||
f"Lv.{world_data_dict[country]['level']}",
|
||||
fill=(255, 255, 255),
|
||||
)
|
||||
@@ -344,45 +350,65 @@ def get_country_data_image(world_data_dict: Dict) -> BuildImage:
|
||||
fill=(255, 255, 255),
|
||||
)
|
||||
elif country in ["龙脊雪山"]:
|
||||
content_bk.text((300, 40), "探索", fill=(239, 211, 114))
|
||||
content_bk.text((300, 40), "雪山探索", fill=(239, 211, 114))
|
||||
content_bk.text(
|
||||
(450, 40),
|
||||
(500, 40),
|
||||
f"{world_data_dict[country]['exploration_percentage'] / 10}%",
|
||||
fill=(255, 255, 255),
|
||||
)
|
||||
content_bk.text((300, 120), "供奉", fill=(239, 211, 114))
|
||||
content_bk.text((300, 120), "忍冬之树", fill=(239, 211, 114))
|
||||
content_bk.text(
|
||||
(450, 120),
|
||||
(500, 120),
|
||||
f"Lv.{world_data_dict[country]['offerings'][0]['level']}",
|
||||
fill=(255, 255, 255),
|
||||
)
|
||||
elif country in ["稻妻"]:
|
||||
content_bk.text((300, 20), "探索", fill=(239, 211, 114))
|
||||
content_bk.text((300, 20), "稻妻探索", fill=(239, 211, 114))
|
||||
content_bk.text(
|
||||
(450, 20),
|
||||
(500, 20),
|
||||
f"{world_data_dict[country]['exploration_percentage'] / 10}%",
|
||||
fill=(255, 255, 255),
|
||||
)
|
||||
content_bk.text((300, 85), "声望", fill=(239, 211, 114))
|
||||
content_bk.text((300, 85), "稻妻声望", fill=(239, 211, 114))
|
||||
content_bk.text(
|
||||
(450, 85),
|
||||
(500, 85),
|
||||
f"Lv.{world_data_dict[country]['level']}",
|
||||
fill=(255, 255, 255),
|
||||
)
|
||||
content_bk.text((300, 150), "神樱", fill=(239, 211, 114))
|
||||
content_bk.text((300, 150), "神樱眷顾", fill=(239, 211, 114))
|
||||
content_bk.text(
|
||||
(450, 150),
|
||||
(500, 150),
|
||||
f"Lv.{world_data_dict[country]['offerings'][0]['level']}",
|
||||
fill=(255, 255, 255),
|
||||
)
|
||||
elif country in ["渊下宫"]:
|
||||
content_bk.text((300, 0), "探索", fill=(239, 211, 114), center_type="by_height")
|
||||
content_bk.text((300, 0), "渊下宫探索", fill=(239, 211, 114), center_type="by_height")
|
||||
content_bk.text(
|
||||
(450, 20),
|
||||
(530, 20),
|
||||
f"{world_data_dict[country]['exploration_percentage'] / 10}%",
|
||||
fill=(255, 255, 255),
|
||||
center_type="by_height",
|
||||
)
|
||||
elif country in ["须弥"]:
|
||||
content_bk.text((300, 20), "须弥探索", fill=(239, 211, 114))
|
||||
content_bk.text(
|
||||
(500, 20),
|
||||
f"{world_data_dict[country]['exploration_percentage'] / 10}%",
|
||||
fill=(255, 255, 255),
|
||||
)
|
||||
content_bk.text((300, 85), "须弥声望", fill=(239, 211, 114))
|
||||
content_bk.text(
|
||||
(500, 85),
|
||||
f"Lv.{world_data_dict[country]['level']}",
|
||||
fill=(255, 255, 255),
|
||||
)
|
||||
content_bk.text((300, 150), "梦之树", fill=(239, 211, 114))
|
||||
content_bk.text(
|
||||
(500, 150),
|
||||
f"Lv.{world_data_dict[country]['offerings'][0]['level']}",
|
||||
fill=(255, 255, 255),
|
||||
)
|
||||
|
||||
x.paste(tmp_bk, alpha=True, center_type="center")
|
||||
x.paste(content_bk, alpha=True, center_type="center")
|
||||
x.circle_corner(20)
|
||||
|
||||
+7
-5
@@ -1,3 +1,7 @@
|
||||
from io import BytesIO
|
||||
|
||||
import imagehash
|
||||
from PIL import Image
|
||||
from nonebot import on_message, on_command
|
||||
from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, Message, ActionFailed
|
||||
from nonebot.adapters.onebot.v11.permission import GROUP
|
||||
@@ -70,10 +74,8 @@ def save_data():
|
||||
json.dump(mute_data, f, indent=4)
|
||||
|
||||
|
||||
async def download_img_and_hash(url, group_id) -> str:
|
||||
if await AsyncHttpx.download_file(url, TEMP_PATH / f"mute_{group_id}_img.jpg"):
|
||||
return str(get_img_hash(TEMP_PATH / f"mute_{group_id}_img.jpg"))
|
||||
return ""
|
||||
async def download_img_and_hash(url) -> str:
|
||||
return str(imagehash.average_hash(Image.open(BytesIO((await AsyncHttpx.get(url)).content))))
|
||||
|
||||
|
||||
mute_dict = {}
|
||||
@@ -87,7 +89,7 @@ async def _(bot: Bot, event: GroupMessageEvent):
|
||||
img_list = get_message_img(event.json())
|
||||
img_hash = ""
|
||||
for img in img_list:
|
||||
img_hash += await download_img_and_hash(img, event.group_id)
|
||||
img_hash += await download_img_and_hash(img)
|
||||
msg += img_hash
|
||||
if not mute_data.get(group_id):
|
||||
mute_data[group_id] = {
|
||||
|
||||
@@ -110,10 +110,10 @@ async def _(event: GroupMessageEvent):
|
||||
vd_info = await video.get_video_base_info(msg)
|
||||
elif "av" in msg:
|
||||
index = msg.find("av")
|
||||
if len(msg[index + 2 :]) >= 9:
|
||||
if len(msg[index + 2 :]) >= 1:
|
||||
msg = msg[index + 2 : index + 11]
|
||||
if is_number(msg):
|
||||
url = f"https://www.bilibili.com/video/{msg}"
|
||||
url = f"https://www.bilibili.com/video/av{msg}"
|
||||
vd_info = await video.get_video_base_info('av' + msg)
|
||||
elif "https://b23.tv" in msg:
|
||||
url = "https://" + msg[msg.find("b23.tv"): msg.find("b23.tv") + 14]
|
||||
|
||||
@@ -24,7 +24,7 @@ __plugin_settings__ = {
|
||||
}
|
||||
|
||||
|
||||
quotations = on_regex("^[语录|二次元]$", priority=5, block=True)
|
||||
quotations = on_regex("^(语录|二次元)$", priority=5, block=True)
|
||||
|
||||
url = "https://international.v1.hitokoto.cn/?c=a"
|
||||
|
||||
|
||||
@@ -81,19 +81,15 @@ async def _handle_check_in(
|
||||
await BagUser.add_gold(user_qq, group, gold)
|
||||
await BagUser.add_property(user_qq, group, gift)
|
||||
gift += ' + 1'
|
||||
|
||||
logger.info(
|
||||
f"(USER {user.user_qq}, GROUP {user.group_id})"
|
||||
f" CHECKED IN successfully. score: {user.impression:.2f} "
|
||||
f"(+{impression_added:.2f}).获取金币:{gold + gift if gift == 'gold' else gold}"
|
||||
)
|
||||
if critx2 + add_probability > 0.97 or critx2 < specify_probability:
|
||||
logger.info(
|
||||
f"(USER {user.user_qq}, GROUP {user.group_id})"
|
||||
f" CHECKED IN successfully. score: {user.impression:.2f} "
|
||||
f"(+{impression_added * 2:.2f}).获取金币:{gold + gift if gift == 'gold' else gold}"
|
||||
)
|
||||
return await get_card(user, nickname, impression_added, gold, gift, True)
|
||||
else:
|
||||
logger.info(
|
||||
f"(USER {user.user_qq}, GROUP {user.group_id})"
|
||||
f" CHECKED IN successfully. score: {user.impression:.2f} "
|
||||
f"(+{impression_added:.2f}).获取金币:{gold + gift if gift == 'gold' else gold}"
|
||||
)
|
||||
return await get_card(user, nickname, impression_added, gold, gift)
|
||||
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import random
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
from nonebot.adapters.onebot.v11 import Message, MessageSegment
|
||||
@@ -187,20 +189,45 @@ async def _():
|
||||
if await WordBank.get_group_all_problem(0):
|
||||
return
|
||||
logger.info('开始迁移词条 纯文本 数据')
|
||||
word_list = await OldWordBank.get_all()
|
||||
for word in word_list:
|
||||
problem: str = word.problem
|
||||
user_id = word.user_qq
|
||||
group_id = word.group_id
|
||||
format_ = word.format
|
||||
answer = word.answer
|
||||
# 仅对纯文本做处理
|
||||
if '[CQ' not in problem and '[CQ' not in answer and '[_to_me' not in problem:
|
||||
if not format_:
|
||||
await WordBank.add_problem_answer(user_id, group_id, 1, 0, problem, answer)
|
||||
await WordBank.add_problem_answer(0, 0, 999, 0, '_[OK', '_[OK')
|
||||
logger.info('词条 纯文本 数据迁移完成')
|
||||
(Path() / 'plugins' / 'word_bank' / '_old_model.py').unlink()
|
||||
try:
|
||||
word_list = await OldWordBank.get_all()
|
||||
new_answer_path = Path() / 'data' / 'word_bank' / 'answer'
|
||||
new_problem_path = Path() / 'data' / 'word_bank' / 'problem'
|
||||
new_answer_path.mkdir(exist_ok=True, parents=True)
|
||||
for word in word_list:
|
||||
problem: str = word.problem
|
||||
user_id = word.user_qq
|
||||
group_id = word.group_id
|
||||
format_ = word.format
|
||||
answer = word.answer
|
||||
# 仅对纯文本做处理
|
||||
if '[CQ' not in problem and '[CQ' not in answer and '[_to_me' not in problem:
|
||||
if not format_:
|
||||
await WordBank.add_problem_answer(user_id, group_id, 1, 0, problem, answer)
|
||||
else:
|
||||
placeholder = []
|
||||
for m in format_.split('<format>'):
|
||||
x = m.split('<_s>')
|
||||
if x[0]:
|
||||
idx, file_name = x[0], x[1]
|
||||
if 'jpg' in file_name:
|
||||
answer = answer.replace(f'[__placeholder_{idx}]', f'[image:placeholder_{idx}]')
|
||||
file = Path() / 'data' / 'word_bank' / f'{group_id}' / file_name
|
||||
rand = int(time.time()) + random.randint(1, 100000)
|
||||
if file.exists():
|
||||
new_file = new_answer_path / f'{group_id}' / f'{user_id}_{rand}.jpg'
|
||||
new_file.parent.mkdir(exist_ok=True, parents=True)
|
||||
with open(file, 'rb') as rb:
|
||||
with open(new_file, 'wb') as wb:
|
||||
wb.write(rb.read())
|
||||
# file.rename(new_file)
|
||||
placeholder.append(f'answer/{group_id}/{user_id}_{rand}.jpg')
|
||||
await WordBank._move(user_id, group_id, problem, answer, ",".join(placeholder))
|
||||
await WordBank.add_problem_answer(0, 0, 999, 0, '_[OK', '_[OK')
|
||||
logger.info('词条 纯文本 数据迁移完成')
|
||||
(Path() / 'plugins' / 'word_bank' / '_old_model.py').unlink()
|
||||
except Exception as e:
|
||||
logger.warning(f'迁移词条发生错误,如果为首次安装请无视 {type(e)}:{e}')
|
||||
|
||||
|
||||
|
||||
|
||||
+84
-22
@@ -48,6 +48,7 @@ class WordBank(db.Model):
|
||||
user_id: Optional[int],
|
||||
group_id: Optional[int],
|
||||
problem: str,
|
||||
answer: Optional[str],
|
||||
word_scope: Optional[int] = None,
|
||||
word_type: Optional[int] = None,
|
||||
) -> bool:
|
||||
@@ -58,6 +59,7 @@ class WordBank(db.Model):
|
||||
:param user_id: 用户id
|
||||
:param group_id: 群号
|
||||
:param problem: 问题
|
||||
:param answer: 回答
|
||||
:param word_scope: 词条范围
|
||||
:param word_type: 词条类型
|
||||
"""
|
||||
@@ -66,6 +68,8 @@ class WordBank(db.Model):
|
||||
query = query.where(cls.user_qq == user_id)
|
||||
if group_id:
|
||||
query = query.where(cls.group_id == group_id)
|
||||
if answer:
|
||||
query = query.where(cls.answer == answer)
|
||||
if word_type:
|
||||
query = query.where(cls.word_type == word_type)
|
||||
if word_scope:
|
||||
@@ -105,19 +109,20 @@ class WordBank(db.Model):
|
||||
problem = str(get_img_hash(_file))
|
||||
image_path = f"problem/{group_id}/{user_id}_{int(time.time())}.jpg"
|
||||
answer, _list = await cls._answer2format(answer, user_id, group_id)
|
||||
await cls.create(
|
||||
user_qq=user_id,
|
||||
group_id=group_id,
|
||||
word_scope=word_scope,
|
||||
word_type=word_type,
|
||||
status=True,
|
||||
problem=problem,
|
||||
answer=answer,
|
||||
image_path=image_path,
|
||||
placeholder=",".join(_list),
|
||||
create_time=datetime.now().replace(microsecond=0),
|
||||
update_time=datetime.now().replace(microsecond=0),
|
||||
)
|
||||
if not await cls.exists(user_id, group_id, problem, answer, word_scope, word_type):
|
||||
await cls.create(
|
||||
user_qq=user_id,
|
||||
group_id=group_id,
|
||||
word_scope=word_scope,
|
||||
word_type=word_type,
|
||||
status=True,
|
||||
problem=problem,
|
||||
answer=answer,
|
||||
image_path=image_path,
|
||||
placeholder=",".join(_list),
|
||||
create_time=datetime.now().replace(microsecond=0),
|
||||
update_time=datetime.now().replace(microsecond=0),
|
||||
)
|
||||
|
||||
@classmethod
|
||||
async def _answer2format(
|
||||
@@ -143,7 +148,7 @@ class WordBank(db.Model):
|
||||
text += seg.data["text"]
|
||||
elif seg.type == "face":
|
||||
text += f"[face:placeholder_{index}]"
|
||||
_list.append(seg.data.id)
|
||||
_list.append(seg.data['id'])
|
||||
elif seg.type == "at":
|
||||
text += f"[at:placeholder_{index}]"
|
||||
_list.append(seg.data["qq"])
|
||||
@@ -234,16 +239,36 @@ class WordBank(db.Model):
|
||||
query = query.where(cls.word_scope == word_type)
|
||||
sql_text += f" and word_scope = {word_scope}"
|
||||
# 完全匹配
|
||||
if await query.where(cls.problem == problem).gino.first():
|
||||
return query.where(cls.problem == problem)
|
||||
if await query.where(
|
||||
((cls.word_type == 0) | (cls.word_type == 3)) & (cls.problem == problem)
|
||||
).gino.first():
|
||||
return query.where(
|
||||
((cls.word_type == 0) | (cls.word_type == 3)) & (cls.problem == problem)
|
||||
)
|
||||
# 模糊匹配
|
||||
if await query.where(cls.problem.contains(problem)).gino.first():
|
||||
return query.where(cls.problem.contains(problem))
|
||||
if await db.first(
|
||||
db.text(
|
||||
sql_text
|
||||
+ f" and word_type = 1 and :problem like '%' || problem || '%';"
|
||||
),
|
||||
problem=problem,
|
||||
):
|
||||
return (
|
||||
sql_text
|
||||
+ f" and word_type = 1 and :problem like '%' || problem || '%';"
|
||||
)
|
||||
# 正则匹配
|
||||
if await db.first(
|
||||
db.text(sql_text + f" and word_type = 2 and word_scope != 999 and '{problem}' ~ problem;")
|
||||
db.text(
|
||||
sql_text
|
||||
+ f" and word_type = 2 and word_scope != 999 and :problem ~ problem;"
|
||||
),
|
||||
problem=problem,
|
||||
):
|
||||
return sql_text + f" and word_type = 2 and word_scope != 999 and '{problem}' ~ problem;"
|
||||
return (
|
||||
sql_text
|
||||
+ f" and word_type = 2 and word_scope != 999 and :problem ~ problem;"
|
||||
)
|
||||
# if await db.first(
|
||||
# db.text(sql_text + f" and word_type = 1 and word_scope != 999 and '{problem}' ~ problem;")
|
||||
# ):
|
||||
@@ -270,10 +295,10 @@ class WordBank(db.Model):
|
||||
query = await cls.check(event, problem, word_scope, word_type)
|
||||
if query is not None:
|
||||
if isinstance(query, str):
|
||||
answer_list = await db.all(db.text(query))
|
||||
answer_list = await db.all(db.text(query), problem=problem)
|
||||
answer = random.choice(answer_list)
|
||||
return (
|
||||
await cls._format2answer(problem, answer[7], answer[1], answer[2])
|
||||
await cls._format2answer(answer[6], answer[7], answer[1], answer[2])
|
||||
if answer.placeholder
|
||||
else answer.answer
|
||||
)
|
||||
@@ -459,3 +484,40 @@ class WordBank(db.Model):
|
||||
problem_list.append(problem)
|
||||
_tmp.append(q.problem)
|
||||
return problem_list
|
||||
|
||||
@classmethod
|
||||
async def _move(
|
||||
cls,
|
||||
user_id: int,
|
||||
group_id: Optional[int],
|
||||
problem: Union[str, Message],
|
||||
answer: Union[str, Message],
|
||||
placeholder: str,
|
||||
):
|
||||
"""
|
||||
说明:
|
||||
旧词条图片移动方法
|
||||
参数:
|
||||
:param user_id: 用户id
|
||||
:param group_id: 群号
|
||||
:param problem: 问题
|
||||
:param answer: 回答
|
||||
:param placeholder: 占位符
|
||||
"""
|
||||
word_scope = 0
|
||||
word_type = 0
|
||||
# 对图片做额外处理
|
||||
if not await cls.exists(user_id, group_id, problem, answer, word_scope, word_type):
|
||||
await cls.create(
|
||||
user_qq=user_id,
|
||||
group_id=group_id,
|
||||
word_scope=word_scope,
|
||||
word_type=word_type,
|
||||
status=True,
|
||||
problem=problem,
|
||||
answer=answer,
|
||||
image_path=None,
|
||||
placeholder=placeholder,
|
||||
create_time=datetime.now().replace(microsecond=0),
|
||||
update_time=datetime.now().replace(microsecond=0),
|
||||
)
|
||||
|
||||
+16
-11
@@ -1,31 +1,36 @@
|
||||
import random
|
||||
import imagehash
|
||||
from PIL import Image
|
||||
from io import BytesIO
|
||||
from httpx import TimeoutException
|
||||
|
||||
from nonebot.typing import T_State
|
||||
from nonebot.adapters.onebot.v11 import MessageEvent
|
||||
|
||||
from configs.path_config import TEMP_PATH
|
||||
from utils.image_utils import get_img_hash
|
||||
from utils.utils import get_message_text, get_message_img, get_message_at
|
||||
from ._model import WordBank
|
||||
from utils.http_utils import AsyncHttpx
|
||||
|
||||
|
||||
async def check(event: MessageEvent) -> bool:
|
||||
async def check(event: MessageEvent, state: T_State) -> bool:
|
||||
text = get_message_text(event.message)
|
||||
img = get_message_img(event.message)
|
||||
at = get_message_at(event.message)
|
||||
rand = random.randint(1, 100)
|
||||
problem = text
|
||||
if not text and len(img) == 1:
|
||||
if await AsyncHttpx.download_file(img[0], TEMP_PATH / f"{event.user_id}_{rand}_word_bank_check.jpg"):
|
||||
problem = str(get_img_hash(TEMP_PATH / f"{event.user_id}_{rand}_word_bank_check.jpg"))
|
||||
try:
|
||||
r = await AsyncHttpx.get(img[0])
|
||||
problem = str(imagehash.average_hash(Image.open(BytesIO(r.content))))
|
||||
except TimeoutException:
|
||||
pass
|
||||
if at:
|
||||
temp = ''
|
||||
for seg in event.message:
|
||||
if seg.type == 'at':
|
||||
temp += f"[at:{seg.data['qq']}]"
|
||||
else:
|
||||
temp += seg
|
||||
elif seg.type == 'text':
|
||||
temp += seg.data["text"]
|
||||
problem = temp
|
||||
if problem:
|
||||
return await WordBank.check(event, problem) is not None
|
||||
if problem and (await WordBank.check(event, problem) is not None):
|
||||
state["problem"] = problem
|
||||
return True
|
||||
return False
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
import random
|
||||
|
||||
from services import logger
|
||||
from utils.image_utils import get_img_hash
|
||||
from ._rule import check
|
||||
from ._model import WordBank
|
||||
from configs.path_config import DATA_PATH, TEMP_PATH
|
||||
from configs.path_config import DATA_PATH
|
||||
from nonebot.adapters.onebot.v11 import GroupMessageEvent, MessageEvent
|
||||
from utils.utils import get_message_img, get_message_text, get_message_at
|
||||
from nonebot import on_message
|
||||
from utils.http_utils import AsyncHttpx
|
||||
from nonebot.typing import T_State
|
||||
|
||||
__zx_plugin_name__ = "词库问答回复操作 [Hidden]"
|
||||
|
||||
@@ -19,26 +15,8 @@ message_handle = on_message(priority=6, block=True, rule=check)
|
||||
|
||||
|
||||
@message_handle.handle()
|
||||
async def _(event: MessageEvent):
|
||||
text = get_message_text(event.message)
|
||||
img = get_message_img(event.message)
|
||||
at = get_message_at(event.message)
|
||||
problem = None
|
||||
if not text and img and len(img) == 1:
|
||||
rand = random.randint(1, 10000)
|
||||
if await AsyncHttpx.download_file(img[0], TEMP_PATH / f"{event.user_id}_{rand}_word_bank.jpg"):
|
||||
problem = str(get_img_hash(TEMP_PATH / f"{event.user_id}_{rand}_word_bank.jpg"))
|
||||
elif at:
|
||||
temp = ''
|
||||
for seg in event.message:
|
||||
if seg.type == 'at':
|
||||
temp += f"[at:{seg.data['qq']}]"
|
||||
else:
|
||||
temp += seg
|
||||
problem = temp
|
||||
elif text:
|
||||
problem = text
|
||||
if problem:
|
||||
async def _(event: MessageEvent, state: T_State):
|
||||
if problem := state.get("problem"):
|
||||
if msg := await WordBank.get_answer(event, problem):
|
||||
await message_handle.send(msg)
|
||||
logger.info(
|
||||
@@ -46,6 +24,3 @@ async def _(event: MessageEvent):
|
||||
f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
|
||||
f" 触发词条 {problem}"
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import re
|
||||
from typing import Tuple, Any, Optional
|
||||
|
||||
from nonebot.internal.params import Arg, ArgStr
|
||||
@@ -5,11 +6,12 @@ from nonebot.typing import T_State
|
||||
|
||||
from utils.utils import get_message_at, is_number, get_message_img
|
||||
from nonebot.params import CommandArg, RegexGroup, Command
|
||||
from nonebot.exception import FinishedException
|
||||
from services.log import logger
|
||||
from configs.path_config import DATA_PATH
|
||||
from utils.message_builder import custom_forward_msg
|
||||
from ._model import WordBank
|
||||
from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, Message, MessageEvent, PrivateMessageEvent
|
||||
from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, Message, MessageEvent, PrivateMessageEvent, unescape
|
||||
from nonebot import on_command, on_regex
|
||||
from configs.config import Config
|
||||
from ._data_source import delete_word, update_word, show_word
|
||||
@@ -23,7 +25,7 @@ usage:
|
||||
更推荐使用id方式删除
|
||||
问题回答支持的CQ:at, face, image
|
||||
查看词条命令:群聊时为 群词条+全局词条,私聊时为 私聊词条+全局词条
|
||||
添加词条正则:添加词条(模糊|正则|图片)?问\s*?(\S*)\s*?答\s?(\S*)
|
||||
添加词条正则:添加词条(模糊|正则|图片)?问\s*?(\S*\s?\S*)\s*?答\s?(\S*)
|
||||
指令:
|
||||
添加词条 ?[模糊|正则|图片]问...答...:添加问答词条,可重复添加相同问题的不同回答
|
||||
删除词条 [问题/下标] ?[下标]:删除指定词条指定或全部回答
|
||||
@@ -47,7 +49,7 @@ __plugin_superuser_usage__ = r"""
|
||||
usage:
|
||||
在私聊中超级用户额外设置
|
||||
指令:
|
||||
(全局|私聊)?添加词条\s*?(模糊|正则|图片)?问\s*?(\S*)\s*?答\s?(\S*):添加问答词条,可重复添加相同问题的不同回答
|
||||
(全局|私聊)?添加词条\s*?(模糊|正则|图片)?问\s*?(\S*\s?\S*)\s*?答\s?(\S*):添加问答词条,可重复添加相同问题的不同回答
|
||||
全局添加词条
|
||||
私聊添加词条
|
||||
(私聊情况下)删除词条: 删除私聊词条
|
||||
@@ -74,7 +76,7 @@ data_dir = DATA_PATH / "word_bank"
|
||||
data_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
add_word = on_regex(
|
||||
r"^(全局|私聊)?添加词条\s*?(模糊|正则|图片)?问\s*?(\S*)\s*?答\s?(\S*)", priority=5, block=True
|
||||
r"^(全局|私聊)?添加词条\s*?(模糊|正则|图片)?问\s*?(\S*\s?\S*)\s*?答\s?(\S*)", priority=5, block=True
|
||||
)
|
||||
|
||||
delete_word_matcher = on_command("删除词条", aliases={'删除全局词条'}, priority=5, block=True)
|
||||
@@ -91,7 +93,7 @@ async def _(
|
||||
state: T_State,
|
||||
reg_group: Tuple[Any, ...] = RegexGroup(),
|
||||
):
|
||||
if str(event.user_id) not in bot.config.superusers:
|
||||
if isinstance(event, PrivateMessageEvent) and str(event.user_id) not in bot.config.superusers:
|
||||
await add_word.finish('权限不足捏')
|
||||
word_scope, word_type, problem, answer = reg_group
|
||||
if (
|
||||
@@ -113,18 +115,22 @@ async def _(
|
||||
if seg.type == 'text' and '答' in str(seg):
|
||||
_problem = event.message[:index]
|
||||
answer = event.message[index:]
|
||||
answer[0] = str(answer[0])[str(answer).index('答')+1:]
|
||||
_problem[0] = str(_problem[0])[str(_problem).index('问')+1:]
|
||||
answer[0] = str(answer[0])[str(answer[0]).index('答')+1:]
|
||||
_problem[0] = str(_problem[0])[str(_problem[0]).index('问')+1:]
|
||||
if _problem[-1].type != 'at' or seg.data['text'][:seg.data['text'].index('答')].lstrip():
|
||||
_problem.append(seg.data['text'][:seg.data['text'].index('答')])
|
||||
temp = ''
|
||||
for g in _problem:
|
||||
if isinstance(g, str) or g.type == 'text':
|
||||
if isinstance(g, str):
|
||||
temp += g
|
||||
elif g.type == 'text':
|
||||
temp += g.data['text']
|
||||
elif g.type == 'at':
|
||||
temp += f"[at:{g.data['qq']}]"
|
||||
problem = temp
|
||||
break
|
||||
index = len((word_scope or "") + "添加词条" + (word_type or "") + problem) + 1
|
||||
event.message[0] = event.message[0].data["text"][index + 1 :].strip()
|
||||
problem = unescape(problem)
|
||||
event.message[0] = event.message[0].data["text"].split('答', maxsplit=1)[-1].strip()
|
||||
state["word_scope"] = word_scope
|
||||
state["word_type"] = word_type
|
||||
state["problem"] = problem
|
||||
@@ -141,6 +147,11 @@ async def _(
|
||||
problem_image: Message = Arg("problem_image"),
|
||||
):
|
||||
try:
|
||||
if word_type == "正则":
|
||||
try:
|
||||
re.compile(problem)
|
||||
except re.error:
|
||||
await add_word.finish(f"添加词条失败,正则表达式 {problem} 非法!")
|
||||
await WordBank.add_problem_answer(
|
||||
event.user_id,
|
||||
event.group_id if isinstance(event, GroupMessageEvent) and (not word_scope or word_scope == '1') else 0,
|
||||
@@ -150,6 +161,8 @@ async def _(
|
||||
answer,
|
||||
)
|
||||
except Exception as e:
|
||||
if isinstance(e, FinishedException):
|
||||
await add_word.finish()
|
||||
logger.error(
|
||||
f"(USER {event.user_id}, GROUP "
|
||||
f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
|
||||
@@ -233,7 +246,7 @@ async def _(bot: Bot, event: GroupMessageEvent, arg: Message = CommandArg()):
|
||||
not is_number(id_)
|
||||
or int(id_) < 0
|
||||
or int(id_)
|
||||
> len(await WordBank.get_group_all_problem(event.group_id))
|
||||
>= len(await WordBank.get_group_all_problem(event.group_id))
|
||||
):
|
||||
await show_word_matcher.finish("id必须为数字且在范围内")
|
||||
id_ = int(id_)
|
||||
@@ -243,7 +256,7 @@ async def _(bot: Bot, event: GroupMessageEvent, arg: Message = CommandArg()):
|
||||
not is_number(gid)
|
||||
or int(gid) < 0
|
||||
or int(gid)
|
||||
> len(await WordBank.get_problem_by_scope(0))
|
||||
>= len(await WordBank.get_problem_by_scope(0))
|
||||
):
|
||||
await show_word_matcher.finish("gid必须为数字且在范围内")
|
||||
gid = int(gid)
|
||||
|
||||
@@ -178,8 +178,8 @@ class BuildImage:
|
||||
self.h = int(h)
|
||||
self.paste_image_width = int(paste_image_width)
|
||||
self.paste_image_height = int(paste_image_height)
|
||||
self.current_w = 0
|
||||
self.current_h = 0
|
||||
self._current_w = 0
|
||||
self._current_h = 0
|
||||
self.font = ImageFont.truetype(str(FONT_PATH / font), int(font_size))
|
||||
if not plain_text and not color:
|
||||
color = (255, 255, 255)
|
||||
@@ -287,11 +287,11 @@ class BuildImage:
|
||||
pos = (width, height)
|
||||
if isinstance(img, BuildImage):
|
||||
img = img.markImg
|
||||
if self.current_w == self.w:
|
||||
self.current_w = 0
|
||||
self.current_h += self.paste_image_height
|
||||
if self._current_w >= self.w:
|
||||
self._current_w = 0
|
||||
self._current_h += self.paste_image_height
|
||||
if not pos:
|
||||
pos = (self.current_w, self.current_h)
|
||||
pos = (self._current_w, self._current_h)
|
||||
if alpha:
|
||||
try:
|
||||
self.markImg.paste(img, pos, img)
|
||||
@@ -300,7 +300,7 @@ class BuildImage:
|
||||
self.markImg.paste(img, pos, img)
|
||||
else:
|
||||
self.markImg.paste(img, pos)
|
||||
self.current_w += self.paste_image_width
|
||||
self._current_w += self.paste_image_width
|
||||
|
||||
def getsize(self, msg: str) -> Tuple[int, int]:
|
||||
"""
|
||||
|
||||
@@ -238,6 +238,25 @@ def get_message_img(data: Union[str, Message]) -> List[str]:
|
||||
return img_list
|
||||
|
||||
|
||||
def get_message_face(data: Union[str, Message]) -> List[str]:
|
||||
"""
|
||||
说明:
|
||||
获取消息中所有的 face Id
|
||||
参数:
|
||||
:param data: event.json()
|
||||
"""
|
||||
face_list = []
|
||||
if isinstance(data, str):
|
||||
data = json.loads(data)
|
||||
for msg in data["message"]:
|
||||
if msg["type"] == "face":
|
||||
face_list.append(msg["data"]["id"])
|
||||
else:
|
||||
for seg in data["face"]:
|
||||
face_list.append(seg.data["id"])
|
||||
return face_list
|
||||
|
||||
|
||||
def get_message_img_file(data: Union[str, Message]) -> List[str]:
|
||||
"""
|
||||
说明:
|
||||
|
||||
Reference in New Issue
Block a user