mirror of
https://github.com/PaiGramTeam/MibooGram.git
synced 2024-11-25 09:37:38 +00:00
✨修复 quiz
模块问题并添加验证群验证插件
✨修复 `quiz` 模块问题并添加验证群验证插件
This commit is contained in:
parent
aa882c3f93
commit
3ec99cc3b8
@ -23,4 +23,7 @@ ERROR_NOTIFICATION_CHAT_ID=chat_id
|
|||||||
CHANNELS=[{ "name": "", "chat_id": 1}]
|
CHANNELS=[{ "name": "", "chat_id": 1}]
|
||||||
|
|
||||||
# bot 管理员
|
# bot 管理员
|
||||||
ADMINS=[{ "username": "", "user_id": 1 }]
|
ADMINS=[{ "username": "", "user_id": -1 }]
|
||||||
|
|
||||||
|
# 群验证功能
|
||||||
|
JOINING_VERIFICATION_GROUPS=[]
|
||||||
|
@ -11,6 +11,7 @@ load_dotenv()
|
|||||||
|
|
||||||
env = os.getenv
|
env = os.getenv
|
||||||
|
|
||||||
|
|
||||||
def str_to_bool(value: Any) -> bool:
|
def str_to_bool(value: Any) -> bool:
|
||||||
"""Return whether the provided string (or any value really) represents true. Otherwise false.
|
"""Return whether the provided string (or any value really) represents true. Otherwise false.
|
||||||
Just like plugin server stringToBoolean.
|
Just like plugin server stringToBoolean.
|
||||||
@ -19,8 +20,9 @@ def str_to_bool(value: Any) -> bool:
|
|||||||
return False
|
return False
|
||||||
return str(value).lower() in ("y", "yes", "t", "true", "on", "1")
|
return str(value).lower() in ("y", "yes", "t", "true", "on", "1")
|
||||||
|
|
||||||
|
|
||||||
_config = {
|
_config = {
|
||||||
"debug": str_to_bool(os.getenv('DEBUG', 'True')),
|
"debug": str_to_bool(os.getenv('DEBUG', 'False')),
|
||||||
|
|
||||||
"mysql": {
|
"mysql": {
|
||||||
"host": env("DB_HOST", "127.0.0.1"),
|
"host": env("DB_HOST", "127.0.0.1"),
|
||||||
@ -55,6 +57,8 @@ _config = {
|
|||||||
# 在环境变量里的格式是 json: [{"username": "", "user_id": 1}]
|
# 在环境变量里的格式是 json: [{"username": "", "user_id": 1}]
|
||||||
*ujson.loads(env('ADMINS', '[]'))
|
*ujson.loads(env('ADMINS', '[]'))
|
||||||
],
|
],
|
||||||
|
|
||||||
|
"joining_verification_groups": env('JOINING_VERIFICATION_GROUPS', '[]'),
|
||||||
}
|
}
|
||||||
|
|
||||||
config = Storage(_config)
|
config = Storage(_config)
|
||||||
|
@ -0,0 +1,14 @@
|
|||||||
|
from utils.mysql import MySQL
|
||||||
|
from utils.redisdb import RedisDB
|
||||||
|
from utils.service.manager import listener_service
|
||||||
|
from .cache import QuizCache
|
||||||
|
from .repositories import QuizRepository
|
||||||
|
from .services import QuizService
|
||||||
|
|
||||||
|
|
||||||
|
@listener_service()
|
||||||
|
def create_quiz_service(mysql: MySQL, redis: RedisDB):
|
||||||
|
_repository = QuizRepository(mysql)
|
||||||
|
_cache = QuizCache(redis)
|
||||||
|
_service = QuizService(_repository, _cache)
|
||||||
|
return _service
|
@ -1,9 +1,9 @@
|
|||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
import ujson
|
import ujson
|
||||||
from app.quiz.models import Question, Answer
|
|
||||||
|
|
||||||
from utils.redisdb import RedisDB
|
from utils.redisdb import RedisDB
|
||||||
|
from .models import Question, Answer
|
||||||
|
|
||||||
|
|
||||||
class QuizCache:
|
class QuizCache:
|
||||||
@ -30,15 +30,18 @@ class QuizCache:
|
|||||||
async def get_one_question(self, question_id: int) -> Question:
|
async def get_one_question(self, question_id: int) -> Question:
|
||||||
qname = f"{self.question_qname}:{question_id}"
|
qname = f"{self.question_qname}:{question_id}"
|
||||||
data = await self.client.get(qname)
|
data = await self.client.get(qname)
|
||||||
return Question.de_json(ujson.loads(data))
|
json_data = str(data, encoding="utf-8")
|
||||||
|
return Question.de_json(ujson.loads(json_data))
|
||||||
|
|
||||||
async def get_one_answer(self, answer_id: int) -> str:
|
async def get_one_answer(self, answer_id: int) -> Answer:
|
||||||
qname = f"{self.answer_qname}:{answer_id}"
|
qname = f"{self.answer_qname}:{answer_id}"
|
||||||
return await self.client.get(qname)
|
data = await self.client.get(qname)
|
||||||
|
json_data = str(data, encoding="utf-8")
|
||||||
|
return Answer.de_json(ujson.loads(json_data))
|
||||||
|
|
||||||
async def add_question(self, question_list: List[Question] = None):
|
async def add_question(self, question_list: List[Question] = None):
|
||||||
for question in question_list:
|
for question in question_list:
|
||||||
await self.client.set(f"{self.question_qname}:{question.question_id}", str(question))
|
await self.client.set(f"{self.question_qname}:{question.question_id}", ujson.dumps(question.to_dict()))
|
||||||
question_id_list = [question.question_id for question in question_list]
|
question_id_list = [question.question_id for question in question_list]
|
||||||
await self.client.lpush(f"{self.question_qname}:id_list", *question_id_list)
|
await self.client.lpush(f"{self.question_qname}:id_list", *question_id_list)
|
||||||
return await self.client.llen(f"{self.question_qname}:id_list")
|
return await self.client.llen(f"{self.question_qname}:id_list")
|
||||||
@ -57,7 +60,7 @@ class QuizCache:
|
|||||||
|
|
||||||
async def add_answer(self, answer_list: List[Answer] = None):
|
async def add_answer(self, answer_list: List[Answer] = None):
|
||||||
for answer in answer_list:
|
for answer in answer_list:
|
||||||
await self.client.set(f"{self.answer_qname}:{answer.answer_id}", str(answer))
|
await self.client.set(f"{self.answer_qname}:{answer.answer_id}", ujson.dumps(answer.to_dict()))
|
||||||
answer_id_list = [answer.answer_id for answer in answer_list]
|
answer_id_list = [answer.answer_id for answer in answer_list]
|
||||||
await self.client.lpush(f"{self.answer_qname}:id_list", *answer_id_list)
|
await self.client.lpush(f"{self.answer_qname}:id_list", *answer_id_list)
|
||||||
return await self.client.llen(f"{self.answer_qname}:id_list")
|
return await self.client.llen(f"{self.answer_qname}:id_list")
|
||||||
|
@ -78,7 +78,7 @@ class Question(BaseObject):
|
|||||||
def to_dict(self) -> JSONDict:
|
def to_dict(self) -> JSONDict:
|
||||||
data = super().to_dict()
|
data = super().to_dict()
|
||||||
if self.answers:
|
if self.answers:
|
||||||
data["sub_item"] = [e.to_dict() for e in self.answers]
|
data["answers"] = [e.to_dict() for e in self.answers]
|
||||||
return data
|
return data
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -86,7 +86,7 @@ class Question(BaseObject):
|
|||||||
data = cls._parse_data(data)
|
data = cls._parse_data(data)
|
||||||
if not data:
|
if not data:
|
||||||
return None
|
return None
|
||||||
data["sub_item"] = Answer.de_list(data.get("sub_item"))
|
data["answers"] = Answer.de_list(data.get("answers"))
|
||||||
return cls(**data)
|
return cls(**data)
|
||||||
|
|
||||||
__slots__ = ("question_id", "text", "answers")
|
__slots__ = ("question_id", "text", "answers")
|
||||||
|
@ -15,14 +15,14 @@ class QuizRepository:
|
|||||||
query = select(QuestionDB)
|
query = select(QuestionDB)
|
||||||
results = await session.exec(query)
|
results = await session.exec(query)
|
||||||
questions = results.all()
|
questions = results.all()
|
||||||
return [question[0] for question in questions]
|
return questions
|
||||||
|
|
||||||
async def get_answers_form_question_id(self, question_id: int) -> List[AnswerDB]:
|
async def get_answers_form_question_id(self, question_id: int) -> List[AnswerDB]:
|
||||||
async with self.mysql.Session() as session:
|
async with self.mysql.Session() as session:
|
||||||
query = select(AnswerDB).where(AnswerDB.question_id == question_id)
|
query = select(AnswerDB).where(AnswerDB.question_id == question_id)
|
||||||
results = await session.exec(query)
|
results = await session.exec(query)
|
||||||
answers = results.all()
|
answers = results.all()
|
||||||
return [answer[0] for answer in answers]
|
return answers
|
||||||
|
|
||||||
async def add_question(self, question: QuestionDB):
|
async def add_question(self, question: QuestionDB):
|
||||||
async with self.mysql.Session() as session:
|
async with self.mysql.Session() as session:
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
|
import asyncio
|
||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
import ujson
|
|
||||||
|
|
||||||
from .cache import QuizCache
|
from .cache import QuizCache
|
||||||
from .models import Question, Answer
|
from .models import Question, Answer
|
||||||
from .repositories import QuizRepository
|
from .repositories import QuizRepository
|
||||||
@ -11,6 +10,7 @@ class QuizService:
|
|||||||
def __init__(self, repository: QuizRepository, cache: QuizCache):
|
def __init__(self, repository: QuizRepository, cache: QuizCache):
|
||||||
self._repository = repository
|
self._repository = repository
|
||||||
self._cache = cache
|
self._cache = cache
|
||||||
|
self.lock = asyncio.Lock()
|
||||||
|
|
||||||
async def get_quiz_form_database(self) -> List[Question]:
|
async def get_quiz_form_database(self) -> List[Question]:
|
||||||
"""从数据库获取问题列表
|
"""从数据库获取问题列表
|
||||||
@ -21,7 +21,6 @@ class QuizService:
|
|||||||
for question in question_list:
|
for question in question_list:
|
||||||
question_id = question.id
|
question_id = question.id
|
||||||
answers = await self._repository.get_answers_form_question_id(question_id)
|
answers = await self._repository.get_answers_form_question_id(question_id)
|
||||||
question.answers = answers
|
|
||||||
data = Question.de_database_data(question)
|
data = Question.de_database_data(question)
|
||||||
data.answers = [Answer.de_database_data(a) for a in answers]
|
data.answers = [Answer.de_database_data(a) for a in answers]
|
||||||
temp.append(data)
|
temp.append(data)
|
||||||
@ -33,6 +32,11 @@ class QuizService:
|
|||||||
await self._repository.add_answer(answers.to_database_data())
|
await self._repository.add_answer(answers.to_database_data())
|
||||||
|
|
||||||
async def refresh_quiz(self) -> int:
|
async def refresh_quiz(self) -> int:
|
||||||
|
"""从数据库刷新问题到Redis缓存 线程安全
|
||||||
|
:return: 已经缓存问题的数量
|
||||||
|
"""
|
||||||
|
# 只允许一个线程访问该区域 让数据被安全有效的访问
|
||||||
|
async with self.lock:
|
||||||
question_list = await self.get_quiz_form_database()
|
question_list = await self.get_quiz_form_database()
|
||||||
await self._cache.del_all_question()
|
await self._cache.del_all_question()
|
||||||
question_count = await self._cache.add_question(question_list)
|
question_count = await self._cache.add_question(question_list)
|
||||||
@ -44,9 +48,14 @@ class QuizService:
|
|||||||
async def get_question_id_list(self) -> List[int]:
|
async def get_question_id_list(self) -> List[int]:
|
||||||
return [int(question_id) for question_id in await self._cache.get_all_question_id_list()]
|
return [int(question_id) for question_id in await self._cache.get_all_question_id_list()]
|
||||||
|
|
||||||
async def get_answer(self, answer_id: int):
|
async def get_answer(self, answer_id: int) -> Answer:
|
||||||
data = await self._cache.get_one_answer(answer_id)
|
return await self._cache.get_one_answer(answer_id)
|
||||||
return ujson.loads(data)
|
|
||||||
|
|
||||||
async def get_question(self, question_id: int) -> Question:
|
async def get_question(self, question_id: int) -> Question:
|
||||||
return await self._cache.get_one_question(question_id)
|
return await self._cache.get_one_question(question_id)
|
||||||
|
|
||||||
|
async def delete_question_by_id(self, question_id: int):
|
||||||
|
return await self._repository.delete_question_by_id(question_id)
|
||||||
|
|
||||||
|
async def delete_answer_by_id(self, answer_id: int):
|
||||||
|
return await self._repository.delete_answer_by_id(answer_id)
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
|
import asyncio
|
||||||
import datetime
|
import datetime
|
||||||
|
from typing import List, Tuple, Callable
|
||||||
|
|
||||||
from telegram import Update, ReplyKeyboardRemove
|
from telegram import Update, ReplyKeyboardRemove
|
||||||
from telegram.error import BadRequest
|
from telegram.error import BadRequest
|
||||||
@ -55,6 +57,12 @@ class NewChatMembersHandler:
|
|||||||
@inject
|
@inject
|
||||||
def __init__(self, bot_admin_service: BotAdminService):
|
def __init__(self, bot_admin_service: BotAdminService):
|
||||||
self.bot_admin_service = bot_admin_service
|
self.bot_admin_service = bot_admin_service
|
||||||
|
self.callback: List[Tuple[Callable, int]] = []
|
||||||
|
|
||||||
|
def add_callback(self, callback, chat_id: int):
|
||||||
|
if chat_id >= 0:
|
||||||
|
raise ValueError
|
||||||
|
self.callback.append((callback, chat_id))
|
||||||
|
|
||||||
async def new_member(self, update: Update, context: CallbackContext) -> None:
|
async def new_member(self, update: Update, context: CallbackContext) -> None:
|
||||||
message = update.message
|
message = update.message
|
||||||
@ -79,4 +87,11 @@ class NewChatMembersHandler:
|
|||||||
Log.warning("不是管理员邀请!退出群聊。")
|
Log.warning("不是管理员邀请!退出群聊。")
|
||||||
await context.bot.send_message(message.chat_id, "派蒙不想进去!不是旅行者的邀请!")
|
await context.bot.send_message(message.chat_id, "派蒙不想进去!不是旅行者的邀请!")
|
||||||
await context.bot.leave_chat(chat.id)
|
await context.bot.leave_chat(chat.id)
|
||||||
|
else:
|
||||||
|
tasks = []
|
||||||
|
for callback, chat_id in self.callback:
|
||||||
|
if chat.id == chat_id:
|
||||||
|
task = asyncio.create_task(callback(update, context))
|
||||||
|
tasks.append(task)
|
||||||
|
if len(tasks) >= 1:
|
||||||
|
await asyncio.gather(*tasks)
|
||||||
|
291
plugins/genshin/quiz.py
Normal file
291
plugins/genshin/quiz.py
Normal file
@ -0,0 +1,291 @@
|
|||||||
|
import random
|
||||||
|
import re
|
||||||
|
from typing import List, Optional
|
||||||
|
|
||||||
|
from redis import DataError, ResponseError
|
||||||
|
from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup, ReplyKeyboardMarkup, Poll, \
|
||||||
|
ReplyKeyboardRemove, Message
|
||||||
|
from telegram.constants import ChatAction
|
||||||
|
from telegram.ext import CallbackContext, ConversationHandler, CommandHandler, MessageHandler, filters
|
||||||
|
from telegram.helpers import escape_markdown
|
||||||
|
|
||||||
|
from core.admin import BotAdminService
|
||||||
|
from core.quiz import QuizService
|
||||||
|
from core.quiz.models import Answer, Question
|
||||||
|
from logger import Log
|
||||||
|
from plugins.base import BasePlugins
|
||||||
|
from utils.decorators.restricts import restricts
|
||||||
|
from utils.plugins.manager import listener_plugins_class
|
||||||
|
from utils.random import MT19937_Random
|
||||||
|
|
||||||
|
|
||||||
|
class QuizCommandData:
|
||||||
|
question_id: int = -1
|
||||||
|
new_question: str = ""
|
||||||
|
new_correct_answer: str = ""
|
||||||
|
new_wrong_answer: List[str] = []
|
||||||
|
status: int = 0
|
||||||
|
|
||||||
|
|
||||||
|
@listener_plugins_class()
|
||||||
|
class QuizPlugin(BasePlugins):
|
||||||
|
"""派蒙的十万个为什么
|
||||||
|
合并了问题修改/添加/删除
|
||||||
|
"""
|
||||||
|
|
||||||
|
CHECK_COMMAND, VIEW_COMMAND, CHECK_QUESTION, \
|
||||||
|
GET_NEW_QUESTION, GET_NEW_CORRECT_ANSWER, GET_NEW_WRONG_ANSWER, \
|
||||||
|
QUESTION_EDIT, SAVE_QUESTION = range(10300, 10308)
|
||||||
|
|
||||||
|
def __init__(self, quiz_service: QuizService = None, bot_admin_service: BotAdminService = None):
|
||||||
|
self.bot_admin_service = bot_admin_service
|
||||||
|
self.user_time = {}
|
||||||
|
self.quiz_service = quiz_service
|
||||||
|
self.time_out = 120
|
||||||
|
self.random = MT19937_Random()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def create_handlers(cls):
|
||||||
|
quiz = cls()
|
||||||
|
quiz_handler = ConversationHandler(
|
||||||
|
entry_points=[CommandHandler('quiz', quiz.command_start, block=True)],
|
||||||
|
states={
|
||||||
|
quiz.CHECK_COMMAND: [MessageHandler(filters.TEXT & ~filters.COMMAND,
|
||||||
|
quiz.check_command, block=True)],
|
||||||
|
quiz.CHECK_QUESTION: [MessageHandler(filters.TEXT & ~filters.COMMAND,
|
||||||
|
quiz.check_question, block=True)],
|
||||||
|
quiz.GET_NEW_QUESTION: [MessageHandler(filters.TEXT & ~filters.COMMAND,
|
||||||
|
quiz.get_new_question, block=True)],
|
||||||
|
quiz.GET_NEW_CORRECT_ANSWER: [MessageHandler(filters.TEXT & ~filters.COMMAND,
|
||||||
|
quiz.get_new_correct_answer, block=True)],
|
||||||
|
quiz.GET_NEW_WRONG_ANSWER: [MessageHandler(filters.TEXT & ~filters.COMMAND,
|
||||||
|
quiz.get_new_wrong_answer, block=True),
|
||||||
|
CommandHandler("finish", quiz.finish_edit)],
|
||||||
|
quiz.SAVE_QUESTION: [MessageHandler(filters.TEXT & ~filters.COMMAND,
|
||||||
|
quiz.save_question, block=True)],
|
||||||
|
},
|
||||||
|
fallbacks=[CommandHandler('cancel', quiz.cancel, block=True)]
|
||||||
|
)
|
||||||
|
return [quiz_handler]
|
||||||
|
|
||||||
|
async def send_poll(self, update: Update) -> Optional[Message]:
|
||||||
|
chat = update.message.chat
|
||||||
|
user = update.effective_user
|
||||||
|
question_id_list = await self.quiz_service.get_question_id_list()
|
||||||
|
if filters.ChatType.GROUPS.filter(update.message):
|
||||||
|
Log.info(f"用户 {user.full_name}[{user.id}] 在群 {chat.title}[{chat.id}] 发送挑战问题命令请求")
|
||||||
|
if len(question_id_list) == 0:
|
||||||
|
return None
|
||||||
|
if len(question_id_list) == 0:
|
||||||
|
return None
|
||||||
|
index = self.random.random(0, len(question_id_list))
|
||||||
|
question = await self.quiz_service.get_question(question_id_list[index])
|
||||||
|
_options = []
|
||||||
|
correct_option = None
|
||||||
|
for answer in question.answers:
|
||||||
|
_options.append(answer.text)
|
||||||
|
if answer.is_correct:
|
||||||
|
correct_option = answer.text
|
||||||
|
if correct_option is None:
|
||||||
|
question_id = question["question_id"]
|
||||||
|
Log.warning(f"Quiz模块 correct_option 异常 question_id[{question_id}] ")
|
||||||
|
return None
|
||||||
|
random.shuffle(_options)
|
||||||
|
index = _options.index(correct_option)
|
||||||
|
return await update.effective_message.reply_poll(question.text, _options,
|
||||||
|
correct_option_id=index, is_anonymous=False,
|
||||||
|
open_period=self.time_out, type=Poll.QUIZ)
|
||||||
|
|
||||||
|
@restricts(filters.ChatType.GROUPS, ConversationHandler.END, restricts_time=20, try_delete_message=True)
|
||||||
|
@restricts(filters.ChatType.PRIVATE, ConversationHandler.END)
|
||||||
|
async def command_start(self, update: Update, context: CallbackContext) -> int:
|
||||||
|
user = update.effective_user
|
||||||
|
message = update.message
|
||||||
|
if filters.ChatType.PRIVATE.filter(message):
|
||||||
|
Log.info(f"用户 {user.full_name}[{user.id}] quiz命令请求")
|
||||||
|
admin_list = await self.bot_admin_service.get_admin_list()
|
||||||
|
if user.id in admin_list:
|
||||||
|
quiz_command_data: QuizCommandData = context.chat_data.get("quiz_command_data")
|
||||||
|
if quiz_command_data is None:
|
||||||
|
quiz_command_data = QuizCommandData()
|
||||||
|
context.chat_data["quiz_command_data"] = quiz_command_data
|
||||||
|
text = f'你好 {user.mention_markdown_v2()} {escape_markdown("!请选择你的操作!")}'
|
||||||
|
reply_keyboard = [
|
||||||
|
["查看问题", "添加问题"],
|
||||||
|
["重载问题"],
|
||||||
|
["退出"]
|
||||||
|
]
|
||||||
|
await message.reply_markdown_v2(text, reply_markup=ReplyKeyboardMarkup(reply_keyboard,
|
||||||
|
one_time_keyboard=True))
|
||||||
|
return self.CHECK_COMMAND
|
||||||
|
else:
|
||||||
|
await self.send_poll(update)
|
||||||
|
elif filters.ChatType.GROUPS.filter(update.message):
|
||||||
|
await update.message.reply_chat_action(ChatAction.TYPING)
|
||||||
|
poll_message = await self.send_poll(update)
|
||||||
|
if poll_message is None:
|
||||||
|
return ConversationHandler.END
|
||||||
|
self._add_delete_message_job(context, update.message.chat_id, update.message.message_id, 300)
|
||||||
|
self._add_delete_message_job(context, poll_message.chat_id, poll_message.message_id, 300)
|
||||||
|
return ConversationHandler.END
|
||||||
|
|
||||||
|
async def view_command(self, update: Update, _: CallbackContext) -> int:
|
||||||
|
keyboard = [
|
||||||
|
[
|
||||||
|
InlineKeyboardButton(text="选择问题", switch_inline_query_current_chat="查看问题 ")
|
||||||
|
]
|
||||||
|
]
|
||||||
|
await update.message.reply_text("请回复你要查看的问题",
|
||||||
|
reply_markup=InlineKeyboardMarkup(keyboard))
|
||||||
|
return self.CHECK_COMMAND
|
||||||
|
|
||||||
|
async def check_question(self, update: Update, _: CallbackContext) -> int:
|
||||||
|
reply_keyboard = [
|
||||||
|
["删除问题"],
|
||||||
|
["退出"]
|
||||||
|
]
|
||||||
|
await update.message.reply_text("请选择你的操作", reply_markup=ReplyKeyboardMarkup(reply_keyboard))
|
||||||
|
return self.CHECK_COMMAND
|
||||||
|
|
||||||
|
async def check_command(self, update: Update, context: CallbackContext) -> int:
|
||||||
|
quiz_command_data: QuizCommandData = context.chat_data.get("quiz_command_data")
|
||||||
|
if update.message.text == "退出":
|
||||||
|
await update.message.reply_text("退出任务", reply_markup=ReplyKeyboardRemove())
|
||||||
|
return ConversationHandler.END
|
||||||
|
elif update.message.text == "查看问题":
|
||||||
|
return await self.view_command(update, context)
|
||||||
|
elif update.message.text == "添加问题":
|
||||||
|
return await self.add_question(update, context)
|
||||||
|
elif update.message.text == "删除问题":
|
||||||
|
return await self.delete_question(update, context)
|
||||||
|
# elif update.message.text == "修改问题":
|
||||||
|
# return await self.edit_question(update, context)
|
||||||
|
elif update.message.text == "重载问题":
|
||||||
|
return await self.refresh_question(update, context)
|
||||||
|
else:
|
||||||
|
result = re.findall(r"问题ID (\d+)", update.message.text)
|
||||||
|
if len(result) == 1:
|
||||||
|
try:
|
||||||
|
question_id = int(result[0])
|
||||||
|
except ValueError:
|
||||||
|
await update.message.reply_text("获取问题ID失败")
|
||||||
|
return ConversationHandler.END
|
||||||
|
quiz_command_data.question_id = question_id
|
||||||
|
await update.message.reply_text("获取问题ID成功")
|
||||||
|
return await self.check_question(update, context)
|
||||||
|
await update.message.reply_text("命令错误", reply_markup=ReplyKeyboardRemove())
|
||||||
|
return ConversationHandler.END
|
||||||
|
|
||||||
|
async def refresh_question(self, update: Update, _: CallbackContext) -> int:
|
||||||
|
try:
|
||||||
|
await self.quiz_service.refresh_quiz()
|
||||||
|
except DataError:
|
||||||
|
await update.message.reply_text("Redis数据错误,重载失败", reply_markup=ReplyKeyboardRemove())
|
||||||
|
return ConversationHandler.END
|
||||||
|
except ResponseError as error:
|
||||||
|
Log.error("重载问题失败", error)
|
||||||
|
await update.message.reply_text("重载问题失败,异常抛出Redis请求错误异常,详情错误请看日记",
|
||||||
|
reply_markup=ReplyKeyboardRemove())
|
||||||
|
return ConversationHandler.END
|
||||||
|
await update.message.reply_text("重载成功", reply_markup=ReplyKeyboardRemove())
|
||||||
|
return ConversationHandler.END
|
||||||
|
|
||||||
|
async def add_question(self, update: Update, context: CallbackContext) -> int:
|
||||||
|
quiz_command_data: QuizCommandData = context.chat_data.get("quiz_command_data")
|
||||||
|
quiz_command_data.new_wrong_answer = []
|
||||||
|
quiz_command_data.new_question = ""
|
||||||
|
quiz_command_data.new_correct_answer = ""
|
||||||
|
quiz_command_data.status = 1
|
||||||
|
await update.message.reply_text("请回复你要添加的问题,或发送 /cancel 取消操作", reply_markup=ReplyKeyboardRemove())
|
||||||
|
return self.GET_NEW_QUESTION
|
||||||
|
|
||||||
|
async def get_new_question(self, update: Update, context: CallbackContext) -> int:
|
||||||
|
quiz_command_data: QuizCommandData = context.chat_data.get("quiz_command_data")
|
||||||
|
reply_text = f"问题:`{escape_markdown(update.message.text, version=2)}`\n" \
|
||||||
|
f"请填写正确答案:"
|
||||||
|
quiz_command_data.new_question = update.message.text
|
||||||
|
await update.message.reply_markdown_v2(reply_text)
|
||||||
|
return self.GET_NEW_CORRECT_ANSWER
|
||||||
|
|
||||||
|
async def get_new_correct_answer(self, update: Update, context: CallbackContext) -> int:
|
||||||
|
quiz_command_data: QuizCommandData = context.chat_data.get("quiz_command_data")
|
||||||
|
reply_text = f"正确答案:`{escape_markdown(update.message.text, version=2)}`\n" \
|
||||||
|
f"请填写错误答案:"
|
||||||
|
await update.message.reply_markdown_v2(reply_text)
|
||||||
|
quiz_command_data.new_correct_answer = update.message.text
|
||||||
|
return self.GET_NEW_WRONG_ANSWER
|
||||||
|
|
||||||
|
async def get_new_wrong_answer(self, update: Update, context: CallbackContext) -> int:
|
||||||
|
quiz_command_data: QuizCommandData = context.chat_data.get("quiz_command_data")
|
||||||
|
reply_text = f"错误答案:`{escape_markdown(update.message.text, version=2)}`\n" \
|
||||||
|
f"可继续填写,并使用 {escape_markdown('/finish', version=2)} 结束。"
|
||||||
|
await update.message.reply_markdown_v2(reply_text)
|
||||||
|
quiz_command_data.new_wrong_answer.append(update.message.text)
|
||||||
|
return self.GET_NEW_WRONG_ANSWER
|
||||||
|
|
||||||
|
async def finish_edit(self, update: Update, context: CallbackContext):
|
||||||
|
quiz_command_data: QuizCommandData = context.chat_data.get("quiz_command_data")
|
||||||
|
reply_text = f"问题:`{escape_markdown(quiz_command_data.new_question, version=2)}`\n" \
|
||||||
|
f"正确答案:`{escape_markdown(quiz_command_data.new_correct_answer, version=2)}`\n" \
|
||||||
|
f"错误答案:`{escape_markdown(' '.join(quiz_command_data.new_wrong_answer), version=2)}`"
|
||||||
|
await update.message.reply_markdown_v2(reply_text)
|
||||||
|
reply_keyboard = [["保存并重载配置", "抛弃修改并退出"]]
|
||||||
|
await update.message.reply_text("请核对问题,并选择下一步操作。", reply_markup=ReplyKeyboardMarkup(reply_keyboard))
|
||||||
|
return self.SAVE_QUESTION
|
||||||
|
|
||||||
|
async def save_question(self, update: Update, context: CallbackContext):
|
||||||
|
quiz_command_data: QuizCommandData = context.chat_data.get("quiz_command_data")
|
||||||
|
if update.message.text == "抛弃修改并退出":
|
||||||
|
await update.message.reply_text("退出任务", reply_markup=ReplyKeyboardRemove())
|
||||||
|
return ConversationHandler.END
|
||||||
|
elif update.message.text == "保存并重载配置":
|
||||||
|
if quiz_command_data.status == 1:
|
||||||
|
answer = [
|
||||||
|
Answer(text=wrong_answer, is_correct=False) for wrong_answer in
|
||||||
|
quiz_command_data.new_wrong_answer
|
||||||
|
]
|
||||||
|
answer.append(Answer(text=quiz_command_data.new_correct_answer, is_correct=True))
|
||||||
|
await self.quiz_service.save_quiz(
|
||||||
|
Question(text=quiz_command_data.new_question))
|
||||||
|
await update.message.reply_text("保存成功", reply_markup=ReplyKeyboardRemove())
|
||||||
|
try:
|
||||||
|
await self.quiz_service.refresh_quiz()
|
||||||
|
except ResponseError as error:
|
||||||
|
Log.error("重载问题失败", error)
|
||||||
|
await update.message.reply_text("重载问题失败,异常抛出Redis请求错误异常,详情错误请看日记",
|
||||||
|
reply_markup=ReplyKeyboardRemove())
|
||||||
|
return ConversationHandler.END
|
||||||
|
await update.message.reply_text("重载配置成功", reply_markup=ReplyKeyboardRemove())
|
||||||
|
return ConversationHandler.END
|
||||||
|
else:
|
||||||
|
await update.message.reply_text("回复错误,请重新选择")
|
||||||
|
return self.SAVE_QUESTION
|
||||||
|
|
||||||
|
async def edit_question(self, update: Update, context: CallbackContext) -> int:
|
||||||
|
quiz_command_data: QuizCommandData = context.chat_data.get("quiz_command_data")
|
||||||
|
quiz_command_data.new_wrong_answer = []
|
||||||
|
quiz_command_data.new_question = ""
|
||||||
|
quiz_command_data.new_correct_answer = ""
|
||||||
|
quiz_command_data.status = 2
|
||||||
|
await update.message.reply_text("请回复你要修改的问题", reply_markup=ReplyKeyboardRemove())
|
||||||
|
return self.GET_NEW_QUESTION
|
||||||
|
|
||||||
|
async def delete_question(self, update: Update, context: CallbackContext) -> int:
|
||||||
|
quiz_command_data: QuizCommandData = context.chat_data.get("quiz_command_data")
|
||||||
|
# 再问题重载Redis 以免redis数据为空时出现奔溃
|
||||||
|
try:
|
||||||
|
await self.quiz_service.refresh_quiz()
|
||||||
|
question = await self.quiz_service.get_question(quiz_command_data.question_id)
|
||||||
|
# 因为外键的存在,先删除答案
|
||||||
|
for answer in question.answers:
|
||||||
|
await self.quiz_service.delete_question_by_id(answer.answer_id)
|
||||||
|
await self.quiz_service.delete_question_by_id(question.question_id)
|
||||||
|
await update.message.reply_text("删除问题成功", reply_markup=ReplyKeyboardRemove())
|
||||||
|
await self.quiz_service.refresh_quiz()
|
||||||
|
except ResponseError as error:
|
||||||
|
Log.error("重载问题失败", error)
|
||||||
|
await update.message.reply_text("重载问题失败,异常抛出Redis请求错误异常,详情错误请看日记",
|
||||||
|
reply_markup=ReplyKeyboardRemove())
|
||||||
|
return ConversationHandler.END
|
||||||
|
await update.message.reply_text("重载配置成功", reply_markup=ReplyKeyboardRemove())
|
||||||
|
return ConversationHandler.END
|
290
plugins/system/auth.py
Normal file
290
plugins/system/auth.py
Normal file
@ -0,0 +1,290 @@
|
|||||||
|
import asyncio
|
||||||
|
import random
|
||||||
|
import time
|
||||||
|
from typing import Tuple, Union, Dict
|
||||||
|
|
||||||
|
from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup, ChatPermissions, ChatMember
|
||||||
|
from telegram.constants import ParseMode
|
||||||
|
from telegram.error import BadRequest
|
||||||
|
from telegram.ext import CallbackContext
|
||||||
|
from telegram.helpers import escape_markdown
|
||||||
|
|
||||||
|
from core.quiz import QuizService
|
||||||
|
from logger import Log
|
||||||
|
from utils.random import MT19937_Random
|
||||||
|
from utils.service.inject import inject
|
||||||
|
|
||||||
|
FullChatPermissions = ChatPermissions(
|
||||||
|
can_send_messages=True,
|
||||||
|
can_send_media_messages=True,
|
||||||
|
can_send_polls=True,
|
||||||
|
can_send_other_messages=True,
|
||||||
|
can_add_web_page_previews=True,
|
||||||
|
can_change_info=True,
|
||||||
|
can_invite_users=True,
|
||||||
|
can_pin_messages=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class GroupJoiningVerification:
|
||||||
|
"""群验证模块"""
|
||||||
|
|
||||||
|
@inject
|
||||||
|
def __init__(self, quiz_service: QuizService = None):
|
||||||
|
self.quiz_service = quiz_service
|
||||||
|
self.time_out = 120
|
||||||
|
self.kick_time = 120
|
||||||
|
self.random = MT19937_Random()
|
||||||
|
self.lock = asyncio.Lock()
|
||||||
|
self.chat_administrators_cache: Dict[Union[str, int], Tuple[float, list[ChatMember]]] = {}
|
||||||
|
self.is_refresh_quiz = False
|
||||||
|
|
||||||
|
async def refresh_quiz(self):
|
||||||
|
async with self.lock:
|
||||||
|
if not self.is_refresh_quiz:
|
||||||
|
await self.quiz_service.refresh_quiz()
|
||||||
|
self.is_refresh_quiz = True
|
||||||
|
|
||||||
|
async def get_chat_administrators(self, context: CallbackContext, chat_id: Union[str, int]) -> list[ChatMember]:
|
||||||
|
async with self.lock:
|
||||||
|
cache_data = self.chat_administrators_cache.get(f"{chat_id}")
|
||||||
|
if cache_data is not None:
|
||||||
|
cache_time, chat_administrators = cache_data
|
||||||
|
if time.time() >= cache_time + 360:
|
||||||
|
return chat_administrators
|
||||||
|
chat_administrators = await context.bot.get_chat_administrators(chat_id)
|
||||||
|
self.chat_administrators_cache[f"{chat_id}"] = (time.time(), chat_administrators)
|
||||||
|
return chat_administrators
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def is_admin(chat_administrators: list[ChatMember], user_id: int) -> bool:
|
||||||
|
return any(admin.user.id == user_id for admin in chat_administrators)
|
||||||
|
|
||||||
|
async def kick_member_job(self, context: CallbackContext):
|
||||||
|
job = context.job
|
||||||
|
Log.debug(f"踢出用户 user_id[{job.user_id}] 在 chat_id[{job.chat_id}]")
|
||||||
|
try:
|
||||||
|
await context.bot.ban_chat_member(chat_id=job.chat_id, user_id=job.user_id,
|
||||||
|
until_date=int(time.time()) + self.kick_time)
|
||||||
|
except BadRequest as error:
|
||||||
|
Log.error(f"Auth模块在 chat_id[{job.chat_id}] user_id[{job.user_id}] 执行kick失败", error)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
async def clean_message_job(context: CallbackContext):
|
||||||
|
job = context.job
|
||||||
|
Log.debug(f"删除消息 chat_id[{job.chat_id}] 的 message_id[{job.data}]")
|
||||||
|
try:
|
||||||
|
await context.bot.delete_message(chat_id=job.chat_id, message_id=job.data)
|
||||||
|
except BadRequest as error:
|
||||||
|
if "not found" in str(error):
|
||||||
|
Log.warning(f"Auth模块删除消息 chat_id[{job.chat_id}] message_id[{job.data}]失败 消息不存在")
|
||||||
|
elif "Message can't be deleted" in str(error):
|
||||||
|
Log.warning(
|
||||||
|
f"Auth模块删除消息 chat_id[{job.chat_id}] message_id[{job.data}]失败 消息无法删除 可能是没有授权")
|
||||||
|
else:
|
||||||
|
Log.error(f"Auth模块删除消息 chat_id[{job.chat_id}] message_id[{job.data}]失败", error)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
async def restore_member(context: CallbackContext, chat_id: int, user_id: int):
|
||||||
|
Log.debug(f"重置用户权限 user_id[{user_id}] 在 chat_id[{chat_id}]")
|
||||||
|
try:
|
||||||
|
await context.bot.restrict_chat_member(chat_id=chat_id, user_id=user_id, permissions=FullChatPermissions)
|
||||||
|
except BadRequest as error:
|
||||||
|
Log.error(f"Auth模块在 chat_id[{chat_id}] user_id[{user_id}] 执行restore失败", error)
|
||||||
|
|
||||||
|
async def admin(self, update: Update, context: CallbackContext) -> None:
|
||||||
|
|
||||||
|
async def admin_callback(callback_query_data: str) -> Tuple[str, int]:
|
||||||
|
_data = callback_query_data.split("|")
|
||||||
|
_result = _data[1]
|
||||||
|
_user_id = int(_data[2])
|
||||||
|
Log.debug(f"admin_callback函数返回 result[{_result}] user_id[{_user_id}]")
|
||||||
|
return _result, _user_id
|
||||||
|
|
||||||
|
callback_query = update.callback_query
|
||||||
|
user = callback_query.from_user
|
||||||
|
message = callback_query.message
|
||||||
|
chat = message.chat
|
||||||
|
Log.info(f"用户 {user.full_name}[{user.id}] 在群 {chat.title}[{chat.id}] 点击Auth管理员命令")
|
||||||
|
chat_administrators = await self.get_chat_administrators(context, chat_id=chat.id)
|
||||||
|
if not self.is_admin(chat_administrators, user.id):
|
||||||
|
Log.debug(f"用户 {user.full_name}[{user.id}] 在群 {chat.title}[{chat.id}] 非群管理")
|
||||||
|
await callback_query.answer(text="你不是管理!\n"
|
||||||
|
"再乱点我叫西风骑士团、千岩军和天领奉行了!", show_alert=True)
|
||||||
|
return
|
||||||
|
result, user_id = await admin_callback(callback_query.data)
|
||||||
|
try:
|
||||||
|
member_info = await context.bot.get_chat_member(chat.id, user_id)
|
||||||
|
except BadRequest as error:
|
||||||
|
Log.warning(f"获取用户 {user_id} 在群 {chat.title}[{chat.id}] 信息失败 \n", error)
|
||||||
|
user_info = f"{user_id}"
|
||||||
|
else:
|
||||||
|
user_info = member_info.user.mention_markdown_v2()
|
||||||
|
|
||||||
|
if result == "pass":
|
||||||
|
await callback_query.answer(text="放行", show_alert=False)
|
||||||
|
await self.restore_member(context, chat.id, user_id)
|
||||||
|
if schedule := context.job_queue.scheduler.get_job(f"{chat.id}|{user_id}|clean_join"):
|
||||||
|
schedule.remove()
|
||||||
|
await message.edit_text(f"{user_info} 被 {user.mention_markdown_v2()} 放行",
|
||||||
|
parse_mode=ParseMode.MARKDOWN_V2)
|
||||||
|
Log.info(f"用户 user_id[{user_id}] 在群 {chat.title}[{chat.id}] 被管理放行")
|
||||||
|
elif result == "kick":
|
||||||
|
await callback_query.answer(text="驱离", show_alert=False)
|
||||||
|
await context.bot.ban_chat_member(chat.id, user_id)
|
||||||
|
await message.edit_text(f"{user_info} 被 {user.mention_markdown_v2()} 驱离",
|
||||||
|
parse_mode=ParseMode.MARKDOWN_V2)
|
||||||
|
Log.info(f"用户 user_id[{user_id}] 在群 {chat.title}[{chat.id}] 被管理踢出")
|
||||||
|
elif result == "unban":
|
||||||
|
await callback_query.answer(text="解除驱离", show_alert=False)
|
||||||
|
await self.restore_member(context, chat.id, user_id)
|
||||||
|
if schedule := context.job_queue.scheduler.get_job(f"{chat.id}|{user_id}|clean_join"):
|
||||||
|
schedule.remove()
|
||||||
|
await message.edit_text(f"{user_info} 被 {user.mention_markdown_v2()} 解除驱离",
|
||||||
|
parse_mode=ParseMode.MARKDOWN_V2)
|
||||||
|
Log.info(f"用户 user_id[{user_id}] 在群 {chat.title}[{chat.id}] 被管理解除封禁")
|
||||||
|
else:
|
||||||
|
Log.warning(f"auth 模块 admin 函数 发现未知命令 result[{result}]")
|
||||||
|
await context.bot.send_message(chat.id, "派蒙这边收到了错误的消息!请检查详细日记!")
|
||||||
|
if schedule := context.job_queue.scheduler.get_job(f"{chat.id}|{user_id}|auth_kick"):
|
||||||
|
schedule.remove()
|
||||||
|
|
||||||
|
async def query(self, update: Update, context: CallbackContext) -> None:
|
||||||
|
|
||||||
|
async def query_callback(callback_query_data: str) -> Tuple[int, bool, str, str]:
|
||||||
|
_data = callback_query_data.split("|")
|
||||||
|
_user_id = int(_data[1])
|
||||||
|
_question_id = int(_data[2])
|
||||||
|
_answer_id = int(_data[3])
|
||||||
|
_answer = await self.quiz_service.get_answer(_answer_id)
|
||||||
|
_question = await self.quiz_service.get_question(_question_id)
|
||||||
|
_result = _answer.is_correct
|
||||||
|
_answer_encode = _answer.text
|
||||||
|
_question_encode = _question.text
|
||||||
|
Log.debug(f"query_callback函数返回 user_id[{_user_id}] result[{_result}] \n"
|
||||||
|
f"question_encode[{_question_encode}] answer_encode[{_answer_encode}]")
|
||||||
|
return _user_id, _result, _question_encode, _answer_encode
|
||||||
|
|
||||||
|
callback_query = update.callback_query
|
||||||
|
user = callback_query.from_user
|
||||||
|
message = callback_query.message
|
||||||
|
chat = message.chat
|
||||||
|
user_id, result, question, answer = await query_callback(callback_query.data)
|
||||||
|
Log.info(f"用户 {user.full_name}[{user.id}] 在群 {chat.title}[{chat.id}] 点击Auth认证命令 ")
|
||||||
|
if user.id != user_id:
|
||||||
|
await callback_query.answer(text="这不是你的验证!\n"
|
||||||
|
"再乱点再按我叫西风骑士团、千岩军和天领奉行了!", show_alert=True)
|
||||||
|
return
|
||||||
|
Log.info(f"用户 {user.full_name}[{user.id}] 在群 {chat.title}[{chat.id}] 认证结果为 {'通过' if result else '失败'}")
|
||||||
|
if result:
|
||||||
|
buttons = [[InlineKeyboardButton("驱离", callback_data=f"auth_admin|kick|{user.id}")]]
|
||||||
|
await callback_query.answer(text="验证成功", show_alert=False)
|
||||||
|
await self.restore_member(context, chat.id, user_id)
|
||||||
|
if schedule := context.job_queue.scheduler.get_job(f"{chat.id}|{user.id}|clean_join"):
|
||||||
|
schedule.remove()
|
||||||
|
text = f"{user.mention_markdown_v2()} 验证成功,向着星辰与深渊!\n" \
|
||||||
|
f"问题:{escape_markdown(question, version=2)} \n" \
|
||||||
|
f"回答:{escape_markdown(answer, version=2)}"
|
||||||
|
Log.info(f"用户 user_id[{user_id}] 在群 {chat.title}[{chat.id}] 验证成功")
|
||||||
|
else:
|
||||||
|
buttons = [[InlineKeyboardButton("驱离", callback_data=f"auth_admin|kick|{user.id}"),
|
||||||
|
InlineKeyboardButton("撤回驱离", callback_data=f"auth_admin|unban|{user.id}")]]
|
||||||
|
await callback_query.answer(text=f"验证失败,请在 {self.time_out} 秒后重试", show_alert=True)
|
||||||
|
await context.bot.ban_chat_member(chat_id=chat.id, user_id=user_id,
|
||||||
|
until_date=int(time.time()) + self.kick_time)
|
||||||
|
text = f"{user.mention_markdown_v2()} 验证失败,已经赶出提瓦特大陆!\n" \
|
||||||
|
f"问题:{escape_markdown(question, version=2)} \n" \
|
||||||
|
f"回答:{escape_markdown(answer, version=2)}"
|
||||||
|
Log.info(f"用户 user_id[{user_id}] 在群 {chat.title}[{chat.id}] 验证失败")
|
||||||
|
try:
|
||||||
|
await message.edit_text(text, reply_markup=InlineKeyboardMarkup(buttons), parse_mode=ParseMode.MARKDOWN_V2)
|
||||||
|
except BadRequest as exc:
|
||||||
|
if 'are exactly the same as ' in str(exc):
|
||||||
|
Log.warning("编辑消息发生异常,可能为用户点按多次键盘导致")
|
||||||
|
else:
|
||||||
|
raise exc
|
||||||
|
if schedule := context.job_queue.scheduler.get_job(f"{chat.id}|{user.id}|auth_kick"):
|
||||||
|
schedule.remove()
|
||||||
|
|
||||||
|
async def new_mem(self, update: Update, context: CallbackContext) -> None:
|
||||||
|
await self.refresh_quiz()
|
||||||
|
message = update.message
|
||||||
|
chat = message.chat
|
||||||
|
for user in message.new_chat_members:
|
||||||
|
if user.id == context.bot.id:
|
||||||
|
return
|
||||||
|
Log.info(f"用户 {user.full_name}[{user.id}] 尝试加入群 {chat.title}[{chat.id}]")
|
||||||
|
not_enough_rights = context.chat_data.get("not_enough_rights", False)
|
||||||
|
if not_enough_rights:
|
||||||
|
return
|
||||||
|
chat_administrators = await self.get_chat_administrators(context, chat_id=chat.id)
|
||||||
|
if self.is_admin(chat_administrators, message.from_user.id):
|
||||||
|
await message.reply_text("派蒙检测到管理员邀请,自动放行了!")
|
||||||
|
return
|
||||||
|
for user in message.new_chat_members:
|
||||||
|
if user.is_bot:
|
||||||
|
continue
|
||||||
|
question_id_list = await self.quiz_service.get_question_id_list()
|
||||||
|
if len(question_id_list) == 0:
|
||||||
|
await message.reply_text("旅行者!!!派蒙的问题清单你还没给我!!快去私聊我给我问题!")
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
await context.bot.restrict_chat_member(chat_id=message.chat.id, user_id=user.id,
|
||||||
|
permissions=ChatPermissions(can_send_messages=False))
|
||||||
|
except BadRequest as err:
|
||||||
|
if "Not enough rights" in str(err):
|
||||||
|
Log.warning(f"权限不够 chat_id[{message.chat_id}]")
|
||||||
|
# reply_message = await message.reply_markdown_v2(f"派蒙无法修改 {user.mention_markdown_v2()} 的权限!"
|
||||||
|
# f"请检查是否给派蒙授权管理了")
|
||||||
|
context.chat_data["not_enough_rights"] = True
|
||||||
|
# await context.bot.delete_message(chat.id, reply_message.message_id)
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
raise err
|
||||||
|
index = self.random.random(0, len(question_id_list))
|
||||||
|
question = await self.quiz_service.get_question(question_id_list[index])
|
||||||
|
buttons = [
|
||||||
|
[
|
||||||
|
InlineKeyboardButton(
|
||||||
|
answer.text,
|
||||||
|
callback_data=f"auth_challenge|{user.id}|{question['question_id']}|{answer['answer_id']}",
|
||||||
|
)
|
||||||
|
]
|
||||||
|
for answer in question.answers
|
||||||
|
]
|
||||||
|
random.shuffle(buttons)
|
||||||
|
buttons.append(
|
||||||
|
[
|
||||||
|
InlineKeyboardButton(
|
||||||
|
"放行",
|
||||||
|
callback_data=f"auth_admin|pass|{user.id}",
|
||||||
|
),
|
||||||
|
InlineKeyboardButton(
|
||||||
|
"驱离",
|
||||||
|
callback_data=f"auth_admin|kick|{user.id}",
|
||||||
|
),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
reply_message = f"*欢迎来到「提瓦特」世界!* \n" \
|
||||||
|
f"问题: {escape_markdown(question.text, version=2)} \n" \
|
||||||
|
f"请在 {self.time_out}S 内回答问题"
|
||||||
|
Log.debug(f"发送入群验证问题 question_id[{question.question_id}] question[{question.text}] \n"
|
||||||
|
f"给{user.full_name}[{user.id}] 在 {chat.title}[{chat.id}]")
|
||||||
|
try:
|
||||||
|
question_message = await message.reply_markdown_v2(reply_message,
|
||||||
|
reply_markup=InlineKeyboardMarkup(buttons))
|
||||||
|
except BadRequest as error:
|
||||||
|
await message.reply_text("派蒙分心了一下,不小心忘记你了,你只能先退出群再重新进来吧。")
|
||||||
|
raise error
|
||||||
|
context.job_queue.run_once(callback=self.kick_member_job, when=self.time_out,
|
||||||
|
name=f"{chat.id}|{user.id}|auth_kick", chat_id=chat.id, user_id=user.id,
|
||||||
|
job_kwargs={"replace_existing": True})
|
||||||
|
context.job_queue.run_once(callback=self.clean_message_job, when=self.time_out, data=message.message_id,
|
||||||
|
name=f"{chat.id}|{user.id}|auth_clean_join_message",
|
||||||
|
chat_id=chat.id, user_id=user.id,
|
||||||
|
job_kwargs={"replace_existing": True})
|
||||||
|
context.job_queue.run_once(callback=self.clean_message_job, when=self.time_out,
|
||||||
|
data=question_message.message_id,
|
||||||
|
name=f"{chat.id}|{user.id}|auth_clean_question_message",
|
||||||
|
chat_id=chat.id, user_id=user.id,
|
||||||
|
job_kwargs={"replace_existing": True})
|
@ -3,8 +3,10 @@ from typing import Optional
|
|||||||
|
|
||||||
from telegram.ext import CommandHandler, MessageHandler, filters, CallbackQueryHandler, Application, InlineQueryHandler
|
from telegram.ext import CommandHandler, MessageHandler, filters, CallbackQueryHandler, Application, InlineQueryHandler
|
||||||
|
|
||||||
|
from config import config
|
||||||
from logger import Log
|
from logger import Log
|
||||||
from plugins.base import NewChatMembersHandler
|
from plugins.base import NewChatMembersHandler
|
||||||
|
from plugins.system.auth import GroupJoiningVerification
|
||||||
from plugins.system.errorhandler import error_handler
|
from plugins.system.errorhandler import error_handler
|
||||||
from plugins.system.inline import Inline
|
from plugins.system.inline import Inline
|
||||||
from plugins.system.start import start, ping, reply_keyboard_remove, unknown_command
|
from plugins.system.start import start, ping, reply_keyboard_remove, unknown_command
|
||||||
@ -33,9 +35,11 @@ def register_plugin_handlers(application: Application):
|
|||||||
Log.info("正在加载动态插件管理器")
|
Log.info("正在加载动态插件管理器")
|
||||||
plugins_manager = PluginsManager()
|
plugins_manager = PluginsManager()
|
||||||
plugins_manager.add_exclude(["start", "auth", "inline", "errorhandler"]) # 忽略内置模块
|
plugins_manager.add_exclude(["start", "auth", "inline", "errorhandler"]) # 忽略内置模块
|
||||||
|
|
||||||
Log.info("正在加载插件")
|
Log.info("正在加载插件")
|
||||||
plugins_manager.refresh_list("plugins/genshin/*")
|
plugins_manager.refresh_list("plugins/genshin/*")
|
||||||
plugins_manager.refresh_list("plugins/system/*")
|
plugins_manager.refresh_list("plugins/system/*")
|
||||||
|
|
||||||
Log.info("加载插件管理器正在加载插件")
|
Log.info("加载插件管理器正在加载插件")
|
||||||
plugins_manager.import_module()
|
plugins_manager.import_module()
|
||||||
plugins_manager.add_handler(application)
|
plugins_manager.add_handler(application)
|
||||||
@ -44,6 +48,13 @@ def register_plugin_handlers(application: Application):
|
|||||||
inline = Inline()
|
inline = Inline()
|
||||||
new_chat_members_handler = NewChatMembersHandler()
|
new_chat_members_handler = NewChatMembersHandler()
|
||||||
|
|
||||||
|
if len(config.joining_verification_groups) >= 1:
|
||||||
|
auth = GroupJoiningVerification()
|
||||||
|
for chat_id in config.joining_verification_groups:
|
||||||
|
new_chat_members_handler.add_callback(auth.new_mem, chat_id)
|
||||||
|
add_handler(auth.query, query=r"^auth_challenge\|")
|
||||||
|
add_handler(auth.admin, query=r"^auth_admin\|")
|
||||||
|
|
||||||
add_handler(start, command="start")
|
add_handler(start, command="start")
|
||||||
add_handler(ping, command="ping")
|
add_handler(ping, command="ping")
|
||||||
# 调试功能
|
# 调试功能
|
||||||
@ -55,6 +66,6 @@ def register_plugin_handlers(application: Application):
|
|||||||
application.add_handler(MessageHandler(filters.COMMAND & filters.ChatType.PRIVATE, unknown_command))
|
application.add_handler(MessageHandler(filters.COMMAND & filters.ChatType.PRIVATE, unknown_command))
|
||||||
application.add_error_handler(error_handler, block=False)
|
application.add_error_handler(error_handler, block=False)
|
||||||
|
|
||||||
import_module(f"plugins.system.admin")
|
import_module("plugins.system.admin")
|
||||||
|
|
||||||
Log.info("插件加载成功")
|
Log.info("插件加载成功")
|
||||||
|
Loading…
Reference in New Issue
Block a user