Merge branch 'HibiKier:main' into main

This commit is contained in:
AkashiCoin
2022-05-25 01:01:11 +08:00
committed by GitHub
42 changed files with 395 additions and 326 deletions
+29 -1
View File
@@ -238,6 +238,34 @@ __Docker 最新版本由 [Sakuracio](https://github.com/Sakuracio) 提供__
## 更新 ## 更新
### 2022/5/24
* fix: 修正了签到时日期时间的显示不补齐零的bug (符合日期时间表示法 ISO 8601)[@pull/600](https://github.com/HibiKier/zhenxun_bot/pull/600)
* 更新"微博热搜"接口 [@pull/579](https://github.com/HibiKier/zhenxun_bot/pull/579)
* refactor&fix(manager): modify argument [@pull/576](https://github.com/HibiKier/zhenxun_bot/pull/576)
* 修复复读不能复读图片的问题 [@pull/573](https://github.com/HibiKier/zhenxun_bot/pull/573)
* 修复抽卡插件:方舟抽卡的抽取和显示问题 [@pull/581](https://github.com/HibiKier/zhenxun_bot/pull/581)
### 2022/5/23 \[v0.1.5.6]
* 修复"清除已删除插件数据" [@pull/545](https://github.com/HibiKier/zhenxun_bot/pull/545)
* 修复有置顶的up主B站动态获取失败 [@pull/552](https://github.com/HibiKier/zhenxun_bot/pull/552)
* 添加pixiv搜图多关键词支持;修复p站搜图数量参数问题 [@pull/441](https://github.com/HibiKier/zhenxun_bot/pull/441)
* 修复开箱更新价格错误传参
* 修复pix无法正确查询uid
* 新增色图插件添加配置项ALLOW_GROUP_R18:允许群聊中使用色图r
* 新增PIX插件添加配置项ALLOW_GROUP_SETU:允许非超级用户使用-s参数
* 新增PIX插件添加配置项ALLOW_GROUP_R18:允许非超级用户使用-r参数
### 2022/5/22 \[v0.1.5.4]
* 使用action自动更新poetry.lock [@pull/515](https://github.com/HibiKier/zhenxun_bot/pull/515)
* fix(bilibili_sub): card is None and timeout [@pull/516](https://github.com/HibiKier/zhenxun_bot/pull/516)
* 修复了epic有时获取新免费游戏消息时获取不到图片
* 修复好感度满时签到出错(虽然是不可能满的
* 修复原神资源图标下载路径错误
* 修复自动更新群组可能失败
### 2022/5/21 ### 2022/5/21
* 修复搜番无结果时报错无正确反馈 * 修复搜番无结果时报错无正确反馈
@@ -279,7 +307,7 @@ __Docker 最新版本由 [Sakuracio](https://github.com/Sakuracio) 提供__
* fix: 更改p搜api,解决p搜无法使用的问题 [@pull/155](https://github.com/HibiKier/zhenxun_bot/pull/155) * fix: 更改p搜api,解决p搜无法使用的问题 [@pull/155](https://github.com/HibiKier/zhenxun_bot/pull/155)
### 2022/5/9 ### 2022/5/9 \[v0.1.5.3]
* 替换了疫情API * 替换了疫情API
* 修复了私聊.ban/.unban出错 * 修复了私聊.ban/.unban出错
+1 -1
View File
@@ -1 +1 @@
__version__: v0.1.5.3 __version__: v0.1.5.6
+3 -2
View File
@@ -2,6 +2,7 @@ from configs.config import Config
from models.chat_history import ChatHistory from models.chat_history import ChatHistory
from nonebot import on_message from nonebot import on_message
from nonebot.adapters.onebot.v11 import GroupMessageEvent, MessageEvent from nonebot.adapters.onebot.v11 import GroupMessageEvent, MessageEvent
from utils.utils import get_message_text
from ._rule import rule from ._rule import rule
@@ -22,10 +23,10 @@ chat_history = on_message(rule=rule, priority=1, block=False)
async def _(event: MessageEvent): async def _(event: MessageEvent):
if isinstance(event, GroupMessageEvent): if isinstance(event, GroupMessageEvent):
await ChatHistory.add_chat_msg( await ChatHistory.add_chat_msg(
event.user_id, event.group_id, str(event.get_message()) event.user_id, event.group_id, str(event.get_message()), get_message_text(event.json())
) )
else: else:
await ChatHistory.add_chat_msg(event.user_id, None, str(event.get_message())) await ChatHistory.add_chat_msg(event.user_id, None, str(event.get_message()), get_message_text(event.json()))
# @test.handle() # @test.handle()
@@ -20,26 +20,36 @@ def init_none_plugin_count_manager():
""" """
清除已删除插件数据 清除已删除插件数据
""" """
modules = [x.module for x in get_matchers()] modules = [x.plugin_name for x in get_matchers()]
for module in none_plugin_count_manager.keys(): plugins_manager_list = list(plugins_manager.keys())
if module not in modules: for module in plugins_manager_list:
none_plugin_count_manager.add_count(module) if module not in modules or none_plugin_count_manager.check(module):
else:
none_plugin_count_manager.reset(module)
if none_plugin_count_manager.check(module):
try: try:
plugin_name = plugins_manager.get(module)["plugin_name"] plugin_name = plugins_manager.get(module)["plugin_name"]
except (AttributeError, KeyError): except (AttributeError, KeyError):
plugin_name = "" plugin_name = ""
try: if none_plugin_count_manager.check(module):
plugins2settings_manager.delete(module) try:
plugins2count_manager.delete(module) plugins2settings_manager.delete(module)
plugins2cd_manager.delete(module) plugins2settings_manager.save()
plugins2block_manager.delete(module) plugins2count_manager.delete(module)
plugins_manager.delete(module) plugins2count_manager.save()
resources_manager.remove_resource(module) plugins2cd_manager.delete(module)
logger.info(f"{module}:{plugin_name} 插件疑似已删除,清除对应插件数据...") plugins2cd_manager.save()
except Exception as e: plugins2block_manager.delete(module)
logger.error( plugins2block_manager.save()
f"{module}:{plugin_name} 插件疑似已删除,清除对应插件数据失败...{type(e)}:{e}" plugins_manager.delete(module)
) plugins_manager.save()
resources_manager.remove_resource(module)
none_plugin_count_manager.delete(module)
logger.info(f"{module}:{plugin_name} 插件疑似已删除,清除对应插件数据...")
except Exception as e:
logger.exception(
f"{module}:{plugin_name} 插件疑似已删除,清除对应插件数据失败...{type(e)}:{e}")
else:
none_plugin_count_manager.add_count(module)
logger.info(
f"{module}:{plugin_name} 插件疑似已删除,加载{none_plugin_count_manager._max_count}次失败后将清除对应插件数据,当前次数:{none_plugin_count_manager._data[module]}")
else:
none_plugin_count_manager.reset(module)
none_plugin_count_manager.save()
@@ -28,7 +28,7 @@ def init_plugins_cd_limit(data_path):
_module = _plugin.module _module = _plugin.module
plugin_cd_limit = _module.__getattribute__("__plugin_cd_limit__") plugin_cd_limit = _module.__getattribute__("__plugin_cd_limit__")
plugins2cd_manager.add_cd_limit( plugins2cd_manager.add_cd_limit(
matcher.plugin_name, data_dict=plugin_cd_limit matcher.plugin_name, **plugin_cd_limit
) )
except AttributeError: except AttributeError:
pass pass
@@ -76,7 +76,7 @@ def init_plugins_block_limit(data_path):
_module = _plugin.module _module = _plugin.module
plugin_block_limit = _module.__getattribute__("__plugin_block_limit__") plugin_block_limit = _module.__getattribute__("__plugin_block_limit__")
plugins2block_manager.add_block_limit( plugins2block_manager.add_block_limit(
matcher.plugin_name, data_dict=plugin_block_limit matcher.plugin_name, **plugin_block_limit
) )
except AttributeError: except AttributeError:
pass pass
@@ -124,7 +124,7 @@ def init_plugins_count_limit(data_path):
_module = _plugin.module _module = _plugin.module
plugin_count_limit = _module.__getattribute__("__plugin_count_limit__") plugin_count_limit = _module.__getattribute__("__plugin_count_limit__")
plugins2count_manager.add_count_limit( plugins2count_manager.add_count_limit(
matcher.plugin_name, data_dict=plugin_count_limit matcher.plugin_name, **plugin_count_limit
) )
except AttributeError: except AttributeError:
pass pass
+4
View File
@@ -108,6 +108,10 @@ async def _():
"ALTER TABLE genshin ADD bind_group Integer;", "ALTER TABLE genshin ADD bind_group Integer;",
"genshin" "genshin"
), # 新增原神群号绑定字段 ), # 新增原神群号绑定字段
(
"ALTER TABLE chat_history ADD plain_text Text;",
"chat_history"
), # 新增纯文本
] ]
for sql in sql_str: for sql in sql_str:
try: try:
+1 -1
View File
@@ -92,7 +92,7 @@ async def _(bot: Bot, event: MessageEvent, arg: Message = CommandArg()):
level = 0 level = 0
if not msg: if not msg:
await add_group_level.finish("用法:修改群权限 [group] [level]") await add_group_level.finish("用法:修改群权限 [group] [level]")
msg = msg.split(" ") msg = msg.split()
if len(msg) < 2: if len(msg) < 2:
await add_group_level.finish("参数不完全..[group] [level]") await add_group_level.finish("参数不完全..[group] [level]")
if is_number(msg[0]) and is_number(msg[1]): if is_number(msg[0]) and is_number(msg[1]):
@@ -45,6 +45,7 @@ async def _():
group_info["group_name"], group_info["group_name"],
group_info["max_member_count"], group_info["max_member_count"],
group_info["member_count"], group_info["member_count"],
1
): ):
num += 1 num += 1
logger.info(f"自动更新群组 {g} 信息成功") logger.info(f"自动更新群组 {g} 信息成功")
+3 -3
View File
@@ -11,12 +11,13 @@ class ChatHistory(db.Model):
user_qq = db.Column(db.BigInteger(), nullable=False) user_qq = db.Column(db.BigInteger(), nullable=False)
group_id = db.Column(db.BigInteger()) group_id = db.Column(db.BigInteger())
text = db.Column(db.Text()) text = db.Column(db.Text())
plain_text = db.Column(db.Text())
create_time = db.Column(db.DateTime(timezone=True), nullable=False) create_time = db.Column(db.DateTime(timezone=True), nullable=False)
@classmethod @classmethod
async def add_chat_msg(cls, user_qq: int, group_id: Optional[int], text: str): async def add_chat_msg(cls, user_qq: int, group_id: Optional[int], text: str, plain_text: str):
await cls.create( await cls.create(
user_qq=user_qq, group_id=group_id, text=text, create_time=datetime.now() user_qq=user_qq, group_id=group_id, text=text, plain_text=plain_text, create_time=datetime.now()
) )
@classmethod @classmethod
@@ -97,7 +98,6 @@ class ChatHistory(db.Model):
if date_scope: if date_scope:
sql += f"AND create_time BETWEEN '{date_scope[0]}' AND '{date_scope[1]}' " sql += f"AND create_time BETWEEN '{date_scope[0]}' AND '{date_scope[1]}' "
sql += f"GROUP BY user_qq ORDER BY sum {order if order and order.upper() != 'DES' else ''} LIMIT {limit}" sql += f"GROUP BY user_qq ORDER BY sum {order if order and order.upper() != 'DES' else ''} LIMIT {limit}"
print(sql)
return await db.all(db.text(sql)) return await db.all(db.text(sql))
@classmethod @classmethod
+1 -1
View File
@@ -37,7 +37,7 @@ 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) ai = on_message(rule=to_me(), priority=998)
@ai.handle() @ai.handle()
-25
View File
@@ -1,7 +1,3 @@
from nonebot.adapters.onebot.v11 import MessageSegment
from utils.image_utils import BuildImage
from utils.message_builder import image
from configs.path_config import IMAGE_PATH
from typing import Optional, Tuple, Union from typing import Optional, Tuple, Union
from configs.config import Config from configs.config import Config
from utils.http_utils import AsyncHttpx from utils.http_utils import AsyncHttpx
@@ -28,24 +24,3 @@ async def get_data(url: str, params: Optional[dict] = None) -> Tuple[Union[dict,
return f'发生了错误...code:{data["code"]}', 999 return f'发生了错误...code:{data["code"]}', 999
except TimeoutError: except TimeoutError:
return "超时了....", 998 return "超时了....", 998
def gen_wbtop_pic(data: dict) -> MessageSegment:
"""
生成微博热搜图片
:param data: 微博热搜数据
"""
bk = BuildImage(700, 32 * 50 + 280, 700, 32, color="#797979")
wbtop_bk = BuildImage(700, 280, background=f"{IMAGE_PATH}/other/webtop.png")
bk.paste(wbtop_bk)
text_bk = BuildImage(700, 32 * 50, 700, 32, color="#797979")
for i, data in enumerate(data):
title = f"{i+1}. {data['hot_word']}"
hot = data["hot_word_num"]
img = BuildImage(700, 30, font_size=20)
w, h = img.getsize(title)
img.text((10, int((30 - h) / 2)), title)
img.text((580, int((30 - h) / 2)), hot)
text_bk.paste(img)
bk.paste(text_bk, (0, 280))
return image(b64=bk.pic2bs4())
+4 -4
View File
@@ -324,21 +324,21 @@ async def get_user_dynamic(
wait_until="networkidle", wait_until="networkidle",
timeout=10000, timeout=10000,
) )
await page.set_viewport_size({"width": 2560, "height": 1080}) await page.set_viewport_size({"width": 2560, "height": 1080, "timeout": 10000*20}) # timeout: 200s
# 删除置顶 # 删除置顶
await page.evaluate( await page.evaluate(
""" """
xs = document.getElementsByClassName('bili-dyn-item__tag'); xs = document.getElementsByClassName('bili-dyn-item__tag');
for (x of xs) { for (x of xs) {
x.parentNode.remove(); x.parentNode.parentNode.remove();
} }
""" """
) )
card = await page.query_selector(".bili-dyn-list__item") card = page.locator(".bili-dyn-list__item").first
await card.wait_for()
# 截图并保存 # 截图并保存
await card.screenshot( await card.screenshot(
path=dynamic_path / f"{local_user.sub_id}_{dynamic_upload_time}.jpg", path=dynamic_path / f"{local_user.sub_id}_{dynamic_upload_time}.jpg",
timeout=100000,
) )
except Exception as e: except Exception as e:
logger.error(f"B站订阅:获取用户动态 发送错误 {type(e)}:{e}") logger.error(f"B站订阅:获取用户动态 发送错误 {type(e)}:{e}")
-1
View File
@@ -38,7 +38,6 @@ async def create_live_des_image(uid: int, title: str, cover: str, tags: str, des
ava = BuildImage(100, 100, background=BytesIO(await get_pic(face))) ava = BuildImage(100, 100, background=BytesIO(await get_pic(face)))
ava.circle() ava.circle()
cover = BuildImage(470, 265, background=BytesIO(await get_pic(cover))) cover = BuildImage(470, 265, background=BytesIO(await get_pic(cover)))
print()
def _create_live_des_image( def _create_live_des_image(
-1
View File
@@ -241,7 +241,6 @@ async def _(event: MessageEvent, arg: Message = CommandArg()):
uid = int(msg[0]) uid = int(msg[0])
id_ = int(msg[1]) id_ = int(msg[1])
punish_level = int(msg[2]) punish_level = int(msg[2])
print(uid, id_, punish_level)
rst = await set_user_punish(uid, id_, punish_level) rst = await set_user_punish(uid, id_, punish_level)
await set_punish.send(rst) await set_punish.send(rst)
logger.info( logger.info(
-1
View File
@@ -68,7 +68,6 @@ class BlackWord(db.Model):
user = await query.where(cls.black_word == black_word).order_by(cls.id.desc()).gino.first() user = await query.where(cls.black_word == black_word).order_by(cls.id.desc()).gino.first()
elif id_: elif id_:
user_list = await query.gino.all() user_list = await query.gino.all()
print(len(user_list))
if len(user_list) == 0 or (id_ < 0 or id_ > len(user_list)): if len(user_list) == 0 or (id_ < 0 or id_ > len(user_list)):
return False return False
user = user_list[id_] user = user_list[id_]
-2
View File
@@ -88,9 +88,7 @@ class BlackWordManager:
:param group_id: 群号 :param group_id: 群号
:param message: 消息 :param message: 消息
""" """
print(user_id, group_id, message)
if data := self._check(message): if data := self._check(message):
print(data)
if data[0]: if data[0]:
await _add_user_black_word( await _add_user_black_word(
user_id, group_id, data[0], message, int(data[1]) user_id, group_id, data[0], message, int(data[1])
+4 -4
View File
@@ -3,10 +3,10 @@ from configs.config import Config
from bs4 import BeautifulSoup from bs4 import BeautifulSoup
import platform import platform
if platform.system() == "Windows": # if platform.system() == "Windows":
import asyncio # import asyncio
#
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) # asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
url = "http://www.eclzz.mobi" url = "http://www.eclzz.mobi"
+3 -3
View File
@@ -14,9 +14,9 @@ import tarfile
import shutil import shutil
import os import os
if str(platform.system()).lower() == "windows": # if str(platform.system()).lower() == "windows":
policy = asyncio.WindowsSelectorEventLoopPolicy() # policy = asyncio.WindowsSelectorEventLoopPolicy()
asyncio.set_event_loop_policy(policy) # asyncio.set_event_loop_policy(policy)
driver = nonebot.get_driver() driver = nonebot.get_driver()
+12 -4
View File
@@ -53,7 +53,7 @@ class PrtsHandle(BaseHandle[Operator]):
all_operators = [ all_operators = [
x x
for x in self.ALL_OPERATOR for x in self.ALL_OPERATOR
if x.star == star and not any([x.limited, x.event_only, x.recruit_only]) if x.star == star and not any([x.limited, x.recruit_only, x.event_only])
] ]
acquire_operator = None acquire_operator = None
@@ -107,9 +107,12 @@ class PrtsHandle(BaseHandle[Operator]):
async def draw(self, count: int, **kwargs) -> Message: async def draw(self, count: int, **kwargs) -> Message:
return await asyncio.get_event_loop().run_in_executor(None, self._draw, count) return await asyncio.get_event_loop().run_in_executor(None, self._draw, count)
def _draw(self, count: int, **kwargs) -> Message: def _draw(self, count: int, **kwargs) -> Message:
index2card = self.get_cards(count) index2card = self.get_cards(count)
cards = [card[0] for card in self.get_cards(count)] """这里cards修复了抽卡图文不符的bug"""
cards = [card[0] for card in index2card]
up_list = [x.name for x in self.UP_EVENT.up_char] if self.UP_EVENT else [] up_list = [x.name for x in self.UP_EVENT.up_char] if self.UP_EVENT else []
result = self.format_result(index2card, up_list=up_list) result = self.format_result(index2card, up_list=up_list)
pool_info = self.format_pool_info() pool_info = self.format_pool_info()
@@ -185,14 +188,15 @@ class PrtsHandle(BaseHandle[Operator]):
avatar = char.xpath("./td[1]/div/div/div/a/img/@srcset")[0] avatar = char.xpath("./td[1]/div/div/div/a/img/@srcset")[0]
name = char.xpath("./td[2]/a/text()")[0] name = char.xpath("./td[2]/a/text()")[0]
star = char.xpath("./td[5]/text()")[0] star = char.xpath("./td[5]/text()")[0]
sources = str(char.xpath("./td[8]/text()")[0]).split("\n") """这里sources修好了干员获取标签有问题的bug,如三星只能抽到卡缇就是这个原因"""
sources = [_.strip('\n') for _ in char.xpath("./td[8]/text()")]
except IndexError: except IndexError:
continue continue
member_dict = { member_dict = {
"头像": unquote(str(avatar).split(" ")[-2]), "头像": unquote(str(avatar).split(" ")[-2]),
"名称": remove_prohibited_str(str(name).strip()), "名称": remove_prohibited_str(str(name).strip()),
"星级": int(str(star).strip()), "星级": int(str(star).strip()),
"获取途径": [s for s in sources if s], "获取途径": sources,
} }
info[member_dict["名称"]] = member_dict info[member_dict["名称"]] = member_dict
self.dump_data(info) self.dump_data(info)
@@ -245,6 +249,10 @@ class PrtsHandle(BaseHandle[Operator]):
if match: if match:
time = match.group(1) time = match.group(1)
if "★" in line: if "★" in line:
"""这里修复了某些池子六星名称显示错误的问题(如奔崖号角)"""
if line[0] != '★':
idx = line.find('★')
line = line[idx:]
chars.append(line) chars.append(line)
if not time: if not time:
continue continue
+24 -37
View File
@@ -1,58 +1,34 @@
from httpx import AsyncClient
from datetime import datetime from datetime import datetime
from nonebot.log import logger from nonebot.log import logger
from nonebot.adapters.onebot.v11 import Bot from nonebot.adapters.onebot.v11 import Bot
from configs.config import NICKNAME from configs.config import NICKNAME
from utils.http_utils import AsyncHttpx
# 获取所有 Epic Game Store 促销游戏 # 获取所有 Epic Game Store 促销游戏
# 方法参考:RSSHub /epicgames 路由 # 方法参考:RSSHub /epicgames 路由
# https://github.com/DIYgod/RSSHub/blob/master/lib/routes/epicgames/index.js # https://github.com/DIYgod/RSSHub/blob/master/lib/routes/epicgames/index.js
async def get_epic_game(): async def get_epic_game():
# 现在没用 graphql 辣
"""prv_graphql Code
epic_url = "https://www.epicgames.com/store/backend/graphql-proxy"
headers = {
"Referer": "https://www.epicgames.com/store/zh-CN/",
"Content-Type": "application/json; charset=utf-8",
}
data = {
"query": "query searchStoreQuery($allowCountries: String, $category: String, $count: Int, $country: String!, $keywords: String, $locale: String, $namespace: String, $sortBy: String, $sortDir: String, $start: Int, $tag: String, $withPrice: Boolean = false, $withPromotions: Boolean = false) {\n Catalog {\n searchStore(allowCountries: $allowCountries, category: $category, count: $count, country: $country, keywords: $keywords, locale: $locale, namespace: $namespace, sortBy: $sortBy, sortDir: $sortDir, start: $start, tag: $tag) {\n elements {\n title\n id\n namespace\n description\n effectiveDate\n keyImages {\n type\n url\n }\n seller {\n id\n name\n }\n productSlug\n urlSlug\n url\n items {\n id\n namespace\n }\n customAttributes {\n key\n value\n }\n categories {\n path\n }\n price(country: $country) @include(if: $withPrice) {\n totalPrice {\n discountPrice\n originalPrice\n voucherDiscount\n discount\n currencyCode\n currencyInfo {\n decimals\n }\n fmtPrice(locale: $locale) {\n originalPrice\n discountPrice\n intermediatePrice\n }\n }\n lineOffers {\n appliedRules {\n id\n endDate\n discountSetting {\n discountType\n }\n }\n }\n }\n promotions(category: $category) @include(if: $withPromotions) {\n promotionalOffers {\n promotionalOffers {\n startDate\n endDate\n discountSetting {\n discountType\n discountPercentage\n }\n }\n }\n upcomingPromotionalOffers {\n promotionalOffers {\n startDate\n endDate\n discountSetting {\n discountType\n discountPercentage\n }\n }\n }\n }\n }\n paging {\n count\n total\n }\n }\n }\n}\n",
"variables": {
"allowCountries": "CN",
"category": "freegames",
"count": 1000,
"country": "CN",
"locale": "zh-CN",
"sortBy": "effectiveDate",
"sortDir": "asc",
"withPrice": True,
"withPromotions": True,
},
}
"""
epic_url = "https://store-site-backend-static-ipv4.ak.epicgames.com/freeGamesPromotions?locale=zh-CN&country=CN&allowCountries=CN" epic_url = "https://store-site-backend-static-ipv4.ak.epicgames.com/freeGamesPromotions?locale=zh-CN&country=CN&allowCountries=CN"
headers = { headers = {
"Referer": "https://www.epicgames.com/store/zh-CN/", "Referer": "https://www.epicgames.com/store/zh-CN/",
"Content-Type": "application/json; charset=utf-8", "Content-Type": "application/json; charset=utf-8",
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.80 Safari/537.36", "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.80 Safari/537.36",
} }
async with AsyncClient(headers=headers) as client: try:
try: res = await AsyncHttpx.get(epic_url, headers=headers, timeout=10)
res = await client.get(epic_url, timeout=10.0) res_json = res.json()
res_json = res.json() games = res_json["data"]["Catalog"]["searchStore"]["elements"]
games = res_json["data"]["Catalog"]["searchStore"]["elements"] return games
return games except Exception as e:
except Exception as e: logger.error(f"Epic 访问接口错误 {type(e)}:{e}")
logger.error(str(e)) return None
return None
# 获取 Epic Game Store 免费游戏信息 # 获取 Epic Game Store 免费游戏信息
# 处理免费游戏的信息方法借鉴 pip 包 epicstore_api 示例 # 处理免费游戏的信息方法借鉴 pip 包 epicstore_api 示例
# https://github.com/SD4RK/epicstore_api/blob/master/examples/free_games_example.py # https://github.com/SD4RK/epicstore_api/blob/master/examples/free_games_example.py
async def get_epic_free(bot: Bot, Type_Event: str): async def get_epic_free(bot: Bot, type_event: str):
games = await get_epic_game() games = await get_epic_game()
if not games: if not games:
return "Epic 可能又抽风啦,请稍后再试(", 404 return "Epic 可能又抽风啦,请稍后再试(", 404
@@ -81,7 +57,7 @@ async def get_epic_free(bot: Bot, Type_Event: str):
end_date = datetime.fromisoformat(end_date_iso).strftime( end_date = datetime.fromisoformat(end_date_iso).strftime(
"%b.%d %H:%M" "%b.%d %H:%M"
) )
if Type_Event == "Group": if type_event == "Group":
_message = "\n由 {} 公司发行的游戏 {} ({}) 在 UTC 时间 {} 即将推出免费游玩,预计截至 {}。".format( _message = "\n由 {} 公司发行的游戏 {} ({}) 在 UTC 时间 {} 即将推出免费游玩,预计截至 {}。".format(
game_corp, game_name, game_price, start_date, end_date game_corp, game_name, game_price, start_date, end_date
) )
@@ -101,8 +77,19 @@ async def get_epic_free(bot: Bot, Type_Event: str):
msg_list.append(msg) msg_list.append(msg)
else: else:
for image in game["keyImages"]: for image in game["keyImages"]:
if image["type"] == "Thumbnail": if (
image.get("url")
and not game_thumbnail
and image["type"]
in [
"Thumbnail",
"VaultOpened",
"DieselStoreFrontWide",
"OfferImageWide",
]
):
game_thumbnail = image["url"] game_thumbnail = image["url"]
break
for pair in game["customAttributes"]: for pair in game["customAttributes"]:
if pair["key"] == "developerName": if pair["key"] == "developerName":
game_dev = pair["value"] game_dev = pair["value"]
@@ -127,7 +114,7 @@ async def get_epic_free(bot: Bot, Type_Event: str):
game_url = "https://www.epicgames.com/store/zh-CN/p/{}".format( game_url = "https://www.epicgames.com/store/zh-CN/p/{}".format(
game_url_part game_url_part
) )
if Type_Event == "Group": if type_event == "Group":
_message = "[CQ:image,file={}]\n\nFREE now :: {} ({})\n{}\n此游戏由 {} 开发、{} 发行,将在 UTC 时间 {} 结束免费游玩,戳链接速度加入你的游戏库吧~\n{}\n".format( _message = "[CQ:image,file={}]\n\nFREE now :: {} ({})\n{}\n此游戏由 {} 开发、{} 发行,将在 UTC 时间 {} 结束免费游玩,戳链接速度加入你的游戏库吧~\n{}\n".format(
game_thumbnail, game_thumbnail,
game_name, game_name,
+7 -6
View File
@@ -106,20 +106,21 @@ async def _(event: GroupMessageEvent):
"fudu", "FUDU_PROBABILITY" "fudu", "FUDU_PROBABILITY"
) and not _fudu_list.is_repeater(event.group_id): ) and not _fudu_list.is_repeater(event.group_id):
if random.random() < 0.2: if random.random() < 0.2:
await fudu.finish("[[_task|fudu]]打断施法!") if msg.endswith("打断施法!"):
await fudu.finish("[[_task|fudu]]打断" + msg)
else:
await fudu.finish("[[_task|fudu]]打断施法!")
_fudu_list.set_repeater(event.group_id) _fudu_list.set_repeater(event.group_id)
if img and msg: if img and msg:
rst = msg + image(f"compare_{event.group_id}_img.jpg", "temp") rst = msg + image(TEMP_PATH / f"compare_{event.group_id}_img.jpg")
elif img: elif img:
rst = image(f"compare_{event.group_id}_img.jpg", "temp") rst = image(TEMP_PATH / f"compare_{event.group_id}_img.jpg")
elif msg: elif msg:
rst = msg rst = msg
else: else:
rst = "" rst = ""
if rst: if rst:
if rst.endswith("打断施法!"): await fudu.finish("[[_task|fudu]]" + rst)
rst = "打断" + rst
await fudu.send("[[_task|fudu]]" + rst)
async def get_fudu_img_hash(url, group_id): async def get_fudu_img_hash(url, group_id):
@@ -1,3 +1,4 @@
from pathlib import Path
from typing import Tuple, Optional, List from typing import Tuple, Optional, List
from configs.path_config import IMAGE_PATH, TEXT_PATH from configs.path_config import IMAGE_PATH, TEXT_PATH
from utils.message_builder import image from utils.message_builder import image
@@ -136,7 +137,7 @@ async def download_resource_data(semaphore: Semaphore):
asyncio.ensure_future( asyncio.ensure_future(
download_image( download_image(
img_url, img_url,
f"{icon_path}/{id_}.png", icon_path / f"{id_}.png",
semaphore, semaphore,
True, True,
) )
@@ -178,7 +179,7 @@ async def download_map_init(semaphore: Semaphore, flag: bool = False):
map_url = _map_data["url"] map_url = _map_data["url"]
await download_image( await download_image(
map_url, map_url,
f"{map_path}/{idx}.png", map_path / f"{idx}.png",
semaphore, semaphore,
force_flag=flag, force_flag=flag,
) )
@@ -227,15 +228,14 @@ async def download_resource_type():
except (TimeoutError, ConnectTimeout): except (TimeoutError, ConnectTimeout):
logger.warning("下载原神资源类型数据超时....") logger.warning("下载原神资源类型数据超时....")
except Exception as e: except Exception as e:
logger.error(f"载原神资源类型数据超时 {type(e)}:{e}") logger.error(f"载原神资源类型数据错误 {type(e)}:{e}")
# 初始化资源图标 # 初始化资源图标
def gen_icon(icon: str): def gen_icon(icon: Path):
A = BuildImage(0, 0, background=f"{icon_path}/box.png") A = BuildImage(0, 0, background=f"{icon_path}/box.png")
B = BuildImage(0, 0, background=f"{icon_path}/box_alpha.png") B = BuildImage(0, 0, background=f"{icon_path}/box_alpha.png")
icon_ = icon_path / f"{icon}" icon_img = BuildImage(115, 115, background=icon)
icon_img = BuildImage(115, 115, background=icon_)
icon_img.circle() icon_img.circle()
B.paste(icon_img, (17, 10), True) B.paste(icon_img, (17, 10), True)
B.paste(A, alpha=True) B.paste(A, alpha=True)
@@ -246,14 +246,14 @@ def gen_icon(icon: str):
# 下载图片 # 下载图片
async def download_image( async def download_image(
img_url: str, img_url: str,
path: str, path: Path,
semaphore: Semaphore, semaphore: Semaphore,
gen_flag: bool = False, gen_flag: bool = False,
force_flag: bool = False, force_flag: bool = False,
): ):
async with semaphore: async with semaphore:
try: try:
if not os.path.exists(path) or not is_valid or force_flag: if not path.exists() or not is_valid(path) or force_flag:
if await AsyncHttpx.download_file(img_url, path, timeout=10): if await AsyncHttpx.download_file(img_url, path, timeout=10):
logger.info(f"下载原神资源图标:{img_url}") logger.info(f"下载原神资源图标:{img_url}")
if gen_flag: if gen_flag:
+4 -4
View File
@@ -211,16 +211,16 @@ update_price = on_command("更新开箱价格", priority=1, permission=SUPERUSER
@update_price.handle() @update_price.handle()
async def _( event: MessageEvent): async def _(event: MessageEvent, arg: Message = CommandArg()):
await update_price.send(await util_get_buff_price(str(event.get_message()))) await update_price.send(await util_get_buff_price(arg.extract_plain_text().strip()))
update_img = on_command("更新开箱图片", priority=1, permission=SUPERUSER, block=True) update_img = on_command("更新开箱图片", priority=1, permission=SUPERUSER, block=True)
@update_img.handle() @update_img.handle()
async def _(event: MessageEvent): async def _(event: MessageEvent, arg: Message = CommandArg()):
await update_img.send(await util_get_buff_img(str(event.get_message()))) await update_img.send(await util_get_buff_img(str(arg.extract_plain_text().strip())))
# 重置开箱 # 重置开箱
+5 -3
View File
@@ -1,7 +1,9 @@
from nonebot import on_command from nonebot import on_command
from nonebot.adapters.onebot.v11 import Bot, MessageEvent, Message, GroupMessageEvent from nonebot.adapters.onebot.v11 import Bot, MessageEvent, Message, GroupMessageEvent
from nonebot.typing import T_State from nonebot.typing import T_State
from utils.utils import is_number
from configs.config import Config
from utils.utils import is_number, change_pixiv_image_links
from utils.message_builder import image from utils.message_builder import image
from services.log import logger from services.log import logger
from asyncio.exceptions import TimeoutError from asyncio.exceptions import TimeoutError
@@ -31,8 +33,6 @@ __plugin_settings__ = {
pid_search = on_command("p搜", aliases={"pixiv搜", "P搜"}, priority=5, block=True) pid_search = on_command("p搜", aliases={"pixiv搜", "P搜"}, priority=5, block=True)
url = "https://api.obfs.dev/api/pixiv/"
@pid_search.handle() @pid_search.handle()
async def _h(event: MessageEvent, state: T_State, arg: Message = CommandArg()): async def _h(event: MessageEvent, state: T_State, arg: Message = CommandArg()):
@@ -50,6 +50,7 @@ headers = {
@pid_search.got("pid", prompt="需要查询的图片PID是?") @pid_search.got("pid", prompt="需要查询的图片PID是?")
async def _g(event: MessageEvent, state: T_State, pid: str = Arg("pid")): async def _g(event: MessageEvent, state: T_State, pid: str = Arg("pid")):
url = Config.get_config("hibiapi", "HIBIAPI") + "/api/pixiv/"
if pid in ["取消", "算了"]: if pid in ["取消", "算了"]:
await pid_search.finish("已取消操作...") await pid_search.finish("已取消操作...")
if not is_number(pid): if not is_number(pid):
@@ -84,6 +85,7 @@ async def _g(event: MessageEvent, state: T_State, pid: str = Arg("pid")):
for image_url in data["meta_pages"]: for image_url in data["meta_pages"]:
image_list.append(image_url["image_urls"]["original"]) image_list.append(image_url["image_urls"]["original"])
for i, img_url in enumerate(image_list): for i, img_url in enumerate(image_list):
img_url = change_pixiv_image_links(img_url)
if not await AsyncHttpx.download_file( if not await AsyncHttpx.download_file(
img_url, img_url,
IMAGE_PATH / "temp" / f"pid_search_{event.user_id}_{i}.png", IMAGE_PATH / "temp" / f"pid_search_{event.user_id}_{i}.png",
+3 -3
View File
@@ -20,9 +20,9 @@ try:
except ModuleNotFoundError: except ModuleNotFoundError:
import json import json
if str(platform.system()).lower() == "windows": # if str(platform.system()).lower() == "windows":
policy = asyncio.WindowsSelectorEventLoopPolicy() # policy = asyncio.WindowsSelectorEventLoopPolicy()
asyncio.set_event_loop_policy(policy) # asyncio.set_event_loop_policy(policy)
headers = { headers = {
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6;" "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6;"
@@ -139,15 +139,14 @@ class OmegaPixivIllusts(db.Model):
data = await cls.select('pid').gino.all() data = await cls.select('pid').gino.all()
return [x[0] for x in data] return [x[0] for x in data]
@classmethod # async def test(cls, nsfw_tag: int = 1):
async def test(cls, nsfw_tag: int = 1): # if nsfw_tag is not None:
if nsfw_tag is not None: # query = cls.query.where(cls.nsfw_tag == nsfw_tag)
query = cls.query.where(cls.nsfw_tag == nsfw_tag) # else:
else: # query = cls.query
query = cls.query # query = query.where((cls.width - cls.height) < 50)
query = query.where((cls.width - cls.height) < 50) # for x in await query.gino.all():
for x in await query.gino.all(): # print(x.pid)
print(x.pid)
+11 -5
View File
@@ -18,6 +18,7 @@ usage:
查看 pix 好康图库 查看 pix 好康图库
指令: 指令:
pix ?*[tags]: 通过 tag 获取相似图片,不含tag时随机抽取 pix ?*[tags]: 通过 tag 获取相似图片,不含tag时随机抽取
pid [uid]: 通过uid获取图片
pix pid[pid]: 查看图库中指定pid图片 pix pid[pid]: 查看图库中指定pid图片
""".strip() """.strip()
__plugin_superuser_usage__ = """ __plugin_superuser_usage__ = """
@@ -49,7 +50,9 @@ __plugin_configs__ = {
"value": None, "value": None,
"help": "单次发送的图片数量达到指定值时转发为合并消息", "help": "单次发送的图片数量达到指定值时转发为合并消息",
"default_value": None, "default_value": None,
} },
"ALLOW_GROUP_SETU": {"value": False, "help": "允许非超级用户使用-s参数", "default_value": False},
"ALLOW_GROUP_R18": {"value": False, "help": "允许非超级用户使用-r参数", "default_value": False},
} }
@@ -80,10 +83,13 @@ async def _(bot: Bot, event: MessageEvent, arg: Message = CommandArg()):
nsfw_tag = 2 nsfw_tag = 2
else: else:
nsfw_tag = 0 nsfw_tag = 0
if nsfw_tag != 0 and str(event.user_id) not in bot.config.superusers: if str(event.user_id) not in bot.config.superusers:
await pix.finish("你不能看这些噢,这些都是是留给管理员看的...") if (nsfw_tag == 1 and not Config.get_config("pix", "ALLOW_GROUP_SETU")) or (
if n := len(x) == 1 and is_number(x[0]): nsfw_tag == 2 and not Config.get_config("pix", "ALLOW_GROUP_R18")
num = int(x[-1]) ):
await pix.finish("你不能看这些噢,这些都是是留给管理员看的...")
if n := len(x) == 1 and is_number(x[0]) and int(x[0]) < 100:
num = int(x[0])
keyword = "" keyword = ""
elif n > 1: elif n > 1:
if is_number(x[-1]): if is_number(x[-1]):
+4 -2
View File
@@ -44,6 +44,8 @@ usage:
搜图 樱岛麻衣 搜图 樱岛麻衣
搜图 樱岛麻衣 5 搜图 樱岛麻衣 5
搜图 樱岛麻衣 5 r18 搜图 樱岛麻衣 5 r18
搜图 樱岛麻衣#1000users 5
【多个关键词用#分割】
【默认为 热度排序】 【默认为 热度排序】
【注意空格!!】【在线搜索会较慢】【数量可能不符?可能该页数量不够,也可能被R-18屏蔽】 【注意空格!!】【在线搜索会较慢】【数量可能不符?可能该页数量不够,也可能被R-18屏蔽】
""".strip() """.strip()
@@ -161,8 +163,8 @@ async def _(bot: Bot, event: MessageEvent, arg: Message = CommandArg()):
info_list = None info_list = None
num = 10 num = 10
page = 1 page = 1
if (n := len(msg)) == 1: if (n := len(msg)) > 0:
keyword = msg[0] keyword = msg[0].replace("#"," ")
if n > 1: if n > 1:
if not is_number(msg[1]): if not is_number(msg[1]):
await pixiv_keyword.finish("图片数量必须是数字!", at_sender=True) await pixiv_keyword.finish("图片数量必须是数字!", at_sender=True)
+4 -4
View File
@@ -8,10 +8,10 @@ from services.log import logger
from pathlib import Path from pathlib import Path
import platform import platform
if platform.system() == "Windows": # if platform.system() == "Windows":
import asyncio # import asyncio
#
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) # asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
headers = { headers = {
+11 -4
View File
@@ -97,6 +97,7 @@ __plugin_configs__ = {
}, },
"TIMEOUT": {"value": 10, "help": "色图下载超时限制(秒)", "default_value": 10}, "TIMEOUT": {"value": 10, "help": "色图下载超时限制(秒)", "default_value": 10},
"SHOW_INFO": {"value": True, "help": "是否显示色图的基本信息,如PID等", "default_value": True}, "SHOW_INFO": {"value": True, "help": "是否显示色图的基本信息,如PID等", "default_value": True},
"ALLOW_GROUP_R18": {"value": False, "help": "在群聊中启用R18权限", "default_value": False},
} }
Config.add_plugin_config("pixiv", "PIXIV_NGINX_URL", "i.pixiv.re", help_="Pixiv反向代理") Config.add_plugin_config("pixiv", "PIXIV_NGINX_URL", "i.pixiv.re", help_="Pixiv反向代理")
@@ -148,10 +149,16 @@ async def _(
if cmd[0] == "色图r" and isinstance(event, PrivateMessageEvent): if cmd[0] == "色图r" and isinstance(event, PrivateMessageEvent):
r18 = 1 r18 = 1
num = 10 num = 10
elif cmd[0] == "色图r" and isinstance(event, GroupMessageEvent): elif (
await setu.finish( cmd[0] == "色图r"
random.choice(["这种不好意思的东西怎么可能给这么多人看啦", "羞羞脸!给我滚出克私聊!", "变态变态变态变态大变态!"]) and isinstance(event, GroupMessageEvent)
) ):
if not Config.get_config("send_setu", "ALLOW_GROUP_R18"):
await setu.finish(
random.choice(["这种不好意思的东西怎么可能给这么多人看啦", "羞羞脸!给我滚出克私聊!", "变态变态变态变态大变态!"])
)
else:
r18 = 1
# 有 数字 的话先尝试本地色图id # 有 数字 的话先尝试本地色图id
if msg and is_number(msg): if msg and is_number(msg):
setu_list, code = await get_setu_list(int(msg), r18=r18) setu_list, code = await get_setu_list(int(msg), r18=r18)
+17 -21
View File
@@ -24,6 +24,8 @@ from io import BytesIO
import asyncio import asyncio
import random import random
import nonebot import nonebot
import time
import locale
import os import os
@@ -55,13 +57,10 @@ async def get_card(
date = datetime.now().date() date = datetime.now().date()
_type = "view" if is_card_view else "sign" _type = "view" if is_card_view else "sign"
card_file = ( card_file = (
Path(SIGN_TODAY_CARD_PATH) Path(SIGN_TODAY_CARD_PATH) / f"{user_id}_{user.group_id}_{_type}_{date}.png"
/ f"{user_id}_{user.group_id}_{_type}_{date}.png"
) )
if card_file.exists(): if card_file.exists():
return image( return image(f"{user_id}_{user.group_id}_{_type}_{date}.png", "sign/today_card")
f"{user_id}_{user.group_id}_{_type}_{date}.png", "sign/today_card"
)
else: else:
if add_impression == -1: if add_impression == -1:
card_file = ( card_file = (
@@ -75,9 +74,7 @@ async def get_card(
) )
is_card_view = True is_card_view = True
ava = BytesIO(await get_user_avatar(user_id)) ava = BytesIO(await get_user_avatar(user_id))
uid = await GroupInfoUser.get_group_member_uid( uid = await GroupInfoUser.get_group_member_uid(user.user_qq, user.group_id)
user.user_qq, user.group_id
)
impression_list = None impression_list = None
if is_card_view: if is_card_view:
_, impression_list, _ = await SignGroupUser.get_all_impression( _, impression_list, _ = await SignGroupUser.get_all_impression(
@@ -128,9 +125,13 @@ def _generate_card(
level, next_impression, previous_impression = get_level_and_next_impression( level, next_impression, previous_impression = get_level_and_next_impression(
user.impression user.impression
) )
interpolation = next_impression - user.impression
if level == "9":
level = "8"
interpolation = 0
info_img.text((0, 0), f"· 好感度等级:{level} [{lik2relation[level]}]") info_img.text((0, 0), f"· 好感度等级:{level} [{lik2relation[level]}]")
info_img.text((0, 20), f"· {NICKNAME}对你的态度:{level2attitude[level]}") info_img.text((0, 20), f"· {NICKNAME}对你的态度:{level2attitude[level]}")
info_img.text((0, 40), f"· 距离升级还差 {next_impression - user.impression:.2f} 好感度") info_img.text((0, 40), f"· 距离升级还差 {interpolation:.2f} 好感度")
bar_bk = BuildImage(220, 20, background=SIGN_RESOURCE_PATH / "bar_white.png") bar_bk = BuildImage(220, 20, background=SIGN_RESOURCE_PATH / "bar_white.png")
bar = BuildImage(220, 20, background=SIGN_RESOURCE_PATH / "bar.png") bar = BuildImage(220, 20, background=SIGN_RESOURCE_PATH / "bar.png")
@@ -240,7 +241,9 @@ def _generate_card(
f"上次签到日期:{'从未' if user.checkin_time_last == datetime.min else user.checkin_time_last.date()}", f"上次签到日期:{'从未' if user.checkin_time_last == datetime.min else user.checkin_time_last.date()}",
) )
today_data.text((0, 25), f"总金币:{gold}") today_data.text((0, 25), f"总金币:{gold}")
default_setu_prob = Config.get_config("send_setu", "INITIAL_SETU_PROBABILITY") * 100 default_setu_prob = (
Config.get_config("send_setu", "INITIAL_SETU_PROBABILITY") * 100
)
today_data.text( today_data.text(
(0, 50), (0, 50),
f"色图概率:{(default_setu_prob + user.impression if user.impression < 100 else 100):.2f}%", f"色图概率:{(default_setu_prob + user.impression if user.impression < 100 else 100):.2f}%",
@@ -259,15 +262,12 @@ def _generate_card(
today_data.text((0, 25), f"金币 + {gold}") today_data.text((0, 25), f"金币 + {gold}")
_type = "sign" _type = "sign"
current_date = datetime.now() current_date = datetime.now()
week = current_date.isoweekday() current_datetime_str = current_date.strftime("%Y-%m-%d %a %H:%M:%S")
data = current_date.date() data = current_date.date()
hour = current_date.hour
minute = current_date.minute
second = current_date.second
data_img = BuildImage( data_img = BuildImage(
0, 0,
0, 0,
plain_text=f"时间:{data} {weekdays[week]} {hour}:{minute}:{second}", plain_text=f"时间:{current_datetime_str}",
color=(255, 255, 255, 0), color=(255, 255, 255, 0),
font_size=20, font_size=20,
) )
@@ -286,12 +286,8 @@ def _generate_card(
bk.paste(today_sign_text_img, (550, 180), True) bk.paste(today_sign_text_img, (550, 180), True)
bk.paste(today_data, (580, 220), True) bk.paste(today_data, (580, 220), True)
bk.paste(watermark, (15, 400), True) bk.paste(watermark, (15, 400), True)
bk.save( bk.save(SIGN_TODAY_CARD_PATH / f"{user_id}_{user.group_id}_{_type}_{data}.png")
SIGN_TODAY_CARD_PATH / f"{user_id}_{user.group_id}_{_type}_{data}.png" return image(f"{user_id}_{user.group_id}_{_type}_{data}.png", "sign/today_card")
)
return image(
f"{user_id}_{user.group_id}_{_type}_{data}.png", "sign/today_card"
)
def generate_progress_bar_pic(): def generate_progress_bar_pic():
+2 -2
View File
@@ -5,8 +5,8 @@ import asyncio
import platform import platform
import os import os
if platform.system() == "Windows": # if platform.system() == "Windows":
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) # asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
url = "https://github.com/Mrs4s/go-cqhttp/releases" url = "https://github.com/Mrs4s/go-cqhttp/releases"
+19 -11
View File
@@ -2,12 +2,12 @@ from nonebot import on_command
from nonebot.adapters.onebot.v11 import MessageEvent, GroupMessageEvent, Message from nonebot.adapters.onebot.v11 import MessageEvent, GroupMessageEvent, Message
from nonebot.params import CommandArg from nonebot.params import CommandArg
from services.log import logger from services.log import logger
from ._data_source import get_data, gen_wbtop_pic from .data_source import gen_wbtop_pic,get_wbtop
from utils.utils import is_number from utils.utils import is_number
from configs.path_config import IMAGE_PATH from configs.path_config import IMAGE_PATH
from utils.http_utils import AsyncPlaywright from utils.http_utils import AsyncPlaywright
import asyncio import asyncio
import datetime
__zx_plugin_name__ = "微博热搜" __zx_plugin_name__ = "微博热搜"
__plugin_usage__ = """ __plugin_usage__ = """
usage: usage:
@@ -19,8 +19,8 @@ usage:
""".strip() """.strip()
__plugin_des__ = "刚买完瓜,在吃瓜现场" __plugin_des__ = "刚买完瓜,在吃瓜现场"
__plugin_cmd__ = ["微博热搜", "微博热搜 [id]"] __plugin_cmd__ = ["微博热搜", "微博热搜 [id]"]
__plugin_version__ = 0.1 __plugin_version__ = 0.2
__plugin_author__ = "HibiKier" __plugin_author__ = "HibiKier & yajiwa"
__plugin_settings__ = { __plugin_settings__ = {
"level": 5, "level": 5,
"default_status": True, "default_status": True,
@@ -31,7 +31,7 @@ __plugin_settings__ = {
wbtop = on_command("wbtop", aliases={"微博热搜"}, priority=5, block=True) wbtop = on_command("wbtop", aliases={"微博热搜"}, priority=5, block=True)
wbtop_url = "https://v2.alapi.cn/api/new/wbtop" wbtop_url = "https://weibo.com/ajax/side/hotSearch"
wbtop_data = [] wbtop_data = []
@@ -41,13 +41,21 @@ async def _(event: MessageEvent, arg: Message = CommandArg()):
global wbtop_data global wbtop_data
msg = arg.extract_plain_text().strip() msg = arg.extract_plain_text().strip()
if not wbtop_data or not msg: if not wbtop_data or not msg:
data, code = await get_data(wbtop_url) if wbtop_data:
if code != 200: now_time = datetime.datetime.now()
await wbtop.finish(data, at_sender=True) if now_time > wbtop_data["time"] + datetime.timedelta(minutes=5):
wbtop_data = data["data"] data, code = await get_wbtop(wbtop_url)
if code != 200:
await wbtop.finish(data, at_sender=True)
wbtop_data = data
else:
data, code = await get_wbtop(wbtop_url)
if code != 200:
await wbtop.finish(data, at_sender=True)
wbtop_data = data
if not msg: if not msg:
img = await asyncio.get_event_loop().run_in_executor( img = await asyncio.get_event_loop().run_in_executor(
None, gen_wbtop_pic, wbtop_data None, gen_wbtop_pic, wbtop_data["data"]
) )
await wbtop.send(img) await wbtop.send(img)
logger.info( logger.info(
@@ -55,7 +63,7 @@ async def _(event: MessageEvent, arg: Message = CommandArg()):
f" 查询微博热搜" f" 查询微博热搜"
) )
if is_number(msg) and 0 < int(msg) <= 50: if is_number(msg) and 0 < int(msg) <= 50:
url = wbtop_data[int(msg) - 1]["url"] url = wbtop_data["data"][int(msg) - 1]["url"]
await wbtop.send("开始截取数据...") await wbtop.send("开始截取数据...")
img = await AsyncPlaywright.screenshot( img = await AsyncPlaywright.screenshot(
url, url,
+62
View File
@@ -0,0 +1,62 @@
from nonebot.adapters.onebot.v11 import MessageSegment
from utils.image_utils import BuildImage
from utils.message_builder import image
from configs.path_config import IMAGE_PATH
from typing import Tuple, Union
from utils.http_utils import AsyncHttpx
import datetime
async def get_wbtop(url: str) -> Tuple[Union[dict, str], int]:
"""
:param url: 请求链接
"""
n = 0
while True:
try:
data = []
get_response = (await AsyncHttpx.get(url, timeout=20))
if get_response.status_code == 200:
data_json = get_response.json()['data']['realtime']
for data_item in data_json:
# 如果是广告,则不添加
if 'is_ad' in data_item:
continue
dic = {
'hot_word': data_item['note'],
'hot_word_num': str(data_item['num']),
'url': 'https://s.weibo.com/weibo?q=%23' + data_item['word'] + '%23',
}
data.append(dic)
if not data:
return "没有搜索到...", 997
return {'data': data, 'time': datetime.datetime.now()}, 200
else:
if n > 2:
return f'获取失败,请十分钟后再试', 999
else:
n += 1
continue
except TimeoutError:
return "超时了....", 998
def gen_wbtop_pic(data: dict) -> MessageSegment:
"""
生成微博热搜图片
:param data: 微博热搜数据
"""
bk = BuildImage(700, 32 * 50 + 280, 700, 32, color="#797979")
wbtop_bk = BuildImage(700, 280, background=f"{IMAGE_PATH}/other/webtop.png")
bk.paste(wbtop_bk)
text_bk = BuildImage(700, 32 * 50, 700, 32, color="#797979")
for i, data in enumerate(data):
title = f"{i + 1}. {data['hot_word']}"
hot = data["hot_word_num"]
img = BuildImage(700, 30, font_size=20)
w, h = img.getsize(title)
img.text((10, int((30 - h) / 2)), title)
img.text((580, int((30 - h) / 2)), hot)
text_bk.paste(img)
bk.paste(text_bk, (0, 280))
return image(b64=bk.pic2bs4())
+19 -11
View File
@@ -12,6 +12,7 @@ import numpy as np
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
from io import BytesIO from io import BytesIO
from configs.path_config import IMAGE_PATH, FONT_PATH from configs.path_config import IMAGE_PATH, FONT_PATH
from services import logger
from utils.http_utils import AsyncHttpx from utils.http_utils import AsyncHttpx
from models.chat_history import ChatHistory from models.chat_history import ChatHistory
from configs.config import Config from configs.config import Config
@@ -19,10 +20,11 @@ from configs.config import Config
async def pre_precess(msg: List[str], config) -> str: async def pre_precess(msg: List[str], config) -> str:
return await asyncio.get_event_loop().run_in_executor( return await asyncio.get_event_loop().run_in_executor(
None, _pre_precess, msg,config) None, _pre_precess, msg, config
)
def _pre_precess(msg: List[str],config) -> str: def _pre_precess(msg: List[str], config) -> str:
"""对消息进行预处理""" """对消息进行预处理"""
# 过滤掉命令 # 过滤掉命令
command_start = tuple([i for i in config.command_start if i]) command_start = tuple([i for i in config.command_start if i])
@@ -46,7 +48,6 @@ def _pre_precess(msg: List[str],config) -> str:
return msg return msg
async def draw_word_cloud(messages, config): async def draw_word_cloud(messages, config):
wordcloud_dir = IMAGE_PATH / "wordcloud" wordcloud_dir = IMAGE_PATH / "wordcloud"
wordcloud_dir.mkdir(exist_ok=True, parents=True) wordcloud_dir.mkdir(exist_ok=True, parents=True)
@@ -57,19 +58,21 @@ async def draw_word_cloud(messages, config):
url = "https://ghproxy.com/https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/resources/image/wordcloud/default.png" url = "https://ghproxy.com/https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/resources/image/wordcloud/default.png"
try: try:
await AsyncHttpx.download_file(url, zx_logo_path) await AsyncHttpx.download_file(url, zx_logo_path)
except: except Exception as e:
logger.error(f"词云图片资源下载发生错误 {type(e)}:{e}")
return False return False
if not wordcloud_ttf.exists(): if not wordcloud_ttf.exists():
ttf_url = 'https://ghproxy.com/https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/resources/font/STKAITI.TTF' ttf_url = "https://ghproxy.com/https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/resources/font/STKAITI.TTF"
try: try:
await AsyncHttpx.download_file(ttf_url, wordcloud_ttf) await AsyncHttpx.download_file(ttf_url, wordcloud_ttf)
except: except Exception as e:
logger.error(f"词云字体资源下载发生错误 {type(e)}:{e}")
return False return False
topK = min(int(len(messages)), 100000) topK = min(int(len(messages)), 100000)
read_name = jieba.analyse.extract_tags(await pre_precess(messages, config), topK=topK, read_name = jieba.analyse.extract_tags(
withWeight=True, await pre_precess(messages, config), topK=topK, withWeight=True, allowPOS=()
allowPOS=()) )
name = [] name = []
value = [] value = []
for t in read_name: for t in read_name:
@@ -79,10 +82,11 @@ async def draw_word_cloud(messages, config):
name[i] = str(name[i]) name[i] = str(name[i])
dic = dict(zip(name, value)) dic = dict(zip(name, value))
if Config.get_config("word_clouds", "WORD_CLOUDS_TEMPLATE") == 1: if Config.get_config("word_clouds", "WORD_CLOUDS_TEMPLATE") == 1:
def random_pic(base_path: str) -> str: def random_pic(base_path: str) -> str:
path_dir = os.listdir(base_path) path_dir = os.listdir(base_path)
path = random.sample(path_dir, 1)[0] path = random.sample(path_dir, 1)[0]
return (str(base_path) + "/" + str(path)) return str(base_path) + "/" + str(path)
mask = np.array(IMG.open(random_pic(wordcloud_dir))) mask = np.array(IMG.open(random_pic(wordcloud_dir)))
wc = WordCloud( wc = WordCloud(
@@ -113,7 +117,11 @@ async def draw_word_cloud(messages, config):
async def get_list_msg(user_id, group_id, days): async def get_list_msg(user_id, group_id, days):
messages_list = await ChatHistory()._get_msg(uid=user_id, gid=group_id, type_="group", days=days).gino.all() messages_list = (
await ChatHistory()
._get_msg(uid=user_id, gid=group_id, type_="group", days=days)
.gino.all()
)
if messages_list: if messages_list:
messages = [i.text for i in messages_list] messages = [i.text for i in messages_list]
return messages return messages
+1 -1
View File
@@ -121,7 +121,7 @@ def fig2b64(plt_: plt) -> str:
return "base64://" + base64_str return "base64://" + base64_str
def is_valid(file: str) -> bool: def is_valid(file: Union[str, Path]) -> bool:
""" """
说明: 说明:
判断图片是否损坏 判断图片是否损坏
+3 -7
View File
@@ -4,7 +4,6 @@ from pathlib import Path
class NonePluginCountManager(StaticData): class NonePluginCountManager(StaticData):
""" """
插件加载容忍管理器,当连续 max_count 次插件加载,视为删除插件,清楚数据 插件加载容忍管理器,当连续 max_count 次插件加载,视为删除插件,清楚数据
""" """
@@ -15,6 +14,8 @@ class NonePluginCountManager(StaticData):
:param max_count: 容忍最大次数 :param max_count: 容忍最大次数
""" """
super().__init__(file) super().__init__(file)
if not self._data:
self._data = {}
self._max_count = max_count self._max_count = max_count
def add_count(self, module: str, count: int = 1): def add_count(self, module: str, count: int = 1):
@@ -42,10 +43,5 @@ class NonePluginCountManager(StaticData):
:param module: 模块 :param module: 模块
""" """
if module in self._data.keys(): if module in self._data.keys():
return self._data.keys() > self._max_count return self._data[module] >= self._max_count
return False return False
+6 -13
View File
@@ -21,18 +21,17 @@ class Plugins2blockManager(StaticData):
with open(file, "r", encoding="utf8") as f: with open(file, "r", encoding="utf8") as f:
self._data = yaml.load(f) self._data = yaml.load(f)
if "PluginBlockLimit" in self._data.keys(): if "PluginBlockLimit" in self._data.keys():
self._data = ( self._data = self._data["PluginBlockLimit"] or {}
self._data["PluginBlockLimit"] if self._data["PluginBlockLimit"] else {}
)
def add_block_limit( def add_block_limit(
self, self,
plugin: str, plugin: str,
*,
status: Optional[bool] = True, status: Optional[bool] = True,
check_type: Optional[str] = "all", check_type: Optional[str] = "all",
limit_type: Optional[str] = "user", limit_type: Optional[str] = "user",
rst: Optional[str] = None, rst: Optional[str] = None,
data_dict: Optional[dict] = None, **kwargs # 用于接收额外实参
): ):
""" """
添加插件调用 block 限制 添加插件调用 block 限制
@@ -41,16 +40,10 @@ class Plugins2blockManager(StaticData):
:param check_type: 检查类型 'private'/'group'/'all',限制私聊/群聊/全部 :param check_type: 检查类型 'private'/'group'/'all',限制私聊/群聊/全部
:param limit_type: 限制类型 监听对象,以user_id或group_id作为键来限制,'user':用户id,'group':群id :param limit_type: 限制类型 监听对象,以user_id或group_id作为键来限制,'user':用户id,'group':群id
:param rst: 回复的话,为空则不回复 :param rst: 回复的话,为空则不回复
:param data_dict: 封装好的字典数据
""" """
if data_dict: status = status or True
status = data_dict.get("status") check_type = check_type or "all"
check_type = data_dict.get("check_type") limit_type = limit_type or "user"
limit_type = data_dict.get("limit_type")
rst = data_dict.get("rst")
status = status if status is not None else True
check_type = check_type if check_type else "all"
limit_type = limit_type if limit_type else "user"
if check_type not in ["all", "group", "private"]: if check_type not in ["all", "group", "private"]:
raise ValueError( raise ValueError(
f"{plugin} 添加block限制错误,‘check_type‘ 必须为 'private'/'group'/'all'" f"{plugin} 添加block限制错误,‘check_type‘ 必须为 'private'/'group'/'all'"
+5 -12
View File
@@ -34,7 +34,7 @@ class Plugins2cdManager(StaticData):
check_type: Optional[str] = "all", check_type: Optional[str] = "all",
limit_type: Optional[str] = "user", limit_type: Optional[str] = "user",
rst: Optional[str] = None, rst: Optional[str] = None,
data_dict: Optional[dict] = None, **kwargs # 用于接收额外实参
): ):
""" """
添加插件调用 cd 限制 添加插件调用 cd 限制
@@ -44,18 +44,11 @@ class Plugins2cdManager(StaticData):
:param check_type: 检查类型 'private'/'group'/'all',限制私聊/群聊/全部 :param check_type: 检查类型 'private'/'group'/'all',限制私聊/群聊/全部
:param limit_type: 限制类型 监听对象,以user_id或group_id作为键来限制,'user':用户id,'group':群id :param limit_type: 限制类型 监听对象,以user_id或group_id作为键来限制,'user':用户id,'group':群id
:param rst: 回复的话,为空则不回复 :param rst: 回复的话,为空则不回复
:param data_dict: 封装好的字典数据
""" """
if data_dict: cd = cd or 5
cd = data_dict.get("cd") status = status or True
status = data_dict.get("status") check_type = check_type or "all"
check_type = data_dict.get("check_type") limit_type = limit_type or "user"
limit_type = data_dict.get("limit_type")
rst = data_dict.get("rst")
cd = cd if cd is not None else 5
status = status if status is not None else True
check_type = check_type if check_type else "all"
limit_type = limit_type if limit_type else "user"
if check_type not in ["all", "group", "private"]: if check_type not in ["all", "group", "private"]:
raise ValueError( raise ValueError(
f"{plugin} 添加cd限制错误,‘check_type‘ 必须为 'private'/'group'/'all'" f"{plugin} 添加cd限制错误,‘check_type‘ 必须为 'private'/'group'/'all'"
+4 -10
View File
@@ -33,7 +33,7 @@ class Plugins2countManager(StaticData):
status: Optional[bool] = True, status: Optional[bool] = True,
limit_type: Optional[str] = "user", limit_type: Optional[str] = "user",
rst: Optional[str] = None, rst: Optional[str] = None,
data_dict: Optional[dict] = None, **kwargs # 用于接收额外实参
): ):
""" """
添加插件调用 次数 限制 添加插件调用 次数 限制
@@ -42,16 +42,10 @@ class Plugins2countManager(StaticData):
:param status: 默认开关状态 :param status: 默认开关状态
:param limit_type: 限制类型 监听对象,以user_id或group_id作为键来限制,'user':用户id,'group':群id :param limit_type: 限制类型 监听对象,以user_id或group_id作为键来限制,'user':用户id,'group':群id
:param rst: 回复的话,为空则不回复 :param rst: 回复的话,为空则不回复
:param data_dict: 封装好的字典数据
""" """
if data_dict: max_count = max_count or 5
max_count = data_dict.get("max_count") status = status or True
status = data_dict.get("status") limit_type = limit_type or "user"
limit_type = data_dict.get("limit_type")
rst = data_dict.get("rst")
status = status if status is not None else True
limit_type = limit_type if limit_type else "user"
max_count = max_count if max_count is not None else 5
if limit_type not in ["user", "group"]: if limit_type not in ["user", "group"]:
raise ValueError(f"{plugin} 添加count限制错误,‘limit_type‘ 必须为 'user'/'group'") raise ValueError(f"{plugin} 添加count限制错误,‘limit_type‘ 必须为 'user'/'group'")
self._data[plugin] = { self._data[plugin] = {
+8 -17
View File
@@ -47,26 +47,17 @@ class Plugins2settingsManager(StaticData):
:param plugin_type: 插件类型 :param plugin_type: 插件类型
:param cost_gold: 需要消费的金币 :param cost_gold: 需要消费的金币
""" """
if kwargs: level = level or 5
level = kwargs.get("level") if kwargs.get("level") is not None else 5 cmd = cmd or []
default_status = ( cost_gold = cost_gold or 0
kwargs.get("default_status")
if kwargs.get("default_status") is not None
else True
)
limit_superuser = (
kwargs.get("limit_superuser")
if kwargs.get("limit_superuser") is not None
else False
)
cmd = kwargs.get("cmd") if kwargs.get("cmd") is not None else []
cost_gold = cost_gold if kwargs.get("cost_gold") else 0
self._data[plugin] = { self._data[plugin] = {
"level": level if level is not None else 5, "level": level if level is not None else 5,
"default_status": default_status if default_status is not None else True, "default_status": default_status if default_status is not None else True,
"limit_superuser": limit_superuser "limit_superuser": (
if limit_superuser is not None limit_superuser
else False, if limit_superuser is not None
else False
),
"cmd": cmd, "cmd": cmd,
"plugin_type": list( "plugin_type": list(
plugin_type if plugin_type is not None else ("normal",) plugin_type if plugin_type is not None else ("normal",)
+3 -1
View File
@@ -1,7 +1,7 @@
from datetime import datetime from datetime import datetime
from collections import defaultdict from collections import defaultdict
from nonebot import require from nonebot import require
from configs.config import SYSTEM_PROXY from configs.config import SYSTEM_PROXY, Config
from typing import List, Union, Optional, Type, Any from typing import List, Union, Optional, Type, Any
from nonebot.adapters.onebot.v11 import Bot, Message from nonebot.adapters.onebot.v11 import Bot, Message
from nonebot.matcher import matchers, Matcher from nonebot.matcher import matchers, Matcher
@@ -389,6 +389,8 @@ def change_pixiv_image_links(
url = img_sp[0] url = img_sp[0]
img_type = img_sp[1] img_type = img_sp[1]
url = url.replace("original", "master") + f"_master1200.{img_type}" url = url.replace("original", "master") + f"_master1200.{img_type}"
if not nginx_url:
nginx_url = Config.get_config("pixiv", "PIXIV_NGINX_URL")
if nginx_url: if nginx_url:
url = ( url = (
url.replace("i.pximg.net", nginx_url) url.replace("i.pximg.net", nginx_url)