# 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}kickme` : Leaves the group. • `{i}date` : Show Calender. • `{i}listreserved` List all usernames (channels/groups) you own. • `{i}stats` : See your profile stats. • `{i}paste` - `Include long text / Reply to text file.` • `{i}info ` Reply to someone's msg. • `{i}invite ` Add user to the chat. • `{i}rmbg ` Remove background from that picture. • `{i}telegraph ` Upload media/text to telegraph. • `{i}json ` Get the json encoding of the message. • `{i}suggest or ` Create a Yes/No poll for the replied suggestion. • `{i}ipinfo ` : Get info about that IP address. • `{i}cpy ` Copy the replied message, with formatting. Expires in 24hrs. • `{i}pst` Paste the copied message, with formatting. • `{i}thumb ` : Download the thumbnail of the replied file. • `{i}getmsg ` Get messages from chats with forward/copy restrictions. """ import asyncio import calendar import html import io import os import pathlib import time from datetime import datetime as dt try: from PIL import Image except ImportError: Image = None from pyUltroid._misc._assistant import asst_cmd from pyUltroid.dB.gban_mute_db import is_gbanned from pyUltroid.fns.tools import get_chat_and_msgid from . import upload_file as uf from telethon.errors.rpcerrorlist import ChatForwardsRestrictedError, UserBotError from telethon.errors import MessageTooLongError from telethon.events import NewMessage from telethon.tl.custom import Dialog from telethon.tl.functions.channels import ( GetAdminedPublicChannelsRequest, InviteToChannelRequest, LeaveChannelRequest, ) from telethon.tl.functions.contacts import GetBlockedRequest from telethon.tl.functions.messages import AddChatUserRequest, GetAllStickersRequest from telethon.tl.functions.users import GetFullUserRequest from telethon.tl.types import ( Channel, Chat, InputMediaPoll, Poll, PollAnswer, TLObject, User, UserStatusOffline, UserStatusOnline, MessageMediaPhoto, MessageMediaDocument, DocumentAttributeVideo, ) from telethon.utils import get_peer_id from pyUltroid.fns.info import get_chat_info from . import ( HNDLR, LOGS, Image, ReTrieveFile, Telegraph, asst, async_searcher, bash, check_filename, eod, eor, get_paste, get_string, inline_mention, json_parser, mediainfo, udB, ultroid_cmd, ) # =================================================================# TMP_DOWNLOAD_DIRECTORY = "resources/downloads/" CAPTION_LIMIT = 1024 # Telegram's caption character limit for non-premium _copied_msg = {} @ultroid_cmd(pattern="kickme$", fullsudo=True) async def leave(ult): await ult.eor(f"`{ult.client.me.first_name} has left this group, bye!!.`") await ult.client(LeaveChannelRequest(ult.chat_id)) @ultroid_cmd( pattern="date$", ) async def date(event): m = dt.now().month y = dt.now().year d = dt.now().strftime("Date - %B %d, %Y\nTime- %H:%M:%S") k = calendar.month(y, m) await event.eor(f"`{k}\n\n{d}`") @ultroid_cmd( pattern="listreserved$", ) async def _(event): result = await event.client(GetAdminedPublicChannelsRequest()) if not result.chats: return await event.eor("`No username Reserved`") output_str = "".join( f"- {channel_obj.title} @{channel_obj.username} \n" for channel_obj in result.chats ) await event.eor(output_str) @ultroid_cmd( pattern="stats$", ) async def stats( event: NewMessage.Event, ): ok = await event.eor("`Collecting stats...`") start_time = time.time() private_chats = 0 bots = 0 groups = 0 broadcast_channels = 0 admin_in_groups = 0 creator_in_groups = 0 admin_in_broadcast_channels = 0 creator_in_channels = 0 unread_mentions = 0 unread = 0 dialog: Dialog async for dialog in event.client.iter_dialogs(): entity = dialog.entity if isinstance(entity, Channel) and entity.broadcast: broadcast_channels += 1 if entity.creator or entity.admin_rights: admin_in_broadcast_channels += 1 if entity.creator: creator_in_channels += 1 elif (isinstance(entity, Channel) and entity.megagroup) or isinstance( entity, Chat ): groups += 1 if entity.creator or entity.admin_rights: admin_in_groups += 1 if entity.creator: creator_in_groups += 1 elif isinstance(entity, User): private_chats += 1 if entity.bot: bots += 1 unread_mentions += dialog.unread_mentions_count unread += dialog.unread_count stop_time = time.time() - start_time try: ct = (await event.client(GetBlockedRequest(1, 0))).count except AttributeError: ct = 0 try: sp = await event.client(GetAllStickersRequest(0)) sp_count = len(sp.sets) except BaseException: sp_count = 0 full_name = inline_mention(event.client.me) response = f"🔸 **Stats for {full_name}** \n\n" response += f"**Private Chats:** {private_chats} \n" response += f"** •• **`Users: {private_chats - bots}` \n" response += f"** •• **`Bots: {bots}` \n" response += f"**Groups:** {groups} \n" response += f"**Channels:** {broadcast_channels} \n" response += f"**Admin in Groups:** {admin_in_groups} \n" response += f"** •• **`Creator: {creator_in_groups}` \n" response += f"** •• **`Admin Rights: {admin_in_groups - creator_in_groups}` \n" response += f"**Admin in Channels:** {admin_in_broadcast_channels} \n" response += f"** •• **`Creator: {creator_in_channels}` \n" response += f"** •• **`Admin Rights: {admin_in_broadcast_channels - creator_in_channels}` \n" response += f"**Unread:** {unread} \n" response += f"**Unread Mentions:** {unread_mentions} \n" response += f"**Blocked Users:** {ct}\n" response += f"**Total Stickers Pack Installed :** `{sp_count}`\n\n" response += f"**__It Took:__** {stop_time:.02f}s \n" await ok.edit(response) @ultroid_cmd(pattern="paste( (.*)|$)", manager=True, allow_all=True) async def _(event): try: input_str = event.text.split(maxsplit=1)[1] except IndexError: input_str = None xx = await event.eor("` 《 Pasting... 》 `") downloaded_file_name = None if input_str: message = input_str elif event.reply_to_msg_id: previous_message = await event.get_reply_message() if previous_message.media: downloaded_file_name = await event.client.download_media( previous_message, "./resources/downloads", ) with open(downloaded_file_name, "r") as fd: message = fd.read() os.remove(downloaded_file_name) else: message = previous_message.message else: message = None if not message: return await xx.eor( "`Reply to a Message/Document or Give me Some Text !`", time=5 ) done, data = await get_paste(message) if not done and data.get("error"): return await xx.eor(data["error"]) reply_text = ( f"• **Pasted to SpaceBin :** [Space]({data['link']})\n• **Raw Url :** : [Raw]({data['raw']})" ) try: if event.client._bot: return await xx.eor(reply_text) ok = await event.client.inline_query(asst.me.username, f"pasta-{data['link']}") await ok[0].click(event.chat_id, reply_to=event.reply_to_msg_id, hide_via=True) await xx.delete() except BaseException as e: LOGS.exception(e) await xx.edit(reply_text) @ultroid_cmd( pattern="info( (.*)|$)", manager=True, ) async def _(event): if match := event.pattern_match.group(1).strip(): try: user = await event.client.parse_id(match) except Exception as er: return await event.eor(str(er)) elif event.is_reply: rpl = await event.get_reply_message() user = rpl.sender_id else: user = event.chat_id xx = await event.eor(get_string("com_1")) try: _ = await event.client.get_entity(user) except Exception as er: return await xx.edit(f"**ERROR :** {er}") if not isinstance(_, User): try: peer = get_peer_id(_) photo, capt = await get_chat_info(_, event) if is_gbanned(peer): capt += "\n• Is Gbanned: True" if not photo: return await xx.eor(capt, parse_mode="html") await event.client.send_message( event.chat_id, capt[:1024], file=photo, parse_mode="html" ) await xx.delete() except Exception as er: await event.eor("**ERROR ON CHATINFO**\n" + str(er)) return try: full_user = (await event.client(GetFullUserRequest(user))).full_user except Exception as er: return await xx.edit(f"ERROR : {er}") user = _ user_photos = ( await event.client.get_profile_photos(user.id, limit=0) ).total or "NaN" user_id = user.id first_name = html.escape(user.first_name) if first_name is not None: first_name = first_name.replace("\u2060", "") last_name = user.last_name last_name = ( last_name.replace("\u2060", "") if last_name else ("Last Name not found") ) user_bio = full_user.about if user_bio is not None: user_bio = html.escape(full_user.about) common_chats = full_user.common_chats_count if user.photo: dc_id = user.photo.dc_id else: dc_id = "Need a Profile Picture to check this" caption = """Exᴛʀᴀᴄᴛᴇᴅ Dᴀᴛᴀ Fʀᴏᴍ Tᴇʟᴇɢʀᴀᴍ's Dᴀᴛᴀʙᴀsᴇ ••Tᴇʟᴇɢʀᴀᴍ ID: {} ••Pᴇʀᴍᴀɴᴇɴᴛ Lɪɴᴋ: Click Here ••Fɪʀsᴛ Nᴀᴍᴇ: {} ••Sᴇᴄᴏɴᴅ Nᴀᴍᴇ: {} ••Bɪᴏ: {} ••Dᴄ ID: {} ••Nᴏ. Oғ PғPs : {} ••Is Rᴇsᴛʀɪᴄᴛᴇᴅ: {} ••Vᴇʀɪғɪᴇᴅ: {} ••Is Pʀᴇᴍɪᴜᴍ: {} ••Is A Bᴏᴛ: {} ••Gʀᴏᴜᴘs Iɴ Cᴏᴍᴍᴏɴ: {} """.format( user_id, user_id, first_name, last_name, user_bio, dc_id, user_photos, user.restricted, user.verified, user.premium, user.bot, common_chats, ) if chk := is_gbanned(user_id): caption += f"""••Gʟᴏʙᴀʟʟʏ Bᴀɴɴᴇᴅ: True ••Rᴇᴀsᴏɴ: {chk}""" await event.client.send_message( event.chat_id, caption, reply_to=event.reply_to_msg_id, parse_mode="HTML", file=full_user.profile_photo, force_document=False, silent=True, ) await xx.delete() @ultroid_cmd( pattern="invite( (.*)|$)", groups_only=True, ) async def _(ult): xx = await ult.eor(get_string("com_1")) to_add_users = ult.pattern_match.group(1).strip() if not ult.is_channel and ult.is_group: for user_id in to_add_users.split(" "): try: await ult.client( AddChatUserRequest( chat_id=ult.chat_id, user_id=await ult.client.parse_id(user_id), fwd_limit=1000000, ), ) await xx.edit(f"Successfully invited `{user_id}` to `{ult.chat_id}`") except Exception as e: await xx.edit(str(e)) else: for user_id in to_add_users.split(" "): try: await ult.client( InviteToChannelRequest( channel=ult.chat_id, users=[await ult.client.parse_id(user_id)], ), ) await xx.edit(f"Successfully invited `{user_id}` to `{ult.chat_id}`") except UserBotError: await xx.edit( f"Bots can only be added as Admins in Channel.\nBetter Use `{HNDLR}promote {user_id}`" ) except Exception as e: await xx.edit(str(e)) @ultroid_cmd( pattern="rmbg($| (.*))", ) async def abs_rmbg(event): RMBG_API = udB.get_key("RMBG_API") if not RMBG_API: return await event.eor( "Get your API key from [here](https://www.remove.bg/) for this plugin to work.", ) match = event.pattern_match.group(1).strip() reply = await event.get_reply_message() if match and os.path.exists(match): dl = match elif reply and reply.media: if reply.document and reply.document.thumbs: dl = await reply.download_media(thumb=-1) else: dl = await reply.download_media() else: return await eod( event, f"Use `{HNDLR}rmbg` as reply to a pic to remove its background." ) if not (dl and dl.endswith(("webp", "jpg", "png", "jpeg"))): os.remove(dl) return await event.eor(get_string("com_4")) if dl.endswith("webp"): file = f"{dl}.png" Image.open(dl).save(file) os.remove(dl) dl = file xx = await event.eor("`Sending to remove.bg`") dn, out = await ReTrieveFile(dl) os.remove(dl) if not dn: dr = out["errors"][0] de = dr.get("detail", "") return await xx.edit( f"**ERROR ~** `{dr['title']}`,\n`{de}`", ) zz = Image.open(out) if zz.mode != "RGB": zz.convert("RGB") wbn = check_filename("ult-rmbg.webp") zz.save(wbn, "webp") await event.client.send_file( event.chat_id, out, force_document=True, reply_to=reply, ) await event.client.send_file(event.chat_id, wbn, reply_to=reply) os.remove(out) os.remove(wbn) await xx.delete() @ultroid_cmd( pattern="telegraph( (.*)|$)", ) async def telegraphcmd(event): xx = await event.eor(get_string("com_1")) match = event.pattern_match.group(1).strip() or "Ultroid" reply = await event.get_reply_message() if not reply: return await xx.eor("`Reply to Message.`") if not reply.media and reply.message: content = reply.message else: getit = await reply.download_media() dar = mediainfo(reply.media) if dar == "sticker": file = f"{getit}.png" Image.open(getit).save(file) os.remove(getit) getit = file elif dar.endswith("animated"): file = f"{getit}.gif" await bash(f"lottie_convert.py '{getit}' {file}") os.remove(getit) getit = file if "document" not in dar: try: nn = uf(getit) amsg = f"Uploaded to [Telegraph]({nn}) !" except Exception as e: amsg = f"Error : {e}" os.remove(getit) return await xx.eor(amsg) content = pathlib.Path(getit).read_text() os.remove(getit) makeit = Telegraph.create_page(title=match, content=[content]) await xx.eor( f"Pasted to Telegraph : [Telegraph]({makeit['url']})", link_preview=False ) @ultroid_cmd(pattern="json( (.*)|$)") async def _(event): reply_to_id = None match = event.pattern_match.group(1).strip() if event.reply_to_msg_id: msg = await event.get_reply_message() reply_to_id = event.reply_to_msg_id else: msg = event reply_to_id = event.message.id if match and hasattr(msg, match.split()[0]): msg = getattr(msg, match.split()[0]) try: if hasattr(msg, "to_json"): msg = msg.to_json(ensure_ascii=False, indent=1) elif hasattr(msg, "to_dict"): msg = json_parser(msg.to_dict(), indent=1) else: msg = TLObject.stringify(msg) except Exception: pass msg = str(msg) else: msg = json_parser(msg.to_json(), indent=1) if "-t" in match: try: data = json_parser(msg) msg = json_parser( {key: data[key] for key in data.keys() if data[key]}, indent=1 ) except Exception: pass if len(msg) > 4096: with io.BytesIO(str.encode(msg)) as out_file: out_file.name = "json-ult.txt" await event.client.send_file( event.chat_id, out_file, force_document=True, allow_cache=False, reply_to=reply_to_id, ) await event.delete() else: await event.eor(f"```{msg or None}```") @ultroid_cmd(pattern="suggest( (.*)|$)", manager=True) async def sugg(event): sll = event.text.split(maxsplit=1) try: text = sll[1] except IndexError: text = None if not (event.is_reply or text): return await eod( event, "`Please reply to a message to make a suggestion poll!`", ) if event.is_reply and not text: reply = await event.get_reply_message() if reply.text and len(reply.text) < 35: text = reply.text else: text = "Do you Agree to Replied Suggestion ?" reply_to = event.reply_to_msg_id if event.is_reply else event.id try: await event.client.send_file( event.chat_id, file=InputMediaPoll( poll=Poll( id=12345, question=text, answers=[PollAnswer("Yes", b"1"), PollAnswer("No", b"2")], ), ), reply_to=reply_to, ) except Exception as e: return await eod(event, f"`Oops, you can't send polls here!\n\n{e}`") await event.delete() @ultroid_cmd(pattern="ipinfo( (.*)|$)") async def ipinfo(event): ip = event.text.split() ipaddr = "" try: ipaddr = f"/{ip[1]}" except IndexError: ipaddr = "" det = await async_searcher(f"https://ipinfo.io{ipaddr}/geo", re_json=True) try: ip = det["ip"] city = det["city"] region = det["region"] country = det["country"] cord = det["loc"] try: zipc = det["postal"] except KeyError: zipc = "None" tz = det["timezone"] await eor( event, """ **IP Details Fetched.** **IP:** `{}` **City:** `{}` **Region:** `{}` **Country:** `{}` **Co-ordinates:** `{}` **Postal Code:** `{}` **Time Zone:** `{}` """.format( ip, city, region, country, cord, zipc, tz, ), ) except BaseException: err = det["error"]["title"] msg = det["error"]["message"] await event.eor(f"ERROR:\n{err}\n{msg}", time=5) @ultroid_cmd( pattern="cpy$", ) async def copp(event): msg = await event.get_reply_message() if not msg: return await event.eor(f"Use `{HNDLR}cpy` as reply to a message!", time=5) _copied_msg["CLIPBOARD"] = msg await event.eor(f"Copied. Use `{HNDLR}pst` to paste!", time=10) @asst_cmd(pattern="pst$") async def pepsodent(event): await toothpaste(event) @ultroid_cmd( pattern="pst$", ) async def colgate(event): await toothpaste(event) async def toothpaste(event): try: await event.respond(_copied_msg["CLIPBOARD"]) except KeyError: return await eod( event, f"Nothing was copied! Use `{HNDLR}cpy` as reply to a message first!", ) except Exception as ex: return await event.eor(str(ex), time=5) await event.delete() @ultroid_cmd(pattern="thumb$") async def thumb_dl(event): reply = await event.get_reply_message() if not (reply and reply.file): return await eod(event, get_string("th_1"), time=5) if not reply.file.media.thumbs: return await eod(event, get_string("th_2")) await event.eor(get_string("com_1")) x = await event.get_reply_message() m = await x.download_media(thumb=-1) await event.reply(file=m) os.remove(m) async def get_video_duration(file_path): cmd = [ "ffprobe", "-v", "error", "-show_entries", "format=duration", "-of", "default=noprint_wrappers=1:nokey=1", file_path, ] try: result = await asyncio.create_subprocess_exec( *cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE ) stdout, stderr = await result.communicate() duration = float(stdout.decode().strip()) return duration except Exception as e: print("Error running ffprobe:", e) return None async def get_thumbnail(file_path, thumbnail_path): try: await asyncio.create_subprocess_exec( "ffmpeg", "-i", file_path, "-ss", "00:00:04", "-vframes", "1", # Extract a single frame as the thumbnail thumbnail_path, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE, ) except Exception as e: print(f"Error extracting thumbnail: {e}") @ultroid_cmd(pattern="getmsg( ?(.*)|$)") async def get_restricted_msg(event): match = event.pattern_match.group(1).strip() if not match: await event.eor("`Please provide a link!`", time=5) return xx = await event.eor("`Loading...`") chat, msg = get_chat_and_msgid(match) if not (chat and msg): return await event.eor( "Invalid link!\nExamples:\n" "`https://t.me/TeamUltroid/3`\n" "`https://t.me/c/1313492028/3`\n" "`tg://openmessage?user_id=1234567890&message_id=1`" ) try: input_entity = await event.client.get_input_entity(chat) message = await event.client.get_messages(input_entity, ids=msg) except BaseException as er: return await event.eor(f"**ERROR**\n`{er}`") if not message: return await event.eor("`Message not found or may not exist.`") try: await event.client.send_message(event.chat_id, message) await xx.try_delete() return except ChatForwardsRestrictedError: pass if message.media: if isinstance(message.media, (MessageMediaPhoto, MessageMediaDocument)): media_path, _ = await event.client.fast_downloader(message.document, show_progress=True, event=xx, message=get_string("com_5")) caption = message.text or "" attributes = [] if message.video: duration = await get_video_duration(media_path.name) width, height = 0, 0 for attribute in message.document.attributes: if isinstance(attribute, DocumentAttributeVideo): width = attribute.w height = attribute.h break thumb_path = media_path.name + "_thumb.jpg" await get_thumbnail(media_path.name, thumb_path) attributes.append( DocumentAttributeVideo( duration=int(duration) if duration else 0, w=width, h=height, supports_streaming=True, ) ) await xx.edit(get_string("com_6")) media_path, _ = await event.client.fast_uploader(media_path.name, event=xx, show_progress=True, to_delete=True) try: await event.client.send_file( event.chat_id, media_path, caption=caption, force_document=False, supports_streaming=True if message.video else False, thumb=thumb_path if message.video else None, attributes=attributes if message.video else None, ) except MessageTooLongError: if len(caption) > CAPTION_LIMIT: caption = caption[:CAPTION_LIMIT] + "..." await event.client.send_file( event.chat_id, media_path, caption=caption, force_document=False, # Set to True if you want to send as a document supports_streaming=True if message.video else False, thumb=thumb_path if message.video else None, attributes=attributes if message.video else None, ) if message.video and os.path.exists(thumb_path): os.remove(thumb_path) await xx.try_delete() else: await event.eor("`Cannot process this type of media.`") else: await event.eor("`No media found in the message.`")