diff --git a/README.md b/README.md index 885c2638..4c58ded0 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,7 @@ - [x] p站排行榜 - [x] p站搜图 - [x] 翻译(日英韩) +- [x] pix图库(一个自己的图库,含有增删查改,黑名单等命令) - [x] 查看当前群欢迎消息 - [x] 查看该群自己的权限 @@ -100,7 +101,7 @@ - [x] 上传/删除/修改商品(需要编写对应的商品功能) - [x] 节日红包发送 - [x] 修改群权限 - + #### 超级用户的被动技能 - [x] 邀请入群提醒(别人邀请真寻入群) - [x] 添加好友提醒(别人添加真寻好友) @@ -123,6 +124,7 @@ - [x] 群聊时间检测(当群聊最后一人发言时间大于当前36小时后将关闭该群所有通知(即被动技能)) - [x] 群管理员监控,自动为新晋管理员增加权限,为失去群管理员的用户删除权限 - [x] 群权限系统 +- [x] 定时更新权限 ## 功能具体指令 @@ -150,7 +152,7 @@ | 原神黄历 | 原神黄历 | 查看今日原神黄历,含有每日10:25的定时任务 | 原神材料 | 今日素材 | 发送可莉特调的截图 | 丘丘语翻译 | 丘丘翻译/丘丘一下/丘丘语翻译 | 示例:丘丘一下 mimi -| 原神资源查询 | 原神资源查询 \[资源名称]/\[资源名称]在哪/哪里有\[资源名称]/原神资源列表 | 示例:嘟嘟莲在哪 +| 原神资源查询 | 原神资源查询 \[资源名称] \[路线]?/\[资源名称]在哪/哪里有\[资源名称]/原神资源列表 | 如果资源名称末尾添加‘路线’的话将生成残缺缺缺版的优先路径
示例:嘟嘟莲在哪
原神资源查询嘟嘟莲路线 | 俄罗斯轮盘 | 装弹\[子弹数] \[金额](默认200)/开枪/结算/我的战绩/胜场排行/败场排行/欧洲人排行/慈善家排行 | 紧张刺激的群内小游戏,使用每日签到的金币作为赌注,具体玩法请发送 真寻帮助 俄罗斯轮盘 | 红包系统 | 塞红包\[金额] \[数量](默认5)/抢/开/戳一戳/退回 | 仿微信明日方舟红包的样式(pil拼图大师!),每个红包金额随机生成,最多会是红包总金额的1/3,退回用于退回一分钟后还未开完的红包 | 金币排行 | 金币排行 | 字面意思 @@ -179,6 +181,10 @@ | 撤回 | 撤回 \[消息位置](默认为最新一条消息) | 按顺序撤回发送的消息,示例:撤回 1 | 滴滴滴- | 滴滴滴- \[文本] | 用于用户联系真寻的超级用户 |功能调用统计可视化 | 功能调用统计(自记录以来的功能调用统计)
周功能调用统计 [plugin_name]
月功能调用统计 [plugin_name]| 当plugin_name为空时为7天或30内的所有功能统计 +| pix | pix/PIX [tags/uid/pid:pid] [num] | 无参数时随机查看pix图库的图片(无r18),num数量默认=1,tags:查看相关tags图片,uid:查找相关画师图片,pid:pid:指定查看pid图片
示例:pix原神 3
pix23493844
pixpid:29429933 +| 添加pix关键词/uid/pid | 添加pix关键词/uid/pid *[关键词/uid/pid]| 添加关键词或uid或pid用于下次搜索,关键词搜索相关tag,uid会收录作者下收藏符合标准的作品,pid收录单张作品
示例:添加pix关键词 原神
添加pixuid 123441
添加pixpid 2748937| +| 查看pix图库 | 查看pix图库 [tags] | 查看已收录的tag相关图片数量
示例:查看pix图库 原神 莫娜 +|显示pix关键词 | 显示pix关键词 | 查看已收录的所有关键词/UID/PID ### 管理员功能 @@ -186,7 +192,7 @@ | 功能 | 权限等级 | 指令 | 说明 | -------------| --------------| :--------------------------------------:| :------------------------: -| 更新群组成员信息 | 1 | 更新群组成员信息/更新群组成员列表 | 存储群员的基本信息,虽然有自动更新,但备个命令以防万一 +| 更新群组成员信息 | 1 | 更新群组成员信息/更新群组成员列表 | 存储群员的基本信息,虽然有自动更新,但备个命令以防万一 | 群功能开关 | 2 | 开启/关闭\[指令名]功能 | 群帮助中左边带有√的功能都可以通过此命令开启或关闭,示例:开启色图 | 查看群被动技能 | 2 | 群通知状态 | 详细请查看被动技能列表 | 被动技能开关 | 2 | 开启/关闭被动技能 | 有时候花里胡哨通知也会很烦人 @@ -211,14 +217,21 @@ | 退群 | 退群 \[群号] | 用于退出某一些群 | 检查系统状态 | 自检 | 略 | 更新好友/群组信息| 更新好友/群组信息 | 包含自动更新,被t出群等等有更好的可视信息 -| 重载卡池 | 略 | 重载抽卡的游戏卡池,请查看 [nonebot_plugin_gamedraw](https://github.com/HibiKier/nonebot_plugin_gamedraw) -| 添加商品 | 添加商品 \[名称] \[价格] \[描述] \[折扣](小数)(可选) \[限时时间](分钟)(可选) | 为真寻的商店添加一点点道具
示例:添加商品 昏睡红茶 300 一杯上好的奇怪红茶 0.9 60 +| 重载卡池 | 略 | 重载抽卡的游戏卡池,请查看 \[nonebot_plugin_gamedraw](https://github.com/HibiKier/nonebot_plugin_gamedraw) +| 添加商品 | 添加商品 \[名称]-\[价格]-\[描述]-\[折扣](小数)(可选)-\[限时时间](分钟)(可选) | 为真寻的商店添加一点点道具
示例:添加商品-昏睡红茶-300-一杯上好的奇怪红茶-0.9-60 | 删除商品 | 删除商品 \[名称或序号] | 在真寻的商店中删除一点东西 -| 修改商品 | 修改商品 -name \[名称或序号] -price \[价格] -des \[描述] -discount \[折扣] -time \[限时]| 注意空格,不需要的参数可以不加
示例:修改商品 -name 1 -price 900 【修改序号为1的商品的价格为900】 +| 修改商品 | 修改商品 -name \[名称或序号] -price \[价格] -des \[描述] -discount \[折扣] -time \[限时]| 注意空格,不需要的参数可以不加
示例:修改商品 -name 1 -price 900 【修改序号为1的商品的价格为900】 | 节日红包 | 节日红包 \[金额] \[数量] \[祝福语](可选) \[群号](可选)... | 群号支持批量,使用空格隔开,不使用群号则对所有群发送节日红包,节日红包有效时间为24小时,祝福语默认为“恭喜发财 大吉大利”
示例:节日红包 10000 15 真寻真可爱 123324423 23423423 | 修改群权限 | 修改群权限 \[group] \[level] | 所以说这功能是对内鬼的无奈,默认群权限为5,默认无法使用 色图/coser/p站排行/搜图(这些功能都要9级权限) |更新原神今日素材| 更新原神今日素材 | 自动更新原神每日素材失败时可以手动触发 - +|更新原神资源信息| 更新原神资源信息 | 除了每日自动更新的资源外,额外更新大地图 +| 清理数据 | 清理数据 | 清理 temp,rar,r18_rar 文件夹的文件数据 +|添加pix关键词/uid/pid | 添加pix关键词/uid/pid [keyword/pid:pid/uid:uid] [-f]? | 与普通功能相同,额外可以通过参数 -f 强制通过检测 +|删除pix关键词 | 删除pix关键词 *[keyword/uid/pid:pid] | 删除已收录的keyword/uid/pid +|更新pix关键词 | 更新pix关键词 [keyword/pid:pid/uid:uid] [num]| 更新keyword,uid,pid或指定uid,pid,未指定时,则更新全部,当num未指定时为keyword/uid/pid更新全部
示例:更新pix关键词
更新pix关键词uid 8
更新pix关键词pid:83457477 +|删除pix图片 | 删除pix图片 [*pid] [-b]? | [-b]参数为删除的同时加入黑名单(不再更新),虽然是pid,但是_p也可以
示例:删除pix图片3458344 8235234_p1 -b +|显示pix关键词 | 显示pix关键词 | 与普通功能相同,额外显示待收录和黑名单 +|pix检测更新 | pix检测更新 [update]? | 检测从未更新过的pid或uid,-update参数将在检测后直接更新未更新过的pid或uid
示例:pix检测更新 update @@ -639,6 +652,35 @@ python bot.py ## 更新 +### 2021/7/30 + +* 重构代码,进行优化,添加注释,删除冗余代码,降低代码耦合 +* 添加功能:PIX(一套快捷的pixiv存图命令,自建图库存储url等信息?意在获取不色的图?) +* 添加功能:清理临时图片文件(temp/rar/r18_rar文件夹) +* 添加额外定时任务(5分钟检测一次),解决加入新群时无法及时为管理员提供权限 +* 添加配置ALAPI_AI_CHECK,开关AI回复文本检测 +* 添加配置IMPORT_DEFAULT_SHOP_GOODS,控制是否导入内置的三个商品(好感度加持卡ⅠⅡⅢ) +* 添加配置ONLY_USE_LOCAL_SETU,仅仅使用本地色图(有的话),提升速度,但无法在线搜索色图和保存链接 +* 添加配置WITHDRAW_SETU_TIME,是否需要延迟撤回色图,可配置仅群里,私聊或全部 +* 好友请求,入群请求,滴滴滴-,/t,被踢出群提醒,的提示消息更加丰富 +* 彻底重写原神资源查找,添加规划路线(路线残缺缺缺缺版,有空补)添加命令‘更新原神资源信息’,强制更新地图等资源 +* 优化色图和P站排行/搜图检测用户是否正在触发命令代码 +* 当群最后发言大于36小时,也会关闭广播通知 +* 功能维护时超级用户依然可以调用(苦了谁都不能苦了自己) +* 修复获取赛马娘UP公告 +* 重写 色图/更新色图 + * 色图数据存储改为数据库,启动时会更新之前的色图数据(有的话),更新完毕后会删除原数据文件,如果需要保留请提前备份, + * lolicon api改为v2 + * 取消r18次数限制 + * 单次搜索至多保存100条链接 + * 添加定时撤回 + * 暂时取消上传/删除色图 +* +* 更新建议(不要替换你的data和resources文件夹!) + * 删除configs,plugins,services,utils,models文件夹重新clone + * 删除多余文件夹,resources/img/genshin/seek_god_eye + * 清空resources/img/genshin/genshin_icon文件夹,仅保留box.png和box_alpha.png + ### 2021/7/27 * 原神今日素材改为单张截图+拼图,更新文件utils/img_utils.py及plugins/genshin/material_remind/__init__.py diff --git a/bot.py b/bot.py index 56a927be..4b2d35a1 100644 --- a/bot.py +++ b/bot.py @@ -1,7 +1,7 @@ - import nonebot from nonebot.adapters.cqhttp import Bot as CQHTTPBot from services.db_context import init, disconnect + nonebot.init() driver = nonebot.get_driver() driver.register_adapter("cqhttp", CQHTTPBot) @@ -13,8 +13,8 @@ nonebot.load_plugins("plugins") nonebot.load_plugins("plugins/shop") nonebot.load_plugins("plugins/genshin") nonebot.load_plugins("plugins/alapi") +nonebot.load_plugins("plugins/pix_gallery") if __name__ == "__main__": nonebot.run() - diff --git a/configs/config.py b/configs/config.py index 8a670f50..951168d2 100644 --- a/configs/config.py +++ b/configs/config.py @@ -1,6 +1,7 @@ from .utils.util import get_config_data -from typing import List, Union -from services.service_config import TL_M_KEY, SYSTEM_PROXY, ALAPI_M_TOKEN +from typing import List, Optional, Tuple +from services.service_config import TL_M_KEY, SYSTEM_M_PROXY, ALAPI_M_TOKEN + try: import ujson as json except ModuleNotFoundError: @@ -8,27 +9,30 @@ except ModuleNotFoundError: # 是否使用配置文件 -USE_CONFIG_FILE = False +USE_CONFIG_FILE: bool = False # API KEY(必要) -LOLICON_KEY: str = "" # lolicon(不需要了,可不填) -RSSHUBAPP: str = "https://rsshub.app/" # rsshub -ALAPI_TOKEN: str = "" # ALAPI +RSSHUBAPP: str = "https://rsshub.app" # rsshub +ALAPI_TOKEN: str = "" # ALAPI https://admin.alapi.cn/user/login +HIBIAPI: str = "https://api.obfs.dev" # 图灵 TL_KEY: List[str] = [] # 数据库(必要) # 如果填写了bind就不需要再填写后面的字段了#) # 示例:"bind": "postgresql://user:password@127.0.0.1:5432/database" -bind: str = '' # 数据库url -sql_name: str = 'postgresql' -user: str = '' # 数据用户名 -password: str = '' # 数据库密码 -address: str = '' # 数据库地址 -port: str = '' # 数据库端口 -database: str = '' # 数据库名称 +bind: str = "" # 数据库连接链接 +sql_name: str = "postgresql" +user: str = "" # 数据用户名 +password: str = "" # 数据库密码 +address: str = "" # 数据库地址 +port: str = "" # 数据库端口 +database: str = "" # 数据库名称 +# 代理 +SYSTEM_PROXY: Optional[str] = None # 全局代理 +BUFF_PROXY: Optional[str] = None # Buff代理 # 公开图库列表 IMAGE_DIR_LIST: List[str] = ["色图", "美图", "萝莉", "壁纸"] @@ -38,252 +42,161 @@ BAN_RESULT: str = "才不会给你发消息." # 插件配置 -MAXINFO_REIMU: int = 7 # 上车(reimu)功能查找目的地的最大数 -COUNT_PER_DAY_REIMU: int = 5 # 每日上车(reimu)次数限制 -MAXINFO_BT: int = 10 # bt功能单次查找最大数 -MAXINFO_PRIVATE_ANIME: int = 20 # 私聊搜索动漫返回的最大数量 -MAXINFO_GROUP_ANIME: int = 5 # 群搜索动漫返回的最大数量 -MAX_FIND_IMG_COUNT: int = 3 # 识图最大返回数 -MAX_SETU_R_COUNT: int = 5 # 每日色图r次数限制 +MAXINFO_REIMU: int = 7 # 上车(reimu)功能查找目的地的最大数 +COUNT_PER_DAY_REIMU: int = 5 # 每日上车(reimu)次数限制 +MAXINFO_BT: int = 10 # bt功能单次查找最大数 +MAXINFO_PRIVATE_ANIME: int = 20 # 私聊搜索动漫返回的最大数量 +MAXINFO_GROUP_ANIME: int = 5 # 群搜索动漫返回的最大数量 +MAX_FIND_IMG_COUNT: int = 3 # 识图最大返回数 +# 参1:延迟撤回色图时间(秒),0 为关闭 | 参2:监控聊天类型,0(私聊) 1(群聊) 2(群聊+私聊) +WITHDRAW_SETU_TIME: Tuple[int, int] = (0, 1) # 各种卡池的开关 -PRTS_FLAG = True # 明日方舟 -GENSHIN_FLAG = True # 原神 -PRETTY_FLAG = True # 赛马娘 -GUARDIAN_FLAG = True # 坎公骑冠剑 -PCR_FLAG = True # 公主连结 -AZUR_FLAG = True # 碧蓝航线 -FGO_FLAG = True # 命运-冠位指定(FGO) -ONMYOJI_FLAG = True # 阴阳师 +PRTS_FLAG = True # 明日方舟 +GENSHIN_FLAG = True # 原神 +PRETTY_FLAG = True # 赛马娘 +GUARDIAN_FLAG = True # 坎公骑冠剑 +PCR_FLAG = True # 公主连结 +AZUR_FLAG = True # 碧蓝航线 +FGO_FLAG = True # 命运-冠位指定(FGO) +ONMYOJI_FLAG = True # 阴阳师 -PCR_TAI = True # pcr是否开启台服卡池 -SEMAPHORE = 5 # 限制碧蓝航线和FGO并发数 +PCR_TAI = True # pcr是否开启台服卡池 +SEMAPHORE = 5 # 限制碧蓝航线和FGO并发数 ADMIN_DEFAULT_AUTH: int = 5 # 默认群管理员权限 -MAX_SIGN_GOLD: int = 200 # 签到好感度加成额外获得的最大金币数 +MAX_SIGN_GOLD: int = 200 # 签到好感度加成额外获得的最大金币数 -INITIAL_SETU_PROBABILITY: float = 0.7 # 色图概率 -FUDU_PROBABILITY: float = 0.7 # 复读概率 - -INITIAL_OPEN_CASE_COUNT: int = 20 # 初始开箱次数 -MUTE_DEFAULT_COUNT: int = 10 # 刷屏禁言默认检测次数 -MUTE_DEFAULT_TIME: int = 7 # 刷屏检测默认规定时间 +INITIAL_SETU_PROBABILITY: float = 0.7 # 色图概率 +FUDU_PROBABILITY: float = 0.7 # 复读概率 +INITIAL_OPEN_CASE_COUNT: int = 20 # 初始开箱次数 +MUTE_DEFAULT_COUNT: int = 10 # 刷屏禁言默认检测次数 +MUTE_DEFAULT_TIME: int = 7 # 刷屏检测默认规定时间 MUTE_DEFAULT_DURATION: int = 10 # 刷屏检测默禁言时长(分钟) -# 注:即在 MALICIOUS_CHECK_TIME 时间内触发相同命令 MALICIOUS_BAN_COUNT 将被 ban MALICIOUS_BAN_TIME 分钟 -MALICIOUS_BAN_TIME: int = 30 # 恶意命令触发检测触发后ban的时长(分钟) -MALICIOUS_BAN_COUNT: int = 8 # 恶意命令触发检测最大触发次数 -MALICIOUS_CHECK_TIME: int = 5 # 恶意命令触发检测规定时间内(秒) +# 注:即在 MALICIOUS_CHECK_TIME 时间内触发相同命令 MALICIOUS_BAN_COUNT 将被ban MALICIOUS_BAN_TIME 分钟 +MALICIOUS_BAN_TIME: int = 30 # 恶意命令触发检测触发后ban的时长(分钟) +MALICIOUS_BAN_COUNT: int = 8 # 恶意命令触发检测最大触发次数 +MALICIOUS_CHECK_TIME: int = 5 # 恶意命令触发检测规定时间内(秒) # LEVEL -DELETE_IMG_LEVEL: int = 7 # 删除图片权限 -MOVE_IMG_LEVEL: int = 7 # 移动图片权限 -UPLOAD_LEVEL: int = 6 # 上传图片权限 -BAN_LEVEL: int = 5 # BAN权限 -OC_LEVEL: int = 2 # 开关群功能权限 -MUTE_LEVEL: int = 5 # 更改禁言设置权限 -MEMBER_ACTIVITY_LEVEL = 5 # 群员活跃检测设置权限 +DELETE_IMG_LEVEL: int = 7 # 删除图片权限 +MOVE_IMG_LEVEL: int = 7 # 移动图片权限 +UPLOAD_LEVEL: int = 6 # 上传图片权限 +BAN_LEVEL: int = 5 # BAN权限 +OC_LEVEL: int = 2 # 开关群功能权限 +MUTE_LEVEL: int = 5 # 更改禁言设置权限 +MEMBER_ACTIVITY_LEVEL = 5 # 群员活跃检测设置权限 + +# 是否开启HIBIAPI搜图功能(该功能会搜索群友提交的xp) +HIBIAPI_FLAG: bool = True +# HIBIAPI搜图图片的最低收藏 +HIBIAPI_BOOKMARKS: int = 5000 # 需要为哪些群更新最新版gocq吗?(上传最新版gocq) # 示例:[434995955, 239483248] UPDATE_GOCQ_GROUP: List[int] = [] -# 代理 -system_proxy: Union[str, None] = None -buff_proxy: Union[str, None] = None - # 是否存储色图 DOWNLOAD_SETU: bool = True +# 仅仅使用本地色图 +ONLY_USE_LOCAL_SETU: bool = False # 是否自动同意好友添加 AUTO_ADD_FRIEND: bool = True +# 当含有ALAPI_TOKEN时是否检测文本合规,开启检测会减慢回复速度 +ALAPI_AI_CHECK: bool = True +# 导入商店自带的三个商品 +IMPORT_DEFAULT_SHOP_GOODS: bool = True # 群管理员功能 与 对应权限 admin_plugins_auth = { - 'admin_bot_manage': OC_LEVEL, - 'ban': BAN_LEVEL, - 'delete_img': DELETE_IMG_LEVEL, - 'move_img': MOVE_IMG_LEVEL, - 'upload_img': UPLOAD_LEVEL, - 'admin_help': 1, - 'mute': MUTE_LEVEL, - 'member_activity_handle': MEMBER_ACTIVITY_LEVEL + "admin_bot_manage": OC_LEVEL, + "ban": BAN_LEVEL, + "delete_img": DELETE_IMG_LEVEL, + "move_img": MOVE_IMG_LEVEL, + "upload_img": UPLOAD_LEVEL, + "admin_help": 1, + "mute": MUTE_LEVEL, + "member_activity_handle": MEMBER_ACTIVITY_LEVEL, } # 模块与对应命令和对应群权限 -# 用于生成帮助图片 和 开关功能 以及 群权限 +# 用于生成帮助图片 和 开关功能 plugins2info_dict = { - 'sign_in': { - 'level': 5, - 'cmd': ['签到'] + "sign_in": {"level": 5, "cmd": ["签到"]}, + "send_img": {"level": 5, "cmd": ["发送图片", "发图", "萝莉", "美图", "壁纸"]}, + "send_setu": {"level": 9, "cmd": ["色图", "涩图", "瑟图", "查色图"]}, + "white2black": {"level": 5, "cmd": ["黑白图", "黑白草图"]}, + "coser": {"level": 9, "cmd": ["coser", "cos"]}, + "quotations": {"level": 5, "cmd": ["语录"]}, + "jitang": {"level": 5, "cmd": ["鸡汤"]}, + "send_dinggong_voice": {"level": 5, "cmd": ["骂我", "骂老子", "骂劳资"]}, + "open_cases": {"level": 5, "cmd": ["开箱", "我的开箱", "群开箱统计", "我的金色"]}, + "luxun": {"level": 5, "cmd": ["鲁迅说", "鲁迅说过"]}, + "fake_msg": {"level": 5, "cmd": ["假消息"]}, + "buy": {"level": 5, "cmd": ["购买", "购买道具"]}, + "my_gold": {"level": 5, "cmd": ["我的金币"]}, + "my_props": {"level": 5, "cmd": ["我的道具"]}, + "shop_handle": {"level": 5, "cmd": ["商店"]}, + "update_pic": {"level": 5, "cmd": ["图片", "操作图片", "修改图片"]}, + "search_buff_skin_price": {"level": 5, "cmd": ["查询皮肤"]}, + "weather": {"level": 5, "cmd": ["天气", "查询天气", "天气查询"]}, + "yiqing": {"level": 5, "cmd": ["疫情", "疫情查询", "查询疫情"]}, + "what_anime": {"level": 5, "cmd": ["识番"]}, + "search_anime": {"level": 5, "cmd": ["搜番"]}, + "songpicker2": {"level": 5, "cmd": ["点歌"]}, + "epic": {"level": 5, "cmd": ["epic"]}, + "pixiv": {"level": 9, "cmd": ["pixiv", "p站排行", "搜图"]}, + "poke": {"level": 5, "cmd": ["戳一戳", "拍一拍"]}, + "draw_card": { + "level": 5, + "cmd": [ + "抽卡", + "游戏抽卡", + "原神抽卡", + "方舟抽卡", + "坎公骑冠剑抽卡", + "pcr抽卡", + "fgo抽卡", + "碧蓝抽卡", + "碧蓝航线抽卡", + "阴阳师抽卡", + ], }, - 'send_img': { - 'level': 5, - 'cmd': ['发送图片', '发图', '萝莉', '美图', '壁纸'] + "ai": {"level": 5, "cmd": ["ai", "Ai", "AI", "aI"]}, + "one_friend": {"level": 5, "cmd": ["我有一个朋友", "我有一个朋友想问问"]}, + "translate": {"level": 5, "cmd": ["翻译", "英翻", "翻英", "日翻", "翻日", "韩翻", "翻韩"]}, + "nonebot_plugin_picsearcher": {"level": 5, "cmd": ["识图"]}, + "almanac": {"level": 5, "cmd": ["原神黄历", "黄历"]}, + "material_remind": {"level": 5, "cmd": ["今日素材", "天赋材料"]}, + "qiu_qiu_translation": {"level": 5, "cmd": ["丘丘翻译", "丘丘一下", "丘丘语翻译"]}, + "query_resource_points": {"level": 5, "cmd": ["原神资源查询", "原神资源列表"]}, + "russian": {"level": 5, "cmd": ["俄罗斯轮盘", "俄罗斯转盘", "装弹"]}, + "gold_redbag": {"level": 5, "cmd": ["塞红包", "红包", "抢红包"]}, + "poetry": {"level": 5, "cmd": ["念诗", "来首诗", "念首诗"]}, + "comments_163": {"level": 5, "cmd": ["到点了", "12点了", "网易云热评", "网易云评论"]}, + "cover": {"level": 5, "cmd": ["b封面", "B封面"]}, + "pid_search": {"level": 9, "cmd": ["p搜", "P搜"]}, + "pix": { + "level": 5, + "cmd": ["pix", "PIX", "pIX", "Pix", "PIx"], }, - 'send_setu': { - 'level': 9, - 'cmd': ['色图', '涩图', '瑟图', '查色图'] - }, - 'white2black': { - 'level': 5, - 'cmd': ['黑白图', '黑白草图'] - }, - 'coser': { - 'level': 9, - 'cmd': ['coser', 'cos'] - }, - 'quotations': { - 'level': 5, - 'cmd': ['语录'] - }, - 'jitang': { - 'level': 5, - 'cmd': ['鸡汤'] - }, - 'send_dinggong_voice': { - 'level': 5, - 'cmd': ['骂我', '骂老子', '骂劳资'] - }, - 'open_cases': { - 'level': 5, - 'cmd': ['开箱', '我的开箱', '群开箱统计', '我的金色'] - }, - 'luxun': { - 'level': 5, - 'cmd': ['鲁迅说', '鲁迅说过'] - }, - 'fake_msg': { - 'level': 5, - 'cmd': ['假消息'] - }, - 'buy': { - 'level': 5, - 'cmd': ['购买', '购买道具'] - }, - 'my_gold': { - 'level': 5, - 'cmd': ['我的金币'] - }, - 'my_props': { - 'level': 5, - 'cmd': ['我的道具'] - }, - 'shop_handle': { - 'level': 5, - 'cmd': ['商店'] - }, - 'update_pic': { - 'level': 5, - 'cmd': ['图片', '操作图片', '修改图片'] - }, - 'search_buff_skin_price': { - 'level': 5, - 'cmd': ['查询皮肤'] - }, - 'weather': { - 'level': 5, - 'cmd': ['天气', '查询天气', '天气查询'] - }, - 'yiqing': { - 'level': 5, - 'cmd': ['疫情', '疫情查询', '查询疫情'] - }, - 'what_anime': { - 'level': 5, - 'cmd': ['识番'] - }, - 'search_anime': { - 'level': 5, - 'cmd': ['搜番'] - }, - 'songpicker2': { - 'level': 5, - 'cmd': ['点歌'] - }, - 'epic': { - 'level': 5, - 'cmd': ['epic'] - }, - 'pixiv': { - 'level': 9, - 'cmd': ['pixiv', 'p站排行', '搜图'] - }, - 'poke': { - 'level': 5, - 'cmd': ['戳一戳', '拍一拍'] - }, - 'draw_card': { - 'level': 5, - 'cmd': ['抽卡', '游戏抽卡', '原神抽卡', '方舟抽卡', '坎公骑冠剑抽卡', 'pcr抽卡', 'fgo抽卡', '碧蓝抽卡', '碧蓝航线抽卡', '阴阳师抽卡'] - }, - 'ai': { - 'level': 5, - 'cmd': ['ai', 'Ai', 'AI', 'aI'] - }, - 'one_friend': { - 'level': 5, - 'cmd': ['我有一个朋友', '我有一个朋友想问问'] - }, - 'translate': { - 'level': 5, - 'cmd': ['翻译', '英翻', '翻英', '日翻', '翻日', '韩翻', '翻韩'] - }, - 'nonebot_plugin_picsearcher': { - 'level': 5, - 'cmd': ['识图'] - }, - 'almanac': { - 'level': 5, - 'cmd': ['原神黄历', '黄历'] - }, - 'material_remind': { - 'level': 5, - 'cmd': ['今日素材', '天赋材料'] - }, - 'qiu_qiu_translation': { - 'level': 5, - 'cmd': ['丘丘翻译', '丘丘一下', '丘丘语翻译'] - }, - 'query_resource_points': { - 'level': 5, - 'cmd': ['原神资源查询', '原神资源列表'] - }, - 'russian': { - 'level': 5, - 'cmd': ['俄罗斯轮盘', '俄罗斯转盘', '装弹'] - }, - 'gold_redbag': { - 'level': 5, - 'cmd': ['塞红包', '红包', '抢红包'] - }, - 'poetry': { - 'level': 5, - 'cmd': ['念诗', '来首诗', '念首诗'] - }, - 'comments_163': { - 'level': 5, - 'cmd': ['到点了', '12点了', '网易云热评', '网易云评论'] - }, - 'cover': { - 'level': 5, - 'cmd': ['b封面', 'B封面'] - }, - 'pid_search': { - 'level': 9, - 'cmd': ['p搜', 'P搜'] - } } if TL_M_KEY: TL_KEY = TL_M_KEY -if SYSTEM_PROXY: - system_proxy = SYSTEM_PROXY +if SYSTEM_M_PROXY: + SYSTEM_PROXY = SYSTEM_M_PROXY if ALAPI_M_TOKEN: ALAPI_TOKEN = ALAPI_M_TOKEN + +HIBIAPI = HIBIAPI[:-1] if HIBIAPI[-1] == "/" else HIBIAPI +RSSHUBAPP = RSSHUBAPP[:-1] if RSSHUBAPP[-1] == "/" else RSSHUBAPP + # 配置文件应用 if USE_CONFIG_FILE: config = get_config_data() @@ -291,14 +204,9 @@ if USE_CONFIG_FILE: for key in config.keys(): if isinstance(config[key], str): config[key] = config[key].strip() - if key.find('proxy') != -1: + if key.find("proxy") != -1: if not config[key]: config[key] = None # if not configs[key] and key.find("PATH") == -1: # configs[key] = None globals().update(config) - - - - - diff --git a/models/bag_user.py b/models/bag_user.py index 40b2d888..9e87ee87 100644 --- a/models/bag_user.py +++ b/models/bag_user.py @@ -1,10 +1,9 @@ - from services.db_context import db +from typing import Optional, List class BagUser(db.Model): - __tablename__ = 'bag_users' - + __tablename__ = "bag_users" id = db.Column(db.Integer(), primary_key=True) user_qq = db.Column(db.BigInteger(), nullable=False) belonging_group = db.Column(db.BigInteger(), nullable=False) @@ -15,25 +14,41 @@ class BagUser(db.Model): get_today_gold = db.Column(db.Integer(), default=0) spend_today_gold = db.Column(db.Integer(), default=0) - _idx1 = db.Index('bag_group_users_idx1', 'user_qq', 'belonging_group', unique=True) + _idx1 = db.Index("bag_group_users_idx1", "user_qq", "belonging_group", unique=True) @classmethod async def get_my_total_gold(cls, user_qq: int, belonging_group: int) -> str: + """ + 说明: + 获取金币概况 + 参数: + :param user_qq: qq号 + :param belonging_group: 所在群号 + """ query = cls.query.where( (cls.user_qq == user_qq) & (cls.belonging_group == belonging_group) ) user = await query.gino.first() if not user: user = await cls.create( - user_qq=user_qq, - belonging_group=belonging_group, - ) - return f'当前金币:{user.gold}\n今日获取金币:{user.get_today_gold}\n今日花费金币:{user.spend_today_gold}' \ - f'\n今日收益:{user.get_today_gold - user.spend_today_gold}' \ - f'\n总赚取金币:{user.get_total_gold}\n总花费金币:{user.spend_total_gold}' + user_qq=user_qq, + belonging_group=belonging_group, + ) + return ( + f"当前金币:{user.gold}\n今日获取金币:{user.get_today_gold}\n今日花费金币:{user.spend_today_gold}" + f"\n今日收益:{user.get_today_gold - user.spend_today_gold}" + f"\n总赚取金币:{user.get_total_gold}\n总花费金币:{user.spend_total_gold}" + ) @classmethod async def get_gold(cls, user_qq: int, belonging_group: int) -> int: + """ + 说明: + 获取当前金币 + 参数: + :param user_qq: qq号 + :param belonging_group: 所在群号 + """ query = cls.query.where( (cls.user_qq == user_qq) & (cls.belonging_group == belonging_group) ) @@ -42,13 +57,20 @@ class BagUser(db.Model): return user.gold else: await cls.create( - user_qq=user_qq, - belonging_group=belonging_group, - ) + user_qq=user_qq, + belonging_group=belonging_group, + ) return 100 @classmethod async def get_props(cls, user_qq: int, belonging_group: int) -> str: + """ + 说明: + 获取当前道具 + 参数: + :param user_qq: qq号 + :param belonging_group: 所在群号 + """ query = cls.query.where( (cls.user_qq == user_qq) & (cls.belonging_group == belonging_group) ) @@ -57,13 +79,21 @@ class BagUser(db.Model): return user.props else: await cls.create( - user_qq=user_qq, - belonging_group=belonging_group, - ) - return '' + user_qq=user_qq, + belonging_group=belonging_group, + ) + return "" @classmethod async def add_gold(cls, user_qq: int, belonging_group: int, num: int) -> bool: + """ + 说明: + 增加金币 + 参数: + :param user_qq: qq号 + :param belonging_group: 所在群号 + :param num: 金币数量 + """ query = cls.query.where( (cls.user_qq == user_qq) & (cls.belonging_group == belonging_group) ) @@ -74,7 +104,7 @@ class BagUser(db.Model): await user.update( gold=user.gold + num, get_total_gold=user.get_total_gold + num, - get_today_gold=user.get_today_gold + num + get_today_gold=user.get_today_gold + num, ).apply() else: await cls.create( @@ -90,6 +120,14 @@ class BagUser(db.Model): @classmethod async def spend_gold(cls, user_qq: int, belonging_group: int, num: int) -> bool: + """ + 说明: + 花费金币 + 参数: + :param user_qq: qq号 + :param belonging_group: 所在群号 + :param num: 金币数量 + """ query = cls.query.where( (cls.user_qq == user_qq) & (cls.belonging_group == belonging_group) ) @@ -100,7 +138,7 @@ class BagUser(db.Model): await user.update( gold=user.gold - num, spend_total_gold=user.spend_total_gold + num, - spend_today_gold=user.spend_today_gold + num + spend_today_gold=user.spend_today_gold + num, ).apply() else: await cls.create( @@ -108,7 +146,7 @@ class BagUser(db.Model): belonging_group=belonging_group, gold=100 - num, spend_total_gold=num, - spend_today_gold=num + spend_today_gold=num, ) return True except Exception: @@ -116,6 +154,14 @@ class BagUser(db.Model): @classmethod async def add_props(cls, user_qq: int, belonging_group: int, name: str) -> bool: + """ + 说明: + 增加道具 + 参数: + :param user_qq: qq号 + :param belonging_group: 所在群号 + :param name: 道具名称 + """ query = cls.query.where( (cls.user_qq == user_qq) & (cls.belonging_group == belonging_group) ) @@ -123,14 +169,10 @@ class BagUser(db.Model): user = await query.gino.first() try: if user: - await user.update( - props=user.props + f'{name},' - ).apply() + await user.update(props=user.props + f"{name},").apply() else: await cls.create( - user_qq=user_qq, - belonging_group=belonging_group, - props=f'{name},' + user_qq=user_qq, belonging_group=belonging_group, props=f"{name}," ) return True except Exception: @@ -138,6 +180,14 @@ class BagUser(db.Model): @classmethod async def del_props(cls, user_qq: int, belonging_group: int, name: str) -> bool: + """ + 说明: + 使用道具 + 参数: + :param user_qq: qq号 + :param belonging_group: 所在群号 + :param name: 道具名称 + """ query = cls.query.where( (cls.user_qq == user_qq) & (cls.belonging_group == belonging_group) ) @@ -145,21 +195,19 @@ class BagUser(db.Model): user = await query.gino.first() try: if user: - rst = '' + rst = "" props = user.props if props.find(name) != -1: - props = props.split(',') + props = props.split(",") try: index = props.index(name) except ValueError: return False - props = props[:index] + props[index + 1:] + props = props[:index] + props[index + 1 :] for p in props: - if p != '': - rst += p + ',' - await user.update( - props=rst - ).apply() + if p != "": + rst += p + "," + await user.update(props=rst).apply() return True else: return False @@ -169,19 +217,15 @@ class BagUser(db.Model): return False @classmethod - async def get_user_all(cls, group_id: int = None) -> list: + async def get_all_users(cls, group_id: Optional[int] = None) -> List["BagUser"]: + """ + 说明: + 获取所有用户数据 + 参数: + :param group_id: 群号 + """ if not group_id: query = await cls.query.gino.all() else: - query = await cls.query.where( - (cls.belonging_group == group_id) - ).gino.all() + query = await cls.query.where((cls.belonging_group == group_id)).gino.all() return query - - - - - - - - diff --git a/models/ban_user.py b/models/ban_user.py index 3e70658f..8c9243b1 100644 --- a/models/ban_user.py +++ b/models/ban_user.py @@ -3,20 +3,25 @@ import time class BanUser(db.Model): - __tablename__ = 'ban_users' + __tablename__ = "ban_users" user_qq = db.Column(db.BigInteger(), nullable=False, primary_key=True) ban_level = db.Column(db.Integer(), nullable=False) ban_time = db.Column(db.BigInteger()) duration = db.Column(db.BigInteger()) - _idx1 = db.Index('ban_group_users_idx1', 'user_qq', unique=True) + _idx1 = db.Index("ban_group_users_idx1", "user_qq", unique=True) @classmethod - async def check_ban_level(cls, user_qq: int, level: int) -> 'bool': - user = await cls.query.where( - (cls.user_qq == user_qq) - ).gino.first() + async def check_ban_level(cls, user_qq: int, level: int) -> bool: + """ + 说明: + 检测ban掉目标的用户与unban用户的权限等级大小 + 参数: + :param user_qq: unban用户的qq号 + :param level: ban掉目标用户的权限等级 + """ + user = await cls.query.where((cls.user_qq == user_qq)).gino.first() if not user: return False if user.ban_level > level: @@ -24,21 +29,31 @@ class BanUser(db.Model): return False @classmethod - async def check_ban_time(cls, user_qq: int) -> 'str': - query = cls.query.where( - (cls.user_qq == user_qq) - ) + async def check_ban_time(cls, user_qq: int) -> str: + """ + 说明: + 检测用户被ban时长 + 参数: + :param user_qq: qq号 + """ + query = cls.query.where((cls.user_qq == user_qq)) user = await query.gino.first() if not user: - return '' + return "" if time.time() - (user.ban_time + user.duration) > 0 and user.duration != -1: - return '' + return "" if user.duration == -1: - return '∞' + return "∞" return time.time() - user.ban_time - user.duration @classmethod - async def isban(cls, user_qq: int) -> 'bool': + async def is_ban(cls, user_qq: int) -> bool: + """ + 说明: + 判断用户是否被ban + 参数: + :param user_qq: qq号 + """ if await cls.check_ban_time(user_qq): return True else: @@ -46,10 +61,16 @@ class BanUser(db.Model): return False @classmethod - async def ban(cls, user_qq: int, ban_level: int, duration: int) -> 'bool': - query = cls.query.where( - (cls.user_qq == user_qq) - ) + async def ban(cls, user_qq: int, ban_level: int, duration: int) -> bool: + """ + 说明: + ban掉目标用户 + 参数: + :param user_qq: 目标用户qq号 + :param ban_level: 使用ban命令用户的权限 + :param duration: ban时长 + """ + query = cls.query.where((cls.user_qq == user_qq)) query = query.with_for_update() user = await query.gino.first() if not await cls.check_ban_time(user_qq): @@ -67,16 +88,18 @@ class BanUser(db.Model): return False @classmethod - async def unban(cls, user_qq: int) -> 'bool': - query = cls.query.where( - (cls.user_qq == user_qq) - ) + async def unban(cls, user_qq: int) -> bool: + """ + 说明: + unban用户 + 参数: + :param user_qq: qq号 + """ + query = cls.query.where((cls.user_qq == user_qq)) query = query.with_for_update() user = await query.gino.first() if user is None: return False else: - await cls.delete.where( - (cls.user_qq == user_qq) - ).gino.status() + await cls.delete.where((cls.user_qq == user_qq)).gino.status() return True diff --git a/models/buff_price.py b/models/buff_price.py index b27451fd..852f30e7 100644 --- a/models/buff_price.py +++ b/models/buff_price.py @@ -1,4 +1,4 @@ -from datetime import datetime + from datetime import datetime from services.db_context import db diff --git a/models/count_user.py b/models/count_user.py index 81a8a72e..39d59771 100644 --- a/models/count_user.py +++ b/models/count_user.py @@ -1,78 +1,63 @@ - from services.db_context import db class UserCount(db.Model): - __tablename__ = 'count_users' + __tablename__ = "count_users" user_qq = db.Column(db.BigInteger(), nullable=False, primary_key=True) reimu_count = db.Column(db.Integer(), nullable=False, default=0) setu_r18_count = db.Column(db.Integer(), nullable=False, default=0) - _idx1 = db.Index('sign_reimu_users_idx1', 'user_qq', unique=True) - - @classmethod - async def add_user(cls, user_qq: int): - query = cls.query.where( - (cls.user_qq == user_qq) - ) - query = query.with_for_update() - if not await query.gino.first(): - await cls.create( - user_qq=user_qq, - ) + _idx1 = db.Index("sign_reimu_users_idx1", "user_qq", unique=True) @classmethod async def add_count(cls, user_qq: int, name: str, count: int = 1): - query = cls.query.where( - (cls.user_qq == user_qq) - ) + """ + 说明: + 用户添加次数 + 参数: + :param user_qq: qq号 + :param name: 目标名称 + :param count: 增加次数 + """ + query = cls.query.where((cls.user_qq == user_qq)) query = query.with_for_update() user = await query.gino.first() - if user: - if name == 'reimu': - await user.update( - reimu_count=cls.reimu_count + count - ).apply() - if name == 'setu_r18': - await user.update( - setu_r18_count=cls.setu_r18_count + count - ).apply() - else: - await cls.create( - user_qq=user_qq - ) - + user = user if user else await cls.create(user_qq=user_qq) + if name == "reimu": + await user.update(reimu_count=cls.reimu_count + count).apply() + if name == "setu_r18": + await user.update(setu_r18_count=cls.setu_r18_count + count).apply() @classmethod async def check_count(cls, user_qq: int, name: str, max_count: int) -> bool: - query = cls.query.where( - (cls.user_qq == user_qq) - ) + """ + 说明: + 检测次数是否到达最大值 + 参数: + :param user_qq: qq号 + :param name: 目标名称 + :param max_count: 最大值 + """ + query = cls.query.where((cls.user_qq == user_qq)) user = await query.gino.first() - if user: - if name == 'reimu': - if user.reimu_count == max_count: - return True - else: - return False - if name == 'setu_r18': - if user.setu_r18_count == max_count: - return True - else: - return False - else: - await cls.add_user(user_qq) - return False + user = user if user else await cls.create(user_qq=user_qq) + if name == "reimu": + if user.reimu_count == max_count: + return True + else: + return False + if name == "setu_r18": + if user.setu_r18_count == max_count: + return True + else: + return False @classmethod async def reset_count(cls): + """ + 说明: + 重置每日次数 + """ for user in await cls.query.gino.all(): - await user.update( - reimu_count=0, - setu_r18_count=0 - ).apply() - - - - + await user.update(reimu_count=0, setu_r18_count=0).apply() diff --git a/models/friend_user.py b/models/friend_user.py index 8e4d5b14..c6b731ef 100644 --- a/models/friend_user.py +++ b/models/friend_user.py @@ -2,32 +2,41 @@ from services.db_context import db class FriendUser(db.Model): - __tablename__ = 'friend_users' + __tablename__ = "friend_users" id = db.Column(db.Integer(), primary_key=True) user_id = db.Column(db.BigInteger(), nullable=False) user_name = db.Column(db.Unicode(), nullable=False, default="") nickname = db.Column(db.Unicode()) - _idx1 = db.Index('friend_users_idx1', 'user_id', unique=True) + _idx1 = db.Index("friend_users_idx1", "user_id", unique=True) @classmethod async def get_user_name(cls, user_id: int) -> str: - query = cls.query.where( - cls.user_id == user_id - ) + """ + 说明: + 获取好友用户名称 + 参数: + :param user_id: qq号 + """ + query = cls.query.where(cls.user_id == user_id) user = await query.gino.first() if user: return user.user_name else: - return '' + return "" @classmethod - async def add_friend_info(cls, user_id: int, user_name: str) -> 'bool': + async def add_friend_info(cls, user_id: int, user_name: str) -> bool: + """ + 说明: + 添加好友信息 + 参数: + :param user_id: qq号 + :param user_name: 用户名称 + """ try: - query = cls.query.where( - cls.user_id == user_id - ) + query = cls.query.where(cls.user_id == user_id) user = await query.with_for_update().gino.first() if not user: await cls.create( @@ -43,11 +52,15 @@ class FriendUser(db.Model): return False @classmethod - async def delete_friend_info(cls, user_id: int) -> 'bool': + async def delete_friend_info(cls, user_id: int) -> bool: + """ + 说明: + 删除好友信息 + 参数: + :param user_id: qq号 + """ try: - query = cls.query.where( - cls.user_id == user_id - ) + query = cls.query.where(cls.user_id == user_id) user = await query.with_for_update().gino.first() if user: await user.delete() @@ -56,22 +69,31 @@ class FriendUser(db.Model): return False @classmethod - async def get_friend_nickname(cls, user_id: int) -> 'str': - query = cls.query.where( - cls.user_id == user_id - ) + async def get_friend_nickname(cls, user_id: int) -> str: + """ + 说明: + 获取用户昵称 + 参数: + :param user_id: qq号 + """ + query = cls.query.where(cls.user_id == user_id) user = await query.gino.first() if user: if user.nickname: return user.nickname - return '' + return "" @classmethod - async def set_friend_nickname(cls, user_id: int, nickname: str) -> 'bool': + async def set_friend_nickname(cls, user_id: int, nickname: str) -> bool: + """ + 说明: + 设置用户昵称 + 参数: + :param user_id: qq号 + :param nickname: 昵称 + """ try: - query = cls.query.where( - cls.user_id == user_id - ) + query = cls.query.where(cls.user_id == user_id) user = await query.with_for_update().gino.first() if not user: await cls.create( diff --git a/models/goods_info.py b/models/goods_info.py index 54373a68..e199f2b9 100644 --- a/models/goods_info.py +++ b/models/goods_info.py @@ -1,83 +1,124 @@ - from services.db_context import db +from typing import Optional, List class GoodsInfo(db.Model): - __tablename__ = 'goods_info' + __tablename__ = "goods_info" id = db.Column(db.Integer(), primary_key=True) - goods_name = db.Column(db.TEXT(), nullable=False) # 名称 - goods_price = db.Column(db.Integer(), nullable=False) # 价格 - goods_description = db.Column(db.TEXT(), nullable=False) # 商品描述 + goods_name = db.Column(db.TEXT(), nullable=False) # 名称 + goods_price = db.Column(db.Integer(), nullable=False) # 价格 + goods_description = db.Column(db.TEXT(), nullable=False) # 商品描述 goods_discount = db.Column(db.Numeric(scale=3, asdecimal=False), default=1) # 打折 - goods_limit_time = db.Column(db.BigInteger(), default=0) # 限时 + goods_limit_time = db.Column(db.BigInteger(), default=0) # 限时 - _idx1 = db.Index('goods_group_users_idx1', 'goods_name', unique=True) + _idx1 = db.Index("goods_group_users_idx1", "goods_name", unique=True) @classmethod - async def add_goods(cls, goods_name: str, goods_price: int, - goods_description: str, goods_discount: float = 1, goods_limit_time: int = 0) -> bool: - # try: - await cls.create( - goods_name=goods_name, - goods_price=goods_price, - goods_description=goods_description, - goods_discount=goods_discount, - goods_limit_time=goods_limit_time + async def add_goods( + cls, + goods_name: str, + goods_price: int, + goods_description: str, + goods_discount: float = 1, + goods_limit_time: int = 0, + ) -> bool: + """ + 说明: + 添加商品 + 参数: + :param goods_name: 商品名称 + :param goods_price: 商品价格 + :param goods_description: 商品简介 + :param goods_discount: 商品折扣 + :param goods_limit_time: 商品限时 + """ + try: + await cls.create( + goods_name=goods_name, + goods_price=goods_price, + goods_description=goods_description, + goods_discount=goods_discount, + goods_limit_time=goods_limit_time, + ) + return True + except Exception: + return False + + @classmethod + async def delete_goods(cls, goods_name: str) -> bool: + """ + 说明: + 删除商品 + 参数: + :param goods_name: 商品名称 + """ + query = ( + await cls.query.where(cls.goods_name == goods_name) + .with_for_update() + .gino.first() ) - return True - # except Exception: - # return False - - @classmethod - async def del_goods(cls, goods_name: str) -> bool: - query = await cls.query.where( - cls.goods_name == goods_name - ).with_for_update().gino.first() if not query: return False await query.delete() return True @classmethod - async def update_goods(cls, goods_name: str, goods_price: int = None, - goods_description: str = None, goods_discount: float = None, - goods_limit_time: int = None) -> bool: + async def update_goods( + cls, + goods_name: str, + goods_price: Optional[int] = None, + goods_description: Optional[str] = None, + goods_discount: Optional[float] = None, + goods_limit_time: Optional[int] = None, + ) -> bool: + """ + 说明: + 更新商品信息 + 参数: + :param goods_name: 商品名称 + :param goods_price: 商品价格 + :param goods_description: 商品简介 + :param goods_discount: 商品折扣 + :param goods_limit_time: 商品限时时间 + """ try: - query = await cls.query.where( - cls.goods_name == goods_name - ).with_for_update().gino.first() + query = ( + await cls.query.where(cls.goods_name == goods_name) + .with_for_update() + .gino.first() + ) if not query: return False if goods_price: - await query.update( - goods_price=goods_price - ).apply() + await query.update(goods_price=goods_price).apply() if goods_description: - await query.update( - goods_description=goods_description - ).apply() + await query.update(goods_description=goods_description).apply() if goods_discount: - await query.update( - goods_discount=goods_discount - ).apply() + await query.update(goods_discount=goods_discount).apply() if goods_limit_time: - await query.update( - goods_limit_time=goods_limit_time - ).apply() + await query.update(goods_limit_time=goods_limit_time).apply() return True except Exception: return False @classmethod - async def get_goods_info(cls, goods_name: str) -> 'GoodsInfo': - query = await cls.query.where( - cls.goods_name == goods_name - ).gino.first() + async def get_goods_info(cls, goods_name: str) -> "GoodsInfo": + """ + 说明: + 获取商品对象 + 参数: + :param goods_name: 商品名称 + """ + query = await cls.query.where(cls.goods_name == goods_name).gino.first() return query @classmethod - async def get_all_goods(cls) -> list: + async def get_all_goods(cls) -> List["GoodsInfo"]: + """ + 说明: + 获得全部有序商品对象 + """ query = await cls.query.gino.all() id_lst = [x.id for x in query] goods_lst = [] @@ -86,13 +127,3 @@ class GoodsInfo(db.Model): goods_lst.append([x for x in query if x.id == min_id][0]) id_lst.remove(min_id) return goods_lst - - - - - - - - - - diff --git a/models/group_info.py b/models/group_info.py index 63beb498..c8999474 100644 --- a/models/group_info.py +++ b/models/group_info.py @@ -1,30 +1,47 @@ - from services.db_context import db +from typing import List class GroupInfo(db.Model): - __tablename__ = 'group_info' + __tablename__ = "group_info" group_id = db.Column(db.BigInteger(), nullable=False, primary_key=True) group_name = db.Column(db.Unicode(), nullable=False, default="") max_member_count = db.Column(db.Integer(), nullable=False, default=0) member_count = db.Column(db.Integer(), nullable=False, default=0) - _idx1 = db.Index('group_info_idx1', 'group_id', unique=True) + _idx1 = db.Index("group_info_idx1", "group_id", unique=True) @classmethod - async def get_group_info(cls, group_id: int) -> 'GroupInfo': - query = cls.query.where( - cls.group_id == group_id - ) + async def get_group_info(cls, group_id: int) -> "GroupInfo": + """ + 说明: + 获取群信息 + 参数: + :param group_id: 群号 + """ + query = cls.query.where(cls.group_id == group_id) return await query.gino.first() @classmethod - async def add_group_info(cls, group_id: int, group_name: str, max_member_count: int, member_count: int) -> bool: + async def add_group_info( + cls, group_id: int, group_name: str, max_member_count: int, member_count: int + ) -> bool: + """ + 说明: + 添加群信息 + 参数: + :param group_id: 群号 + :param group_name: 群名称 + :param max_member_count: 群员最大数量 + :param member_count: 群员数量 + """ try: - group = await cls.query.where( - cls.group_id == group_id - ).with_for_update().gino.first() + group = ( + await cls.query.where(cls.group_id == group_id) + .with_for_update() + .gino.first() + ) if group: await cls.update( group_id=group_id, @@ -45,12 +62,23 @@ class GroupInfo(db.Model): @classmethod async def delete_group_info(cls, group_id: int) -> bool: + """ + 说明: + 删除群信息 + 参数: + :param group_id: 群号 + """ try: - await cls.delete.where( - cls.group_id == group_id - ).gino.status() + await cls.delete.where(cls.group_id == group_id).gino.status() return True except Exception: return False - + @classmethod + async def get_all_group(cls) -> List["GroupInfo"]: + """ + 说明: + 获取所有群对象 + """ + query = await cls.query.gino.all() + return query diff --git a/models/group_member_info.py b/models/group_member_info.py index 3f2ef1b4..14c20ef4 100644 --- a/models/group_member_info.py +++ b/models/group_member_info.py @@ -1,10 +1,11 @@ from datetime import datetime from services.db_context import db +from typing import List class GroupInfoUser(db.Model): - __tablename__ = 'group_info_users' + __tablename__ = "group_info_users" id = db.Column(db.Integer(), primary_key=True) user_qq = db.Column(db.BigInteger(), nullable=False) @@ -13,16 +14,30 @@ class GroupInfoUser(db.Model): user_join_time = db.Column(db.DateTime(), nullable=False) nickname = db.Column(db.Unicode()) - _idx1 = db.Index('info_group_users_idx1', 'user_qq', 'belonging_group', unique=True) + _idx1 = db.Index("info_group_users_idx1", "user_qq", "belonging_group", unique=True) @classmethod - async def insert(cls, user_qq: int, belonging_group: int, user_name: str, user_join_time: datetime) -> 'bool': + async def add_member_info( + cls, + user_qq: int, + belonging_group: int, + user_name: str, + user_join_time: datetime, + ) -> bool: + """ + 说明: + 添加群内用户信息 + 参数: + :param user_qq: qq号 + :param belonging_group: 群号 + :param user_name: 用户名称 + :param user_join_time: 入群时间 + """ query = cls.query.where( (cls.user_qq == user_qq) & (cls.belonging_group == belonging_group) ) - query = query.with_for_update() try: - if await query.gino.first() is None: + if not await query.gino.first(): await cls.create( user_qq=user_qq, user_name=user_name, @@ -30,18 +45,34 @@ class GroupInfoUser(db.Model): user_join_time=user_join_time, ) return True - except: + except Exception: return False @classmethod - async def select_member_info(cls, user_qq: int, belonging_group: int) -> 'GroupInfoUser': + async def get_member_info( + cls, user_qq: int, belonging_group: int + ) -> "GroupInfoUser": + """ + 说明: + 查询群员信息 + 参数: + :param user_qq: qq号 + :param belonging_group: 群号 + """ query = cls.query.where( (cls.user_qq == user_qq) & (cls.belonging_group == belonging_group) ) return await query.gino.first() @classmethod - async def delete_member_info(cls, user_qq: int, belonging_group: int) -> 'bool': + async def delete_member_info(cls, user_qq: int, belonging_group: int) -> bool: + """ + 说明: + 删除群员信息 + 参数: + :param user_qq: qq号 + :param belonging_group: 群号 + """ query = cls.query.where( (cls.user_qq == user_qq) & (cls.belonging_group == belonging_group) ) @@ -55,34 +86,53 @@ class GroupInfoUser(db.Model): (cls.user_qq == user_qq) & (cls.belonging_group == belonging_group) ).gino.status() return True - except: + except Exception: return False @classmethod - async def query_group_member_list(cls, belonging_group: int) -> 'list': + async def get_group_member_id_list(cls, belonging_group: int) -> List[int]: + """ + 说明: + 获取该群所有用户qq + 参数: + :param belonging_group: 群号 + """ member_list = [] - query = cls.query.where( - (cls.belonging_group == belonging_group) - ) + query = cls.query.where((cls.belonging_group == belonging_group)) for user in await query.gino.all(): member_list.append(user.user_qq) return member_list @classmethod - async def set_group_member_nickname(cls, user_qq: int, belonging_group: int, nickname: str) -> 'bool': + async def set_group_member_nickname( + cls, user_qq: int, belonging_group: int, nickname: str + ) -> bool: + """ + 说明: + 设置群员在该群内的昵称 + 参数: + :param user_qq: qq号 + :param belonging_group: 群号 + :param nickname: 昵称 + """ query = cls.query.where( (cls.user_qq == user_qq) & (cls.belonging_group == belonging_group) ) user = await query.with_for_update().gino.first() if user: - await user.update( - nickname=nickname - ).apply() + await user.update(nickname=nickname).apply() return True return False @classmethod - async def get_group_member_nickname(cls, user_qq: int, belonging_group: int) -> 'str': + async def get_group_member_nickname(cls, user_qq: int, belonging_group: int) -> str: + """ + 说明: + 获取用户在该群的昵称 + 参数: + :param user_qq: qq号 + :param belonging_group: 群号 + """ query = cls.query.where( (cls.user_qq == user_qq) & (cls.belonging_group == belonging_group) ) @@ -90,10 +140,4 @@ class GroupInfoUser(db.Model): if user: if user.nickname: return user.nickname - return '' - - - - - - + return "" diff --git a/models/group_remind.py b/models/group_remind.py index 1e06110d..4f7216f7 100644 --- a/models/group_remind.py +++ b/models/group_remind.py @@ -2,99 +2,104 @@ from services.db_context import db class GroupRemind(db.Model): - __tablename__ = 'group_reminds' + __tablename__ = "group_reminds" id = db.Column(db.Integer(), primary_key=True) group_id = db.Column(db.BigInteger(), nullable=False) - hy = db.Column(db.Boolean(), default=False) # 进群欢迎 - kxcz = db.Column(db.Boolean(), default=False) # 开箱重置 - zwa = db.Column(db.Boolean(), default=False) # 早晚安 - gb = db.Column(db.Boolean(), default=True) # 广播 - blpar = db.Column(db.Boolean(), default=True) # bilibili转发解析 - pa = db.Column(db.Boolean(), default=True) # 爬 - epic = db.Column(db.Boolean(), default=False) # epic - almanac = db.Column(db.Boolean(), default=False) # 原神黄历 + hy = db.Column(db.Boolean(), default=False) # 进群欢迎 + kxcz = db.Column(db.Boolean(), default=False) # 开箱重置 + zwa = db.Column(db.Boolean(), default=False) # 早晚安 + gb = db.Column(db.Boolean(), default=True) # 广播 + blpar = db.Column(db.Boolean(), default=True) # bilibili转发解析 + pa = db.Column(db.Boolean(), default=True) # 爬 + epic = db.Column(db.Boolean(), default=False) # epic + almanac = db.Column(db.Boolean(), default=False) # 原神黄历 - _idx1 = db.Index('info_group_reminds_idx1', 'group_id', unique=True) + _idx1 = db.Index("info_group_reminds_idx1", "group_id", unique=True) @classmethod async def get_status(cls, group_id: int, name: str) -> bool: - group = await cls.query.where( - (cls.group_id == group_id) - ).gino.first() + """ + 说明: + 获取群通知状态 + 参数: + :param group_id: 群号 + :param name: 目标名称 + """ + group = await cls.query.where((cls.group_id == group_id)).gino.first() if not group: group = await cls.create( group_id=group_id, ) - if name == 'hy': + if name == "hy": return group.hy - if name == 'kxcz': + if name == "kxcz": return group.kxcz - if name == 'zwa': + if name == "zwa": return group.zwa - if name == 'gb': + if name == "gb": return group.gb - if name == 'blpar': + if name == "blpar": return group.blpar - if name == 'epic': + if name == "epic": return group.epic - if name == 'pa': + if name == "pa": return group.pa - if name == 'almanac': + if name == "almanac": return group.almanac @classmethod async def set_status(cls, group_id: int, name: str, status: bool) -> bool: + """ + 说明: + 设置群通知状态 + 参数: + :param group_id: 群号 + :param name: 目标名称 + :param status: 通知状态 + """ try: - group = await cls.query.where( - (cls.group_id == group_id) - ).with_for_update().gino.first() + group = ( + await cls.query.where((cls.group_id == group_id)) + .with_for_update() + .gino.first() + ) if not group: group = await cls.create( group_id=group_id, ) - if name == 'hy': + if name == "hy": await group.update( hy=status, ).apply() - if name == 'kxcz': + if name == "kxcz": await group.update( kxcz=status, ).apply() - if name == 'zwa': + if name == "zwa": await group.update( zwa=status, ).apply() - if name == 'gb': + if name == "gb": await group.update( gb=status, ).apply() - if name == 'blpar': + if name == "blpar": await group.update( blpar=status, ).apply() - if name == 'epic': + if name == "epic": await group.update( epic=status, ).apply() - if name == 'pa': + if name == "pa": await group.update( pa=status, ).apply() - if name == 'almanac': + if name == "almanac": await group.update( almanac=status, ).apply() return True except Exception as e: return False - - - - - - - - - - diff --git a/models/level_user.py b/models/level_user.py index 850626b7..720c55cd 100644 --- a/models/level_user.py +++ b/models/level_user.py @@ -2,7 +2,7 @@ from services.db_context import db class LevelUser(db.Model): - __tablename__ = 'level_users' + __tablename__ = "level_users" id = db.Column(db.Integer(), primary_key=True) user_qq = db.Column(db.BigInteger(), nullable=False) @@ -10,13 +10,18 @@ class LevelUser(db.Model): user_level = db.Column(db.BigInteger(), nullable=False) group_flag = db.Column(db.Integer(), nullable=False, default=0) - _idx1 = db.Index('level_group_users_idx1', 'user_qq', 'group_id', unique=True) + _idx1 = db.Index("level_group_users_idx1", "user_qq", "group_id", unique=True) @classmethod async def get_user_level(cls, user_qq: int, group_id: int) -> int: - query = cls.query.where( - (cls.user_qq == user_qq) & (cls.group_id == group_id) - ) + """ + 说明: + 获取用户在群内的等级 + 参数: + :param user_qq: qq号 + :param group_id: 群号 + """ + query = cls.query.where((cls.user_qq == user_qq) & (cls.group_id == group_id)) user = await query.gino.first() if user: return user.user_level @@ -24,10 +29,19 @@ class LevelUser(db.Model): return -1 @classmethod - async def set_level(cls, user_qq: int, group_id: int, level: int, group_flag: int = 0) -> 'bool': - query = cls.query.where( - (cls.user_qq == user_qq) & (cls.group_id == group_id) - ) + async def set_level( + cls, user_qq: int, group_id: int, level: int, group_flag: int = 0 + ) -> bool: + """ + 说明: + 设置用户在群内的权限 + 参数: + :param user_qq: qq号 + :param group_id: 群号 + :param level: 权限等级 + :param group_flag: 是否被自动更新刷新权限 0:是,1:否 + """ + query = cls.query.where((cls.user_qq == user_qq) & (cls.group_id == group_id)) query = query.with_for_update() user = await query.gino.first() if user is None: @@ -43,10 +57,15 @@ class LevelUser(db.Model): return False @classmethod - async def delete_level(cls, user_qq: int, group_id: int) -> 'bool': - query = cls.query.where( - (cls.user_qq == user_qq) & (cls.group_id == group_id) - ) + async def delete_level(cls, user_qq: int, group_id: int) -> bool: + """ + 说明: + 删除用户权限 + 参数: + :param user_qq: qq号 + :param group_id: 群号 + """ + query = cls.query.where((cls.user_qq == user_qq) & (cls.group_id == group_id)) query = query.with_for_update() user = await query.gino.first() if user is None: @@ -56,7 +75,15 @@ class LevelUser(db.Model): return True @classmethod - async def check_level(cls, user_qq: int, group_id: int, level: int) -> 'bool': + async def check_level(cls, user_qq: int, group_id: int, level: int) -> bool: + """ + 说明: + 检查用户权限等级是否大于 level + 参数: + :param user_qq: qq号 + :param group_id: 群号 + :param level: 权限等级 + """ if group_id != 0: query = cls.query.where( (cls.user_qq == user_qq) & (cls.group_id == group_id) @@ -66,9 +93,7 @@ class LevelUser(db.Model): return False user_level = user.user_level else: - query = cls.query.where( - cls.user_qq == user_qq - ) + query = cls.query.where(cls.user_qq == user_qq) highest_level = 0 for user in await query.gino.all(): if user.user_level > highest_level: @@ -80,7 +105,14 @@ class LevelUser(db.Model): return False @classmethod - async def is_group_flag(cls, user_qq: int, group_id: int) -> 'bool': + async def is_group_flag(cls, user_qq: int, group_id: int) -> bool: + """ + 说明: + 检测是否会被自动更新刷新权限 + 参数: + :param user_qq: qq号 + :param group_id: 群号 + """ user = await cls.query.where( (cls.user_qq == user_qq) & (cls.group_id == group_id) ).gino.first() @@ -90,4 +122,3 @@ class LevelUser(db.Model): return True else: return False - diff --git a/models/pixiv.py b/models/pixiv.py new file mode 100644 index 00000000..07a659cf --- /dev/null +++ b/models/pixiv.py @@ -0,0 +1,165 @@ +from typing import Optional, List +from services.db_context import db + + +class Pixiv(db.Model): + __tablename__ = "pixiv" + + id = db.Column(db.Integer(), primary_key=True) + pid = db.Column(db.BigInteger(), nullable=False) + title = db.Column(db.String(), nullable=False) + width = db.Column(db.Integer(), nullable=False) + height = db.Column(db.Integer(), nullable=False) + view = db.Column(db.Integer(), nullable=False) + bookmarks = db.Column(db.Integer(), nullable=False) + img_url = db.Column(db.String(), nullable=False) + img_p = db.Column(db.String(), nullable=False) + uid = db.Column(db.BigInteger(), nullable=False) + author = db.Column(db.String(), nullable=False) + is_r18 = db.Column(db.Boolean(), nullable=False) + tags = db.Column(db.String(), nullable=False) + + _idx1 = db.Index("pixiv_idx1", "pid", "img_url", unique=True) + + @classmethod + async def add_image_data( + cls, + pid: int, + title: str, + width: int, + height: int, + view: int, + bookmarks: int, + img_url: str, + img_p: str, + uid: int, + author: str, + tags: str, + ): + """ + 说明: + 添加图片信息 + 参数: + :param pid: pid + :param title: 标题 + :param width: 宽度 + :param height: 长度 + :param view: 被查看次数 + :param bookmarks: 收藏数 + :param img_url: url链接 + :param img_p: 张数 + :param uid: 作者uid + :param author: 作者名称 + :param tags: 相关tag + """ + if not await cls.check_exists(pid, img_p): + await cls.create( + pid=pid, + title=title, + width=width, + height=height, + view=view, + bookmarks=bookmarks, + img_url=img_url, + img_p=img_p, + uid=uid, + author=author, + is_r18=True if 'R-18' in tags else False, + tags=tags, + ) + return True + return False + + @classmethod + async def remove_image_data(cls, pid: int, img_p: str) -> bool: + """ + 说明: + 删除图片数据 + 参数: + :param pid: 图片pid + :param img_p: 图片pid的张数,如:p0,p1 + """ + try: + if img_p: + await cls.delete.where( + (cls.pid == pid) & (cls.img_p == img_p) + ).gino.status() + else: + await cls.delete.where(cls.pid == pid).gino.status() + return True + except Exception: + return False + + @classmethod + async def get_all_pid(cls) -> List[int]: + """ + 说明: + 获取所有PID + """ + pid = [] + query = await cls.query.gino.all() + for image in query: + if image.pid not in pid: + pid.append(image.pid) + return pid + + # 0:非r18 1:r18 2:混合 + @classmethod + async def query_images( + cls, + keyword: Optional[List[str]] = None, + uid: Optional[int] = None, + pid: Optional[int] = None, + r18: int = 0, + ) -> List[Optional["Pixiv"]]: + """ + 说明: + 查找符合条件的图片 + 参数: + :param keyword: 关键词 + :param uid: 画师uid + :param pid: 图片pid + :param r18: 是否r18,0:非r18 1:r18 2:混合 + """ + if r18 == 0: + query = await cls.query.where(cls.is_r18 == False).gino.all() + elif r18 == 1: + query = await cls.query.where(cls.is_r18 == True).gino.all() + else: + query = await cls.query.gino.all() + if keyword: + query = [x for x in query if set(x.tags.split(',')) > set(keyword)] + elif uid: + query = [x for x in query if x.uid == uid] + elif pid: + query = [x for x in query if x.pid == pid] + return query + + @classmethod + async def check_exists(cls, pid: int, img_p: str) -> bool: + """ + 说明: + 检测pid是否已存在 + 参数: + :param pid: 图片PID + :param img_p: 张数 + """ + query = await cls.query.where( + (cls.pid == pid) & (cls.img_p == img_p) + ).gino.all() + return bool(query) + + @classmethod + async def get_keyword_num(cls, keyword: List[str]) -> "int, int": + """ + 说明: + 获取相关关键词(keyword, tag)在图库中的数量 + 参数: + :param keyword: 关键词/Tag + """ + query = await cls.query.gino.all() + query = [x for x in query if set(x.tags.split(',')) > set(keyword)] + r18_count = len([x for x in query if x.is_r18]) + return len(query) - r18_count, r18_count + + diff --git a/models/pixiv_keyword_user.py b/models/pixiv_keyword_user.py new file mode 100644 index 00000000..7c5b864c --- /dev/null +++ b/models/pixiv_keyword_user.py @@ -0,0 +1,126 @@ +from services.db_context import db +from typing import Set, List + + +class PixivKeywordUser(db.Model): + __tablename__ = "pixiv_keyword_users" + + id = db.Column(db.Integer(), primary_key=True) + user_qq = db.Column(db.BigInteger(), nullable=False) + group_id = db.Column(db.BigInteger(), nullable=False) + keyword = db.Column(db.String(), nullable=False) + is_pass = db.Column(db.Boolean(), default=False) + + _idx1 = db.Index("pixiv_keyword_users_idx1", "keyword", unique=True) + + @classmethod + async def add_keyword( + cls, user_qq: int, group_id: int, keyword: str, superusers: Set[str] + ) -> bool: + """ + 说明: + 添加搜图的关键词 + 参数: + :param user_qq: qq号 + :param group_id: 群号 + :param keyword: 关键词 + :param superusers: 是否为超级用户 + """ + is_pass = True if str(user_qq) in superusers else False + if not await cls._check_keyword_exists(keyword): + await cls.create( + user_qq=user_qq, group_id=group_id, keyword=keyword, is_pass=is_pass + ) + return True + return False + + @classmethod + async def delete_keyword(cls, keyword: str) -> bool: + """ + 说明: + 删除关键词 + 参数: + :param keyword: 关键词 + """ + if await cls._check_keyword_exists(keyword): + query = cls.query.where(cls.keyword == keyword).with_for_update() + query = await query.gino.first() + await query.delete() + return True + return False + + @classmethod + async def set_keyword_pass(cls, keyword: str, is_pass: bool) -> "int, int": + """ + 说明: + 通过或禁用关键词 + 参数: + :param keyword: 关键词 + :param is_pass: 通过状态 + """ + if await cls._check_keyword_exists(keyword): + query = cls.query.where(cls.keyword == keyword).with_for_update() + query = await query.gino.first() + await query.update( + is_pass=is_pass, + ).apply() + return query.user_qq, query.group_id + return 0, 0 + + @classmethod + async def get_all_user_dict(cls) -> dict: + """ + 说明: + 获取关键词数据库各个用户贡献的关键词字典 + """ + tmp = {} + query = await cls.query.gino.all() + for user in query: + if not tmp.get(user.user_qq): + tmp[user.user_qq] = {"keyword": []} + tmp[user.user_qq]["keyword"].append(user.keyword) + return tmp + + @classmethod + async def get_current_keyword(cls) -> "List[str], List[str]": + """ + 说明: + 获取当前通过与未通过的关键词 + """ + pass_keyword = [] + not_pass_keyword = [] + query = await cls.query.gino.all() + for user in query: + if user.is_pass: + pass_keyword.append(user.keyword) + else: + not_pass_keyword.append(user.keyword) + return pass_keyword, not_pass_keyword + + @classmethod + async def get_black_pid(cls) -> List[str]: + """ + 说明: + 获取黑名单PID + """ + black_pid = [] + query = await cls.query.where(cls.user_qq == 114514).gino.all() + for image in query: + black_pid.append(image.keyword[6:]) + return black_pid + + @classmethod + async def _check_keyword_exists(cls, keyword: str) -> bool: + """ + 说明: + 检测关键词是否已存在 + 参数: + :param keyword: 关键词 + """ + current_keyword = [] + query = await cls.query.gino.all() + for user in query: + current_keyword.append(user.keyword) + if keyword in current_keyword: + return True + return False diff --git a/models/redbag_user.py b/models/redbag_user.py index d7a3cf57..d129207d 100644 --- a/models/redbag_user.py +++ b/models/redbag_user.py @@ -1,9 +1,9 @@ - from services.db_context import db +from typing import List class RedbagUser(db.Model): - __tablename__ = 'redbag_users' + __tablename__ = "redbag_users" id = db.Column(db.Integer(), primary_key=True) user_qq = db.Column(db.BigInteger(), nullable=False) @@ -13,18 +13,25 @@ class RedbagUser(db.Model): spend_gold = db.Column(db.Integer(), default=0) get_gold = db.Column(db.Integer(), default=0) - _idx1 = db.Index('redbag_group_users_idx1', 'user_qq', 'group_id', unique=True) + _idx1 = db.Index("redbag_group_users_idx1", "user_qq", "group_id", unique=True) @classmethod async def add_redbag_data(cls, user_qq: int, group_id: int, itype: str, money: int): - query = cls.query.where( - (cls.user_qq == user_qq) & (cls.group_id == group_id) - ) + """ + 说明: + 添加收发红包数据 + 参数: + :param user_qq: qq号 + :param group_id: 群号 + :param itype: 收或发 + :param money: 金钱数量 + """ + query = cls.query.where((cls.user_qq == user_qq) & (cls.group_id == group_id)) user = await query.with_for_update().gino.first() or await cls.create( - user_qq=user_qq, - group_id=group_id, - ) - if itype == 'get': + user_qq=user_qq, + group_id=group_id, + ) + if itype == "get": await user.update( get_redbag_count=user.get_redbag_count + 1, get_gold=user.get_gold + money, @@ -37,9 +44,14 @@ class RedbagUser(db.Model): @classmethod async def ensure(cls, user_qq: int, group_id: int) -> bool: - query = cls.query.where( - (cls.user_qq == user_qq) & (cls.group_id == group_id) - ) + """ + 说明: + 获取用户对象 + 参数: + :param user_qq: qq号 + :param group_id: 群号 + """ + query = cls.query.where((cls.user_qq == user_qq) & (cls.group_id == group_id)) user = await query.gino.first() or await cls.create( user_qq=user_qq, group_id=group_id, @@ -47,19 +59,15 @@ class RedbagUser(db.Model): return user @classmethod - async def get_user_all(cls, group_id: int = None) -> list: + async def get_user_all(cls, group_id: int = None) -> List["RedbagUser"]: + """ + 说明: + 获取所有用户对象 + 参数: + :param group_id: 群号 + """ if not group_id: query = await cls.query.gino.all() else: - query = await cls.query.where( - (cls.group_id == group_id) - ).gino.all() + query = await cls.query.where((cls.group_id == group_id)).gino.all() return query - - - - - - - - diff --git a/models/russian_user.py b/models/russian_user.py index 7aadf860..0de17014 100644 --- a/models/russian_user.py +++ b/models/russian_user.py @@ -3,7 +3,7 @@ from typing import List class RussianUser(db.Model): - __tablename__ = 'russian_users' + __tablename__ = "russian_users" id = db.Column(db.Integer(), primary_key=True) user_qq = db.Column(db.BigInteger(), nullable=False) @@ -13,34 +13,49 @@ class RussianUser(db.Model): make_money = db.Column(db.Integer(), default=0) lose_money = db.Column(db.Integer(), default=0) - _idx1 = db.Index('russian_group_users_idx1', 'user_qq', 'group_id', unique=True) + _idx1 = db.Index("russian_group_users_idx1", "user_qq", "group_id", unique=True) @classmethod - async def ensure(cls, user_qq: int, group_id: int) -> 'RussianUser': - user = await cls.query.where( - (cls.user_qq == user_qq) & (cls.group_id == group_id) - ).with_for_update().gino.first() - return user or await cls.create( - user_qq=user_qq, - group_id=group_id + async def ensure(cls, user_qq: int, group_id: int) -> "RussianUser": + """ + 说明: + 获取用户对象 + 参数: + :param user_qq: qq号 + :param group_id: 群号 + """ + user = ( + await cls.query.where((cls.user_qq == user_qq) & (cls.group_id == group_id)) + .with_for_update() + .gino.first() ) + return user or await cls.create(user_qq=user_qq, group_id=group_id) @classmethod async def add_count(cls, user_qq: int, group_id: int, itype: str) -> bool: + """ + 说明: + 添加用户输赢次数 + 说明: + :param user_qq: qq号 + :param group_id: 群号 + :param itype: 输或赢 'win' or 'lose' + """ try: - user = await cls.query.where( - (cls.user_qq == user_qq) & (cls.group_id == group_id) - ).with_for_update().gino.first() - if not user: - user = await cls.create( - user_qq=user_qq, - group_id=group_id + user = ( + await cls.query.where( + (cls.user_qq == user_qq) & (cls.group_id == group_id) ) - if itype == 'win': + .with_for_update() + .gino.first() + ) + if not user: + user = await cls.create(user_qq=user_qq, group_id=group_id) + if itype == "win": await user.update( win_count=user.win_count + 1, ).apply() - elif itype == 'lose': + elif itype == "lose": await user.update( fail_count=user.fail_count + 1, ).apply() @@ -50,20 +65,30 @@ class RussianUser(db.Model): @classmethod async def money(cls, user_qq: int, group_id: int, itype: str, count: int) -> bool: + """ + 说明: + 添加用户输赢金钱 + 参数: + :param user_qq: qq号 + :param group_id: 群号 + :param itype: 输或赢 'win' or 'lose' + :param count: 金钱数量 + """ try: - user = await cls.query.where( - (cls.user_qq == user_qq) & (cls.group_id == group_id) - ).with_for_update().gino.first() - if not user: - user = await cls.create( - user_qq=user_qq, - group_id=group_id + user = ( + await cls.query.where( + (cls.user_qq == user_qq) & (cls.group_id == group_id) ) - if itype == 'win': + .with_for_update() + .gino.first() + ) + if not user: + user = await cls.create(user_qq=user_qq, group_id=group_id) + if itype == "win": await user.update( make_money=user.make_money + count, ).apply() - elif itype == 'lose': + elif itype == "lose": await user.update( lose_money=user.lose_money + count, ).apply() @@ -72,9 +97,12 @@ class RussianUser(db.Model): return False @classmethod - async def all_user(cls, group_id: int) -> List['RussianUser']: - users = await cls.query.where( - (cls.group_id == group_id) - ).gino.all() + async def get_all_user(cls, group_id: int) -> List["RussianUser"]: + """ + 说明: + 获取该群所有用户对象 + 参数: + :param group_id: 群号 + """ + users = await cls.query.where((cls.group_id == group_id)).gino.all() return users - diff --git a/models/setu.py b/models/setu.py new file mode 100644 index 00000000..23fb31c6 --- /dev/null +++ b/models/setu.py @@ -0,0 +1,168 @@ +from services.db_context import db +from typing import List, Optional + + +class Setu(db.Model): + __tablename__ = "setu" + id = db.Column(db.Integer(), primary_key=True) + local_id = db.Column(db.Integer(), nullable=False) + title = db.Column(db.String(), nullable=False) + author = db.Column(db.String(), nullable=False) + pid = db.Column(db.BigInteger(), nullable=False) + img_hash = db.Column(db.String(), nullable=False) + img_url = db.Column(db.String(), nullable=False) + is_r18 = db.Column(db.Boolean(), nullable=False) + tags = db.Column(db.String()) + + _idx1 = db.Index("setu_pid_img_url_idx1", "pid", "img_url", unique=True) + + @classmethod + async def add_setu_data( + cls, + local_id: int, + title: str, + author: str, + pid: int, + img_hash: str, + img_url: str, + tags: str, + ): + """ + 说明: + 添加一份色图数据 + 参数: + :param local_id: 本地存储id + :param title: 标题 + :param author: 作者 + :param pid: 图片pid + :param img_hash: 图片hash值 + :param img_url: 图片链接 + :param tags: 图片标签 + """ + if not await cls._check_exists(pid, img_url): + await cls.create( + local_id=local_id, + title=title, + author=author, + pid=pid, + img_hash=img_hash, + img_url=img_url, + is_r18=True if "R-18" in tags else False, + tags=tags, + ) + + @classmethod + async def query_image( + cls, + local_id: Optional[int] = None, + tags: Optional[List[str]] = None, + r18: int = 0, + ): + """ + 说明: + 通过tag查找色图 + 参数: + :param local_id: 本地色图 id + :param tags: tags + :param r18: 是否 r18,0:非r18 1:r18 2:混合 + """ + if local_id: + flag = True if r18 == 1 else False + return await cls.query.where( + (cls.local_id == local_id) & (cls.is_r18 == flag) + ).gino.first() + if r18 == 0: + query = await cls.query.where(cls.is_r18 == False).gino.all() + elif r18 == 1: + query = await cls.query.where(cls.is_r18 == True).gino.all() + else: + query = await cls.query.gino.all() + if tags: + query = [x for x in query if set(x.tags.split(",")) > set(tags)] + return query + + @classmethod + async def get_image_count(cls, r18: int = 0): + """ + 说明: + 查询图片数量 + """ + return len(await cls.query_image(r18=r18)) + # return await db.func.count(cls.local_id).gino.scalar() + + @classmethod + async def get_image_in_hash(cls, img_hash: str) -> "Setu": + """ + 说明: + 通过图像hash获取图像信息 + 参数: + :param img_hash: = 图像hash值 + """ + query = await cls.query.where(cls.img_hash == img_hash).gino.first() + return query + + @classmethod + async def _check_exists(cls, pid: int, img_url: str) -> bool: + """ + 说明: + 检测图片是否存在 + 参数: + :param pid: 图片pid + :param img_url: 图片链接 + """ + return bool( + await cls.query.where( + (cls.pid == pid) & (cls.img_url == img_url) + ).gino.first() + ) + + @classmethod + async def update_setu_data( + cls, + pid: int, + *, + local_id: Optional[int] = None, + title: Optional[str] = None, + author: Optional[str] = None, + img_hash: Optional[str] = None, + img_url: Optional[str] = None, + tags: Optional[str] = None, + ) -> bool: + """ + 说明: + 根据PID修改图片数据 + 参数: + :param local_id: 本地id + :param pid: 图片pid + :param title: 标题 + :param author: 作者 + :param img_hash: 图片hash值 + :param img_url: 图片链接 + :param tags: 图片标签 + """ + query = cls.query.where(cls.pid == pid).with_for_update() + image_list = await query.gino.all() + if image_list: + for image in image_list: + if local_id: + await image.update(local_id=local_id).apply() + if title: + await image.update(title=title).apply() + if author: + await image.update(author=author).apply() + if img_hash: + await image.update(img_hash=img_hash).apply() + if img_url: + await image.update(img_url=img_url).apply() + if tags: + await image.update(tags=tags).apply() + return True + return False + + @classmethod + async def get_all_setu(cls) -> List["Setu"]: + """ + 说明: + 获取所有图片对象 + """ + return await cls.query.gino.all() diff --git a/models/sigin_group_user.py b/models/sigin_group_user.py index 8b1df594..34ab0fbe 100644 --- a/models/sigin_group_user.py +++ b/models/sigin_group_user.py @@ -4,7 +4,7 @@ from services.db_context import db class SignGroupUser(db.Model): - __tablename__ = 'sign_group_users' + __tablename__ = "sign_group_users" id = db.Column(db.Integer(), primary_key=True) user_qq = db.Column(db.BigInteger(), nullable=False) @@ -13,13 +13,19 @@ class SignGroupUser(db.Model): checkin_count = db.Column(db.Integer(), nullable=False) checkin_time_last = db.Column(db.DateTime(timezone=True), nullable=False) impression = db.Column(db.Numeric(scale=3, asdecimal=False), nullable=False) - add_probability = db.Column(db.Numeric(scale=3, asdecimal=False), nullable=False, default=0) - specify_probability = db.Column(db.Numeric(scale=3, asdecimal=False), nullable=False, default=0) + add_probability = db.Column( + db.Numeric(scale=3, asdecimal=False), nullable=False, default=0 + ) + specify_probability = db.Column( + db.Numeric(scale=3, asdecimal=False), nullable=False, default=0 + ) - _idx1 = db.Index('sign_group_users_idx1', 'user_qq', 'belonging_group', unique=True) + _idx1 = db.Index("sign_group_users_idx1", "user_qq", "belonging_group", unique=True) @classmethod - async def ensure(cls, user_qq: int, belonging_group: int, for_update: bool = False) -> 'SignGroupUser': + async def ensure( + cls, user_qq: int, belonging_group: int, for_update: bool = False + ) -> "SignGroupUser": query = cls.query.where( (cls.user_qq == user_qq) & (cls.belonging_group == belonging_group) ) @@ -35,14 +41,18 @@ class SignGroupUser(db.Model): ) @classmethod - async def query_impression_all(cls, belonging_group: int) -> 'list,list': + async def get_all_impression(cls, belonging_group: int) -> "list, list": + """ + 说明: + 获取该群所有用户 id 及对应 好感度 + 参数: + :param belonging_group: 群号 + """ impression_list = [] user_qq_list = [] user_group = [] if belonging_group: - query = cls.query.where( - (cls.belonging_group == belonging_group) - ) + query = cls.query.where(cls.belonging_group == belonging_group) else: query = cls.query for user in await query.gino.all(): @@ -50,4 +60,3 @@ class SignGroupUser(db.Model): user_qq_list.append(user.user_qq) user_group.append(user.belonging_group) return user_qq_list, impression_list, user_group - diff --git a/plugins/aconfig/__init__.py b/plugins/aconfig/__init__.py index 676be9e9..2ead8e17 100644 --- a/plugins/aconfig/__init__.py +++ b/plugins/aconfig/__init__.py @@ -1,7 +1,7 @@ import random from nonebot import on_keyword import os -from utils.init_result import image +from utils.message_builder import image from configs.path_config import IMAGE_PATH from nonebot import on_command from nonebot.rule import to_me @@ -11,14 +11,14 @@ from nonebot.adapters.cqhttp.permission import GROUP from utils.utils import FreqLimiter -__plugin_name__ = '基本设置 [Hidden]' -__plugin_usage__ = '用法: 无' +__plugin_name__ = "基本设置 [Hidden]" +__plugin_usage__ = "用法: 无" _flmt = FreqLimiter(300) -config_playgame = on_keyword({'打游戏'}, permission=GROUP, priority=1, block=True) +config_playgame = on_keyword({"打游戏"}, permission=GROUP, priority=1, block=True) @config_playgame.handle() @@ -26,46 +26,33 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State): if not _flmt.check(event.group_id): return _flmt.start_cd(event.group_id) - await config_playgame.finish(image( - random.choice(os.listdir(IMAGE_PATH + "dayouxi/") - ), "dayouxi")) + await config_playgame.finish( + image(random.choice(os.listdir(IMAGE_PATH + "dayouxi/")), "dayouxi") + ) -self_introduction = on_command("自我介绍", aliases={'介绍', '你是谁', '你叫什么'}, - rule=to_me(), priority=5, block=True) +self_introduction = on_command( + "自我介绍", aliases={"介绍", "你是谁", "你叫什么"}, rule=to_me(), priority=5, block=True +) @self_introduction.handle() async def _(bot: Bot, event: MessageEvent, state: T_State): - result = "我叫绪山真寻\n" \ - "你们可以叫我真寻,小真寻,哪怕你们叫我小寻子我也能接受!\n" \ - "年龄的话我还是个**岁初中生(至少现在是)\n" \ - "身高保密!!!(也就比美波里(姐姐..(妹妹))矮一点)\n" \ - "我生日是在3月6号, 能记住的话我会很高兴的\n现在是自宅警备系的现役JC\n" \ - "最好的朋友是椛!\n"\ - + image("zhenxun") + result = ( + "我叫绪山真寻\n" + "你们可以叫我真寻,小真寻,哪怕你们叫我小寻子我也能接受!\n" + "年龄的话我还是个**岁初中生(至少现在是)\n" + "身高保密!!!(也就比美波里(姐姐..(妹妹))矮一点)\n" + "我生日是在3月6号, 能记住的话我会很高兴的\n现在是自宅警备系的现役JC\n" + "最好的朋友是椛!\n" + image("zhenxun") + ) await self_introduction.finish(result) -laopo = on_keyword({'老婆'}, rule=to_me(), priority=5, block=True) + +my_wife = on_keyword({"老婆"}, rule=to_me(), priority=5, block=True) -@laopo.handle() +@my_wife.handle() async def _(bot: Bot, event: MessageEvent, state: T_State): - await laopo.finish(image('laopo.jpg', 'other')) + await my_wife.finish(image("laopo.jpg", "other")) - -# config_robot = on_keyword({"机器人"}, permission=GROUP, priority=5, block=True) -# @config_robot.handle() -# async def _(bot: Bot, event: Event, state: T_State): -# if not _flmt.check(event.group_id) or event.group_id == 774261838: -# return -# _flmt.start_cd(event.group_id) -# await config_robot.send("小真寻才不是机器人!") -# result = "我叫绪山真寻\n" \ -# "你们可以叫我真寻,小真寻,哪怕你们叫我小寻子我也能接受!\n" \ -# "年龄的话我还是个**岁初中生(至少现在是)\n" \ -# "身高保密!!!(也就比美波里(姐姐..(妹妹))矮一点)\n" \ -# "我生日是在3月6号, 能记住的话我会很高兴的\n现在是自宅警备系的现役JC\n" \ -# "最好的朋友是椛!\n"\ -# + image("zhenxun", "") -# await config_jieshao.finish(result) diff --git a/plugins/admin_bot_manage/__init__.py b/plugins/admin_bot_manage/__init__.py index 582aab08..8841f17c 100644 --- a/plugins/admin_bot_manage/__init__.py +++ b/plugins/admin_bot_manage/__init__.py @@ -1,89 +1,11 @@ -from nonebot import on_command -from utils.utils import get_message_text, get_message_imgs, scheduler, get_bot -from nonebot.typing import T_State -from nonebot.adapters.cqhttp import Bot, GroupMessageEvent -from .data_source import set_group_status, custom_group_welcome, change_group_switch, \ - update_member_info, group_current_status -from nonebot.adapters.cqhttp.permission import GROUP -from services.log import logger -from configs.config import plugins2info_dict from nonebot.plugin import export +from .data_source import update_member_info +from .custom_welcome_message import * +from .group_notification_state import * +from .switch_rule import * +from .update_group_member_info import * +from .timing_task import * -__plugin_name__ = '自定义进群欢迎消息' - -__plugin_usage__ = '''自定义进群欢迎消息 [消息] [图片](可省略) - \t示例:自定义进群欢迎消息 欢迎新人![图片]''' export = export() export.update_member_info = update_member_info - -cmds = [] -for cmd_list in plugins2info_dict.values(): - for cmd in cmd_list['cmd']: - cmds.append(f'开启{cmd}') - cmds.append(f'关闭{cmd}') -cmds = set(cmds) - -group_status = on_command('oc_reminds', aliases={'开启早晚安', '关闭早晚安', - '开启进群欢迎', '关闭进群欢迎', - '开启每日开箱重置提醒', '关闭每日开箱重置提醒', - '开启b站转发解析', '关闭b站转发解析', - '开启epic通知', '关闭epic通知', - '开启丢人爬', '关闭丢人爬', - '开启原神黄历提醒', '关闭原神黄历提醒', - '开启全部通知', '开启所有通知', '关闭全部通知', '关闭所有通知', - '群通知状态'}, permission=GROUP, priority=1, block=True) - -switch_rule = on_command('switch_rule', aliases=cmds, permission=GROUP, priority=4, block=True) -custom_welcome = on_command('自定义进群欢迎消息', aliases={'自定义欢迎消息', '自定义群欢迎消息', '设置群欢迎消息'}, permission=GROUP, priority=5, block=True) -refresh_member_group = on_command("更新群组成员列表", aliases={"更新群组成员信息"}, permission=GROUP, priority=5, block=True) - - -@switch_rule.handle() -async def _(bot: Bot, event: GroupMessageEvent, state: T_State): - await switch_rule.send(await change_group_switch(state["_prefix"]["raw_command"].strip(), event.group_id)) - logger.info(f'USER {event.user_id} GROUP {event.group_id} 使用群功能管理命令 {state["_prefix"]["raw_command"]}') - - -@group_status.handle() -async def _(bot: Bot, event: GroupMessageEvent, state: T_State): - if state["_prefix"]["raw_command"] in ['群通知状态']: - await group_status.finish(await group_current_status(event.group_id)) - await group_status.send(await set_group_status(state["_prefix"]["raw_command"], event.group_id), - at_sender=True) - logger.info(f'USER {event.user_id} GROUP {event.group_id} 使用群通知管理命令 {state["_prefix"]["raw_command"]}') - - -@custom_welcome.handle() -async def _(bot: Bot, event: GroupMessageEvent, state: T_State): - msg = get_message_text(event.json()) - imgs = get_message_imgs(event.json()) - if not msg and not imgs: - await custom_welcome.finish('格式:自定义群欢迎消息 [文本] [图片]\n\t示例:自定义群欢迎消息 我们都是萝莉控,你呢? [图片]') - await custom_welcome.finish(await custom_group_welcome(msg, imgs, event.user_id, event.group_id), at_sender=True) - - -@refresh_member_group.handle() -async def _(bot: Bot, event: GroupMessageEvent, state: T_State): - if await update_member_info(event.group_id): - await refresh_member_group.finish("更新群员信息成功!", at_sender=True) - else: - await refresh_member_group.finish("更新群员信息失败!", at_sender=True) - - -# 自动更新群员信息 -@scheduler.scheduled_job( - 'cron', - hour=2, - minute=1, -) -async def _(): - bot = get_bot() - gl = await bot.get_group_list(self_id=bot.self_id) - gl = [g['group_id'] for g in gl] - for g in gl: - try: - await update_member_info(g) - logger.info(f'更新群组 g:{g} 成功') - except Exception as e: - logger.error(f'更新群组错误 g:{g} e:{e}') diff --git a/plugins/admin_bot_manage/custom_welcome_message.py b/plugins/admin_bot_manage/custom_welcome_message.py new file mode 100644 index 00000000..a83a047d --- /dev/null +++ b/plugins/admin_bot_manage/custom_welcome_message.py @@ -0,0 +1,37 @@ +from nonebot import on_command +from utils.utils import get_message_text, get_message_imgs +from nonebot.typing import T_State +from nonebot.adapters.cqhttp import Bot, GroupMessageEvent +from .data_source import custom_group_welcome +from nonebot.adapters.cqhttp.permission import GROUP +from services.log import logger + + +__plugin_name__ = "自定义进群欢迎消息" + +__plugin_usage__ = """ + 自定义进群欢迎消息 [消息] [图片](可省略) + 示例:自定义进群欢迎消息 欢迎新人![图片] +""" + + +custom_welcome = on_command( + "自定义进群欢迎消息", + aliases={"自定义欢迎消息", "自定义群欢迎消息", "设置群欢迎消息"}, + permission=GROUP, + priority=5, + block=True, +) + + +@custom_welcome.handle() +async def _(bot: Bot, event: GroupMessageEvent, state: T_State): + msg = get_message_text(event.json()) + imgs = get_message_imgs(event.json()) + if not msg and not imgs: + await custom_welcome.finish(__plugin_usage__) + await custom_welcome.send( + await custom_group_welcome(msg, imgs, event.user_id, event.group_id), + at_sender=True, + ) + logger.info(f"USER {event.user_id} GROUP {event.group_id} 自定义群欢迎消息:{msg}") diff --git a/plugins/admin_bot_manage/data_source.py b/plugins/admin_bot_manage/data_source.py index e0e5d389..92d54136 100644 --- a/plugins/admin_bot_manage/data_source.py +++ b/plugins/admin_bot_manage/data_source.py @@ -4,7 +4,7 @@ from configs.path_config import DATA_PATH import os import aiofiles import aiohttp -from utils.init_result import image +from utils.message_builder import image from utils.utils import get_local_proxy, get_bot from pathlib import Path from nonebot import require @@ -173,7 +173,7 @@ async def update_member_info(group_id: int) -> bool: await LevelUser.set_level(user_info['user_id'], user_info['group_id'], ADMIN_DEFAULT_AUTH) if str(user_info['user_id']) in bot.config.superusers: await LevelUser.set_level(user_info['user_id'], user_info['group_id'], 9) - user = await GroupInfoUser.select_member_info(user_info['user_id'], user_info['group_id']) + user = await GroupInfoUser.get_member_info(user_info['user_id'], user_info['group_id']) if user: if user.user_name != nickname: await user.update(user_name=nickname).apply() @@ -182,7 +182,7 @@ async def update_member_info(group_id: int) -> bool: continue join_time = datetime.strptime( time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(user_info['join_time'])), "%Y-%m-%d %H:%M:%S") - if await GroupInfoUser.insert( + if await GroupInfoUser.add_member_info( user_info['user_id'], user_info['group_id'], nickname, @@ -191,7 +191,7 @@ async def update_member_info(group_id: int) -> bool: logger.info(f"用户{user_info['user_id']} 所属{user_info['group_id']} 更新成功") else: _error_member_list.append(f"用户{user_info['user_id']} 所属{user_info['group_id']} 更新失败\n") - _del_member_list = list(set(_exist_member_list).difference(set(await GroupInfoUser.query_group_member_list(group_id)))) + _del_member_list = list(set(_exist_member_list).difference(set(await GroupInfoUser.get_group_member_id_list(group_id)))) if _del_member_list: for del_user in _del_member_list: if await GroupInfoUser.delete_member_info(del_user, group_id): diff --git a/plugins/admin_bot_manage/group_notification_state.py b/plugins/admin_bot_manage/group_notification_state.py new file mode 100644 index 00000000..c406b5ea --- /dev/null +++ b/plugins/admin_bot_manage/group_notification_state.py @@ -0,0 +1,57 @@ +from nonebot.typing import T_State +from nonebot.adapters.cqhttp import Bot, GroupMessageEvent +from .data_source import set_group_status, group_current_status +from nonebot.adapters.cqhttp.permission import GROUP +from services.log import logger +from nonebot import on_command + + +__plugin_name__ = "群通知开关" + +__plugin_usage__ = """ + 示例: + 开启早晚安 + 关闭早晚安 +""" + + +group_status = on_command( + "oc_reminds", + aliases={ + "开启早晚安", + "关闭早晚安", + "开启进群欢迎", + "关闭进群欢迎", + "开启每日开箱重置提醒", + "关闭每日开箱重置提醒", + "开启b站转发解析", + "关闭b站转发解析", + "开启epic通知", + "关闭epic通知", + "开启丢人爬", + "关闭丢人爬", + "开启原神黄历提醒", + "关闭原神黄历提醒", + "开启全部通知", + "开启所有通知", + "关闭全部通知", + "关闭所有通知", + "群通知状态", + }, + permission=GROUP, + priority=1, + block=True, +) + + +@group_status.handle() +async def _(bot: Bot, event: GroupMessageEvent, state: T_State): + if state["_prefix"]["raw_command"] in ["群通知状态"]: + await group_status.finish(await group_current_status(event.group_id)) + await group_status.send( + await set_group_status(state["_prefix"]["raw_command"], event.group_id), + at_sender=True, + ) + logger.info( + f'USER {event.user_id} GROUP {event.group_id} 使用群通知管理命令 {state["_prefix"]["raw_command"]}' + ) diff --git a/plugins/admin_bot_manage/switch_rule.py b/plugins/admin_bot_manage/switch_rule.py new file mode 100644 index 00000000..56021317 --- /dev/null +++ b/plugins/admin_bot_manage/switch_rule.py @@ -0,0 +1,41 @@ +from nonebot import on_command +from nonebot.typing import T_State +from nonebot.adapters.cqhttp import Bot, GroupMessageEvent +from .data_source import change_group_switch +from nonebot.adapters.cqhttp.permission import GROUP +from services.log import logger +from configs.config import plugins2info_dict + + +__plugin_name__ = "群功能开关" + +__plugin_usage__ = """ + 示例: + 开启色图 + 关闭色图 +""" + + +cmds = [] +for cmd_list in plugins2info_dict.values(): + for cmd in cmd_list["cmd"]: + cmds.append(f"开启{cmd}") + cmds.append(f"关闭{cmd}") +cmds = set(cmds) + + +switch_rule = on_command( + "switch_rule", aliases=cmds, permission=GROUP, priority=4, block=True +) + + +@switch_rule.handle() +async def _(bot: Bot, event: GroupMessageEvent, state: T_State): + await switch_rule.send( + await change_group_switch( + state["_prefix"]["raw_command"].strip(), event.group_id + ) + ) + logger.info( + f'USER {event.user_id} GROUP {event.group_id} 使用群功能管理命令 {state["_prefix"]["raw_command"]}' + ) diff --git a/plugins/admin_bot_manage/timing_task.py b/plugins/admin_bot_manage/timing_task.py new file mode 100644 index 00000000..edf5c345 --- /dev/null +++ b/plugins/admin_bot_manage/timing_task.py @@ -0,0 +1,38 @@ +from utils.utils import scheduler, get_bot +from .data_source import update_member_info +from services.log import logger +from models.group_info import GroupInfo + + +# 自动更新群员信息 +@scheduler.scheduled_job( + "cron", + hour=2, + minute=1, +) +async def _(): + bot = get_bot() + gl = await bot.get_group_list(self_id=bot.self_id) + gl = [g["group_id"] for g in gl] + for g in gl: + try: + await update_member_info(g) + logger.info(f"更新群组 g:{g} 成功") + except Exception as e: + logger.error(f"更新群组错误 g:{g} e:{e}") + + +# 快速更新群员信息以及管理员权限 +@scheduler.scheduled_job( + "interval", + minutes=5, +) +async def _(): + bot = get_bot() + gl = await bot.get_group_list(self_id=bot.self_id) + gl = [g["group_id"] for g in gl] + all_group = [x.group_id for x in await GroupInfo.get_all_group()] + for g in gl: + if g not in all_group: + await update_member_info(g) + logger.info(f"快速更新群信息以及权限:{g}") diff --git a/plugins/admin_bot_manage/update_group_member_info.py b/plugins/admin_bot_manage/update_group_member_info.py new file mode 100644 index 00000000..712d4c3b --- /dev/null +++ b/plugins/admin_bot_manage/update_group_member_info.py @@ -0,0 +1,27 @@ +from nonebot import on_command +from nonebot.typing import T_State +from nonebot.adapters.cqhttp import Bot, GroupMessageEvent +from .data_source import update_member_info + +__plugin_name__ = "更新群组成员列表" + +__plugin_usage__ = """ + 说明: + 更新群组成员的基本信息 + 示例: + 更新群组成员列表 +""" + +from nonebot.adapters.cqhttp.permission import GROUP + +refresh_member_group = on_command( + "更新群组成员列表", aliases={"更新群组成员信息"}, permission=GROUP, priority=5, block=True +) + + +@refresh_member_group.handle() +async def _(bot: Bot, event: GroupMessageEvent, state: T_State): + if await update_member_info(event.group_id): + await refresh_member_group.finish("更新群员信息成功!", at_sender=True) + else: + await refresh_member_group.finish("更新群员信息失败!", at_sender=True) diff --git a/plugins/admin_config.py b/plugins/admin_config.py index 858fb53d..797c97df 100644 --- a/plugins/admin_config.py +++ b/plugins/admin_config.py @@ -7,9 +7,9 @@ from models.group_member_info import GroupInfoUser from configs.config import ADMIN_DEFAULT_AUTH -__plugin_name__ = '群管理员监测' +__plugin_name__ = "群管理员监测" -__plugin_usage__ = '无' +__plugin_usage__ = "无" admin_notice = on_notice(priority=5) @@ -18,15 +18,14 @@ admin_notice = on_notice(priority=5) @admin_notice.handle() async def _(bot: Bot, event: GroupAdminNoticeEvent, state: T_State): try: - nickname = (await GroupInfoUser.select_member_info(event.user_id, event.group_id)).user_name + nickname = ( + await GroupInfoUser.get_member_info(event.user_id, event.group_id) + ).user_name except AttributeError: nickname = event.user_id - if event.sub_type == 'set': + if event.sub_type == "set": await LevelUser.set_level(event.user_id, event.group_id, ADMIN_DEFAULT_AUTH) - logger.info(f'为新晋管理员 {nickname}({event.user_id}) 添加权限等级:{ADMIN_DEFAULT_AUTH}') - elif event.sub_type == 'unset': + logger.info(f"为新晋管理员 {nickname}({event.user_id}) 添加权限等级:{ADMIN_DEFAULT_AUTH}") + elif event.sub_type == "unset": await LevelUser.delete_level(event.user_id, event.group_id) - logger.info(f'将非管理员 {nickname}({event.user_id}) 取消权限等级') - - - + logger.info(f"将非管理员 {nickname}({event.user_id}) 取消权限等级") diff --git a/plugins/admin_help/__init__.py b/plugins/admin_help/__init__.py index 5b0f73dd..8fe969a9 100644 --- a/plugins/admin_help/__init__.py +++ b/plugins/admin_help/__init__.py @@ -2,9 +2,9 @@ from nonebot import on_command from nonebot.typing import T_State from nonebot.adapters import Bot from nonebot.adapters.cqhttp import GroupMessageEvent -from utils.img_utils import CreateImg +from utils.image_utils import CreateImg from configs.path_config import IMAGE_PATH -from utils.init_result import image +from utils.message_builder import image __plugin_name__ = '管理帮助 [Hidden]' @@ -28,6 +28,7 @@ __plugin_usage__ = '''管理帮助(权限等级): ''' passive_help = '''【被动技能开关(2): + 开启/关闭早晚安 开启/关闭进群欢迎 开启/关闭每日开箱重置提醒 开启/关闭b站转发解析 @@ -41,7 +42,9 @@ admin_help = on_command("管理员帮助", aliases={"管理帮助"}, priority=5, admin_help_img = CreateImg(1000, 600, font_size=24) admin_help_img.text((10, 10), __plugin_usage__) -admin_help_img.paste(CreateImg(450, 600, font_size=24).text((0, 0), passive_help), (650, 50)) +text_img = CreateImg(450, 600, font_size=24) +text_img.text((0, 0), passive_help) +admin_help_img.paste(text_img, (650, 50)) admin_help_img.save(IMAGE_PATH + 'admin_help_img.png') diff --git a/plugins/ai/__init__.py b/plugins/ai/__init__.py index a957c681..a08a5823 100644 --- a/plugins/ai/__init__.py +++ b/plugins/ai/__init__.py @@ -1,61 +1,60 @@ -from .data_source import get_qqbot_chat_result, hello, no_result +from .data_source import get_chat_result, hello, no_result from services.log import logger from nonebot import on_message from nonebot.rule import to_me from nonebot.typing import T_State -from nonebot.adapters.cqhttp import Bot, GroupMessageEvent, PrivateMessageEvent, Message +from nonebot.adapters.cqhttp import ( + Bot, + PrivateMessageEvent, + Message, + MessageEvent, +) from utils.utils import get_message_text, get_message_imgs from models.friend_user import FriendUser from models.group_member_info import GroupInfoUser -__plugin_name__ = 'AI [Hidden]' +__plugin_name__ = "AI [Hidden]" ai = on_message(rule=to_me(), priority=8) @ai.handle() -async def _(bot: Bot, event: PrivateMessageEvent, state: T_State): +async def _(bot: Bot, event: MessageEvent, state: T_State): msg = get_message_text(event.json()) imgs = get_message_imgs(event.json()) - if str(event.get_message()).find('CQ:xml') != -1: + if "CQ:xml" in msg: return # 打招呼 - if (not msg and not imgs) or msg in ['你好啊', '你好', '在吗', '在不在', '您好', '您好啊', '你好', '在']: + if (not msg and not imgs) or msg in [ + "你好啊", + "你好", + "在吗", + "在不在", + "您好", + "您好啊", + "你好", + "在", + ]: await ai.finish(hello()) - img = imgs[0] if imgs else '' - nickname = await FriendUser.get_friend_nickname(event.user_id) + img = imgs[0] if imgs else "" + if isinstance(event, PrivateMessageEvent): + nickname = await FriendUser.get_friend_nickname(event.user_id) + else: + nickname = await GroupInfoUser.get_group_member_nickname( + event.user_id, event.group_id + ) if not nickname: - nickname = await FriendUser.get_user_name(event.user_id) - if not nickname: - nickname = "你" - result = await get_qqbot_chat_result(msg, img, event.user_id, nickname) - logger.info(f"USER {event.user_id} 问题:{msg}\n回答:{result}") + if isinstance(event, PrivateMessageEvent): + nickname = event.sender.nickname + else: + nickname = event.sender.card if event.sender.card else event.sender.nickname + result = await get_chat_result(msg, img, event.user_id, nickname) + logger.info( + f"USER {event.user_id} GROUP {event.group_id if not isinstance(event, PrivateMessageEvent) else ''} " + f"问题:{msg}\n回答:{result}" + ) if result: await ai.finish(Message(result)) else: await ai.finish(no_result()) - - -@ai.handle() -async def _(bot: Bot, event: GroupMessageEvent, state: T_State): - # if await GroupRemind.get_status(event.group_id, 'ai'): - msg = get_message_text(event.json()) - imgs = get_message_imgs(event.json()) - # 打招呼 - if not msg and not imgs: - await ai.finish(hello()) - img = imgs[0] if imgs else '' - nickname = await GroupInfoUser.get_group_member_nickname(event.user_id, event.group_id) - if not nickname: - try: - nickname = (await GroupInfoUser.select_member_info(event.user_id, event.group_id)).user_name - except AttributeError: - nickname = "你" - result = await get_qqbot_chat_result(msg, img, event.user_id, nickname) - logger.info(f"问题:{msg}\n回答:{result}") - if result: - await ai.finish(Message(result)) - else: - await ai.finish(no_result()) - diff --git a/plugins/ai/data_source.py b/plugins/ai/data_source.py index 978df78e..5fad6e2f 100644 --- a/plugins/ai/data_source.py +++ b/plugins/ai/data_source.py @@ -1,12 +1,13 @@ -from configs.config import TL_KEY, ALAPI_TOKEN +from configs.config import TL_KEY, ALAPI_TOKEN, ALAPI_AI_CHECK +from configs.path_config import IMAGE_PATH, DATA_PATH +from aiohttp.client import ClientSession +from services.log import logger +from utils.message_builder import image, face +from utils.utils import get_bot +import re import aiohttp import random import os -from configs.path_config import IMAGE_PATH, DATA_PATH -from services.log import logger -from utils.init_result import image, face -from utils.utils import get_bot -import re try: import ujson as json @@ -19,11 +20,11 @@ check_url = "https://v2.alapi.cn/api/censor/text" index = 0 -anime_data = json.load(open(DATA_PATH + 'anime.json', 'r', encoding='utf8')) +anime_data = json.load(open(DATA_PATH + "anime.json", "r", encoding="utf8")) # 图灵AI -async def get_qqbot_chat_result(text: str, img_url: str, user_id: int, user_name: str) -> str: +async def get_chat_result(text: str, img_url: str, user_id: int, nickname: str) -> str: global index if index == 5: index = 0 @@ -31,112 +32,110 @@ async def get_qqbot_chat_result(text: str, img_url: str, user_id: int, user_name keys = anime_data.keys() for key in keys: if text.find(key) != -1: - return random.choice(anime_data[key]).replace('你', user_name) + return random.choice(anime_data[key]).replace("你", nickname) + async with aiohttp.ClientSession() as sess: + rst = await tu_ling(text, img_url, user_id, sess) + if not rst: + rst = await xie_ai(text, sess) + if not rst: + return no_result() + if nickname: + if len(nickname) < 5: + if random.random() < 0.5: + nickname = "~".join(nickname) + "~" + if random.random() < 0.2: + if nickname.find("大人") == -1: + nickname += "大~人~" + rst = rst.replace("小主人", nickname).replace("小朋友", nickname) + return rst + + +# 图灵接口 +async def tu_ling(text: str, img_url: str, user_id: int, sess: ClientSession): + global index try: if text: req = { - "perception": - { - "inputText": - { - "text": text - }, - "selfInfo": - { - "location": - { - "city": "陨石坑", - "province": "火星", - "street": "第5坑位" - } - } + "perception": { + "inputText": {"text": text}, + "selfInfo": { + "location": {"city": "陨石坑", "province": "火星", "street": "第5坑位"} }, - "userInfo": - { - "apiKey": TL_KEY[index], - "userId": str(user_id) - } + }, + "userInfo": {"apiKey": TL_KEY[index], "userId": str(user_id)}, } elif img_url: req = { "reqType": 1, - "perception": - { - "inputImage": { - "url": img_url - }, - "selfInfo": - { - "location": - { - "city": "陨石坑", - "province": "火星", - "street": "第5坑位" - } - } + "perception": { + "inputImage": {"url": img_url}, + "selfInfo": { + "location": {"city": "陨石坑", "province": "火星", "street": "第5坑位"} }, - "userInfo": - { - "apiKey": TL_KEY[index], - "userId": str(user_id) - } + }, + "userInfo": {"apiKey": TL_KEY[index], "userId": str(user_id)}, } except IndexError: index = 0 - return no_result() - async with aiohttp.ClientSession() as sess: - async with sess.post(url, json=req) as response: - if response.status != 200: - return '' - resp_payload = json.loads(await response.text()) - if resp_payload['intent']: - if int(resp_payload['intent']['code']) in [4003]: - index += 1 - # 该AI很屑!!!!!!!!!!!! - async with sess.get(f'http://api.qingyunke.com/api.php?key=free&appid=0&msg={text}') as res: - data = json.loads(await res.text()) - if data['result'] == 0: - content = data['content'] - if content.find('菲菲') != -1: - content = content.replace('菲菲', list(get_bot().config.nickname)[0]) - if content.find('公众号') != -1: - content = '' - if content.find('{br}') != -1: - content = content.replace('{br}', '\n') - if content.find('提示') != -1: - content = content[:content.find('提示')] - while True: - r = re.search('{face:(.*)}', content) - if r: - id_ = r.group(1) - content = content.replace('{' + f'face:{id_}' + '}', str(face(id_))) - else: - break - return content - if resp_payload['results']: - for result in resp_payload['results']: - if result['resultType'] == 'text': - text = result['values']['text'] - if user_name: - text = text.replace('小朋友', user_name) - if len(user_name) < 5: - if random.random() < 0.5: - user_name = "~".join(user_name) + '~' - if random.random() < 0.2: - if user_name.find('大人') == -1: - user_name += '大~人~' - text = text.replace('小主人', user_name) - return text + return "" + text = "" + async with sess.post(url, json=req) as response: + if response.status != 200: + return no_result() + resp_payload = json.loads(await response.text()) + if int(resp_payload["intent"]["code"]) in [4003]: + return "" + if resp_payload["results"]: + for result in resp_payload["results"]: + if result["resultType"] == "text": + text = result["values"]["text"] + if "请求次数超过" in text: + text = "" + return text +# 屑 AI +async def xie_ai(text: str, sess: ClientSession): + async with sess.get( + f"http://api.qingyunke.com/api.php?key=free&appid=0&msg={text}" + ) as res: + content = "" + data = json.loads(await res.text()) + if data["result"] == 0: + content = data["content"] + if "菲菲" in content: + content = content.replace("菲菲", "真寻") + if "公众号" in content: + content = "" + if "{br}" in content: + content = content.replace("{br}", "\n") + if "提示" in content: + content = content[: content.find("提示")] + if "淘宝" in content: + return "" + while True: + r = re.search("{face:(.*)}", content) + if r: + id_ = r.group(1) + content = content.replace( + "{" + f"face:{id_}" + "}", str(face(int(id_))) + ) + else: + break + return content if not content and not ALAPI_AI_CHECK else await check_text(content, sess) + + +# 打招呼内容 def hello() -> str: - result = random.choice(( - "哦豁?!", - "你好!Ov<", - f"库库库,呼唤{list(get_bot().config.nickname)[0]}做什么呢", - "我在呢!", - "呼呼,叫俺干嘛" - )) + result = random.choice( + ( + "哦豁?!", + "你好!Ov<", + f"库库库,呼唤{list(get_bot().config.nickname)[0]}做什么呢", + "我在呢!", + "呼呼,叫俺干嘛", + ) + ) img = random.choice(os.listdir(IMAGE_PATH + "zai/")) if img[-4:] == ".gif": result += image(img, "zai") @@ -145,32 +144,33 @@ def hello() -> str: return result +# 没有回答时回复内容 def no_result() -> str: - return random.choice([ - '你在说啥子?', - f'纯洁的{list(get_bot().config.nickname)[0]}没听懂', - '下次再告诉你(下次一定)', - '你觉得我听懂了吗?嗯?', - '我!不!知!道!' - ]) + image( - random.choice(os.listdir(IMAGE_PATH + "noresult/") - ), "noresult") + return ( + random.choice( + [ + "你在说啥子?", + f"纯洁的{list(get_bot().config.nickname)[0]}没听懂", + "下次再告诉你(下次一定)", + "你觉得我听懂了吗?嗯?", + "我!不!知!道!", + ] + ) + + image(random.choice(os.listdir(IMAGE_PATH + "noresult/")), "noresult") + ) -async def check_text(text: str): +# 检测屑AI回复的文本是否是 *话 +async def check_text(text: str, sess: ClientSession) -> str: if not ALAPI_TOKEN: return text - params = { - 'token': ALAPI_TOKEN, - 'text': text - } - async with aiohttp.ClientSession() as session: - try: - async with session.get(url, timeout=2, params=params) as response: - data = await response.json() - if data['code'] == 200: - if data['conclusion_type'] == 2: - return no_result() - except Exception as e: - logger.error(f'检测违规文本错误...e:{e}') - return text + params = {"token": ALAPI_TOKEN, "text": text} + try: + async with sess.get(check_url, timeout=2, params=params) as response: + data = await response.json() + if data["code"] == 200: + if data["conclusion_type"] == 2: + return '' + except Exception as e: + logger.error(f"检测违规文本错误...e:{e}") + return text diff --git a/plugins/alapi/cover.py b/plugins/alapi/cover.py index c0ee425d..71914a08 100644 --- a/plugins/alapi/cover.py +++ b/plugins/alapi/cover.py @@ -2,7 +2,7 @@ from nonebot import on_command from nonebot.adapters.cqhttp import Bot, MessageEvent, Message from nonebot.typing import T_State from configs.config import ALAPI_TOKEN -from utils.init_result import image +from utils.message_builder import image from utils.utils import get_message_text from .util import get_data from services.log import logger diff --git a/plugins/auto_invite/__init__.py b/plugins/auto_invite/__init__.py index 652c9082..c8b3ed4e 100644 --- a/plugins/auto_invite/__init__.py +++ b/plugins/auto_invite/__init__.py @@ -5,23 +5,26 @@ from datetime import datetime from configs.config import AUTO_ADD_FRIEND from nonebot.adapters.cqhttp.exception import ActionFailed -__plugin_name__ = '好友群聊处理请求 [Hidden]' +__plugin_name__ = "好友群聊处理请求 [Hidden]" friend_req = on_request(priority=5) @friend_req.handle() async def _(bot: Bot, event: FriendRequestEvent, state: dict): + user = await bot.get_stranger_info(user_id=event.user_id) + nickname = user["nickname"] + await bot.send_private_msg( + user_id=int(list(bot.config.superusers)[0]), + message=f"*****一份好友申请*****\n" + f"昵称:{nickname}({event.user_id})\n" + f"自动同意:{'√' if AUTO_ADD_FRIEND else '×'}\n" + f"日期:{str(datetime.now()).split('.')[0]}\n" + f"备注:{event.comment}", + ) if AUTO_ADD_FRIEND: - nickname = '' - for user in await bot.get_friend_list(): - if user['user_id'] == event.user_id: - nickname = user['nickname'] - await FriendUser.add_friend_info(user['user_id'], user['nickname']) - break - await bot.send_private_msg(user_id=int(list(bot.config.superusers)[0]), message=f"{nickname}({event.user_id})" - f" 添加小真寻好友(已自动同意)") await bot.set_friend_add_request(flag=event.flag, approve=True) + await FriendUser.add_friend_info(user["user_id"], user["nickname"]) group_req = on_request(priority=5, block=True) @@ -29,16 +32,26 @@ group_req = on_request(priority=5, block=True) @group_req.handle() async def _(bot: Bot, event: GroupRequestEvent, state: dict): - if event.sub_type == 'invite': - nickname = await FriendUser.get_user_name(event.user_id) + if event.sub_type == "invite": if str(event.user_id) in bot.config.superusers: try: - await bot.set_group_add_request(flag=event.flag, sub_type='invite', approve=True) + await bot.set_group_add_request( + flag=event.flag, sub_type="invite", approve=True + ) except ActionFailed: pass else: - await bot.send_private_msg(user_id=int(list(bot.config.superusers)[0]), - message=f"报告..\n{nickname}({event.user_id})" - f" 希望邀请我加入 {event.group_id}\n邀请日期:{str(datetime.now()).split('.')[0]}") - await bot.send_private_msg(user_id=event.user_id, - message="想要邀请我偷偷入群嘛~~已经提醒管理员了\n等待管理员处理吧~") + nickname = await FriendUser.get_user_name(event.user_id) + await bot.send_private_msg( + user_id=int(list(bot.config.superusers)[0]), + message=f"*****一份入群申请*****\n" + f"申请人:{nickname}({event.user_id})\n" + f"群聊:{event.group_id}\n" + f"邀请日期:{str(datetime.now()).split('.')[0]}", + ) + await bot.send_private_msg( + user_id=event.user_id, + message="想要邀请我偷偷入群嘛~已经提醒真寻的管理员大人了\n" + "请确保已经群主或群管理沟通过!\n" + "等待管理员处理吧!", + ) diff --git a/plugins/ban/__init__.py b/plugins/ban/__init__.py index 1a54ac28..18c56228 100644 --- a/plugins/ban/__init__.py +++ b/plugins/ban/__init__.py @@ -2,82 +2,101 @@ from nonebot import on_command from models.ban_user import BanUser from models.level_user import LevelUser from nonebot.typing import T_State -from nonebot.adapters import Bot +from nonebot.adapters.cqhttp import Bot from nonebot.adapters.cqhttp import GroupMessageEvent from nonebot.adapters.cqhttp.permission import GROUP from utils.utils import get_message_at, get_message_text, is_number from services.log import logger -from models.group_member_info import GroupInfoUser -__plugin_name__ = 'Ban/unBan' -__plugin_usage__ = '用法:\n' \ - '(不是禁言!是针对真寻是否处理封禁用户消息)\n' \ - '封禁/解封用户 [小时] [分钟]\n' \ - '示例:.ban @djdsk\n' \ - '示例:.ban @djdsk 0 30\n' \ - '示例:.ban @sdasf 4\n' \ - '示例:.unban @sdasf' +__plugin_name__ = "Ban/unBan" +__plugin_usage__ = ( + "用法:\n" + "(不是禁言!是针对真寻是否处理封禁用户消息)\n" + "封禁/解封用户 [小时] [分钟]\n" + "示例:.ban @djdsk\n" + "示例:.ban @djdsk 0 30\n" + "示例:.ban @sdasf 4\n" + "示例:.unban @sdasf" +) -ban = on_command(".ban", aliases={'.unban', '/ban', '/unban'}, priority=5, permission=GROUP, block=True) +ban = on_command( + ".ban", + aliases={".unban", "/ban", "/unban"}, + priority=5, + permission=GROUP, + block=True, +) @ban.handle() async def _(bot: Bot, event: GroupMessageEvent, state: T_State): - # try: - result = '' - qq = int(get_message_at(event.json())[0]) + result = "" + qq = get_message_at(event.json())[0] if qq: - try: - user_name = (await GroupInfoUser.select_member_info(qq, event.group_id)).user_name - except AttributeError: - user_name = '用户' + user_name = await bot.get_group_member_info(group_id=event.group_id, user_id=qq) + user_name = user_name['card'] if user_name['card'] else user_name['nickname'] msg = get_message_text(event.json()) if msg: - msg = msg.split(" ") + msg = msg.split() if len(msg) == 2: if not is_number(msg[0].strip()) or not is_number(msg[1].strip()): - await ban.finish('参数必须是数字!', at_sender=True) + await ban.finish("参数必须是数字!", at_sender=True) time = int(msg[0]) * 60 * 60 + int(msg[1]) * 60 else: if not is_number(msg[0].strip()): - await ban.finish('参数必须是数字!', at_sender=True) + await ban.finish("参数必须是数字!", at_sender=True) time = int(msg[0]) * 60 * 60 else: time = -1 - if state["_prefix"]["raw_command"] in [".ban", '/ban']: - if await LevelUser.get_user_level(event.user_id, event.group_id) <= await \ - LevelUser.get_user_level(qq, event.group_id) and str(event.user_id) not in bot.config.superusers: - await ban.finish(f"您的权限等级比对方低或相等, {list(bot.config.nickname)[0]}不能为您使用此功能!", at_sender=True) - if await BanUser.ban(qq, await LevelUser.get_user_level(event.user_id, event.group_id), time): - logger.info(f"USER {event.user_id} GROUP {event.group_id} 将 USER {qq} 封禁 时长 {time/60} 分钟") + if state["_prefix"]["raw_command"] in [".ban", "/ban"]: + if ( + await LevelUser.get_user_level(event.user_id, event.group_id) + <= await LevelUser.get_user_level(qq, event.group_id) + and str(event.user_id) not in bot.config.superusers + ): + await ban.finish( + f"您的权限等级比对方低或相等, {list(bot.config.nickname)[0]}不能为您使用此功能!", + at_sender=True, + ) + if await BanUser.ban( + qq, await LevelUser.get_user_level(event.user_id, event.group_id), time + ): + logger.info( + f"USER {event.user_id} GROUP {event.group_id} 将 USER {qq} 封禁 时长 {time/60} 分钟" + ) result = f"已经将 {user_name} 加入{list(bot.config.nickname)[0]}的黑名单了!" if time != -1: result += f"将在 {time/60} 分钟后解封" else: time = await BanUser.check_ban_time(qq) if is_number(time): - time = abs(time) + time = abs(int(time)) if time < 60: - time = str(int(time)) + ' 秒' + time = str(time) + " 秒" else: - time = str(int(time / 60)) + ' 分钟' + time = str(int(time / 60)) + " 分钟" else: - time += ' 分钟' + time += " 分钟" result = f"{user_name} 已在黑名单!预计 {time}后解封" else: - if await BanUser.check_ban_level(qq, await LevelUser.get_user_level(event.user_id, event.group_id)) and\ - str(event.user_id) not in bot.config.superusers: - await ban.finish(f"ban掉 {user_name} 的管理员权限比您高,无法进行unban", at_sender=True) + if ( + await BanUser.check_ban_level( + qq, await LevelUser.get_user_level(event.user_id, event.group_id) + ) + and str(event.user_id) not in bot.config.superusers + ): + await ban.finish( + f"ban掉 {user_name} 的管理员权限比您高,无法进行unban", at_sender=True + ) if await BanUser.unban(qq): - logger.info(f"USER {event.user_id} GROUP {event.group_id} 将 USER {qq} 解禁") + logger.info( + f"USER {event.user_id} GROUP {event.group_id} 将 USER {qq} 解禁" + ) result = f"已经把 {user_name} 从黑名单中删除了!" else: result = f"{user_name} 不在黑名单!" else: - await ban.finish('艾特人了吗??', at_sender=True) - # except Exception as e: - # result = 'ban/unban执行出错,确定艾特人了吗?' - # logger.error(f'ban/unban执行出错 e:{e}') + await ban.finish("艾特人了吗??", at_sender=True) await ban.finish(result, at_sender=True) diff --git a/plugins/broadcast/__init__.py b/plugins/broadcast/__init__.py index 5f861bab..e899398c 100644 --- a/plugins/broadcast/__init__.py +++ b/plugins/broadcast/__init__.py @@ -6,7 +6,7 @@ import asyncio from utils.utils import get_message_text, get_message_imgs from services.log import logger from models.group_remind import GroupRemind -from utils.init_result import image +from utils.message_builder import image __plugin_name__ = "广播 [Hidden]" diff --git a/plugins/bt/__init__.py b/plugins/bt/__init__.py index df84b7a6..86431c09 100644 --- a/plugins/bt/__init__.py +++ b/plugins/bt/__init__.py @@ -10,7 +10,7 @@ from utils.utils import UserExistLimiter from asyncio.exceptions import TimeoutError from aiohttp.client_exceptions import ServerDisconnectedError -__plugin_name__ = '磁力搜索' +__plugin_name__ = "磁力搜索" __plugin_usage__ = r""" * 请各位使用后不要转发 * * 拒绝反冲斗士! * @@ -22,67 +22,66 @@ bt 钢铁侠 3 _ulmt = UserExistLimiter() -bt = on_command('bt', permission=PRIVATE, priority=5, block=True) +bt = on_command("bt", permission=PRIVATE, priority=5, block=True) @bt.args_parser async def _(bot: Bot, event: PrivateMessageEvent, state: T_State): - if str(event.get_message()) in ['取消', '算了']: + if str(event.get_message()) in ["取消", "算了"]: await bt.finish("已取消操作..", at_sender=True) msg = get_message_text(event.json()) if not msg: - await bt.reject('你想搜索什么呢?', at_sender=True) - state['keyword'] = msg - state['page'] = '1' + await bt.reject("你想搜索什么呢?", at_sender=True) + state["keyword"] = msg + state["page"] = "1" @bt.handle() async def _(bot: Bot, event: PrivateMessageEvent, state: T_State): if _ulmt.check(event.user_id): - await bt.finish('您有bt任务正在进行,请等待结束.', at_sender=True) + await bt.finish("您有bt任务正在进行,请等待结束.", at_sender=True) mp = get_message_text(event.json()) if not mp: return - mp = mp.split(' ') + mp = mp.split() if len(mp) == 2: - state['keyword'] = mp[0] - state['page'] = mp[1] + state["keyword"] = mp[0] + state["page"] = mp[1] else: - state['keyword'] = mp[0] - state['page'] = '1' + state["keyword"] = mp[0] + state["page"] = "1" -@bt.got('keyword', prompt='虚空磁力?查什么GKD') +@bt.got("keyword", prompt="虚空磁力?查什么GKD") async def _(bot: Bot, event: PrivateMessageEvent, state: T_State): _ulmt.set_True(event.user_id) - keyword = state['keyword'] - page = state['page'] - await bt.send('开始搜索....', at_sender=True) + keyword = state["keyword"] + page = state["page"] + await bt.send("开始搜索....", at_sender=True) send_flag = False try: - async for title, itype, create_time, file_size, link in get_bt_info(keyword, page): - await bt.send(f'标题:{title}\n' - f'类型:{itype}\n' - f'创建时间:{create_time}\n' - f'文件大小:{file_size}\n' - f'种子:{link}') + async for title, itype, create_time, file_size, link in get_bt_info( + keyword, page + ): + await bt.send( + f"标题:{title}\n" + f"类型:{itype}\n" + f"创建时间:{create_time}\n" + f"文件大小:{file_size}\n" + f"种子:{link}" + ) send_flag = True except TimeoutError: _ulmt.set_False(event.user_id) - await bt.finish(f'搜索 {keyword} 超时...') + await bt.finish(f"搜索 {keyword} 超时...") except ServerDisconnectedError: _ulmt.set_False(event.user_id) - await bt.finish(f'搜索 {keyword} 连接失败') + await bt.finish(f"搜索 {keyword} 连接失败") except Exception as e: _ulmt.set_False(event.user_id) - await bt.finish(f'bt 其他未知错误..') - logger.error(f'bt 错误 e:{e}') + await bt.finish(f"bt 其他未知错误..") + logger.error(f"bt 错误 e:{e}") if not send_flag: - await bt.send(f'{keyword} 未搜索到...') - logger.info(f'USER {event.user_id} BT搜索 {keyword} 第 {page} 页') + await bt.send(f"{keyword} 未搜索到...") + logger.info(f"USER {event.user_id} BT搜索 {keyword} 第 {page} 页") _ulmt.set_False(event.user_id) - - - - - diff --git a/plugins/bt/data_source.py b/plugins/bt/data_source.py index bab3c462..80a5433b 100644 --- a/plugins/bt/data_source.py +++ b/plugins/bt/data_source.py @@ -4,38 +4,45 @@ from configs.config import MAXINFO_BT from bs4 import BeautifulSoup from utils.utils import get_local_proxy import platform -if platform.system() == 'Windows': + +if platform.system() == "Windows": import asyncio + asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) -url = 'http://www.eclzz.world' +url = "http://www.eclzz.world" async def get_bt_info(keyword: str, page: str): async with aiohttp.ClientSession(headers=get_user_agent()) as session: - async with session.get(f"{url}/s/{keyword}_rel_{page}.html", proxy=get_local_proxy(), timeout=5) as response: + async with session.get( + f"{url}/s/{keyword}_rel_{page}.html", proxy=get_local_proxy(), timeout=5 + ) as response: text = await response.text() - if text.find('大约0条结果') != -1: + if text.find("大约0条结果") != -1: return - soup = BeautifulSoup(text, 'lxml') - item_lst = soup.find_all('div', {'class': 'search-item'}) + soup = BeautifulSoup(text, "lxml") + item_lst = soup.find_all("div", {"class": "search-item"}) for item in item_lst[:MAXINFO_BT]: - divs = item.find_all('div') - title = str(divs[0].find('a').text).replace('', '').replace('', '').strip() - spans = divs[2].find_all('span') + divs = item.find_all("div") + title = ( + str(divs[0].find("a").text) + .replace("", "") + .replace("", "") + .strip() + ) + spans = divs[2].find_all("span") itype = spans[0].text - create_time = spans[1].find('b').text - file_size = spans[2].find('b').text - link = await get_download_link(divs[0].find('a')['href'], session) + create_time = spans[1].find("b").text + file_size = spans[2].find("b").text + link = await get_download_link(divs[0].find("a")["href"], session) yield title, itype, create_time, file_size, link async def get_download_link(_url: str, session) -> str: - async with session.get(f"{url}{_url}", proxy=get_local_proxy(), timeout=30) as response: - soup = BeautifulSoup(await response.text(), 'lxml') - return soup.find('a', {'id': 'down-url'})['href'] - - - - + async with session.get( + f"{url}{_url}", proxy=get_local_proxy(), timeout=30 + ) as response: + soup = BeautifulSoup(await response.text(), "lxml") + return soup.find("a", {"id": "down-url"})["href"] diff --git a/plugins/check/__init__.py b/plugins/check/__init__.py index 65260683..22415f46 100644 --- a/plugins/check/__init__.py +++ b/plugins/check/__init__.py @@ -1,4 +1,3 @@ - from nonebot import on_command from .data_source import Check from nonebot.adapters.cqhttp import Bot, Event @@ -7,14 +6,16 @@ from nonebot.rule import to_me from nonebot.permission import SUPERUSER -__plugin_name__ = '自我检查 [Hidden]' +__plugin_name__ = "自我检查 [Hidden]" -__plugin_usage__ = '用法:自检' +__plugin_usage__ = "用法:自检" check = Check() -check_ = on_command('自检', aliases={'check'}, rule=to_me(), permission=SUPERUSER, block=True, priority=1) +check_ = on_command( + "自检", aliases={"check"}, rule=to_me(), permission=SUPERUSER, block=True, priority=1 +) @check_.handle() diff --git a/plugins/check/data_source.py b/plugins/check/data_source.py index 49de9365..baa0af81 100644 --- a/plugins/check/data_source.py +++ b/plugins/check/data_source.py @@ -33,35 +33,40 @@ class Check: async def check_network(self): async with aiohttp.ClientSession(headers=get_user_agent()) as session: try: - async with session.get('https://www.baidu.com/', proxy=get_local_proxy(), timeout=3) as response: + async with session.get( + "https://www.baidu.com/", proxy=get_local_proxy(), timeout=3 + ) as response: pass except (TimeoutError, ClientConnectorError) as e: - logger.warning(f'访问BaiDu失败... e: {e}') + logger.warning(f"访问BaiDu失败... e: {e}") self.baidu = 404 try: - async with session.get('https://www.google.com/', proxy=get_local_proxy(), timeout=3) as response: + async with session.get( + "https://www.google.com/", proxy=get_local_proxy(), timeout=3 + ) as response: pass except (TimeoutError, ClientConnectorError) as e: - logger.warning(f'访问Google失败... e: {e}') + logger.warning(f"访问Google失败... e: {e}") self.google = 404 def check_user(self): - rst = '' + rst = "" for user in psutil.users(): rst += f'[{user.name}] {time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(user.started))}\n' self.user = rst[:-1] async def show(self): await self.check_all() - rst = f'[Time] {str(datetime.now()).split(".")[0]}\n' \ - f'-----System-----\n' \ - f'[CPU] {self.cpu}%\n' \ - f'[Memory] {self.memory}%\n' \ - f'[Disk] {self.disk}%\n' \ - f'-----Network-----\n' \ - f'[BaiDu] {self.baidu}\n' \ - f'[Google] {self.google}\n' + rst = ( + f'[Time] {str(datetime.now()).split(".")[0]}\n' + f"-----System-----\n" + f"[CPU] {self.cpu}%\n" + f"[Memory] {self.memory}%\n" + f"[Disk] {self.disk}%\n" + f"-----Network-----\n" + f"[BaiDu] {self.baidu}\n" + f"[Google] {self.google}\n" + ) if self.user: - rst += '-----User-----\n' + self.user + rst += "-----User-----\n" + self.user return rst - diff --git a/plugins/coser/__init__.py b/plugins/coser/__init__.py index cd9a7ece..2197618c 100644 --- a/plugins/coser/__init__.py +++ b/plugins/coser/__init__.py @@ -1,39 +1,27 @@ from nonebot import on_command -from utils.utils import get_message_text from nonebot.typing import T_State -from nonebot.adapters import Bot, Event +from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent from services.log import logger -from utils.init_result import image +from utils.message_builder import image import requests -__plugin_name__ = 'coser' +__plugin_name__ = "coser" -__plugin_usage__ = '用法:发送‘coser’' +__plugin_usage__ = "用法:发送‘coser’" -coser = on_command('cos', aliases={'coser', '括丝', 'COS', 'Cos', 'cOS', 'coS'}, priority=5, block=True) +coser = on_command( + "cos", aliases={"coser", "括丝", "COS", "Cos", "cOS", "coS"}, priority=5, block=True +) -url_2 = 'http://api.rosysun.cn/cos' +url_2 = "http://api.rosysun.cn/cos" @coser.handle() -async def _(bot: Bot, event: Event, state: T_State): +async def _(bot: Bot, event: MessageEvent, state: T_State): img_url = requests.get(url_2).text await coser.send(image(img_url)) logger.info( - f"(USER {event.user_id}, GROUP {event.group_id if event.message_type != 'private' else 'private'}) 发送COSER") - - - - - - - - - - - - - - + f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'}) 发送COSER" + ) diff --git a/plugins/delete_img/__init__.py b/plugins/delete_img/__init__.py index 9567edd3..e6891b82 100644 --- a/plugins/delete_img/__init__.py +++ b/plugins/delete_img/__init__.py @@ -1,54 +1,56 @@ from configs.path_config import IMAGE_PATH, TEMP_PATH -import os -from utils.init_result import image +from utils.message_builder import image from services.log import logger from nonebot import on_command from nonebot.rule import to_me from nonebot.typing import T_State -from nonebot.adapters import Bot, Event +from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent from configs.config import IMAGE_DIR_LIST from utils.utils import is_number, cn2py +import os -__plugin_name__ = '删除图片' -__plugin_usage__ = '删除图片帮助:\n\t' \ - '1.查看列表 --> 指令: 删除图片 列表/目录\n\t' \ - '2.删除图片 图库 id, 即在相应目录下删除图片\n\t\t示例: 删除图片 色图 1 ' +__plugin_name__ = "删除图片" +__plugin_usage__ = ( + "删除图片帮助:\n\t" + "1.查看列表 --> 指令: 删除图片 列表/目录\n\t" + "2.删除图片 图库 id, 即在相应目录下删除图片\n\t\t示例: 删除图片 色图 1 " +) delete_img = on_command("删除图片", priority=5, rule=to_me(), block=True) @delete_img.args_parser -async def parse(bot: Bot, event: Event, state: T_State): - if str(event.get_message()) in ['取消', '算了']: +async def parse(bot: Bot, event: MessageEvent, state: T_State): + if str(event.get_message()) in ["取消", "算了"]: await delete_img.finish("已取消操作..", at_sender=True) - if state["_current_key"] in ['path']: + if state["_current_key"] in ["path"]: if str(event.get_message()) not in IMAGE_DIR_LIST: await delete_img.reject("此目录不正确,请重新输入目录!") state[state["_current_key"]] = str(event.get_message()) - if state["_current_key"] == 'id': + if state["_current_key"] == "id": if not is_number(str(event.get_message())): await delete_img.reject("id不正确!请重新输入数字...") state[state["_current_key"]] = str(event.get_message()) @delete_img.handle() -async def _(bot: Bot, event: Event, state: T_State): +async def _(bot: Bot, event: MessageEvent, state: T_State): raw_arg = str(event.get_message()).strip() if raw_arg: args = raw_arg.split(" ") - if args[0] in ['帮助']: + if args[0] in ["帮助"]: await delete_img.finish(__plugin_usage__) if len(args) >= 2 and args[0] in IMAGE_DIR_LIST and is_number(args[1]): - state['path'] = args[0] - state['id'] = args[1] + state["path"] = args[0] + state["id"] = args[1] @delete_img.got("path", prompt="请输入要删除的目标图库?") @delete_img.got("id", prompt="请输入要删除的图片id?") -async def arg_handle(bot: Bot, event: Event, state: T_State): - path = cn2py(state['path']) - img_id = state['id'] +async def arg_handle(bot: Bot, event: MessageEvent, state: T_State): + path = cn2py(state["path"]) + img_id = state["id"] path = IMAGE_PATH + path max_id = len(os.listdir(path)) - 1 if int(img_id) > max_id or int(img_id) < 0: @@ -71,9 +73,11 @@ async def arg_handle(bot: Bot, event: Event, state: T_State): except FileExistsError: logger.error(f"{path}{max_id}.jpg 替换 {path}{img_id}.jpg 失败 e:{e}") logger.info(f"{path}{max_id}.jpg 替换 {path}{img_id}.jpg 成功") - logger.info(f"USER {event.user_id} GROUP {event.group_id if event.message_type != 'private' else 'private'}" - f" -> id: {img_id} 删除成功") - await delete_img.finish(f"id: {img_id} 删除成功" + image("delete.jpg", TEMP_PATH), at_sender=True) + logger.info( + f"USER {event.user_id} GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'}" + f" -> id: {img_id} 删除成功" + ) + await delete_img.finish( + f"id: {img_id} 删除成功" + image("delete.jpg", TEMP_PATH), at_sender=True + ) await delete_img.finish(f"id: {img_id} 删除失败!") - - diff --git a/plugins/dialogue/__init__.py b/plugins/dialogue/__init__.py index c03d8873..3dc9c506 100644 --- a/plugins/dialogue/__init__.py +++ b/plugins/dialogue/__init__.py @@ -1,22 +1,22 @@ from nonebot import on_command from nonebot.typing import T_State -from nonebot.adapters.cqhttp import Bot, MessageEvent, Message +from nonebot.adapters.cqhttp import Bot, MessageEvent, Message, GroupMessageEvent from nonebot.permission import SUPERUSER from utils.utils import get_message_text, is_number, get_message_imgs -from utils.init_result import image +from utils.message_builder import image from services.log import logger -from utils.init_result import at +from utils.message_builder import at -__plugin_name__ = '联系管理员' -__plugin_usage__ = '用法:滴滴滴- [消息]' +__plugin_name__ = "联系管理员" +__plugin_usage__ = "用法:滴滴滴- [消息]" dialogue_data = {} dialogue = on_command("[滴滴滴]", aliases={"滴滴滴-"}, priority=1, block=True) -reply = on_command("/t", priority=1, permission=SUPERUSER, block=True) +reply = on_command("/t", priority=1, permission=SUPERUSER, block=True) @dialogue.handle() @@ -24,21 +24,41 @@ async def _(bot: Bot, event: MessageEvent, state: T_State): uid = event.user_id coffee = int(list(bot.config.superusers)[0]) text = get_message_text(event.json()) - img_msg = '' + img_msg = "" for img in get_message_imgs(event.json()): img_msg += image(img) - if not text or text in ['帮助']: + if not text or text in ["帮助"]: await dialogue.send("请发送[滴滴滴]+您要说的内容~", at_sender=True) else: - if event.get_event_name() == "message.private.friend": - group_id = 0 - else: + group_id = 0 + group_name = "None" + nickname = event.sender.nickname + if isinstance(event, GroupMessageEvent): group_id = event.group_id - await bot.send_private_msg(user_id=coffee, message=Message(f'Q{uid}@群{group_id}\n{text} {img_msg}')) - await dialogue.send(Message(f'您的话已发送至管理员!\n======\n{text}{img_msg}'), at_sender=True) + group_name = (await bot.get_group_info(group_id=event.group_id))[ + "group_name" + ] + nickname = event.sender.card if event.sender.card else event.sender.nickname + await bot.send_private_msg( + user_id=coffee, + message=Message( + f"*****一份交流报告*****\n" + f"昵称:{nickname}({uid})\n" + f"群聊:{group_name}({group_id})\n" + f"消息:{text} {img_msg}" + ), + ) + await dialogue.send( + Message(f"您的话已发送至管理员!\n======\n{text}{img_msg}"), at_sender=True + ) nickname = event.sender.nickname if event.sender.nickname else event.sender.card - dialogue_data[len(dialogue_data)] = {'nickname': nickname, 'user_id': event.user_id, - 'group_id': group_id, 'msg': text} + dialogue_data[len(dialogue_data)] = { + "nickname": nickname, + "user_id": event.user_id, + "group_id": group_id, + "group_name": group_name, + "msg": f'{text} {img_msg}', + } # print(dialogue_data) logger.info(f"Q{uid}@群{group_id} 联系管理员:{coffee} text:{text}") @@ -46,19 +66,26 @@ async def _(bot: Bot, event: MessageEvent, state: T_State): @reply.handle() async def _(bot: Bot, event: MessageEvent, state: T_State): msg = get_message_text(event.json()) - if msg in ['帮助']: - await reply.finish(f'/t [qq] [group] [text] -> 回复指定用户\n' - f'/t [qq] [text] -> 私聊用户\n' - f'/t -1 [group] -> 在某群发送消息\n' - f'/t [id] [text] -> 回复指定id的对话') + if msg in ["帮助"]: + await reply.finish( + f"/t [qq] [group] [text] -> 回复指定用户\n" + f"/t [qq] [text] -> 私聊用户\n" + f"/t -1 [group] -> 在某群发送消息\n" + f"/t [id] [text] -> 回复指定id的对话" + ) if not msg: - result = '' + result = "*****待回复消息总览*****\n" for key in dialogue_data.keys(): - result += f'{key}:{dialogue_data[key]["nickname"]}({dialogue_data[key]["user_id"]})' \ - f' @群 {dialogue_data[key]["group_id"]}\n-------\n{dialogue_data[key]["msg"]}\n-------\n' - await reply.finish(result[:-1]) - msg = msg.split(" ") - text = '' + result += ( + f'id:{key}\n' + f'\t昵称:{dialogue_data[key]["nickname"]}({dialogue_data[key]["user_id"]})\n' + f'\t群群:{dialogue_data[key]["group_name"]}({dialogue_data[key]["group_id"]})\n' + f'\t消息:{dialogue_data[key]["msg"]}' + f'\n--------------------\n' + ) + await reply.finish(Message(result[:-1])) + msg = msg.split() + text = "" group_id = 0 user_id = -1 if is_number(msg[0]): @@ -76,7 +103,7 @@ async def _(bot: Bot, event: MessageEvent, state: T_State): group_id = int(msg[1]) text = msg[2] else: - await reply.finish('群号错误...', at_sender=True) + await reply.finish("群号错误...", at_sender=True) else: user_id = int(msg[0]) if is_number(msg[1]) and len(msg[1]) > 5: @@ -86,19 +113,24 @@ async def _(bot: Bot, event: MessageEvent, state: T_State): group_id = 0 text = msg[1] else: - await reply.finish('请输入数字..', at_sender=True) + await reply.finish("第一参数,请输入数字.....", at_sender=True) for img in get_message_imgs(event.json()): text += image(img) if group_id: if user_id: - await bot.send_group_msg(group_id=group_id, message=at(user_id) + "\n管理员回复\n=======\n" + text) - await reply.finish("发送成功", at_sender=True) + await bot.send_group_msg( + group_id=group_id, message=at(user_id) + "\n管理员回复\n=======\n" + text + ) else: await bot.send_group_msg(group_id=group_id, message=text) - await reply.finish("发送成功", at_sender=True) + await reply.finish("消息发送成功...", at_sender=True) else: - if user_id in [qq['user_id'] for qq in await bot.get_friend_list()]: - await bot.send_private_msg(user_id=user_id, message="管理员回复\n=======\n" + text) + if user_id in [qq["user_id"] for qq in await bot.get_friend_list()]: + await bot.send_private_msg( + user_id=user_id, message="管理员回复\n=======\n" + text + ) await reply.finish("发送成功", at_sender=True) else: - await reply.send(f"对象不是{list(bot.config.nickname)[0]}的好友...", at_sender=True) + await reply.send( + f"对象不是{list(bot.config.nickname)[0]}的好友...", at_sender=True + ) diff --git a/plugins/draw_card/util.py b/plugins/draw_card/util.py index 7156feb1..07c267ce 100644 --- a/plugins/draw_card/util.py +++ b/plugins/draw_card/util.py @@ -10,7 +10,7 @@ from .config import path_dict from configs.path_config import IMAGE_PATH import nonebot import pypinyin -from utils.img_utils import CreateImg +from utils.image_utils import CreateImg import platform from services.log import logger import random @@ -321,5 +321,5 @@ def remove_prohibited_str(name: str): tmp += i name = tmp else: - name = name.replace('/', '') + name = name.replace('/', '\\') return name diff --git a/plugins/epic/__init__.py b/plugins/epic/__init__.py index dbce0aec..872ff952 100644 --- a/plugins/epic/__init__.py +++ b/plugins/epic/__init__.py @@ -7,9 +7,9 @@ from .data_source import get_epic_game from models.group_remind import GroupRemind from nonebot.adapters.cqhttp.exception import ActionFailed -__plugin_name__ = 'epic免费游戏提醒' +__plugin_name__ = "epic免费游戏提醒" -__plugin_usage__ = '用法:发送’epic‘' +__plugin_usage__ = "用法:发送’epic‘" epic = on_command("epic", priority=5, block=True) @@ -17,44 +17,29 @@ epic = on_command("epic", priority=5, block=True) @epic.handle() async def _(bot: Bot, event: MessageEvent, state: T_State): - # try: - try: - result = await get_epic_game() - except: - result = '网络出错了!' + result, code = await get_epic_game() await epic.send(result) logger.info( f"(USER {event.user_id}, GROUP {event.group_id if event.message_type != 'private' else 'private'})" - f" 获取epic免费游戏") - # except Exception as e: - # logger.error(f'epic 出错 e:{e}') - # await epic.finish('网络好像炸了,再试一次?', at_sender=True) + f" 获取epic免费游戏" + ) # epic免费游戏 @scheduler.scheduled_job( - 'cron', + "cron", hour=12, minute=1, ) async def _(): - # try: bot = get_bot() gl = await bot.get_group_list(self_id=bot.self_id) - gl = [g['group_id'] for g in gl] + gl = [g["group_id"] for g in gl] for g in gl: - if await GroupRemind.get_status(g, 'epic'): - result = await get_epic_game() - if result == '今天没有游戏可以白嫖了!': - return - try: - await bot.send_group_msg(group_id=g, - message=result) - except ActionFailed: - logger.error(f'{g}群 epic免费游戏推送错误') - # except Exception as e: - # logger.error(f'epic免费游戏推送错误 e:{e}') - - - - + if await GroupRemind.get_status(g, "epic"): + result, code = await get_epic_game() + if code == 200: + try: + await bot.send_group_msg(group_id=g, message=result) + except ActionFailed: + logger.error(f"{g}群 epic免费游戏推送错误") diff --git a/plugins/epic/data_source.py b/plugins/epic/data_source.py index b9f48eeb..1272407a 100644 --- a/plugins/epic/data_source.py +++ b/plugins/epic/data_source.py @@ -3,43 +3,54 @@ import aiofiles from utils.utils import get_local_proxy import feedparser import platform -from utils.init_result import image +from utils.message_builder import image from configs.path_config import IMAGE_PATH from utils.user_agent import get_user_agent -if platform.system() == 'Windows': +from asyncio.exceptions import TimeoutError +from configs.config import RSSHUBAPP + +if platform.system() == "Windows": import asyncio + asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) -url = 'https://rsshub.app/epicgames/freegames' - - -async def get_epic_game() -> str: - result = '今天没有游戏可以白嫖了!' - async with aiohttp.ClientSession(headers=get_user_agent()) as session: - async with session.get(url, proxy=get_local_proxy(), timeout=7) as response: - data = feedparser.parse(await response.text())['entries'] - if len(data) == 0: - return result - index = 0 - for item in data: - title = item['title'] - img_url = item['summary'][item['summary'].find('src="')+5: item['summary'].rfind('"')] - async with session.get(img_url, proxy=get_local_proxy(), timeout=7) as res: - async with aiofiles.open(IMAGE_PATH + f'temp/epic_{index}.jpg', 'wb') as f: - await f.write(await res.read()) - link = item['link'] - result += image(f'epic_{index}.jpg', 'temp') + f'\n【游戏】| {title}\n【链接】 | {link}\n' - index += 1 - if result != '': - result = 'epic限免游戏(速速白嫖):\n' + result - else: - result = '今天没有游戏可以白嫖了!' - return result - - - - -# print(asyncio.get_event_loop().run_until_complete(get_epic_game())) +url = f"{RSSHUBAPP}/epicgames/freegames" +async def get_epic_game() -> "str, int": + result = "今天没有游戏可以白嫖了!" + code = 999 + try: + async with aiohttp.ClientSession(headers=get_user_agent()) as session: + async with session.get(url, proxy=get_local_proxy(), timeout=7) as response: + data = feedparser.parse(await response.text())["entries"] + if len(data) == 0: + return result + index = 0 + for item in data: + title = item["title"] + img_url = item["summary"][ + item["summary"].find('src="') + 5 : item["summary"].rfind('"') + ] + async with session.get( + img_url, proxy=get_local_proxy(), timeout=7 + ) as res: + async with aiofiles.open( + IMAGE_PATH + f"temp/epic_{index}.jpg", "wb" + ) as f: + await f.write(await res.read()) + link = item["link"] + result += ( + image(f"epic_{index}.jpg", "temp") + + f"\n【游戏】| {title}\n【链接】 | {link}\n" + ) + code = 200 + index += 1 + if result != "": + result = "epic限免游戏(速速白嫖):\n" + result + else: + result = "今天没有游戏可以白嫖了!" + except TimeoutError: + return "请求超时!", code + return result, code diff --git a/plugins/fake_msg.py b/plugins/fake_msg.py index 8f95deed..78c68151 100644 --- a/plugins/fake_msg.py +++ b/plugins/fake_msg.py @@ -1,29 +1,31 @@ from nonebot.typing import T_State -from nonebot.adapters.cqhttp import Bot, MessageEvent +from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent from nonebot import on_command from utils.utils import get_message_imgs, get_message_text -from utils.init_result import share +from utils.message_builder import share from services.log import logger -__plugin_name = '假消息' +__plugin_name = "假消息" -__plugin_usage__ = '用法:\n格式:假消息 [网址] [标题] [内容](可省) [图片](可省)\n' \ - '示例:假消息 www.4399.com 我喜欢萝莉 为什么我喜欢... [图片]' +__plugin_usage__ = ( + "用法:\n格式:假消息 [网址] [标题] [内容](可省) [图片](可省)\n" + "示例:假消息 www.4399.com 我喜欢萝莉 为什么我喜欢... [图片]" +) -fake_msg = on_command('假消息', priority=5, block=True) +fake_msg = on_command("假消息", priority=5, block=True) @fake_msg.handle() async def _(bot: Bot, event: MessageEvent, state: T_State): - msg = get_message_text(event.json()).split(' ') + msg = get_message_text(event.json()).split(" ") img = get_message_imgs(event.json()) if len(msg) > 1: if len(msg) == 2: url = msg[0] title = msg[1] - content = '' + content = "" else: url = msg[0] title = msg[1] @@ -31,26 +33,13 @@ async def _(bot: Bot, event: MessageEvent, state: T_State): if img: img = img[0] else: - img = '' - if url.find('http://') == -1: - url = 'http://' + url + img = "" + if "http" not in url: + url = "http://" + url await fake_msg.send(share(url, title, content, img)) logger.info( - f"(USER {event.user_id}, GROUP {event.group_id if event.message_type != 'private' else 'private'})" - f" 构造假消息 url {url}, title {title}, content {content}") + f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" + f" 构造假消息 url {url}, title {title}, content {content}" + ) else: - await fake_msg.finish('消息格式错误:\n网址 标题 内容(可省略) 图片(可省略)') - - - - - - - - - - - - - - + await fake_msg.finish("消息格式错误:\n网址 标题 内容(可省略) 图片(可省略)") diff --git a/plugins/fudu.py b/plugins/fudu.py index 95f86e5a..d2a106c6 100644 --- a/plugins/fudu.py +++ b/plugins/fudu.py @@ -1,8 +1,8 @@ from nonebot.adapters.cqhttp.permission import GROUP from configs.path_config import IMAGE_PATH -from utils.img_utils import get_img_hash +from utils.image_utils import get_img_hash import random -from utils.init_result import image +from utils.message_builder import image from nonebot import on_message from utils.utils import get_message_text, get_message_imgs, get_local_proxy from nonebot.typing import T_State @@ -10,6 +10,7 @@ from nonebot.adapters.cqhttp import Bot, GroupMessageEvent import aiohttp import aiofiles from collections import defaultdict +from asyncio.exceptions import TimeoutError from configs.config import FUDU_PROBABILITY @@ -44,8 +45,8 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State): if event.is_tome() or state["_prefix"]["raw_command"]: return if get_message_text(event.json()): - if get_message_text(event.json()).find('@可爱的小真寻') != -1: - await fudu.finish('复制粘贴的虚空艾特?', at_sender=True) + if get_message_text(event.json()).find("@可爱的小真寻") != -1: + await fudu.finish("复制粘贴的虚空艾特?", at_sender=True) imgs = get_message_imgs(event.json()) msg = get_message_text(event.json()) if not imgs and not msg: @@ -53,8 +54,8 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State): if imgs: img_hash = await get_fudu_img_hash(imgs[0], event.group_id) else: - img_hash = '' - add_msg = msg + '|-|' + img_hash + img_hash = "" + add_msg = msg + "|-|" + img_hash if _fudulist.size(event.group_id) == 0: _fudulist.append(event.group_id, add_msg) elif _fudulist.check(event.group_id, add_msg): @@ -67,22 +68,27 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State): if random.random() < 0.2: await fudu.finish("打断施法!") if imgs and msg: - rst = msg + image(f'compare_{event.group_id}_img.jpg', 'temp') + rst = msg + image(f"compare_{event.group_id}_img.jpg", "temp") elif imgs: - rst = image(f'compare_{event.group_id}_img.jpg', 'temp') + rst = image(f"compare_{event.group_id}_img.jpg", "temp") elif msg: rst = msg else: - rst = '' + rst = "" if rst: await fudu.send(rst) _fudulist.clear(event.group_id) async def get_fudu_img_hash(url, group_id): - async with aiohttp.ClientSession() as session: - async with session.get(url, proxy=get_local_proxy(), timeout=5) as response: - async with aiofiles.open(IMAGE_PATH + f"temp/compare_{group_id}_img.jpg", 'wb') as f: - await f.write(await response.read()) - img_hash = get_img_hash(IMAGE_PATH + f"temp/compare_{group_id}_img.jpg") - return str(img_hash) + try: + async with aiohttp.ClientSession() as session: + async with session.get(url, proxy=get_local_proxy(), timeout=5) as response: + async with aiofiles.open( + IMAGE_PATH + f"temp/compare_{group_id}_img.jpg", "wb" + ) as f: + await f.write(await response.read()) + img_hash = get_img_hash(IMAGE_PATH + f"temp/compare_{group_id}_img.jpg") + return str(img_hash) + except TimeoutError: + return '' diff --git a/plugins/genshin/almanac/__init__.py b/plugins/genshin/almanac/__init__.py index 0d0cfc97..7bf55422 100644 --- a/plugins/genshin/almanac/__init__.py +++ b/plugins/genshin/almanac/__init__.py @@ -4,26 +4,27 @@ from utils.utils import get_bot, scheduler from nonebot import on_command from models.level_user import LevelUser from nonebot.typing import T_State -from nonebot.adapters.cqhttp import Bot, GroupMessageEvent -from utils.init_result import image +from nonebot.adapters.cqhttp import Bot, GroupMessageEvent, MessageEvent +from utils.message_builder import image from services.log import logger from models.group_remind import GroupRemind FILE_PATH = os.path.dirname(__file__) -almanac = on_command('原神黄历', priority=5, block=True) -reload = on_command('重载原神黄历数据', priority=5, block=True) +almanac = on_command("原神黄历", priority=5, block=True) +reload = on_command("重载原神黄历数据", priority=5, block=True) @almanac.handle() -async def _(bot: Bot, event: GroupMessageEvent, state: T_State): +async def _(bot: Bot, event: MessageEvent, state: T_State): almanac_base64 = get_almanac_base64_str() mes = image(b64=almanac_base64) + "\n ※ 黄历数据来源于 genshin.pub" await almanac.send(mes) logger.info( - f"(USER {event.user_id}, GROUP {event.group_id if event.message_type != 'private' else 'private'})" - f" 发送查看原神黄历") + f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" + f" 发送查看原神黄历" + ) @reload.handle() @@ -34,7 +35,7 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State): @scheduler.scheduled_job( - 'cron', + "cron", hour=10, minute=25, ) @@ -42,9 +43,9 @@ async def _(): # 每日提醒 bot = get_bot() gl = await bot.get_group_list(self_id=bot.self_id) - gl = [g['group_id'] for g in gl] + gl = [g["group_id"] for g in gl] almanac_base64 = get_almanac_base64_str() mes = image(b64=almanac_base64) + "\n ※ 黄历数据来源于 genshin.pub" for gid in gl: - if await GroupRemind.get_status(gid, 'almanac'): + if await GroupRemind.get_status(gid, "almanac"): await bot.send_group_msg(group_id=int(gid), message=mes) diff --git a/plugins/genshin/material_remind/__init__.py b/plugins/genshin/material_remind/__init__.py index 5bc6ab06..ca7ae13d 100644 --- a/plugins/genshin/material_remind/__init__.py +++ b/plugins/genshin/material_remind/__init__.py @@ -1,8 +1,8 @@ from nonebot import on_command, Driver from nonebot.typing import T_State from nonebot.adapters.cqhttp import Bot, MessageEvent, Message -from utils.img_utils import CreateImg -from utils.init_result import image +from utils.message_builder import image +from utils.image_utils import CreateImg from utils.browser import get_browser from configs.path_config import IMAGE_PATH import nonebot @@ -109,7 +109,6 @@ def get_background_height(weapons_imgs: List[str]) -> int: height += CreateImg(0, 0, background=weapons).size[1] last_weapon = CreateImg(0, 0, background=weapons_imgs[-1]) w, h = last_weapon.size - print(w, h) last_weapon.crop((0, 0, w, h - 10)) last_weapon.save(weapons_imgs[-1]) diff --git a/plugins/genshin/qiu_qiu_translation/__init__.py b/plugins/genshin/qiu_qiu_translation/__init__.py index fd6585ad..a8beb978 100644 --- a/plugins/genshin/qiu_qiu_translation/__init__.py +++ b/plugins/genshin/qiu_qiu_translation/__init__.py @@ -1,19 +1,17 @@ from .qiu_translation import qiu_qiu_word_translation, qiu_qiu_phrase_translation -from nonebot.adapters.cqhttp import Bot, MessageEvent +from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent from nonebot.typing import T_State from nonebot import on_command from utils.utils import get_message_text from services.log import logger -__plugin_name__ = '丘丘语翻译' +__plugin_name__ = "丘丘语翻译" -__plugin_usage__ = '用法:丘丘翻译 [消息]' +__plugin_usage__ = "用法:丘丘翻译 [消息]" qiuqiu = on_command("丘丘语翻译", aliases={"丘丘一下", "丘丘翻译"}, priority=5, block=True) -suffix = "\n※ 只能从丘丘语翻译为中文,不能反向翻译\n" \ - "※ 注意空格,不要加入任何标点符号\n" \ - "※ 翻译数据来源于 米游社论坛" +suffix = "\n※ 只能从丘丘语翻译为中文,不能反向翻译\n" "※ 注意空格,不要加入任何标点符号\n" "※ 翻译数据来源于 米游社论坛" @qiuqiu.handle() @@ -28,13 +26,6 @@ async def _(bot: Bot, event: MessageEvent, state: T_State): # print(mes) await qiuqiu.send(mes, at_sender=True) logger.info( - f"(USER {event.user_id}, GROUP {event.group_id if event.message_type != 'private' else 'private'})" - f" 发送丘丘翻译:" + txt) - - - - - - - - + f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" + f" 发送丘丘翻译:" + txt + ) diff --git a/plugins/genshin/qiu_qiu_translation/qiu_translation.py b/plugins/genshin/qiu_qiu_translation/qiu_translation.py index 74442ffb..45c236f4 100644 --- a/plugins/genshin/qiu_qiu_translation/qiu_translation.py +++ b/plugins/genshin/qiu_qiu_translation/qiu_translation.py @@ -6,7 +6,9 @@ FILE_PATH = os.path.dirname(__file__) QIU_QIU_WORD = {} QIU_QIU_PHRASE = {} -with open(os.path.join(FILE_PATH, 'qiu_qiu_dictionary.json'), 'r', encoding='UTF-8') as f: +with open( + os.path.join(FILE_PATH, "qiu_qiu_dictionary.json"), "r", encoding="UTF-8" +) as f: data = json.load(f) QIU_QIU_WORD = data["word"] QIU_QIU_PHRASE = data["phrase"] diff --git a/plugins/genshin/query_resource_points/__init__.py b/plugins/genshin/query_resource_points/__init__.py index 63e2be49..26906682 100644 --- a/plugins/genshin/query_resource_points/__init__.py +++ b/plugins/genshin/query_resource_points/__init__.py @@ -1,96 +1,92 @@ from nonebot import on_command, on_regex from nonebot.rule import to_me -from .query_resource import get_resource_map_mes, get_resource_list_mes, up_label_and_point_list +from .query_resource import get_resource_type_list, query_resource, init from utils.utils import get_message_text, scheduler -from nonebot.adapters.cqhttp import Bot, MessageEvent +from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent, Message from nonebot.typing import T_State -import os from services.log import logger import re + try: import ujson as json except ModuleNotFoundError: import json -__plugin_name__ = '原神资源查询' +__plugin_name__ = "原神资源查询" -__plugin_usage__ = '用法:\n' \ - '\t原神资源查询 [消息]\n' \ - '\t原神资源列表\n' \ - '\t[消息]在哪\n' \ - '\t哪有[消息]\n' \ - '[消息] = 资源名称' +__plugin_usage__ = ( + "用法:\n" "\t原神资源查询 [消息]\n" "\t原神资源列表\n" "\t[消息]在哪\n" "\t哪有[消息]\n" "[消息] = 资源名称" +) qr = on_command("原神资源查询", priority=5, block=True) qr_lst = on_command("原神资源列表", priority=5, block=True) -rex_qr = on_regex('.*?(在哪|在哪里|哪有|哪里有).*?', rule=to_me(), priority=5, block=True) - - -with open(os.path.dirname(__file__) + '/resource_type_id.json', 'r', encoding='utf-8') as f: - in_list = [n['name'] for n in json.load(f).values()] +rex_qr = on_regex(".*?(在哪|在哪里|哪有|哪里有).*?", rule=to_me(), priority=5, block=True) +update_info = on_command('更新原神资源信息', priority=1, block=True) @qr.handle() async def _(bot: Bot, event: MessageEvent, state: T_State): resource_name = get_message_text(event.json()) - if resource_name == "" or resource_name not in in_list: - return - - await qr.send(await get_resource_map_mes(resource_name), at_sender=True) + await qr.send('正在生成位置....') + rst = await query_resource(resource_name) + await qr.send(Message(rst), at_sender=True) logger.info( - f"(USER {event.user_id}, GROUP {event.group_id if event.message_type != 'private' else 'private'})" - f" 查询原神材料:" + resource_name) + f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" + f" 查询原神材料:" + resource_name + ) @rex_qr.handle() async def _(bot: Bot, event: MessageEvent, state: T_State): msg = get_message_text(event.json()) - if msg.find('在哪') != -1: - rs = re.search('(.*)在哪.*?', msg) - resource_name = rs.group(1) if rs else '' + if msg.find("在哪") != -1: + rs = re.search("(.*)在哪.*?", msg) + resource_name = rs.group(1) if rs else "" else: - rs = re.search('.*?(哪有|哪里有)(.*)', msg) - resource_name = rs.group(2) if rs else '' + rs = re.search(".*?(哪有|哪里有)(.*)", msg) + resource_name = rs.group(2) if rs else "" if resource_name: - msg = await get_resource_map_mes(resource_name) - if msg == '发送 原神资源列表 查看所有资源名称': - return - await rex_qr.send(msg, at_sender=True) + await qr.send('正在生成位置....') + msg = await query_resource(resource_name) + await rex_qr.send(Message(msg), at_sender=True) logger.info( - f"(USER {event.user_id}, GROUP {event.group_id if event.message_type != 'private' else 'private'})" - f" 查询原神材料:" + resource_name) + f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" + f" 查询原神材料:" + resource_name + ) @qr_lst.handle() async def _(bot: Bot, event: MessageEvent, state: T_State): - # 长条消息经常发送失败,所以只能这样了 mes_list = [] - txt = get_resource_list_mes() + txt = get_resource_type_list() txt_list = txt.split("\n") - if event.message_type == 'group': + if event.message_type == "group": for txt in txt_list: data = { "type": "node", "data": { "name": f"这里是{list(bot.config.nickname)[0]}酱", "uin": f"{bot.self_id}", - "content": txt - } + "content": txt, + }, } mes_list.append(data) - # await bot.send(ev, get_resource_list_mes(), at_sender=True) - if event.message_type == 'group': + if isinstance(event, GroupMessageEvent): await bot.send_group_forward_msg(group_id=event.group_id, messages=mes_list) else: - rst = '' + rst = "" for i in range(len(txt_list)): - rst += txt_list[i] + '\n' + rst += txt_list[i] + "\n" if i % 5 == 0: if rst: await qr_lst.send(rst) - rst = '' + rst = "" - # await qr_lst.send(Message(mes_list)) + +@update_info.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + await init(True) + await update_info.send('更新原神资源信息完成...') @scheduler.scheduled_job( @@ -100,7 +96,7 @@ async def _(bot: Bot, event: MessageEvent, state: T_State): ) async def _(): try: - await up_label_and_point_list() + await init() logger.info(f'每日更新原神材料信息成功!') except Exception as e: logger.error(f'每日更新原神材料信息错误:{e}') diff --git a/plugins/genshin/query_resource_points/map.py b/plugins/genshin/query_resource_points/map.py new file mode 100644 index 00000000..460aa850 --- /dev/null +++ b/plugins/genshin/query_resource_points/map.py @@ -0,0 +1,158 @@ +from pathlib import Path +from configs.path_config import IMAGE_PATH, TXT_PATH +from utils.image_utils import CreateImg +from typing import Tuple +from math import sqrt, pow +import random + +try: + import ujson as json +except ModuleNotFoundError: + import json + +icon_path = Path(IMAGE_PATH) / "genshin" / "genshin_icon" +map_path = Path(IMAGE_PATH) / "genshin" / "map" / "map.png" +resource_label_file = Path(TXT_PATH) / "genshin" / "resource_label_file.json" +resource_point_file = Path(TXT_PATH) / "genshin" / "resource_point_file.json" + + +class Map: + """ + 原神资源生成类 + """ + + def __init__( + self, + resource_name: str, + center_point: Tuple[int, int], + deviation: Tuple[int, int] = (25, 51), + padding: int = 100, + planning_route: bool = False, + ): + """ + 参数: + :param resource_name: 资源名称 + :param center_point: 中心点 + :param deviation: 坐标误差 + :param padding: 截图外边距 + :param planning_route: 是否规划最佳线路 + """ + self.map = CreateImg(0, 0, background=map_path) + self.resource_name = resource_name + self.center_x = center_point[0] + self.center_y = center_point[1] + self.deviation = deviation + self.padding = padding + self.planning_route = planning_route + + data = json.load(open(resource_label_file, "r", encoding="utf8")) + # 资源 id + self.resource_id = [ + data[x]["id"] + for x in data + if x != "CENTER_POINT" and data[x]["name"] == resource_name + ][0] + # 传送锚点 id + self.teleport_anchor_id = [ + data[x]["id"] + for x in data + if x != "CENTER_POINT" and data[x]["name"] == "传送锚点" + ][0] + # 神像 id + self.teleport_god_id = [ + data[x]["id"] + for x in data + if x != "CENTER_POINT" and data[x]["name"] == "七天神像" + ][0] + # 资源坐标 + data = json.load(open(resource_point_file, "r", encoding="utf8")) + self.resource_point = [ + ( + self.center_x + int(data[x]["x_pos"]), + self.center_y + int(data[x]["y_pos"]), + ) + for x in data + if x != "CENTER_POINT" and data[x]["label_id"] == self.resource_id + ] + # 传送锚点坐标 + self.teleport_anchor_point = [ + ( + self.center_x + int(data[x]["x_pos"]), + self.center_y + int(data[x]["y_pos"]), + ) + for x in data + if x != "CENTER_POINT" and data[x]["label_id"] == self.teleport_anchor_id + ] + # 神像坐标 + self.teleport_god_point = [ + ( + self.center_x + int(data[x]["x_pos"]), + self.center_y + int(data[x]["y_pos"]), + ) + for x in data + if x != "CENTER_POINT" and data[x]["label_id"] == self.teleport_god_id + ] + + # 将地图上生成资源图标 + def generate_resource_icon_in_map(self) -> int: + x_list = [x[0] for x in self.resource_point] + y_list = [x[1] for x in self.resource_point] + min_width = min(x_list) - self.padding + max_width = max(x_list) + self.padding + min_height = min(y_list) - self.padding + max_height = max(y_list) + self.padding + if self.planning_route: + self._generate_best_route() + self._generate_transfer_icon((min_width, min_height, max_width, max_height)) + for x, y in self.resource_point: + icon = self._get_icon_image(self.resource_id) + self.map.paste(icon, (x - self.deviation[0], y - self.deviation[1]), True) + 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') + return rand + + # 资源数量 + def get_resource_count(self) -> int: + return len(self.resource_point) + + # 生成传送锚点和神像 + def _generate_transfer_icon(self, box: Tuple[int, int, int, int]): + min_width, min_height, max_width, max_height = box + for points in [self.teleport_anchor_point, self.teleport_god_point]: + id_ = ( + self.teleport_anchor_id + if points == self.teleport_anchor_point + else self.teleport_god_id + ) + for x, y in points: + if min_width < x < max_width and min_height < y < max_height: + icon = self._get_icon_image(id_) + self.map.paste( + icon, (x - self.deviation[0], y - self.deviation[1]), True + ) + + # 生成最优路线(说是最优其实就是直线最短) + def _generate_best_route(self): + for x, y in self.resource_point: + min_deviation = 999999 + xy = None + for points in [ + self.resource_point, + self.teleport_anchor_point, + self.teleport_god_point, + ]: + for r_x, r_y in points: + distance = int(sqrt(pow(abs(r_x - x), 2) + pow(abs(r_y - y), 2))) + if distance < min_deviation and x != r_x and y != r_y: + min_deviation = distance + xy = (x, y, r_x, r_y) + + self.map.line(xy, (255, 0, 0), width=3) + + # 获取资源图标 + def _get_icon_image(self, id_: int) -> "CreateImg": + icon = icon_path / f"{id_}.png" + if icon.exists(): + return CreateImg(50, 50, background=icon) + return CreateImg(50, 50, background=f"{icon_path}/box.png") diff --git a/plugins/genshin/query_resource_points/query_resource.py b/plugins/genshin/query_resource_points/query_resource.py index 51df9484..2c5157de 100644 --- a/plugins/genshin/query_resource_points/query_resource.py +++ b/plugins/genshin/query_resource_points/query_resource.py @@ -1,312 +1,240 @@ -from urllib import request -from PIL import Image, ImageMath -from io import BytesIO -import json -import os -import time -import base64 -from configs.path_config import IMAGE_PATH -from utils.init_result import image +from typing import Tuple, Optional, List +from configs.path_config import IMAGE_PATH, TXT_PATH +from PIL.Image import UnidentifiedImageError +from utils.message_builder import image from services.log import logger +from .map import Map +from utils.image_utils import CreateImg import asyncio +from pathlib import Path +from asyncio.exceptions import TimeoutError +from asyncio import Semaphore +from aiohttp.client import ClientSession +from utils.user_agent import get_user_agent +from utils.image_utils import is_valid import nonebot +import aiohttp +import aiofiles +import os +try: + import ujson as json +except ModuleNotFoundError: + import json driver: nonebot.Driver = nonebot.get_driver() -LABEL_URL = 'https://api-static.mihoyo.com/common/blackboard/ys_obc/v1/map/label/tree?app_sn=ys_obc' -POINT_LIST_URL = 'https://api-static.mihoyo.com/common/blackboard/ys_obc/v1/map/point/list?map_id=2&app_sn=ys_obc' +LABEL_URL = "https://api-static.mihoyo.com/common/blackboard/ys_obc/v1/map/label/tree?app_sn=ys_obc" +POINT_LIST_URL = "https://api-static.mihoyo.com/common/blackboard/ys_obc/v1/map/point/list?map_id=2&app_sn=ys_obc" +MAP_URL = "https://api-static.mihoyo.com/common/map_user/ys_obc/v1/map/info?map_id=2&app_sn=ys_obc&lang=zh-cn" -header = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) ' \ - 'Chrome/84.0.4147.105 Safari/537.36' +icon_path = Path(IMAGE_PATH) / "genshin" / "genshin_icon" +map_path = Path(IMAGE_PATH) / "genshin" / "map" +resource_label_file = Path(TXT_PATH) / "genshin" / "resource_label_file.json" +resource_point_file = Path(TXT_PATH) / "genshin" / "resource_point_file.json" +resource_type_file = Path(TXT_PATH) / "genshin" / "resource_type_file.json" -FILE_PATH = IMAGE_PATH + 'genshin/' -if not os.path.exists(IMAGE_PATH + 'genshin/genshin_icon/'): - os.mkdir(IMAGE_PATH + 'genshin/genshin_icon/') +# 地图中心坐标 +CENTER_POINT: Optional[Tuple[int, int]] = None -MAP_PATH = None -MAP_IMAGE = None -MAP_SIZE = None - -# resource_point里记录的坐标是相对坐标,是以蒙德城的大雕像为中心的,所以图片合成时需要转换坐标 -CENTER = (3505, 1907) - -zoom = 0.75 -resource_icon_offset = (-int(150 * 0.5 * zoom), -int(150 * zoom)) +resource_name_list: List[str] = [] -@driver.on_startup -async def init(): - global MAP_SIZE, MAP_PATH, MAP_IMAGE - MAP_PATH = os.path.join(IMAGE_PATH, "genshin", "seek_god_eye", "icon", "map_icon.jpg") - MAP_IMAGE = await asyncio.get_event_loop().run_in_executor(None, Image.open, MAP_PATH) - MAP_SIZE = MAP_IMAGE.size - await up_label_and_point_list() +# 查找资源 +async def query_resource(resource_name: str) -> str: + planning_route: bool = False + if resource_name and resource_name[-2:] == "路径": + resource_name = resource_name[:-2].strip() + planning_route = True + if not resource_name or resource_name not in resource_name_list: + return f"未查找到 {resource_name} 资源,可通过 “原神资源列表” 获取全部资源名称.." + map_ = Map(resource_name, CENTER_POINT, planning_route=planning_route) + count = map_.get_resource_count() + rand = await asyncio.get_event_loop().run_in_executor(None, map_.generate_resource_icon_in_map) + return f"{image(f'genshin_map_{rand}.png', 'temp')}" \ + f"\n\n※ {resource_name} 一共找到 {count} 个位置点\n※ 数据来源于米游社wiki" -data = { - "all_resource_type": { - # 这个字典保存所有资源类型, - # "1": { - # "id": 1, - # "name": "传送点", - # "icon": "", - # "parent_id": 0, - # "depth": 1, - # "node_type": 1, - # "jump_type": 0, - # "jump_target_id": 0, - # "display_priority": 0, - # "children": [] - # }, - }, - "can_query_type_list": { - # 这个字典保存所有可以查询的资源类型名称和ID,这个字典只有名称和ID - # 上边字典里"depth": 2的类型才可以查询,"depth": 1的是1级目录,不能查询 - # "七天神像":"2" - # "风神瞳":"5" - - }, - "all_resource_point_list": [ - # 这个列表保存所有资源点的数据 - # { - # "id": 2740, - # "label_id": 68, - # "x_pos": -1789, - # "y_pos": 2628, - # "author_name": "✟紫灵心✟", - # "ctime": "2020-10-29 10:41:21", - # "display_state": 1 - # }, - ], - "date": "" # 记录上次更新"all_resource_point_list"的日期 -} - - -def up_icon_image(sublist): - # 检查是否有图标,没有图标下载保存到本地 - id = sublist["id"] - icon_url = sublist["icon"] - - icon_path = os.path.join(FILE_PATH, "genshin_icon", f"{id}.png") - - if not os.path.exists(icon_path): - schedule = request.Request(icon_url) - schedule.add_header('User-Agent', header) - with request.urlopen(schedule) as f: - icon = Image.open(f) - icon = icon.resize((150, 150)) - - box_alpha = Image.open(os.path.join(FILE_PATH, "genshin_icon", "box_alpha.png")).getchannel("A") - box = Image.open(os.path.join(FILE_PATH, "genshin_icon", "box.png")) - logger.info(f'原神资源更新 --> {sublist}') - - try: - icon_alpha = icon.getchannel("A") - icon_alpha = ImageMath.eval("convert(a*b/256, 'L')", a=icon_alpha, b=box_alpha) - except ValueError: - # 米游社的图有时候会没有alpha导致报错,这时候直接使用box_alpha当做alpha就行 - icon_alpha = box_alpha - - icon2 = Image.new("RGBA", (150, 150), "#00000000") - icon2.paste(icon, (0, -10)) - - bg = Image.new("RGBA", (150, 150), "#00000000") - bg.paste(icon2, mask=icon_alpha) - bg.paste(box, mask=box) - - with open(icon_path, "wb") as icon_file: - bg.save(icon_file) - - -async def up_label_and_point_list(): - # 更新label列表和资源点列表 - try: - if os.path.exists(os.path.dirname(__file__) + '/icon/box_alpha.png'): - os.rename(os.path.dirname(__file__) + '/icon/box_alpha.png', IMAGE_PATH + 'genshin/genshin_icon/box_alpha.png') - except: - pass - try: - if os.path.exists(os.path.dirname(__file__) + '/icon/box.png'): - os.rename(os.path.dirname(__file__) + '/icon/box.png', IMAGE_PATH + 'genshin/genshin_icon/box.png') - except: - pass - - schedule = request.Request(LABEL_URL) - schedule.add_header('User-Agent', header) - with request.urlopen(schedule, timeout=5) as f: - if f.code != 200: # 检查返回的状态码是否是200 - raise ValueError(f"资源标签列表初始化失败,错误代码{f.code}") - label_data = json.loads(f.read().decode('utf-8')) - tasks = [] - for label in label_data["data"]["tree"]: - data["all_resource_type"][str(label["id"])] = label - - for sublist in label["children"]: - data["all_resource_type"][str(sublist["id"])] = sublist - data["can_query_type_list"][sublist["name"]] = str(sublist["id"]) - tasks.append(asyncio.get_event_loop().run_in_executor(None, up_icon_image, sublist)) - # logger.info(f'原神资源更新 --> {sublist}') - - label["children"] = [] - await asyncio.gather(*tasks) - - schedule = request.Request(POINT_LIST_URL) - schedule.add_header('User-Agent', header) - with request.urlopen(schedule) as f: - if f.code != 200: # 检查返回的状态码是否是200 - raise ValueError(f"资源点列表初始化失败,错误代码{f.code}") - test = json.loads(f.read().decode('utf-8')) - data["all_resource_point_list"] = test["data"]["point_list"] - - data["date"] = time.strftime("%d") - - -# def load_resource_type_id(): -# with open(os.path.join(FILE_PATH,'resource_type_id.json'), 'r', encoding='UTF-8') as f: -# json_data = json.load(f) -# for id in json_data.keys(): -# data["all_resource_type"][id] = json_data[id] -# if json_data[id]["depth"] != 1: -# data["can_query_type_list"][json_data[id]["name"]] = id - - -# 初始化 -# load_resource_type_id() - - -class Resource_map(object): - - def __init__(self, resource_name): - self.resource_id = str(data["can_query_type_list"][resource_name]) - - # 地图要要裁切的左上角和右下角坐标 - # 这里初始化为地图的大小 - self.x_start = MAP_SIZE[0] - self.y_start = MAP_SIZE[1] - self.x_end = 0 - self.y_end = 0 - - self.map_image = MAP_IMAGE.copy() - - self.resource_icon = Image.open(self.get_icon_path()) - self.resource_icon = self.resource_icon.resize((int(150 * zoom), int(150 * zoom))) - - self.resource_xy_list = self.get_resource_point_list() - - def get_icon_path(self): - # 检查有没有图标,有返回正确图标,没有返回默认图标 - icon_path = os.path.join(FILE_PATH, "genshin_icon", f"{self.resource_id}.png") - - if os.path.exists(icon_path): - return icon_path - else: - return os.path.join(FILE_PATH, "genshin_icon", "0.png") - - def get_resource_point_list(self): - temp_list = [] - for resource_point in data["all_resource_point_list"]: - if str(resource_point["label_id"]) == self.resource_id: - # 获取xy坐标,然后加上中心点的坐标完成坐标转换 - x = resource_point["x_pos"] + CENTER[0] - y = resource_point["y_pos"] + CENTER[1] - temp_list.append((int(x), int(y))) - return temp_list - - def paste(self): - for x, y in self.resource_xy_list: - # 把资源图片贴到地图上 - self.map_image.paste(self.resource_icon, (x + resource_icon_offset[0], y + resource_icon_offset[1]), - self.resource_icon) - - # 找出4个方向最远的坐标,用于后边裁切 - self.x_start = min(x, self.x_start) - self.y_start = min(y, self.y_start) - self.x_end = max(x, self.x_end) - self.y_end = max(y, self.y_end) - - def crop(self): - - # 先把4个方向扩展150像素防止把资源图标裁掉 - self.x_start -= 150 - self.y_start -= 150 - self.x_end += 150 - self.y_end += 150 - - # 如果图片裁切的太小会看不出资源的位置在哪,检查图片裁切的长和宽看够不够1000,不到1000的按1000裁切 - if (self.x_end - self.x_start) < 1000: - center = int((self.x_end + self.x_start) / 2) - self.x_start = center - 500 - self.x_end = center + 500 - if (self.y_end - self.y_start) < 1000: - center = int((self.y_end + self.y_start) / 2) - self.y_start = center - 500 - self.y_end = center + 500 - - self.map_image = self.map_image.crop((self.x_start, self.y_start, self.x_end, self.y_end)) - - def get_cq_cod(self): - - if not self.resource_xy_list: - return "没有这个资源的信息" - - self.paste() - - self.crop() - - bio = BytesIO() - self.map_image.save(bio, format='JPEG') - base64_str = 'base64://' + base64.b64encode(bio.getvalue()).decode() - - return image(b64=base64_str) - - def get_resource_count(self): - return len(self.resource_xy_list) - - -async def get_resource_map_mes(name): - if data["date"] != time.strftime("%d"): - await up_label_and_point_list() - - if not (name in data["can_query_type_list"]): - return f"没有 {name} 这种资源。\n发送 原神资源列表 查看所有资源名称" - - map = Resource_map(name) - count = map.get_resource_count() - - if not count: - return f"没有找到 {name} 资源的位置,可能米游社wiki还没更新。" - - mes = f"资源 {name} 的位置如下\n" - mes += map.get_cq_cod() - - mes += f"\n\n※ {name} 一共找到 {count} 个位置点\n※ 数据来源于米游社wiki" - return mes - - -def get_resource_list_mes(): +# 原神资源列表 +def get_resource_type_list(): + with open(resource_type_file, "r", encoding="utf8") as f: + data = json.load(f) temp = {} - - for id in data["all_resource_type"].keys(): - # 先找1级目录 - if data["all_resource_type"][id]["depth"] == 1: - temp[id] = [] - - for id in data["all_resource_type"].keys(): - # 再找2级目录 - if data["all_resource_type"][id]["depth"] == 2: - temp[str(data["all_resource_type"][id]["parent_id"])].append(id) + for id_ in data.keys(): + temp[data[id_]["name"]] = [] + for x in data[id_]["children"]: + temp[data[id_]["name"]].append(x["name"]) mes = "当前资源列表如下:\n" - for resource_type_id in temp.keys(): - - if resource_type_id in ["1", "12", "50", "51", "95", "131"]: - # 在游戏里能查到的数据这里就不列举了,不然消息太长了 - continue - - mes += f"{data['all_resource_type'][resource_type_id]['name']}:" - for resource_id in temp[resource_type_id]: - mes += f"{data['all_resource_type'][resource_id]['name']}," - mes += "\n" - + for resource_type in temp.keys(): + mes += f"{resource_type}:{','.join(temp[resource_type])}\n" return mes + + +@driver.on_startup +async def init(flag: bool = False): + global CENTER_POINT, resource_name_list + semaphore = asyncio.Semaphore(10) + async with aiohttp.ClientSession(headers=get_user_agent()) as session: + await download_map_init(session, semaphore, flag) + await download_resource_data(session, semaphore) + await download_resource_type(session) + if not CENTER_POINT: + CENTER_POINT = json.load(open(resource_label_file, "r", encoding="utf8"))[ + "CENTER_POINT" + ] + with open(resource_type_file, "r", encoding="utf8") as f: + data = json.load(f) + for id_ in data: + for x in data[id_]["children"]: + resource_name_list.append(x["name"]) + + +# 图标及位置资源 +async def download_resource_data(session: ClientSession, semaphore: Semaphore): + icon_path.mkdir(parents=True, exist_ok=True) + resource_label_file.parent.mkdir(parents=True, exist_ok=True) + try: + async with session.get(POINT_LIST_URL, timeout=5) as response: + if response.status == 200: + data = await response.json() + if data["message"] == "OK": + data = data["data"] + for lst in ["label_list", "point_list"]: + resource_data = {"CENTER_POINT": CENTER_POINT} + tasks = [] + file = ( + resource_label_file + if lst == "label_list" + else resource_point_file + ) + for x in data[lst]: + id_ = x["id"] + if lst == "label_list": + img_url = x["icon"] + tasks.append( + asyncio.ensure_future( + download_image( + img_url, + f"{icon_path}/{id_}.png", + session, + semaphore, + True, + ) + ) + ) + resource_data[id_] = x + await asyncio.gather(*tasks) + with open(file, "w", encoding="utf8") as f: + json.dump(resource_data, f, ensure_ascii=False, indent=4) + else: + logger.warning(f'获取原神资源失败 msg: {data["message"]}') + else: + logger.warning(f"获取原神资源失败 code:{response.status}") + except TimeoutError: + logger.warning("获取原神资源数据超时...已再次尝试...") + await download_resource_data(session, semaphore) + + +# 下载原神地图并拼图 +async def download_map_init(session: ClientSession, semaphore: Semaphore, flag: bool = False): + global CENTER_POINT + map_path.mkdir(exist_ok=True, parents=True) + async with session.get(MAP_URL, timeout=5) as response: + if response.status == 200: + data = await response.json() + if data["message"] == "OK": + data = json.loads(data["data"]["info"]["detail"]) + CENTER_POINT = (data["origin"][0], data["origin"][1]) + # padding_w, padding_h = data['padding'] + data = data["slices"] + idx = 0 + for x in data: + idj = 0 + for j in x: + await download_image( + j["url"], + f"{map_path}/{idx}_{idj}.png", + session, + semaphore, + force_flag=flag + ) + idj += 1 + idx += 1 + map_width, map_height = CreateImg( + 0, 0, background=f"{map_path}/0_0.png" + ).size + lens = len([x for x in os.listdir(f"{map_path}") if x.startswith("0")]) + background_image = CreateImg( + map_width * lens, map_height * lens, map_width, map_height + ) + for i in range(idx): + for j in range(idj): + x = CreateImg(0, 0, background=f"{map_path}/{i}_{j}.png") + background_image.paste(x) + background_image.save(f"{map_path}/map.png") + else: + logger.warning(f'获取原神地图失败 msg: {data["message"]}') + else: + logger.warning(f"获取原神地图失败 code:{response.status}") + + +# 下载资源类型数据 +async def download_resource_type(session: ClientSession): + resource_type_file.parent.mkdir(parents=True, exist_ok=True) + async with session.get(LABEL_URL, timeout=5) as response: + if response.status == 200: + data = await response.json() + if data["message"] == "OK": + data = data["data"]["tree"] + resource_data = {} + for x in data: + id_ = x["id"] + resource_data[id_] = x + with open(resource_type_file, "w", encoding="utf8") as f: + json.dump(resource_data, f, ensure_ascii=False, indent=4) + logger.info(f"更新原神资源类型成功...") + else: + logger.warning(f'获取原神资源类型失败 msg: {data["message"]}') + else: + logger.warning(f"获取原神资源类型失败 code:{response.status}") + + +# 初始化资源图标 +def gen_icon(icon: str): + A = CreateImg(0, 0, background=f"{icon_path}/box.png") + B = CreateImg(0, 0, background=f"{icon_path}/box_alpha.png") + icon_ = icon_path / f"{icon}" + icon_img = CreateImg(115, 115, background=icon_) + icon_img.circle() + B.paste(icon_img, (17, 10), True) + B.paste(A, alpha=True) + B.save(icon) + logger.info(f"生成图片成功 file:{str(icon)}") + + +# 下载图片 +async def download_image( + img_url: str, + path: str, + session: ClientSession, + semaphore: Semaphore, + gen_flag: bool = False, + force_flag: bool = False, +): + async with semaphore: + try: + if not os.path.exists(path) or not is_valid or force_flag: + async with session.get(img_url, timeout=5) as response: + async with aiofiles.open(path, "wb") as f: + await f.write(await response.read()) + logger.info(f"下载原神资源图标:{img_url}") + if gen_flag: + gen_icon(path) + except TimeoutError: + logger.warning("下载原神资源图片超时...已再次尝试...") + await download_image(img_url, path, session, semaphore, gen_flag) + except UnidentifiedImageError: + logger.warning(f"原神图片打开错误..已删除,等待下次更新... file: {path}") + if os.path.exists(path): + os.remove(path) diff --git a/plugins/genshin/query_resource_points/resource_type_id.json b/plugins/genshin/query_resource_points/resource_type_id.json deleted file mode 100644 index 9459f34b..00000000 --- a/plugins/genshin/query_resource_points/resource_type_id.json +++ /dev/null @@ -1,926 +0,0 @@ -{ - "1": { - "id": 1, - "name": "传送点", - "icon": "", - "parent_id": 0, - "depth": 1, - "node_type": 1, - "jump_type": 0, - "jump_target_id": 0, - "display_priority": 0, - "children": [] - }, - "2": { - "id": 2, - "name": "七天神像", - "icon": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/08/75276545/c59585d1fabc9c22ad3fcf94e1622aa8_357413506633071859.png", - "parent_id": 1, - "depth": 2, - "node_type": 2, - "jump_type": 0, - "jump_target_id": 0, - "display_priority": 0, - "children": [] - }, - "3": { - "id": 3, - "name": "传送锚点", - "icon": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/08/75276545/0cc42d15134cbb724304050fd0bbcaac_8799482478853097434.png", - "parent_id": 1, - "depth": 2, - "node_type": 2, - "jump_type": 0, - "jump_target_id": 0, - "display_priority": 0, - "children": [] - }, - "4": { - "id": 4, - "name": "神瞳", - "icon": "", - "parent_id": 0, - "depth": 1, - "node_type": 1, - "jump_type": 0, - "jump_target_id": 0, - "display_priority": 0, - "children": [] - }, - "5": { - "id": 5, - "name": "风神瞳", - "icon": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/08/75276545/4b568dcdea1c699456464c611ce87e4f_3484482126589511603.png", - "parent_id": 4, - "depth": 2, - "node_type": 2, - "jump_type": 0, - "jump_target_id": 0, - "display_priority": 0, - "children": [] - }, - "6": { - "id": 6, - "name": "岩神瞳", - "icon": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/08/75276545/f4409292be83f81d0c41c350a91ebac1_355618004292502793.png", - "parent_id": 4, - "depth": 2, - "node_type": 2, - "jump_type": 0, - "jump_target_id": 0, - "display_priority": 0, - "children": [] - }, - "7": { - "id": 7, - "name": "地灵龛", - "icon": "", - "parent_id": 0, - "depth": 1, - "node_type": 1, - "jump_type": 0, - "jump_target_id": 0, - "display_priority": 0, - "children": [] - }, - "8": { - "id": 8, - "name": "蒙德地灵龛", - "icon": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/08/75276545/a9f458cf7ba149ec659aabb581050a0b_3339433578214432669.png", - "parent_id": 7, - "depth": 2, - "node_type": 2, - "jump_type": 0, - "jump_target_id": 0, - "display_priority": 0, - "children": [] - }, - "9": { - "id": 9, - "name": "璃月地灵龛", - "icon": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/08/75276545/a9f458cf7ba149ec659aabb581050a0b_3479171218459475297.png", - "parent_id": 7, - "depth": 2, - "node_type": 2, - "jump_type": 0, - "jump_target_id": 0, - "display_priority": 0, - "children": [] - }, - "10": { - "id": 10, - "name": "区域特产", - "icon": "", - "parent_id": 0, - "depth": 1, - "node_type": 1, - "jump_type": 0, - "jump_target_id": 0, - "display_priority": 0, - "children": [] - }, - "29": { - "id": 29, - "name": "落落莓", - "icon": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/08/75276545/11e1861e2b94d1b1e132d61c0f7c3948_5882174105995719914.png", - "parent_id": 10, - "depth": 2, - "node_type": 2, - "jump_type": 0, - "jump_target_id": 0, - "display_priority": 0, - "children": [] - }, - "30": { - "id": 30, - "name": "绝云椒椒", - "icon": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/08/75276545/3184e356629e8b071a878063d1c0df94_5172487424247849295.png", - "parent_id": 10, - "depth": 2, - "node_type": 2, - "jump_type": 0, - "jump_target_id": 0, - "display_priority": 0, - "children": [] - }, - "31": { - "id": 31, - "name": "嘟嘟莲", - "icon": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/08/75276545/07a2252a60301893294ef377e563f0e7_7014418407781853378.png", - "parent_id": 10, - "depth": 2, - "node_type": 2, - "jump_type": 0, - "jump_target_id": 0, - "display_priority": 0, - "children": [] - }, - "32": { - "id": 32, - "name": "清心", - "icon": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/08/75276545/0de9385a957ef0cb981a5ed6a6984f57_7797138076139828289.png", - "parent_id": 10, - "depth": 2, - "node_type": 2, - "jump_type": 0, - "jump_target_id": 0, - "display_priority": 0, - "children": [] - }, - "33": { - "id": 33, - "name": "小灯草", - "icon": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/08/75276545/348fe1164e9330226b225c011aea4d5a_2068903308538485227.png", - "parent_id": 10, - "depth": 2, - "node_type": 2, - "jump_type": 0, - "jump_target_id": 0, - "display_priority": 0, - "children": [] - }, - "34": { - "id": 34, - "name": "琉璃袋", - "icon": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/08/75276545/77dc4532fe938a24c4cc714d77d48b0d_6612669651078791213.png", - "parent_id": 10, - "depth": 2, - "node_type": 2, - "jump_type": 0, - "jump_target_id": 0, - "display_priority": 0, - "children": [] - }, - "35": { - "id": 35, - "name": "塞西莉亚花", - "icon": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/08/75276545/e55cfc43f5e483362e9ddf433653d326_9031812646608361297.png", - "parent_id": 10, - "depth": 2, - "node_type": 2, - "jump_type": 0, - "jump_target_id": 0, - "display_priority": 0, - "children": [] - }, - "36": { - "id": 36, - "name": "霓裳花", - "icon": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/08/75276545/e22d205c11b39b9b6984a4ec8adefeb2_2210397206297609865.png", - "parent_id": 10, - "depth": 2, - "node_type": 2, - "jump_type": 0, - "jump_target_id": 0, - "display_priority": 0, - "children": [] - }, - "37": { - "id": 37, - "name": "蒲公英籽", - "icon": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/08/75276545/a348a4302f58f499bdf89025d2cb2cdb_2244957478004520779.png", - "parent_id": 10, - "depth": 2, - "node_type": 2, - "jump_type": 0, - "jump_target_id": 0, - "display_priority": 0, - "children": [] - }, - "38": { - "id": 38, - "name": "琉璃百合", - "icon": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/08/75276545/dfe6d92e18060cd724ebadd73ba084c6_7141991283452930467.png", - "parent_id": 10, - "depth": 2, - "node_type": 2, - "jump_type": 0, - "jump_target_id": 0, - "display_priority": 0, - "children": [] - }, - "39": { - "id": 39, - "name": "慕风蘑菇", - "icon": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/08/75276545/d0f4c83add3dc326ffc92b5fedb6f82c_3118392491847470850.png", - "parent_id": 10, - "depth": 2, - "node_type": 2, - "jump_type": 0, - "jump_target_id": 0, - "display_priority": 0, - "children": [] - }, - "40": { - "id": 40, - "name": "石珀", - "icon": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/08/75276545/5789d9dc9c8840f21f9afc2437e9026e_8487232206600716291.png", - "parent_id": 10, - "depth": 2, - "node_type": 2, - "jump_type": 0, - "jump_target_id": 0, - "display_priority": 0, - "children": [] - }, - "41": { - "id": 41, - "name": "钩钩果", - "icon": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/08/75276545/7dedeee543b94525f0812a18eb09b7d7_4938366361261655916.png", - "parent_id": 10, - "depth": 2, - "node_type": 2, - "jump_type": 0, - "jump_target_id": 0, - "display_priority": 0, - "children": [] - }, - "42": { - "id": 42, - "name": "夜泊石", - "icon": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/08/75276545/51adcbda83a2f2b4422db9ad4908245b_7898256673375056193.png", - "parent_id": 10, - "depth": 2, - "node_type": 2, - "jump_type": 0, - "jump_target_id": 0, - "display_priority": 0, - "children": [] - }, - "43": { - "id": 43, - "name": "风车菊", - "icon": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/08/75276545/387684f5c9a891135e05f95192b6dc21_2171784899125537458.png", - "parent_id": 10, - "depth": 2, - "node_type": 2, - "jump_type": 0, - "jump_target_id": 0, - "display_priority": 0, - "children": [] - }, - "11": { - "id": 11, - "name": "矿物", - "icon": "", - "parent_id": 0, - "depth": 1, - "node_type": 1, - "jump_type": 0, - "jump_target_id": 0, - "display_priority": 0, - "children": [] - }, - "15": { - "id": 15, - "name": "白铁块", - "icon": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/08/75276545/4d0e329b0458a918d376bbbabd4415cd_9079005216548585535.png", - "parent_id": 11, - "depth": 2, - "node_type": 2, - "jump_type": 0, - "jump_target_id": 0, - "display_priority": 0, - "children": [] - }, - "16": { - "id": 16, - "name": "水晶块", - "icon": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/08/75276545/ceede1f6fa9a3e53986b5ac63e2aacb9_3251440935681461856.png", - "parent_id": 11, - "depth": 2, - "node_type": 2, - "jump_type": 0, - "jump_target_id": 0, - "display_priority": 0, - "children": [] - }, - "12": { - "id": 12, - "name": "怪物(精英)", - "icon": "", - "parent_id": 0, - "depth": 1, - "node_type": 1, - "jump_type": 0, - "jump_target_id": 0, - "display_priority": 0, - "children": [] - }, - "19": { - "id": 19, - "name": "火斧丘丘暴徒", - "icon": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/08/75276545/cef1444b97c8f49cd8cd8aa10e5448ae_1229715974301029440.png", - "parent_id": 12, - "depth": 2, - "node_type": 2, - "jump_type": 0, - "jump_target_id": 0, - "display_priority": 0, - "children": [] - }, - "20": { - "id": 20, - "name": "木盾丘丘暴徒", - "icon": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/08/75276545/cef1444b97c8f49cd8cd8aa10e5448ae_2049522587981126653.png", - "parent_id": 12, - "depth": 2, - "node_type": 2, - "jump_type": 0, - "jump_target_id": 0, - "display_priority": 0, - "children": [] - }, - "21": { - "id": 21, - "name": "岩盾丘丘暴徒", - "icon": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/08/75276545/cef1444b97c8f49cd8cd8aa10e5448ae_4383986804076887911.png", - "parent_id": 12, - "depth": 2, - "node_type": 2, - "jump_type": 0, - "jump_target_id": 0, - "display_priority": 0, - "children": [] - }, - "22": { - "id": 22, - "name": "火深渊法师", - "icon": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/08/75276545/3c287ea61919d856723ba802fd91677f_2936850964636137610.png", - "parent_id": 12, - "depth": 2, - "node_type": 2, - "jump_type": 0, - "jump_target_id": 0, - "display_priority": 0, - "children": [] - }, - "23": { - "id": 23, - "name": "水深渊法师", - "icon": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/08/75276545/5d59b593551bb62506096c76bd55ab2a_5561999899365928315.png", - "parent_id": 12, - "depth": 2, - "node_type": 2, - "jump_type": 0, - "jump_target_id": 0, - "display_priority": 0, - "children": [] - }, - "24": { - "id": 24, - "name": "冰深渊法师", - "icon": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/08/75276545/4b9b07114f30df8715f41835d3215add_6327856690043584281.png", - "parent_id": 12, - "depth": 2, - "node_type": 2, - "jump_type": 0, - "jump_target_id": 0, - "display_priority": 0, - "children": [] - }, - "25": { - "id": 25, - "name": "愚人众 债务处理人", - "icon": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/08/75276545/359e5697e4609a7f34714bfa65cce97e_7545179401283346969.png", - "parent_id": 12, - "depth": 2, - "node_type": 2, - "jump_type": 0, - "jump_target_id": 0, - "display_priority": 0, - "children": [] - }, - "26": { - "id": 26, - "name": "愚人众 雷萤术士", - "icon": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/08/75276545/a73112218e1329ae4728a096554258d7_4082505538504798279.png", - "parent_id": 12, - "depth": 2, - "node_type": 2, - "jump_type": 0, - "jump_target_id": 0, - "display_priority": 0, - "children": [] - }, - "27": { - "id": 27, - "name": "遗迹守卫", - "icon": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/08/75276545/86ddf140b786c6a2a73b038e341403a7_6217455819326456307.png", - "parent_id": 12, - "depth": 2, - "node_type": 2, - "jump_type": 0, - "jump_target_id": 0, - "display_priority": 0, - "children": [] - }, - "28": { - "id": 28, - "name": "遗迹猎者", - "icon": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/08/75276545/99ce18638304f8169918c85436d0585c_5415189466598569723.png", - "parent_id": 12, - "depth": 2, - "node_type": 2, - "jump_type": 0, - "jump_target_id": 0, - "display_priority": 0, - "children": [] - }, - "47": { - "id": 47, - "name": "冰霜骗骗花", - "icon": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/14/75276545/54904a37ab2165e5b8ad91c7ee805061_2703681786862158841.png", - "parent_id": 12, - "depth": 2, - "node_type": 2, - "jump_type": 0, - "jump_target_id": 0, - "display_priority": 0, - "children": [] - }, - "48": { - "id": 48, - "name": "炽热骗骗花", - "icon": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/14/75276545/1848c68a09d54637721529abb35b22a1_7882961499456527312.png", - "parent_id": 12, - "depth": 2, - "node_type": 2, - "jump_type": 0, - "jump_target_id": 0, - "display_priority": 0, - "children": [] - }, - "49": { - "id": 49, - "name": "幼岩龙蜥", - "icon": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/14/75276545/83d1ef1dbb3b6d489c0b150886597cbc_5647365818975751099.png", - "parent_id": 12, - "depth": 2, - "node_type": 2, - "jump_type": 0, - "jump_target_id": 0, - "display_priority": 0, - "children": [] - }, - "53": { - "id": 53, - "name": "丘丘岩盔王", - "icon": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/17/75276545/a6e1a33b8f4bd4c8a091f69ead200948_2480904266424771100.png", - "parent_id": 12, - "depth": 2, - "node_type": 2, - "jump_type": 0, - "jump_target_id": 0, - "display_priority": 0, - "children": [] - }, - "54": { - "id": 54, - "name": "愚人众先遣队", - "icon": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/17/75276545/8dd741d0f58dc6abef2f9ee6d278a062_7759801496426769012.png", - "parent_id": 12, - "depth": 2, - "node_type": 2, - "jump_type": 0, - "jump_target_id": 0, - "display_priority": 0, - "children": [] - }, - "13": { - "id": 13, - "name": "宝箱", - "icon": "", - "parent_id": 0, - "depth": 1, - "node_type": 1, - "jump_type": 0, - "jump_target_id": 0, - "display_priority": 0, - "children": [] - }, - "17": { - "id": 17, - "name": "普通的宝箱", - "icon": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/08/75276545/35cf41aad7620ce6d5dc516defb967f7_7806440070871726330.png", - "parent_id": 13, - "depth": 2, - "node_type": 2, - "jump_type": 0, - "jump_target_id": 0, - "display_priority": 0, - "children": [] - }, - "44": { - "id": 44, - "name": "精致的宝箱", - "icon": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/14/75276545/35cf41aad7620ce6d5dc516defb967f7_6214340810257945197.png", - "parent_id": 13, - "depth": 2, - "node_type": 2, - "jump_type": 0, - "jump_target_id": 0, - "display_priority": 0, - "children": [] - }, - "45": { - "id": 45, - "name": "珍贵的宝箱", - "icon": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/14/75276545/44a7de58782f36f15bec044c1069da76_7523256410581248233.png", - "parent_id": 13, - "depth": 2, - "node_type": 2, - "jump_type": 0, - "jump_target_id": 0, - "display_priority": 0, - "children": [] - }, - "46": { - "id": 46, - "name": "华丽的宝箱", - "icon": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/14/75276545/35cf41aad7620ce6d5dc516defb967f7_3987566332689612662.png", - "parent_id": 13, - "depth": 2, - "node_type": 2, - "jump_type": 0, - "jump_target_id": 0, - "display_priority": 0, - "children": [] - }, - "14": { - "id": 14, - "name": "解谜", - "icon": "", - "parent_id": 0, - "depth": 1, - "node_type": 1, - "jump_type": 0, - "jump_target_id": 0, - "display_priority": 0, - "children": [] - }, - "18": { - "id": 18, - "name": "指引仙灵", - "icon": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/08/75276545/8de3558f991ac422463712dd095278b3_109842752079072307.png", - "parent_id": 14, - "depth": 2, - "node_type": 2, - "jump_type": 0, - "jump_target_id": 0, - "display_priority": 0, - "children": [] - }, - "64": { - "id": 64, - "name": "限时挑战", - "icon": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/21/75795471/a6978cb8965e81de4a25accbf6c236ce_6189233402996409228.png", - "parent_id": 14, - "depth": 2, - "node_type": 2, - "jump_type": 0, - "jump_target_id": 0, - "display_priority": 0, - "children": [] - }, - "65": { - "id": 65, - "name": "风车机关", - "icon": "https://uploadstatic.mihoyo.com/ys-obc/2020/10/21/75795471/de87958fa1e78efcc84da49aad61dede_7547151797455251961.jpg", - "parent_id": 14, - "depth": 2, - "node_type": 2, - "jump_type": 0, - "jump_target_id": 0, - "display_priority": 0, - "children": [] - }, - "66": { - "id": 66, - "name": "漂浮的风史莱姆", - "icon": "https://uploadstatic.mihoyo.com/ys-obc/2020/10/21/75795471/de87958fa1e78efcc84da49aad61dede_6780166156178323412.jpg", - "parent_id": 14, - "depth": 2, - "node_type": 2, - "jump_type": 0, - "jump_target_id": 0, - "display_priority": 0, - "children": [] - }, - "67": { - "id": 67, - "name": "压力机关", - "icon": "https://uploadstatic.mihoyo.com/ys-obc/2020/10/21/75795471/de87958fa1e78efcc84da49aad61dede_5932273944571249310.jpg", - "parent_id": 14, - "depth": 2, - "node_type": 2, - "jump_type": 0, - "jump_target_id": 0, - "display_priority": 0, - "children": [] - }, - "68": { - "id": 68, - "name": "蓬蓬果", - "icon": "https://uploadstatic.mihoyo.com/ys-obc/2020/10/21/75795471/de87958fa1e78efcc84da49aad61dede_7627546850715675857.jpg", - "parent_id": 14, - "depth": 2, - "node_type": 2, - "jump_type": 0, - "jump_target_id": 0, - "display_priority": 0, - "children": [] - }, - "69": { - "id": 69, - "name": "挖掘宝箱", - "icon": "https://uploadstatic.mihoyo.com/ys-obc/2020/10/21/75795471/de87958fa1e78efcc84da49aad61dede_200949033774003499.jpg", - "parent_id": 14, - "depth": 2, - "node_type": 2, - "jump_type": 0, - "jump_target_id": 0, - "display_priority": 0, - "children": [] - }, - "70": { - "id": 70, - "name": "元素方碑", - "icon": "https://uploadstatic.mihoyo.com/ys-obc/2020/10/21/75795471/de87958fa1e78efcc84da49aad61dede_8520221369291334139.jpg", - "parent_id": 14, - "depth": 2, - "node_type": 2, - "jump_type": 0, - "jump_target_id": 0, - "display_priority": 0, - "children": [] - }, - "71": { - "id": 71, - "name": "火炬机关", - "icon": "https://uploadstatic.mihoyo.com/ys-obc/2020/10/21/75795471/de87958fa1e78efcc84da49aad61dede_1061118532437351431.jpg", - "parent_id": 14, - "depth": 2, - "node_type": 2, - "jump_type": 0, - "jump_target_id": 0, - "display_priority": 0, - "children": [] - }, - "72": { - "id": 72, - "name": "巨型碎石", - "icon": "https://uploadstatic.mihoyo.com/ys-obc/2020/10/21/75795471/de87958fa1e78efcc84da49aad61dede_6449674692302465374.jpg", - "parent_id": 14, - "depth": 2, - "node_type": 2, - "jump_type": 0, - "jump_target_id": 0, - "display_priority": 0, - "children": [] - }, - "73": { - "id": 73, - "name": "小石堆", - "icon": "https://uploadstatic.mihoyo.com/ys-obc/2020/10/21/75795471/de87958fa1e78efcc84da49aad61dede_7798666829547657712.jpg", - "parent_id": 14, - "depth": 2, - "node_type": 2, - "jump_type": 0, - "jump_target_id": 0, - "display_priority": 0, - "children": [] - }, - "74": { - "id": 74, - "name": "拔植物", - "icon": "https://uploadstatic.mihoyo.com/ys-obc/2020/10/21/75795471/de87958fa1e78efcc84da49aad61dede_3891992353724563278.jpg", - "parent_id": 14, - "depth": 2, - "node_type": 2, - "jump_type": 0, - "jump_target_id": 0, - "display_priority": 0, - "children": [] - }, - "75": { - "id": 75, - "name": "被束缚的宝箱", - "icon": "https://uploadstatic.mihoyo.com/ys-obc/2020/10/21/75795471/de87958fa1e78efcc84da49aad61dede_7069442788377617628.jpg", - "parent_id": 14, - "depth": 2, - "node_type": 2, - "jump_type": 0, - "jump_target_id": 0, - "display_priority": 0, - "children": [] - }, - "76": { - "id": 76, - "name": "岩种子", - "icon": "https://uploadstatic.mihoyo.com/ys-obc/2020/10/21/75795471/de87958fa1e78efcc84da49aad61dede_6714446188416963945.jpg", - "parent_id": 14, - "depth": 2, - "node_type": 2, - "jump_type": 0, - "jump_target_id": 0, - "display_priority": 0, - "children": [] - }, - "77": { - "id": 77, - "name": "微解谜", - "icon": "https://uploadstatic.mihoyo.com/ys-obc/2020/10/21/75795471/de87958fa1e78efcc84da49aad61dede_3053004782269703586.jpg", - "parent_id": 14, - "depth": 2, - "node_type": 2, - "jump_type": 0, - "jump_target_id": 0, - "display_priority": 0, - "children": [] - }, - "50": { - "id": 50, - "name": "怪物(普通)", - "icon": "", - "parent_id": 0, - "depth": 1, - "node_type": 1, - "jump_type": 0, - "jump_target_id": 0, - "display_priority": 0, - "children": [] - }, - "55": { - "id": 55, - "name": "丘丘人", - "icon": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/17/75276545/74c2350936386d12f84e4244b8bd9750_8061221501486024583.png", - "parent_id": 50, - "depth": 2, - "node_type": 2, - "jump_type": 0, - "jump_target_id": 0, - "display_priority": 0, - "children": [] - }, - "56": { - "id": 56, - "name": "射手丘丘人", - "icon": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/17/75276545/74c2350936386d12f84e4244b8bd9750_8668908145618002848.png", - "parent_id": 50, - "depth": 2, - "node_type": 2, - "jump_type": 0, - "jump_target_id": 0, - "display_priority": 0, - "children": [] - }, - "57": { - "id": 57, - "name": "丘丘萨满", - "icon": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/17/75276545/fbd2f8a868f6c3a6f587d57d655f40c9_1156685569894627424.png", - "parent_id": 50, - "depth": 2, - "node_type": 2, - "jump_type": 0, - "jump_target_id": 0, - "display_priority": 0, - "children": [] - }, - "58": { - "id": 58, - "name": "盗宝团", - "icon": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/17/75276545/a9ecb7952e877dfd7c1dfae5132c7181_242314377398779361.png", - "parent_id": 50, - "depth": 2, - "node_type": 2, - "jump_type": 0, - "jump_target_id": 0, - "display_priority": 0, - "children": [] - }, - "59": { - "id": 59, - "name": "史莱姆", - "icon": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/17/75276545/de444c63df86b407045c0cca2d9c24b2_761844897909742071.png", - "parent_id": 50, - "depth": 2, - "node_type": 2, - "jump_type": 0, - "jump_target_id": 0, - "display_priority": 0, - "children": [] - }, - "51": { - "id": 51, - "name": "世界任务", - "icon": "", - "parent_id": 0, - "depth": 1, - "node_type": 1, - "jump_type": 0, - "jump_target_id": 0, - "display_priority": 0, - "children": [] - }, - "52": { - "id": 52, - "name": "世界任务", - "icon": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/16/75795471/c7263000d1f1ec150eddd27fca4d8630_2386396225561541114.png", - "parent_id": 51, - "depth": 2, - "node_type": 2, - "jump_type": 0, - "jump_target_id": 0, - "display_priority": 0, - "children": [] - }, - "60": { - "id": 60, - "name": "材料", - "icon": "", - "parent_id": 0, - "depth": 1, - "node_type": 1, - "jump_type": 0, - "jump_target_id": 0, - "display_priority": 0, - "children": [] - }, - "61": { - "id": 61, - "name": "冰雾花花朵", - "icon": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/17/75276545/cca41572bb14d20c094ce9af77eefda9_2844359998862121652.png", - "parent_id": 60, - "depth": 2, - "node_type": 2, - "jump_type": 0, - "jump_target_id": 0, - "display_priority": 0, - "children": [] - }, - "62": { - "id": 62, - "name": "烈焰花花蕊", - "icon": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/17/75276545/82a7a8327bfa630eb5e52695a2b456e9_4760074611076337816.png", - "parent_id": 60, - "depth": 2, - "node_type": 2, - "jump_type": 0, - "jump_target_id": 0, - "display_priority": 0, - "children": [] - }, - "63": { - "id": 63, - "name": "电气水晶", - "icon": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/17/75276545/aab9020763a7bee1fb532b7117b21fd1_5874466862813696108.png", - "parent_id": 60, - "depth": 2, - "node_type": 2, - "jump_type": 0, - "jump_target_id": 0, - "display_priority": 0, - "children": [] - } -} \ No newline at end of file diff --git a/plugins/group_handle/__init__.py b/plugins/group_handle/__init__.py index c17e068a..2a41f718 100644 --- a/plugins/group_handle/__init__.py +++ b/plugins/group_handle/__init__.py @@ -1,29 +1,32 @@ from nonebot import on_notice, on_request from configs.path_config import IMAGE_PATH, DATA_PATH -from utils.init_result import image +from utils.message_builder import image import os import random from models.group_member_info import GroupInfoUser from datetime import datetime from services.log import logger from models.group_remind import GroupRemind -from nonebot.adapters.cqhttp import Bot, GroupIncreaseNoticeEvent, GroupDecreaseNoticeEvent, GroupRequestEvent +from nonebot.adapters.cqhttp import ( + Bot, + GroupIncreaseNoticeEvent, + GroupDecreaseNoticeEvent, +) from nonebot.adapters.cqhttp.exception import ActionFailed from pathlib import Path -from nonebot import require +from models.group_info import GroupInfo + try: import ujson as json except ModuleNotFoundError: import json -__plugin_name__ = '群事件处理 [Hidden]' +__plugin_name__ = "群事件处理 [Hidden]" -__usage__ = '用法:无' +__usage__ = "用法:无" -export = require("admin_bot_manage") - # 群员增加处理 group_increase_handle = on_notice(priority=1, block=False) # 群员减少处理 @@ -34,98 +37,91 @@ add_group = on_request(priority=1, block=False) @group_increase_handle.handle() async def _(bot: Bot, event: GroupIncreaseNoticeEvent, state: dict): - if event.user_id == int(bot.self_id): - await export.update_member_info(event.group_id) - logger.info('被邀请入群,自动更新群成员列表...') + join_time = datetime.now() + user_info = await bot.get_group_member_info( + group_id=event.group_id, user_id=event.user_id + ) + if await GroupInfoUser.add_member_info( + user_info["user_id"], + user_info["group_id"], + user_info["nickname"], + join_time, + ): + logger.info(f"用户{user_info['user_id']} 所属{user_info['group_id']} 更新成功") else: - join_time = datetime.now() - user_info = await bot.get_group_member_info(group_id=event.group_id, user_id=event.user_id) - if await GroupInfoUser.insert( - user_info['user_id'], - user_info['group_id'], - user_info['nickname'], - join_time, - ): - logger.info(f"用户{user_info['user_id']} 所属{user_info['group_id']} 更新成功") + logger.info(f"用户{user_info['user_id']} 所属{user_info['group_id']} 更新失败") + + # 群欢迎消息 + if await GroupRemind.get_status(event.group_id, "hy"): + msg = "" + img = "" + at_flag = False + custom_welcome_msg_json = ( + Path() / "data" / "custom_welcome_msg" / "custom_welcome_msg.json" + ) + if custom_welcome_msg_json.exists(): + data = json.load(open(custom_welcome_msg_json, "r")) + if data.get(str(event.group_id)): + msg = data[str(event.group_id)] + if msg.find("[at]") != -1: + msg = msg.replace("[at]", "") + at_flag = True + if os.path.exists(DATA_PATH + f"custom_welcome_msg/{event.group_id}.jpg"): + img = image(abspath=DATA_PATH + f"custom_welcome_msg/{event.group_id}.jpg") + if msg or img: + await group_increase_handle.send( + "\n" + msg.strip() + img, at_sender=at_flag + ) else: - logger.info(f"用户{user_info['user_id']} 所属{user_info['group_id']} 更新失败") - if await GroupRemind.get_status(event.group_id, 'hy'): - msg = '' - img = '' - at_flag = False - custom_welcome_msg_json = Path() / "data" / "custom_welcome_msg" / "custom_welcome_msg.json" - if custom_welcome_msg_json.exists(): - data = json.load(open(custom_welcome_msg_json, 'r')) - if data.get(str(event.group_id)): - msg = data[str(event.group_id)] - if msg.find('[at]') != -1: - msg = msg.replace('[at]', '') - at_flag = True - if os.path.exists(DATA_PATH + f'custom_welcome_msg/{event.group_id}.jpg'): - img = image(abspath=DATA_PATH + f'custom_welcome_msg/{event.group_id}.jpg') - if msg or img: - await group_increase_handle.send("\n" + msg + img, at_sender=at_flag) - else: - await group_increase_handle.send( - '新人快跑啊!!本群现状↓(快使用自定义!)' + image(random.choice(os.listdir(IMAGE_PATH + "qxz/")), "qxz")) + await group_increase_handle.send( + "新人快跑啊!!本群现状↓(快使用自定义!)" + + image(random.choice(os.listdir(IMAGE_PATH + "qxz/")), "qxz") + ) @group_decrease_handle.handle() async def _(bot: Bot, event: GroupDecreaseNoticeEvent, state: dict): # 真寻被踢出群 - if event.sub_type == 'kick_me': + if event.sub_type == "kick_me": group_id = event.group_id operator_id = event.operator_id try: - operator_name = (await GroupInfoUser.select_member_info(event.operator_id, event.group_id)).user_name + operator_name = ( + await GroupInfoUser.get_member_info(event.operator_id, event.group_id) + ).user_name except AttributeError: - operator_name = 'None' + operator_name = "None" + group = await GroupInfo.get_group_info(group_id) + group_name = group.group_name if group else "" coffee = int(list(bot.config.superusers)[0]) await bot.send_private_msg( user_id=coffee, - message=f'报告..\n' - f'我被 {operator_name}({operator_id})\n' - f'踢出了 {group_id}') + message=f"****呜..一份踢出报告****\n" + f"我被 {operator_name}({operator_id})\n" + f"踢出了 {group_name}({group_id})\n" + f"日期:{str(datetime.now()).split('.')[0]}", + ) return try: - user_name = (await GroupInfoUser.select_member_info(event.user_id, event.group_id)).user_name + user_name = ( + await GroupInfoUser.get_member_info(event.user_id, event.group_id) + ).user_name except AttributeError: user_name = str(event.user_id) - rst = '' - if event.sub_type == 'leave': - rst = f'{user_name}离开了我们...' - if event.sub_type == 'kick': - try: - operator_name = (await GroupInfoUser.select_member_info(event.operator_id, event.group_id)).user_name - except AttributeError: - operator_name = event.operator_id - rst = f'{user_name} 被 {operator_name} 送走了.' - try: - await group_decrease_handle.send(f"{rst}") - except ActionFailed: - return + rst = "" + if event.sub_type == "leave": + rst = f"{user_name}离开了我们..." + if event.sub_type == "kick": + operator = await bot.get_group_member_info( + user_id=event.operator_id, group_id=event.group_id + ) + operator_name = operator["card"] if operator["card"] else operator["nickname"] + rst = f"{user_name} 被 {operator_name} 送走了." if await GroupInfoUser.delete_member_info(event.user_id, event.group_id): logger.info(f"用户{user_name}, qq={event.user_id} 所属{event.group_id} 删除成功") else: logger.info(f"用户{user_name}, qq={event.user_id} 所属{event.group_id} 删除失败") - - -@add_group.handle() -async def _(bot: Bot, event: GroupRequestEvent, state: dict): - pass - # user_info = await bot._get_vip_info(user_id=event.user_id) - # if user_info['level'] > 16: - # bot.set - - - - - - - - - - - - - + try: + await group_decrease_handle.send(f"{rst}") + except ActionFailed: + return diff --git a/plugins/group_level/__init__.py b/plugins/group_level/__init__.py index 3e4a9d8a..6ccc2010 100644 --- a/plugins/group_level/__init__.py +++ b/plugins/group_level/__init__.py @@ -9,90 +9,108 @@ from nonebot.permission import SUPERUSER from pathlib import Path from configs.config import plugins2info_dict from nonebot.message import run_preprocessor, IgnoredException + try: import ujson as json except ModuleNotFoundError: import json -__plugin_name__ = '群权限' -__plugin_usage__ = '区分权限功能' +__plugin_name__ = "群权限" +__plugin_usage__ = "区分权限功能" flmt = FreqLimiter(60) -group_level_data = Path() / 'data'/ 'manager' / 'group_level.json' +group_level_data = Path() / "data" / "manager" / "group_level.json" group_level_data.parent.mkdir(exist_ok=True, parents=True) group_data = {} if group_level_data.exists(): - group_data = json.load(open(group_level_data, 'r', encoding='utf8')) + group_data = json.load(open(group_level_data, "r", encoding="utf8")) @run_preprocessor async def _(matcher: Matcher, bot: Bot, event: GroupMessageEvent, state: T_State): if not isinstance(event, MessageEvent): return - if matcher.type == 'message' and matcher.priority not in [1, 9]: + if matcher.type == "message" and matcher.priority not in [1, 9]: if isinstance(event, GroupMessageEvent): if not group_data.get(str(event.group_id)): group_data[str(event.group_id)] = 5 - if plugins2info_dict.get(matcher.module): - if plugins2info_dict[matcher.module]['level'] > group_data[str(event.group_id)]: - try: - if flmt.check(event.group_id): - flmt.start_cd(event.group_id) - await bot.send_group_msg(group_id=event.group_id, message='群权限不足...') - except Exception: - pass - raise IgnoredException('群权限不足') + if not check_level_group(matcher.module, event.group_id): + try: + if flmt.check(event.group_id): + flmt.start_cd(event.group_id) + await bot.send_group_msg( + group_id=event.group_id, message="群权限不足..." + ) + except AttributeError: + pass + raise IgnoredException("群权限不足") -add_group_level = on_command('修改群权限', priority=1, permission=SUPERUSER, block=True) -my_group_level = on_command('查看群权限', aliases={'群权限'}, priority=5, permission=GROUP, block=True) -what_up_group_level = on_regex('.*?(提高|提升|升高|增加|加上)(.*?)群权限.*?', rule=to_me(), priority=5, permission=GROUP, block=True) +add_group_level = on_command("修改群权限", priority=1, permission=SUPERUSER, block=True) +my_group_level = on_command( + "查看群权限", aliases={"群权限"}, priority=5, permission=GROUP, block=True +) +what_up_group_level = on_regex( + ".*?(提高|提升|升高|增加|加上)(.*?)群权限.*?", + rule=to_me(), + priority=5, + permission=GROUP, + block=True, +) @add_group_level.handle() async def _(bot: Bot, event: MessageEvent, state: T_State): msg = get_message_text(event.json()) if not msg: - await add_group_level.finish('用法:修改群权限 [group] [level]') - msg = msg.split(' ') + await add_group_level.finish("用法:修改群权限 [group] [level]") + msg = msg.split(" ") if len(msg) < 2: - await add_group_level.finish('参数不完全..[group] [level]') + await add_group_level.finish("参数不完全..[group] [level]") if is_number(msg[0]) and is_number(msg[1]): group_id = msg[0] level = int(msg[1]) else: - await add_group_level.finish('参数错误...group和level必须是数字..') + await add_group_level.finish("参数错误...group和level必须是数字..") if not group_data.get(group_id): group_data[group_id] = 5 - await add_group_level.send('修改成功...', at_sender=True) - await bot.send_group_msg(group_id=int(group_id), message=f'管理员修改了此群权限:{group_data[group_id]} -> {level}') + await add_group_level.send("修改成功...", at_sender=True) + await bot.send_group_msg( + group_id=int(group_id), message=f"管理员修改了此群权限:{group_data[group_id]} -> {level}" + ) group_data[group_id] = level - with open(group_level_data, 'w', encoding='utf8') as f: + with open(group_level_data, "w", encoding="utf8") as f: json.dump(group_data, f, ensure_ascii=False, indent=4) - logger.info(f'{event.user_id} 修改了 {group_id} 的权限:{level}') + logger.info(f"{event.user_id} 修改了 {group_id} 的权限:{level}") @my_group_level.handle() async def _(bot: Bot, event: GroupMessageEvent, state: T_State): if group_data.get(str(event.group_id)): level = group_data[str(event.group_id)] - tmp = '' + tmp = "" for plugin in plugins2info_dict: - if plugins2info_dict[plugin]['level'] > level: - plugin_name = plugins2info_dict[plugin]['cmd'][0] - if plugin_name == 'pixiv': - plugin_name = '搜图 p站排行' - tmp += f'{plugin_name}\n' + if plugins2info_dict[plugin]["level"] > level: + plugin_name = plugins2info_dict[plugin]["cmd"][0] + if plugin_name == "pixiv": + plugin_name = "搜图 p站排行" + tmp += f"{plugin_name}\n" if tmp: - tmp = '\n目前无法使用的功能:\n' + tmp - await my_group_level.finish(f'当前群权限:{level}{tmp}') + tmp = "\n目前无法使用的功能:\n" + tmp + await my_group_level.finish(f"当前群权限:{level}{tmp}") @what_up_group_level.handle() async def _(bot: Bot, event: GroupMessageEvent, state: T_State): - await what_up_group_level.finish(f'[此功能用于防止内鬼,如果引起不便那真是抱歉了]\n' - f'目前提高群权限的方法:\n' - f'\t1.管理员修改权限') + await what_up_group_level.finish( + f"[此功能用于防止内鬼,如果引起不便那真是抱歉了]\n" f"目前提高群权限的方法:\n" f"\t1.管理员修改权限" + ) +# 检查权限 +def check_level_group(module: str, group_id: int) -> bool: + if plugins2info_dict.get(module): + if plugins2info_dict[module]["level"] > group_data[str(group_id)]: + return False + return True diff --git a/plugins/group_welcome_msg.py b/plugins/group_welcome_msg.py index 984eaee7..63e8ba88 100644 --- a/plugins/group_welcome_msg.py +++ b/plugins/group_welcome_msg.py @@ -3,44 +3,40 @@ from nonebot.typing import T_State from nonebot.adapters.cqhttp import Bot, GroupMessageEvent from nonebot.adapters.cqhttp.permission import GROUP from configs.path_config import DATA_PATH -from utils.init_result import image +from utils.message_builder import image import os from pathlib import Path + try: import ujson as json except ModuleNotFoundError: import json -__plugin_name__ = '查看群欢迎消息' +__plugin_name__ = "查看群欢迎消息" -__plugin_usage__ = '无' +__plugin_usage__ = "无" -view_custom_welcome = on_command('群欢迎消息', aliases={'查看群欢迎消息', '查看当前群欢迎消息'}, permission=GROUP, priority=5, block=True) +view_custom_welcome = on_command( + "群欢迎消息", aliases={"查看群欢迎消息", "查看当前群欢迎消息"}, permission=GROUP, priority=5, block=True +) @view_custom_welcome.handle() async def _(bot: Bot, event: GroupMessageEvent, state: T_State): - img = '' - msg = '' - if os.path.exists(DATA_PATH + f'custom_welcome_msg/{event.group_id}.jpg'): - img = image(abspath=DATA_PATH + f'custom_welcome_msg/{event.group_id}.jpg') - custom_welcome_msg_json = Path() / "data" / "custom_welcome_msg" / "custom_welcome_msg.json" + img = "" + msg = "" + if os.path.exists(DATA_PATH + f"custom_welcome_msg/{event.group_id}.jpg"): + img = image(abspath=DATA_PATH + f"custom_welcome_msg/{event.group_id}.jpg") + custom_welcome_msg_json = ( + Path() / "data" / "custom_welcome_msg" / "custom_welcome_msg.json" + ) if custom_welcome_msg_json.exists(): - data = json.load(open(custom_welcome_msg_json, 'r')) + data = json.load(open(custom_welcome_msg_json, "r")) if data.get(str(event.group_id)): msg = data[str(event.group_id)] - if msg.find('[at]') != -1: - msg = msg.replace('[at]', '') + if msg.find("[at]") != -1: + msg = msg.replace("[at]", "") if img or msg: await view_custom_welcome.finish(msg + img, at_sender=True) else: - await view_custom_welcome.finish('当前还没有自定义群欢迎消息哦', at_sender=True) - - - - - - - - - + await view_custom_welcome.finish("当前还没有自定义群欢迎消息哦", at_sender=True) diff --git a/plugins/help/__init__.py b/plugins/help/__init__.py index e69f4455..76c8e8e1 100644 --- a/plugins/help/__init__.py +++ b/plugins/help/__init__.py @@ -3,7 +3,7 @@ from nonebot.adapters.cqhttp import Bot, GroupMessageEvent, PrivateMessageEvent from nonebot.typing import T_State from nonebot.rule import to_me from configs.path_config import DATA_PATH -from utils.init_result import image +from utils.message_builder import image import os from .data_source import create_help_img, create_group_help_img, get_plugin_help from nonebot import require @@ -12,25 +12,27 @@ from utils.utils import get_message_text export = require("nonebot_plugin_manager") -__plugin_name__ = '帮助' +__plugin_name__ = "帮助" -if not os.path.exists(DATA_PATH + 'group_help/'): - os.mkdir(DATA_PATH + 'group_help/') +if not os.path.exists(DATA_PATH + "group_help/"): + os.mkdir(DATA_PATH + "group_help/") create_help_img() -for file in os.listdir(DATA_PATH + 'group_help/'): - os.remove(DATA_PATH + 'group_help/' + file) +for file in os.listdir(DATA_PATH + "group_help/"): + os.remove(DATA_PATH + "group_help/" + file) -_help = on_command("功能", rule=to_me(), aliases={'help', '帮助'}, priority=1, block=True) +_help = on_command("功能", rule=to_me(), aliases={"help", "帮助"}, priority=1, block=True) @_help.handle() async def _(bot: Bot, event: GroupMessageEvent, state: T_State): msg = get_message_text(event.json()) if not msg: - if not os.path.exists(DATA_PATH + f'group_help/{event.group_id}.png'): + if not os.path.exists(DATA_PATH + f"group_help/{event.group_id}.png"): create_group_help_img(event.group_id) - await _help.finish(image(abspath=DATA_PATH + f'group_help/{event.group_id}.png')) + await _help.finish( + image(abspath=DATA_PATH + f"group_help/{event.group_id}.png") + ) else: await _help.finish(get_plugin_help(msg)) @@ -39,7 +41,6 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State): async def _(bot: Bot, event: PrivateMessageEvent, state: T_State): msg = get_message_text(event.json()) if not msg: - await _help.finish(image('help.png')) + await _help.finish(image("help.png")) else: await _help.finish(get_plugin_help(msg)) - diff --git a/plugins/help/config.py b/plugins/help/config.py index 2c7cdcff..ca0d7752 100644 --- a/plugins/help/config.py +++ b/plugins/help/config.py @@ -1,65 +1,66 @@ - # 实用 utility_help = { - 'update_pic': '一些对图片的操作 --> 指令:操作图片/图片/修改图片(包含 10 种图片操作)', - 'search_buff_skin_price': 'BUFF皮肤底价查询 --> 指令:查询皮肤(代理ip不得劲)', - 'weather': '天气查询 --> 指令:xx天气', - 'yiqing': '实时疫情数据 --> 指令:疫情查询/疫情/查询疫情', - 'bt': 'bt(磁力搜索){仅支持私聊,懂的都懂} --> 指令:bt', - 'reimu': '老司机必备!{仅支持私聊,懂的都懂}-> 指令:上车', - 'what_anime': '靠图识番 --> 指令:识番', - 'nonebot_plugin_picsearcher': '以图搜图 --> 指令:识图/这是什么/上一张图是什么', - 'search_anime': '找不到想看的动漫吗? --> 指令:搜番', - 'songpicker2': '来一首歌听听? --> 指令:点歌', - 'epic': 'epic速速白嫖 --> 指令:epic', - 'pixiv_r': 'P站排行榜直接冲 --> 指令:p站排行(可含参数)', - 'pixiv_s': 'P站的图随便搜搜 --> 指令:搜图(可含参数)', - 'pid_search': '通过PID搜索图片 --> 指令:p搜 [pid]', - 'translate': '出国旅游助手(狗头) --> 指令:英翻/翻英/日翻/翻日/韩翻/翻韩', - 'cover': '快捷的b站封面获取方式 --> 指令:b封面 [连接/av/bv/cv/直播id]' + "update_pic": "一些对图片的操作 --> 指令:操作图片/图片/修改图片(包含 10 种图片操作)", + "search_buff_skin_price": "BUFF皮肤底价查询 --> 指令:查询皮肤(代理ip不得劲)", + "weather": "天气查询 --> 指令:xx天气", + "yiqing": "实时疫情数据 --> 指令:疫情查询/疫情/查询疫情", + "bt": "bt(磁力搜索){仅支持私聊,懂的都懂} --> 指令:bt", + "reimu": "老司机必备!{仅支持私聊,懂的都懂}-> 指令:上车", + "what_anime": "靠图识番 --> 指令:识番", + "nonebot_plugin_picsearcher": "以图搜图 --> 指令:识图/这是什么/上一张图是什么", + "search_anime": "找不到想看的动漫吗? --> 指令:搜番", + "songpicker2": "来一首歌听听? --> 指令:点歌", + "epic": "epic速速白嫖 --> 指令:epic", + "pixiv_r": "P站排行榜直接冲 --> 指令:p站排行(可含参数)", + "pixiv_s": "P站的图随便搜搜 --> 指令:搜图(可含参数)", + "pid_search": "通过PID搜索图片 --> 指令:p搜 [pid]", + "translate": "出国旅游助手(狗头) --> 指令:英翻/翻英/日翻/翻日/韩翻/翻韩", + "cover": "快捷的b站封面获取方式 --> 指令:b封面 [连接/av/bv/cv/直播id]", } # 娱乐 entertainment_help = { - 'sign_in': '签到(影响色图和开箱) --> 指令:签到/我的签到/好感度排行/好感度总榜/好感度总榜[屏蔽我/显示我]', - 'send_img': '发送图片 --> 指令:美图/萝莉/壁纸/(美图/萝莉/壁纸[id])/N张图xx(N<=9)', - 'send_setu': '不要小看涩图啊混蛋! --> 指令:色图/色图[id]/n张色图/n张xx色图/查色图/...(请查看 色图 帮助)', - 'white2black_img': '黑白草图 --> 指令:黑白图/黑白草图', - 'coser': '三次元也不戳 --> 指令:coser', - 'jitang': '不喝点什么不舒服 --> 指令:鸡汤/语录', - 'send_dinggong_voice': '骂我(傲娇?) --> 指令:骂老子', - 'poke': '戳一戳发送语音美图萝莉图不美哉?', - 'open_cases': '模拟开箱(戒赌) --> 指令:开箱(N连开箱[N<=30])/我的开箱/群开箱统计/我的金色', - 'luxun': '鲁迅说过 --> 指令:鲁迅说', - 'fake_msg': '构造一个假消息 --> 指令:假消息', - 'shop_handle': '商店系统(初始送100金币) --> 指令:商店/我的金币/购买道具/使用道具', - 'draw_card_prts': '换个地方当非酋TvT... --> 指令:方舟一井/方舟N抽(0 指令:原神一井/原神N抽(0 指令:赛马娘一井/赛马娘N抽/赛马娘卡一井/赛马娘卡N抽(0 指令:坎公骑冠剑一井/坎公骑冠剑武器一井/坎公骑冠剑N抽/坎公骑冠剑武器N抽(0 指令:pcr一井/pcrN抽(0 指令:碧蓝轻型/重型/特型N抽(0 指令:fgoN抽(0 指令:阴阳师N抽(0 指令:我有一个朋友想问问xxx(内容)', - 'nickname': '区区昵称! --> 指令:以后叫我xx(昵称)/我是谁/取消昵称', - 'almanac': '这是一张正经的黄历 --> 指令:原神黄历', - 'material_remind': '看看原神今天要刷什么 --> 指令:今日素材/今日材料/今天素材/今天材料', - 'qiu_qiu_translation': '这家伙到底在说什么? --> 指令:丘丘翻译/丘丘一下/丘丘语翻译', - 'query_resource_points': '地图资源速速查看 --> 指令:原神资源查询xx/原神资源列表/哪里有xx/xx在哪(xx=资源名称)', - 'russian': '紧张刺激的俄罗斯轮盘 --> 指令:俄罗斯轮盘帮助', - 'gold_redbag': '运气项目又来啦! --> 指令:塞红包/ 抢红包指令: 开/抢/戳一戳真寻/ 退回:退回未抢完的红包', - 'poetry': '突然文艺的真寻是否搞错了什么 --> 指令:念诗/来首诗/念首诗', - 'comments_163': '生了个人,我很抱歉 --> 指令:到点了/12点了/网易云热评/网易云评论', + "sign_in": "签到(影响色图和开箱) --> 指令:签到/我的签到/好感度排行/好感度总榜/好感度总榜[屏蔽我/显示我]", + "send_img": "发送图片 --> 指令:美图/萝莉/壁纸/(美图/萝莉/壁纸[id])/N张图xx(N<=9)", + "send_setu": "不要小看涩图啊混蛋! --> 指令:色图/色图[id]/n张色图/n张xx色图/查色图/...(请查看 色图 帮助)", + "white2black_img": "黑白草图 --> 指令:黑白图/黑白草图", + "coser": "三次元也不戳 --> 指令:coser", + "jitang": "不喝点什么不舒服 --> 指令:鸡汤/语录", + "send_dinggong_voice": "骂我(傲娇?) --> 指令:骂老子", + "poke": "戳一戳发送语音美图萝莉图不美哉?", + "open_cases": "模拟开箱(戒赌) --> 指令:开箱(N连开箱[N<=30])/我的开箱/群开箱统计/我的金色", + "luxun": "鲁迅说过 --> 指令:鲁迅说", + "fake_msg": "构造一个假消息 --> 指令:假消息", + "shop_handle": "商店系统(初始送100金币) --> 指令:商店/我的金币/购买道具/使用道具", + "draw_card_prts": "换个地方当非酋TvT... --> 指令:方舟一井/方舟N抽(0 指令:原神一井/原神N抽(0 指令:赛马娘一井/赛马娘N抽/赛马娘卡一井/赛马娘卡N抽(0 指令:坎公骑冠剑一井/坎公骑冠剑武器一井/坎公骑冠剑N抽/坎公骑冠剑武器N抽(0 指令:pcr一井/pcrN抽(0 指令:碧蓝轻型/重型/特型N抽(0 指令:fgoN抽(0 指令:阴阳师N抽(0 指令:我有一个朋友想问问xxx(内容)", + "nickname": "区区昵称! --> 指令:以后叫我xx(昵称)/我是谁/取消昵称", + "almanac": "这是一张正经的黄历 --> 指令:原神黄历", + "material_remind": "看看原神今天要刷什么 --> 指令:今日素材/今日材料/今天素材/今天材料", + "qiu_qiu_translation": "这家伙到底在说什么? --> 指令:丘丘翻译/丘丘一下/丘丘语翻译", + "query_resource_points": "地图资源速速查看 --> 指令:原神资源查询xx[路径]/原神资源列表/哪里有xx/xx在哪(xx=资源名称)", + "russian": "紧张刺激的俄罗斯轮盘 --> 指令:俄罗斯轮盘帮助", + "gold_redbag": "运气项目又来啦! --> 指令:塞红包/ 抢红包指令: 开/抢/戳一戳真寻/ 退回:退回未抢完的红包", + "poetry": "突然文艺的真寻是否搞错了什么 --> 指令:念诗/来首诗/念首诗", + "comments_163": "生了个人,我很抱歉 --> 指令:到点了/12点了/网易云热评/网易云评论", + "pix_gallery": "偶尔也想看看美图? --> 指令:PIX [关键词/uid/pid:pid] [num]/查看pix图库 [关键词]/显示pix关键词", } # 其他 other_help = [ - '查看当前的群欢迎消息 --> 指令:群欢迎消息', - '这是一份正经的自我介绍 --> 指令:自我介绍', - '不得看看自己权力多大? --> 指令:我的权限', - '有人记得你是什么时候加入我们的 --> 指令:我的信息', - '让我看看更新了什么 --> 指令:更新信息', - '真寻给我把话收回去! --> 指令:撤回 [id](默认0)', - '群拥有的权限 --> 指令:查看群权限', - '数据统计可视化_1 --> 指令:功能调用统计/日功能调用统计/周功能调用统计/月功能调用统计', - '数据统计可视化_2 --> 指令:周功能调用统计 [功能名称]/月功能调用统计 [功能名称]' + "查看当前的群欢迎消息 --> 指令:群欢迎消息", + "这是一份正经的自我介绍 --> 指令:自我介绍", + "不得看看自己权力多大? --> 指令:我的权限", + "有人记得你是什么时候加入我们的 --> 指令:我的信息", + "让我看看更新了什么 --> 指令:更新信息", + "真寻给我把话收回去! --> 指令:撤回 [id](默认0)", + "群拥有的权限 --> 指令:查看群权限", + "数据统计可视化_1 --> 指令:功能调用统计/日功能调用统计/周功能调用统计/月功能调用统计", + "数据统计可视化_2 --> 指令:周功能调用统计 [功能名称]/月功能调用统计 [功能名称]", + "这里是PIX图库! --> 指令:添加pix关键词 [关键词]/添加pixuid/pid [UID/PID]/我的pix关键词", ] diff --git a/plugins/help/data_source.py b/plugins/help/data_source.py index 9b14d860..4598bb12 100644 --- a/plugins/help/data_source.py +++ b/plugins/help/data_source.py @@ -1,16 +1,20 @@ -from utils.img_utils import CreateImg +from utils.image_utils import CreateImg from configs.path_config import IMAGE_PATH, DATA_PATH import ujson as json import os from .config import * from nonebot import require -from configs.config import INITIAL_OPEN_CASE_COUNT, INITIAL_SETU_PROBABILITY, ADMIN_DEFAULT_AUTH +from configs.config import ( + INITIAL_OPEN_CASE_COUNT, + INITIAL_SETU_PROBABILITY, + ADMIN_DEFAULT_AUTH, +) from configs.config import plugins2info_dict import nonebot export = require("nonebot_plugin_manager") -width = 1500 +width = 1600 e_height = 0 u_height = 950 o_height = 1500 @@ -18,139 +22,166 @@ o_height = 1500 def create_help_img(): - if os.path.exists(IMAGE_PATH + 'help.png'): - os.remove(IMAGE_PATH + 'help.png') - h = (100 + len(utility_help) * 24 + len(entertainment_help) * 24 + len(other_help) * 24) * 2 + if os.path.exists(IMAGE_PATH + "help.png"): + os.remove(IMAGE_PATH + "help.png") + h = ( + 100 + + len(utility_help) * 24 + + len(entertainment_help) * 24 + + len(other_help) * 24 + ) * 2 A = CreateImg(width, h - 200, font_size=24) e = CreateImg(width, len(entertainment_help) * 42, font_size=24) - rst = '' + rst = "" i = 0 for cmd in entertainment_help: - rst += f'{i + 1}.{entertainment_help[cmd]}\n' + rst += f"{i + 1}.{entertainment_help[cmd]}\n" i += 1 - e.text((10, 10), '娱乐功能:') + e.text((10, 10), "娱乐功能:") e.text((40, 40), rst) - u = CreateImg(width, len(utility_help) * 40 + 50, font_size=24, color='black') - rst = '' + u = CreateImg(width, len(utility_help) * 40 + 50, font_size=24, color="black") + rst = "" i = 0 for cmd in utility_help: - rst += f'{i + 1}.{utility_help[cmd]}\n' + rst += f"{i + 1}.{utility_help[cmd]}\n" i += 1 - u.text((10, 10), '实用功能:', fill=(255, 255, 255)) + u.text((10, 10), "实用功能:", fill=(255, 255, 255)) u.text((40, 40), rst, fill=(255, 255, 255)) o = CreateImg(width, len(other_help) * 40, font_size=24) - rst = '' + rst = "" i = 0 for i in range(len(other_help)): - rst += f'{i + 1}.{other_help[i]}\n' + rst += f"{i + 1}.{other_help[i]}\n" i += 1 - o.text((10, 10), '其他功能:') + o.text((10, 10), "其他功能:") o.text((40, 40), rst) A.paste(e, (0, 0)) A.paste(u, (0, u_height)) A.paste(o, (0, o_height)) - A.text((10, h * 0.68), '大部分交互功能可以通过输入‘取消’,‘算了’来取消当前交互\n对真寻说 “真寻帮助 指令名” 获取对应详细帮助\n' - '可以通过 “滴滴滴- [消息]” 联系管理员(有趣的想法尽管来吧!<还有Bug和建议>)' - '\n[群管理员请看 管理员帮助(群主与管理员自带 5 级权限)]\n\n' - '\t「如果真寻回复了一些不符合人设的话,那是因为每日白嫖的图灵次数已用完,使用的是备用接口【QAQ】」') + A.text( + (10, h * 0.68), + "大部分交互功能可以通过输入‘取消’,‘算了’来取消当前交互\n对真寻说 “真寻帮助 指令名” 获取对应详细帮助\n" + "可以通过 “滴滴滴- [消息]” 联系管理员(有趣的想法尽管来吧!<还有Bug和建议>)" + "\n[群管理员请看 管理员帮助(群主与管理员自带 5 级权限)]\n\n" + "\t「如果真寻回复了一些不符合人设的话,那是因为每日白嫖的图灵次数已用完,使用的是备用接口【QAQ】」", + ) - A.save(IMAGE_PATH + 'help.png') + A.save(IMAGE_PATH + "help.png") def create_group_help_img(group_id: int): group_id = str(group_id) try: - with open(DATA_PATH + 'manager/plugin_list.json', 'r', encoding='utf8') as f: + with open(DATA_PATH + "manager/plugin_list.json", "r", encoding="utf8") as f: plugin_list = json.load(f) except (ValueError, FileNotFoundError): pass - h = (100 + len(utility_help) * 24 + len(entertainment_help) * 24 + len(other_help) * 24) * 2 - A = CreateImg(1200, h - 200, font_size=24) - u = CreateImg(1200, len(utility_help) * 40, font_size=24, color='black') - o = CreateImg(1200, len(other_help) * 40, font_size=24) + h = ( + 100 + + len(utility_help) * 24 + + len(entertainment_help) * 24 + + len(other_help) * 24 + ) * 2 + A = CreateImg(width, h - 200, font_size=24) + u = CreateImg(width, len(utility_help) * 40, font_size=24, color="black") + o = CreateImg(width, len(other_help) * 40, font_size=24) e = CreateImg(width, len(entertainment_help) * 42, font_size=24) - rst = '' + rst = "" i = 1 for cmd in entertainment_help.keys(): flag, dfg = parse_cmd(cmd, group_id, plugin_list) if dfg: cmd = rcmd(dfg) - rst += f'【{flag}】{i}.{entertainment_help[cmd]}\n' + rst += f"【{flag}】{i}.{entertainment_help[cmd]}\n" i += 1 - e.text((10, 10), '娱乐功能:') + e.text((10, 10), "娱乐功能:") e.text((40, 40), rst) - rst = '' + rst = "" i = 1 for cmd in utility_help.keys(): flag, dfg = parse_cmd(cmd, group_id, plugin_list) - rst += f'【{flag}】{i}.{utility_help[cmd]}\n' + rst += f"【{flag}】{i}.{utility_help[cmd]}\n" i += 1 - u.text((10, 10), '实用功能:', fill=(255, 255, 255)) + u.text((10, 10), "实用功能:", fill=(255, 255, 255)) u.text((40, 40), rst, fill=(255, 255, 255)) - rst = '' + rst = "" for i in range(len(other_help)): - rst += f'{i + 1}.{other_help[i]}\n' - o.text((10, 10), '其他功能:') + rst += f"{i + 1}.{other_help[i]}\n" + o.text((10, 10), "其他功能:") o.text((40, 40), rst) A.paste(e, (0, 0)) A.paste(u, (0, u_height)) A.paste(o, (0, o_height)) # A.text((width, 10), f'总开关【{"√" if data["总开关"] else "×"}】') - A.text((10, h * 0.68), '大部分交互功能可以通过输入‘取消’,‘算了’来取消当前交互\n对真寻说 “真寻帮助 指令名” 获取对应详细帮助\n' - '可以通过 “滴滴滴- [消息]” 联系管理员(有趣的想法尽管来吧!<还有Bug和建议>)' - f'\n[群管理员请看 管理员帮助(群主与管理员自带 {ADMIN_DEFAULT_AUTH} 级权限)]') - A.text((10, h * 0.77), f"【注】「色图概率:好感度 + {int(INITIAL_SETU_PROBABILITY*100)}%\n" - f"\t\t每 3 点好感度 + 1次开箱,初始 {INITIAL_OPEN_CASE_COUNT} 次\n" - f"\t\t开启/关闭功能只需输入‘开启/关闭 指令名’(每个功能的第一个指令)」\n" - f"\t\t示例:开启签到\n" - f"\t\t可以通过管理员开关自动发送消息(早晚安等)\n" - f"\t\t^请查看管理员帮助^\n\n" - f"\t「如果真寻回复了一些不符合人设的话,那是因为每日白嫖的图灵次数已用完,使用的是备用接口【QAQ】」") - A.save(DATA_PATH + f'group_help/{group_id}.png') + A.text( + (10, h * 0.68), + "大部分交互功能可以通过输入‘取消’,‘算了’来取消当前交互\n对真寻说 “真寻帮助 指令名” 获取对应详细帮助\n" + "可以通过 “滴滴滴- [消息]” 联系管理员(有趣的想法尽管来吧!<还有Bug和建议>)" + f"\n[群管理员请看 管理员帮助(群主与管理员自带 {ADMIN_DEFAULT_AUTH} 级权限)]", + ) + A.text( + (10, h * 0.77), + f"【注】「色图概率:好感度 + {int(INITIAL_SETU_PROBABILITY*100)}%\n" + f"\t\t每 3 点好感度 + 1次开箱,初始 {INITIAL_OPEN_CASE_COUNT} 次\n" + f"\t\t开启/关闭功能只需输入‘开启/关闭 指令名’(每个功能的第一个指令)」\n" + f"\t\t示例:开启签到\n" + f"\t\t可以通过管理员开关自动发送消息(早晚安等)\n" + f"\t\t^请查看管理员帮助^\n\n" + f"\t「如果真寻回复了一些不符合人设的话,那是因为每日白嫖的图灵次数已用完,使用的是备用接口【QAQ】」", + ) + A.save(DATA_PATH + f"group_help/{group_id}.png") def parse_cmd(cmd, group_id, plugin_list): - flag = '√' + flag = "√" dfg = None - if cmd.find('draw_card') != -1: - lst = cmd.split('_') - cmd = lst[0] + '_' + lst[1] + if cmd.find("draw_card") != -1: + lst = cmd.split("_") + cmd = lst[0] + "_" + lst[1] dfg = lst[-1] - elif cmd == 'pixiv_r': - cmd = 'pixiv' - dfg = 'r' - elif cmd == 'pixiv_s': - cmd = 'pixiv' - dfg = 's' + elif cmd == "pixiv_r": + cmd = "pixiv" + dfg = "r" + elif cmd == "pixiv_s": + cmd = "pixiv" + dfg = "s" if group_id in plugin_list[cmd]: if not plugin_list[cmd][group_id]: - flag = '×' - if cmd in ['bt', 'reimu', 'nickname']: - flag = '- ' + flag = "×" + if cmd in ["bt", "reimu", "nickname"]: + flag = "- " return flag, dfg def rcmd(dfg): - if dfg in ['prts', 'genshin', 'pretty', 'guardian', 'pcr', 'azur', 'onmyoji', 'fgo']: - return 'draw_card_' + dfg - if dfg == 'r': - return 'pixiv_r' - if dfg == 's': - return 'pixiv_s' + if dfg in [ + "prts", + "genshin", + "pretty", + "guardian", + "pcr", + "azur", + "onmyoji", + "fgo", + ]: + return "draw_card_" + dfg + if dfg == "r": + return "pixiv_r" + if dfg == "s": + return "pixiv_s" def get_plugin_help(msg: str) -> str: plugin = None for p in plugins2info_dict.keys(): - if msg in plugins2info_dict[p]['cmd']: + if msg in plugins2info_dict[p]["cmd"]: plugin = nonebot.plugin.get_plugin(p) break if plugin: result = plugin.module.__getattribute__("__plugin_usage__") return result else: - return '没有此功能的帮助信息...' - + return "没有此功能的帮助信息..." diff --git a/plugins/hook.py b/plugins/hook.py index 38fd58da..c0c84be1 100644 --- a/plugins/hook.py +++ b/plugins/hook.py @@ -1,13 +1,25 @@ from nonebot.matcher import Matcher from nonebot.message import run_preprocessor, IgnoredException from nonebot.typing import T_State -from nonebot.adapters.cqhttp import Bot, MessageEvent, PrivateMessageEvent, GroupMessageEvent -from configs.config import BAN_RESULT, admin_plugins_auth, MALICIOUS_BAN_TIME, MALICIOUS_CHECK_TIME, MALICIOUS_BAN_COUNT +from nonebot.adapters.cqhttp import ( + Bot, + MessageEvent, + PrivateMessageEvent, + GroupMessageEvent, +) +from configs.config import ( + BAN_RESULT, + admin_plugins_auth, + MALICIOUS_BAN_TIME, + MALICIOUS_CHECK_TIME, + MALICIOUS_BAN_COUNT, +) from models.ban_user import BanUser from utils.utils import is_number, static_flmt, BanCheckLimiter -from utils.init_result import at +from utils.message_builder import at from services.log import logger from models.level_user import LevelUser + try: import ujson as json except ModuleNotFoundError: @@ -19,32 +31,39 @@ except ModuleNotFoundError: async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State): if not isinstance(event, MessageEvent): return - if matcher.type == 'message' and matcher.priority not in [1, 9]: - if await BanUser.isban(event.user_id) and str(event.user_id) not in bot.config.superusers: + if matcher.type == "message" and matcher.priority not in [1, 9]: + if ( + await BanUser.is_ban(event.user_id) + and str(event.user_id) not in bot.config.superusers + ): time = await BanUser.check_ban_time(event.user_id) if is_number(time): time = abs(int(time)) if time < 60: - time = str(time) + ' 秒' + time = str(time) + " 秒" else: - time = str(int(time / 60)) + ' 分' + time = str(int(time / 60)) + " 分" else: - time = str(time) + ' 分' - if event.message_type == 'group': + time = str(time) + " 分" + if event.message_type == "group": if not static_flmt.check(event.user_id): - raise IgnoredException('用户处于黑名单中') + raise IgnoredException("用户处于黑名单中") static_flmt.start_cd(event.user_id) if matcher.priority != 9: - await bot.send_group_msg(group_id=event.group_id, - message=at(event.user_id) + BAN_RESULT + f' 在..在 {time}后才会理你喔') + await bot.send_group_msg( + group_id=event.group_id, + message=at(event.user_id) + BAN_RESULT + f" 在..在 {time}后才会理你喔", + ) else: if not static_flmt.check(event.user_id): - raise IgnoredException('用户处于黑名单中') + raise IgnoredException("用户处于黑名单中") static_flmt.start_cd(event.user_id) if matcher.priority != 9: - await bot.send_private_msg(user_id=event.user_id, - message=at(event.user_id) + BAN_RESULT + f' 在..在 {time}后才会理你喔') - raise IgnoredException('用户处于黑名单中') + await bot.send_private_msg( + user_id=event.user_id, + message=at(event.user_id) + BAN_RESULT + f" 在..在 {time}后才会理你喔", + ) + raise IgnoredException("用户处于黑名单中") _blmt = BanCheckLimiter(MALICIOUS_CHECK_TIME, MALICIOUS_BAN_COUNT) @@ -55,104 +74,76 @@ _blmt = BanCheckLimiter(MALICIOUS_CHECK_TIME, MALICIOUS_BAN_COUNT) async def _(matcher: Matcher, bot: Bot, event: GroupMessageEvent, state: T_State): if not isinstance(event, MessageEvent): return - if matcher.type == 'message' and matcher.priority not in [1, 9]: + if matcher.type == "message" and matcher.priority not in [1, 9]: if state["_prefix"]["raw_command"]: # print(state["_prefix"]["raw_command"]) if _blmt.check(f'{event.user_id}{state["_prefix"]["raw_command"]}'): if await BanUser.ban(event.user_id, 9, MALICIOUS_BAN_TIME * 60): - logger.info(f'USER {event.user_id} 触发了恶意触发检测') + logger.info(f"USER {event.user_id} 触发了恶意触发检测") # await update_img.finish('检测到恶意触发命令,您将被封禁 30 分钟', at_sender=True) - if event.message_type == 'group': + if event.message_type == "group": if not static_flmt.check(event.user_id): return static_flmt.start_cd(event.user_id) - await bot.send_group_msg(group_id=event.group_id, - message=at(event.user_id) + '检测到恶意触发命令,您将被封禁 30 分钟') + await bot.send_group_msg( + group_id=event.group_id, + message=at(event.user_id) + "检测到恶意触发命令,您将被封禁 30 分钟", + ) else: if not static_flmt.check(event.user_id): return static_flmt.start_cd(event.user_id) - await bot.send_private_msg(user_id=event.user_id, - message=at(event.user_id) + '检测到恶意触发命令,您将被封禁 30 分钟') - raise IgnoredException('检测到恶意触发命令') + await bot.send_private_msg( + user_id=event.user_id, + message=at(event.user_id) + "检测到恶意触发命令,您将被封禁 30 分钟", + ) + raise IgnoredException("检测到恶意触发命令") _blmt.add(f'{event.user_id}{state["_prefix"]["raw_command"]}') # 权限检测 @run_preprocessor async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State): - if not isinstance(event, MessageEvent): - return - if await BanUser.isban(event.user_id): + if not isinstance(event, MessageEvent) or await BanUser.is_ban(event.user_id): return if matcher.module in admin_plugins_auth.keys() and matcher.priority not in [1, 9]: - if event.message_type == 'group': - if not await LevelUser.check_level(event.user_id, event.group_id, admin_plugins_auth[matcher.module]): - await bot.send_group_msg(group_id=event.group_id, - message=f'{at(event.user_id)}你的权限不足喔,该功能需要的权限等级:' - f'{admin_plugins_auth[matcher.module]}') - raise IgnoredException('权限不足') + if event.message_type == "group": + if not await LevelUser.check_level( + event.user_id, event.group_id, admin_plugins_auth[matcher.module] + ): + await bot.send_group_msg( + group_id=event.group_id, + message=f"{at(event.user_id)}你的权限不足喔,该功能需要的权限等级:" + f"{admin_plugins_auth[matcher.module]}", + ) + raise IgnoredException("权限不足") else: - if not await LevelUser.check_level(event.user_id, 0, admin_plugins_auth[matcher.module]): - await bot.send_private_msg(user_id=event.user_id, - message=f'你的权限不足喔,该功能需要的权限等级:{admin_plugins_auth[matcher.module]}') - raise IgnoredException('权限不足') + if not await LevelUser.check_level( + event.user_id, 0, admin_plugins_auth[matcher.module] + ): + await bot.send_private_msg( + user_id=event.user_id, + message=f"你的权限不足喔,该功能需要的权限等级:{admin_plugins_auth[matcher.module]}", + ) + raise IgnoredException("权限不足") # 为什么AI会自己和自己聊天 @run_preprocessor async def _(matcher: Matcher, bot: Bot, event: PrivateMessageEvent, state: T_State): + if not isinstance(event, MessageEvent): + return if event.user_id == int(bot.self_id): - raise IgnoredException('为什么AI会自己和自己聊天') + raise IgnoredException("为什么AI会自己和自己聊天") # 有命令就别说话了 @run_preprocessor async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State): - if matcher.type == 'message': - if state["_prefix"]["raw_command"] and matcher.module == 'ai': - raise IgnoredException('有命令就别说话了') - - -# _ulmt = UserExistLimiter() -# -# -# # 是否有命令正在处理 -# @run_preprocessor -# async def _(matcher: Matcher, bot: Bot, event: Event, state: T_State): -# if matcher.module == 'admin_bot_manage': -# return -# if event.user_id == bot.self_id: -# raise IgnoredException('自己和自己聊天?') -# print(state["_prefix"]["raw_command"]) -# print(matcher.type) -# if (event.is_tome() and (state["_prefix"]["raw_command"] and matcher.type == 'message') -# and matcher.module == 'ai') or not static_group_dict[event.group_id]['总开关']: -# raise IgnoredException('Ai给爷爬?') -# # if matcher.module in ['ai']: -# # raise IgnoredException('Ai给爷爬?') -# if matcher.type == 'message' and matcher.priority not in [1, 8, 9]: -# if _ulmt.check(event.user_id): -# if event.message_type == 'group': -# await bot.send_group_msg(group_id=event.group_id, message=at(event.user_id) + '您有命令正在处理,等一哈!') -# else: -# await bot.send_private_msg(user_id=event.user_id, message='您有命令正在处理,等一哈!') -# raise IgnoredException('有命令正在处理') -# _ulmt.set_True(event.user_id) -# -# -# # 结束正在处理的命令 -# @run_postprocessor -# async def _(matcher: Matcher, bot: Bot, event: Event, state: T_State): -# _ulmt.set_False(event.user_id) -# -# -# # 结束正在处理的命令 -# @event_preprocessor -# async def _(bot: Bot, event: Event, state: T_State): -# try: -# _ulmt.set_False(event.user_id) -# except AttributeError: -# pass + if not isinstance(event, MessageEvent): + return + if matcher.type == "message": + if state["_prefix"]["raw_command"] and matcher.module == "ai": + raise IgnoredException("有命令就别说话了") diff --git a/plugins/jitang.py b/plugins/jitang.py index 44b73146..1f57fa26 100644 --- a/plugins/jitang.py +++ b/plugins/jitang.py @@ -1,17 +1,13 @@ from nonebot import on_command from utils.user_agent import get_user_agent from services.log import logger -from nonebot.adapters.cqhttp import Bot, Event +from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent from nonebot.typing import T_State import aiohttp from asyncio.exceptions import TimeoutError -try: - import ujson as json -except ModuleNotFoundError: - import json -__plugin_name__ = '鸡汤' -__plugin_usage__ = '用法: 发送’鸡汤‘,真寻亲自为你喝鸡汤' +__plugin_name__ = "鸡汤" +__plugin_usage__ = "用法: 发送’鸡汤‘,真寻亲自为你喝鸡汤" url = "https://v2.alapi.cn/api/soul" @@ -21,24 +17,20 @@ jitang = on_command("鸡汤", aliases={"毒鸡汤"}, priority=5, block=True) @jitang.handle() -async def _(bot: Bot, event: Event, state: T_State): - params = { - "format": "json", - "token": "h0KuF6qNniMHGUtA" - } +async def _(bot: Bot, event: MessageEvent, state: T_State): + params = {"format": "json", "token": "h0KuF6qNniMHGUtA"} try: async with aiohttp.ClientSession(headers=get_user_agent()) as session: async with session.get(url, timeout=7, params=params) as response: if response.status == 200: data = await response.json() - await jitang.send(data['data']['content']) + await jitang.send(data["data"]["content"]) logger.info( - f"(USER {event.user_id}, GROUP {event.group_id if event.message_type != 'private' else 'private'})" - f" 发送鸡汤:" + data['data']['content']) + f"(USER {event.user_id}, GROUP " + f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" + f" 发送鸡汤:" + data["data"]["content"] + ) else: - await jitang.send('鸡汤煮坏掉了...') + await jitang.send("鸡汤煮坏掉了...") except TimeoutError: await jitang.send("鸡汤煮超时了##", at_sender=True) - # except Exception as e: - # await jitang.send("出错啦!再试一次吧!", at_sender=True) - # logger.info(f'鸡汤error e:{e}') diff --git a/plugins/last_chat/__init__.py b/plugins/last_chat/__init__.py index 042bc0ef..8078b551 100644 --- a/plugins/last_chat/__init__.py +++ b/plugins/last_chat/__init__.py @@ -7,9 +7,9 @@ from .data_source import cancel_all_notice, save_data, get_data, set_data_value from services.log import logger -__plugin_name__ = '查看群最后聊天时间 [Hidden]' +__plugin_name__ = "查看群最后聊天时间 [Hidden]" -__plugin_usage__ = '用法:无' +__plugin_usage__ = "用法:无" last_chat = on_message(priority=1, block=False, permission=GROUP) @@ -19,37 +19,17 @@ last_chat = on_message(priority=1, block=False, permission=GROUP) async def _(bot: Bot, event: GroupMessageEvent, state: T_State): time_data = await get_data() set_data_value(event.group_id, time.time()) - if event.group_id in time_data['_group']: - time_data['_group'].remove(event.group_id) - set_data_value('_group', time_data['_group']) + if event.group_id in time_data["_group"]: + time_data["_group"].remove(event.group_id) + set_data_value("_group", time_data["_group"]) for key in time_data.keys(): - if key not in ['check_time', '_group']: - if key not in time_data['_group']: + if key not in ["check_time", "_group"]: + if key not in time_data["_group"]: if time.time() - time_data[key] > 60 * 60 * 36: await cancel_all_notice(key) time_data["_group"].append(key) - set_data_value('_group', time_data['_group']) - logger.info(f'GROUP {event.group_id} 因群内发言时间大于36小时被取消全部通知') - if time.time() - time_data['check_time'] > 60 * 60 * 1: - set_data_value('check_time', time.time()) + set_data_value("_group", time_data["_group"]) + logger.info(f"GROUP {event.group_id} 因群内发言时间大于36小时被取消全部通知") + if time.time() - time_data["check_time"] > 60 * 60 * 1: + set_data_value("check_time", time.time()) save_data() - - - - - - - - - - - - - - - - - - - - diff --git a/plugins/last_chat/data_source.py b/plugins/last_chat/data_source.py index 7f89b49f..269f62a9 100644 --- a/plugins/last_chat/data_source.py +++ b/plugins/last_chat/data_source.py @@ -4,6 +4,7 @@ from models.group_remind import GroupRemind from datetime import datetime import time from services.log import logger + try: import ujson as json except ModuleNotFoundError: @@ -17,34 +18,36 @@ async def init(): global time_data bot = get_bot() gl = await bot.get_group_list(self_id=bot.self_id) - gl = [g['group_id'] for g in gl] - data = read_data('group_last_chat_time.json') + gl = [g["group_id"] for g in gl] + data = read_data("group_last_chat_time.json") for g in gl: if not data.get(g): time_data[g] = time.time() - if not time_data.get('check_time'): - time_data['check_time'] = time.time() - if not time_data.get('_group'): - time_data['_group'] = [] + if not time_data.get("check_time"): + time_data["check_time"] = time.time() + if not time_data.get("_group"): + time_data["_group"] = [] save_data() return time_data def read_data(file_name: str): try: - with open(DATA_PATH + file_name, 'r', encoding='utf8') as f: + with open(DATA_PATH + file_name, "r", encoding="utf8") as f: return json.load(f) except (ValueError, FileNotFoundError): return {} def save_data(): - with open(DATA_PATH + 'group_last_chat_time.json', 'w') as f: + with open(DATA_PATH + "group_last_chat_time.json", "w") as f: json.dump(time_data, f, indent=4) - logger.info(f'自动存储 group_last_chat_time.json 时间:{str(datetime.now()).split(".")[0]}') + logger.info( + f'自动存储 group_last_chat_time.json 时间:{str(datetime.now()).split(".")[0]}' + ) -command_list = ['zwa', 'hy', 'kxcz', 'blpar', 'epic', 'pa'] +command_list = ["zwa", "hy", "kxcz", "blpar", "epic", "pa", "gb"] # 取消全部通知 @@ -53,7 +56,7 @@ async def cancel_all_notice(group_id): for command in command_list: if await GroupRemind.get_status(group_id, command): await GroupRemind.set_status(group_id, command, False) - logger.info(f'关闭了 {group_id} 群的全部通知') + logger.info(f"关闭了 {group_id} 群的全部通知") async def get_data(): @@ -66,9 +69,3 @@ async def get_data(): def set_data_value(key, value): global time_data time_data[key] = value - - - - - - diff --git a/plugins/luxun/__init__.py b/plugins/luxun/__init__.py index 7a9dafd1..51b3b412 100644 --- a/plugins/luxun/__init__.py +++ b/plugins/luxun/__init__.py @@ -3,15 +3,15 @@ import textwrap from configs.path_config import IMAGE_PATH, TTF_PATH from nonebot import on_command from nonebot.typing import T_State -from nonebot.adapters.cqhttp import Bot, MessageEvent -from utils.init_result import image +from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent +from utils.message_builder import image from services.log import logger from utils.utils import UserExistLimiter, get_message_text -from utils.img_utils import pic2b64 +from utils.image_utils import pic2b64 -__plugin_name__ = '鲁迅说' +__plugin_name__ = "鲁迅说" -__plugin_usage__ = '用法:鲁迅说 [消息]' +__plugin_usage__ = "用法:鲁迅说 [消息]" _ulmt = UserExistLimiter() @@ -22,17 +22,17 @@ luxun = on_command("鲁迅说过", aliases={"鲁迅说"}, priority=5, block=True @luxun.handle() async def handle(bot: Bot, event: MessageEvent, state: T_State): if _ulmt.check(event.user_id): - await luxun.finish('你的鲁迅正在说,等会', at_sender=True) + await luxun.finish("你的鲁迅正在说,等会", at_sender=True) args = get_message_text(event.json()) if args: - state["content"] = args if args else '烦了,不说了' + state["content"] = args if args else "烦了,不说了" @luxun.got("content", prompt="你让鲁迅说点啥?") async def handle_event(bot: Bot, event: MessageEvent, state: T_State): filename = str(event.user_id) + "_.jpg" content = state["content"].strip() - if content.startswith(',') or content.startswith(','): + if content.startswith(",") or content.startswith(","): content = content[1:] _ulmt.set_True(event.user_id) if len(content) > 20: @@ -40,10 +40,12 @@ async def handle_event(bot: Bot, event: MessageEvent, state: T_State): await luxun.finish("太长了, 鲁迅说不完!", at_sender=True) else: if len(content) >= 12: - content = content[:12] + '\n' + content[12:] + content = content[:12] + "\n" + content[12:] img = image(b64=process_pic(content, filename)) - logger.info(f"USER {event.user_id} GROUP " - f"{event.group_id if event.message_type != 'private' else 'private'} 鲁迅说过 {content}") + logger.info( + f"USER {event.user_id} GROUP " + f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'} 鲁迅说过 {content}" + ) await luxun.send(img) _ulmt.set_False(event.user_id) @@ -64,5 +66,3 @@ def process_pic(content, filename) -> str: current_h += h + pad draw.text((320, 400), "——鲁迅", font=font2, fill=(255, 255, 255)) return pic2b64(bk_img) - - diff --git a/plugins/move_img/__init__.py b/plugins/move_img/__init__.py index ef2eca0c..2bc033b6 100644 --- a/plugins/move_img/__init__.py +++ b/plugins/move_img/__init__.py @@ -3,30 +3,32 @@ from services.log import logger from nonebot import on_command from nonebot.rule import to_me from nonebot.typing import T_State -from nonebot.adapters.cqhttp import Bot, MessageEvent +from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent from configs.config import IMAGE_DIR_LIST from utils.utils import is_number, cn2py from configs.path_config import IMAGE_PATH -__plugin_name__ = '移动图片' -__plugin_usage__ = '移动图片帮助:\n\t' \ - '1.查看列表 --> 指令: 移动图片 列表/目录\n\t' \ - '2.移动图片 源 目的 id\n\t\t示例: 移动图片 色图 美图 1234' +__plugin_name__ = "移动图片" +__plugin_usage__ = ( + "移动图片帮助:\n\t" + "1.查看列表 --> 指令: 移动图片 列表/目录\n\t" + "2.移动图片 源 目的 id\n\t\t示例: 移动图片 色图 美图 1234" +) -move_img = on_command('移动图片', priority=5, rule=to_me(), block=True) +move_img = on_command("移动图片", priority=5, rule=to_me(), block=True) @move_img.args_parser async def parse(bot: Bot, event: MessageEvent, state: T_State): - if str(event.get_message()) in ['取消', '算了']: + if str(event.get_message()) in ["取消", "算了"]: await move_img.finish("已取消操作..", at_sender=True) - if state["_current_key"] in ['source_path', 'destination_path']: + if state["_current_key"] in ["source_path", "destination_path"]: if str(event.get_message()) not in IMAGE_DIR_LIST: await move_img.reject("此目录不正确,请重新输入目录!") state[state["_current_key"]] = str(event.get_message()) - if state["_current_key"] == 'id': + if state["_current_key"] == "id": if not is_number(str(event.get_message())): await move_img.reject("id不正确!请重新输入数字...") state[state["_current_key"]] = str(event.get_message()) @@ -37,12 +39,17 @@ async def _(bot: Bot, event: MessageEvent, state: T_State): raw_arg = str(event.get_message()).strip() if raw_arg: args = raw_arg.split(" ") - if args[0] in ['帮助']: + if args[0] in ["帮助"]: await move_img.finish(__plugin_usage__) - if len(args) >= 3 and args[0] in IMAGE_DIR_LIST and args[1] in IMAGE_DIR_LIST and is_number(args[2]): - state['source_path'] = args[0] - state['destination_path'] = args[1] - state['id'] = args[2] + if ( + len(args) >= 3 + and args[0] in IMAGE_DIR_LIST + and args[1] in IMAGE_DIR_LIST + and is_number(args[2]) + ): + state["source_path"] = args[0] + state["destination_path"] = args[1] + state["id"] = args[2] else: await move_img.finish("参数错误,请重试", at_sender=True) @@ -51,34 +58,36 @@ async def _(bot: Bot, event: MessageEvent, state: T_State): @move_img.got("destination_path", prompt="要移动到哪个图库?") @move_img.got("id", prompt="要移动的图片id是?") async def _(bot: Bot, event: MessageEvent, state: T_State): - img_id = state['id'] - source_path = IMAGE_PATH + cn2py(state['source_path']) - destination_path = IMAGE_PATH + cn2py(state['destination_path']) + img_id = state["id"] + source_path = IMAGE_PATH + cn2py(state["source_path"]) + destination_path = IMAGE_PATH + cn2py(state["destination_path"]) max_id = len(os.listdir(source_path)) - 1 des_max_id = len(os.listdir(destination_path)) if int(img_id) > max_id or int(img_id) < 0: await move_img.finish(f"Id超过上下限,上限:{max_id}", at_sender=True) try: - os.rename(source_path + img_id + ".jpg", destination_path + str(des_max_id) + ".jpg") - logger.info(f"移动 {source_path}{img_id}.jpg ---> {destination_path}{des_max_id} 移动成功") + os.rename( + source_path + img_id + ".jpg", destination_path + str(des_max_id) + ".jpg" + ) + logger.info( + f"移动 {source_path}{img_id}.jpg ---> {destination_path}{des_max_id} 移动成功" + ) except Exception as e: - logger.warning(f"移动 {source_path}{img_id}.jpg ---> {destination_path}{des_max_id} 移动失败 e:{e}") + logger.warning( + f"移动 {source_path}{img_id}.jpg ---> {destination_path}{des_max_id} 移动失败 e:{e}" + ) await move_img.finish(f"移动图片id:{img_id} 失败了...", at_sender=True) if max_id > 0: try: os.rename(source_path + str(max_id) + ".jpg", source_path + img_id + ".jpg") logger.info(f"{source_path}{max_id}.jpg 替换 {source_path}{img_id}.jpg 成功") except Exception as e: - logger.warning(f"{source_path}{max_id}.jpg 替换 {source_path}{img_id}.jpg 失败 e:{e}") + logger.warning( + f"{source_path}{max_id}.jpg 替换 {source_path}{img_id}.jpg 失败 e:{e}" + ) await move_img.finish(f"替换图片id:{max_id} -> {img_id} 失败了...", at_sender=True) - logger.info(f"USER {event.user_id} GROUP {event.group_id if event.message_type != 'private' else 'private'} ->" - f" {source_path} --> {destination_path} (id:{img_id}) 移动图片成功") + logger.info( + f"USER {event.user_id} GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'} ->" + f" {source_path} --> {destination_path} (id:{img_id}) 移动图片成功" + ) await move_img.finish(f"移动图片 id:{img_id} --> id:{des_max_id}成功", at_sender=True) - - - - - - - - diff --git a/plugins/mute.py b/plugins/mute.py index 22d22852..9ada8000 100644 --- a/plugins/mute.py +++ b/plugins/mute.py @@ -6,28 +6,34 @@ from nonebot.typing import T_State import time from nonebot.adapters.cqhttp.exception import ActionFailed from configs.path_config import DATA_PATH, IMAGE_PATH -from utils.img_utils import get_img_hash +from utils.image_utils import get_img_hash from services.log import logger import aiohttp import aiofiles from configs.config import MUTE_DEFAULT_COUNT, MUTE_DEFAULT_TIME, MUTE_DEFAULT_DURATION + try: import ujson as json except ModuleNotFoundError: import json -__plugin_name__ = '刷屏禁言' +__plugin_name__ = "刷屏禁言" -__plugin_usage__ = '刷屏禁言检测' +__plugin_usage__ = "刷屏禁言检测" mute = on_message(priority=1, block=False) -mute_setting = on_command('mute_setting', aliases={'设置检测时间', '设置检测次数', '设置禁言时长', '刷屏检测设置'}, permission=GROUP, block=True) +mute_setting = on_command( + "mute_setting", + aliases={"设置检测时间", "设置检测次数", "设置禁言时长", "刷屏检测设置"}, + permission=GROUP, + block=True, +) def get_data(): try: - with open(DATA_PATH + 'group_mute_data.json', 'r', encoding='utf8') as f: + with open(DATA_PATH + "group_mute_data.json", "r", encoding="utf8") as f: data = json.load(f) except (ValueError, FileNotFoundError): data = {} @@ -36,16 +42,18 @@ def get_data(): def save_data(): global mute_data - with open(DATA_PATH + 'group_mute_data.json', 'w', encoding='utf8') as f: + with open(DATA_PATH + "group_mute_data.json", "w", encoding="utf8") as f: json.dump(mute_data, f, indent=4) async def download_img_and_hash(url, group_id): async with aiohttp.ClientSession() as session: async with session.get(url, proxy=get_local_proxy(), timeout=10) as response: - async with aiofiles.open(IMAGE_PATH + f'temp/mute_{group_id}_img.jpg', 'wb') as f: + async with aiofiles.open( + IMAGE_PATH + f"temp/mute_{group_id}_img.jpg", "wb" + ) as f: await f.write(await response.read()) - return str(get_img_hash(IMAGE_PATH + f'temp/mute_{group_id}_img.jpg')) + return str(get_img_hash(IMAGE_PATH + f"temp/mute_{group_id}_img.jpg")) mute_dict = {} @@ -57,44 +65,48 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State): group_id = str(event.group_id) msg = get_message_text(event.json()) imgs = get_message_imgs(event.json()) - img_hash = '' + img_hash = "" for img in imgs: img_hash += await download_img_and_hash(img, event.group_id) msg += img_hash if not mute_data.get(group_id): mute_data[group_id] = { - 'count': MUTE_DEFAULT_COUNT, - 'time': MUTE_DEFAULT_TIME, - 'duration': MUTE_DEFAULT_DURATION + "count": MUTE_DEFAULT_COUNT, + "time": MUTE_DEFAULT_TIME, + "duration": MUTE_DEFAULT_DURATION, } if not mute_dict.get(event.user_id): - mute_dict[event.user_id] = { - 'time': time.time(), - 'count': 1, - 'msg': msg - } + mute_dict[event.user_id] = {"time": time.time(), "count": 1, "msg": msg} else: if state["_prefix"]["raw_command"] or not msg: return - if msg and msg.find(mute_dict[event.user_id]['msg']) != -1: - mute_dict[event.user_id]['count'] += 1 + if msg and msg.find(mute_dict[event.user_id]["msg"]) != -1: + mute_dict[event.user_id]["count"] += 1 else: - mute_dict[event.user_id]['time'] = time.time() - mute_dict[event.user_id]['count'] = 1 - mute_dict[event.user_id]['msg'] = msg - if time.time() - mute_dict[event.user_id]['time'] > mute_data[group_id]['time']: - mute_dict[event.user_id]['time'] = time.time() - mute_dict[event.user_id]['count'] = 1 - if mute_dict[event.user_id]['count'] > mute_data[group_id]['count'] and\ - time.time() - mute_dict[event.user_id]['time'] < mute_data[group_id]['time']: + mute_dict[event.user_id]["time"] = time.time() + mute_dict[event.user_id]["count"] = 1 + mute_dict[event.user_id]["msg"] = msg + if time.time() - mute_dict[event.user_id]["time"] > mute_data[group_id]["time"]: + mute_dict[event.user_id]["time"] = time.time() + mute_dict[event.user_id]["count"] = 1 + if ( + mute_dict[event.user_id]["count"] > mute_data[group_id]["count"] + and time.time() - mute_dict[event.user_id]["time"] + < mute_data[group_id]["time"] + ): try: - if mute_data[group_id]['duration'] != 0: - await bot.set_group_ban(group_id=event.group_id, user_id=event.user_id, - duration=mute_data[group_id]['duration']) - await mute.send('检测到恶意刷屏,真寻要把你关进小黑屋!', at_sender=True) - mute_dict[event.user_id]['count'] = 0 - logger.info(f'USER {event.user_id} GROUP {event.group_id} ' - f'检测刷屏 被禁言 {mute_data[group_id]["duration"] / 60} 分钟') + if mute_data[group_id]["duration"] != 0: + await bot.set_group_ban( + group_id=event.group_id, + user_id=event.user_id, + duration=mute_data[group_id]["duration"], + ) + await mute.send("检测到恶意刷屏,真寻要把你关进小黑屋!", at_sender=True) + mute_dict[event.user_id]["count"] = 0 + logger.info( + f"USER {event.user_id} GROUP {event.group_id} " + f'检测刷屏 被禁言 {mute_data[group_id]["duration"] / 60} 分钟' + ) except ActionFailed: pass @@ -103,29 +115,28 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State): async def _(bot: Bot, event: GroupMessageEvent, state: T_State): group_id = str(event.group_id) if not mute_data.get(group_id): - mute_data[group_id] = { - 'count': 10, - 'time': 7, - 'duration': 0 - } + mute_data[group_id] = {"count": 10, "time": 7, "duration": 0} msg = get_message_text(event.json()) - if state["_prefix"]["raw_command"] == '刷屏检测设置': - await mute_setting.finish(f'最大次数:{mute_data[group_id]["count"]} 次\n' - f'规定时间:{mute_data[group_id]["time"]} 秒\n' - f'禁言时长:{mute_data[group_id]["duration"] / 60} 分钟\n' - f'【在规定时间内发送相同消息超过最大次数则禁言\n当禁言时长为0时关闭此功能】') + if state["_prefix"]["raw_command"] == "刷屏检测设置": + await mute_setting.finish( + f'最大次数:{mute_data[group_id]["count"]} 次\n' + f'规定时间:{mute_data[group_id]["time"]} 秒\n' + f'禁言时长:{mute_data[group_id]["duration"] / 60} 分钟\n' + f"【在规定时间内发送相同消息超过最大次数则禁言\n当禁言时长为0时关闭此功能】" + ) if not is_number(msg): - await mute.finish('设置的参数必须是数字啊!', at_sender=True) - if state["_prefix"]["raw_command"] == '设置检测时间': - mute_data[group_id]['time'] = int(msg) - msg += '秒' - if state["_prefix"]["raw_command"] == '设置检测次数': - mute_data[group_id]['count'] = int(msg) - msg += ' 次' - if state["_prefix"]["raw_command"] == '设置禁言时长': - mute_data[group_id]['duration'] = int(msg) * 60 - msg += ' 分钟' + await mute.finish("设置的参数必须是数字啊!", at_sender=True) + if state["_prefix"]["raw_command"] == "设置检测时间": + mute_data[group_id]["time"] = int(msg) + msg += "秒" + if state["_prefix"]["raw_command"] == "设置检测次数": + mute_data[group_id]["count"] = int(msg) + msg += " 次" + if state["_prefix"]["raw_command"] == "设置禁言时长": + mute_data[group_id]["duration"] = int(msg) * 60 + msg += " 分钟" await mute_setting.send(f'刷屏检测:{state["_prefix"]["raw_command"]}为 {msg}') - logger.info(f'USER {event.user_id} GROUP {group_id} {state["_prefix"]["raw_command"]}:{msg}') + logger.info( + f'USER {event.user_id} GROUP {group_id} {state["_prefix"]["raw_command"]}:{msg}' + ) save_data() - diff --git a/plugins/my_info.py b/plugins/my_info.py index ae15619a..edca6e2f 100644 --- a/plugins/my_info.py +++ b/plugins/my_info.py @@ -6,15 +6,12 @@ from models.group_member_info import GroupInfoUser from datetime import timedelta from models.level_user import LevelUser -__plugin_name__ = '更新群组成员列表 [Hidden]' -__plugin_usage__ = ( - '用法:\n' - '更新群员的信息' -) +__plugin_name__ = "更新群组成员列表 [Hidden]" +__plugin_usage__ = "用法:\n" "更新群员的信息" get_my_group_info = on_command("我的信息", permission=GROUP, priority=1, block=True) -my_level = on_command('我的权限', permission=GROUP, priority=5, block=True) +my_level = on_command("我的权限", permission=GROUP, priority=5, block=True) @get_my_group_info.handle() @@ -24,7 +21,7 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State): async def get_member_info(user_qq: int, group_id: int) -> str: - user = await GroupInfoUser.select_member_info(user_qq, group_id) + user = await GroupInfoUser.get_member_info(user_qq, group_id) if user is None: return "该群员不在列表中,请更新群成员信息" result = "" @@ -36,5 +33,5 @@ async def get_member_info(user_qq: int, group_id: int) -> str: @my_level.handle() async def _(bot: Bot, event: GroupMessageEvent, state: T_State): if (level := await LevelUser.get_user_level(event.user_id, event.group_id)) == -1: - await my_level.finish('您目前没有任何权限了,硬要说的话就是0吧~', at_sender=True) - await my_level.finish(f'您目前的权限等级:{level}', at_sender=True) \ No newline at end of file + await my_level.finish("您目前没有任何权限了,硬要说的话就是0吧~", at_sender=True) + await my_level.finish(f"您目前的权限等级:{level}", at_sender=True) diff --git a/plugins/nickname.py b/plugins/nickname.py index d41251bd..7d2a8aec 100644 --- a/plugins/nickname.py +++ b/plugins/nickname.py @@ -10,166 +10,175 @@ from models.ban_user import BanUser from services.log import logger -__plugin_name__ = '昵称系统' +__plugin_name__ = "昵称系统" -__plugin_usage__ = '用法:\n' \ - '以后叫我 [名称]\n' \ - '真寻我是谁' +__plugin_usage__ = "用法:\n" "以后叫我 [名称]\n" "真寻我是谁" -nickname = on_command('nickname', - aliases={'以后叫我', '以后请叫我', '称呼我', '以后请称呼我', '以后称呼我', '叫我', '请叫我'}, - rule=to_me(), priority=5, block=True) +nickname = on_command( + "nickname", + aliases={"以后叫我", "以后请叫我", "称呼我", "以后请称呼我", "以后称呼我", "叫我", "请叫我"}, + rule=to_me(), + priority=5, + block=True, +) -my_nickname = on_command('my_name', aliases={'我叫什么', '我是谁', '我的名字'}, rule=to_me(), priority=5, block=True) +my_nickname = on_command( + "my_name", aliases={"我叫什么", "我是谁", "我的名字"}, rule=to_me(), priority=5, block=True +) -cancel_nickname = on_command('取消昵称', rule=to_me(), priority=5, block=True) +cancel_nickname = on_command("取消昵称", rule=to_me(), priority=5, block=True) @nickname.handle() async def _(bot: Bot, event: GroupMessageEvent, state: T_State): msg = get_message_text(event.json()) if not msg: - await nickname.finish('叫你空白?叫你虚空?叫你无名??', at_sender=True) + await nickname.finish("叫你空白?叫你虚空?叫你无名??", at_sender=True) if len(msg) > 10: - await nickname.finish('昵称可不能超过10个字!', at_sender=True) - if await GroupInfoUser.set_group_member_nickname(event.user_id, event.group_id, msg): + await nickname.finish("昵称可不能超过10个字!", at_sender=True) + if await GroupInfoUser.set_group_member_nickname( + event.user_id, event.group_id, msg + ): if len(msg) < 5: if random.random() < 0.5: msg = "~".join(msg) - await nickname.send(random.choice([ - '好啦好啦,我知道啦,{},以后就这么叫你吧', - '嗯嗯,小真寻记住你的昵称了哦,{}', - '好突然,突然要叫你昵称什么的...{}..', - '小真寻会好好记住的{}的,放心吧', - '好..好.,那窝以后就叫你{}了.' - ]).format(msg)) - logger.info(f'USER {event.user_id} GROUP {event.group_id} 设置群昵称 {msg}') + await nickname.send( + random.choice( + [ + "好啦好啦,我知道啦,{},以后就这么叫你吧", + "嗯嗯,小真寻记住你的昵称了哦,{}", + "好突然,突然要叫你昵称什么的...{}..", + "小真寻会好好记住的{}的,放心吧", + "好..好.,那窝以后就叫你{}了.", + ] + ).format(msg) + ) + logger.info(f"USER {event.user_id} GROUP {event.group_id} 设置群昵称 {msg}") else: - await nickname.send('设置昵称失败,请更新群组成员信息!', at_sender=True) - logger.warning(f'USER {event.user_id} GROUP {event.group_id} 设置群昵称 {msg} 失败') + await nickname.send("设置昵称失败,请更新群组成员信息!", at_sender=True) + logger.warning(f"USER {event.user_id} GROUP {event.group_id} 设置群昵称 {msg} 失败") @nickname.handle() async def _(bot: Bot, event: PrivateMessageEvent, state: T_State): msg = get_message_text(event.json()) if not msg: - await nickname.finish('叫你空白?叫你虚空?叫你无名??', at_sender=True) + await nickname.finish("叫你空白?叫你虚空?叫你无名??", at_sender=True) if len(msg) > 10: - await nickname.finish('不要超过10个字!', at_sender=True) + await nickname.finish("不要超过10个字!", at_sender=True) if await FriendUser.set_friend_nickname(event.user_id, msg): - await nickname.send(random.choice([ - '好啦好啦,我知道啦,{},以后就这么叫你吧', - '嗯嗯,小真寻记住你的昵称了哦,{}', - '好突然,突然要叫你昵称什么的...{}..', - '小真寻会好好记住的{}的,放心吧', - '好..好.,那窝以后就叫你{}了.' - ]).format(msg)) - logger.info(f'USER {event.user_id} 设置昵称 {msg}') + await nickname.send( + random.choice( + [ + "好啦好啦,我知道啦,{},以后就这么叫你吧", + "嗯嗯,小真寻记住你的昵称了哦,{}", + "好突然,突然要叫你昵称什么的...{}..", + "小真寻会好好记住的{}的,放心吧", + "好..好.,那窝以后就叫你{}了.", + ] + ).format(msg) + ) + logger.info(f"USER {event.user_id} 设置昵称 {msg}") else: - await nickname.send('设置昵称失败了,明天再来试一试!或联系管理员更新好友!', at_sender=True) - logger.warning(f'USER {event.user_id} 设置昵称 {msg} 失败') + await nickname.send("设置昵称失败了,明天再来试一试!或联系管理员更新好友!", at_sender=True) + logger.warning(f"USER {event.user_id} 设置昵称 {msg} 失败") @my_nickname.handle() async def _(bot: Bot, event: GroupMessageEvent, state: T_State): try: - nickname = await GroupInfoUser.get_group_member_nickname(event.user_id, event.group_id) + nickname_ = await GroupInfoUser.get_group_member_nickname( + event.user_id, event.group_id + ) except AttributeError: - nickname = '' - if nickname: - await my_nickname.send(random.choice([ - '我肯定记得你啊,你是{}啊', - '我不会忘记你的,你也不要忘记我!{}', - '哼哼,真寻记忆力可是很好的,{}', - '嗯?你是失忆了嘛...{}..', - '不要小看真寻的记忆力啊!笨蛋{}!QAQ', - '哎?{}..怎么了吗..突然这样问..' - ]).format(nickname)) + nickname_ = "" + if nickname_: + await my_nickname.send( + random.choice( + [ + "我肯定记得你啊,你是{}啊", + "我不会忘记你的,你也不要忘记我!{}", + "哼哼,真寻记忆力可是很好的,{}", + "嗯?你是失忆了嘛...{}..", + "不要小看真寻的记忆力啊!笨蛋{}!QAQ", + "哎?{}..怎么了吗..突然这样问..", + ] + ).format(nickname_) + ) else: - try: - nickname = (await GroupInfoUser.select_member_info(event.user_id, event.group_id)).user_name - except AttributeError: - nickname = event.user_id - await my_nickname.finish(random.choice([ - '对不起,我只记得一串神秘代码..{}\n【群管理快更新群组成员信息!】' - ]).format(nickname)) - await my_nickname.send(random.choice([ - '没..没有昵称嘛,{}', - '啊,你是{}啊,我想叫你的昵称!', - '是{}啊,有什么事吗?', - '你是{}?' - ]).format(nickname)) + nickname_ = event.sender.card if event.sender.card else event.sender.nickname + await my_nickname.send( + random.choice( + ["没..没有昵称嘛,{}", "啊,你是{}啊,我想叫你的昵称!", "是{}啊,有什么事吗?", "你是{}?"] + ).format(nickname_) + ) @my_nickname.handle() async def _(bot: Bot, event: PrivateMessageEvent, state: T_State): - nickname = await FriendUser.get_friend_nickname(event.user_id) - if nickname: - await my_nickname.send(random.choice([ - '我肯定记得你啊,你是{}啊', - '我不会忘记你的,你也不要忘记我!{}', - '哼哼,真寻记忆力可是很好的,{}', - '嗯?你是失忆了嘛...{}..', - '不要小看真寻的记忆力啊!笨蛋{}!QAQ', - '哎?{}..怎么了吗..突然这样问..' - ]).format(nickname)) + nickname_ = await FriendUser.get_friend_nickname(event.user_id) + if nickname_: + await my_nickname.send( + random.choice( + [ + "我肯定记得你啊,你是{}啊", + "我不会忘记你的,你也不要忘记我!{}", + "哼哼,真寻记忆力可是很好的,{}", + "嗯?你是失忆了嘛...{}..", + "不要小看真寻的记忆力啊!笨蛋{}!QAQ", + "哎?{}..怎么了吗..突然这样问..", + ] + ).format(nickname_) + ) else: - nickname = await FriendUser.get_user_name(event.user_id) - if nickname: - await my_nickname.send(random.choice([ - '没..没有昵称嘛,{}', - '啊,你是{}啊,我想叫你的昵称!', - '是{}啊,有什么事吗?', - '你是{}?' - ]).format(nickname)) - else: - nickname = event.user_id - await my_nickname.finish(random.choice([ - '对不起,我只记得一串神秘代码..{}\n【火速联系管理员更新好友列表!】' - ]).format(nickname)) + nickname_ = (await bot.get_stranger_info(user_id=event.user_id))["nickname"] + await my_nickname.send( + random.choice( + ["没..没有昵称嘛,{}", "啊,你是{}啊,我想叫你的昵称!", "是{}啊,有什么事吗?", "你是{}?"] + ).format(nickname_) + ) @cancel_nickname.handle() async def _(bot: Bot, event: GroupMessageEvent, state: T_State): - nickname = await GroupInfoUser.get_group_member_nickname(event.user_id, event.group_id) - if nickname: - await cancel_nickname.send(random.choice([ - '呜..真寻睡一觉就会忘记的..和梦一样..{}', - '窝知道了..{}..', - '是真寻哪里做的不好嘛..好吧..晚安{}', - '呃,{},下次我绝对绝对绝对不会再忘记你!', - '可..可恶!{}!太可恶了!呜' - ]).format(nickname)) - await GroupInfoUser.set_group_member_nickname(event.user_id, event.group_id, '') + nickname_ = await GroupInfoUser.get_group_member_nickname( + event.user_id, event.group_id + ) + if nickname_: + await cancel_nickname.send( + random.choice( + [ + "呜..真寻睡一觉就会忘记的..和梦一样..{}", + "窝知道了..{}..", + "是真寻哪里做的不好嘛..好吧..晚安{}", + "呃,{},下次我绝对绝对绝对不会再忘记你!", + "可..可恶!{}!太可恶了!呜", + ] + ).format(nickname_) + ) + await GroupInfoUser.set_group_member_nickname(event.user_id, event.group_id, "") await BanUser.ban(event.user_id, 9, 60) else: - await cancel_nickname.send('你在做梦吗?你没有昵称啊', at_sender=True) + await cancel_nickname.send("你在做梦吗?你没有昵称啊", at_sender=True) @cancel_nickname.handle() async def _(bot: Bot, event: PrivateMessageEvent, state: T_State): - nickname = await FriendUser.get_user_name(event.user_id) - if nickname: - await cancel_nickname.send(random.choice([ - '呜..真寻睡一觉就会忘记的..和梦一样..{}', - '窝知道了..{}..', - '是真寻哪里做的不好嘛..好吧..晚安{}', - '呃,{},下次我绝对绝对绝对不会再忘记你!', - '可..可恶!{}!太可恶了!呜' - ]).format(nickname)) + nickname_ = await FriendUser.get_friend_nickname(event.user_id) + if nickname_: + await cancel_nickname.send( + random.choice( + [ + "呜..真寻睡一觉就会忘记的..和梦一样..{}", + "窝知道了..{}..", + "是真寻哪里做的不好嘛..好吧..晚安 {}", + "呃,{},下次我绝对绝对绝对不会再忘记你!", + "可..可恶!{}!太可恶了!呜", + ] + ).format(nickname_) + ) await FriendUser.get_user_name(event.user_id) await BanUser.ban(event.user_id, 9, 60) else: - await cancel_nickname.send('你在做梦吗?你没有昵称啊', at_sender=True) - - - - - - - - - - - + await cancel_nickname.send("你在做梦吗?你没有昵称啊", at_sender=True) diff --git a/plugins/nonebot_plugin_manager/__init__.py b/plugins/nonebot_plugin_manager/__init__.py index b8272cce..d21d65cf 100644 --- a/plugins/nonebot_plugin_manager/__init__.py +++ b/plugins/nonebot_plugin_manager/__init__.py @@ -3,7 +3,13 @@ from nonebot.matcher import Matcher from nonebot.typing import T_State from nonebot.exception import IgnoredException from nonebot.message import run_preprocessor -from nonebot.adapters.cqhttp import Event, Bot, GroupMessageEvent, PrivateMessageEvent, MessageEvent +from nonebot.adapters.cqhttp import ( + Event, + Bot, + GroupMessageEvent, + PrivateMessageEvent, + MessageEvent, +) from configs.config import plugins2info_dict from utils.utils import FreqLimiter from models.ban_user import BanUser @@ -19,7 +25,6 @@ from .parser import npm_parser export = export() export.block_plugin = block_plugin export.unblock_plugin = unblock_plugin -export.unblock_plugin = unblock_plugin export.get_group_plugin_list = get_group_plugin_list # 注册 shell_like 事件响应器 @@ -39,37 +44,35 @@ async def _(matcher: Matcher, bot: Bot, event: Event, state: T_State): plugin_list = auto_update_plugin_list(loaded_plugin_list) # 无视本插件的 Matcher - if plugin not in plugins2info_dict or matcher.priority in [1, 9] or await BanUser.isban(event.user_id): + if ( + plugin not in plugins2info_dict + or matcher.priority in [1, 9] + or await BanUser.is_ban(event.user_id) + ): + return + if isinstance(event, PrivateMessageEvent) and plugin_list[plugin]["default"]: return - try: - if isinstance(event, PrivateMessageEvent) and plugin_list[plugin]["default"]: - return - except: - pass - - # print(matcher.module) - # print(f'plugin_list[plugin]["default"] = {plugin_list[plugin]["default"]}') - # print(f'{matcher.module} -> this is hook') if not plugin_list[plugin]["default"]: - if event.message_type == 'group': + if str(event.user_id) in bot.config.superusers: + return + if isinstance(event, GroupMessageEvent): if flmt.check(event.group_id): flmt.start_cd(event.group_id) - await bot.send_group_msg(group_id=event.group_id, message='此功能正在维护...') + await bot.send_group_msg(group_id=event.group_id, message="此功能正在维护...") else: if flmt.check(event.user_id): flmt.start_cd(event.user_id) - await bot.send_private_msg(user_id=event.user_id, message='此功能正在维护...') + await bot.send_private_msg(user_id=event.user_id, message="此功能正在维护...") raise IgnoredException(f"Nonebot Plugin Manager has blocked {plugin} !") - # print(plugin_list[plugin]) - # print(group_id) - # print(plugin_list[plugin][group_id]) - # print(not plugin_list[plugin][group_id]) + if group_id in plugin_list[plugin]: if not plugin_list[plugin][group_id]: - if plugin != 'ai' and matcher.type == 'message': + if plugin != "ai" and matcher.type == "message": if flmt.check(event.group_id): flmt.start_cd(event.group_id) - await bot.send_group_msg(group_id=event.group_id, message='该群未开启此功能..') + await bot.send_group_msg( + group_id=event.group_id, message="该群未开启此功能.." + ) raise IgnoredException(f"Nonebot Plugin Manager has blocked {plugin} !") @@ -80,7 +83,7 @@ async def _(bot: Bot, event: Event, state: T_State): is_admin = _is_admin(event) is_superuser = _is_superuser(bot, event) if group_id == 0: - group_id = 'default' + group_id = "default" if hasattr(args, "handle"): await plugin_manager.finish(args.handle(args, group_id, is_admin, is_superuser)) @@ -116,6 +119,4 @@ def _is_superuser(bot: Bot, event: Event) -> bool: return str(event.user_id) in bot.config.superusers -plugins_dict = { - -} +plugins_dict = {} diff --git a/plugins/nonebot_plugin_picsearcher/__init__.py b/plugins/nonebot_plugin_picsearcher/__init__.py index 26d9df03..a3baf29c 100644 --- a/plugins/nonebot_plugin_picsearcher/__init__.py +++ b/plugins/nonebot_plugin_picsearcher/__init__.py @@ -19,12 +19,9 @@ from .trace import get_des as get_des_trace from .yandex import get_des as get_des_yandex -__plugin_name__ = '识图' +__plugin_name__ = "识图" -__plugin_usage__ = '用法:识图 [参数](默认nao) [图片]\n' \ - '参数列表:\n' \ - '\t1.nao\n' \ - '\t2.asc' +__plugin_usage__ = "用法:识图 [参数](默认nao) [图片]\n" "参数列表:\n" "\t1.nao\n" "\t2.asc" async def get_des(url: str, mode: str, user_id: int): @@ -60,8 +57,8 @@ setu = on_command("识图", aliases={"search"}, block=True, priority=5) async def handle_first_receive(bot: Bot, event: MessageEvent, state: T_State): msg = get_message_text(event.json()) imgs = get_message_imgs(event.json()) - if msg in ['帮助']: - await setu.finish('示例:\n\t识图 (图片)\n\t识图asc (图片)') + if msg in ["帮助"]: + await setu.finish("示例:\n\t识图 (图片)\n\t识图asc (图片)") if imgs: state["setu"] = imgs[0] if msg: @@ -73,6 +70,7 @@ async def handle_first_receive(bot: Bot, event: MessageEvent, state: T_State): # async def get_func(bot: Bot, event: MessageEvent, state: dict): # pass + @setu.args_parser async def get_setu(bot: Bot, event: MessageEvent, state: T_State): imgs = get_message_imgs(event.json()) @@ -80,7 +78,7 @@ async def get_setu(bot: Bot, event: MessageEvent, state: T_State): if not imgs: await setu.reject() if msg: - state['mod'] = msg + state["mod"] = msg state["setu"] = imgs[0] @@ -100,8 +98,10 @@ async def get_setu(bot: Bot, event: MessageEvent, state: T_State): if idx == MAX_FIND_IMG_COUNT: break idx += 1 - logger.info(f"(USER {event.user_id}, GROUP " - f"{event.group_id if event.message_type != 'private' else 'private'}) 识图:{url}") + logger.info( + f"(USER {event.user_id}, GROUP " + f"{event.group_id if event.message_type != 'private' else 'private'}) 识图:{url}" + ) # image_data: List[Tuple] = await get_pic_from_url(url) # await setu.finish("hso") except IndexError: diff --git a/plugins/nonebot_plugin_picsearcher/ascii2d.py b/plugins/nonebot_plugin_picsearcher/ascii2d.py index c45635f4..b6b56547 100644 --- a/plugins/nonebot_plugin_picsearcher/ascii2d.py +++ b/plugins/nonebot_plugin_picsearcher/ascii2d.py @@ -6,7 +6,7 @@ from utils.utils import get_local_proxy from utils.user_agent import get_user_agent from configs.path_config import IMAGE_PATH from asyncio.exceptions import TimeoutError -from utils.init_result import image +from utils.message_builder import image from lxml.html import fromstring import aiohttp diff --git a/plugins/nonebot_plugin_picsearcher/saucenao.py b/plugins/nonebot_plugin_picsearcher/saucenao.py index c3533c39..58ebbe61 100644 --- a/plugins/nonebot_plugin_picsearcher/saucenao.py +++ b/plugins/nonebot_plugin_picsearcher/saucenao.py @@ -6,7 +6,7 @@ from utils.utils import get_local_proxy from utils.user_agent import get_user_agent from configs.path_config import IMAGE_PATH from asyncio.exceptions import TimeoutError -from utils.init_result import image +from utils.message_builder import image import aiohttp from lxml.html import fromstring diff --git a/plugins/nonebot_plugin_withdraw/__init__.py b/plugins/nonebot_plugin_withdraw/__init__.py index 657e118b..f0d4f4a3 100644 --- a/plugins/nonebot_plugin_withdraw/__init__.py +++ b/plugins/nonebot_plugin_withdraw/__init__.py @@ -5,6 +5,7 @@ from nonebot.rule import to_me from nonebot.typing import T_State, T_CalledAPIHook from .config import Config + global_config = get_driver().config withdraw_config = Config(**global_config.dict()) @@ -12,33 +13,34 @@ msg_ids = {} max_size = withdraw_config.withdraw_max_size -__plugin_name__ = '撤回' +__plugin_name__ = "撤回" -__plugin_usage__ = '用法:撤回 [消息位置](默认0)\n' \ - '示例:\n' \ - '\t撤回0 -> 撤回倒数第一条消息(即最新发送的消息)' \ - '\t撤回1 -> 撤回倒数第2条消息' +__plugin_usage__ = ( + "用法:撤回 [消息位置](默认0)\n" "示例:\n" "\t撤回0 -> 撤回倒数第一条消息(即最新发送的消息)" "\t撤回1 -> 撤回倒数第2条消息" +) def get_key(msg_type, id): - return f'{msg_type}_{id}' + return f"{msg_type}_{id}" -async def save_msg_id(bot: Bot, e: Exception, api: str, data: Dict[str, Any], result: Any) -> T_CalledAPIHook: +async def save_msg_id( + bot: Bot, e: Exception, api: str, data: Dict[str, Any], result: Any +) -> T_CalledAPIHook: try: - if api == 'send_msg': - msg_type = data['message_type'] - id = data['group_id'] if msg_type == 'group' else data['user_id'] - elif api == 'send_private_msg': - msg_type = 'private' - id = data['user_id'] - elif api == 'send_group_msg': - msg_type = 'group' - id = data['group_id'] + if api == "send_msg": + msg_type = data["message_type"] + id = data["group_id"] if msg_type == "group" else data["user_id"] + elif api == "send_private_msg": + msg_type = "private" + id = data["user_id"] + elif api == "send_group_msg": + msg_type = "group" + id = data["group_id"] else: return key = get_key(msg_type, id) - msg_id = result['message_id'] + msg_id = result["message_id"] if key not in msg_ids: msg_ids[key] = [] @@ -52,16 +54,16 @@ async def save_msg_id(bot: Bot, e: Exception, api: str, data: Dict[str, Any], re Bot._called_api_hook.add(save_msg_id) -withdraw = on_command('withdraw', aliases={'撤回'}, rule=to_me(), priority=1, block=True) +withdraw = on_command("withdraw", aliases={"撤回"}, rule=to_me(), priority=1, block=True) @withdraw.handle() async def _(bot: Bot, event: Event, state: T_State): if isinstance(event, GroupMessageEvent): - msg_type = 'group' + msg_type = "group" id = event.group_id elif isinstance(event, PrivateMessageEvent): - msg_type = 'private' + msg_type = "private" id = event.user_id else: return @@ -80,4 +82,4 @@ async def _(bot: Bot, event: Event, state: T_State): await bot.delete_msg(message_id=msg_ids[key][idx]) msg_ids[key].pop(idx) except: - await withdraw.finish('撤回失败,可能已超时') + await withdraw.finish("撤回失败,可能已超时") diff --git a/plugins/one_friend/__init__.py b/plugins/one_friend/__init__.py index b30ade13..2ae2763f 100644 --- a/plugins/one_friend/__init__.py +++ b/plugins/one_friend/__init__.py @@ -1,4 +1,3 @@ - import aiohttp from utils.user_agent import get_user_agent from io import BytesIO @@ -7,20 +6,23 @@ from nonebot import on_regex from nonebot.typing import T_State from nonebot.adapters.cqhttp import Bot, GroupMessageEvent from utils.utils import get_message_text, get_local_proxy, get_message_at -from utils.init_result import image +from utils.message_builder import image import re -from utils.img_utils import CreateImg +from utils.image_utils import CreateImg -__plugin_name__ = '我有一个朋友' +__plugin_name__ = "我有一个朋友" -__plugin_usage__ = '用法:我有一个朋友说/问 [消息] [at](不艾特则群员随机)' +__plugin_usage__ = "用法:我有一个朋友说/问 [消息] [at](不艾特则群员随机)" -one_friend = on_regex('^我.*?朋友.*?(想问问|说|让我问问|想问|让我问|想知道|让我帮他问问|让我' - '帮他问|让我帮忙问|让我帮忙问问|问).*', priority=5, block=True) +one_friend = on_regex( + "^我.*?朋友.*?(想问问|说|让我问问|想问|让我问|想知道|让我帮他问问|让我" "帮他问|让我帮忙问|让我帮忙问问|问).*", + priority=5, + block=True, +) async def get_pic(qq): - url = f'http://q1.qlogo.cn/g?b=qq&nk={qq}&s=100' + url = f"http://q1.qlogo.cn/g?b=qq&nk={qq}&s=100" async with aiohttp.ClientSession(headers=get_user_agent()) as session: async with session.get(url, proxy=get_local_proxy(), timeout=5) as response: return await response.read() @@ -31,27 +33,31 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State): msg = get_message_text(event.json()) qq = get_message_at(event.json()) if not qq: - qq = choice([x['user_id'] for x in - await bot.get_group_member_list(self_id=event.self_id, group_id=event.group_id)]) + qq = choice( + [ + x["user_id"] + for x in await bot.get_group_member_list( + self_id=event.self_id, group_id=event.group_id + ) + ] + ) else: qq = qq[0] - msg = re.search(r'^我.*?朋友.*?' - r'(想问问|说|让我问问|想问|让我问|想知道|让我帮他问问|让我帮他问|让我帮忙问|让我帮忙问问|问)(.*)', - msg) + msg = re.search( + r"^我.*?朋友.*?" r"(想问问|说|让我问问|想问|让我问|想知道|让我帮他问问|让我帮他问|让我帮忙问|让我帮忙问问|问)(.*)", msg + ) msg = msg.group(2) if not msg: - msg = '都不知道问什么' - msg = msg.replace('他', '我').replace('她', '我').replace('它', '我') + msg = "都不知道问什么" + msg = msg.replace("他", "我").replace("她", "我").replace("它", "我") ava = CreateImg(100, 100, background=BytesIO(await get_pic(qq))) ava.circle() text = CreateImg(60, 30, font_size=30) - text.text((0, 0), '朋友') + text.text((0, 0), "朋友") # text.show() - A = CreateImg(700, 150, font_size=25, color='white') + A = CreateImg(700, 150, font_size=25, color="white") A.paste(ava, (30, 25), True) A.paste(text, (150, 40)) A.text((150, 85), msg, (125, 125, 125)) await one_friend.send(image(b64=A.pic2bs4())) - - diff --git a/plugins/open_cases/__init__.py b/plugins/open_cases/__init__.py index 92499c0c..d779667c 100644 --- a/plugins/open_cases/__init__.py +++ b/plugins/open_cases/__init__.py @@ -7,28 +7,34 @@ from nonebot.permission import SUPERUSER import random from nonebot.plugin import MatcherGroup import re -from .open_cases_c import open_case, total_open_statistics, group_statistics, my_knifes_name, open_shilian_case +from .open_cases_c import ( + open_case, + total_open_statistics, + group_statistics, + my_knifes_name, + open_shilian_case, +) from .utils import util_get_buff_price, util_get_buff_img, update_count_daily -__plugin_name__ = '开箱' +__plugin_name__ = "开箱" __plugin_usage__ = ( - '用法:\n' - '看看你的人品罢了\n' - '目前只支持\n\t' - '1.狂牙大行动武器箱\n\t' - '2.突围大行动武器箱\n\t' - '3.命悬一线武器箱\n\t' - '4.裂空武器箱\n\t' - '5.光谱武器箱\n' - '示例:小真寻开箱 突围大行动(不输入指定武器箱则随机)\n' - '示例:我的开箱(开箱统计)\n' - '示例:群开箱统计\n' - '示例:我的金色' + "用法:\n" + "看看你的人品罢了\n" + "目前只支持\n\t" + "1.狂牙大行动武器箱\n\t" + "2.突围大行动武器箱\n\t" + "3.命悬一线武器箱\n\t" + "4.裂空武器箱\n\t" + "5.光谱武器箱\n" + "示例:小真寻开箱 突围大行动(不输入指定武器箱则随机)\n" + "示例:我的开箱(开箱统计)\n" + "示例:群开箱统计\n" + "示例:我的金色" ) _flmt = FreqLimiter(3) -cases_name = ['狂牙大行动', '突围大行动', '命悬一线', '裂空', '光谱'] +cases_name = ["狂牙大行动", "突围大行动", "命悬一线", "裂空", "光谱"] cases_matcher_group = MatcherGroup(priority=5, permission=GROUP, block=True) @@ -38,20 +44,24 @@ k_open_case = cases_matcher_group.on_command("开箱") @k_open_case.handle() async def _(bot: Bot, event: GroupMessageEvent, state: T_State): - if str(event.get_message()).strip() in ['帮助']: + if str(event.get_message()).strip() in ["帮助"]: await k_open_case.finish(__plugin_usage__) if not _flmt.check(event.user_id): - await k_open_case.finish('着什么急啊,慢慢来!', at_sender=True) + await k_open_case.finish("着什么急啊,慢慢来!", at_sender=True) _flmt.start_cd(event.user_id) case_name = get_message_text(event.json()) if case_name: result = await open_case(event.user_id, event.group_id, case_name) else: - result = await open_case(event.user_id, event.group_id, random.choice(cases_name)) + result = await open_case( + event.user_id, event.group_id, random.choice(cases_name) + ) await k_open_case.finish(result, at_sender=True) -total_case_data = cases_matcher_group.on_command("我的开箱", aliases={'开箱统计', '开箱查询', '查询开箱'}) +total_case_data = cases_matcher_group.on_command( + "我的开箱", aliases={"开箱统计", "开箱查询", "查询开箱"} +) @total_case_data.handle() @@ -75,7 +85,9 @@ my_kinfes = on_command("我的金色", priority=1, permission=GROUP, block=True) @my_kinfes.handle() async def _(bot: Bot, event: GroupMessageEvent, state: T_State): - await my_kinfes.finish(await my_knifes_name(event.user_id, event.group_id), at_sender=True) + await my_kinfes.finish( + await my_knifes_name(event.user_id, event.group_id), at_sender=True + ) open_shilian = cases_matcher_group.on_regex(".*连开箱") @@ -87,7 +99,7 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State): # await k_open_case.finish('着什么急啊,慢慢来!', at_sender=True) _flmt.start_cd(event.user_id) msg = get_message_text(event.json()) - rs = re.search(r'(.*)连开箱(.*)', msg) + rs = re.search(r"(.*)连开箱(.*)", msg) if rs: num = rs.group(1).strip() if is_number(num) or num_dict.get(num): @@ -96,61 +108,53 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State): except KeyError: num = int(num) if num > 30: - await open_shilian.finish('开箱次数不要超过30啊笨蛋!', at_sender=True) + await open_shilian.finish("开箱次数不要超过30啊笨蛋!", at_sender=True) else: - await open_shilian.finish('必须要是数字切不要超过30啊笨蛋!中文也可!', at_sender=True) + await open_shilian.finish("必须要是数字切不要超过30啊笨蛋!中文也可!", at_sender=True) case_name = rs.group(2).strip() - if case_name.find('武器箱') != -1: - case_name = case_name.replace('武器箱', '').strip() + if case_name.find("武器箱") != -1: + case_name = case_name.replace("武器箱", "").strip() if not case_name: case_name = random.choice(cases_name) elif case_name not in cases_name: - await open_shilian.finish('武器箱未收录!', at_sender=True) - await open_shilian.finish(await open_shilian_case(event.user_id, event.group_id, case_name, num), at_sender=True) - # await open_shilian.send("嘟嘟嘟...开箱中...") - # num = 10 - # if str(state["_prefix"]["raw_command"]).find('十') != -1: - # num = 10 - # elif str(state["_prefix"]["raw_command"]).find('五') != -1: - # num = 5 - # if case_name: - # await open_shilian.finish(await open_shilian_case(event.user_id, event.group_id, case_name, num), at_sender=True) - # else: - # await open_shilian.finish(await open_shilian_case(event.user_id, event.group_id, random.choice(cases_name), num), - # at_sender=True) + await open_shilian.finish("武器箱未收录!", at_sender=True) + await open_shilian.finish( + await open_shilian_case(event.user_id, event.group_id, case_name, num), + at_sender=True, + ) num_dict = { - '一': 1, - '二': 2, - '三': 3, - '四': 4, - '五': 5, - '六': 6, - '七': 7, - '八': 8, - '九': 9, - '十': 10, - '十一': 11, - '十二': 12, - '十三': 13, - '十四': 14, - '十五': 15, - '十六': 16, - '十七': 17, - '十八': 18, - '十九': 19, - '二十': 20, - '二十一': 21, - '二十二': 22, - '二十三': 23, - '二十四': 24, - '二十五': 25, - '二十六': 26, - '二十七': 27, - '二十八': 28, - '二十九': 29, - '三十': 30 + "一": 1, + "二": 2, + "三": 3, + "四": 4, + "五": 5, + "六": 6, + "七": 7, + "八": 8, + "九": 9, + "十": 10, + "十一": 11, + "十二": 12, + "十三": 13, + "十四": 14, + "十五": 15, + "十六": 16, + "十七": 17, + "十八": 18, + "十九": 19, + "二十": 20, + "二十一": 21, + "二十二": 22, + "二十三": 23, + "二十四": 24, + "二十五": 25, + "二十六": 26, + "二十七": 27, + "二十八": 28, + "二十九": 29, + "三十": 30, } @@ -172,10 +176,9 @@ async def _(bot: Bot, event: MessageEvent, state: T_State): # 重置开箱 @scheduler.scheduled_job( - 'cron', + "cron", hour=0, minute=1, ) async def _(): await update_count_daily() - diff --git a/plugins/open_cases/open_cases_c.py b/plugins/open_cases/open_cases_c.py index 1723cc59..e665540e 100644 --- a/plugins/open_cases/open_cases_c.py +++ b/plugins/open_cases/open_cases_c.py @@ -4,13 +4,13 @@ from services.log import logger from services.db_context import db from models.open_cases_user import OpenCasesUser from models.sigin_group_user import SignGroupUser -from utils.init_result import image +from utils.message_builder import image import pypinyin import random from .utils import get_price from models.buff_price import BuffPrice from PIL import Image -from utils.img_utils import alphabg2white_PIL, CreateImg +from utils.image_utils import alpha2white_PIL, CreateImg from configs.path_config import IMAGE_PATH import asyncio from utils.utils import cn2py @@ -221,7 +221,7 @@ async def open_shilian_case(user_qq: int, group: int, case_name: str, num: int = skin_name += ''.join(i) # img = image(skin_name, "cases/" + case, "png") wImg = CreateImg(200, 270, 200, 200) - wImg.paste(alphabg2white_PIL(Image.open(IMAGE_PATH + f'cases/{case}/{skin_name}.png').resize((200, 200), Image.ANTIALIAS)), (0, 0)) + wImg.paste(alpha2white_PIL(Image.open(IMAGE_PATH + f'cases/{case}/{skin_name}.png').resize((200, 200), Image.ANTIALIAS)), (0, 0)) wImg.text((5, 200), skin) wImg.text((5, 220), f'磨损:{str(mosun)[:9]}') wImg.text((5, 240), f'价格:{price_result}') @@ -370,7 +370,7 @@ def _pst_my_knife(w, h, knifes_list): style=pypinyin.NORMAL): skin_name += ''.join(i) knife_img = CreateImg(470, 600, 470, 470, font_size=20) - knife_img.paste(alphabg2white_PIL( + knife_img.paste(alpha2white_PIL( Image.open(IMAGE_PATH + f'cases/{case}/{skin_name}.png').resize((470, 470), Image.ANTIALIAS)), (0, 0)) knife_img.text((5, 500), f'\t{name}({itype})') knife_img.text((5, 530), f'\t磨损:{mosun}') diff --git a/plugins/open_cases/utils.py b/plugins/open_cases/utils.py index eaec3147..148bf6e2 100644 --- a/plugins/open_cases/utils.py +++ b/plugins/open_cases/utils.py @@ -12,9 +12,9 @@ from utils.utils import get_bot from models.group_remind import GroupRemind from utils.utils import get_cookie_text from asyncio.exceptions import TimeoutError -from .config import * +import pypinyin from nonebot.adapters.cqhttp.exception import ActionFailed -from configs.config import buff_proxy +from configs.config import BUFF_PROXY url = "https://buff.163.com/api/market/goods" # proxies = 'http://49.75.59.242:3128' @@ -60,7 +60,7 @@ async def util_get_buff_price(case_name: str = "狂牙大行动") -> str: "search": skin } try: - async with session.get(url, proxy=buff_proxy, params=parameter, timeout=20) as response: + async with session.get(url, proxy=BUFF_PROXY, params=parameter, timeout=20) as response: if response.status == 200: data = (await response.json())["data"] total_page = data["total_page"] @@ -168,7 +168,7 @@ async def util_get_buff_img(case_name: str = "狂牙大行动") -> str: print(skin) skin_name = '' # try: - async with session.get(url, proxy=buff_proxy, params=parameter, timeout=20) as response: + async with session.get(url, proxy=BUFF_PROXY, params=parameter, timeout=20) as response: if response.status == 200: data = (await response.json())["data"] total_page = data["total_page"] diff --git a/plugins/parse_bilibili_json.py b/plugins/parse_bilibili_json.py index 75d31cca..5a348422 100644 --- a/plugins/parse_bilibili_json.py +++ b/plugins/parse_bilibili_json.py @@ -1,4 +1,4 @@ -from nonebot import on_message +from nonebot import on_message, on_command from services.log import logger from nonebot.adapters.cqhttp import Bot, GroupMessageEvent from nonebot.typing import T_State @@ -7,63 +7,55 @@ import json from utils.user_agent import get_user_agent from nonebot.adapters.cqhttp.permission import GROUP from bilibili_api import video -from utils.init_result import image +from utils.message_builder import image from models.group_remind import GroupRemind from nonebot.adapters.cqhttp.exception import ActionFailed import time import aiohttp from bilibili_api import settings + if get_local_proxy(): settings.proxy = get_local_proxy() - parse_bilibili_json = on_message(priority=1, permission=GROUP, block=False) @parse_bilibili_json.handle() async def _(bot: Bot, event: GroupMessageEvent, state: T_State): - if await GroupRemind.get_status(event.group_id, 'blpar') and get_message_json(event.json()): - data = json.loads(get_message_json(event.json())['data']) + if await GroupRemind.get_status(event.group_id, "blpar") and get_message_json( + event.json() + ): + data = json.loads(get_message_json(event.json())[0]["data"]) if data: - if data.get('desc') == '哔哩哔哩': + if data.get("desc") == "哔哩哔哩": async with aiohttp.ClientSession(headers=get_user_agent()) as session: - async with session.get(data['meta']['detail_1']['qqdocurl'], proxy=get_local_proxy(), timeout=7) as response: + async with session.get( + data["meta"]["detail_1"]["qqdocurl"], + proxy=get_local_proxy(), + timeout=7, + ) as response: url = str(response.url).split("?")[0] - bvid = url.split('/')[-1] + bvid = url.split("/")[-1] vd_info = await video.Video(bvid=bvid).get_info() - aid = vd_info['aid'] - title = vd_info['title'] - author = vd_info['owner']['name'] - reply = vd_info['stat']['reply'] # 回复 - favorite = vd_info['stat']['favorite'] # 收藏 - coin = vd_info['stat']['coin'] # 投币 + aid = vd_info["aid"] + title = vd_info["title"] + author = vd_info["owner"]["name"] + reply = vd_info["stat"]["reply"] # 回复 + favorite = vd_info["stat"]["favorite"] # 收藏 + coin = vd_info["stat"]["coin"] # 投币 # like = vd_info['stat']['like'] # 点赞 # danmu = vd_info['stat']['danmaku'] # 弹幕 - date = time.strftime("%Y-%m-%d", time.localtime(vd_info['ctime'])) + date = time.strftime("%Y-%m-%d", time.localtime(vd_info["ctime"])) try: await parse_bilibili_json.send( - image(vd_info["pic"]) + - f'\nav{aid}\n标题:{title}\n' - f'UP:{author}\n' - f'上传日期:{date}\n' - f'回复:{reply},收藏:{favorite},投币:{coin}\n' - f'{url}' + image(vd_info["pic"]) + f"\nav{aid}\n标题:{title}\n" + f"UP:{author}\n" + f"上传日期:{date}\n" + f"回复:{reply},收藏:{favorite},投币:{coin}\n" + f"{url}" ) except ActionFailed: - logger.warning(f'{event.group_id} 发送bilibili解析失败') - logger.info(f'USER {event.user_id} GROUP {event.group_id} 解析bilibili转发 {url}') - - - - - - - - - - - - - - - + logger.warning(f"{event.group_id} 发送bilibili解析失败") + logger.info( + f"USER {event.user_id} GROUP {event.group_id} 解析bilibili转发 {url}" + ) diff --git a/plugins/pid_search.py b/plugins/pid_search.py index 0b94602c..e73dff0e 100644 --- a/plugins/pid_search.py +++ b/plugins/pid_search.py @@ -2,13 +2,12 @@ from nonebot import on_command from nonebot.adapters.cqhttp import Bot, MessageEvent, Message, GroupMessageEvent from nonebot.typing import T_State from utils.utils import get_message_text, is_number -from utils.init_result import image +from utils.message_builder import image import aiohttp from services.log import logger from asyncio.exceptions import TimeoutError import asyncio import aiofiles -from io import BytesIO from configs.path_config import IMAGE_PATH try: @@ -16,38 +15,38 @@ try: except ModuleNotFoundError: import json -__plugin_name__ = 'p搜' -__plugin_usage__ = '用法: 通过pid在Pixiv上搜索图片\n格式:p搜 [pid]\n\t示例:p搜 79520120' +__plugin_name__ = "p搜" +__plugin_usage__ = "用法: 通过pid在Pixiv上搜索图片\n格式:p搜 [pid]\n\t示例:p搜 79520120" -pid_search = on_command("p搜", aliases={'pixiv搜', 'P搜'}, priority=5, block=True) +pid_search = on_command("p搜", aliases={"pixiv搜", "P搜"}, priority=5, block=True) -url = 'https://api.fantasyzone.cc/tu/search.php' +url = "https://api.fantasyzone.cc/tu/search.php" @pid_search.args_parser async def _(bot: Bot, event: MessageEvent, state: T_State): pid = get_message_text(event.json()) if pid: - if pid in ['取消', '算了']: - await pid_search.finish('已取消操作...') + if pid in ["取消", "算了"]: + await pid_search.finish("已取消操作...") if not is_number(pid): - await pid_search.reject('笨蛋,重新输入数!字!', at_sender=True) - state['pid'] = pid + await pid_search.reject("笨蛋,重新输入数!字!", at_sender=True) + state["pid"] = pid @pid_search.handle() async def _(bot: Bot, event: MessageEvent, state: T_State): pid = get_message_text(event.json()) if pid: - state['pid'] = pid + state["pid"] = pid -@pid_search.got('pid', prompt='需要查询的图片PID是?') +@pid_search.got("pid", prompt="需要查询的图片PID是?") async def _(bot: Bot, event: MessageEvent, state: T_State): - pid = state['pid'] + pid = state["pid"] params = { - 'id': pid, - 'p': 1, + "id": pid, + "p": 1, } async with aiohttp.ClientSession() as session: for _ in range(10): @@ -57,13 +56,13 @@ async def _(bot: Bot, event: MessageEvent, state: T_State): except TimeoutError: pass else: - if not data['width'] and not data['height']: - await pid_search.finish(f'没有搜索到 PID:{pid} 的图片', at_sender=True) - pid = data['id'] - title = data['title'] - author = data['userName'] - author_id = data['userId'] - img_url = data['url'] + if not data["width"] and not data["height"]: + await pid_search.finish(f"没有搜索到 PID:{pid} 的图片", at_sender=True) + pid = data["id"] + title = data["title"] + author = data["userName"] + author_id = data["userId"] + img_url = data["url"] for _ in range(5): try: await download_pic(img_url, event.user_id) @@ -72,29 +71,38 @@ async def _(bot: Bot, event: MessageEvent, state: T_State): else: break else: - await pid_search.finish('图片下载失败了....', at_sender=True) - tmp = '' + await pid_search.finish("图片下载失败了....", at_sender=True) + tmp = "" if isinstance(event, GroupMessageEvent): - tmp = '\n【注】将在30后撤回......' - msg_id = await pid_search.send(Message(f'title:{title}\n' - f'pid:{pid}\n' - f'author:{author}\n' - f'author_id:{author_id}\n' - f'{image(f"pid_search_{event.user_id}.png", "temp")}' - f'{tmp}')) + tmp = "\n【注】将在30后撤回......" + msg_id = await pid_search.send( + Message( + f"title:{title}\n" + f"pid:{pid}\n" + f"author:{author}\n" + f"author_id:{author_id}\n" + f'{image(f"pid_search_{event.user_id}.png", "temp")}' + f"{tmp}" + ) + ) logger.info( - f"(USER {event.user_id}, GROUP {event.group_id if event.message_type != 'private' else 'private'})" - f" 查询图片 PID:{pid}") + f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" + f" 查询图片 PID:{pid}" + ) if isinstance(event, GroupMessageEvent): await asyncio.sleep(30) - await bot.delete_msg(message_id=msg_id['message_id'], self_id=int(bot.self_id)) + await bot.delete_msg( + message_id=msg_id["message_id"], self_id=int(bot.self_id) + ) break else: - await pid_search.finish('图片下载失败了....', at_sender=True) + await pid_search.finish("图片下载失败了....", at_sender=True) async def download_pic(img_url: str, user_id: int): async with aiohttp.ClientSession() as session: async with session.get(img_url, timeout=2) as res: - async with aiofiles.open(f'{IMAGE_PATH}/temp/pid_search_{user_id}.png', 'wb') as f: + async with aiofiles.open( + f"{IMAGE_PATH}/temp/pid_search_{user_id}.png", "wb" + ) as f: await f.write(await res.read()) diff --git a/plugins/pix_gallery/__init__.py b/plugins/pix_gallery/__init__.py new file mode 100644 index 00000000..5cf5d3dc --- /dev/null +++ b/plugins/pix_gallery/__init__.py @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/plugins/pix_gallery/data_source.py b/plugins/pix_gallery/data_source.py new file mode 100644 index 00000000..76796c0c --- /dev/null +++ b/plugins/pix_gallery/data_source.py @@ -0,0 +1,353 @@ +from aiohttp.client_exceptions import ( + ClientOSError, + ServerDisconnectedError, + ClientConnectorError, +) +from asyncpg.exceptions import UniqueViolationError +from asyncio.locks import Semaphore +from aiohttp import ClientPayloadError +from aiohttp.client import ClientSession +from asyncio.exceptions import TimeoutError +from models.pixiv import Pixiv +from typing import List +from utils.utils import get_local_proxy +from utils.image_utils import CreateImg +from services.log import logger +from configs.config import HIBIAPI_BOOKMARKS, HIBIAPI +from configs.path_config import TEMP_PATH +import platform +import aiohttp +import asyncio +import aiofiles +import math + +try: + import ujson as json +except ModuleNotFoundError: + import json + +if str(platform.system()).lower() == "windows": + policy = asyncio.WindowsSelectorEventLoopPolicy() + asyncio.set_event_loop_policy(policy) + +search_url = f"{HIBIAPI}/api/pixiv/search" +member_illust_url = f"{HIBIAPI}/api/pixiv/member_illust" +illust_url = f"{HIBIAPI}/api/pixiv/illust" + +headers = { + "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6;" + " rv:2.0.1) Gecko/20100101 Firefox/4.0.1", + "Referer": "https://www.pixiv.net", +} + + +# 开始更新 +async def start_update_image_url(current_keyword: List[str], black_pid: List[str]): + pid_count = 0 + pic_count = 0 + tasks = [] + semaphore = asyncio.Semaphore(10) + async with aiohttp.ClientSession(headers=headers) as session: + for keyword in current_keyword: + for page in range(1, 110): + if keyword.startswith("uid:"): + url = member_illust_url + params = {"id": keyword[4:], "page": page} + if page == 30: + break + elif keyword.startswith("pid:"): + url = illust_url + params = {"id": keyword[4:]} + else: + url = search_url + params = {"word": keyword, "page": page} + tasks.append( + asyncio.ensure_future( + search_image( + url, keyword, params, semaphore, session, page, black_pid + ) + ) + ) + if keyword.startswith("pid:"): + break + result = await asyncio.gather(*tasks) + for x in result: + pid_count += x[0] + pic_count += x[1] + return pid_count, pic_count + + +async def search_image( + url: str, + keyword: str, + params: dict, + semaphore: Semaphore, + session: ClientSession, + page: int = 1, + black: List[str] = None, +): + tmp_pid = [] + pic_count = 0 + pid_count = 0 + async with semaphore: + try: + async with session.get( + url, + params=params, + proxy=get_local_proxy(), + ) as response: + data = await response.json() + if ( + not data + or data.get("error") + or (not data.get("illusts") and not data.get("illust")) + ): + return 0, 0 + if url != illust_url: + logger.info(f'{keyword}: 获取数据成功...数据总量:{len(data["illusts"])}') + data = data["illusts"] + else: + logger.info(f'获取数据成功...PID:{params.get("id")}') + data = [data["illust"]] + img_data = {} + for x in data: + pid = x["id"] + title = x["title"] + width = x["width"] + height = x["height"] + view = x["total_view"] + bookmarks = x["total_bookmarks"] + uid = x["user"]["id"] + author = x["user"]["name"] + tags = [] + for tag in x["tags"]: + for i in tag: + if tag[i]: + tags.append(tag[i]) + img_urls = [] + if x["page_count"] == 1: + img_urls.append(x["meta_single_page"]["original_image_url"]) + else: + for urls in x["meta_pages"]: + img_urls.append(urls["image_urls"]["original"]) + if ( + ( + bookmarks >= HIBIAPI_BOOKMARKS + or (url == member_illust_url and bookmarks >= 1500) + or (url == illust_url) + ) + and len(img_urls) < 10 + and _check_black(img_urls, black) + ): + img_data[pid] = { + "pid": pid, + "title": title, + "width": width, + "height": height, + "view": view, + "bookmarks": bookmarks, + "img_urls": img_urls, + "uid": uid, + "author": author, + "tags": tags, + } + else: + continue + for x in img_data.keys(): + data = img_data[x] + for img_url in data["img_urls"]: + img_p = img_url[img_url.rfind("_") + 1 : img_url.rfind(".")] + try: + if await Pixiv.add_image_data( + data["pid"], + data["title"], + data["width"], + data["height"], + data["view"], + data["bookmarks"], + img_url, + img_p, + data["uid"], + data["author"], + ",".join(data["tags"]), + ): + if data["pid"] not in tmp_pid: + pid_count += 1 + tmp_pid.append(data["pid"]) + pic_count += 1 + logger.info(f'存储图片PID:{data["pid"]} IMG_P:{img_p}') + # await download_image(img_url, session) + except UniqueViolationError: + logger.warning(f'{data["pid"]} | {img_url} 已存在...') + # 下载图片 + # if not os.path.exists(f'{image_path}/ + # {img_url[img_url.rfind("/") + 1:]}'): + # await download_image(img_url, session) + except (ServerDisconnectedError, ClientConnectorError, ClientOSError): + logger.warning("搜索图片服务被关闭,再次调用....") + await search_image(url, keyword, params, semaphore, session, page, black) + return pid_count, pic_count + + +# 下载图片 +async def download_image(img_url: str, session: ClientSession, _count: int = 1): + try: + async with session.get(img_url, proxy=get_local_proxy()) as response: + logger.info(f"下载图片 --> {img_url}") + async with aiofiles.open(f'tmp/{img_url.split("/")[-1]}', "wb") as f: + await f.write(await response.read()) + # async with semaphore: + # await asyncio.get_event_loop().run_in_executor(None, + # upload_image, img_url[img_url.rfind("/")+1:]) + except ServerDisconnectedError: + logger.warning(f"下载图片服务被关闭,第 {_count} 次调用....") + await download_image(img_url, session, _count + 1) + except ClientOSError: + logger.warning(f"远程连接被关闭,第 {_count} 次调用....") + await download_image( + img_url.replace("i.pximg.net", "i.pixiv.cat"), session, _count + 1 + ) + except TimeoutError: + logger.warning(f"下载或写入超时,第 {_count} 次调用....") + await download_image(img_url, session, _count + 1) + except ClientPayloadError: + pass + + +async def get_image(img_url: str, user_id: int) -> str: + try: + async with aiohttp.ClientSession(headers=headers) as session: + async with session.get( + img_url, + proxy=get_local_proxy(), + ) as response: + async with aiofiles.open( + f"{TEMP_PATH}/pix_{user_id}_{img_url[-10:-4]}.jpg", "wb" + ) as f: + await f.write(await response.read()) + return f"pix_{user_id}_{img_url[-10:-4]}.jpg" + except (ClientConnectorError, TimeoutError): + return await get_image(img_url, user_id) + + +# 检测UID或PID是否有效 +async def uid_pid_exists(id_: str) -> bool: + if id_.startswith("uid:"): + url = f"{HIBIAPI}/api/pixiv/member" + elif id_.startswith("pid:"): + url = illust_url + else: + return False + params = {"id": int(id_[4:])} + async with aiohttp.ClientSession(headers=headers) as session: + async with session.get(url, params=params, proxy=get_local_proxy()) as response: + data = await response.json() + if data.get("error"): + return False + return True + + +async def get_keyword_num(keyword: str) -> "int , int": + return await Pixiv.get_keyword_num(keyword.split()) + + +async def remove_image(pid: int, img_p: str) -> bool: + if img_p: + if "p" not in img_p: + img_p = f"p{img_p}" + return await Pixiv.remove_image_data(pid, img_p) + + +def gen_keyword_pic( + _pass_keyword: List[str], not_pass_keyword: List[str], is_superuser: bool +): + _keyword = [ + x + for x in _pass_keyword + if not x.startswith("uid:") + and not x.startswith("pid:") + and not x.startswith("black:") + ] + _uid = [x for x in _pass_keyword if x.startswith("uid:")] + _pid = [x for x in _pass_keyword if x.startswith("pid:")] + _n_keyword = [ + x + for x in not_pass_keyword + if not x.startswith("uid:") + and not x.startswith("pid:") + and not x.startswith("black:") + ] + _n_uid = [ + x + for x in not_pass_keyword + if x.startswith("uid:") and not x.startswith("black:") + ] + _n_pid = [ + x + for x in not_pass_keyword + if x.startswith("pid:") and not x.startswith("black:") + ] + img_width = 0 + img_data = { + "_keyword": {"width": 0, "data": _keyword}, + "_uid": {"width": 0, "data": _uid}, + "_pid": {"width": 0, "data": _pid}, + "_n_keyword": {"width": 0, "data": _n_keyword}, + "_n_uid": {"width": 0, "data": _n_uid}, + "_n_pid": {"width": 0, "data": _n_pid}, + } + for x in list(img_data.keys()): + img_data[x]["width"] = math.ceil(len(img_data[x]["data"]) / 40) + img_width += img_data[x]["width"] * 200 + if not is_superuser: + img_width = ( + img_width + - ( + img_data["_n_keyword"]["width"] + + img_data["_n_uid"]["width"] + + img_data["_n_pid"]["width"] + ) + * 200 + ) + del img_data["_n_keyword"] + del img_data["_n_pid"] + del img_data["_n_uid"] + current_width = 0 + A = CreateImg(img_width, 1100) + for x in list(img_data.keys()): + if img_data[x]["data"]: + img = CreateImg(img_data[x]["width"] * 200, 1100, 200, 1100, font_size=40) + start_index = 0 + end_index = 40 + total_index = img_data[x]["width"] * 40 + for _ in range(img_data[x]["width"]): + tmp = CreateImg(198, 1100, font_size=20) + text_img = CreateImg(198, 100, font_size=50) + key_str = "\n".join( + [key for key in img_data[x]["data"][start_index:end_index]] + ) + tmp.text((10, 100), key_str) + if x.find("_n") == -1: + text_img.text((24, 24), "已收录") + else: + text_img.text((24, 24), "待收录") + tmp.paste(text_img, (0, 0)) + start_index += 40 + end_index = ( + end_index + 40 if end_index + 40 <= total_index else total_index + ) + background_img = CreateImg(200, 1100, color="#FFE4C4") + background_img.paste(tmp, (1, 1)) + img.paste(background_img) + A.paste(img, (current_width, 0)) + current_width += img_data[x]["width"] * 200 + return A.pic2bs4() + + +def _check_black(img_urls: List[str], black: List[str]): + for b in black: + for img_url in img_urls: + # img_url = img_url[img_url.rfind('/')+1: img_url.rfind('.')] + if b in img_url: + return False + return True diff --git a/plugins/pix_gallery/pix.py b/plugins/pix_gallery/pix.py new file mode 100644 index 00000000..0e45a58b --- /dev/null +++ b/plugins/pix_gallery/pix.py @@ -0,0 +1,63 @@ +from nonebot import on_command +from nonebot.adapters.cqhttp.event import GroupMessageEvent +from nonebot.adapters.cqhttp.message import Message +from utils.utils import get_message_text, is_number +from utils.message_builder import image +from services.log import logger +from nonebot.adapters.cqhttp import Bot, MessageEvent +from nonebot.typing import T_State +from .data_source import get_image +from models.pixiv import Pixiv +import random + + +pix = on_command("pix", aliases={'PIX'}, priority=5, block=True) + + +@pix.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + num = 1 + keyword = get_message_text(event.json()) + if is_number(keyword): + all_image = await Pixiv.query_images(uid=int(keyword)) + elif keyword.lower().startswith("pid"): + pid = keyword.replace("pid", "").replace(":", "") + if not is_number(pid): + await pix.finish("PID必须是数字...", at_sender=True) + all_image = await Pixiv.query_images(pid=int(pid)) + else: + x = keyword.split() + if len(x) > 1: + if is_number(x[-1]): + num = int(x[-1]) + if num > 11: + num = random.randint(1, 10) + await pix.send(f"太贪心了,就给你发 {num}张 好了") + x = x[:-1] + keyword = " ".join(x) + all_image = await Pixiv.query_images(x) + if not all_image: + await pix.finish(f"未在图库中找到与 {keyword} 相关Tag/UID/PID的图片...", at_sender=True) + for _ in range(num): + if not all_image: + await pix.finish("坏了...发完了,没图了...") + img = random.choice(all_image) + all_image.remove(img) + img_url = img.img_url + pid = img.pid + title = img.title + author = img.author + uid = img.uid + # tags = img.tags + await pix.send( + Message( + f"title:{title}\n" + f"author:{author}\n" + f"PID:{pid}\nUID:{uid}\n" + f"{image(await get_image(img_url, event.user_id), 'temp')}" + ) + ) + logger.info( + f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" + f" 查看PIX图库PID: {pid}" + ) \ No newline at end of file diff --git a/plugins/pix_gallery/pix_add_keyword.py b/plugins/pix_gallery/pix_add_keyword.py new file mode 100644 index 00000000..35f18a65 --- /dev/null +++ b/plugins/pix_gallery/pix_add_keyword.py @@ -0,0 +1,115 @@ +from nonebot import on_command +from utils.utils import get_message_text, is_number +from services.log import logger +from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent +from nonebot.typing import T_State +from .data_source import uid_pid_exists +from models.pixiv_keyword_user import PixivKeywordUser +from models.pixiv import Pixiv +from nonebot.permission import SUPERUSER + +__plugin_name__ = "添加PIX关键词/UID/PID | 添加PIX黑名单" + +__plugin_usage__ = """ + 关键词:添加搜图的关键词(效果拉胯) + UID:搜集该画师的图片,可指定收藏数 + PID:收录单张图片 + PIX黑名单:可以是当个pid,也可以是pid_p0等 +""" + + +add_keyword = on_command("添加pix关键词", aliases={"添加pix关键字"}, priority=1, block=True) + +add_black_pid = on_command("添加pix黑名单", permission=SUPERUSER, priority=1, block=True) + +# 超级用户可以通过字符 -f 来强制收录不检查是否存在 +add_uid_pid = on_command( + "添加pixuid", + aliases={ + "添加pixpid", + }, + priority=1, + block=True, +) + + +@add_keyword.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + msg = get_message_text(event.json()) + group_id = -1 + if isinstance(event, GroupMessageEvent): + group_id = event.group_id + if msg: + if await PixivKeywordUser.add_keyword( + event.user_id, group_id, msg, bot.config.superusers + ): + await add_keyword.send( + f"已成功添加pixiv搜图关键词:{msg},请等待管理员通过该关键词!", at_sender=True + ) + logger.info( + f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" + f" 添加了pixiv搜图关键词:" + msg + ) + else: + await add_keyword.finish(f"该关键词 {msg} 已存在...") + else: + await add_keyword.finish(f"虚空关键词?.?.?.?") + + +@add_uid_pid.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + msg = get_message_text(event.json()) + exists_flag = True + if msg.find("-f") != -1 and str(event.user_id) in bot.config.superusers: + exists_flag = False + msg = msg.replace("-f", "").strip() + if msg: + for msg in msg.split(): + if not is_number(msg): + await add_uid_pid.finish("UID只能是数字的说...", at_sender=True) + if state["_prefix"]["raw_command"].lower().endswith("uid"): + msg = f"uid:{msg}" + else: + msg = f"pid:{msg}" + if await Pixiv.check_exists(int(msg[4:]), "p0"): + await add_uid_pid.finish(f"该PID:{msg[4:]}已存在...", at_sender=True) + if not await uid_pid_exists(msg) and exists_flag: + await add_uid_pid.finish("画师或作品不存在或搜索正在CD,请稍等...", at_sender=True) + group_id = -1 + if isinstance(event, GroupMessageEvent): + group_id = event.group_id + if await PixivKeywordUser.add_keyword( + event.user_id, group_id, msg, bot.config.superusers + ): + await add_uid_pid.send( + f"已成功添加pixiv搜图UID/PID:{msg[4:]},请等待管理员通过!", at_sender=True + ) + else: + await add_uid_pid.finish(f"该UID/PID:{msg[4:]} 已存在...") + else: + await add_uid_pid.finish("湮灭吧!虚空的UID!") + + +@add_black_pid.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + pid = get_message_text(event.json()) + img_p = "" + if "p" in pid: + img_p = pid.split("p")[-1] + pid = pid.replace("_", "") + pid = pid[: pid.find("p")] + if not is_number(pid): + await add_black_pid.finish("PID必须全部是数字!", at_sender=True) + if await PixivKeywordUser.add_keyword( + 114514, + 114514, + f"black:{pid}{f'_p{img_p}' if img_p else ''}", + bot.config.superusers, + ): + await add_black_pid.send(f"已添加PID:{pid} 至黑名单中...") + logger.info( + f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" + f" 添加了pixiv搜图黑名单 PID:{pid}" + ) + else: + await add_black_pid.send(f"PID:{pid} 已添加黑名单中,添加失败...") diff --git a/plugins/pix_gallery/pix_pass_del_keyword.py b/plugins/pix_gallery/pix_pass_del_keyword.py new file mode 100644 index 00000000..ecdf1cd1 --- /dev/null +++ b/plugins/pix_gallery/pix_pass_del_keyword.py @@ -0,0 +1,167 @@ +from nonebot import on_command +from nonebot.adapters.cqhttp.message import Message +from utils.utils import get_message_text, is_number +from utils.message_builder import at +from services.log import logger +from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent +from nonebot.permission import SUPERUSER +from nonebot.typing import T_State +from .data_source import remove_image +from models.pixiv_keyword_user import PixivKeywordUser +from models.pixiv import Pixiv + + +__plugin_name__ = "通过/取消/删除pix关键词 | 删除pix图片" + +__plugin_usage__ = """ + 通过/取消/删除pix关键词 [关键词/pid/uid] 示例:通过pix关键词萝莉,通过pix关键词uid:123456,通过pix关键词pid:123456 + 删除pix图片 *[pid] 示例:删除pix图片4223442 +""" + + +pass_keyword = on_command( + "通过pix关键词", + aliases={"通过pix关键字", "取消pix关键词", "取消pix关键字"}, + permission=SUPERUSER, + priority=1, + block=True, +) + +del_keyword = on_command( + "删除pix关键词", aliases={"删除pix关键字"}, permission=SUPERUSER, priority=1, block=True +) + +del_pic = on_command( + "删除pix图片", permission=SUPERUSER, priority=1, block=True +) + + +@del_keyword.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + msg = get_message_text(event.json()) + if not msg: + await del_keyword.finish("好好输入要删除什么关键字啊笨蛋!") + if is_number(msg): + msg = f"uid:{msg}" + if msg.lower().startswith("pid"): + msg = "pid:" + msg.replace("pid", "").replace(":", "") + if await PixivKeywordUser.delete_keyword(msg): + await del_keyword.send(f"删除搜图关键词/UID:{msg} 成功...") + logger.info( + f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" + f" 删除了pixiv搜图关键词:" + msg + ) + else: + await del_keyword.send(f"未查询到搜索关键词/UID/PID:{msg},删除失败!") + + +@del_pic.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + pid_arr = get_message_text(event.json()) + if pid_arr: + msg = "" + black_pid = "" + flag = False + pid_arr = pid_arr.split() + if pid_arr[-1] in ["-black", "-b"]: + flag = True + pid_arr = pid_arr[:-1] + for pid in pid_arr: + img_p = None + if "p" in pid or "ugoira" in pid: + if "p" in pid: + img_p = pid.split("p")[-1] + pid = pid.replace("_", "") + pid = pid[: pid.find("p")] + elif "ugoira" in pid: + img_p = pid.split("ugoira")[-1] + pid = pid.replace("_", "") + pid = pid[: pid.find("ugoira")] + if is_number(pid): + if await Pixiv.query_images(pid=int(pid), r18=2): + if await remove_image(int(pid), img_p): + msg += f'{pid}{f"_p{img_p}" if img_p else ""},' + if flag: + if await PixivKeywordUser.add_keyword( + 114514, + 114514, + f"black:{pid}{f'_p{img_p}' if img_p else ''}", + bot.config.superusers, + ): + black_pid += f'{pid}{f"_p{img_p}" if img_p else ""},' + logger.info( + f"(USER {event.user_id}, GROUP " + f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" + f" 删除了PIX图片 PID:{pid}{f'_p{img_p}' if img_p else ''}" + ) + else: + await del_pic.send( + f"PIX:删除pid:{pid}{f'_p{img_p}' if img_p else ''} 失败.." + ) + else: + await del_pic.send( + f"PIX:图片pix:{pid}{f'_p{img_p}' if img_p else ''} 不存在...无法删除.." + ) + else: + await del_pic.send(f"PID必须为数字!pid:{pid}", at_sender=True) + await del_pic.send(f"PIX:成功删除图片:{msg[:-1]}") + if flag: + await del_pic.send(f"成功图片PID加入黑名单:{black_pid[:-1]}") + else: + await del_pic.send("虚空删除?") + + +@pass_keyword.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + tmp = {"group": {}, "private": {}} + msg = get_message_text(event.json()) + if not msg: + await pass_keyword.finish("通过虚空的关键词/UID?离谱...") + msg = msg.split() + flag = True if state["_prefix"]["raw_command"][:2] == "通过" else False + for x in msg: + if x.lower().startswith("uid"): + x = x.replace("uid", "").replace(":", "") + x = f"uid:{x}" + elif x.lower().startswith("pid"): + x = x.replace("pid", "").replace(":", "") + x = f"pid:{x}" + if x.lower().find("pid") != -1 or x.lower().find("uid") != -1: + if not is_number(x[4:]): + await pass_keyword.send(f"UID/PID:{x} 非全数字,跳过该关键词...") + continue + user_id, group_id = await PixivKeywordUser.set_keyword_pass(x, flag) + if not user_id: + await pass_keyword.send(f"未找到关键词/UID:{x},请检查关键词/UID是否存在...") + continue + if flag: + if group_id == -1: + if not tmp["private"].get(user_id): + tmp["private"][user_id] = {"keyword": [x]} + else: + tmp["private"][user_id]["keyword"].append(x) + else: + if not tmp["group"].get(group_id): + tmp["group"][group_id] = {} + if not tmp["group"][group_id].get(user_id): + tmp["group"][group_id][user_id] = {"keyword": [x]} + else: + tmp["group"][group_id][user_id]["keyword"].append(x) + msg = " ".join(msg) + await pass_keyword.send(f'已成功{state["_prefix"]["raw_command"][:2]}搜图关键词:{msg}....') + for user in tmp["private"]: + x = ",".join(tmp["private"][user]["keyword"]) + await bot.send_private_msg( + user_id=user, message=f"你的关键词/UID/PID {x} 已被管理员通过,将在下一次进行更新..." + ) + for group in tmp["group"]: + for user in tmp["group"][group]: + x = ",".join(tmp["group"][group][user]["keyword"]) + await bot.send_group_msg( + group_id=group, + message=Message(f"{at(user)}你的关键词/UID/PID {x} 已被管理员通过,将在下一次进行更新..."), + ) + logger.info( + f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" + f" 通过了pixiv搜图关键词/UID:" + msg + ) diff --git a/plugins/pix_gallery/pix_show_info.py b/plugins/pix_gallery/pix_show_info.py new file mode 100644 index 00000000..e779f08d --- /dev/null +++ b/plugins/pix_gallery/pix_show_info.py @@ -0,0 +1,65 @@ +from nonebot import on_command +from utils.utils import get_message_text +from utils.message_builder import image +from nonebot.adapters.cqhttp import Bot, MessageEvent +from nonebot.typing import T_State +from .data_source import gen_keyword_pic, get_keyword_num +from models.pixiv_keyword_user import PixivKeywordUser +import asyncio + +__plugin_name__ = "查看pix图库" + +__plugin_usage__ = """ + 查看pix图库 [keyword]:当keyword为空时检查所有图片 +""" + + +my_keyword = on_command("我的pix关键词", aliases={"我的pix关键字"}, priority=1, block=True) + +show_keyword = on_command("显示pix关键词", aliases={"显示pix关键字"}, priority=1, block=True) + +show_pix = on_command("查看pix图库", priority=1, block=True) + + +@my_keyword.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + data = await PixivKeywordUser.get_all_user_dict() + if data.get(event.user_id) is None or not data[event.user_id]["keyword"]: + await my_keyword.finish("您目前没有提供任何Pixiv搜图关键字...", at_sender=True) + await my_keyword.send( + f"您目前提供的如下关键字:\n\t" + ",".join(data[event.user_id]["keyword"]) + ) + + +@show_keyword.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + _pass_keyword, not_pass_keyword = await PixivKeywordUser.get_current_keyword() + if _pass_keyword or not_pass_keyword: + await show_keyword.send( + image( + b64=await asyncio.get_event_loop().run_in_executor( + None, + gen_keyword_pic, + _pass_keyword, + not_pass_keyword, + str(event.user_id) in bot.config.superusers, + ) + ) + ) + else: + if str(event.user_id) in bot.config.superusers: + await show_keyword.finish(f"目前没有已收录或待收录的搜索关键词...") + else: + await show_keyword.finish(f"目前没有已收录的搜索关键词...") + + +@show_pix.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + keyword = get_message_text(event.json()) + count, r18_count = await get_keyword_num(keyword) + await show_pix.send( + f"PIX图库:{keyword}\n" + f"总数:{count + r18_count}\n" + f"美图:{count}\n" + f"r18:{r18_count}" + ) diff --git a/plugins/pix_gallery/pix_update.py b/plugins/pix_gallery/pix_update.py new file mode 100644 index 00000000..a4849b64 --- /dev/null +++ b/plugins/pix_gallery/pix_update.py @@ -0,0 +1,126 @@ +from nonebot import on_command +from utils.utils import get_message_text, is_number +from nonebot.permission import SUPERUSER +from nonebot.typing import T_State +from .data_source import start_update_image_url +from models.pixiv_keyword_user import PixivKeywordUser +from models.pixiv import Pixiv +from nonebot.adapters.cqhttp import Bot, MessageEvent +import time + + +__plugin_name__ = "更新pix关键字 | pix检查更新" + +__plugin_usage__ = """ + 更新pix收录的所有或指定数量的关键词/uid/pid + 更新pix关键词 [keyword/uid/pid] [num]=all 示例:更新pix关键词uid 5 + pix检测更新:检测从未更新过的uid和pid +""" + + +start_update = on_command( + "更新pix关键词", aliases={"更新pix关键字"}, permission=SUPERUSER, priority=1, block=True +) + +check_not_update_uid_pid = on_command( + "pix检测更新", + aliases={"pix检查更新"}, + permission=SUPERUSER, + priority=1, + block=True, +) + + +@start_update.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + msg_sp = get_message_text(event.json()).split() + _pass_keyword, _ = await PixivKeywordUser.get_current_keyword() + _pass_keyword.reverse() + black_pid = await PixivKeywordUser.get_black_pid() + _keyword = [ + x + for x in _pass_keyword + if not x.startswith("uid:") + and not x.startswith("pid:") + and not x.startswith("black:") + ] + _uid = [x for x in _pass_keyword if x.startswith("uid:")] + _pid = [x for x in _pass_keyword if x.startswith("pid:")] + num = 9999 + msg = msg_sp[0] if len(msg_sp) else "" + if len(msg_sp) == 2: + if is_number(msg_sp[1]): + num = int(msg_sp[1]) + else: + await start_update.finish("参数错误...第二参数必须为数字") + if num < 10000: + keyword_str = ",".join( + _keyword[: num if num < len(_keyword) else len(_keyword)] + ) + uid_str = ",".join(_uid[: num if num < len(_uid) else len(_uid)]) + pid_str = ",".join(_pid[: num if num < len(_pid) else len(_pid)]) + if msg.lower() == "pid": + update_lst = _pid + info = f"开始更新Pixiv搜图PID:\n{pid_str}" + elif msg.lower() == "uid": + update_lst = _uid + info = f"开始更新Pixiv搜图UID:\n{uid_str}" + elif msg.lower() == "keyword": + update_lst = _keyword + info = f"开始更新Pixiv搜图关键词:\n{keyword_str}" + else: + update_lst = _pass_keyword + info = f"开始更新Pixiv搜图关键词:\n{keyword_str}\n更新UID:{uid_str}\n更新PID:{pid_str}" + num = num if num < len(update_lst) else len(update_lst) + else: + if msg.lower() == "pid": + update_lst = [f"pid:{num}"] + info = f"开始更新Pixiv搜图UID:\npid:{num}" + else: + update_lst = [f"uid:{num}"] + info = f"开始更新Pixiv搜图UID:\nuid:{num}" + await start_update.send(info) + start_time = time.time() + pid_count, pic_count = await start_update_image_url(update_lst[:num], black_pid) + await start_update.send( + f"Pixiv搜图关键词搜图更新完成...\n" + f"累计更新PID {pid_count} 个\n" + f"累计更新图片 {pic_count} 张" + "\n耗时:{:.2f}秒".format((time.time() - start_time)) + ) + + +@check_not_update_uid_pid.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + msg = get_message_text(event.json()) + flag = False + if msg == "update": + flag = True + _pass_keyword, _ = await PixivKeywordUser.get_current_keyword() + x_uid = [] + x_pid = [] + _uid = [int(x[4:]) for x in _pass_keyword if x.startswith("uid:")] + _pid = [int(x[4:]) for x in _pass_keyword if x.startswith("pid:")] + all_images = await Pixiv.query_images(r18=2) + for img in all_images: + if img.pid not in x_pid: + x_pid.append(img.pid) + if img.uid not in x_uid: + x_uid.append(img.uid) + await check_not_update_uid_pid.send( + "从未更新过的UID:" + + ",".join([f"uid:{x}" for x in _uid if x not in x_uid]) + + "\n" + + "从未更新过的PID:" + + ",".join([f"pid:{x}" for x in _pid if x not in x_pid]) + ) + if flag: + await check_not_update_uid_pid.send("开始自动自动更新PID....") + update_lst = [f"pid:{x}" for x in _uid if x not in x_uid] + black_pid = await PixivKeywordUser.get_black_pid() + start_time = time.time() + pid_count, pic_count = await start_update_image_url(update_lst, black_pid) + await check_not_update_uid_pid.send( + f"Pixiv搜图关键词搜图更新完成...\n" + f"累计更新PID {pid_count} 个\n" + f"累计更新图片 {pic_count} 张" + "\n耗时:{:.2f}秒".format((time.time() - start_time)) + ) diff --git a/plugins/pixiv/__init__.py b/plugins/pixiv/__init__.py index 1f928ab6..22481b2b 100644 --- a/plugins/pixiv/__init__.py +++ b/plugins/pixiv/__init__.py @@ -1,18 +1,22 @@ from nonebot.typing import T_State -from nonebot.adapters.cqhttp import Bot, MessageEvent +from nonebot.adapters.cqhttp import Bot, MessageEvent, Event, GroupMessageEvent, Message +from nonebot.matcher import Matcher from nonebot import on_command from utils.utils import get_message_text, UserExistLimiter, is_number from .data_source import get_pixiv_urls, download_pixiv_imgs, search_pixiv_urls -import time +from utils.message_builder import at from services.log import logger from nonebot.adapters.cqhttp.exception import NetworkError from asyncio.exceptions import TimeoutError from aiohttp.client_exceptions import ClientConnectorError +from nonebot.exception import IgnoredException +from nonebot.message import run_preprocessor, run_postprocessor +from typing import Optional +import time +__plugin_name__ = "P站" -__plugin_name__ = 'P站' - -__plugin_usage__ = '''P站排行榜帮助: +__plugin_usage__ = """P站排行榜帮助: 可选参数: 类型: 1. 日排行 @@ -45,132 +49,155 @@ p站排行榜 [参数] [数量](可选) [日期](可选) 搜图 樱岛麻衣 5 2 r18 【默认为 热度排序】 【注意空格!!】【在线搜索会较慢】【数量可能不符】 -''' +""" rank_dict = { - '1': 'day', - '2': 'week', - '3': 'month', - '4': 'week_original', - '5': 'week_rookie', - '6': 'day_r18', - '7': 'week_r18', - '8': 'day_male_r18', - '9': 'week_r18g' + "1": "day", + "2": "week", + "3": "month", + "4": "week_original", + "5": "week_rookie", + "6": "day_r18", + "7": "week_r18", + "8": "day_male_r18", + "9": "week_r18g", } _ulmt = UserExistLimiter() -pixiv_rank = on_command('p站排行', aliases={'P站排行榜', 'p站排行榜', 'P站排行榜'}, priority=5, block=True) -pixiv_keyword = on_command('搜图', priority=5, block=True) + +@run_preprocessor +async def _(matcher: Matcher, bot: Bot, event: Event, state: T_State): + if isinstance(event, MessageEvent): + if matcher.module == "pixiv": + if _ulmt.check(event.user_id): + if isinstance(event, GroupMessageEvent): + await bot.send_group_msg( + group_id=event.group_id, + message=Message(f"{at(event.user_id)}P站排行榜或搜图正在搜索噢,不要重复触发命令呀"), + ) + else: + await bot.send_private_msg( + user_id=event.user_id, message=f"P站排行榜或搜图正在搜索噢,不要重复触发命令呀" + ) + raise IgnoredException("pixiv插件正在访问!") + _ulmt.set_True(event.user_id) + + +@run_postprocessor +async def do_something( + matcher: Matcher, + exception: Optional[Exception], + bot: Bot, + event: Event, + state: T_State, +): + if isinstance(event, MessageEvent): + if matcher.module == "pixiv": + _ulmt.set_False(event.user_id) + + +pixiv_rank = on_command( + "p站排行", aliases={"P站排行榜", "p站排行榜", "P站排行榜"}, priority=5, block=True +) +pixiv_keyword = on_command("搜图", priority=5, block=True) @pixiv_rank.handle() async def _(bot: Bot, event: MessageEvent, state: T_State): msg = get_message_text(event.json()).strip() - msg = msg.split(' ') + msg = msg.split(" ") msg = [m for m in msg if m] if not msg: - msg = ['1'] - if msg[0] in ['6', '7', '8', '9']: - if event.message_type == 'group': - await pixiv_rank.finish('羞羞脸!私聊里自己看!', at_sender=True) - # print(msg) - if _ulmt.check(event.user_id): - await pixiv_rank.finish("P站排行榜正在搜索噢,不要重复触发命令呀") - _ulmt.set_True(event.user_id) - if len(msg) == 0 or msg[0] == '': - text_list, urls, code = await get_pixiv_urls(rank_dict.get('1')) + msg = ["1"] + if msg[0] in ["6", "7", "8", "9"]: + if event.message_type == "group": + await pixiv_rank.finish("羞羞脸!私聊里自己看!", at_sender=True) + if len(msg) == 0 or msg[0] == "": + text_list, urls, code = await get_pixiv_urls(rank_dict.get("1")) elif len(msg) == 1: - if msg[0] not in ['1', '2', '3', '4', '5', '6', '7', '8', '9']: - _ulmt.set_False(event.user_id) + if msg[0] not in ["1", "2", "3", "4", "5", "6", "7", "8", "9"]: await pixiv_rank.finish("要好好输入要看什么类型的排行榜呀!", at_sender=True) text_list, urls, code = await get_pixiv_urls(rank_dict.get(msg[0])) elif len(msg) == 2: text_list, urls, code = await get_pixiv_urls(rank_dict.get(msg[0]), int(msg[1])) elif len(msg) == 3: if not check_date(msg[2]): - _ulmt.set_False(event.user_id) - await pixiv_rank.finish('日期格式错误了', at_sender=True) - text_list, urls, code = await get_pixiv_urls(rank_dict.get(msg[0]), int(msg[1]), msg[2]) + await pixiv_rank.finish("日期格式错误了", at_sender=True) + text_list, urls, code = await get_pixiv_urls( + rank_dict.get(msg[0]), int(msg[1]), msg[2] + ) else: - _ulmt.set_False(event.user_id) - await pixiv_rank.finish('格式错了噢,看看帮助?', at_sender=True) + await pixiv_rank.finish("格式错了噢,看看帮助?", at_sender=True) if code != 200: - _ulmt.set_False(event.user_id) await pixiv_keyword.finish(text_list[0]) else: if not text_list or not urls: - _ulmt.set_False(event.user_id) - await pixiv_rank.finish('没有找到啊,等等再试试吧~V', at_sender=True) + await pixiv_rank.finish("没有找到啊,等等再试试吧~V", at_sender=True) for i in range(len(text_list)): try: - await pixiv_rank.send(text_list[i] + await download_pixiv_imgs(urls[i], event.user_id)) + await pixiv_rank.send( + text_list[i] + await download_pixiv_imgs(urls[i], event.user_id) + ) except (NetworkError, TimeoutError, ClientConnectorError): - await pixiv_keyword.send('这张图网络炸了!', at_sender=True) + await pixiv_keyword.send("这张图网络炸了!", at_sender=True) logger.info( - f"(USER {event.user_id}, GROUP {event.group_id if event.message_type != 'private' else 'private'})" - f" 查看了P站排行榜 code:{msg[0]}") - _ulmt.set_False(event.user_id) + f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" + f" 查看了P站排行榜 code:{msg[0]}" + ) @pixiv_keyword.handle() async def _(bot: Bot, event: MessageEvent, state: T_State): msg = get_message_text(event.json()).strip() - if event.message_type == 'group': - if msg.find('r18') != -1: - await pixiv_keyword.finish('(脸红#) 你不会害羞的 八嘎!', at_sender=True) - if msg.find('r18') == -1: + if event.message_type == "group": + if msg.find("r18") != -1: + await pixiv_keyword.finish("(脸红#) 你不会害羞的 八嘎!", at_sender=True) + if msg.find("r18") == -1: r18 = 1 else: r18 = 2 - msg = msg.replace('r18', '').strip() - if _ulmt.check(event.user_id): - await pixiv_rank.finish("P站关键词正在搜索噢,不要重复触发命令呀") - _ulmt.set_True(event.user_id) - msg = msg.split(' ') + msg = msg.replace("r18", "").strip() + msg = msg.split(" ") msg = [m for m in msg if m] if len(msg) == 1: keyword = msg[0].strip() num = 5 - order = 'popular' + order = "popular" elif len(msg) == 2: keyword = msg[0].strip() if not is_number(msg[1].strip()): - _ulmt.set_False(event.user_id) - await pixiv_keyword.finish('图片数量必须是数字!', at_sender=True) + await pixiv_keyword.finish("图片数量必须是数字!", at_sender=True) num = int(msg[1].strip()) - order = 'popular' + order = "popular" elif len(msg) == 3: keyword = msg[0].strip() if not is_number(msg[1].strip()): - _ulmt.set_False(event.user_id) - await pixiv_keyword.finish('图片数量必须是数字!', at_sender=True) + await pixiv_keyword.finish("图片数量必须是数字!", at_sender=True) num = int(msg[1].strip()) if not is_number(msg[2].strip()): - _ulmt.set_False(event.user_id) - await pixiv_keyword.finish('排序方式必须是数字!', at_sender=True) - if msg[2].strip() == '1': - order = 'popular' + await pixiv_keyword.finish("排序方式必须是数字!", at_sender=True) + if msg[2].strip() == "1": + order = "popular" else: - order = 'xxx' + order = "xxx" else: - _ulmt.set_False(event.user_id) - await pixiv_keyword.finish('参数不正确,一定要好好看看帮助啊!', at_sender=True) + await pixiv_keyword.finish("参数不正确,一定要好好看看帮助啊!", at_sender=True) text_list, urls, code = await search_pixiv_urls(keyword, num, order, r18) if code != 200: - _ulmt.set_False(event.user_id) await pixiv_keyword.finish(text_list[0]) else: for i in range(len(text_list)): try: - await pixiv_keyword.send(text_list[i] + await download_pixiv_imgs(urls[i], event.user_id)) + await pixiv_keyword.send( + text_list[i] + await download_pixiv_imgs(urls[i], event.user_id) + ) except (NetworkError, TimeoutError, ClientConnectorError): - await pixiv_keyword.send('这张图网络炸了!', at_sender=True) + await pixiv_keyword.send("这张图网络炸了!", at_sender=True) logger.info( f"(USER {event.user_id}, GROUP {event.group_id if event.message_type != 'private' else 'private'})" - f" 查看了搜索 {keyword} R18:{r18}") - _ulmt.set_False(event.user_id) + f" 查看了搜索 {keyword} R18:{r18}" + ) def check_date(date): @@ -179,20 +206,3 @@ def check_date(date): return True except: return False - - - - - - - - - - - - - - - - - diff --git a/plugins/pixiv/data_source.py b/plugins/pixiv/data_source.py index 04e0b85e..b3e7ca3c 100644 --- a/plugins/pixiv/data_source.py +++ b/plugins/pixiv/data_source.py @@ -5,20 +5,22 @@ from utils.utils import get_local_proxy from utils.user_agent import get_user_agent from bs4 import BeautifulSoup import feedparser -from utils.init_result import image +from utils.message_builder import image from asyncio.exceptions import TimeoutError from configs.config import RSSHUBAPP from aiohttp.client_exceptions import ClientConnectorError import platform -if platform.system() == 'Windows': + +if platform.system() == "Windows": import asyncio + asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) -async def get_pixiv_urls(mode: str, num: int = 5, date: str = '') -> 'list, list, int': - url = f'{RSSHUBAPP}pixiv/ranking/{mode}' +async def get_pixiv_urls(mode: str, num: int = 5, date: str = "") -> "list, list, int": + url = f"{RSSHUBAPP}/pixiv/ranking/{mode}" if date: - url += f'/{date}' + url += f"/{date}" try: return await parser_data(url, num) except ClientConnectorError: @@ -26,62 +28,70 @@ async def get_pixiv_urls(mode: str, num: int = 5, date: str = '') -> 'list, list async def download_pixiv_imgs(urls: list, user_id: int) -> str: - result = '' + result = "" index = 0 for img in urls: async with aiohttp.ClientSession(headers=get_user_agent()) as session: for _ in range(3): - async with session.get(img, proxy=get_local_proxy(), timeout=2) as response: - async with aiofiles.open(IMAGE_PATH + f'temp/{user_id}_{index}_pixiv.jpg', 'wb') as f: + async with session.get( + img, proxy=get_local_proxy(), timeout=2 + ) as response: + async with aiofiles.open( + IMAGE_PATH + f"temp/{user_id}_{index}_pixiv.jpg", "wb" + ) as f: try: await f.write(await response.read()) - result += image(f'{user_id}_{index}_pixiv.jpg', 'temp') + result += image(f"{user_id}_{index}_pixiv.jpg", "temp") index += 1 break except TimeoutError: # result += '\n这张图下载失败了..\n' pass else: - result += '\n这张图下载失败了..\n' + result += "\n这张图下载失败了..\n" return result -async def search_pixiv_urls(keyword: str, num: int, order: str, r18: int) -> 'list, list': - url = f'{RSSHUBAPP}pixiv/search/{keyword}/{order}/{r18}' +async def search_pixiv_urls( + keyword: str, num: int, order: str, r18: int +) -> "list, list": + url = f"{RSSHUBAPP}/pixiv/search/{keyword}/{order}/{r18}" return await parser_data(url, num) -async def parser_data(url: str, num: int) -> 'list, list, int': +async def parser_data(url: str, num: int) -> "list, list, int": text_list = [] urls = [] async with aiohttp.ClientSession() as session: for _ in range(3): try: - async with session.get(url, proxy=get_local_proxy(), timeout=2) as response: - data = feedparser.parse(await response.text())['entries'] + async with session.get( + url, proxy=get_local_proxy(), timeout=2 + ) as response: + data = feedparser.parse(await response.text())["entries"] break except TimeoutError: pass else: - return ['网络不太好,也许过一会就好了'], [], 998 + return ["网络不太好,也许过一会就好了"], [], 998 try: if len(data) == 0: - return ['没有搜索到喔'], [], 997 + return ["没有搜索到喔"], [], 997 if num > len(data): num = len(data) data = data[:num] for data in data: - soup = BeautifulSoup(data['summary'], 'lxml') - title = "标题:" + data['title'] - pl = soup.find_all('p') - author = pl[0].text.split('-')[0].strip() + soup = BeautifulSoup(data["summary"], "lxml") + title = "标题:" + data["title"] + pl = soup.find_all("p") + author = pl[0].text.split("-")[0].strip() imgs = [] - text_list.append(f'{title}\n{author}\n') + text_list.append(f"{title}\n{author}\n") for p in pl[1:]: - imgs.append(p.find('img').get('src')) + imgs.append(p.find("img").get("src")) urls.append(imgs) except ValueError: - return ['是网站坏了啊,也许过一会就好了'], [], 999 + return ["是网站坏了啊,也许过一会就好了"], [], 999 return text_list, urls, 200 diff --git a/plugins/poke/__init__.py b/plugins/poke/__init__.py index 19a13b98..d4ea7f70 100644 --- a/plugins/poke/__init__.py +++ b/plugins/poke/__init__.py @@ -3,21 +3,34 @@ from nonebot.adapters.cqhttp import Bot, PokeNotifyEvent from nonebot.typing import T_State from configs.path_config import VOICE_PATH, IMAGE_PATH import os -from utils.init_result import record, image, poke +from utils.message_builder import record, image, poke from services.log import logger import random from utils.utils import CountLimiter from models.ban_user import BanUser -__plugin_name__ = '戳一戳 [Hidden]' +__plugin_name__ = "戳一戳 [Hidden]" -__plugin_usage__ = '用法:无' +__plugin_usage__ = "用法:无" poke__reply = [ - "lsp你再戳?", "连个可爱美少女都要戳的肥宅真恶心啊。", - "你再戳!", "?再戳试试?", "别戳了别戳了再戳就坏了555", "我爪巴爪巴,球球别再戳了", "你戳你🐎呢?!", - "那...那里...那里不能戳...绝对...", "(。´・ω・)ん?", "有事恁叫我,别天天一个劲戳戳戳!", "欸很烦欸!你戳🔨呢", - "?", "再戳一下试试?", "???", "正在关闭对您的所有服务...关闭成功", "啊呜,太舒服刚刚竟然睡着了。什么事?", "正在定位您的真实地址。。。\r\n定位成功。轰炸机已起飞" + "lsp你再戳?", + "连个可爱美少女都要戳的肥宅真恶心啊。", + "你再戳!", + "?再戳试试?", + "别戳了别戳了再戳就坏了555", + "我爪巴爪巴,球球别再戳了", + "你戳你🐎呢?!", + "那...那里...那里不能戳...绝对...", + "(。´・ω・)ん?", + "有事恁叫我,别天天一个劲戳戳戳!", + "欸很烦欸!你戳🔨呢", + "?", + "再戳一下试试?", + "???", + "正在关闭对您的所有服务...关闭成功", + "啊呜,太舒服刚刚竟然睡着了。什么事?", + "正在定位您的真实地址...定位成功。轰炸机已起飞", ] @@ -31,24 +44,25 @@ async def _poke_(bot: Bot, event: PokeNotifyEvent, state: T_State): if event.self_id == event.target_id: _clmt.add(event.user_id) if _clmt.check(event.user_id) or random.random() < 0.3: - rst = '' + rst = "" if random.random() < 0.15: await BanUser.ban(event.user_id, 1, 60) - rst = '气死我了!' + rst = "气死我了!" await poke_.finish(rst + random.choice(poke__reply), at_sender=True) rand = random.random() if rand <= 0.3: - path = random.choice(['luoli/', 'meitu/']) + path = random.choice(["luoli/", "meitu/"]) index = random.randint(0, len(os.listdir(IMAGE_PATH + path))) - result = f'id:{index}' + image(f'{index}.jpg', path) + result = f"id:{index}" + image(f"{index}.jpg", path) await poke_.send(result) - logger.info(f'USER {event.user_id} 戳了戳我 回复: {result} \n {result}') + logger.info(f"USER {event.user_id} 戳了戳我 回复: {result} \n {result}") elif 0.3 < rand < 0.6: - voice = random.choice(os.listdir(VOICE_PATH + 'dinggong/')) + voice = random.choice(os.listdir(VOICE_PATH + "dinggong/")) result = record(voice, "dinggong") await poke_.send(result) - await poke_.send(voice.split('_')[1]) - logger.info(f'USER {event.user_id} 戳了戳我 回复: {result} \n {voice.split("_")[1]}') + await poke_.send(voice.split("_")[1]) + logger.info( + f'USER {event.user_id} 戳了戳我 回复: {result} \n {voice.split("_")[1]}' + ) else: await poke_.send(poke(event.user_id)) - diff --git a/plugins/quotations.py b/plugins/quotations.py index d85bbec5..6e4056c9 100644 --- a/plugins/quotations.py +++ b/plugins/quotations.py @@ -1,18 +1,18 @@ from nonebot import on_command from services.log import logger -from nonebot.adapters.cqhttp import Bot, MessageEvent +from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent from nonebot.typing import T_State import aiohttp from utils.utils import get_local_proxy -__plugin_name__ = '语录' -__plugin_usage__ = '用法: 二次元语录给你力量' +__plugin_name__ = "语录" +__plugin_usage__ = "用法: 二次元语录给你力量" -quotations = on_command("语录", aliases={'二次元', '二次元语录'}, priority=5, block=True) +quotations = on_command("语录", aliases={"二次元", "二次元语录"}, priority=5, block=True) -url = 'https://international.v1.hitokoto.cn/?c=a' +url = "https://international.v1.hitokoto.cn/?c=a" @quotations.handle() @@ -23,6 +23,6 @@ async def _(bot: Bot, event: MessageEvent, state: T_State): result = f'{data["hitokoto"]}\t——{data["from"]}' await quotations.send(result) logger.info( - f"(USER {event.user_id}, GROUP {event.group_id if event.message_type != 'private' else 'private'}) 发送语录:" - + result) - + f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'}) 发送语录:" + + result + ) diff --git a/plugins/reimu/__init__.py b/plugins/reimu/__init__.py index 082059c0..95e74a9f 100644 --- a/plugins/reimu/__init__.py +++ b/plugins/reimu/__init__.py @@ -2,13 +2,13 @@ from nonebot import on_command from nonebot.adapters.cqhttp.permission import PRIVATE from .data_source import from_reimu_get_info from services.log import logger -from nonebot.adapters.cqhttp import Bot, Event +from nonebot.adapters.cqhttp import Bot, Event, PrivateMessageEvent from nonebot.typing import T_State from utils.utils import is_number, get_message_text, UserExistLimiter, scheduler from models.count_user import UserCount from configs.config import COUNT_PER_DAY_REIMU -__plugin_name__ = '上车' +__plugin_name__ = "上车" __plugin_usage__ = r""" * 请各位使用后不要转发 * * 大部分解压密码是⑨ * @@ -22,49 +22,48 @@ ps: 请尽量提供具体的目的地名称 _ulmt = UserExistLimiter() -reimu = on_command('上车', permission=PRIVATE, block=True, priority=1) +reimu = on_command("上车", permission=PRIVATE, block=True, priority=1) @reimu.args_parser -async def _(bot: Bot, event: Event, state: T_State): - if get_message_text(event.json()) in ['取消', '算了']: +async def _(bot: Bot, event: PrivateMessageEvent, state: T_State): + if get_message_text(event.json()) in ["取消", "算了"]: await reimu.finish("已取消操作..", at_sender=True) if not get_message_text(event.json()): - await reimu.reject('没时间等了!快说你要去哪里?', at_sender=True) - state['keyword'] = get_message_text(event.json()) - state['page'] = 1 + await reimu.reject("没时间等了!快说你要去哪里?", at_sender=True) + state["keyword"] = get_message_text(event.json()) + state["page"] = 1 @reimu.handle() -async def _(bot: Bot, event: Event, state: T_State): - if str(event.get_message()) in ['帮助']: +async def _(bot: Bot, event: PrivateMessageEvent, state: T_State): + if str(event.get_message()) in ["帮助"]: await reimu.finish(__plugin_usage__) - if await UserCount.check_count(event.user_id, 'reimu', COUNT_PER_DAY_REIMU): - await reimu.finish('今天已经没车了,请明天再来...', at_sender=True) + if await UserCount.check_count(event.user_id, "reimu", COUNT_PER_DAY_REIMU): + await reimu.finish("今天已经没车了,请明天再来...", at_sender=True) if _ulmt.check(event.user_id): - await reimu.finish('您还没下车呢,请稍等...', at_sender=True) + await reimu.finish("您还没下车呢,请稍等...", at_sender=True) _ulmt.set_True(event.user_id) msg = get_message_text(event.json()) args = msg.split(" ") - if msg in ['!', '!', '?', '?', ',', ',', '.', '。']: + if msg in ["!", "!", "?", "?", ",", ",", ".", "。"]: await reimu.finish(__plugin_usage__) if msg: if len(args) > 1 and is_number(args[0]): - state['keyword'] = args[1] - state['page'] = args[0] + state["keyword"] = args[1] + state["page"] = args[0] else: - state['keyword'] = msg - state['page'] = 1 + state["keyword"] = msg + state["page"] = 1 -@reimu.got('keyword', '你的目的地是哪?') -async def _(bot: Bot, event: Event, state: T_State): +@reimu.got("keyword", "你的目的地是哪?") +async def _(bot: Bot, event: PrivateMessageEvent, state: T_State): try: - keyword = state['keyword'] - page = state['page'] - print(keyword, page) - await UserCount.add_count(event.user_id, 'reimu') - await reimu.send('已经帮你关好车门了,请等待发车(不加真寻好友的话是欣赏不到旅途的风景的)', at_sender=True) + keyword = state["keyword"] + page = state["page"] + await UserCount.add_count(event.user_id, "reimu") + await reimu.send("已经帮你关好车门了,请等待发车(不加真寻好友的话是欣赏不到旅途的风景的)", at_sender=True) reimu_report = await from_reimu_get_info(keyword, page) if reimu_report: await reimu.send(reimu_report) @@ -77,20 +76,9 @@ async def _(bot: Bot, event: Event, state: T_State): @scheduler.scheduled_job( - 'cron', - # year=None, - # month=None, - # day=None, - # week=None, - # day_of_week="mon,tue,wed,thu,fri", + "cron", hour=0, minute=1, - # second=None, - # start_date=None, - # end_date=None, - # timezone=None, ) async def _(): await UserCount.reset_count() - - diff --git a/plugins/reimu/data_source.py b/plugins/reimu/data_source.py index 3feaecf0..926eb967 100644 --- a/plugins/reimu/data_source.py +++ b/plugins/reimu/data_source.py @@ -11,106 +11,102 @@ from asyncio.exceptions import TimeoutError async def from_reimu_get_info(key_word: str, page: int) -> str: if "miku" in key_word.lower(): logger.warning("Taboo words are being triggered") - return None + return '' repass = "" - url = 'https://blog.reimu.net/search/' + key_word + '/page/' + str(page) - url_s = 'https://blog.reimu.net/' - try: - if key_word == "最近的存档": - logger.debug("Now starting get the {}".format(url_s)) - repass = await get_repass(url_s) - else: - logger.debug("Now starting get the {}".format(url)) - repass = await get_repass(url) - except TimeoutError as e: - logger.error("Timeout! {}".format(e)) - - return repass + url = f"https://blog.reimu.net/search/{key_word}/page/{page}" + url_s = "https://blog.reimu.net/" + # try: + # if key_word == "最近的存档": + # logger.debug("Now starting get the {}".format(url_s)) + # repass = await get_repass(url_s) + # else: + # logger.debug("Now starting get the {}".format(url)) + # repass = await get_repass(url) + # except TimeoutError as e: + # logger.error("Timeout! {}".format(e)) + # + # return repass -async def get_repass(url: str) -> str: - repass = "" - info = "[Note]注意大部分资源解压密码为⑨\n" - fund = None - print(url) - async with aiohttp.ClientSession(headers=get_user_agent()) as session: - async with session.get(url, proxy=get_local_proxy(), timeout=15) as response: - html = etree.HTML(await response.text()) - - fund_l = html.xpath('//h1[@class="page-title"]/text()') - if fund_l: - fund = fund_l[0] - if fund == "未找到": - return "老司机也找不到路了……" - else: - pass - - headers = html.xpath('//article/header/h2/a/text()') - urls = html.xpath('//article/header/h2/a/@href') - logger.debug("Now get {} post from search page".format(len(headers))) - - headers_d = [] - urls_d = [] - for i, header in enumerate(headers): - if check_need_list(header): - headers_d.append(headers[i]) - urls_d.append(urls[i]) - else: - logger.debug("This title {} does not meet the requirements".format(header)) - - header_len = len(headers_d) - logger.debug("Get {} post after processing".format(header_len)) - if header_len > MAXINFO_REIMU: - headers_d = headers_d[:MAXINFO_REIMU] - urls_d = urls_d[:MAXINFO_REIMU] - - for h_s, url_s in zip(headers_d, urls_d): - if h_s != "审核结果存档": - time.sleep(1.5) - putline = await get_son_html_info(h_s, url_s) - if putline: - if repass: - repass = "\n\n- - - - - - - - \n".join([repass, putline]) - else: - repass = putline - else: - logger.info("审核归档页面已跳过") - - if repass: - repass = info + repass - return repass +# async def get_repass(url: str) -> str: +# repass = "" +# info = "[Note]注意大部分资源解压密码为⑨\n" +# fund = None +# print(url) +# async with aiohttp.ClientSession(headers=get_user_agent()) as session: +# async with session.get(url, proxy=get_local_proxy(), timeout=15) as response: +# html = etree.HTML(await response.text()) +# +# fund_l = html.xpath('//h1[@class="page-title"]/text()') +# if fund_l: +# fund = fund_l[0] +# if fund == "未找到": +# return "老司机也找不到路了……" +# else: +# pass +# +# headers = html.xpath("//article/header/h2/a/text()") +# urls = html.xpath("//article/header/h2/a/@href") +# logger.debug("Now get {} post from search page".format(len(headers))) +# +# headers_d = [] +# urls_d = [] +# for i, header in enumerate(headers): +# if check_need_list(header): +# headers_d.append(headers[i]) +# urls_d.append(urls[i]) +# else: +# logger.debug( +# "This title {} does not meet the requirements".format(header) +# ) +# +# header_len = len(headers_d) +# logger.debug("Get {} post after processing".format(header_len)) +# if header_len > MAXINFO_REIMU: +# headers_d = headers_d[:MAXINFO_REIMU] +# urls_d = urls_d[:MAXINFO_REIMU] +# +# for h_s, url_s in zip(headers_d, urls_d): +# if h_s != "审核结果存档": +# time.sleep(1.5) +# putline = await get_son_html_info(h_s, url_s) +# if putline: +# if repass: +# repass = "\n\n- - - - - - - - \n".join([repass, putline]) +# else: +# repass = putline +# else: +# logger.info("审核归档页面已跳过") +# +# if repass: +# repass = info + repass +# return repass +# +# +# async def get_son_html_info(h_s, url_s) -> str: +# repass = "" +# logger.debug("Now starting get the {}".format(url_s)) +# async with aiohttp.ClientSession(headers=get_user_agent()) as session: +# async with session.get(url_s, proxy=get_local_proxy(), timeout=15) as response: +# html = etree.HTML(await response.text()) +# pres = html.xpath('//div[@class="entry-content"]/pre/text()') +# a_texts = html.xpath('//div[@class="entry-content"]/pre//a/text()') +# a_hrefs = html.xpath('//div[@class="entry-content"]/pre//a/@href') +# +# if pres and a_texts and a_hrefs: +# while "" in pres: +# pres.remove("") +# +# repass = "【资源名称】 {}\n\n{}".format(h_s, pres[0].strip()) +# for i, (a_t_s, a_h_s) in enumerate(zip(a_texts, a_hrefs)): +# a = "\n {} {} {} ".format(a_t_s, a_h_s, pres[i + 1].strip()) +# repass += a +# else: +# logger.warning("Not get putline from {}".format(url_s)) +# +# return repass -async def get_son_html_info(h_s, url_s) -> str: - repass = "" - logger.debug("Now starting get the {}".format(url_s)) - async with aiohttp.ClientSession(headers=get_user_agent()) as session: - async with session.get(url_s, proxy=get_local_proxy(), timeout=15) as response: - html = etree.HTML(await response.text()) - pres = html.xpath('//div[@class="entry-content"]/pre/text()') - a_texts = html.xpath('//div[@class="entry-content"]/pre//a/text()') - a_hrefs = html.xpath('//div[@class="entry-content"]/pre//a/@href') - - if pres and a_texts and a_hrefs: - while "" in pres: - pres.remove("") - - repass = "【资源名称】 {}\n\n{}".format(h_s, pres[0].strip()) - for i, (a_t_s, a_h_s) in enumerate(zip(a_texts, a_hrefs)): - a = "\n {} {} {} ".format(a_t_s, a_h_s, pres[i + 1].strip()) - repass += a - else: - logger.warning("Not get putline from {}".format(url_s)) - - return repass -def check_need_list(header: str) -> bool: - not_need = ['音乐', '御所动态'] - for nd in not_need: - if nd in header: - return False - return True - -# print(asyncio.get_event_loop().run_until_complete(from_reimu_get_info('萝莉'))) \ No newline at end of file diff --git a/plugins/remind/__init__.py b/plugins/remind/__init__.py index e1af0fcb..dc912641 100644 --- a/plugins/remind/__init__.py +++ b/plugins/remind/__init__.py @@ -1,4 +1,4 @@ -from utils.init_result import image +from utils.message_builder import image from utils.utils import scheduler, get_bot from services.log import logger from models.group_remind import GroupRemind @@ -11,7 +11,7 @@ __name__ = "早晚安 [Hidden]" # 早上好 @scheduler.scheduled_job( - 'cron', + "cron", # year=None, # month=None, # day=None, @@ -28,22 +28,21 @@ async def _(): try: bot = get_bot() gl = await bot.get_group_list(self_id=bot.self_id) - gl = [g['group_id'] for g in gl] + gl = [g["group_id"] for g in gl] for g in gl: - if await GroupRemind.get_status(g, 'zwa'): + if await GroupRemind.get_status(g, "zwa"): result = image("zao.jpg", "zhenxun") try: - await bot.send_group_msg(group_id=g, - message="早上好" + result) + await bot.send_group_msg(group_id=g, message="早上好" + result) except ActionFailed: - logger.warning(f'{g} 群被禁言中,无法发送早安') + logger.warning(f"{g} 群被禁言中,无法发送早安") except Exception as e: - logger.error(f'早晚安错误 e:{e}') + logger.error(f"早晚安错误 e:{e}") # 睡觉了 @scheduler.scheduled_job( - 'cron', + "cron", hour=23, minute=59, ) @@ -51,22 +50,23 @@ async def _(): try: bot = get_bot() gl = await bot.get_group_list(self_id=bot.self_id) - gl = [g['group_id'] for g in gl] + gl = [g["group_id"] for g in gl] for g in gl: - if await GroupRemind.get_status(g, 'zwa'): + if await GroupRemind.get_status(g, "zwa"): result = image("sleep.jpg", "zhenxun") try: - await bot.send_group_msg(group_id=g, - message="小真寻要睡觉了,你们也要早点睡呀" + result) + await bot.send_group_msg( + group_id=g, message="小真寻要睡觉了,你们也要早点睡呀" + result + ) except ActionFailed: - logger.warning(f'{g} 群被禁言中,无法发送晚安') + logger.warning(f"{g} 群被禁言中,无法发送晚安") except Exception as e: - logger.error(f'早晚安错误 e:{e}') + logger.error(f"早晚安错误 e:{e}") # 自动更新群组信息 @scheduler.scheduled_job( - 'cron', + "cron", hour=3, minute=1, ) @@ -74,19 +74,23 @@ async def _(): try: bot = get_bot() gl = await bot.get_group_list(self_id=bot.self_id) - gl = [g['group_id'] for g in gl] + gl = [g["group_id"] for g in gl] for g in gl: group_info = await bot.get_group_info(group_id=g) - await GroupInfo.add_group_info(group_info['group_id'], group_info['group_name'], - group_info['max_member_count'], group_info['member_count']) - logger.info(f'自动更新群组 {g} 信息成功') + await GroupInfo.add_group_info( + group_info["group_id"], + group_info["group_name"], + group_info["max_member_count"], + group_info["member_count"], + ) + logger.info(f"自动更新群组 {g} 信息成功") except Exception as e: - logger.error(f'自动更新群组信息错误 e:{e}') + logger.error(f"自动更新群组信息错误 e:{e}") # 自动更新好友信息 @scheduler.scheduled_job( - 'cron', + "cron", hour=3, minute=1, ) @@ -95,12 +99,13 @@ async def _(): bot = get_bot() fl = await bot.get_friend_list(self_id=bot.self_id) for f in fl: - if await FriendUser.add_friend_info(f['user_id'], f['nickname']): + if await FriendUser.add_friend_info(f["user_id"], f["nickname"]): logger.info(f'自动更新好友 {f["user_id"]} 信息成功') else: logger.warning(f'自动更新好友 {f["user_id"]} 信息失败') except Exception as e: - logger.error(f'自动更新群组信息错误 e:{e}') + logger.error(f"自动更新群组信息错误 e:{e}") + # 一次性任务 # 固定时间触发,仅触发一次: @@ -189,6 +194,3 @@ async def _(): # misfire_grace_time=60, # 允许的误差时间,建议不要省略 # # jobstore='default', # 任务储存库,在下一小节中说明 # ) - - - diff --git a/plugins/russian/__init__.py b/plugins/russian/__init__.py index 37b7e350..f01dfee4 100644 --- a/plugins/russian/__init__.py +++ b/plugins/russian/__init__.py @@ -5,7 +5,7 @@ from nonebot.adapters.cqhttp import GROUP, Bot, GroupMessageEvent, Message from nonebot.typing import T_State from utils.utils import get_message_text, is_number, get_message_at from models.group_member_info import GroupInfoUser -from utils.init_result import at +from utils.message_builder import at from models.russian_user import RussianUser from models.bag_user import BagUser from services.log import logger @@ -93,7 +93,7 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State): if rs_player[event.group_id]['at'] != 0 and event.user_id != rs_player[event.group_id]['at']: await accept.finish('又不是找你决斗,你拒绝什么啊!气!', at_sender=True) if rs_player[event.group_id]['at'] == event.user_id: - at_player_name = (await GroupInfoUser.select_member_info(event.user_id, event.group_id)).user_name + at_player_name = (await GroupInfoUser.get_member_info(event.user_id, event.group_id)).user_name await accept.send(Message(f'{at(rs_player[event.group_id][1])}\n' f'{at_player_name}拒绝了你的对决!')) rs_player[event.group_id] = {} @@ -191,7 +191,10 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State): if at_: at_ = at_[0] - at_player_name = event.sender.card if event.sender.card else event.sender.nickname + try: + at_player_name = (await GroupInfoUser.get_member_info(at_, event.group_id)).user_name + except AttributeError: + at_player_name = at(at_) msg = f'{player1_name} 向 {at(at_)} 发起了决斗!请 {at_player_name} 在30秒内回复‘接受对决’ or ‘拒绝对决’,超时此次决斗作废!' else: at_ = 0 @@ -242,10 +245,10 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State): await shot.finish(random.choice([ f'不要打扰 {player1_name} 和 {player2_name} 的决斗啊!', '给我好好做好一个观众!不然小真寻就要生气了', - f'不要捣乱啊baka{(await GroupInfoUser.select_member_info(event.user_id, event.group_id)).user_name}!' + f'不要捣乱啊baka{(await GroupInfoUser.get_member_info(event.user_id, event.group_id)).user_name}!' ]), at_sender=True) await shot.finish(f'你的左轮不是连发的!该 ' - f'{(await GroupInfoUser.select_member_info(int(rs_player[event.group_id]["next"]), event.group_id)).user_name} 开枪了') + f'{(await GroupInfoUser.get_member_info(int(rs_player[event.group_id]["next"]), event.group_id)).user_name} 开枪了') if rs_player[event.group_id]['bullet'][rs_player[event.group_id]['index']] != 1: await shot.send(Message(random.choice([ '呼呼,没有爆裂的声响,你活了下来', diff --git a/plugins/russian/data_source.py b/plugins/russian/data_source.py index 8a2abef9..21041596 100644 --- a/plugins/russian/data_source.py +++ b/plugins/russian/data_source.py @@ -3,22 +3,23 @@ from utils.data_utils import init_rank async def rank(group_id: int, itype) -> str: - users = await RussianUser.all_user(group_id) + all_users = await RussianUser.get_all_user(group_id) + all_user_id = [user.user_qq for user in all_users] if itype == 'win_rank': rank_name = '\t胜场排行榜\n' - all_user_data = [user.win_count for user in users] + all_user_data = [user.win_count for user in all_users] elif itype == 'lose_rank': rank_name = '\t败场排行榜\n' - all_user_data = [user.fail_count for user in users] + all_user_data = [user.fail_count for user in all_users] elif itype == 'make_money': rank_name = '\t赢取金币排行榜\n' - all_user_data = [user.make_money for user in users] + all_user_data = [user.make_money for user in all_users] else: rank_name = '\t输掉金币排行榜\n' - all_user_data = [user.lose_money for user in users] + all_user_data = [user.lose_money for user in all_users] rst = '' - if users: - rst = await init_rank(users, all_user_data, group_id) + if all_users: + rst = await init_rank(all_user_id, all_user_data, group_id) return rank_name + rst diff --git a/plugins/search_anime/__init__.py b/plugins/search_anime/__init__.py index 3d92ea68..6d62f3a8 100644 --- a/plugins/search_anime/__init__.py +++ b/plugins/search_anime/__init__.py @@ -1,13 +1,13 @@ from nonebot import on_command from .data_source import from_anime_get_info from services.log import logger -from nonebot.adapters.cqhttp import Bot, Event +from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent from nonebot.typing import T_State from configs.config import MAXINFO_GROUP_ANIME, MAXINFO_PRIVATE_ANIME -from utils.utils import get_message_text, get_message_type, UserExistLimiter +from utils.utils import get_message_text, UserExistLimiter -__plugin_name__ = '搜番' +__plugin_name__ = "搜番" __plugin_usage__ = r""" 在群内使用此功能只返还5个结果,私聊返还 20 个结果(绝不能打扰老色批们看色图!) 搜索动漫资源 @@ -16,39 +16,43 @@ __plugin_usage__ = r""" """.strip() _ulmt = UserExistLimiter() -search_anime = on_command('搜番', aliases={'搜动漫'}, priority=5, block=True) +search_anime = on_command("搜番", aliases={"搜动漫"}, priority=5, block=True) @search_anime.args_parser -async def _(bot: Bot, event: Event, state: T_State): +async def _(bot: Bot, event: MessageEvent, state: T_State): msg = get_message_text(event.json()) if not msg: - await search_anime.reject('番名番名番名呢?', at_sender=True) - state['anime'] = msg + await search_anime.reject("番名番名番名呢?", at_sender=True) + state["anime"] = msg @search_anime.handle() -async def _(bot: Bot, event: Event, state: T_State): +async def _(bot: Bot, event: MessageEvent, state: T_State): if _ulmt.check(event.user_id): - await search_anime.finish('您有动漫正在搜索,请稍等...', at_sender=True) + await search_anime.finish("您有动漫正在搜索,请稍等...", at_sender=True) _ulmt.set_True(event.user_id) if get_message_text(event.json()): - state['anime'] = get_message_text(event.json()) + state["anime"] = get_message_text(event.json()) -@search_anime.got('anime', prompt='是不是少了番名?') -async def _(bot: Bot, event: Event, state: T_State): - key_word = state['anime'] - await search_anime.send(f'开始搜番 {key_word}', at_sender=True) - anime_report = await from_anime_get_info(key_word, MAXINFO_GROUP_ANIME if get_message_type(event.json()) in ['group', 'discuss'] else MAXINFO_PRIVATE_ANIME) +@search_anime.got("anime", prompt="是不是少了番名?") +async def _(bot: Bot, event: MessageEvent, state: T_State): + key_word = state["anime"] + await search_anime.send(f"开始搜番 {key_word}", at_sender=True) + anime_report = await from_anime_get_info( + key_word, + MAXINFO_GROUP_ANIME + if isinstance(event, GroupMessageEvent) + else MAXINFO_PRIVATE_ANIME, + ) if anime_report: await search_anime.send(anime_report) - logger.info(f"USER {event.user_id} GROUP" - f" {event.group_id if event.message_type != 'private' else 'private'} 搜索番剧 {key_word} 成功") + logger.info( + f"USER {event.user_id} GROUP" + f" {event.group_id if isinstance(event, GroupMessageEvent) else 'private'} 搜索番剧 {key_word} 成功" + ) else: logger.warning(f"未找到番剧 {key_word}") await search_anime.send(f"未找到番剧 {key_word}(也有可能是超时,再尝试一下?)") _ulmt.set_False(event.user_id) - - - diff --git a/plugins/search_anime/data_source.py b/plugins/search_anime/data_source.py index 450c93e9..6526387e 100644 --- a/plugins/search_anime/data_source.py +++ b/plugins/search_anime/data_source.py @@ -7,41 +7,55 @@ import time from utils.utils import get_local_proxy -async def from_anime_get_info(key_word: str, max: int) -> str: +async def from_anime_get_info(key_word: str, max_: int) -> str: s_time = time.time() repass = "" - url = 'https://share.dmhy.org/topics/rss/rss.xml?keyword=' + parse.quote(key_word) + url = "https://share.dmhy.org/topics/rss/rss.xml?keyword=" + parse.quote(key_word) try: logger.debug("Now starting get the {}".format(url)) - repass = await get_repass(url, max) + repass = await get_repass(url, max_) except Exception as e: logger.error("Timeout! {}".format(e)) return f"搜索 {key_word} 结果(耗时 {int(time.time() - s_time)} 秒):\n" + repass -async def get_repass(url: str, max: int) -> str: +async def get_repass(url: str, max_: int) -> str: putline = [] async with aiohttp.ClientSession() as session: async with session.get(url, proxy=get_local_proxy(), timeout=20) as response: d = feedparser.parse(await response.text()) - url_list = [e.link for e in d.entries][:max] + url_list = [e.link for e in d.entries][:max_] for u in url_list: print(u) try: - async with session.get(u, proxy=get_local_proxy(), timeout=20) as res: + async with session.get( + u, proxy=get_local_proxy(), timeout=20 + ) as res: html = etree.HTML(await res.text()) magent = html.xpath('.//a[@id="a_magnet"]/text()')[0] - title = html.xpath('.//h3/text()')[0] - item = html.xpath('//div[@class="info resource-info right"]/ul/li') - class_a = item[0].xpath('string(.)')[5:].strip().replace("\xa0", "").replace("\t", "") - size = item[3].xpath('string(.)')[5:].strip() + title = html.xpath(".//h3/text()")[0] + item = html.xpath( + '//div[@class="info resource-info right"]/ul/li' + ) + class_a = ( + item[0] + .xpath("string(.)")[5:] + .strip() + .replace("\xa0", "") + .replace("\t", "") + ) + size = item[3].xpath("string(.)")[5:].strip() - putline.append("【{}】| {}\n【{}】| {}".format(class_a, title, size, magent)) + putline.append( + "【{}】| {}\n【{}】| {}".format(class_a, title, size, magent) + ) except Exception as e: - logger.warning(f'搜番超时 e:{e}') + logger.warning(f"搜番超时 e:{e}") - repass = '\n\n'.join(putline) + repass = "\n\n".join(putline) return repass -# print(asyncio.get_event_loop().run_until_complete(from_anime_get_info('进击的巨人', 1234556))) \ No newline at end of file + + +# print(asyncio.get_event_loop().run_until_complete(from_anime_get_info('进击的巨人', 1234556))) diff --git a/plugins/search_buff_skin_price/__init__.py b/plugins/search_buff_skin_price/__init__.py index 3cd8c953..d158e97b 100644 --- a/plugins/search_buff_skin_price/__init__.py +++ b/plugins/search_buff_skin_price/__init__.py @@ -2,52 +2,51 @@ from nonebot import on_command from .data_source import get_price, update_buff_cookie from services.log import logger from nonebot.typing import T_State -from nonebot.adapters import Bot, Event +from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent from nonebot.rule import to_me from nonebot.permission import SUPERUSER from utils.utils import UserExistLimiter, get_message_text -__plugin_name__ = '查询皮肤' -__plugin_usage__ = '查询皮肤帮助:\n\t' \ - '查询皮肤 [枪械名] [皮肤]\n\t' \ - '示例: 查询皮肤 awp 二西莫夫' +__plugin_name__ = "查询皮肤" +__plugin_usage__ = "查询皮肤帮助:\n\t" "查询皮肤 [枪械名] [皮肤]\n\t" "示例: 查询皮肤 awp 二西莫夫" _ulmt = UserExistLimiter() -search_skin = on_command('查询皮肤', aliases={'皮肤查询'}, priority=5, block=True) +search_skin = on_command("查询皮肤", aliases={"皮肤查询"}, priority=5, block=True) @search_skin.args_parser -async def parse(bot: Bot, event: Event, state: T_State): - if get_message_text(event.json()) in ['取消', '算了']: +async def parse(bot: Bot, event: MessageEvent, state: T_State): + if get_message_text(event.json()) in ["取消", "算了"]: await search_skin.finish("已取消操作..", at_sender=True) state[state["_current_key"]] = str(event.get_message()) @search_skin.handle() -async def _(bot: Bot, event: Event, state: T_State): - if str(event.get_message()) in ['帮助']: +async def _(bot: Bot, event: MessageEvent, state: T_State): + if str(event.get_message()) in ["帮助"]: await search_skin.finish(__plugin_usage__) raw_arg = get_message_text(event.json()) if _ulmt.check(event.user_id): - await search_skin.finish('您有皮肤正在搜索,请稍等...', at_sender=True) + await search_skin.finish("您有皮肤正在搜索,请稍等...", at_sender=True) if raw_arg: args = raw_arg.split(" ") if len(args) >= 2: - state['name'] = args[0] - state['skin'] = args[1] + state["name"] = args[0] + state["skin"] = args[1] -@search_skin.got('name', prompt="要查询什么武器呢?") -@search_skin.got('skin', prompt="要查询该武器的什么皮肤呢?") -async def arg_handle(bot: Bot, event: Event, state: T_State): +@search_skin.got("name", prompt="要查询什么武器呢?") +@search_skin.got("skin", prompt="要查询该武器的什么皮肤呢?") +async def arg_handle(bot: Bot, event: MessageEvent, state: T_State): + result = "" _ulmt.set_True(event.user_id) - if state['name'] in ['ak', 'ak47']: - state['name'] = 'ak-47' - name = state['name'] + " | " + state['skin'] + if state["name"] in ["ak", "ak47"]: + state["name"] = "ak-47" + name = state["name"] + " | " + state["skin"] try: result, status_code = await get_price(name) except FileNotFoundError: @@ -57,19 +56,29 @@ async def arg_handle(bot: Bot, event: Event, state: T_State): await search_skin.finish(result) if result: logger.info( - f"(USER {event.user_id}, GROUP {event.group_id if event.message_type != 'private' else 'private'}) 查询皮肤:" + name) + f"(USER {event.user_id}, GROUP " + f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'}) 查询皮肤:" + + name + ) _ulmt.set_False(event.user_id) await search_skin.finish(result) else: - logger.info(f"USER {event.user_id}, GROUP {event.group_id if event.message_type != 'private' else 'private'}" - f" 查询皮肤:{name} 没有查询到") + logger.info( + f"USER {event.user_id}, GROUP " + f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'}" + f" 查询皮肤:{name} 没有查询到" + ) _ulmt.set_False(event.user_id) await search_skin.finish("没有查询到哦,请检查格式吧") -update_buff_session = on_command("更新cookie", rule=to_me(), permission=SUPERUSER, priority=1) +update_buff_session = on_command( + "更新cookie", rule=to_me(), permission=SUPERUSER, priority=1 +) @update_buff_session.handle() -async def _(bot: Bot, event: Event, state: T_State): - await update_buff_session.finish(update_buff_cookie(str(event.get_message())), at_sender=True) +async def _(bot: Bot, event: MessageEvent, state: T_State): + await update_buff_session.finish( + update_buff_cookie(str(event.get_message())), at_sender=True + ) diff --git a/plugins/search_buff_skin_price/data_source.py b/plugins/search_buff_skin_price/data_source.py index 8eade884..97c1c7b5 100644 --- a/plugins/search_buff_skin_price/data_source.py +++ b/plugins/search_buff_skin_price/data_source.py @@ -3,7 +3,7 @@ import aiohttp from utils.utils import get_cookie_text from configs.path_config import TXT_PATH from asyncio.exceptions import TimeoutError -from configs.config import buff_proxy +from configs.config import BUFF_PROXY from pathlib import Path from services.log import logger @@ -12,17 +12,17 @@ url = "https://buff.163.com/api/market/goods" async def get_price(dname): - cookie = {'session': get_cookie_text('buff')} + cookie = {"session": get_cookie_text("buff")} name_list = [] price_list = [] - parameter = { - "game": "csgo", - "page_num": "1", - "search": dname - } + parameter = {"game": "csgo", "page_num": "1", "search": dname} try: - async with aiohttp.ClientSession(cookies=cookie, headers=get_user_agent()) as session: - async with session.get(url, proxy=buff_proxy, params=parameter, timeout=5) as response: + async with aiohttp.ClientSession( + cookies=cookie, headers=get_user_agent() + ) as session: + async with session.get( + url, proxy=BUFF_PROXY, params=parameter, timeout=5 + ) as response: if response.status == 200: try: if str(await response.text()).find("Login Required") != -1: @@ -53,14 +53,13 @@ def update_buff_cookie(cookie: str): _cookie = Path(TXT_PATH + "cookie/buff.txt") try: _cookie.parent.mkdir(parents=True, exist_ok=True) - with open(_cookie, 'w') as f: + with open(_cookie, "w") as f: f.write(cookie) return "更新cookie成功" except Exception as e: - logger.error(f'更新cookie失败 e:{e}') + logger.error(f"更新cookie失败 e:{e}") return "更新cookie失败" -if __name__ == '__main__': +if __name__ == "__main__": print(get_price("awp 二西莫夫")) - diff --git a/plugins/send_dinggong_voice/__init__.py b/plugins/send_dinggong_voice/__init__.py index 80968385..3a012b6d 100644 --- a/plugins/send_dinggong_voice/__init__.py +++ b/plugins/send_dinggong_voice/__init__.py @@ -1,21 +1,21 @@ from nonebot import on_keyword -from utils.init_result import record +from utils.message_builder import record from configs.path_config import VOICE_PATH import random from services.log import logger from utils.utils import FreqLimiter from nonebot.typing import T_State -from nonebot.adapters.cqhttp import Bot, MessageEvent +from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent from nonebot.rule import to_me import os -__plugin_name__ = '骂我' +__plugin_name__ = "骂我" __plugin_usage__ = '对我说 "骂我",我真的会骂你哦' _flmt = FreqLimiter(3) -dg_voice = on_keyword({'骂'}, rule=to_me(), priority=5, block=True) +dg_voice = on_keyword({"骂"}, rule=to_me(), priority=5, block=True) @dg_voice.handle() @@ -23,11 +23,13 @@ async def _(bot: Bot, event: MessageEvent, state: T_State): if len(str((event.get_message()))) == 1: return if not _flmt.check(event.user_id): - await dg_voice.finish('就...就算求我骂你也得慢慢来...', at_sender=True) + await dg_voice.finish("就...就算求我骂你也得慢慢来...", at_sender=True) _flmt.start_cd(event.user_id) - voice = random.choice(os.listdir(VOICE_PATH + 'dinggong/')) + voice = random.choice(os.listdir(VOICE_PATH + "dinggong/")) result = record(voice, "dinggong") await dg_voice.send(result) - await dg_voice.send(voice.split('_')[1]) + await dg_voice.send(voice.split("_")[1]) logger.info( - f"(USER {event.user_id}, GROUP {event.group_id if event.message_type != 'private' else 'private'}) 发送钉宫骂人:" + result) \ No newline at end of file + f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'}) 发送钉宫骂人:" + + result + ) diff --git a/plugins/send_img/__init__.py b/plugins/send_img/__init__.py index 3c09f55a..479db526 100644 --- a/plugins/send_img/__init__.py +++ b/plugins/send_img/__init__.py @@ -1,12 +1,12 @@ from nonebot import on_command, on_keyword, on_regex from configs.path_config import IMAGE_PATH -from utils.init_result import image +from utils.message_builder import image from utils.utils import get_message_text, is_number import os import random from services.log import logger from nonebot.typing import T_State -from nonebot.adapters.cqhttp import Bot, MessageEvent, Message +from nonebot.adapters.cqhttp import Bot, MessageEvent, Message, GroupMessageEvent from utils.utils import FreqLimiter, cn2py from models.group_remind import GroupRemind from asyncio.exceptions import TimeoutError @@ -20,86 +20,88 @@ try: except ModuleNotFoundError: import json -__plugin_name__ = '壁纸/萝莉/美图/在线搜图' -__plugin_usage__ = '用法: \n' \ - '\t1.发送"壁纸/萝莉/美图", 回复图片,后添加id获得指定图片 示例:萝莉 123\n' \ - '\t2.在线搜图 示例:1张米浴的图' +__plugin_name__ = "壁纸/萝莉/美图/在线搜图" +__plugin_usage__ = ( + "用法: \n" '\t1.发送"壁纸/萝莉/美图", 回复图片,后添加id获得指定图片 示例:萝莉 123\n' "\t2.在线搜图 示例:1张米浴的图" +) _flmt = FreqLimiter(1) -IMAGE_DIR_LIST.remove('色图') +IMAGE_DIR_LIST.remove("色图") cmd = set(IMAGE_DIR_LIST) # print(cmd) send_img = on_command("img", aliases=cmd, priority=5, block=True) pa = on_keyword({"爬", "爪巴"}, priority=1, block=True) -search_img = on_regex('.*[份|发|张|个|次|点]图.*?', priority=6, block=True) +search_img = on_regex(".*[份|发|张|个|次|点]图.*?", priority=6, block=True) -search_url = 'https://api.fantasyzone.cc/tu/search.php' +search_url = "https://api.fantasyzone.cc/tu/search.php" @send_img.handle() async def _(bot: Bot, event: MessageEvent, state: T_State): img_id = get_message_text(event.json()) - path = cn2py(state["_prefix"]["raw_command"]) + '/' + path = cn2py(state["_prefix"]["raw_command"]) + "/" if path in IMAGE_DIR_LIST: - if not os.path.exists(f'{IMAGE_PATH}/{path}/'): - os.mkdir(f'{IMAGE_PATH}/{path}/') + if not os.path.exists(f"{IMAGE_PATH}/{path}/"): + os.mkdir(f"{IMAGE_PATH}/{path}/") length = len(os.listdir(IMAGE_PATH + path)) - 1 if length < 1: - await send_img.finish('该图库中没有图片噢') - logger.warning(f'图库 {path} 为空,调用取消!') + await send_img.finish("该图库中没有图片噢") + logger.warning(f"图库 {path} 为空,调用取消!") return index = img_id if img_id else str(random.randint(0, length)) if not is_number(index): await send_img.finish("id错误!") if int(index) > length or int(index) < 0: await send_img.finish(f"超过当前上下限!({length - 1})") - result = image(f'{index}.jpg', path) + result = image(f"{index}.jpg", path) if result: logger.info( - f"(USER {event.user_id}, GROUP {event.group_id if event.message_type != 'private' else 'private'}) 发送{path}:" + result) + f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'}) 发送{path}:" + + result + ) await send_img.finish(f"id:{index}" + result) else: logger.info( - f"(USER {event.user_id}, GROUP {event.group_id if event.message_type != 'private' else 'private'}) 发送 {path} 失败") + f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'}) 发送 {path} 失败" + ) await send_img.finish(f"不想给你看Ov|") @pa.handle() async def _(bot: Bot, event: MessageEvent, state: T_State): - if await GroupRemind.get_status(event.group_id, 'pa'): - try: - if str(event.get_message()[:2]) in ['开启', '关闭']: + if isinstance(event, GroupMessageEvent): + if await GroupRemind.get_status(event.group_id, "pa"): + msg = get_message_text(event.json()) + if not msg or str(event.get_message()[:2]) in ["开启", "关闭"]: return - except: - return if not _flmt.check(event.user_id): return _flmt.start_cd(event.user_id) - await pa.finish(image(random.choice(os.listdir(IMAGE_PATH + "pa")), 'pa')) + await pa.finish(image(random.choice(os.listdir(IMAGE_PATH + "pa")), "pa")) num_key = { - '一': 1, - '二': 2, - '两': 2, - '双': 2, - '三': 3, - '四': 4, - '五': 5, - '六': 6, - '七': 7, - '八': 8, - '九': 9 + "一": 1, + "二": 2, + "两": 2, + "双": 2, + "三": 3, + "四": 4, + "五": 5, + "六": 6, + "七": 7, + "八": 8, + "九": 9, } @search_img.handle() async def _(bot: Bot, event: MessageEvent, state: T_State): msg = get_message_text(event.json()) - r = re.search('[来要]?(.*)[份发张个次点]图(.*)', msg) + r = re.search("[来要]?(.*)[份发张个次点]图(.*)", msg) num = r.group(1) if num in num_key.keys(): num = num_key[num] @@ -108,28 +110,27 @@ async def _(bot: Bot, event: MessageEvent, state: T_State): else: return keyword = r.group(2) - params = { - 'search': keyword, - 'r18': 0 - } + params = {"search": keyword, "r18": 0} async with aiohttp.ClientSession() as session: exists_id = [] for _ in range(num): for _ in range(10): try: - async with session.get(search_url, timeout=5, params=params) as response: + async with session.get( + search_url, timeout=5, params=params + ) as response: data = json.loads(await response.text()) except TimeoutError: pass else: - if data['id'] == 'null': - await send_img.finish(f'没有搜索到 {keyword} 的图片...', at_sender=True) - if data['id'] in exists_id: + if data["id"] == "null": + await send_img.finish(f"没有搜索到 {keyword} 的图片...", at_sender=True) + if data["id"] in exists_id: continue - title = data['title'] - author = data['userName'] - pid = data['id'] - img_url = data['url'] + title = data["title"] + author = data["userName"] + pid = data["id"] + img_url = data["url"] exists_id.append(pid) for _ in range(5): try: @@ -139,21 +140,27 @@ async def _(bot: Bot, event: MessageEvent, state: T_State): else: break else: - await search_img.finish('图片下载失败...', at_sender=True) - await search_img.send(Message(f'title:{title}\n' - f'pid:{pid}\n' - f'author:{author}\n' - f'{image(f"send_img_{event.user_id}.png", "temp")}')) + await search_img.finish("图片下载失败...", at_sender=True) + await search_img.send( + Message( + f"title:{title}\n" + f"pid:{pid}\n" + f"author:{author}\n" + f'{image(f"send_img_{event.user_id}.png", "temp")}' + ) + ) break else: - await search_img.send('图片下载惜败了....', at_sender=True) + await search_img.send("图片下载惜败了....", at_sender=True) logger.info( - f"(USER {event.user_id}, GROUP {event.group_id if event.message_type != 'private' else 'private'})" - f" 发送搜索了 {num} 张 {keyword} 的图片") + f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" + f" 发送搜索了 {num} 张 {keyword} 的图片" + ) async def download_pic(img_url: str, user_id: int, session): async with session.get(img_url, timeout=2) as res: - async with aiofiles.open(f'{IMAGE_PATH}/temp/send_img_{user_id}.png', 'wb') as f: + async with aiofiles.open( + f"{IMAGE_PATH}/temp/send_img_{user_id}.png", "wb" + ) as f: await f.write(await res.read()) - diff --git a/plugins/send_setu/__init__.py b/plugins/send_setu/__init__.py index 61278605..625c95e3 100644 --- a/plugins/send_setu/__init__.py +++ b/plugins/send_setu/__init__.py @@ -1,252 +1,209 @@ import random from nonebot import on_command, on_regex -from nonebot.permission import SUPERUSER from services.log import logger from models.sigin_group_user import SignGroupUser -from utils.utils import FreqLimiter, UserExistLimiter, is_number, get_message_text, get_message_imgs +from nonebot.exception import IgnoredException +from nonebot.message import run_preprocessor, run_postprocessor +from nonebot.matcher import Matcher +from typing import Optional, Type +from gino.exceptions import UninitializedError +from utils.utils import ( + FreqLimiter, + UserExistLimiter, + is_number, + get_message_text, + get_message_imgs, +) from nonebot.typing import T_State -from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent, PrivateMessageEvent -from .data_source import get_setu, get_luoxiang, search_online_setu, get_setu_urls, \ - check_r18_and_keyword, find_img_index, delete_img, add_img +from nonebot.adapters.cqhttp import ( + Bot, + MessageEvent, + GroupMessageEvent, + PrivateMessageEvent, + Message, + Event, +) +from .data_source import ( + get_setu_list, + get_luoxiang, + search_online_setu, + get_setu_urls, + find_img_index, + gen_message, + check_local_exists_or_download, + add_data_to_database, +) from nonebot.adapters.cqhttp.exception import ActionFailed +from configs.config import ONLY_USE_LOCAL_SETU, WITHDRAW_SETU_TIME +from utils.message_builder import at import re -from models.count_user import UserCount -from aiohttp.client_exceptions import ClientConnectorError -from configs.config import MAX_SETU_R_COUNT -from configs.path_config import TXT_PATH +import asyncio + try: import ujson as json except ModuleNotFoundError: import json -__plugin_name__ = '色图' -__plugin_usage__ = f'''示例: +__plugin_name__ = "色图" +__plugin_usage__ = f"""示例: 1. 色图 (随机本地色图) 2. 色图r (随机在线十张r18涩图) 3. 色图 666 (本地色图id) - 4. 色图 xx (在线搜索xx色图) + 4. 色图 xx xx (在线搜索xx xx色图) 5. 色图r xx (搜索十张xx的r18涩图,注意空格)(仅私聊,每日限制5次) 6. 来n张涩图 (本地涩图连发)(1<=n<=9) 7. 来n张xx的涩图 (在线搜索xx涩图)(较慢,看网速) -注:【色图r每日提供{MAX_SETU_R_COUNT}次 +注: + xx 为 tag,多余20取前20 示例:原神 黑丝 本地涩图没有r18! 联网搜索会较慢! 如果图片数量与数字不符, 原因1:网络不好,网线被拔QAQ 原因2:搜索到的总数小于数字 - 原因3:图太色或者小错误了】''' + 原因3:图太色或者小错误了】""" -url = "https://api.lolicon.app/setu/" _flmt = FreqLimiter(5) _ulmt = UserExistLimiter() -path = "_setu/" +setu_data_list = [] +withdraw_message_id = [] -setu = on_command("色图", aliases={"涩图", "不够色", "来一发", "再来点"}, priority=5, block=True) -setu_reg = on_regex('(.*)[份|发|张|个|次|点](.*)[瑟|色|涩]图', priority=5, block=True) -find_setu = on_command("查色图", priority=5, block=True) -delete_setu = on_command('删除色图', aliases={'删除涩图'}, priority=5, block=True, permission=SUPERUSER) -upload_setu = on_command('上传色图', aliases={'上传涩图'}, priority=5, block=True, permission=SUPERUSER) - - -@setu.handle() -async def _(bot: Bot, event: GroupMessageEvent, state: T_State): - img_id = get_message_text(event.json()) - if img_id in ['帮助']: - await setu.finish(__plugin_usage__) - impression = (await SignGroupUser.ensure(event.user_id, event.group_id)).impression - luox = get_luoxiang(impression) - if luox: - await setu.finish(luox) - if not _flmt.check(event.user_id): - await setu.finish('您冲得太快了,请稍候再冲', at_sender=True) - _flmt.start_cd(event.user_id) - if _ulmt.check(event.user_id): - await setu.finish(f"您有色图正在处理,请稍等") - _ulmt.set_True(event.user_id) - try: - setu_data = json.load(open(f'{TXT_PATH}/setu_data.json', 'r', encoding='utf8')) - except Exception: - setu_data = {} - setu_img, index = get_setu(img_id, setu_data) - if setu_img: - try: - await setu.send(setu_img) - except: - _ulmt.set_False(event.user_id) - await setu.finish('这张图色过头了,我自己看看就行了!', at_sender=True) - logger.info( - f"USER {event.user_id} GROUP {event.group_id} 发送色图 {index}.jpg 成功") - else: - msg = img_id - if msg.find('真寻') != -1: - _ulmt.set_False(event.user_id) - await setu.finish('咳咳咳,虽然我很可爱,但是我木有自己的色图~~~有的话记得发我一份呀') - keyword, r18, num = await check_r18_and_keyword(msg, event.user_id) - if r18 == 1: - _ulmt.set_False(event.user_id) - await setu.finish(random.choice([ - "这种不好意思的东西怎么可能给这么多人看啦", - "羞羞脸!给我滚出克私聊!", - "变态变态变态变态大变态!" - ])) - try: - urls, text_list, code = await get_setu_urls(keyword, num, r18=r18) - except ClientConnectorError: - _ulmt.set_False(event.user_id) - await setu.finish('网络失败了..别担心!正在靠运气上网!', at_sender=True) - else: - if code == 200: - for i in range(num): - try: - setu_img, index = await search_online_setu(urls[i]) - if index == -1: - setu_img, index = get_setu('', setu_data, keyword) - if setu_img: - await setu.send(setu_img) - logger.error(f'色图下载失败 本地发送 {index}.jpg') - else: - await setu.send(text_list[i] + setu_img) - logger.info( - f"USER {event.user_id} GROUP {event.group_id}" - f" 发送在线色图 {keyword}.jpg 成功") - except Exception as e: - setu_img, index = get_setu('', setu_data, keyword) - if setu_img: - await setu.send(setu_img) - logger.error(f'色图发送错误 本地发送 {index}.jpg e:{e}') - else: - await setu.send(urls) - _ulmt.set_False(event.user_id) - - -@setu.handle() -async def _(bot: Bot, event: PrivateMessageEvent, state: T_State): - img_id = get_message_text(event.json()) - if img_id in ['帮助']: - await setu.finish(__plugin_usage__) - if not _flmt.check(event.user_id): - await setu.finish('您冲得太快了,请稍候再冲', at_sender=True) - _flmt.start_cd(event.user_id) - if _ulmt.check(event.user_id): - await setu.finish(f"您有色图正在处理,请稍等") - _ulmt.set_True(event.user_id) - try: - setu_data = json.load(open(f'{TXT_PATH}/setu_data.json', 'r', encoding='utf8')) - except Exception: - setu_data = {} - setu_img, index = get_setu(img_id, setu_data) - if setu_img: - await setu.send(setu_img) - logger.info( - f"USER {event.user_id} GROUP private 发送色图 {index}.jpg 成功") - else: - msg = img_id - if msg.find('真寻') != -1: - _ulmt.set_False(event.user_id) - await setu.finish('咳咳咳,虽然我很可爱,但是我木有自己的色图~~~有的话记得发我一份呀') - keyword, r18, num = await check_r18_and_keyword(msg, event.user_id) - if r18 == 1: - if await UserCount.check_count(event.user_id, 'setu_r18', MAX_SETU_R_COUNT): - _ulmt.set_False(event.user_id) - await setu.finish('要节制啊,请明天再来...\n【每日提供5次】', at_sender=True) - else: - await UserCount.add_count(event.user_id, 'setu_r18', count=1) - try: - setu_data = json.load(open(f'{TXT_PATH}/r18_setu_data.json', 'r', encoding='utf8')) - except Exception: - setu_data = {} - try: - urls, text_list, code = await get_setu_urls(keyword, num, r18=r18) - except ClientConnectorError: - await UserCount.add_count(event.user_id, 'setu_r18', count=-1) - _ulmt.set_False(event.user_id) - await setu.finish('网络失败了..别担心!这次搜索不算数喔', at_sender=True) - else: - count = 0 - if code == 200: - for i in range(num): - try: - setu_img, index = await search_online_setu(urls[i]) - if index == -1: - if img_id == 'r': - setu_img, index = get_setu('', setu_data, keyword, True) - else: - setu_img, index = get_setu('', setu_data, keyword) - if setu_img: - await setu.send(setu_img) - logger.error(f'色图下载失败 本地发送 {index}.jpg') - else: - await setu.send(text_list[i] + setu_img) - logger.info( - f"USER {event.user_id} GROUP private" - f" 发送{'r18' if img_id == 'r' else ''}色图 {index}.jpg 成功") - except Exception as e: - if img_id == 'r': - img_setu, index = get_setu('', setu_data, keyword, True) - else: - img_setu, index = get_setu('', setu_data, keyword) - if img_setu: - await setu.send(img_setu) - logger.error(f'色图发送错误 本地发送 {index}.jpg e:{e}') - count += 1 - if count > 6: - await setu.send('检测到下载惜败的图片过多,这次就不算数了,果咩..', at_sender=True) - await UserCount.add_count(event.user_id, 'setu_r18', count=-1) - else: - if code == 401: - if r18 == 1: - await UserCount.add_count(event.user_id, 'setu_r18', count=-1) - await setu.send(urls + ' 色图r次数返还!') - else: - await setu.send(urls, at_sender=True) +@run_preprocessor +async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State): + if isinstance(event, MessageEvent): + if matcher.module == "send_setu": + if _ulmt.check(event.user_id): + if isinstance(event, GroupMessageEvent): + await bot.send_group_msg( + group_id=event.group_id, + message=Message(f"{at(event.user_id)}您有色图正在处理,请稍等....."), + ) else: - if r18 == 1: - await setu.send('这次不是小真寻的戳!色图r次数返还!', at_sender=True) - await UserCount.add_count(event.user_id, 'setu_r18', count=-1) - else: - await setu.send(urls, at_sender=True) - _ulmt.set_False(event.user_id) + await bot.send_private_msg( + user_id=event.user_id, message=f"您有色图正在处理,请稍等....." + ) + raise IgnoredException("色图正在处理!") + _ulmt.set_True(event.user_id) + + +@run_postprocessor +async def do_something( + matcher: Matcher, + exception: Optional[Exception], + bot: Bot, + event: Event, + state: T_State, +): + global setu_data_list, withdraw_message_id + if isinstance(event, MessageEvent): + if matcher.module == "send_setu": + # 解除占用 + _ulmt.set_False(event.user_id) + tasks = [] + # 撤回色图 + for id_ in withdraw_message_id[:]: + tasks.append(asyncio.ensure_future(withdraw_message(bot, event, id_))) + withdraw_message_id.remove(id_) + await asyncio.gather(*tasks) + # 添加数据至数据库 + try: + await add_data_to_database(setu_data_list) + logger.info("色图数据自动存储数据库成功...") + setu_data_list = [] + except UninitializedError: + pass + + +setu = on_command( + "色图", aliases={"涩图", "不够色", "来一发", "再来点", "色图r"}, priority=5, block=True +) + +setu_reg = on_regex("(.*)[份|发|张|个|次|点](.*)[瑟|色|涩]图", priority=5, block=True) + +find_setu = on_command("查色图", priority=5, block=True) + + +@setu.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + global withdraw_message_id + msg = get_message_text(event.json()) + if isinstance(event, GroupMessageEvent): + impression = ( + await SignGroupUser.ensure(event.user_id, event.group_id) + ).impression + luox = get_luoxiang(impression) + if luox: + await setu.finish(luox) + if not _flmt.check(event.user_id): + await setu.finish("您冲得太快了,请稍候再冲", at_sender=True) + _flmt.start_cd(event.user_id) + r18 = 0 + num = 1 + # 是否看r18 + if state["_prefix"]["raw_command"] == "色图r" and isinstance( + event, PrivateMessageEvent + ): + r18 = 1 + num = 10 + elif state["_prefix"]["raw_command"] == "色图r" and isinstance( + event, GroupMessageEvent + ): + await setu.finish( + random.choice(["这种不好意思的东西怎么可能给这么多人看啦", "羞羞脸!给我滚出克私聊!", "变态变态变态变态大变态!"]) + ) + # 有 数字 的话先尝试本地色图id + if msg and is_number(msg): + setu_list, code = await get_setu_list(int(msg), r18=r18) + if code != 200: + await setu.finish(setu_list[0], at_sender=True) + setu_img, code = await check_local_exists_or_download(setu_list[0]) + msg_id = await setu.send(gen_message(setu_list[0]) + setu_img, at_sender=True) + logger.info( + f"(USER {event.user_id}, GROUP " + f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" + f" 发送色图 {setu_list[0].local_id}.png" + ) + withdraw_message_id.append(msg_id["message_id"]) + return + await send_setu_handle(setu, event, state["_prefix"]["raw_command"], msg, num, r18) num_key = { - '一': 1, - '二': 2, - '两': 2, - '双': 2, - '三': 3, - '四': 4, - '五': 5, - '六': 6, - '七': 7, - '八': 8, - '九': 9 + "一": 1, + "二": 2, + "两": 2, + "双": 2, + "三": 3, + "四": 4, + "五": 5, + "六": 6, + "七": 7, + "八": 8, + "九": 9, } @setu_reg.handle() async def _(bot: Bot, event: MessageEvent, state: T_State): - if event.message_type == "group": - impression = (await SignGroupUser.ensure(event.user_id, event.group_id)).impression + if isinstance(event, GroupMessageEvent): + impression = ( + await SignGroupUser.ensure(event.user_id, event.group_id) + ).impression luox = get_luoxiang(impression) if luox: await setu.finish(luox, at_sender=True) - if _ulmt.check(event.user_id): - await setu.finish(f"您有色图正在处理,请稍等") - _ulmt.set_True(event.user_id) if not _flmt.check(event.user_id): - _ulmt.set_False(event.user_id) - await setu.finish('您冲得太快了,请稍候再冲', at_sender=True) + await setu.finish("您冲得太快了,请稍候再冲", at_sender=True) _flmt.start_cd(event.user_id) msg = get_message_text(event.json()) num = 1 - msg = re.search(r'(.*)[份|发|张|个|次|点](.*)[瑟|涩|色]图', msg) + msg = re.search(r"(.*)[份发张个次点](.*)[瑟涩色]图", msg) + # 解析 tags 以及 num if msg: num = msg.group(1) - keyword = msg.group(2) - if keyword: - if keyword[-1] == '的': - keyword = keyword[:-1] + tags = msg.group(2) + if tags: + tags = tags[:-1] if tags[-1] == "的" else tags if num: num = num[-1] if num_key.get(num): @@ -260,111 +217,124 @@ async def _(bot: Bot, event: MessageEvent, state: T_State): num = 1 else: return - # try: - try: - setu_data = json.load(open(f'{TXT_PATH}/setu_data.json', 'r', encoding='utf8')) - except Exception: - setu_data = {} - if not keyword: - for _ in range(num): - try: - img, index = get_setu('', setu_data) - if not img: - break - await setu_reg.send(img) - except Exception as e: - await setu_reg.send('有图太色了发不出来...') - else: - logger.info( - f"USER {event.user_id} GROUP {event.group_id if event.message_type != 'private' else 'private'}" - f" 发送 {index} 色图成功") - _ulmt.set_False(event.user_id) - else: - _ulmt.set_False(event.user_id) - return - if keyword.find('真寻') != -1: - _ulmt.set_False(event.user_id) - await setu.finish('咳咳咳,虽然我很可爱,但是我木有自己的色图~~~有的话记得发我一份呀') - urls, text_list, code = await get_setu_urls(keyword, num) - if code == 200: - for i in range(len(urls)): - try: - setu_img, index = await search_online_setu(urls[i]) - if index == -1: - setu_img, index = get_setu('', setu_data, keyword) - if setu_img: - await setu_reg.send(setu_img) - logger.error(f'色图下载失败 本地发送 {index}.jpg') - else: - await setu_reg.send(text_list[i] + '\n' + setu_img) - except ActionFailed as e: - await setu_reg.send('这图太色了,会教坏小孩子的,不给看..') - except Exception as e: - setu_img, index = get_setu('', setu_data, keyword) - if setu_img: - await setu_reg.send(setu_img) - logger.error(f'色图发送错误 e:{e}') - else: - logger.info( - f"USER {event.user_id} GROUP {event.group_id if event.message_type != 'private' else 'private'}" - f" 发送 {keyword} {num}连 色图成功") - else: - _ulmt.set_False(event.user_id) - await setu_reg.finish(urls, at_sender=True) - _ulmt.set_False(event.user_id) + await send_setu_handle(setu_reg, event, tags, num, 0) @find_setu.args_parser async def _(bot: Bot, event: MessageEvent, state: T_State): - if str(event.message) == '取消': - await find_setu.finish('取消了操作', at_sender=True) + if str(event.message) == "取消": + await find_setu.finish("取消了操作", at_sender=True) imgs = get_message_imgs(event.json()) if not imgs: await find_setu.reject("不搞错了,俺要图!") - state['img'] = imgs[0] + state["img"] = imgs[0] @find_setu.handle() async def _(bot: Bot, event: MessageEvent, state: T_State): - if get_message_text(event.json()) in ['帮助']: - await find_setu.finish('通过图片获取本地色图id\n\t示例:查色图(图片)') + if get_message_text(event.json()) in ["帮助"]: + await find_setu.finish("通过图片获取本地色图id\n\t示例:查色图(图片)") imgs = get_message_imgs(event.json()) if imgs: - state['img'] = imgs[0] + state["img"] = imgs[0] -@find_setu.got('img', prompt="速速来图!") +@find_setu.got("img", prompt="速速来图!") async def _(bot: Bot, event: MessageEvent, state: T_State): - img = state['img'] + img = state["img"] await find_setu.send(await find_img_index(img, event.user_id), at_sender=True) -@delete_setu.handle() -async def _(bot: Bot, event: MessageEvent, state: T_State): - _id = get_message_text(event.json()) - if _id: - flag, text = delete_img(int(_id)) - if flag: - await delete_setu.finish(f'删除色图 id:{_id} 成功', at_sender=True) - else: - await delete_setu.finish(f'删除色图 id:{_id} 失败,{text}', at_sender=True) - - -@upload_setu.handle() -async def _(bot: Bot, event: MessageEvent, state: T_State): - imgs = get_message_imgs(event.json()) - if imgs: - lens, add_count = await add_img(imgs) - if add_count == 0: - await upload_setu.finish('上传的涩图已在色图库中,未成功上传,可以通过 查色图 来查看色图在图库中的id', at_sender=True) - id_s = '' - for i in range(add_count, 0, -1): - id_s += f'{lens - add_count} ' - await upload_setu.finish(f"这次一共为 色图 库 添加了 {add_count} 张图片\n" - f"依次的Id为:{id_s[:-1]}\n" - f"小真寻感谢您对图库的扩充!WW", at_sender=True) - logger.info( - f"(USER {event.user_id}, GROUP {event.group_id if event.message_type != 'private' else 'private'})" - f" 上传色图ID:{id_s[:-1]}") +async def send_setu_handle( + matcher: Type[Matcher], event: MessageEvent, command: str, msg: str, num: int, r18: int +): + global setu_data_list, withdraw_message_id + error_info = "" + # 非 id,在线搜索 + tags = msg.split() + # 真寻的色图?怎么可能 + if "真寻" in tags: + await matcher.finish("咳咳咳,虽然我很可爱,但是我木有自己的色图~~~有的话记得发我一份呀") + # 本地先拿图,下载失败补上去 + setu_list, code = await get_setu_list(tags=msg.split(), r18=r18) + if not ONLY_USE_LOCAL_SETU and (tags or not setu_list or num > len(setu_list)): + # 先尝试获取在线图片 + urls, text_list, add_databases_list, code = await get_setu_urls(tags, num, r18, command) + for x in add_databases_list: + setu_data_list.append(x) + # 未找到符合的色图,想来本地应该也没有 + if code == 401: + await setu.finish(urls[0], at_sender=True) + if code == 200: + for i in range(len(urls)): + try: + setu_img, index = await search_online_setu(urls[i]) + # 下载成功的话 + if index != -1: + logger.info( + f"(USER {event.user_id}, GROUP " + f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" + f" 发送色图 {index}.png" + ) + msg_id = await matcher.send(Message(f"{text_list[i]}\n{setu_img}")) + withdraw_message_id.append(msg_id["message_id"]) + else: + if setu_list: + setu_image = random.choice(setu_list) + setu_list.remove(setu_image) + msg_id = await matcher.send( + Message( + gen_message(setu_image) + + (await check_local_exists_or_download(setu_image))[0] + ) + ) + logger.info( + f"(USER {event.user_id}, GROUP " + f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" + f" 发送本地色图 {setu_image.local_id}.png" + ) + else: + msg_id = await matcher.send(text_list[i] + "\n" + setu_img) + withdraw_message_id.append(msg_id["message_id"]) + except ActionFailed: + await matcher.finish("坏了,这张图色过头了,我自己看看就行了!", at_sender=True) + return + # 本地无图 或 超过上下限 + if code != 200 or (not setu_list and ONLY_USE_LOCAL_SETU): + await matcher.finish(setu_list[0], at_sender=True) + elif not setu_list: + await matcher.finish(error_info, at_sender=True) + # 开始发图 + for _ in range(num): + if not setu_list: + await setu.finish("坏了,已经没图了,被榨干了!") + setu_image = random.choice(setu_list) + setu_list.remove(setu_image) + try: + msg_id = await matcher.send( + Message( + gen_message(setu_image) + + (await check_local_exists_or_download(setu_image))[0] + ) + ) + withdraw_message_id.append(msg_id["message_id"]) + logger.info( + f"(USER {event.user_id}, GROUP " + f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" + f" 发送本地色图 {setu_image.local_id}.png" + ) + except ActionFailed: + await matcher.finish("坏了,这张图色过头了,我自己看看就行了!", at_sender=True) +# 撤回图片 +async def withdraw_message(bot: Bot, event: MessageEvent, id_: int): + if WITHDRAW_SETU_TIME[0]: + if ( + (WITHDRAW_SETU_TIME[1] == 0 and isinstance(event, PrivateMessageEvent)) + or (WITHDRAW_SETU_TIME[1] == 1 and isinstance(event, GroupMessageEvent)) + or WITHDRAW_SETU_TIME[1] == 2 + ): + await asyncio.sleep(WITHDRAW_SETU_TIME[0]) + await bot.delete_msg(message_id=id_, self_id=int(bot.self_id)) + logger.info(f"自动撤回色图 消息id:{id_}") diff --git a/plugins/send_setu/check_setu_hash.py b/plugins/send_setu/check_setu_hash.py deleted file mode 100644 index e4ce21b1..00000000 --- a/plugins/send_setu/check_setu_hash.py +++ /dev/null @@ -1,62 +0,0 @@ -import os -import imagehash -from PIL import Image -try: - import ujson as json -except ModuleNotFoundError: - import json - -IMAGE_PATH = r"/home/hibiki/hibikibot/resources/img/" -TXT_PATH = r"/home/hibiki/hibikibot/resources/txt/" - - -def get_img_hash(image_file): - with open(image_file, 'rb') as fp: - hash_value = imagehash.average_hash(Image.open(fp)) - return hash_value - - -def check_file_index(): - for dir_name in ['setu/', 'r18/']: - lens = len(os.listdir(IMAGE_PATH + dir_name)) - for i in range(lens): - if i > lens: - return - if not os.path.exists(f"{IMAGE_PATH}{dir_name}{i}.jpg"): - os.rename(f"{IMAGE_PATH}{dir_name}{lens}.jpg", f"{IMAGE_PATH}{dir_name}{i}.jpg") - print(f'{lens}.jpg --> {i}.jpg') - lens -= 1 - if not i % 100: - print(f'已检测 {i} 份数据') - print(f'{dir_name} 检测完毕') - - -def check_setu_hash(): - check_file_index() - for dir_name in ['setu/', 'r18/']: - img_data = {} - if dir_name == 'setu/': - fn = 'setu_img_hash.json' - else: - fn = 'r18_setu_img_hash.json' - file_list_len = len(os.listdir(IMAGE_PATH + dir_name)) - 1 - print(file_list_len) - for i in range(file_list_len): - file = f"{i}.jpg" - index = file.split(".")[0] - img_hash = str(get_img_hash(IMAGE_PATH + dir_name + file)) - print(f'{index}.jpg --> {img_hash}') - if img_hash in img_data.values(): - k = [k for k, v in img_data.items() if v == img_hash] - print(f'文件 {index}.jpg 与 {k}.jpg 重复,使用 {file_list_len}.jpg 进行替换') - os.remove(IMAGE_PATH + dir_name + file) - os.rename(IMAGE_PATH + f'{dir_name}{file_list_len}.jpg', IMAGE_PATH + dir_name + file) - file_list_len -= 1 - continue - img_data[index] = img_hash - # print(f'{index}.jpg --> {img_data}') - with open(TXT_PATH + fn, 'w') as f: - json.dump(img_data, f, indent=4) - - -check_setu_hash() \ No newline at end of file diff --git a/plugins/send_setu/data_source.py b/plugins/send_setu/data_source.py index 7e4fa4be..6bfacba4 100644 --- a/plugins/send_setu/data_source.py +++ b/plugins/send_setu/data_source.py @@ -1,260 +1,227 @@ -from configs.path_config import IMAGE_PATH, TXT_PATH -import os -import random -from utils.init_result import image -from configs.config import LOLICON_KEY +from configs.path_config import IMAGE_PATH +from utils.message_builder import image +from services.log import logger +from aiohttp.client_exceptions import ClientConnectorError +from utils.image_utils import get_img_hash +from asyncpg.exceptions import UniqueViolationError +from utils.utils import get_local_proxy +from asyncio.exceptions import TimeoutError +from typing import List, Optional +from models.setu import Setu import aiohttp import aiofiles -from services.log import logger -from utils.img_utils import get_img_hash -from utils.utils import get_local_proxy, is_number -from asyncio.exceptions import TimeoutError -from models.count_user import UserCount -from configs.config import DOWNLOAD_SETU +import os +import random + try: import ujson as json except ModuleNotFoundError: import json -url = "https://api.lolicon.app/setu/" -path = '_setu/' +url = "https://api.lolicon.app/setu/v2" +path = "_setu/" +r18_path = "_r18/" -async def get_setu_urls(keyword: str, num: int = 1, r18: int = 0): - # print(keyword) - if r18 == 1: - file_name = 'setu_r18_url.json' - else: - file_name = 'setu_url.json' - try: - with open(TXT_PATH + file_name, 'r', encoding='utf8') as f: - txt_data = json.load(f) - except (FileNotFoundError, ValueError): - txt_data = {} - txt_urls = [txt_data[x]['img_url'] for x in txt_data.keys()] +# 获取url +async def get_setu_urls( + tags: List[str], num: int = 1, r18: int = 0, command: str = '' +) -> "List[str], List[str], List[tuple], int": + tags = tags[:20] if len(tags) > 20 else tags params = { - "apikey": LOLICON_KEY, # 添加apikey - 'r18': r18, # 添加r18参数 0为否,1为是,2为混合 - 'keyword': keyword, # 若指定关键字,将会返回从插画标题、作者、标签中模糊搜索的结果 - 'num': 100, # 一次返回的结果数量,范围为1到10,不提供 APIKEY 时固定为1 - 'size1200': 1, # 是否使用 master_1200 缩略图,以节省流量或提升加载速度 + "r18": r18, # 添加r18参数 0为否,1为是,2为混合 + "tag": "|".join(tags), # 若指定tag + "num": 100, # 一次返回的结果数量,范围为1到10,不提供 APIKEY 时固定为1 + "size1200": "original", # 是否使用 master_1200 缩略图,以节省流量或提升加载速度 } urls = [] text_list = [] - for count in range(3): - print(f'get_setu_url: count --> {count}') - async with aiohttp.ClientSession() as session: + add_databases_list = [] + async with aiohttp.ClientSession() as session: + for count in range(3): + logger.info(f"get_setu_url: count --> {count}") try: - async with session.get(url, proxy=get_local_proxy(), timeout=2, params=params) as response: - if response.status == 429: - return '调用达到上限,明日赶早呀~', '', 429 - if response.status == 404: - return "网站裂开了...", '', 998 + async with session.get( + url, proxy=get_local_proxy(), timeout=2, params=params + ) as response: if response.status == 200: data = await response.json() - if data['code'] == 0: - lens = len(data['data']) - for i in range(lens): - img_url = data['data'][i]['url'] - title = data['data'][i]['title'] - author = data['data'][i]['author'] - pid = data['data'][i]['pid'] + if not data["error"]: + data = data["data"] + for i in range(len(data)): + img_url = data[i]["urls"]["original"] + img_url = ( + img_url.replace("i.pixiv.cat", "i.pximg.net") + if "i.pixiv.cat" in img_url + else img_url + ) + title = data[i]["title"] + author = data[i]["author"] + pid = data[i]["pid"] urls.append(img_url) - text_list.append(f'title:{title}\nauthor:{author}\nPID:{pid}') + text_list.append( + f"title:{title}\nauthor:{author}\nPID:{pid}" + ) tags = [] - for j in range(len(data['data'][i]['tags'])): - tags.append(data['data'][i]['tags'][j]) - if img_url not in txt_urls: - save_setu_dict = { - 'title': title, - 'author': author, - 'pid': pid, - 'img_url': img_url, - 'tags': tags - } - if str(pid) not in txt_data.keys(): - txt_data[pid] = save_setu_dict - if DOWNLOAD_SETU: - with open(TXT_PATH + file_name, 'w', encoding='utf8') as f: - json.dump(txt_data, f, ensure_ascii=False, indent=4) - num = lens if num > lens else num - random_idx = random.sample(range(len(data['data'])), num) + for j in range(len(data[i]["tags"])): + tags.append(data[i]["tags"][j]) + if command != '色图r': + if 'R-18' in tags: + tags.remove('R-18') + add_databases_list.append( + ( + title, + author, + pid, + "", + img_url, + ",".join(tags), + ) + ) + random_idx = random.sample(range(len(data)), num) x_urls = [] x_text_lst = [] for x in random_idx: x_urls.append(urls[x]) x_text_lst.append(text_list[x]) - return x_urls, x_text_lst, 200 + return x_urls, x_text_lst, add_databases_list, 200 else: - return "没找到符合条件的色图...", '', 401 - except TimeoutError: + return ["没找到符合条件的色图..."], [], [], 401 + except (TimeoutError, ClientConnectorError): pass - return '我网线被人拔了..QAQ', '', 999 + return ["我网线被人拔了..QAQ"], [], [], 999 -async def search_online_setu(url: str): - async with aiohttp.ClientSession() as session: +headers = { + "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6;" + " rv:2.0.1) Gecko/20100101 Firefox/4.0.1", + "Referer": "https://www.pixiv.net", +} + + +async def search_online_setu(url_: str) -> "MessageSegment, int": + if "i.pixiv.cat" in url_: + url_ = url_.replace("i.pixiv.cat", "i.pximg.net") + async with aiohttp.ClientSession(headers=headers) as session: for i in range(3): - print(f'search_online_setu --> {i}') + logger.info(f"search_online_setu --> {i}") try: - async with session.get(url, proxy=get_local_proxy(), timeout=2) as res: + async with session.get(url_, proxy=get_local_proxy(), timeout=3) as res: if res.status == 200: index = str(random.randint(1, 100000)) - async with aiofiles.open(IMAGE_PATH + 'temp/' + index + "_temp_setu.jpg", 'wb') as f: + async with aiofiles.open( + IMAGE_PATH + "temp/" + index + "_temp_setu.jpg", "wb" + ) as f: try: await f.write(await res.read()) except TimeoutError: # return '\n这图没下载过来~(网太差?)', -1, False continue - logger.info(f"下载 lolicon图片 {url} 成功, id:{index}") - return image(f'{index}_temp_setu.jpg', 'temp'), index + logger.info(f"下载 lolicon图片 {url_} 成功, id:{index}") + return image(f"{index}_temp_setu.jpg", "temp"), index else: - logger.warning(f"访问 lolicon图片 {url} 失败 status:{res.status}") + logger.warning(f"访问 lolicon图片 {url_} 失败 status:{res.status}") # return '\n这图好难下载啊!QAQ', -1, False - except TimeoutError: + except (TimeoutError, ClientConnectorError): pass - return '\n图片被小怪兽恰掉啦..!QAQ', -1 + return "图片被小怪兽恰掉啦..!QAQ", -1 -def get_setu(index: str, setu_data: dict, num: int = 1, tag: str = None, r18: bool = False): - if r18: - if not os.path.exists(IMAGE_PATH + '_r18'): - os.mkdir(IMAGE_PATH + '_r18') - length = len(os.listdir(IMAGE_PATH + '_r18')) +# 检测本地是否有id涩图,无的话则下载 +async def check_local_exists_or_download(setu_image: Setu) -> "MessageSegment, int": + if setu_image.is_r18: + path_ = "_r18" else: - if not os.path.exists(IMAGE_PATH + path): - os.mkdir(IMAGE_PATH + path) - length = len(os.listdir(IMAGE_PATH + path)) - if length == 0: - return None, None - if not index: - index = random.randint(0, length - 1) - if tag: - all_tag_setu = [x for x in setu_data.keys() if tag in setu_data[x]['tags']] - if all_tag_setu: - index = random.choice(all_tag_setu) - if is_number(index): - if int(index) > length or int(index) < 0: - return f"超过当前上下限!({length - 1})", 999 - else: - if setu_data: - index = str(index) - return f'id:{index}\n' \ - f'title:{setu_data[index]["title"]}\n' \ - f'author:{setu_data[index]["author"]}\n' \ - f'PID:{setu_data[index]["pid"]}' + image(f'{index}.jpg', path), index - else: - return f'id:{index}' + image(f'{index}.jpg', path), index - return None, None + path_ = path + if os.path.exists(f"{IMAGE_PATH}/{path_}/{setu_image.local_id}.jpg"): + return image(f"{setu_image.local_id}.jpg", path_), 200 + return await search_online_setu(setu_image.img_url) +# 添加涩图数据到数据库 +async def add_data_to_database(lst: List[tuple]): + tmp = [] + for x in lst: + if x not in tmp: + tmp.append(x) + if tmp: + for x in tmp: + try: + r18 = 1 if 'R-18' in x[5] else 0 + idx = await Setu.get_image_count(r18) + await Setu.add_setu_data( + idx, + x[0], + x[1], + x[2], + x[3], + x[4], + x[5], + ) + except UniqueViolationError: + pass + + +# 拿到本地色图列表 +async def get_setu_list( + index: Optional[int] = None, tags: Optional[List[str]] = None, r18: int = 0 +) -> "list, int": + if index: + image_count = await Setu.get_image_count(r18) + if index < 0 or index > image_count: + return [f"超过当前上下限!({image_count})"], 999 + image_list = [await Setu.query_image(index, r18=r18)] + elif tags: + image_list = await Setu.query_image(tags=tags, r18=r18) + else: + image_list = await Setu.query_image(r18=r18) + if not image_list: + return ["没找到符合条件的色图..."], 998 + return image_list, 200 + + +# 初始化消息 +def gen_message(setu_image: Setu, img_msg: bool = False): + local_id = setu_image.local_id + title = setu_image.title + author = setu_image.author + pid = setu_image.pid + return ( + f"id:{local_id}\n" + f"title:{title}\n" + f"author:{author}\n" + f"PID:{pid}\n" + f"{image(f'{local_id}', f'{r18_path if setu_image.is_r18 else path}') if img_msg else ''}" + ) + + +# 罗翔老师! def get_luoxiang(impression): probability = impression + 70 if probability < random.randint(1, 101): - return "我为什么要给你发这个?" + image(random.choice(os.listdir(IMAGE_PATH + "luoxiang/")), 'luoxiang') + \ - "\n(快向小真寻签到提升好感度吧!)" + return ( + "我为什么要给你发这个?" + + image(random.choice(os.listdir(IMAGE_PATH + "luoxiang/")), "luoxiang") + + "\n(快向小真寻签到提升好感度吧!)" + ) return None -async def check_r18_and_keyword(msg: str, user_id) -> 'str, int, int': - msg_list = msg.split(' ') - num = 1 - r18 = 0 - keyword = '' - if len(msg_list) == 1: - if msg_list[0].strip().lower() in ['r', 'r18']: - r18 = 1 - num = 10 - else: - keyword = msg_list[0] - elif len(msg_list) == 2: - keyword = msg_list[1].strip() - if msg_list[0].strip().lower() in ['r', 'r18']: - r18 = 1 - num = 10 - else: - keyword = msg[0] - if r18 == 1: - await UserCount.add_user(user_id) - return keyword, r18, num - - async def find_img_index(img_url, user_id): - try: - setu_data = json.load(open(TXT_PATH + 'setu_data.json', encoding='utf8')) - except (FileNotFoundError, ValueError): - setu_data = {} async with aiohttp.ClientSession() as session: async with session.get(img_url, proxy=get_local_proxy(), timeout=5) as res: - async with aiofiles.open(IMAGE_PATH + f"temp/{user_id}_find_setu_index.jpg", 'wb') as f: + async with aiofiles.open( + IMAGE_PATH + f"temp/{user_id}_find_setu_index.jpg", "wb" + ) as f: await f.write(await res.read()) img_hash = str(get_img_hash(IMAGE_PATH + f"temp/{user_id}_find_setu_index.jpg")) - try: - index = str([setu_data[x]['img_hash'] for x in setu_data.keys()].index(img_hash)) - return f"id:{index}\n" \ - f"title:{setu_data[index]['title']}\n" \ - f"author:{setu_data[index]['author']}\n" \ - f"PID:{setu_data[index]['pid']}" - except ValueError: - return "该图不在色图库中!" - - -def delete_img(_id: int): - lens = len(os.listdir(IMAGE_PATH + path)) - 1 - if _id < 0 or _id > lens: - return False, f'超过上下限限制,上限:{lens}' - try: - os.remove(IMAGE_PATH + path + f'{_id}.jpg') - if _id != lens: - setu_hash_dict = json.load(open(TXT_PATH + 'setu_data.json', encoding='utf8')) - setu_hash_dict[str(_id)] = setu_hash_dict[str(lens)] - os.rename(IMAGE_PATH + path + f'{lens}.jpg', IMAGE_PATH + path + f'{_id}.jpg') - with open(TXT_PATH + 'setu_data.json', 'w', encoding='utf8') as f: - json.dump(setu_hash_dict, f, ensure_ascii=False, indent=4) - return True, '' - except Exception as e: - logger.error(f'删除色图错误 e:{e}') - return False, str(type(e)) - - -# 添加涩图 -async def add_img(imgs: list): - index = 0 - lens = len(os.listdir(IMAGE_PATH + path)) - add_count = 0 - setu_data_dict = json.load(open(TXT_PATH + 'setu_data.json', encoding='utf8')) - async with aiohttp.ClientSession() as session: - for img in imgs: - async with session.get(img, proxy=get_local_proxy(), timeout=5) as res: - async with aiofiles.open(IMAGE_PATH + f"temp/add_setu_check_{index}.jpg", 'wb') as f: - await f.write(await res.read()) - index += 1 - index -= 1 - for i in range(index, -1, -1): - img_hash = str(get_img_hash(IMAGE_PATH + f"temp/add_setu_check_{index}.jpg")) - if img_hash not in [setu_data_dict[x]['img_hash'] for x in setu_data_dict.keys()]: - os.rename(IMAGE_PATH + f"temp/add_setu_check_{index}.jpg", IMAGE_PATH + path + f'/{lens}.jpg') - setu_data_dict[lens] = { - 'title': 'not title', - 'author': 'not author', - 'pid': 'not pid', - 'img_hash': img_hash, - 'img_url': 'not url', - } - lens += 1 - add_count += 1 - if add_count: - with open(TXT_PATH + 'setu_data.json', 'w', encoding='utf8') as f: - json.dump(setu_data_dict, f, ensure_ascii=False, indent=4) - return lens, add_count - - - - - - - - - - + setu_img = await Setu.get_image_in_hash(img_hash) + if setu_img: + return ( + f"id:{setu_img.local_id}\n" + f"title:{setu_img.title}\n" + f"author:{setu_img.author}\n" + f"PID:{setu_img.pid}" + ) + return "该图不在色图库中或色图库未更新!" diff --git a/plugins/shop/buy.py b/plugins/shop/buy.py index fd5ef8ac..04946866 100644 --- a/plugins/shop/buy.py +++ b/plugins/shop/buy.py @@ -9,31 +9,31 @@ from nonebot.adapters.cqhttp.permission import GROUP from models.goods_info import GoodsInfo -__plugin_name__ = '商店购买' -__plugin_usage__ = '格式:购买 [名称或序号] [数量](默认1)\n\t示例:购买 好感双倍加持卡Ⅰ\n\t示例:购买 1 4' +__plugin_name__ = "商店购买" +__plugin_usage__ = "格式:购买 [名称或序号] [数量](默认1)\n\t示例:购买 好感双倍加持卡Ⅰ\n\t示例:购买 1 4" -buy = on_command("购买", aliases={'购买道具'}, priority=5, block=True, permission=GROUP) +buy = on_command("购买", aliases={"购买道具"}, priority=5, block=True, permission=GROUP) @buy.handle() async def _(bot: Bot, event: GroupMessageEvent, state: T_State): - if get_message_text(event.json()) in ['神秘药水']: + if get_message_text(event.json()) in ["神秘药水"]: await buy.finish("你们看看就好啦,这是不可能卖给你们的~", at_sender=True) goods_lst = await GoodsInfo.get_all_goods() goods_name_lst = [x.goods_name for x in goods_lst] - msg = get_message_text(event.json()).strip().split(' ') + msg = get_message_text(event.json()).strip().split(" ") num = 1 if len(msg) > 1: if is_number(msg[1]): num = int(msg[1]) else: - await buy.finish('购买的数量要是数字!', at_sender=True) + await buy.finish("购买的数量要是数字!", at_sender=True) # print(msg, num) if is_number(msg[0]): msg = int(msg[0]) if msg > len(goods_lst) or msg < 1: - await buy.finish('请输入正确的商品id!', at_sender=True) + await buy.finish("请输入正确的商品id!", at_sender=True) goods = goods_lst[msg - 1] else: if msg[0] in goods_name_lst: @@ -42,27 +42,30 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State): goods = goods_lst[i] break else: - await buy.finish('请输入正确的商品名称!') + await buy.finish("请输入正确的商品名称!") else: - await buy.finish('请输入正确的商品名称!', at_sender=True) + await buy.finish("请输入正确的商品名称!", at_sender=True) async with db.transaction(): - if (await BagUser.get_gold(event.user_id, event.group_id)) < goods.goods_price * num: - await buy.finish('您的金币好像不太够哦', at_sender=True) - if await BagUser.spend_gold(event.user_id, event.group_id, goods.goods_price * num): + if ( + await BagUser.get_gold(event.user_id, event.group_id) + ) < goods.goods_price * num: + await buy.finish("您的金币好像不太够哦", at_sender=True) + if await BagUser.spend_gold( + event.user_id, event.group_id, goods.goods_price * num + ): for _ in range(num): await BagUser.add_props(event.user_id, event.group_id, goods.goods_name) - await buy.send(f'花费 {goods.goods_price*num} 金币购买 {goods.goods_name} ×{num} 成功!', at_sender=True) - logger.info(f'USER {event.user_id} GROUP {event.group_id} ' - f'花费 {goods.goods_price*num} 金币购买 {goods.goods_name} ×{num} 成功!') + await buy.send( + f"花费 {goods.goods_price*num} 金币购买 {goods.goods_name} ×{num} 成功!", + at_sender=True, + ) + logger.info( + f"USER {event.user_id} GROUP {event.group_id} " + f"花费 {goods.goods_price*num} 金币购买 {goods.goods_name} ×{num} 成功!" + ) else: - await buy.send(f'{goods.goods_name} 购买失败!', at_sender=True) - logger.info(f'USER {event.user_id} GROUP {event.group_id} ' - f'花费 {goods.goods_price*num} 金币购买 {goods.goods_name} ×{num} 失败!') - - - - - - - - + await buy.send(f"{goods.goods_name} 购买失败!", at_sender=True) + logger.info( + f"USER {event.user_id} GROUP {event.group_id} " + f"花费 {goods.goods_price*num} 金币购买 {goods.goods_name} ×{num} 失败!" + ) diff --git a/plugins/shop/gold.py b/plugins/shop/gold.py index 5a2ced97..6a981fc4 100644 --- a/plugins/shop/gold.py +++ b/plugins/shop/gold.py @@ -8,7 +8,7 @@ from models.bag_user import BagUser my_gold = on_command("我的金币", priority=5, block=True, permission=GROUP) -gold_rank = on_command('金币排行', priority=5, block=True, permission=GROUP) +gold_rank = on_command("金币排行", priority=5, block=True, permission=GROUP) @my_gold.handle() @@ -18,11 +18,9 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State): @gold_rank.handle() async def _(bot: Bot, event: GroupMessageEvent, state: T_State): - users = await BagUser.get_user_all(event.group_id) - all_user_data = [user.gold for user in users] - await gold_rank.finish(await init_rank(users, all_user_data, event.group_id)) - - - - - + all_users = await BagUser.get_all_users(event.group_id) + all_user_id = [user.qq for user in all_users] + all_user_data = [user.gold for user in all_users] + await gold_rank.finish( + "金币排行:\n" + await init_rank(all_user_id, all_user_data, event.group_id) + ) diff --git a/plugins/shop/gold_redbag/__init__.py b/plugins/shop/gold_redbag/__init__.py index 9d07e6e4..4a6e1c78 100644 --- a/plugins/shop/gold_redbag/__init__.py +++ b/plugins/shop/gold_redbag/__init__.py @@ -1,12 +1,23 @@ from nonebot import on_command, on_notice -from nonebot.adapters.cqhttp import Bot, GroupMessageEvent, PokeNotifyEvent, MessageEvent +from nonebot.adapters.cqhttp import ( + Bot, + GroupMessageEvent, + PokeNotifyEvent, + MessageEvent, +) from nonebot.typing import T_State -from .data_source import check_gold, generate_send_redbag_pic, open_redbag, generate_open_redbag_pic, return_gold +from .data_source import ( + check_gold, + generate_send_redbag_pic, + open_redbag, + generate_open_redbag_pic, + return_gold, +) from nonebot.adapters.cqhttp.permission import GROUP from nonebot.message import run_preprocessor, IgnoredException from nonebot.matcher import Matcher from utils.utils import get_message_text, is_number, scheduler -from utils.init_result import image +from utils.message_builder import image from services.log import logger from nonebot.permission import SUPERUSER from nonebot.rule import to_me @@ -15,24 +26,30 @@ import random import time -__plugin_name__ = '金币红包' +__plugin_name__ = "金币红包" -__plugin_usage__ = '金币红包帮助:\n' \ - '\t塞红包 [金币数] [红包个数](默认5)\n' \ - '示例:\n' \ - '\t塞红包 500 5\n' \ - '抢红包 --> 戳一戳,开\n' \ - '退还剩余红包 --> 退还' +__plugin_usage__ = ( + "金币红包帮助:\n" + "\t塞红包 [金币数] [红包个数](默认5)\n" + "示例:\n" + "\t塞红包 500 5\n" + "抢红包 --> 戳一戳,开\n" + "退还剩余红包 --> 退还" +) -gold_redbag = on_command('塞红包', aliases={'金币红包'}, priority=5, block=True, permission=GROUP) +gold_redbag = on_command( + "塞红包", aliases={"金币红包"}, priority=5, block=True, permission=GROUP +) -open_ = on_command('开', aliases={'抢'}, priority=5, block=True, permission=GROUP) +open_ = on_command("开", aliases={"抢"}, priority=5, block=True, permission=GROUP) poke_ = on_notice(priority=6, block=False) -return_ = on_command('退回', aliases={'退还'}, priority=5, block=True, permission=GROUP) +return_ = on_command("退回", aliases={"退还"}, priority=5, block=True, permission=GROUP) -festive_redbag = on_command('节日红包', priority=5, block=True, permission=SUPERUSER, rule=to_me()) +festive_redbag = on_command( + "节日红包", priority=5, block=True, permission=SUPERUSER, rule=to_me() +) redbag_data = {} @@ -43,27 +60,30 @@ festive_redbag_data = {} @run_preprocessor async def _(matcher: Matcher, bot: Bot, event: PokeNotifyEvent, state: T_State): try: - if matcher.type == 'notice' and event.self_id == event.target_id: + if matcher.type == "notice" and event.self_id == event.target_id: flag1 = True flag2 = True try: - if festive_redbag_data[event.group_id]['user_id']: - if event.user_id in festive_redbag_data[event.group_id]['open_user']: + if festive_redbag_data[event.group_id]["user_id"]: + if ( + event.user_id + in festive_redbag_data[event.group_id]["open_user"] + ): flag1 = False except KeyError: flag1 = False try: - if redbag_data[event.group_id]['user_id']: - if event.user_id in redbag_data[event.group_id]['open_user']: + if redbag_data[event.group_id]["user_id"]: + if event.user_id in redbag_data[event.group_id]["open_user"]: flag2 = False except KeyError: flag2 = False if flag1 or flag2: - if matcher.module == 'poke': - raise IgnoredException('目前正在抢红包...') + if matcher.module == "poke": + raise IgnoredException("目前正在抢红包...") else: - if matcher.module == 'gold_redbag': - raise IgnoredException('目前没有红包...') + if matcher.module == "gold_redbag": + raise IgnoredException("目前没有红包...") except AttributeError: pass @@ -72,18 +92,25 @@ async def _(matcher: Matcher, bot: Bot, event: PokeNotifyEvent, state: T_State): async def _(bot: Bot, event: GroupMessageEvent, state: T_State): global redbag_data, festive_redbag_data try: - if time.time() - redbag_data[event.group_id]['time'] > 60: - amount = redbag_data[event.group_id]['amount'] - redbag_data[event.group_id]['open_amount'] - await gold_redbag.send(f'{redbag_data[event.group_id]["nickname"]}的红包过时未开完,退还{amount}金币...') + if time.time() - redbag_data[event.group_id]["time"] > 60: + amount = ( + redbag_data[event.group_id]["amount"] + - redbag_data[event.group_id]["open_amount"] + ) + await gold_redbag.send( + f'{redbag_data[event.group_id]["nickname"]}的红包过时未开完,退还{amount}金币...' + ) redbag_data[event.group_id] = {} else: - await gold_redbag.finish(f'目前 {redbag_data[event.group_id]["nickname"]} 的红包还没有开完噢,' - f'还剩下 {len(redbag_data[event.group_id]["redbag"])} 个红包!' - f'(或等待{str(60 - time.time() + redbag_data[event.group_id]["time"])[:2]}秒红包过时)') + await gold_redbag.finish( + f'目前 {redbag_data[event.group_id]["nickname"]} 的红包还没有开完噢,' + f'还剩下 {len(redbag_data[event.group_id]["redbag"])} 个红包!' + f'(或等待{str(60 - time.time() + redbag_data[event.group_id]["time"])[:2]}秒红包过时)' + ) except KeyError: pass msg = get_message_text(event.json()) - msg = msg.split(' ') + msg = msg.split(" ") if len(msg) == 1: flag, amount = await check_gold(event.user_id, event.group_id, msg[0]) if not flag: @@ -96,52 +123,72 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State): if not flag: await gold_redbag.finish(amount, at_sender=True) if not is_number(num): - await gold_redbag.finish('红包个数给我输正确啊!', at_sender=True) + await gold_redbag.finish("红包个数给我输正确啊!", at_sender=True) num = int(num) nickname = event.sender.card if event.sender.card else event.sender.nickname - flag, result = init_redbag(event.user_id, event.group_id, nickname, amount, num, int(bot.self_id)) + flag, result = init_redbag( + event.user_id, event.group_id, nickname, amount, num, int(bot.self_id) + ) if not flag: await gold_redbag.finish(result, at_sender=True) else: - await gold_redbag.send(f'{nickname}发起了金币红包\n金额:{amount}\n数量:{num}\n' + - image(b64=await generate_send_redbag_pic(redbag_data[event.group_id]['user_id']))) - logger.info(f"USER {event.user_id} GROUP {event.group_id} 塞入 {num} 个红包,共 {amount} 金币") + await gold_redbag.send( + f"{nickname}发起了金币红包\n金额:{amount}\n数量:{num}\n" + + image( + b64=await generate_send_redbag_pic( + redbag_data[event.group_id]["user_id"] + ) + ) + ) + logger.info( + f"USER {event.user_id} GROUP {event.group_id} 塞入 {num} 个红包,共 {amount} 金币" + ) @open_.handle() async def _(bot: Bot, event: GroupMessageEvent, state: T_State): global redbag_data, festive_redbag_data msg = get_message_text(event.json()) - msg = msg.replace('!', '').replace('!', '').replace(',', '').replace(',', '').replace('.', '').replace('。', '') + msg = ( + msg.replace("!", "") + .replace("!", "") + .replace(",", "") + .replace(",", "") + .replace(".", "") + .replace("。", "") + ) if msg: - if msg.find('红包') == -1: + if msg.find("红包") == -1: return flag1 = True flag2 = True open_flag1 = True open_flag2 = True try: - if festive_redbag_data[event.group_id]['user_id']: - if event.user_id in festive_redbag_data[event.group_id]['open_user']: + if festive_redbag_data[event.group_id]["user_id"]: + if event.user_id in festive_redbag_data[event.group_id]["open_user"]: open_flag1 = False except KeyError: open_flag1 = False flag1 = False try: - if redbag_data[event.group_id]['user_id']: - if event.user_id in redbag_data[event.group_id]['open_user']: + if redbag_data[event.group_id]["user_id"]: + if event.user_id in redbag_data[event.group_id]["open_user"]: open_flag2 = False except KeyError: flag2 = False if not flag1 and not flag2: - await open_.finish('目前没有红包可以开...', at_sender=True) + await open_.finish("目前没有红包可以开...", at_sender=True) if open_flag1 or open_flag2: try: - await open_.send(image(b64=await get_redbag_img(event.user_id, event.group_id)), at_sender=True) + await open_.send( + image(b64=await get_redbag_img(event.user_id, event.group_id)), + at_sender=True, + ) except KeyError: - await open_.finish('真贪心,明明已经开过这个红包了的说...', at_sender=True) + await open_.finish("真贪心,明明已经开过这个红包了的说...", at_sender=True) else: - await open_.finish('真贪心,明明已经开过这个红包了的说...', at_sender=True) + await open_.finish("真贪心,明明已经开过这个红包了的说...", at_sender=True) @poke_.handle() @@ -151,39 +198,54 @@ async def _poke_(bot: Bot, event: PokeNotifyEvent, state: T_State): flag1 = True flag2 = True try: - if event.user_id in festive_redbag_data[event.group_id]['open_user']: + if event.user_id in festive_redbag_data[event.group_id]["open_user"]: flag1 = False except KeyError: flag1 = False try: - if event.user_id in redbag_data[event.group_id]['open_user']: + if event.user_id in redbag_data[event.group_id]["open_user"]: flag2 = False except KeyError: flag2 = False if not flag1 and not flag2: return - await poke_.send(image(b64=await get_redbag_img(event.user_id, event.group_id)), at_sender=True) + await poke_.send( + image(b64=await get_redbag_img(event.user_id, event.group_id)), + at_sender=True, + ) @return_.handle() async def _(bot: Bot, event: GroupMessageEvent, state: T_State): global redbag_data try: - if redbag_data[event.group_id]['user_id'] != event.user_id: - await return_.finish('不是你的红包你退回什么!', at_sender=True) - if time.time() - redbag_data[event.group_id]['time'] <= 60: - await return_.finish(f'你的红包还没有过时,在 {str(60 - time.time() + redbag_data[event.group_id]["time"])[:2]} ' - f'秒后可以退回..', at_sender=True) - await return_gold(event.user_id, event.group_id, - redbag_data[event.group_id]['amount'] - redbag_data[event.group_id]['open_amount']) - await return_.send(f"已成功退还了 " - f"{redbag_data[event.group_id]['amount'] - redbag_data[event.group_id]['open_amount']} " - f"金币", at_sender=True) - logger.info(f"USER {event.user_id} GROUP {event.group_id} 退回了" - f"红包 {redbag_data[event.group_id]['amount'] - redbag_data[event.group_id]['open_amount']} 金币") + if redbag_data[event.group_id]["user_id"] != event.user_id: + await return_.finish("不是你的红包你退回什么!", at_sender=True) + if time.time() - redbag_data[event.group_id]["time"] <= 60: + await return_.finish( + f'你的红包还没有过时,在 {str(60 - time.time() + redbag_data[event.group_id]["time"])[:2]} ' + f"秒后可以退回..", + at_sender=True, + ) + await return_gold( + event.user_id, + event.group_id, + redbag_data[event.group_id]["amount"] + - redbag_data[event.group_id]["open_amount"], + ) + await return_.send( + f"已成功退还了 " + f"{redbag_data[event.group_id]['amount'] - redbag_data[event.group_id]['open_amount']} " + f"金币", + at_sender=True, + ) + logger.info( + f"USER {event.user_id} GROUP {event.group_id} 退回了" + f"红包 {redbag_data[event.group_id]['amount'] - redbag_data[event.group_id]['open_amount']} 金币" + ) redbag_data[event.group_id] = {} except KeyError: - await return_.finish('目前没有红包可以退回...', at_sender=True) + await return_.finish("目前没有红包可以退回...", at_sender=True) @festive_redbag.handle() @@ -191,66 +253,83 @@ async def _(bot: Bot, event: MessageEvent, state: T_State): global redbag_data msg = get_message_text(event.json()) if msg: - msg = msg.split(' ') + msg = msg.split() amount = 0 num = 0 - greetings = '恭喜发财 大吉大利' + greetings = "恭喜发财 大吉大利" gl = [] if (lens := len(msg)) < 2: - await festive_redbag.finish('参数不足,格式:节日红包 [金额] [数量] [祝福语](可省) [指定群](可省)') + await festive_redbag.finish("参数不足,格式:节日红包 [金额] [数量] [祝福语](可省) [指定群](可省)") if lens > 1: if not is_number(msg[0]): - await festive_redbag.finish('金额必须要是数字!', at_sender=True) + await festive_redbag.finish("金额必须要是数字!", at_sender=True) amount = int(msg[0]) if not is_number(msg[1]): - await festive_redbag.finish('数量必须要是数字!', at_sender=True) + await festive_redbag.finish("数量必须要是数字!", at_sender=True) num = int(msg[1]) if lens > 2: greetings = msg[2] if lens > 3: for i in range(3, lens): if not is_number(msg[i]): - await festive_redbag.finish('指定的群号必须要是数字啊!', at_sender=True) + await festive_redbag.finish("指定的群号必须要是数字啊!", at_sender=True) gl.append(int(msg[i])) if not gl: gl = await bot.get_group_list(self_id=int(bot.self_id)) - gl = [g['group_id'] for g in gl] + gl = [g["group_id"] for g in gl] for g in gl: - init_redbag(int(bot.self_id), g, '可爱的小真寻', amount, num, int(bot.self_id), 1) - scheduler.add_job(end_festive_redbag, "date", - run_date=str(datetime.now() + timedelta(hours=24)).split('.')[0], - id=f'festive_redbag_{g}', args=[bot, g]) + init_redbag(int(bot.self_id), g, "可爱的小真寻", amount, num, int(bot.self_id), 1) + scheduler.add_job( + end_festive_redbag, + "date", + run_date=str(datetime.now() + timedelta(hours=24)).split(".")[0], + id=f"festive_redbag_{g}", + args=[bot, g], + ) try: - await bot.send_group_msg(group_id=g, message=f'可爱的小真寻发起了金币红包\n金额:{amount}\n数量:{num}\n' + image( - b64=await generate_send_redbag_pic(int(bot.self_id), greetings))) + await bot.send_group_msg( + group_id=g, + message=f"可爱的小真寻发起了金币红包\n金额:{amount}\n数量:{num}\n" + + image( + b64=await generate_send_redbag_pic(int(bot.self_id), greetings) + ), + ) except AttributeError: pass # 红包数据初始化 -def init_redbag(user_id: int, group_id: int, nickname: str, amount: int, num: int, bot_self_id: int, mode: int = 0): +def init_redbag( + user_id: int, + group_id: int, + nickname: str, + amount: int, + num: int, + bot_self_id: int, + mode: int = 0, +): global redbag_data, festive_redbag_data data = redbag_data if mode == 0 else festive_redbag_data if not data.get(group_id): data[group_id] = {} try: - if data[group_id]['user_id'] and user_id != bot_self_id: + if data[group_id]["user_id"] and user_id != bot_self_id: return False, f'{data[group_id]["nickname"]}的红包还没抢完呢...' except KeyError: pass - data[group_id]['user_id'] = user_id - data[group_id]['nickname'] = nickname - data[group_id]['amount'] = amount - data[group_id]['num'] = num - data[group_id]['open_amount'] = 0 - data[group_id]['time'] = time.time() - data[group_id]['redbag'] = random_redbag(amount, num) - data[group_id]['open_user'] = [] + data[group_id]["user_id"] = user_id + data[group_id]["nickname"] = nickname + data[group_id]["amount"] = amount + data[group_id]["num"] = num + data[group_id]["open_amount"] = 0 + data[group_id]["time"] = time.time() + data[group_id]["redbag"] = random_redbag(amount, num) + data[group_id]["open_user"] = [] if mode == 0: redbag_data = data else: festive_redbag_data = data - return True, '' + return True, "" # 随机红包排列 @@ -271,19 +350,24 @@ async def get_redbag_img(user_id: int, group_id: int): mode = 0 if festive_redbag_data.get(group_id): try: - if user_id not in festive_redbag_data[group_id]['open_user']: + if user_id not in festive_redbag_data[group_id]["open_user"]: data = festive_redbag_data mode = 1 except KeyError: pass amount, data = await open_redbag(user_id, group_id, data) - text = f'已领取' \ - f'{data[group_id]["num"] - len(data[group_id]["redbag"])}' \ - f'/{data[group_id]["num"]}个,' \ - f'共{data[group_id]["open_amount"]}/{data[group_id]["amount"]}金币' - logger.info(f"USER {user_id} GROUP {group_id} 抢到了 {data[group_id]['user_id']} 的红包,获取了{amount}金币") - b64 = await generate_open_redbag_pic(data[group_id]['user_id'], - data[group_id]['nickname'], amount, text) + text = ( + f"已领取" + f'{data[group_id]["num"] - len(data[group_id]["redbag"])}' + f'/{data[group_id]["num"]}个,' + f'共{data[group_id]["open_amount"]}/{data[group_id]["amount"]}金币' + ) + logger.info( + f"USER {user_id} GROUP {group_id} 抢到了 {data[group_id]['user_id']} 的红包,获取了{amount}金币" + ) + b64 = await generate_open_redbag_pic( + data[group_id]["user_id"], data[group_id]["nickname"], amount, text + ) if data[group_id]["open_amount"] == data[group_id]["amount"]: data[group_id] = {} if mode == 0: @@ -295,10 +379,10 @@ async def get_redbag_img(user_id: int, group_id: int): async def end_festive_redbag(bot: Bot, group_id: int): global festive_redbag_data - message = f"真寻的节日红包过时了,一共开启了 " \ - f"{festive_redbag_data[group_id]['num'] - len(festive_redbag_data[group_id]['redbag'])}" \ - f" 个红包,共 {festive_redbag_data[group_id]['open_amount']} 金币" + message = ( + f"真寻的节日红包过时了,一共开启了 " + f"{festive_redbag_data[group_id]['num'] - len(festive_redbag_data[group_id]['redbag'])}" + f" 个红包,共 {festive_redbag_data[group_id]['open_amount']} 金币" + ) await bot.send_group_msg(group_id=group_id, message=message) festive_redbag_data[group_id] = {} - - diff --git a/plugins/shop/gold_redbag/data_source.py b/plugins/shop/gold_redbag/data_source.py index 45d26afe..fb14c094 100644 --- a/plugins/shop/gold_redbag/data_source.py +++ b/plugins/shop/gold_redbag/data_source.py @@ -1,6 +1,6 @@ from models.bag_user import BagUser from utils.utils import is_number, get_local_proxy -from utils.img_utils import CreateImg +from utils.image_utils import CreateImg from utils.user_agent import get_user_agent from configs.path_config import IMAGE_PATH from models.redbag_user import RedbagUser diff --git a/plugins/shop/my_props.py b/plugins/shop/my_props.py index c2020769..138ff7e3 100644 --- a/plugins/shop/my_props.py +++ b/plugins/shop/my_props.py @@ -6,7 +6,7 @@ from models.bag_user import BagUser from nonebot.adapters.cqhttp.permission import GROUP -__plugin_name__ = '商店基础显示' +__plugin_name__ = "商店基础显示" my_props = on_command("我的道具", priority=5, block=True, permission=GROUP) @@ -18,29 +18,18 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State): if props: pname_list = [] pnum_list = [] - rst = '' - props = props[:-1].split(',') + rst = "" + props = props[:-1].split(",") for p in props: - if p != '': + if p != "": if p in pname_list: pnum_list[pname_list.index(p)] += 1 else: pname_list.append(p) pnum_list.append(1) for i in range(len(pname_list)): - rst += f'{i+1}.{pname_list[i]}\t×{pnum_list[i]}\n' - await my_props.send('\n' + rst[:-1], at_sender=True) - logger.info(f'USER {event.user_id} GROUP {event.group_id} 查看我的道具') + rst += f"{i+1}.{pname_list[i]}\t×{pnum_list[i]}\n" + await my_props.send("\n" + rst[:-1], at_sender=True) + logger.info(f"USER {event.user_id} GROUP {event.group_id} 查看我的道具") else: - await my_props.finish('您的背包里没有任何的道具噢~', at_sender=True) - - - - - - - - - - - + await my_props.finish("您的背包里没有任何的道具噢~", at_sender=True) diff --git a/plugins/shop/reset_today_gold.py b/plugins/shop/reset_today_gold.py index 589d6672..14e77e1a 100644 --- a/plugins/shop/reset_today_gold.py +++ b/plugins/shop/reset_today_gold.py @@ -5,36 +5,17 @@ from services.log import logger # 重置每日金币 @scheduler.scheduled_job( - 'cron', - # year=None, - # month=None, - # day=None, - # week=None, - # day_of_week="mon,tue,wed,thu,fri", + "cron", hour=0, minute=1, - # second=None, - # start_date=None, - # end_date=None, - # timezone=None, ) async def _(): try: - user_list = await BagUser.get_user_all() + user_list = await BagUser.get_all_users() for user in user_list: await user.update( get_today_gold=0, spend_today_gold=0, ).apply() except Exception as e: - logger.error(f'重置每日金币错误 e:{e}') - - - - - - - - - - + logger.error(f"重置每日金币错误 e:{e}") diff --git a/plugins/shop/shop_handle/__init__.py b/plugins/shop/shop_handle/__init__.py index 781e3722..abe73ebd 100644 --- a/plugins/shop/shop_handle/__init__.py +++ b/plugins/shop/shop_handle/__init__.py @@ -2,7 +2,7 @@ from nonebot import on_command from nonebot.adapters.cqhttp import Bot, GroupMessageEvent, MessageEvent from nonebot.typing import T_State from configs.path_config import IMAGE_PATH -from utils.init_result import image +from utils.message_builder import image from .data_source import create_shop_help, add_goods, del_goods, update_goods from nonebot.permission import SUPERUSER from utils.utils import get_message_text, is_number @@ -11,73 +11,72 @@ import os import time -__plugin_name__ = '商店' +__plugin_name__ = "商店" shop_help = on_command("商店", priority=5, block=True) -shop_add_goods = on_command('添加商品', priority=5, permission=SUPERUSER, block=True) +shop_add_goods = on_command("添加商品", priority=5, permission=SUPERUSER, block=True) -shop_del_goods = on_command('删除商品', priority=5, permission=SUPERUSER, block=True) +shop_del_goods = on_command("删除商品", priority=5, permission=SUPERUSER, block=True) -shop_update_goods = on_command('修改商品', priority=5, permission=SUPERUSER, block=True) +shop_update_goods = on_command("修改商品", priority=5, permission=SUPERUSER, block=True) @shop_help.handle() async def _(bot: Bot, event: GroupMessageEvent, state: T_State): - if not os.path.exists(f'{IMAGE_PATH}/shop_help.png'): + if not os.path.exists(f"{IMAGE_PATH}/shop_help.png"): await create_shop_help() - await shop_help.send(image('shop_help.png')) + await shop_help.send(image("shop_help.png")) @shop_add_goods.handle() async def _(bot: Bot, event: MessageEvent, state: T_State): msg = get_message_text(event.json()) if msg: - msg = msg.split('-') + msg = msg.split("-") if len(msg) < 3: - await shop_add_goods.finish('商品参数不完全...', at_sender=True) + await shop_add_goods.finish("商品参数不完全...", at_sender=True) if not is_number(msg[1]): - await shop_add_goods.finish('商品的价格必须是合法数字!', at_sender=True) + await shop_add_goods.finish("商品的价格必须是合法数字!", at_sender=True) msg[1] = int(msg[1]) if len(msg) > 3: if not is_number(msg[3]): - await shop_add_goods.finish('商品的折扣要小数啊!', at_sender=True) + await shop_add_goods.finish("商品的折扣要小数啊!", at_sender=True) msg[3] = float(msg[3]) if len(msg) > 4: if not is_number(msg[4]): - await shop_add_goods.finish('商品的限时时间是要写多少分钟噢!', at_sender=True) + await shop_add_goods.finish("商品的限时时间是要写多少分钟噢!", at_sender=True) msg[4] = time.time() + int(msg[4]) * 60 if await add_goods(msg): - await shop_add_goods.send(f'添加商品 {msg[0]} 成功...', at_sender=True) - if os.path.exists(f'{IMAGE_PATH}/shop_help.png'): - os.remove(f'{IMAGE_PATH}/shop_help.png') - logger.info(f'USER {event.user_id} 上传商品 {msg} 成功') + await shop_add_goods.send(f"添加商品 {msg[0]} 成功...", at_sender=True) + if os.path.exists(f"{IMAGE_PATH}/shop_help.png"): + os.remove(f"{IMAGE_PATH}/shop_help.png") + logger.info(f"USER {event.user_id} 上传商品 {msg} 成功") else: - await shop_add_goods.send(f'添加商品 {msg[0]} 失败了...', at_sender=True) - logger.warning(f'USER {event.user_id} 上传商品 {msg} 失败') + await shop_add_goods.send(f"添加商品 {msg[0]} 失败了...", at_sender=True) + logger.warning(f"USER {event.user_id} 上传商品 {msg} 失败") @shop_del_goods.handle() async def _(bot: Bot, event: MessageEvent, state: T_State): msg = get_message_text(event.json()) if msg: - name = '' + name = "" id_ = 0 if is_number(msg): id_ = int(msg) else: name = msg rst, goods_name, code = await del_goods(name, id_) - await shop_del_goods.send(rst, at_sender=True) if code == 200: - await shop_del_goods.send(f'删除商品 {goods_name} 成功了...', at_sender=True) - if os.path.exists(f'{IMAGE_PATH}/shop_help.png'): - os.remove(f'{IMAGE_PATH}/shop_help.png') - logger.info(f'USER {event.user_id} 删除商品 {goods_name} 成功') + await shop_del_goods.send(f"删除商品 {goods_name} 成功了...", at_sender=True) + if os.path.exists(f"{IMAGE_PATH}/shop_help.png"): + os.remove(f"{IMAGE_PATH}/shop_help.png") + logger.info(f"USER {event.user_id} 删除商品 {goods_name} 成功") else: - await shop_del_goods.send(f'删除商品 {goods_name} 失败了...', at_sender=True) - logger.info(f'USER {event.user_id} 删除商品 {goods_name} 失败') + await shop_del_goods.send(f"删除商品 {goods_name} 失败了...", at_sender=True) + logger.info(f"USER {event.user_id} 删除商品 {goods_name} 失败") @shop_update_goods.handle() @@ -85,41 +84,37 @@ async def _(bot: Bot, event: MessageEvent, state: T_State): msg = get_message_text(event.json()) if msg: tmp = {} - msg = msg.split('-') + msg = msg.split("-") for x in msg: - if x.find('name') != -1: - tmp['name'] = x.split(' ')[1].strip() - elif x.find('price') != -1: - tmp['price'] = x.split(' ')[1].strip() - if not is_number(tmp['price']): - await shop_update_goods.finish('价格必须是数字啊!', at_sender=True) - tmp['price'] = int(tmp['price']) - elif x.find('des') != -1: - tmp['des'] = x.split(' ')[1].strip() - elif x.find('discount') != -1: - tmp['discount'] = x.split(' ')[1].strip() - if not is_number(tmp['discount']): - await shop_update_goods.finish('折扣必须是数字啊!', at_sender=True) - tmp['discount'] = float(tmp['discount']) - elif x.find('time') != -1: - tmp['time'] = x.split(' ')[1].strip() - if not is_number(tmp['time']): - await shop_update_goods.finish('限时时间必须是数字啊!', at_sender=True) - tmp['time'] = time.time() + tmp['time'] * 60 - if not tmp.get('name'): - await shop_update_goods.finish('未指定商品名称(序号也可)!', at_sender=True) - if is_number(tmp['name']): - tmp['name'] = int(tmp['name']) + if x.find("name") != -1: + tmp["name"] = x.split(" ")[1].strip() + elif x.find("price") != -1: + tmp["price"] = x.split(" ")[1].strip() + if not is_number(tmp["price"]): + await shop_update_goods.finish("价格必须是数字啊!", at_sender=True) + tmp["price"] = int(tmp["price"]) + elif x.find("des") != -1: + tmp["des"] = x.split(" ")[1].strip() + elif x.find("discount") != -1: + tmp["discount"] = x.split(" ")[1].strip() + if not is_number(tmp["discount"]): + await shop_update_goods.finish("折扣必须是数字啊!", at_sender=True) + tmp["discount"] = float(tmp["discount"]) + elif x.find("time") != -1: + tmp["time"] = x.split(" ")[1].strip() + if not is_number(tmp["time"]): + await shop_update_goods.finish("限时时间必须是数字啊!", at_sender=True) + tmp["time"] = time.time() + tmp["time"] * 60 + if not tmp.get("name"): + await shop_update_goods.finish("未指定商品名称(序号也可)!", at_sender=True) + if is_number(tmp["name"]): + tmp["name"] = int(tmp["name"]) flag, name, text = await update_goods(tmp) if flag: - await shop_update_goods.send(f'修改商品 {name} 成功了...\n{text}', at_sender=True) - if os.path.exists(f'{IMAGE_PATH}/shop_help.png'): - os.remove(f'{IMAGE_PATH}/shop_help.png') - logger.info(f'USER {event.user_id} 修改商品 {name} 数据 {tmp} 成功') + await shop_update_goods.send(f"修改商品 {name} 成功了...\n{text}", at_sender=True) + if os.path.exists(f"{IMAGE_PATH}/shop_help.png"): + os.remove(f"{IMAGE_PATH}/shop_help.png") + logger.info(f"USER {event.user_id} 修改商品 {name} 数据 {tmp} 成功") else: - await shop_update_goods.send(f'修改商品 {name} 失败了...', at_sender=True) - logger.info(f'USER {event.user_id} 修改商品 {name} 数据 {tmp} 失败') - - - - + await shop_update_goods.send(f"修改商品 {name} 失败了...", at_sender=True) + logger.info(f"USER {event.user_id} 修改商品 {name} 数据 {tmp} 失败") diff --git a/plugins/shop/shop_handle/data_source.py b/plugins/shop/shop_handle/data_source.py index 2a0ff195..2a3a745e 100644 --- a/plugins/shop/shop_handle/data_source.py +++ b/plugins/shop/shop_handle/data_source.py @@ -1,99 +1,123 @@ from models.goods_info import GoodsInfo -from utils.img_utils import CreateImg +from utils.image_utils import CreateImg from utils.utils import is_number from configs.path_config import IMAGE_PATH +from configs.config import IMPORT_DEFAULT_SHOP_GOODS +from nonebot import Driver +import nonebot import time +driver: Driver = nonebot.get_driver() + + +# 导入内置商品 +@driver.on_startup +async def init_default_shop_goods(): + if IMPORT_DEFAULT_SHOP_GOODS: + await add_goods(["好感度双倍加持卡Ⅰ", 30, "下次签到双倍好感度概率 + 10%(谁才是真命天子?)(同类商品将覆盖)"]) + await add_goods(["好感度双倍加持卡Ⅱ", 150, "下次签到双倍好感度概率 + 20%(平平庸庸)(同类商品将覆盖)"]) + await add_goods(["好感度双倍加持卡Ⅲ", 250, "下次签到双倍好感度概率 + 30%(金币才是真命天子!)(同类商品将覆盖)"]) + + +# 创建商店界面 async def create_shop_help(): goods_lst = await GoodsInfo.get_all_goods() - tmp = '' + tmp = "" idx = 1 for goods in goods_lst: - tmp += f'{idx}.{goods.goods_name}\t\t售价:{goods.goods_price}金币\n' \ - f'\t\t{goods.goods_description}\n' + tmp += ( + f"{idx}.{goods.goods_name}\t\t售价:{goods.goods_price}金币\n" + f"\t\t{goods.goods_description}\n" + ) idx += 1 w = 1000 h = 400 + len(goods_lst) * 40 h = 1000 if h < 1000 else h - shop_logo = CreateImg(100, 100, background=f'{IMAGE_PATH}/other/shop_text.png') + shop_logo = CreateImg(100, 100, background=f"{IMAGE_PATH}/other/shop_text.png") shop = CreateImg(w, h, font_size=20) - zhenxun_img = CreateImg(525, 581, background=f'{IMAGE_PATH}/zhenxun/toukan_2.png') + zhenxun_img = CreateImg(525, 581, background=f"{IMAGE_PATH}/zhenxun/toukan_2.png") shop.paste(zhenxun_img, (780, 100)) shop.paste(shop_logo, (450, 30), True) - shop.text((int((1000 - shop.getsize('注【通过 序号 或者 商品名称 购买】')[0]) / 2), 170), '注【通过 序号 或者 商品名称 购买】') + shop.text( + (int((1000 - shop.getsize("注【通过 序号 或者 商品名称 购买】")[0]) / 2), 170), + "注【通过 序号 或者 商品名称 购买】", + ) shop.text((20, 230), tmp[:-1]) - shop.text((20, h - 100), '神秘药水\t\t售价:9999999金币\n\t\t鬼知道会有什么效果~') - shop.save(f'{IMAGE_PATH}/shop_help.png') + shop.text((20, h - 100), "神秘药水\t\t售价:9999999金币\n\t\t鬼知道会有什么效果~") + shop.save(f"{IMAGE_PATH}/shop_help.png") +# 添加商品 async def add_goods(msg: list): data = { - '名称': None, - '价格': None, - '描述': None, - '折扣': 1, - '限时': 0, + "名称": None, + "价格": None, + "描述": None, + "折扣": 1, + "限时": 0, } keys = list(data.keys()) idx = 0 for x in msg: data[keys[idx]] = x idx += 1 - return await GoodsInfo.add_goods(data['名称'], data['价格'], data['描述'], data['折扣'], data['限时']) + return await GoodsInfo.add_goods( + data["名称"], data["价格"], data["描述"], data["折扣"], data["限时"] + ) +# 删除商品 async def del_goods(name: str, id_: int): goods_lst = await GoodsInfo.get_all_goods() if id_: if id_ < 1 or id_ > len(goods_lst): - return '序号错误,没有该序号商品...', '', 999 + return "序号错误,没有该序号商品...", "", 999 goods_name = goods_lst[id_ - 1].goods_name - if await GoodsInfo.del_goods(goods_name): - return f'删除商品 {goods_name} 成功!', goods_name, 200 + if await GoodsInfo.delete_goods(goods_name): + return f"删除商品 {goods_name} 成功!", goods_name, 200 else: - return f'删除商品 {goods_name} 失败!', goods_name, 999 + return f"删除商品 {goods_name} 失败!", goods_name, 999 if name: - if await GoodsInfo.del_goods(name): - return f'删除商品 {name} 成功!', name, 200 + if await GoodsInfo.delete_goods(name): + return f"删除商品 {name} 成功!", name, 200 else: - return f'删除商品 {name} 失败!', name, 999 + return f"删除商品 {name} 失败!", name, 999 +# 更新商品信息 async def update_goods(data: dict): goods_lst = await GoodsInfo.get_all_goods() - if is_number(data['name']): - if data['name'] < 1 or data['name'] > len(goods_lst): - return '序号错误,没有该序号的商品...', '', 999 - goods = goods_lst[data['name'] - 1] + if is_number(data["name"]): + if data["name"] < 1 or data["name"] > len(goods_lst): + return "序号错误,没有该序号的商品...", "", 999 + goods = goods_lst[data["name"] - 1] else: - goods = await GoodsInfo.get_goods_info(data['name']) + goods = await GoodsInfo.get_goods_info(data["name"]) if not goods: - return '名称错误,没有该名称的商品...', '', 999 + return "名称错误,没有该名称的商品...", "", 999 name = goods.goods_name price = goods.goods_price des = goods.goods_description discount = goods.goods_discount limit_time = goods.goods_limit_time - tmp = '' - if data.get('price'): + tmp = "" + if data.get("price"): tmp += f'价格:{price} --> {data["price"]}\n' - price = data['price'] - if data.get('des'): + price = data["price"] + if data.get("des"): tmp += f'描述:{des} --> {data["des"]}\n' - des = data['des'] - if data.get('discount'): + des = data["des"] + if data.get("discount"): tmp += f'折扣:{discount} --> {data["discount"]}\n' - discount = data['discount'] - if data.get('time'): + discount = data["discount"] + if data.get("time"): old_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(limit_time)) - new_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(data['time'])) - tmp += f'折扣:{old_time} --> {new_time}\n' - limit_time = data['time'] - return await GoodsInfo.update_goods(name, price, des, discount, limit_time), name, tmp[:-1] - - - - - - + new_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(data["time"])) + tmp += f"折扣:{old_time} --> {new_time}\n" + limit_time = data["time"] + return ( + await GoodsInfo.update_goods(name, price, des, discount, limit_time), + name, + tmp[:-1], + ) diff --git a/plugins/shop/use/__init__.py b/plugins/shop/use/__init__.py index 69b08398..b31b6469 100644 --- a/plugins/shop/use/__init__.py +++ b/plugins/shop/use/__init__.py @@ -9,8 +9,8 @@ from services.db_context import db from .data_source import effect -__plugin_name__ = '使用道具' -__plugin_usage__ = '输入 “使用道具 xxx(序号 或 道具名称)“ 即可使用道具\n【注】序号以我的道具序号为准,更推荐使用道具名称使用道具(怕出错)' +__plugin_name__ = "使用道具" +__plugin_usage__ = "输入 “使用道具 xxx(序号 或 道具名称)“ 即可使用道具\n【注】序号以我的道具序号为准,更推荐使用道具名称使用道具(怕出错)" use_props = on_command("使用道具", priority=5, block=True, permission=GROUP) @@ -19,43 +19,37 @@ use_props = on_command("使用道具", priority=5, block=True, permission=GROUP) @use_props.handle() async def _(bot: Bot, event: GroupMessageEvent, state: T_State): msg = get_message_text(event.json()) - if msg in ['', '帮助']: + if msg in ["", "帮助"]: await use_props.finish(__plugin_usage__) props = await BagUser.get_props(event.user_id, event.group_id) if props: async with db.transaction(): pname_list = [] - props = props[:-1].split(',') + props = props[:-1].split(",") for p in props: - if p != '': + if p != "": if p not in pname_list: pname_list.append(p) if is_number(msg): if 0 < int(msg) <= len(pname_list): name = pname_list[int(msg) - 1] else: - await use_props.finish('仔细看看自己的道具仓库有没有这个道具?', at_sender=True) + await use_props.finish("仔细看看自己的道具仓库有没有这个道具?", at_sender=True) else: if msg not in pname_list: - await use_props.finish('道具名称错误!', at_sender=True) + await use_props.finish("道具名称错误!", at_sender=True) name = msg - if await BagUser.del_props(event.user_id, event.group_id, name) and\ - await effect(event.user_id, event.group_id, name): - await use_props.send(f'使用道具 {name} 成功!', at_sender=True) - logger.info(f'USER {event.user_id} GROUP {event.group_id} 使用道具 {name} 成功') + if await BagUser.del_props( + event.user_id, event.group_id, name + ) and await effect(event.user_id, event.group_id, name): + await use_props.send(f"使用道具 {name} 成功!", at_sender=True) + logger.info( + f"USER {event.user_id} GROUP {event.group_id} 使用道具 {name} 成功" + ) else: - await use_props.send(f'使用道具 {name} 失败!', at_sender=True) - logger.info(f'USER {event.user_id} GROUP {event.group_id} 使用道具 {name} 失败') + await use_props.send(f"使用道具 {name} 失败!", at_sender=True) + logger.info( + f"USER {event.user_id} GROUP {event.group_id} 使用道具 {name} 失败" + ) else: - await use_props.send('您的背包里没有任何的道具噢~', at_sender=True) - - - - - - - - - - - + await use_props.send("您的背包里没有任何的道具噢~", at_sender=True) diff --git a/plugins/shop/use/data_source.py b/plugins/shop/use/data_source.py index d6d70a29..72095b4b 100644 --- a/plugins/shop/use/data_source.py +++ b/plugins/shop/use/data_source.py @@ -2,39 +2,14 @@ from models.sigin_group_user import SignGroupUser async def effect(user_id: int, group_id: int, name: str) -> bool: - print(name) - # try: - if name == '好感双倍加持卡Ⅰ': + if name == "好感双倍加持卡Ⅰ": user = await SignGroupUser.ensure(user_id, group_id) - await user.update( - add_probability=0.1 - ).apply() - if name == '好感双倍加持卡Ⅱ': + await user.update(add_probability=0.1).apply() + if name == "好感双倍加持卡Ⅱ": user = await SignGroupUser.ensure(user_id, group_id) - await user.update( - add_probability=0.2 - ).apply() - if name == '好感双倍加持卡Ⅲ': + await user.update(add_probability=0.2).apply() + if name == "好感双倍加持卡Ⅲ": user = await SignGroupUser.ensure(user_id, group_id) print(user.user_qq) - await user.update( - add_probability=0.3 - ).apply() + await user.update(add_probability=0.3).apply() return True - # except Exception: - # return False - - - - - - - - - - - - - - - diff --git a/plugins/sign_in/__init__.py b/plugins/sign_in/__init__.py index 07d31557..f4ae9b92 100644 --- a/plugins/sign_in/__init__.py +++ b/plugins/sign_in/__init__.py @@ -1,41 +1,53 @@ -from .group_user_checkin import group_user_check_in, group_user_check, group_impression_rank, impression_rank +from .group_user_checkin import ( + group_user_check_in, + group_user_check, + group_impression_rank, + impression_rank, +) from nonebot.typing import T_State from nonebot.adapters.cqhttp import Bot, GroupMessageEvent from nonebot.adapters.cqhttp.permission import GROUP -from utils.init_result import image +from utils.message_builder import image from nonebot import on_command from utils.utils import get_message_text from pathlib import Path from configs.path_config import DATA_PATH + try: import ujson as json except ModuleNotFoundError: import json -__plugin_name__ = '签到' +__plugin_name__ = "签到" __plugin_usage__ = ( - '用法:\n' - '对我说 “签到” 来签到\n' - '“我的签到” 来获取历史签到信息\n' - '“好感度排行” 来查看当前好感度前十的伙伴\n' - '/ 签到时有 3% 概率 * 2 /' + "用法:\n" + "对我说 “签到” 来签到\n" + "“我的签到” 来获取历史签到信息\n" + "“好感度排行” 来查看当前好感度前十的伙伴\n" + "/ 签到时有 3% 概率 * 2 /" ) -_file = Path(f'{DATA_PATH}/not_show_sign_rank_user.json') +_file = Path(f"{DATA_PATH}/not_show_sign_rank_user.json") try: - data = json.load(open(_file, 'r', encoding='utf8')) + data = json.load(open(_file, "r", encoding="utf8")) except (FileNotFoundError, ValueError, TypeError): - data = { - '0': [] - } + data = {"0": []} sign = on_command("签到", priority=5, permission=GROUP, block=True) -my_sign = on_command(cmd="我的签到", aliases={'好感度'}, priority=5, permission=GROUP, block=True) -sign_rank = on_command(cmd="积分排行", aliases={'好感度排行', '签到排行', '积分排行', '好感排行', - '好感度排名,签到排名,积分排名'}, - priority=5, permission=GROUP, block=True) -total_sign_rank = on_command('签到总排行', aliases={'好感度总排行', '好感度总榜', '签到总榜'}, priority=5, block=True) +my_sign = on_command( + cmd="我的签到", aliases={"好感度"}, priority=5, permission=GROUP, block=True +) +sign_rank = on_command( + cmd="积分排行", + aliases={"好感度排行", "签到排行", "积分排行", "好感排行", "好感度排名,签到排名,积分排名"}, + priority=5, + permission=GROUP, + block=True, +) +total_sign_rank = on_command( + "签到总排行", aliases={"好感度总排行", "好感度总榜", "签到总榜"}, priority=5, block=True +) @sign.handle() @@ -56,31 +68,24 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State): @sign_rank.handle() async def _(bot: Bot, event: GroupMessageEvent, state: T_State): - await sign_rank.send( - await group_impression_rank(event.group_id) - ) + await sign_rank.send(await group_impression_rank(event.group_id)) @total_sign_rank.handle() async def _(bot: Bot, event: GroupMessageEvent, state: T_State): msg = get_message_text(event.json()) if not msg: - await total_sign_rank.send('请稍等..正在整理数据...') + await total_sign_rank.send("请稍等..正在整理数据...") await total_sign_rank.send(image(b64=await impression_rank(0, data))) - elif msg in ['屏蔽我']: - if event.user_id in data['0']: - await total_sign_rank.finish('您已经在屏蔽名单中了,请勿重复添加!', at_sender=True) - data['0'].append(event.user_id) - await total_sign_rank.send('设置成功,您不会出现在签到总榜中!', at_sender=True) - elif msg in ['显示我']: - if event.user_id not in data['0']: - await total_sign_rank.finish('您不在屏蔽名单中!', at_sender=True) - data['0'].remove(event.user_id) - await total_sign_rank.send('设置成功,签到总榜将会显示您的头像名称以及好感度!', at_sender=True) - with open(_file, 'w', encoding='utf8') as f: + elif msg in ["屏蔽我"]: + if event.user_id in data["0"]: + await total_sign_rank.finish("您已经在屏蔽名单中了,请勿重复添加!", at_sender=True) + data["0"].append(event.user_id) + await total_sign_rank.send("设置成功,您不会出现在签到总榜中!", at_sender=True) + elif msg in ["显示我"]: + if event.user_id not in data["0"]: + await total_sign_rank.finish("您不在屏蔽名单中!", at_sender=True) + data["0"].remove(event.user_id) + await total_sign_rank.send("设置成功,签到总榜将会显示您的头像名称以及好感度!", at_sender=True) + with open(_file, "w", encoding="utf8") as f: json.dump(data, f, ensure_ascii=False, indent=4) - - - - - diff --git a/plugins/sign_in/group_user_checkin.py b/plugins/sign_in/group_user_checkin.py index 2ebed46f..90f2a554 100644 --- a/plugins/sign_in/group_user_checkin.py +++ b/plugins/sign_in/group_user_checkin.py @@ -7,7 +7,7 @@ from models.sigin_group_user import SignGroupUser from models.group_member_info import GroupInfoUser from models.bag_user import BagUser from configs.config import MAX_SIGN_GOLD -from utils.img_utils import CreateImg +from utils.image_utils import CreateImg import aiohttp from asyncio.exceptions import TimeoutError import math @@ -15,7 +15,7 @@ import asyncio async def group_user_check_in(user_qq: int, group: int) -> str: - 'Returns string describing the result of checking in' + "Returns string describing the result of checking in" present = datetime.now() async with db.transaction(): # 取得相应用户 @@ -23,11 +23,11 @@ async def group_user_check_in(user_qq: int, group: int) -> str: # 如果同一天签到过,特殊处理 if user.checkin_time_last.date() == present.date(): return _handle_already_checked_in(user) - return await _handle_check_in(user_qq, group, present) # ok + return await _handle_check_in(user_qq, group, present) # ok def _handle_already_checked_in(user: SignGroupUser) -> str: - return f'已经签到过啦~ 好感度:{user.impression:.2f}' + return f"已经签到过啦~ 好感度:{user.impression:.2f}" async def _handle_check_in(user_qq: int, group: int, present: datetime) -> str: @@ -42,10 +42,12 @@ async def _handle_check_in(user_qq: int, group: int, present: datetime) -> str: elif critx2 < specify_probability: impression_added *= 2 new_impression = user.impression + impression_added - message = random.choice(( - '谢谢,你是个好人!', - '对了,来喝杯茶吗?', - )) + message = random.choice( + ( + "谢谢,你是个好人!", + "对了,来喝杯茶吗?", + ) + ) await user.update( checkin_count=user.checkin_count + 1, checkin_time_last=present, @@ -64,30 +66,36 @@ async def _handle_check_in(user_qq: int, group: int, present: datetime) -> str: imgold = MAX_SIGN_GOLD await BagUser.add_gold(user_qq, group, gold + imgold) if critx2 + add_probability > 0.97 or critx2 < specify_probability: - logger.info(f'(USER {user.user_qq}, GROUP {user.belonging_group})' - f' CHECKED IN successfully. score: {new_impression:.2f} (+{impression_added * 2:.2f}).获取金币:{gold+imgold}') - return f'{message} 好感度:{new_impression:.2f} (+{impression_added/2:.2f}×2)!!!\n获取金币:{gold} \n好感度额外获得金币:{imgold}' + logger.info( + f"(USER {user.user_qq}, GROUP {user.belonging_group})" + f" CHECKED IN successfully. score: {new_impression:.2f} (+{impression_added * 2:.2f}).获取金币:{gold+imgold}" + ) + return f"{message} 好感度:{new_impression:.2f} (+{impression_added/2:.2f}×2)!!!\n获取金币:{gold} \n好感度额外获得金币:{imgold}" else: - logger.info(f'(USER {user.user_qq}, GROUP {user.belonging_group})' - f' CHECKED IN successfully. score: {new_impression:.2f} (+{impression_added:.2f}).获取金币:{gold+imgold}') - return f'{message} 好感度:{new_impression:.2f} (+{impression_added:.2f})\n获取金币:{gold} \n好感度额外获得金币:{imgold}' + logger.info( + f"(USER {user.user_qq}, GROUP {user.belonging_group})" + f" CHECKED IN successfully. score: {new_impression:.2f} (+{impression_added:.2f}).获取金币:{gold+imgold}" + ) + return f"{message} 好感度:{new_impression:.2f} (+{impression_added:.2f})\n获取金币:{gold} \n好感度额外获得金币:{imgold}" async def group_user_check(user_qq: int, group: int) -> str: # heuristic: if users find they have never checked in they are probable to check in user = await SignGroupUser.ensure(user_qq, group) gold = await BagUser.get_gold(user_qq, group) - return '好感度:{:.2f}\n金币:{}\n历史签到数:{}\n上次签到日期:{}'.format( + return "好感度:{:.2f}\n金币:{}\n历史签到数:{}\n上次签到日期:{}".format( user.impression, gold, user.checkin_count, - user.checkin_time_last.date() if user.checkin_time_last != datetime.min else '从未', + user.checkin_time_last.date() + if user.checkin_time_last != datetime.min + else "从未", ) async def group_impression_rank(group: int) -> str: result = "\t好感度排行榜\t\n" - user_qq_list, impression_list, _ = await SignGroupUser.query_impression_all(group) + user_qq_list, impression_list, _ = await SignGroupUser.get_all_impression(group) _count = 11 if user_qq_list and impression_list: for i in range(1, len(user_qq_list)): @@ -97,7 +105,9 @@ async def group_impression_rank(group: int) -> str: index = impression_list.index(impression) user_qq = user_qq_list[index] try: - user_name = (await GroupInfoUser.select_member_info(user_qq, group)).user_name + user_name = ( + await GroupInfoUser.get_member_info(user_qq, group) + ).user_name except Exception as e: logger.info(f"USER {user_qq}, GROUP {group} 不在群内") _count += 1 @@ -120,8 +130,11 @@ async def random_gold(user_id, group_id, impression): return 0 +# 签到总榜 async def impression_rank(group_id: int, data: dict): - user_qq_list, impression_list, group_list = await SignGroupUser.query_impression_all(group_id) + user_qq_list, impression_list, group_list = await SignGroupUser.get_all_impression( + group_id + ) users, impressions, groups = [], [], [] num = 0 for i in range(105 if len(user_qq_list) > 105 else len(user_qq_list)): @@ -133,7 +146,7 @@ async def impression_rank(group_id: int, data: dict): impression_list.pop(index) group_list.pop(index) if user not in users and impression < 100000: - if user not in data['0']: + if user not in data["0"]: users.append(user) impressions.append(impression) groups.append(group) @@ -159,10 +172,10 @@ async def _pst(users: list, impressions: list, groups: list): count = math.ceil(lens / 33) width = 10 idx = 0 - A = CreateImg(1740, 3300, color='#FFE4C4') + A = CreateImg(1740, 3300, color="#FFE4C4") async with aiohttp.ClientSession() as session: for _ in range(count): - col_img = CreateImg(550, 3300, 550, 100, color='#FFE4C4') + col_img = CreateImg(550, 3300, 550, 100, color="#FFE4C4") for _ in range(33 if int(lens / 33) >= 1 else lens % 33 - 1): idx += 1 if idx > 100: @@ -175,29 +188,37 @@ async def _pst(users: list, impressions: list, groups: list): users.pop(index) groups.pop(index) try: - user_name = (await GroupInfoUser.select_member_info(user, group)).user_name + user_name = ( + await GroupInfoUser.get_member_info(user, group) + ).user_name except AttributeError: - user_name = f'我名字呢?' - user_name = user_name if len(user_name) < 11 else user_name[:10] + '...' - impression = str(impression)[:4] if len(str(impression)) > 4 else impression + user_name = f"我名字呢?" + user_name = user_name if len(user_name) < 11 else user_name[:10] + "..." + impression = ( + str(impression)[:4] if len(str(impression)) > 4 else impression + ) try: - async with session.get(f'http://q1.qlogo.cn/g?b=qq&nk={user}&s=160', timeout=5) as response: - ava = CreateImg(50, 50, background=BytesIO(await response.read())) + async with session.get( + f"http://q1.qlogo.cn/g?b=qq&nk={user}&s=160", timeout=5 + ) as response: + ava = CreateImg( + 50, 50, background=BytesIO(await response.read()) + ) except TimeoutError: - ava = CreateImg(50, 50, color='white') + ava = CreateImg(50, 50, color="white") ava.circle() - bk = CreateImg(550, 100, color='#FFE4C4', font_size=30) - font_w, font_h = bk.getsize(f'{idx}') - bk.text((5, int((100-font_h)/2)), f'{idx}.') - bk.paste(ava, (55, int((100-50)/2)), True) - bk.text((120, int((100-font_h)/2)), f'{user_name}') - bk.text((460, int((100-font_h)/2)), f'[{impression}]') + bk = CreateImg(550, 100, color="#FFE4C4", font_size=30) + font_w, font_h = bk.getsize(f"{idx}") + bk.text((5, int((100 - font_h) / 2)), f"{idx}.") + bk.paste(ava, (55, int((100 - 50) / 2)), True) + bk.text((120, int((100 - font_h) / 2)), f"{user_name}") + bk.text((460, int((100 - font_h) / 2)), f"[{impression}]") col_img.paste(bk) A.paste(col_img, (width, 0)) lens -= 33 width += 580 - W = CreateImg(1740, 3700, color='#FFE4C4', font_size=130) + W = CreateImg(1740, 3700, color="#FFE4C4", font_size=130) W.paste(A, (0, 260)) - font_w, font_h = W.getsize('真寻的好感度总榜') - W.text((int((1740-font_w)/2), int((260-font_h)/2)), '真寻的好感度总榜') + font_w, font_h = W.getsize("真寻的好感度总榜") + W.text((int((1740 - font_w) / 2), int((260 - font_h) / 2)), "真寻的好感度总榜") return W.pic2bs4() diff --git a/plugins/songpicker2/__init__.py b/plugins/songpicker2/__init__.py index 54e28431..50a8f4ea 100644 --- a/plugins/songpicker2/__init__.py +++ b/plugins/songpicker2/__init__.py @@ -3,9 +3,9 @@ from nonebot.adapters import Bot, Event from nonebot.typing import T_State from nonebot import on_command -__plugin_name__ = '点歌' +__plugin_name__ = "点歌" -__plugin_usage__ = '用法:点歌 [歌名]' +__plugin_usage__ = "用法:点歌 [歌名]" dataget = dataGet() @@ -27,7 +27,7 @@ async def handle_songName(bot: Bot, event: Event, state: T_State): await songpicker.finish("没有找到这首歌!", at_sender=True) for _ in range(3): songInfoDict = await dataget.songInfo(songIdList[0]) - if songInfoDict != '网易云网络繁忙!': + if songInfoDict != "网易云网络繁忙!": break else: await songpicker.finish("网易云繁忙...") @@ -38,14 +38,5 @@ async def handle_songName(bot: Bot, event: Event, state: T_State): async def handle_songNum(bot: Bot, event: Event, state: T_State): songIdList = state["songIdList"] selectedSongId = songIdList[0] - songContent = [ - { - "type": "music", - "data": { - "type": 163, - "id": selectedSongId - } - } - ] + songContent = [{"type": "music", "data": {"type": 163, "id": selectedSongId}}] await songpicker.send(songContent) - diff --git a/plugins/songpicker2/music_163.py b/plugins/songpicker2/music_163.py index ce904e04..65586cdc 100644 --- a/plugins/songpicker2/music_163.py +++ b/plugins/songpicker2/music_163.py @@ -5,30 +5,40 @@ from configs.config import ALAPI_TOKEN from asyncio.exceptions import TimeoutError -class dataApi(): - ''' +class dataApi: + """ 从网易云音乐接口直接获取数据(实验性) - ''' + """ + headers = {"referer": "http://music.163.com"} cookies = {"appver": "2.0.2"} async def search(self, songName: str): - ''' + """ 搜索接口,用于由歌曲名查找id - ''' - async with aiohttp.ClientSession(headers=self.headers, cookies=self.cookies) as session: - async with session.post(f"http://music.163.com/api/search/get/", data={"s": songName, "limit": 5, "type": 1, "offset": 0},) as r: + """ + async with aiohttp.ClientSession( + headers=self.headers, cookies=self.cookies + ) as session: + async with session.post( + f"http://music.163.com/api/search/get/", + data={"s": songName, "limit": 5, "type": 1, "offset": 0}, + ) as r: if r.status != 200: return None r = await r.text() return json.loads(r) async def getSongInfo(self, songId: int): - ''' + """ 获取歌曲信息 - ''' - async with aiohttp.ClientSession(headers=self.headers, cookies=self.cookies) as session: - async with session.post(f"http://music.163.com/api/song/detail/?id={songId}&ids=%5B{songId}%5D",) as r: + """ + async with aiohttp.ClientSession( + headers=self.headers, cookies=self.cookies + ) as session: + async with session.post( + f"http://music.163.com/api/song/detail/?id={songId}&ids=%5B{songId}%5D", + ) as r: if r.status != 200: return None r = await r.text() @@ -36,36 +46,37 @@ class dataApi(): class dataGet(dataApi): - ''' + """ 从dataApi获取数据,并做简单处理 - ''' + """ api = dataApi() async def songIds(self, songName: str, amount=5) -> list: - ''' + """ 根据用户输入的songName 获取候选songId列表 [默认songId数量:5] - ''' + """ songIds = list() r = await self.api.search(songName=songName) if r is None: raise WrongDataError - idRange = amount if amount < len( - r["result"]["songs"]) else len(r["result"]["songs"]) + idRange = ( + amount if amount < len(r["result"]["songs"]) else len(r["result"]["songs"]) + ) for i in range(idRange): songIds.append(r["result"]["songs"][i]["id"]) return songIds async def songInfo(self, songId: int): - ''' + """ 根据传递的songId,获取歌曲名、歌手、专辑等信息,作为dict返回 - ''' + """ songInfo = dict() r = await self.api.getSongInfo(songId) if r is None: raise WrongDataError - if r['code'] == -460: - return '网易云网络繁忙!' + if r["code"] == -460: + return "网易云网络繁忙!" songInfo["songName"] = r["songs"][0]["name"] songArtists = list() @@ -79,17 +90,17 @@ class dataGet(dataApi): return songInfo -class dataProcess(): - ''' +class dataProcess: + """ 将获取的数据处理为用户能看懂的形式 - ''' + """ @staticmethod async def mergeSongInfo(songInfos: list) -> str: - ''' + """ 将歌曲信息list处理为字符串,供用户点歌 传递进的歌曲信息list含有多个歌曲信息dict - ''' + """ songInfoMessage = "请输入欲点播歌曲的序号:\n" numId = 0 for songInfo in songInfos: @@ -105,15 +116,17 @@ class dataProcess(): @staticmethod async def mergeSongComments(songComments: dict) -> str: - songCommentsMessage = '\n'.join( - ['%s: %s' % (key, value) for (key, value) in songComments.items()]) + songCommentsMessage = "\n".join( + ["%s: %s" % (key, value) for (key, value) in songComments.items()] + ) return songCommentsMessage class Error(Exception): - ''' + """ 谁知道网易的接口会出什么幺蛾子 - ''' + """ + pass @@ -125,20 +138,18 @@ class WrongDataError(Error): async def get_music_id(keyword: str): - url = f'https://v2.alapi.cn/api/music/search' - params = { - 'token': ALAPI_TOKEN, - 'keyword': keyword - } + url = f"https://v2.alapi.cn/api/music/search" + params = {"token": ALAPI_TOKEN, "keyword": keyword} async with aiohttp.ClientSession() as session: try: - async with session.get(url, proxy=get_local_proxy(), timeout=2, params=params) as response: + async with session.get( + url, proxy=get_local_proxy(), timeout=2, params=params + ) as response: data = await response.json() - if data['code'] == 200: - data = data['data']['songs'] - return data['id'], 200 + if data["code"] == 200: + data = data["data"]["songs"] + return data["id"], 200 else: return f'访问失败...code:{data["code"]}', 999 except TimeoutError: - return '超时了...', 999 - + return "超时了...", 999 diff --git a/plugins/statistics_handle.py b/plugins/statistics_handle.py index 75859c7a..9acfb2d1 100644 --- a/plugins/statistics_handle.py +++ b/plugins/statistics_handle.py @@ -1,73 +1,109 @@ from nonebot import on_command -from nonebot.adapters.cqhttp import Bot, GroupMessageEvent, GROUP +from nonebot.adapters.cqhttp import Bot, GroupMessageEvent, MessageEvent from models.group_info import GroupInfo from nonebot.typing import T_State from pathlib import Path from configs.path_config import DATA_PATH, TTF_PATH import matplotlib.pyplot as plt from utils.utils import get_message_text -from utils.img_utils import fig2b64 -from utils.init_result import image +from utils.image_utils import fig2b64 +from utils.message_builder import image from configs.config import plugins2info_dict from matplotlib.font_manager import FontProperties -plt.rcParams['font.family'] = ['SimHei', 'FangSong', 'KaiTi'] -plt.rcParams['font.sans-serif'] = ['SimHei', 'FangSong', 'KaiTi'] -plt.rcParams['axes.unicode_minus'] = False +plt.rcParams["font.family"] = ["SimHei", "FangSong", "KaiTi"] +plt.rcParams["font.sans-serif"] = ["SimHei", "FangSong", "KaiTi"] +plt.rcParams["axes.unicode_minus"] = False -font = FontProperties(fname=f'{TTF_PATH}/yz.ttf', size=10) +font = FontProperties(fname=f"{TTF_PATH}/yz.ttf", size=10) try: import ujson as json except ModuleNotFoundError: import json -__plugin_name__ = '功能调用统计' -__plugin_usage__ = '用法: 无' +__plugin_name__ = "功能调用统计" +__plugin_usage__ = "用法: 无" -statistics = on_command("功能调用统计", aliases={'日功能调用统计', '周功能调用统计', '月功能调用统计'}, permission=GROUP, priority=5, block=True) +statistics = on_command( + "功能调用统计", + aliases={ + "日功能调用统计", + "周功能调用统计", + "月功能调用统计", + "我的功能调用统计", + "我的日功能调用统计", + "我的周功能调用统计", + "我的月功能调用统计", + }, + priority=5, + block=True, +) -statistics_file = Path(f'{DATA_PATH}/_prefix_count.json') + +statistics_group_file = Path(f"{DATA_PATH}/_prefix_count.json") +statistics_user_file = Path(f"{DATA_PATH}/_prefix_user_count.json") @statistics.handle() -async def _(bot: Bot, event: GroupMessageEvent, state: T_State): +async def _(bot: Bot, event: MessageEvent, state: T_State): msg = get_message_text(event.json()) - plugin = '' - if not statistics_file.exists(): - await statistics.finish('统计文件不存在...', at_sender=True) - if state["_prefix"]["raw_command"][0] == '日': - arg = 'day_statistics' - elif state["_prefix"]["raw_command"][0] == '周': - arg = 'week_statistics' - elif state["_prefix"]["raw_command"][0] == '月': - arg = 'month_statistics' + if state["_prefix"]["raw_command"][:2] == "我的": + itype = "user" + key = str(event.user_id) + state["_prefix"]["raw_command"] = state["_prefix"]["raw_command"][2:] + if not statistics_user_file.exists(): + await statistics.finish("统计文件不存在...", at_sender=True) else: - arg = 'total_statistics' + if not isinstance(event, GroupMessageEvent): + await statistics.finish("请在群内调用此功能...") + itype = "group" + key = str(event.group_id) + if not statistics_group_file.exists(): + await statistics.finish("统计文件不存在...", at_sender=True) + plugin = "" + if state["_prefix"]["raw_command"][0] == "日": + arg = "day_statistics" + elif state["_prefix"]["raw_command"][0] == "周": + arg = "week_statistics" + elif state["_prefix"]["raw_command"][0] == "月": + arg = "month_statistics" + else: + arg = "total_statistics" if msg: - for key in plugins2info_dict.keys(): - if msg in plugins2info_dict[key]['cmd']: - plugin = plugins2info_dict[key]['cmd'][0] + for x in plugins2info_dict.keys(): + if msg in plugins2info_dict[x]["cmd"]: + plugin = plugins2info_dict[x]["cmd"][0] break else: - if arg not in ['day_statistics', 'total_statistics']: - await statistics.finish('未找到此功能的调用...', at_sender=True) - data: dict = json.load(open(statistics_file, 'r', encoding='utf8')) - if not data[arg].get(str(event.group_id)): - await statistics.finish('该群统计数据不存在...', at_sender=True) - day_index = data['day_index'] - data = data[arg][str(event.group_id)] - group_name = await GroupInfo.get_group_info(event.group_id) - group_name = group_name.group_name if group_name else str(event.group_id) - img = generate_statistics_img(data, arg, group_name, plugin, day_index) + if arg not in ["day_statistics", "total_statistics"]: + await statistics.finish("未找到此功能的调用...", at_sender=True) + if itype == "group": + data: dict = json.load(open(statistics_group_file, "r", encoding="utf8")) + if not data[arg].get(str(event.group_id)): + await statistics.finish("该群统计数据不存在...", at_sender=True) + else: + data: dict = json.load(open(statistics_user_file, "r", encoding="utf8")) + if not data[arg].get(str(event.user_id)): + await statistics.finish("该用户统计数据不存在...", at_sender=True) + day_index = data["day_index"] + data = data[arg][key] + if itype == "group": + name = await GroupInfo.get_group_info(event.group_id) + name = name.group_name if name else str(event.group_id) + else: + name = event.sender.card if event.sender.card else event.sender.nickname + img = generate_statistics_img(data, arg, name, plugin, day_index) await statistics.send(image(b64=img)) plt.cla() -def generate_statistics_img(data: dict, arg: str, group_name: str, plugin: str, day_index: int): - if arg == 'day_statistics': - init_bar_graph(data, f'{group_name} 日功能调用统计') - elif arg == 'week_statistics': +def generate_statistics_img( + data: dict, arg: str, name: str, plugin: str, day_index: int +): + if arg == "day_statistics": + init_bar_graph(data, f"{name} 日功能调用统计") + elif arg == "week_statistics": if plugin: current_week = day_index % 7 week_lst = [] @@ -81,15 +117,21 @@ def generate_statistics_img(data: dict, arg: str, group_name: str, plugin: str, count = [] for i in range(7): if int(week_lst[i]) == 7: - count.append(data[str(0)][plugin]) + try: + count.append(data[str(0)][plugin]) + except KeyError: + count.append(0) else: - count.append(data[str(week_lst[i])][plugin]) - week_lst = ['7' if i == '0' else i for i in week_lst] + try: + count.append(data[str(week_lst[i])][plugin]) + except KeyError: + count.append(0) + week_lst = ["7" if i == "0" else i for i in week_lst] plt.plot(week_lst, count) - plt.title(f'{group_name} 周 {plugin} 功能调用统计【为7天统计】', FontProperties=font) + plt.title(f"{name} 周 {plugin} 功能调用统计【为7天统计】", FontProperties=font) else: - init_bar_graph(update_data(data), f'{group_name} 周功能调用统计【为7天统计】') - elif arg == 'month_statistics': + init_bar_graph(update_data(data), f"{name} 周功能调用统计【为7天统计】") + elif arg == "month_statistics": if plugin: day_index = day_index % 30 day_lst = [] @@ -99,24 +141,24 @@ def generate_statistics_img(data: dict, arg: str, group_name: str, plugin: str, day_lst.append(i) count = [data[str(day_lst[i])][plugin] for i in range(30)] day_lst = [str(x + 1) for x in day_lst] - plt.title(f'{group_name} 月 {plugin} 功能调用统计【为30天统计】', FontProperties=font) + plt.title(f"{name} 月 {plugin} 功能调用统计【为30天统计】", FontProperties=font) plt.plot(day_lst, count) else: - init_bar_graph(update_data(data), f'{group_name} 月功能调用统计【为30天统计】') - elif arg == 'total_statistics': - init_bar_graph(data, f'{group_name} 功能调用统计') + init_bar_graph(update_data(data), f"{name} 月功能调用统计【为30天统计】") + elif arg == "total_statistics": + init_bar_graph(data, f"{name} 功能调用统计") return fig2b64(plt) -def init_bar_graph(data: dict, title: str, ha: str = 'left', va: str = 'center'): - plt.tick_params(axis='y', labelsize=7) +def init_bar_graph(data: dict, title: str, ha: str = "left", va: str = "center"): + plt.tick_params(axis="y", labelsize=7) tmp_x = list(data.keys()) tmp_y = list(data.values()) x = [tmp_x[i] for i in range(len(tmp_y)) if tmp_y[i]] y = [tmp_y[i] for i in range(len(tmp_y)) if tmp_y[i]] plt.barh(x, y) - plt.title(f'{title}', FontProperties=font) + plt.title(f"{title}", FontProperties=font) for y, x in zip(y, x): plt.text(y, x, s=str(y), ha=ha, va=va, fontsize=8) diff --git a/plugins/statistics_hook.py b/plugins/statistics_hook.py index 1f004fd3..6d762102 100644 --- a/plugins/statistics_hook.py +++ b/plugins/statistics_hook.py @@ -7,6 +7,7 @@ from datetime import datetime from configs.config import plugins2info_dict from utils.utils import scheduler from nonebot.typing import Optional + try: import ujson as json except ModuleNotFoundError: @@ -14,126 +15,177 @@ except ModuleNotFoundError: try: - with open(DATA_PATH + '_prefix_count.json', 'r', encoding='utf8') as f: + with open(DATA_PATH + "_prefix_count.json", "r", encoding="utf8") as f: _prefix_count_dict = json.load(f) except (FileNotFoundError, ValueError): _prefix_count_dict = { - 'total_statistics': { - 'total': {}, + "total_statistics": { + "total": {}, }, - 'day_statistics': { - 'total': {}, + "day_statistics": { + "total": {}, }, - 'week_statistics': { - 'total': {}, + "week_statistics": { + "total": {}, }, - 'month_statistics': { - 'total': {}, + "month_statistics": { + "total": {}, }, - 'start_time': str(datetime.now().date()), - 'day_index': 0 + "start_time": str(datetime.now().date()), + "day_index": 0, + } + +try: + with open(DATA_PATH + "_prefix_user_count.json", "r", encoding="utf8") as f: + _prefix_user_count_dict = json.load(f) +except (FileNotFoundError, ValueError): + _prefix_user_count_dict = { + "total_statistics": { + "total": {}, + }, + "day_statistics": { + "total": {}, + }, + "week_statistics": { + "total": {}, + }, + "month_statistics": { + "total": {}, + }, + "start_time": str(datetime.now().date()), + "day_index": 0, } # 以前版本转换 -if not _prefix_count_dict.get('day_index'): +if not _prefix_count_dict.get("day_index"): tmp = _prefix_count_dict.copy() - _prefix_count_dict = {'total_statistics': tmp['total_statistics'], 'day_statistics': { - 'total': {}, - }, 'week_statistics': { - 'total': {}, - }, 'month_statistics': { - 'total': {}, - }, 'start_time': tmp['start_time'], 'day_index': 0} + _prefix_count_dict = { + "total_statistics": tmp["total_statistics"], + "day_statistics": { + "total": {}, + }, + "week_statistics": { + "total": {}, + }, + "month_statistics": { + "total": {}, + }, + "start_time": tmp["start_time"], + "day_index": 0, + } # 添加命令次数 @run_postprocessor -async def _(matcher: Matcher, exception: Optional[Exception], bot: Bot, event: GroupMessageEvent, state: T_State): +async def _( + matcher: Matcher, + exception: Optional[Exception], + bot: Bot, + event: GroupMessageEvent, + state: T_State, +): global _prefix_count_dict - if matcher.type == 'message' and matcher.priority not in [1, 9]: + if matcher.type == "message" and matcher.priority not in [1, 9]: model = matcher.module - day_index = _prefix_count_dict['day_index'] + day_index = _prefix_count_dict["day_index"] # print(f'model --> {model}') for plugin in plugins2info_dict: if plugin == model: - # print(f'plugin --> {plugin}') + print(f'plugin --> {plugin}') try: group_id = str(event.group_id) except AttributeError: - group_id = 'total' - plugin_name = plugins2info_dict[plugin]['cmd'][0] - check_exists_key(group_id, plugin_name) - _prefix_count_dict['total_statistics']['total'][plugin_name] += 1 - _prefix_count_dict['day_statistics']['total'][plugin_name] += 1 - _prefix_count_dict['week_statistics']['total'][plugin_name] += 1 - _prefix_count_dict['month_statistics']['total'][plugin_name] += 1 + group_id = "total" + user_id = str(event.user_id) + plugin_name = plugins2info_dict[plugin]["cmd"][0] + check_exists_key(group_id, user_id, plugin_name) + for data in [_prefix_count_dict, _prefix_user_count_dict]: + data["total_statistics"]["total"][plugin_name] += 1 + data["day_statistics"]["total"][plugin_name] += 1 + data["week_statistics"]["total"][plugin_name] += 1 + data["month_statistics"]["total"][plugin_name] += 1 # print(_prefix_count_dict) - if group_id != 'total': - _prefix_count_dict['total_statistics'][group_id][plugin_name] += 1 - _prefix_count_dict['day_statistics'][group_id][plugin_name] += 1 - _prefix_count_dict['week_statistics'][group_id][str(day_index % 7)][plugin_name] += 1 - _prefix_count_dict['month_statistics'][group_id][str(day_index % 30)][plugin_name] += 1 - with open(DATA_PATH + '_prefix_count.json', 'w', encoding='utf8') as f: + if group_id != "total": + for data in [_prefix_count_dict, _prefix_user_count_dict]: + if data == _prefix_count_dict: + key = group_id + else: + key = user_id + data["total_statistics"][key][plugin_name] += 1 + data["day_statistics"][key][plugin_name] += 1 + data["week_statistics"][key][str(day_index % 7)][ + plugin_name + ] += 1 + data["month_statistics"][key][str(day_index % 30)][ + plugin_name + ] += 1 + with open(DATA_PATH + "_prefix_count.json", "w", encoding="utf8") as f: json.dump(_prefix_count_dict, f, indent=4, ensure_ascii=False) + with open( + DATA_PATH + "_prefix_user_count.json", "w", encoding="utf8" + ) as f: + json.dump(_prefix_user_count_dict, f, ensure_ascii=False, indent=4) break -def check_exists_key(group_id: str, plugin_name: str): - global _prefix_count_dict - if not _prefix_count_dict['total_statistics']['total'].get(plugin_name): - _prefix_count_dict['total_statistics']['total'][plugin_name] = 0 - if not _prefix_count_dict['day_statistics']['total'].get(plugin_name): - _prefix_count_dict['day_statistics']['total'][plugin_name] = 0 - if not _prefix_count_dict['week_statistics']['total'].get(plugin_name): - _prefix_count_dict['week_statistics']['total'][plugin_name] = 0 - if not _prefix_count_dict['month_statistics']['total'].get(plugin_name): - _prefix_count_dict['month_statistics']['total'][plugin_name] = 0 +def check_exists_key(group_id: str, user_id: str, plugin_name: str): + global _prefix_count_dict, _prefix_user_count_dict + for data in [_prefix_count_dict, _prefix_user_count_dict]: + if data == _prefix_count_dict: + key = group_id + else: + key = user_id + if not data["total_statistics"]["total"].get(plugin_name): + data["total_statistics"]["total"][plugin_name] = 0 + if not data["day_statistics"]["total"].get(plugin_name): + data["day_statistics"]["total"][plugin_name] = 0 + if not data["week_statistics"]["total"].get(plugin_name): + data["week_statistics"]["total"][plugin_name] = 0 + if not data["month_statistics"]["total"].get(plugin_name): + data["month_statistics"]["total"][plugin_name] = 0 - if not _prefix_count_dict['total_statistics'].get(group_id): - _prefix_count_dict['total_statistics'][group_id] = {} - if not _prefix_count_dict['total_statistics'][group_id].get(plugin_name): - _prefix_count_dict['total_statistics'][group_id][plugin_name] = 0 - if not _prefix_count_dict['day_statistics'].get(group_id): - _prefix_count_dict['day_statistics'][group_id] = {} - if not _prefix_count_dict['day_statistics'][group_id].get(plugin_name): - _prefix_count_dict['day_statistics'][group_id][plugin_name] = 0 + if not data["total_statistics"].get(key): + data["total_statistics"][key] = {} + if not data["total_statistics"][key].get(plugin_name): + data["total_statistics"][key][plugin_name] = 0 + if not data["day_statistics"].get(key): + data["day_statistics"][key] = {} + if not data["day_statistics"][key].get(plugin_name): + data["day_statistics"][key][plugin_name] = 0 - if not _prefix_count_dict['week_statistics'].get(group_id): - _prefix_count_dict['week_statistics'][group_id] = {} - if _prefix_count_dict['week_statistics'][group_id].get('0') is None: - for i in range(7): - _prefix_count_dict['week_statistics'][group_id][str(i)] = {} - if _prefix_count_dict['week_statistics'][group_id]['0'].get(plugin_name) is None: - for i in range(7): - _prefix_count_dict['week_statistics'][group_id][str(i)][plugin_name] = 0 + if not data["week_statistics"].get(key): + data["week_statistics"][key] = {} + if data["week_statistics"][key].get("0") is None: + for i in range(7): + data["week_statistics"][key][str(i)] = {} + if data["week_statistics"][key]["0"].get(plugin_name) is None: + for i in range(7): + data["week_statistics"][key][str(i)][plugin_name] = 0 - if not _prefix_count_dict['month_statistics'].get(group_id): - _prefix_count_dict['month_statistics'][group_id] = {} - if _prefix_count_dict['month_statistics'][group_id].get('0') is None: - for i in range(30): - _prefix_count_dict['month_statistics'][group_id][str(i)] = {} - if _prefix_count_dict['month_statistics'][group_id]['0'].get(plugin_name) is None: - for i in range(30): - _prefix_count_dict['month_statistics'][group_id][str(i)][plugin_name] = 0 + if not data["month_statistics"].get(key): + data["month_statistics"][key] = {} + if data["month_statistics"][key].get("0") is None: + for i in range(30): + data["month_statistics"][key][str(i)] = {} + if data["month_statistics"][key]["0"].get(plugin_name) is None: + for i in range(30): + data["month_statistics"][key][str(i)][plugin_name] = 0 # 天 @scheduler.scheduled_job( - 'cron', + "cron", hour=0, minute=1, ) async def _(): - for group_id in _prefix_count_dict['day_statistics'].keys(): - for key in _prefix_count_dict['day_statistics'][group_id].keys(): - _prefix_count_dict['day_statistics'][group_id][key] = 0 - _prefix_count_dict['day_index'] += 1 - if _prefix_count_dict['day_index'] % 7 == 0: - _prefix_count_dict['week_statistics']['total'] = {} - if _prefix_count_dict['day_index'] % 30 == 0: - _prefix_count_dict['month_statistics']['total'] = {} - with open(DATA_PATH + '_prefix_count.json', 'w', encoding='utf8') as f: + for data in [_prefix_count_dict, _prefix_user_count_dict]: + for x in _prefix_count_dict["day_statistics"].keys(): + for key in _prefix_count_dict["day_statistics"][x].keys(): + data["day_statistics"][x][key] = 0 + data["day_index"] += 1 + with open(DATA_PATH + "_prefix_count.json", "w", encoding="utf8") as f: json.dump(_prefix_count_dict, f, indent=4, ensure_ascii=False) - - + with open(DATA_PATH + "_prefix_user_count.json", "w", encoding="utf8") as f: + json.dump(_prefix_user_count_dict, f, indent=4, ensure_ascii=False) diff --git a/plugins/super_cmd/__init__.py b/plugins/super_cmd/__init__.py index 8edb6410..6d0bc12f 100644 --- a/plugins/super_cmd/__init__.py +++ b/plugins/super_cmd/__init__.py @@ -2,28 +2,55 @@ from nonebot import on_command from nonebot.permission import SUPERUSER from models.level_user import LevelUser from nonebot.typing import T_State -from nonebot.adapters.cqhttp import Bot, Event, MessageEvent, Message +from nonebot.adapters.cqhttp import Bot, MessageEvent, Message from nonebot.rule import to_me from utils.utils import get_message_at, get_message_text, is_number, get_bot from services.log import logger from .data_source import open_remind, close_remind from models.group_info import GroupInfo from models.friend_user import FriendUser -from utils.init_result import at +from utils.message_builder import at +from configs.path_config import IMAGE_PATH +import asyncio +import os -__plugin_name__ = '超级用户指令 [Hidden]' -__plugin_usage__ = '用法' +__plugin_name__ = "超级用户指令 [Hidden]" +__plugin_usage__ = "用法" -super_cmd = on_command("添加管理", aliases={'删除管理', '添加权限', '删除权限'}, rule=to_me(), priority=1, permission=SUPERUSER, block=True) -oc_gb = on_command('开启广播通知', aliases={'关闭广播通知'}, rule=to_me(), permission=SUPERUSER, priority=1, block=True) -cls_group = on_command("所有群组", rule=to_me(), permission=SUPERUSER, priority=1, block=True) -cls_friend = on_command("所有好友", rule=to_me(), permission=SUPERUSER, priority=1, block=True) -del_group = on_command('退群', rule=to_me(), permission=SUPERUSER, priority=1, block=True) -update_group_info = on_command('更新群信息', rule=to_me(), permission=SUPERUSER, priority=1, block=True) -update_friend_info = on_command('更新好友信息', rule=to_me(), permission=SUPERUSER, priority=1, block=True) -# update_cmd = on_command('更新命令开关', rule=to_me(), permission=SUPERUSER, priority=1, block=True) +super_cmd = on_command( + "添加管理", + aliases={"删除管理", "添加权限", "删除权限"}, + rule=to_me(), + priority=1, + permission=SUPERUSER, + block=True, +) +oc_gb = on_command( + "开启广播通知", + aliases={"关闭广播通知"}, + rule=to_me(), + permission=SUPERUSER, + priority=1, + block=True, +) +cls_group = on_command( + "所有群组", rule=to_me(), permission=SUPERUSER, priority=1, block=True +) +cls_friend = on_command( + "所有好友", rule=to_me(), permission=SUPERUSER, priority=1, block=True +) +del_group = on_command("退群", rule=to_me(), permission=SUPERUSER, priority=1, block=True) +update_group_info = on_command( + "更新群信息", rule=to_me(), permission=SUPERUSER, priority=1, block=True +) +update_friend_info = on_command( + "更新好友信息", rule=to_me(), permission=SUPERUSER, priority=1, block=True +) +clear_data = on_command( + "清理数据", rule=to_me(), permission=SUPERUSER, priority=1, block=True +) @super_cmd.handle() @@ -40,10 +67,13 @@ async def _(bot: Bot, event: MessageEvent, state: T_State): level = int(args[2]) flag = 1 else: - await super_cmd.finish('所有参数必须是数字!', at_sender=True) + await super_cmd.finish("所有参数必须是数字!", at_sender=True) else: - await super_cmd.finish('权限参数不完全\n\t格式:添加/删除权限 [at] [level]' - '\n\t格式:添加/删除权限 [qq] [group] [level]', at_sender=True) + await super_cmd.finish( + "权限参数不完全\n\t格式:添加/删除权限 [at] [level]" + "\n\t格式:添加/删除权限 [qq] [group] [level]", + at_sender=True, + ) else: qq = int(qq[0]) group_id = event.group_id @@ -51,7 +81,7 @@ async def _(bot: Bot, event: MessageEvent, state: T_State): if is_number(args[0]): level = int(args[0]) else: - await super_cmd.finish('权限等级必须是数字!', at_sender=True) + await super_cmd.finish("权限等级必须是数字!", at_sender=True) if state["_prefix"]["raw_command"][:2] == "添加": if await LevelUser.set_level(qq, group_id, level, 1): result = "添加管理成功, 权限: " + str(level) @@ -65,120 +95,142 @@ async def _(bot: Bot, event: MessageEvent, state: T_State): if flag == 2: await super_cmd.send(result) elif flag == 1: - await bot.send_group_msg(group_id=group_id, message=Message(f'{at(qq)}管理员修改了你的权限' - f'\n--------\n你当前的权限等级:{level}')) - await super_cmd.send('修改成功') + await bot.send_group_msg( + group_id=group_id, + message=Message(f"{at(qq)}管理员修改了你的权限" f"\n--------\n你当前的权限等级:{level}"), + ) + await super_cmd.send("修改成功") except Exception as e: await super_cmd.send("执行指令失败!") - logger.error(f'执行指令失败 e{e}') + logger.error(f"执行指令失败 e{e}") @oc_gb.handle() -async def _(bot: Bot, event: Event, state: T_State): +async def _(bot: Bot, event: MessageEvent, state: T_State): group = get_message_text(event.json()) if group: if is_number(group): group = int(group) for g in await bot.get_group_list(): - if g['group_id'] == group: + if g["group_id"] == group: break else: - await oc_gb.finish('没有加入这个群...', at_sender=True) + await oc_gb.finish("没有加入这个群...", at_sender=True) # try: - if state["_prefix"]["raw_command"] == '开启广播通知': - logger.info(f'USER {event.user_id} 开启了 GROUP {group} 的广播') - await oc_gb.finish(await open_remind(group, 'gb'), at_sender=True) + if state["_prefix"]["raw_command"] == "开启广播通知": + logger.info(f"USER {event.user_id} 开启了 GROUP {group} 的广播") + await oc_gb.finish(await open_remind(group, "gb"), at_sender=True) else: - logger.info(f'USER {event.user_id} 关闭了 GROUP {group} 的广播') - await oc_gb.finish(await close_remind(group, 'gb'), at_sender=True) + logger.info(f"USER {event.user_id} 关闭了 GROUP {group} 的广播") + await oc_gb.finish(await close_remind(group, "gb"), at_sender=True) # except Exception as e: # await oc_gb.finish(f'关闭 {group} 的广播失败', at_sender=True) else: - await oc_gb.finish('请输入正确的群号', at_sender=True) + await oc_gb.finish("请输入正确的群号", at_sender=True) else: - await oc_gb.finish('请输入要关闭广播的群号', at_sender=True) + await oc_gb.finish("请输入要关闭广播的群号", at_sender=True) @del_group.handle() -async def _(bot: Bot, event: Event, state: T_State): +async def _(bot: Bot, event: MessageEvent, state: T_State): group = get_message_text(event.json()) if group: if is_number(group): try: await bot.set_group_leave(group_id=int(group)) - logger.info(f'退出群聊 {group} 成功') - await del_group.finish(f'退出群聊 {group} 成功', at_sender=True) + logger.info(f"退出群聊 {group} 成功") + await del_group.finish(f"退出群聊 {group} 成功", at_sender=True) except Exception as e: - logger.info(f'退出群聊 {group} 失败 e:{e}') + logger.info(f"退出群聊 {group} 失败 e:{e}") else: - await del_group.finish(f'请输入正确的群号', at_sender=True) + await del_group.finish(f"请输入正确的群号", at_sender=True) else: - await del_group.finish(f'请输入群号', at_sender=True) + await del_group.finish(f"请输入群号", at_sender=True) @cls_group.handle() -async def _(bot: Bot, event: Event, state: T_State): - gl = await bot.get_group_list(self_id=bot.self_id) +async def _(bot: Bot, event: MessageEvent, state: T_State): + gl = await bot.get_group_list(self_id=int(bot.self_id)) msg = ["{group_id} {group_name}".format_map(g) for g in gl] msg = "\n".join(msg) msg = f"bot:{bot.self_id}\n| 群号 | 群名 | 共{len(gl)}个群\n" + msg - await bot.send_private_msg(self_id=bot.self_id, user_id=int(list(bot.config.superusers)[0]), message=msg) + await bot.send_private_msg( + self_id=int(bot.self_id), + user_id=int(list(bot.config.superusers)[0]), + message=msg, + ) @cls_friend.handle() -async def _(bot: Bot, event: Event, state: T_State): - gl = await bot.get_friend_list(self_id=bot.self_id) +async def _(bot: Bot, event: MessageEvent, state: T_State): + gl = await bot.get_friend_list(self_id=int(bot.self_id)) msg = ["{user_id} {nickname}".format_map(g) for g in gl] msg = "\n".join(msg) msg = f"| QQ号 | 昵称 | 共{len(gl)}个好友\n" + msg - await bot.send_private_msg(self_id=bot.self_id, user_id=int(list(bot.config.superusers)[0]), message=msg) + await bot.send_private_msg( + self_id=int(bot.self_id), + user_id=int(list(bot.config.superusers)[0]), + message=msg, + ) @update_group_info.handle() -async def _(bot: Bot, event: Event, state: T_State): +async def _(bot: Bot, event: MessageEvent, state: T_State): bot = get_bot() gl = await bot.get_group_list(self_id=bot.self_id) - gl = [g['group_id'] for g in gl] + gl = [g["group_id"] for g in gl] num = 0 - rst = '' + rst = "" for g in gl: group_info = await bot.get_group_info(group_id=g) - if await GroupInfo.add_group_info(group_info['group_id'], group_info['group_name'], - group_info['max_member_count'], group_info['member_count']): + if await GroupInfo.add_group_info( + group_info["group_id"], + group_info["group_name"], + group_info["max_member_count"], + group_info["member_count"], + ): num += 1 - logger.info(f'自动更新群组 {g} 信息成功') + logger.info(f"自动更新群组 {g} 信息成功") else: - logger.info(f'自动更新群组 {g} 信息失败') - rst += f'{g} 更新失败\n' - await update_group_info.send(f'成功更新了 {num} 个群的信息\n{rst[:-1]}') + logger.info(f"自动更新群组 {g} 信息失败") + rst += f"{g} 更新失败\n" + await update_group_info.send(f"成功更新了 {num} 个群的信息\n{rst[:-1]}") @update_friend_info.handle() -async def _(bot: Bot, event: Event, state: T_State): +async def _(bot: Bot, event: MessageEvent, state: T_State): num = 0 - rst = '' + rst = "" fl = await get_bot().get_friend_list(self_id=bot.self_id) for f in fl: - if await FriendUser.add_friend_info(f['user_id'], f['nickname']): + if await FriendUser.add_friend_info(f["user_id"], f["nickname"]): logger.info(f'自动更新好友 {f["user_id"]} 信息成功') num += 1 else: logger.warning(f'自动更新好友 {f["user_id"]} 信息失败') rst += f'{f["user_id"]} 更新失败\n' - await update_friend_info.send(f'成功更新了 {num} 个好友的信息\n{rst[:-1]}') - - -# @update_cmd.handle() -# async def _(bot: Bot, event: Event, state: T_State): -# gl = await bot.get_group_list(self_id=bot.self_id) -# gl = [g['group_id'] for g in gl] -# for g in gl: -# check_group_switch_json(g) -# for file in os.listdir(DATA_PATH + 'group_help'): -# os.remove(DATA_PATH + f'group_help/{file}') -# await update_cmd.finish('更新命令完成!', at_sender=True) - + await update_friend_info.send(f"成功更新了 {num} 个好友的信息\n{rst[:-1]}") +@clear_data.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + await clear_data.send("开始清理临时数据....") + size = await asyncio.get_event_loop().run_in_executor( + None, _clear_data + ) + await clear_data.send("共清理了 {:.2f}MB 的数据...".format(size / 1024 / 1024)) +def _clear_data() -> float: + size = 0 + for dir_name in ['temp', 'rar', 'r18_rar']: + dir_name = f'{IMAGE_PATH}/{dir_name}' + for file in os.listdir(dir_name): + try: + file_size = os.path.getsize(os.path.join(dir_name, file)) + os.remove(os.path.join(dir_name, file)) + except Exception as e: + logger.error(f"清理临时数据错误...e:{e}") + file_size = 0 + size += file_size + return float(size) diff --git a/plugins/super_cmd/data_source.py b/plugins/super_cmd/data_source.py index d680ef92..6b6eb783 100644 --- a/plugins/super_cmd/data_source.py +++ b/plugins/super_cmd/data_source.py @@ -2,43 +2,43 @@ from models.group_remind import GroupRemind async def open_remind(group: int, name: str) -> str: - _name = '' - if name == 'zwa': - _name = '早晚安' - if name == 'dz': - _name = '地震播报' - if name == 'hy': - _name = '群欢迎' - if name == 'kxcz': - _name = '开箱重置提醒' - if name == 'gb': - _name = '广播' + _name = "" + if name == "zwa": + _name = "早晚安" + if name == "dz": + _name = "地震播报" + if name == "hy": + _name = "群欢迎" + if name == "kxcz": + _name = "开箱重置提醒" + if name == "gb": + _name = "广播" if await GroupRemind.get_status(group, name): - return f'该群已经开启过 {_name} 通知,请勿重复开启!' + return f"该群已经开启过 {_name} 通知,请勿重复开启!" if await GroupRemind.set_status(group, name, True): - return f'成功开启 {_name} 通知!0v0' + return f"成功开启 {_name} 通知!0v0" else: - return f'开启 {_name} 通知失败了...' + return f"开启 {_name} 通知失败了..." async def close_remind(group: int, name: str) -> str: - _name = '' - if name == 'zwa': - _name = '早晚安' - if name == 'dz': - _name = '地震播报' - if name == 'hy': - _name = '群欢迎' - if name == 'kxcz': - _name = '开箱重置提醒' - if name == 'gb': - _name = '广播' + _name = "" + if name == "zwa": + _name = "早晚安" + if name == "dz": + _name = "地震播报" + if name == "hy": + _name = "群欢迎" + if name == "kxcz": + _name = "开箱重置提醒" + if name == "gb": + _name = "广播" if not await GroupRemind.get_status(group, name): - return f'该群已经取消过 {_name} 通知,请勿重复取消!' + return f"该群已经取消过 {_name} 通知,请勿重复取消!" if await GroupRemind.set_status(group, name, False): - return f'成功关闭 {_name} 通知!0v0' + return f"成功关闭 {_name} 通知!0v0" else: - return f'关闭 {_name} 通知失败了...' + return f"关闭 {_name} 通知失败了..." # cmd_list = ['总开关', '签到', '发送图片', '色图', '黑白草图', 'coser', '鸡汤/语录', '骂我', '开箱', '鲁迅说', '假消息', '商店系统', @@ -73,4 +73,3 @@ async def close_remind(group: int, name: str) -> str: def reload(data): static_group_dict = data - diff --git a/plugins/super_help/__init__.py b/plugins/super_help/__init__.py index f2a8c613..51e64af4 100644 --- a/plugins/super_help/__init__.py +++ b/plugins/super_help/__init__.py @@ -5,12 +5,14 @@ from nonebot.adapters import Bot, Event from nonebot.rule import to_me -super_help = on_command("超级用户帮助", rule=to_me(), priority=1, permission=SUPERUSER, block=True) +super_help = on_command( + "超级用户帮助", rule=to_me(), priority=1, permission=SUPERUSER, block=True +) @super_help.handle() async def _(bot: Bot, event: Event, state: T_State): - result = '''超级用户帮助: + result = """超级用户帮助: 1.添加/删除管理 [at] [level] 2.查看群组/查看好友 3.广播 --> 指令:广播- [msg] @@ -23,12 +25,17 @@ async def _(bot: Bot, event: Event, state: T_State): 10.更新好友信息 11.更新群群信息 12.重载原神/方舟/赛马娘/坎公骑冠剑卡池 - 13.添加商品 [名称] [价格] [描述] [折扣] [限时时间] + 13.添加商品 [名称]-[价格]-[描述]-[折扣]-[限时时间] 14.删除商品 [名称(序号)] 15.修改商品 -name [名称(序号)] -price [价格] -des [描述] -discount [折扣] -time [限时] - 16.节日红包 [金额] [数量] [祝福语](可省) [指定群](可省) [指定群]... - 17.更新原神今日素材''' + 16.节日红包 [金额] [数量] [祝福语](可省) *[指定群](可省) + 17.更新原神今日素材 + 18.更新原神资源信息 + 19.添加pix关键词/uid/pid *[关键词/uid/pid] [-f](强制通过不检测) + 20.通过/取消pix关键词 [keyword/pid:pid/uid:uid] + 21.删除pix关键词 [keyword/uid/pid:pid] + 22.更新pix关键词 [keyword/pid:pid/uid:uid] [num] + 23.删除pix图片 *[pid] [-b](同时加入黑名单)? + 24.查看pix图库 [keyword] + 25.pix检测更新 [update]""" await super_help.finish(result, at_sender=True) - - - diff --git a/plugins/test.py b/plugins/test.py deleted file mode 100644 index fdaa3abc..00000000 --- a/plugins/test.py +++ /dev/null @@ -1,59 +0,0 @@ -from nonebot.rule import to_me -from nonebot.typing import T_State -import base64 -from nonebot.adapters.cqhttp import Bot, GroupMessageEvent, MessageEvent -from nonebot import on_command, on_keyword, on_metaevent -from nonebot.plugin import MatcherGroup -from nonebot.adapters.cqhttp.event import GroupRequestEvent -import nonebot -from models.open_cases_user import OpenCasesUser -from tzlocal import get_localzone -from datetime import datetime, timezone, timedelta -from utils.utils import get_bot, get_message_imgs, get_local_proxy -from utils.init_result import * -from nonebot.adapters.cqhttp.message import MessageSegment -import requests -import aiohttp -from models.bag_user import BagUser -from nonebot.adapters.cqhttp.message import Message -import asyncio -from models.group_member_info import GroupInfoUser -from matplotlib import font_manager -from matplotlib import pyplot as plt -import matplotlib as mpl -from utils.utils import scheduler -from utils.browser import get_browser -from playwright.async_api import async_playwright, Browser -from typing import Optional -import playwright -# erm = on_command('异世相遇,尽享美味', aliases={'异世相遇 尽享美味', '异世相遇,尽享美味'}, priority=5, block=True) - - -matcher = on_keyword({"test"}) - - -@matcher.handle() -async def _(bot: Bot, event: MessageEvent, state: T_State): - browser = await get_browser() - url = 'https://genshin.pub/daily' - # try: - page = await browser.new_page() - await page.goto(url) - await page.set_viewport_size({"width": 2560, "height": 1080}) - await page.click("button") - card = await page.query_selector(".GSContainer_inner_border_box__21_vs") - clip = await card.bounding_box() - await page.screenshot(path=f'xxxx.png', clip=clip) - await page.close() - # except Exception: - if page: - await page.close() - # raise - -# @erm.handle() -# async def first_receive(bot: Bot, event: Event, state: T_State): -# print(record('erm')) -# await matcher.send(record('erm')) - - - diff --git a/plugins/translate/__init__.py b/plugins/translate/__init__.py index ed7afb5d..4d57abe6 100644 --- a/plugins/translate/__init__.py +++ b/plugins/translate/__init__.py @@ -1,42 +1,35 @@ from nonebot import on_command from utils.utils import get_message_text from services.log import logger -from nonebot.adapters.cqhttp import Bot, MessageEvent +from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent from nonebot.typing import T_State from .data_source import translate_msg -__plugin_name__ = '翻译' +__plugin_name__ = "翻译" -__plugin_usage__ = '用法:\n' \ - '英翻 [英文] 翻英 [中文]\n' \ - '日翻 [日文] 翻日 [中文]\n' \ - '韩翻 [韩文] 翻韩 [中文]' +__plugin_usage__ = "用法:\n" "英翻 [英文] 翻英 [中文]\n" "日翻 [日文] 翻日 [中文]\n" "韩翻 [韩文] 翻韩 [中文]" -translate = on_command("translate", aliases={'英翻', '翻英', - '日翻', '翻日', - '韩翻', '翻韩' - }, priority=5, block=True) +translate = on_command( + "translate", aliases={"英翻", "翻英", "日翻", "翻日", "韩翻", "翻韩"}, priority=5, block=True +) @translate.handle() async def _(bot: Bot, event: MessageEvent, state: T_State): msg = get_message_text(event.json()) if msg: - state['msg'] = msg + state["msg"] = msg -@translate.got('msg', prompt='你要翻译的消息是啥?') +@translate.got("msg", prompt="你要翻译的消息是啥?") async def _(bot: Bot, event: MessageEvent, state: T_State): - msg = state['msg'] + msg = state["msg"] if len(msg) > 150: - await translate.finish('翻译过长!请不要超过150字', at_sender=True) + await translate.finish("翻译过长!请不要超过150字", at_sender=True) await translate.send(await translate_msg(state["_prefix"]["raw_command"], msg)) logger.info( f"(USER {event.user_id}, GROUP " - f"{event.group_id if event.message_type != 'private' else 'private'}) 使用翻译:{msg}") - - - - + f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'}) 使用翻译:{msg}" + ) diff --git a/plugins/translate/data_source.py b/plugins/translate/data_source.py index 6bdd3d11..e2fd80f5 100644 --- a/plugins/translate/data_source.py +++ b/plugins/translate/data_source.py @@ -2,26 +2,27 @@ import aiohttp from utils.utils import get_local_proxy from utils.user_agent import get_user_agent -url = f'http://fanyi.youdao.com/translate?smartresult=dict&smartresult=rule&smartresult=ugc&sessionFrom=null' +url = f"http://fanyi.youdao.com/translate?smartresult=dict&smartresult=rule&smartresult=ugc&sessionFrom=null" async def translate_msg(language_type, msg): data = { - 'type': parse_language(language_type), - 'i': msg, + "type": parse_language(language_type), + "i": msg, "doctype": "json", "version": "2.1", "keyfrom": "fanyi.web", "ue": "UTF-8", "action": "FY_BY_CLICKBUTTON", - "typoResult": "true" + "typoResult": "true", } async with aiohttp.ClientSession(headers=get_user_agent()) as session: async with session.post(url, data=data, proxy=get_local_proxy()) as res: data = await res.json() - if data['errorCode'] == 0: - return data['translateResult'][0][0]['tgt'] - return '翻译惜败..' + if data["errorCode"] == 0: + return data["translateResult"][0][0]["tgt"] + return "翻译惜败.." + # ZH_CN2EN 中文 » 英语 # ZH_CN2JA 中文 » 日语 @@ -38,32 +39,23 @@ async def translate_msg(language_type, msg): def parse_language(language_type): - if language_type == '英翻': - return 'EN2ZH_CN' - if language_type == '日翻': - return 'JA2ZH_CN' - if language_type == '韩翻': - return 'KR2ZH_CN' + if language_type == "英翻": + return "EN2ZH_CN" + if language_type == "日翻": + return "JA2ZH_CN" + if language_type == "韩翻": + return "KR2ZH_CN" # if language_type == '法翻': # return 'FR2ZH_CN' # if language_type == '俄翻': # return 'RU2ZH_CN' - if language_type == '翻英': - return 'ZH_CN2EN' - if language_type == '翻日': - return 'ZH_CN2JA' - if language_type == '翻韩': - return 'ZH_CN2KR' + if language_type == "翻英": + return "ZH_CN2EN" + if language_type == "翻日": + return "ZH_CN2JA" + if language_type == "翻韩": + return "ZH_CN2KR" # if language_type == '翻法': # return 'ZH_CN2FR' # if language_type == '翻俄': # return 'ZH_CN2RU' - - - - - - - - - diff --git a/plugins/update_gocqhttp/__init__.py b/plugins/update_gocqhttp/__init__.py index 6c5a01ae..4b9df65d 100644 --- a/plugins/update_gocqhttp/__init__.py +++ b/plugins/update_gocqhttp/__init__.py @@ -9,11 +9,11 @@ from utils.utils import scheduler, get_bot, UserExistLimiter from configs.config import UPDATE_GOCQ_GROUP from pathlib import Path -__plugin_name__ = '更新gocq' +__plugin_name__ = "更新gocq" -__plugin_usage__ = '用法:发送’更新gocq‘,指定群 自动检测最新版gocq下载并上传' +__plugin_usage__ = "用法:发送’更新gocq‘,指定群 自动检测最新版gocq下载并上传" -path = str((Path() / "resources" / "gocqhttp_file").absolute()) + '/' +path = str((Path() / "resources" / "gocqhttp_file").absolute()) + "/" lasted_gocqhttp = on_command("更新gocq", permission=GROUP, priority=5, block=True) @@ -25,26 +25,26 @@ _ulmt = UserExistLimiter() async def _(bot: Bot, event: GroupMessageEvent, state: T_State): # try: if event.group_id in UPDATE_GOCQ_GROUP: - await lasted_gocqhttp.send('检测中...') + await lasted_gocqhttp.send("检测中...") info = await download_gocq_lasted(path) - if info == 'gocqhttp没有更新!': - await lasted_gocqhttp.finish('gocqhttp没有更新!') + if info == "gocqhttp没有更新!": + await lasted_gocqhttp.finish("gocqhttp没有更新!") if _ulmt.check(event.group_id): - await lasted_gocqhttp.finish('gocqhttp正在更新,请勿重复使用该命令', at_sender=True) + await lasted_gocqhttp.finish("gocqhttp正在更新,请勿重复使用该命令", at_sender=True) _ulmt.set_True(event.group_id) try: for file in os.listdir(path): await upload_gocq_lasted(path, file, event.group_id) - logger.info(f'更新了cqhttp...{file}') - await lasted_gocqhttp.send(f'gocqhttp更新了,已上传成功!\n更新内容:\n{info}') + logger.info(f"更新了cqhttp...{file}") + await lasted_gocqhttp.send(f"gocqhttp更新了,已上传成功!\n更新内容:\n{info}") except Exception as e: - logger.error(f'更新gocq错误 e:{e}') + logger.error(f"更新gocq错误 e:{e}") _ulmt.set_False(event.group_id) # 更新gocq @scheduler.scheduled_job( - 'cron', + "cron", hour=3, minute=1, ) @@ -53,13 +53,14 @@ async def _(): bot = get_bot() try: info = await download_gocq_lasted(path) - if info == 'gocqhttp没有更新!': - logger.info('gocqhttp没有更新!') + if info == "gocqhttp没有更新!": + logger.info("gocqhttp没有更新!") return for group in UPDATE_GOCQ_GROUP: for file in os.listdir(path): await upload_gocq_lasted(path, file, group) - await bot.send_group_msg(group_id=group, message=f"gocqhttp更新了,已上传成功!\n更新内容:\n{info}") + await bot.send_group_msg( + group_id=group, message=f"gocqhttp更新了,已上传成功!\n更新内容:\n{info}" + ) except Exception as e: - logger.error(f'自动更新gocq出错 e:{e}') - + logger.error(f"自动更新gocq出错 e:{e}") diff --git a/plugins/update_gocqhttp/data_source.py b/plugins/update_gocqhttp/data_source.py index b17fe46f..2782f629 100644 --- a/plugins/update_gocqhttp/data_source.py +++ b/plugins/update_gocqhttp/data_source.py @@ -6,45 +6,60 @@ import platform import aiofiles from bs4 import BeautifulSoup import os -if platform.system() == 'Windows': + +if platform.system() == "Windows": asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) -url = 'https://github.com/Mrs4s/go-cqhttp/releases' +url = "https://github.com/Mrs4s/go-cqhttp/releases" async def download_gocq_lasted(path: str): async with aiohttp.ClientSession(headers=get_user_agent()) as session: async with session.get(url, proxy=get_local_proxy()) as response: - soup = BeautifulSoup(await response.text(), 'lxml') - a = soup.find('div', {'class': 'release-header'}).find('a') + soup = BeautifulSoup(await response.text(), "lxml") + a = soup.find("div", {"class": "release-header"}).find("a") title = a.text - _url = a.get('href') + _url = a.get("href") for file in os.listdir(path): - if file.endswith('.zip'): - if file == title + '-windows-amd64.zip' or file == title + '_windows_amd64.zip': - return 'gocqhttp没有更新!' + if file.endswith(".zip"): + if ( + file == title + "-windows-amd64.zip" + or file == title + "_windows_amd64.zip" + ): + return "gocqhttp没有更新!" for file in os.listdir(path): os.remove(path + file) - async with session.get("https://github.com" + _url, proxy=get_local_proxy()) as res: - update_info = '' - soup = BeautifulSoup(await res.text(), 'lxml') - info_div = soup.find('div', {'class': 'markdown-body'}) - for p in info_div.find_all('p'): - update_info += p.text.replace('
', '\n') + '\n' - div_all = soup.select('div.d-flex.flex-justify-between.flex-items-center.py-1.py-md-2.Box-body.px-2') + async with session.get( + "https://github.com" + _url, proxy=get_local_proxy() + ) as res: + update_info = "" + soup = BeautifulSoup(await res.text(), "lxml") + info_div = soup.find("div", {"class": "markdown-body"}) + for p in info_div.find_all("p"): + update_info += p.text.replace("
", "\n") + "\n" + div_all = soup.select( + "div.d-flex.flex-justify-between.flex-items-center.py-1.py-md-2.Box-body.px-2" + ) for div in div_all: - if div.find('a').find('span').text == title + '-windows-amd64.zip' or\ - div.find('a').find('span').text == title + '-linux-arm64.tar.gz' or\ - div.find('a').find('span').text == 'go-cqhttp_windows_amd64.zip' or \ - div.find('a').find('span').text == 'go-cqhttp_linux_arm64.tar.gz': - file_url = div.find('a').get('href') - if div.find('a').find('span').text.find('windows') == -1: - tag = '-linux-arm64.tar.gz' + if ( + div.find("a").find("span").text == title + "-windows-amd64.zip" + or div.find("a").find("span").text + == title + "-linux-arm64.tar.gz" + or div.find("a").find("span").text + == "go-cqhttp_windows_amd64.zip" + or div.find("a").find("span").text + == "go-cqhttp_linux_arm64.tar.gz" + ): + file_url = div.find("a").get("href") + if div.find("a").find("span").text.find("windows") == -1: + tag = "-linux-arm64.tar.gz" else: - tag = '-windows-amd64.zip' - async with session.get("https://github.com" + file_url, proxy=get_local_proxy()) as res_file: - async with aiofiles.open(path + title + tag, 'wb') as f: + tag = "-windows-amd64.zip" + async with session.get( + "https://github.com" + file_url, proxy=get_local_proxy() + ) as res_file: + async with aiofiles.open(path + title + tag, "wb") as f: await f.write(await res_file.read()) return update_info @@ -52,22 +67,17 @@ async def download_gocq_lasted(path: str): async def upload_gocq_lasted(path, name, group_id): bot = get_bot() folder_id = 0 - for folder in (await bot.get_group_root_files(group_id=group_id))['folders']: - if folder['folder_name'] == 'gocq': - folder_id = folder['folder_id'] + for folder in (await bot.get_group_root_files(group_id=group_id))["folders"]: + if folder["folder_name"] == "gocq": + folder_id = folder["folder_id"] if not folder_id: - await bot.send_group_msg(group_id=group_id, - message=f'请创建gocq文件夹后重试!') + await bot.send_group_msg(group_id=group_id, message=f"请创建gocq文件夹后重试!") for file in os.listdir(path): - os.remove(path+file) + os.remove(path + file) else: await bot.upload_group_file( - group_id=group_id, - folder=folder_id, - file=path + name, - name=name + group_id=group_id, folder=folder_id, file=path + name, name=name ) # asyncio.get_event_loop().run_until_complete(download_gocq_lasted()) - diff --git a/plugins/update_info.py b/plugins/update_info.py index 5d2e744b..abfe89fc 100644 --- a/plugins/update_info.py +++ b/plugins/update_info.py @@ -1,7 +1,7 @@ from nonebot import on_command from nonebot.adapters.cqhttp import Bot, Event from nonebot.typing import T_State -from utils.init_result import image +from utils.message_builder import image __plugin_name__ = '更新信息' diff --git a/plugins/update_pic.py b/plugins/update_pic.py index 83764149..bfa14ecc 100644 --- a/plugins/update_pic.py +++ b/plugins/update_pic.py @@ -1,22 +1,22 @@ from nonebot import on_command from PIL import Image, ImageFilter -from utils.init_result import image +from utils.message_builder import image from configs.path_config import IMAGE_PATH from services.log import logger from nonebot.rule import to_me -from nonebot.adapters.cqhttp import Bot, MessageEvent +from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent from nonebot.typing import T_State from utils.utils import get_message_imgs import aiofiles import aiohttp from utils.utils import is_number, get_message_text -from utils.img_utils import CreateImg, pic2b64 +from utils.image_utils import CreateImg, pic2b64 import cv2 import numpy as np -__plugin_name__ = '图片' -__plugin_usage__ = '''图片用法(目前): +__plugin_name__ = "图片" +__plugin_usage__ = """图片用法(目前): 可以输入 指定操作 或 序号 来进行选择 1.修改尺寸 宽(x) 高(y) 图片(在文字后跟图片即可) 示例:小真寻修改图片 修改尺寸 200 300 图片(在文字后跟图片即可) @@ -37,64 +37,101 @@ __plugin_usage__ = '''图片用法(目前): 9.边缘检测 示例:小真寻修改图片 边缘检测 图片 10.底色替换 颜色 颜色 图片 (不专业)支持:(红,蓝)->(红,蓝,白,绿,黄) - 示例:小真寻修改图片 底色替换 蓝色 红色 图片''' + 示例:小真寻修改图片 底色替换 蓝色 红色 图片""" # IMAGE_LOCAL = IMAGE_PATH + "temp/{}_update.png" -method_flag = '' +method_flag = "" -update_img = on_command("修改图片", aliases={'图片', '操作图片', '改图'}, priority=5, rule=to_me(), block=True) +update_img = on_command( + "修改图片", aliases={"图片", "操作图片", "改图"}, priority=5, rule=to_me(), block=True +) -method_list = ['修改尺寸', '等比压缩', '旋转图片', '水平翻转', '铅笔滤镜', '模糊效果', '锐化效果', '高斯模糊', '边缘检测', '底色替换'] -method_str = '' +method_list = [ + "修改尺寸", + "等比压缩", + "旋转图片", + "水平翻转", + "铅笔滤镜", + "模糊效果", + "锐化效果", + "高斯模糊", + "边缘检测", + "底色替换", +] +method_str = "" method_oper = [] for i in range(len(method_list)): - method_str += f'\n{i + 1}.{method_list[i]}' + method_str += f"\n{i + 1}.{method_list[i]}" method_oper.append(method_list[i]) method_oper.append(str(i + 1)) update_img_help = CreateImg(960, 700, font_size=24) update_img_help.text((10, 10), __plugin_usage__) -update_img_help.save(IMAGE_PATH + 'update_img_help.png') +update_img_help.save(IMAGE_PATH + "update_img_help.png") @update_img.args_parser async def _(bot: Bot, event: MessageEvent, state: T_State): global method_flag - if str(event.get_message()) in ['取消', '算了']: + if str(event.get_message()) in ["取消", "算了"]: await update_img.finish("已取消操作..", at_sender=True) - if state["_current_key"] in ['method']: + if state["_current_key"] in ["method"]: if str(event.get_message()) not in method_oper: await update_img.reject(f"操作不正确,请重新输入!{method_str}") state[state["_current_key"]] = str(event.get_message()) method_flag = str(event.get_message()) - if method_flag in ['1', '修改尺寸']: - if state["_current_key"] == 'x': + if method_flag in ["1", "修改尺寸"]: + if state["_current_key"] == "x": if not is_number(str(event.get_message())): await update_img.reject("宽度不正确!请重新输入数字...") state[state["_current_key"]] = str(event.get_message()) - if state["_current_key"] == 'y': + if state["_current_key"] == "y": if not is_number(str(event.get_message())): await update_img.reject("长度不正确!请重新输入数字...") state[state["_current_key"]] = str(event.get_message()) - elif method_flag in ['2', '等比压缩', '3', '旋转图片']: - if state["_current_key"] == 'x': + elif method_flag in ["2", "等比压缩", "3", "旋转图片"]: + if state["_current_key"] == "x": if not is_number(str(event.get_message())): await update_img.reject("比率不正确!请重新输入数字...") state[state["_current_key"]] = str(event.get_message()) - state['y'] = '' - elif method_flag in ['4', '水平翻转', '5', '铅笔滤镜', '6', '模糊效果', '7', '锐化效果', '8', '高斯模糊', '9', '边缘检测']: - state['x'] = '' - state['y'] = '' - elif method_flag in ['10', '底色替换']: - if state["_current_key"] == 'x': - if str(event.get_message()) not in ['红色', '蓝色', '红', '蓝']: - await update_img.reject('请输入支持的被替换的底色:\n红色 蓝色') - state['x'] = str(event.get_message()) - if state["_current_key"] == 'y': - if str(event.get_message()) not in ['红色', '白色', '蓝色', '绿色', '黄色', '红', '白', '蓝', '绿', '黄']: - await update_img.reject('请输入支持的替换的底色:\n红色 蓝色 白色 绿色') - state['y'] = str(event.get_message()) - if state["_current_key"] == 'imgs': + state["y"] = "" + elif method_flag in [ + "4", + "水平翻转", + "5", + "铅笔滤镜", + "6", + "模糊效果", + "7", + "锐化效果", + "8", + "高斯模糊", + "9", + "边缘检测", + ]: + state["x"] = "" + state["y"] = "" + elif method_flag in ["10", "底色替换"]: + if state["_current_key"] == "x": + if str(event.get_message()) not in ["红色", "蓝色", "红", "蓝"]: + await update_img.reject("请输入支持的被替换的底色:\n红色 蓝色") + state["x"] = str(event.get_message()) + if state["_current_key"] == "y": + if str(event.get_message()) not in [ + "红色", + "白色", + "蓝色", + "绿色", + "黄色", + "红", + "白", + "蓝", + "绿", + "黄", + ]: + await update_img.reject("请输入支持的替换的底色:\n红色 蓝色 白色 绿色") + state["y"] = str(event.get_message()) + if state["_current_key"] == "imgs": if not get_message_imgs(event.json()): await update_img.reject("没图?没图?没图?来图速来!") state[state["_current_key"]] = get_message_imgs(event.json()) @@ -102,35 +139,35 @@ async def _(bot: Bot, event: MessageEvent, state: T_State): @update_img.handle() async def _(bot: Bot, event: MessageEvent, state: T_State): - if str(event.get_message()) in ['帮助']: - await update_img.finish(image('update_img_help.png')) + if str(event.get_message()) in ["帮助"]: + await update_img.finish(image("update_img_help.png")) raw_arg = get_message_text(event.json()) img_list = get_message_imgs(event.json()) if raw_arg: args = raw_arg.split("[")[0].split(" ") print(args) - state['method'] = args[0] + state["method"] = args[0] if len(args) == 2: - if args[0] in ["等比压缩", '旋转图片']: + if args[0] in ["等比压缩", "旋转图片"]: if is_number(args[1]): - state['x'] = args[1] - state['y'] = '' + state["x"] = args[1] + state["y"] = "" elif len(args) > 2: if args[0] in ["修改尺寸"]: if is_number(args[1]): - state['x'] = args[1] + state["x"] = args[1] if is_number(args[2]): - state['y'] = args[2] - if args[0] in ['底色替换']: - if args[1] in ['红色', '蓝色', '蓝', '红']: - state['x'] = args[1] - if args[2] in ['红色', '白色', '蓝色', '绿色', '黄色', '红', '白', '蓝', '绿', '黄']: - state['y'] = args[2] - if args[0] in ['水平翻转', '铅笔滤镜', '模糊效果', '锐化效果', '高斯模糊', '边缘检测']: - state['x'] = '' - state['y'] = '' + state["y"] = args[2] + if args[0] in ["底色替换"]: + if args[1] in ["红色", "蓝色", "蓝", "红"]: + state["x"] = args[1] + if args[2] in ["红色", "白色", "蓝色", "绿色", "黄色", "红", "白", "蓝", "绿", "黄"]: + state["y"] = args[2] + if args[0] in ["水平翻转", "铅笔滤镜", "模糊效果", "锐化效果", "高斯模糊", "边缘检测"]: + state["x"] = "" + state["y"] = "" if img_list: - state['imgs'] = img_list + state["imgs"] = img_list @update_img.got("method", prompt=f"要使用图片的什么操作呢?{method_str}") @@ -138,96 +175,110 @@ async def _(bot: Bot, event: MessageEvent, state: T_State): @update_img.got("y", prompt="[长度? 0 0 底色?]") @update_img.got("imgs", prompt="图呢图呢图呢图呢?GKD!") async def _(bot: Bot, event: MessageEvent, state: T_State): - method = state['method'] - x = state['x'] if state['x'] else '' - y = state['y'] if state['y'] else '' - img_list = state['imgs'] + method = state["method"] + x = state["x"] if state["x"] else "" + y = state["y"] if state["y"] else "" + img_list = state["imgs"] if is_number(x): x = float(x) if is_number(y): y = int(y) index = 0 - result = '' + result = "" async with aiohttp.ClientSession() as session: for img_url in img_list: async with session.get(img_url, timeout=7) as response: if response.status == 200: - async with aiofiles.open(IMAGE_PATH + f'temp/{event.user_id}_{index}_update.png', 'wb') as f: + async with aiofiles.open( + IMAGE_PATH + f"temp/{event.user_id}_{index}_update.png", "wb" + ) as f: await f.write(await response.read()) index += 1 else: - logger.warning(f"USER {event.user_id} GROUP " - f"{event.group_id if event.message_type != 'private' else 'private'} " - f"使用 {method} 时下载图片超时") + logger.warning( + f"USER {event.user_id} GROUP " + f"{event.group_id if event.message_type != 'private' else 'private'} " + f"使用 {method} 时下载图片超时" + ) await update_img.finish("获取图片超时了...", at_sender=True) if index == 0: return - if method in ['修改尺寸', '1']: + if method in ["修改尺寸", "1"]: for i in range(index): - img = Image.open(IMAGE_PATH + f'temp/{event.user_id}_{i}_update.png') - img = img.convert('RGB') + img = Image.open(IMAGE_PATH + f"temp/{event.user_id}_{i}_update.png") + img = img.convert("RGB") img = img.resize((int(x), int(y)), Image.ANTIALIAS) result += image(b64=pic2b64(img)) await update_img.finish(result, at_sender=True) - if method in ['等比压缩', '2']: + if method in ["等比压缩", "2"]: for i in range(index): - img = Image.open(IMAGE_PATH + f'temp/{event.user_id}_{i}_update.png') + img = Image.open(IMAGE_PATH + f"temp/{event.user_id}_{i}_update.png") width, height = img.size - img = img.convert('RGB') + img = img.convert("RGB") if width * x < 8000 and height * x < 8000: img = img.resize((int(x * width), int(x * height))) result += image(b64=pic2b64(img)) else: - await update_img.finish('小真寻不支持图片压缩后宽或高大于8000的存在!!') - if method in ['旋转图片', '3']: + await update_img.finish("小真寻不支持图片压缩后宽或高大于8000的存在!!") + if method in ["旋转图片", "3"]: for i in range(index): - img = Image.open(IMAGE_PATH + f'temp/{event.user_id}_{i}_update.png') + img = Image.open(IMAGE_PATH + f"temp/{event.user_id}_{i}_update.png") img = img.rotate(x) result += image(b64=pic2b64(img)) - if method in ['水平翻转', '4']: + if method in ["水平翻转", "4"]: for i in range(index): - img = Image.open(IMAGE_PATH + f'temp/{event.user_id}_{i}_update.png') + img = Image.open(IMAGE_PATH + f"temp/{event.user_id}_{i}_update.png") img = img.transpose(Image.FLIP_LEFT_RIGHT) result += image(b64=pic2b64(img)) - if method in ['铅笔滤镜', '5']: + if method in ["铅笔滤镜", "5"]: for i in range(index): - img = Image.open(IMAGE_PATH + f'temp/{event.user_id}_{i}_update.png').filter(ImageFilter.CONTOUR) + img = Image.open( + IMAGE_PATH + f"temp/{event.user_id}_{i}_update.png" + ).filter(ImageFilter.CONTOUR) result += image(b64=pic2b64(img)) - if method in ['模糊效果', '6']: + if method in ["模糊效果", "6"]: for i in range(index): - img = Image.open(IMAGE_PATH + f'temp/{event.user_id}_{i}_update.png').filter(ImageFilter.BLUR) + img = Image.open( + IMAGE_PATH + f"temp/{event.user_id}_{i}_update.png" + ).filter(ImageFilter.BLUR) result += image(b64=pic2b64(img)) - if method in ['锐化效果', '7']: + if method in ["锐化效果", "7"]: for i in range(index): - img = Image.open(IMAGE_PATH + f'temp/{event.user_id}_{i}_update.png').filter(ImageFilter.EDGE_ENHANCE) + img = Image.open( + IMAGE_PATH + f"temp/{event.user_id}_{i}_update.png" + ).filter(ImageFilter.EDGE_ENHANCE) result += image(b64=pic2b64(img)) - if method in ['高斯模糊', '8']: + if method in ["高斯模糊", "8"]: for i in range(index): - img = Image.open(IMAGE_PATH + f'temp/{event.user_id}_{i}_update.png').filter(ImageFilter.GaussianBlur) + img = Image.open( + IMAGE_PATH + f"temp/{event.user_id}_{i}_update.png" + ).filter(ImageFilter.GaussianBlur) result += image(b64=pic2b64(img)) - if method in ['边缘检测', '9']: + if method in ["边缘检测", "9"]: for i in range(index): - img = Image.open(IMAGE_PATH + f'temp/{event.user_id}_{i}_update.png').filter(ImageFilter.FIND_EDGES) + img = Image.open( + IMAGE_PATH + f"temp/{event.user_id}_{i}_update.png" + ).filter(ImageFilter.FIND_EDGES) result += image(b64=pic2b64(img)) - if method in ['底色替换', '10']: - if x in ['蓝色', '蓝']: + if method in ["底色替换", "10"]: + if x in ["蓝色", "蓝"]: lower = np.array([90, 70, 70]) upper = np.array([110, 255, 255]) - if x in ['红色', '红']: + if x in ["红色", "红"]: lower = np.array([0, 135, 135]) upper = np.array([180, 245, 230]) - if y in ['蓝色', '蓝']: + if y in ["蓝色", "蓝"]: color = (255, 0, 0) - if y in ['红色', '红']: + if y in ["红色", "红"]: color = (0, 0, 255) - if y in ['白色', '白']: + if y in ["白色", "白"]: color = (255, 255, 255) - if y in ['绿色', '绿']: + if y in ["绿色", "绿"]: color = (0, 255, 0) - if y in ['黄色', '黄']: + if y in ["黄色", "黄"]: color = (0, 255, 255) for k in range(index): - img = cv2.imread(IMAGE_PATH + f'temp/{event.user_id}_{k}_update.png') + img = cv2.imread(IMAGE_PATH + f"temp/{event.user_id}_{k}_update.png") img = cv2.resize(img, None, fx=0.3, fy=0.3) rows, cols, channels = img.shape hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) @@ -238,13 +289,13 @@ async def _(bot: Bot, event: MessageEvent, state: T_State): for j in range(cols): if dilate[i, j] == 255: img[i, j] = color - cv2.imwrite(IMAGE_PATH + f'temp/{event.user_id}_{k}_ok_update.png', img) + cv2.imwrite(IMAGE_PATH + f"temp/{event.user_id}_{k}_ok_update.png", img) for i in range(index): - result += image(f'{event.user_id}_{i}_ok_update.png', 'temp') + result += image(f"{event.user_id}_{i}_ok_update.png", "temp") if is_number(method): method = method_list[int(method) - 1] logger.info( f"(USER {event.user_id}, GROUP" - f" {event.group_id if event.message_type != 'private' else 'private'}) 使用{method}") + f" {event.group_id if isinstance(event, GroupMessageEvent) else 'private'}) 使用{method}" + ) await update_img.finish(result, at_sender=True) - diff --git a/plugins/update_setu/__init__.py b/plugins/update_setu/__init__.py index 99a26bf1..dfe66696 100644 --- a/plugins/update_setu/__init__.py +++ b/plugins/update_setu/__init__.py @@ -28,17 +28,8 @@ async def _(bot: Bot, event: Event, state: T_State): # 更新色图 @scheduler.scheduled_job( 'cron', - # year=None, - # month=None, - # day=None, - # week=None, - # day_of_week="mon,tue,wed,thu,fri", hour=4, minute=30, - # second=None, - # start_date=None, - # end_date=None, - # timezone=None, ) async def _(): if DOWNLOAD_SETU: diff --git a/plugins/update_setu/data_source.py b/plugins/update_setu/data_source.py index 53b77ca1..eacc304b 100644 --- a/plugins/update_setu/data_source.py +++ b/plugins/update_setu/data_source.py @@ -1,127 +1,160 @@ from configs.path_config import IMAGE_PATH, TXT_PATH -import os -from utils.user_agent import get_user_agent from services.log import logger from datetime import datetime -from utils.img_utils import rar_imgs, get_img_hash +from utils.image_utils import compressed_image, get_img_hash from utils.utils import get_bot, get_local_proxy from asyncio.exceptions import TimeoutError +from models.setu import Setu +from aiohttp.client_exceptions import ClientConnectorError +from asyncpg.exceptions import UniqueViolationError +from pathlib import Path +from nonebot import Driver +import nonebot import aiofiles import aiohttp -from aiohttp.client_exceptions import ClientConnectorError +import os +import ujson as json -try: - import ujson as json -except ModuleNotFoundError: - import json +driver: Driver = nonebot.get_driver() + +_path = Path(IMAGE_PATH) + + +@driver.on_startup +async def update_old_setu_data(): + path = Path(TXT_PATH) + setu_data_file = path / "setu_data.json" + r18_data_file = path / "r18_setu_data.json" + index = 0 + r18_index = 0 + count = 0 + fail_count = 0 + for file in [setu_data_file, r18_data_file]: + if file.exists(): + data = json.load(open(file, "r", encoding="utf8")) + for x in data: + if file == setu_data_file: + idx = index + if 'R-18' in data[x]["tags"]: + data[x]["tags"].remove('R-18') + else: + idx = r18_index + img_url = ( + data[x]["img_url"].replace("i.pixiv.cat", "i.pximg.net") + if "i.pixiv.cat" in data[x]["img_url"] + else data[x]["img_url"] + ) + # idx = r18_index if 'R-18' in data[x]["tags"] else index + try: + await Setu.add_setu_data( + idx, + data[x]["title"], + data[x]["author"], + data[x]["pid"], + data[x]["img_hash"], + img_url, + ",".join(data[x]["tags"]), + ) + count += 1 + if 'R-18' in data[x]["tags"]: + r18_index += 1 + else: + index += 1 + logger.info(f'添加旧色图数据成功 PID:{data[x]["pid"]} index:{idx}....') + except UniqueViolationError: + fail_count += 1 + logger.info(f'添加旧色图数据失败,色图重复 PID:{data[x]["pid"]} index:{idx}....') + file.unlink() + setu_url_path = path / "setu_url.json" + setu_r18_url_path = path / "setu_r18_url.json" + if setu_url_path.exists(): + setu_url_path.unlink() + if setu_r18_url_path.exists(): + setu_r18_url_path.unlink() + logger.info(f"更新旧色图数据完成,成功更新数据:{count} 条,累计失败:{fail_count} 条") + + +headers = { + "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6;" + " rv:2.0.1) Gecko/20100101 Firefox/4.0.1", + "Referer": "https://www.pixiv.net", +} async def update_setu_img(): - async with aiohttp.ClientSession(headers=get_user_agent()) as session: - for file_name in ['setu_url.json', 'setu_r18_url.json']: - if file_name == 'setu_url.json': - json_name = 'setu_data.json' - path = '_setu/' - rar_path = 'setu_rar/' - else: - json_name = 'r18_setu_data.json' - path = '_r18/' - rar_path = 'r18_rar/' - if not os.path.exists(IMAGE_PATH + path): - os.mkdir(IMAGE_PATH + path) - if not os.path.exists(IMAGE_PATH + rar_path): - os.mkdir(IMAGE_PATH + rar_path) - try: - data = json.load(open(TXT_PATH + json_name, encoding='utf8')) - if not data: - data = {} - except (FileNotFoundError, TypeError): - data = {} - _success = 0 - _similar = 0 - try: - with open(TXT_PATH + file_name, 'r', encoding='utf8') as f: - txt_data = json.load(f) - if not txt_data: - continue - except (FileNotFoundError, ValueError): - continue - total = len(txt_data) - urls = [data[x]['img_url'] for x in data.keys()] - for pid in txt_data.keys(): - index = str(len(os.listdir(IMAGE_PATH + path))) - url = txt_data[pid]["img_url"].replace('img-master', 'img-original').replace('_master1200', '') - if url in urls or txt_data[pid]["img_url"] in urls: - continue - logger.info(f'开始更新 index:{index} --> {url}') + image_list = await Setu.get_all_setu() + image_list.reverse() + _success = 0 + error_info = [] + error_type = [] + async with aiohttp.ClientSession(headers=headers) as session: + for image in image_list: + path = _path / "_r18" if image.is_r18 else _path / "_setu" + rar_path = "r18_rar" if image.is_r18 else "rar" + local_image = path / f"{image.local_id}.jpg" + if not local_image.exists(): for _ in range(3): try: - async with session.get(url, proxy=get_local_proxy(), timeout=15) as response: + async with session.get( + image.img_url, proxy=get_local_proxy(), timeout=30 + ) as response: if response.status == 200: - async with aiofiles.open(IMAGE_PATH + rar_path + index + ".jpg", 'wb') as f: + async with aiofiles.open( + f"{IMAGE_PATH}/{rar_path}/{image.local_id}.jpg", + "wb", + ) as f: await f.write(await response.read()) _success += 1 - else: - logger.info(f'{url} 不存在,使用更新原url') - url = txt_data[pid]["img_url"] - async with session.get(txt_data[pid]["img_url"], proxy=get_local_proxy(), - timeout=15) as response: - if response.status == 200: - async with aiofiles.open(IMAGE_PATH + rar_path + index + ".jpg", 'wb') as f: - await f.write(await response.read()) - _success += 1 - try: - if os.path.getsize(IMAGE_PATH + rar_path + str(index) + ".jpg") > 1024 * 1024 * 1.5: - rar_imgs( - rar_path, - path, - in_file_name=index, - out_file_name=index + try: + if ( + os.path.getsize( + f"{IMAGE_PATH}/{rar_path}/{image.local_id}.jpg" + ) + > 1024 * 1024 * 1.5 + ): + compressed_image( + os.path.join( + rar_path, f"{image.local_id}.jpg" + ), + os.path.join(path, f"{image.local_id}.jpg"), + ) + else: + logger.info( + f"不需要压缩,移动图片 {IMAGE_PATH}/{rar_path}/{image.local_id}.jpg " + f"--> /{path}/{image.local_id}.jpg" + ) + os.rename( + f"{IMAGE_PATH}/{rar_path}/{image.local_id}.jpg", + f"{path}/{image.local_id}.jpg", + ) + except FileNotFoundError: + logger.warning(f"文件 {image.local_id}.jpg 不存在,跳过...") + _success -= 1 + continue + img_hash = str( + get_img_hash( + f"{path}/{image.local_id}.jpg" + ) ) - else: - logger.info('不需要压缩,移动图片 ' + IMAGE_PATH + rar_path + index + ".jpg --> " - + IMAGE_PATH + path + index + ".jpg") - os.rename(IMAGE_PATH + rar_path + index + ".jpg", - IMAGE_PATH + path + index + ".jpg") - except FileNotFoundError: - logger.warning(f'文件 {index}.jpg 不存在,跳过...') - _success -= 1 - continue - img_hash = str(get_img_hash(f'{IMAGE_PATH}{path}{index}.jpg')) - if img_hash in [data[x]['img_hash'] for x in data.keys()]: - logger.info(f'index:{index} 与 ' - f'{[data[x]["img_hash"] for x in data.keys()].index(img_hash)} 存在重复,删除') - os.remove(IMAGE_PATH + path + index + ".jpg") - _similar += 1 - else: - data[index] = { - 'title': txt_data[pid]['title'], - 'author': txt_data[pid]['author'], - 'pid': txt_data[pid]['pid'], - 'img_hash': img_hash, - 'img_url': url, - 'tags': txt_data[pid]['tags'], - } - break + await Setu.update_setu_data( + image.pid, img_hash=img_hash + ) + break except (TimeoutError, ClientConnectorError) as e: - logger.warning(f'{url} 更新失败 ..{type(e)}:{e}') - continue + logger.warning(f"{image.local_id}.jpg 更新失败 ..{type(e)}:{e}") + pass except Exception as e: - await get_bot().send_private_msg( - user_id=int(list(get_bot().config.superusers)[0]), - message=f'更新 {index}.jpg 色图错误 {type(e)}: {e}' - ) _success -= 1 - logger.error(f'更新色图 {index}.jpg 错误 {type(e)}: {e}') - continue - with open(TXT_PATH + json_name, 'w', encoding='utf-8') as f: - json.dump(data, f, indent=4, ensure_ascii=False) - open(TXT_PATH + file_name, 'w') - logger.info( - f'{str(datetime.now()).split(".")[0]} 更新 {file_name.split(".")[0]}完成,预计更新 {total} 张,' - f'实际更新 {_success} 张,相似 {_similar} 张,实际存入 {_success - _similar} 张') - await get_bot().send_private_msg( - user_id=int(list(get_bot().config.superusers)[0]), - message=f'{str(datetime.now()).split(".")[0]} 更新{file_name.split(".")[0]}完成,预计更新 {total} 张,' - f'实际更新 {_success} 张,相似 {_similar} 张,实际存入 {_success - _similar} 张' - ) + logger.error(f"更新色图 {image.local_id}.jpg 错误 {type(e)}: {e}") + if type(e) not in error_type: + error_type.append(type(e)) + error_info.append( + f"更新色图 {image.local_id}.jpg 错误 {type(e)}: {e}" + ) + await get_bot().send_private_msg( + user_id=int(list(get_bot().config.superusers)[0]), + message=f'{str(datetime.now()).split(".")[0]} 更新 色图 完成,实际更新 {_success} 张,以下为更新时未知错误:\n' + + "\n".join(error_info), + ) + + diff --git a/plugins/upload_img/__init__.py b/plugins/upload_img/__init__.py index f5b36c13..ab10571a 100644 --- a/plugins/upload_img/__init__.py +++ b/plugins/upload_img/__init__.py @@ -4,54 +4,56 @@ from services.log import logger import os from nonebot.rule import to_me from nonebot.typing import T_State -from nonebot.adapters import Bot, Event +from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent from utils.utils import get_message_imgs, get_message_text import aiohttp import aiofiles from utils.utils import cn2py from configs.config import IMAGE_DIR_LIST -__plugin_name__ = '上传图片' -__plugin_usage__ = '上传图片帮助:\n\t' \ - '1.查看列表 --> 指令: 上传图片 列表/目录\n\t' \ - '2.上传图片 [序号] [图片], 即在相应目录下添加图片\n\t\t示例: 上传图片 1 [图片]' +__plugin_name__ = "上传图片" +__plugin_usage__ = ( + "上传图片帮助:\n\t" + "1.查看列表 --> 指令: 上传图片 列表/目录\n\t" + "2.上传图片 [序号] [图片], 即在相应目录下添加图片\n\t\t示例: 上传图片 1 [图片]" +) upload_img = on_command("上传图片", rule=to_me(), priority=5, block=True) @upload_img.args_parser -async def parse(bot: Bot, event: Event, state: T_State): - if str(event.get_message()) in ['取消', '算了']: +async def parse(bot: Bot, event: MessageEvent, state: T_State): + if str(event.get_message()) in ["取消", "算了"]: await upload_img.finish("已取消操作..", at_sender=True) - if state["_current_key"] in ['path']: + if state["_current_key"] in ["path"]: if str(event.get_message()) not in IMAGE_DIR_LIST: await upload_img.reject("此目录不正确,请重新输入目录!") state[state["_current_key"]] = str(event.get_message()) - if state["_current_key"] in ['imgs']: + if state["_current_key"] in ["imgs"]: if not get_message_imgs(event.json()): await upload_img.reject("图呢图呢图呢图呢!GKD!") - state[state['_current_key']] = get_message_imgs(event.json()) + state[state["_current_key"]] = get_message_imgs(event.json()) @upload_img.handle() -async def _(bot: Bot, event: Event, state: T_State): +async def _(bot: Bot, event: MessageEvent, state: T_State): raw_arg = get_message_text(event.json()) img_list = get_message_imgs(event.json()) if raw_arg: - if str(event.get_message()) in ['帮助']: + if str(event.get_message()) in ["帮助"]: await upload_img.finish(__plugin_usage__) if raw_arg.split("[")[0] in IMAGE_DIR_LIST: - state['path'] = raw_arg.split("[")[0] + state["path"] = raw_arg.split("[")[0] if img_list: - state['imgs'] = img_list + state["imgs"] = img_list @upload_img.got("path", prompt="要将图片上传至什么图库呢?") @upload_img.got("imgs", prompt="图呢图呢图呢图呢!GKD!") -async def _(bot: Bot, event: Event, state: T_State): - path = IMAGE_PATH + cn2py(state['path']) - img_list = state['imgs'] +async def _(bot: Bot, event: MessageEvent, state: T_State): + path = IMAGE_PATH + cn2py(state["path"]) + img_list = state["imgs"] img_id = len(os.listdir(path)) failed_list = [] success_id = "" @@ -60,7 +62,9 @@ async def _(bot: Bot, event: Event, state: T_State): try: async with session.get(img_url, timeout=7) as response: if response.status == 200: - async with aiofiles.open(path + str(img_id) + ".jpg", 'wb') as f: + async with aiofiles.open( + path + str(img_id) + ".jpg", "wb" + ) as f: await f.write(await response.read()) success_id += str(img_id) + "," img_id += 1 @@ -74,14 +78,22 @@ async def _(bot: Bot, event: Event, state: T_State): failed_result = "" for img in failed_list: failed_result += str(img) + "\n" - logger.info(f"USER {event.user_id} GROUP {event.group_id if event.message_type != 'private' else 'private'}" - f" 上传图片至 {state['path']} 共 {len(img_list)} 张,失败 {len(failed_list)} 张,id={success_id[:-1]}") + logger.info( + f"USER {event.user_id} GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'}" + f" 上传图片至 {state['path']} 共 {len(img_list)} 张,失败 {len(failed_list)} 张,id={success_id[:-1]}" + ) if failed_result: - await upload_img.finish(f"这次一共为 {state['path']}库 添加了 {len(img_list) - len(failed_list)} 张图片\n" - f"依次的Id为:{success_id[:-1]}\n" - f"上传失败:{failed_result[:-1]}\n" - f"小真寻感谢您对图库的扩充!WW", at_sender=True) + await upload_img.finish( + f"这次一共为 {state['path']}库 添加了 {len(img_list) - len(failed_list)} 张图片\n" + f"依次的Id为:{success_id[:-1]}\n" + f"上传失败:{failed_result[:-1]}\n" + f"小真寻感谢您对图库的扩充!WW", + at_sender=True, + ) else: - await upload_img.finish(f"这次一共为 {state['path']}库 添加了 {len(img_list)} 张图片\n" - f"依次的Id为:{success_id[:-1]}\n" - f"小真寻感谢您对图库的扩充!WW", at_sender=True) + await upload_img.finish( + f"这次一共为 {state['path']}库 添加了 {len(img_list)} 张图片\n" + f"依次的Id为:{success_id[:-1]}\n" + f"小真寻感谢您对图库的扩充!WW", + at_sender=True, + ) diff --git a/plugins/weather/__init__.py b/plugins/weather/__init__.py index 0b1ed3ce..d5a788ea 100644 --- a/plugins/weather/__init__.py +++ b/plugins/weather/__init__.py @@ -1,6 +1,6 @@ from nonebot import on_regex from .data_source import get_weather_of_city -from nonebot.adapters.cqhttp import Bot, Event +from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent from jieba import posseg from services.log import logger from nonebot.typing import T_State @@ -8,7 +8,7 @@ from .config import city_list import re from utils.utils import get_message_text -__plugin_name__ = '天气查询' +__plugin_name__ = "天气查询" __plugin_usage__ = "普普通通的查天气吧\n示例:北京天气" @@ -16,15 +16,15 @@ weather = on_regex(r".*?(.*)市?的?天气.*?", priority=5, block=True) @weather.handle() -async def _(bot: Bot, event: Event, state: T_State): +async def _(bot: Bot, event: MessageEvent, state: T_State): msg = get_message_text(event.json()) - msg = re.search(r'.*?(.*)市?的?天气.*?', msg) + msg = re.search(r".*?(.*)市?的?天气.*?", msg) msg = msg.group(1) - if msg[-1] == '的': + if msg[-1] == "的": msg = msg[:-1] - if msg[-1] != '市': - msg += '市' - city = '' + if msg[-1] != "市": + msg += "市" + city = "" if msg: citys = [] for x in city_list.keys(): @@ -33,65 +33,20 @@ async def _(bot: Bot, event: Event, state: T_State): for word in posseg.lcut(msg): if word.word in city_list.keys(): await weather.finish("不要查一个省的天气啊,这么大查起来很累人的..", at_sender=True) - if word.flag == 'ns' or word.word[:-1] in citys: + if word.flag == "ns" or word.word[:-1] in citys: city = str(word.word).strip() break - if word.word == '火星': - await weather.finish('没想到你个小呆子还真的想看火星天气!\n火星大气中含有95%的二氧化碳,气压低,加之极度的干燥,' - '就阻止了水的形成积聚。这意味着火星几乎没有云,冰层覆盖了火星的两极,它们的融化和冻结受到火星与太' - '阳远近距离的影响,它产生了强大的尘埃云,阻挡了太阳光,使冰层的融化慢下来。\n所以说火星天气太恶劣了,' - '去过一次就不想再去第二次了') + if word.word == "火星": + await weather.finish( + "没想到你个小呆子还真的想看火星天气!\n火星大气中含有95%的二氧化碳,气压低,加之极度的干燥," + "就阻止了水的形成积聚。这意味着火星几乎没有云,冰层覆盖了火星的两极,它们的融化和冻结受到火星与太" + "阳远近距离的影响,它产生了强大的尘埃云,阻挡了太阳光,使冰层的融化慢下来。\n所以说火星天气太恶劣了," + "去过一次就不想再去第二次了" + ) if city: city_weather = await get_weather_of_city(city) - logger.info(f'(USER {event.user_id}, GROUP {event.group_id if event.message_type != "private" else "private"} ) ' - f'查询天气:' + city) + logger.info( + f'(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else "private"} ) ' + f"查询天气:" + city + ) await weather.finish(city_weather) - # if str(event.get_message()).strip() == '天气': - # state['city'] = '-1' - # return - # msg = str(event.get_message()).strip() - # print(msg) - # flag = True - # if msg.find('开启') != -1 or msg.find('关闭') != -1: - # state['city'] = '-1' - # return - # if msg.startswith('天气'): - # if not msg.endswith('市'): - # state['city'] = msg[2:] + '市' - # else: - # state['city'] = msg[2:] - # flag = False - # elif msg.find('的天气') != -1: - # i = msg.find('的天气') - # msg = msg[:i] + '市' + msg[i:] - # elif msg.find('天气') != -1: - # i = msg.find('天气') - # msg = msg[:i] + '市' + msg[i:] - # if msg != "天气" and flag: - # print(posseg.lcut(msg)) - # for word in posseg.lcut(msg): - # if word.word in city_list.keys():# and word.word in ['北京', '上海', '天津', '重庆', '台湾']: - # await weather.finish("不要查一个省的天气啊,这么大查起来很累人的..", at_sender=True) - # if word.flag == 'ns': - # state["city"] = word.word - # break - # if word.word == '火星': - # await weather.finish('没想到你个小呆子还真的想看火星天气!\n火星大气中含有95%的二氧化碳,气压低,加之极度的干燥,' - # '就阻止了水的形成积聚。这意味着火星几乎没有云,冰层覆盖了火星的两极,它们的融化和冻结受到火星与太' - # '阳远近距离的影响,它产生了强大的尘埃云,阻挡了太阳光,使冰层的融化慢下来。\n所以说火星天气太恶劣了,' - # '去过一次就不想再去第二次了') - - -# @weather.got("city", prompt="你想查询哪个城市的天气呢?") -# async def _(bot: Bot, event: Event, state: dict): -# if state['city'] == '-1' or not state['city'].strip(): -# return -# if state['city'] in ['取消', '算了']: -# await weather.finish('已取消此次调用..') -# if state['city'][-1] != '市': -# state['city'] += '市' -# city_weather = await get_weather_of_city(state['city'].strip()) -# logger.info(f'(USER {event.user_id}, GROUP {event.group_id if event.message_type != "private" else "private"} ) ' -# f'查询天气:' + state['city']) -# await weather.finish(city_weather) - diff --git a/plugins/weather/config.py b/plugins/weather/config.py index 6aee7603..48efab72 100644 --- a/plugins/weather/config.py +++ b/plugins/weather/config.py @@ -1,34 +1,327 @@ city_list = { - "北京": ["北京"], - "天津": ["天津"], - "山西": ["太原", "阳泉", "晋城", "长治", "临汾", "运城", "忻州", "吕梁", "晋中", "大同", "朔州"], - "河北": ["沧州", "石家庄", "唐山", "保定", "廊坊", "衡水", "邯郸", "邢台", "张家口", "辛集", "秦皇岛", "定州", "承德", "涿州"], - "山东": ["济南", "淄博", "聊城", "德州", "滨州", "济宁", "菏泽", "枣庄", "烟台", "威海", "泰安", "青岛", "临沂", "莱芜", "东营", "潍坊", "日照"], - "河南": ["郑州", "新乡", "鹤壁", "安阳", "焦作", "濮阳", "开封", "驻马店", "商丘", "三门峡", "南阳", "洛阳", "周口", "许昌", "信阳", "漯河", "平顶山", "济源"], - "广东": ["珠海", "中山", "肇庆", "深圳", "清远", "揭阳", "江门", "惠州", "河源", "广州", "佛山", "东莞", "潮州", "汕尾", "梅州", "阳江", "云浮", "韶关", "湛江", "汕头", "茂名"], - "浙江": ["舟山", "温州", "台州", "绍兴", "衢州", "宁波", "丽水", "金华", "嘉兴", "湖州", "杭州"], - "宁夏": ["中卫", "银川", "吴忠", "石嘴山", "固原"], - "江苏": ["镇江", "扬州", "盐城", "徐州", "宿迁", "无锡", "苏州", "南通", "南京", "连云港", "淮安", "常州", "泰州"], - "湖南": ["长沙", "邵阳", "怀化", "株洲", "张家界", "永州", "益阳", "湘西", "娄底", "衡阳", "郴州", "岳阳", "常德", "湘潭"], - "吉林": ["长春", "长春", "通化", "松原", "四平", "辽源", "吉林", "延边", "白山", "白城"], - "福建": ["漳州", "厦门", "福州", "三明", "莆田", "宁德", "南平", "龙岩", "泉州"], - "甘肃": ["张掖", "陇南", "兰州", "嘉峪关", "白银", "武威", "天水", "庆阳", "平凉", "临夏", "酒泉", "金昌", "甘南", "定西"], - "陕西": ["榆林", "西安", "延安", "咸阳", "渭南", "铜川", "商洛", "汉中", "宝鸡", "安康"], - "辽宁": ["营口", "铁岭", "沈阳", "盘锦", "辽阳", "锦州", "葫芦岛", "阜新", "抚顺", "丹东", "大连", "朝阳", "本溪", "鞍山"], - "江西": ["鹰潭", "宜春", "上饶", "萍乡", "南昌", "景德镇", "吉安", "抚州", "新余", "九江", "赣州"], - "黑龙江": ["伊春", "七台河", "牡丹江", "鸡西", "黑河", "鹤岗", "哈尔滨", "大兴安岭", "绥化", "双鸭山", "齐齐哈尔", "佳木斯", "大庆"], - "安徽": ["宣城", "铜陵", "六安", "黄山", "淮南", "合肥", "阜阳", "亳州", "安庆", "池州", "宿州", "芜湖", "马鞍山", "淮北", "滁州", "蚌埠"], - "湖北": ["孝感", "武汉", "十堰", "荆门", "黄冈", "襄阳", "咸宁", "随州", "黄石", "恩施", "鄂州", "荆州", "宜昌", "潜江", "天门", "神农架", "仙桃"], - "青海": ["西宁", "海西", "海东", "玉树", "黄南", "海南", "海北", "果洛"], - "新疆": ["乌鲁木齐", "克州", "阿勒泰", "五家渠", "石河子", "伊犁", "吐鲁番", "塔城", "克拉玛依", "喀什", "和田", "哈密", "昌吉", "博尔塔拉", "阿克苏", "巴音郭楞", "阿拉尔", "图木舒克", "铁门关"], - "贵州": ["铜仁", "黔东南", "贵阳", "安顺", "遵义", "黔西南", "黔南", "六盘水", "毕节"], - "四川": ["遂宁", "攀枝花", "眉山", "凉山", "成都", "巴中", "广安", "自贡", "甘孜", "资阳", "宜宾", "雅安", "内江", "南充", "绵阳", "泸州", "凉山", "乐山", "广元", "甘孜", "德阳", "达州", "阿坝"], - "上海": ["上海"], - "广西": ["南宁", "贵港", "玉林", "梧州", "钦州", "柳州", "来宾", "贺州", "河池", "桂林", "防城港", "崇左", "北海", "百色"], - "西藏": ["拉萨", "山南", "日喀则", "那曲", "林芝", "昌都", "阿里"], - "云南": ["昆明", "红河", "大理", "玉溪", "昭通", "西双版纳", "文山", "曲靖", "普洱", "怒江", "临沧", "丽江", "红河", "迪庆", "德宏", "大理", "楚雄", "保山"], - "内蒙古": ["呼和浩特", "乌兰察布", "兴安", "赤峰", "呼伦贝尔", "锡林郭勒", "乌海", "通辽", "巴彦淖尔", "阿拉善", "鄂尔多斯", "包头"], - "海南": ["海口", "三沙", "三亚", "临高", "五指山", "陵水", "文昌", "万宁", "白沙", "乐东", "澄迈", "屯昌", "定安", "东方", "保亭", "琼中", "琼海", "儋州", "昌江"], - "重庆": ["重庆"], - "台湾": ["台北", "高雄", "基隆", "台中", "台南", "新竹", "嘉义", "新北", "桃园"] -} \ No newline at end of file + "北京": ["北京"], + "天津": ["天津"], + "山西": ["太原", "阳泉", "晋城", "长治", "临汾", "运城", "忻州", "吕梁", "晋中", "大同", "朔州"], + "河北": [ + "沧州", + "石家庄", + "唐山", + "保定", + "廊坊", + "衡水", + "邯郸", + "邢台", + "张家口", + "辛集", + "秦皇岛", + "定州", + "承德", + "涿州", + ], + "山东": [ + "济南", + "淄博", + "聊城", + "德州", + "滨州", + "济宁", + "菏泽", + "枣庄", + "烟台", + "威海", + "泰安", + "青岛", + "临沂", + "莱芜", + "东营", + "潍坊", + "日照", + ], + "河南": [ + "郑州", + "新乡", + "鹤壁", + "安阳", + "焦作", + "濮阳", + "开封", + "驻马店", + "商丘", + "三门峡", + "南阳", + "洛阳", + "周口", + "许昌", + "信阳", + "漯河", + "平顶山", + "济源", + ], + "广东": [ + "珠海", + "中山", + "肇庆", + "深圳", + "清远", + "揭阳", + "江门", + "惠州", + "河源", + "广州", + "佛山", + "东莞", + "潮州", + "汕尾", + "梅州", + "阳江", + "云浮", + "韶关", + "湛江", + "汕头", + "茂名", + ], + "浙江": ["舟山", "温州", "台州", "绍兴", "衢州", "宁波", "丽水", "金华", "嘉兴", "湖州", "杭州"], + "宁夏": ["中卫", "银川", "吴忠", "石嘴山", "固原"], + "江苏": [ + "镇江", + "扬州", + "盐城", + "徐州", + "宿迁", + "无锡", + "苏州", + "南通", + "南京", + "连云港", + "淮安", + "常州", + "泰州", + ], + "湖南": [ + "长沙", + "邵阳", + "怀化", + "株洲", + "张家界", + "永州", + "益阳", + "湘西", + "娄底", + "衡阳", + "郴州", + "岳阳", + "常德", + "湘潭", + ], + "吉林": ["长春", "长春", "通化", "松原", "四平", "辽源", "吉林", "延边", "白山", "白城"], + "福建": ["漳州", "厦门", "福州", "三明", "莆田", "宁德", "南平", "龙岩", "泉州"], + "甘肃": [ + "张掖", + "陇南", + "兰州", + "嘉峪关", + "白银", + "武威", + "天水", + "庆阳", + "平凉", + "临夏", + "酒泉", + "金昌", + "甘南", + "定西", + ], + "陕西": ["榆林", "西安", "延安", "咸阳", "渭南", "铜川", "商洛", "汉中", "宝鸡", "安康"], + "辽宁": [ + "营口", + "铁岭", + "沈阳", + "盘锦", + "辽阳", + "锦州", + "葫芦岛", + "阜新", + "抚顺", + "丹东", + "大连", + "朝阳", + "本溪", + "鞍山", + ], + "江西": ["鹰潭", "宜春", "上饶", "萍乡", "南昌", "景德镇", "吉安", "抚州", "新余", "九江", "赣州"], + "黑龙江": [ + "伊春", + "七台河", + "牡丹江", + "鸡西", + "黑河", + "鹤岗", + "哈尔滨", + "大兴安岭", + "绥化", + "双鸭山", + "齐齐哈尔", + "佳木斯", + "大庆", + ], + "安徽": [ + "宣城", + "铜陵", + "六安", + "黄山", + "淮南", + "合肥", + "阜阳", + "亳州", + "安庆", + "池州", + "宿州", + "芜湖", + "马鞍山", + "淮北", + "滁州", + "蚌埠", + ], + "湖北": [ + "孝感", + "武汉", + "十堰", + "荆门", + "黄冈", + "襄阳", + "咸宁", + "随州", + "黄石", + "恩施", + "鄂州", + "荆州", + "宜昌", + "潜江", + "天门", + "神农架", + "仙桃", + ], + "青海": ["西宁", "海西", "海东", "玉树", "黄南", "海南", "海北", "果洛"], + "新疆": [ + "乌鲁木齐", + "克州", + "阿勒泰", + "五家渠", + "石河子", + "伊犁", + "吐鲁番", + "塔城", + "克拉玛依", + "喀什", + "和田", + "哈密", + "昌吉", + "博尔塔拉", + "阿克苏", + "巴音郭楞", + "阿拉尔", + "图木舒克", + "铁门关", + ], + "贵州": ["铜仁", "黔东南", "贵阳", "安顺", "遵义", "黔西南", "黔南", "六盘水", "毕节"], + "四川": [ + "遂宁", + "攀枝花", + "眉山", + "凉山", + "成都", + "巴中", + "广安", + "自贡", + "甘孜", + "资阳", + "宜宾", + "雅安", + "内江", + "南充", + "绵阳", + "泸州", + "凉山", + "乐山", + "广元", + "甘孜", + "德阳", + "达州", + "阿坝", + ], + "上海": ["上海"], + "广西": [ + "南宁", + "贵港", + "玉林", + "梧州", + "钦州", + "柳州", + "来宾", + "贺州", + "河池", + "桂林", + "防城港", + "崇左", + "北海", + "百色", + ], + "西藏": ["拉萨", "山南", "日喀则", "那曲", "林芝", "昌都", "阿里"], + "云南": [ + "昆明", + "红河", + "大理", + "玉溪", + "昭通", + "西双版纳", + "文山", + "曲靖", + "普洱", + "怒江", + "临沧", + "丽江", + "红河", + "迪庆", + "德宏", + "大理", + "楚雄", + "保山", + ], + "内蒙古": [ + "呼和浩特", + "乌兰察布", + "兴安", + "赤峰", + "呼伦贝尔", + "锡林郭勒", + "乌海", + "通辽", + "巴彦淖尔", + "阿拉善", + "鄂尔多斯", + "包头", + ], + "海南": [ + "海口", + "三沙", + "三亚", + "临高", + "五指山", + "陵水", + "文昌", + "万宁", + "白沙", + "乐东", + "澄迈", + "屯昌", + "定安", + "东方", + "保亭", + "琼中", + "琼海", + "儋州", + "昌江", + ], + "重庆": ["重庆"], + "台湾": ["台北", "高雄", "基隆", "台中", "台南", "新竹", "嘉义", "新北", "桃园"], +} diff --git a/plugins/weather/data_source.py b/plugins/weather/data_source.py index 57f19871..040b4014 100644 --- a/plugins/weather/data_source.py +++ b/plugins/weather/data_source.py @@ -1,21 +1,21 @@ import requests -from utils.init_result import image +from utils.message_builder import image async def get_weather_of_city(city) -> str: - url = 'http://wthrcdn.etouch.cn/weather_mini?city=' + city + url = "http://wthrcdn.etouch.cn/weather_mini?city=" + city data_json = requests.get(url).json() - if 'desc' in data_json: - if data_json['desc'] == "invilad-citykey": + if "desc" in data_json: + if data_json["desc"] == "invilad-citykey": return "你为啥不查火星的天气呢?小真寻只支持国内天气查询!!" + image("shengqi", "zhenxun") - elif data_json['desc'] == "OK": - w_type = data_json['data']['forecast'][0]['type'] - w_max = data_json['data']['forecast'][0]['high'][3:] - w_min = data_json['data']['forecast'][0]['low'][3:] - fengli = data_json['data']['forecast'][0]['fengli'][9:-3] - ganmao = data_json['data']["ganmao"] - fengxiang = data_json['data']['forecast'][0]['fengxiang'] - repass = f'{city}的天气是 {w_type} 天\n最高温度: {w_max}\n最低温度: {w_min}\n风力: {fengli} {fengxiang}\n{ganmao}' + elif data_json["desc"] == "OK": + w_type = data_json["data"]["forecast"][0]["type"] + w_max = data_json["data"]["forecast"][0]["high"][3:] + w_min = data_json["data"]["forecast"][0]["low"][3:] + fengli = data_json["data"]["forecast"][0]["fengli"][9:-3] + ganmao = data_json["data"]["ganmao"] + fengxiang = data_json["data"]["forecast"][0]["fengxiang"] + repass = f"{city}的天气是 {w_type} 天\n最高温度: {w_max}\n最低温度: {w_min}\n风力: {fengli} {fengxiang}\n{ganmao}" return repass else: - return '好像出错了?' + return "好像出错了?" diff --git a/plugins/what_anime/__init__.py b/plugins/what_anime/__init__.py index 95fda9d7..2fbec594 100644 --- a/plugins/what_anime/__init__.py +++ b/plugins/what_anime/__init__.py @@ -1,14 +1,13 @@ - from .data_source import get_anime from nonebot import on_command from nonebot.typing import T_State -from nonebot.adapters import Bot, Event +from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent from utils.utils import get_message_imgs from services.log import logger from utils.utils import UserExistLimiter -__plugin_name__ = '识番' +__plugin_name__ = "识番" __plugin_usage__ = r""" 以图识番 识番 [图片] @@ -16,61 +15,47 @@ __plugin_usage__ = r""" _ulmt = UserExistLimiter() -what_anime = on_command('识番', priority=5, block=True) +what_anime = on_command("识番", priority=5, block=True) @what_anime.args_parser -async def _(bot: Bot, event: Event, state: T_State): - if str(event.get_message()) in ['取消', '算了']: +async def _(bot: Bot, event: MessageEvent, state: T_State): + if str(event.get_message()) in ["取消", "算了"]: await what_anime.finish("已取消操作..", at_sender=True) img_url = get_message_imgs(event.json()) if not img_url: - await what_anime.reject(prompt='图呢图呢图呢图呢GKD', at_sender=True) - state['img_url'] = img_url + await what_anime.reject(prompt="图呢图呢图呢图呢GKD", at_sender=True) + state["img_url"] = img_url @what_anime.handle() -async def _(bot: Bot, event: Event, state: T_State): - if str(event.get_message()) in ['帮助']: +async def _(bot: Bot, event: MessageEvent, state: T_State): + if str(event.get_message()) in ["帮助"]: await what_anime.finish(__plugin_usage__) if _ulmt.check(event.user_id): - await what_anime.finish('您有识番任务正在进行,请稍等...', at_sender=True) + await what_anime.finish("您有识番任务正在进行,请稍等...", at_sender=True) img_url = get_message_imgs(event.json()) if img_url: - state['img_url'] = img_url + state["img_url"] = img_url -@what_anime.got('img_url', prompt='虚空识番?来图来图GKD') -async def _(bot: Bot, event: Event, state: T_State): - img_url = state['img_url'][0] +@what_anime.got("img_url", prompt="虚空识番?来图来图GKD") +async def _(bot: Bot, event: MessageEvent, state: T_State): + img_url = state["img_url"][0] _ulmt.set_True(event.user_id) - await what_anime.send('开始识别.....') + await what_anime.send("开始识别.....") anime_data_report = await get_anime(img_url) if anime_data_report: await what_anime.send(anime_data_report, at_sender=True) - logger.info(f"USER {event.user_id} GROUP "f"{event.group_id if event.message_type != 'private' else 'private'}" - f" 识番 {img_url} --> {anime_data_report}") + logger.info( + f"USER {event.user_id} GROUP " + f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'}" + f" 识番 {img_url} --> {anime_data_report}" + ) else: - logger.info(f"USER {event.user_id} GROUP " - f"{event.group_id if event.message_type != 'private' else 'private'} 识番 {img_url} 未找到!!") + logger.info( + f"USER {event.user_id} GROUP " + f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'} 识番 {img_url} 未找到!!" + ) await what_anime.send(f"没有寻找到该番剧,果咩..", at_sender=True) _ulmt.set_False(event.user_id) - -# @whatanime.args_parser -# async def _(session: CommandSession): -# image_arg = session.current_arg_images -# -# if session.is_first_run: -# if image_arg: -# session.state['whatanime'] = image_arg[0] -# return -# -# if not image_arg: -# session.pause('没图说个J*,GKD!') -# -# session.state[session.current_key] = image_arg -# -# @on_natural_language(keywords={'whatanime', '识番', '識番'}, permission=get_bot().level) -# async def _(session: NLPSession): -# msg = session.msg -# return IntentCommand(90.0, 'whatanime', current_arg=msg or '') \ No newline at end of file diff --git a/plugins/what_anime/data_source.py b/plugins/what_anime/data_source.py index e417544c..d80b65a7 100644 --- a/plugins/what_anime/data_source.py +++ b/plugins/what_anime/data_source.py @@ -1,4 +1,3 @@ - import time from services.log import logger from utils.langconv import * @@ -8,43 +7,47 @@ from utils.user_agent import get_user_agent async def get_anime(anime: str) -> str: s_time = time.time() - url = 'https://api.trace.moe/search?anilistInfo&url={}'.format(anime) + url = "https://api.trace.moe/search?anilistInfo&url={}".format(anime) logger.debug("[info]Now starting get the {}".format(url)) try: async with aiohttp.ClientSession(headers=get_user_agent()) as session: async with session.get(url, timeout=45) as response: if response.status == 200: anime_json = await response.json() - if not anime_json['error']: - if anime_json == 'Error reading imagenull': + if not anime_json["error"]: + if anime_json == "Error reading imagenull": return "图像源错误,注意必须是静态图片哦" repass = "" # 拿到动漫 中文名 for anime in anime_json["result"][:5]: - synonyms = anime['anilist']['synonyms'] + synonyms = anime["anilist"]["synonyms"] for x in synonyms: _count_ch = 0 for word in x: - if '\u4e00' <= word <= '\u9fff': + if "\u4e00" <= word <= "\u9fff": _count_ch += 1 if _count_ch > 3: anime_name = x break else: - anime_name = anime['anilist']['title']['native'] + anime_name = anime["anilist"]["title"]["native"] episode = anime["episode"] from_ = int(anime["from"]) m, s = divmod(from_, 60) similarity = anime["similarity"] - putline = "[ {} ][{}][{}:{}] 相似度:{:.2%}". \ - format(Converter("zh-hans").convert(anime_name), - episode if episode else '?', m, s, similarity) - repass += putline + '\n' - return f'耗时 {int(time.time() - s_time)} 秒\n' + repass[:-1] + putline = "[ {} ][{}][{}:{}] 相似度:{:.2%}".format( + Converter("zh-hans").convert(anime_name), + episode if episode else "?", + m, + s, + similarity, + ) + repass += putline + "\n" + return f"耗时 {int(time.time() - s_time)} 秒\n" + repass[:-1] else: return f'访问错误 error:{anime_json["error"]}' else: - return f'访问失败,请再试一次吧, status: {response.status}' + return f"访问失败,请再试一次吧, status: {response.status}" except Exception as e: - logger.error(f'识番发生错误 e:{e}') - return '发生了奇怪的错误,那就没办法了,再试一次?' + logger.error(f"识番发生错误 e:{e}") + return "发生了奇怪的错误,那就没办法了,再试一次?" diff --git a/plugins/white2black_img.py b/plugins/white2black_img.py index b33e5972..f56d304e 100644 --- a/plugins/white2black_img.py +++ b/plugins/white2black_img.py @@ -1,12 +1,12 @@ from nonebot.typing import T_State -from nonebot.adapters.cqhttp import Bot, MessageEvent +from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent from nonebot import on_command from utils.utils import get_message_imgs, get_local_proxy, get_message_text, is_Chinese -from utils.init_result import image +from utils.message_builder import image import aiohttp import aiofiles from configs.path_config import IMAGE_PATH -from utils.img_utils import CreateImg +from utils.image_utils import CreateImg from utils.user_agent import get_user_agent from services.log import logger @@ -23,11 +23,11 @@ from services.log import logger # RU2ZH_CN 俄语 » 中文 # SP2ZH_CN 西语 » 中文 -__plugin_name__ = '黑白草图' +__plugin_name__ = "黑白草图" -__plugin_usage__ = '用法: \n\t黑白图 [文字] [图片]\n示例:黑白草图 没有人不喜欢萝莉 [图片]' +__plugin_usage__ = "用法: \n\t黑白图 [文字] [图片]\n示例:黑白草图 没有人不喜欢萝莉 [图片]" -w2b_img = on_command('黑白草图', aliases={'黑白图'}, priority=5, block=True) +w2b_img = on_command("黑白草图", aliases={"黑白图"}, priority=5, block=True) @w2b_img.handle() @@ -40,22 +40,24 @@ async def _(bot: Bot, event: MessageEvent, state: T_State): img = img[0] async with aiohttp.ClientSession(headers=get_user_agent()) as session: async with session.get(img, proxy=get_local_proxy()) as response: - async with aiofiles.open(IMAGE_PATH + f'temp/{event.user_id}_w2b.png', 'wb') as f: + async with aiofiles.open( + IMAGE_PATH + f"temp/{event.user_id}_w2b.png", "wb" + ) as f: await f.write(await response.read()) msg = await get_translate(msg) - w2b = CreateImg(0, 0, background=IMAGE_PATH + f'temp/{event.user_id}_w2b.png') - w2b.convert('L') - msg_sp = msg.split('<|>') + w2b = CreateImg(0, 0, background=IMAGE_PATH + f"temp/{event.user_id}_w2b.png") + w2b.convert("L") + msg_sp = msg.split("<|>") w, h = w2b.size add_h, font_size = init_h_font_size(h) - bg = CreateImg(w, h + add_h, color='black', font_size=font_size) + bg = CreateImg(w, h + add_h, color="black", font_size=font_size) bg.paste(w2b) chinese_msg = formalization_msg(msg) if not bg.check_font_size(chinese_msg): if len(msg_sp) == 1: centered_text(bg, chinese_msg, add_h) else: - centered_text(bg, chinese_msg + '<|>' + msg_sp[1], add_h) + centered_text(bg, chinese_msg + "<|>" + msg_sp[1], add_h) elif not bg.check_font_size(msg_sp[0]): centered_text(bg, msg, add_h) else: @@ -65,14 +67,15 @@ async def _(bot: Bot, event: MessageEvent, state: T_State): centered_text(bg, msg, add_h) await w2b_img.send(image(b64=bg.pic2bs4())) logger.info( - f"(USER {event.user_id}, GROUP {event.group_id if event.message_type != 'private' else 'private'})" - f" 制作黑白草图 {msg}") + f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" + f" 制作黑白草图 {msg}" + ) def centered_text(img: CreateImg, text: str, add_h: int): top_h = img.h - add_h + (img.h / 100) bottom_h = img.h - (img.h / 100) - text_sp = text.split('<|>') + text_sp = text.split("<|>") w, h = img.getsize(text_sp[0]) if len(text_sp) == 1: w = (img.w - w) / 2 @@ -90,38 +93,38 @@ def centered_text(img: CreateImg, text: str, add_h: int): async def get_translate(msg: str) -> str: - url = f'http://fanyi.youdao.com/translate?smartresult=dict&smartresult=rule&smartresult=ugc&sessionFrom=null' + url = f"http://fanyi.youdao.com/translate?smartresult=dict&smartresult=rule&smartresult=ugc&sessionFrom=null" data = { - 'type': "ZH_CN2JA", - 'i': msg, + "type": "ZH_CN2JA", + "i": msg, "doctype": "json", "version": "2.1", "keyfrom": "fanyi.web", "ue": "UTF-8", "action": "FY_BY_CLICKBUTTON", - "typoResult": "true" + "typoResult": "true", } async with aiohttp.ClientSession(headers=get_user_agent()) as session: try: async with session.post(url, data=data, proxy=get_local_proxy()) as res: data = await res.json() - if data['errorCode'] == 0: - translate = data['translateResult'][0][0]['tgt'] - msg += '<|>' + translate + if data["errorCode"] == 0: + translate = data["translateResult"][0][0]["tgt"] + msg += "<|>" + translate except Exception as e: - logger.warning(f'黑白草图翻译出错 e:{e}') + logger.warning(f"黑白草图翻译出错 e:{e}") return msg def formalization_msg(msg: str) -> str: - rst = '' + rst = "" for i in range(len(msg)): if is_Chinese(msg[i]): - rst += msg[i] + ' ' + rst += msg[i] + " " else: rst += msg[i] if i + 1 < len(msg) and is_Chinese(msg[i + 1]) and msg[i].isalpha(): - rst += ' ' + rst += " " return rst @@ -132,4 +135,3 @@ def init_h_font_size(h): elif 400 < h < 800: return init_h_font_size(800) return h * 0.2, h * 0.05 - diff --git a/plugins/yiqing/__init__.py b/plugins/yiqing/__init__.py index 5b8a3db4..17a5145c 100644 --- a/plugins/yiqing/__init__.py +++ b/plugins/yiqing/__init__.py @@ -1,27 +1,27 @@ from nonebot import on_command from .data_source import get_yiqing_data, clear_data from services.log import logger -from nonebot.adapters.cqhttp import Bot, Event +from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent from nonebot.typing import T_State from .config import city_list from utils.utils import scheduler -__plugin_name__ = '疫情查询' -__plugin_usage__ = '查询疫情帮助:\n\t对我说 查询疫情 省份/城市,我会回复疫情的实时数据\n\t示例: 查询疫情 温州' +__plugin_name__ = "疫情查询" +__plugin_usage__ = "查询疫情帮助:\n\t对我说 查询疫情 省份/城市,我会回复疫情的实时数据\n\t示例: 查询疫情 温州" yiqing = on_command("疫情", aliases={"查询疫情", "疫情查询"}, priority=5, block=True) @yiqing.handle() -async def _(bot: Bot, event: Event, state: T_State): - if str(event.get_message()).strip() in ['帮助']: - await yiqing.finish(__plugin_usage__) +async def _(bot: Bot, event: MessageEvent, state: T_State): msg = str(event.get_message()).strip() + if not msg or msg in ["帮助"]: + await yiqing.finish(__plugin_usage__) if msg: if msg in city_list.keys(): province = msg - city = '' + city = "" else: for key in city_list.keys(): if msg in city_list.get(key): @@ -35,17 +35,22 @@ async def _(bot: Bot, event: Event, state: T_State): if result: await yiqing.send(result) logger.info( - f"(USER {event.user_id}, GROUP {event.group_id if event.message_type != 'private' else 'private'}) 查询疫情:" + result) + f"(USER {event.user_id}, GROUP " + f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'}) 查询疫情:" + + result + ) else: await yiqing.send("查询失败!!!!", at_sender=True) logger.info( - f"(USER {event.user_id}, GROUP {event.group_id if event.message_type != 'private' else 'private'}) 查询疫情失败") + f"(USER {event.user_id}, GROUP " + f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'}) 查询疫情失败" + ) except UnboundLocalError: - await yiqing.finish('参数正确吗?只要一个参数啊', at_sender=True) + await yiqing.finish("参数正确吗?只要一个参数啊", at_sender=True) @scheduler.scheduled_job( - 'cron', + "cron", hour=0, minute=1, ) diff --git a/plugins/yiqing/data_source.py b/plugins/yiqing/data_source.py index 7c68e28a..33c0db5c 100644 --- a/plugins/yiqing/data_source.py +++ b/plugins/yiqing/data_source.py @@ -10,32 +10,50 @@ from utils.utils import get_local_proxy url = "https://api.yimian.xyz/coro/" -async def get_yiqing_data(province, city_=''): +async def get_yiqing_data(province, city_=""): if not os.path.exists(TXT_PATH + "yiqing/"): os.mkdir(TXT_PATH + "yiqing/") if not os.path.exists(TXT_PATH + "yiqing/" + str(datetime.now().date()) + ".json"): async with aiohttp.ClientSession(headers=get_user_agent()) as session: async with session.get(url, proxy=get_local_proxy(), timeout=7) as response: datalist = await response.json() - with open(TXT_PATH + "yiqing/" + str(datetime.now().date()) + ".json", 'w') as f: + with open( + TXT_PATH + "yiqing/" + str(datetime.now().date()) + ".json", "w" + ) as f: json.dump(datalist, f) - datalist = json.load(open(TXT_PATH + "yiqing/" + str(datetime.now().date()) + ".json", 'r')) - result = '' + datalist = json.load( + open(TXT_PATH + "yiqing/" + str(datetime.now().date()) + ".json", "r") + ) + result = "" for data in datalist: - if data['provinceShortName'] == province: - if city_ == '': - result = province + "疫情数据:\n现存确诊: " + \ - str(data['currentConfirmedCount']) + "\n累计确诊: " + \ - str(data['confirmedCount']) + "\n治愈: " + \ - str(data['curedCount']) + "\n死亡: " + \ - str(data['deadCount']) + if data["provinceShortName"] == province: + if city_ == "": + result = ( + province + + "疫情数据:\n现存确诊: " + + str(data["currentConfirmedCount"]) + + "\n累计确诊: " + + str(data["confirmedCount"]) + + "\n治愈: " + + str(data["curedCount"]) + + "\n死亡: " + + str(data["deadCount"]) + ) break else: - for city in data['cities']: - if city['cityName'] == city_: - result = city_ + "疫情数据:\n现存确诊: " + \ - str(city['currentConfirmedCount']) + "\n累计确诊: " + str(city['confirmedCount']) +\ - "\n治愈: " + str(city['curedCount']) + "\n死亡: " + str(city['deadCount']) + for city in data["cities"]: + if city["cityName"] == city_: + result = ( + city_ + + "疫情数据:\n现存确诊: " + + str(city["currentConfirmedCount"]) + + "\n累计确诊: " + + str(city["confirmedCount"]) + + "\n治愈: " + + str(city["curedCount"]) + + "\n死亡: " + + str(city["deadCount"]) + ) break return result @@ -45,6 +63,5 @@ def clear_data(): os.remove(TXT_PATH + "yiqing/" + file) -if __name__ == '__main__': - print(get_yiqing_data("浙江", city_='')) - +if __name__ == "__main__": + print(get_yiqing_data("浙江", city_="")) diff --git a/resources/img/genshin/seek_god_eye/Minimal.ttf b/resources/img/genshin/seek_god_eye/Minimal.ttf deleted file mode 100644 index 7a47b06a..00000000 Binary files a/resources/img/genshin/seek_god_eye/Minimal.ttf and /dev/null differ diff --git a/resources/img/genshin/seek_god_eye/__init__.py b/resources/img/genshin/seek_god_eye/__init__.py deleted file mode 100644 index 2d0e19ff..00000000 --- a/resources/img/genshin/seek_god_eye/__init__.py +++ /dev/null @@ -1,182 +0,0 @@ -from hoshino import Service -from nonebot import on_command -<<<<<<< HEAD -# from ..configs import ALLOW_PRIVATE_CHAT -======= -# from ..config import ALLOW_PRIVATE_CHAT ->>>>>>> master -import random -from .seek_god_eye import (JSON_LIST, - GOD_EYE_INFO, - GOD_EYE_CLASS_LIST, - init_uid_info, - get_random_god_eye_id, - get_god_eye_message, - get_uid_number_found, - found_god_eye, - all_god_eye_map, - delete_god_eye_info, - reset_god_eye_info) - - - - - - -sv = Service('原神神瞳信息查询') - -search_god_eye_command = [] -for eye_type in JSON_LIST: - search_god_eye_command.append(f"找{eye_type}") - -reset_god_eye_command = [] -for eye_type in JSON_LIST: - reset_god_eye_command.append(f"重置{eye_type}找到记录") - -verification_code_list = { - # 重置已找到神瞳列表时需要二次确认,这个字典用来存验证码 - # "QQ号" : 验证码 -} - - -@sv.on_prefix(search_god_eye_command) -async def search_god_eye(bot, ev): - - command_txt = ev['prefix'] - god_eye_id = ev.message.extract_plain_text().strip() - userid = str(ev['user_id']) - init_uid_info(userid) - - if not (god_eye_id in GOD_EYE_INFO): - if god_eye_id != "": - await bot.send(ev, f"找不到编号为 {god_eye_id} 的神瞳" , at_sender=True) - return - - god_eye_type = command_txt[1:] # 把指令前边的 找 字去掉 - - if god_eye_id == "": - # 如果用户没有给神瞳ID就根据属性随机一个没找到过的返回 - god_eye_id = get_random_god_eye_id(userid,god_eye_type) - - if god_eye_id == "": - # 如果随机之后还是空字符串,就表示这种神瞳已经都找完了 - await bot.send(ev, f"你已经找完这种神瞳了!", at_sender=True) - return - - mes = get_god_eye_message(god_eye_id) - - await bot.send(ev, mes, at_sender=True) - - - -@sv.on_prefix("找到神瞳了") -async def found_it(bot, ev): - god_eye_id = ev.message.extract_plain_text().strip() - userid = str(ev['user_id']) - init_uid_info(userid) - - if god_eye_id == "": - await bot.send(ev, f"你需要发送一个神瞳编号" , at_sender=True) - return - - if not (god_eye_id in GOD_EYE_INFO): - await bot.send(ev, f"找不到编号为 {god_eye_id} 的神瞳" , at_sender=True) - return - - mes = found_god_eye(userid,god_eye_id) - - await bot.send(ev, mes, at_sender=True) - - - -@sv.on_prefix("删除找到神瞳",only_to_me=True) -async def delete_god_eye_id(bot, ev): - - god_eye_id = ev.message.extract_plain_text().strip() - userid = str(ev['user_id']) - init_uid_info(userid) - - if god_eye_id == "": - await bot.send(ev, f"你需要发送一个神瞳编号" , at_sender=True) - return - - if not (god_eye_id in GOD_EYE_INFO): - await bot.send(ev, f"找不到编号为 {god_eye_id} 的神瞳" , at_sender=True) - return - - mes = delete_god_eye_info(userid,god_eye_id) - - await bot.send(ev, mes, at_sender=True) - - -@sv.on_prefix(reset_god_eye_command,only_to_me=True) -async def reset_god_eye_(bot, ev): - - verification_code = ev.message.extract_plain_text().strip() - userid = (ev['user_id']) - init_uid_info(userid) - command_txt = ev['prefix'].strip() - god_eye_type = command_txt[2:-4] - - if not (god_eye_type in JSON_LIST): - await bot.send(ev, f"没有这种神瞳" , at_sender=True) - return - - if verification_code == "": - - new_verification_code = "" - for i in range(4): - # 生成一个4位的字母验证码 - new_verification_code += str(chr(random.randint(65,90))) - - verification_code_list[userid] = new_verification_code - await bot.send(ev, f"你确定要重置已经找到的神瞳记录吗?如果确定请发送:\n{command_txt}{new_verification_code}" , at_sender=True) - return - - - if verification_code_list[userid] == verification_code: - reset_god_eye_info(userid,god_eye_type) - verification_code_list.pop(userid) - await bot.send(ev, f"已重置已经找到的{god_eye_type}记录" , at_sender=True) - return - - await bot.send(ev, f"验证码错误,请检查验证码是否正确或重新生成验证码。", at_sender=True) - - - -@sv.on_prefix("找到多少神瞳了",only_to_me=True) -async def found_god_eye_info(bot, ev): - userid = str(ev['user_id']) - init_uid_info(userid) - - mes = get_uid_number_found(userid) - - await bot.send(ev, mes, at_sender=True) - - - -@sv.on_prefix("没找到的",only_to_me=True) -async def not_found_god_eye_info(bot, ev): - userid = str(ev['user_id']) - init_uid_info(userid) - god_eye_type = ev.message.extract_plain_text().strip() - - if not (god_eye_type in JSON_LIST): - await bot.send(ev, f"没有这种神瞳" , at_sender=True) - return - - await bot.send(ev, all_god_eye_map(userid,god_eye_type,""), at_sender=True) - - - -@sv.on_prefix("所有的",only_to_me=True) -async def not_found_god_eye_info(bot, ev): - userid = str(ev['user_id']) - init_uid_info(userid) - god_eye_type = ev.message.extract_plain_text().strip() - - if not (god_eye_type in JSON_LIST): - await bot.send(ev, f"没有这种神瞳" , at_sender=True) - return - - await bot.send(ev, all_god_eye_map(userid,god_eye_type,"all"), at_sender=True) \ No newline at end of file diff --git a/resources/img/genshin/seek_god_eye/icon/map_icon.jpg b/resources/img/genshin/seek_god_eye/icon/map_icon.jpg deleted file mode 100644 index a7479597..00000000 Binary files a/resources/img/genshin/seek_god_eye/icon/map_icon.jpg and /dev/null differ diff --git a/resources/img/genshin/seek_god_eye/icon/map_icon.png b/resources/img/genshin/seek_god_eye/icon/map_icon.png deleted file mode 100644 index e0521d6d..00000000 Binary files a/resources/img/genshin/seek_god_eye/icon/map_icon.png and /dev/null differ diff --git a/resources/img/genshin/seek_god_eye/icon/map_icon_back.jpg b/resources/img/genshin/seek_god_eye/icon/map_icon_back.jpg deleted file mode 100644 index e0521d6d..00000000 Binary files a/resources/img/genshin/seek_god_eye/icon/map_icon_back.jpg and /dev/null differ diff --git a/resources/img/genshin/seek_god_eye/icon/raw_rock_god_eye.png b/resources/img/genshin/seek_god_eye/icon/raw_rock_god_eye.png deleted file mode 100644 index 30f2346e..00000000 Binary files a/resources/img/genshin/seek_god_eye/icon/raw_rock_god_eye.png and /dev/null differ diff --git a/resources/img/genshin/seek_god_eye/icon/raw_wind_god_eye.png b/resources/img/genshin/seek_god_eye/icon/raw_wind_god_eye.png deleted file mode 100644 index 858faccd..00000000 Binary files a/resources/img/genshin/seek_god_eye/icon/raw_wind_god_eye.png and /dev/null differ diff --git a/resources/img/genshin/seek_god_eye/icon/岩神瞳.png b/resources/img/genshin/seek_god_eye/icon/岩神瞳.png deleted file mode 100644 index bc7c42a3..00000000 Binary files a/resources/img/genshin/seek_god_eye/icon/岩神瞳.png and /dev/null differ diff --git a/resources/img/genshin/seek_god_eye/icon/风神瞳.png b/resources/img/genshin/seek_god_eye/icon/风神瞳.png deleted file mode 100644 index e95de89f..00000000 Binary files a/resources/img/genshin/seek_god_eye/icon/风神瞳.png and /dev/null differ diff --git a/resources/img/genshin/seek_god_eye/seek_god_eye.py b/resources/img/genshin/seek_god_eye/seek_god_eye.py deleted file mode 100644 index 66bee40a..00000000 --- a/resources/img/genshin/seek_god_eye/seek_god_eye.py +++ /dev/null @@ -1,346 +0,0 @@ - -from PIL import Image,ImageDraw,ImageFont -from io import BytesIO - -import os -import json -import random -import base64 - -FILE_PATH = os.path.dirname(__file__) - -JSON_LIST = ["风神瞳","岩神瞳"] - - - -GOD_EYE_TOTAL = { - # 每种神瞳有多少个,这个字典会在导入神瞳的json时初始化 - # "风神瞳" : 100 -} - -GOD_EYE_INFO = { - # 所有神瞳的信息 - # "56": { - # "属性": "风神瞳", - # "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/21/76373921/cbd63e9fbb00160b045dafc424c1657f_1299454333660281584.gif", - # "备注": "", - # "x_pos": 1922.0018670150284, - # "y_pos": 683.9995073891628 - # } -} - -GOD_EYE_CLASS_LIST = { - # 每种神瞳的编号列表 - # "风神瞳":["1","2","3"], - # "岩神瞳":["4","5","6"] - -} - - -MAP_IMAGE = Image.open(os.path.join(FILE_PATH,"icon","map_icon.jpg")) -MAP_SIZE = MAP_IMAGE.size - - -# 风神瞳.json里记录的坐标是相对坐标,是以蒙德城的大雕像为中心的,所以图片合成时需要转换坐标 -CENTER = (3505,1907) - -# 神瞳位置图的裁切尺寸,默认是1000,表示图片长宽都是1000 -CROP_SIZE = 1000 - -uid_info = { - # 这个字典记录用户已经找到的神瞳编号 - # "12345":{ - # "风神瞳":[], - # "岩神瞳":[] - # } -} - - - -for json_name in JSON_LIST: - # 导入神瞳的.json文件 - with open(os.path.join(FILE_PATH, f"{json_name}.json"), 'r', encoding='UTF-8') as f: - data = json.load(f) - GOD_EYE_TOTAL[json_name] = len(data) - GOD_EYE_INFO.update(data) - GOD_EYE_CLASS_LIST.setdefault(json_name,list(data.keys())) - - - - -def save_uid_info(): - with open(os.path.join(FILE_PATH,'uid_info.json'),'w',encoding='UTF-8') as f: - json.dump(uid_info,f,ensure_ascii=False,indent=4) - - -# 检查uid_info.json是否存在,没有创建空的 -if not os.path.exists(os.path.join(FILE_PATH,'uid_info.json')): - save_uid_info() - -# 读取uid_info.json的信息 -with open(os.path.join(FILE_PATH,'uid_info.json'),'r',encoding='UTF-8') as f: - uid_info = json.load(f) - - - - - -class God_eye_position_image(object): - # 获取神瞳的位置图像 - # 传入的参数是神瞳的编号,实例化后直接调用get_cq_code即可返回图片的CQ码,使用base64发送 - def __init__(self,god_eye_id): - self.id = str(god_eye_id) - - # ID对应的png文件名 - self.png_name = GOD_EYE_INFO[self.id]["属性"] + '.png' - - # 复制一份地图文件 - self.map_image = MAP_IMAGE.copy() - - # 神瞳的坐标 - self.x,self.y = self.transform_position() - - # 神瞳图片在paste时的偏移量 - self.offset = [50,120] - - def transform_position(self): - # 风神瞳.json里记录的坐标是相对坐标,需要转换一下 - x = GOD_EYE_INFO[self.id]["x_pos"] + CENTER[0] - y = GOD_EYE_INFO[self.id]["y_pos"] + CENTER[1] - return [int(x),int(y)] - - def get_crop_pos(self): - # 返回地图的裁切尺寸,检查裁切点是否越界 - x = max(self.x - CROP_SIZE/2,0) - y = max(self.y - CROP_SIZE/2,0) - r = min(self.x + CROP_SIZE/2,MAP_SIZE[0]) - l = min(self.y + CROP_SIZE/2,MAP_SIZE[1]) - return [x,y,r,l] - - def paste(self): - # 把神瞳的图贴到地图上,然后以神瞳为中心裁切地图 - god_eye_image = Image.open(os.path.join(FILE_PATH, "icon", self.png_name)) - self.map_image.paste(god_eye_image,(self.x - self.offset[0],self.y - self.offset[1]),god_eye_image) - self.map_image = self.map_image.crop(self.get_crop_pos()) - - def get_cq_code(self): - self.paste() - bio = BytesIO() - self.map_image.save(bio, format='JPEG') - base64_str = 'base64://' + base64.b64encode(bio.getvalue()).decode() - - return f"[CQ:image,file={base64_str}]" - - - -class God_eye_map(object): - - def __init__(self,_resource_name,_uid,_mode = ""): - self.resource_name = _resource_name - self.uid = _uid - self.mode = _mode - - # 地图要要裁切的左上角和右下角坐标 - # 这里初始化为地图的大小 - self.x_start = MAP_SIZE[0] - self.y_start = MAP_SIZE[1] - self.x_end = 0 - self.y_end = 0 - - self.map_image = MAP_IMAGE.copy() - - self.resource_icon = Image.open(os.path.join(FILE_PATH,"icon",f"{self.resource_name}.png")) - #self.resource_icon = self.resource_icon.resize((int(150*zoom),int(150*zoom))) - - - self.resource_id_list = self.get_resource_point_list() - - - def get_resource_point_list(self): - - temp_list = GOD_EYE_CLASS_LIST[self.resource_name].copy() - - if self.mode == "all": - return temp_list - - for id in set(uid_info[self.uid][self.resource_name]): - temp_list.remove(id) - - return temp_list - - - def paste(self): - for id in self.resource_id_list: - # 把资源图片贴到地图上 - x = int(GOD_EYE_INFO[id]["x_pos"] + CENTER[0]) - y = int(GOD_EYE_INFO[id]["y_pos"] + CENTER[1]) - - - - self.map_image.paste(self.resource_icon,(x - 50 , y - 120),self.resource_icon) - - draw = ImageDraw.Draw(self.map_image) - setfont = ImageFont.truetype(FILE_PATH + '/Minimal.ttf', size=50) - - draw.text((x + 40, y - 60), str(id), fill="#000000", font=setfont) - - # 找出4个方向最远的坐标,用于后边裁切 - self.x_start = min(x,self.x_start) - self.y_start = min(y,self.y_start) - self.x_end = max(x,self.x_end) - self.y_end = max(y,self.y_end) - - - def crop(self): - - # 先把4个方向扩展150像素防止把资源图标裁掉 - self.x_start -= 150 - self.y_start -= 150 - self.x_end += 150 - self.y_end += 150 - - # 如果图片裁切的太小会看不出资源的位置在哪,检查图片裁切的长和宽看够不够1000,不到1000的按1000裁切 - if (self.x_end - self.x_start)<1000: - center = int((self.x_end + self.x_start) / 2) - self.x_start = center - 500 - self.x_end = center +500 - if (self.y_end - self.y_start)<1000: - center = int((self.y_end + self.y_start) / 2) - self.y_start = center - 500 - self.y_end = center +500 - - self.map_image = self.map_image.crop((self.x_start,self.y_start,self.x_end,self.y_end)) - - def get_cq_cod(self): - - if not self.resource_id_list: - return "没有这个资源的信息" - - self.paste() - - self.crop() - - bio = BytesIO() - self.map_image.save(bio, format='JPEG') - base64_str = 'base64://' + base64.b64encode(bio.getvalue()).decode() - - return f"[CQ:image,file={base64_str}]" - - def get_resource_count(self): - return len(self.resource_id_list) - - - -def get_uid_number_found(uid:str): - mes = "你找到的神瞳信息如下:\n" - for eye_type in JSON_LIST: - number = len(uid_info[uid][eye_type]) - mes += f"你已经找到了 {number} 个 {eye_type} ,该神瞳一共有 {GOD_EYE_TOTAL[eye_type]} 个!\n" - return mes - - -def get_eye_gif_path(eye_id): - # 获取gif的路径,找不到会返回空字符串 - eye_type = GOD_EYE_INFO[eye_id]["属性"] - gif_path = os.path.join(FILE_PATH,"icon",eye_type,str(eye_id) + ".gif") - if os.path.exists(gif_path): - return gif_path - else: - return "" - - -def get_eye_gif_cq_code(eye_id): - # 获取gif的CQ码,找不到gif文件会返回空字符串 - gif_path = get_eye_gif_path(eye_id) - if gif_path == "": - return "" - - gif_path = gif_path.replace("\\","/") - cq_code = f'[CQ:image,file=file://{gif_path}]' - return cq_code - -def get_eye_remarks(eye_id): - # 获取神瞳的备注,注意有的神瞳备注是空字符串 - return GOD_EYE_INFO[eye_id]["备注"] - -def add_god_eye_info(uid,eye_id): - eye_type = GOD_EYE_INFO[eye_id]["属性"] - uid_info[uid][eye_type].append(eye_id) - uid_info[uid][eye_type] = list(set(uid_info[uid][eye_type])) - save_uid_info() - -def init_uid_info(uid): - # 初始化用户的信息 - if not (uid in uid_info): - uid_info.setdefault(uid, {}) - for eye_type in JSON_LIST: - if not (eye_type in uid_info[uid]): - uid_info[uid].setdefault(eye_type, []) - -def get_random_god_eye_id(uid,eye_type): - # 获取一个随机没找到过的神瞳ID,返回随机到的神瞳ID,如果返回空字符串表示这种神瞳已经全部找到了 - if len(uid_info[uid][eye_type]) == GOD_EYE_TOTAL[eye_type]: - return "" - # 找出没找到过的神瞳列表 - temp_list = GOD_EYE_CLASS_LIST[eye_type].copy() - - for id in uid_info[uid][eye_type]: - temp_list.remove(id) - - # eyes_never_found = set(GOD_EYE_CLASS_LIST[eye_type]).difference(set(uid_info[uid][eye_type])) - # r = random.choice(list(eyes_never_found)) - r = random.choice(temp_list) - return str(r) - -def delete_god_eye_info(uid,eye_id): - eye_type = GOD_EYE_INFO[eye_id]["属性"] - if not (eye_id in uid_info[uid][eye_type]): - return "你还没有找到这个神瞳!" - - uid_info[uid][eye_type].remove(eye_id) - save_uid_info() - return f"已经在你的记录列表删除编号为 {eye_id} 的神瞳" - -def reset_god_eye_info(uid,eye_type): - # 重置某一种神瞳的已找到列表 - uid_info[uid][eye_type].clear() - save_uid_info() - return "已重置已找到这种神瞳的列表" - -def get_god_eye_message(eye_id): - message = f"当前神瞳编号 {eye_id} \n" - message += God_eye_position_image(eye_id).get_cq_code() # 获取神瞳位置图 - message += "\n" - - gif_cq_code = get_eye_gif_cq_code(eye_id) # 获取找神瞳的动图,没有找到这就是个空字符串 - if gif_cq_code: - message += gif_cq_code - message += "\n" - - remarks_txt = get_eye_remarks(eye_id) # 获取神瞳的备注信息 - if remarks_txt: - message += "备注:" - message += remarks_txt - message += "\n" - - message += "\n※ 如果你找到了神瞳或者你确定这个神瞳已经找过了,可以发送 找到神瞳了 神瞳编号\n" - message += "※ 机器人记录你找到这个神瞳之后将不再给你发送这个神瞳位置\n" - message += "※ 图片及数据来源于原神观测枢wiki\n" - message += "※ 神瞳位置有可能有细微误差,具体以游戏里为准" - - return message - -def found_god_eye(uid,eye_id): - add_god_eye_info(uid,eye_id) - save_uid_info() - return f"已添加编号为 {eye_id} 的神瞳找到记录!" - - - -def all_god_eye_map(uid,eye_type,mode = ""): - mes = "神之眼信息如下:\n" - - mes += God_eye_map(eye_type,uid,mode).get_cq_cod() - - return mes - diff --git a/resources/img/genshin/seek_god_eye/岩神瞳.json b/resources/img/genshin/seek_god_eye/岩神瞳.json deleted file mode 100644 index 96c0e09b..00000000 --- a/resources/img/genshin/seek_god_eye/岩神瞳.json +++ /dev/null @@ -1,919 +0,0 @@ -{ - "2110": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/24/15363053/bfb00fa168d8684d7487fce07f5b543a_4424026387832946167.gif", - "备注": "", - "x_pos": -2407.5, - "y_pos": 1602.5 - }, - "2042": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/10/14/76373921/122c9fcb50d48c4c61d25e1c0a8cf96a_2764643910865172456.gif", - "备注": "", - "x_pos": -0.5, - "y_pos": 2664 - }, - "1865": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/25/6142363/1923190da67768eb52de24d62e1937be_2494748305899061128.gif", - "备注": "", - "x_pos": -2178.5, - "y_pos": 2167.6726049978897 - }, - "1747": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/10/14/76373921/76441984ff1d830d6b6b8018c1c7bfde_8520420476823328876.gif", - "备注": "从山上滑翔过去就能拿到了", - "x_pos": -2438, - "y_pos": 1607.6449953037095 - }, - "1744": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/22/4839652/101fce2c902850371c4c26c5192a2bf8_7299553878110113351.gif", - "备注": "神瞳在大木桩中央,但是需从旁边高处飞过去才能捡到,大木桩下面怎么跳也够不着的(别问我怎么知道的", - "x_pos": -1205, - "y_pos": 489 - }, - "1734": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/24/15363053/681afc5fbe538cb62494b5f0aeb91296_17354936688802550.gif", - "备注": "", - "x_pos": -2388.5495293809718, - "y_pos": 1226.270843798893 - }, - "1720": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/24/15363053/46b07f1e7ec0633e0d8a93456de53042_1319508688072624084.gif", - "备注": "", - "x_pos": 368.50021848898723, - "y_pos": 2696.000602283162 - }, - "1716": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/25/6142363/35b7c22e8c0c820eaee0a146271f8ff7_7014348581897273900.gif", - "备注": "", - "x_pos": -1082.4994537775328, - "y_pos": 2196.001204566325 - }, - "1714": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/25/6142363/dda98a1af8a91130de916760159c7c22_3702109991219548578.gif", - "备注": "在矿洞中", - "x_pos": -619.9987983105725, - "y_pos": 1446.0013250229576 - }, - "1692": { - "属性": "岩神瞳", - "gif_url": "", - "备注": "", - "x_pos": 9.001092444933875, - "y_pos": 1926.4993977168378 - }, - "1675": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/25/6142363/774bbecb02e6b0b4e6709a1f43c3de6e_2872670206992713583.gif", - "备注": "", - "x_pos": -1230.5002184889868, - "y_pos": 1867.4991568035725 - }, - "1674": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/24/15363053/8cc6328607f8e026e98be7fd1e1d4f3e_1568835659620315348.gif", - "备注": "", - "x_pos": -2494.4995630220265, - "y_pos": 1357.0002409132649 - }, - "1673": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/24/15363053/072762f0094569036595d0cbe9ab0518_3145564383828401599.gif", - "备注": "", - "x_pos": -2399.4995630220265, - "y_pos": 1371.5001204566324 - }, - "1672": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/22/4839652/93d513e5eccaa8668a5c7b790c7fd791_3605844902050387078.gif", - "备注": "神瞳在NPC冒险家丹迪旁边的老歪脖子树上。(不是吊死崇祯那颗啦!", - "x_pos": -1373.069958101236, - "y_pos": 368.26120726829276 - }, - "286": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/24/4839652/326213c5e0b5433d9a5c2ea6979eff5a_2139784998682881218.gif", - "备注": "神瞳在水车旁边高层塔楼的屋顶上。(我也是爬了半天悬崖才看见的( ̄﹏ ̄;)", - "x_pos": -1340.6024352857153, - "y_pos": 617.403184152372 - }, - "284": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/22/4839652/311834efa3e815663ef3b8403e1e0111_3944235440707971506.gif", - "备注": "神瞳在坑上方的空中。", - "x_pos": -1198.8504157425355, - "y_pos": 846.8043551015844 - }, - "283": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/22/4839652/20585c32a9164fcca9222b42daaaa305_3617152667042309742.gif", - "备注": "", - "x_pos": -1268.4995709468053, - "y_pos": 650.500798676363 - }, - "281": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/24/4839652/77075947eb779fa655b916e8e754a4d1_6363599850565816084.gif", - "备注": "神瞳在房檐下面,注意别飞过了。", - "x_pos": -1283.9306425450695, - "y_pos": 531.9160212744209 - }, - "280": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/22/4839652/9a4e5bcda1025328397c2cd2782b8584_3011514102623077634.gif", - "备注": "神瞳在高空中,需要先爬树再到房顶才能飞过去捡到。(真麻烦╭(╯^╰)╮)", - "x_pos": -1429.972403722703, - "y_pos": 552.4269365180517 - }, - "278": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/21/4839652/8c60960615eee3c3b5905126d93bdb98_3197582560415631799.gif", - "备注": "", - "x_pos": -1502.9143385236644, - "y_pos": 621.409365638061 - }, - "277": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/10/14/76373921/a4b7cf88ed796ff724ba4432cf37f074_7889730050607266460.gif", - "备注": "放个荒星就好了", - "x_pos": -1172.841548643175, - "y_pos": 647.7660186361509 - }, - "276": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/22/4839652/7d845562bafec2b33526a5cece8df423_1936290300520583862.gif", - "备注": "", - "x_pos": -1452.844981068734, - "y_pos": 479.83896441065644 - }, - "275": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/22/4839652/920b10fb304010e4bb1ded92dc40a707_4273448204704312890.gif", - "备注": "", - "x_pos": -1330.8781611824702, - "y_pos": 817.8336399015684 - }, - "274": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/22/4839652/5e4380aacc9d83c3bc53cd0eae3d6f98_8783309690886067650.gif", - "备注": "神瞳在庙屋顶上,可以爬后面的岩壁上去捡。", - "x_pos": -1622.0027234818067, - "y_pos": 578.914690147149 - }, - "273": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/22/4839652/e1041f1b4de72f6e078a934d9152f08f_7753023967918108067.gif", - "备注": "", - "x_pos": -1637.9529533112022, - "y_pos": 712.9580848962164 - }, - "272": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/22/4839652/3ff088aad84bbf6b29c48ab00c156c13_8209708308707735294.gif", - "备注": "神瞳在瀑布坑里的小洞,要仔细一点找哦~", - "x_pos": -1578.9985698226837, - "y_pos": 446.00425960727034 - }, - "271": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/22/4839652/a0cc053115cd320238c073a6d0f80292_3460622677525059934.gif", - "备注": "神瞳在竹子顶上,耐心的爬哦~(我要一步一步往上爬~♫ 咦我怎么唱起来了)", - "x_pos": -1496.9998569822683, - "y_pos": 801.0018635781807 - }, - "270": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/22/4839652/8955a467a8ed09df9e42c0e831c60805_8828823783563834949.gif", - "备注": "", - "x_pos": -1532.9183430201501, - "y_pos": 817.4652729834852 - }, - "269": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/22/4839652/113f5ae8217c8ea07bbe1adf57b41e38_7820847643669684849.gif", - "备注": "神瞳在瀑布旁边的大洞穴里面,遗迹守卫旁边岩壁往上爬就可以看到了。(我还以为它会突然起来打我)", - "x_pos": -1626.905048516015, - "y_pos": 373.90339097062133 - }, - "268": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/22/4839652/06cce7738ee0d1bfd1ba945150ad9e7a_318743286187121433.gif", - "备注": "神瞳在宝箱上方,爬树可以直接捡了飞跑,如果你不想被打的话。(等等,我怎么感觉我还是要被打┭┮﹏┭┮)", - "x_pos": -1345.9982837872203, - "y_pos": 736.0063894109057 - }, - "267": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/24/15363053/2a4b50143f952e432f5b018b20d9fc0d_5571795988927030483.gif", - "备注": "", - "x_pos": -1486.0005720709264, - "y_pos": 161.00851921454068 - }, - "265": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/22/4839652/1e6cfa25cbefd47f531e9ea3e67b246e_287668464508994588.gif", - "备注": "", - "x_pos": -1366.8858841399779, - "y_pos": 869.8576001924648 - }, - "264": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/10/14/76373921/1d2312d3d9fe9bc396f453c454bb3f21_3563484467109557283.gif", - "备注": "在丘丘人营地的屋顶上", - "x_pos": -1100.9994279290736, - "y_pos": 1101.0079867636318 - }, - "263": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/23/4839652/8e0236299764076ba25c5b2c348fa7a5_4505073313672454428.gif", - "备注": "", - "x_pos": -1117.8286770473292, - "y_pos": 887.8336399015684 - }, - "262": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/23/4839652/5571691d5c81615261f28dbac8b3d13b_1116984694150301308.gif", - "备注": "神瞳在半空中,需要到远处的一块石头上利用风元素激活风车产生风场飞过去才能捡到。", - "x_pos": -1191.9979977517573, - "y_pos": 1030.0063894109057 - }, - "261": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/23/4839652/013498519baea0f756c7f6ac1b26c157_447724792811709310.gif", - "备注": "在圆坑宝箱的上方。(顺便一提宝箱开启方法是用岩元素攻击圆坑中地上的阵哦~)", - "x_pos": -1213.8561364518005, - "y_pos": 911.8464187233799 - }, - "260": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/24/4839652/de08a10c8a11aaabcd44900fc7823bcc_1708027969562824562.gif", - "备注": "神瞳在石墩上较高的位置,用岩主砸个荒星当垫脚就能够到了。(像我这样憨跳是没有结果的┭┮﹏┭┮)", - "x_pos": -1049.9299336013173, - "y_pos": 957.9007287160775 - }, - "259": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/23/4839652/7a01b229bd3110675b5da4fa1ea8277c_3563199774348189887.gif", - "备注": "在山坡顶端的树上。", - "x_pos": -1170.9994279290736, - "y_pos": 1254.0037271563615 - }, - "257": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/22/4839652/234e44af005d0ff1c8ff1bf90c9da952_4891759829643618237.gif", - "备注": "找完屋顶的神瞳后,从屋顶找到大树黄叶子的方位往外跳,落下去的时候调整角度就可以看到神瞳在拐角桥底下(注意控制下落速度哦~)", - "x_pos": -1122.5, - "y_pos": 1433.0037271563615 - }, - "256": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/22/4839652/96542d31133c0b2e71fb2a947a0e4e2a_9009663536041532691.gif", - "备注": "神瞳在客栈屋顶上,通过客栈传送点旁边的大树可爬到屋顶。", - "x_pos": -1085.5004290531947, - "y_pos": 1433.5031947054526 - }, - "255": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/10/14/76373921/fbbcbc244bfacc3a07b3515b0e6b9237_5029872380655701823.gif", - "备注": "", - "x_pos": -1693, - "y_pos": 987.5 - }, - "254": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/10/14/76373921/57a8ddf17d98291d799a0a4495ab329e_2564133905221417090.gif", - "备注": "", - "x_pos": -1791, - "y_pos": 916 - }, - "253": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/24/15363053/72496f97addf4d9d60f91964cfdd9625_6295784218623893117.gif", - "备注": "", - "x_pos": -1895, - "y_pos": 732 - }, - "252": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/24/15363053/54febda9ea1207ec7a2d5ec54d76d991_2009195493168024515.gif", - "备注": "", - "x_pos": -2051.5, - "y_pos": 889.5 - }, - "251": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/24/15363053/4435bb4a7089055764f50bbf9e45ab0b_8952950108931868056.gif", - "备注": "", - "x_pos": -1479.0706135681962, - "y_pos": 2222.7820462657164 - }, - "250": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/24/15363053/f87cc4140a63d3ce829856bd710d1180_1039695060986393236.gif", - "备注": "", - "x_pos": -1991, - "y_pos": 942 - }, - "249": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/24/15363053/185bd072e14fe1b68e60fa19f5d2a84c_9081089639178637983.gif", - "备注": "", - "x_pos": -1657, - "y_pos": 1167 - }, - "248": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/24/15363053/745fe60555ea00397a7601976a76c5e5_9018417527542931034.gif", - "备注": "", - "x_pos": -1400, - "y_pos": 1500 - }, - "247": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/24/15363053/9ad571f6bb3f662295ac1fdb05a26876_3120868171121670107.gif", - "备注": "", - "x_pos": -1702, - "y_pos": 1549 - }, - "246": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/25/6142363/19d4454a4043b5f5b54966d66f4479f8_8043083059829183162.gif", - "备注": "", - "x_pos": -1641.580773306082, - "y_pos": 2477.2780711968435 - }, - "245": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/25/6142363/c78cfab542b64795d474fa18f2e17b61_4504037929925149836.gif", - "备注": "在山上的位置,可以利用风元素激活机关利用风场获得,也可以从更高的山上自高而下获取。", - "x_pos": -1213.0754203259057, - "y_pos": 2543.258557222377 - }, - "244": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/25/6142363/139b983785817eddee3074367e57a644_4117257246987464572.gif", - "备注": "天衡山的半山腰,进入山洞后从内部爬到制高点飞下来获取。", - "x_pos": -1365.5620924977115, - "y_pos": 2843.7763848039885 - }, - "243": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/25/6142363/d033e656dcb40da4930160301c31642d_5582272038780795632.gif", - "备注": "在一尊石像的正前方,跳下隐藏的坑道获取。", - "x_pos": -1260.575311081412, - "y_pos": 3075.2766257172534 - }, - "241": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/24/15363053/e530bf5bb47e8d58ff946fd71b6d032f_270147925109447429.gif", - "备注": "", - "x_pos": -1952, - "y_pos": 1303.5 - }, - "239": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/24/15363053/20812d40cd8c620b5b47f436778ed1ea_3953539578528078557.gif", - "备注": "", - "x_pos": -2064, - "y_pos": 1351 - }, - "234": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/25/6142363/11ca281c49160a765540aa1887cba43d_5461665652443691007.gif", - "备注": "想办法解开这里的秘密,获取封印在里面的岩神瞳。", - "x_pos": -1689.0703950792094, - "y_pos": 2494.7822871789813 - }, - "233": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/24/15363053/85e6bfa4dd51daccecc509ba3d868d86_3226381862921063078.gif", - "备注": "", - "x_pos": -1957, - "y_pos": 1398 - }, - "231": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/25/6142363/f2694c1f3147eb83a8ecba3488f7308f_2340210256149830404.gif", - "备注": "", - "x_pos": -1785.0410083104869, - "y_pos": 2483.2746984111336 - }, - "230": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/10/14/76373921/af444f8474d91c89b6e7c8515fb18db8_2038267040722511085.gif", - "备注": "", - "x_pos": -2037, - "y_pos": 1592 - }, - "228": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/25/6142363/961cea37862271dd31b5eb62b316b776_8281991535622649793.gif", - "备注": "", - "x_pos": -1988.5528067157732, - "y_pos": 2574.2830099187768 - }, - "227": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/24/15363053/96d9f8f6109df03b47f7a1e7740bce62_1954288530748010308.gif", - "备注": "", - "x_pos": -1965, - "y_pos": 1591 - }, - "225": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/10/14/76373921/af976a5c1e2d292e795eea8157dc24e4_4103645056210980394.gif", - "备注": "", - "x_pos": -1889.077277482293, - "y_pos": 2667.7905986866244 - }, - "224": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/10/14/76373921/c36dfdbb8f846d61caba8694bc3e61b4_2494813006484203754.gif", - "备注": "", - "x_pos": -2058, - "y_pos": 1561 - }, - "222": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/24/15363053/7f8d47727966f1e5c64ad1872c55ca41_5375743215916018392.gif", - "备注": "", - "x_pos": -2174, - "y_pos": 1493 - }, - "221": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/25/6142363/efed9717f6360aa5d8481dccc768667c_5669949726399085061.gif", - "备注": "", - "x_pos": -2032.070832057183, - "y_pos": 2841.2774689136813 - }, - "220": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/25/6142363/9f36786b07cd60e2e70ef77d57a1fe4d_8252897460680296460.gif", - "备注": "", - "x_pos": -2022.0319410175352, - "y_pos": 2979.79565786519 - }, - "219": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/24/15363053/44732d11a05c8084cad774f6cdee5412_4766101376373994746.gif", - "备注": "", - "x_pos": -2293, - "y_pos": 1439 - }, - "218": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/24/15363053/699a9419c8ff90a4ea471c77b01cd6b3_3665858705999641065.gif", - "备注": "", - "x_pos": -2357, - "y_pos": 1416 - }, - "217": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/25/6142363/49790871e4472593a878cc3fd63a9db1_6376197462342305513.gif", - "备注": "", - "x_pos": -1628.1128911871392, - "y_pos": 2820.270241515731 - }, - "216": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/24/15363053/7b35a1060f7961079dcb86110911c906_4578560071566404473.gif", - "备注": "", - "x_pos": -2372, - "y_pos": 1358.5 - }, - "215": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/25/6142363/600715fe2ea0fe748b529fc83e70ffde_8538213771536996277.gif", - "备注": "", - "x_pos": -1840.0785884162137, - "y_pos": 2870.2760234340913 - }, - "213": { - "属性": "岩神瞳", - "gif_url": "", - "备注": "", - "x_pos": -2024, - "y_pos": 1109 - }, - "212": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/25/6142363/248416e4fd9905ab79bdc3006053a084_4247108571851178371.gif", - "备注": "", - "x_pos": -2017.0734539250243, - "y_pos": 3198.28987594683 - }, - "211": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/24/15363053/6119ac8bfba3bdd7620bf1c5b28e2c30_6827682258836613594.gif", - "备注": "", - "x_pos": -2327, - "y_pos": 1094 - }, - "209": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/25/6142363/53ef4e77435978678cad49deba9f0673_2206200901886480498.gif", - "备注": "", - "x_pos": -1685.0439579118083, - "y_pos": 3161.7778302835786 - }, - "206": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/25/6142363/1b2442005095bca2b224e7f5d2f0ac6c_8463665409900924715.gif", - "备注": "", - "x_pos": -1530.5528067157734, - "y_pos": 3164.787346357547 - }, - "205": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/24/15363053/42de276fdbc9d6540b9fc21b4c65162d_6646310003246211582.gif", - "备注": "", - "x_pos": -2502.5, - "y_pos": 1580.5 - }, - "204": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/25/6142363/835d002c876155b66b460be040a1a1cf_5488112487273325804.gif", - "备注": "半山腰的石梯旁,进入不显眼山洞中获取。", - "x_pos": -1296.0726892135708, - "y_pos": 3007.768796036141 - }, - "202": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/24/15363053/e3f9bce83540de4610cfa8845ac1342c_634898536492956369.gif", - "备注": "", - "x_pos": -2554.5, - "y_pos": 1657.5 - }, - "201": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/25/6142363/e2069f78c0bd6b99f09c776e31ebee9e_2893864038704858742.gif", - "备注": "从一个制高点飞下来获取。", - "x_pos": -1445.6428241783287, - "y_pos": 2908.2703619723634 - }, - "200": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/24/15363053/ef1e07cc2c38a6e4ffbc660626fb845b_1517944109194517082.gif", - "备注": "", - "x_pos": -2593, - "y_pos": 1549.5 - }, - "198": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/26/6142363/1b6fe9f1df7519671ac51063a3df85fe_4888526966067712178.gif", - "备注": "从这座遗迹的楼顶进入内部获取岩神瞳。", - "x_pos": -1386.5665715219407, - "y_pos": 2755.775662064194 - }, - "197": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/24/15363053/d5246e49ca57101dff7104a9bf7fa811_6906477258596576472.gif", - "备注": "", - "x_pos": -2592, - "y_pos": 1672 - }, - "196": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/26/6142363/01f9590dfa5d16bdb3d3cbded50b0d04_7683001418661662742.gif", - "备注": "必须使用「荒星」(旅行者-岩)等类似技能才能获取。", - "x_pos": -1336.544176400795, - "y_pos": 2736.2726506483814 - }, - "195": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/25/6142363/b51af121fb6bf592227a4944ce0a49f5_8560203252075976300.gif", - "备注": "", - "x_pos": -995, - "y_pos": 2454 - }, - "193": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/25/6142363/03844827f6e29a50716ce1f88e8897a8_3547450626444842851.gif", - "备注": "", - "x_pos": -817, - "y_pos": 1782 - }, - "192": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/22/4839652/6992c5b2183e16fee5986a4f41dcb297_7454094125350444875.gif", - "备注": "", - "x_pos": -292, - "y_pos": 1655 - }, - "189": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/25/6142363/db4d00e8e2ee1e3511e85c84cf5cb7ed_5276418955432135382.gif", - "备注": "在山的顶端,使用火元素触发机关后利用风场起飞。", - "x_pos": -1086.5733446805311, - "y_pos": 2503.2760234340913 - }, - "188": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/22/4839652/f952dd7c54745342134bf45df64367d1_7571055893715095062.gif", - "备注": "神瞳在空中,需用风属性攻击风车产生风场才能捡到(e都e歪来,别骂了别骂了", - "x_pos": -666, - "y_pos": 1579.5 - }, - "187": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/21/4839652/12194a5ba7a0ac8929b4a872db0eac93_3768692455753349989.gif", - "备注": "", - "x_pos": -458.5, - "y_pos": 1586.5 - }, - "186": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/25/6142363/61f15f1b4ef775c99937a671bff1399b_5938135536381073318.gif", - "备注": "", - "x_pos": -717, - "y_pos": 2391 - }, - "183": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/24/15363053/2ce76b55dcf94f66735aa116fff5c33a_8845561111445684473.gif", - "备注": "", - "x_pos": 270.5, - "y_pos": 2537 - }, - "181": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/24/15363053/6cf4fd5db774ac8c2664d307f771224f_7327564672374274530.gif", - "备注": "", - "x_pos": -2135, - "y_pos": 1694 - }, - "180": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/24/15363053/69f4a1b4c9aee51eea4ee0c869c496a1_7647815399867701538.gif", - "备注": "", - "x_pos": -2001, - "y_pos": 1676 - }, - "179": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/24/15363053/92b274ed6819294c6e0498ede849aab3_4183482075727968921.gif", - "备注": "", - "x_pos": -1920, - "y_pos": 1761 - }, - "178": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/24/15363053/bc957329c93b2a9c26cd140f35a7e620_907051812852491627.gif", - "备注": "", - "x_pos": -1828, - "y_pos": 1703 - }, - "176": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/25/6142363/b9eb11e485e8224f69de4c49ba150bef_7166808008913459783.gif", - "备注": "", - "x_pos": -2183, - "y_pos": 2033 - }, - "175": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/24/15363053/b2ef8dddfccec6abf90df453afe45112_8341540754372647063.gif", - "备注": "", - "x_pos": -1637, - "y_pos": 1676 - }, - "174": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/10/14/76373921/ba299233468314ff869aa12255589d8c_956654482019651306.gif", - "备注": "", - "x_pos": -1413, - "y_pos": 1582 - }, - "173": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/26/6142363/8df4323f56862c8752d9348b603128a7_3924467011530438781.gif", - "备注": "", - "x_pos": -968, - "y_pos": 2289 - }, - "172": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/25/6142363/d06f90be30288c4f974baf22b86a7072_2681671454633752228.gif", - "备注": "", - "x_pos": -320, - "y_pos": 1860.5 - }, - "171": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/25/6142363/497d02a38cdce1306f5eb5b9765c9f11_8688165451316173668.gif", - "备注": "", - "x_pos": -286.5, - "y_pos": 1846.5 - }, - "170": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/25/6142363/d14fd84d817f693171f491cc5b7ef90f_7101805020475141960.gif", - "备注": "", - "x_pos": -527.5, - "y_pos": 1756 - }, - "168": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/21/4839652/e79826e96e945b1d4cb2d6f0a3356b7b_683988355454225141.gif", - "备注": "对准悬崖边的的草堆方向跳然后滑翔一会,可以看到神瞳在一个突出小岩石上", - "x_pos": -471, - "y_pos": 1729 - }, - "167": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/21/4839652/cd6c5f04c599137ab3eeb94eeaee864f_5337502825101761146.gif", - "备注": "", - "x_pos": -767, - "y_pos": 1670.5 - }, - "166": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/24/15363053/c222b675bd51e6c2772b23d2fe7046f9_2636542149190493706.gif", - "备注": "", - "x_pos": 152.5, - "y_pos": 2430.5 - }, - "158": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/24/15363053/d5f408d35d40b5a960e807da9f071141_5277418228530076285.gif", - "备注": "", - "x_pos": -1560, - "y_pos": 1918 - }, - "157": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/24/15363053/87e1d8618516e8a115d5fe1d293976d4_8413870914419726597.gif", - "备注": "", - "x_pos": -1473.0634034316322, - "y_pos": 1982.2633754876779 - }, - "156": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/25/6142363/3b138b7dae3c73a6b5800dc264fb8abb_3184230967842273348.gif", - "备注": "", - "x_pos": -962.5, - "y_pos": 1992 - }, - "155": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/25/6142363/984fec5b1d954f8e9c6e764f5f1472b0_3053009522588116077.gif", - "备注": "", - "x_pos": -551.5, - "y_pos": 1960.5 - }, - "154": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/25/6142363/28f799532eb4adad7d78ac8678060040_2338800999151887859.gif", - "备注": "", - "x_pos": -637.5, - "y_pos": 2052.5 - }, - "153": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/24/15363053/880e4c2af98d5e9a5dbbad0fad564668_5803930787453812284.gif", - "备注": "", - "x_pos": -175, - "y_pos": 2462 - }, - "152": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/25/6142363/0cf5d32e553834600e009973ad6cb85c_6640937310525308677.gif", - "备注": "在船上较高的位置,需要使用可以垫高角色的「荒星」(旅行者-岩)技能进行辅助攀爬,以及其他类似的技能以帮助角色达到。", - "x_pos": -636.0585966739231, - "y_pos": 2970.277107543784 - }, - "151": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/25/6142363/6584ccefa5b7483fc1170646db63fd5d_2850961042148796944.gif", - "备注": "需消灭入口的遗迹守卫,进入地下室", - "x_pos": -955.5, - "y_pos": 2048.5 - }, - "150": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/25/6142363/8a9e50275412419d1fb9f15879c01637_8432328208543305954.gif", - "备注": "", - "x_pos": -885, - "y_pos": 2087 - }, - "149": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/25/6142363/c706c742cc0c826c8daf5f56f189afed_4468459482056504433.gif", - "备注": "", - "x_pos": -624, - "y_pos": 2300 - }, - "148": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/10/14/76373921/403b0ed9cadf15f5572e0ae573a1a912_657689235043951204.gif", - "备注": "", - "x_pos": -510.5, - "y_pos": 1480.5 - }, - "147": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/24/15363053/7eba1aca68204b694032138f7a0c3d02_1250081311439252867.gif", - "备注": "", - "x_pos": -1269, - "y_pos": 1559 - }, - "146": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/10/14/76373921/4f510248b47d8bfa3bbba2a0c2c974f8_6951344005046235577.gif", - "备注": "", - "x_pos": -910.8770108957115, - "y_pos": 1378.3856715726192 - }, - "145": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/25/6142363/19388f6a8e9cd149e18e7f94e5df93c2_2560322040730289663.gif", - "备注": "", - "x_pos": -1022.9090468675945, - "y_pos": 1504.4527603871284 - }, - "144": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/22/4839652/4151ab56981ef469aadef48edc6bccab_7736619160476278206.gif", - "备注": "", - "x_pos": -242, - "y_pos": 1705 - }, - "143": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/25/6142363/8609c25ce039413d85a5f57b82440284_7591286282868156570.gif", - "备注": "", - "x_pos": -504.5, - "y_pos": 1375.5 - }, - "142": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/24/15363053/37ad1be5a090dc521b6ff003b1c03ee5_3954024625757420747.gif", - "备注": "", - "x_pos": -612.8501235621661, - "y_pos": 1110.7743893326801 - }, - "141": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/22/4839652/dbe8166689f5a574cff2e6a4822785b7_4050422453528787652.gif", - "备注": "神瞳在一块小岩石上,你可以像我一样飞过来,潇洒地落地,也可以直接爬石头。", - "x_pos": -1394.8938931329485, - "y_pos": 965.9087154797094 - }, - "140": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/22/4839652/75c1c781d4e9847a519d8380992a22b2_7705519360302196642.gif", - "备注": "", - "x_pos": -1322.9004719486034, - "y_pos": 1189.8607948979175 - }, - "139": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/25/6142363/15a6583ab2c1e9a5126ba5002418979f_2760622410182714499.gif", - "备注": "", - "x_pos": -665, - "y_pos": 1494.5 - }, - "138": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/24/15363053/680b145ce53b686547b80fdaf294faa8_3934738963438389761.gif", - "备注": "", - "x_pos": -1299, - "y_pos": 1412 - }, - "137": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/24/4839652/86b45907e056e28861e981a7a8d56da7_5966454234679688902.gif", - "备注": "在空心树桩上方。", - "x_pos": -943.7963550399827, - "y_pos": 1046.8378995088387 - }, - "136": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/22/4839652/c54cf025500e1b5a83ba20ca3c79fdca_5319557142256667902.gif", - "备注": "", - "x_pos": -1117.788918117938, - "y_pos": 781.7303444252611 - }, - "135": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/18/76373921/5f36f03b756a30e7d62cf370ea9c0665_3915499219172823185.gif", - "备注": "", - "x_pos": -938.8734354524208, - "y_pos": 740.8806132889858 - }, - "102": { - "属性": "岩神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/25/6142363/687bd1e53491186b59d3ef36367891b5_5790248486939030006.gif", - "备注": "", - "x_pos": -1212, - "y_pos": 2046.5 - } -} \ No newline at end of file diff --git a/resources/img/genshin/seek_god_eye/风神瞳.json b/resources/img/genshin/seek_god_eye/风神瞳.json deleted file mode 100644 index 42d8bbde..00000000 --- a/resources/img/genshin/seek_god_eye/风神瞳.json +++ /dev/null @@ -1,457 +0,0 @@ -{ - "1743": { - "属性": "风神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/22/21665168/599becc1b8938b69ad12cf3064c19361_2841686593498054236.gif", - "备注": "", - "x_pos": -1069.5, - "y_pos": -262.5 - }, - "858": { - "属性": "风神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/21/76373921/0e2edd0fd05bdcea0bf55f6199ca895a_5603430889581911175.gif", - "备注": "", - "x_pos": 625.0006223383425, - "y_pos": 595.5019704433498 - }, - "839": { - "属性": "风神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/21/76373921/6ddbe1809b8f13b696937f009ae7ba31_3494102935990581234.gif", - "备注": "收集三颗风种子或者使用风元素激活风种子产生风场,利用风场收集神瞳。", - "x_pos": 637.2934871754605, - "y_pos": 795.0310176454741 - }, - "165": { - "属性": "风神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/22/21665168/5b5f0eb24c0bb9093f839ee753e86873_5281396088957363427.gif", - "备注": "", - "x_pos": -931, - "y_pos": -404.29632719353094 - }, - "164": { - "属性": "风神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/22/21665168/560816ff86230c4752cde8c18adc38da_2799127768479756543.gif", - "备注": "", - "x_pos": -834, - "y_pos": -537.7670703263034 - }, - "163": { - "属性": "风神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/22/21665168/cbebf705e1212987f86022935a63d94b_2962405899946701809.gif", - "备注": "", - "x_pos": -731.5, - "y_pos": -490.1717979637931 - }, - "162": { - "属性": "风神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/22/21665168/6a8e6f7a9e3358d9b4aadb34f10a56db_2752851540887832021.gif", - "备注": "", - "x_pos": -385.5, - "y_pos": -196.51876204291034 - }, - "161": { - "属性": "风神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/22/21665168/142ed9e15c78f4e64bc0ed686cc12bdc_3420108624570441481.gif", - "备注": "", - "x_pos": -302, - "y_pos": -230.3330858215861 - }, - "160": { - "属性": "风神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/22/21665168/ffa29f79aed9dab17a0fdb5b42181f87_5627909948763941450.gif", - "备注": "藏在树叶里,一定要望清楚。", - "x_pos": -308, - "y_pos": -163.2175587048414 - }, - "134": { - "属性": "风神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/10/14/76373921/10534b259531987fd0b6222c4f9455f9_1915937069589438328.gif", - "备注": "", - "x_pos": 443.5, - "y_pos": -449 - }, - "133": { - "属性": "风神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/19/76373921/9ac65d5357e06034cf30417aab5ef1d7_6067053597102730250.gif", - "备注": "在悬崖上,需要跳下去捡。", - "x_pos": 1062, - "y_pos": -629.5 - }, - "132": { - "属性": "风神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/21/76373921/35fa153f0e5aa5c7d2f141239ebc9406_1325897500051831636.gif", - "备注": "", - "x_pos": 209, - "y_pos": 394 - }, - "131": { - "属性": "风神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/21/76373921/66c955e54c0c4dbac55c47dd818ba9a7_9114948938439204761.gif", - "备注": "使用风元素激活风种子产生风场,利用风场收集神瞳。", - "x_pos": 102, - "y_pos": 391 - }, - "130": { - "属性": "风神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/21/76373921/d3f671518f8f785b5091ebe6243ca800_4538580540662507879.gif", - "备注": "", - "x_pos": 286.05730802390826, - "y_pos": 555.5060495334828 - }, - "129": { - "属性": "风神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/21/76373921/e2433140fa8cde64cd44b5e90f185118_3061927369379320315.gif", - "备注": "", - "x_pos": 362.50382763372636, - "y_pos": 590.0141716833114 - }, - "128": { - "属性": "风神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/22/21665168/7133b878a1b60055d6b018b7ec9708ec_6197320491016987686.gif", - "备注": "在悬崖边下面,建议飞下去", - "x_pos": -319, - "y_pos": 131 - }, - "127": { - "属性": "风神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/22/21665168/8b3277d9187169d76a9b4f5037dce401_6706491041681976451.gif", - "备注": "藏在悬崖下面。", - "x_pos": -203, - "y_pos": 330 - }, - "126": { - "属性": "风神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/22/21665168/6b36162fdcf659955f1b29b7f50dad7f_6994973593330281318.gif", - "备注": "需要正确顺序收集种子开启风场", - "x_pos": -308.5, - "y_pos": -7.908486158744836 - }, - "124": { - "属性": "风神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/22/21665168/9283ebe824b4d41ecf85b0e6253bac77_538202390249227488.gif", - "备注": "", - "x_pos": -1132.5, - "y_pos": -229.69130254194488 - }, - "123": { - "属性": "风神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/21/76373921/58ea498a2df8d3a929e63bba0ae695f3_7991661368514890561.gif", - "备注": "", - "x_pos": -881, - "y_pos": -330.70330535926905 - }, - "122": { - "属性": "风神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/22/21665168/779e8789320a4af99f7c4d7f79ed7890_6517193530064431571.gif", - "备注": "", - "x_pos": -933.5, - "y_pos": -361.7273109939172 - }, - "121": { - "属性": "风神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/22/21665168/fdfec3d2a6be7ed82c88f949bd6366a1_522173916302655695.gif", - "备注": "藏在树里。", - "x_pos": -242, - "y_pos": 65.87846383375177 - }, - "120": { - "属性": "风神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/19/76373921/ca4f5085aa3f38d9d1181ecbf3cdee8b_4641106841585232557.gif", - "备注": "", - "x_pos": 814, - "y_pos": 79.5 - }, - "117": { - "属性": "风神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/19/76373921/8e3cbbfe0de89a3e00697e1a21f28071_4175826072546034072.gif", - "备注": "", - "x_pos": 1023, - "y_pos": -17.5 - }, - "115": { - "属性": "风神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/10/14/76373921/be1a0d2b6fb990157c55eca3e825662a_7866939288638921788.gif", - "备注": "", - "x_pos": 511.22063137116857, - "y_pos": 437.94524316855086 - }, - "114": { - "属性": "风神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/19/76373921/b5017e7de9953b1c87324665999d35bf_4145393686446844445.gif", - "备注": "", - "x_pos": 478.6085467210878, - "y_pos": 494.4307550093972 - }, - "113": { - "属性": "风神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/21/76373921/51daf4cdb9200f3c319d3b24dc88b56b_3742468746138299880.gif", - "备注": "", - "x_pos": 603.1671509809876, - "y_pos": 463.4204376839393 - }, - "112": { - "属性": "风神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/19/76373921/dabde510570eb7ce9320e227cd8095c8_8421697441205627271.gif", - "备注": "", - "x_pos": 516.5, - "y_pos": 13 - }, - "99": { - "属性": "风神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/21/76373921/992f2108d9b19472a8d368b7babbe5ae_4168731665053840805.gif", - "备注": "收集三颗风种子或者使用风元素激活风种子产生风场,利用风场收集神瞳", - "x_pos": 376.50186701502844, - "y_pos": 640.4980295566502 - }, - "95": { - "属性": "风神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/21/76373921/5d8f5d605beeeced438e4f682b976163_3987533745357986584.gif", - "备注": "", - "x_pos": 438.6482567114026, - "y_pos": 755.9138521570512 - }, - "93": { - "属性": "风神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/21/76373921/ec8d9c4d73227a91a3dd4742147e7485_4353719059385670811.gif", - "备注": "", - "x_pos": 852.5012446766859, - "y_pos": 845.5004926108377 - }, - "91": { - "属性": "风神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/21/76373921/9fd6847f719940ee503a0819bd312ee1_5406786667169067497.gif", - "备注": "收集该神瞳需要解锁剑冢的三层封印,详情请参考世界任务——探索剑冢封印", - "x_pos": 821.9993776616575, - "y_pos": 948.9985221674879 - }, - "90": { - "属性": "风神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/21/76373921/48a384d24c8f435e651aaaf1b9e273d8_6518040070865002555.gif", - "备注": "神瞳在悬崖下方", - "x_pos": 1172.2702688590616, - "y_pos": 830.5409528259893 - }, - "89": { - "属性": "风神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/10/14/76373921/357bf357786845db042159bc781515ac_7427367320019615865.gif", - "备注": "", - "x_pos": 840.5, - "y_pos": -484 - }, - "88": { - "属性": "风神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/19/76373921/486908479267a050e5fcb8e8acea3692_466608666518060779.gif", - "备注": "", - "x_pos": 892, - "y_pos": -181 - }, - "87": { - "属性": "风神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/19/76373921/8dcefeb7c777e6ca711ba53d5fb55d9e_976993417096589983.gif", - "备注": "", - "x_pos": 519.7085379916562, - "y_pos": 349.49801271936576 - }, - "86": { - "属性": "风神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/21/76373921/c92ec8dff3ee18d1a731feb300c174ab_5604801784480104215.gif", - "备注": "", - "x_pos": 361.0947739370349, - "y_pos": 400.93123061017695 - }, - "85": { - "属性": "风神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/22/21665168/59563dfb7d697abbcfc1835f1e7c102b_7821659766969342499.gif", - "备注": "", - "x_pos": -20, - "y_pos": 244 - }, - "84": { - "属性": "风神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/22/21665168/c247034a91b6d7d9289ccb2f2e12116f_8875494114136382303.gif", - "备注": "收集三个种子开启风场并且解锁风碑开启加速环即可√", - "x_pos": -306, - "y_pos": 707 - }, - "83": { - "属性": "风神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/29/21665168/901d3fb29e69377455d130156570bdd5_8637882940862091530.gif", - "备注": "难点在于如何想办法爬到卢老爷房顶上...荒星的一百种用法(", - "x_pos": -338.5, - "y_pos": 561 - }, - "82": { - "属性": "风神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/10/14/76373921/f3433d8d8031ffa147a681998df79d7a_5218887315681682261.gif", - "备注": "只要爬上去就能拿到了。", - "x_pos": -283, - "y_pos": 238 - }, - "81": { - "属性": "风神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/22/21665168/07ea7a4da5d8bbbf0d3c557c02d1147e_3176842945635427509.gif", - "备注": "", - "x_pos": -1006, - "y_pos": -142.8653433931447 - }, - "80": { - "属性": "风神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/22/21665168/e7060f05d871461a75a95b60ba721e7d_2089841828398520281.gif", - "备注": "找准位置利用风场和加速圈轻松获取。", - "x_pos": -266, - "y_pos": -317.2550675089792 - }, - "79": { - "属性": "风神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/22/21665168/71866e7ad6e5a05b23fd60428f85cd10_7823192415336553234.gif", - "备注": "", - "x_pos": -373, - "y_pos": -323.65154320955844 - }, - "78": { - "属性": "风神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/22/21665168/39f430abbe7752eada6dc15def179673_4999401363991100229.gif", - "备注": "依旧藏在石头后面。", - "x_pos": -365.5, - "y_pos": -372.799327897862 - }, - "77": { - "属性": "风神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/22/21665168/0abed4019490b4d62ee8c9ab95081e62_1473567454678905132.gif", - "备注": "在悬崖下面的柱子里面。", - "x_pos": -718, - "y_pos": -603.5033757923725 - }, - "76": { - "属性": "风神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/21/76373921/bbfc98b30998c376635f2a650e932a5c_2878748680097205899.gif", - "备注": "", - "x_pos": -1051, - "y_pos": -550.8450886389103 - }, - "75": { - "属性": "风神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/22/21665168/f3b1c42035332aece6c2d8e0fc52ac8c_4590034539054074685.gif", - "备注": "藏在蜿蜒曲折的缝隙里", - "x_pos": -988, - "y_pos": -383.715308176593 - }, - "74": { - "属性": "风神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/22/21665168/b741824da2cc7f48ac03aeb0b162932f_716139821476978575.gif", - "备注": "藏在岩石后面打碎就可以了。ps:用大剑更快", - "x_pos": -436.5, - "y_pos": -56.40435254944828 - }, - "73": { - "属性": "风神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/22/21665168/40dcfa29e6a95032669980ddf431f429_3066257973500691506.gif", - "备注": "", - "x_pos": -680.5, - "y_pos": 3.9137221096414123 - }, - "72": { - "属性": "风神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/22/21665168/0a8113bc80edfb9df12d6c52af5adc72_8276835375016036277.gif", - "备注": "需要上我们的凯亚帮忙造一下冰面。", - "x_pos": -468, - "y_pos": 661 - }, - "71": { - "属性": "风神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/21/76373921/fea71fe3a9a065ea04a8b3dedb98fbc3_344345432348298655.gif", - "备注": "", - "x_pos": 944.6556222741219, - "y_pos": 985.8949736466393 - }, - "70": { - "属性": "风神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/21/76373921/7daea163f3a01639e2e174205b601d41_1009418490777707416.gif", - "备注": "", - "x_pos": 707, - "y_pos": 1110 - }, - "68": { - "属性": "风神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/21/76373921/eae317417f4f8f41f5e0761154dff2c3_1051702848344068202.gif", - "备注": "", - "x_pos": 676.7465709616163, - "y_pos": 691.9680729950956 - }, - "67": { - "属性": "风神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/19/76373921/c11468205f7e8f2683b6786fd5374e4d_6411278277336054186.gif", - "备注": "", - "x_pos": 652.6421721161123, - "y_pos": 317.02097672776245 - }, - "66": { - "属性": "风神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/19/76373921/953d887f091578a372338dd52b17864c_8645806874444553189.gif", - "备注": "", - "x_pos": 516, - "y_pos": 256.5 - }, - "65": { - "属性": "风神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/22/21665168/0e562f909aac898137df61b1edaab709_4557505565462303841.gif", - "备注": "开启风场就可以飞上去 了~", - "x_pos": -59, - "y_pos": 471 - }, - "64": { - "属性": "风神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/22/21665168/0a6d434b69945b3164024d33b78bdde0_5832839350587041734.gif", - "备注": "虽然风瞳在最上面但是要到最下面激活风碑飞上去。", - "x_pos": -559, - "y_pos": -332.7168085287585 - }, - "63": { - "属性": "风神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/22/21665168/49de0265432412ea9d39c0f7cc2fa5c1_2206156493334154654.gif", - "备注": "", - "x_pos": -771.5, - "y_pos": 124.5 - }, - "62": { - "属性": "风神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/22/21665168/c85777858ff14bf01e2561609ee8093f_23517194600674915.gif", - "备注": "就在草丛中间√", - "x_pos": -559, - "y_pos": -125.34358828674476 - }, - "61": { - "属性": "风神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/22/21665168/f1ff4286ba10bf3de8bfdfc667118ddf_6175207438281676259.gif", - "备注": "激活风系尖碑开启风场获取,也可以顺便把相邻下方的风神瞳一并获取。", - "x_pos": -74.5, - "y_pos": -340 - }, - "60": { - "属性": "风神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/22/21665168/9299ae1982dc3cc9a1086b2c11086184_8578846124513791350.gif", - "备注": "", - "x_pos": -23.5, - "y_pos": -243.5 - }, - "59": { - "属性": "风神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/10/14/76373921/e1c8e427dec9c27113767a97789f2c5d_4665036776404972414.gif", - "备注": "", - "x_pos": 356, - "y_pos": -585.5 - }, - "58": { - "属性": "风神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/21/76373921/452d016e62949afc1c717c35eb7bf170_7767005666218883453.gif", - "备注": "", - "x_pos": 1438, - "y_pos": -334 - }, - "56": { - "属性": "风神瞳", - "gif_url": "https://uploadstatic.mihoyo.com/ys-obc/2020/09/21/76373921/cbd63e9fbb00160b045dafc424c1657f_1299454333660281584.gif", - "备注": "", - "x_pos": 1922.0018670150284, - "y_pos": 683.9995073891628 - } -} \ No newline at end of file diff --git a/services/db_context.py b/services/db_context.py index 54a8963c..3549ace6 100644 --- a/services/db_context.py +++ b/services/db_context.py @@ -1,5 +1,4 @@ - from gino import Gino from .log import logger from configs.config import bind, sql_name, user, password, address, port, database @@ -14,7 +13,6 @@ async def init(): i_bind = DATABASE_URI if DATABASE_URI else bind if not i_bind: i_bind = f"{sql_name}://{user}:{password}@{address}:{port}/{database}" - # print(i_bind) try: await db.set_bind(i_bind) await db.gino.create_all() diff --git a/services/service_config.py b/services/service_config.py index 69f6ac4e..2d89d872 100644 --- a/services/service_config.py +++ b/services/service_config.py @@ -4,7 +4,7 @@ DATABASE_URI = os.environ.get('DATABASE_URI') TL_M_KEY = os.environ.get('TL_KEY') -SYSTEM_PROXY = os.environ.get('SYSTEM_PROXY') +SYSTEM_M_PROXY = os.environ.get('SYSTEM_PROXY') ALAPI_M_TOKEN = os.environ.get('ALAPI_TOKEN') diff --git a/utils/browser.py b/utils/browser.py index 53c0a6b7..a953116f 100644 --- a/utils/browser.py +++ b/utils/browser.py @@ -1,11 +1,8 @@ from typing import Optional -import shutil from nonebot.log import logger from playwright.async_api import Browser, async_playwright import nonebot from nonebot import Driver -from appdirs import AppDirs -from pathlib import Path from services.log import logger @@ -22,7 +19,7 @@ async def init(**kwargs) -> Optional[Browser]: _browser = await browser.chromium.launch(**kwargs) return _browser except NotImplementedError: - logger.warning('win环境下 初始化playwright失败....请替换环境至linux') + logger.warning("win环境下 初始化playwright失败....请替换环境至linux") return None @@ -36,18 +33,6 @@ def install(): logger.info("正在检查 Chromium 更新") import sys from playwright.__main__ import main - sys.argv = ['', 'install', 'chromium'] + + sys.argv = ["", "install", "chromium"] main() - - -@driver.on_startup -def delete_pyppeteer(): - """删除 Pyppeteer 遗留的 Chromium""" - - dir = Path(AppDirs('pyppeteer').user_data_dir) - if not dir.exists(): - return - - shutil.rmtree(dir) - logger.info("已清理 Pyppeteer 依赖残留") - diff --git a/utils/data_utils.py b/utils/data_utils.py index 903c2d6e..b3c8cfa5 100644 --- a/utils/data_utils.py +++ b/utils/data_utils.py @@ -1,9 +1,16 @@ from models.group_member_info import GroupInfoUser +from typing import List -# 生成通用排行榜 -async def init_rank(users: list, all_user_data: list, group_id: int): - all_user_id = [user.user_qq for user in users] +async def init_rank(all_user_id: List[int], all_user_data: List[int], group_id: int) -> str: + """ + 说明: + 初始化通用的数据排行榜 + 参数: + :param all_user_id: 所有用户的qq号 + :param all_user_data: 所有用户需要排行的对应数据 + :param group_id: 群号,用于从数据库中获取该用户在此群的昵称 + """ rst = '' for i in range(len(all_user_id) if len(all_user_id) < 10 else 10): _max = max(all_user_data) @@ -11,7 +18,7 @@ async def init_rank(users: list, all_user_data: list, group_id: int): all_user_id.remove(max_user_id) all_user_data.remove(_max) try: - user_name = (await GroupInfoUser.select_member_info(max_user_id, group_id)).user_name + user_name = (await GroupInfoUser.get_member_info(max_user_id, group_id)).user_name except AttributeError: user_name = f'{max_user_id}' rst += f'{user_name}: {_max}\n' diff --git a/utils/image_utils.py b/utils/image_utils.py new file mode 100644 index 00000000..96ab81ca --- /dev/null +++ b/utils/image_utils.py @@ -0,0 +1,389 @@ +from configs.path_config import IMAGE_PATH, TTF_PATH +from PIL import Image, ImageFile, ImageDraw, ImageFont +from imagehash import ImageHash +from io import BytesIO +from matplotlib import pyplot as plt +from typing import Tuple, Optional +from pathlib import Path +import cv2 +import base64 +import imagehash + + +def compare_image_with_hash( + image_file1: str, image_file2: str, max_dif: int = 1.5 +) -> bool: + """ + 说明: + 比较两张图片的hash值是否相同 + 参数: + :param image_file1: 图片文件路径 + :param image_file2: 图片文件路径 + :param max_dif: 允许最大hash差值, 越小越精确,最小为0 + """ + ImageFile.LOAD_TRUNCATED_IMAGES = True + hash_1 = get_img_hash(image_file1) + hash_2 = get_img_hash(image_file2) + dif = hash_1 - hash_2 + if dif < 0: + dif = -dif + if dif <= max_dif: + return True + else: + return False + + +def get_img_hash(image_file: str) -> ImageHash: + """ + 说明: + 获取图片的hash值 + 参数: + :param image_file: 图片文件路径 + """ + with open(image_file, "rb") as fp: + hash_value = imagehash.average_hash(Image.open(fp)) + return hash_value + + +def compressed_image(in_file: str, out_file: str = None, ratio: float = 0.9): + """ + 说明: + 压缩图片 + 参数: + :param in_file: 被压缩的文件路径 + :param out_file: 压缩后输出的文件路径 + :param ratio: 压缩率,宽高 * 压缩率 + """ + in_file = Path(IMAGE_PATH) / in_file + out_file = Path(IMAGE_PATH) / out_file if out_file else in_file + h, w, d = cv2.imread(str(in_file.absolute())).shape + img = cv2.resize(cv2.imread(str(in_file.absolute())), (int(w * ratio), int(h * ratio))) + cv2.imwrite(str(out_file.absolute()), img) + + +def alpha2white_PIL(pic: Image) -> Image: + """ + 说明: + 将图片透明背景转化为白色 + 参数: + :param pic: 通过PIL打开的图片文件 + """ + img = pic.convert("RGBA") + width, height = img.size + for yh in range(height): + for xw in range(width): + dot = (xw, yh) + color_d = img.getpixel(dot) + if color_d[3] == 0: + color_d = (255, 255, 255, 255) + img.putpixel(dot, color_d) + return img + + +def pic2b64(pic: Image) -> str: + """ + 说明: + PIL图片转base64 + 参数: + :param pic: 通过PIL打开的图片文件 + """ + buf = BytesIO() + pic.save(buf, format="PNG") + base64_str = base64.b64encode(buf.getvalue()).decode() + return "base64://" + base64_str + + +def fig2b64(plt_: plt) -> str: + """ + 说明: + matplotlib图片转base64 + 参数: + :param plt_: matplotlib生成的图片 + """ + buf = BytesIO() + plt_.savefig(buf, format="PNG", dpi=100) + base64_str = base64.b64encode(buf.getvalue()).decode() + return "base64://" + base64_str + + +def is_valid(file: str) -> bool: + """ + 说明: + 判断图片是否损坏 + 参数: + :param file: 图片文件路径 + """ + valid = True + try: + Image.open(file).load() + except OSError: + valid = False + return valid + + +class CreateImg: + """ + 快捷生成图片与操作图片的工具类 + """ + + def __init__( + self, + w, + h, + paste_image_width=0, + paste_image_height=0, + color="white", + image_type="RGBA", + font_size=10, + background="", + ttf="yz.ttf", + ratio=1, + ): + """ + 参数: + :param w: 自定义图片的宽度,w=0时为图片原本宽度 + :param h: 自定义图片的高度,h=0时为图片原本高度 + :param paste_image_width: 当图片做为背景图时,设置贴图的宽度,用于贴图自动换行 + :param paste_image_height: 当图片做为背景图时,设置贴图的高度,用于贴图自动换行 + :param color: 生成图片的颜色 + :param image_type: 图片的类型 + :param font_size: 文字大小 + :param background: 打开图片的路径 + :param ttf: 字体,默认在 resource/ttf/ 路径下 + :param ratio: 倍率压缩 + """ + self.w = int(w) + 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.ttfont = ImageFont.truetype(TTF_PATH + ttf, int(font_size)) + if not background: + self.markImg = Image.new(image_type, (self.w, self.h), color) + else: + if not w and not h: + self.markImg = Image.open(background) + w, h = self.markImg.size + if ratio and ratio > 0 and ratio != 1: + self.w = int(ratio * w) + self.h = int(ratio * h) + self.markImg = self.markImg.resize( + (self.w, self.h), Image.ANTIALIAS + ) + else: + self.w = w + self.h = h + else: + self.markImg = Image.open(background).resize( + (self.w, self.h), Image.ANTIALIAS + ) + self.draw = ImageDraw.Draw(self.markImg) + self.size = self.w, self.h + + def paste( + self, + img: "CreateImg" or Image, + pos: Tuple[int, int] = None, + alpha: bool = False, + ): + """ + 说明: + 贴图 + 参数: + :param img: 已打开的图片文件,可以为 CreateImg 或 Image + :param pos: 贴图位置(左上角) + :param alpha: 图片背景是否为透明 + """ + if isinstance(img, CreateImg): + img = img.markImg + 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) + if alpha: + try: + self.markImg.paste(img, pos, img) + except ValueError: + img = img.convert("RGBA") + self.markImg.paste(img, pos, img) + else: + self.markImg.paste(img, pos) + self.current_w += self.paste_image_width + + def getsize(self, msg: str) -> Tuple[int, int]: + """ + 说明: + 获取文字在该图片 font_size 下所需要的空间 + 参数: + :param msg: 文字内容 + """ + return self.ttfont.getsize(msg) + + def text( + self, pos: Tuple[int, int], text: str, fill: Tuple[int, int, int] = (0, 0, 0) + ): + """ + 说明: + 在图片上添加文字 + 参数: + :param pos: 文字位置 + :param text: 文字内容 + :param fill: 文字颜色 + """ + self.draw.text(pos, text, fill=fill, font=self.ttfont) + + def save(self, path: str): + """ + 说明: + 保存图片 + 参数: + :param path: 图片路径 + """ + self.markImg.save(path) + + def show(self): + """ + 说明: + 显示图片 + """ + self.markImg.show(self.markImg) + + def resize(self, ratio: float = 0, w: int = 0, h: int = 0): + """ + 说明: + 压缩图片 + 参数: + :param ratio: 压缩倍率 + :param w: 压缩图片宽度至 w + :param h: 压缩图片高度至 h + """ + if not w and not h and not ratio: + raise Exception("缺少参数...") + if not w and not h and ratio: + w = int(self.w * ratio) + h = int(self.h * ratio) + self.markImg = self.markImg.resize((w, h), Image.ANTIALIAS) + self.w, self.h = self.markImg.size + self.size = self.w, self.h + self.draw = ImageDraw.Draw(self.markImg) + + def crop(self, box: Tuple[int, int, int, int]): + """ + 说明: + 裁剪图片 + 参数: + :param box: 左上角坐标,右下角坐标 (left, upper, right, lower) + """ + self.markImg = self.markImg.crop(box) + self.w, self.h = self.markImg.size + self.size = self.w, self.h + self.draw = ImageDraw.Draw(self.markImg) + + def check_font_size(self, word: str) -> bool: + """ + 说明: + 检查文本所需宽度是否大于图片宽度 + :param word: 文本内容 + """ + return self.ttfont.getsize(word)[0] > self.w + + def transparent(self, n: int = 0): + """ + 说明: + 图片透明化 + 参数: + :param n: 透明化大小内边距 + """ + self.markImg = self.markImg.convert("RGBA") + x, y = self.markImg.size + for i in range(n, x - n): + for k in range(n, y - n): + color = self.markImg.getpixel((i, k)) + color = color[:-1] + (100,) + self.markImg.putpixel((i, k), color) + + def pic2bs4(self) -> str: + """ + 说明: + CreateImg 转 base64 + """ + buf = BytesIO() + self.markImg.save(buf, format="PNG") + base64_str = base64.b64encode(buf.getvalue()).decode() + return base64_str + + def convert(self, type_: str): + """ + 说明: + 修改图片类型 + 参数: + :param type_: 类型 + """ + self.markImg = self.markImg.convert(type_) + + def rectangle( + self, + xy: Tuple[int, int, int, int], + fill: Optional[Tuple[int, int, int]] = None, + outline: str = None, + width: int = 1, + ): + """ + 说明: + 画框 + 参数: + :param xy: 坐标 + :param fill: 填充颜色 + :param outline: 轮廓颜色 + :param width: 线宽 + """ + self.draw.rectangle(xy, fill, outline, width) + + def line( + self, + xy: Tuple[int, int, int, int], + fill: Optional[Tuple[int, int, int]] = None, + width: int = 1, + ): + """ + 说明: + 画线 + 参数: + :param xy: 坐标 + :param fill: 填充 + :param width: 线宽 + """ + self.draw.line(xy, fill, width) + + def circle(self): + """ + 说明: + 将 CreateImg 图片变为圆形 + """ + self.convert("RGBA") + r2 = min(self.w, self.h) + if self.w != self.h: + self.resize(w=r2, h=r2) + r3 = int(r2 / 2) + imb = Image.new("RGBA", (r3 * 2, r3 * 2), (255, 255, 255, 0)) + pim_a = self.markImg.load() # 像素的访问对象 + pim_b = imb.load() + r = float(r2 / 2) + for i in range(r2): + for j in range(r2): + lx = abs(i - r) # 到圆心距离的横坐标 + ly = abs(j - r) # 到圆心距离的纵坐标 + l = (pow(lx, 2) + pow(ly, 2)) ** 0.5 # 三角函数 半径 + if l < r3: + pim_b[i - (r - r3), j - (r - r3)] = pim_a[i, j] + self.markImg = imb + + # + def getchannel(self, itype): + self.markImg = self.markImg.getchannel(itype) + + +if __name__ == "__main__": + pass diff --git a/utils/img_utils.py b/utils/img_utils.py deleted file mode 100644 index 3bbb2293..00000000 --- a/utils/img_utils.py +++ /dev/null @@ -1,339 +0,0 @@ -import os -from configs.path_config import IMAGE_PATH, TXT_PATH, TTF_PATH -from PIL import Image, ImageFile, ImageDraw, ImageFont -import cv2 -from typing import Tuple -import imagehash -import base64 -from io import BytesIO -from matplotlib import pyplot as plt - - -# 扫描图库id是否连贯 -def scan_img(path): - path = IMAGE_PATH + path - nolist = [] - length = len(os.listdir(path)) - print(length) - for i in range(length): - if i in nolist: - continue - img_path = path + "{}.jpg".format(i) - if not os.path.exists(img_path): - print("不存在=== " + str(length) + ".jpg -------> " + str(i) + ".jpg") - os.rename(path + "{}.jpg".format(length - 1), img_path) - nolist.append(length) - length -= 1 - - -# 比较hash值 -def compare_image_with_hash(image_file1, image_file2, max_dif=1.5): - """ - max_dif: 允许最大hash差值, 越小越精确,最小为0 - 推荐使用 - """ - ImageFile.LOAD_TRUNCATED_IMAGES = True - hash_1 = None - hash_2 = None - hash_1 = get_img_hash(image_file1) - hash_2 = get_img_hash(image_file2) - dif = hash_1 - hash_2 - if dif < 0: - dif = -dif - if dif <= max_dif: - return True - else: - return False - - -# 比较图片与hash值 -def compare_one_img_hash(image_file, hash_2, max_dif=1.5): - hash_1 = get_img_hash(image_file) - dif = hash_1 - hash_2 - if dif < 0: - dif = -dif - if dif <= max_dif: - return True - else: - return False - - -def get_img_hash(image_file): - with open(image_file, 'rb') as fp: - hash_value = imagehash.average_hash(Image.open(fp)) - return hash_value - - -# 压缩图片 -def rar_imgs(inpath, outpath, ratio=0.9, start=0, end=0, lens=0, maxsize=0.0, in_file_name='', out_file_name='', - itype='jpg'): - in_path = IMAGE_PATH + inpath + '/' - out_path = IMAGE_PATH + outpath + '/' - # scan_img(inpath) - l = [] - if in_file_name != '' and out_file_name != '': - filein = in_path + in_file_name + "." + itype - fileout = out_path + out_file_name + "." + itype - h, w, d = cv2.imread(filein).shape - width = int(w * ratio) - height = int(h * ratio) - ResizeImage(filein, fileout, width, height) - else: - if lens == 0: - lens = len(os.listdir(in_path)) - if end == 0: - end = lens - for i in range(start, end): - if i in l: - continue - if maxsize != 0: - if os.path.getsize(in_path + str(i) + ".jpg") > maxsize: - print("压缩----->", i, ".jpg") - filein = in_path + str(i) + ".jpg" - fileout = out_path + str(i) + ".jpg" - h, w, d = cv2.imread(filein).shape - width = int(w * ratio) - height = int(h * ratio) - ResizeImage(filein, fileout, width, height) - else: - continue - else: - print("压缩----->", i, ".jpg") - filein = in_path + str(i) + ".jpg" - fileout = out_path + str(i) + ".jpg" - h, w, d = cv2.imread(filein).shape - width = int(w * ratio) - height = int(h * ratio) - ResizeImage(filein, fileout, width, height) - - -# 压缩 -def ResizeImage(filein, fileout, width, height): - img = cv2.resize(cv2.imread(filein), (int(width), int(height))) - cv2.imwrite(fileout, img) - - -# 保存图片压缩后的hash值 -def save_img_hash(path, name): - for file in os.listdir(IMAGE_PATH + path): - if os.path.getsize(IMAGE_PATH + path + file) > 1024 * 1024 * 1.5: - compare_img_hash_in_txt(IMAGE_PATH + 'rar/' + file, name) - else: - compare_img_hash_in_txt(IMAGE_PATH + path + file, name) - - -# 比较色图hash值 -def compare_img_hash_in_txt(file, name, mode=1): - with open(TXT_PATH + name + ".txt", 'a+') as txtfile: - txtfile.seek(0) - hash_list = txtfile.read()[:-1].strip(",") - txtfile.seek(2) - with open(file, 'rb') as fp: - img_hash = str(imagehash.average_hash(Image.open(fp))) - if img_hash not in hash_list: - if mode == 1: - txtfile.write(img_hash + ",") - return False - return True - - -# 透明背景 -> 白色 -def alphabg2white_PIL(img): - img = img.convert('RGBA') - sp = img.size - width = sp[0] - height = sp[1] - for yh in range(height): - for xw in range(width): - dot = (xw, yh) - color_d = img.getpixel(dot) - if color_d[3] == 0: - color_d = (255, 255, 255, 255) - img.putpixel(dot, color_d) - return img - - -def pic2b64(pic: Image) -> str: - buf = BytesIO() - pic.save(buf, format='PNG') - base64_str = base64.b64encode(buf.getvalue()).decode() - return 'base64://' + base64_str - - -def fig2b64(plt: plt) -> str: - buf = BytesIO() - plt.savefig(buf, format='PNG', dpi=100) - base64_str = base64.b64encode(buf.getvalue()).decode() - return 'base64://' + base64_str - - -class CreateImg: - def __init__(self, - w, - h, - img_w=0, - img_h=0, - color='white', - image_type='RGBA', - font_size=10, - background='', - ttf='yz.ttf', - divisor=1): - self.w = int(w) - self.h = int(h) - self.img_w = int(img_w) - self.img_h = int(img_h) - self.current_w = 0 - self.current_h = 0 - self.ttfont = ImageFont.truetype(TTF_PATH + ttf, int(font_size)) - if not background: - self.markImg = Image.new(image_type, (self.w, self.h), color) - else: - if w == 0 and h == 0: - self.markImg = Image.open(background) - w, h = self.markImg.size - if divisor: - self.w = int(divisor * w) - self.h = int(divisor * h) - self.markImg = self.markImg.resize((self.w, self.h), Image.ANTIALIAS) - else: - self.w = w - self.h = h - else: - self.markImg = Image.open(background).resize((self.w, self.h), Image.ANTIALIAS) - self.draw = ImageDraw.Draw(self.markImg) - self.size = self.w, self.h - - # 贴图 - def paste(self, img, pos=None, alpha=False): - if isinstance(img, CreateImg): - img = img.markImg - if self.current_w == self.w: - self.current_w = 0 - self.current_h += self.img_h - if not pos: - pos = (self.current_w, self.current_h) - if alpha: - try: - self.markImg.paste(img, pos, img) - except ValueError: - img = img.convert("RGBA") - self.markImg.paste(img, pos, img) - else: - self.markImg.paste(img, pos) - self.current_w += self.img_w - return self.markImg - - def crop(self, box: Tuple[int, int, int, int]): - """ - 说明: - 裁剪图片 - 参数: - :param box: 左上角坐标,右下角坐标 (left, upper, right, lower) - """ - self.markImg = self.markImg.crop(box) - self.w, self.h = self.markImg.size - self.size = self.w, self.h - self.draw = ImageDraw.Draw(self.markImg) - - # 获取文字大小 - def getsize(self, msg): - return self.ttfont.getsize(msg) - - # 写字 - def text(self, pos, text, fill=(0, 0, 0)): - self.draw.text(pos, text, fill=fill, font=self.ttfont) - return self.markImg - - # 饼图 - def pieslice(self): - self.draw.pieslice((350, 50, 500, 200), -150, -30, 'pink', 'crimson') - return self.markImg - - # 保存 - def save(self, path): - self.markImg.save(path) - - # 显示 - def show(self): - self.markImg.show(self.markImg) - - # 压缩 - def resize(self, ratio=0, w=0, h=0): - if not w and not h and not ratio: - raise Exception('缺少参数...') - if not w and not h and ratio: - w = int(self.w * ratio) - h = int(self.h * ratio) - self.markImg = self.markImg.resize((w, h), Image.ANTIALIAS) - self.w, self.h = self.markImg.size - self.size = self.w, self.h - self.draw = ImageDraw.Draw(self.markImg) - - # 检查字体大小 - def check_font_size(self, word): - return self.ttfont.getsize(word)[0] > self.w - - # 透明化 - def transparent(self, n=0): - self.markImg = self.markImg.convert('RGBA') # 修改颜色通道为RGBA - x, y = self.markImg.size # 获得长和宽 - # 设置每个像素点颜色的透明度 - for i in range(n, x - n): - for k in range(n, y - n): - color = self.markImg.getpixel((i, k)) - color = color[:-1] + (100, ) - self.markImg.putpixel((i, k), color) - return self.markImg - - # 转bs4: - def pic2bs4(self): - buf = BytesIO() - self.markImg.save(buf, format='PNG') - base64_str = base64.b64encode(buf.getvalue()).decode() - return base64_str - - # - def convert(self, itype): - self.markImg = self.markImg.convert(itype) - - # 变圆 - def circle(self): - self.convert('RGBA') - r2 = min(self.w, self.h) - if self.w != self.h: - self.resize(w=r2, h=r2) - r3 = int(r2 / 2) - imb = Image.new('RGBA', (r3 * 2, r3 * 2), (255, 255, 255, 0)) - pima = self.markImg.load() # 像素的访问对象 - pimb = imb.load() - r = float(r2 / 2) - for i in range(r2): - for j in range(r2): - lx = abs(i - r) # 到圆心距离的横坐标 - ly = abs(j - r) # 到圆心距离的纵坐标 - l = (pow(lx, 2) + pow(ly, 2)) ** 0.5 # 三角函数 半径 - if l < r3: - pimb[i - (r - r3), j - (r - r3)] = pima[i, j] - self.markImg = imb - - # - def getchannel(self, itype): - self.markImg = self.markImg.getchannel(itype) - - - -if __name__ == '__main__': - pass - - - - - - - - - - - - diff --git a/utils/init_result.py b/utils/init_result.py deleted file mode 100644 index 92d4b0d0..00000000 --- a/utils/init_result.py +++ /dev/null @@ -1,88 +0,0 @@ -from configs.path_config import IMAGE_PATH, VOICE_PATH -from nonebot.adapters.cqhttp.message import MessageSegment -import os -from services.log import logger -import ujson - - -def image(img_name: str = None, path: str = '', abspath: str = None, b64: str = None): - if abspath: - if os.path.exists(abspath): - return MessageSegment.image("file:///" + abspath) - else: - return '' - elif b64: - if b64.find('base64://') != -1: - return MessageSegment.image(b64) - else: - return MessageSegment.image('base64://' + b64) - else: - img_name = str(img_name) - if img_name.find('http') == -1: - if len(img_name.split('.')) == 1: - img_name += '.jpg' - if os.path.exists(IMAGE_PATH + path + '/' + img_name): - return MessageSegment.image("file:///" + IMAGE_PATH + path + '/' + img_name) - else: - logger.warning(f"图片 {path}/{img_name}缺失.") - return '' - else: - return MessageSegment.image(img_name) - - -def at(qq): - return MessageSegment.at(qq) - - -def record(voice_name='', path=''): - if len(voice_name.split('.')) == 1: - voice_name += '.mp3' - if path == "": - name = VOICE_PATH + "{}.".format(voice_name) - else: - name = VOICE_PATH + "{}/{}".format(path, voice_name) - if voice_name.find('http') == -1: - if os.path.exists(name): - result = MessageSegment.record("file:///" + name) - return result - else: - logger.warning(f"语音{path}/{voice_name}缺失...") - return "" - else: - return MessageSegment.record(voice_name) - - -def text(msg): - return MessageSegment.text(msg) - - -def contact_user(qq): - return MessageSegment.contact_user(qq) - - -def share(url, title, content='', image_url=''): - return MessageSegment.share(url, title, content, image_url) - - -def xml(data): - return MessageSegment.xml(data) - - -def json(data): - data = ujson.dumps(data) - return MessageSegment.json(data) - - -def face(id_): - return MessageSegment.face(id_) - - -def poke(qq): - return MessageSegment('poke', {"qq": qq}) - - -def forward(): - return MessageSegment.forward() - -# if __name__ == '__main__': -# print(get_record_result("dadada", "", type="amr")) diff --git a/utils/message_builder.py b/utils/message_builder.py new file mode 100644 index 00000000..452e5356 --- /dev/null +++ b/utils/message_builder.py @@ -0,0 +1,151 @@ +from configs.path_config import IMAGE_PATH, VOICE_PATH +from nonebot.adapters.cqhttp.message import MessageSegment +from services.log import logger +from typing import Optional +from pathlib import Path +import os +import ujson + + +def image( + img_name: str = None, path: str = None, abspath: str = None, b64: str = None +) -> MessageSegment or str: + """ + 说明: + 生成一个 MessageSegment.image 消息 + 生成顺序:绝对路径(abspath) > base64(b64) > img_name + 参数: + :param img_name: 图片文件名称,默认在 resource/img 目录下 + :param path: 图片所在路径,默认在 resource/img 目录下 + :param abspath: 图片绝对路径 + :param b64: 图片base64 + """ + if abspath: + return ( + MessageSegment.image("file:///" + abspath) + if os.path.exists(abspath) + else "" + ) + elif b64: + return MessageSegment.image(b64 if "base64://" in b64 else "base64://" + b64) + else: + if "http" in img_name: + return MessageSegment.image(img_name) + if len(img_name.split(".")) == 1: + img_name += ".jpg" + file = ( + Path(IMAGE_PATH) / path / img_name if path else Path(IMAGE_PATH) / img_name + ) + if file.exists(): + return MessageSegment.image(f"file:///{file.absolute()}") + else: + logger.warning(f"图片 {file.absolute()}缺失...") + return "" + + +def at(qq: int) -> MessageSegment: + """ + 说明: + 生成一个 MessageSegment.at 消息 + 参数: + :param qq: qq号 + """ + return MessageSegment.at(qq) + + +def record(voice_name: str, path: str = None) -> MessageSegment or str: + """ + 说明: + 生成一个 MessageSegment.record 消息 + 参数: + :param voice_name: 音频文件名称,默认在 resource/voice 目录下 + :param path: 音频文件路径,默认在 resource/voice 目录下 + """ + if len(voice_name.split(".")) == 1: + voice_name += ".mp3" + file = ( + Path(VOICE_PATH) / path / voice_name if path else Path(VOICE_PATH) / voice_name + ) + if "http" in voice_name: + return MessageSegment.record(voice_name) + if file.exists(): + result = MessageSegment.record(f"file:///{file.absolute()}") + return result + else: + logger.warning(f"语音{file.absolute()}缺失...") + return "" + + +def text(msg: str) -> MessageSegment: + """ + 说明: + 生成一个 MessageSegment.text 消息 + 参数: + :param msg: 消息文本 + """ + return MessageSegment.text(msg) + + +def contact_user(qq: int) -> MessageSegment: + """ + 说明: + 生成一个 MessageSegment.contact_user 消息 + 参数: + :param qq: qq号 + """ + return MessageSegment.contact_user(qq) + + +def share( + url: str, title: str, content: str = None, image_url: str = None +) -> MessageSegment: + """ + 说明: + 生成一个 MessageSegment.share 消息 + 参数: + :param url: 自定义分享的链接 + :param title: 自定义分享的包体 + :param content: 自定义分享的内容 + :param image_url: 自定义分享的展示图片 + """ + return MessageSegment.share(url, title, content, image_url) + + +def xml(data: str) -> MessageSegment: + """ + 说明: + 生成一个 MessageSegment.xml 消息 + 参数: + :param data: 数据文本 + """ + return MessageSegment.xml(data) + + +def json(data: str) -> MessageSegment: + """ + 说明: + 生成一个 MessageSegment.json 消息 + 参数: + :param data: 消息数据 + """ + return MessageSegment.json(data) + + +def face(id_: int) -> MessageSegment: + """ + 说明: + 生成一个 MessageSegment.face 消息 + 参数: + :param id_: 表情id + """ + return MessageSegment.face(id_) + + +def poke(qq: int) -> MessageSegment: + """ + 说明: + 生成一个 MessageSegment.poke 消息 + 参数: + :param qq: qq号 + """ + return MessageSegment("poke", {"qq": qq}) diff --git a/utils/user_agent.py b/utils/user_agent.py index 8cf7d94f..94d400c4 100644 --- a/utils/user_agent.py +++ b/utils/user_agent.py @@ -39,9 +39,9 @@ user_agent = [ "Openwave/ UCWEB7.0.2.37/28/999", "Mozilla/4.0 (compatible; MSIE 6.0; ) Opera/UCWEB7.0.2.37/28/999", # iPhone 6: - "Mozilla/6.0 (iPhone; CPU iPhone OS 8_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/8.0 Mobile/10A5376e Safari/8536.25" + "Mozilla/6.0 (iPhone; CPU iPhone OS 8_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/8.0 Mobile/10A5376e Safari/8536.25", ] def get_user_agent(): - return {'User-Agent': random.choice(user_agent)} + return {"User-Agent": random.choice(user_agent)} diff --git a/utils/utils.py b/utils/utils.py index b0b7b0bd..668cd7e6 100644 --- a/utils/utils.py +++ b/utils/utils.py @@ -1,104 +1,128 @@ -import time from datetime import datetime, timedelta from collections import defaultdict from nonebot import require -import nonebot -import json -import pytz from configs.path_config import TXT_PATH -from configs.config import system_proxy +from configs.config import SYSTEM_PROXY +from typing import List, Union +from nonebot.adapters import Bot +import nonebot +import pytz import pypinyin import aiohttp +import time + +try: + import ujson as json +except ModuleNotFoundError: + import json -scheduler = require('nonebot_plugin_apscheduler').scheduler +scheduler = require("nonebot_plugin_apscheduler").scheduler -# 次数检测 class CountLimiter: - def __init__(self, max): - self.count = defaultdict(int) - self.max = max + """ + 次数检测工具,检测调用次数是否超过设定值 + """ - def add(self, key): + def __init__(self, max_count: int): + self.count = defaultdict(int) + self.max_count = max_count + + def add(self, key: Union[str, int, float]): self.count[key] += 1 - def check(self, key) -> bool: - if self.count[key] >= self.max: + def check(self, key: Union[str, int, float]) -> bool: + if self.count[key] >= self.max_count: self.count[key] = 0 return True return False -# 用户正在执行此命令 class UserExistLimiter: + """ + 检测用户是否正在调用命令 + """ + def __init__(self): - self.mbool = defaultdict(bool) + self.flag_data = defaultdict(bool) self.time = time.time() - def set_True(self, key): + def set_True(self, key: Union[str, int, float]): self.time = time.time() - self.mbool[key] = True + self.flag_data[key] = True - def set_False(self, key): - self.mbool[key] = False + def set_False(self, key: Union[str, int, float]): + self.flag_data[key] = False - def check(self, key): + def check(self, key: Union[str, int, float]) -> bool: if time.time() - self.time > 30: self.set_False(key) return False - return self.mbool[key] + return self.flag_data[key] -# 命令cd class FreqLimiter: - def __init__(self, default_cd_seconds): + """ + 命令冷却,检测用户是否处于冷却状态 + """ + + def __init__(self, default_cd_seconds: int): self.next_time = defaultdict(float) self.default_cd = default_cd_seconds - def check(self, key) -> bool: + def check(self, key: Union[str, int, float]) -> bool: return time.time() >= self.next_time[key] - def start_cd(self, key, cd_time=0): - self.next_time[key] = time.time() + (cd_time if cd_time > 0 else self.default_cd) + def start_cd(self, key: Union[str, int, float], cd_time: int = 0): + self.next_time[key] = time.time() + ( + cd_time if cd_time > 0 else self.default_cd + ) - def left_time(self, key) -> float: + def left_time(self, key: Union[str, int, float]) -> float: return self.next_time[key] - time.time() static_flmt = FreqLimiter(15) -# 恶意触发命令检测 class BanCheckLimiter: + """ + 恶意命令触发检测 + """ + def __init__(self, default_check_time: float = 5, default_count: int = 4): self.mint = defaultdict(int) self.mtime = defaultdict(float) self.default_check_time = default_check_time self.default_count = default_count - def add(self, key): + def add(self, key: Union[str, int, float]): if self.mint[key] == 1: self.mtime[key] = time.time() self.mint[key] += 1 - def check(self, key) -> bool: - # print(self.mint[key]) - # print(time.time() - self.mtime[key]) + def check(self, key: Union[str, int, float]) -> bool: if time.time() - self.mtime[key] > self.default_check_time: self.mtime[key] = time.time() self.mint[key] = 0 return False - if self.mint[key] >= self.default_count and time.time() - self.mtime[key] < self.default_check_time: + if ( + self.mint[key] >= self.default_count + and time.time() - self.mtime[key] < self.default_check_time + ): self.mtime[key] = time.time() self.mint[key] = 0 return True return False -# 每日次数 class DailyNumberLimiter: - tz = pytz.timezone('Asia/Shanghai') + """ + 每日调用命令次数限制 + """ + + tz = pytz.timezone("Asia/Shanghai") def __init__(self, max_num): self.today = -1 @@ -123,7 +147,13 @@ class DailyNumberLimiter: self.count[key] = 0 -def is_number(s) -> bool: +def is_number(s: str) -> bool: + """ + 说明: + 检测 s 是否为数字 + 参数: + :param s: 文本 + """ try: float(s) return True @@ -131,6 +161,7 @@ def is_number(s) -> bool: pass try: import unicodedata + unicodedata.numeric(s) return True except (TypeError, ValueError): @@ -138,115 +169,156 @@ def is_number(s) -> bool: return False -# 获取bot -def get_bot(): +def get_bot() -> Bot: + """ + 说明: + 获取 bot 对象 + """ return list(nonebot.get_bots().values())[0] -def get_message_at(data: str) -> list: +def get_message_at(data: str) -> List[int]: + """ + 说明: + 获取消息中所有的 at 对象的 qq + 参数: + :param data: event.json() + """ qq_list = [] data = json.loads(data) - try: - for msg in data['message']: - if msg['type'] == 'at': - qq_list.append(int(msg['data']['qq'])) - return qq_list - except Exception: - return [] + for msg in data["message"]: + if msg["type"] == "at": + qq_list.append(int(msg["data"]["qq"])) + return qq_list -def get_message_imgs(data: str) -> list: +def get_message_imgs(data: str) -> List[str]: + """ + 说明: + 获取消息中所有的 图片 的链接 + 参数: + :param data: event.json() + """ img_list = [] data = json.loads(data) - try: - for msg in data['message']: - if msg['type'] == 'image': - img_list.append(msg['data']['url']) - return img_list - except Exception: - return [] + for msg in data["message"]: + if msg["type"] == "image": + img_list.append(msg["data"]["url"]) + return img_list def get_message_text(data: str) -> str: + """ + 说明: + 获取消息中 纯文本 的信息 + 参数: + :param data: event.json() + """ data = json.loads(data) - result = '' - try: - for msg in data['message']: - if msg['type'] == 'text': - result += msg['data']['text'].strip() + ' ' - return result.strip() - except Exception: - return '' + result = "" + for msg in data["message"]: + if msg["type"] == "text": + result += msg["data"]["text"].strip() + " " + return result.strip() -def get_message_type(data: str) -> str: - return json.loads(data)['message_type'] - - -def get_message_record(data: str) -> str: +def get_message_record(data: str) -> List[str]: + """ + 说明: + 获取消息中所有 语音 的链接 + 参数: + :param data: event.json() + """ + record_list = [] data = json.loads(data) - try: - for msg in data['message']: - if msg['type'] == 'record': - return msg['data']['url'] - return '' - except Exception: - return '' + for msg in data["message"]: + if msg["type"] == "record": + record_list.append(msg["data"]["url"]) + return record_list -def get_message_json(data: str) -> dict: +def get_message_json(data: str) -> List[dict]: + """ + 说明: + 获取消息中所有 json + 参数: + :param data: event.json() + """ + json_list = [] data = json.loads(data) - try: - for msg in data['message']: - if msg['type'] == 'json': - return msg['data'] - return {} - except Exception: - return {} - - -def add_to_16(value): - while len(value) % 16 != 0: - value += '\0' - return str.encode(value) + for msg in data["message"]: + if msg["type"] == "json": + json_list.append(msg["data"]) + return json_list # 获取文本加密后的cookie def get_cookie_text(cookie_name: str) -> str: - with open(TXT_PATH + "cookie/" + cookie_name + ".txt", 'r') as f: + """ + 说明: + 获取 txt/cookie 目录下指定 cookie 的内容 + 参数: + :param cookie_name: cookie文件名称 + """ + with open(TXT_PATH + "cookie/" + cookie_name + ".txt", "r") as f: return f.read() -# 获取本地http代理 def get_local_proxy(): - return system_proxy if system_proxy else None + """ + 说明: + 获取 config.py 中设置的代理 + """ + return SYSTEM_PROXY if SYSTEM_PROXY else None -# 判断是否为中文 -def is_Chinese(word): +def is_Chinese(word: str) -> bool: + """ + 说明: + 判断字符串是否为纯中文 + 参数: + :param word: 文本 + """ for ch in word: - if '\u4e00' <= ch <= '\u9fff': - return True - return False + if not "\u4e00" <= ch <= "\u9fff": + return False + return True -async def user_avatar(qq): - url = f'http://q1.qlogo.cn/g?b=qq&nk={qq}&s=160' +async def user_avatar(qq: int) -> bytes: + """ + 说明: + 快捷获取用户头像 + 参数: + :param qq: qq号 + """ + url = f"http://q1.qlogo.cn/g?b=qq&nk={qq}&s=160" async with aiohttp.ClientSession() as session: async with session.get(url, proxy=get_local_proxy(), timeout=5) as response: return await response.read() -async def group_avatar(group_id): - url = f'http://p.qlogo.cn/gh/{group_id}/{group_id}/640/' +async def group_avatar(group_id: int) -> bytes: + """ + 说明: + 快捷获取用群头像 + 参数: + :param group_id: 群号 + """ + url = f"http://p.qlogo.cn/gh/{group_id}/{group_id}/640/" async with aiohttp.ClientSession() as session: async with session.get(url, proxy=get_local_proxy(), timeout=5) as response: return await response.read() -def cn2py(word) -> str: +def cn2py(word: str) -> str: + """ + 说明: + 将字符串转化为拼音 + 参数: + :param word: 文本 + """ temp = "" for i in pypinyin.pinyin(word, style=pypinyin.NORMAL): - temp += ''.join(i) + temp += "".join(i) return temp -