From 9e3d9f81c80bc47eb61154b4b618b7169484f500 Mon Sep 17 00:00:00 2001
From: AbOuLfOoOoOuF <67458252+AbOuLfOoOoOuF@users.noreply.github.com>
Date: Tue, 27 Jul 2021 06:15:40 +0000
Subject: [PATCH] admin: check admin perms before pin-unpin (#33)
check whether the admin has perms to pin messages before pinning or unpinnig
---
AstrakoBot/modules/admin.py | 545 ++++++++++++++++++++++++++++++++++++
tg_bot/modules/admin.py | 13 +
2 files changed, 558 insertions(+)
create mode 100644 AstrakoBot/modules/admin.py
diff --git a/AstrakoBot/modules/admin.py b/AstrakoBot/modules/admin.py
new file mode 100644
index 000000000..a3493d462
--- /dev/null
+++ b/AstrakoBot/modules/admin.py
@@ -0,0 +1,545 @@
+import html
+
+from telegram import ParseMode, Update
+from telegram.error import BadRequest
+from telegram.ext import CallbackContext, CommandHandler, Filters, run_async
+from telegram.utils.helpers import mention_html
+
+from AstrakoBot import SUDO_USERS, dispatcher
+from AstrakoBot.modules.disable import DisableAbleCommandHandler
+from AstrakoBot.modules.helper_funcs.chat_status import (
+ bot_admin,
+ can_pin,
+ can_promote,
+ connection_status,
+ user_admin,
+ ADMIN_CACHE,
+)
+
+from AstrakoBot.modules.helper_funcs.extraction import (
+ extract_user,
+ extract_user_and_text,
+)
+from AstrakoBot.modules.log_channel import loggable
+from AstrakoBot.modules.helper_funcs.alternate import send_message
+
+
+@connection_status
+@bot_admin
+@can_promote
+@user_admin
+@loggable
+def promote(update: Update, context: CallbackContext) -> str:
+ bot = context.bot
+ args = context.args
+
+ message = update.effective_message
+ chat = update.effective_chat
+ user = update.effective_user
+
+ promoter = chat.get_member(user.id)
+
+ if (
+ not (promoter.can_promote_members or promoter.status == "creator")
+ and user.id not in SUDO_USERS
+ ):
+ message.reply_text("You don't have the necessary rights to do that!")
+ return
+
+ user_id = extract_user(message, args)
+
+ if not user_id:
+ message.reply_text(
+ "You don't seem to be referring to a user or the ID specified is incorrect.."
+ )
+ return
+
+ try:
+ user_member = chat.get_member(user_id)
+ except:
+ return
+
+ if user_member.status == "administrator" or user_member.status == "creator":
+ message.reply_text("How am I meant to promote someone that's already an admin?")
+ return
+
+ if user_id == bot.id:
+ message.reply_text("I can't promote myself! Get an admin to do it for me.")
+ return
+
+ # set same perms as bot - bot can't assign higher perms than itself!
+ bot_member = chat.get_member(bot.id)
+
+ try:
+ bot.promoteChatMember(
+ chat.id,
+ user_id,
+ can_change_info=bot_member.can_change_info,
+ can_post_messages=bot_member.can_post_messages,
+ can_edit_messages=bot_member.can_edit_messages,
+ can_delete_messages=bot_member.can_delete_messages,
+ can_invite_users=bot_member.can_invite_users,
+ # can_promote_members=bot_member.can_promote_members,
+ can_restrict_members=bot_member.can_restrict_members,
+ can_pin_messages=bot_member.can_pin_messages,
+ )
+ except BadRequest as err:
+ if err.message == "User_not_mutual_contact":
+ message.reply_text("I can't promote someone who isn't in the group.")
+ else:
+ message.reply_text("An error occured while promoting.")
+ return
+
+ bot.sendMessage(
+ chat.id,
+ f"Sucessfully promoted {user_member.user.first_name or user_id}!",
+ parse_mode=ParseMode.HTML,
+ )
+
+ log_message = (
+ f"{html.escape(chat.title)}:\n"
+ f"#PROMOTED\n"
+ f"Admin: {mention_html(user.id, user.first_name)}\n"
+ f"User: {mention_html(user_member.user.id, user_member.user.first_name)}"
+ )
+
+ return log_message
+
+
+@connection_status
+@bot_admin
+@can_promote
+@user_admin
+@loggable
+def demote(update: Update, context: CallbackContext) -> str:
+ bot = context.bot
+ args = context.args
+
+ chat = update.effective_chat
+ message = update.effective_message
+ user = update.effective_user
+
+ demoter = chat.get_member(user.id)
+
+ if (
+ not (demoter.can_promote_members or demoter.status == "creator")
+ and user.id not in SUDO_USERS
+ ):
+ message.reply_text("You don't have the necessary rights to do that!")
+ return
+
+ user_id = extract_user(message, args)
+ if not user_id:
+ message.reply_text(
+ "You don't seem to be referring to a user or the ID specified is incorrect.."
+ )
+ return
+
+ try:
+ user_member = chat.get_member(user_id)
+ except:
+ return
+
+ if user_member.status == "creator":
+ message.reply_text("This person CREATED the chat, how would I demote them?")
+ return
+
+ if not user_member.status == "administrator":
+ message.reply_text("Can't demote what wasn't promoted!")
+ return
+
+ if user_id == bot.id:
+ message.reply_text("I can't demote myself! Get an admin to do it for me.")
+ return
+
+ try:
+ bot.promoteChatMember(
+ chat.id,
+ user_id,
+ can_change_info=False,
+ can_post_messages=False,
+ can_edit_messages=False,
+ can_delete_messages=False,
+ can_invite_users=False,
+ can_restrict_members=False,
+ can_pin_messages=False,
+ can_promote_members=False,
+ )
+
+ bot.sendMessage(
+ chat.id,
+ f"Sucessfully demoted {user_member.user.first_name or user_id}!",
+ parse_mode=ParseMode.HTML,
+ )
+
+ log_message = (
+ f"{html.escape(chat.title)}:\n"
+ f"#DEMOTED\n"
+ f"Admin: {mention_html(user.id, user.first_name)}\n"
+ f"User: {mention_html(user_member.user.id, user_member.user.first_name)}"
+ )
+
+ return log_message
+ except BadRequest:
+ message.reply_text(
+ "Could not demote. I might not be admin, or the admin status was appointed by another"
+ " user, so I can't act upon them!"
+ )
+ return
+
+
+@user_admin
+def refresh_admin(update, _):
+ try:
+ ADMIN_CACHE.pop(update.effective_chat.id)
+ except KeyError:
+ pass
+
+ update.effective_message.reply_text("Admins cache refreshed!")
+
+
+@connection_status
+@bot_admin
+@can_promote
+@user_admin
+def set_title(update: Update, context: CallbackContext):
+ bot = context.bot
+ args = context.args
+
+ chat = update.effective_chat
+ message = update.effective_message
+
+ user_id, title = extract_user_and_text(message, args)
+ try:
+ user_member = chat.get_member(user_id)
+ except:
+ return
+
+ if not user_id:
+ message.reply_text(
+ "You don't seem to be referring to a user or the ID specified is incorrect.."
+ )
+ return
+
+ if user_member.status == "creator":
+ message.reply_text(
+ "This person CREATED the chat, how can i set custom title for him?"
+ )
+ return
+
+ if user_member.status != "administrator":
+ message.reply_text(
+ "Can't set title for non-admins!\nPromote them first to set custom title!"
+ )
+ return
+
+ if user_id == bot.id:
+ message.reply_text(
+ "I can't set my own title myself! Get the one who made me admin to do it for me."
+ )
+ return
+
+ if not title:
+ message.reply_text("Setting blank title doesn't do anything!")
+ return
+
+ if len(title) > 16:
+ message.reply_text(
+ "The title length is longer than 16 characters.\nTruncating it to 16 characters."
+ )
+
+ try:
+ bot.setChatAdministratorCustomTitle(chat.id, user_id, title)
+ except BadRequest:
+ message.reply_text("Either they aren't promoted by me or you set a title text that is impossible to set.")
+ return
+
+ bot.sendMessage(
+ chat.id,
+ f"Sucessfully set title for {user_member.user.first_name or user_id}
"
+ f"to {html.escape(title[:16])}
!",
+ parse_mode=ParseMode.HTML,
+ )
+
+
+@bot_admin
+@can_pin
+@user_admin
+@loggable
+def pin(update: Update, context: CallbackContext) -> str:
+ bot = context.bot
+ args = context.args
+
+ user = update.effective_user
+ chat = update.effective_chat
+
+ is_group = chat.type != "private" and chat.type != "channel"
+ prev_message = update.effective_message.reply_to_message
+
+ is_silent = True
+ if len(args) >= 1:
+ is_silent = not (
+ args[0].lower() == "notify"
+ or args[0].lower() == "loud"
+ or args[0].lower() == "violent"
+ )
+
+ message = update.effective_message
+ pinner = chat.get_member(user.id)
+
+ if (
+ not (pinner.can_pin_messages or pinner.status == "creator")
+ and user.id not in SUDO_USERS
+ ):
+ message.reply_text("You don't have the necessary rights to do that!")
+ return
+
+ if prev_message and is_group:
+ try:
+ bot.pinChatMessage(
+ chat.id, prev_message.message_id, disable_notification=is_silent
+ )
+ except BadRequest as excp:
+ if excp.message == "Chat_not_modified":
+ pass
+ else:
+ raise
+ log_message = (
+ f"{html.escape(chat.title)}:\n"
+ f"#PINNED\n"
+ f"Admin: {mention_html(user.id, html.escape(user.first_name))}"
+ )
+
+ return log_message
+
+
+@bot_admin
+@can_pin
+@user_admin
+@loggable
+def unpin(update: Update, context: CallbackContext) -> str:
+ bot = context.bot
+ chat = update.effective_chat
+ user = update.effective_user
+ message = update.effective_message
+ unpinner = chat.get_member(user.id)
+
+ if (
+ not (unpinner.can_pin_messages or unpinner.status == "creator")
+ and user.id not in SUDO_USERS
+ ):
+ message.reply_text("You don't have the necessary rights to do that!")
+ return
+
+ try:
+ bot.unpinChatMessage(chat.id)
+ except BadRequest as excp:
+ if excp.message == "Chat_not_modified":
+ pass
+ else:
+ raise
+
+ log_message = (
+ f"{html.escape(chat.title)}:\n"
+ f"#UNPINNED\n"
+ f"Admin: {mention_html(user.id, html.escape(user.first_name))}"
+ )
+
+ return log_message
+
+
+@bot_admin
+@user_admin
+@connection_status
+def invite(update: Update, context: CallbackContext):
+ bot = context.bot
+ chat = update.effective_chat
+
+ if chat.username:
+ update.effective_message.reply_text(f"https://t.me/{chat.username}")
+ elif chat.type in [chat.SUPERGROUP, chat.CHANNEL]:
+ bot_member = chat.get_member(bot.id)
+ if bot_member.can_invite_users:
+ invitelink = bot.exportChatInviteLink(chat.id)
+ update.effective_message.reply_text(invitelink)
+ else:
+ update.effective_message.reply_text(
+ "I don't have access to the invite link, try changing my permissions!"
+ )
+ else:
+ update.effective_message.reply_text(
+ "I can only give you invite links for supergroups and channels, sorry!"
+ )
+
+
+@connection_status
+def adminlist(update: Update, context: CallbackContext):
+ chat = update.effective_chat # type: Optional[Chat]
+ user = update.effective_user # type: Optional[User]
+ args = context.args
+ bot = context.bot
+
+ if update.effective_message.chat.type == "private":
+ send_message(update.effective_message, "This command only works in Groups.")
+ return
+
+ chat = update.effective_chat
+ chat_id = update.effective_chat.id
+ chat_name = update.effective_message.chat.title
+
+ try:
+ msg = update.effective_message.reply_text(
+ "Fetching group admins...", parse_mode=ParseMode.HTML
+ )
+ except BadRequest:
+ msg = update.effective_message.reply_text(
+ "Fetching group admins...", quote=False, parse_mode=ParseMode.HTML
+ )
+
+ administrators = bot.getChatAdministrators(chat_id)
+ text = "Admins in {}:".format(html.escape(update.effective_chat.title))
+
+ bot_admin_list = []
+
+ for admin in administrators:
+ user = admin.user
+ status = admin.status
+ custom_title = admin.custom_title
+
+ if user.first_name == "":
+ name = "ā Deleted Account"
+ else:
+ name = "{}".format(
+ mention_html(
+ user.id, html.escape(user.first_name + " " + (user.last_name or ""))
+ )
+ )
+
+ if user.is_bot:
+ bot_admin_list.append(name)
+ administrators.remove(admin)
+ continue
+
+ # if user.username:
+ # name = escape_markdown("@" + user.username)
+ if status == "creator":
+ text += "\n š Creator:"
+ text += "\n ā¢
{}\n".format(name)
+
+ if custom_title:
+ text += f" āā {html.escape(custom_title)}
\n"
+
+ text += "\nš± Admins:"
+
+ custom_admin_list = {}
+ normal_admin_list = []
+
+ for admin in administrators:
+ user = admin.user
+ status = admin.status
+ custom_title = admin.custom_title
+
+ if user.first_name == "":
+ name = "ā Deleted Account"
+ else:
+ name = "{}".format(
+ mention_html(
+ user.id, html.escape(user.first_name + " " + (user.last_name or ""))
+ )
+ )
+ # if user.username:
+ # name = escape_markdown("@" + user.username)
+ if status == "administrator":
+ if custom_title:
+ try:
+ custom_admin_list[custom_title].append(name)
+ except KeyError:
+ custom_admin_list.update({custom_title: [name]})
+ else:
+ normal_admin_list.append(name)
+
+ for admin in normal_admin_list:
+ text += "\n ā¢
{}".format(admin)
+
+ for admin_group in custom_admin_list.copy():
+ if len(custom_admin_list[admin_group]) == 1:
+ text += "\n ā¢
{} | {}
".format(
+ custom_admin_list[admin_group][0], html.escape(admin_group)
+ )
+ custom_admin_list.pop(admin_group)
+
+ text += "\n"
+ for admin_group, value in custom_admin_list.items():
+ text += "\nšØ {}
".format(admin_group)
+ for admin in value:
+ text += "\n ā¢
{}".format(admin)
+ text += "\n"
+
+ text += "\nš¤ Bots:"
+ for each_bot in bot_admin_list:
+ text += "\n ā¢
{}".format(each_bot)
+
+ try:
+ msg.edit_text(text, parse_mode=ParseMode.HTML)
+ except BadRequest: # if original message is deleted
+ return
+
+
+__help__ = """
+ ā¢ `/admins`*:* list of admins in the chat
+
+*Admins only:*
+ ā¢ `/pin`*:* silently pins the message replied to - add `'loud'` or `'notify'` to give notifs to users
+ ā¢ `/unpin`*:* unpins the currently pinned message
+ ā¢ `/invitelink`*:* gets invitelink
+ ā¢ `/link`*:* same as invitelink
+ ā¢ `/promote`*:* promotes the user replied to
+ ā¢ `/demote`*:* demotes the user replied to
+ ā¢ `/title