Compare commits

...
8 Commits
Author SHA1 Message Date
HibiKier f32d8b4eff Update __version__ 2022-02-10 11:58:22 +08:00
HibiKier 685f2b8c53 Update README.md 2022-02-10 11:57:41 +08:00
HibiKier 1e63ca2549 Update buy.py 2022-02-10 11:55:36 +08:00
HibiKier c1bc4eff2b Update README.md 2022-02-09 23:02:14 +08:00
HibiKier bc903d3813 Update __init__.py 2022-02-09 22:28:49 +08:00
HibiKier 80fc17b610 Update __version__ 2022-02-09 21:44:09 +08:00
HibiKier 0efb2bbc60 update v0.1 2022-02-09 20:05:49 +08:00
HibiKier 4cb91b47e6 update v0.0.9.0 2022-01-16 14:52:50 +08:00
72 changed files with 1581 additions and 550 deletions
+1
View File
@@ -145,3 +145,4 @@ Yu-Gi-Oh/
search_image/
black_word/
csgo/
fantasy_card/
+50 -5
View File
@@ -18,6 +18,9 @@
## 声明
此项目仅用于学习交流,请勿用于非法用途
## Gitee同步
# [Gitee](https://gitee.com/two_Dimension/zhenxun_bot)
## 未完成的文档
# [传送门](https://hibikier.github.io/zhenxun_bot/)
@@ -25,6 +28,9 @@
## 真寻的帮助
请对真寻说: '真寻帮助' or '管理员帮助' or '超级用户帮助' or '真寻帮助 指令'
## 普通帮助图片
![x](https://github.com/HibiKier/zhenxun_bot/blob/0.0.8.2/docs_image/3238573864-836268675-E2FFBB2AC143EAF4DDDF150438508721.png)
## 提供符合真寻标准的插件仓库
[AkashiCoin/nonebot_plugins_zhenxun_bot](https://github.com/AkashiCoin/nonebot_plugins_zhenxun_bot)
@@ -32,9 +38,6 @@
## 来点优点?
一.作为bot:
* 实现了许多功能,且提供了大量功能管理命令
* __..... 更多详细请通过`传送门`查看文档!__
二.作为框架?:
* 通过Config配置项将所有插件配置统计保存至config.yaml,利于统一用户修改
* 方便增删插件,原生nonebot2 matcher,不需要额外修改,仅仅通过简单的配置属性就可以生成`帮助图片`和`帮助信息`
* 提供了cd,阻塞,每日次数等限制,仅仅通过简单的属性就可以生成一个限制,例如:`__plugin_cd_limit__`
@@ -67,6 +70,8 @@
- [x] 原神资源查询 (借鉴[Genshin_Impact_bot](https://github.com/H-K-Y/Genshin_Impact_bot)插件)
- [x] 原神便笺查询
- [x] 原神玩家查询
- [x] 原神树脂提醒
- [x] 原神签到/自动签到
- [x] 金币红包
- [x] 微博热搜
- [x] B站主播/UP/番剧订阅
@@ -147,6 +152,7 @@
- [x] 清理临时数据
- [x] 增删群认证
- [x] 同意/拒绝好友/群聊请求
- [x] 配置重载
#### 超级用户的被动技能
- [x] 邀请入群提醒(别人邀请真寻入群)
@@ -171,6 +177,7 @@
- [x] 群管理员监控,自动为新晋管理员增加权限,为失去群管理员的用户删除权限
- [x] 群权限系统
- [x] 定时更新权限
- [x] 自动配置重载
</details>
## 详细配置请前往文档,以下为最简部署和配置,如果你有基础并学习过nonebot2的话
@@ -217,11 +224,49 @@ python bot.py
```
## 使用Docker
__Docker 最新版本由 [Sakuracio](https://github.com/Sakuracio) 提供__
#### GitHub:[Sakuracio/zxenv](https://github.com/Sakuracio/zxenv)
#### DockerHub:[hibikier/zhenxun_bot](https://hub.docker.com/r/hibikier/zhenxun_bot)
## 更新
### 2021/1/5 \[v0.0.7.2]
### 2021/2/10 \[v0.1.1]
* 修复购买道具出错
### 2021/2/9 \[v0.1]
* 新增原神自动签到和手动签到
* 新增原神树脂提醒
* 新增手动重载Config.yaml命令以及重载配置定时任务(极少部分帮助或配置可能需要重启
* 修改了发送本地图库的matcher,改为on_message
* register_use可以通过返回值发送消息
* 修复修改商品时限制时间出错
* 修复超时商品依旧可以被购买
### 2021/1/16 \[v0.0.9.0]
* Ai提供文本敏感词过滤器
* 疫情插件适配新版腾讯API
* 修复/t回复带空格切分
* 修复原神玩家查询缺少渊下宫和稻妻家园以及角色不完全
* 修复方法 text2image 中 padding 和 font 无法对纯文本生效
* 修复签到图片中信息并未使用配置文件中的色图概率
* 修改原神大地图合成方式,改为先压缩再合成
* bag_user弃用字段props(该字段会在下次更新删除),使用新字段property
* 数据库中所有belonging_group统一修改为group_id
* 商店将registered_use和register_goods更名为register_use何register_goods
* 商品注册提供了kwargs参数提供:
* bot
* event
* 特殊字段
* “send_success_msg”(发送成功的交互信息->即:使用道具 {name} {num} 次成功)
* “_max_num_limit”(该道具单次使用的最多个数,默认1)
### 2021/1/5 \[v0.0.8.2]
* 提供金币消费hook,可在plugins2settings.yaml中配置该功能需要消费的金币
* 商店插件将作为内置插件移动至basic_plugins
@@ -292,5 +337,5 @@ __..... 更多更新信息请查看文档__
[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机器人
[Ailitonia / omega-miya](https://github.com/Ailitonia/omega-miya) :基于nonebot2的qq机器人
[KimigaiiWuyi / GenshinUID]("https://github.com/KimigaiiWuyi/GenshinUID") :一个基于HoshinoBot/NoneBot2的原神UID查询插件
+1 -1
View File
@@ -1 +1 @@
__version__: v0.0.8.0
__version__: v0.1.1
@@ -1,5 +1,5 @@
from nonebot import on_command
from utils.utils import get_message_text, get_message_imgs
from utils.utils import get_message_text, get_message_img
from nonebot.typing import T_State
from nonebot.adapters.cqhttp import Bot, GroupMessageEvent
from .data_source import custom_group_welcome
@@ -38,7 +38,7 @@ custom_welcome = on_command(
async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
try:
msg = get_message_text(event.json())
imgs = get_message_imgs(event.json())
imgs = get_message_img(event.json())
if not msg and not imgs:
await custom_welcome.finish(__plugin_usage__)
await custom_welcome.send(
+2 -2
View File
@@ -3,7 +3,7 @@ from nonebot.typing import T_State
from nonebot.adapters import Bot, Event
from nonebot.permission import SUPERUSER
import asyncio
from utils.utils import get_message_text, get_message_imgs
from utils.utils import get_message_text, get_message_img
from services.log import logger
from utils.message_builder import image
from utils.manager import group_manager
@@ -35,7 +35,7 @@ broadcast = on_command("广播-", priority=1, permission=SUPERUSER, block=True)
@broadcast.handle()
async def _(bot: Bot, event: Event, state: T_State):
msg = get_message_text(event.json())
imgs = get_message_imgs(event.json())
imgs = get_message_img(event.json())
rst = ""
for img in imgs:
rst += image(img)
+4 -4
View File
@@ -111,7 +111,7 @@ def _create_help_img(
logger.warning(f"{type(e)}: {e}")
else:
matchers_data[plugin_type][plugin_name] = {
"module": matcher.module,
"modules": matcher.module,
"des": plugin_des,
"cmd": plugin_cmd,
"text_type": text_type,
@@ -147,16 +147,16 @@ def _create_help_img(
# 禁用flag
flag = True
if plugins_manager.get_plugin_status(
matchers_data[type_][k]["module"], "all"
matchers_data[type_][k]["modules"], "all"
):
flag = False
if group_id:
flag = flag and plugins_manager.get_plugin_status(
matchers_data[type_][k]["module"], "group"
matchers_data[type_][k]["modules"], "group"
)
simple_help_str += (
f"{i+1}.{k}<|_|~|>"
f"{group_manager.get_plugin_status(matchers_data[type_][k]['module'], group_id) if group_id else '_'}|"
f"{group_manager.get_plugin_status(matchers_data[type_][k]['modules'], group_id) if group_id else '_'}|"
f"{flag}\n"
)
if matchers_data[type_][k]["text_type"] == 1:
-28
View File
@@ -260,34 +260,6 @@ async def _(matcher: Matcher, bot: Bot, event: MessageEvent, state: T_State):
# 以下为限制检测 #######################################################
# 以下为限制检测 #######################################################
# 以下为限制检测 #######################################################
# 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)
@@ -11,7 +11,6 @@ from .init_plugins_limit import (
)
from .init import init
from .check_plugin_status import check_plugin_status
from utils.manager import admin_manager
from nonebot.adapters.cqhttp import Bot
from configs.path_config import DATA_PATH
from services.log import logger
+96 -3
View File
@@ -1,11 +1,17 @@
from asyncpg.exceptions import (
DuplicateColumnError,
UndefinedColumnError,
PostgresSyntaxError,
)
from nonebot import Driver
from services.db_context import db
from asyncpg.exceptions import DuplicateColumnError
from models.group_info import GroupInfo
from models.bag_user import BagUser
from nonebot.adapters.cqhttp import Bot
from services.log import logger
from configs.path_config import TEXT_PATH
from asyncio.exceptions import TimeoutError
from typing import List
from utils.http_utils import AsyncHttpx
from utils.utils import scheduler
from pathlib import Path
@@ -61,15 +67,82 @@ async def _():
"""
数据库表结构变换
"""
_flag = []
sql_str = [
"ALTER TABLE group_info ADD group_flag Integer NOT NULL DEFAULT 0;" # group_info表添加一个group_flag
(
"ALTER TABLE group_info ADD group_flag Integer NOT NULL DEFAULT 0;",
"group_info",
), # group_info表添加一个group_flag
(
"ALTER TABLE bag_users rename belonging_group To group_id;",
"bag_users",
), # 将 bag_users 的 belonging_group 改为 group_id
(
"ALTER TABLE group_info_users rename belonging_group To group_id;",
"group_info_users",
),
(
"ALTER TABLE sign_group_users rename belonging_group To group_id;",
"sign_group_users",
),
(
"ALTER TABLE open_cases_users rename belonging_group To group_id;",
"open_cases_users",
),
(
"ALTER TABLE bag_users ADD property json NOT NULL DEFAULT '{}';",
"bag_users",
), # bag_users 新增字段 property 替代 props
(
"ALTER TABLE genshin ADD auto_sign_time timestamp with time zone;",
"genshin"
), # 新增原神自动签到字段
(
"ALTER TABLE genshin ADD resin_remind boolean DEFAULT False;",
"genshin"
), # 新增原神自动签到字段
(
"ALTER TABLE genshin ADD resin_recovery_time timestamp with time zone;",
"genshin"
), # 新增原神自动签到字段
]
for sql in sql_str:
try:
flag = sql[1]
sql = sql[0]
query = db.text(sql)
await db.first(query)
except DuplicateColumnError:
logger.info(f"完成sql操作:{sql}")
_flag.append(flag)
except (DuplicateColumnError, UndefinedColumnError):
pass
except PostgresSyntaxError:
logger.error(f"语法错误:执行sql失败:{sql}")
# bag_user 将文本转为字典格式
await __database_script(_flag)
# 完成后
end_sql_str = [
# "ALTER TABLE bag_users DROP COLUMN props;" # 删除 bag_users 的 props 字段(还不到时候)
]
for sql in end_sql_str:
try:
query = db.text(sql)
await db.first(query)
logger.info(f"完成执行sql操作:{sql}")
except (DuplicateColumnError, UndefinedColumnError):
pass
except PostgresSyntaxError:
logger.error(f"语法错误:执行sql失败:{sql}")
# str2json_sql = ["alter table bag_users alter COLUMN props type json USING props::json;"] # 字段类型替换
# rename_sql = 'alter table {} rename {} to {};' # 字段更名
# for sql in str2json_sql:
# try:
# query = db.text(sql)
# await db.first(query)
# except DuplicateColumnError:
# pass
@driver.on_bot_connect
@@ -105,6 +178,26 @@ async def _(bot: Bot):
logger.info(f"移除不存在的群聊信息:{group_id}")
async def __database_script(_flag: List[str]):
# bag_user 将文本转为字典格式
if "bag_users" in _flag:
for x in await BagUser.get_all_users():
props = {}
if x.props:
for prop in [p for p in x.props.split(",") if p]:
if props.get(prop):
props[prop] += 1
else:
props[prop] = 1
logger.info(
f"__database_script USER {x.user_qq} GROUP {x.group_id} 更新数据 {props}"
)
await x.update(
property=props,
props="",
).apply()
# 自动更新城市列表
@scheduler.scheduled_job(
"cron",
+19 -11
View File
@@ -7,6 +7,7 @@ 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
import time
__zx_plugin_name__ = "商店 - 购买道具"
@@ -20,7 +21,7 @@ usage:
""".strip()
__plugin_des__ = "商店 - 购买道具"
__plugin_cmd__ = ["购买 [序号或名称] ?[数量=1]"]
__plugin_type__ = ('商店',)
__plugin_type__ = ("商店",)
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
@@ -29,6 +30,7 @@ __plugin_settings__ = {
"limit_superuser": False,
"cmd": ["商店", "购买道具"],
}
__plugin_cd_limit__ = {"cd": 3}
buy = on_command("购买", aliases={"购买道具"}, priority=5, block=True, permission=GROUP)
@@ -39,8 +41,15 @@ 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]
goods_list = [
x
for x in await GoodsInfo.get_all_goods()
if x.goods_limit_time > time.time() or x.goods_limit_time == 0
]
goods_name_list = [
x.goods_name
for x in goods_list
]
msg = get_message_text(event.json()).split()
num = 1
if len(msg) > 1:
@@ -48,17 +57,16 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
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:
if msg > len(goods_name_list) or msg < 1:
await buy.finish("请输入正确的商品id!", at_sender=True)
goods = goods_lst[msg - 1]
goods = goods_list[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]
if msg[0] in goods_name_list:
for i in range(len(goods_name_list)):
if msg[0] == goods_name_list[i]:
goods = goods_list[i]
break
else:
await buy.finish("请输入正确的商品名称!")
@@ -69,7 +77,7 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
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):
if await BagUser.buy_property(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,
+1 -1
View File
@@ -34,7 +34,7 @@ gold_rank = on_command("金币排行", priority=5, block=True, permission=GROUP)
@my_gold.handle()
async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
await my_gold.finish(await BagUser.get_my_total_gold(event.user_id, event.group_id))
await my_gold.finish(await BagUser.get_user_total_gold(event.user_id, event.group_id))
@gold_rank.handle()
+3 -13
View File
@@ -31,21 +31,11 @@ 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)
props = await BagUser.get_property(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"
for i, p in enumerate(props.keys()):
rst += f"{i+1}.{p}\t×{props[p]}\n"
await my_props.send("\n" + rst[:-1], at_sender=True)
logger.info(f"USER {event.user_id} GROUP {event.group_id} 查看我的道具")
else:
+3 -3
View File
@@ -3,7 +3,7 @@ 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 .data_source import create_shop_help, delete_goods, update_goods, register_goods, parse_goods_info
from nonebot.permission import SUPERUSER
from utils.utils import get_message_text, is_number
from nonebot.plugin import export
@@ -53,7 +53,7 @@ __plugin_block_limit__ = {
# 导出方法供其他插件使用
export = export()
export.registered_goods = registered_goods
export.register_goods = register_goods
export.delete_goods = delete_goods
export.update_goods = update_goods
@@ -80,7 +80,7 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
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):
if await register_goods(**data):
await shop_add_goods.send(f"添加商品 {data['name']} 成功!\n"
f"名称:{data['name']}\n"
f"价格:{data['price']}金币\n"
+13 -25
View File
@@ -20,36 +20,24 @@ async def init_default_shop_goods():
"""
导入内置的三个商品
"""
async def sign_card_1(**kwargs):
async def sign_card(**kwargs):
user_id = kwargs['user_id']
group_id = kwargs['group_id']
prob = kwargs["prob"]
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()
await user.update(add_probability=prob).apply()
if Config.get_config("shop", "IMPORT_DEFAULT_SHOP_GOODS"):
await registered_goods(
await register_goods(
"好感度双倍加持卡Ⅰ", 30, "下次签到双倍好感度概率 + 10%(谁才是真命天子?)(同类商品将覆盖)"
)
use.registered_use("好感度双倍加持卡Ⅰ", sign_card_1)
await registered_goods("好感度双倍加持卡Ⅱ", 150, "下次签到双倍好感度概率 + 20%(平平庸庸)(同类商品将覆盖)")
use.registered_use("好感度双倍加持卡Ⅱ", sign_card_2)
await registered_goods(
use.register_use("好感度双倍加持卡Ⅰ", sign_card, **{"prob": 0.1})
await register_goods("好感度双倍加持卡Ⅱ", 150, "下次签到双倍好感度概率 + 20%(平平庸庸)(同类商品将覆盖)")
use.register_use("好感度双倍加持卡Ⅱ", sign_card, **{"prob": 0.2})
await register_goods(
"好感度双倍加持卡Ⅲ", 250, "下次签到双倍好感度概率 + 30%(金币才是真命天子!)(同类商品将覆盖)"
)
use.registered_use("好感度双倍加持卡Ⅲ", sign_card_3)
use.register_use("好感度双倍加持卡Ⅲ", sign_card, **{"prob": 0.3})
# 创建商店界面
@@ -136,7 +124,7 @@ async def create_shop_help() -> str:
return shop.pic2bs4()
async def registered_goods(
async def register_goods(
name: str,
price: int,
des: str,
@@ -229,11 +217,12 @@ async def update_goods(**kwargs) -> "str, str, int":
tmp += f'折扣:{discount} --> {kwargs["discount"]}\n'
discount = kwargs["discount"]
if kwargs.get("limit_time"):
kwargs["limit_time"] = float(kwargs["limit_time"])
new_time = time.strftime(
"%Y-%m-%d %H:%M:%S",
time.localtime(time.time() + int(kwargs["limit_time"] * 60 * 60)),
time.localtime(time.time() + kwargs["limit_time"] * 60 * 60),
)
tmp += f"折扣至: {new_time}\n"
tmp += f"限时至: {new_time}\n"
limit_time = kwargs["limit_time"]
return (
await GoodsInfo.update_goods(
@@ -261,7 +250,6 @@ def parse_goods_info(msg: str) -> Union[dict, str]:
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":
+29 -22
View File
@@ -7,7 +7,7 @@ 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
from .data_source import effect, register_use, func_manager
__zx_plugin_name__ = "商店 - 使用道具"
@@ -15,7 +15,7 @@ __plugin_usage__ = """
usage:
普通的使用道具
指令:
使用道具 [序号或道具名称]
使用道具 [序号或道具名称] ?[数量]=1
* 序号以 ”我的道具“ 为准 *
""".strip()
__plugin_des__ = "商店 - 使用道具"
@@ -32,7 +32,7 @@ __plugin_settings__ = {
# 导出方法供其他插件使用
export = export()
export.registered_use = registered_use
export.register_use = register_use
use_props = on_command("使用道具", priority=5, block=True, permission=GROUP)
@@ -42,35 +42,42 @@ 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:
num = 1
msg_sp = msg.split()
if len(msg_sp) > 1 and is_number(msg_sp[-1]) and int(msg_sp[-1]) > 0:
num = int(msg.split()[-1])
msg = " ".join(msg.split()[:-1])
property_ = await BagUser.get_property(event.user_id, event.group_id)
if property_:
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]
if 0 < int(msg) <= len(property_):
name = list(property_.keys())[int(msg) - 1]
else:
await use_props.finish("仔细看看自己的道具仓库有没有这个道具?", at_sender=True)
else:
if msg not in pname_list:
if msg not in property_.keys():
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)
_user_prop_count = property_[name]
if num > _user_prop_count:
await use_props.finish(f"道具数量不足,无法使用{num}次!")
if num > (n := func_manager.get_max_num_limit(name)):
await use_props.finish(f"该道具单次只能使用 {n} 个!")
if await BagUser.delete_property(
event.user_id, event.group_id, name, num
):
if func_manager.check_send_success_message(name):
await use_props.send(f"使用道具 {name} {num} 次成功!", at_sender=True)
if msg := await effect(bot, event, name, num):
await use_props.send(msg, at_sender=True)
logger.info(
f"USER {event.user_id} GROUP {event.group_id} 使用道具 {name} 成功"
f"USER {event.user_id} GROUP {event.group_id} 使用道具 {name} {num} 次成功"
)
else:
await use_props.send(f"使用道具 {name} 失败!", at_sender=True)
await use_props.send(f"使用道具 {name} {num} 次失败!", at_sender=True)
logger.info(
f"USER {event.user_id} GROUP {event.group_id} 使用道具 {name} 失败"
f"USER {event.user_id} GROUP {event.group_id} 使用道具 {name} {num} 次失败"
)
else:
await use_props.send("您的背包里没有任何的道具噢~", at_sender=True)
await use_props.send("您的背包里没有任何的道具噢", at_sender=True)
+99 -23
View File
@@ -1,45 +1,121 @@
import asyncio
from nonebot.adapters.cqhttp import GroupMessageEvent, MessageSegment
from services.log import logger
_use_func_data = {}
from nonebot.adapters.cqhttp import Bot
from typing import Optional, Union
import asyncio
async def effect(user_id: int, group_id: int, goods_name: str) -> bool:
class GoodsUseFuncManager:
def __init__(self):
self._data = {}
def register_use(self, goods_name: str, **kwargs):
"""
注册商品使用方法
:param goods_name: 商品名称
:param kwargs: kwargs
"""
self._data[goods_name] = kwargs
def exists(self, goods_name: str) -> bool:
"""
判断商品使用方法是否被注册
:param goods_name: 商品名称
"""
return bool(self._data.get(goods_name))
def get_max_num_limit(self, goods_name: str) -> int:
"""
获取单次商品使用数量
:param goods_name: 商品名称
"""
if self.exists(goods_name):
return self._data[goods_name]["kwargs"]["_max_num_limit"]
return 1
async def use(self, **kwargs) -> Optional[Union[str, MessageSegment]]:
"""
使用道具
:param kwargs: kwargs
"""
goods_name = kwargs.get("goods_name")
if self.exists(goods_name):
if asyncio.iscoroutinefunction(self._data[goods_name]["func"]):
return await self._data[goods_name]["func"](
**kwargs,
)
else:
return self._data[goods_name]["func"](
**kwargs,
)
def check_send_success_message(self, goods_name: str) -> bool:
"""
检查是否发送使用成功信息
:param goods_name: 商品名称
"""
if self.exists(goods_name):
return bool(self._data[goods_name]["kwargs"]["send_success_msg"])
return False
def get_kwargs(self, goods_name: str) -> dict:
"""
获取商品使用方法的kwargs
:param goods_name: 商品名称
"""
if self.exists(goods_name):
return self._data[goods_name]["kwargs"]
return {}
func_manager = GoodsUseFuncManager()
async def effect(
bot: Bot, event: GroupMessageEvent, goods_name: str, num: int
) -> Optional[Union[str, MessageSegment]]:
"""
商品生效
:param user_id: 用户id
:param group_id: 群号
:param bot: Bot
:param event: GroupMessageEvent
:param goods_name: 商品名称
:param num: 使用数量
: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"](
if func_manager.exists(goods_name):
_kwargs = func_manager.get_kwargs(goods_name)
return await func_manager.use(
**{
**_kwargs,
)
else:
_use_func_data[goods_name]["func"](
**_kwargs,
)
return True
"_bot": bot,
"event": event,
"group_id": event.group_id,
"user_id": event.user_id,
"num": num,
"goods_name": goods_name,
}
)
except Exception as e:
logger.error(f"use 商品生效函数effect 发生错误 {type(e)}:{e}")
return False
return None
def registered_use(goods_name: str, func, **kwargs):
def register_use(goods_name: str, func, **kwargs):
"""
注册商品使用方法
:param goods_name: 商品名称
:param func: 使用函数
:param kwargs: kwargs
"""
if goods_name in _use_func_data.keys():
if func_manager.exists(goods_name):
raise ValueError("该商品使用函数已被注册!")
_use_func_data[goods_name] = {"func": func, "kwargs": kwargs}
# 发送使用成功信息
if kwargs.get("send_success_msg") is None:
kwargs["send_success_msg"] = True
kwargs["_max_num_limit"] = (
kwargs.get("_max_num_limit") if kwargs.get("_max_num_limit") else 1
)
func_manager.register_use(goods_name, **{"func": func, "kwargs": kwargs})
logger.info(f"register_use 成功注册商品:{goods_name} 的使用函数")
+30
View File
@@ -8,7 +8,10 @@ from utils.manager import (
group_manager,
)
from nonebot.typing import T_State
from configs.config import Config
from services.log import logger
from nonebot.adapters.cqhttp import Bot, MessageEvent
from utils.utils import scheduler
__zx_plugin_name__ = "重载插件配置 [Superuser]"
@@ -28,6 +31,18 @@ __plugin_cmd__ = [
]
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_configs__ = {
"AUTO_RELOAD": {
"value": False,
"help": "自动重载配置文件",
"default_value": False
},
"AUTO_RELOAD_TIME": {
"value": 180,
"help": "控制自动重载配置文件时长",
"default_value": 180
}
}
reload_plugins_manager = on_command(
@@ -41,4 +56,19 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
plugins2cd_manager.reload()
plugins2block_manager.reload()
group_manager.reload()
Config.reload()
await reload_plugins_manager.send("重载完成...")
@scheduler.scheduled_job(
'interval',
seconds=Config.get_config("reload_setting", "AUTO_RELOAD_TIME", 180),
)
async def _():
if Config.get_config("reload_setting", "AUTO_RELOAD"):
plugins2settings_manager.reload()
plugins2cd_manager.reload()
plugins2block_manager.reload()
group_manager.reload()
Config.reload()
logger.debug("已自动重载所有配置文件...")
Binary file not shown.

After

Width:  |  Height:  |  Size: 772 KiB

+59 -74
View File
@@ -1,4 +1,5 @@
from services.db_context import db
from typing import Dict
from typing import Optional, List
from services.log import logger
@@ -7,33 +8,32 @@ class BagUser(db.Model):
__tablename__ = "bag_users"
id = db.Column(db.Integer(), primary_key=True)
user_qq = db.Column(db.BigInteger(), nullable=False)
belonging_group = db.Column(db.BigInteger(), nullable=False)
group_id = db.Column(db.BigInteger(), nullable=False)
gold = db.Column(db.Integer(), default=100)
props = db.Column(db.TEXT(), nullable=False, default="")
props = db.Column(db.TEXT(), nullable=False, default="") # 旧道具字段(废弃)
spend_total_gold = db.Column(db.Integer(), default=0)
get_total_gold = db.Column(db.Integer(), default=0)
get_today_gold = db.Column(db.Integer(), default=0)
spend_today_gold = db.Column(db.Integer(), default=0)
property = db.Column(db.JSON(), nullable=False, default={}) # 新道具字段
_idx1 = db.Index("bag_group_users_idx1", "user_qq", "belonging_group", unique=True)
_idx1 = db.Index("bag_group_users_idx1", "user_qq", "group_id", unique=True)
@classmethod
async def get_my_total_gold(cls, user_qq: int, belonging_group: int) -> str:
async def get_user_total_gold(cls, user_qq: int, group_id: int) -> str:
"""
说明:
获取金币概况
参数:
:param user_qq: qq号
:param belonging_group: 所在群号
:param group_id: 所在群号
"""
query = cls.query.where(
(cls.user_qq == user_qq) & (cls.belonging_group == belonging_group)
)
query = cls.query.where((cls.user_qq == user_qq) & (cls.group_id == group_id))
user = await query.gino.first()
if not user:
user = await cls.create(
user_qq=user_qq,
belonging_group=belonging_group,
group_id=group_id,
)
return (
f"当前金币:{user.gold}\n今日获取金币:{user.get_today_gold}\n今日花费金币:{user.spend_today_gold}"
@@ -42,62 +42,56 @@ class BagUser(db.Model):
)
@classmethod
async def get_gold(cls, user_qq: int, belonging_group: int) -> int:
async def get_gold(cls, user_qq: int, group_id: int) -> int:
"""
说明:
获取当前金币
参数:
:param user_qq: qq号
:param belonging_group: 所在群号
:param group_id: 所在群号
"""
query = cls.query.where(
(cls.user_qq == user_qq) & (cls.belonging_group == belonging_group)
)
query = cls.query.where((cls.user_qq == user_qq) & (cls.group_id == group_id))
user = await query.gino.first()
if user:
return user.gold
else:
await cls.create(
user_qq=user_qq,
belonging_group=belonging_group,
group_id=group_id,
)
return 100
@classmethod
async def get_props(cls, user_qq: int, belonging_group: int) -> str:
async def get_property(cls, user_qq: int, group_id: int) -> Dict[str, int]:
"""
说明:
获取当前道具
参数:
:param user_qq: qq号
:param belonging_group: 所在群号
:param group_id: 所在群号
"""
query = cls.query.where(
(cls.user_qq == user_qq) & (cls.belonging_group == belonging_group)
)
query = cls.query.where((cls.user_qq == user_qq) & (cls.group_id == group_id))
user = await query.gino.first()
if user:
return user.props
return user.property
else:
await cls.create(
user_qq=user_qq,
belonging_group=belonging_group,
group_id=group_id,
)
return ""
return {}
@classmethod
async def add_gold(cls, user_qq: int, belonging_group: int, num: int):
async def add_gold(cls, user_qq: int, group_id: int, num: int):
"""
说明:
增加金币
参数:
:param user_qq: qq号
:param belonging_group: 所在群号
:param group_id: 所在群号
:param num: 金币数量
"""
query = cls.query.where(
(cls.user_qq == user_qq) & (cls.belonging_group == belonging_group)
)
query = cls.query.where((cls.user_qq == user_qq) & (cls.group_id == group_id))
query = query.with_for_update()
user = await query.gino.first()
if user:
@@ -109,25 +103,23 @@ class BagUser(db.Model):
else:
await cls.create(
user_qq=user_qq,
belonging_group=belonging_group,
group_id=group_id,
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):
async def spend_gold(cls, user_qq: int, group_id: int, num: int):
"""
说明:
花费金币
参数:
:param user_qq: qq号
:param belonging_group: 所在群号
:param group_id: 所在群号
:param num: 金币数量
"""
query = cls.query.where(
(cls.user_qq == user_qq) & (cls.belonging_group == belonging_group)
)
query = cls.query.where((cls.user_qq == user_qq) & (cls.group_id == group_id))
query = query.with_for_update()
user = await query.gino.first()
if user:
@@ -139,91 +131,84 @@ class BagUser(db.Model):
else:
await cls.create(
user_qq=user_qq,
belonging_group=belonging_group,
group_id=group_id,
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):
async def add_property(cls, user_qq: int, group_id: int, name: str):
"""
说明:
增加道具
参数:
:param user_qq: qq号
:param belonging_group: 所在群号
:param group_id: 所在群号
:param name: 道具名称
"""
query = cls.query.where(
(cls.user_qq == user_qq) & (cls.belonging_group == belonging_group)
)
query = cls.query.where((cls.user_qq == user_qq) & (cls.group_id == group_id))
query = query.with_for_update()
user = await query.gino.first()
if user:
await user.update(props=user.props + f"{name},").apply()
p = user.property
if p.get(name) is None:
p[name] = 1
else:
p[name] += 1
await user.update(property=p).apply()
else:
await cls.create(
user_qq=user_qq, belonging_group=belonging_group, props=f"{name},"
)
await cls.create(user_qq=user_qq, group_id=group_id, property={name: 1})
@classmethod
async def del_props(cls, user_qq: int, belonging_group: int, name: str) -> bool:
async def delete_property(
cls, user_qq: int, group_id: int, name: str, num: int = 1
) -> bool:
"""
说明:
使用道具
使用/删除 道具
参数:
:param user_qq: qq号
:param belonging_group: 所在群号
:param group_id: 所在群号
:param name: 道具名称
:param num: 使用个数
"""
query = cls.query.where(
(cls.user_qq == user_qq) & (cls.belonging_group == belonging_group)
)
query = cls.query.where((cls.user_qq == user_qq) & (cls.group_id == group_id))
query = query.with_for_update()
user = await query.gino.first()
if user:
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()
property_ = user.property
if name in property_:
if property_.get(name) == num:
del property_[name]
else:
property_[name] -= num
await user.update(property=property_).apply()
return True
else:
return False
else:
return False
return False
@classmethod
async def buy_props(
cls, user_qq: int, belonging_group: int, goods: "GoodsInfo", goods_num: int
async def buy_property(
cls, user_qq: int, group_id: int, goods: "GoodsInfo", goods_num: int
) -> bool:
"""
说明:
购买道具
参数:
:param user_qq: 用户qq
:param belonging_group: 所在群聊
:param group_id: 所在群聊
: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)
await BagUser.spend_gold(user_qq, group_id, spend_gold)
for _ in range(goods_num):
await BagUser.add_props(user_qq, belonging_group, goods.goods_name)
await BagUser.add_property(user_qq, group_id, goods.goods_name)
return True
except Exception as e:
logger.error(f"buy_props 发生错误 {type(e)}:{e}")
logger.error(f"buy_property 发生错误 {type(e)}:{e}")
return False
@classmethod
@@ -237,5 +222,5 @@ class BagUser(db.Model):
if not group_id:
query = await cls.query.gino.all()
else:
query = await cls.query.where((cls.belonging_group == group_id)).gino.all()
query = await cls.query.where((cls.group_id == group_id)).gino.all()
return query
+32 -30
View File
@@ -10,18 +10,18 @@ class GroupInfoUser(db.Model):
id = db.Column(db.Integer(), primary_key=True)
user_qq = db.Column(db.BigInteger(), nullable=False)
user_name = db.Column(db.Unicode(), nullable=False)
belonging_group = db.Column(db.BigInteger(), nullable=False)
group_id = db.Column(db.BigInteger(), nullable=False)
user_join_time = db.Column(db.DateTime(), nullable=False)
nickname = db.Column(db.Unicode())
uid = db.Column(db.BigInteger())
_idx1 = db.Index("info_group_users_idx1", "user_qq", "belonging_group", unique=True)
_idx1 = db.Index("info_group_users_idx1", "user_qq", "group_id", unique=True)
@classmethod
async def add_member_info(
cls,
user_qq: int,
belonging_group: int,
group_id: int,
user_name: str,
user_join_time: datetime,
uid: Optional[int] = None,
@@ -31,20 +31,20 @@ class GroupInfoUser(db.Model):
添加群内用户信息
参数:
:param user_qq: qq号
:param belonging_group: 群号
:param group_id: 群号
:param user_name: 用户名称
:param user_join_time: 入群时间
:param uid: 用户唯一 id(自动生成)
"""
query = cls.query.where(
(cls.user_qq == user_qq) & (cls.belonging_group == belonging_group)
(cls.user_qq == user_qq) & (cls.group_id == group_id)
)
try:
if not await query.gino.first():
await cls.create(
user_qq=user_qq,
user_name=user_name,
belonging_group=belonging_group,
group_id=group_id,
user_join_time=user_join_time,
uid=uid
)
@@ -54,31 +54,31 @@ class GroupInfoUser(db.Model):
@classmethod
async def get_member_info(
cls, user_qq: int, belonging_group: int
cls, user_qq: int, group_id: int
) -> "GroupInfoUser":
"""
说明:
查询群员信息
参数:
:param user_qq: qq号
:param belonging_group: 群号
:param group_id: 群号
"""
query = cls.query.where(
(cls.user_qq == user_qq) & (cls.belonging_group == belonging_group)
(cls.user_qq == user_qq) & (cls.group_id == group_id)
)
return await query.gino.first()
@classmethod
async def delete_member_info(cls, user_qq: int, belonging_group: int) -> bool:
async def delete_member_info(cls, user_qq: int, group_id: int) -> bool:
"""
说明:
删除群员信息
参数:
:param user_qq: qq号
:param belonging_group: 群号
:param group_id: 群号
"""
query = cls.query.where(
(cls.user_qq == user_qq) & (cls.belonging_group == belonging_group)
(cls.user_qq == user_qq) & (cls.group_id == group_id)
)
query = query.with_for_update()
user = await query.gino.first()
@@ -87,40 +87,40 @@ class GroupInfoUser(db.Model):
return True
else:
await cls.delete.where(
(cls.user_qq == user_qq) & (cls.belonging_group == belonging_group)
(cls.user_qq == user_qq) & (cls.group_id == group_id)
).gino.status()
return True
except Exception:
return False
@classmethod
async def get_group_member_id_list(cls, belonging_group: int) -> List[int]:
async def get_group_member_id_list(cls, group_id: int) -> List[int]:
"""
说明:
获取该群所有用户qq
参数:
:param belonging_group: 群号
:param group_id: 群号
"""
member_list = []
query = cls.query.where((cls.belonging_group == belonging_group))
query = cls.query.where((cls.group_id == group_id))
for user in await query.gino.all():
member_list.append(user.user_qq)
return member_list
@classmethod
async def set_group_member_nickname(
cls, user_qq: int, belonging_group: int, nickname: str
cls, user_qq: int, group_id: int, nickname: str
) -> bool:
"""
说明:
设置群员在该群内的昵称
参数:
:param user_qq: qq号
:param belonging_group: 群号
:param group_id: 群号
:param nickname: 昵称
"""
query = cls.query.where(
(cls.user_qq == user_qq) & (cls.belonging_group == belonging_group)
(cls.user_qq == user_qq) & (cls.group_id == group_id)
)
user = await query.with_for_update().gino.first()
if user:
@@ -131,25 +131,27 @@ class GroupInfoUser(db.Model):
@classmethod
async def get_user_all_group(cls, user_qq: int) -> List[int]:
"""
获取该用户所在的所有群聊
:param user_qq: 用户qq
说明:
获取该用户所在的所有群聊
参数:
:param user_qq: 用户qq
"""
query = await cls.query.where(cls.user_qq == user_qq).gino.all()
if query:
query = [x.belonging_group for x in query]
query = [x.group_id for x in query]
return query
@classmethod
async def get_group_member_nickname(cls, user_qq: int, belonging_group: int) -> str:
async def get_group_member_nickname(cls, user_qq: int, group_id: int) -> str:
"""
说明:
获取用户在该群的昵称
参数:
:param user_qq: qq号
:param belonging_group: 群号
:param group_id: 群号
"""
query = cls.query.where(
(cls.user_qq == user_qq) & (cls.belonging_group == belonging_group)
(cls.user_qq == user_qq) & (cls.group_id == group_id)
)
user = await query.gino.first()
if user:
@@ -163,12 +165,12 @@ class GroupInfoUser(db.Model):
return ""
@classmethod
async def get_group_member_uid(cls, user_qq: int, belonging_group: int) -> Optional[str]:
async def get_group_member_uid(cls, user_qq: int, group_id: int) -> Optional[str]:
query = cls.query.where(
(cls.user_qq == user_qq) & (cls.belonging_group == belonging_group)
(cls.user_qq == user_qq) & (cls.group_id == group_id)
)
user = await query.gino.first()
_max_uid = cls.query.where((cls.user_qq == 114514) & (cls.belonging_group == 114514)).with_for_update()
_max_uid = cls.query.where((cls.user_qq == 114514) & (cls.group_id == 114514)).with_for_update()
_max_uid_user = await _max_uid.gino.first()
_max_uid = _max_uid_user.uid
if not user or not user.uid:
@@ -178,9 +180,9 @@ class GroupInfoUser(db.Model):
return x.uid
else:
if not user:
await GroupInfoUser.add_member_info(user_qq, belonging_group, '', datetime.min)
await GroupInfoUser.add_member_info(user_qq, group_id, '', datetime.min)
user = await cls.query.where(
(cls.user_qq == user_qq) & (cls.belonging_group == belonging_group)
(cls.user_qq == user_qq) & (cls.group_id == group_id)
).gino.first()
await user.update(
uid=_max_uid + 1,
+11 -12
View File
@@ -8,8 +8,7 @@ class SignGroupUser(db.Model):
id = db.Column(db.Integer(), primary_key=True)
user_qq = db.Column(db.BigInteger(), nullable=False)
belonging_group = db.Column(db.BigInteger(), nullable=False)
group_id = db.Column(db.BigInteger(), nullable=False)
checkin_count = db.Column(db.Integer(), nullable=False)
checkin_time_last = db.Column(db.DateTime(timezone=True), nullable=False)
impression = db.Column(db.Numeric(scale=3, asdecimal=False), nullable=False)
@@ -20,29 +19,29 @@ class SignGroupUser(db.Model):
db.Numeric(scale=3, asdecimal=False), nullable=False, default=0
)
_idx1 = db.Index("sign_group_users_idx1", "user_qq", "belonging_group", unique=True)
_idx1 = db.Index("sign_group_users_idx1", "user_qq", "group_id", unique=True)
@classmethod
async def ensure(
cls, user_qq: int, belonging_group: int, for_update: bool = False
cls, user_qq: int, group_id: int, for_update: bool = False
) -> "SignGroupUser":
"""
说明:
获取签到用户
参数:
:param user_qq: 用户qq
:param belonging_group: 所在群聊
:param group_id: 所在群聊
:param for_update: 是否存在修改数据
"""
query = cls.query.where(
(cls.user_qq == user_qq) & (cls.belonging_group == belonging_group)
(cls.user_qq == user_qq) & (cls.group_id == group_id)
)
if for_update:
query = query.with_for_update()
user = await query.gino.first()
return user or await cls.create(
user_qq=user_qq,
belonging_group=belonging_group,
group_id=group_id,
checkin_count=0,
checkin_time_last=datetime.min, # 从未签到过
impression=0,
@@ -79,22 +78,22 @@ class SignGroupUser(db.Model):
).apply()
@classmethod
async def get_all_impression(cls, belonging_group: int) -> "list, list, list":
async def get_all_impression(cls, group_id: int) -> "list, list, list":
"""
说明:
获取该群所有用户 id 及对应 好感度
参数:
:param belonging_group: 群号
:param group_id: 群号
"""
impression_list = []
user_qq_list = []
user_group = []
if belonging_group:
query = cls.query.where(cls.belonging_group == belonging_group)
if group_id:
query = cls.query.where(cls.group_id == group_id)
else:
query = cls.query
for user in await query.gino.all():
impression_list.append(user.impression)
user_qq_list.append(user.user_qq)
user_group.append(user.belonging_group)
user_group.append(user.group_id)
return user_qq_list, impression_list, user_group
+14 -17
View File
@@ -11,7 +11,7 @@ from nonebot.typing import T_State
from models.friend_user import FriendUser
from models.group_member_info import GroupInfoUser
from services.log import logger
from utils.utils import get_message_text, get_message_imgs
from utils.utils import get_message_text, get_message_img
from .data_source import get_chat_result, hello, no_result
from configs.config import NICKNAME, Config
@@ -21,27 +21,22 @@ usage:
与{NICKNAME}普普通通的对话吧!
"""
__plugin_version__ = 0.1
__plugin_author__ = 'HibiKier'
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"level": 5,
"cmd": ["Ai", "ai", "AI", "aI"],
}
__plugin_configs__ = {
"TL_KEY": {
"value": [],
"help": "图灵Key"
"TL_KEY": {"value": [], "help": "图灵Key"},
"ALAPI_AI_CHECK": {"value": False, "help": "是否检测青云客骂娘回复", "default_value": False},
"TEXT_FILTER": {
"value": ["鸡", "口交"],
"help": "文本过滤器,将敏感词更改为*",
"default_value": [],
},
"ALAPI_AI_CHECK": {
"value": False,
"help": "是否检测青云客骂娘回复",
"default_value": False
}
}
Config.add_plugin_config(
"alapi",
"ALAPI_TOKEN",
None,
help_="在https://admin.alapi.cn/user/login登录后获取token"
"alapi", "ALAPI_TOKEN", None, help_="在https://admin.alapi.cn/user/login登录后获取token"
)
ai = on_message(rule=to_me(), priority=8)
@@ -50,11 +45,11 @@ ai = on_message(rule=to_me(), priority=8)
@ai.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
msg = get_message_text(event.json())
imgs = get_message_imgs(event.json())
img = get_message_img(event.json())
if "CQ:xml" in str(event.get_message()):
return
# 打招呼
if (not msg and not imgs) or msg in [
if (not msg and not img) or msg in [
"你好啊",
"你好",
"在吗",
@@ -65,7 +60,7 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
"在",
]:
await ai.finish(hello())
img = imgs[0] if imgs else ""
img = img[0] if img else ""
if isinstance(event, GroupMessageEvent):
nickname = await GroupInfoUser.get_group_member_nickname(
event.user_id, event.group_id
@@ -83,6 +78,8 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
f"问题:{msg} ---- 回答:{result}"
)
if result:
for t in Config.get_config("ai", "TEXT_FILTER"):
result = result.replace(t, "*")
await ai.finish(Message(result))
else:
await ai.finish(no_result())
+9 -7
View File
@@ -9,7 +9,7 @@ from .data_source import (
get_media_id,
get_sub_status,
SubManager,
BilibiliSub
BilibiliSub,
)
from models.level_user import LevelUser
from configs.config import Config
@@ -163,11 +163,10 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
@show_sub_info.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
id_ = (
f"{event.user_id}:{event.group_id}"
if isinstance(event, GroupMessageEvent)
else f"{event.user_id}"
)
if isinstance(event, GroupMessageEvent):
id_ = f"{event.group_id}"
else:
id_ = f"{event.user_id}"
data = await BilibiliSub.get_sub_data(id_)
live_rst = ""
up_rst = ""
@@ -181,6 +180,7 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
up_rst += f"\tUP:{x.uname}\n" f"\tuid:{x.uid}\n" f"------------------\n"
if x.sub_type == "season":
season_rst += (
f"\t番剧id:{x.sub_id}\n"
f"\t番名:{x.season_name}\n"
f"\t当前集数:{x.season_current_episode}\n"
f"------------------\n"
@@ -189,7 +189,9 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
up_rst = "当前订阅的UP:\n" + up_rst if up_rst else up_rst
season_rst = "当前订阅的番剧:\n" + season_rst if season_rst else season_rst
if not live_rst and not up_rst and not season_rst:
live_rst = "您目前没有任何订阅..."
live_rst = (
"该群目前没有任何订阅..." if isinstance(event, GroupMessageEvent) else "您目前没有任何订阅..."
)
await show_sub_info.send(live_rst + up_rst + season_rst)
+10 -4
View File
@@ -165,7 +165,9 @@ async def get_media_id(keyword: str) -> dict:
for _ in range(3):
try:
_season_data = {}
response = await AsyncHttpx.get(bilibili_search_url, params=params, timeout=5)
response = await AsyncHttpx.get(
bilibili_search_url, params=params, timeout=5
)
if response.status_code == 200:
data = response.json()
if data.get("data"):
@@ -191,7 +193,6 @@ async def get_sub_status(id_: int, sub_type: str) -> Optional[str]:
获取订阅状态
:param id_: 订阅 id
:param sub_type: 订阅类型
:return:
"""
try:
if sub_type == "live":
@@ -201,7 +202,7 @@ async def get_sub_status(id_: int, sub_type: str) -> Optional[str]:
elif sub_type == "season":
return await _get_season_status(id_)
except ResponseCodeException:
return "获取信息失败...请检查订阅Id是否存在或稍后再试..."
return f"Id:{id_} 获取信息失败...请检查订阅Id是否存在或稍后再试..."
# except Exception as e:
# logger.error(f"获取订阅状态发生预料之外的错误 id_:{id_} {type(e)}:{e}")
# return "发生了预料之外的错误..请稍后再试或联系管理员....."
@@ -254,7 +255,12 @@ async def _get_up_status(id_: int) -> Optional[str]:
if dynamic_img:
await BilibiliSub.update_sub_info(id_, dynamic_upload_time=dynamic_upload_time)
rst += f"{uname} 发布了动态!\n" f"{dynamic_img}\n"
if _user.latest_video_created < latest_video_created and video:
if (
latest_video_created
and _user.latest_video_created
and video
and _user.latest_video_created < latest_video_created
):
rst = rst + "-------------\n" if rst else rst
await BilibiliSub.update_sub_info(
id_, latest_video_created=latest_video_created
+7 -7
View File
@@ -2,7 +2,7 @@ from nonebot import on_command
from nonebot.typing import T_State
from nonebot.adapters.cqhttp import Bot, MessageEvent, Message, GroupMessageEvent
from nonebot.permission import SUPERUSER
from utils.utils import get_message_text, is_number, get_message_imgs
from utils.utils import get_message_text, is_number, get_message_img
from utils.message_builder import image
from utils.message_builder import text as _text
from services.log import logger
@@ -63,7 +63,7 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
coffee = int(list(bot.config.superusers)[0])
text = get_message_text(event.json())
img_msg = _text("")
for img in get_message_imgs(event.json()):
for img in get_message_img(event.json()):
img_msg += image(img)
if not text and not img_msg:
await dialogue.send("请发送[滴滴滴]+您要说的内容~", at_sender=True)
@@ -127,26 +127,26 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
id_ = msg[0]
user_id = dialogue_data[id_]["user_id"]
group_id = dialogue_data[id_]["group_id"]
text = msg[1]
text = " ".join(msg[1:])
dialogue_data.pop(id_)
else:
user_id = 0
if is_number(msg[1]):
group_id = int(msg[1])
text = msg[2]
text = " ".join(msg[2:])
else:
await reply.finish("群号错误...", at_sender=True)
else:
user_id = int(msg[0])
if is_number(msg[1]) and len(msg[1]) > 5:
group_id = int(msg[1])
text = msg[2]
text = " ".join(msg[2:])
else:
group_id = 0
text = msg[1]
text = " ".join(msg[1:])
else:
await reply.finish("第一参数,请输入数字.....", at_sender=True)
for img in get_message_imgs(event.json()):
for img in get_message_img(event.json()):
text += image(img)
if group_id:
if user_id:
+2 -2
View File
@@ -35,7 +35,7 @@ def is_expired(data: dict):
now = datetime.now().date()
except ValueError:
return False
return start_date <= now <= end_date
return not start_date <= now <= end_date
# 检查写入
@@ -175,7 +175,7 @@ class GenshinAnnouncement:
data[itype]['time'] = data[itype]['time'][:-1]
if '版本更新后' in data[itype]['time']:
sp = data[itype]['time'].split('~')
end_time = datetime.strptime(sp[1].strip(), "%Y/%m/%d %H:%M:%S")
end_time = datetime.strptime(sp[1].strip(), "%Y/%m/%d %H:%M")
start_time = end_time - timedelta(days=20)
data[itype]['time'] = start_time.strftime('%Y/%m/%d') + ' ~ ' + end_time.strftime('%Y/%m/%d')
tmp = ''
+109 -55
View File
@@ -2,9 +2,24 @@ import os
from nonebot.adapters.cqhttp import MessageSegment, Message
import random
from .update_game_info import update_info
from .util import generate_img, init_star_rst, BaseData, set_list, get_star, init_up_char
from .config import GENSHIN_FIVE_P, GENSHIN_FOUR_P, GENSHIN_G_FIVE_P, GENSHIN_G_FOUR_P, GENSHIN_THREE_P, I72_ADD, \
DRAW_PATH, GENSHIN_FLAG
from .util import (
generate_img,
init_star_rst,
BaseData,
set_list,
get_star,
init_up_char,
)
from .config import (
GENSHIN_FIVE_P,
GENSHIN_FOUR_P,
GENSHIN_G_FIVE_P,
GENSHIN_G_FOUR_P,
GENSHIN_THREE_P,
I72_ADD,
DRAW_PATH,
GENSHIN_FLAG,
)
from dataclasses import dataclass
from .init_card_pool import init_game_pool
from .announcement import GenshinAnnouncement
@@ -27,9 +42,9 @@ ALL_ARMS = []
UP_CHAR = []
UP_ARMS = []
_CURRENT_CHAR_POOL_TITLE = ''
_CURRENT_ARMS_POOL_TITLE = ''
POOL_IMG = ''
_CURRENT_CHAR_POOL_TITLE = ""
_CURRENT_ARMS_POOL_TITLE = ""
POOL_IMG = ""
@dataclass
@@ -39,19 +54,25 @@ class GenshinChar(BaseData):
async def genshin_draw(user_id: int, count: int, pool_name: str):
# 0 1 2
cnlist = ['★★★★★', '★★★★', '★★★']
char_list, five_list, five_index_list, char_dict, star_list = _format_card_information(count, user_id, pool_name)
temp = ''
title = ''
cnlist = ["★★★★★", "★★★★", "★★★"]
(
char_list,
five_list,
five_index_list,
char_dict,
star_list,
) = _format_card_information(count, user_id, pool_name)
temp = ""
title = ""
up_type = []
up_list = []
if pool_name == 'char' and _CURRENT_CHAR_POOL_TITLE:
if pool_name == "char" and _CURRENT_CHAR_POOL_TITLE:
up_type = UP_CHAR
title = _CURRENT_CHAR_POOL_TITLE
elif pool_name == 'arms' and _CURRENT_ARMS_POOL_TITLE:
elif pool_name == "arms" and _CURRENT_ARMS_POOL_TITLE:
up_type = UP_ARMS
title = _CURRENT_ARMS_POOL_TITLE
tmp = ''
tmp = ""
if up_type:
for x in up_type:
for operator in x.operators:
@@ -61,80 +82,105 @@ async def genshin_draw(user_id: int, count: int, pool_name: str):
elif x.star == 4:
tmp += f'四星UP:{" ".join(x.operators)}'
rst = init_star_rst(star_list, cnlist, five_list, five_index_list, up_list)
pool_info = f'当前up池:{title}\n{tmp}' if title else ''
pool_info = f"当前up池:{title}\n{tmp}" if title else ""
if count > 90:
char_list = set_list(char_list)
return pool_info + '\n' + MessageSegment.image(
"base64://" + await generate_img(char_list, 'genshin', star_list)) + '\n' + rst[:-1] + \
temp[:-1] + f'\n距离保底发还剩 {90 - genshin_count[user_id] if genshin_count.get(user_id) else "^"} 抽' \
+ "\n【五星:0.6%,四星:5.1%\n第72抽开始五星概率每抽加0.585%】"
return (
pool_info
+ "\n"
+ MessageSegment.image(
"base64://" + await generate_img(char_list, "genshin", star_list)
)
+ "\n"
+ rst[:-1]
+ temp[:-1]
+ f'\n距离保底发还剩 {90 - genshin_count[user_id] if genshin_count.get(user_id) else "^"} 抽'
+ "\n【五星:0.6%,四星:5.1%\n第72抽开始五星概率每抽加0.585%】"
)
async def update_genshin_info():
global ALL_CHAR, ALL_ARMS
url = 'https://wiki.biligame.com/ys/角色筛选'
data, code = await update_info(url, 'genshin')
url = "https://wiki.biligame.com/ys/角色筛选"
data, code = await update_info(url, "genshin")
if code == 200:
ALL_CHAR = init_game_pool('genshin', data, GenshinChar)
url = 'https://wiki.biligame.com/ys/武器图鉴'
data, code = await update_info(url, 'genshin_arms', ['头像', '名称', '类型', '稀有度.alt',
'获取途径', '初始基础属性1', '初始基础属性2',
'攻击力(MAX)', '副属性(MAX)', '技能'])
ALL_CHAR = init_game_pool("genshin", data, GenshinChar)
url = "https://wiki.biligame.com/ys/武器图鉴"
data, code = await update_info(
url,
"genshin_arms",
)
if code == 200:
ALL_ARMS = init_game_pool('genshin_arms', data, GenshinChar)
ALL_ARMS = init_game_pool("genshin_arms", data, GenshinChar)
await _genshin_init_up_char()
async def init_genshin_data():
global ALL_CHAR, ALL_ARMS
if GENSHIN_FLAG:
if not os.path.exists(DRAW_PATH + 'genshin.json') or not os.path.exists(DRAW_PATH + 'genshin_arms.json'):
if not os.path.exists(DRAW_PATH + "genshin.json") or not os.path.exists(
DRAW_PATH + "genshin_arms.json"
):
await update_genshin_info()
else:
with open(DRAW_PATH + 'genshin.json', 'r', encoding='utf8') as f:
with open(DRAW_PATH + "genshin.json", "r", encoding="utf8") as f:
genshin_dict = json.load(f)
with open(DRAW_PATH + 'genshin_arms.json', 'r', encoding='utf8') as f:
with open(DRAW_PATH + "genshin_arms.json", "r", encoding="utf8") as f:
genshin_ARMS_dict = json.load(f)
ALL_CHAR = init_game_pool('genshin', genshin_dict, GenshinChar)
ALL_ARMS = init_game_pool('genshin_arms', genshin_ARMS_dict, GenshinChar)
ALL_CHAR = init_game_pool("genshin", genshin_dict, GenshinChar)
ALL_ARMS = init_game_pool("genshin_arms", genshin_ARMS_dict, GenshinChar)
await _genshin_init_up_char()
# 抽取卡池
def _get_genshin_card(mode: int = 1, pool_name: str = '', add: float = 0.0):
def _get_genshin_card(mode: int = 1, pool_name: str = "", add: float = 0.0):
global ALL_ARMS, ALL_CHAR, UP_ARMS, UP_CHAR, _CURRENT_ARMS_POOL_TITLE, _CURRENT_CHAR_POOL_TITLE
if mode == 1:
star = get_star([5, 4, 3], [GENSHIN_FIVE_P + add, GENSHIN_FOUR_P, GENSHIN_THREE_P])
star = get_star(
[5, 4, 3], [GENSHIN_FIVE_P + add, GENSHIN_FOUR_P, GENSHIN_THREE_P]
)
elif mode == 2:
star = get_star([5, 4], [GENSHIN_G_FIVE_P + add, GENSHIN_G_FOUR_P])
else:
star = 5
if pool_name == 'char':
if pool_name == "char":
data_lst = UP_CHAR
flag = _CURRENT_CHAR_POOL_TITLE
itype_all_lst = ALL_CHAR + [x for x in ALL_ARMS if x.star == star and x.star < 5]
elif pool_name == 'arms':
itype_all_lst = ALL_CHAR + [
x for x in ALL_ARMS if x.star == star and x.star < 5
]
elif pool_name == "arms":
data_lst = UP_ARMS
flag = _CURRENT_ARMS_POOL_TITLE
itype_all_lst = ALL_ARMS + [x for x in ALL_CHAR if x.star == star and x.star < 5]
itype_all_lst = ALL_ARMS + [
x for x in ALL_CHAR if x.star == star and x.star < 5
]
else:
data_lst = ''
flag = ''
itype_all_lst = ''
data_lst = ""
flag = ""
itype_all_lst = ""
all_lst = ALL_ARMS + ALL_CHAR
# 是否UP
if flag and star > 3 and pool_name:
# 获取up角色列表
up_char_lst = [x.operators for x in data_lst if x.star == star][0]
# 成功获取up角色
if random.random() < 0.5:
up_char_name = random.choice(up_char_lst)
acquire_char = [x for x in all_lst if x.name == up_char_name][0]
try:
if flag and star > 3 and pool_name:
# 获取up角色列表
up_char_lst = [x.operators for x in data_lst if x.star == star][0]
# 成功获取up角色
if random.random() < 0.5:
up_char_name = random.choice(up_char_lst)
acquire_char = [x for x in all_lst if x.name == up_char_name][0]
else:
# 无up
all_char_lst = [
x
for x in itype_all_lst
if x.star == star and x.name not in up_char_lst and not x.limited
]
acquire_char = random.choice(all_char_lst)
else:
# 无up
all_char_lst = [x for x in itype_all_lst if x.star == star and x.name not in up_char_lst and not x.limited]
acquire_char = random.choice(all_char_lst)
else:
chars = [x for x in all_lst if x.star == star and not x.limited]
acquire_char = random.choice(chars)
except IndexError:
chars = [x for x in all_lst if x.star == star and not x.limited]
acquire_char = random.choice(chars)
return acquire_char, 5 - star
@@ -146,8 +192,8 @@ 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 # 保底
_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 <= _x:
f_count = genshin_count[user_id]
@@ -202,9 +248,17 @@ def reset_count(user_id: int):
# 获取up和概率
async def _genshin_init_up_char():
global _CURRENT_CHAR_POOL_TITLE, _CURRENT_ARMS_POOL_TITLE, UP_CHAR, UP_ARMS, POOL_IMG
_CURRENT_CHAR_POOL_TITLE, _CURRENT_ARMS_POOL_TITLE, POOL_IMG, UP_CHAR, UP_ARMS = await init_up_char(announcement)
(
_CURRENT_CHAR_POOL_TITLE,
_CURRENT_ARMS_POOL_TITLE,
POOL_IMG,
UP_CHAR,
UP_ARMS,
) = await init_up_char(announcement)
async def reload_genshin_pool():
await _genshin_init_up_char()
return Message(f'当前UP池子:{_CURRENT_CHAR_POOL_TITLE} & {_CURRENT_ARMS_POOL_TITLE} {POOL_IMG}')
return Message(
f"当前UP池子:{_CURRENT_CHAR_POOL_TITLE} & {_CURRENT_ARMS_POOL_TITLE} {POOL_IMG}"
)
+4 -1
View File
@@ -179,10 +179,13 @@ async def _last_check(data: dict, game_name: str):
# 对抓取每行数据是否需要额外处理?
def intermediate_check(member_dict: dict, key: str, game_name: str, td: bs4.element.Tag):
if game_name == "genshin_arms":
if key == "稀有度":
member_dict["稀有度"] = td.find("img")["alt"].split('.')[0]
if game_name == 'prts':
if key == '获取途径':
msg = re.search('<td.*?>([\\s\\S]*)</td>', str(td)).group(1).strip()
msg = msg[:-1] if msg[-1] == '\n' else msg
msg = msg[:-1] if msg and msg[-1] == '\n' else msg
if msg.find('<a') != -1:
for a in td.find_all('a'):
msg = msg.replace(str(a), a.text)
+2 -1
View File
@@ -131,8 +131,9 @@ async def retrieve_char_data(char: bs4.element.Tag, game_name: str, data: dict,
'名称': remove_prohibited_str(char.find('a')['title']),
'星级': 3 - index}
if game_name == 'azur':
char = char.find('div').find('div').find('div').find('div')
char = char.find('div').find('div').find('div')
avatar_img = char.find('a').find('img')
char = char.find('div')
try:
member_dict['名称'] = remove_prohibited_str(char.find('a')['title'])
except TypeError:
+2 -2
View File
@@ -1,7 +1,7 @@
from nonebot.typing import T_State
from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent
from nonebot import on_command
from utils.utils import get_message_imgs, get_message_text
from utils.utils import get_message_img, get_message_text
from utils.message_builder import share
from services.log import logger
@@ -32,7 +32,7 @@ fake_msg = on_command("假消息", priority=5, block=True)
@fake_msg.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
msg = get_message_text(event.json()).split(" ")
img = get_message_imgs(event.json())
img = get_message_img(event.json())
if len(msg) > 1:
if len(msg) == 2:
url = msg[0]
+9 -7
View File
@@ -4,7 +4,7 @@ from utils.image_utils import get_img_hash
import random
from utils.message_builder import image
from nonebot import on_message
from utils.utils import get_message_text, get_message_imgs
from utils.utils import get_message_text, get_message_img
from nonebot.typing import T_State
from nonebot.adapters.cqhttp import Bot, GroupMessageEvent
from configs.config import Config
@@ -90,12 +90,12 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
if get_message_text(event.json()):
if get_message_text(event.json()).find("@可爱的小真寻") != -1:
await fudu.finish("复制粘贴的虚空艾特?", at_sender=True)
imgs = get_message_imgs(event.json())
img = get_message_img(event.json())
msg = get_message_text(event.json())
if not imgs and not msg:
if not img and not msg:
return
if imgs:
img_hash = await get_fudu_img_hash(imgs[0], event.group_id)
if img:
img_hash = await get_fudu_img_hash(img[0], event.group_id)
else:
img_hash = ""
add_msg = msg + "|-|" + img_hash
@@ -113,15 +113,17 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
if random.random() < 0.2:
await fudu.finish("打断施法!")
_fudu_list.set_repeater(event.group_id)
if imgs and msg:
if img and msg:
rst = msg + image(f"compare_{event.group_id}_img.jpg", "temp")
elif imgs:
elif img:
rst = image(f"compare_{event.group_id}_img.jpg", "temp")
elif msg:
rst = msg
else:
rst = ""
if rst:
if rst.endswith("打断施法!"):
rst = "打断" + rst
await fudu.send(rst)
@@ -40,6 +40,9 @@ __plugin_settings__ = {
"limit_superuser": False,
"cmd": ["原神资源查询", "原神资源列表"],
}
__plugin_block_limit__ = {
"rst": "您有资源正在查询!"
}
qr = on_command("原神资源查询", aliases={"原神资源查找"}, priority=5, block=True)
qr_lst = on_command("原神资源列表", priority=5, block=True)
@@ -108,7 +108,6 @@ async def init(flag: bool = False):
resource_name_list.append(x["name"])
except TimeoutError:
logger.warning('原神资源查询信息初始化超时....')
pass
# 图标及位置资源
@@ -185,12 +184,13 @@ async def download_map_init(
semaphore,
force_flag=flag,
)
BuildImage(0, 0, background=f"{map_path}/{idx}.png", ratio=MAP_RATIO).save()
idx += 1
_w, h = BuildImage(0, 0, background=f"{map_path}/0.png", ratio=MAP_RATIO).size
_w, h = BuildImage(0, 0, background=f"{map_path}/0.png").size
w = _w * len(os.listdir(map_path))
map_file = BuildImage(w, h, _w, h, ratio=MAP_RATIO)
for i in range(idx):
map_file.paste(BuildImage(0, 0, background=f"{map_path}/{i}.png", ratio=MAP_RATIO))
map_file.paste(BuildImage(0, 0, background=f"{map_path}/{i}.png"))
map_file.save(f"{map_path}/map.png")
else:
logger.warning(f'获取原神地图失败 msg: {data["message"]}')
+7
View File
@@ -14,6 +14,12 @@ Config.add_plugin_config(
"xV8v4Qu54lUKrEYFZkJhB8cuOh9Asafs"
)
Config.add_plugin_config(
"genshin",
"n",
"h8w582wxwgqvahcdkpvdhbh2w9casgfl"
)
Config.add_plugin_config(
"genshin",
"client_type",
@@ -24,3 +30,4 @@ nonebot.load_plugins("plugins/genshin/query_user")
+2 -2
View File
@@ -70,9 +70,9 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
uid = await Genshin.get_user_uid(event.user_id)
if not uid:
await bind.finish("请先绑定原神uid..")
if msg.startswith('"'):
if msg.startswith('"') or msg.startswith("'"):
msg = msg[1:]
if msg.endswith('"'):
if msg.endswith('"') or msg.endswith("'"):
msg = msg[:-1]
await Genshin.set_cookie(uid, msg)
_x = f"已成功为uid:{uid} 设置cookie"
@@ -0,0 +1,101 @@
from .data_source import get_sign_reward_list, genshin_sign
from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent
from nonebot import on_command
from nonebot.typing import T_State
from services.log import logger
from .init_task import add_job, scheduler, _sign
from apscheduler.jobstores.base import JobLookupError
from ..models import Genshin
__zx_plugin_name__ = "原神自动签到"
__plugin_usage__ = """
usage:
米游社原神签到,需要uid以及cookie
且在第二天自动排序签到时间
# 不听,就要手动签到!(使用命令 “原神我硬签 or 米游社我硬签”
指令:
开/关原神自动签到
原神我硬签
""".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": ["原神签到"],
}
genshin_matcher = on_command(
"开原神自动签到", aliases={"关原神自动签到", "原神我硬签"}, priority=5, block=True
)
@genshin_matcher.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
uid = await Genshin.get_user_uid(event.user_id)
if not uid or not await Genshin.get_user_cookie(uid, True):
await genshin_matcher.finish("请先绑定uid和cookie!")
if "account_id" not in await Genshin.get_user_cookie(uid, True):
await genshin_matcher.finish("请更新cookie!")
if state["_prefix"]["raw_command"] == "原神我硬签":
try:
msg = await genshin_sign(uid)
logger.info(
f"(USER {event.user_id}, "
f"GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'}) UID:{uid} 原神签到"
)
# 硬签,移除定时任务
try:
for i in range(3):
scheduler.remove_job(f"genshin_auto_sign_{uid}_{event.user_id}_{i}",)
except JobLookupError:
pass
u = await Genshin.get_user_by_uid(uid)
if u and u.auto_sign:
await u.clear_sign_time(uid)
next_date = await Genshin.random_sign_time(uid)
add_job(event.user_id, uid, next_date)
msg += f"因开启自动签到\n下一次签到时间为:{next_date.replace(microsecond=0)}"
except Exception as e:
msg = "原神签到失败..请尝试检查cookie或报告至管理员!"
logger.info(
f"(USER {event.user_id}, "
f"GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'}) UID:{uid} 原神签到发生错误 "
f"{type(e)}:{e}"
)
msg = msg or "请检查cookie是否更新!"
await genshin_matcher.send(msg, at_sender=True)
else:
for i in range(3):
try:
scheduler.remove_job(f"genshin_auto_sign_{uid}_{event.user_id}_{i}")
except JobLookupError:
pass
if state["_prefix"]["raw_command"][0] == "开":
await Genshin.set_auto_sign(uid, True)
next_date = await Genshin.random_sign_time(uid)
add_job(event.user_id, uid, next_date)
await genshin_matcher.send(
f"已开启原神自动签到!\n下一次签到时间为:{next_date.replace(microsecond=0)}",
at_sender=True,
)
logger.info(
f"(USER {event.user_id}, GROUP "
f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
f" 开启原神自动签到"
)
else:
await Genshin.set_auto_sign(uid, False)
await Genshin.clear_sign_time(uid)
await genshin_matcher.send(f"已关闭原神自动签到!", at_sender=True)
logger.info(
f"(USER {event.user_id}, GROUP "
f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
f" 关闭原神自动签到"
)
@@ -0,0 +1,104 @@
from utils.http_utils import AsyncHttpx
from configs.config import Config
from services.log import logger
from ..utils import random_hex, get_old_ds
from ..models import Genshin
from typing import Optional, Dict
async def genshin_sign(uid: int) -> Optional[str]:
"""
原神签到信息
:param uid: uid
"""
data = await _sign(uid)
if not data:
return "签到失败..."
status = data["message"]
if status == "OK":
sign_info = await _get_sign_info(uid)
if sign_info:
sign_info = sign_info["data"]
sign_list = await get_sign_reward_list()
get_reward = sign_list["data"]["awards"][
int(sign_info["total_sign_day"]) - 1
]["name"]
reward_num = sign_list["data"]["awards"][
int(sign_info["total_sign_day"]) - 1
]["cnt"]
get_im = f"本次签到获得:{get_reward}x{reward_num}"
if status == "OK" and sign_info["is_sign"]:
return f"\n原神签到成功!\n{get_im}\n本月漏签次数:{sign_info['sign_cnt_missed']}"
else:
return status
return None
async def _sign(uid: int, server_id: str = "cn_gf01") -> Optional[Dict[str, str]]:
"""
米游社签到
:param uid: uid
:param server_id: 服务器id
"""
if str(uid)[0] == "5":
server_id = "cn_qd01"
try:
req = await AsyncHttpx.post(
url="https://api-takumi.mihoyo.com/event/bbs_sign_reward/sign",
headers={
"User_Agent": "Mozilla/5.0 (Linux; Android 10; MIX 2 Build/QKQ1.190825.002; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/83.0.4103.101 Mobile Safari/537.36 miHoYoBBS/2.3.0",
"Cookie": await Genshin.get_user_cookie(int(uid), True),
"x-rpc-device_id": random_hex(32),
"Origin": "https://webstatic.mihoyo.com",
"X_Requested_With": "com.mihoyo.hyperion",
"DS": get_old_ds(),
"x-rpc-client_type": "5",
"Referer": "https://webstatic.mihoyo.com/bbs/event/signin-ys/index.html?bbs_auth_required=true&act_id=e202009291139501&utm_source=bbs&utm_medium=mys&utm_campaign=icon",
"x-rpc-app_version": "2.3.0",
},
json={"act_id": "e202009291139501", "uid": uid, "region": server_id},
)
return req.json()
except Exception as e:
logger.error(f"米游社签到发生错误 UID:{uid} {type(e)}:{e}")
return None
async def get_sign_reward_list():
"""
获取签到奖励列表
"""
try:
req = await AsyncHttpx.get(
url="https://api-takumi.mihoyo.com/event/bbs_sign_reward/home?act_id=e202009291139501",
headers={
"x-rpc-app_version": str(Config.get_config("genshin", "mhyVersion")),
"User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) miHoYoBBS/2.11.1",
"x-rpc-client_type": str(Config.get_config("genshin", "client_type")),
"Referer": "https://webstatic.mihoyo.com/",
},
)
return req.json()
except Exception as e:
logger.error(f"获取签到奖励列表发生错误 {type(e)}:{e}")
return None
async def _get_sign_info(uid: int, server_id: str = "cn_gf01"):
if str(uid)[0] == "5":
server_id = "cn_qd01"
try:
req = await AsyncHttpx.get(
url=f"https://api-takumi.mihoyo.com/event/bbs_sign_reward/info?act_id=e202009291139501&region={server_id}&uid={uid}",
headers={
"x-rpc-app_version": str(Config.get_config("genshin", "mhyVersion")),
"Cookie": await Genshin.get_user_cookie(int(uid), True),
"User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) miHoYoBBS/2.11.1",
"x-rpc-client_type": str(Config.get_config("genshin", "client_type")),
"Referer": "https://webstatic.mihoyo.com/",
},
)
return req.json()
except Exception as e:
logger.error(f"获取签到信息发生错误 UID:{uid} {type(e)}:{e}")
return None
@@ -0,0 +1,110 @@
from .data_source import genshin_sign
from models.group_member_info import GroupInfoUser
from utils.message_builder import at
from services.log import logger
from utils.utils import scheduler, get_bot
from apscheduler.jobstores.base import ConflictingIdError
from ..models import Genshin
from datetime import datetime, timedelta
from nonebot import Driver
import nonebot
import random
import pytz
driver: Driver = nonebot.get_driver()
@driver.on_startup
async def _():
"""
启动时分配定时任务
"""
g_list = await Genshin.get_all_auto_sign_user()
for u in g_list:
if u.auto_sign_time:
date = await Genshin.random_sign_time(u.uid)
scheduler.add_job(
_sign,
"date",
run_date=date.replace(microsecond=0),
id=f"genshin_auto_sign_{u.uid}_{u.user_qq}_0",
args=[u.user_qq, u.uid, 0],
)
logger.info(
f"genshin_sign add_job:USER:{u.user_qq} UID:{u.uid} " f"{date} 原神自动签到"
)
def add_job(user_id: int, uid: int, date: datetime):
try:
scheduler.add_job(
_sign,
"date",
run_date=date.replace(microsecond=0),
id=f"genshin_auto_sign_{uid}_{user_id}_0",
args=[user_id, uid, 0],
)
logger.debug(f"genshin_sign add_job:{date.replace(microsecond=0)} 原神自动签到")
except ConflictingIdError:
pass
async def _sign(user_id: int, uid: int, count: int):
"""
执行签到任务
:param user_id: 用户id
:param uid: uid
:param count: 执行次数
"""
if count < 3:
try:
msg = await genshin_sign(uid)
next_time = await Genshin.random_sign_time(uid)
msg += f"\n下一次签到时间为:{next_time.replace(microsecond=0)}"
logger.info(f"USER:{user_id} UID:{uid} 原神自动签到任务发生成功...")
try:
scheduler.add_job(
_sign,
"date",
run_date=next_time.replace(microsecond=0),
id=f"genshin_auto_sign_{uid}_{user_id}_0",
args=[user_id, uid, 0],
)
except ConflictingIdError:
msg += "\n定时任务设定失败..."
except Exception as e:
logger.error(f"USER:{user_id} UID:{uid} 原神自动签到任务发生错误 {type(e)}:{e}")
msg = None
if not msg:
now = datetime.now(pytz.timezone("Asia/Shanghai"))
if now.hour < 23:
random_hours = random.randint(1, 23 - now.hour)
next_time = now + timedelta(hours=random_hours)
scheduler.add_job(
_sign,
"date",
run_date=next_time.replace(microsecond=0),
id=f"genshin_auto_sign_{uid}_{user_id}_{count}",
args=[user_id, uid, count + 1],
)
msg = (
f"{now.replace(microsecond=0)} 原神"
f"签到失败,将在 {next_time.replace(microsecond=0)} 时重试!"
)
else:
msg = "今日原神签到失败,请手动签到..."
logger.debug(f"USER:{user_id} UID:{uid} 原神今日签到失败...")
else:
msg = "今日原神自动签到重试次数已达到3次,请手动签到。"
logger.debug(f"USER:{user_id} UID:{uid} 原神今日签到失败次数打到 3 次...")
bot = get_bot()
if bot:
if user_id in [x["user_id"] for x in await bot.get_friend_list()]:
await bot.send_private_msg(user_id=user_id, message=msg)
else:
group_list = await GroupInfoUser.get_user_all_group(user_id)
if group_list:
await bot.send_group_msg(
group_id=group_list[0], message=at(user_id) + msg
)
+159 -8
View File
@@ -1,5 +1,8 @@
from services.db_context import db
from typing import Optional, Union
from typing import Optional, Union, List
from datetime import datetime, timedelta
import random
import pytz
class Genshin(db.Model):
@@ -11,7 +14,10 @@ class Genshin(db.Model):
mys_id = db.Column(db.BigInteger())
cookie = db.Column(db.String(), default="")
today_query_uid = db.Column(db.String(), default="") # 该cookie今日查询的uid
auto_sign = db.Column(db.Boolean(), default=False) # 自动签到
auto_sign = db.Column(db.Boolean(), default=False)
auto_sign_time = db.Column(db.DateTime(timezone=True))
resin_remind = db.Column(db.Boolean(), default=False) # 树脂提醒
resin_recovery_time = db.Column(db.DateTime(timezone=True)) # 满树脂提醒日期
_idx1 = db.Index("genshin_uid_idx1", "user_qq", "uid", unique=True)
@@ -66,11 +72,75 @@ class Genshin(db.Model):
return True
return False
@classmethod
async def set_resin_remind(cls, uid: int, flag: bool) -> bool:
"""
说明:
设置体力提醒
参数:
:param uid: 原神uid
:param flag: 开关状态
"""
query = cls.query.where(cls.uid == uid).with_for_update()
user = await query.gino.first()
if user:
await user.update(resin_remind=flag).apply()
return True
return False
@classmethod
async def set_user_resin_recovery_time(cls, uid: int, date: datetime):
"""
说明:
设置体力完成时间
参数:
:param uid: uid
:param date: 提醒日期
"""
u = await cls.query.where(cls.uid == uid).gino.first()
if u:
await u.update(resin_recovery_time=date).apply()
@classmethod
async def get_user_resin_recovery_time(cls, uid: int) -> Optional[datetime]:
"""
说明:
获取体力完成时间
参数:
:param uid: uid
"""
u = await cls.query.where(cls.uid == uid).gino.first()
if u:
return u.resin_recovery_time.astimezone(pytz.timezone("Asia/Shanghai"))
return None
@classmethod
async def get_all_resin_remind_user(cls) -> List["Genshin"]:
"""
说明:
获取所有开启体力提醒的用户
"""
return await cls.query.where(cls.resin_remind == True).gino.all()
@classmethod
async def clear_resin_remind_time(cls, uid: int) -> bool:
"""
说明:
清空提醒日期
参数:
:param uid: uid
"""
user = await cls.query.where(cls.uid == uid).gino.first()
if user:
await user.update(resin_recovery_time=None).apply()
return True
return False
@classmethod
async def set_auto_sign(cls, uid: int, flag: bool) -> bool:
"""
说明:
设置米游社自动签到
设置米游社/原神自动签到
参数:
:param uid: 原神uid
:param flag: 开关状态
@@ -82,6 +152,66 @@ class Genshin(db.Model):
return True
return False
@classmethod
async def get_all_auto_sign_user(cls) -> List["Genshin"]:
"""
说明:
获取所有开启自动签到的用户
"""
return await cls.query.where(cls.auto_sign == True).gino.all()
@classmethod
async def get_all_sign_user(cls) -> List["Genshin"]:
"""
说明:
获取 原神 所有今日签到用户
"""
return await cls.query.where(cls.auto_sign_time != None).gino.all()
@classmethod
async def clear_sign_time(cls, uid: int) -> bool:
"""
说明:
清空签到日期
参数:
:param uid: uid
"""
user = await cls.query.where(cls.uid == uid).gino.first()
if user:
await user.update(auto_sign_time=None).apply()
return True
return False
@classmethod
async def random_sign_time(cls, uid: int) -> Optional[datetime]:
"""
说明:
随机签到时间
说明:
:param uid: uid
"""
query = cls.query.where(cls.uid == uid).with_for_update()
user = await query.gino.first()
if user and user.cookie:
if user.auto_sign_time and user.auto_sign_time.astimezone(
pytz.timezone("Asia/Shanghai")
) - timedelta(seconds=2) >= datetime.now(pytz.timezone("Asia/Shanghai")):
return user.auto_sign_time.astimezone(pytz.timezone("Asia/Shanghai"))
hours = int(str(datetime.now()).split()[1].split(":")[0])
minutes = int(str(datetime.now()).split()[1].split(":")[1])
date = (
datetime.now()
+ timedelta(days=1)
- timedelta(hours=hours)
- timedelta(minutes=minutes - 1)
)
random_hours = random.randint(0, 22)
random_minutes = random.randint(1, 59)
date += timedelta(hours=random_hours) + timedelta(minutes=random_minutes)
await user.update(auto_sign_time=date).apply()
return date
return None
@classmethod
async def get_query_cookie(cls, uid: int) -> Optional[str]:
"""
@@ -95,7 +225,9 @@ class Genshin(db.Model):
x = await query.gino.first()
if x:
return x.cookie
for u in [x for x in await cls.query.order_by(db.func.random()).gino.all() if x.cookie]:
for u in [
x for x in await cls.query.order_by(db.func.random()).gino.all() if x.cookie
]:
if not u.today_query_uid or len(u.today_query_uid[:-1].split()) < 30:
await cls._add_query_uid(uid, u.uid)
return u.cookie
@@ -115,6 +247,26 @@ class Genshin(db.Model):
cookie = await cls.get_query_cookie(uid)
return cookie
@classmethod
async def get_user_by_qq(cls, user_qq: int) -> Optional["Genshin"]:
"""
说明:
通过qq获取用户对象
参数:
:param user_qq: qq
"""
return await cls.query.where(cls.user_qq == user_qq).gino.first()
@classmethod
async def get_user_by_uid(cls, uid: int) -> Optional["Genshin"]:
"""
说明:
通过uid获取用户对象
参数:
:param uid: qq
"""
return await cls.query.where(cls.uid == uid).gino.first()
@classmethod
async def get_user_uid(cls, user_qq: int) -> Optional[int]:
"""
@@ -178,7 +330,7 @@ class Genshin(db.Model):
@classmethod
async def _get_user_data(
cls, user_qq: Optional[int], uid: Optional[int], type_: str
cls, user_qq: Optional[int], uid: Optional[int], type_: str
) -> Optional[Union[int, str]]:
"""
说明:
@@ -198,11 +350,10 @@ class Genshin(db.Model):
return user.mys_id
elif type_ == "cookie":
return user.cookie
return None
@classmethod
async def reset_today_query_uid(cls):
for u in await cls.query.with_for_update().gino.all():
if u.today_query_uid:
await u.update(
today_query_uid=""
).apply()
await u.update(today_query_uid="").apply()
@@ -2,8 +2,9 @@ from nonebot import on_command
from nonebot.typing import T_State
from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent
from services.log import logger
from .data_source import get_user_memo
from .data_source import get_user_memo, get_memo
from ..models import Genshin
from nonebot.plugin import export
__zx_plugin_name__ = "原神便笺查询"
@@ -25,6 +26,12 @@ __plugin_settings__ = {
"limit_superuser": False,
"cmd": ["原神便笺查询"],
}
__plugin_block_limit__ = {}
export = export()
export.get_memo = get_memo
query_memo_matcher = on_command("原神便签查询", aliases={"原神便笺查询", "yss"}, priority=5, block=True)
@@ -43,7 +50,8 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
await query_memo_matcher.send(data)
logger.info(
f"(USER {event.user_id}, "
f"GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'}) 使用原神便笺查询 uid:{uid}"
f"GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'}) "
f"使用原神便笺查询 uid:{uid}"
)
else:
await query_memo_matcher.send("未查询到数据...")
@@ -51,3 +59,4 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
@@ -43,7 +43,7 @@ async def _():
async def get_user_memo(user_id: int, uid: int, uname: str) -> Optional[Union[str, MessageSegment]]:
uid = str(uid)
if uid[0] == "1" or uid[0] == "2":
if uid[0] in ["1", "2"]:
server_id = "cn_gf01"
elif uid[0] == "5":
server_id = "cn_qd01"
@@ -72,7 +72,7 @@ async def get_memo(uid: str, server_id: str) -> "Union[str, dict], int":
except TimeoutError:
return "访问超时,请稍后再试", 997
except Exception as e:
logger.info(f"便签查询获取失败未知错误 {e}:{e}")
logger.error(f"便签查询获取失败未知错误 {e}:{e}")
return "发生了一些错误,请稍后再试", 998
@@ -26,6 +26,7 @@ __plugin_settings__ = {
"limit_superuser": False,
"cmd": ["原神玩家查询"],
}
__plugin_block_limit__ = {}
query_role_info_matcher = on_command("原神玩家查询", aliases={"原神玩家查找", "ys"}, priority=5, block=True)
@@ -64,7 +64,7 @@ async def get_image(
"affix_level": char["weapon"]["affix_level"],
}
await init_image(char_data_list, _x, home_data_list)
await init_image(world_data_dict, char_data_list, _x, home_data_list)
return await get_genshin_image(
user_id,
uid,
@@ -204,6 +204,7 @@ def parsed_data(
"image": world["icon"],
"name": world["name"],
"offerings": world["offerings"],
"icon": world["icon"]
}
world_data_dict[world["name"]] = _x
home_data_list = []
@@ -77,42 +77,38 @@ def _get_genshin_image(
:param nickname: 用户昵称
:param user_ava:用户头像
"""
x = 450 if char_detailed_dict else 330
char_height = (
len(char_data_list) / 7
if len(char_data_list) % 7 == 0
else len(char_data_list) / 7 + 1
)
foot = BuildImage(1700, 87, background=image_path / "head.png")
head = BuildImage(1700, 87, background=image_path / "head.png")
head.rotate(180)
middle = BuildImage(
1700, int(1600 + 200 + x * char_height), background=image_path / "middle.png"
)
A = BuildImage(middle.w, middle.h + foot.h + head.h)
A.paste(head, (-5, 0), True)
A.paste(middle, (0, head.h), True)
A.paste(foot, (0, head.h + middle.h), True)
A.crop((0, 0, A.w - 5, A.h))
user_image = get_user_data_image(uid, role_data, mys_data, nickname, user_ava)
home_image = get_home_data_image(home_data_list)
country_image = get_country_data_image(world_data_dict)
char_image, _h = get_char_data_image(char_data_list, char_detailed_dict)
top_bk = BuildImage(user_image.w, user_image.h + home_image.h + 100, color="#F9F6F2")
char_image = get_char_data_image(char_data_list, char_detailed_dict)
top_bk = BuildImage(user_image.w, user_image.h + max([home_image.h, country_image.h]) + 100, color="#F9F6F2")
top_bk.paste(user_image, alpha=True)
top_bk.paste(home_image, (0, user_image.h + 50), alpha=True)
top_bk.paste(country_image, (home_image.w + 100, user_image.h + 50), alpha=True)
bar = BuildImage(1600, 200, font_size=50, color="#F9F6F2", font="HYWenHei-85W.ttf")
bar.text((50, 10), "角色背包", (104, 103, 101))
bar.line((50, 90, 1550, 90), (227, 219, 209), width=10)
if A.h - top_bk.h - bar.h - _h > 200:
_h = A.h - top_bk.h - bar.h - _h - 200
foot = BuildImage(1700, 87, background=image_path / "head.png")
head = BuildImage(1700, 87, background=image_path / "head.png")
head.rotate(180)
middle = BuildImage(
1700, top_bk.h + bar.h + char_image.h, background=image_path / "middle.png"
)
A = BuildImage(middle.w, middle.h + foot.h + head.h)
A.paste(head, (-5, 0), True)
A.paste(middle, (0, head.h), True)
A.paste(foot, (0, head.h + middle.h), True)
A.crop((0, 0, A.w - 5, A.h))
if A.h - top_bk.h - bar.h - char_image.h > 200:
_h = A.h - top_bk.h - bar.h - char_image.h - 200
A.crop((0, 0, A.w, A.h - _h))
A.paste(foot, (0, A.h - 87))
A.paste(top_bk, (0, 100), center_type="by_width")
A.paste(bar, (50, top_bk.h + 80))
A.paste(char_image, (0, top_bk.h + bar.h + 10), center_type="by_width")
rand = random.randint(1, 10000)
A.resize(0.8)
A.save(Path(IMAGE_PATH) / "temp" / f"genshin_user_card_{rand}.png")
return image(f"genshin_user_card_{rand}.png", "temp")
@@ -233,19 +229,20 @@ def get_home_data_image(home_data_list: List[Dict]) -> BuildImage:
画出家园数据
:param home_data_list: 家园列表
"""
h = 130 + 300 * 4
region = BuildImage(
550, 1050, color="#E3DBD1", font="HYWenHei-85W.ttf", font_size=40
550, h, color="#E3DBD1", font="HYWenHei-85W.ttf", font_size=40
)
try:
region.text(
(0, 30), f'尘歌壶 Lv.{home_data_list[0]["level"]}', center_type="by_width"
)
region.text(
(0, 980), f'仙力: {home_data_list[0]["comfort_num"]}', center_type="by_width"
(0, region.h - 70), f'仙力: {home_data_list[0]["comfort_num"]}', center_type="by_width"
)
except (IndexError, KeyError):
region.text((0, 30), f"尘歌壶 Lv.0", center_type="by_width")
region.text((0, 980), f"仙力: 0", center_type="by_width")
region.text((0, region.h - 70), f"仙力: 0", center_type="by_width")
region.circle_corner(30)
height = 100
homes = os.listdir(image_path / "homes")
@@ -300,9 +297,9 @@ def get_country_data_image(world_data_dict: Dict) -> BuildImage:
画出国家探索供奉等图像
:param world_data_dict: 国家数据字典
"""
region = BuildImage(790, 1050, color="#F9F6F2")
region = BuildImage(790, 267 * len(world_data_dict), color="#F9F6F2")
height = 0
for country in ["蒙德", "龙脊雪山", "璃月", "稻妻"]:
for country in ["蒙德", "龙脊雪山", "璃月", "稻妻", "渊下宫"]:
x = BuildImage(790, 250, color="#3A4467")
logo = BuildImage(180, 180, background=image_path / "logo" / f"{country}.png")
tmp_bk = BuildImage(770, 230, color="#606779")
@@ -356,6 +353,14 @@ def get_country_data_image(world_data_dict: Dict) -> BuildImage:
f"Lv.{world_data_dict[country]['offerings'][0]['level']}",
fill=(255, 255, 255),
)
elif country in ["渊下宫"]:
content_bk.text((300, 0), "探索", fill=(239, 211, 114), center_type="by_height")
content_bk.text(
(450, 20),
f"{world_data_dict[country]['exploration_percentage'] / 10}%",
fill=(255, 255, 255),
center_type="by_height",
)
x.paste(tmp_bk, alpha=True, center_type="center")
x.paste(content_bk, alpha=True, center_type="center")
x.circle_corner(20)
@@ -372,12 +377,9 @@ def get_char_data_image(
:param char_data_list: 角色列表
:param char_detailed_dict: 角色武器
"""
x = 420 if char_detailed_dict else 350
_h = x * int(
len(char_data_list) / 7
if len(char_data_list) % 7 == 0
else len(char_data_list) / 7 + 1
)
lens = len(char_data_list) / 7 if len(char_data_list) % 7 == 0 else len(char_data_list) / 7 + 1
x = 500
_h = int(x * lens)
region = BuildImage(
1600,
_h,
@@ -389,12 +391,12 @@ def get_char_data_image(
for char in char_data_list:
if width + 230 > 1550:
width = 120
height += x
height += 420
idx += 1
char_img = image_path / "chars" / f'{char["name"]}.png'
char_bk = BuildImage(
270,
500 if char_detailed_dict else 400,
500,
background=image_path / "element.png",
font="HYWenHei-85W.ttf",
font_size=35,
@@ -507,16 +509,23 @@ def get_char_data_image(
char_bk.resize(0.8)
region.paste(char_bk, (width, height), True)
width += 230
return region, _h
region.crop((0, 0, region.w, height + 430))
return region
async def init_image(char_data_list: List[Dict], char_detailed_dict: dict, home_data_list: List[Dict]):
async def init_image(world_data_dict: Dict[str, Dict[str, str]], char_data_list: List[Dict[str, str]], char_detailed_dict: dict, home_data_list: List[Dict]):
"""
下载头像
:param world_data_dict: 地图标志
:param char_data_list: 角色列表
:param char_detailed_dict: 角色武器
:param home_data_list: 家园列表
"""
for world in world_data_dict:
file = image_path / "logo" / f'{world_data_dict[world]["name"]}.png'
file.parent.mkdir(parents=True, exist_ok=True)
if not file.exists():
await AsyncHttpx.download_file(world_data_dict[world]["icon"], file)
for char in char_data_list:
file = image_path / "chars" / f'{char["name"]}.png'
file.parent.mkdir(parents=True, exist_ok=True)
@@ -0,0 +1,83 @@
from nonebot import on_command
from nonebot.typing import T_State
from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent
from apscheduler.jobstores.base import JobLookupError
from services.log import logger
from .init_task import scheduler, add_job
from ..models import Genshin
from datetime import datetime
import random
import asyncio
import pytz
__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": ["原神树脂提醒", "关原神树脂提醒", "开原神树脂提醒"],
}
resin_remind = on_command("开原神树脂提醒", aliases={"关原神树脂提醒"}, priority=5, block=True)
@resin_remind.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
uid = await Genshin.get_user_uid(event.user_id)
if not uid or not await Genshin.get_user_cookie(uid, True):
await resin_remind.finish("请先绑定uid和cookie!")
try:
scheduler.remove_job(f"genshin_resin_remind_{uid}_{event.user_id}")
except JobLookupError:
pass
if state["_prefix"]["raw_command"][0] == "开":
await Genshin.set_resin_remind(uid, True)
add_job(event.user_id, uid)
logger.info(
f"(USER {event.user_id}, GROUP "
f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
f" 开启原神体力提醒"
)
await resin_remind.send("开启原神树脂提醒成功!", at_sender=True)
else:
await Genshin.set_resin_remind(uid, False)
await Genshin.clear_resin_remind_time(uid)
logger.info(
f"(USER {event.user_id}, GROUP "
f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
f" 关闭原神体力提醒"
)
await resin_remind.send("已关闭原神树脂提醒..", at_sender=True)
@scheduler.scheduled_job(
"interval",
minutes=30,
)
async def _():
for u in await Genshin.get_all_resin_remind_user():
if u.resin_recovery_time:
if await Genshin.get_user_resin_recovery_time(u.uid) < datetime.now(
pytz.timezone("Asia/Shanghai")
):
await Genshin.clear_resin_remind_time(u.uid)
elif (
await Genshin.get_user_resin_recovery_time(u.uid)
- datetime.now(pytz.timezone("Asia/Shanghai"))
).seconds > 360:
continue
add_job(u.user_qq, u.uid)
await asyncio.sleep(random.randint(10, 30))
@@ -0,0 +1,123 @@
from utils.utils import get_bot, scheduler
from utils.message_builder import at
from models.group_member_info import GroupInfoUser
from apscheduler.jobstores.base import ConflictingIdError
from nonebot import Driver
from ..models import Genshin
from datetime import datetime, timedelta
from services.log import logger
from nonebot.plugin import require
import time
import nonebot
import pytz
driver: Driver = nonebot.get_driver()
get_memo = require('query_memo').get_memo
class UserManager:
def __init__(self):
self._data = []
def append(self, o: str):
if o not in self._data:
self._data.append(o)
def remove(self, o: str):
if o in self._data:
self._data.remove(o)
def exists(self, o: str):
return o in self._data
user_manager = UserManager()
@driver.on_startup
async def _():
"""
启动时分配定时任务
"""
g_list = await Genshin.get_all_resin_remind_user()
for u in g_list:
if u.resin_recovery_time and await Genshin.get_user_resin_recovery_time(
u.uid
) > datetime.now(pytz.timezone("Asia/Shanghai")):
date = await Genshin.get_user_resin_recovery_time(u.uid)
scheduler.add_job(
_remind,
"date",
run_date=date.replace(microsecond=0),
id=f"genshin_resin_remind_{u.uid}_{u.user_qq}",
args=[u.user_qq, u.uid],
)
logger.info(
f"genshin_resin_remind add_job:USER:{u.user_qq} UID:{u.uid} "
f"{date} 原神树脂提醒"
)
def add_job(user_id: int, uid: int):
date = datetime.now(pytz.timezone("Asia/Shanghai")) + timedelta(seconds=30)
try:
scheduler.add_job(
_remind,
"date",
run_date=date.replace(microsecond=0),
id=f"genshin_resin_remind_{uid}_{user_id}",
args=[user_id, uid],
)
except ConflictingIdError:
pass
async def _remind(user_id: int, uid: str):
uid = str(uid)
if uid[0] in ["1", "2"]:
server_id = "cn_gf01"
elif uid[0] == "5":
server_id = "cn_qd01"
else:
return
data, code = await get_memo(uid, server_id)
if code == 200:
current_resin = data["current_resin"] # 当前树脂
max_resin = data["max_resin"] # 最大树脂
resin_recovery_time = data["resin_recovery_time"] # 树脂全部回复时间
if max_resin - current_resin > 5:
user_manager.remove(uid)
next_time = datetime.strptime(time.strftime(
"%Y-%m-%d %H:%M:%S", time.localtime(time.time() + float(resin_recovery_time))
), "%Y-%m-%d %H:%M:%S")
await Genshin.set_user_resin_recovery_time(int(uid), next_time)
scheduler.add_job(
_remind,
"date",
run_date=next_time,
id=f"genshin_resin_remind_{uid}_{user_id}",
args=[user_id, uid],
)
logger.info(f"genshin_resin_remind add_job:{next_time.replace(microsecond=0)} 原神树脂提醒")
else:
if not user_manager.exists(uid):
user_manager.append(uid)
bot = get_bot()
if bot:
if user_id in [x["user_id"] for x in await bot.get_friend_list()]:
await bot.send_private_msg(
user_id=user_id,
message=f"树脂已经 {current_resin} 个啦" f",马上就要溢出了!快快刷掉刷掉!",
)
else:
group_list = await GroupInfoUser.get_user_all_group(user_id)
if group_list:
await bot.send_group_msg(
group_id=group_list[0],
message=at(user_id) + f"树脂已经 {current_resin} 个啦"
f",马上就要溢出了!快快刷掉刷掉!",
)
+17 -1
View File
@@ -3,6 +3,7 @@ import json
import time
import random
import hashlib
import string
def _md5(text):
@@ -11,7 +12,15 @@ def _md5(text):
return md5.hexdigest()
def get_ds(q: str = "", b: dict = None):
def get_old_ds() -> str:
n = Config.get_config("genshin", "n")
i = str(int(time.time()))
r = ''.join(random.sample(string.ascii_lowercase + string.digits, 6))
c = _md5("salt=" + n + "&t=" + i + "&r=" + r)
return i + "," + r + "," + c
def get_ds(q: str = "", b: dict = None) -> str:
if b:
br = json.dumps(b)
else:
@@ -23,6 +32,13 @@ def get_ds(q: str = "", b: dict = None):
return t + "," + r + "," + c
def random_hex(length: int) -> str:
result = hex(random.randint(0, 16 ** length)).replace("0x", "").upper()
if len(result) < length:
result = "0" * (length - len(result)) + result
return result
element_mastery = {
"anemo": "风",
"pyro": "火",
+1 -1
View File
@@ -323,7 +323,7 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
scheduler.add_job(
end_festive_redbag,
"date",
run_date=str(datetime.now() + timedelta(hours=24)).split(".")[0],
run_date=(datetime.now() + timedelta(hours=24)).replace(microsecond=0),
id=f"festive_redbag_{g}",
args=[bot, g],
)
+18 -12
View File
@@ -1,4 +1,4 @@
from nonebot import on_command, on_keyword, on_regex
from nonebot import on_message, on_keyword, on_regex
from configs.path_config import IMAGE_PATH
from utils.message_builder import image
from utils.utils import get_message_text, is_number
@@ -9,6 +9,7 @@ from utils.utils import FreqLimiter, cn2py
from pathlib import Path
from configs.config import Config
from utils.manager import group_manager, withdraw_message_manager
from .rule import rule
import random
import os
@@ -50,9 +51,8 @@ Config.add_plugin_config(
_flmt = FreqLimiter(1)
cmd = set(Config.get_config("image_management", "IMAGE_DIR_LIST"))
send_img = on_command("img", aliases=cmd, priority=5, block=True)
send_img = on_message(priority=5, rule=rule, block=True)
pa = on_keyword({"丢人爬", "爪巴"}, priority=5, block=True)
pa_reg = on_regex("^爬$", priority=5, block=True)
@@ -62,18 +62,24 @@ _path = Path(IMAGE_PATH) / "image_management"
@send_img.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
img_id = get_message_text(event.json())
path = _path / cn2py(state["_prefix"]["raw_command"])
if state["_prefix"]["raw_command"] in Config.get_config(
msg = get_message_text(event.json()).split()
gallery = msg[0]
if gallery not in Config.get_config("image_management", "IMAGE_DIR_LIST"):
return
img_id = None
if len(msg) > 1:
img_id = msg[1]
path = _path / cn2py(gallery)
if gallery in Config.get_config(
"image_management", "IMAGE_DIR_LIST"
):
if not path.exists() and (path.parent.parent / cn2py(state["_prefix"]["raw_command"])).exists():
path = Path(IMAGE_PATH) / cn2py(state["_prefix"]["raw_command"])
if not path.exists() and (path.parent.parent / cn2py(gallery)).exists():
path = Path(IMAGE_PATH) / cn2py(gallery)
else:
path.mkdir(parents=True, exist_ok=True)
length = len(os.listdir(path))
if length == 0:
logger.warning(f'图库 {cn2py(state["_prefix"]["raw_command"])} 为空,调用取消!')
logger.warning(f'图库 {cn2py(gallery)} 为空,调用取消!')
await send_img.finish("该图库中没有图片噢")
index = img_id if img_id else str(random.randint(0, length - 1))
if not is_number(index):
@@ -85,7 +91,7 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
logger.info(
f"(USER {event.user_id}, GROUP "
f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'}) "
f"发送{cn2py(state['_prefix']['raw_command'])}:"
f"发送{cn2py(gallery)}:"
+ result
)
msg_id = await send_img.send(
@@ -102,7 +108,7 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
logger.info(
f"(USER {event.user_id}, GROUP "
f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'}) "
f"发送 {cn2py(state['_prefix']['raw_command'])} 失败"
f"发送 {cn2py(gallery)} 失败"
)
await send_img.finish(f"不想给你看Ov|")
@@ -135,4 +141,4 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
return
if _flmt.check(event.user_id):
_flmt.start_cd(event.user_id)
await pa.finish(image(random.choice(os.listdir(IMAGE_PATH + "pa")), "pa"))
await pa_reg.finish(image(random.choice(os.listdir(IMAGE_PATH + "pa")), "pa"))
@@ -0,0 +1,18 @@
from nonebot.adapters.cqhttp import Bot, Event
from nonebot.typing import T_State
from utils.utils import get_message_text
from configs.config import Config
def rule(bot: Bot, event: Event, state: T_State) -> bool:
"""
检测文本是否是关闭功能命令
:param bot: pass
:param event: pass
:param state: pass
"""
msg = get_message_text(event.json())
for x in Config.get_config("image_management", "IMAGE_DIR_LIST"):
if msg.startswith(x):
return True
return False
@@ -3,7 +3,7 @@ from nonebot.rule import to_me
from nonebot.typing import T_State
from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent
from configs.config import Config
from utils.utils import get_message_imgs, get_message_text
from utils.utils import get_message_img, get_message_text
from .data_source import upload_image_to_local
@@ -49,15 +49,15 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
await upload_img.reject("此目录不正确,请重新输入目录!")
state["path"] = msg
if state["_current_key"] in ["imgs"]:
if not get_message_imgs(event.json()):
if not get_message_img(event.json()):
await upload_img.reject("图呢图呢图呢图呢!GKD!")
state["imgs"] = get_message_imgs(event.json())
state["imgs"] = get_message_img(event.json())
@upload_img.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
raw_arg = get_message_text(event.json())
img_list = get_message_imgs(event.json())
img_list = get_message_img(event.json())
if raw_arg:
if raw_arg in Config.get_config("image_management", "IMAGE_DIR_LIST"):
state["path"] = raw_arg
@@ -92,7 +92,7 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
state[state["_current_key"]] = str(event.get_message())
else:
if get_message_text(event.json()) not in ["stop"]:
img = get_message_imgs(event.json())
img = get_message_img(event.json())
if img:
state["tmp"].extend(img)
await continuous_upload_img.reject("图再来!!")
@@ -102,7 +102,7 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
@continuous_upload_img.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
path = get_message_imgs(event.json())
path = get_message_img(event.json())
if path in Config.get_config("image_management", "IMAGE_DIR_LIST"):
state["path"] = path
await continuous_upload_img.send("图来!!【停止请发送 ‘stop’ 开始上传】")
+2 -2
View File
@@ -1,7 +1,7 @@
from nonebot import on_message, on_command
from nonebot.adapters.cqhttp import Bot, GroupMessageEvent
from nonebot.adapters.cqhttp.permission import GROUP
from utils.utils import get_message_text, is_number, get_message_imgs
from utils.utils import get_message_text, is_number, get_message_img
from nonebot.typing import T_State
from nonebot.adapters.cqhttp.exception import ActionFailed
from configs.path_config import DATA_PATH, TEMP_PATH
@@ -87,7 +87,7 @@ mute_data = get_data()
async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
group_id = str(event.group_id)
msg = get_message_text(event.json())
imgs = get_message_imgs(event.json())
imgs = get_message_img(event.json())
img_hash = ""
for img in imgs:
img_hash += await download_img_and_hash(img, event.group_id)
@@ -5,7 +5,7 @@ from nonebot.plugin import on_command, on_message
from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent
from nonebot.typing import T_State
from services.log import logger
from utils.utils import get_message_text, get_message_imgs
from utils.utils import get_message_text, get_message_img
from configs.config import Config
from nonebot.rule import to_me
@@ -72,7 +72,7 @@ setu = on_command("识图", aliases={"search"}, block=True, priority=5)
@setu.handle()
async def handle_first_receive(bot: Bot, event: MessageEvent, state: T_State):
msg = get_message_text(event.json())
imgs = get_message_imgs(event.json())
imgs = get_message_img(event.json())
if imgs:
state["setu"] = imgs[0]
if msg:
@@ -87,7 +87,7 @@ async def handle_first_receive(bot: Bot, event: MessageEvent, state: T_State):
@setu.args_parser
async def get_setu(bot: Bot, event: MessageEvent, state: T_State):
imgs = get_message_imgs(event.json())
imgs = get_message_img(event.json())
msg = get_message_text(event.json())
if not imgs:
await setu.reject()
+6 -6
View File
@@ -9,7 +9,7 @@ class OpenCasesUser(db.Model):
id = db.Column(db.Integer(), primary_key=True)
user_qq = db.Column(db.BigInteger(), nullable=False)
belonging_group = db.Column(db.BigInteger(), nullable=False)
group_id = db.Column(db.BigInteger(), nullable=False)
total_count = db.Column(db.Integer(), nullable=False, default=0)
blue_count = db.Column(db.Integer(), nullable=False, default=0)
blue_st_count = db.Column(db.Integer(), nullable=False, default=0)
@@ -27,19 +27,19 @@ class OpenCasesUser(db.Model):
open_cases_time_last = db.Column(db.DateTime(timezone=True), nullable=False, default=datetime.now())
knifes_name = db.Column(db.Unicode(), nullable=False, default="")
_idx1 = db.Index('open_cases_group_users_idx1', 'user_qq', 'belonging_group', unique=True)
_idx1 = db.Index('open_cases_group_users_idx1', 'user_qq', 'group_id', unique=True)
@classmethod
async def ensure(cls, user_qq: int, belonging_group: int, for_update: bool = False) -> 'OpenCasesUser':
async def ensure(cls, user_qq: int, group_id: int, for_update: bool = False) -> 'OpenCasesUser':
query = cls.query.where(
(cls.user_qq == user_qq) & (cls.belonging_group == belonging_group)
(cls.user_qq == user_qq) & (cls.group_id == group_id)
)
if for_update:
query = query.with_for_update()
user = await query.gino.first()
return user or await cls.create(
user_qq=user_qq,
belonging_group=belonging_group,
group_id=group_id,
)
@classmethod
@@ -49,7 +49,7 @@ class OpenCasesUser(db.Model):
query = await cls.query.gino.all()
else:
query = await cls.query.where(
(cls.belonging_group == group_id)
(cls.group_id == group_id)
).gino.all()
for user in query:
user_list.append(user)
+3 -3
View File
@@ -9,7 +9,7 @@ from gino.exceptions import UninitializedError
from utils.utils import (
is_number,
get_message_text,
get_message_imgs,
get_message_img,
)
from nonebot.typing import T_State
from nonebot.adapters.cqhttp import (
@@ -243,7 +243,7 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
async def _(bot: Bot, event: MessageEvent, state: T_State):
if str(event.message) == "取消":
await find_setu.finish("取消了操作", at_sender=True)
imgs = get_message_imgs(event.json())
imgs = get_message_img(event.json())
if not imgs:
await find_setu.reject("不搞错了,俺要图!")
state["img"] = imgs[0]
@@ -253,7 +253,7 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
async def _(bot: Bot, event: MessageEvent, state: T_State):
if get_message_text(event.json()) in ["帮助"]:
await find_setu.finish("通过图片获取本地色图id\n\t示例:查色图(图片)")
imgs = get_message_imgs(event.json())
imgs = get_message_img(event.json())
if imgs:
state["img"] = imgs[0]
+1 -1
View File
@@ -32,7 +32,7 @@ update_setu = on_command(
async def _(bot: Bot, event: Event, state: T_State):
if Config.get_config("send_setu", "DOWNLOAD_SETU"):
await update_setu.send("开始更新色图...", at_sender=True)
await update_setu.send(await update_setu_img(), at_sender=True)
await update_setu_img(True)
else:
await update_setu.finish("更新色图配置未开启")
@@ -90,7 +90,11 @@ headers = {
}
async def update_setu_img():
async def update_setu_img(flag: bool = False):
"""
更新色图
:param flag: 是否手动更新
"""
image_list = await Setu.get_all_setu()
image_list.reverse()
_success = 0
@@ -105,6 +109,9 @@ async def update_setu_img():
path.mkdir(exist_ok=True, parents=True)
rar_path.mkdir(exist_ok=True, parents=True)
if not local_image.exists() or not image.img_hash:
temp_file = rar_path / f"{image.local_id}.jpg"
if temp_file.exists():
temp_file.unlink()
url_ = image.img_url
ws_url = Config.get_config("pixiv", "PIXIV_NGINX_URL")
if ws_url:
@@ -158,8 +165,7 @@ async def update_setu_img():
error_info.append(f"更新色图 {image.local_id}.jpg 错误 {type(e)}: {e}")
else:
logger.info(f"更新色图 {image.local_id}.jpg 已存在")
error_info = ["无报错.."] if not error_info else error_info
if count or _success or error_info:
if _success or error_info or flag:
await get_bot().send_private_msg(
user_id=int(list(get_bot().config.superusers)[0]),
message=f'{str(datetime.now()).split(".")[0]} 更新 色图 完成,本地存在 {count} 张,实际更新 {_success} 张,'
+4 -4
View File
@@ -50,7 +50,7 @@ async def check_in_all(nickname: str, user_qq: int):
async with db.transaction():
present = datetime.now()
for u in await SignGroupUser.get_user_all_data(user_qq):
group = u.belonging_group
group = u.group_id
if not ((
u.checkin_time_last + timedelta(hours=8)
).date() >= present.date() or f"{u}_{group}_sign_{datetime.now().date()}" in os.listdir(
@@ -79,18 +79,18 @@ async def _handle_check_in(
gift = f"额外金币 + {gift}"
else:
await BagUser.add_gold(user_qq, group, gold)
await BagUser.add_props(user_qq, group, gift)
await BagUser.add_property(user_qq, group, gift)
gift += ' + 1'
if critx2 + add_probability > 0.97 or critx2 < specify_probability:
logger.info(
f"(USER {user.user_qq}, GROUP {user.belonging_group})"
f"(USER {user.user_qq}, GROUP {user.group_id})"
f" CHECKED IN successfully. score: {user.impression:.2f} "
f"(+{impression_added * 2:.2f}).获取金币:{gold + gift if gift == 'gold' else gold}"
)
return await get_card(user, nickname, impression_added, gold, gift, True)
else:
logger.info(
f"(USER {user.user_qq}, GROUP {user.belonging_group})"
f"(USER {user.user_qq}, GROUP {user.group_id})"
f" CHECKED IN successfully. score: {user.impression:.2f} "
f"(+{impression_added:.2f}).获取金币:{gold + gift if gift == 'gold' else gold}"
)
+11 -9
View File
@@ -11,6 +11,7 @@ from .config import (
from models.sign_group_user import SignGroupUser
from models.group_member_info import GroupInfoUser
from nonebot.adapters.cqhttp import MessageSegment
from configs.config import Config
from utils.utils import get_user_avatar
from utils.image_utils import BuildImage
from utils.message_builder import image
@@ -55,32 +56,32 @@ async def get_card(
_type = "view" if is_card_view else "sign"
card_file = (
Path(SIGN_TODAY_CARD_PATH)
/ f"{user_id}_{user.belonging_group}_{_type}_{date}.png"
/ f"{user_id}_{user.group_id}_{_type}_{date}.png"
)
if card_file.exists():
return image(
f"{user_id}_{user.belonging_group}_{_type}_{date}.png", "sign/today_card"
f"{user_id}_{user.group_id}_{_type}_{date}.png", "sign/today_card"
)
else:
if add_impression == -1:
card_file = (
Path(SIGN_TODAY_CARD_PATH)
/ f"{user_id}_{user.belonging_group}_view_{date}.png"
/ f"{user_id}_{user.group_id}_view_{date}.png"
)
if card_file.exists():
return image(
f"{user_id}_{user.belonging_group}_view_{date}.png",
f"{user_id}_{user.group_id}_view_{date}.png",
"sign/today_card",
)
is_card_view = True
ava = BytesIO(await get_user_avatar(user_id))
uid = await GroupInfoUser.get_group_member_uid(
user.user_qq, user.belonging_group
user.user_qq, user.group_id
)
impression_list = None
if is_card_view:
_, impression_list, _ = await SignGroupUser.get_all_impression(
user.belonging_group
user.group_id
)
return await asyncio.get_event_loop().run_in_executor(
None,
@@ -239,9 +240,10 @@ def _generate_card(
f"上次签到日期:{'从未' if user.checkin_time_last == datetime.min else user.checkin_time_last.date()}",
)
today_data.text((0, 25), f"总金币:{gold}")
default_setu_prob = Config.get_config("send_setu", "INITIAL_SETU_PROBABILITY") * 100
today_data.text(
(0, 50),
f"色图概率:{(70 + user.impression if user.impression < 100 else 100):.2f}%",
f"色图概率:{(default_setu_prob + user.impression if user.impression < 100 else 100):.2f}%",
)
today_data.text((0, 75), f"开箱次数:{(20 + int(user.impression / 3))}")
_type = "view"
@@ -285,10 +287,10 @@ def _generate_card(
bk.paste(today_data, (580, 220), True)
bk.paste(watermark, (15, 400), True)
bk.save(
SIGN_TODAY_CARD_PATH / f"{user_id}_{user.belonging_group}_{_type}_{data}.png"
SIGN_TODAY_CARD_PATH / f"{user_id}_{user.group_id}_{_type}_{data}.png"
)
return image(
f"{user_id}_{user.belonging_group}_{_type}_{data}.png", "sign/today_card"
f"{user_id}_{user.group_id}_{_type}_{data}.png", "sign/today_card"
)
+1 -1
View File
@@ -16,7 +16,7 @@ async def translate_msg(language_type, msg):
}
data = (await AsyncHttpx.post(url, data=data)).json()
if data["errorCode"] == 0:
return data["translateResult"][0][0]["tgt"]
return f'原文:{msg}\n翻译{data["translateResult"][0][0]["tgt"]}'
return "翻译惜败.."
+4 -4
View File
@@ -6,7 +6,7 @@ from services.log import logger
from nonebot.rule import to_me
from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent
from nonebot.typing import T_State
from utils.utils import get_message_imgs
from utils.utils import get_message_img
from pathlib import Path
from utils.utils import is_number, get_message_text
from utils.image_utils import BuildImage, pic2b64
@@ -150,9 +150,9 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
await update_img.reject("请输入支持的替换的底色:\n红色 蓝色 白色 绿色")
state["y"] = str(event.get_message())
if state["_current_key"] == "imgs":
if not get_message_imgs(event.json()):
if not get_message_img(event.json()):
await update_img.reject("没图?没图?没图?来图速来!")
state[state["_current_key"]] = get_message_imgs(event.json())
state[state["_current_key"]] = get_message_img(event.json())
@update_img.handle()
@@ -160,7 +160,7 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
if str(event.get_message()) in ["帮助"]:
await update_img.finish(image("update_img_help.png"))
raw_arg = get_message_text(event.json())
img_list = get_message_imgs(event.json())
img_list = get_message_img(event.json())
if raw_arg:
args = raw_arg.split("[")[0].split(" ")
print(args)
+3 -3
View File
@@ -2,7 +2,7 @@ from .data_source import get_anime
from nonebot import on_command
from nonebot.typing import T_State
from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent
from utils.utils import get_message_imgs
from utils.utils import get_message_img
from services.log import logger
@@ -33,7 +33,7 @@ what_anime = on_command("识番", priority=5, block=True)
async def _(bot: Bot, event: MessageEvent, state: T_State):
if str(event.get_message()) in ["取消", "算了"]:
await what_anime.finish("已取消操作..", at_sender=True)
img_url = get_message_imgs(event.json())
img_url = get_message_img(event.json())
if not img_url:
await what_anime.reject(prompt="图呢图呢图呢图呢GKD", at_sender=True)
state["img_url"] = img_url
@@ -43,7 +43,7 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
async def _(bot: Bot, event: MessageEvent, state: T_State):
if str(event.get_message()) in ["帮助"]:
await what_anime.finish(__plugin_usage__)
img_url = get_message_imgs(event.json())
img_url = get_message_img(event.json())
if img_url:
state["img_url"] = img_url
+2 -2
View File
@@ -1,7 +1,7 @@
from nonebot.typing import T_State
from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent
from nonebot import on_command
from utils.utils import get_message_imgs, get_message_text, is_chinese
from utils.utils import get_message_img, get_message_text, is_chinese
from utils.message_builder import image
from configs.path_config import TEMP_PATH
from utils.image_utils import BuildImage
@@ -47,7 +47,7 @@ w2b_img = on_command("黑白草图", aliases={"黑白图"}, priority=5, block=Tr
@w2b_img.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
# try:
img = get_message_imgs(event.json())
img = get_message_img(event.json())
msg = get_message_text(event.json())
if not img or not msg:
await w2b_img.finish(f"格式错误:\n" + __plugin_usage__)
+23 -19
View File
@@ -1,9 +1,11 @@
from utils.utils import get_message_at, is_number, get_message_imgs, get_message_text
from utils.utils import get_message_at, is_number, get_message_img, get_message_text
from services.log import logger
from configs.path_config import DATA_PATH
from utils.http_utils import AsyncHttpx
from .data_source import WordBankBuilder
from configs.config import Config
from utils.message_builder import image
from utils.image_utils import text2image
from .model import WordBank
from nonebot.adapters.cqhttp import (
Bot,
@@ -42,7 +44,7 @@ __plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"admin_level": Config.get_config("word_bank", "WORD_BANK_LEVEL"),
"cmd": ["词库问答", "添加词条", "删除词条", "查看词条"]
"cmd": ["词库问答", "添加词条", "删除词条", "查看词条"],
}
data_dir = Path(DATA_PATH) / "word_bank"
@@ -65,13 +67,13 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
problem = r.group(1).strip()
if not problem:
await add_word.finish("未检测到词条问题...")
answer = msg.split('答', maxsplit=1)[-1]
answer = msg.split("答", maxsplit=1)[-1]
if not answer:
await add_word.finish("未检测到词条回答...")
idx = 0
for n in bot.config.nickname:
if problem.startswith(n):
_problem = f"[_to_me|{n}]" + problem[len(n):]
_problem = f"[_to_me|{n}]" + problem[len(n) :]
break
else:
_problem = problem
@@ -82,7 +84,7 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
answer = answer.replace(f"[CQ:at,qq={at_}]", f"[__placeholder_{idx}]", 1)
_builder.set_placeholder(idx, at_)
idx += 1
for img in get_message_imgs(event.json()):
for img in get_message_img(event.json()):
_x = img.split("?")[0]
r = re.search(rf"\[CQ:image,file=(.*),url={_x}.*?]", answer)
if r:
@@ -90,7 +92,8 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
for i in range(3):
answer = answer.replace(f",subType={i}", "")
answer = answer.replace(
rf"[CQ:image,file={r.group(1)},url={img}]", f"[__placeholder_{idx}]",
rf"[CQ:image,file={r.group(1)},url={img}]",
f"[__placeholder_{idx}]",
)
await AsyncHttpx.download_file(
img, data_dir / f"{event.group_id}" / f"__placeholder_{rand}_{idx}.jpg"
@@ -119,11 +122,13 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
x = problem.split(":")[-1]
if not is_number(x) or int(x) < 0:
await delete_word.finish("id必须为数字且符合规范!")
p = (await WordBank.get_group_all_problem(event.group_id))
p = await WordBank.get_group_all_problem(event.group_id)
if p:
problem = p[int(x)]
try:
if answer := await WordBank.delete_problem_answer(event.user_id, event.group_id, problem, index):
if answer := await WordBank.delete_problem_answer(
event.user_id, event.group_id, problem, index
):
await delete_word.send(f"删除词条成功:{problem}\n回答:\n{answer}")
logger.info(
f"(USER {event.user_id}, GROUP "
@@ -143,21 +148,20 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
_problem_list = await WordBank.get_group_all_problem(event.group_id)
if not _problem_list:
await show_word.finish("该群未收录任何词条..")
_problem_list = [f"{i}. {x}" for i, x in enumerate(_problem_list)]
_problem_list = [f"\t{i}. {x}" for i, x in enumerate(_problem_list)]
await show_word.send(
"该群已收录的词条:\n" + "\n".join(_problem_list)
image(
b64=await text2image(
"该群已收录的词条:\n\n" + "\n".join(_problem_list),
padding=10,
color="#f9f6f2",
)
)
)
else:
_answer_list = await WordBank.get_group_all_answer(event.group_id, msg)
if not _answer_list:
await show_word.send(
"未收录该词条..."
)
await show_word.send("未收录该词条...")
else:
_answer_list = [f"{i}. {x}" for i, x in enumerate(_answer_list)]
await show_word.send(
f"词条 {msg} 回答:\n" + "\n".join(_answer_list)
)
await show_word.send(f"词条 {msg} 回答:\n" + "\n".join(_answer_list))
+1 -1
View File
@@ -20,7 +20,7 @@ usage:
""".strip()
__plugin_des__ = "实时疫情数据查询"
__plugin_cmd__ = ["疫情 [省份/城市]", "疫情 中国"]
__plugin_type__ = ('一些工具',)
__plugin_type__ = ("一些工具",)
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier & yzyyz1387"
__plugin_settings__ = {
+14 -6
View File
@@ -56,18 +56,26 @@ async def get_yiqing_data(area: str) -> Union[str, MessageSegment]:
confirm = data_["total"]["confirm"] # 累计确诊
heal = data_["total"]["heal"] # 累计治愈
dead = data_["total"]["dead"] # 累计死亡
dead_rate = data_["total"]["deadRate"] # 死亡率
heal_rate = data_["total"]["healRate"] # 治愈率
now_confirm = data_["total"]["nowConfirm"] # 目前确诊
suspect = data_["total"]["suspect"] # 疑似
add_confirm = data_["today"]["confirm"] # 新增确诊
grade = ""
_grade_color = ""
if data_["total"].get("grade"):
grade = data_["total"]["grade"]
if "中风险" in grade:
_grade_color = "#fa9424"
else:
_grade_color = "red"
dead_rate = f"{dead / confirm * 100:.2f}" # 死亡率
heal_rate = f"{heal / confirm * 100:.2f}" # 治愈率
x = f"{city}市" if city else f"{province}{province_type}"
return image(b64=await text2image(
f"""
{x} 疫情数据:
{x} 疫情数据 {f"(<f font_color={_grade_color}>{grade}</f>)" if grade else ""}:
目前确诊:
确诊人数:<f font_color=red>{now_confirm}(+{add_confirm})</f>
疑似人数:{suspect}
-----------------
累计数据:
确诊人数:<f font_color=red>{confirm}</f>
@@ -75,7 +83,7 @@ async def get_yiqing_data(area: str) -> Union[str, MessageSegment]:
死亡人数:<f font_color=#191d19>{dead}</f>
治愈率:{heal_rate}%
死亡率:{dead_rate}%
更新日期:{last_update_time}
更新日期:{last_update_time}
""", font_size=30, color="#f9f6f2"
))
+2 -2
View File
@@ -4,8 +4,8 @@
"models",
"services",
"utils",
"resources/ttf"
"basic_plugins"
],
"add_file": ["resources/img/other/time.png"],
"add_file": [],
"delete_file": []
}
+34 -25
View File
@@ -168,6 +168,17 @@ async def text2image(
:param font_color: 普通字体颜色
:param padding: 文本外边距,元组类型时为 (上,左,下,右)
"""
pw = ph = top_padding = left_padding = 0
if padding:
if isinstance(padding, int):
pw = padding * 2
ph = padding * 2
top_padding = left_padding = padding
elif isinstance(padding, tuple):
pw = padding[0] + padding[2]
ph = padding[1] + padding[3]
top_padding = padding[0]
left_padding = padding[1]
if auto_parse and re.search(r"<f(.*)>(.*)</f>", text):
_data = []
new_text = ""
@@ -237,13 +248,13 @@ async def text2image(
for e in placeholder[2].split():
if e.startswith("font="):
_font = e.split("=")[-1]
if e.startswith("font_size="):
if e.startswith("font_size=") or e.startswith("fs="):
_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"):
if e.startswith("font_color") or e.startswith("fc="):
_font_color = e.split("=")[-1]
text_img = BuildImage(
0,
@@ -277,24 +288,13 @@ async def text2image(
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]
width += pw
height += ph
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
@@ -303,10 +303,18 @@ async def text2image(
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()
width += pw
height += ph
A = BuildImage(
width + left_padding,
height + top_padding,
font_size=font_size,
color=color,
font=font,
)
await A.atext((left_padding, top_padding), text, font_color)
# A.show()
return A.pic2bs4()
class BuildImage:
@@ -354,6 +362,7 @@ class BuildImage:
self.font = ImageFont.truetype(FONT_PATH + font, int(font_size))
if not plain_text and not color:
color = (255, 255, 255)
self.background = background
if not background:
if plain_text:
if not color:
@@ -589,7 +598,7 @@ class BuildImage:
pos = (w, h)
self.draw.text(pos, text, fill=fill, font=self.font)
async def asave(self, path: Union[str, Path]):
async def asave(self, path: Optional[Union[str, Path]] = None):
"""
说明:
异步 保存图片
@@ -598,15 +607,15 @@ class BuildImage:
"""
await self.loop.run_in_executor(None, self.save, path)
def save(self, path: Union[str, Path]):
def save(self, path: Optional[Union[str, Path]] = None):
"""
说明:
保存图片
参数:
:param path: 图片路径
"""
if isinstance(path, Path):
path = path.absolute()
if not path:
path = self.background
self.markImg.save(path)
def show(self):
@@ -847,8 +856,8 @@ class BuildImage:
for j in range(r2):
lx = abs(i - r) # 到圆心距离的横坐标
ly = abs(j - r) # 到圆心距离的纵坐标
l = (pow(lx, 2) + pow(ly, 2)) ** 0.5 # 三角函数 半径
if l < r3:
l_ = (pow(lx, 2) + pow(ly, 2)) ** 0.5 # 三角函数 半径
if l_ < r3:
pim_b[i - (r - r3), j - (r - r3)] = pim_a[i, j]
self.markImg = imb
+1 -1
View File
@@ -59,7 +59,7 @@ class AdminManager(StaticData):
def get_plugin_module(self, cmd: str) -> Optional[str]:
"""
根据 cmd 获取功能 module
根据 cmd 获取功能 modules
:param cmd: 命令
"""
for key in self._data.keys():
+16 -16
View File
@@ -18,7 +18,7 @@
#
# def add_plugin_config(
# self,
# module: str,
# modules: str,
# key: str,
# value: str,
# help_: Optional[str] = None,
@@ -26,39 +26,39 @@
# ):
# """
# 为插件添加一个配置
# :param module: 模块
# :param modules: 模块
# :param key: 键
# :param value: 值
# :param help_: 配置注解
# :param default_value: 默认值
# """
# if self._data.get(module) is None:
# self._data[module] = {}
# self._data[module][key] = {
# if self._data.get(modules) is None:
# self._data[modules] = {}
# self._data[modules][key] = {
# "value": value,
# "help": help_,
# "default_value": default_value,
# }
#
# def remove_plugin_config(self, module: str):
# def remove_plugin_config(self, modules: str):
# """
# 为插件删除一个配置
# :param module: 模块名
# :param modules: 模块名
# """
# if module in self._data.keys():
# del self._data[module]
# if modules in self._data.keys():
# del self._data[modules]
#
# def get_config(self, module: str, key: str) -> Optional[Any]:
# def get_config(self, modules: str, key: str) -> Optional[Any]:
# """
# 获取指定配置值
# :param module: 模块名
# :param modules: 模块名
# :param key: 配置名称
# """
# if module in self._data.keys():
# if self._data[module].get(key):
# if self._data[module][key]["value"] is None:
# return self._data[module][key]["default_value"]
# return self._data[module][key]["value"]
# if modules in self._data.keys():
# if self._data[modules].get(key):
# if self._data[modules][key]["value"] is None:
# return self._data[modules][key]["default_value"]
# return self._data[modules][key]["value"]
# return None
#
#
+1 -1
View File
@@ -86,7 +86,7 @@ class Plugins2settingsManager(StaticData):
self, cmd: str, is_all: bool = False
) -> Union[str, List[str]]:
"""
根据 cmd 获取功能 module
根据 cmd 获取功能 modules
:param cmd: 命令
:param is_all: 获取全部包含cmd的模块
"""
+1 -1
View File
@@ -208,7 +208,7 @@ def get_message_at(data: str) -> List[int]:
return []
def get_message_imgs(data: str) -> List[str]:
def get_message_img(data: str) -> List[str]:
"""
说明:
获取消息中所有的 图片 的链接