# Ultroid - UserBot # Copyright (C) 2021-2025 TeamUltroid # # This file is a part of < https://github.com/TeamUltroid/Ultroid/ > # PLease read the GNU Affero General Public License in # . """ ✘ Commands Available - • `{i}a` or `{i}approve` Approve someone to PM. • `{i}da` or `{i}disapprove` Disapprove someone to PM. • `{i}block` Block someone. • `{i}unblock` | `{i}unblock all` Unblock someone. • `{i}nologpm` Stop logging messages from the user. • `{i}logpm` Start logging messages from the user. • `{i}startarchive` Archive new PMs. • `{i}stoparchive` Don't archive new PMs. • `{i}cleararchive` Unarchive all chats. • `{i}listapproved` List all approved PMs. """ import asyncio import re from os import remove from pyUltroid.dB import DEVLIST try: from tabulate import tabulate except ImportError: tabulate = None from telethon import events from telethon.errors import MessageNotModifiedError from telethon.tl.functions.contacts import ( BlockRequest, GetBlockedRequest, UnblockRequest, ) from telethon.tl.functions.messages import ReportSpamRequest from telethon.utils import get_display_name, resolve_bot_file_id from pyUltroid.dB.base import KeyManager from . import * # ========================= CONSTANTS ============================= COUNT_PM = {} LASTMSG = {} WARN_MSGS = {} U_WARNS = {} if isinstance(udB.get_key("PMPERMIT"), (int, str)): value = [udB.get_key("PMPERMIT")] udB.set_key("PMPERMIT", value) keym = KeyManager("PMPERMIT", cast=list) Logm = KeyManager("LOGUSERS", cast=list) PMPIC = udB.get_key("PMPIC") LOG_CHANNEL = udB.get_key("LOG_CHANNEL") UND = get_string("pmperm_1") UNS = get_string("pmperm_2") NO_REPLY = get_string("pmperm_3") UNAPPROVED_MSG = "**PMSecurity of {ON}!**\n\n{UND}\n\nYou have {warn}/{twarn} warnings!" if udB.get_key("PM_TEXT"): UNAPPROVED_MSG = ( "**PMSecurity of {ON}!**\n\n" + udB.get_key("PM_TEXT") + "\n\nYou have {warn}/{twarn} warnings!" ) # 1 WARNS = udB.get_key("PMWARNS") or 4 PMCMDS = [ f"{HNDLR}a", f"{HNDLR}approve", f"{HNDLR}da", f"{HNDLR}disapprove", f"{HNDLR}block", f"{HNDLR}unblock", ] _not_approved = {} _to_delete = {} my_bot = asst.me.username def update_pm(userid, message, warns_given): try: WARN_MSGS.update({userid: message}) except KeyError: pass try: U_WARNS.update({userid: warns_given}) except KeyError: pass async def delete_pm_warn_msgs(chat: int): try: await _to_delete[chat].delete() except KeyError: pass # ================================================================= if udB.get_key("PMLOG"): @ultroid_cmd( pattern="logpm$", ) async def _(e): if not e.is_private: return await e.eor("`Use me in Private.`", time=3) if not Logm.contains(e.chat_id): return await e.eor("`Wasn't logging msgs from here.`", time=3) Logm.remove(e.chat_id) return await e.eor("`Now I Will log msgs from here.`", time=3) @ultroid_cmd( pattern="nologpm$", ) async def _(e): if not e.is_private: return await e.eor("`Use me in Private.`", time=3) if Logm.contains(e.chat_id): return await e.eor("`Wasn't logging msgs from here.`", time=3) Logm.add(e.chat_id) return await e.eor("`Now I Won't log msgs from here.`", time=3) @ultroid_bot.on( events.NewMessage( incoming=True, func=lambda e: e.is_private, ), ) async def permitpm(event): user = await event.get_sender() if user.bot or user.is_self or user.verified or Logm.contains(user.id): return await event.forward_to(udB.get_key("PMLOGGROUP") or LOG_CHANNEL) if udB.get_key("PMSETTING"): if udB.get_key("AUTOAPPROVE"): @ultroid_bot.on( events.NewMessage( outgoing=True, func=lambda e: e.is_private and e.out and not e.text.startswith(HNDLR), ), ) async def autoappr(e): miss = await e.get_chat() if miss.bot or miss.is_self or miss.verified or miss.id in DEVLIST: return if keym.contains(miss.id): return keym.add(miss.id) await delete_pm_warn_msgs(miss.id) try: await ultroid_bot.edit_folder(miss.id, folder=0) except BaseException: pass try: await asst.edit_message( LOG_CHANNEL, _not_approved[miss.id], f"#AutoApproved : OutGoing Message.\nUser : {inline_mention(miss, html=True)} [{miss.id}]", parse_mode="html", ) except KeyError: await asst.send_message( LOG_CHANNEL, f"#AutoApproved : OutGoing Message.\nUser : {inline_mention(miss, html=True)} [{miss.id}]", parse_mode="html", ) except MessageNotModifiedError: pass @ultroid_bot.on( events.NewMessage( incoming=True, func=lambda e: e.is_private and e.sender_id not in DEVLIST and not e.out and not e.sender.bot and not e.sender.is_self and not e.sender.verified, ) ) async def permitpm(event): inline_pm = Redis("INLINE_PM") or False user = event.sender if not keym.contains(user.id) and event.text != UND: if Redis("MOVE_ARCHIVE"): try: await ultroid_bot.edit_folder(user.id, folder=1) except BaseException as er: LOGS.info(er) if event.media and not udB.get_key("DISABLE_PMDEL"): await event.delete() name = user.first_name fullname = get_display_name(user) username = f"@{user.username}" mention = inline_mention(user) count = keym.count() try: wrn = COUNT_PM[user.id] + 1 await asst.edit_message( udB.get_key("LOG_CHANNEL"), _not_approved[user.id], f"Incoming PM from **{mention}** [`{user.id}`] with **{wrn}/{WARNS}** warning!", buttons=[ Button.inline("Approve PM", data=f"approve_{user.id}"), Button.inline("Block PM", data=f"block_{user.id}"), ], ) except KeyError: _not_approved[user.id] = await asst.send_message( udB.get_key("LOG_CHANNEL"), f"Incoming PM from **{mention}** [`{user.id}`] with **1/{WARNS}** warning!", buttons=[ Button.inline("Approve PM", data=f"approve_{user.id}"), Button.inline("Block PM", data=f"block_{user.id}"), ], ) wrn = 1 except MessageNotModifiedError: wrn = 1 if user.id in LASTMSG: prevmsg = LASTMSG[user.id] if event.text != prevmsg: if "PMSecurity" in event.text or "**PMSecurity" in event.text: return await delete_pm_warn_msgs(user.id) message_ = UNAPPROVED_MSG.format( ON=OWNER_NAME, warn=wrn, twarn=WARNS, UND=UND, name=name, fullname=fullname, username=username, count=count, mention=mention, ) update_pm(user.id, message_, wrn) if inline_pm: results = await ultroid_bot.inline_query( my_bot, f"ip_{user.id}" ) try: _to_delete[user.id] = await results[0].click( user.id, reply_to=event.id, hide_via=True ) except Exception as e: LOGS.info(str(e)) elif PMPIC: _to_delete[user.id] = await ultroid_bot.send_file( user.id, PMPIC, caption=message_, ) else: _to_delete[user.id] = await ultroid_bot.send_message( user.id, message_ ) else: await delete_pm_warn_msgs(user.id) message_ = UNAPPROVED_MSG.format( ON=OWNER_NAME, warn=wrn, twarn=WARNS, UND=UND, name=name, fullname=fullname, username=username, count=count, mention=mention, ) update_pm(user.id, message_, wrn) if inline_pm: try: results = await ultroid_bot.inline_query( my_bot, f"ip_{user.id}" ) _to_delete[user.id] = await results[0].click( user.id, reply_to=event.id, hide_via=True ) except Exception as e: LOGS.info(str(e)) elif PMPIC: _to_delete[user.id] = await ultroid_bot.send_file( user.id, PMPIC, caption=message_, ) else: _to_delete[user.id] = await ultroid_bot.send_message( user.id, message_ ) LASTMSG.update({user.id: event.text}) else: await delete_pm_warn_msgs(user.id) message_ = UNAPPROVED_MSG.format( ON=OWNER_NAME, warn=wrn, twarn=WARNS, UND=UND, name=name, fullname=fullname, username=username, count=count, mention=mention, ) update_pm(user.id, message_, wrn) if inline_pm: try: results = await ultroid_bot.inline_query( my_bot, f"ip_{user.id}" ) _to_delete[user.id] = await results[0].click( user.id, reply_to=event.id, hide_via=True ) except Exception as e: LOGS.info(str(e)) elif PMPIC: _to_delete[user.id] = await ultroid_bot.send_file( user.id, PMPIC, caption=message_, ) else: _to_delete[user.id] = await ultroid_bot.send_message( user.id, message_ ) LASTMSG.update({user.id: event.text}) if user.id not in COUNT_PM: COUNT_PM.update({user.id: 1}) else: COUNT_PM[user.id] = COUNT_PM[user.id] + 1 if COUNT_PM[user.id] >= WARNS: await delete_pm_warn_msgs(user.id) _to_delete[user.id] = await event.respond(UNS) try: del COUNT_PM[user.id] del LASTMSG[user.id] except KeyError: await asst.send_message( udB.get_key("LOG_CHANNEL"), "PMPermit is messed! Pls restart the bot!!", ) return LOGS.info("COUNT_PM is messed.") await ultroid_bot(BlockRequest(user.id)) await ultroid_bot(ReportSpamRequest(peer=user.id)) await asst.edit_message( udB.get_key("LOG_CHANNEL"), _not_approved[user.id], f"**{mention}** [`{user.id}`] was Blocked for spamming.", ) @ultroid_cmd(pattern="(start|stop|clear)archive$", fullsudo=True) async def _(e): x = e.pattern_match.group(1).strip() if x == "start": udB.set_key("MOVE_ARCHIVE", "True") await e.eor("Now I will move new Unapproved DM's to archive", time=5) elif x == "stop": udB.set_key("MOVE_ARCHIVE", "False") await e.eor("Now I won't move new Unapproved DM's to archive", time=5) elif x == "clear": try: await e.client.edit_folder(unpack=1) await e.eor("Unarchived all chats", time=5) except Exception as mm: await e.eor(str(mm), time=5) @ultroid_cmd(pattern="(a|approve)(?: |$)", fullsudo=True) async def approvepm(apprvpm): if apprvpm.reply_to_msg_id: user = (await apprvpm.get_reply_message()).sender elif apprvpm.is_private: user = await apprvpm.get_chat() else: return await apprvpm.edit(NO_REPLY) if user.id in DEVLIST: return await eor( apprvpm, "Lol, He is my Developer\nHe is auto Approved", ) if not keym.contains(user.id): keym.add(user.id) try: await delete_pm_warn_msgs(user.id) await apprvpm.client.edit_folder(user.id, folder=0) except BaseException: pass await eod( apprvpm, f"{inline_mention(user, html=True)} approved to PM!", parse_mode="html", ) try: await asst.edit_message( udB.get_key("LOG_CHANNEL"), _not_approved[user.id], f"#APPROVED\n\n{inline_mention(user, html=True)} [{user.id}] was approved to PM you!", buttons=[ Button.inline("Disapprove PM", data=f"disapprove_{user.id}"), Button.inline("Block", data=f"block_{user.id}"), ], parse_mode="html", ) except KeyError: _not_approved[user.id] = await asst.send_message( udB.get_key("LOG_CHANNEL"), f"#APPROVED\n\n{inline_mention(user, html=True)} [{user.id}] was approved to PM you!", buttons=[ Button.inline("Disapprove PM", data=f"disapprove_{user.id}"), Button.inline("Block", data=f"block_{user.id}"), ], parse_mode="html", ) except MessageNotModifiedError: pass else: await apprvpm.eor("`User may already be approved.`", time=5) @ultroid_cmd(pattern="(da|disapprove)(?: |$)", fullsudo=True) async def disapprovepm(e): if e.reply_to_msg_id: user = (await e.get_reply_message()).sender elif e.is_private: user = await e.get_chat() else: return await e.edit(NO_REPLY) if user.id in DEVLIST: return await eor( e, "`Lol, He is my Developer\nHe Can't Be DisApproved.`", ) if keym.contains(user.id): keym.remove(user.id) await eod( e, f"{inline_mention(user, html=True)} Disapproved to PM!", parse_mode="html", ) try: await asst.edit_message( udB.get_key("LOG_CHANNEL"), _not_approved[user.id], f"#DISAPPROVED\n\n{inline_mention(user, html=True)} [{user.id}] was disapproved to PM you.", buttons=[ Button.inline("Approve PM", data=f"approve_{user.id}"), Button.inline("Block", data=f"block_{user.id}"), ], parse_mode="html", ) except KeyError: _not_approved[user.id] = await asst.send_message( udB.get_key("LOG_CHANNEL"), f"#DISAPPROVED\n\n{inline_mention(user, html=True)} [{user.id}] was disapproved to PM you.", buttons=[ Button.inline("Approve PM", data=f"approve_{user.id}"), Button.inline("Block", data=f"block_{user.id}"), ], parse_mode="html", ) except MessageNotModifiedError: pass else: await eod( e, f"{inline_mention(user, html=True)} was never approved!", parse_mode="html", ) @ultroid_cmd(pattern="block( (.*)|$)", fullsudo=True) async def blockpm(block): match = block.pattern_match.group(1).strip() if block.reply_to_msg_id: user = (await block.get_reply_message()).sender_id elif match: try: user = await block.client.parse_id(match) except Exception as er: return await block.eor(str(er)) elif block.is_private: user = block.chat_id else: return await eor(block, NO_REPLY, time=10) await block.client(BlockRequest(user)) aname = await block.client.get_entity(user) await block.eor(f"{inline_mention(aname)} [`{user}`] `has been blocked!`") try: keym.remove(user) except AttributeError: pass try: await asst.edit_message( udB.get_key("LOG_CHANNEL"), _not_approved[user], f"#BLOCKED\n\n{inline_mention(aname)} [`{user}`] has been **blocked**.", buttons=[ Button.inline("UnBlock", data=f"unblock_{user}"), ], ) except KeyError: _not_approved[user] = await asst.send_message( udB.get_key("LOG_CHANNEL"), f"#BLOCKED\n\n{inline_mention(aname)} [`{user}`] has been **blocked**.", buttons=[ Button.inline("UnBlock", data=f"unblock_{user}"), ], ) except MessageNotModifiedError: pass @ultroid_cmd(pattern="unblock( (.*)|$)", fullsudo=True) async def unblockpm(event): match = event.pattern_match.group(1).strip() reply = await event.get_reply_message() if reply: user = reply.sender_id elif match: if match == "all": msg = await event.eor(get_string("com_1")) u_s = await event.client(GetBlockedRequest(0, 0)) count = len(u_s.users) if not count: return await eor(msg, "__You have not blocked Anyone...__") for user in u_s.users: await asyncio.sleep(1) await event.client(UnblockRequest(user.id)) # GetBlockedRequest return 20 users at most. if count < 20: return await eor(msg, f"__Unblocked {count} Users!__") while u_s.users: u_s = await event.client(GetBlockedRequest(0, 0)) for user in u_s.users: await asyncio.sleep(3) await event.client(UnblockRequest(user.id)) count += len(u_s.users) return await eor(msg, f"__Unblocked {count} users.__") try: user = await event.client.parse_id(match) except Exception as er: return await event.eor(str(er)) elif event.is_private: user = event.chat_id else: return await event.eor(NO_REPLY, time=10) try: await event.client(UnblockRequest(user)) aname = await event.client.get_entity(user) await event.eor(f"{inline_mention(aname)} [`{user}`] `has been UnBlocked!`") except Exception as et: return await event.eor(f"ERROR - {et}") try: await asst.edit_message( udB.get_key("LOG_CHANNEL"), _not_approved[user], f"#UNBLOCKED\n\n{inline_mention(aname)} [`{user}`] has been **unblocked**.", buttons=[ Button.inline("Block", data=f"block_{user}"), ], ) except KeyError: _not_approved[user] = await asst.send_message( udB.get_key("LOG_CHANNEL"), f"#UNBLOCKED\n\n{inline_mention(aname)} [`{user}`] has been **unblocked**.", buttons=[ Button.inline("Block", data=f"block_{user}"), ], ) except MessageNotModifiedError: pass @ultroid_cmd(pattern="listapproved$", owner=True) async def list_approved(event): xx = await event.eor(get_string("com_1")) all = keym.get() if not all: return await xx.eor("`You haven't approved anyone yet!`", time=5) users = [] for i in all: try: name = get_display_name(await ultroid_bot.get_entity(i)) except BaseException: name = "" users.append([name.strip(), str(i)]) with open("approved_pms.txt", "w") as list_appr: if tabulate: list_appr.write( tabulate(users, headers=["UserName", "UserID"], showindex="always") ) else: text = "".join(f"[{user[-1]}] - {user[0]}" for user in users) list_appr.write(text) await event.reply( f"List of users approved by [{OWNER_NAME}](tg://user?id={OWNER_ID})", file="approved_pms.txt", ) await xx.delete() remove("approved_pms.txt") @callback( re.compile( b"approve_(.*)", ), from_users=[ultroid_bot.uid], ) async def apr_in(event): uid = int(event.data_match.group(1).decode("UTF-8")) if uid in DEVLIST: await event.edit("It's a dev! Approved!") if not keym.contains(uid): keym.add(uid) try: await ultroid_bot.edit_folder(uid, folder=0) except BaseException: pass try: user = await ultroid_bot.get_entity(uid) except BaseException: return await event.delete() await event.edit( f"#APPROVED\n\n{inline_mention(user, html=True)} [{user.id}] was approved to PM you!", buttons=[ [ Button.inline("Disapprove PM", data=f"disapprove_{uid}"), Button.inline("Block", data=f"block_{uid}"), ], ], parse_mode="html", ) await delete_pm_warn_msgs(uid) await event.answer("Approved.", alert=True) else: await event.edit( "`User may already be approved.`", buttons=[ [ Button.inline("Disapprove PM", data=f"disapprove_{uid}"), Button.inline("Block", data=f"block_{uid}"), ], ], ) @callback( re.compile( b"disapprove_(.*)", ), from_users=[ultroid_bot.uid], ) async def disapr_in(event): uid = int(event.data_match.group(1).decode("UTF-8")) if keym.contains(uid): keym.remove(uid) try: user = await ultroid_bot.get_entity(uid) except BaseException: return await event.delete() await event.edit( f"#DISAPPROVED\n\n{inline_mention(user, html=True)} [{user.id}] was disapproved to PM you!", buttons=[ [ Button.inline("Approve PM", data=f"approve_{uid}"), Button.inline("Block", data=f"block_{uid}"), ], ], parse_mode="html", ) await event.answer("Disapproved.", alert=True) else: await event.edit( "`User was never approved!`", buttons=[ [ Button.inline("Disapprove PM", data=f"disapprove_{uid}"), Button.inline("Block", data=f"block_{uid}"), ], ], ) @callback( re.compile( b"block_(.*)", ), from_users=[ultroid_bot.uid], ) async def blck_in(event): uid = int(event.data_match.group(1).decode("UTF-8")) try: await ultroid_bot(BlockRequest(uid)) except BaseException: pass try: user = await ultroid_bot.get_entity(uid) except BaseException: return await event.delete() await event.edit( f"BLOCKED\n\n{inline_mention(user, html=True)} [{user.id}] was blocked!", buttons=Button.inline("UnBlock", data=f"unblock_{uid}"), parse_mode="html", ) await event.answer("Blocked.", alert=True) @callback( re.compile( b"unblock_(.*)", ), from_users=[ultroid_bot.uid], ) async def unblck_in(event): uid = int(event.data_match.group(1).decode("UTF-8")) try: await ultroid_bot(UnblockRequest(uid)) except BaseException: pass try: user = await ultroid_bot.get_entity(uid) except BaseException: return await event.delete() await event.edit( f"#UNBLOCKED\n\n{inline_mention(user, html=True)} [{user.id}] was unblocked!", buttons=Button.inline("Block", data=f"block_{uid}"), parse_mode="html", ) await event.answer("Unblocked.", alert=True) @callback("deletedissht") async def ytfuxist(e): try: await e.answer("Deleted.") await e.delete() except BaseException: await ultroid_bot.delete_messages(e.chat_id, e.id) @in_pattern(re.compile("ip_(.*)"), owner=True) async def in_pm_ans(event): from_user = int(event.pattern_match.group(1).strip()) try: warns = U_WARNS[from_user] except Exception as e: LOGS.info(e) warns = "?" try: msg_ = WARN_MSGS[from_user] except KeyError: msg_ = "**PMSecurity of {OWNER_NAME}**" wrns = f"{warns}/{WARNS}" buttons = [ [ Button.inline("Warns", data=f"admin_only{from_user}"), Button.inline(wrns, data=f"don_{wrns}"), ] ] include_media = True mime_type, res = None, None cont = None try: ext = PMPIC.split(".")[-1].lower() except (AttributeError, IndexError): ext = None if ext in ["img", "jpg", "png"]: _type = "photo" mime_type = "image/jpg" elif ext in ["mp4", "mkv", "gif"]: mime_type = "video/mp4" _type = "gif" else: try: res = resolve_bot_file_id(PMPIC) except ValueError: pass if res: res = [ await event.builder.document( res, title="Inline PmPermit", description="~ @TeamUltroid", text=msg_, buttons=buttons, link_preview=False, ) ] else: _type = "article" include_media = False if not res: if include_media: cont = types.InputWebDocument(PMPIC, 0, mime_type, []) res = [ event.builder.article( title="Inline PMPermit.", type=_type, text=msg_, description="@TeamUltroid", include_media=include_media, buttons=buttons, thumb=cont, content=cont, ) ] await event.answer(res, switch_pm="• Ultroid •", switch_pm_param="start") @callback(re.compile("admin_only(.*)"), from_users=[ultroid_bot.uid]) async def _admin_tools(event): chat = int(event.pattern_match.group(1).strip()) await event.edit( buttons=[ [ Button.inline("Approve PM", data=f"approve_{chat}"), Button.inline("Block PM", data=f"block_{chat}"), ], [Button.inline("« Back", data=f"pmbk_{chat}")], ], ) @callback(re.compile("don_(.*)")) async def _mejik(e): data = e.pattern_match.group(1).strip().decode("utf-8").split("/") text = "👮‍♂ Warn Count : " + data[0] text += "\n🤖 Total Warn Count : " + data[1] await e.answer(text, alert=True) @callback(re.compile("pmbk_(.*)")) async def edt(event): from_user = int(event.pattern_match.group(1).strip()) try: warns = U_WARNS[from_user] except Exception as e: LOGS.info(str(e)) warns = "0" wrns = f"{warns}/{WARNS}" await event.edit( buttons=[ [ Button.inline("Warns", data=f"admin_only{from_user}"), Button.inline(wrns, data=f"don_{wrns}"), ] ], )