import html from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update from telegram.constants import ChatMemberStatus, ParseMode from telegram.error import BadRequest from telegram.ext import CallbackQueryHandler, ContextTypes from telegram.helpers import mention_html import Database.sql.approve_sql as sql from Mikobot import DRAGONS, dispatcher from Mikobot.plugins.disable import DisableAbleCommandHandler from Mikobot.plugins.helper_funcs.chat_status import check_admin from Mikobot.plugins.helper_funcs.extraction import extract_user from Mikobot.plugins.log_channel import loggable @loggable @check_admin(is_user=True) async def approve(update: Update, context: ContextTypes.DEFAULT_TYPE): message = update.effective_message chat_title = message.chat.title chat = update.effective_chat args = context.args user = update.effective_user user_id = await extract_user(message, context, args) if not user_id: await message.reply_text( "I don't know who you're talking about, you're going to need to specify a user!", ) return "" try: member = await chat.get_member(user_id) except BadRequest: return "" if member.status in [ChatMemberStatus.ADMINISTRATOR, ChatMemberStatus.OWNER]: await message.reply_text( "User is already admin - locks, blocklists, and antiflood already don't apply to them.", ) return "" if sql.is_approved(message.chat_id, user_id): await message.reply_text( f"[{member.user.first_name}](tg://user?id={member.user.id}) is already approved in {chat_title}", parse_mode=ParseMode.MARKDOWN, ) return "" sql.approve(message.chat_id, user_id) await message.reply_text( f"[{member.user.first_name}](tg://user?id={member.user.id}) has been approved in {chat_title}! They will now be ignored by automated admin actions like locks, blocklists, and antiflood.", parse_mode=ParseMode.MARKDOWN, ) log_message = ( f"{html.escape(chat.title)}:\n" f"#APPROVED\n" f"Admin: {mention_html(user.id, user.first_name)}\n" f"User: {mention_html(member.user.id, member.user.first_name)}" ) return log_message @loggable @check_admin(is_user=True) async def disapprove(update: Update, context: ContextTypes.DEFAULT_TYPE): message = update.effective_message chat_title = message.chat.title chat = update.effective_chat args = context.args user = update.effective_user user_id = await extract_user(message, context, args) if not user_id: await message.reply_text( "I don't know who you're talking about, you're going to need to specify a user!", ) return "" try: member = await chat.get_member(user_id) except BadRequest: return "" if member.status in [ChatMemberStatus.ADMINISTRATOR, ChatMemberStatus.OWNER]: await message.reply_text("This user is an admin, they can't be unapproved.") return "" if not sql.is_approved(message.chat_id, user_id): await message.reply_text(f"{member.user.first_name} isn't approved yet!") return "" sql.disapprove(message.chat_id, user_id) await message.reply_text( f"{member.user.first_name} is no longer approved in {chat_title}.", ) log_message = ( f"{html.escape(chat.title)}:\n" f"#UNAPPROVED\n" f"Admin: {mention_html(user.id, user.first_name)}\n" f"User: {mention_html(member.user.id, member.user.first_name)}" ) return log_message @check_admin(is_user=True) async def approved(update: Update, context: ContextTypes.DEFAULT_TYPE): message = update.effective_message chat_title = message.chat.title chat = update.effective_chat msg = "The following users are approved.\n" approved_users = sql.list_approved(message.chat_id) if not approved_users: await message.reply_text(f"No users are approved in {chat_title}.") return "" else: for i in approved_users: member = await chat.get_member(int(i.user_id)) msg += f"- `{i.user_id}`: {member.user['first_name']}\n" await message.reply_text(msg, parse_mode=ParseMode.MARKDOWN) @check_admin(is_user=True) async def approval(update: Update, context: ContextTypes.DEFAULT_TYPE): message = update.effective_message chat = update.effective_chat args = context.args user_id = await extract_user(message, context, args) if not user_id: await message.reply_text( "I don't know who you're talking about, you're going to need to specify a user!", ) return "" member = await chat.get_member(int(user_id)) if sql.is_approved(message.chat_id, user_id): await message.reply_text( f"{member.user['first_name']} is an approved user. Locks, antiflood, and blocklists won't apply to them.", ) else: await message.reply_text( f"{member.user['first_name']} is not an approved user. They are affected by normal commands.", ) async def unapproveall(update: Update, context: ContextTypes.DEFAULT_TYPE): chat = update.effective_chat user = update.effective_user member = await chat.get_member(user.id) approved_users = sql.list_approved(chat.id) if not approved_users: await update.effective_message.reply_text( f"No users are approved in {chat.title}." ) return if member.status != ChatMemberStatus.OWNER and user.id not in DRAGONS: await update.effective_message.reply_text( "Only the chat owner can unapprove all users at once.", ) else: buttons = InlineKeyboardMarkup( [ [ InlineKeyboardButton( text="Unapprove all users", callback_data="unapproveall_user", ), ], [ InlineKeyboardButton( text="Cancel", callback_data="unapproveall_cancel", ), ], ], ) await update.effective_message.reply_text( f"Are you sure you would like to unapprove ALL users in {chat.title}? This action cannot be undone.", reply_markup=buttons, parse_mode=ParseMode.MARKDOWN, ) async def unapproveall_btn(update: Update, context: ContextTypes.DEFAULT_TYPE): query = update.callback_query chat = update.effective_chat message = update.effective_message member = await chat.get_member(query.from_user.id) if query.data == "unapproveall_user": if member.status == ChatMemberStatus.OWNER or query.from_user.id in DRAGONS: approved_users = sql.list_approved(chat.id) users = [int(i.user_id) for i in approved_users] for user_id in users: sql.disapprove(chat.id, user_id) await message.edit_text("Successfully Unapproved all user in this Chat.") return if member.status == "administrator": await query.answer("Only owner of the chat can do this.") if member.status == "member": await query.answer("You need to be admin to do this.") elif query.data == "unapproveall_cancel": if member.status == "creator" or query.from_user.id in DRAGONS: await message.edit_text( "Removing of all approved users has been cancelled." ) return "" if member.status == "administrator": await query.answer("Only owner of the chat can do this.") if member.status == "member": await query.answer("You need to be admin to do this.") __help__ = """ ➠ Sometimes, you might trust a user not to send unwanted content. Maybe not enough to make them admin, but you might be ok with locks, blacklists, and antiflood not applying to them. ➠ That's what approvals are for - approve of trustworthy users to allow them to send ➠ *Admin commands:* » /approval: Check a user's approval status in this chat. » /approve: Approve of a user. Locks, blacklists, and antiflood won't apply to them anymore. » /unapprove: Unapprove of a user. They will now be subject to locks, blacklists, and antiflood again. » /approved: List all approved users. » /unapproveall: Unapprove *ALL* users in a chat. This cannot be undone. """ APPROVE = DisableAbleCommandHandler("approve", approve, block=False) DISAPPROVE = DisableAbleCommandHandler("unapprove", disapprove, block=False) APPROVED = DisableAbleCommandHandler("approved", approved, block=False) APPROVAL = DisableAbleCommandHandler("approval", approval, block=False) UNAPPROVEALL = DisableAbleCommandHandler("unapproveall", unapproveall, block=False) UNAPPROVEALL_BTN = CallbackQueryHandler( unapproveall_btn, pattern=r"unapproveall_.*", block=False ) dispatcher.add_handler(APPROVE) dispatcher.add_handler(DISAPPROVE) dispatcher.add_handler(APPROVED) dispatcher.add_handler(APPROVAL) dispatcher.add_handler(UNAPPROVEALL) dispatcher.add_handler(UNAPPROVEALL_BTN) __mod_name__ = "APPROVALS" __command_list__ = ["approve", "unapprove", "approved", "approval"] __handlers__ = [APPROVE, DISAPPROVE, APPROVED, APPROVAL]