使用修饰器加载插件

This commit is contained in:
洛水.山岭居室 2022-06-26 14:17:43 +08:00 committed by GitHub
parent 0d10707812
commit cb2ac781a4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 214 additions and 75 deletions

View File

@ -2,25 +2,14 @@ from typing import Optional
from telegram.ext import CommandHandler, MessageHandler, filters, CallbackQueryHandler, InlineQueryHandler, Application
from plugins.admin import Admin
from plugins.artifact_rate import ArtifactRate
from logger import Log
from manager import PluginsManager
from plugins.auth import Auth
from plugins.base import NewChatMembersHandler
from plugins.cookies import Cookies
from plugins.daily_note import DailyNote
from plugins.errorhandler import error_handler
from plugins.gacha import Gacha
from plugins.help import Help
from plugins.inline import Inline
from plugins.post import Post
from plugins.quiz import Quiz
from plugins.sign import Sign
from plugins.start import start, ping, reply_keyboard_remove, unknown_command
from plugins.strategy import Strategy
from plugins.uid import Uid
from plugins.weapon import Weapon
from plugins.wiki import Wiki
from plugins.ledger import Ledger
from service import BaseService
@ -43,51 +32,39 @@ def register_handlers(application: Application, service: BaseService):
application.add_handler(CallbackQueryHandler(handler, pattern=query, block=block))
# 初始化
Log.info("正在加载插件管理器")
plugins_manager = PluginsManager()
plugins_manager.refresh_list("./plugins/*")
# 忽略内置模块
plugins_manager.add_exclude(["help", "start", "base", "auth", "inline", "errorhandler"])
Log.info("加载插件管理器正在加载插件")
plugins_manager.import_module()
plugins_manager.add_handler(application, (service,))
Log.info("正在加载内置插件")
plugins_help = Help()
inline = Inline(service)
auth = Auth(service)
gacha = Gacha(service)
admin = Admin(service)
weapon = Weapon(service)
strategy = Strategy(service)
wiki = Wiki(service)
add_handler(start, command="start")
add_handler(plugins_help.command_start, command="help")
add_handler(ping, command="ping")
add_handler(wiki.refresh_wiki, command="wiki_refresh")
add_handler(auth.query, query=r"^auth_challenge\|")
add_handler(auth.admin, query=r"^auth_admin\|")
add_handler(admin.add_admin, command="add_admin")
add_handler(admin.del_admin, command="del_admin")
add_handler(gacha.command_start, command="gacha", regex=r"^抽卡模拟器(.*)")
add_handler(weapon.command_start, command="weapon", regex=r"^武器查询(.*)")
add_handler(strategy.command_start, command="strategy", regex=r"^角色攻略查询(.*)")
# 调试功能
add_handler(reply_keyboard_remove, command="reply_keyboard_remove")
add_handler(admin.leave_chat, command="leave_chat")
cookies_handler = Cookies.create_conversation_handler(service)
uid_handler = Uid.create_conversation_handler(service)
daily_note_handler = DailyNote.create_conversation_handler(service)
sign_handler = Sign.create_conversation_handler(service)
quiz_handler = Quiz.create_conversation_handler(service)
post_handler = Post.create_conversation_handler(service)
artifact_rate_handler = ArtifactRate.create_conversation_handler(service)
ledger_handler = Ledger.create_conversation_handler(service)
new_chat_members_handler = NewChatMembersHandler(service, auth.new_mem)
application.add_handler(MessageHandler(filters.StatusUpdate.NEW_CHAT_MEMBERS,
new_chat_members_handler.new_member, block=False))
application.add_handler(sign_handler)
application.add_handler(quiz_handler)
application.add_handler(cookies_handler)
application.add_handler(uid_handler)
application.add_handler(daily_note_handler)
application.add_handler(post_handler)
application.add_handler(ledger_handler)
application.add_handler(artifact_rate_handler)
application.add_handler(InlineQueryHandler(inline.inline_query, block=False))
application.add_handler(MessageHandler(filters.COMMAND & filters.ChatType.PRIVATE, unknown_command))
application.add_error_handler(error_handler, block=False)
Log.info("插件加载成功")

78
manager.py Normal file
View File

@ -0,0 +1,78 @@
import os
from glob import glob
from importlib import import_module
from os import path
from typing import Callable, List, Union
from telegram.ext import Application
from logger import Log
PluginsClass: List[Callable] = []
def listener_plugins_class():
def decorator(func: Callable):
PluginsClass.append(
func
)
return func
return decorator
class PluginsManager:
def __init__(self):
self.plugin_list: List[str] = [] # 用于存储文件名称
self.exclude_list: List[str] = []
def refresh_list(self, plugin_paths):
self.plugin_list.clear()
plugin_paths = glob(plugin_paths)
for plugin_path in plugin_paths:
if plugin_path.startswith('__'):
continue
module_name = path.basename(path.normpath(plugin_path))
root, ext = os.path.splitext(module_name)
if ext == ".py":
self.plugin_list.append(root)
def add_exclude(self, exclude: Union[str, List[str]]):
if type(exclude) == str:
self.exclude_list.append(exclude)
elif type(exclude) == list:
self.exclude_list.extend(exclude)
else:
raise TypeError
def import_module(self):
for plugin_name in self.plugin_list:
if plugin_name not in self.exclude_list:
try:
import_module(f"plugins.{plugin_name}")
except ImportError as exc:
Log.warning(f"插件 {plugin_name} 导入失败", exc)
except ImportWarning as exc:
Log.warning(f"插件 {plugin_name} 加载成功但有警告", exc)
except BaseException as exc:
Log.warning(f"插件 {plugin_name} 加载失败", exc)
else:
Log.debug(f"插件 {plugin_name} 加载成功")
@staticmethod
def add_handler(application: Application, args=None):
for pc in PluginsClass:
if callable(pc):
try:
ist = pc(*args)
handlers_list = ist.create_handlers(*args)
for handler in handlers_list:
application.add_handler(handler)
except AttributeError as exc:
if "create_handlers" in str(exc):
Log.error("创建 handlers 函数未找到", exc)
Log.error("初始化Class失败", exc)
except BaseException as exc:
Log.error("初始化Class失败", exc)
finally:
pass

View File

@ -14,15 +14,17 @@
from telegram import Update
from telegram.ext import CallbackContext
from manager import listener_plugins_class
from plugins.base import BasePlugins, restricts
from plugins.errorhandler import conversation_error_handler
@listener_plugins_class()
class Example(BasePlugins):
@staticmethod
def create_conversation_handler(service: BaseService):
def create_handlers(service: BaseService):
example = Example(service)
return CommandHandler('example', example.command_start)
return [CommandHandler('example', example.command_start)]
@conversation_error_handler
@restricts()
@ -35,7 +37,7 @@ class Example(BasePlugins):
plugins 模块下的类需要继承 `BasePlugins`
plugins 模块下的类必须提供 `create_conversation_handler` 静态函数作为构建相应会话过程`handle.py`
plugins 模块下的类必须提供 `create_handlers` 静态函数作为构建相应处理程序`handle.py`
在函数注册为命令处理过程(如 `CommandHandler` )需要添加 `conversation_error_handler` 修饰器作为错误统一处理
@ -43,5 +45,9 @@ plugins 模块下的类必须提供 `create_conversation_handler` 静态函数
入口函数必须使用 `@restricts()` 修饰器 防止洪水攻击
我也不知道从那个版本开始 `plugins` 文件夹下的全部模块无需再次修改 `handler` 文件实现注册处理程序
只需在构建的类前加上 `@listener_plugins_class()` 修饰器即可
**注意:`@restricts()` 修饰器带参,必须带括号,否则会出现调用错误**

View File

@ -2,10 +2,12 @@ from typing import Callable
from telegram import Update
from telegram.error import BadRequest, Forbidden
from telegram.ext import CallbackContext
from telegram.ext import CallbackContext, CommandHandler
from logger import Log
from manager import listener_plugins_class
from plugins.base import BasePlugins
from service import BaseService
def bot_admins_only(func: Callable) -> Callable: # noqa
@ -20,11 +22,21 @@ def bot_admins_only(func: Callable) -> Callable: # noqa
return decorator
@listener_plugins_class()
class Admin(BasePlugins):
"""
有关BOT ADMIN处理
"""
@staticmethod
def create_handlers(service: BaseService) -> list:
admin = Admin(service)
return [
CommandHandler("add_admin", admin.add_admin, block=False),
CommandHandler("del_admin", admin.del_admin, block=False),
CommandHandler("leave_chat", admin.leave_chat, block=False),
]
@bot_admins_only
async def add_admin(self, update: Update, _: CallbackContext):
message = update.message

View File

@ -1,3 +1,5 @@
from typing import List
from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup
from telegram.constants import ChatAction, ParseMode
from telegram.ext import CallbackContext, ConversationHandler, CommandHandler, CallbackQueryHandler, MessageHandler, \
@ -5,12 +7,14 @@ from telegram.ext import CallbackContext, ConversationHandler, CommandHandler, C
from telegram.helpers import escape_markdown
from logger import Log
from manager import listener_plugins_class
from model.apihelper.artifact import ArtifactOcrRate, get_comment, get_format_sub_item
from plugins.base import BasePlugins, restricts
from plugins.errorhandler import conversation_error_handler
from service import BaseService
@listener_plugins_class()
class ArtifactRate(BasePlugins):
COMMAND_RESULT = 1
@ -32,17 +36,19 @@ class ArtifactRate(BasePlugins):
self.artifact_rate = ArtifactOcrRate()
@staticmethod
def create_conversation_handler(service: BaseService):
def create_handlers(service: BaseService) -> list:
artifact_rate = ArtifactRate(service)
return ConversationHandler(
entry_points=[CommandHandler('artifact_rate', artifact_rate.command_start),
MessageHandler(filters.Regex(r"^圣遗物评分(.*)"), artifact_rate.command_start),
MessageHandler(filters.CaptionRegex(r"^圣遗物评分(.*)"), artifact_rate.command_start)],
states={
artifact_rate.COMMAND_RESULT: [CallbackQueryHandler(artifact_rate.command_result)]
},
fallbacks=[CommandHandler('cancel', artifact_rate.cancel)]
)
return [
ConversationHandler(
entry_points=[CommandHandler('artifact_rate', artifact_rate.command_start),
MessageHandler(filters.Regex(r"^圣遗物评分(.*)"), artifact_rate.command_start),
MessageHandler(filters.CaptionRegex(r"^圣遗物评分(.*)"), artifact_rate.command_start)],
states={
artifact_rate.COMMAND_RESULT: [CallbackQueryHandler(artifact_rate.command_result)]
},
fallbacks=[CommandHandler('cancel', artifact_rate.cancel)]
)
]
async def get_rate(self, artifact_attr: dict) -> str:
rate_result_req = await self.artifact_rate.rate_artifact(artifact_attr)

View File

@ -1,4 +1,6 @@
from http.cookies import SimpleCookie, CookieError
from typing import List
from urllib.request import BaseHandler
import genshin
import ujson
@ -8,6 +10,7 @@ from telegram.ext import CallbackContext, CommandHandler, MessageHandler, filter
from telegram.helpers import escape_markdown
from logger import Log
from manager import listener_plugins_class
from model.base import ServiceEnum
from plugins.base import BasePlugins, restricts
from plugins.errorhandler import conversation_error_handler
@ -22,6 +25,7 @@ class CookiesCommandData:
user_info: UserInfoData = UserInfoData()
@listener_plugins_class()
class Cookies(BasePlugins):
"""
Cookie绑定
@ -30,7 +34,7 @@ class Cookies(BasePlugins):
CHECK_SERVER, CHECK_COOKIES, COMMAND_RESULT = range(10100, 10103)
@staticmethod
def create_conversation_handler(service: BaseService):
def create_handlers(service: BaseService):
cookies = Cookies(service)
cookies_handler = ConversationHandler(
entry_points=[CommandHandler('adduser', cookies.command_start, filters.ChatType.PRIVATE, block=True),
@ -46,7 +50,7 @@ class Cookies(BasePlugins):
},
fallbacks=[CommandHandler('cancel', cookies.cancel, block=True)],
)
return cookies_handler
return [cookies_handler]
@conversation_error_handler
async def command_start(self, update: Update, context: CallbackContext) -> int:

View File

@ -9,6 +9,7 @@ from telegram.ext import CallbackContext, CommandHandler, MessageHandler, Conver
CallbackQueryHandler
from logger import Log
from manager import listener_plugins_class
from model.base import ServiceEnum
from plugins.base import BasePlugins, restricts
from plugins.errorhandler import conversation_error_handler
@ -20,6 +21,7 @@ class UidCommandData:
user_info: UserInfoData = UserInfoData()
@listener_plugins_class()
class DailyNote(BasePlugins):
"""
每日便签
@ -32,7 +34,7 @@ class DailyNote(BasePlugins):
self.current_dir = os.getcwd()
@staticmethod
def create_conversation_handler(service: BaseService):
def create_handlers(service: BaseService) -> list:
daily_note = DailyNote(service)
daily_note_handler = ConversationHandler(
entry_points=[CommandHandler('dailynote', daily_note.command_start, block=True),
@ -42,7 +44,7 @@ class DailyNote(BasePlugins):
},
fallbacks=[CommandHandler('cancel', daily_note.cancel, block=True)]
)
return daily_note_handler
return [daily_note_handler]
async def _start_get_daily_note(self, user_info_data: UserInfoData, service: ServiceEnum) -> bytes:
if service == ServiceEnum.HYPERION:

View File

@ -3,20 +3,31 @@ import os
from pyppeteer import launch
from telegram import Update
from telegram.constants import ChatAction
from telegram.ext import CallbackContext, ConversationHandler, filters
from telegram.ext import CallbackContext, ConversationHandler, filters, CommandHandler, MessageHandler
from logger import Log
from manager import listener_plugins_class
from plugins.base import BasePlugins, restricts
from plugins.errorhandler import conversation_error_handler
from service import BaseService
from service.wish import WishCountInfo, get_one
@listener_plugins_class()
class Gacha(BasePlugins):
"""
抽卡模拟器非首模拟器/减寿模拟器
"""
@staticmethod
def create_handlers(service: BaseService) -> list:
gacha = Gacha(service)
return [
CommandHandler("gacha", gacha.command_start, block=False),
MessageHandler(filters.Regex("^抽卡模拟器(.*)"), gacha.command_start, block=False),
MessageHandler(filters.Regex("^非首模拟器(.*)"), gacha.command_start, block=False),
]
def __init__(self, service: BaseService):
super().__init__(service)
self.browser: launch = None

View File

@ -11,6 +11,7 @@ from telegram.ext import CallbackContext, CommandHandler, MessageHandler, Conver
CallbackQueryHandler
from logger import Log
from manager import listener_plugins_class
from model.base import ServiceEnum
from plugins.base import BasePlugins, restricts
from plugins.errorhandler import conversation_error_handler
@ -58,6 +59,7 @@ def process_ledger_month(context: CallbackContext) -> int:
return now_time.month
@listener_plugins_class()
class Ledger(BasePlugins):
"""
旅行札记
@ -70,16 +72,16 @@ class Ledger(BasePlugins):
self.current_dir = os.getcwd()
@staticmethod
def create_conversation_handler(service: BaseService):
def create_handlers(service: BaseService):
ledger = Ledger(service)
return ConversationHandler(
return [ConversationHandler(
entry_points=[CommandHandler("ledger", ledger.command_start, block=True),
MessageHandler(filters.Regex(r"^旅行扎记(.*)"), ledger.command_start, block=True)],
states={
ledger.COMMAND_RESULT: [CallbackQueryHandler(ledger.command_result, block=True)]
},
fallbacks=[CommandHandler("cancel", ledger.cancel, block=True)]
)
)]
async def _start_get_ledger(self, user_info_data: UserInfoData, service: ServiceEnum, month=None) -> bytes:
if service == ServiceEnum.HYPERION:

View File

@ -9,6 +9,7 @@ from telegram.helpers import escape_markdown
from config import config
from logger import Log
from manager import listener_plugins_class
from model.apihelper import Hyperion, ArtworkImage
from plugins.base import BasePlugins
from plugins.errorhandler import conversation_error_handler
@ -25,6 +26,7 @@ class PostHandlerData:
self.tags: Optional[List[str]] = []
@listener_plugins_class()
class Post(BasePlugins):
"""
文章推送
@ -40,7 +42,7 @@ class Post(BasePlugins):
self.bbs = Hyperion()
@staticmethod
def create_conversation_handler(service: BaseService):
def create_handlers(service: BaseService):
_post = Post(service)
post_handler = ConversationHandler(
entry_points=[CommandHandler('post', _post.command_start, block=True)],
@ -57,7 +59,7 @@ class Post(BasePlugins):
},
fallbacks=[CommandHandler('cancel', _post.cancel, block=True)]
)
return post_handler
return [post_handler]
@conversation_error_handler
async def command_start(self, update: Update, context: CallbackContext) -> int:

View File

@ -10,6 +10,7 @@ from telegram.ext import CallbackContext, ConversationHandler, CommandHandler, M
from telegram.helpers import escape_markdown
from logger import Log
from manager import listener_plugins_class
from utils.random import MT19937_Random
from plugins.base import BasePlugins, restricts
from service import BaseService
@ -24,6 +25,7 @@ class QuizCommandData:
status: int = 0
@listener_plugins_class()
class Quiz(BasePlugins):
"""
派蒙的十万个为什么
@ -42,7 +44,7 @@ class Quiz(BasePlugins):
self.random = MT19937_Random()
@staticmethod
def create_conversation_handler(service: BaseService):
def create_handlers(service: BaseService):
quiz = Quiz(service)
quiz_handler = ConversationHandler(
entry_points=[CommandHandler('quiz', quiz.command_start, block=True)],
@ -63,7 +65,7 @@ class Quiz(BasePlugins):
},
fallbacks=[CommandHandler('cancel', quiz.cancel, block=True)]
)
return quiz_handler
return [quiz_handler]
async def send_poll(self, update: Update) -> Optional[Message]:
chat = update.message.chat

View File

@ -9,6 +9,7 @@ from telegram.ext import CallbackContext, CommandHandler, MessageHandler, Conver
CallbackQueryHandler
from logger import Log
from manager import listener_plugins_class
from model.base import ServiceEnum
from plugins.base import BasePlugins, restricts
from plugins.errorhandler import conversation_error_handler
@ -22,6 +23,7 @@ class SignCommandData:
reply_to_message_id: int = 0
@listener_plugins_class()
class Sign(BasePlugins):
"""
每日签到
@ -30,7 +32,7 @@ class Sign(BasePlugins):
CHECK_SERVER, COMMAND_RESULT = range(10400, 10402)
@staticmethod
def create_conversation_handler(service: BaseService):
def create_handlers(service: BaseService):
sign = Sign(service)
sign_handler = ConversationHandler(
entry_points=[CommandHandler('sign', sign.command_start, block=True),
@ -40,7 +42,7 @@ class Sign(BasePlugins):
},
fallbacks=[CommandHandler('cancel', sign.cancel, block=True)]
)
return sign_handler
return [sign_handler]
@staticmethod
async def _start_sign(user_info: UserInfoData, service: ServiceEnum) -> str:

View File

@ -1,14 +1,17 @@
from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup
from telegram.constants import ChatAction, ParseMode
from telegram.ext import CallbackContext, filters, ConversationHandler
from telegram.ext import CallbackContext, filters, ConversationHandler, CommandHandler, MessageHandler
from logger import Log
from manager import listener_plugins_class
from metadata.shortname import roleToName
from model.helpers import url_to_file
from plugins.base import BasePlugins, restricts
from plugins.errorhandler import conversation_error_handler
from service import BaseService
@listener_plugins_class()
class Strategy(BasePlugins):
"""
角色攻略查询
@ -16,6 +19,14 @@ class Strategy(BasePlugins):
KEYBOARD = [[InlineKeyboardButton(text="查看角色攻略列表并查询", switch_inline_query_current_chat="查看角色攻略列表并查询")]]
@staticmethod
def create_handlers(service: BaseService) -> list:
strategy = Strategy(service)
return [
CommandHandler("strategy", strategy.command_start, block=False),
MessageHandler(filters.Regex("^角色攻略查询(.*)"), strategy.command_start, block=False),
]
@conversation_error_handler
@restricts(return_data=ConversationHandler.END)
async def command_start(self, update: Update, context: CallbackContext) -> None:

View File

@ -9,6 +9,7 @@ from telegram.ext import CallbackContext, CommandHandler, MessageHandler, Conver
CallbackQueryHandler
from logger import Log
from manager import listener_plugins_class
from model.base import ServiceEnum
from model.helpers import url_to_file
from plugins.base import BasePlugins, restricts
@ -21,6 +22,7 @@ class UidCommandData:
user_info: UserInfoData = UserInfoData()
@listener_plugins_class()
class Uid(BasePlugins):
"""
玩家查询
@ -33,7 +35,7 @@ class Uid(BasePlugins):
self.current_dir = os.getcwd()
@staticmethod
def create_conversation_handler(service: BaseService):
def create_handlers(service: BaseService):
uid = Uid(service)
uid_handler = ConversationHandler(
entry_points=[CommandHandler('uid', uid.command_start, block=True),
@ -43,7 +45,7 @@ class Uid(BasePlugins):
},
fallbacks=[CommandHandler('cancel', uid.cancel, block=True)]
)
return uid_handler
return [uid_handler]
async def _start_get_user_info(self, user_info_data: UserInfoData, service: ServiceEnum, uid: int = -1) -> bytes:
if service == ServiceEnum.HYPERION:

View File

@ -1,14 +1,17 @@
from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup
from telegram.constants import ChatAction
from telegram.ext import CallbackContext, filters
from telegram.ext import CallbackContext, filters, CommandHandler, MessageHandler
from logger import Log
from manager import listener_plugins_class
from metadata.shortname import weaponToName
from model.helpers import url_to_file
from plugins.base import BasePlugins, restricts
from plugins.errorhandler import conversation_error_handler
from service import BaseService
@listener_plugins_class()
class Weapon(BasePlugins):
"""
武器查询
@ -16,6 +19,14 @@ class Weapon(BasePlugins):
KEYBOARD = [[InlineKeyboardButton(text="查看武器列表并查询", switch_inline_query_current_chat="查看武器列表并查询")]]
@staticmethod
def create_handlers(service: BaseService) -> list:
weapon = Weapon(service)
return [
CommandHandler("weapon", weapon.command_start, block=False),
MessageHandler(filters.Regex("^武器查询(.*)"), weapon.command_start, block=False)
]
@conversation_error_handler
@restricts()
async def command_start(self, update: Update, context: CallbackContext) -> None:

View File

@ -1,14 +1,25 @@
from telegram import Update
from telegram.ext import CallbackContext
from telegram.ext import CallbackContext, CommandHandler
from manager import listener_plugins_class
from plugins.admin import bot_admins_only
from plugins.base import BasePlugins
from service import BaseService
@listener_plugins_class()
class Wiki(BasePlugins):
"""
有关WIKI
"""
@staticmethod
def create_handlers(service: BaseService) -> list:
wiki = Wiki(service)
return [
CommandHandler("refresh_wiki", wiki.refresh_wiki, block=False),
]
@bot_admins_only
async def refresh_wiki(self, update: Update, _: CallbackContext):
message = update.message