diff --git a/.gitignore b/.gitignore index 4f753bbb3..7b9fe23bb 100644 --- a/.gitignore +++ b/.gitignore @@ -142,4 +142,6 @@ test.py server.py member_activity_handle.py Yu-Gi-Oh/ - +search_image/ +black_word/ +csgo/ diff --git a/README.md b/README.md index c37ec3a7e..af826ec24 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ ## 未完成的文档 -[传送门](https://hibikier.github.io/zhenxun_bot/) +# [传送门](https://hibikier.github.io/zhenxun_bot/) ## 真寻的帮助 请对真寻说: '真寻帮助' or '管理员帮助' or '超级用户帮助' or '真寻帮助 指令' @@ -29,6 +29,17 @@ [AkashiCoin/nonebot_plugins_zhenxun_bot](https://github.com/AkashiCoin/nonebot_plugins_zhenxun_bot) +## 来点优点? +一.作为bot: + * 实现了许多功能,且提供了大量功能管理命令 + * __..... 更多详细请通过`传送门`查看文档!__ + +二.作为框架?: + * 通过Config配置项将所有插件配置统计保存至config.yaml,利于统一用户修改 + * 方便增删插件,原生nonebot2 matcher,不需要额外修改,仅仅通过简单的配置属性就可以生成`帮助图片`和`帮助信息` + * 提供了cd,阻塞,每日次数等限制,仅仅通过简单的属性就可以生成一个限制,例如:`__plugin_cd_limit__` + * __..... 更多详细请通过`传送门`查看文档!__ + ## 功能列表
@@ -210,6 +221,20 @@ python bot.py ## 更新 +### 2021/1/5 \[v0.0.7.2] + +* 提供金币消费hook,可在plugins2settings.yaml中配置该功能需要消费的金币 +* 商店插件将作为内置插件移动至basic_plugins +* 商店插件通过export提供了方法,不需要修改商店插件代码添加商品数据和生效方法 +* 修改了hook插件顺序,主要以auth_hook为主 +* 修改商店图片样式 +* 取消每次启动更新城市列表(首次除外),采用定时更新,加快bot启动速度 +* 取消每次启动时截取今日素材,采用调用时截取保存,加快bot启动速度 +* 更新色图时当图片404时会删除并替换 +* 疫情消息回复改为图片 +* 修复商店折扣和限时时间无法生效 +* 修复原神玩家查询尘歌壶缺少图片 + ### 2021/12/26 * 修复群词条问题 空格 会被录入导致不断回复 @@ -221,7 +246,7 @@ python bot.py ### 2021/12/20 -* 只有当真的发布新动态/视频的时候才获取并推送 [@pull/96](https://github.com/HibiKier/zhenxun_bot/pull/96) +* 只有发布小于存储时间的新动态/视频的时候才获取并推送 [@pull/96](https://github.com/HibiKier/zhenxun_bot/pull/96) ### 2021/12/16 \[v0.0.7.0] @@ -246,366 +271,26 @@ python bot.py * 修改了权限插件加载顺序防止小概率优先加载权限插件引起报错 * 本地图库新图库会统一建立在resource/img/image_management文件夹下,如果该文件夹内未找到图库,会从上级目录查找(即:resource/img/) -### 2021/12/1 \[v0.0.6.5/6] - -* 群权限-1时超级用户命令依旧生效 -* 修复以真寻为开头的词条不会被回复 -* 修复购买道具可以为负数 -* P站排行/搜图提供配置项,将略过大于指定张数的作品 -* 昵称提供关键词屏蔽配置项,会将指定关键词替换为“*” -* 取消了自动更新,改为自动检测版本 -* 自动更新不再覆盖config.py和移动config.yaml - -### 2021/11/29 \[v0.0.6.4] - -* 新增cos图撤回配置项 -* 新增默认群权限配置项 -* 修复权限等级类配置无法正常应用 - -### 2021/11/24 \[v0.0.6.3] - -* 修复在线搜索色图出错 -* 修复pix无法正确获取HIBIAPI - -### 2021/11/23 \[v0.0.6.2] - -* 替换cos API -* 提供私聊b了,即跨群b了用户 -* 修复游戏抽卡导入角色失败(原神) -* 修复无Pixiv代理时报错 -* 将项目中大部分aiohttp替换为httpx -* 删除了丘丘人翻译插件 -* 新增群词条 -* 修复游戏抽卡碧蓝航线bwiki格式更改导致获取报错 -* 首次启动会生成配置文件后停止程序,配置后再次启动即可 - -### 2021/11/18 - -* 修复超级用户无法正确拉真寻入群 - -### 2021/11/14 - -* 修复功能总开关无法正确开启 - -### 2021/11/12 - -* 修复PIX无法url无法正确获取 +
-### 2021/11/10 - -* 修复PIX表重复创建导致首次无法运行 -* 检测Omage图库改为命令方式:检测omega图库 - -### 2021/11/9 - -* 修复管理员帮助无法正常响应 -* 修复被ban时会一直回复被ban提醒 - -### 2021/11/5 - -* 修复ai没有图灵key时报错 -* 提供图片路径resource/img/background/check - -### 2021/11/4 - -* 通用排行榜改用图片消息,且可以自定义排行榜人数 -* 优化CreateMat排行榜数据显示 -* 修复了pix更新多余参数导致失败的问题 -* 修复滴滴滴-注入风险 -* 修复无法正常关闭滴滴滴,戳一戳 -* 添加了发送图片撤回配置项WITHDRAW_IMAGE_TIME -* 修复了天气regex文本过长时会正则匹配过久导致nb卡顿 -* message_build新增custom_forward_msg用于快捷生成转发消息 -* 插件配置改为yaml存储,新增Config,用于获取和新增插件配置 -* 新增 当插件加载失败时,会发送消息提醒超级用户,且在功能状态中对应失败插件写上[ERROR] -* 修复当查看-spuer插件帮助时无法正确回复 -* 群内帮助图片会随群内功能开关和插件总开关变化 -* 自检改为图像形式 -* 更新色图删除了rar_setu,r18_rar和rar文件夹,压缩将统一在temp文件夹 -* 更新色图只有在有更新数量或报错时才会提醒超级用户 -* 群欢迎消息加入cd -* 加入资源管理resources_manager -* 新增 好友请求/群聊邀请 控制命令 - -### 2021/10/15 - -* 适配了原神资源查询米游社地图返回的新格式 - -### 2021/10/8 - -* 修复疫情省份查询失效 -* 修复功能调用统计全局下统计可能发生错误 - -### 2021/10/4 - -* 修复了功能调用统计失效问题 -* 当色图库中没有色图时,会在线搜索色图而不是‘没找到符合条件的色图...’ -* 快速更新权限再给超级用户发送错误日志 -* 修复疫情未加载省份城市无法正常使用 - -### 2021/10/3 - -* 对插件进行分离 -* 重写了插件与限制管理器以及帮助获取 -* 修改一些插件目录和数据存储目录 -* 插件通用配置与限制数据将以ymal文件存储 \[路径:data/configs] -* 所有商店相关操作调用统计合并为商店(包括之前已经保存的数据,会先进行备份) -* 简化了点歌的代码相关 -* 修复了碧蓝航线抽卡新框导致报错无法正常初始化 -* 修复了P站排行/搜图在PC端无法正常显示 -* 添加了插件对超级用户是否限制的配置 ‘limit_superuser’ -* 添加命令 ‘重载插件配置’,用于生效手动修改配文件 -* 超级用户帮助可以添加 -super 来显示该插件的超级用户帮助,示例:帮助.ban -super -* 原神黄历改为网页截图 -* 修改了鲁迅说逻辑结构 -* 修改了统计图表样式,改为自定义CreateMat -* 节日红包不再被24小时限制,群内多个节日红包将会覆盖 -* 当群权限为-1时,不会对群发送修改权限通知,并屏蔽此群一切命令(包括提醒) -* 修复了红包数量可以过大或为负数,红包数量大于群员数量时会修改为群员数量 -* 修复了负数开箱 -* 签到最低好感度设置为0.01 [@pull/53](https://github.com/HibiKier/zhenxun_bot/pull/53) -* pip安装新依赖 ruamel.yaml -* 修复功能 EPIC [@pull/58](https://github.com/HibiKier/zhenxun_bot/pull/58) - -### 2021/9/10 - -* 修复撤回消息有时无法正确获取消息id - -### 2021/9/9 - -* 替换coser API -* 修复签到uid可能不默认为0 -* 修复签到可能重复的问题 -* 修复无订阅时递归出错 -* 启用了plugins2info_dict, plugins2cd_dict, plugins2exists_dict配置文件,通过USE_CONFIG_FILE=True开启 -* 修复涩图local_id会被固定为50 -* 优化图库数量查询 -* 修复原神大地图过大无法打开报错 -* 修复无法显示正确的涩图上限 - -### 2021/9/7 - -* 修改 update_info.json -* 修改 更新信息 图片大小 -* 修复 查看订阅 命令 UP和番剧无法正常显示 -* 修复订阅推送无法正确推送 -* 修复搜图返回列表为空时无法正确回复 [@pull/40](https://github.com/HibiKier/zhenxun_bot/pull/40) - -### 2021/9/5 -* 添加配置PIX_IMAGE_SIZE,调整PIX下载图片大小,当设置的图片404时,改为原图 -* 新增配置DEFAULT_GROUP_LEVEL,默认群等级 -* 新增超级用户功能 super ban,将屏蔽被ban用户的所有消息,指令:b了 -* b站转发解析支持纯BV号解析,且五分钟内不会解析相同url -* 俄罗斯轮盘新增 连胜/最高连胜/连败/最高连败 纪录,新增 最高连胜排行榜/最高连败排行榜 -* 增加扩展图库 OmegaPixivIllusts,不想自己找图的人福音([Ailitonia](https://github.com/Ailitonia) 佬的高质量精品手筛图库)([传送门](https://github.com/Ailitonia/omega-miya/blob/master/archive_data/db_pixiv.7z) ),可以手动导入图库,也可以将解压文件放在bot.py同级目录重启bot -* 增加配置PIX_OMEGA_PIXIV_RATIO,PIX功能发送PIX图库和扩展图库OmegaPixivIllusts图片的比例,如果没有使用扩展图库OmegaPixivIllusts,请设置为(10, 0) -* 增加配置WITHDRAW_PIX_TIME,用于配置在开关PIX图片在群私聊的自动撤回 -* 上传图库cases, 开箱 也可以连抽(未更新过没有价格) -* 新增命令 查看群白名单 -* plugins2info_dict新增键"default_status",设置加入新群时功能的默认开关状态 -* 增加配置plugins2exists_dict,可自定义是否阻塞某命令同时触发多次 -* 增加配置plugins2cd_dict,可自定义为命令添加cd -* 新增B站订阅(直播/番剧/UP)[测试],提供命令:添加订阅 [主播/UP/番剧] [id/链接/番名],删除订阅 [id],查看订阅 -* 优化pix和色图的数据库查询 -* 触发已关闭的功能的正则时不再触发ai -* 更换coser API -* PIX搜索pid功能在群聊无法搜索PIX图库的r18和OmegaPixivIllusts的r15以及r18,超级用户除外 -* PIX单次搜索的图片张数超级用户限制为至多30张,普通用户10张 -* PIX超级用户新增-s,-r,可以通过pix -s 查看图库的涩图,pix -r查看图库的r18图,支持搜索,当然,pix图库只区分了r18和非r18,如果-s查询到不色的图也问题不大 -* 优化P站排行和搜图,现在需要艾特,改为使用HIBIAPI,在群内时将使用合并消息(群聊搜图会屏蔽R-18) -* win10下playwright相关功能无法使用,但是不再需要删除文件 -* 签到大改,优化签到方式与逻辑,改为图片形式发送,有概率额外获得随机道具(好感度有加成) -* 修改撤回功能,改为回复撤回,回复发送撤回 -* 更改logging为loguru -* 删除了 发送图片 中的 [N]张图[keyword] 功能 -* 修复私聊 关闭[功能] 默认不为 全部 而要添加参数 ‘a’ -* 修复0权限用户可以修改禁言检测相关设置 - -### 2021/8/17 - -* 新增配置CHECK_NOTICE_INFO_CD,修改群权限,个人权限检测各种检测的提示消息cd -* 新增功能‘连续上传图片’功能,直到输入‘stop’停止 -* 新增功能维护时白名单以及对应命令(白名单中的群聊不受维护限制) -* 新增ALAPI功能,微博热搜,可以通过序号来查看部分热搜内容 -* 新增配置NICKNAME,偶尔也想换换名字的说(自我介绍仅当NICKNAME=真寻时生效) -* 提供 更新信息 命令,可以使群员查看更新内容(可开关,与其他功能无异,但不会被统计,该命令图片由自动更新生成) -* 超级用户可以通过私聊来对指定qq进行ban/unban -* 超级用户帮助改为图片形式 -* 公开图库删除‘色图’ -* 群权限检测,个人权限检测,功能开关检测合并,权限检测顺序:个人权限 > 群权限 > 插件开关 >超级用户禁用某群插件 > 超级用户限制群里插件 > 插件维护检测 -* 重写群功能管理,超级用户可对群/私聊分别禁用,也可禁用指定群指定功能,新增命令‘功能状态’,超级用户关闭功能提供参数(默认ALL):group/g(群聊),private/p(私聊) -* 超级用户不会被任何权限等检测阻挡 -* 不会重复复读,复读消息只会发送一次 -* b站转发解析支持b23.tv,www.bilibili.com链接,cv专栏(playwright截图,压缩倍率0.5,较慢且文字可能不清晰,后优化) -* 我有一个朋友功能,当艾特一个对象时,‘朋友’改为艾特对象的群名片或昵称 -* 修复‘上传/删除/移动图片’目录不正确 -* 修复天气功能,当城市名在‘天气’后时报错 -* 修复配置INITIAL_SETU_PROBABILITY不生效 - -### 2021/8/10 - -* 重复的好友请求和邀群提示在5分钟内不会重复提示 -* 疫情会优先检查城市,城市省份市区重名时请添加‘市’ -* 添加命令‘原神资源查找’,‘设置cookie’ -* 添加配置AUTO_UPDATE_ZHENXUN,是否自动更新真寻,默认True -* 添加配置MAX_RUSSIAN_BET_GOLD,俄罗斯轮盘赌注最大金额,默认1000 -* 检查更新真寻定时任务时间改为12 : 00 -* 添加功能能不能好好说话(nbnhhsh) -* 添加功能随机roll,无参为数字,有参为随机参数 -* 添加linux重启脚本以及重启命令‘重启’(建议首次生成restart.sh先查看命令是否正确) -* 修复管理员功能的权限检测 -* 修复丢人爬开关 - -### 2021/8/6 - -* 天气查询会优先遍历城市 -* 添加自动更新真寻命令 - -##### 如果你的版本为 2021/8/4,可以直接复制plugins/check_zhenxun_update后,通过指令来更新真寻 - -### 2021/8/4 - -* 修改天气与疫情城市数据,改为api获取,丰富疫情的回复消息 -* 原神资源查询,大地图将被压缩至9M,且启动时当大地图存在时不再自动更新地图 -* 下载数据库内色图时将直接存储至_setu,不再存储至临时文件 -* 重复的好友请求或邀请请求在一定时间不会重复发送提醒 -* 添加每日自动清理临时图片定时任务 -* 修复金币排行显示 -* 修复无法正常关闭戳一戳功能 - -### 2021/7/30 - -* 重构代码,进行优化,添加注释,删除冗余代码,降低代码耦合 -* 添加功能:PIX(一套快捷的pixiv存图命令,自建图库存储url等信息?意在获取自己或群友xp的图) -* 添加功能:清理临时图片文件(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 - * 替换bot.py - -### 2021/7/27 - -* 原神今日素材改为单张截图+拼图,更新文件utils/img_utils.py及plugins/genshin/material_remind/__init__.py - -### 2021/7/26 - -* 修复原神今日素材稻妻城开放后截图不完整的问题 - -### 2021/7/14 - -* 原神今日素材自动更新时间由 00:01 -> 04:01 [#issues7](https://github.com/HibiKier/zhenxun_bot/issues/7) -* 小问题的修复和优化 - -### 2021/7/12 - -* 修复开箱功能单抽出金时存储格式错误导致 ‘我的金色’ 无法正常发送图片 -* 小问题的修复和优化 - -### 2021/7/6 - -* 识番功能 trace.moe 替换为新API(旧API已失效) -* 小问题的修复和优化 - -### 2021/6/30 - -* 将plugin2name和plugin2level合并为plugin2info -* util改为utils(。。!) -* 修复当用户发送速度极快时开箱会突破每日限制 -* 新增功能:通过PID获取图片 -* 发送图片新增功能:搜索图片 -* 功能统计可视化 -* 新增命令:好感度总排行 -* 原神每日素材改为从"可莉特调"截图,提供命令‘更新原神每日素材’和定时任务 -* 修复月功能统计错误的问题 - -### 2021/6/24 - -* 添加了一些ALAPI:网易云热评,获取b站视频封面,古诗(需要填写ALAPI_TOKEN) -* 如果填写了ALAPI_TOKEN,将会检测备用接口回复的文本是否合规 -* 优化了色图,当搜索色图下载失败时,会从本地色图库中发送相关tag色图 -* 当网易云点歌繁忙时会尝试多次点歌 - -### 2021/6/23 - -* 添加功能:群权限(所以说内鬼都快爬,可以在configs/config.py中修改各个功能的权限等级) -* 优化了数据统计,将以7天,30天为周期,为将来更方便实现数据可视化 -* 更新坎公骑冠剑UP卡池 -* 修复赛马娘UP卡池 -* 修复一些小问题 - -### 2021/6/18 - -* 修复p站排行,搜图因网络问题爆炸时没有具体回复 -* 更换色图显示方式为 id,title,author,pid -* 修复修改商品后商品顺序改变 -* 滴滴滴- 和 /t支持图片回复 -* 将/t回复更加简单(可以通过序号),且可以直接发送群 -* 修复bt功能无法交互 - -### 2021/6/17 - -* 修复p站排行,搜图因网络问题爆炸时没有具体回复 -* 更换色图显示方式为 id,title,author,pid - -### 2021/6/15 -* 修改了‘帮助’功能,具体为‘帮助 指令名’,未指定指令名时则为查看全部功能列表 -* 修改了色图的存储数据格式 -* 色图功能搜索的色图改为随机从urls中随机抽取 -* 将商品数据存储入数据库,提供 '增加/删除/修改商品' 指令 -* 商店列表图片不再使用固定背景图,改为直接拼图 -* 增加功能:俄罗斯轮盘/胜场排行/败场排行/欧洲人排行/慈善家排行 -* 增加功能:金币红包(节日红包与群红包相互独立) -* 金币排行 -* 重写一个朋友插件 -* 其他微小调整 - -### 2021/6/4 -* 重写BT功能 -* 进行一些BUG修复和微小调整 -* 添加撤回功能[nonebot-plugin-withdraw](https://github.com/MeetWq/nonebot-plugin-withdraw) -* 为色图功能添加额外的 上传色图 和 删除色图方法(影响hash) - -### 2021/5/26 -* 将语录源更换为一言api +__..... 更多更新信息请查看文档__ ## Todo -- [ ] 提供更多对插件的控制 -- [ ] 明日方舟卡片式的签到..(大概) -- [ ] 更多的群管理功能 -- [ ] 数据清理控制 - [ ] docker容器 +- [ ] web管理 ## 感谢 -[Onebot](https://github.com/howmanybots/onebot) -[go-cqhttp](https://github.com/Mrs4s/go-cqhttp) -[nonebot2](https://github.com/nonebot/nonebot2) -[XUN_Langskip](https://github.com/Angel-Hair/XUN_Bot) -[cappuccilo_plugins](https://github.com/pcrbot/cappuccilo_plugins#%E7%94%9F%E6%88%90%E5%99%A8%E6%8F%92%E4%BB%B6) -[nonebot-plugin-withdraw](https://github.com/MeetWq/nonebot-plugin-withdraw) -[nonebot_plugin_songpicker2](https://github.com/maxesisn/nonebot_plugin_songpicker2) -[nonebot_plugin_manager](https://github.com/Jigsaw111/nonebot_plugin_manager) -[Genshin_Impact_bot](https://github.com/H-K-Y/Genshin_Impact_bot) -[nonebot2_luxun_says](https://github.com/NothAmor/nonebot2_luxun_says) -[AnimeThesaurus](https://github.com/Kyomotoi/AnimeThesaurus) -[omega-miya](https://github.com/Ailitonia/omega-miya) +[botuniverse / onebot](https://github.com/botuniverse/onebot) :超棒的机器人协议 +[Mrs4s / go-cqhttp](https://github.com/Mrs4s/go-cqhttp) :cqhttp的golang实现,轻量、原生跨平台. +[nonebot / nonebot2](https://github.com/nonebot/nonebot2) :跨平台Python异步机器人框架 +[Angel-Hair / XUN_Bot](https://github.com/Angel-Hair/XUN_Bot) :一个基于NoneBot和酷Q的功能性QQ机器人 +[pcrbot / cappuccilo_plugins](https://github.com/pcrbot/cappuccilo_plugins) :hoshino插件合集 +[MeetWq /nonebot-plugin-withdraw](https://github.com/MeetWq/nonebot-plugin-withdraw) :A simple withdraw plugin for Nonebot2 +[maxesisn / nonebot_plugin_songpicker2](https://github.com/maxesisn/nonebot_plugin_songpicker2) :适用于nonebot2的点歌插件 +[nonepkg / nonebot-plugin-manager](https://github.com/nonepkg/nonebot-plugin-manager) :Nonebot Plugin Manager base on import hook +[H-K-Y / Genshin_Impact_bot](https://github.com/H-K-Y/Genshin_Impact_bot) :原神bot,这是一个基于nonebot和HoshinoBot的原神娱乐及信息查询插件 +[NothAmor / nonebot2_luxun_says](https://github.com/NothAmor/nonebot2_luxun_says) :基于nonebot2机器人框架的鲁迅说插件 +[Kyomotoi / AnimeThesaurus](https://github.com/Kyomotoi/AnimeThesaurus) :一个~~特二刺螈~~(文爱)的适用于任何bot的词库 +[Ailitonia / omega-miya](https://github.com/Ailitonia/omega-miya) :基于nonebot2的qq机器人 +[KimigaiiWuyi / GenshinUID]("https://github.com/KimigaiiWuyi/GenshinUID") :一个基于HoshinoBot/NoneBot2的原神UID查询插件 diff --git a/__version__ b/__version__ index db1976c3c..0cfce081b 100644 --- a/__version__ +++ b/__version__ @@ -1 +1 @@ -__version__: v0.0.7.1 +__version__: v0.0.8.0 diff --git a/basic_plugins/hooks/auth_hook.py b/basic_plugins/hooks/auth_hook.py index 7e9403ce6..865b07bc5 100755 --- a/basic_plugins/hooks/auth_hook.py +++ b/basic_plugins/hooks/auth_hook.py @@ -1,19 +1,29 @@ from nonebot.matcher import Matcher -from nonebot.message import run_preprocessor, IgnoredException +from nonebot.message import run_preprocessor, run_postprocessor, IgnoredException from nonebot.adapters.cqhttp.exception import ActionFailed +from models.friend_user import FriendUser +from models.group_member_info import GroupInfoUser +from models.bag_user import BagUser from utils.manager import ( plugins2settings_manager, admin_manager, group_manager, plugins_manager, + plugins2cd_manager, + plugins2block_manager, + plugins2count_manager ) from .utils import set_block_limit_false, status_message_manager from nonebot.typing import T_State +from typing import Optional from nonebot.adapters.cqhttp import ( Bot, MessageEvent, GroupMessageEvent, - PokeNotifyEvent + PokeNotifyEvent, + PrivateMessageEvent, + Message, + Event ) from configs.config import Config from models.ban_user import BanUser @@ -22,13 +32,11 @@ from models.level_user import LevelUser import nonebot - _flmt = FreqLimiter(Config.get_config("hook", "CHECK_NOTICE_INFO_CD")) _flmt_g = FreqLimiter(Config.get_config("hook", "CHECK_NOTICE_INFO_CD")) _flmt_s = FreqLimiter(Config.get_config("hook", "CHECK_NOTICE_INFO_CD")) _flmt_c = FreqLimiter(Config.get_config("hook", "CHECK_NOTICE_INFO_CD")) - ignore_rst_module = ["ai", "poke", "dialogue"] @@ -37,15 +45,27 @@ async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State): module = matcher.module plugins2info_dict = plugins2settings_manager.get_data() + # 功能的金币检测 ####################################### + # 功能的金币检测 ####################################### + # 功能的金币检测 ####################################### + cost_gold = 0 + if isinstance(event, GroupMessageEvent) and plugins2settings_manager.get_plugin_data(module).get('cost_gold'): + cost_gold = plugins2settings_manager.get_plugin_data(module).get('cost_gold') + if await BagUser.get_gold(event.user_id, event.group_id) < cost_gold: + await send_msg(f"金币不足..该功能需要{cost_gold}金币..", bot, event) + raise IgnoredException(f"{module} 金币限制...") + # 当插件不阻塞超级用户时,超级用户提前扣除金币 + if str(event.user_id) in bot.config.superusers and not plugins2info_dict[module]["limit_superuser"]: + await BagUser.spend_gold(event.user_id, event.group_id, cost_gold) try: if ( - (not isinstance(event, MessageEvent) and module != "poke") - or await BanUser.is_ban(event.user_id) - and str(event.user_id) not in bot.config.superusers + (not isinstance(event, MessageEvent) and module != "poke") + or await BanUser.is_ban(event.user_id) + and str(event.user_id) not in bot.config.superusers ) or ( - str(event.user_id) in bot.config.superusers - and plugins2info_dict.get(module) - and not plugins2info_dict[module]["limit_superuser"] + str(event.user_id) in bot.config.superusers + and plugins2info_dict.get(module) + and not plugins2info_dict[module]["limit_superuser"] ): return except AttributeError: @@ -56,8 +76,8 @@ async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State): _module = _plugin.module plugin_name = _module.__getattribute__("__zx_plugin_name__") if ( - "[superuser]" in plugin_name.lower() - and str(event.user_id) in bot.config.superusers + "[superuser]" in plugin_name.lower() + and str(event.user_id) in bot.config.superusers ): return except AttributeError: @@ -79,12 +99,12 @@ async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State): if isinstance(event, GroupMessageEvent): # 个人权限 if ( - not await LevelUser.check_level( - event.user_id, - event.group_id, - admin_manager.get_plugin_level(module), - ) - and admin_manager.get_plugin_level(module) > 0 + not await LevelUser.check_level( + event.user_id, + event.group_id, + admin_manager.get_plugin_level(module), + ) + and admin_manager.get_plugin_level(module) > 0 ): try: if _flmt.check(event.user_id): @@ -92,7 +112,7 @@ async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State): await bot.send_group_msg( group_id=event.group_id, message=f"{at(event.user_id)}你的权限不足喔,该功能需要的权限等级:" - f"{admin_manager.get_plugin_level(module)}", + f"{admin_manager.get_plugin_level(module)}", ) except ActionFailed: pass @@ -102,7 +122,7 @@ async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State): raise IgnoredException("权限不足") else: if not await LevelUser.check_level( - event.user_id, 0, admin_manager.get_plugin_level(module) + event.user_id, 0, admin_manager.get_plugin_level(module) ): try: await bot.send_private_msg( @@ -118,12 +138,12 @@ async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State): if module in plugins2info_dict.keys() and matcher.priority not in [1, 9]: # 戳一戳单独判断 if isinstance(event, GroupMessageEvent) or ( - isinstance(event, PokeNotifyEvent) and event.group_id + isinstance(event, PokeNotifyEvent) and event.group_id ): if status_message_manager.get(event.group_id) is None: status_message_manager.delete(event.group_id) if plugins2info_dict[module]["level"] > group_manager.get_group_level( - event.group_id + event.group_id ): try: if _flmt_g.check(event.user_id) and module not in ignore_rst_module: @@ -141,7 +161,7 @@ async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State): if not group_manager.get_plugin_status(module, event.group_id): try: if module not in ignore_rst_module and _flmt_s.check( - event.group_id + event.group_id ): _flmt_s.start_cd(event.group_id) await bot.send_group_msg( @@ -157,8 +177,8 @@ async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State): if not group_manager.get_plugin_status(f"{module}:super", event.group_id): try: if ( - _flmt_s.check(event.group_id) - and module not in ignore_rst_module + _flmt_s.check(event.group_id) + and module not in ignore_rst_module ): _flmt_s.start_cd(event.group_id) await bot.send_group_msg( @@ -174,8 +194,8 @@ async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State): if not plugins_manager.get_plugin_status(module, block_type="group"): try: if ( - _flmt_c.check(event.group_id) - and module not in ignore_rst_module + _flmt_c.check(event.group_id) + and module not in ignore_rst_module ): _flmt_c.start_cd(event.group_id) await bot.send_group_msg( @@ -205,14 +225,14 @@ async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State): # 维护 if not plugins_manager.get_plugin_status(module, block_type="all"): if isinstance( - event, GroupMessageEvent + event, GroupMessageEvent ) and group_manager.check_group_is_white(event.group_id): return try: if isinstance(event, GroupMessageEvent): if ( - _flmt_c.check(event.group_id) - and module not in ignore_rst_module + _flmt_c.check(event.group_id) + and module not in ignore_rst_module ): _flmt_c.start_cd(event.group_id) await bot.send_group_msg( @@ -233,3 +253,154 @@ async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State): status_message_manager.add(id_) set_block_limit_false(event, module) raise IgnoredException("此功能正在维护...") + + # 以下为限制检测 ####################################################### + # 以下为限制检测 ####################################################### + # 以下为限制检测 ####################################################### + # 以下为限制检测 ####################################################### + # 以下为限制检测 ####################################################### + # 以下为限制检测 ####################################################### + + # Cd + if plugins2cd_manager.check_plugin_cd_status(module): + plugin_cd_data = plugins2cd_manager.get_plugin_cd_data(module) + check_type = plugin_cd_data["check_type"] + limit_type = plugin_cd_data["limit_type"] + rst = plugin_cd_data["rst"] + if ( + (isinstance(event, PrivateMessageEvent) and check_type == "private") + or (isinstance(event, GroupMessageEvent) and check_type == "group") + or plugins2cd_manager.get_plugin_data(module).get("check_type") == "all" + ): + cd_type_ = event.user_id + if limit_type == "group" and isinstance(event, GroupMessageEvent): + cd_type_ = event.group_id + if not plugins2cd_manager.check(module, cd_type_): + if rst: + rst = await init_rst(rst, event) + await send_msg(rst, bot, event) + raise IgnoredException(f"{module} 正在cd中...") + else: + plugins2cd_manager.start_cd(module, cd_type_) + module = matcher.module + if ( + isinstance(event, GroupMessageEvent) + and status_message_manager.get(event.group_id) is None + ): + status_message_manager.delete(event.group_id) + # Cd + if plugins2cd_manager.check_plugin_cd_status(module): + plugin_cd_data = plugins2cd_manager.get_plugin_cd_data(module) + check_type = plugin_cd_data["check_type"] + limit_type = plugin_cd_data["limit_type"] + rst = plugin_cd_data["rst"] + if ( + (isinstance(event, PrivateMessageEvent) and check_type == "private") + or (isinstance(event, GroupMessageEvent) and check_type == "group") + or plugins2cd_manager.get_plugin_data(module).get("check_type") == "all" + ): + cd_type_ = event.user_id + if limit_type == "group" and isinstance(event, GroupMessageEvent): + cd_type_ = event.group_id + if not plugins2cd_manager.check(module, cd_type_): + if rst: + rst = await init_rst(rst, event) + await send_msg(rst, bot, event) + raise IgnoredException(f"{module} 正在cd中...") + else: + plugins2cd_manager.start_cd(module, cd_type_) + # Block + if plugins2block_manager.check_plugin_block_status(module): + plugin_block_data = plugins2block_manager.get_plugin_block_data(module) + check_type = plugin_block_data["check_type"] + limit_type = plugin_block_data["limit_type"] + rst = plugin_block_data["rst"] + if ( + (isinstance(event, PrivateMessageEvent) and check_type == "private") + or (isinstance(event, GroupMessageEvent) and check_type == "group") + or check_type == "all" + ): + block_type_ = event.user_id + if limit_type == "group" and isinstance(event, GroupMessageEvent): + block_type_ = event.group_id + if plugins2block_manager.check(block_type_, module): + if rst: + rst = await init_rst(rst, event) + await send_msg(rst, bot, event) + raise IgnoredException(f"{event.user_id}正在调用{module}....") + else: + plugins2block_manager.set_true(block_type_, module) + # Count + if ( + plugins2count_manager.check_plugin_count_status(module) + and event.user_id not in bot.config.superusers + ): + plugin_count_data = plugins2count_manager.get_plugin_count_data(module) + limit_type = plugin_count_data["limit_type"] + rst = plugin_count_data["rst"] + count_type_ = event.user_id + if limit_type == "group" and isinstance(event, GroupMessageEvent): + count_type_ = event.group_id + if not plugins2count_manager.check(module, count_type_): + if rst: + rst = await init_rst(rst, event) + await send_msg(rst, bot, event) + raise IgnoredException(f"{module} count次数限制...") + else: + plugins2count_manager.increase(module, count_type_) + # 功能花费的金币 ####################################### + # 功能花费的金币 ####################################### + if cost_gold: + await BagUser.spend_gold(event.user_id, event.group_id, cost_gold) + + +async def send_msg(rst: str, bot: Bot, event: MessageEvent): + """ + 发送信息 + :param rst: pass + :param bot: pass + :param event: pass + """ + rst = await init_rst(rst, event) + try: + if isinstance(event, GroupMessageEvent): + status_message_manager.add(event.group_id) + await bot.send_group_msg(group_id=event.group_id, message=Message(rst)) + else: + status_message_manager.add(event.user_id) + await bot.send_private_msg(user_id=event.user_id, message=Message(rst)) + except ActionFailed: + pass + + +# 解除命令block阻塞 +@run_postprocessor +async def _( + matcher: Matcher, + exception: Optional[Exception], + bot: Bot, + event: Event, + state: T_State, +): + if not isinstance(event, MessageEvent) and matcher.module != "poke": + return + module = matcher.module + set_block_limit_false(event, module) + + +async def init_rst(rst: str, event: MessageEvent): + if "[uname]" in rst: + uname = event.sender.card if event.sender.card else event.sender.nickname + rst = rst.replace("[uname]", uname) + if "[nickname]" in rst: + if isinstance(event, GroupMessageEvent): + nickname = await GroupInfoUser.get_group_member_nickname( + event.user_id, event.group_id + ) + else: + nickname = await FriendUser.get_friend_nickname(event.user_id) + rst = rst.replace("[nickname]", nickname) + if "[at]" in rst and isinstance(event, GroupMessageEvent): + rst = rst.replace("[at]", str(at(event.user_id))) + return rst + diff --git a/basic_plugins/hooks/limit_hook.py b/basic_plugins/hooks/limit_hook.py deleted file mode 100755 index 8e7473167..000000000 --- a/basic_plugins/hooks/limit_hook.py +++ /dev/null @@ -1,146 +0,0 @@ -from nonebot.matcher import Matcher -from nonebot.message import run_preprocessor, run_postprocessor, IgnoredException -from nonebot.adapters.cqhttp.exception import ActionFailed -from models.friend_user import FriendUser -from models.group_member_info import GroupInfoUser -from utils.message_builder import at -from .utils import status_message_manager, set_block_limit_false -from utils.manager import ( - plugins2cd_manager, - plugins2block_manager, - plugins2count_manager, -) -from typing import Optional -from nonebot.typing import T_State -from nonebot.adapters.cqhttp import ( - Bot, - Event, - MessageEvent, - PrivateMessageEvent, - GroupMessageEvent, - Message, -) - - -# 命令cd | 命令阻塞 | 命令次数 -@run_preprocessor -async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State): - if not isinstance(event, MessageEvent) and matcher.module != "poke": - return - module = matcher.module - if ( - isinstance(event, GroupMessageEvent) - and status_message_manager.get(event.group_id) is None - ): - status_message_manager.delete(event.group_id) - # Count - if ( - plugins2count_manager.check_plugin_count_status(module) - and event.user_id not in bot.config.superusers - ): - plugin_count_data = plugins2count_manager.get_plugin_count_data(module) - limit_type = plugin_count_data["limit_type"] - rst = plugin_count_data["rst"] - count_type_ = event.user_id - if limit_type == "group" and isinstance(event, GroupMessageEvent): - count_type_ = event.group_id - if not plugins2count_manager.check(module, count_type_): - if rst: - rst = await init_rst(rst, event) - await send_msg(rst, bot, event) - raise IgnoredException(f"{module} count次数限制...") - else: - plugins2count_manager.increase(module, count_type_) - # Cd - if plugins2cd_manager.check_plugin_cd_status(module): - plugin_cd_data = plugins2cd_manager.get_plugin_cd_data(module) - check_type = plugin_cd_data["check_type"] - limit_type = plugin_cd_data["limit_type"] - rst = plugin_cd_data["rst"] - if ( - (isinstance(event, PrivateMessageEvent) and check_type == "private") - or (isinstance(event, GroupMessageEvent) and check_type == "group") - or plugins2cd_manager.get_plugin_data(module).get("check_type") == "all" - ): - cd_type_ = event.user_id - if limit_type == "group" and isinstance(event, GroupMessageEvent): - cd_type_ = event.group_id - if not plugins2cd_manager.check(module, cd_type_): - if rst: - rst = await init_rst(rst, event) - await send_msg(rst, bot, event) - raise IgnoredException(f"{module} 正在cd中...") - else: - plugins2cd_manager.start_cd(module, cd_type_) - # Block - if plugins2block_manager.check_plugin_block_status(module): - plugin_block_data = plugins2block_manager.get_plugin_block_data(module) - check_type = plugin_block_data["check_type"] - limit_type = plugin_block_data["limit_type"] - rst = plugin_block_data["rst"] - if ( - (isinstance(event, PrivateMessageEvent) and check_type == "private") - or (isinstance(event, GroupMessageEvent) and check_type == "group") - or check_type == "all" - ): - block_type_ = event.user_id - if limit_type == "group" and isinstance(event, GroupMessageEvent): - block_type_ = event.group_id - if plugins2block_manager.check(block_type_, module): - if rst: - rst = await init_rst(rst, event) - await send_msg(rst, bot, event) - raise IgnoredException(f"{event.user_id}正在调用{module}....") - else: - plugins2block_manager.set_true(block_type_, module) - - -async def send_msg(rst: str, bot: Bot, event: MessageEvent): - """ - 发送信息 - :param rst: pass - :param bot: pass - :param event: pass - """ - rst = await init_rst(rst, event) - try: - if isinstance(event, GroupMessageEvent): - status_message_manager.add(event.group_id) - await bot.send_group_msg(group_id=event.group_id, message=Message(rst)) - else: - status_message_manager.add(event.user_id) - await bot.send_private_msg(user_id=event.user_id, message=Message(rst)) - except ActionFailed: - pass - - -# 解除命令block阻塞 -@run_postprocessor -async def _( - matcher: Matcher, - exception: Optional[Exception], - bot: Bot, - event: Event, - state: T_State, -): - if not isinstance(event, MessageEvent) and matcher.module != "poke": - return - module = matcher.module - set_block_limit_false(event, module) - - -async def init_rst(rst: str, event: MessageEvent): - if "[uname]" in rst: - uname = event.sender.card if event.sender.card else event.sender.nickname - rst = rst.replace("[uname]", uname) - if "[nickname]" in rst: - if isinstance(event, GroupMessageEvent): - nickname = await GroupInfoUser.get_group_member_nickname( - event.user_id, event.group_id - ) - else: - nickname = await FriendUser.get_friend_nickname(event.user_id) - rst = rst.replace("[nickname]", nickname) - if "[at]" in rst and isinstance(event, GroupMessageEvent): - rst = rst.replace("[at]", str(at(event.user_id))) - return rst diff --git a/basic_plugins/init_plugin_config/init_plugins_settings.py b/basic_plugins/init_plugin_config/init_plugins_settings.py index 82f0dced3..55203d32a 100755 --- a/basic_plugins/init_plugin_config/init_plugins_settings.py +++ b/basic_plugins/init_plugin_config/init_plugins_settings.py @@ -71,6 +71,8 @@ def init_plugins_settings(data_path: str): plugin_settings = _module.__getattribute__( "__plugin_settings__" ) + if plugin_settings.get('cost_gold') is None: + plugin_settings['cost_gold'] = 0 if ( plugin_settings["cmd"] is not None and plugin_name not in plugin_settings["cmd"] @@ -97,7 +99,7 @@ def init_plugins_settings(data_path: str): plugins2settings_manager.add_plugin_settings( matcher.module, plugin_type=plugin_type, - data_dict=plugin_settings, + **plugin_settings, ) except AttributeError: pass diff --git a/basic_plugins/scripts.py b/basic_plugins/scripts.py index 11abd3c35..90ae168e3 100755 --- a/basic_plugins/scripts.py +++ b/basic_plugins/scripts.py @@ -7,6 +7,7 @@ from configs.path_config import TEXT_PATH from asyncio.exceptions import TimeoutError from utils.http_utils import AsyncHttpx +from utils.utils import scheduler from pathlib import Path import nonebot @@ -27,31 +28,32 @@ async def update_city(): """ china_city = Path(TEXT_PATH) / "china_city.json" data = {} - try: - res = await AsyncHttpx.get( - "http://www.weather.com.cn/data/city3jdata/china.html", timeout=5 - ) - res.encoding = "utf8" - provinces_data = json.loads(res.text) - for province in provinces_data.keys(): - data[provinces_data[province]] = [] + if not china_city.exists(): + try: res = await AsyncHttpx.get( - f"http://www.weather.com.cn/data/city3jdata/provshi/{province}.html", - timeout=5, + "http://www.weather.com.cn/data/city3jdata/china.html", timeout=5 ) res.encoding = "utf8" - city_data = json.loads(res.text) - for city in city_data.keys(): - data[provinces_data[province]].append(city_data[city]) - with open(china_city, "w", encoding="utf8") as f: - json.dump(data, f, indent=4, ensure_ascii=False) - logger.info("自动更新城市列表完成.....") - except TimeoutError: - logger.warning("自动更新城市列表超时.....") - except ValueError: - logger.warning("自动城市列表失败.....") - except Exception as e: - logger.error(f"自动城市列表未知错误 {type(e)}:{e}") + provinces_data = json.loads(res.text) + for province in provinces_data.keys(): + data[provinces_data[province]] = [] + res = await AsyncHttpx.get( + f"http://www.weather.com.cn/data/city3jdata/provshi/{province}.html", + timeout=5, + ) + res.encoding = "utf8" + city_data = json.loads(res.text) + for city in city_data.keys(): + data[provinces_data[province]].append(city_data[city]) + with open(china_city, "w", encoding="utf8") as f: + json.dump(data, f, indent=4, ensure_ascii=False) + logger.info("自动更新城市列表完成.....") + except TimeoutError: + logger.warning("自动更新城市列表超时.....") + except ValueError: + logger.warning("自动城市列表失败.....") + except Exception as e: + logger.error(f"自动城市列表未知错误 {type(e)}:{e}") @driver.on_startup @@ -101,3 +103,13 @@ async def _(bot: Bot): else: await GroupInfo.delete_group_info(group_id) logger.info(f"移除不存在的群聊信息:{group_id}") + + +# 自动更新城市列表 +@scheduler.scheduled_job( + "cron", + hour=6, + minute=1, +) +async def _(): + await update_city() diff --git a/plugins/shop/__init__.py b/basic_plugins/shop/__init__.py old mode 100755 new mode 100644 similarity index 82% rename from plugins/shop/__init__.py rename to basic_plugins/shop/__init__.py index 43482d7ac..74df65bb3 --- a/plugins/shop/__init__.py +++ b/basic_plugins/shop/__init__.py @@ -11,4 +11,4 @@ ) -nonebot.load_plugins("plugins/shop") +nonebot.load_plugins("basic_plugins/shop") diff --git a/plugins/shop/buy.py b/basic_plugins/shop/buy.py old mode 100755 new mode 100644 similarity index 81% rename from plugins/shop/buy.py rename to basic_plugins/shop/buy.py index cce978f64..8b627c4df --- a/plugins/shop/buy.py +++ b/basic_plugins/shop/buy.py @@ -1,89 +1,86 @@ -from nonebot import on_command -from services.log import logger -from nonebot.adapters.cqhttp import Bot, GroupMessageEvent -from nonebot.typing import T_State -from utils.utils import get_message_text, is_number -from models.bag_user import BagUser -from services.db_context import db -from nonebot.adapters.cqhttp.permission import GROUP -from .models.goods_info import GoodsInfo - - -__zx_plugin_name__ = "商店 - 购买道具" -__plugin_usage__ = """ -usage: - 购买道具 - 指令: - 购买 [序号或名称] ?[数量=1] - 示例:购买 好感双倍加持卡Ⅰ - 示例:购买 1 4 -""".strip() -__plugin_des__ = "商店 - 购买道具" -__plugin_cmd__ = ["购买 [序号或名称] ?[数量=1]"] -__plugin_type__ = ('商店',) -__plugin_version__ = 0.1 -__plugin_author__ = "HibiKier" -__plugin_settings__ = { - "level": 5, - "default_status": True, - "limit_superuser": False, - "cmd": ["商店", "购买道具"], -} - - -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 ["神秘药水"]: - 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(" ") - num = 1 - if len(msg) > 1: - if is_number(msg[1]) and int(msg[1]) > 0: - num = int(msg[1]) - else: - await buy.finish("购买的数量要是数字且大于0!", 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) - goods = goods_lst[msg - 1] - else: - if msg[0] in goods_name_lst: - for i in range(len(goods_name_lst)): - if msg[0] == goods_name_lst[i]: - goods = goods_lst[i] - break - else: - await buy.finish("请输入正确的商品名称!") - else: - 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 - ): - 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} 成功!" - ) - 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} 失败!" - ) +from nonebot import on_command +from services.log import logger +from nonebot.adapters.cqhttp import Bot, GroupMessageEvent +from nonebot.typing import T_State +from utils.utils import get_message_text, is_number +from models.bag_user import BagUser +from services.db_context import db +from nonebot.adapters.cqhttp.permission import GROUP +from models.goods_info import GoodsInfo + + +__zx_plugin_name__ = "商店 - 购买道具" +__plugin_usage__ = """ +usage: + 购买道具 + 指令: + 购买 [序号或名称] ?[数量=1] + 示例:购买 好感双倍加持卡Ⅰ + 示例:购买 1 4 +""".strip() +__plugin_des__ = "商店 - 购买道具" +__plugin_cmd__ = ["购买 [序号或名称] ?[数量=1]"] +__plugin_type__ = ('商店',) +__plugin_version__ = 0.1 +__plugin_author__ = "HibiKier" +__plugin_settings__ = { + "level": 5, + "default_status": True, + "limit_superuser": False, + "cmd": ["商店", "购买道具"], +} + + +buy = on_command("购买", aliases={"购买道具"}, priority=5, block=True, permission=GROUP) + + +@buy.handle() +async def _(bot: Bot, event: GroupMessageEvent, state: T_State): + goods = None + 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()).split() + num = 1 + if len(msg) > 1: + if is_number(msg[1]) and int(msg[1]) > 0: + num = int(msg[1]) + else: + await buy.finish("购买的数量要是数字且大于0!", 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) + goods = goods_lst[msg - 1] + else: + if msg[0] in goods_name_lst: + for i in range(len(goods_name_lst)): + if msg[0] == goods_name_lst[i]: + goods = goods_lst[i] + break + else: + await buy.finish("请输入正确的商品名称!") + else: + 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 * goods.goods_discount: + await buy.finish("您的金币好像不太够哦", at_sender=True) + if await BagUser.buy_props(event.user_id, event.group_id, goods, num): + await buy.send( + f"花费 {goods.goods_price * num * goods.goods_discount} 金币购买 {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_discount} 金币购买 {goods.goods_name} ×{num} 失败!" + ) diff --git a/plugins/shop/gold.py b/basic_plugins/shop/gold.py old mode 100755 new mode 100644 similarity index 100% rename from plugins/shop/gold.py rename to basic_plugins/shop/gold.py diff --git a/plugins/shop/my_props.py b/basic_plugins/shop/my_props.py old mode 100755 new mode 100644 similarity index 96% rename from plugins/shop/my_props.py rename to basic_plugins/shop/my_props.py index 8474463b1..cbf0f7780 --- a/plugins/shop/my_props.py +++ b/basic_plugins/shop/my_props.py @@ -1,52 +1,52 @@ -from nonebot import on_command -from services.log import logger -from nonebot.adapters.cqhttp import Bot, GroupMessageEvent -from nonebot.typing import T_State -from models.bag_user import BagUser -from nonebot.adapters.cqhttp.permission import GROUP - - -__zx_plugin_name__ = "商店 - 我的道具" -__plugin_usage__ = """ -usage: - 我的道具 - 指令: - 我的道具 -""".strip() -__plugin_des__ = "商店 - 我的道具" -__plugin_cmd__ = ["我的道具"] -__plugin_type__ = ('商店',) -__plugin_version__ = 0.1 -__plugin_author__ = "HibiKier" -__plugin_settings__ = { - "level": 5, - "default_status": True, - "limit_superuser": False, - "cmd": ["商店", "我的道具"], -} - - -my_props = on_command("我的道具", priority=5, block=True, permission=GROUP) - - -@my_props.handle() -async def _(bot: Bot, event: GroupMessageEvent, state: T_State): - props = await BagUser.get_props(event.user_id, event.group_id) - if props: - pname_list = [] - pnum_list = [] - rst = "" - props = props[:-1].split(",") - for p in props: - 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} 查看我的道具") - else: - await my_props.finish("您的背包里没有任何的道具噢~", at_sender=True) +from nonebot import on_command +from services.log import logger +from nonebot.adapters.cqhttp import Bot, GroupMessageEvent +from nonebot.typing import T_State +from models.bag_user import BagUser +from nonebot.adapters.cqhttp.permission import GROUP + + +__zx_plugin_name__ = "商店 - 我的道具" +__plugin_usage__ = """ +usage: + 我的道具 + 指令: + 我的道具 +""".strip() +__plugin_des__ = "商店 - 我的道具" +__plugin_cmd__ = ["我的道具"] +__plugin_type__ = ('商店',) +__plugin_version__ = 0.1 +__plugin_author__ = "HibiKier" +__plugin_settings__ = { + "level": 5, + "default_status": True, + "limit_superuser": False, + "cmd": ["商店", "我的道具"], +} + + +my_props = on_command("我的道具", priority=5, block=True, permission=GROUP) + + +@my_props.handle() +async def _(bot: Bot, event: GroupMessageEvent, state: T_State): + props = await BagUser.get_props(event.user_id, event.group_id) + if props: + pname_list = [] + pnum_list = [] + rst = "" + props = props[:-1].split(",") + for p in props: + 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} 查看我的道具") + else: + await my_props.finish("您的背包里没有任何的道具噢~", at_sender=True) diff --git a/plugins/shop/reset_today_gold.py b/basic_plugins/shop/reset_today_gold.py old mode 100755 new mode 100644 similarity index 95% rename from plugins/shop/reset_today_gold.py rename to basic_plugins/shop/reset_today_gold.py index bdb896e07..7097f0436 --- a/plugins/shop/reset_today_gold.py +++ b/basic_plugins/shop/reset_today_gold.py @@ -1,26 +1,26 @@ -from utils.utils import scheduler -from models.bag_user import BagUser -from services.log import logger - - -__zx_plugin_name__ = "每日金币重置 [Hidden]" -__plugin_version__ = 0.1 -__plugin_author__ = "HibiKier" - - -# 重置每日金币 -@scheduler.scheduled_job( - "cron", - hour=0, - minute=1, -) -async def _(): - try: - 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}") +from utils.utils import scheduler +from models.bag_user import BagUser +from services.log import logger + + +__zx_plugin_name__ = "每日金币重置 [Hidden]" +__plugin_version__ = 0.1 +__plugin_author__ = "HibiKier" + + +# 重置每日金币 +@scheduler.scheduled_job( + "cron", + hour=0, + minute=1, +) +async def _(): + try: + 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}") diff --git a/basic_plugins/shop/shop_handle/__init__.py b/basic_plugins/shop/shop_handle/__init__.py new file mode 100644 index 000000000..a1db15cf5 --- /dev/null +++ b/basic_plugins/shop/shop_handle/__init__.py @@ -0,0 +1,133 @@ +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.message_builder import image +from .data_source import create_shop_help, delete_goods, update_goods, registered_goods, parse_goods_info +from nonebot.permission import SUPERUSER +from utils.utils import get_message_text, is_number +from nonebot.plugin import export +from services.log import logger +import os + + +__zx_plugin_name__ = "商店" +__plugin_usage__ = """ +usage: + 商店项目,这可不是奸商 + 指令: + 商店 +""".strip() +__plugin_superuser_usage__ = """ +usage: + 商品操作 + 指令: + 添加商品 name:[名称] price:[价格] des:[描述] ?discount:[折扣](小数) ?limit_time:[限时时间](小时) + 删除商品 [名称或序号] + 修改商品 name:[名称或序号] price:[价格] des:[描述] discount:[折扣] limit_time:[限时] + 示例:添加商品 name:萝莉酒杯 price:9999 des:普通的酒杯,但是里面.. discount:0.4 limit_time:90 + 示例:添加商品 name:可疑的药 price:5 des:效果未知 + 示例:删除商品 2 + 示例:修改商品 name:1 price:900 修改序号为1的商品的价格为900 + * 修改商品只需添加需要值即可 * +""".strip() +__plugin_des__ = "商店系统[金币回收计划]" +__plugin_cmd__ = [ + "商店", + "添加商品 name:[名称] price:[价格] des:[描述] ?discount:[折扣](小数) ?limit_time:[限时时间](小时)) [_superuser]", + "删除商品 [名称或序号] [_superuser]", + "修改商品 name:[名称或序号] price:[价格] des:[描述] discount:[折扣] limit_time:[限时] [_superuser]", +] +__plugin_type__ = ('商店',) +__plugin_version__ = 0.1 +__plugin_author__ = "HibiKier" +__plugin_settings__ = { + "level": 5, + "default_status": True, + "limit_superuser": False, + "cmd": ["商店"], +} +__plugin_block_limit__ = { + "limit_type": "group" +} + +# 导出方法供其他插件使用 +export = export() +export.registered_goods = registered_goods +export.delete_goods = delete_goods +export.update_goods = update_goods + +shop_help = on_command("商店", priority=5, 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_update_goods = on_command("修改商品", priority=5, permission=SUPERUSER, block=True) + + +@shop_help.handle() +async def _(bot: Bot, event: GroupMessageEvent, state: T_State): + await shop_help.send(image(b64=await create_shop_help())) + + +@shop_add_goods.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + msg = get_message_text(event.json()) + if msg: + data = parse_goods_info(msg) + if isinstance(data, str): + await shop_add_goods.finish(data) + if not data.get("name") or not data.get("price") or not data.get("des"): + await shop_add_goods.finish("name:price:des 参数不可缺少!") + if await registered_goods(**data): + await shop_add_goods.send(f"添加商品 {data['name']} 成功!\n" + f"名称:{data['name']}\n" + f"价格:{data['price']}金币\n" + f"简介:{data['des']}\n" + f"折扣:{data.get('discount')}\n" + f"限时:{data.get('limit_time')}", at_sender=True) + logger.info(f"USER {event.user_id} 添加商品 {msg} 成功") + else: + await shop_add_goods.send(f"添加商品 {msg} 失败了...", 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 = "" + id_ = 0 + if is_number(msg): + id_ = int(msg) + else: + name = msg + rst, goods_name, code = await delete_goods(name, id_) + 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} 成功") + else: + await shop_del_goods.send(f"删除商品 {goods_name} 失败了...", at_sender=True) + logger.info(f"USER {event.user_id} 删除商品 {goods_name} 失败") + + +@shop_update_goods.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + msg = get_message_text(event.json()) + if msg: + data = parse_goods_info(msg) + if isinstance(data, str): + await shop_add_goods.finish(data) + if not data.get("name"): + await shop_add_goods.finish("name 参数不可缺少!") + flag, name, text = await update_goods(**data) + if flag: + await shop_update_goods.send(f"修改商品 {name} 成功了...\n{text}", at_sender=True) + logger.info(f"USER {event.user_id} 修改商品 {name} 数据 {text} 成功") + else: + await shop_update_goods.send(f"修改商品 {name} 失败了...", at_sender=True) + logger.info(f"USER {event.user_id} 修改商品 {name} 数据 {text} 失败") + diff --git a/basic_plugins/shop/shop_handle/data_source.py b/basic_plugins/shop/shop_handle/data_source.py new file mode 100644 index 000000000..e2196f27e --- /dev/null +++ b/basic_plugins/shop/shop_handle/data_source.py @@ -0,0 +1,281 @@ +from models.goods_info import GoodsInfo +from utils.image_utils import BuildImage +from models.sign_group_user import SignGroupUser +from utils.utils import is_number +from configs.path_config import IMAGE_PATH +from typing import Optional, Union +from configs.config import Config +from nonebot import Driver +from nonebot.plugin import require +import nonebot +import time + +driver: Driver = nonebot.get_driver() + +use = require("use") + + +@driver.on_startup +async def init_default_shop_goods(): + """ + 导入内置的三个商品 + """ + + async def sign_card_1(**kwargs): + user_id = kwargs['user_id'] + group_id = kwargs['group_id'] + user = await SignGroupUser.ensure(user_id, group_id) + await user.update(add_probability=0.1).apply() + + async def sign_card_2(**kwargs): + user_id = kwargs['user_id'] + group_id = kwargs['group_id'] + user = await SignGroupUser.ensure(user_id, group_id) + await user.update(add_probability=0.2).apply() + + async def sign_card_3(**kwargs): + user_id = kwargs['user_id'] + group_id = kwargs['group_id'] + user = await SignGroupUser.ensure(user_id, group_id) + await user.update(add_probability=0.3).apply() + + if Config.get_config("shop", "IMPORT_DEFAULT_SHOP_GOODS"): + await registered_goods( + "好感度双倍加持卡Ⅰ", 30, "下次签到双倍好感度概率 + 10%(谁才是真命天子?)(同类商品将覆盖)" + ) + use.registered_use("好感度双倍加持卡Ⅰ", sign_card_1) + await registered_goods("好感度双倍加持卡Ⅱ", 150, "下次签到双倍好感度概率 + 20%(平平庸庸)(同类商品将覆盖)") + use.registered_use("好感度双倍加持卡Ⅱ", sign_card_2) + await registered_goods( + "好感度双倍加持卡Ⅲ", 250, "下次签到双倍好感度概率 + 30%(金币才是真命天子!)(同类商品将覆盖)" + ) + use.registered_use("好感度双倍加持卡Ⅲ", sign_card_3) + + +# 创建商店界面 +async def create_shop_help() -> str: + """ + 制作商店图片 + :return: 图片base64 + """ + goods_lst = await GoodsInfo.get_all_goods() + idx = 1 + _dc = {} + font_h = BuildImage(0, 0).getsize("正")[1] + h = 10 + _list = [] + for goods in goods_lst: + if goods.goods_limit_time == 0 or time.time() < goods.goods_limit_time: + h += len(goods.goods_description.strip().split("\n")) * font_h + 80 + _list.append(goods) + A = BuildImage(1000, h, color="#f9f6f2") + current_h = 0 + for goods in _list: + bk = BuildImage( + 700, 80, font_size=15, color="#f9f6f2", font="CJGaoDeGuo.otf" + ) + goods_image = BuildImage( + 600, 80, font_size=20, color="#a29ad6", font="CJGaoDeGuo.otf" + ) + name_image = BuildImage( + 580, 40, font_size=25, color="#e67b6b", font="CJGaoDeGuo.otf" + ) + await name_image.atext( + (15, 0), f"{idx}.{goods.goods_name}", center_type="by_height" + ) + await name_image.aline((380, -5, 280, 45), "#a29ad6", 5) + await name_image.atext((390, 0), "售价:", center_type="by_height") + await name_image.atext( + (440, 0), str(goods.goods_price), (255, 255, 255), center_type="by_height" + ) + await name_image.atext( + ( + 440 + + BuildImage(0, 0, plain_text=str(goods.goods_price), font_size=25).w, + 0, + ), + " 金币", + center_type="by_height", + ) + await name_image.acircle_corner(5) + await goods_image.apaste(name_image, (0, 5), True, center_type="by_width") + await goods_image.atext((15, 50), f"简介:{goods.goods_description}") + await goods_image.acircle_corner(20) + await bk.apaste(goods_image, alpha=True) + # 添加限时图标和时间 + if goods.goods_limit_time > 0: + _limit_time_logo = BuildImage(40, 40, background=f"{IMAGE_PATH}/other/time.png") + await bk.apaste(_limit_time_logo, (600, 0), True) + await bk.apaste(BuildImage(0, 0, plain_text="限时!", font_size=23, font="CJGaoDeGuo.otf"), (640, 10), True) + limit_time = time.strftime("%Y-%m-%d %H:%M", time.localtime(goods.goods_limit_time)).split() + y_m_d = limit_time[0] + _h_m = limit_time[1].split(":") + h_m = _h_m[0] + "时 " + _h_m[1] + "分" + await bk.atext((605, 38), str(y_m_d)) + await bk.atext((615, 57), str(h_m)) + await bk.aline((550, -1, 710, -1), "#a29ad6", 5) + await bk.aline((550, 80, 710, 80), "#a29ad6", 5) + idx += 1 + await A.apaste(bk, (0, current_h), True) + current_h += 90 + w = 1000 + h = A.h + 230 + 100 + h = 1000 if h < 1000 else h + shop_logo = BuildImage(100, 100, background=f"{IMAGE_PATH}/other/shop_text.png") + shop = BuildImage(w, h, font_size=20, color="#f9f6f2") + shop.paste(A, (20, 230)) + zx_img = BuildImage(0, 0, background=f"{IMAGE_PATH}/zhenxun/toukan.png") + zx_img.replace_color_tran(((240, 240, 240), (255, 255, 255)), (249, 246, 242)) + await shop.apaste(zx_img, (780, 100)) + await shop.apaste(shop_logo, (450, 30), True) + shop.text( + (int((1000 - shop.getsize("注【通过 序号 或者 商品名称 购买】")[0]) / 2), 170), + "注【通过 序号 或者 商品名称 购买】", + ) + shop.text((20, h - 100), "神秘药水\t\t售价:9999999金币\n\t\t鬼知道会有什么效果~") + return shop.pic2bs4() + + +async def registered_goods( + name: str, + price: int, + des: str, + discount: Optional[float] = 1, + limit_time: Optional[int] = 0, + **kwargs, +): + """ + 添加商品 + 例如: 折扣:可选参数↓ 限时时间:可选,单位为小时 + 添加商品 name:萝莉酒杯 price:9999 des:普通的酒杯,但是里面.. discount:0.4 limit_time:90 + 添加商品 name:可疑的药 price:5 des:效果未知 + :param name: 商品名称 + :param price: 商品价格 + :param des: 商品简介 + :param discount: 商品折扣 + :param limit_time: 商品限时销售时间,单位为小时 + :param kwargs: kwargs + :return: 是否添加成功 + """ + if kwargs: + name = kwargs.get("name") + price = kwargs.get("price") + des = kwargs.get("des") + discount = kwargs.get("discount") + limit_time = kwargs.get("time_limit") + limit_time = float(limit_time) if limit_time else limit_time + discount = discount if discount is None else 1 + limit_time = int(time.time() + limit_time * 60 * 60) if limit_time is not None and limit_time != 0 else 0 + return await GoodsInfo.add_goods( + name, int(price), des, float(discount), limit_time + ) + + +# 删除商品 +async def delete_goods(name: str, id_: int) -> "str, str, int": + """ + 删除商品 + :param name: 商品名称 + :param id_: 商品id + :return: 删除状况 + """ + goods_lst = await GoodsInfo.get_all_goods() + if id_: + if id_ < 1 or id_ > len(goods_lst): + return "序号错误,没有该序号商品...", "", 999 + goods_name = goods_lst[id_ - 1].goods_name + if await GoodsInfo.delete_goods(goods_name): + return f"删除商品 {goods_name} 成功!", goods_name, 200 + else: + return f"删除商品 {goods_name} 失败!", goods_name, 999 + if name: + if await GoodsInfo.delete_goods(name): + return f"删除商品 {name} 成功!", name, 200 + else: + return f"删除商品 {name} 失败!", name, 999 + + +# 更新商品信息 +async def update_goods(**kwargs) -> "str, str, int": + """ + 更新商品信息 + :param kwargs: kwargs + :return: 更新状况 + """ + if kwargs: + goods_lst = await GoodsInfo.get_all_goods() + if is_number(kwargs["name"]): + if int(kwargs["name"]) < 1 or int(kwargs["name"]) > len(goods_lst): + return "序号错误,没有该序号的商品...", "", 999 + goods = goods_lst[int(kwargs["name"]) - 1] + else: + goods = await GoodsInfo.get_goods_info(kwargs["name"]) + if not goods: + return "名称错误,没有该名称的商品...", "", 999 + name = goods.goods_name + price = goods.goods_price + des = goods.goods_description + discount = goods.goods_discount + limit_time = goods.goods_limit_time + new_time = 0 + tmp = "" + if kwargs.get("price"): + tmp += f'价格:{price} --> {kwargs["price"]}\n' + price = kwargs["price"] + if kwargs.get("des"): + tmp += f'描述:{des} --> {kwargs["des"]}\n' + des = kwargs["des"] + if kwargs.get("discount"): + tmp += f'折扣:{discount} --> {kwargs["discount"]}\n' + discount = kwargs["discount"] + if kwargs.get("limit_time"): + new_time = time.strftime( + "%Y-%m-%d %H:%M:%S", + time.localtime(time.time() + int(kwargs["limit_time"] * 60 * 60)), + ) + tmp += f"折扣至: {new_time}\n" + limit_time = kwargs["limit_time"] + return ( + await GoodsInfo.update_goods( + name, + int(price), + des, + float(discount), + int(time.time() + limit_time * 60 * 60 if limit_time != 0 and new_time else 0), + ), + name, + tmp[:-1], + ) + + +def parse_goods_info(msg: str) -> Union[dict, str]: + """ + 解析格式数据 + :param msg: 消息 + :return: 解析完毕的数据data + """ + if "name:" not in msg: + return "必须指定修改的商品名称或序号!" + data = {} + for x in msg.split(): + sp = x.split(":", maxsplit=1) + if str(sp[1]).strip(): + sp[1] = sp[1].strip() + print(sp) + if sp[0] == "name": + data["name"] = sp[1] + elif sp[0] == "price": + if not is_number(sp[1]) or int(sp[1]) < 0: + return "price参数不合法,必须大于等于0!" + data["price"] = sp[1] + elif sp[0] == "des": + data["des"] = sp[1] + elif sp[0] == "discount": + if not is_number(sp[1]) or float(sp[1]) < 0: + return "discount参数不合法,必须大于0!" + data["discount"] = sp[1] + elif sp[0] == "limit_time": + if not is_number(sp[1]) or float(sp[1]) < 0: + return "limit_time参数不合法,必须大于0!" + data["limit_time"] = sp[1] + return data diff --git a/plugins/shop/use/__init__.py b/basic_plugins/shop/use/__init__.py old mode 100755 new mode 100644 similarity index 93% rename from plugins/shop/use/__init__.py rename to basic_plugins/shop/use/__init__.py index 875afd408..addcd6958 --- a/plugins/shop/use/__init__.py +++ b/basic_plugins/shop/use/__init__.py @@ -1,71 +1,76 @@ -from nonebot import on_command -from services.log import logger -from nonebot.adapters.cqhttp import Bot, GroupMessageEvent -from nonebot.typing import T_State -from utils.utils import is_number, get_message_text -from models.bag_user import BagUser -from nonebot.adapters.cqhttp.permission import GROUP -from services.db_context import db -from .data_source import effect - - -__zx_plugin_name__ = "商店 - 使用道具" -__plugin_usage__ = """ -usage: - 普通的使用道具 - 指令: - 使用道具 [序号或道具名称] - * 序号以 ”我的道具“ 为准 * -""".strip() -__plugin_des__ = "商店 - 使用道具" -__plugin_cmd__ = ["使用道具 [序号或道具名称]"] -__plugin_type__ = ('商店',) -__plugin_version__ = 0.1 -__plugin_author__ = "HibiKier" -__plugin_settings__ = { - "level": 5, - "default_status": True, - "limit_superuser": False, - "cmd": ["商店", "使用道具"], -} - -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 ["", "帮助"]: - 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(",") - for p in props: - 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) - else: - if msg not in pname_list: - 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} 成功" - ) - else: - 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) +from nonebot import on_command +from services.log import logger +from nonebot.adapters.cqhttp import Bot, GroupMessageEvent +from nonebot.typing import T_State +from utils.utils import is_number, get_message_text +from models.bag_user import BagUser +from nonebot.adapters.cqhttp.permission import GROUP +from services.db_context import db +from nonebot.plugin import export +from .data_source import effect, registered_use + + +__zx_plugin_name__ = "商店 - 使用道具" +__plugin_usage__ = """ +usage: + 普通的使用道具 + 指令: + 使用道具 [序号或道具名称] + * 序号以 ”我的道具“ 为准 * +""".strip() +__plugin_des__ = "商店 - 使用道具" +__plugin_cmd__ = ["使用道具 [序号或道具名称]"] +__plugin_type__ = ('商店',) +__plugin_version__ = 0.1 +__plugin_author__ = "HibiKier" +__plugin_settings__ = { + "level": 5, + "default_status": True, + "limit_superuser": False, + "cmd": ["商店", "使用道具"], +} + +# 导出方法供其他插件使用 +export = export() +export.registered_use = registered_use + +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 ["", "帮助"]: + 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(",") + for p in props: + 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) + else: + if msg not in pname_list: + 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} 成功" + ) + else: + 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) diff --git a/basic_plugins/shop/use/data_source.py b/basic_plugins/shop/use/data_source.py new file mode 100644 index 000000000..4629e7578 --- /dev/null +++ b/basic_plugins/shop/use/data_source.py @@ -0,0 +1,45 @@ +import asyncio +from services.log import logger + +_use_func_data = {} + + +async def effect(user_id: int, group_id: int, goods_name: str) -> bool: + """ + 商品生效 + :param user_id: 用户id + :param group_id: 群号 + :param goods_name: 商品名称 + :return: 使用是否成功 + """ + # 优先使用注册的商品插件 + try: + if _use_func_data.get(goods_name): + _kwargs = _use_func_data[goods_name]["kwargs"] + _kwargs["goods_name"] = goods_name + _kwargs["user_id"] = user_id + _kwargs["group_id"] = group_id + if asyncio.iscoroutinefunction(_use_func_data[goods_name]["func"]): + await _use_func_data[goods_name]["func"]( + **_kwargs, + ) + else: + _use_func_data[goods_name]["func"]( + **_kwargs, + ) + return True + except Exception as e: + logger.error(f"use 商品生效函数effect 发生错误 {type(e)}:{e}") + return False + + +def registered_use(goods_name: str, func, **kwargs): + """ + 注册商品使用方法 + :param goods_name: 商品名称 + :param func: 使用函数 + :param kwargs: kwargs + """ + if goods_name in _use_func_data.keys(): + raise ValueError("该商品使用函数已被注册!") + _use_func_data[goods_name] = {"func": func, "kwargs": kwargs} diff --git a/models/bag_user.py b/models/bag_user.py index 9e87ee87b..935d50c5a 100755 --- a/models/bag_user.py +++ b/models/bag_user.py @@ -1,5 +1,6 @@ from services.db_context import db from typing import Optional, List +from services.log import logger class BagUser(db.Model): @@ -85,7 +86,7 @@ async def get_props(cls, user_qq: int, belonging_group: int) -> str: return "" @classmethod - async def add_gold(cls, user_qq: int, belonging_group: int, num: int) -> bool: + async def add_gold(cls, user_qq: int, belonging_group: int, num: int): """ 说明: 增加金币 @@ -99,27 +100,23 @@ async def add_gold(cls, user_qq: int, belonging_group: int, num: int) -> bool: ) query = query.with_for_update() user = await query.gino.first() - try: - if user: - await user.update( - gold=user.gold + num, - get_total_gold=user.get_total_gold + num, - get_today_gold=user.get_today_gold + num, - ).apply() - else: - await cls.create( - user_qq=user_qq, - belonging_group=belonging_group, - gold=100 + num, - get_total_gold=num, - get_today_gold=num, - ) - return True - except Exception: - return False + if user: + await user.update( + gold=user.gold + num, + get_total_gold=user.get_total_gold + num, + get_today_gold=user.get_today_gold + num, + ).apply() + else: + await cls.create( + user_qq=user_qq, + belonging_group=belonging_group, + gold=100 + num, + get_total_gold=num, + get_today_gold=num, + ) @classmethod - async def spend_gold(cls, user_qq: int, belonging_group: int, num: int) -> bool: + async def spend_gold(cls, user_qq: int, belonging_group: int, num: int): """ 说明: 花费金币 @@ -133,27 +130,23 @@ async def spend_gold(cls, user_qq: int, belonging_group: int, num: int) -> bool: ) query = query.with_for_update() user = await query.gino.first() - try: - if user: - await user.update( - gold=user.gold - num, - spend_total_gold=user.spend_total_gold + num, - spend_today_gold=user.spend_today_gold + num, - ).apply() - else: - await cls.create( - user_qq=user_qq, - belonging_group=belonging_group, - gold=100 - num, - spend_total_gold=num, - spend_today_gold=num, - ) - return True - except Exception: - return False + if user: + await user.update( + gold=user.gold - num, + spend_total_gold=user.spend_total_gold + num, + spend_today_gold=user.spend_today_gold + num, + ).apply() + else: + await cls.create( + user_qq=user_qq, + belonging_group=belonging_group, + gold=100 - num, + spend_total_gold=num, + spend_today_gold=num, + ) @classmethod - async def add_props(cls, user_qq: int, belonging_group: int, name: str) -> bool: + async def add_props(cls, user_qq: int, belonging_group: int, name: str): """ 说明: 增加道具 @@ -167,16 +160,12 @@ async def add_props(cls, user_qq: int, belonging_group: int, name: str) -> bool: ) query = query.with_for_update() user = await query.gino.first() - try: - if user: - await user.update(props=user.props + f"{name},").apply() - else: - await cls.create( - user_qq=user_qq, belonging_group=belonging_group, props=f"{name}," - ) - return True - except Exception: - return False + if user: + await user.update(props=user.props + f"{name},").apply() + else: + await cls.create( + user_qq=user_qq, belonging_group=belonging_group, props=f"{name}," + ) @classmethod async def del_props(cls, user_qq: int, belonging_group: int, name: str) -> bool: @@ -193,27 +182,48 @@ async def del_props(cls, user_qq: int, belonging_group: int, name: str) -> bool: ) query = query.with_for_update() user = await query.gino.first() - try: - if user: - rst = "" - props = user.props - if props.find(name) != -1: - props = props.split(",") - try: - index = props.index(name) - except ValueError: - return False - props = props[:index] + props[index + 1 :] - for p in props: - if p != "": - rst += p + "," - await user.update(props=rst).apply() - return True - else: + if user: + rst = "" + props = user.props + if props.find(name) != -1: + props = props.split(",") + try: + index = props.index(name) + except ValueError: return False + props = props[:index] + props[index + 1 :] + for p in props: + if p != "": + rst += p + "," + await user.update(props=rst).apply() + return True else: return False - except Exception: + else: + return False + + @classmethod + async def buy_props( + cls, user_qq: int, belonging_group: int, goods: "GoodsInfo", goods_num: int + ) -> bool: + """ + 说明: + 购买道具 + 参数: + :param user_qq: 用户qq + :param belonging_group: 所在群聊 + :param goods: 商品 + :param goods_num: 商品数量 + """ + try: + # 折扣后金币 + spend_gold = goods.goods_discount * goods.goods_price * goods_num + await BagUser.spend_gold(user_qq, belonging_group, spend_gold) + for _ in range(goods_num): + await BagUser.add_props(user_qq, belonging_group, goods.goods_name) + return True + except Exception as e: + logger.error(f"buy_props 发生错误 {type(e)}:{e}") return False @classmethod diff --git a/plugins/shop/models/goods_info.py b/models/goods_info.py old mode 100755 new mode 100644 similarity index 85% rename from plugins/shop/models/goods_info.py rename to models/goods_info.py index e199f2b9c..ccd5afd37 --- a/plugins/shop/models/goods_info.py +++ b/models/goods_info.py @@ -1,5 +1,6 @@ from services.db_context import db from typing import Optional, List +from services.log import logger class GoodsInfo(db.Model): @@ -34,16 +35,18 @@ async def add_goods( :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 + if not await cls.get_goods_info(goods_name): + 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 as e: + logger.error(f"GoodsInfo add_goods 发生错误 {type(e)}:{e}") + return False @classmethod async def delete_goods(cls, goods_name: str) -> bool: @@ -99,7 +102,8 @@ async def update_goods( if goods_limit_time: await query.update(goods_limit_time=goods_limit_time).apply() return True - except Exception: + except Exception as e: + logger.error(f"GoodsInfo update_goods 发生错误 {type(e)}:{e}") return False @classmethod diff --git a/plugins/draw_card/announcement.py b/plugins/draw_card/announcement.py index 42abc5592..f5c9f7f2f 100755 --- a/plugins/draw_card/announcement.py +++ b/plugins/draw_card/announcement.py @@ -40,7 +40,7 @@ def is_expired(data: dict): # 检查写入 def check_write(data: dict, up_char_file): - if not is_expired(data['char']): + if is_expired(data['char']): for x in list(data.keys()): data[x]['title'] = '' else: diff --git a/plugins/draw_card/genshin_handle.py b/plugins/draw_card/genshin_handle.py index c05aab919..e1f551aad 100755 --- a/plugins/draw_card/genshin_handle.py +++ b/plugins/draw_card/genshin_handle.py @@ -146,12 +146,14 @@ def _format_card_information(_count: int, user_id, pool_name): five_index_list = [] five_list = [] five_dict = {} + _start_add_count = 72 if pool_name == 'char' else 62 + _x = 90 if pool_name == 'char' else 80 # 保底 add = 0.0 - if genshin_count.get(user_id) and _count <= 90: + if genshin_count.get(user_id) and _count <= _x: f_count = genshin_count[user_id] else: f_count = 0 - if genshin_pl_count.get(user_id) and _count <= 90: + if genshin_pl_count.get(user_id) and _count <= _x: count = genshin_pl_count[user_id] else: count = 0 @@ -159,23 +161,23 @@ def _format_card_information(_count: int, user_id, pool_name): count += 1 f_count += 1 # 十连保底 - if count == 10 and f_count != 90: - if f_count >= 72: + if count == 10 and f_count != _x: + if f_count >= _start_add_count: add += I72_ADD char, code = _get_genshin_card(2, pool_name, add=add) count = 0 # 大保底 - elif f_count == 90: + elif f_count == _x: char, code = _get_genshin_card(3, pool_name) else: - if f_count >= 72: + if f_count >= _start_add_count: add += I72_ADD char, code = _get_genshin_card(pool_name=pool_name, add=add) if code == 1: count = 0 star_list[code] += 1 if code == 0: - if _count <= 90: + if _count <= _x: genshin_five[user_id] = f_count add = 0.0 f_count = 0 @@ -186,7 +188,7 @@ def _format_card_information(_count: int, user_id, pool_name): except KeyError: five_dict[char.name] = 1 char_list.append(char) - if _count <= 90: + if _count <= _x: genshin_count[user_id] = f_count genshin_pl_count[user_id] = count return char_list, five_list, five_index_list, five_dict, star_list diff --git a/plugins/genshin/material_remind/__init__.py b/plugins/genshin/material_remind/__init__.py index 223428d7a..05cca0ac3 100755 --- a/plugins/genshin/material_remind/__init__.py +++ b/plugins/genshin/material_remind/__init__.py @@ -7,9 +7,10 @@ from configs.path_config import IMAGE_PATH import nonebot from services.log import logger -from utils.utils import scheduler from nonebot.permission import SUPERUSER +from pathlib import Path from typing import List +from datetime import datetime, timedelta import os import asyncio import time @@ -52,9 +53,12 @@ async def _(bot: Bot, event: MessageEvent, state: T_State): if time.strftime("%w") == "0": await material.send("今天是周日,所有材料副本都开放了。") return + file_name = str((datetime.now() - timedelta(hours=4)).date()) + if not (Path(IMAGE_PATH) / "genshin" / "material" / f"{file_name}.png").exists(): + await update_image() await material.send( Message( - image("daily_material.png", "genshin/material") + image(f"{file_name}.png", "genshin/material") + "\n※ 每日素材数据来源于 genshin.pub" ) ) @@ -73,7 +77,6 @@ async def _(bot: Bot, event: MessageEvent, state: T_State): await super_cmd.send(f"更新失败...") -@driver.on_startup async def update_image(): page = None try: @@ -140,7 +143,8 @@ async def update_image(): background_img.paste(x, (current_width, current_height)) current_height += x.size[1] current_width += 600 - background_img.save(f"{IMAGE_PATH}/genshin/material/daily_material.png") + file_name = str((datetime.now() - timedelta(hours=4)).date()) + background_img.save(f"{IMAGE_PATH}/genshin/material/{file_name}.png") await page.close() return True except Exception as e: @@ -163,16 +167,3 @@ def get_background_height(weapons_imgs: List[str]) -> int: return height -@scheduler.scheduled_job( - "cron", - hour=4, - minute=1, -) -async def _(): - for _ in range(5): - try: - await update_image() - logger.info(f"更新每日天赋素材成功...") - break - except Exception as e: - logger.error(f"更新每日天赋素材出错 e:{e}") diff --git a/plugins/genshin/query_user/query_role/data_source.py b/plugins/genshin/query_user/query_role/data_source.py index 0e8b3cbd7..14772a9aa 100644 --- a/plugins/genshin/query_user/query_role/data_source.py +++ b/plugins/genshin/query_user/query_role/data_source.py @@ -64,7 +64,7 @@ async def get_image( "affix_level": char["weapon"]["affix_level"], } - await init_image(char_data_list, _x) + await init_image(char_data_list, _x, home_data_list) return await get_genshin_image( user_id, uid, diff --git a/plugins/genshin/query_user/query_role/draw_image.py b/plugins/genshin/query_user/query_role/draw_image.py index 556e1cae1..efa13abfb 100644 --- a/plugins/genshin/query_user/query_role/draw_image.py +++ b/plugins/genshin/query_user/query_role/draw_image.py @@ -510,11 +510,12 @@ def get_char_data_image( return region, _h -async def init_image(char_data_list: List[Dict], char_detailed_dict: dict): +async def init_image(char_data_list: List[Dict], char_detailed_dict: dict, home_data_list: List[Dict]): """ 下载头像 :param char_data_list: 角色列表 :param char_detailed_dict: 角色武器 + :param home_data_list: 家园列表 """ for char in char_data_list: file = image_path / "chars" / f'{char["name"]}.png' @@ -528,3 +529,10 @@ async def init_image(char_data_list: List[Dict], char_detailed_dict: dict): await AsyncHttpx.download_file( char_detailed_dict[char]["weapon_image"], file ) + for home in home_data_list: + file = image_path / "homes" / f'{home["name"]}.png' + file.parent.mkdir(parents=True, exist_ok=True) + if not file.exists(): + await AsyncHttpx.download_file( + home["icon"], file + ) diff --git a/plugins/gold_redbag/__init__.py b/plugins/gold_redbag/__init__.py index c11b74377..74f629ed5 100755 --- a/plugins/gold_redbag/__init__.py +++ b/plugins/gold_redbag/__init__.py @@ -41,8 +41,19 @@ 示例:塞红包 1000 示例:塞红包 1000 10 """.strip() +__plugin_superuser_usage__ = """ +usage: + 节日全群红包指令 + 指令: + 节日红包 [金额] [数量] ?[祝福语] ?[指定群] +""".strip() __plugin_des__ = "运气项目又来了" -__plugin_cmd__ = ["塞红包 [金币数] ?[红包数=5]", "开/抢", "退回"] +__plugin_cmd__ = [ + "塞红包 [金币数] ?[红包数=5]", + "开/抢", + "退回", + "节日红包 [金额] [数量] ?[祝福语] ?[指定群] [_superuser]", +] __plugin_version__ = 0.1 __plugin_author__ = "HibiKier" __plugin_settings__ = { @@ -51,9 +62,7 @@ "limit_superuser": False, "cmd": ["金币红包", "塞红包"], } -__plugin_resources__ = { - "prts": IMAGE_PATH -} +__plugin_resources__ = {"prts": IMAGE_PATH} gold_redbag = on_command( "塞红包", aliases={"金币红包"}, priority=5, block=True, permission=GROUP @@ -143,10 +152,12 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State): flag, amount = await check_gold(event.user_id, event.group_id, amount) if not flag: await gold_redbag.finish(amount, at_sender=True) - group_member_num = (await bot.get_group_info(group_id=event.group_id))['member_count'] + group_member_num = (await bot.get_group_info(group_id=event.group_id))[ + "member_count" + ] num = int(num) if num > group_member_num: - await gold_redbag.send('你发的红包数量也太多了,已经为你修改成与本群人数相同的红包数量...') + await gold_redbag.send("你发的红包数量也太多了,已经为你修改成与本群人数相同的红包数量...") num = group_member_num nickname = event.sender.card if event.sender.card else event.sender.nickname flag, result = init_redbag( @@ -181,7 +192,7 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State): .replace("。", "") ) if msg: - if '红包' not in msg: + if "红包" not in msg: return flag1 = True flag2 = True @@ -306,7 +317,9 @@ async def _(bot: Bot, event: MessageEvent, state: T_State): await end_festive_redbag(bot, g) except JobLookupError: pass - init_redbag(int(bot.self_id), g, f"{NICKNAME}", amount, num, int(bot.self_id), 1) + init_redbag( + int(bot.self_id), g, f"{NICKNAME}", amount, num, int(bot.self_id), 1 + ) scheduler.add_job( end_festive_redbag, "date", diff --git a/plugins/image_management/send_image/__init__.py b/plugins/image_management/send_image/__init__.py index e352a717d..99db0d08e 100755 --- a/plugins/image_management/send_image/__init__.py +++ b/plugins/image_management/send_image/__init__.py @@ -20,7 +20,7 @@ __zx_plugin_name__ = "本地图库" __plugin_usage__ = f""" usage: - 发送指定图库下的随机或指定id图片 + 发送指定图库下的随机或指定id图片genshin_memo 指令: {Config.get_config("image_management", "IMAGE_DIR_LIST")} ?[id] 示例:美图 diff --git a/plugins/image_management/upload_image/__init__.py b/plugins/image_management/upload_image/__init__.py index b75ce1ae6..b67abf61c 100755 --- a/plugins/image_management/upload_image/__init__.py +++ b/plugins/image_management/upload_image/__init__.py @@ -105,7 +105,7 @@ async def _(bot: Bot, event: MessageEvent, state: T_State): path = get_message_imgs(event.json()) if path in Config.get_config("image_management", "IMAGE_DIR_LIST"): state["path"] = path - await continuous_upload_img.send("图来!!") + await continuous_upload_img.send("图来!!【停止请发送 ‘stop’ 开始上传】") state["tmp"] = [] diff --git a/plugins/send_setu_/model.py b/plugins/send_setu_/model.py index b35881c91..4e80d7a38 100755 --- a/plugins/send_setu_/model.py +++ b/plugins/send_setu_/model.py @@ -124,6 +124,27 @@ async def _check_exists(cls, pid: int, img_url: str) -> bool: ).gino.first() ) + @classmethod + async def delete_image(cls, pid: int) -> int: + """ + 说明: + 删除图片并替换 + 参数: + :param pid: 图片pid + """ + query = await cls.query.where(cls.pid == pid).gino.first() + if query: + is_r18 = query.is_r18 + num = await cls.get_image_count(is_r18) + x = await cls.query.where((cls.is_r18 == is_r18) & (cls.local_id == num - 1)).gino.first() + _tmp_local_id = x.local_id + if x: + x.update(local_id=query.local_id).apply() + await cls.delete.where(cls.pid == pid).gino.status() + return _tmp_local_id + return -1 + + @classmethod async def update_setu_data( cls, diff --git a/plugins/send_setu_/update_setu/data_source.py b/plugins/send_setu_/update_setu/data_source.py index 5c9bd3fef..18189eaa7 100755 --- a/plugins/send_setu_/update_setu/data_source.py +++ b/plugins/send_setu_/update_setu/data_source.py @@ -3,7 +3,7 @@ from datetime import datetime from utils.image_utils import compressed_image, get_img_hash from utils.utils import get_bot -from asyncio.exceptions import TimeoutError +from PIL import UnidentifiedImageError from ..model import Setu from asyncpg.exceptions import UniqueViolationError from configs.config import Config @@ -142,6 +142,14 @@ async def update_setu_img(): continue img_hash = str(get_img_hash(f"{path}/{image.local_id}.jpg")) await Setu.update_setu_data(image.pid, img_hash=img_hash) + except UnidentifiedImageError: + # 图片已删除 + with open(local_image, 'r') as f: + if '404 Not Found' in f.read(): + max_num = await Setu.delete_image(image.pid) + local_image.unlink() + os.rename(path / f"{max_num}.jpg", local_image) + logger.warning(f"更新色图 PID:{image.pid} 404,已删除并替换") except Exception as e: _success -= 1 logger.error(f"更新色图 {image.local_id}.jpg 错误 {type(e)}: {e}") diff --git a/plugins/server_ip.py b/plugins/server_ip.py new file mode 100644 index 000000000..adbd041c6 --- /dev/null +++ b/plugins/server_ip.py @@ -0,0 +1,36 @@ +from nonebot import on_command +from nonebot.adapters.cqhttp import Bot, PrivateMessageEvent, GroupMessageEvent +from nonebot.typing import T_State +from nonebot.rule import to_me + +__zx_plugin_name__ = "服务器 [Hidden]" +__plugin_version__ = 0.1 +__plugin_author__ = "HibiKier" + + +server_ip = on_command("服务器", aliases={"ip"}, rule=to_me(), priority=5, block=True) + + +@server_ip.handle() +async def _(bot: Bot, event: PrivateMessageEvent, state: T_State): + await server_ip.finish( + "|* 请不要发给其他人! *|\n" + "\t121.40.195.22\n" + "|* 请不要发给其他人! *|\n" + "csgo ~号键控制台输入 connect 121.40.195.22 进入服务器\n" + "然后再公屏输入 !diy 来使用皮肤(英文感叹号,注意)\n" + "【说不定可以凑到内战噢,欢迎~{娱乐为主}】", + at_sender=True, + ) + + +@server_ip.handle() +async def _(bot: Bot, event: GroupMessageEvent, state: T_State): + if event.group_id == 698279647: + await server_ip.finish( + "嗨呀!当前服务器地址是:" "\n\tay: 121.40.195.22" "\n\t夜之北枭: 101.132.170.254" + ) + elif event.group_id == 1046451860: + await server_ip.finish("嗨呀!当前服务器地址是:\n121.40.195.22\n !diy") + else: + await server_ip.finish("不好意思呀,小真寻不能为你使用此功能,因为服务器IP是真寻的小秘密呀!", at_sender=True) diff --git a/plugins/shop/models/__init__.py b/plugins/shop/models/__init__.py deleted file mode 100755 index 8a0c62fb0..000000000 --- a/plugins/shop/models/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .goods_info import * diff --git a/plugins/shop/shop_handle/__init__.py b/plugins/shop/shop_handle/__init__.py deleted file mode 100755 index 20150853e..000000000 --- a/plugins/shop/shop_handle/__init__.py +++ /dev/null @@ -1,154 +0,0 @@ -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.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 -from services.log import logger -import os -import time - - -__zx_plugin_name__ = "商店" -__plugin_usage__ = """ -usage: - 商店项目,这可不是奸商 - 指令: - 商店 -""".strip() -__plugin_superuser_usage__ = """ -usage: - 商品操作 - 指令: - 添加商品 [名称]-[价格]-[描述]-?[折扣](小数)-?[限时时间](分钟) - 删除商品 [名称或序号] - 修改商品 -name [名称或序号] -price [价格] -des [描述] -discount [折扣] -time [限时] - 示例:添加商品-昏睡红茶-300-一杯上好的奇怪红茶-0.9-60 - 示例:删除商品 2 - 示例:修改商品 -name 1 -price 900: 修改序号为1的商品的价格为900 - * 修改商品只需添加需要值即可 * -""".strip() -__plugin_des__ = "商店系统[金币回收计划]" -__plugin_cmd__ = [ - "商店", - "添加商品 [名称]-[价格]-[描述]-?[折扣](小数)-?[限时时间](分钟) [_superuser]", - "删除商品 [名称或序号] [_superuser]", - "修改商品 -name [名称或序号] -price [价格] -des [描述] -discount [折扣] -time [限时] [_superuser]", -] -__plugin_type__ = ('商店',) -__plugin_version__ = 0.1 -__plugin_author__ = "HibiKier" -__plugin_settings__ = { - "level": 5, - "default_status": True, - "limit_superuser": False, - "cmd": ["商店"], -} - - -shop_help = on_command("商店", priority=5, 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_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"): - await create_shop_help() - 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("-") - if len(msg) < 3: - await shop_add_goods.finish("商品参数不完全...", at_sender=True) - if not is_number(msg[1]): - 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) - msg[3] = float(msg[3]) - if len(msg) > 4: - if not is_number(msg[4]): - 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} 成功") - else: - 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 = "" - id_ = 0 - if is_number(msg): - id_ = int(msg) - else: - name = msg - rst, goods_name, code = await del_goods(name, id_) - 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} 成功") - else: - await shop_del_goods.send(f"删除商品 {goods_name} 失败了...", at_sender=True) - logger.info(f"USER {event.user_id} 删除商品 {goods_name} 失败") - - -@shop_update_goods.handle() -async def _(bot: Bot, event: MessageEvent, state: T_State): - msg = get_message_text(event.json()) - if msg: - tmp = {} - 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"]) - 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} 成功") - else: - 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 deleted file mode 100755 index 0c4f4ac6a..000000000 --- a/plugins/shop/shop_handle/data_source.py +++ /dev/null @@ -1,126 +0,0 @@ -from ..models.goods_info import GoodsInfo -from utils.image_utils import BuildImage -from utils.utils import is_number -from configs.path_config import IMAGE_PATH -from configs.config import Config -from nonebot import Driver -import nonebot -import time -import os - - -driver: Driver = nonebot.get_driver() - - -# 导入内置商品 | 重新生成商店图片 -@driver.on_startup -async def init_default_shop_goods(): - if os.path.exists(f"{IMAGE_PATH}/shop_help.png"): - os.remove(f"{IMAGE_PATH}/shop_help.png") - if Config.get_config("shop", "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 = "" - 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" - ) - idx += 1 - w = 1000 - h = 400 + len(goods_lst) * 40 - h = 1000 if h < 1000 else h - shop_logo = BuildImage(100, 100, background=f"{IMAGE_PATH}/other/shop_text.png") - shop = BuildImage(w, h, font_size=20) - zhenxun_img = BuildImage(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((20, 230), tmp[:-1]) - 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, - } - 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["限时"] - ) - - -# 删除商品 -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 - goods_name = goods_lst[id_ - 1].goods_name - if await GoodsInfo.delete_goods(goods_name): - return f"删除商品 {goods_name} 成功!", goods_name, 200 - else: - return f"删除商品 {goods_name} 失败!", goods_name, 999 - if name: - if await GoodsInfo.delete_goods(name): - return f"删除商品 {name} 成功!", name, 200 - else: - 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] - else: - goods = await GoodsInfo.get_goods_info(data["name"]) - if not goods: - 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 += f'价格:{price} --> {data["price"]}\n' - price = data["price"] - if data.get("des"): - tmp += f'描述:{des} --> {data["des"]}\n' - des = data["des"] - if data.get("discount"): - tmp += f'折扣:{discount} --> {data["discount"]}\n' - 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], - ) diff --git a/plugins/shop/use/data_source.py b/plugins/shop/use/data_source.py deleted file mode 100755 index 4d6287998..000000000 --- a/plugins/shop/use/data_source.py +++ /dev/null @@ -1,14 +0,0 @@ -from models.sign_group_user import SignGroupUser - - -async def effect(user_id: int, group_id: int, name: str) -> bool: - if name in ["好感双倍加持卡Ⅰ", "好感度双倍加持卡Ⅰ"]: - user = await SignGroupUser.ensure(user_id, group_id) - await user.update(add_probability=0.1).apply() - if name in ["好感双倍加持卡Ⅱ", "好感度双倍加持卡Ⅱ"]: - user = await SignGroupUser.ensure(user_id, group_id) - await user.update(add_probability=0.2).apply() - if name in ["好感双倍加持卡Ⅲ", "好感度双倍加持卡Ⅲ"]: - user = await SignGroupUser.ensure(user_id, group_id) - await user.update(add_probability=0.3).apply() - return True diff --git a/plugins/sign_in/__init__.py b/plugins/sign_in/__init__.py index 5b578616c..37edbefea 100755 --- a/plugins/sign_in/__init__.py +++ b/plugins/sign_in/__init__.py @@ -28,14 +28,14 @@ 每日签到 会影响色图概率和开箱次数,以及签到的随机道具获取 指令: - 签到 + 签到 ?[all]: all代表签到所有群 我的签到 好感度排行 好感度总排行 * 签到时有 3% 概率 * 2 * """.strip() __plugin_des__ = "每日签到,证明你在这里" -__plugin_cmd__ = ["签到", "我的签到", "好感度排行", "好感度总排行"] +__plugin_cmd__ = ["签到 ?[all]", "我的签到", "好感度排行", "好感度总排行"] __plugin_version__ = 0.1 __plugin_author__ = "HibiKier" __plugin_settings__ = { diff --git a/plugins/yiqing/__init__.py b/plugins/yiqing/__init__.py index df306eab0..fd6c103eb 100755 --- a/plugins/yiqing/__init__.py +++ b/plugins/yiqing/__init__.py @@ -59,7 +59,7 @@ async def _(bot: Bot, event: MessageEvent, state: T_State): await yiqing.send(rely) logger.info( f"(USER {event.user_id}, GROUP " - f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'}) 查询疫情失败" + f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'}) 查询疫情成功" ) else: await yiqing.send(f"{NICKNAME}没有查到{msg}的疫情查询...") diff --git a/plugins/yiqing/data_source.py b/plugins/yiqing/data_source.py index 02a7381f4..61b4462d1 100755 --- a/plugins/yiqing/data_source.py +++ b/plugins/yiqing/data_source.py @@ -1,7 +1,10 @@ from configs.path_config import TEXT_PATH -from typing import List +from typing import List, Union from pathlib import Path from utils.http_utils import AsyncHttpx +from utils.image_utils import text2image +from utils.message_builder import image +from nonebot.adapters.cqhttp import MessageSegment import ujson as json china_city = Path(TEXT_PATH) / "china_city.json" @@ -12,7 +15,7 @@ url = "https://view.inews.qq.com/g2/getOnsInfo?name=disease_h5" -async def get_yiqing_data(area: str): +async def get_yiqing_data(area: str) -> Union[str, MessageSegment]: """ 查看疫情数据 :param area: 省份/城市 @@ -40,16 +43,16 @@ async def get_yiqing_data(area: str): if area == "中国": data_ = epidemic_data["areaTree"][0] else: - data_ = [ - x - for x in epidemic_data["areaTree"][0]["children"] - if x["name"] == province - ][0] - if city: - try: + try: + data_ = [ + x + for x in epidemic_data["areaTree"][0]["children"] + if x["name"] == province + ][0] + if city: data_ = [x for x in data_["children"] if x["name"] == city][0] - except IndexError: - return "未查询到..." + except IndexError: + return "未查询到..." confirm = data_["total"]["confirm"] # 累计确诊 heal = data_["total"]["heal"] # 累计治愈 dead = data_["total"]["dead"] # 累计死亡 @@ -59,20 +62,22 @@ async def get_yiqing_data(area: str): suspect = data_["total"]["suspect"] # 疑似 add_confirm = data_["today"]["confirm"] # 新增确诊 x = f"{city}市" if city else f"{province}{province_type}" - return ( - f"{x} 疫情数据:\n" - f"\t目前确诊:\n" - f"\t\t确诊人数:{now_confirm}(+{add_confirm})\n" - f"\t\t疑似人数:{suspect}\n" - f"==================\n" - f"\t累计数据:\n" - f"\t\t确诊人数:{confirm}\n" - f"\t\t治愈人数:{heal}\n" - f"\t\t死亡人数:{dead}\n" - f"\t治愈率:{heal_rate}%\n" - f"\t死亡率:{dead_rate}%\n" - f"更新日期:{last_update_time}" - ) + return image(b64=await text2image( + f""" + {x} 疫情数据: + 目前确诊: + 确诊人数:{now_confirm}(+{add_confirm}) + 疑似人数:{suspect} + ----------------- + 累计数据: + 确诊人数:{confirm} + 治愈人数:{heal} + 死亡人数:{dead} + 治愈率:{heal_rate}% + 死亡率:{dead_rate}% + 更新日期:{last_update_time} + """, font_size=30, color="#f9f6f2" + )) def get_city_and_province_list() -> List[str]: diff --git a/plugins/yiqing/other_than.py b/plugins/yiqing/other_than.py index 505382081..d47a6d5fe 100644 --- a/plugins/yiqing/other_than.py +++ b/plugins/yiqing/other_than.py @@ -6,8 +6,12 @@ # @File : other_than.py # @Software: PyCharm from utils.http_utils import AsyncHttpx +from nonebot.adapters.cqhttp import MessageSegment from typing import Optional from services.log import logger +from utils.image_utils import text2image +from utils.message_builder import image +from json.decoder import JSONDecodeError import re import json @@ -23,50 +27,66 @@ def intcomma(value) -> str: return new if orig == new else intcomma(new) -async def get_other_data(place: str) -> Optional[str]: +async def get_other_data(place: str, count: int = 0) -> Optional[MessageSegment]: """ :param place: 地名 + :param count: 递归次数 :return: 格式化字符串 """ + if count == 5: + return None try: html = ( (await AsyncHttpx.get("https://news.ifeng.com/c/special/7uLj4F83Cqm")) .text.replace("\n", "") .replace(" ", "") ) + find_data = re.compile(r"varallData=(.*?);") + sum_ = re.findall(find_data, html)[0] + sum_ = json.loads(sum_) + except JSONDecodeError: + return await get_other_data(place, count + 1) except Exception as e: logger.error(f"疫情查询发生错误 {type(e)}:{e}") return None - find_data = re.compile(r"varallData=(.*?);") - sum_ = re.findall(find_data, html)[0] try: - sum_ = json.loads(sum_) other_country = sum_["yiqing_v2"]["dataList"][29]["child"] for country in other_country: if place == country["name2"]: - return ( - f"{place} 疫情数据:\n" - "——————————————\n" - f"新增病例:{intcomma(country['quezhen_add'])}\n" - f"现有确诊:{intcomma(country['quezhen_xianyou'])}\n" - f"累计确诊:{intcomma(country['quezhen'])}\n" - f"累计治愈:{intcomma(country['zhiyu'])}\n" - f"死亡:{intcomma(country['siwang'])}\n" - "——————————————" - # f"更新时间:{country['sys_publishDateTime']}" - # 时间无法精确到分钟,网页用了js我暂时找不到 + return image( + b64=await text2image( + f" {place} 疫情数据:\n" + "——————————————\n" + f" 新增病例:{intcomma(country['quezhen_add'])}\n" + f" 现有确诊:{intcomma(country['quezhen_xianyou'])}\n" + f" 累计确诊:{intcomma(country['quezhen'])}\n" + f" 累计治愈:{intcomma(country['zhiyu'])}\n" + f" 死亡:{intcomma(country['siwang'])}\n" + "——————————————" + # f"更新时间:{country['sys_publishDateTime']}" + # 时间无法精确到分钟,网页用了js我暂时找不到 + , + font_size=30, + color="#f9f6f2", + padding=15 + ) ) else: for city in country["child"]: if place == city["name3"]: - return ( - f"{place} 疫情数据:\n" - "——————————————\n" - f"新增病例:{intcomma(city['quezhen_add'])}\n" - f"累计确诊:{intcomma(city['quezhen'])}\n" - f"累计治愈:{intcomma(city['zhiyu'])}\n" - f"死亡:{intcomma(city['siwang'])}\n" - "——————————————" + return image( + b64=await text2image( + f"\n{place} 疫情数据:\n" + "——————————————\n" + f"\t新增病例:{intcomma(city['quezhen_add'])}\n" + f"\t累计确诊:{intcomma(city['quezhen'])}\n" + f"\t累计治愈:{intcomma(city['zhiyu'])}\n" + f"\t死亡:{intcomma(city['siwang'])}\n" + "——————————————\n", + font_size=30, + color="#f9f6f2", + padding=15 + ) ) except Exception as e: logger.error(f"疫情查询发生错误 {type(e)}:{e}") diff --git a/resources/img/other/time.png b/resources/img/other/time.png new file mode 100644 index 000000000..c795b9e4f Binary files /dev/null and b/resources/img/other/time.png differ diff --git a/resources/ttf/CJGaoDeGuo.otf b/resources/ttf/CJGaoDeGuo.otf new file mode 100644 index 000000000..1e7d11952 Binary files /dev/null and b/resources/ttf/CJGaoDeGuo.otf differ diff --git a/resources/ttf/SweiSpringCJKtc-Bold.ttf b/resources/ttf/SweiSpringCJKtc-Bold.ttf new file mode 100644 index 000000000..ebfafe8ce Binary files /dev/null and b/resources/ttf/SweiSpringCJKtc-Bold.ttf differ diff --git a/resources/ttf/SweiSpringSugarCJKtc-Regular.ttf b/resources/ttf/SweiSpringSugarCJKtc-Regular.ttf new file mode 100644 index 000000000..2028acd55 Binary files /dev/null and b/resources/ttf/SweiSpringSugarCJKtc-Regular.ttf differ diff --git a/resources/ttf/YSHaoShenTi-2.ttf b/resources/ttf/YSHaoShenTi-2.ttf new file mode 100644 index 000000000..a67764a9f Binary files /dev/null and b/resources/ttf/YSHaoShenTi-2.ttf differ diff --git a/resources/ttf/sarasa-mono-sc-nerd-regular.ttf b/resources/ttf/sarasa-mono-sc-nerd-regular.ttf new file mode 100644 index 000000000..636c2f445 Binary files /dev/null and b/resources/ttf/sarasa-mono-sc-nerd-regular.ttf differ diff --git a/services/db_context.py b/services/db_context.py index 40891e186..86139a1f8 100755 --- a/services/db_context.py +++ b/services/db_context.py @@ -17,7 +17,7 @@ async def init(): await db.gino.create_all() logger.info(f'Database loaded successfully!') except Exception as e: - raise Exception(f'数据库连接错误.... e: {e}') + raise Exception(f'数据库连接错误.... {type(e)}: {e}') async def disconnect(): diff --git a/update_info.json b/update_info.json index 7569e0f52..58aed6467 100644 --- a/update_info.json +++ b/update_info.json @@ -2,11 +2,10 @@ "update_file": [ "plugins", "models", - "basic_plugins", - "utils", "services", - "configs/utils", + "utils", + "resources/ttf" ], - "add_file": ["resources/img/genshin/genshin_card", "resources/img/genshin/genshin_memo"], + "add_file": ["resources/img/other/time.png"], "delete_file": [] } diff --git a/utils/image_utils.py b/utils/image_utils.py index ed04e6034..a66b09735 100755 --- a/utils/image_utils.py +++ b/utils/image_utils.py @@ -11,6 +11,7 @@ import cv2 import base64 import imagehash +import re ImageFile.LOAD_TRUNCATED_IMAGES = True Image.MAX_IMAGE_PIXELS = None @@ -136,6 +137,178 @@ def is_valid(file: str) -> bool: return valid +async def text2image( + text: str, + auto_parse: bool = True, + font_size: int = 20, + color: Union[str, Tuple[int, int, int], Tuple[int, int, int, int]] = "white", + font: str = "CJGaoDeGuo.otf", + font_color: Union[str, Tuple[int, int, int]] = "black", + padding: Union[int, Tuple[int, int, int, int]] = 0, +) -> str: + """ + 说明: + 解析文本并转为图片 + 使用标签 + + 可选配置项 + font: str -> 特殊文本字体 + font_size: int -> 特殊文本大小 + font_color: str -> 特殊文本颜色 + 示例 + 在不在,HibiKi小姐, + 你最近还好吗,我非常想你,这段时间我非常不好过, + 抽卡抽不到金色,这让我很痛苦 + 参数: + :param text: 文本 + :param auto_parse: 是否自动解析,否则原样发送 + :param font_size: 普通字体大小 + :param color: 背景颜色 + :param font: 普通字体 + :param font_color: 普通字体颜色 + :param padding: 文本外边距,元组类型时为 (上,左,下,右) + """ + if auto_parse and re.search(r"(.*)", text): + _data = [] + new_text = "" + placeholder_index = 0 + for s in text.split(""): + r = re.search(r"(.*)", s) + if r: + start, end = r.span() + if start != 0 and (t := s[:start]): + new_text += t + _data.append( + [ + (start, end), + f"[placeholder_{placeholder_index}]", + r.group(1).strip(), + r.group(2), + ] + ) + new_text += f"[placeholder_{placeholder_index}]" + placeholder_index += 1 + new_text += text.split("")[-1] + image_list = [] + current_placeholder_index = 0 + # 切分换行,每行为单张图片 + for s in new_text.split("\n"): + _tmp_text = s + img_height = BuildImage(0, 0, font_size=font_size).getsize("正")[1] + img_width = 0 + _tmp_index = current_placeholder_index + for _ in range(s.count("[placeholder_")): + placeholder = _data[_tmp_index] + if "font_size" in placeholder[2]: + r = re.search(r"font_size=['\"]?(\d+)", placeholder[2]) + if r: + w, h = BuildImage(0, 0, font_size=int(r.group(1))).getsize( + placeholder[3] + ) + img_height = img_height if img_height > h else h + img_width += w + else: + img_width += BuildImage(0, 0, font_size=font_size).getsize( + placeholder[3] + )[0] + _tmp_text = _tmp_text.replace(f"[placeholder_{_tmp_index}]", "") + _tmp_index += 1 + img_width += BuildImage(0, 0, font_size=font_size).getsize(_tmp_text)[0] + # img_width += len(_tmp_text) * font_size + # 开始画图 + A = BuildImage( + img_width, img_height, color=color, font=font, font_size=font_size + ) + basic_font_h = A.getsize("正")[1] + current_width = 0 + # 遍历占位符 + for _ in range(s.count("[placeholder_")): + if not s.startswith(f"[placeholder_{current_placeholder_index}]"): + slice_ = s.split(f"[placeholder_{current_placeholder_index}]") + await A.atext( + (current_width, A.h - basic_font_h - 1), slice_[0], font_color + ) + current_width += A.getsize(slice_[0])[0] + placeholder = _data[current_placeholder_index] + # 解析配置 + _font = font + _font_size = font_size + _font_color = font_color + for e in placeholder[2].split(): + if e.startswith("font="): + _font = e.split("=")[-1] + if e.startswith("font_size="): + _font_size = int(e.split("=")[-1]) + if _font_size > 1000: + _font_size = 1000 + if _font_size < 1: + _font_size = 1 + if e.startswith("font_color"): + _font_color = e.split("=")[-1] + text_img = BuildImage( + 0, + 0, + plain_text=placeholder[3], + font_size=_font_size, + font_color=_font_color, + font=_font, + ) + _img_h = ( + int(A.h / 2 - text_img.h / 2) + if new_text == "[placeholder_0]" + else A.h - text_img.h + ) + await A.apaste(text_img, (current_width, _img_h - 1), True) + current_width += text_img.w + s = s[ + s.index(f"[placeholder_{current_placeholder_index}]") + + len(f"[placeholder_{current_placeholder_index}]") : + ] + current_placeholder_index += 1 + if s: + slice_ = s.split(f"[placeholder_{current_placeholder_index}]") + await A.atext((current_width, A.h - basic_font_h), slice_[0]) + current_width += A.getsize(slice_[0])[0] + A.crop((0, 0, current_width, A.h)) + # A.show() + image_list.append(A) + height = 0 + width = 0 + for img in image_list: + height += img.h + width = width if width > img.w else img.w + top_padding = left_padding = 0 + if padding: + if isinstance(padding, int): + width += padding * 2 + height += padding * 2 + top_padding = left_padding = padding + elif isinstance(padding, tuple): + width += padding[0] + padding[2] + height += padding[1] + padding[3] + top_padding = padding[0] + left_padding = padding[1] + A = BuildImage(width + left_padding, height + top_padding, color=color) + current_height = top_padding + for img in image_list: + await A.apaste(img, (left_padding, current_height), True) + current_height += img.h + # A.show() + return A.pic2bs4() + else: + width = 0 + height = 0 + _tmp = BuildImage(0, 0, font_size=font_size) + for x in text.split("\n"): + w, h = _tmp.getsize(x) + height += h + width = width if width > w else w + A = BuildImage(width, height, font_size=font_size, color=color) + await A.atext((0, 0), text, font_color) + # A.show() + return A.pic2bs4() + + class BuildImage: """ 快捷生成图片与操作图片的工具类 @@ -155,7 +328,7 @@ def __init__( ratio: float = 1, is_alpha: bool = False, plain_text: Optional[str] = None, - font_color: Optional[Tuple[int, int, int]] = None, + font_color: Optional[Union[str, Tuple[int, int, int]]] = None, ): """ 参数: @@ -167,7 +340,7 @@ def __init__( :param image_mode: 图片的类型 :param font_size: 文字大小 :param background: 打开图片的路径 - :param ttf: 字体,默认在 resource/ttf/ 路径下 + :param font: 字体,默认在 resource/ttf/ 路径下 :param ratio: 倍率压缩 :param is_alpha: 是否背景透明 :param plain_text: 纯文字文本 @@ -367,7 +540,7 @@ async def atext( self, pos: Tuple[int, int], text: str, - fill: Tuple[int, int, int] = (0, 0, 0), + fill: Union[str, Tuple[int, int, int]] = (0, 0, 0), center_type: Optional[Literal["center", "by_height", "by_width"]] = None, ): """ @@ -385,7 +558,7 @@ def text( self, pos: Tuple[int, int], text: str, - fill: Tuple[int, int, int] = (0, 0, 0), + fill: Union[str, Tuple[int, int, int]] = (0, 0, 0), center_type: Optional[Literal["center", "by_height", "by_width"]] = None, ): """ @@ -620,7 +793,7 @@ def polygon( async def aline( self, xy: Tuple[int, int, int, int], - fill: Optional[Tuple[int, int, int]] = None, + fill: Optional[Union[str, Tuple[int, int, int]]] = None, width: int = 1, ): """ @@ -636,7 +809,7 @@ async def aline( def line( self, xy: Tuple[int, int, int, int], - fill: Optional[Tuple[int, int, int]] = None, + fill: Optional[Union[Tuple[int, int, int], str]] = None, width: int = 1, ): """ @@ -784,6 +957,58 @@ def filter(self, filter_: str, aud: int = None): self.markImg = self.markImg.filter(_x) self.draw = ImageDraw.Draw(self.markImg) + async def areplace_color_tran( + self, + src_color: Union[ + Tuple[int, int, int], Tuple[Tuple[int, int, int], Tuple[int, int, int]] + ], + replace_color: Tuple[int, int, int], + ): + """ + 说明: + 异步 颜色替换 + 参数: + :param src_color: 目标颜色,或者使用列表,设置阈值 + :param replace_color: 替换颜色 + """ + self.loop.run_in_executor( + None, self.replace_color_tran, src_color, replace_color + ) + + def replace_color_tran( + self, + src_color: Union[ + Tuple[int, int, int], Tuple[Tuple[int, int, int], Tuple[int, int, int]] + ], + replace_color: Tuple[int, int, int], + ): + """ + 说明: + 颜色替换 + 参数: + :param src_color: 目标颜色,或者使用元祖,设置阈值 + :param replace_color: 替换颜色 + """ + if isinstance(src_color, tuple): + start_ = src_color[0] + end_ = src_color[1] + else: + start_ = src_color + end_ = None + for i in range(self.w): + for j in range(self.h): + r, g, b = self.markImg.getpixel((i, j)) + if not end_: + if r == start_[0] and g == start_[1] and b == start_[2]: + self.markImg.putpixel((i, j), replace_color) + else: + if ( + start_[0] <= r <= end_[0] + and start_[1] <= g <= end_[1] + and start_[2] <= b <= end_[2] + ): + self.markImg.putpixel((i, j), replace_color) + # def getchannel(self, type_): self.markImg = self.markImg.getchannel(type_) diff --git a/utils/manager/plugins2settings_manager.py b/utils/manager/plugins2settings_manager.py index 0a7949782..5b3abf7dd 100755 --- a/utils/manager/plugins2settings_manager.py +++ b/utils/manager/plugins2settings_manager.py @@ -21,6 +21,9 @@ def __init__(self, file: Path): self._data = ( self._data["PluginSettings"] if self._data["PluginSettings"] else {} ) + for x in self._data.keys(): + if self._data[x].get("cost_gold") is None: + self._data[x]["cost_gold"] = 0 def add_plugin_settings( self, @@ -30,7 +33,8 @@ def add_plugin_settings( level: Optional[int] = 5, limit_superuser: Optional[bool] = False, plugin_type: Tuple[Union[str, int]] = ("normal",), - data_dict: Optional[dict] = None, + cost_gold: int = 0, + **kwargs ): """ 添加一个插件设置 @@ -40,21 +44,22 @@ def add_plugin_settings( :param level: 功能权限等级 :param limit_superuser: 功能状态是否限制超级用户 :param plugin_type: 插件类型 - :param data_dict: 封装好的字典数据 + :param cost_gold: 需要消费的金币 """ - if data_dict: - level = data_dict.get("level") if data_dict.get("level") is not None else 5 + if kwargs: + level = kwargs.get("level") if kwargs.get("level") is not None else 5 default_status = ( - data_dict.get("default_status") - if data_dict.get("default_status") is not None + kwargs.get("default_status") + if kwargs.get("default_status") is not None else True ) limit_superuser = ( - data_dict.get("limit_superuser") - if data_dict.get("limit_superuser") is not None + kwargs.get("limit_superuser") + if kwargs.get("limit_superuser") is not None else False ) - cmd = data_dict.get("cmd") if data_dict.get("cmd") is not None else [] + cmd = kwargs.get("cmd") if kwargs.get("cmd") is not None else [] + cost_gold = cost_gold if kwargs.get("cost_gold") else 0 self._data[plugin] = { "level": level if level is not None else 5, "default_status": default_status if default_status is not None else True, @@ -65,6 +70,7 @@ def add_plugin_settings( "plugin_type": list( plugin_type if plugin_type is not None else ("normal",) ), + "cost_gold": cost_gold, } def get_plugin_data(self, module: str) -> dict: