Support inline use

This commit is contained in:
omg-xtao 2024-06-19 01:33:28 +08:00 committed by GitHub
parent b923f9bea3
commit 9c886fbbe6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 531 additions and 53 deletions

View File

@ -38,6 +38,8 @@ OWNER=0
# 文章推送群组 可选配置项 # 文章推送群组 可选配置项
# CHANNELS=[] # CHANNELS=[]
# 消息帮助频道 可选配置项
# CHANNELS_HELPER=0
# 是否允许机器人邀请到其他群 默认不允许 如果允许 可以允许全部人或有认证选项 可选配置项 # 是否允许机器人邀请到其他群 默认不允许 如果允许 可以允许全部人或有认证选项 可选配置项
# JOIN_GROUPS = "NO_ALLOW" # JOIN_GROUPS = "NO_ALLOW"

@ -1 +1 @@
Subproject commit c478924e064d9dff9a944107ce34ee639f3febf6 Subproject commit 9bce0f921f7400701edcf5191b97ce89e58007db

View File

@ -1,5 +1,5 @@
import asyncio import asyncio
from typing import Awaitable, Dict, List, cast from typing import Awaitable, Dict, List, cast, Tuple
from uuid import uuid4 from uuid import uuid4
from telegram import ( from telegram import (
@ -10,15 +10,22 @@ from telegram import (
InputTextMessageContent, InputTextMessageContent,
Update, Update,
InlineQueryResultsButton, InlineQueryResultsButton,
InlineKeyboardMarkup,
InlineKeyboardButton,
InlineQueryResultPhoto,
) )
from telegram.constants import ParseMode from telegram.constants import ParseMode
from telegram.error import BadRequest from telegram.error import BadRequest
from telegram.ext import CallbackContext from telegram.ext import CallbackContext, ContextTypes
from core.dependence.assets import AssetsService from core.dependence.assets import AssetsService
from core.plugin import Plugin, handler from core.plugin import Plugin, handler
from core.services.search.services import SearchServices from core.services.search.services import SearchServices
from core.services.wiki.services import WikiService from core.services.wiki.services import WikiService
from gram_core.config import config
from gram_core.plugin.methods.inline_use_data import IInlineUseData
from gram_core.services.cookies import CookiesService
from gram_core.services.players import PlayersService
from utils.log import logger from utils.log import logger
@ -30,6 +37,8 @@ class Inline(Plugin):
asset_service: AssetsService, asset_service: AssetsService,
search_service: SearchServices, search_service: SearchServices,
wiki_service: WikiService, wiki_service: WikiService,
cookies_service: CookiesService,
players_service: PlayersService,
): ):
self.asset_service = asset_service self.asset_service = asset_service
self.wiki_service = wiki_service self.wiki_service = wiki_service
@ -41,6 +50,11 @@ class Inline(Plugin):
self.relics_list: List[Dict[str, str]] = [] self.relics_list: List[Dict[str, str]] = []
self.refresh_task: List[Awaitable] = [] self.refresh_task: List[Awaitable] = []
self.search_service = search_service self.search_service = search_service
self.cookies_service = cookies_service
self.players_service = players_service
self.inline_use_data: List[IInlineUseData] = []
self.inline_use_data_map: Dict[str, IInlineUseData] = {}
self.img_url = "https://i.dawnlab.me/b1bdf9cc3061d254f038e557557694bc.jpg"
async def initialize(self): async def initialize(self):
async def task_light_cone(): async def task_light_cone():
@ -120,8 +134,113 @@ class Inline(Plugin):
self.refresh_task.append(asyncio.create_task(task_light_cone())) self.refresh_task.append(asyncio.create_task(task_light_cone()))
self.refresh_task.append(asyncio.create_task(task_relics())) self.refresh_task.append(asyncio.create_task(task_relics()))
async def init_inline_use_data(self):
if self.inline_use_data:
return
for _, instance in self.application.managers.plugins_map.items():
if _data := await instance.get_inline_use_data():
self.inline_use_data.extend(_data)
for data in self.inline_use_data:
self.inline_use_data_map[data.hash] = data
async def user_base_data(self, user_id: int, player_id: int, offset: int) -> Tuple[int, bool, bool]:
uid, has_cookie, has_player = 0, False, False
player = await self.players_service.get_player(user_id, None, player_id, offset)
if player is not None:
uid = player.player_id
has_player = True
if player.account_id is not None:
cookie_model = await self.cookies_service.get(player.user_id, player.account_id, player.region)
if cookie_model is not None:
has_cookie = True
return uid, has_cookie, has_player
def get_inline_use_button_data(self, user_id: int, uid: int, cookie: bool, player: bool) -> InlineKeyboardMarkup:
button_data = []
start = f"use_inline_func|{user_id}|{uid}"
for data in self.inline_use_data:
if data.is_show(cookie, player):
button_data.append(
InlineKeyboardButton(text=data.text, callback_data=data.get_button_callback_data(start))
)
# 每三个一行
button_data = [button_data[i : i + 3] for i in range(0, len(button_data), 3)]
return InlineKeyboardMarkup(button_data)
@handler.callback_query(pattern=r"^use_inline_func\|", block=False)
async def use_by_inline_query_callback(self, update: "Update", context: "ContextTypes.DEFAULT_TYPE") -> None:
user = update.effective_user
callback_query = update.callback_query
async def get_inline_query_callback(callback_query_data: str) -> Tuple[int, int, str]:
_data = callback_query_data.split("|")
_user_id = int(_data[1])
_uid = int(_data[2])
_hash = _data[3]
logger.debug("callback_query_data函数返回 user_id[%s] uid[%s] hash[%s]", _user_id, _uid, _hash)
return _user_id, _uid, _hash
user_id, uid, hash_str = await get_inline_query_callback(callback_query.data)
if user.id != user_id:
await callback_query.answer(text="这不是你的按钮!\n" + config.notice.user_mismatch, show_alert=True)
return
callback = self.inline_use_data_map.get(hash_str)
if callback is None:
await callback_query.answer(text="数据不存在,请重新生成按钮", show_alert=True)
return
IInlineUseData.set_uid_to_context(context, uid)
await callback.callback(update, context)
@handler.inline_query(pattern="^功能", block=False)
async def use_by_inline_query(self, update: "Update", _: "ContextTypes.DEFAULT_TYPE") -> None:
if not config.channels_helper:
logger.warning("未设置 helper 频道")
return
await self.init_inline_use_data()
user = update.effective_user
ilq = cast(InlineQuery, update.inline_query)
query = ilq.query
switch_pm_text = "需要帮助嘛?"
logger.info("用户 %s[%s] inline_query 功能查询\nquery[%s]", user.full_name, user.id, query)
user_id = user.id
uid, offset = self.get_real_uid_or_offset(update)
real_uid, has_cookie, has_player = await self.user_base_data(user_id, uid, offset)
button_data = self.get_inline_use_button_data(user_id, real_uid, has_cookie, has_player)
try:
await ilq.answer(
results=[
InlineQueryResultPhoto(
id=str(uuid4()),
photo_url=self.img_url,
thumbnail_url=self.img_url,
caption="请从下方按钮选择功能",
reply_markup=button_data,
)
],
cache_time=0,
auto_pagination=True,
button=InlineQueryResultsButton(
text=switch_pm_text,
start_parameter="inline_message",
),
)
except BadRequest as exc:
if "Query is too old" in exc.message: # 过时请求全部忽略
logger.warning("用户 %s[%s] inline_query 请求过时", user.full_name, user.id)
return
if "can't parse entities" not in exc.message:
raise exc
logger.warning("inline_query发生BadRequest错误", exc_info=exc)
await ilq.answer(
results=[],
button=InlineQueryResultsButton(
text="糟糕,发生错误了。",
start_parameter="inline_message",
),
)
@handler.inline_query(block=False) @handler.inline_query(block=False)
async def inline_query(self, update: Update, _: CallbackContext) -> None: async def z_inline_query(self, update: Update, _: CallbackContext) -> None:
user = update.effective_user user = update.effective_user
ilq = cast(InlineQuery, update.inline_query) ilq = cast(InlineQuery, update.inline_query)
query = ilq.query query = ilq.query
@ -146,8 +265,18 @@ class Inline(Plugin):
input_message_content=InputTextMessageContent(i[0]), input_message_content=InputTextMessageContent(i[0]),
) )
) )
results_list.append(
InlineQueryResultArticle(
id=str(uuid4()),
title="使用功能",
description="输入 功能 即可直接使用 BOT 功能",
input_message_content=InputTextMessageContent("Inline 模式下输入 功能 即可直接使用 BOT 功能"),
)
)
elif args[0] == "cookies_export": elif args[0] == "cookies_export":
return return
elif args[0] == "功能":
return
else: else:
if args[0] in [ if args[0] in [
"查看角色攻略列表并查询", "查看角色攻略列表并查询",

View File

@ -1,4 +1,4 @@
from typing import TYPE_CHECKING, Dict from typing import TYPE_CHECKING, Dict, List, Optional
from telegram.constants import ChatAction from telegram.constants import ChatAction
from telegram.ext import filters from telegram.ext import filters
@ -7,6 +7,7 @@ from simnet import Region
from core.services.self_help.services import ActionLogService from core.services.self_help.services import ActionLogService
from gram_core.plugin import Plugin, handler from gram_core.plugin import Plugin, handler
from gram_core.plugin.methods.inline_use_data import IInlineUseData
from gram_core.services.template.services import TemplateService from gram_core.services.template.services import TemplateService
from modules.action_log.client import ActionLogAnalyse from modules.action_log.client import ActionLogAnalyse
from plugins.tools.action_log_system import ActionLogSystem from plugins.tools.action_log_system import ActionLogSystem
@ -22,6 +23,8 @@ if TYPE_CHECKING:
from simnet import StarRailClient from simnet import StarRailClient
from gram_core.services.template.models import RenderResult
class NotSupport(Exception): class NotSupport(Exception):
"""不支持的服务器""" """不支持的服务器"""
@ -100,6 +103,15 @@ class ActionLogPlugins(Plugin):
data["background"] = (await self.phone_theme.get_phone_theme(player_id)).as_uri() data["background"] = (await self.phone_theme.get_phone_theme(player_id)).as_uri()
return data return data
async def render(self, client: "StarRailClient") -> "RenderResult":
data = await self.get_render_data(client.player_id)
return await self.template_service.render(
"starrail/action_log/action_log.html",
await self.add_theme_data(data, client.player_id),
full_page=True,
query_selector=".container",
)
@handler.command(command="action_log", cookie=True, block=False) @handler.command(command="action_log", cookie=True, block=False)
async def action_log(self, update: "Update", _: "ContextTypes.DEFAULT_TYPE") -> None: async def action_log(self, update: "Update", _: "ContextTypes.DEFAULT_TYPE") -> None:
user_id = await self.get_real_user_id(update) user_id = await self.get_real_user_id(update)
@ -110,16 +122,37 @@ class ActionLogPlugins(Plugin):
try: try:
async with self.helper.genshin(user_id, player_id=uid, offset=offset) as client: async with self.helper.genshin(user_id, player_id=uid, offset=offset) as client:
client: "StarRailClient" client: "StarRailClient"
data = await self.get_render_data(client.player_id) render = await self.render(client)
render = await self.template_service.render(
"starrail/action_log/action_log.html",
await self.add_theme_data(data, client.player_id),
full_page=True,
query_selector=".container",
)
await render.reply_photo(message) await render.reply_photo(message)
except NotSupport as e: except NotSupport as e:
msg = await message.reply_text(e.msg) msg = await message.reply_text(e.msg)
if filters.ChatType.GROUPS.filter(message): if filters.ChatType.GROUPS.filter(message):
self.add_delete_message_job(message, delay=60) self.add_delete_message_job(message, delay=60)
self.add_delete_message_job(msg, delay=60) self.add_delete_message_job(msg, delay=60)
async def action_log_use_by_inline(self, update: "Update", context: "ContextTypes.DEFAULT_TYPE") -> None:
callback_query = update.callback_query
user = update.effective_user
user_id = user.id
uid = IInlineUseData.get_uid_from_context(context)
self.log_user(update, logger.info, "查询登录记录")
try:
async with self.helper.genshin(user_id, player_id=uid) as client:
client: "StarRailClient"
render = await self.render(client)
except NotSupport as e:
await callback_query.answer(e.msg, show_alert=True)
return
await render.edit_inline_media(callback_query)
async def get_inline_use_data(self) -> List[Optional[IInlineUseData]]:
return [
IInlineUseData(
text="登录统计",
hash="action_log",
callback=self.action_log_use_by_inline,
cookie=True,
player=True,
)
]

View File

@ -13,6 +13,7 @@ from core.services.cookies import CookiesService
from core.services.template.models import FileType from core.services.template.models import FileType
from core.services.template.services import TemplateService from core.services.template.services import TemplateService
from core.services.wiki.services import WikiService from core.services.wiki.services import WikiService
from gram_core.plugin.methods.inline_use_data import IInlineUseData
from gram_core.services.template.models import RenderGroupResult from gram_core.services.template.models import RenderGroupResult
from plugins.tools.genshin import GenshinHelper, CharacterDetails from plugins.tools.genshin import GenshinHelper, CharacterDetails
from plugins.tools.head_icon import HeadIconService from plugins.tools.head_icon import HeadIconService
@ -185,6 +186,25 @@ class AvatarListPlugin(Plugin):
tasks = [render_task(i * image_count, c) for i, c in enumerate(avatar_datas_group)] tasks = [render_task(i * image_count, c) for i, c in enumerate(avatar_datas_group)]
return await asyncio.gather(*tasks) return await asyncio.gather(*tasks)
async def render(self, client: "StarRailClient", all_avatars: bool = False) -> List["RenderResult"]:
characters: List["StarRailDetailCharacter"] = await self.get_avatars_data(client)
record_card = await client.get_record_card()
nickname = record_card.nickname
has_more = (not all_avatars) and len(characters) > MAX_AVATAR_COUNT
if has_more:
characters = characters[:MAX_AVATAR_COUNT]
avatar_datas = await self.get_final_data(characters, client)
base_render_data = {
"uid": mask_number(client.player_id), # 玩家uid
"nickname": nickname, # 玩家昵称
"has_more": has_more, # 是否显示了全部角色
"avatar": (await self.head_icon.get_head_icon(client.player_id)).as_uri(),
"background": (await self.phone_theme.get_phone_theme(client.player_id)).as_uri(),
}
return await self.avatar_list_render(base_render_data, avatar_datas, has_more)
@handler.command("avatars", cookie=True, block=False) @handler.command("avatars", cookie=True, block=False)
@handler.message(filters.Regex(r"^(全部)?练度统计$"), cookie=True, block=False) @handler.message(filters.Regex(r"^(全部)?练度统计$"), cookie=True, block=False)
async def avatar_list(self, update: "Update", _: "ContextTypes.DEFAULT_TYPE"): async def avatar_list(self, update: "Update", _: "ContextTypes.DEFAULT_TYPE"):
@ -198,23 +218,7 @@ class AvatarListPlugin(Plugin):
async with self.helper.genshin(user_id, player_id=uid, offset=offset) as client: async with self.helper.genshin(user_id, player_id=uid, offset=offset) as client:
notice = await message.reply_text("彦卿需要收集整理数据,还请耐心等待哦~") notice = await message.reply_text("彦卿需要收集整理数据,还请耐心等待哦~")
self.add_delete_message_job(notice, delay=60) self.add_delete_message_job(notice, delay=60)
characters: List["StarRailDetailCharacter"] = await self.get_avatars_data(client) images = await self.render(client, all_avatars)
record_card = await client.get_record_card()
nickname = record_card.nickname
has_more = (not all_avatars) and len(characters) > MAX_AVATAR_COUNT
if has_more:
characters = characters[:MAX_AVATAR_COUNT]
avatar_datas = await self.get_final_data(characters, client)
base_render_data = {
"uid": mask_number(client.player_id), # 玩家uid
"nickname": nickname, # 玩家昵称
"has_more": has_more, # 是否显示了全部角色
"avatar": (await self.head_icon.get_head_icon(client.player_id)).as_uri(),
"background": (await self.phone_theme.get_phone_theme(client.player_id)).as_uri(),
}
images = await self.avatar_list_render(base_render_data, avatar_datas, has_more)
for group in ArkoWrapper(images).group(10): # 每 10 张图片分一个组 for group in ArkoWrapper(images).group(10): # 每 10 张图片分一个组
await RenderGroupResult(results=group).reply_media_group(message, write_timeout=60) await RenderGroupResult(results=group).reply_media_group(message, write_timeout=60)
@ -225,3 +229,27 @@ class AvatarListPlugin(Plugin):
"[bold]练度统计[/bold]发送图片成功", "[bold]练度统计[/bold]发送图片成功",
extra={"markup": True}, extra={"markup": True},
) )
async def avatar_list_use_by_inline(self, update: "Update", context: "ContextTypes.DEFAULT_TYPE") -> None:
callback_query = update.callback_query
user = update.effective_user
user_id = user.id
uid = IInlineUseData.get_uid_from_context(context)
self.log_user(update, logger.info, "查询练度统计")
async with self.helper.genshin(user_id, player_id=uid) as client:
client: "StarRailClient"
images = await self.render(client)
render = images[0]
await render.edit_inline_media(callback_query)
async def get_inline_use_data(self) -> List[Optional[IInlineUseData]]:
return [
IInlineUseData(
text="练度统计",
hash="avatar_list",
callback=self.avatar_list_use_by_inline,
cookie=True,
player=True,
)
]

View File

@ -3,7 +3,7 @@
import asyncio import asyncio
import math import math
import re import re
from functools import lru_cache from functools import lru_cache, partial
from typing import Any, List, Optional, Tuple, Union, TYPE_CHECKING from typing import Any, List, Optional, Tuple, Union, TYPE_CHECKING
from arkowrapper import ArkoWrapper from arkowrapper import ArkoWrapper
@ -21,6 +21,7 @@ from core.services.template.models import RenderGroupResult, RenderResult
from core.services.template.services import TemplateService from core.services.template.services import TemplateService
from gram_core.config import config from gram_core.config import config
from gram_core.dependence.redisdb import RedisDB from gram_core.dependence.redisdb import RedisDB
from gram_core.plugin.methods.inline_use_data import IInlineUseData
from plugins.tools.genshin import GenshinHelper from plugins.tools.genshin import GenshinHelper
from utils.enkanetwork import RedisCache from utils.enkanetwork import RedisCache
from utils.log import logger from utils.log import logger
@ -584,3 +585,45 @@ class ChallengePlugin(Plugin):
await self.get_abyss_history_floor(update, data_id, detail) await self.get_abyss_history_floor(update, data_id, detail)
return return
await self.get_abyss_history_season(update, data_id) await self.get_abyss_history_season(update, data_id)
async def abyss_use_by_inline(self, update: "Update", context: "ContextTypes.DEFAULT_TYPE", previous: bool):
callback_query = update.callback_query
user = update.effective_user
user_id = user.id
uid = IInlineUseData.get_uid_from_context(context)
self.log_user(update, logger.info, "查询混沌回忆挑战总览数据 previous[%s]", previous)
notice = None
try:
async with self.helper.genshin_or_public(user_id, uid=uid) as client:
if not client.public:
await client.get_record_cards()
abyss_data = await self.get_rendered_pic_data(client, uid, previous)
images = await self.get_rendered_pic(abyss_data, uid, 0, False)
image = images[0]
except AbyssUnlocked: # 若深渊未解锁
notice = "还未解锁混沌回忆哦~"
except TooManyRequestPublicCookies:
notice = "查询次数太多,请您稍后重试"
if notice:
await callback_query.answer(notice, show_alert=True)
return
await image.edit_inline_media(callback_query)
async def get_inline_use_data(self) -> List[Optional[IInlineUseData]]:
return [
IInlineUseData(
text="本期混沌回忆总览",
hash="challenge_current",
callback=partial(self.abyss_use_by_inline, previous=False),
player=True,
),
IInlineUseData(
text="上期混沌回忆总览",
hash="challenge_previous",
callback=partial(self.abyss_use_by_inline, previous=True),
player=True,
),
]

View File

@ -3,7 +3,7 @@
import asyncio import asyncio
import math import math
import re import re
from functools import lru_cache from functools import lru_cache, partial
from typing import Any, List, Optional, Tuple, Union, TYPE_CHECKING from typing import Any, List, Optional, Tuple, Union, TYPE_CHECKING
from arkowrapper import ArkoWrapper from arkowrapper import ArkoWrapper
@ -21,6 +21,7 @@ from core.services.template.models import RenderGroupResult, RenderResult
from core.services.template.services import TemplateService from core.services.template.services import TemplateService
from gram_core.config import config from gram_core.config import config
from gram_core.dependence.redisdb import RedisDB from gram_core.dependence.redisdb import RedisDB
from gram_core.plugin.methods.inline_use_data import IInlineUseData
from plugins.tools.genshin import GenshinHelper from plugins.tools.genshin import GenshinHelper
from utils.enkanetwork import RedisCache from utils.enkanetwork import RedisCache
from utils.log import logger from utils.log import logger
@ -600,3 +601,47 @@ class ChallengeStoryPlugin(Plugin):
await self.get_challenge_story_history_floor(update, data_id, detail) await self.get_challenge_story_history_floor(update, data_id, detail)
return return
await self.get_challenge_story_history_season(update, data_id) await self.get_challenge_story_history_season(update, data_id)
async def challenge_story_use_by_inline(
self, update: "Update", context: "ContextTypes.DEFAULT_TYPE", previous: bool
):
callback_query = update.callback_query
user = update.effective_user
user_id = user.id
uid = IInlineUseData.get_uid_from_context(context)
self.log_user(update, logger.info, "查询虚构叙事挑战总览数据 previous[%s]", previous)
notice = None
try:
async with self.helper.genshin_or_public(user_id, uid=uid) as client:
if not client.public:
await client.get_record_cards()
abyss_data, season = await self.get_rendered_pic_data(client, uid, previous)
images = await self.get_rendered_pic(abyss_data, season, uid, 0, False)
image = images[0]
except AbyssUnlocked: # 若深渊未解锁
notice = "还未解锁虚构叙事哦~"
except TooManyRequestPublicCookies:
notice = "查询次数太多,请您稍后重试"
if notice:
await callback_query.answer(notice, show_alert=True)
return
await image.edit_inline_media(callback_query)
async def get_inline_use_data(self) -> List[Optional[IInlineUseData]]:
return [
IInlineUseData(
text="本期虚构叙事总览",
hash="challenge_story_current",
callback=partial(self.challenge_story_use_by_inline, previous=False),
player=True,
),
IInlineUseData(
text="上期虚构叙事总览",
hash="challenge_story_previous",
callback=partial(self.challenge_story_use_by_inline, previous=True),
player=True,
),
]

View File

@ -1,21 +1,24 @@
import datetime import datetime
from datetime import datetime from datetime import datetime
from typing import Optional, TYPE_CHECKING from typing import Optional, TYPE_CHECKING, List
from simnet.errors import DataNotPublic from simnet.errors import DataNotPublic
from telegram import Update, InlineKeyboardMarkup, InlineKeyboardButton from telegram import InlineKeyboardMarkup, InlineKeyboardButton
from telegram.constants import ChatAction from telegram.constants import ChatAction
from telegram.ext import ConversationHandler, filters, CallbackContext from telegram.ext import ConversationHandler, filters
from telegram.helpers import create_deep_linked_url from telegram.helpers import create_deep_linked_url
from core.plugin import Plugin, handler from core.plugin import Plugin, handler
from core.services.template.models import RenderResult from core.services.template.models import RenderResult
from core.services.template.services import TemplateService from core.services.template.services import TemplateService
from gram_core.plugin.methods.inline_use_data import IInlineUseData
from plugins.tools.genshin import GenshinHelper from plugins.tools.genshin import GenshinHelper
from utils.log import logger from utils.log import logger
from utils.uid import mask_number from utils.uid import mask_number
if TYPE_CHECKING: if TYPE_CHECKING:
from telegram import Update
from telegram.ext import ContextTypes
from simnet import StarRailClient from simnet import StarRailClient
@ -89,7 +92,7 @@ class DailyNotePlugin(Plugin):
@handler.command("dailynote", cookie=True, block=False) @handler.command("dailynote", cookie=True, block=False)
@handler.message(filters.Regex("^当前状态(.*)"), cookie=True, block=False) @handler.message(filters.Regex("^当前状态(.*)"), cookie=True, block=False)
async def command_start(self, update: Update, context: CallbackContext) -> Optional[int]: async def command_start(self, update: "Update", context: "ContextTypes.DEFAULT_TYPE") -> Optional[int]:
user_id = await self.get_real_user_id(update) user_id = await self.get_real_user_id(update)
message = update.effective_message message = update.effective_message
uid, offset = self.get_real_uid_or_offset(update) uid, offset = self.get_real_uid_or_offset(update)
@ -113,3 +116,33 @@ class DailyNotePlugin(Plugin):
filename=f"{client.player_id}.png", filename=f"{client.player_id}.png",
reply_markup=self.get_task_button(context.bot.username), reply_markup=self.get_task_button(context.bot.username),
) )
async def daily_note_use_by_inline(self, update: "Update", context: "ContextTypes.DEFAULT_TYPE"):
callback_query = update.callback_query
user = update.effective_user
user_id = user.id
uid = IInlineUseData.get_uid_from_context(context)
self.log_user(update, logger.info, "每日便签命令请求")
try:
async with self.helper.genshin(user_id, player_id=uid) as client:
render_result = await self._get_daily_note(client)
except DataNotPublic:
await callback_query.answer(
"查询失败惹,可能是便签功能被禁用了?请尝试通过米游社或者 hoyolab 获取一次便签信息后重试。",
show_alert=True,
)
return ConversationHandler.END
await render_result.edit_inline_media(callback_query)
async def get_inline_use_data(self) -> List[Optional[IInlineUseData]]:
return [
IInlineUseData(
text="当前状态",
hash="dailynote",
callback=self.daily_note_use_by_inline,
cookie=True,
player=True,
)
]

View File

@ -1,13 +1,19 @@
from typing import List, Optional, TYPE_CHECKING
from telegram import Update from telegram import Update
from telegram.constants import ChatAction from telegram.constants import ChatAction
from telegram.ext import CallbackContext, filters from telegram.ext import CallbackContext, filters
from core.plugin import Plugin, handler from core.plugin import Plugin, handler
from core.services.template.services import TemplateService from core.services.template.services import TemplateService
from gram_core.plugin.methods.inline_use_data import IInlineUseData
from utils.log import logger from utils.log import logger
__all__ = ("HelpPlugin",) __all__ = ("HelpPlugin",)
if TYPE_CHECKING:
from gram_core.services.template.models import RenderResult
class HelpPlugin(Plugin): class HelpPlugin(Plugin):
def __init__(self, template_service: TemplateService = None): def __init__(self, template_service: TemplateService = None):
@ -15,17 +21,35 @@ class HelpPlugin(Plugin):
raise ModuleNotFoundError raise ModuleNotFoundError
self.template_service = template_service self.template_service = template_service
async def get_help_render(self) -> "RenderResult":
return await self.template_service.render(
"bot/help/help.html",
{"bot_username": self.application.bot.username},
{"width": 1280, "height": 900},
ttl=30 * 24 * 60 * 60,
)
@handler.command(command="help", block=False) @handler.command(command="help", block=False)
@handler.command(command="start", filters=filters.Regex("inline_message$"), block=False) @handler.command(command="start", filters=filters.Regex("inline_message$"), block=False)
async def start(self, update: Update, _: CallbackContext): async def start(self, update: Update, _: CallbackContext):
message = update.effective_message message = update.effective_message
self.log_user(update, logger.info, "发出help命令") self.log_user(update, logger.info, "发出help命令")
await message.reply_chat_action(ChatAction.TYPING) await message.reply_chat_action(ChatAction.TYPING)
render_result = await self.template_service.render( render_result = await self.get_help_render()
"bot/help/help.html",
{"bot_username": self.application.bot.username},
{"width": 1280, "height": 900},
ttl=30 * 24 * 60 * 60,
)
await message.reply_chat_action(ChatAction.UPLOAD_PHOTO) await message.reply_chat_action(ChatAction.UPLOAD_PHOTO)
await render_result.reply_photo(message, filename="help.png") await render_result.reply_photo(message, filename="help.png")
async def start_use_by_inline(self, update: Update, _: CallbackContext):
callback_query = update.callback_query
self.log_user(update, logger.info, "发出help命令")
render_result = await self.get_help_render()
await render_result.edit_inline_media(callback_query)
async def get_inline_use_data(self) -> List[Optional[IInlineUseData]]:
return [
IInlineUseData(
text="帮助",
hash="help",
callback=self.start_use_by_inline,
)
]

View File

@ -2,7 +2,7 @@ import math
import os import os
import re import re
from datetime import datetime, timedelta from datetime import datetime, timedelta
from typing import TYPE_CHECKING, List, Tuple from typing import TYPE_CHECKING, List, Tuple, Optional
from simnet.errors import BadRequest as SimnetBadRequest, DataNotPublic from simnet.errors import BadRequest as SimnetBadRequest, DataNotPublic
from simnet.models.starrail.diary import StarRailDiary from simnet.models.starrail.diary import StarRailDiary
@ -18,6 +18,7 @@ from core.services.template.models import RenderResult
from core.services.template.services import TemplateService from core.services.template.services import TemplateService
from gram_core.config import config from gram_core.config import config
from gram_core.dependence.redisdb import RedisDB from gram_core.dependence.redisdb import RedisDB
from gram_core.plugin.methods.inline_use_data import IInlineUseData
from plugins.tools.genshin import GenshinHelper from plugins.tools.genshin import GenshinHelper
from utils.enkanetwork import RedisCache from utils.enkanetwork import RedisCache
from utils.log import logger from utils.log import logger
@ -313,3 +314,44 @@ class LedgerPlugin(Plugin):
await callback_query.answer("正在渲染图片中 请稍等 请不要重复点击按钮") await callback_query.answer("正在渲染图片中 请稍等 请不要重复点击按钮")
render = await self._start_get_ledger_render(user_id, HistoryDataLedger.from_data(data).diary_data) render = await self._start_get_ledger_render(user_id, HistoryDataLedger.from_data(data).diary_data)
await render.edit_media(message) await render.edit_media(message)
async def ledger_use_by_inline(self, update: "Update", context: "ContextTypes.DEFAULT_TYPE"):
callback_query = update.callback_query
user = update.effective_user
user_id = user.id
uid = IInlineUseData.get_uid_from_context(context)
self.log_user(update, logger.info, "查询开拓月历")
now = datetime.now()
now_time = (now - timedelta(days=1)) if now.day == 1 and now.hour <= 4 else now
year, month = now_time.year, now_time.month
try:
async with self.helper.genshin(user_id, player_id=uid) as client:
render_result = await self._start_get_ledger(client, year, month)
except DataNotPublic:
await callback_query.answer(
"查询失败惹,可能是开拓月历功能被禁用了?请先通过米游社或者 hoyolab 获取一次开拓月历后重试。",
show_alert=True,
)
return
except SimnetBadRequest as exc:
if exc.ret_code == -120:
await callback_query.answer(
"当前角色开拓等级不足,暂时无法获取信息",
show_alert=True,
)
return
raise exc
await render_result.edit_inline_media(callback_query)
async def get_inline_use_data(self) -> List[Optional[IInlineUseData]]:
return [
IInlineUseData(
text="当月开拓月历",
hash="ledger",
callback=self.ledger_use_by_inline,
cookie=True,
player=True,
)
]

View File

@ -12,6 +12,8 @@ from core.services.cookies.error import TooManyRequestPublicCookies
from core.services.players.services import PlayerInfoService from core.services.players.services import PlayerInfoService
from core.services.template.models import RenderResult from core.services.template.models import RenderResult
from core.services.template.services import TemplateService from core.services.template.services import TemplateService
from gram_core.config import config
from gram_core.plugin.methods.inline_use_data import IInlineUseData
from plugins.tools.genshin import GenshinHelper from plugins.tools.genshin import GenshinHelper
from plugins.tools.head_icon import HeadIconService from plugins.tools.head_icon import HeadIconService
from plugins.tools.phone_theme import PhoneThemeService from plugins.tools.phone_theme import PhoneThemeService
@ -142,3 +144,41 @@ class PlayerStatsPlugins(Plugin):
return return
await self.phone_theme.set_to_cache(player_id, phone_theme_id) await self.phone_theme.set_to_cache(player_id, phone_theme_id)
await self.player_info_service.set_name_card(player_id, phone_theme_id) await self.player_info_service.set_name_card(player_id, phone_theme_id)
async def stats_use_by_inline(self, update: "Update", context: "ContextTypes.DEFAULT_TYPE"):
callback_query = update.callback_query
user = update.effective_user
user_id = user.id
uid = IInlineUseData.get_uid_from_context(context)
self.log_user(update, logger.info, "查询游戏用户命令请求")
notice = None
try:
async with self.helper.genshin_or_public(user_id, uid=uid) as client:
if not client.public:
await client.get_record_cards()
render_result = await self.render(client, client.player_id)
except TooManyRequestPublicCookies:
notice = "用户查询次数过多 请稍后重试"
except AttributeError as exc:
logger.error("角色数据有误")
logger.exception(exc)
notice = f"角色数据有误 估计是{config.notice.bot_name}晕了"
except ValueError as exc:
logger.warning("获取 uid 发生错误! 错误信息为 %s", str(exc))
notice = "UID 内部错误"
if notice:
await callback_query.answer(notice, show_alert=True)
return
await render_result.edit_inline_media(callback_query)
async def get_inline_use_data(self) -> List[Optional[IInlineUseData]]:
return [
IInlineUseData(
text="玩家统计",
hash="stats",
callback=self.stats_use_by_inline,
player=True,
),
]

View File

@ -1,10 +1,11 @@
from functools import partial
from io import BytesIO from io import BytesIO
from typing import Optional, TYPE_CHECKING, List, Union, Tuple, Dict from typing import Optional, TYPE_CHECKING, List, Union, Tuple, Dict
from simnet.models.starrail.wish import StarRailBannerType from simnet.models.starrail.wish import StarRailBannerType
from telegram import Document, InlineKeyboardButton, InlineKeyboardMarkup, Message, Update, User from telegram import InlineKeyboardButton, InlineKeyboardMarkup
from telegram.constants import ChatAction from telegram.constants import ChatAction
from telegram.ext import CallbackContext, ConversationHandler, filters from telegram.ext import ConversationHandler, filters
from telegram.helpers import create_deep_linked_url from telegram.helpers import create_deep_linked_url
from core.dependence.assets import AssetsService from core.dependence.assets import AssetsService
@ -14,6 +15,7 @@ from core.services.players import PlayersService
from core.services.template.models import FileType from core.services.template.models import FileType
from core.services.template.services import TemplateService from core.services.template.services import TemplateService
from gram_core.config import config from gram_core.config import config
from gram_core.plugin.methods.inline_use_data import IInlineUseData
from modules.gacha_log.const import SRGF_VERSION, GACHA_TYPE_LIST_REVERSE from modules.gacha_log.const import SRGF_VERSION, GACHA_TYPE_LIST_REVERSE
from modules.gacha_log.error import ( from modules.gacha_log.error import (
GachaLogAccountNotFound, GachaLogAccountNotFound,
@ -80,7 +82,7 @@ class WishLogPlugin(Plugin.Conversation):
return player.player_id return player.player_id
async def _refresh_user_data( async def _refresh_user_data(
self, user: User, player_id: int, data: dict = None, authkey: str = None, verify_uid: bool = True self, user: "User", player_id: int, data: dict = None, authkey: str = None, verify_uid: bool = True
) -> str: ) -> str:
"""刷新用户数据 """刷新用户数据
:param user: 用户 :param user: 用户
@ -112,7 +114,9 @@ class WishLogPlugin(Plugin.Conversation):
logger.info("未查询到用户 %s[%s] 所绑定的账号信息", user.full_name, user.id) logger.info("未查询到用户 %s[%s] 所绑定的账号信息", user.full_name, user.id)
return config.notice.user_not_found return config.notice.user_not_found
async def import_from_file(self, user: User, player_id: int, message: Message, document: Document = None) -> None: async def import_from_file(
self, user: "User", player_id: int, message: "Message", document: "Document" = None
) -> None:
if not document: if not document:
document = message.document document = message.document
# TODO: 使用 mimetype 判断文件类型 # TODO: 使用 mimetype 判断文件类型
@ -189,7 +193,7 @@ class WishLogPlugin(Plugin.Conversation):
@conversation.state(state=INPUT_URL) @conversation.state(state=INPUT_URL)
@handler.message(filters=~filters.COMMAND, block=False) @handler.message(filters=~filters.COMMAND, block=False)
async def import_data_from_message(self, update: Update, context: CallbackContext) -> int: async def import_data_from_message(self, update: "Update", context: "ContextTypes.DEFAULT_TYPE") -> int:
message = update.effective_message message = update.effective_message
user = update.effective_user user = update.effective_user
player_id = context.chat_data["uid"] player_id = context.chat_data["uid"]
@ -209,7 +213,7 @@ class WishLogPlugin(Plugin.Conversation):
@conversation.entry_point @conversation.entry_point
@handler.command(command="warp_log_delete", filters=filters.ChatType.PRIVATE, block=False) @handler.command(command="warp_log_delete", filters=filters.ChatType.PRIVATE, block=False)
@handler.message(filters=filters.Regex("^删除跃迁记录(.*)") & filters.ChatType.PRIVATE, block=False) @handler.message(filters=filters.Regex("^删除跃迁记录(.*)") & filters.ChatType.PRIVATE, block=False)
async def command_start_delete(self, update: Update, context: CallbackContext) -> int: async def command_start_delete(self, update: "Update", context: "ContextTypes.DEFAULT_TYPE") -> int:
uid, offset = self.get_real_uid_or_offset(update) uid, offset = self.get_real_uid_or_offset(update)
message = update.effective_message message = update.effective_message
user = update.effective_user user = update.effective_user
@ -232,7 +236,7 @@ class WishLogPlugin(Plugin.Conversation):
@conversation.state(state=CONFIRM_DELETE) @conversation.state(state=CONFIRM_DELETE)
@handler.message(filters=filters.TEXT & ~filters.COMMAND, block=False) @handler.message(filters=filters.TEXT & ~filters.COMMAND, block=False)
async def command_confirm_delete(self, update: Update, context: CallbackContext) -> int: async def command_confirm_delete(self, update: "Update", context: "ContextTypes.DEFAULT_TYPE") -> int:
message = update.effective_message message = update.effective_message
user = update.effective_user user = update.effective_user
if message.text == "确定": if message.text == "确定":
@ -243,7 +247,7 @@ class WishLogPlugin(Plugin.Conversation):
return ConversationHandler.END return ConversationHandler.END
@handler.command(command="warp_log_force_delete", block=False, admin=True) @handler.command(command="warp_log_force_delete", block=False, admin=True)
async def command_warp_log_force_delete(self, update: Update, context: CallbackContext): async def command_warp_log_force_delete(self, update: "Update", context: "ContextTypes.DEFAULT_TYPE"):
uid, offset = self.get_real_uid_or_offset(update) uid, offset = self.get_real_uid_or_offset(update)
message = update.effective_message message = update.effective_message
args = self.get_args(context) args = self.get_args(context)
@ -270,7 +274,7 @@ class WishLogPlugin(Plugin.Conversation):
@handler.command(command="warp_log_export", filters=filters.ChatType.PRIVATE, block=False) @handler.command(command="warp_log_export", filters=filters.ChatType.PRIVATE, block=False)
@handler.message(filters=filters.Regex("^导出跃迁记录(.*)") & filters.ChatType.PRIVATE, block=False) @handler.message(filters=filters.Regex("^导出跃迁记录(.*)") & filters.ChatType.PRIVATE, block=False)
async def command_start_export(self, update: Update, context: CallbackContext) -> None: async def command_start_export(self, update: "Update", context: "ContextTypes.DEFAULT_TYPE") -> None:
uid, offset = self.get_real_uid_or_offset(update) uid, offset = self.get_real_uid_or_offset(update)
message = update.effective_message message = update.effective_message
user = update.effective_user user = update.effective_user
@ -373,7 +377,7 @@ class WishLogPlugin(Plugin.Conversation):
@handler.command(command="warp_log", block=False) @handler.command(command="warp_log", block=False)
@handler.message(filters=filters.Regex("^跃迁记录?(光锥|角色|常驻|新手)$"), block=False) @handler.message(filters=filters.Regex("^跃迁记录?(光锥|角色|常驻|新手)$"), block=False)
async def command_start_analysis(self, update: Update, context: CallbackContext) -> None: async def command_start_analysis(self, update: "Update", context: "ContextTypes.DEFAULT_TYPE") -> None:
user_id = await self.get_real_user_id(update) user_id = await self.get_real_user_id(update)
uid, offset = self.get_real_uid_or_offset(update) uid, offset = self.get_real_uid_or_offset(update)
message = update.effective_message message = update.effective_message
@ -513,3 +517,44 @@ class WishLogPlugin(Plugin.Conversation):
old_user_id: int, new_user_id: int, old_players: List["Player"] old_user_id: int, new_user_id: int, old_players: List["Player"]
) -> Optional[GachaLogMigrate]: ) -> Optional[GachaLogMigrate]:
return await GachaLogMigrate.create(old_user_id, new_user_id, old_players) return await GachaLogMigrate.create(old_user_id, new_user_id, old_players)
async def wish_log_use_by_inline(
self, update: "Update", context: "ContextTypes.DEFAULT_TYPE", pool_type: "StarRailBannerType"
):
callback_query = update.callback_query
user = update.effective_user
user_id = user.id
uid = IInlineUseData.get_uid_from_context(context)
self.log_user(update, logger.info, "跃迁记录命令请求 || 参数 %s", pool_type.name if pool_type else None)
notice = None
try:
render_result = await self.rander_wish_log_analysis(user_id, uid, pool_type)
if isinstance(render_result, str):
notice = render_result
else:
await render_result.edit_inline_media(callback_query, filename="跃迁统计.png")
except GachaLogNotFound:
self.log_user(update, logger.info, "未找到跃迁记录")
notice = "未找到跃迁记录"
if notice:
await callback_query.answer(notice, show_alert=True)
async def get_inline_use_data(self) -> List[Optional[IInlineUseData]]:
types = {
"角色": StarRailBannerType.CHARACTER,
"武器": StarRailBannerType.WEAPON,
"常驻": StarRailBannerType.STANDARD,
"新手": StarRailBannerType.NOVICE,
}
data = []
for k, v in types.items():
data.append(
IInlineUseData(
text=f"{k}跃迁",
hash=f"wish_log_{v.value}",
callback=partial(self.wish_log_use_by_inline, pool_type=v),
player=True,
)
)
return data

View File

@ -111,7 +111,7 @@ class ErrorHandler(Plugin):
else: else:
buttons = ReplyKeyboardRemove() buttons = ReplyKeyboardRemove()
if chat.id == user.id: if (not chat) or chat.id == user.id:
logger.info("用户 %s[%s] 尝试通知错误信息[%s]", user.full_name, user.id, content) logger.info("用户 %s[%s] 尝试通知错误信息[%s]", user.full_name, user.id, content)
else: else:
self.log_user(update, logger.info, "尝试通知在 %s[%s] 的错误信息[%s]", chat.title, chat.id, content) self.log_user(update, logger.info, "尝试通知在 %s[%s] 的错误信息[%s]", chat.title, chat.id, content)

14
utils/patch/telegram.py Normal file
View File

@ -0,0 +1,14 @@
import telegram
from utils.patch.methods import patch, patchable
# https://github.com/python-telegram-bot/python-telegram-bot/issues/4295
@patch(telegram.Bot)
class Bot:
@patchable
def _effective_inline_results(self, results, next_offset=None, current_offset=None):
if current_offset == "[]":
current_offset = 50
return self.old__effective_inline_results(results, next_offset, current_offset)