├── .gitignore ├── COPYING ├── Procfile ├── README.md ├── app.json ├── pyrobot ├── __init__.py ├── __main__.py ├── helper_functions │ ├── admin_check.py │ ├── check_if_thumb_exists.py │ ├── cust_p_filters.py │ ├── display_progress_dl_up.py │ ├── extract_link.py │ ├── extract_user.py │ ├── get_file_id.py │ ├── last_online_hlpr.py │ ├── msg_types.py │ ├── paste_bin_hlpr.py │ ├── run_shell_cmnd.py │ ├── scheme.py │ ├── sql_helpers │ │ ├── __init__.py │ │ ├── antiflood_sql.py │ │ ├── gDrive_sql.py │ │ ├── notes_sql.py │ │ └── welcome_sql.py │ ├── string_handling.py │ └── warn_hlprs │ │ └── remove_warn.py ├── plugins │ ├── admemes │ │ ├── antiflood.py │ │ ├── get_cached_media.py │ │ ├── pin_message.py │ │ ├── purge.py │ │ ├── show_file_id.py │ │ ├── telegraph.py │ │ └── whois.py │ ├── assistant │ │ ├── docs.py │ │ ├── inline.py │ │ └── test.py │ ├── call_back_button_s.py │ ├── default.py │ ├── memes │ │ ├── aesthetic.py │ │ ├── dart.py │ │ ├── dice.py │ │ └── runs.py │ ├── miss_rose_sgnak │ │ └── antichannelpin.py │ ├── notes │ │ ├── get.py │ │ ├── others.py │ │ └── save.py │ ├── restrictions │ │ ├── ban.py │ │ ├── restrict.py │ │ └── unban.py │ ├── tlifers │ │ ├── get.py │ │ ├── others.py │ │ └── save.py │ ├── tools │ │ ├── eval.py │ │ ├── exec.py │ │ ├── google.py │ │ ├── json.py │ │ ├── pastebin.py │ │ └── ping.py │ ├── up_utils │ │ ├── download.py │ │ ├── gDrive.py │ │ ├── thumbnail.py │ │ └── upload.py │ ├── warns │ │ ├── get_warns.py │ │ ├── reset_warn.py │ │ ├── set_warn_limit.py │ │ └── warn_user.py │ └── welcome │ │ ├── new_users.py │ │ ├── others.py │ │ └── save.py ├── pyrobot.py └── sample_config.py ├── requirements.txt └── runtime.txt /.gitignore: -------------------------------------------------------------------------------- 1 | venv 2 | config.env 3 | __pycache__ 4 | **/*.session 5 | .vscode 6 | /DLS 7 | # File used to manually test new changes, contains sensitive data 8 | /example/**.py 9 | 10 | OwneRoBot 11 | 12 | pyrobot/a.py 13 | pyrobot/uas 14 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | worker: python3 -m pyrobot -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Bot 🔥🤖 2 | 3 | A Telegram Bot ~~[still WIP, not stable]~~ based on [Pyrogram](https://github.com/pyrogram/pyrogram) 4 | 5 | >> This was an attempt to learn bot development using Pyrogram. I had made several mistakes (or, bad design decisions), in the creation of this bot. So, ✌️ please do not judge theis codes. 6 | 7 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the [COPYING](./COPYING) for more details. 8 | 9 | ## installing 10 | 11 | ## ENVironment VARiables 12 | 13 | #### Mandatory Environment Variables 14 | 15 | * `TG_BOT_TOKEN_BF_HER`: Create a bot using [@BotFather](https://telegram.dog/BotFather), and get the Telegram API token. 16 | 17 | * `APP_ID` 18 | * `API_HASH`: Get these two values from [my.telegram.org/apps](https://my.telegram.org/apps). 19 | * N.B.: if Telegram is blocked by your ISP, try our [Telegram bot](https://telegram.dog/UseTGXBot) to get the IDs. 20 | 21 | #### Optional Environment Variables 22 | 23 | * `ENV`: set this to `ANYTHING` if you want to use ENVIRON mode. 24 | 25 | * `COMMAND_HAND_LER`: the default value is `/`, which is the recommended value. Change this only __if__ you know what you are doing. 26 | 27 | * `TMP_DOWNLOAD_DIRECTORY`: the path (as a string) to store the temporary files, which are used by some of the plugins. 28 | 29 | * `DATABASE_URL`: ~~if you are using Heroku, this value is automatically filled by the Postgres Plugin.~~ if you are not using Heroku, Read the guide on how to Install Database?, in [the Wiki](https://github.com/SpEcHiDe/PyroGramBot/wiki/How-to-Install-Database-%3F). **[** __to be deprecated__ **]** 30 | 31 | * `TELEGRAM_URL`: create a Telegram Channel / Super Group, with you robot as administrator, and add the channel id as an integer in this variable. 32 | 33 | * `G_DRIVE_CLIENT_ID`: check [the Telegram Channel](https://t.me/UniBorg/48) for Instructions on Setting up Google Drive. 34 | 35 | * `G_DRIVE_CLIENT_SECRET`: check [the Telegram Channel](https://t.me/UniBorg/48) for Instructions on Setting up Google Drive. 36 | 37 | * `OWNER_ID`: this is not used currently, and might be used in the future. 38 | 39 | * `SUDO_USERS`: The Telegram user_ids who should be allowed to use the sensitive features of the robot. 40 | 41 | * `TG_IRU_S_M_ID`: this is used for the `sretlif` plugins. 42 | 43 | * `WARN_DATA_ID`: this is used for the `warns` plugins. 44 | 45 | * `WARN_SETTINGS_ID`: this is used for the `warns` plugins. 46 | 47 | * `A_PIN_MESSAGE_ID`: this is used for the `antichannelpin` plugins. 48 | 49 | * `USE_TG_BOT_APP_ID`: this is used for the google search plugin. you can get it by sending /getapi to [@useTGBot](https://useTGBot.t.me) 50 | 51 | ## Credits, and Thanks to 52 | 53 | * [Dan Tès](https://telegram.dog/haskell) for his [Pyrogram Library](https://github.com/pyrogram/pyrogram) 54 | * [Colin Shark](https://telegram.dog/ColinShark) for his [PyroBot](https://git.colinshark.de/PyroBot/PyroBot) 55 | * [![CopyLeft](https://telegra.ph/file/b514ed14d994557a724cb.jpg)](https://telegra.ph/file/fab1017e21c42a5c1e613.mp4 "CopyLeft Credit Video") 56 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "PyroGramBot", 3 | "description": "PyroGram Telegram User / Bot. Powered by @UserBotTalk", 4 | "keywords": [ 5 | "telegram", 6 | "best", 7 | "indian", 8 | "pyrogram", 9 | "userbot", 10 | "3", 11 | "plugin", 12 | "modular", 13 | "productivity" 14 | ], 15 | "repository": "https://github.com/SpEcHiDe/PyroGramBot", 16 | "success_url": "https://telegram.dog/UserBotTalk", 17 | "website": "https://github.com/SpEcHiDe/PyroGramBot", 18 | "env": { 19 | "ENV": { 20 | "description": "Setting this to ANYTHING will enable webhooks when in env mode", 21 | "value": "ANYTHING" 22 | }, 23 | "APP_ID": { 24 | "description": "Get this value from https://my.telegram.org" 25 | }, 26 | "API_HASH": { 27 | "description": "Get this value from https://my.telegram.org" 28 | }, 29 | "TG_BOT_TOKEN_BF_HER": { 30 | "description": "Get this value from @BotFather" 31 | }, 32 | "COMMAND_HAND_LER": { 33 | "description": "specify command handler that should be used for the plugins", 34 | "required": false 35 | } 36 | }, 37 | "addons": [{ 38 | "plan": "heroku-postgresql", 39 | "options": { 40 | "version": "12" 41 | } 42 | }], 43 | "buildpacks": [{ 44 | "url": "https://github.com/jonathanong/heroku-buildpack-ffmpeg-latest" 45 | }, { 46 | "url": "heroku/python" 47 | }], 48 | "formation": { 49 | "worker": { 50 | "quantity": 1, 51 | "size": "free" 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /pyrobot/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # (c) Shrimadhav U K 4 | 5 | # needed to appropriately, select ENV vars / Config vars 6 | import os 7 | 8 | # the logging things 9 | import logging 10 | 11 | from pyrobot.sample_config import Config 12 | 13 | 14 | logging.basicConfig( 15 | level=logging.DEBUG, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s" 16 | ) 17 | logging.getLogger("pyrogram").setLevel(logging.WARNING) 18 | 19 | 20 | # TODO: is there a better way? 21 | LOGGER = logging.getLogger(__name__) 22 | APP_ID = Config.APP_ID 23 | API_HASH = Config.API_HASH 24 | TG_COMPANION_BOT = Config.TG_COMPANION_BOT 25 | COMMAND_HAND_LER = Config.COMMAND_HAND_LER 26 | MAX_MESSAGE_LENGTH = Config.MAX_MESSAGE_LENGTH 27 | TMP_DOWNLOAD_DIRECTORY = Config.TMP_DOWNLOAD_DIRECTORY 28 | # create download directory, if not exist 29 | if not os.path.isdir(TMP_DOWNLOAD_DIRECTORY): 30 | os.makedirs(TMP_DOWNLOAD_DIRECTORY) 31 | DB_URI = Config.DB_URI 32 | TG_URI = int(Config.TG_URI) 33 | G_DRIVE_CLIENT_ID = Config.G_DRIVE_CLIENT_ID 34 | G_DRIVE_CLIENT_SECRET = Config.G_DRIVE_CLIENT_SECRET 35 | IS_BOT = True 36 | USE_AS_BOT = True 37 | OWNER_ID = Config.OWNER_ID 38 | SUDO_USERS = list(Config.SUDO_USERS) 39 | SUDO_USERS.append(OWNER_ID) 40 | SUDO_USERS = list(set(SUDO_USERS)) 41 | TG_MAX_SELECT_LEN = int(Config.TG_MAX_SELECT_LEN) 42 | TG_IRU_S_M_ID = int(Config.TG_IRU_S_M_ID) 43 | WARN_DATA_ID = int(Config.WARN_DATA_ID) 44 | WARN_SETTINGS_ID = int(Config.WARN_SETTINGS_ID) 45 | # define the "types" that should be uplaoded as streamable 46 | # copied from SpEcHiDe/UniBorg 47 | TL_VID_STREAM_TYPES = ("MP4", "WEBM", "MKV") 48 | TL_MUS_STREAM_TYPES = ("MP3", "WAV", "FLAC") 49 | TL_FF_NOAQ_TYPES = "WEBP" 50 | A_PIN_MESSAGE_ID = int(Config.A_PIN_MESSAGE_ID) 51 | LAYER_FEED_CHAT = Config.LAYER_FEED_CHAT 52 | if LAYER_FEED_CHAT: 53 | LAYER_FEED_CHAT = int(LAYER_FEED_CHAT) 54 | LAYER_UPDATE_INTERVAL = Config.LAYER_UPDATE_INTERVAL 55 | if LAYER_UPDATE_INTERVAL: 56 | LAYER_UPDATE_INTERVAL = int(LAYER_UPDATE_INTERVAL) 57 | LAYER_UPDATE_MESSAGE_CAPTION = Config.LAYER_UPDATE_MESSAGE_CAPTION 58 | 59 | # a dictionary to store different pastebin URIs 60 | paste_bin_store_s = { 61 | # "deldog": { 62 | # "URL": "https://del.dog/documents", 63 | # "GAS": "https://github.com/dogbin/dogbin", 64 | # }, 65 | "nekobin": { 66 | "URL": "https://nekobin.com/api/documents", 67 | "RAV": "result.key", 68 | "GAS": "https://github.com/nekobin/nekobin", 69 | }, 70 | "pasty": { 71 | "URL": "https://pasty.lus.pm/api/v2/pastes", 72 | "HEADERS": { 73 | "User-Agent": "PyroGramBot/6.9", 74 | "Content-Type": "application/json", 75 | }, 76 | "RAV": "id", 77 | "GAS": "https://github.com/lus/pasty", 78 | "AVDTS": "modificationToken", 79 | }, 80 | "pasting": { 81 | "URL": "https://pasting.codes/api", 82 | }, 83 | } 84 | 85 | TE_LEGRA_PH_DOMAIN = Config.TE_LEGRA_PH_DOMAIN 86 | -------------------------------------------------------------------------------- /pyrobot/__main__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2019-2020 Dan 2 | # 3 | # Permission is hereby granted, free of charge, to any person obtaining a 4 | # copy of this software and associated documentation files (the "Software"), 5 | # to deal in the Software without restriction, including without 6 | # limitation the rights to use, copy, modify, merge, publish, 7 | # distribute, sublicense, and/or sell copies of the Software, 8 | # and to permit persons to whom the Software is 9 | # furnished to do so, subject to the following conditions: 10 | # 11 | # The above copyright notice and this permission notice shall be 12 | # included in all copies or substantial portions of the Software. 13 | # 14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 19 | # ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 20 | # OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | from .pyrobot import PyroBot 23 | 24 | if __name__ == "__main__": 25 | PyroBot().run() 26 | -------------------------------------------------------------------------------- /pyrobot/helper_functions/admin_check.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | from pyrogram.types import Message 5 | from pyrogram.enums import ChatType, ChatMemberStatus 6 | 7 | 8 | async def admin_check(message: Message) -> bool: 9 | if not message.from_user: 10 | return False 11 | 12 | if message.chat.type not in [ChatType.SUPERGROUP, ChatType.CHANNEL]: 13 | return False 14 | 15 | if message.from_user.id in [ 16 | 777000, # Telegram Service Notifications 17 | 1087968824, # GroupAnonymousBot 18 | ]: 19 | return True 20 | 21 | client = message._client 22 | chat_id = message.chat.id 23 | user_id = message.from_user.id 24 | 25 | check_status = await client.get_chat_member(chat_id=chat_id, user_id=user_id) 26 | if check_status.status not in [ 27 | ChatMemberStatus.OWNER, 28 | ChatMemberStatus.ADMINISTRATOR 29 | ]: 30 | return False 31 | else: 32 | return True 33 | -------------------------------------------------------------------------------- /pyrobot/helper_functions/check_if_thumb_exists.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # (c) Shrimadhav U K 4 | 5 | 6 | import os 7 | import random 8 | import time 9 | from hachoir.metadata import extractMetadata 10 | from hachoir.parser import createParser 11 | from PIL import Image 12 | from pyrobot import TMP_DOWNLOAD_DIRECTORY, TL_VID_STREAM_TYPES 13 | from pyrobot.helper_functions.run_shell_cmnd import run_command 14 | 15 | 16 | async def is_thumb_image_exists(file_name: str): 17 | thumb_image_path = os.path.join(TMP_DOWNLOAD_DIRECTORY, "thumb_image.jpg") 18 | if os.path.exists(thumb_image_path): 19 | thumb_image_path = os.path.join(TMP_DOWNLOAD_DIRECTORY, "thumb_image.jpg") 20 | elif file_name and file_name.upper().endswith(TL_VID_STREAM_TYPES): 21 | metadata = extractMetadata(createParser(file_name)) 22 | duration = 0 23 | if metadata.has("duration"): 24 | duration = metadata.get("duration").seconds 25 | # get a random TTL from the duration 26 | ttl = str(random.randint(0, duration - 1)) 27 | # 28 | thumb_image_path = gen_tg_thumbnail(await take_screen_shot(file_name, ttl)) 29 | else: 30 | thumb_image_path = None 31 | return thumb_image_path 32 | 33 | 34 | async def take_screen_shot(file_name: str, ttl: str) -> str: 35 | out_put_file_name = os.path.join( 36 | os.path.dirname(file_name), ttl + "_" + str(time.time()) + ".jpg" 37 | ) 38 | file_genertor_command = [ 39 | "ffmpeg", 40 | "-ss", 41 | ttl, 42 | "-i", 43 | file_name, 44 | "-vframes", 45 | "1", 46 | out_put_file_name, 47 | ] 48 | stdout, stderr = await run_command(file_genertor_command) 49 | if os.path.lexists(out_put_file_name): 50 | return out_put_file_name 51 | return None 52 | 53 | 54 | def gen_tg_thumbnail(downloaded_file_name: str) -> str: 55 | # https://stackoverflow.com/a/21669827/4723940 56 | Image.open(downloaded_file_name).convert("RGB").save(downloaded_file_name) 57 | metadata = extractMetadata(createParser(downloaded_file_name)) 58 | height = 0 59 | if metadata.has("height"): 60 | height = metadata.get("height") 61 | # resize image 62 | # ref: https://t.me/PyrogramChat/44663 63 | img = Image.open(downloaded_file_name) 64 | # https://stackoverflow.com/a/37631799/4723940 65 | # img.thumbnail((320, 320)) 66 | img.resize((320, height)) 67 | img.save(downloaded_file_name, "JPEG") 68 | # https://pillow.readthedocs.io/en/3.1.x/reference/Image.html#create-thumbnails 69 | return downloaded_file_name 70 | -------------------------------------------------------------------------------- /pyrobot/helper_functions/cust_p_filters.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | 5 | from pyrogram import filters 6 | from pyrobot import SUDO_USERS, USE_AS_BOT 7 | from pyrobot.helper_functions.admin_check import admin_check 8 | 9 | 10 | def f_sudo_filter(filt, client, message): 11 | return bool( 12 | ( 13 | (message.from_user and message.from_user.id in SUDO_USERS) 14 | or (message.sender_chat and message.sender_chat.id in SUDO_USERS) 15 | ) 16 | and 17 | # t, lt, fl 2013 18 | not message.edit_date 19 | ) 20 | 21 | 22 | sudo_filter = filters.create(func=f_sudo_filter, name="SudoFilter") 23 | 24 | 25 | def onw_filter(filt, client, message): 26 | if USE_AS_BOT: 27 | return bool( 28 | True 29 | and # message.from_user.id in SUDO_USERS 30 | # t, lt, fl 2013 31 | not message.edit_date 32 | ) 33 | else: 34 | return bool( 35 | message.from_user 36 | and message.from_user.is_self 37 | and 38 | # t, lt, fl 2013 39 | not message.edit_date 40 | ) 41 | 42 | 43 | f_onw_fliter = filters.create(func=onw_filter, name="OnwFilter") 44 | 45 | 46 | async def admin_filter_f(filt, client, message): 47 | return ( 48 | # t, lt, fl 2013 49 | not message.edit_date 50 | and await admin_check(message) 51 | ) 52 | 53 | 54 | admin_fliter = filters.create(func=admin_filter_f, name="AdminFilter") 55 | -------------------------------------------------------------------------------- /pyrobot/helper_functions/display_progress_dl_up.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # (c) Shrimadhav U K 4 | 5 | 6 | import math 7 | import time 8 | from pyrogram.errors import MessageNotModified, FloodWait 9 | 10 | 11 | async def progress_for_pyrogram(current, total, ud_type, message, start): 12 | """ generic progress display for Telegram Upload / Download status """ 13 | now = time.time() 14 | diff = now - start 15 | if round(diff % 10.00) == 0 or current == total: 16 | # if round(current / total * 100, 0) % 5 == 0: 17 | percentage = current * 100 / total 18 | elapsed_time = round(diff) 19 | if elapsed_time == 0: 20 | return 21 | speed = current / diff 22 | time_to_completion = round((total - current) / speed) 23 | estimated_total_time = elapsed_time + time_to_completion 24 | 25 | elapsed_time = time_formatter(elapsed_time) 26 | estimated_total_time = time_formatter(estimated_total_time) 27 | 28 | progress = "[{0}{1}] \nP: {2}%\n".format( 29 | "".join(["█" for i in range(math.floor(percentage / 5))]), 30 | "".join(["░" for i in range(20 - math.floor(percentage / 5))]), 31 | round(percentage, 2), 32 | ) 33 | 34 | tmp = progress + "{0} of {1}\nSpeed: {2}/s\nETA: {3}\n".format( 35 | humanbytes(current), 36 | humanbytes(total), 37 | humanbytes(speed), 38 | # elapsed_time if elapsed_time != "" else "0 s", 39 | estimated_total_time if estimated_total_time != "" else "0 s", 40 | ) 41 | try: 42 | await message.edit(f"{ud_type}\n {tmp}") 43 | except (MessageNotModified, FloodWait): 44 | pass 45 | 46 | 47 | def humanbytes(size: int) -> str: 48 | """ converts bytes into human readable format """ 49 | # https://stackoverflow.com/a/49361727/4723940 50 | # 2**10 = 1024 51 | if not size: 52 | return "" 53 | power = 2 ** 10 54 | number = 0 55 | dict_power_n = {0: " ", 1: "Ki", 2: "Mi", 3: "Gi", 4: "Ti"} 56 | while size > power: 57 | size /= power 58 | number += 1 59 | return str(round(size, 2)) + " " + dict_power_n[number] + "B" 60 | 61 | 62 | def time_formatter(seconds: int) -> str: 63 | result = "" 64 | v_m = 0 65 | remainder = seconds 66 | r_ange_s = {"days": (24 * 60 * 60), "hours": (60 * 60), "minutes": 60, "seconds": 1} 67 | for age in r_ange_s: 68 | divisor = r_ange_s[age] 69 | v_m, remainder = divmod(remainder, divisor) 70 | v_m = int(v_m) 71 | if v_m != 0: 72 | result += f" {v_m} {age} " 73 | return result 74 | -------------------------------------------------------------------------------- /pyrobot/helper_functions/extract_link.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # (c) Shrimadhav U K 4 | 5 | from pyrogram.types import Message, MessageEntity 6 | from pyrogram.enums import MessageEntityType 7 | 8 | 9 | def extract_url_from_entity(entities: MessageEntity, text: str): 10 | url = None 11 | for entity in entities: 12 | if entity.type == MessageEntityType.TEXT_LINK: 13 | url = entity.url 14 | elif entity.type == MessageEntityType.URL: 15 | o_ = entity.offset 16 | l_ = entity.length 17 | url = text[o_ : o_ + l_] 18 | return url 19 | 20 | 21 | def extract_link(message: Message): 22 | custom_file_name = None 23 | url = None 24 | youtube_dl_username = None 25 | youtube_dl_password = None 26 | 27 | if message is None: 28 | url = None 29 | custom_file_name = None 30 | 31 | elif message.text is not None: 32 | if "|" in message.text: 33 | url_parts = message.text.split("|") 34 | if len(url_parts) == 2: 35 | url = url_parts[0] 36 | custom_file_name = url_parts[1] 37 | elif len(url_parts) == 4: 38 | url = url_parts[0] 39 | custom_file_name = url_parts[1] 40 | youtube_dl_username = url_parts[2] 41 | youtube_dl_password = url_parts[3] 42 | 43 | elif message.entities is not None: 44 | url = extract_url_from_entity(message.entities, message.text) 45 | 46 | else: 47 | url = message.text.strip() 48 | 49 | elif message.caption is not None: 50 | if "|" in message.caption: 51 | url_parts = message.caption.split("|") 52 | if len(url_parts) == 2: 53 | url = url_parts[0] 54 | custom_file_name = url_parts[1] 55 | elif len(url_parts) == 4: 56 | url = url_parts[0] 57 | custom_file_name = url_parts[1] 58 | youtube_dl_username = url_parts[2] 59 | youtube_dl_password = url_parts[3] 60 | 61 | elif message.caption_entities is not None: 62 | url = extract_url_from_entity(message.caption_entities, message.caption) 63 | 64 | else: 65 | url = message.caption.strip() 66 | 67 | elif message.entities is not None: 68 | url = message.text 69 | 70 | # trim blank spaces from the URL 71 | # might have some issues with #45 72 | if url is not None: 73 | url = url.strip() 74 | if custom_file_name is not None: 75 | custom_file_name = custom_file_name.strip() 76 | # https://stackoverflow.com/a/761825/4723940 77 | if youtube_dl_username is not None: 78 | youtube_dl_username = youtube_dl_username.strip() 79 | if youtube_dl_password is not None: 80 | youtube_dl_password = youtube_dl_password.strip() 81 | 82 | return url, custom_file_name, youtube_dl_username, youtube_dl_password 83 | -------------------------------------------------------------------------------- /pyrobot/helper_functions/extract_user.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | from pyrogram.types import Message, User, Chat 5 | from typing import Tuple, Union 6 | from pyrogram.enums import MessageEntityType 7 | 8 | 9 | def extract_user(message: Message) -> Tuple[int, str, Union[Chat, User]]: 10 | """extracts the user from a message""" 11 | user_id = None 12 | user_first_name = None 13 | aviyal = None 14 | 15 | if len(message.command) > 1: 16 | if ( 17 | len(message.entities) > 1 and 18 | message.entities[1].type == MessageEntityType.TEXT_MENTION 19 | ): 20 | # 0: is the command used 21 | # 1: should be the user specified 22 | required_entity = message.entities[1] 23 | user_id = required_entity.user.id 24 | user_first_name = required_entity.user.first_name 25 | aviyal = required_entity.user 26 | else: 27 | user_id = message.command[1] 28 | # don't want to make a request -_- 29 | user_first_name = user_id 30 | aviyal = True 31 | 32 | try: 33 | user_id = int(user_id) 34 | except ValueError: 35 | pass 36 | 37 | elif message.reply_to_message: 38 | user_id, user_first_name, aviyal = _eufm(message.reply_to_message) 39 | 40 | elif message: 41 | user_id, user_first_name, aviyal = _eufm(message) 42 | 43 | return (user_id, user_first_name, aviyal) 44 | 45 | 46 | def _eufm(message: Message) -> Tuple[int, str, Union[Chat, User]]: 47 | user_id = None 48 | user_first_name = None 49 | ithuenthoothengaa = None 50 | 51 | if message.from_user: 52 | ithuenthoothengaa = message.from_user 53 | user_id = ithuenthoothengaa.id 54 | user_first_name = ithuenthoothengaa.first_name 55 | 56 | elif message.sender_chat: 57 | ithuenthoothengaa = message.sender_chat 58 | user_id = ithuenthoothengaa.id 59 | user_first_name = ithuenthoothengaa.title 60 | 61 | return (user_id, user_first_name, ithuenthoothengaa) 62 | -------------------------------------------------------------------------------- /pyrobot/helper_functions/get_file_id.py: -------------------------------------------------------------------------------- 1 | # outdated code :( don't bother reading this shit 2 | 3 | from pyrogram.types import Message 4 | from pyrogram.types.messages_and_media import message 5 | 6 | 7 | def get_file_id(msg: Message): 8 | if msg.media: 9 | for message_type in ( 10 | "photo", 11 | "animation", 12 | "audio", 13 | "document", 14 | "video", 15 | "video_note", 16 | "voice", 17 | # "contact", 18 | # "dice", 19 | # "poll", 20 | # "location", 21 | # "venue", 22 | "sticker", 23 | ): 24 | obj = getattr(msg, message_type) 25 | if obj: 26 | setattr(obj, "message_type", message_type) 27 | return obj 28 | -------------------------------------------------------------------------------- /pyrobot/helper_functions/last_online_hlpr.py: -------------------------------------------------------------------------------- 1 | from pyrogram.types import User 2 | from datetime import datetime 3 | 4 | 5 | def last_online(from_user: User) -> str: 6 | time = "" 7 | if from_user.is_bot: 8 | time += "🤖 Bot :(" 9 | elif from_user.status == "recently": 10 | time += "Recently" 11 | elif from_user.status == "within_week": 12 | time += "Within the last week" 13 | elif from_user.status == "within_month": 14 | time += "Within the last month" 15 | elif from_user.status == "long_time_ago": 16 | time += "A long time ago :(" 17 | elif from_user.status == "online": 18 | time += "Currently Online" 19 | elif from_user.status == "offline": 20 | time += datetime.fromtimestamp(from_user.last_online_date).strftime( 21 | "%a, %d %b %Y, %H:%M:%S" 22 | ) 23 | return time 24 | -------------------------------------------------------------------------------- /pyrobot/helper_functions/msg_types.py: -------------------------------------------------------------------------------- 1 | # outdated code :( don't bother reading this shit 2 | 3 | from enum import IntEnum, unique 4 | from pyrogram.types import Message 5 | from pyrobot.helper_functions.string_handling import button_markdown_parser 6 | 7 | 8 | @unique 9 | class Types(IntEnum): 10 | TEXT = 0 11 | BUTTON_TEXT = 1 12 | STICKER = 2 13 | DOCUMENT = 3 14 | PHOTO = 4 15 | AUDIO = 5 16 | VOICE = 6 17 | VIDEO = 7 18 | VIDEO_NOTE = 8 19 | 20 | 21 | def get_note_type(msg: Message, split: int): 22 | data_type = None 23 | content = None 24 | text = "" 25 | raw_text = msg.text or msg.caption 26 | args = raw_text.split(None, split) 27 | # use python's maxsplit to separate cmd and args 28 | note_name = msg.command[1] 29 | 30 | buttons = [] 31 | # determine what the contents of the filter are - text, image, sticker, etc 32 | if len(args) >= (split + 1) and not msg.reply_to_message: 33 | text, buttons = button_markdown_parser(msg) 34 | if buttons: 35 | data_type = Types.BUTTON_TEXT 36 | else: 37 | data_type = Types.TEXT 38 | 39 | elif msg.reply_to_message: 40 | if len(args) >= split and msg.reply_to_message.text: 41 | # not caption, text 42 | text, buttons = button_markdown_parser(msg.reply_to_message) 43 | if buttons: 44 | data_type = Types.BUTTON_TEXT 45 | else: 46 | data_type = Types.TEXT 47 | 48 | elif msg.reply_to_message.sticker: 49 | content = msg.reply_to_message.sticker.file_id 50 | # stickers can't "officially" have captions in Telegram 51 | text, buttons = button_markdown_parser(msg) 52 | data_type = Types.STICKER 53 | 54 | elif msg.reply_to_message.document: 55 | content = msg.reply_to_message.document.file_id 56 | text, buttons = button_markdown_parser(msg.reply_to_message) 57 | data_type = Types.DOCUMENT 58 | 59 | elif msg.reply_to_message.photo: 60 | content = msg.reply_to_message.photo.file_id 61 | text, buttons = button_markdown_parser(msg.reply_to_message) 62 | data_type = Types.PHOTO 63 | 64 | elif msg.reply_to_message.audio: 65 | content = msg.reply_to_message.audio.file_id 66 | text, buttons = button_markdown_parser(msg.reply_to_message) 67 | data_type = Types.AUDIO 68 | 69 | elif msg.reply_to_message.voice: 70 | content = msg.reply_to_message.voice.file_id 71 | text, buttons = button_markdown_parser(msg.reply_to_message) 72 | data_type = Types.VOICE 73 | 74 | elif msg.reply_to_message.video: 75 | content = msg.reply_to_message.video.file_id 76 | text, buttons = button_markdown_parser(msg.reply_to_message) 77 | data_type = Types.VIDEO 78 | 79 | elif msg.reply_to_message.video_note: 80 | content = msg.reply_to_message.video_note.file_id 81 | # video notes can never have captions in Telegram 82 | text, buttons = button_markdown_parser(msg) 83 | data_type = Types.VIDEO_NOTE 84 | 85 | if split == 1 and note_name == "False": 86 | note_name = False 87 | 88 | return note_name, text, data_type, content, buttons 89 | 90 | 91 | def get_file_id(msg: Message): 92 | data_type = None 93 | content = None 94 | if msg.media: 95 | if msg.sticker: 96 | content = msg.sticker.file_id 97 | data_type = Types.STICKER 98 | 99 | elif msg.document: 100 | content = msg.document.file_id 101 | data_type = Types.DOCUMENT 102 | 103 | elif msg.photo: 104 | content = msg.photo.file_id 105 | data_type = Types.PHOTO 106 | 107 | elif msg.audio: 108 | content = msg.audio.file_id 109 | data_type = Types.AUDIO 110 | 111 | elif msg.voice: 112 | content = msg.voice.file_id 113 | data_type = Types.VOICE 114 | 115 | elif msg.video: 116 | content = msg.video.file_id 117 | data_type = Types.VIDEO 118 | 119 | elif msg.video_note: 120 | content = msg.video_note.file_id 121 | data_type = Types.VIDEO_NOTE 122 | 123 | return data_type, content 124 | -------------------------------------------------------------------------------- /pyrobot/helper_functions/paste_bin_hlpr.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # (c) @AlbertEinsteinTG 4 | 5 | import aiohttp 6 | from json import dumps 7 | from pyrogram.types import CallbackQuery 8 | from pyrobot import paste_bin_store_s 9 | from pyrobot.pyrobot import PyroBot 10 | 11 | 12 | async def del_pasty_ao(client: PyroBot, c_q: CallbackQuery): 13 | _, AlbertEinsteinTG_dt, pasty_id = c_q.data.split("_") 14 | if not c_q.message.reply_to_message: 15 | await c_q.answer( 16 | text=("cannot delete, " "if you deleted the original message"), 17 | show_alert=True, 18 | ) 19 | return False 20 | if c_q.from_user.id != c_q.message.reply_to_message.from_user.id: 21 | await c_q.answer(text=("this is not your paste!!"), show_alert=True) 22 | return False 23 | 24 | await c_q.answer(text="✅ deleted pasty.lus.pm") 25 | 26 | pasty = paste_bin_store_s.get("pasty") 27 | # only pasty.lus.pm supports 28 | # deletion at the moment 29 | async with aiohttp.ClientSession() as requests: 30 | AlbertEinsteinTG_url = f"{pasty.get('URL')}/{pasty_id}" 31 | AlbertEinsteinTG_headers = pasty.get("HEADERS") 32 | AlbertEinsteinTG_headers.update({ 33 | "Authorization": f"Bearer {AlbertEinsteinTG_dt}" 34 | }) 35 | await requests.request( 36 | method="DELETE", 37 | url=AlbertEinsteinTG_url, 38 | headers=AlbertEinsteinTG_headers, 39 | ) 40 | await c_q.message.edit_text( 41 | text=(f"❌ {c_q.message.text}"), reply_markup=None 42 | ) 43 | -------------------------------------------------------------------------------- /pyrobot/helper_functions/run_shell_cmnd.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # (c) Shrimadhav U K 4 | 5 | import asyncio 6 | from typing import List, Tuple 7 | 8 | 9 | async def run_command(shell_command: List) -> Tuple[str, str]: 10 | """executes a shell_command, 11 | and returns the stdout and stderr""" 12 | process = await asyncio.create_subprocess_exec( 13 | *shell_command, 14 | # stdout must a pipe to be accessible as process.stdout 15 | stdout=asyncio.subprocess.PIPE, 16 | stderr=asyncio.subprocess.PIPE, 17 | ) 18 | # Wait for the subprocess to finish 19 | stdout, stderr = await process.communicate() 20 | e_response = stderr.decode().strip() 21 | t_response = stdout.decode().strip() 22 | return t_response, e_response 23 | -------------------------------------------------------------------------------- /pyrobot/helper_functions/scheme.py: -------------------------------------------------------------------------------- 1 | """The entire code is copied without permission 2 | from https://github.com/Lonami/TelethonianBotExt/blob/master/layer.py 3 | modified to suit the needs of this application""" 4 | 5 | import asyncio 6 | import aiohttp 7 | import os 8 | from io import BytesIO 9 | from pyrogram import filters 10 | from pyrobot import LAYER_FEED_CHAT, LAYER_UPDATE_INTERVAL, LAYER_UPDATE_MESSAGE_CAPTION 11 | 12 | 13 | async def fetch(scheme_url: str): 14 | async with aiohttp.ClientSession() as session: 15 | response = await session.get(scheme_url) 16 | return str.encode(await response.text()) 17 | 18 | 19 | async def check_feed(client): 20 | layer_uri = ( 21 | "https://" 22 | "github.com" 23 | "/telegramdesktop/tdesktop/raw/dev/Telegram/Resources/tl/" 24 | "api.tl" 25 | ) 26 | last_hash = hash(await fetch(layer_uri)) 27 | while True: 28 | contents = await fetch(layer_uri) 29 | if hash(contents) != last_hash: 30 | file = BytesIO(contents) 31 | file.name = os.path.basename(layer_uri) 32 | message = await client.send_document( 33 | LAYER_FEED_CHAT, file, caption=LAYER_UPDATE_MESSAGE_CAPTION 34 | ) 35 | await message.pin(disable_notification=True) 36 | last_hash = hash(contents) 37 | await asyncio.sleep(LAYER_UPDATE_INTERVAL) 38 | -------------------------------------------------------------------------------- /pyrobot/helper_functions/sql_helpers/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # (c) Shrimadhav U K 4 | 5 | from sqlalchemy import create_engine 6 | from sqlalchemy.ext.declarative import declarative_base 7 | from sqlalchemy.orm import sessionmaker, scoped_session 8 | from pyrobot import DB_URI 9 | 10 | 11 | def start() -> scoped_session: 12 | engine = create_engine(DB_URI) 13 | BASE.metadata.bind = engine 14 | BASE.metadata.create_all(engine) 15 | return scoped_session(sessionmaker(bind=engine, autoflush=False)) 16 | 17 | 18 | BASE = declarative_base() 19 | SESSION = start() 20 | -------------------------------------------------------------------------------- /pyrobot/helper_functions/sql_helpers/antiflood_sql.py: -------------------------------------------------------------------------------- 1 | import threading 2 | from sqlalchemy import Column, Integer, String 3 | from pyrobot.helper_functions.sql_helpers import BASE, SESSION 4 | 5 | 6 | DEF_COUNT = 0 7 | DEF_LIMIT = 0 8 | DEF_OBJ = (None, DEF_COUNT, DEF_LIMIT) 9 | 10 | 11 | class FloodControl(BASE): 12 | __tablename__ = "antiflood" 13 | chat_id = Column(String(14), primary_key=True) 14 | user_id = Column(Integer) 15 | count = Column(Integer, default=DEF_COUNT) 16 | limit = Column(Integer, default=DEF_LIMIT) 17 | 18 | def __init__(self, chat_id): 19 | self.chat_id = str(chat_id) # ensure string 20 | 21 | def __repr__(self): 22 | return "" % self.chat_id 23 | 24 | 25 | FloodControl.__table__.create(checkfirst=True) 26 | 27 | INSERTION_LOCK = threading.RLock() 28 | 29 | CHAT_FLOOD = {} 30 | 31 | 32 | def set_flood(chat_id, amount): 33 | with INSERTION_LOCK: 34 | flood = SESSION.query(FloodControl).get(str(chat_id)) 35 | if not flood: 36 | flood = FloodControl(str(chat_id)) 37 | 38 | flood.user_id = None 39 | flood.limit = amount 40 | 41 | CHAT_FLOOD[str(chat_id)] = (None, DEF_COUNT, amount) 42 | 43 | SESSION.add(flood) 44 | SESSION.commit() 45 | 46 | 47 | def update_flood(chat_id: str, user_id) -> bool: 48 | if str(chat_id) in CHAT_FLOOD: 49 | curr_user_id, count, limit = CHAT_FLOOD.get(str(chat_id), DEF_OBJ) 50 | 51 | if limit == 0: # no antiflood 52 | return False 53 | 54 | if user_id != curr_user_id or user_id is None: # other user 55 | CHAT_FLOOD[str(chat_id)] = (user_id, DEF_COUNT + 1, limit) 56 | return False 57 | 58 | count += 1 59 | if count > limit: # too many msgs, kick 60 | CHAT_FLOOD[str(chat_id)] = (None, DEF_COUNT, limit) 61 | return True 62 | 63 | # default -> update 64 | CHAT_FLOOD[str(chat_id)] = (user_id, count, limit) 65 | return False 66 | 67 | 68 | def get_flood_limit(chat_id): 69 | return CHAT_FLOOD.get(str(chat_id), DEF_OBJ)[2] 70 | 71 | 72 | def migrate_chat(old_chat_id, new_chat_id): 73 | with INSERTION_LOCK: 74 | flood = SESSION.query(FloodControl).get(str(old_chat_id)) 75 | if flood: 76 | CHAT_FLOOD[str(new_chat_id)] = CHAT_FLOOD.get(str(old_chat_id), DEF_OBJ) 77 | flood.chat_id = str(new_chat_id) 78 | SESSION.commit() 79 | 80 | SESSION.close() 81 | 82 | 83 | def __load_flood_settings(): 84 | global CHAT_FLOOD 85 | try: 86 | all_chats = SESSION.query(FloodControl).all() 87 | CHAT_FLOOD = {chat.chat_id: (None, DEF_COUNT, chat.limit) for chat in all_chats} 88 | finally: 89 | SESSION.close() 90 | return CHAT_FLOOD 91 | -------------------------------------------------------------------------------- /pyrobot/helper_functions/sql_helpers/gDrive_sql.py: -------------------------------------------------------------------------------- 1 | import pickle 2 | import threading 3 | from sqlalchemy import Column, Integer, LargeBinary 4 | from pyrobot.helper_functions.sql_helpers import BASE, SESSION 5 | 6 | 7 | class gDriveCreds(BASE): 8 | __tablename__ = "gDrive" 9 | chat_id = Column(Integer, primary_key=True) 10 | credential_string = Column(LargeBinary) 11 | 12 | def __init__(self, chat_id): 13 | self.chat_id = chat_id 14 | 15 | 16 | gDriveCreds.__table__.create(checkfirst=True) 17 | 18 | INSERTION_LOCK = threading.RLock() 19 | 20 | 21 | def set_credential(chat_id, credential_string): 22 | with INSERTION_LOCK: 23 | saved_cred = SESSION.query(gDriveCreds).get(chat_id) 24 | if not saved_cred: 25 | saved_cred = gDriveCreds(chat_id) 26 | 27 | saved_cred.credential_string = pickle.dumps(credential_string) 28 | 29 | SESSION.add(saved_cred) 30 | SESSION.commit() 31 | 32 | 33 | def get_credential(chat_id): 34 | with INSERTION_LOCK: 35 | saved_cred = SESSION.query(gDriveCreds).get(chat_id) 36 | creds = None 37 | # The gDrive table stores the user's access and refresh tokens, and is 38 | # created automatically when the authorization flow 39 | # completes for the first time. 40 | if saved_cred is not None: 41 | creds = pickle.loads(saved_cred.credential_string) 42 | return creds 43 | 44 | 45 | def clear_credential(chat_id): 46 | with INSERTION_LOCK: 47 | saved_cred = SESSION.query(gDriveCreds).get(chat_id) 48 | if saved_cred: 49 | SESSION.delete(saved_cred) 50 | SESSION.commit() 51 | -------------------------------------------------------------------------------- /pyrobot/helper_functions/sql_helpers/notes_sql.py: -------------------------------------------------------------------------------- 1 | import threading 2 | from sqlalchemy import Column, String, UnicodeText, Integer, func, distinct 3 | from pyrobot.helper_functions.sql_helpers import SESSION, BASE 4 | 5 | 6 | class Notes(BASE): 7 | __tablename__ = "notes" 8 | chat_id = Column(String(14), primary_key=True) 9 | name = Column(UnicodeText, primary_key=True) 10 | d_message_id = Column(Integer) 11 | 12 | def __init__(self, chat_id, name, d_message_id): 13 | self.chat_id = str(chat_id) # ensure string 14 | self.name = name 15 | self.d_message_id = d_message_id 16 | 17 | def __repr__(self): 18 | return "" % self.name 19 | 20 | 21 | Notes.__table__.create(checkfirst=True) 22 | 23 | NOTES_INSERTION_LOCK = threading.RLock() 24 | 25 | 26 | def add_note_to_db(chat_id, note_name, note_message_id): 27 | with NOTES_INSERTION_LOCK: 28 | prev = SESSION.query(Notes).get((str(chat_id), note_name)) 29 | if prev: 30 | SESSION.delete(prev) 31 | note = Notes(str(chat_id), note_name, note_message_id) 32 | SESSION.add(note) 33 | SESSION.commit() 34 | 35 | 36 | def get_note(chat_id, note_name): 37 | try: 38 | return SESSION.query(Notes).get((str(chat_id), note_name)) 39 | finally: 40 | SESSION.close() 41 | 42 | 43 | def rm_note(chat_id, note_name): 44 | with NOTES_INSERTION_LOCK: 45 | note = SESSION.query(Notes).get((str(chat_id), note_name)) 46 | if note: 47 | SESSION.delete(note) 48 | SESSION.commit() 49 | return True 50 | else: 51 | SESSION.close() 52 | return False 53 | 54 | 55 | def get_all_chat_notes(chat_id): 56 | try: 57 | return ( 58 | SESSION.query(Notes) 59 | .filter(Notes.chat_id == str(chat_id)) 60 | .order_by(Notes.name.asc()) 61 | .all() 62 | ) 63 | finally: 64 | SESSION.close() 65 | 66 | 67 | def num_notes(): 68 | try: 69 | return SESSION.query(Notes).count() 70 | finally: 71 | SESSION.close() 72 | 73 | 74 | def num_chats(): 75 | try: 76 | return SESSION.query(func.count(distinct(Notes.chat_id))).scalar() 77 | finally: 78 | SESSION.close() 79 | -------------------------------------------------------------------------------- /pyrobot/helper_functions/sql_helpers/welcome_sql.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy import Boolean, Column, Numeric 2 | from pyrobot.helper_functions.sql_helpers import SESSION, BASE 3 | 4 | 5 | class Welcome(BASE): 6 | __tablename__ = "welcome" 7 | chat_id = Column(Numeric, primary_key=True) 8 | should_clean_welcome = Column(Boolean, default=False) 9 | previous_welcome = Column(Numeric) 10 | f_mesg_id = Column(Numeric) 11 | 12 | def __init__(self, chat_id, should_clean_welcome, previous_welcome, f_mesg_id): 13 | self.chat_id = chat_id 14 | self.should_clean_welcome = should_clean_welcome 15 | self.previous_welcome = previous_welcome 16 | self.f_mesg_id = f_mesg_id 17 | 18 | 19 | Welcome.__table__.create(checkfirst=True) 20 | 21 | 22 | def get_current_welcome_settings(chat_id): 23 | try: 24 | return SESSION.query(Welcome).filter(Welcome.chat_id == chat_id).one() 25 | except: 26 | return None 27 | finally: 28 | SESSION.close() 29 | 30 | 31 | def add_welcome_setting(chat_id, should_clean_welcome, previous_welcome, f_mesg_id): 32 | adder = SESSION.query(Welcome).get(chat_id) 33 | if adder: 34 | adder.should_clean_welcome = should_clean_welcome 35 | adder.previous_welcome = previous_welcome 36 | adder.f_mesg_id = f_mesg_id 37 | else: 38 | adder = Welcome(chat_id, should_clean_welcome, previous_welcome, f_mesg_id) 39 | SESSION.add(adder) 40 | SESSION.commit() 41 | 42 | 43 | def rm_welcome_setting(chat_id): 44 | rem = SESSION.query(Welcome).get(chat_id) 45 | if rem: 46 | SESSION.delete(rem) 47 | SESSION.commit() 48 | 49 | 50 | def update_previous_welcome(chat_id, previous_welcome): 51 | row = SESSION.query(Welcome).get(chat_id) 52 | row.previous_welcome = previous_welcome 53 | # commit the changes to the DB 54 | SESSION.commit() 55 | -------------------------------------------------------------------------------- /pyrobot/helper_functions/string_handling.py: -------------------------------------------------------------------------------- 1 | import re 2 | import time 3 | from typing import List, Tuple 4 | from pyrogram.types import Message, InlineKeyboardButton 5 | from pyrobot import COMMAND_HAND_LER 6 | from datetime import datetime, timedelta 7 | 8 | # NOTE: the url \ escape may cause double escapes 9 | # match * (bold) (don't escape if in url) 10 | # match _ (italics) (don't escape if in url) 11 | # match ` (code) 12 | # match []() (markdown link) 13 | # else, escape *, _, `, and [ 14 | MATCH_MD = re.compile( 15 | r"\*(.*?)\*|" 16 | r"_(.*?)_|" 17 | r"`(.*?)`|" 18 | r"(?[*_`\[])" 20 | ) 21 | 22 | # regex to find []() links -> hyperlinks/buttons 23 | LINK_REGEX = re.compile(r"(? Tuple[str, List]: 28 | # offset = len(args[2]) - len(raw_text) 29 | # set correct offset relative to command + notename 30 | markdown_note = None 31 | if msg.media: 32 | if msg.caption: 33 | markdown_note = msg.caption.markdown 34 | else: 35 | markdown_note = msg.text.markdown 36 | note_data = "" 37 | buttons = [] 38 | if markdown_note is None: 39 | return note_data, buttons 40 | # 41 | if markdown_note.startswith(COMMAND_HAND_LER): 42 | args = markdown_note.split(None, 2) 43 | # use python's maxsplit to separate cmd and args 44 | markdown_note = args[2] 45 | prev = 0 46 | for match in BTN_URL_REGEX.finditer(markdown_note): 47 | # Check if btnurl is escaped 48 | n_escapes = 0 49 | to_check = match.start(1) - 1 50 | while to_check > 0 and markdown_note[to_check] == "\\": 51 | n_escapes += 1 52 | to_check -= 1 53 | 54 | # if even, not escaped -> create button 55 | if n_escapes % 2 == 0: 56 | # create a thruple with button label, url, and newline status 57 | if bool(match.group(4)) and buttons: 58 | buttons[-1].append( 59 | InlineKeyboardButton(text=match.group(2), url=match.group(3)) 60 | ) 61 | else: 62 | buttons.append( 63 | [InlineKeyboardButton(text=match.group(2), url=match.group(3))] 64 | ) 65 | note_data += markdown_note[prev : match.start(1)] 66 | prev = match.end(1) 67 | # if odd, escaped -> move along 68 | else: 69 | note_data += markdown_note[prev:to_check] 70 | prev = match.start(1) - 1 71 | else: 72 | note_data += markdown_note[prev:] 73 | 74 | return note_data, buttons 75 | 76 | 77 | def extract_time(time_val): 78 | if any(time_val.endswith(unit) for unit in ("s", "m", "h", "d")): 79 | unit = time_val[-1] 80 | time_num = time_val[:-1] # type: str 81 | if not time_num.isdigit(): 82 | return None 83 | 84 | if unit == "s": 85 | bantime = datetime.now() + timedelta(seconds=int(time_num)) 86 | elif unit == "m": 87 | bantime = datetime.now() + timedelta(minutes=int(time_num)) 88 | elif unit == "h": 89 | bantime = datetime.now() + timedelta(hours=int(time_num)) 90 | elif unit == "d": 91 | bantime = datetime.now() + timedelta(days=int(time_num)) 92 | else: 93 | # how even...? 94 | return None 95 | return bantime 96 | else: 97 | return None 98 | 99 | 100 | def format_welcome_caption(html_string, chat_member): 101 | return html_string.format( 102 | dc_id=chat_member.dc_id, 103 | first_name=chat_member.first_name, 104 | id=chat_member.id, 105 | last_name=chat_member.last_name, 106 | mention=chat_member.mention, 107 | username=chat_member.username, 108 | ) 109 | -------------------------------------------------------------------------------- /pyrobot/helper_functions/warn_hlprs/remove_warn.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | from pyrogram.types import CallbackQuery 4 | 5 | from pyrobot import WARN_DATA_ID 6 | from pyrobot.pyrobot import PyroBot 7 | 8 | 9 | async def remove_warn(client: PyroBot, c_q: CallbackQuery, user_id: str, warner: int): 10 | chat_id = str(c_q.message.chat.id) 11 | 12 | if chat_id not in client.warndatastore: 13 | client.warndatastore[chat_id] = {} 14 | 15 | DATA = client.warndatastore[chat_id] 16 | 17 | if c_q.from_user.id == warner: 18 | if DATA.get(user_id): 19 | up_l = DATA[user_id]["limit"] - 1 # up_l = updated limit 20 | if up_l > 0: 21 | DATA[user_id]["limit"] = up_l 22 | del DATA[user_id]["reason"][-1] 23 | else: 24 | DATA.pop(user_id) 25 | mention = f"" 26 | mention += c_q.from_user.first_name 27 | mention += "" 28 | text = f"{mention} removed this Warn." 29 | await c_q.edit_message_text(text) 30 | else: 31 | await c_q.edit_message_text("😕😕 This User does not have any Warn.") 32 | await c_q.answer() # ensure no spinny circle -_- 33 | else: 34 | await c_q.answer( 35 | ( 36 | "Only " 37 | f"{(await client.get_users(warner)).first_name} " 38 | "Can Remove this Warn" 39 | ), 40 | show_alert=True, 41 | ) 42 | 43 | client.warndatastore[chat_id] = DATA 44 | await client.save_public_store(WARN_DATA_ID, json.dumps(client.warndatastore)) 45 | -------------------------------------------------------------------------------- /pyrobot/plugins/admemes/antiflood.py: -------------------------------------------------------------------------------- 1 | """Set Antiflood 2 | Syntax: .setflood""" 3 | 4 | import asyncio 5 | from pyrogram import Client, filters 6 | from pyrogram.types import ChatPermissions 7 | from pyrobot import COMMAND_HAND_LER, DB_URI 8 | from pyrobot.helper_functions.cust_p_filters import admin_fliter 9 | 10 | if DB_URI is not None: 11 | import pyrobot.helper_functions.sql_helpers.antiflood_sql as sql 12 | 13 | CHAT_FLOOD = sql.__load_flood_settings() 14 | 15 | 16 | @Client.on_message( 17 | (filters.incoming & ~filters.service & ~admin_fliter), group=1 18 | ) 19 | async def check_flood(client, message): 20 | """ check all messages """ 21 | if DB_URI is None: 22 | return 23 | # 24 | if not CHAT_FLOOD: 25 | return 26 | if not str(message.chat.id) in CHAT_FLOOD: 27 | return 28 | # copy @chathelp_bot bio here -_- 29 | if not message.from_user: 30 | return 31 | should_ban = sql.update_flood(message.chat.id, message.from_user.id) 32 | if not should_ban: 33 | return 34 | try: 35 | await message.chat.restrict_member( 36 | user_id=message.from_user.id, permissions=ChatPermissions() 37 | ) 38 | except Exception as e: # pylint:disable=C0103,W0703 39 | no_admin_privilege_message = await message.reply_text( 40 | text=( 41 | "Automatic AntiFlooder\n" 42 | "@admin {} " 43 | "is flooding this chat.\n\n" 44 | "{}" 45 | ).format(message.from_user.id, message.from_user.first_name, str(e)) 46 | ) 47 | await asyncio.sleep(10) 48 | await no_admin_privilege_message.edit_text( 49 | text="https://t.me/c/1092696260/724970", disable_web_page_preview=True 50 | ) 51 | else: 52 | await client.send_message( 53 | chat_id=message.chat.id, 54 | text=( 55 | "Automatic AntiFlooder\n" 56 | "{} " 57 | "has been automatically restricted " 58 | "because he reached the defined flood limit. \n\n" 59 | "#FLOOD".format(message.from_user.id, message.from_user.first_name) 60 | ), 61 | reply_to_message_id=message.id, 62 | ) 63 | 64 | 65 | @Client.on_message(filters.command("setflood", COMMAND_HAND_LER) & admin_fliter) 66 | async def set_flood(_, message): 67 | """ /setflood command """ 68 | if len(message.command) == 2: 69 | input_str = message.command[1] 70 | try: 71 | sql.set_flood(message.chat.id, input_str) 72 | global CHAT_FLOOD 73 | CHAT_FLOOD = sql.__load_flood_settings() 74 | await message.reply_text( 75 | "Antiflood updated to {} in the current chat".format(input_str) 76 | ) 77 | except Exception as e: # pylint:disable=C0103,W0703 78 | await message.reply_text(str(e)) 79 | 80 | 81 | @Client.on_message(filters.command("flood", COMMAND_HAND_LER)) 82 | async def get_flood_settings(_, message): 83 | flood_limit = sql.get_flood_limit(message.chat.id) 84 | if flood_limit == 0: 85 | await message.reply_text("This chat is not currently enforcing flood control.") 86 | else: 87 | await message.reply_text( 88 | "This chat is currently " 89 | "enforcing flood control after " 90 | f"{flood_limit} messages. \n" 91 | "⚠️⚠️ Any users sending more than that amount of messages " 92 | "will be muted." 93 | ) 94 | -------------------------------------------------------------------------------- /pyrobot/plugins/admemes/get_cached_media.py: -------------------------------------------------------------------------------- 1 | # © BugHunterCodeLabs ™ 2 | # © bughunter0 3 | # 2021 4 | # Copyright https://en.m.wikipedia.org/wiki/Fair_use 5 | from pyrogram import Client, filters 6 | from pyrobot.helper_functions.cust_p_filters import sudo_filter 7 | 8 | 9 | @Client.on_message(filters.command(["findbyfileid"]) & sudo_filter) 10 | async def fine_by_file_id(_, message): 11 | if not message.reply_to_message: 12 | return 13 | stickerid = str(message.reply_to_message.text) 14 | try: 15 | await message.reply_cached_media(stickerid, quote=True) 16 | except Exception as error: 17 | await message.reply_text(str(error), quote=True) 18 | -------------------------------------------------------------------------------- /pyrobot/plugins/admemes/pin_message.py: -------------------------------------------------------------------------------- 1 | # credits: https://github.com/SpEcHiDe/PyroGramBot/pull/34 2 | 3 | from pyrogram import Client, filters 4 | from pyrogram.types import Message 5 | from pyrobot import COMMAND_HAND_LER 6 | from pyrobot.helper_functions.cust_p_filters import admin_fliter 7 | 8 | 9 | @Client.on_message(filters.command(["pin"], COMMAND_HAND_LER) & admin_fliter) 10 | async def pin(_, message: Message): 11 | if not message.reply_to_message: 12 | return 13 | args = message.text.lower().split() 14 | notify = not any(arg in args for arg in ('loud', 'notify')) 15 | await message.reply_to_message.pin(disable_notification=notify) 16 | 17 | 18 | @Client.on_message(filters.command(["unpin"], COMMAND_HAND_LER) & admin_fliter) 19 | async def unpin(_, message: Message): 20 | if not message.reply_to_message: 21 | return 22 | await message.reply_to_message.unpin() 23 | -------------------------------------------------------------------------------- /pyrobot/plugins/admemes/purge.py: -------------------------------------------------------------------------------- 1 | """Purge Messages 2 | Syntax: .purge""" 3 | 4 | import asyncio 5 | from pyrogram import Client, filters 6 | from pyrogram.enums import ChatType 7 | from pyrobot import COMMAND_HAND_LER, TG_MAX_SELECT_LEN 8 | from pyrobot.helper_functions.cust_p_filters import admin_fliter 9 | 10 | 11 | @Client.on_message(filters.command("purge", COMMAND_HAND_LER) & admin_fliter) 12 | async def purge(client, message): 13 | """ purge upto the replied message """ 14 | if message.chat.type not in [ChatType.SUPERGROUP, ChatType.CHANNEL]: 15 | # https://t.me/c/1312712379/84174 16 | return 17 | 18 | status_message = await message.reply_text("...", quote=True) 19 | await message.delete() 20 | message_ids = [] 21 | count_del_etion_s = 0 22 | 23 | if message.reply_to_message: 24 | for a_s_message_id in range( 25 | message.reply_to_message.id, message.id 26 | ): 27 | message_ids.append(a_s_message_id) 28 | if len(message_ids) == TG_MAX_SELECT_LEN: 29 | count_del_etion_s += await client.delete_messages( 30 | chat_id=message.chat.id, message_ids=message_ids, revoke=True 31 | ) 32 | message_ids = [] 33 | if len(message_ids) > 0: 34 | count_del_etion_s += await client.delete_messages( 35 | chat_id=message.chat.id, message_ids=message_ids, revoke=True 36 | ) 37 | 38 | await status_message.edit_text(f"deleted {count_del_etion_s} messages") 39 | await asyncio.sleep(5) 40 | await status_message.delete() 41 | -------------------------------------------------------------------------------- /pyrobot/plugins/admemes/show_file_id.py: -------------------------------------------------------------------------------- 1 | """Get id of the replied user 2 | Syntax: .id""" 3 | 4 | from pyrogram import Client, filters 5 | from pyrogram.enums import ChatType 6 | from pyrobot import COMMAND_HAND_LER 7 | from pyrobot.helper_functions.cust_p_filters import f_onw_fliter 8 | from pyrobot.helper_functions.get_file_id import get_file_id 9 | 10 | 11 | @Client.on_message(filters.command(["id"], COMMAND_HAND_LER) & f_onw_fliter) 12 | async def showid(client, message): 13 | chat_type = message.chat.type 14 | 15 | if chat_type == ChatType.PRIVATE: 16 | user_id = message.chat.id 17 | await message.reply_text(f"{user_id}", quote=True) 18 | 19 | elif chat_type in [ChatType.SUPERGROUP, ChatType.CHANNEL]: 20 | _id = "" 21 | _id += "Chat ID: " f"{message.chat.id}\n" 22 | if message.reply_to_message: 23 | _id += ( 24 | "Replied User ID: " 25 | f"{message.reply_to_message.from_user.id}\n" 26 | ) 27 | file_info = get_file_id(message.reply_to_message) 28 | if file_info: 29 | _id += ( 30 | f"{file_info.message_type}: " 31 | f"{file_info.file_id}\n" 32 | ) 33 | else: 34 | _id += "User ID: " f"{message.from_user.id}\n" 35 | file_info = get_file_id(message) 36 | if file_info: 37 | _id += ( 38 | f"{file_info.message_type}: " 39 | f"{file_info.file_id}\n" 40 | ) 41 | await message.reply_text(_id, quote=True) 42 | -------------------------------------------------------------------------------- /pyrobot/plugins/admemes/telegraph.py: -------------------------------------------------------------------------------- 1 | import os 2 | import shutil 3 | from pyrogram import Client, filters 4 | from telegraph import upload_file 5 | from pyrobot import TMP_DOWNLOAD_DIRECTORY, TE_LEGRA_PH_DOMAIN 6 | from pyrobot.helper_functions.cust_p_filters import sudo_filter 7 | from pyrobot.helper_functions.get_file_id import get_file_id 8 | 9 | 10 | @Client.on_message( 11 | filters.command([ 12 | "telegraph", 13 | "graphorg", 14 | ]) & sudo_filter 15 | ) 16 | async def telegraph(client, message): 17 | replied = message.reply_to_message 18 | if not replied: 19 | await message.reply_text("Reply to a supported media file") 20 | return 21 | file_info = get_file_id(replied) 22 | if not file_info: 23 | await message.reply_text("Not supported!") 24 | return 25 | _t = os.path.join(TMP_DOWNLOAD_DIRECTORY, str(replied.id)) 26 | if not os.path.isdir(_t): 27 | os.makedirs(_t) 28 | _t += "/" 29 | download_location = await replied.download(_t) 30 | try: 31 | response = upload_file(download_location) 32 | except Exception as document: 33 | await message.reply_text(message, text=document) 34 | else: 35 | await message.reply( 36 | f"{TE_LEGRA_PH_DOMAIN}{response[0]}", disable_web_page_preview=True 37 | ) 38 | finally: 39 | shutil.rmtree(_t, ignore_errors=True) 40 | -------------------------------------------------------------------------------- /pyrobot/plugins/admemes/whois.py: -------------------------------------------------------------------------------- 1 | """Get info about the replied user 2 | Syntax: .whois""" 3 | 4 | from io import BytesIO 5 | from time import time 6 | from datetime import datetime 7 | from pyrogram import Client, filters 8 | from pyrogram.types import Message, User, Chat 9 | from pyrogram.enums import ChatType 10 | from pyrogram.errors import UserNotParticipant 11 | from pyrobot import COMMAND_HAND_LER 12 | from pyrobot.helper_functions.extract_user import extract_user 13 | from pyrobot.helper_functions.cust_p_filters import f_onw_fliter 14 | from pyrogram.raw.functions.users import GetFullUser 15 | from pyrogram.raw.functions.channels import GetFullChannel 16 | from pyrogram.raw.types import PhotoEmpty 17 | from pyrogram.file_id import FileId, FileType, ThumbnailSource 18 | from pyrogram.errors import ( 19 | PeerIdInvalid, 20 | ChannelInvalid, 21 | UserIdInvalid, 22 | UsernameNotOccupied 23 | ) 24 | 25 | 26 | @Client.on_message(filters.command(["whois", "info"], COMMAND_HAND_LER) & f_onw_fliter) 27 | async def who_is(client: Client, message: Message): 28 | """ extract user information """ 29 | status_message = await message.reply_text("🤔😳😳🙄") 30 | from_user = None 31 | from_user_id, _, thengaa = extract_user(message) 32 | 33 | small_user = None 34 | full_user = None 35 | profile_vid = None 36 | 37 | if thengaa or isinstance(thengaa, User): 38 | try: 39 | from_user = await client.invoke( 40 | GetFullUser( 41 | id=( 42 | await client.resolve_peer( 43 | from_user_id 44 | ) 45 | ) 46 | ) 47 | ) 48 | except UserIdInvalid: 49 | pass 50 | except PeerIdInvalid as error: 51 | return await status_message.edit(str(error)) 52 | 53 | if from_user: 54 | small_user = from_user.users[0] 55 | full_user = from_user.full_user 56 | from_user = User._parse(client, small_user) 57 | 58 | if ( 59 | not from_user and 60 | thengaa or isinstance(thengaa, Chat) 61 | ): 62 | try: 63 | from_user = await client.invoke( 64 | GetFullChannel( 65 | channel=( 66 | await client.resolve_peer( 67 | from_user_id 68 | ) 69 | ) 70 | ) 71 | ) 72 | except (ChannelInvalid, UsernameNotOccupied) as error: 73 | return await status_message.edit(str(error)) 74 | 75 | small_user = from_user.chats[0] 76 | full_user = from_user.full_chat 77 | from_user = Chat._parse_channel_chat(client, small_user) 78 | 79 | if not from_user and thengaa == True: 80 | return await status_message.edit("🏃🏻‍♂️🏃🏻‍♂️🏃🏻‍♂️") 81 | 82 | first_name = getattr( 83 | from_user, 84 | "title", 85 | getattr( 86 | from_user, 87 | "first_name", 88 | " " 89 | ) 90 | ) 91 | last_name = getattr(from_user, "last_name", "") 92 | username = from_user.username or "" 93 | 94 | message_out_str = "" 95 | 96 | if isinstance(from_user, User): 97 | message_out_str += ( 98 | "Name: " 99 | f"{first_name}" 100 | ) 101 | 102 | if isinstance(from_user, Chat): 103 | puzhu = message.reply_to_message or message 104 | message_out_str += ( 105 | "Name: " 106 | f"{first_name}" 107 | ) 108 | 109 | if last_name: 110 | message_out_str += f" {last_name}" 111 | message_out_str += "\n" 112 | 113 | if getattr(full_user, "private_forward_name", None): 114 | message_out_str += ( 115 | f"#Tg_Name: {full_user.private_forward_name}\n" 116 | ) 117 | 118 | if username: 119 | message_out_str += ( 120 | f"Username: @{username}\n" 121 | ) 122 | 123 | message_out_str += ( 124 | f"{type(from_user)} ID: {from_user.id}\n" 125 | ) 126 | 127 | if getattr(full_user, "about", None): 128 | message_out_str += ( 129 | f"About: {full_user.about}\n" 130 | ) 131 | 132 | if getattr(full_user, "common_chats_count", None): 133 | message_out_str += ( 134 | f"Groups in Common: {full_user.common_chats_count}\n" 135 | ) 136 | 137 | if ( 138 | isinstance(from_user, User) and 139 | message.chat.type in [ChatType.SUPERGROUP, ChatType.CHANNEL] 140 | ): 141 | try: 142 | chat_member_p = await message.chat.get_member(small_user.id) 143 | joined_date = (chat_member_p.joined_date or datetime.fromtimestamp( 144 | time() 145 | )).strftime("%Y.%m.%d %H:%M:%S") 146 | message_out_str += "Joined on: " f"{joined_date}" "\n" 147 | except UserNotParticipant: 148 | pass 149 | 150 | a, z, r, e, m = "_", "c", "t", "i", "d" 151 | msaurk = "".join([a, a, m, e, z, r, a, a]) 152 | rkmsau = getattr(from_user, msaurk, {}) 153 | for rohiv in rkmsau: 154 | hiorv = rkmsau[rohiv] 155 | if rohiv.startswith("is_") and hiorv: 156 | lavorvih = "✅" if hiorv else "❌" 157 | message_out_str += f"{rohiv[3:]}: " 158 | message_out_str += f"{lavorvih} " 159 | message_out_str += "\n" 160 | 161 | if len(message_out_str) < 334: 162 | plose = getattr( 163 | full_user, 164 | "settings", 165 | getattr( 166 | full_user, 167 | "available_reactions", 168 | None 169 | ) 170 | ) 171 | if plose: 172 | message_out_str += f"Available Reactions / User Settings: {len(plose)}\n" 173 | 174 | if getattr(full_user, "online_count", None): 175 | message_out_str += ( 176 | f"online count: {full_user.online_count}\n" 177 | ) 178 | 179 | if getattr(full_user, "pinned_msg_id", None): 180 | message_out_str += ( 181 | f"" 182 | "pinned msg" 183 | "\n" 184 | ) 185 | 186 | if getattr(full_user, "linked_chat_id", None): 187 | message_out_str += ( 188 | f"" 189 | "linked chat" 190 | "\n" 191 | ) 192 | 193 | chat_photo = from_user.photo 194 | 195 | if chat_photo: 196 | p_p_u_t = None 197 | tUpo = getattr( 198 | full_user, 199 | "profile_photo", 200 | getattr( 201 | full_user, 202 | "chat_photo", 203 | None 204 | ) 205 | ) 206 | profile_vid = None 207 | if tUpo and not isinstance(tUpo, PhotoEmpty): 208 | profile_vid = tUpo.video_sizes[0] if tUpo.video_sizes else None 209 | p_p_u_t = datetime.fromtimestamp( 210 | tUpo.date 211 | ).strftime( 212 | "%Y.%m.%d %H:%M:%S" 213 | ) 214 | message_out_str += f"Upload Date: {p_p_u_t}\n" 215 | if profile_vid: 216 | file_obj = BytesIO() 217 | async for chunk in client.stream_media( 218 | message=FileId( 219 | file_type=FileType.PHOTO, 220 | dc_id=tUpo.dc_id, 221 | media_id=tUpo.id, 222 | access_hash=tUpo.access_hash, 223 | file_reference=tUpo.file_reference, 224 | thumbnail_source=ThumbnailSource.THUMBNAIL, 225 | thumbnail_file_type=FileType.PHOTO, 226 | thumbnail_size=profile_vid.type, 227 | volume_id=0, 228 | local_id=0, 229 | ).encode(), 230 | ): 231 | file_obj.write(chunk) 232 | file_obj.name = "profile_vid_.mp4" 233 | await message.reply_video( 234 | video=file_obj, 235 | quote=True, 236 | caption=message_out_str, 237 | disable_notification=True, 238 | ) 239 | else: 240 | if isinstance(tUpo, PhotoEmpty): 241 | return await status_message.edit_text( 242 | text=message_out_str 243 | ) 244 | 245 | file_obj = BytesIO() 246 | async for chunk in client.stream_media( 247 | message=chat_photo.big_file_id, 248 | ): 249 | file_obj.write(chunk) 250 | file_obj.name = "profile_pic_.jpg" 251 | await message.reply_photo( 252 | photo=file_obj, 253 | quote=True, 254 | caption=message_out_str, 255 | disable_notification=True, 256 | ) 257 | else: 258 | await message.reply_text( 259 | text=message_out_str, quote=True, disable_notification=True 260 | ) 261 | 262 | await status_message.delete() 263 | -------------------------------------------------------------------------------- /pyrobot/plugins/assistant/docs.py: -------------------------------------------------------------------------------- 1 | # https://github.com/pyrogram/assistant/blob/343a43f/assistant/utils/docs.py 2 | # MIT License 3 | # 4 | # Copyright (c) 2019-present Dan 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in all 14 | # copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | # SOFTWARE. 23 | 24 | import re 25 | 26 | from pyrogram import filters, emoji, types 27 | from pyrogram.types import ( 28 | InlineQueryResultArticle, 29 | InputTextMessageContent, 30 | InlineKeyboardMarkup, 31 | InlineKeyboardButton, 32 | Object, 33 | LinkPreviewOptions 34 | ) 35 | from pyrogram.raw import types as raw_types, functions as raw_methods 36 | from pyrogram.raw.all import layer 37 | from pyrogram import Client 38 | from pyrogram.enums import ParseMode 39 | 40 | 41 | class Result: 42 | DESCRIPTION_MAX_LEN = 60 43 | 44 | @staticmethod 45 | def get_description(item): 46 | full = item.__doc__.split("\n")[0] 47 | short = full[: Result.DESCRIPTION_MAX_LEN].strip() 48 | 49 | if len(short) >= Result.DESCRIPTION_MAX_LEN - 1: 50 | short += "…" 51 | 52 | return short, full 53 | 54 | @staticmethod 55 | def snek(s: str): 56 | s = re.sub(r"(.)([A-Z][a-z]+)", r"\1_\2", s) 57 | return re.sub(r"([a-z0-9])([A-Z])", r"\1_\2", s).lower() 58 | 59 | class Method: 60 | DOCS = "https://telegramplayground.github.io/pyrogram/api/methods/{}" 61 | THUMB = "https://i.imgur.com/S5lY8fy.png" 62 | 63 | def __new__(cls, item): 64 | short, full = Result.get_description(item) 65 | doc_url = cls.DOCS.format(item.__name__) 66 | return InlineQueryResultArticle( 67 | title=f"{item.__name__}", 68 | description="Method - " + short, 69 | input_message_content=InputTextMessageContent( 70 | f"{emoji.CLOSED_BOOK} **Pyrogram Docs**\n\n" 71 | f"[{item.__name__}]({doc_url}) - Method\n\n" 72 | f"`{full}`\n", 73 | link_preview_options=LinkPreviewOptions( 74 | url=doc_url, 75 | show_above_text=True 76 | ), 77 | parse_mode=ParseMode.MARKDOWN 78 | ), 79 | thumb_url=cls.THUMB, 80 | ) 81 | 82 | class Decorator: 83 | DOCS = "https://telegramplayground.github.io/pyrogram/api/decorators#pyrogram.Client.{}" 84 | THUMB = "https://i.imgur.com/xp3jld1.png" 85 | 86 | def __new__(cls, item): 87 | short, full = Result.get_description(item) 88 | doc_url = cls.DOCS.format(item.__name__) 89 | 90 | return InlineQueryResultArticle( 91 | title=f"{item.__name__}", 92 | description="Decorator - " + short, 93 | input_message_content=InputTextMessageContent( 94 | f"{emoji.ARTIST_PALETTE} **Pyrogram Docs**\n\n" 95 | f"[{item.__name__}]({doc_url}) - Decorator\n\n" 96 | f"`{full}`\n", 97 | link_preview_options=LinkPreviewOptions( 98 | url=doc_url, 99 | show_above_text=True 100 | ), 101 | parse_mode=ParseMode.MARKDOWN 102 | ), 103 | thumb_url=cls.THUMB, 104 | ) 105 | 106 | class Type: 107 | DOCS = "https://telegramplayground.github.io/pyrogram/api/types/{}" 108 | THUMB = "https://i.imgur.com/dw1lLBX.png" 109 | 110 | def __new__(cls, item): 111 | short, full = Result.get_description(item) 112 | doc_url = cls.DOCS.format(item.__name__) 113 | 114 | return InlineQueryResultArticle( 115 | title=f"{item.__name__}", 116 | description="Type - " + short, 117 | input_message_content=InputTextMessageContent( 118 | f"{emoji.GREEN_BOOK} **Pyrogram Docs**\n\n" 119 | f"[{item.__name__}]({doc_url}) - Type\n\n" 120 | f"`{full}`", 121 | link_preview_options=LinkPreviewOptions( 122 | url=doc_url, 123 | show_above_text=True 124 | ), 125 | parse_mode=ParseMode.MARKDOWN 126 | ), 127 | thumb_url=cls.THUMB, 128 | ) 129 | 130 | class Filter: 131 | DOCS = "https://telegramplayground.github.io/pyrogram/api/filters#pyrogram.filters.{}" 132 | THUMB = "https://i.imgur.com/YRe6cKU.png" 133 | 134 | def __new__(cls, item): 135 | doc_url = cls.DOCS.format(item.__class__.__name__.lower()) 136 | return InlineQueryResultArticle( 137 | title=f"{item.__class__.__name__}", 138 | description=f"Filter - {item.__class__.__name__}", 139 | input_message_content=InputTextMessageContent( 140 | f"{emoji.CONTROL_KNOBS} **Pyrogram Docs**\n\n" 141 | f"[{item.__class__.__name__}]({doc_url}) - Filter", 142 | link_preview_options=LinkPreviewOptions( 143 | url=doc_url, 144 | show_above_text=True 145 | ), 146 | parse_mode=ParseMode.MARKDOWN 147 | ), 148 | thumb_url=cls.THUMB, 149 | ) 150 | 151 | class BoundMethod: 152 | DOCS = "https://telegramplayground.github.io/pyrogram/api/bound-methods/{}.{}" 153 | THUMB = "https://i.imgur.com/GxFuuks.png" 154 | 155 | def __new__(cls, item): 156 | a, b = item.__qualname__.split(".") 157 | doc_url = cls.DOCS.format(a, b) 158 | return InlineQueryResultArticle( 159 | title=f"{item.__qualname__}", 160 | description=f'Bound Method "{b}" of {a}', 161 | input_message_content=InputTextMessageContent( 162 | f"{emoji.LEDGER} **Pyrogram Docs**\n\n" 163 | f"[{item.__qualname__}]({doc_url}) - Bound Method", 164 | link_preview_options=LinkPreviewOptions( 165 | url=doc_url, 166 | show_above_text=True 167 | ), 168 | parse_mode=ParseMode.MARKDOWN 169 | ), 170 | thumb_url=cls.THUMB, 171 | ) 172 | 173 | class RawMethod: 174 | DOCS = "https://telegramplayground.github.io/pyrogram/telegram/functions/{}" 175 | THUMB = "https://i.imgur.com/NY4uasQ.png" 176 | 177 | def __new__(cls, item): 178 | constructor_id = hex(item[1].ID) 179 | path = cls.DOCS.format(Result.snek(item[0]).replace("_", "-").replace(".-", "/")) 180 | 181 | return InlineQueryResultArticle( 182 | title=f"{item[0]}", 183 | description=f"Raw Method - {constructor_id}\nSchema: Layer {layer}", 184 | input_message_content=InputTextMessageContent( 185 | f"{emoji.BLUE_BOOK} **Pyrogram Docs**\n\n" 186 | f"[{item[0]}]({path}) - Raw Method\n\n" 187 | f"`ID`: **{constructor_id}**\n" 188 | f"`Schema`: **Layer {layer}**", 189 | link_preview_options=LinkPreviewOptions( 190 | url=path, 191 | show_above_text=True 192 | ), 193 | parse_mode=ParseMode.MARKDOWN 194 | ), 195 | thumb_url=cls.THUMB, 196 | ) 197 | 198 | class RawType: 199 | DOCS = "https://telegramplayground.github.io/pyrogram/telegram/types/{}" 200 | THUMB = "https://i.imgur.com/b33rM21.png" 201 | 202 | def __new__(cls, item): 203 | constructor_id = hex(item[1].ID) 204 | path = cls.DOCS.format(Result.snek(item[0]).replace("_", "-").replace(".-", "/")) 205 | 206 | return InlineQueryResultArticle( 207 | title=f"{item[0]}", 208 | description=f"Raw Type - {constructor_id}\nSchema: Layer {layer}", 209 | input_message_content=InputTextMessageContent( 210 | f"{emoji.ORANGE_BOOK} **Pyrogram Docs**\n\n" 211 | f"[{item[0]}]({path}) - Raw Type\n\n" 212 | f"`ID`: **{constructor_id}**\n" 213 | f"`Schema`: **Layer {layer}**", 214 | link_preview_options=LinkPreviewOptions( 215 | url=path, 216 | show_above_text=True 217 | ), 218 | parse_mode=ParseMode.MARKDOWN 219 | ), 220 | thumb_url=cls.THUMB, 221 | ) 222 | 223 | 224 | METHODS = [] 225 | 226 | for a in dir(Client): 227 | m = getattr(Client, a) 228 | 229 | try: 230 | if not a.startswith("_") and a[0].islower() and m.__doc__ and not a.startswith("on_"): 231 | METHODS.append((a.lower(), Result.Method(m))) 232 | except AttributeError: 233 | pass 234 | 235 | DECORATORS = [] 236 | 237 | for a in dir(Client): 238 | m = getattr(Client, a) 239 | 240 | try: 241 | if not a.startswith("_") and a[0].islower() and m.__doc__ and a.startswith("on_"): 242 | DECORATORS.append((a.lower(), Result.Decorator(m))) 243 | except AttributeError: 244 | pass 245 | 246 | TYPES = [] 247 | 248 | for a in dir(types): 249 | t = getattr(types, a) 250 | 251 | if not a.startswith("_") and a[0].isupper() and t.__doc__: 252 | TYPES.append((a, Result.Type(t))) 253 | 254 | FILTERS = [ 255 | (i.lower(), Result.Filter(getattr(filters, i))) 256 | for i in filter( 257 | lambda x: not x.startswith("_") 258 | and x[0].islower(), 259 | dir(filters) 260 | ) 261 | ] 262 | 263 | BOUND_METHODS = [] 264 | 265 | for a in dir(types): 266 | try: 267 | c = getattr(types, a) 268 | if issubclass(c, Object): 269 | for m in dir(c): 270 | if ( 271 | not m.startswith("_") 272 | and callable(getattr(c, m)) 273 | and m not in ["default", "read", "write", "with_traceback", "continue_propagation", 274 | "stop_propagation", "bind"] 275 | ): 276 | BOUND_METHODS.append((f"{a}.{m}", Result.BoundMethod(getattr(c, m)))) 277 | except TypeError: 278 | pass 279 | 280 | RAW_METHODS = [] 281 | 282 | for i in filter(lambda x: not x.startswith("_"), dir(raw_methods)): 283 | if i[0].isupper(): 284 | RAW_METHODS.append((i, Result.RawMethod((i, getattr(raw_methods, i))))) 285 | else: 286 | if "Int" not in dir(getattr(raw_methods, i)): 287 | for j in filter(lambda x: not x.startswith("_") and x[0].isupper(), dir(getattr(raw_methods, i))): 288 | RAW_METHODS.append((f"{i}.{j}", Result.RawMethod((f"{i}.{j}", getattr(getattr(raw_methods, i), j))))) 289 | 290 | for i in RAW_METHODS[:]: 291 | if "." not in i[0]: 292 | RAW_METHODS.remove(i) 293 | RAW_METHODS.append(i) 294 | 295 | RAW_TYPES = [] 296 | 297 | for i in filter(lambda x: not x.startswith("_"), dir(raw_types)): 298 | if i[0].isupper(): 299 | RAW_TYPES.append((i, Result.RawType((i, getattr(raw_types, i))))) 300 | else: 301 | if "Int" not in dir(getattr(raw_types, i)): 302 | for j in filter(lambda x: not x.startswith("_") and x[0].isupper(), dir(getattr(raw_types, i))): 303 | RAW_TYPES.append((f"{i}.{j}", Result.RawType((f"{i}.{j}", getattr(getattr(raw_types, i), j))))) 304 | 305 | for i in RAW_TYPES[:]: 306 | if "." not in i[0]: 307 | RAW_TYPES.remove(i) 308 | RAW_TYPES.append(i) 309 | 310 | FIRE_THUMB = "https://i.imgur.com/qhYYqZa.png" 311 | ROCKET_THUMB = "https://i.imgur.com/PDaYHd8.png" 312 | ABOUT_BOT_THUMB = "https://i.imgur.com/zRglRz3.png" 313 | OPEN_BOOK_THUMB = "https://i.imgur.com/v1XSJ1D.png" 314 | RED_HEART_THUMB = "https://i.imgur.com/66FVFXz.png" 315 | SCROLL_THUMB = "https://i.imgur.com/L1u0VlX.png" 316 | 317 | HELP = ( 318 | f"{emoji.ROBOT} **Pyrogram Assistant**\n\n" 319 | f"You can use this bot in inline mode to search for Pyrogram **FORK** `PyroTGFork` methods, types and other resources from " 320 | f"https://telegramplayground.github.io/pyrogram.\n\n" 321 | 322 | f"**__Search__**\n" 323 | f"`@pyrogrambot ` – Pyrogram API\n" 324 | f"`@pyrogrambot !r ` – Telegram Raw API\n\n" 325 | 326 | f"**__List__**\n" 327 | f"`@pyrogrambot !m` – Methods\n" 328 | f"`@pyrogrambot !t` – Types\n" 329 | f"`@pyrogrambot !f` – Filters\n" 330 | f"`@pyrogrambot !b` – Bound Methods\n" 331 | f"`@pyrogrambot !d` – Decorators\n" 332 | f"`@pyrogrambot !rm` – Raw Methods\n" 333 | f"`@pyrogrambot !rt` – Raw Types\n\n" 334 | ) 335 | 336 | DEFAULT_RESULTS = [ 337 | InlineQueryResultArticle( 338 | title="About Pyrogram", 339 | input_message_content=InputTextMessageContent( 340 | f"{emoji.FIRE} **Pyrogram**\n\n" 341 | f"Pyrogram is an elegant, easy-to-use Telegram client library and framework written from the ground up in " 342 | f"Python and C. It enables you to easily create custom apps using both user and bot identities (bot API " 343 | f"alternative) via the MTProto API.", 344 | parse_mode=ParseMode.MARKDOWN 345 | ), 346 | reply_markup=InlineKeyboardMarkup( 347 | [ 348 | [ 349 | InlineKeyboardButton(f"{emoji.BUSTS_IN_SILHOUETTE} Community", url="https://PyroTGFork.t.me/2") 350 | ], 351 | [ 352 | InlineKeyboardButton(f"{emoji.CARD_INDEX_DIVIDERS} GitHub", url="https://github.com/TelegramPlayground/pyrogram"), 353 | InlineKeyboardButton(f"{emoji.OPEN_BOOK} Docs", url="https://telegramplayground.github.io/pyrogram/releases/changes-in-this-fork.html") 354 | ], 355 | # [ 356 | # InlineKeyboardButton( 357 | # text="DO NOT CLIECK HREE", 358 | # callback_data="thisistestbutton" 359 | # ) 360 | # ], 361 | ] 362 | ), 363 | description="Pyrogram is an elegant, easy-to-use Telegram client library and framework.", 364 | thumb_url=FIRE_THUMB, 365 | ), 366 | InlineQueryResultArticle( 367 | title="About this Bot", 368 | input_message_content=InputTextMessageContent( 369 | HELP, 370 | link_preview_options=LinkPreviewOptions( 371 | is_disabled=True 372 | ), 373 | parse_mode=ParseMode.MARKDOWN 374 | ), 375 | reply_markup=InlineKeyboardMarkup([[ 376 | InlineKeyboardButton( 377 | f"{emoji.CARD_INDEX_DIVIDERS} Source Code", 378 | url="https://github.com/pyrogram/assistant" 379 | ), 380 | InlineKeyboardButton( 381 | f"{emoji.FIRE} Go!", 382 | switch_inline_query="" 383 | ) 384 | ]]), 385 | description="How to use Pyrogram Assistant Bot.", 386 | thumb_url=ABOUT_BOT_THUMB, 387 | ), 388 | InlineQueryResultArticle( 389 | title="Quick Start", 390 | input_message_content=InputTextMessageContent( 391 | f"{emoji.ROCKET} **Pyrogram Docs**\n\n" 392 | f"[Quick Start](https://telegramplayground.github.io/pyrogram/intro/quickstart) - Introduction\n\n" 393 | f"`Quick overview to get you started as fast as possible`", 394 | link_preview_options=LinkPreviewOptions( 395 | is_disabled=True 396 | ), 397 | parse_mode=ParseMode.MARKDOWN 398 | ), 399 | description="Quick overview to get you started as fast as possible.", 400 | thumb_url=ROCKET_THUMB, 401 | ), 402 | InlineQueryResultArticle( 403 | title="Support", 404 | input_message_content=InputTextMessageContent( 405 | f"{emoji.RED_HEART} **Support Pyrogram**\n\n" 406 | f"[Support](https://docs.pyrogram.org/support-pyrogram) - Meta\n\n" 407 | f"`Ways to show your appreciation.`", 408 | link_preview_options=LinkPreviewOptions( 409 | is_disabled=True 410 | ), 411 | parse_mode=ParseMode.MARKDOWN 412 | ), 413 | description="Ways to show your appreciation.", 414 | thumb_url=RED_HEART_THUMB, 415 | ), 416 | ] 417 | 418 | rules = """ 419 | **Pyrogram Rules** 420 | 421 | ` 0.` Follow rules; improve chances of getting answers. 422 | ` 1.` English only. Other groups by language: #groups. 423 | ` 2.` Spam, flood and hate speech is strictly forbidden. 424 | ` 3.` Talks unrelated to Pyrogram (ot) are not allowed. 425 | ` 4.` Keep unrelated media and emojis to a minimum. 426 | ` 5.` Be nice, respect people and use common sense. 427 | ` 6.` Ask before sending PMs and respect the answers. 428 | ` 7.` "Doesn't work" means nothing. Explain in details. 429 | ` 8.` Ask if you get any error, not if the code is correct. 430 | ` 9.` Make use of nekobin.com for sharing long code. 431 | `10.` No photos unless they are meaningful and small. 432 | 433 | __Rules are subject to change without notice.__ 434 | """ 435 | 436 | RULES = [ 437 | InlineQueryResultArticle( 438 | title=f"Rule {i}", 439 | description=re.sub(r"` ?\d+.` ", "", rule), 440 | input_message_content=InputTextMessageContent( 441 | "**Pyrogram Rules**\n\n" + rule, 442 | parse_mode=ParseMode.MARKDOWN 443 | ), 444 | thumb_url=SCROLL_THUMB, 445 | ) 446 | for i, rule in enumerate(rules.split("\n")[3:-3]) 447 | ] 448 | -------------------------------------------------------------------------------- /pyrobot/plugins/assistant/inline.py: -------------------------------------------------------------------------------- 1 | # https://github.com/pyrogram/assistant/blob/343a43f/assistant/plugins/inline.py 2 | # MIT License 3 | # 4 | # Copyright (c) 2019-present Dan 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in all 14 | # copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | # SOFTWARE. 23 | 24 | from pyrogram import Client, emoji, __version__ 25 | from pyrogram.types import ( 26 | InlineQuery, 27 | InlineQueryResultCachedAudio, 28 | InlineQueryResultCachedDocument, 29 | InlineQueryResultCachedAnimation, 30 | InlineQueryResultCachedPhoto, 31 | InlineQueryResultCachedSticker, 32 | InlineQueryResultCachedVideo, 33 | InlineQueryResultCachedVoice, 34 | InlineQueryResultArticle, 35 | InlineQueryResultAudio, 36 | InlineQueryResultContact, 37 | InlineQueryResultDocument, 38 | InlineQueryResultAnimation, 39 | InlineQueryResultLocation, 40 | InlineQueryResultPhoto, 41 | InlineQueryResultVenue, 42 | InlineQueryResultVideo, 43 | InlineQueryResultVoice, 44 | InputTextMessageContent, 45 | InlineKeyboardButton, 46 | InlineKeyboardMarkup 47 | ) 48 | 49 | from . import docs 50 | 51 | NEXT_OFFSET = 25 52 | CACHE_TIME = 5 53 | 54 | FIRE_THUMB = "https://i.imgur.com/qhYYqZa.png" 55 | ROCKET_THUMB = "https://i.imgur.com/PDaYHd8.png" 56 | OPEN_BOOK_THUMB = "https://i.imgur.com/v1XSJ1D.png" 57 | SCROLL_THUMB = "https://i.imgur.com/L1u0VlX.png" 58 | 59 | VERSION = __version__ 60 | 61 | 62 | @Client.on_inline_query() 63 | async def inline(_, query: InlineQuery): 64 | string = query.query.lower() 65 | 66 | if string == "": 67 | await query.answer( 68 | results=docs.DEFAULT_RESULTS, 69 | cache_time=CACHE_TIME, 70 | switch_pm_text=f"{emoji.MAGNIFYING_GLASS_TILTED_RIGHT} Type to search Pyrogram Docs", 71 | switch_pm_parameter="start", 72 | ) 73 | 74 | return 75 | 76 | results = [] 77 | offset = int(query.offset or 0) 78 | switch_pm_text = f"{emoji.OPEN_BOOK} Pyrogram Docs" 79 | 80 | if string == "!m": 81 | switch_pm_text = f"{emoji.CLOSED_BOOK} Pyrogram Methods ({len(docs.METHODS)})" 82 | 83 | if offset == 0: 84 | results.append( 85 | InlineQueryResultArticle( 86 | title="Methods", 87 | description="Pyrogram Methods online documentation page", 88 | input_message_content=InputTextMessageContent( 89 | f"{emoji.FIRE} **Pyrogram Methods**\n\n" 90 | f"`This page contains all available high-level Methods existing in Pyrogram v{VERSION}.`" 91 | ), 92 | reply_markup=InlineKeyboardMarkup([[ 93 | InlineKeyboardButton( 94 | f"{emoji.OPEN_BOOK} Online docs", 95 | url="https://telegramplayground.github.io/pyrogram/api/methods" 96 | ) 97 | ]]), 98 | thumb_url=FIRE_THUMB, 99 | ) 100 | ) 101 | 102 | for i in docs.METHODS[offset: offset + NEXT_OFFSET]: 103 | results.append(i[1]) 104 | elif string == "!t": 105 | switch_pm_text = f"{emoji.GREEN_BOOK} Pyrogram Types ({len(docs.TYPES)})" 106 | 107 | if offset == 0: 108 | results.append( 109 | InlineQueryResultArticle( 110 | title="Types", 111 | description="Pyrogram Types online documentation page", 112 | input_message_content=InputTextMessageContent( 113 | f"{emoji.FIRE} **Pyrogram Types**\n\n" 114 | f"`This page contains all available high-level Types existing in Pyrogram v{VERSION}.`" 115 | ), 116 | reply_markup=InlineKeyboardMarkup([[ 117 | InlineKeyboardButton( 118 | f"{emoji.OPEN_BOOK} Online docs", 119 | url="https://telegramplayground.github.io/pyrogram/api/types" 120 | ) 121 | ]]), 122 | thumb_url=FIRE_THUMB, 123 | ) 124 | ) 125 | 126 | for i in docs.TYPES[offset: offset + NEXT_OFFSET]: 127 | results.append(i[1]) 128 | elif string == "!b": 129 | switch_pm_text = f"{emoji.CLOSED_BOOK} Pyrogram Bound Methods ({len(docs.BOUND_METHODS)})" 130 | 131 | if offset == 0: 132 | results.append( 133 | InlineQueryResultArticle( 134 | title="Types", 135 | description="Pyrogram Bound Methods online documentation page", 136 | input_message_content=InputTextMessageContent( 137 | f"{emoji.FIRE} **Pyrogram Bound Methods**\n\n" 138 | f"`This page contains all available bound methods existing in Pyrogram v{VERSION}.`" 139 | ), 140 | reply_markup=InlineKeyboardMarkup([[ 141 | InlineKeyboardButton( 142 | f"{emoji.OPEN_BOOK} Online docs", 143 | url="https://telegramplayground.github.io/pyrogram/api/bound-methods" 144 | )]] 145 | ), 146 | thumb_url=FIRE_THUMB, 147 | ) 148 | ) 149 | 150 | for i in docs.BOUND_METHODS[offset: offset + NEXT_OFFSET]: 151 | results.append(i[1]) 152 | elif string == "!d": 153 | switch_pm_text = f"{emoji.CLOSED_BOOK} Pyrogram Decorators ({len(docs.DECORATORS)})" 154 | 155 | if offset == 0: 156 | results.append( 157 | InlineQueryResultArticle( 158 | title="Decorators", 159 | description="Pyrogram Decorators online documentation page", 160 | input_message_content=InputTextMessageContent( 161 | f"{emoji.FIRE} **Pyrogram Decorators**\n\n" 162 | f"`This page contains all available decorators existing in Pyrogram v{VERSION}.`" 163 | ), 164 | reply_markup=InlineKeyboardMarkup([[ 165 | InlineKeyboardButton( 166 | f"{emoji.OPEN_BOOK} Online docs", 167 | url="https://telegramplayground.github.io/pyrogram/api/decorators" 168 | )]] 169 | ), 170 | thumb_url=FIRE_THUMB, 171 | ) 172 | ) 173 | 174 | for i in docs.DECORATORS[offset: offset + NEXT_OFFSET]: 175 | results.append(i[1]) 176 | elif string == "!f": 177 | switch_pm_text = f"{emoji.CONTROL_KNOBS} Pyrogram Filters ({len(docs.FILTERS)})" 178 | 179 | if offset == 0: 180 | results.append( 181 | InlineQueryResultArticle( 182 | title="Filters", 183 | description="Pyrogram Filters online documentation page", 184 | input_message_content=InputTextMessageContent( 185 | f"{emoji.FIRE} **Pyrogram Filters**\n\n" 186 | f"`This page contains all library-defined Filters available in Pyrogram v{VERSION}.`" 187 | ), 188 | reply_markup=InlineKeyboardMarkup([[ 189 | InlineKeyboardButton( 190 | f"{emoji.OPEN_BOOK} Online docs", 191 | url="https://telegramplayground.github.io/pyrogram/api/filters" 192 | ) 193 | ]]), 194 | thumb_url=FIRE_THUMB, 195 | ) 196 | ) 197 | 198 | for i in docs.FILTERS[offset: offset + NEXT_OFFSET]: 199 | results.append(i[1]) 200 | elif string == "!rm": 201 | switch_pm_text = f"{emoji.BLUE_BOOK} Raw Methods ({len(docs.RAW_METHODS)})" 202 | 203 | if offset == 0: 204 | results.append( 205 | InlineQueryResultArticle( 206 | title="Raw Methods", 207 | description="Pyrogram Raw Methods online documentation page", 208 | input_message_content=InputTextMessageContent( 209 | f"{emoji.FIRE} **Pyrogram Raw Methods**\n\n" 210 | f"`This page contains all available Raw Methods existing in the Telegram Schema, Layer `" 211 | f"`{docs.layer}.`" 212 | ), 213 | reply_markup=InlineKeyboardMarkup([[ 214 | InlineKeyboardButton( 215 | f"{emoji.OPEN_BOOK} Online docs", 216 | url="https://telegramplayground.github.io/pyrogram/telegram/functions" 217 | ), 218 | InlineKeyboardButton( 219 | f"{emoji.SCROLL} TL Schema", 220 | url="https://github.com/pyrogram/pyrogram/blob/develop/compiler/api/source/main_api.tl" 221 | ), 222 | ]]), 223 | thumb_url=FIRE_THUMB, 224 | ) 225 | ) 226 | 227 | for i in docs.RAW_METHODS[offset: offset + NEXT_OFFSET]: 228 | results.append(i[1]) 229 | elif string == "!rt": 230 | switch_pm_text = f"{emoji.ORANGE_BOOK} Raw Types ({len(docs.RAW_TYPES)})" 231 | 232 | if offset == 0: 233 | results.append( 234 | InlineQueryResultArticle( 235 | title="Raw Types", 236 | description="Pyrogram Raw Types online documentation page", 237 | input_message_content=InputTextMessageContent( 238 | f"{emoji.FIRE} **Pyrogram Raw Types**\n\n" 239 | f"`This page contains all available Raw Types existing in the Telegram Schema, Layer " 240 | f"{docs.layer}.`" 241 | ), 242 | reply_markup=InlineKeyboardMarkup([[ 243 | InlineKeyboardButton( 244 | f"{emoji.OPEN_BOOK} Online docs", 245 | url="https://telegramplayground.github.io/pyrogram/telegram/types" 246 | ), 247 | InlineKeyboardButton( 248 | f"{emoji.SCROLL} TL Schema", 249 | url="https://github.com/pyrogram/pyrogram/blob/develop/compiler/api/source/main_api.tl", 250 | ), 251 | ]]), 252 | thumb_url=FIRE_THUMB, 253 | ) 254 | ) 255 | 256 | for i in docs.RAW_TYPES[offset: offset + NEXT_OFFSET]: 257 | results.append(i[1]) 258 | elif string == "rules": 259 | switch_pm_text = f"{emoji.SCROLL} Chat Rules" 260 | 261 | if offset == 0: 262 | results.append( 263 | InlineQueryResultArticle( 264 | title="Chat Rules", 265 | description="These are the rules for the Pyrogram Inn and the chats for other languages.", 266 | input_message_content=InputTextMessageContent(docs.rules), 267 | thumb_url=FIRE_THUMB, 268 | ) 269 | ) 270 | 271 | for i in docs.RULES[offset: offset + NEXT_OFFSET]: 272 | results.append(i) 273 | elif string == "colin": 274 | switch_pm_text = f"{emoji.SHARK} Hidden Shark" 275 | 276 | if offset == 0: 277 | results.append( 278 | InlineQueryResultPhoto( 279 | photo_url="https://i.imgur.com/f32hngs.jpg", 280 | # thumb_url="https://i.imgur.com/f32hngs.jpg", 281 | title="You found the secret Sharkception :O", 282 | description="You might not get anything from it, but you can feel proud to have found me!", 283 | caption=f"Hey, I found @ColinShark {emoji.SHARK}", 284 | # input_message_content=InputTextMessageContent(f"Hey, I found @ColinShark {emoji.SHARK}"), 285 | ) 286 | ) 287 | 288 | elif string == "test": 289 | switch_pm_text = "" 290 | results.append( 291 | InlineQueryResultCachedAudio( 292 | audio_file_id="CQACAgEAAx0CSMbgUAABFS1BZ78vrjihY3Fqo0gq04o30pLTTIUAAloFAAL3BulEXnVG7aXUwAweBA", 293 | caption="", 294 | # reply_markup 295 | ) 296 | ) 297 | results.append( 298 | InlineQueryResultCachedDocument( 299 | document_file_id="BQACAgIAAx0CSMbgUAABFS1CZ78v1U6mIyqrxwABgcWRAZ9B9AKtAAJNKgACqMjQSK83dhim_yj2HgQ", 300 | title="DC 2 TITLE", 301 | description="DC 2 Description", 302 | caption="", 303 | # reply_markup 304 | ) 305 | ) 306 | results.append( 307 | InlineQueryResultCachedAnimation( 308 | animation_file_id="CgACAgIAAx0CSMbgUAABFS1JZ782CvfH3IGM3P2cw9BhwSgiraAAAmc-AAKSPeFLG1Q-Hv3Yh9QeBA", 309 | title="GiF Title", 310 | caption="Animation CAPTION", 311 | show_caption_above_media=True, 312 | # reply_markup 313 | ) 314 | ) 315 | results.append( 316 | InlineQueryResultCachedPhoto( 317 | photo_file_id="AgACAgQAAx0CSMbgUAABFS09Z78mkXAYFqulZ9ovqpnnOchh-OoAAr-wMRvLhiVR3f1_D3NvnioACAEAAwIAA3gABx4E", 318 | title="photo title", 319 | description="photo description", 320 | caption="", 321 | show_caption_above_media=False, 322 | # reply_markup 323 | ) 324 | ) 325 | results.append( 326 | InlineQueryResultCachedSticker( 327 | sticker_file_id="CAACAgIAAx0CSMbgUAABFS1QZ782TNe7eWlxF8nr9gzkB8PzaTAAAqQMAAKqFMhKAW7uprJ8jhweBA", 328 | # reply_markup 329 | ) 330 | ) 331 | results.append( 332 | InlineQueryResultCachedVideo( 333 | video_file_id="BAACAgIAAx0CSMbgUAABFS1UZ782Y1VNuUNyp6ufAULIPQZabr0AAiA_AAL8QeBLNgYzcdW3aTseBA", 334 | title="video title", 335 | description="video description", 336 | caption="", 337 | show_caption_above_media=False, 338 | # reply_markup 339 | ) 340 | ) 341 | results.append( 342 | InlineQueryResultCachedVoice( 343 | voice_file_id="AwACAgQAAx0CSMbgUAABFS1YZ782fTRtgt8SDaOlhpJf4i1dw7gAArAXAALox-lTDpW4lZCI-8YeBA", 344 | title="voice title", 345 | caption="", 346 | # reply_markup 347 | ) 348 | ) 349 | results.append( 350 | InlineQueryResultContact( 351 | phone_number="424314159", 352 | first_name="BoT ", 353 | last_name="SUPPort", 354 | # vcard="", 355 | # reply_markup= 356 | thumb_url="https://mediaflare.adasin.workers.dev/dl/CDVlj6R" 357 | ) 358 | ) 359 | # results.append( 360 | # InlineQueryResultLocation( 361 | 362 | # ) 363 | # ) 364 | # results.append( 365 | # InlineQueryResultVenue( 366 | 367 | # ) 368 | # ) 369 | 370 | if results: 371 | await query.answer( 372 | results=results, 373 | cache_time=CACHE_TIME, 374 | switch_pm_text=switch_pm_text, 375 | switch_pm_parameter="start", 376 | next_offset=str(offset + NEXT_OFFSET), 377 | is_gallery=False 378 | ) 379 | else: 380 | if offset: 381 | await query.answer( 382 | results=[], 383 | cache_time=CACHE_TIME, 384 | switch_pm_text=switch_pm_text, 385 | switch_pm_parameter="start", 386 | next_offset="", 387 | ) 388 | 389 | if string.startswith("!r"): 390 | string = " ".join(string.split(" ")[1:]) 391 | 392 | if string == "": 393 | await query.answer( 394 | results=[], 395 | cache_time=CACHE_TIME, 396 | switch_pm_text=f"{emoji.MAGNIFYING_GLASS_TILTED_RIGHT} Type to search Raw Docs", 397 | switch_pm_parameter="start", 398 | ) 399 | 400 | for i in docs.RAW_METHODS: 401 | if string in i[0].lower(): 402 | results.append(i[1]) 403 | 404 | for i in docs.RAW_TYPES: 405 | if string in i[0].lower(): 406 | results.append(i[1]) 407 | 408 | else: 409 | for i in docs.METHODS: 410 | if string in i[0].lower(): 411 | results.append(i[1]) 412 | 413 | for i in docs.TYPES: 414 | if string in i[0].lower(): 415 | results.append(i[1]) 416 | 417 | for i in docs.BOUND_METHODS: 418 | if string in i[0].lower(): 419 | results.append(i[1]) 420 | 421 | for i in docs.DECORATORS: 422 | if string in i[0].lower(): 423 | results.append(i[1]) 424 | 425 | for i in docs.FILTERS: 426 | if string in i[0].lower(): 427 | results.append(i[1]) 428 | 429 | if results: 430 | count = len(results) 431 | switch_pm_text = f"{emoji.OPEN_BOOK} {count} Result{'s' if count > 1 else ''} for \"{string}\"" 432 | 433 | await query.answer( 434 | results=results[:50], 435 | cache_time=CACHE_TIME, 436 | switch_pm_text=switch_pm_text, 437 | switch_pm_parameter="start" 438 | ) 439 | else: 440 | await query.answer( 441 | results=[], 442 | cache_time=CACHE_TIME, 443 | switch_pm_text=f'{emoji.CROSS_MARK} No results for "{string}"', 444 | switch_pm_parameter="okay", 445 | ) 446 | 447 | 448 | # @Client.on_chosen_inline_result() 449 | # async def chosen_inline_result_handler(client, chosen_inline_result): 450 | # # print(chosen_inline_result) 451 | # from pyrogram.types import InputMediaAudio 452 | # print(await chosen_inline_result.edit_message_media( 453 | # InputMediaAudio( 454 | # "/downloads/Oru_Poo_Maathram_Chodhichu_Swapnakoodu_Sreenivas_Sujatha_Mohan_Mohan.mp3", 455 | # caption="testing edit message" 456 | # ) 457 | # )) 458 | -------------------------------------------------------------------------------- /pyrobot/plugins/assistant/test.py: -------------------------------------------------------------------------------- 1 | from pyrogram import Client, filters 2 | from pyrogram.enums import ParseMode 3 | 4 | @Client.on_message( 5 | filters.command("test") 6 | ) 7 | async def testcomnd(_, message): 8 | m = """*bold \*text* 9 | _italic \*text_ 10 | __underline__ 11 | ~strikethrough~ 12 | ||spoiler|| 13 | *bold _italic bold ~italic bold strikethrough ||italic bold strikethrough spoiler||~ __underline italic bold___ bold* 14 | [inline URL](http://www.example.com/) 15 | [inline mention of a user](tg://user?id=123456789) 16 | ![👍](tg://emoji?id=5368324170671202286) 17 | `inline fixed-width code` 18 | ``` 19 | pre-formatted fixed-width code block 20 | ``` 21 | ```python 22 | pre-formatted fixed-width code block written in the Python programming language 23 | ``` 24 | >Block quotation started 25 | >Block quotation continued 26 | >Block quotation continued 27 | >Block quotation continued 28 | >The last line of the block quotation 29 | **>The expandable block quotation started right after the previous block quotation 30 | <** 31 | >It is separated from the previous block quotation by an empty bold entity 32 | **>Expandable block quotation continued 33 | Hidden by default part of the expandable block quotation started 34 | Expandable block quotation continued 35 | The last line of the expandable block quotation with the expandability mark 36 | <**""" 37 | await message.reply(m, parse_mode=ParseMode.MARKDOWN) 38 | h = """bold, bold 39 | italic, italic 40 | underline, underline 41 | strikethrough, strikethrough, strikethrough 42 | spoiler, spoiler 43 | bold italic bold italic bold strikethrough italic bold strikethrough spoiler underline italic bold bold 44 | inline URL 45 | inline mention of a user 46 | 👍 47 | inline fixed-width code 48 |
pre-formatted fixed-width code block
49 |
pre-formatted fixed-width code block written in the Python programming language
50 |
Block quotation started\nBlock quotation continued\nThe last line of the block quotation
51 |
Expandable block quotation started\nExpandable block quotation continued\nExpandable block quotation continued\nHidden by default part of the block quotation started\nExpandable block quotation continued\nThe last line of the block quotation
""" 52 | await message.reply(h, parse_mode=ParseMode.HTML) 53 | 54 | 55 | # @Client.on_message( 56 | # filters.private & 57 | # filters.via_bot(["@vid", "@gif"]) 58 | # ) 59 | # async def _(_, m): 60 | # print(m) 61 | # await m.reply(str(m.media)) 62 | 63 | # @Client.on_poll( 64 | # ) 65 | # async def _(_, p): 66 | # print(p) 67 | 68 | # @Client.on_chat_member_updated() 69 | # async def _(c, u): 70 | # await c.send_message(-1001220993104, str(u._raw), message_thread_id=1378275) 71 | 72 | 73 | @Client.on_callback_query() 74 | async def _(client, callback_query): 75 | print(callback_query) 76 | await callback_query.answer() 77 | -------------------------------------------------------------------------------- /pyrobot/plugins/call_back_button_s.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # (c) Shrimadhav U K 4 | 5 | from pyrogram.types import CallbackQuery 6 | from pyrobot.pyrobot import PyroBot 7 | from pyrobot.helper_functions.warn_hlprs.remove_warn import remove_warn 8 | from pyrobot.helper_functions.paste_bin_hlpr import del_pasty_ao 9 | 10 | 11 | @PyroBot.on_callback_query() 12 | async def button(client: PyroBot, callback_query: CallbackQuery): 13 | # NOTE: You should always answer, 14 | # but we want different conditionals to 15 | # be able to answer to differnetly 16 | # (and we can only answer once), 17 | # so we do always answer here. 18 | # and, do any heavy processing later! 19 | cb_data = callback_query.data 20 | 21 | if cb_data.startswith("rmwarn_"): 22 | _c, first_i, second_i = cb_data.split("_") 23 | await remove_warn(client, callback_query, str(first_i), int(second_i)) 24 | 25 | elif cb_data.startswith("pb_"): 26 | await del_pasty_ao(client, callback_query) 27 | -------------------------------------------------------------------------------- /pyrobot/plugins/default.py: -------------------------------------------------------------------------------- 1 | """ The core 'pyrobot' module""" 2 | 3 | import os 4 | from importlib import import_module, reload 5 | from pathlib import Path 6 | from pyrogram import Client, filters 7 | from pyrogram.types import Message 8 | from pyrogram.handlers.handler import Handler 9 | from pyrobot import COMMAND_HAND_LER, LOGGER 10 | from pyrobot.helper_functions.cust_p_filters import sudo_filter 11 | 12 | 13 | @Client.on_message(filters.command(["load", "install"], COMMAND_HAND_LER) & sudo_filter) 14 | async def load_plugin(client: Client, message: Message): 15 | """ load TG Plugins """ 16 | status_message = await message.reply("...") 17 | try: 18 | if message.reply_to_message is not None: 19 | down_loaded_plugin_name = await message.reply_to_message.download( 20 | file_name="./plugins/" 21 | ) 22 | if down_loaded_plugin_name is not None: 23 | # LOGGER.info(down_loaded_plugin_name) 24 | relative_path_for_dlpn = os.path.relpath( 25 | down_loaded_plugin_name, os.getcwd() 26 | ) 27 | # LOGGER.info(relative_path_for_dlpn) 28 | lded_count = 0 29 | path = Path(relative_path_for_dlpn) 30 | module_path = ".".join(path.parent.parts + (path.stem,)) 31 | # LOGGER.info(module_path) 32 | module = reload(import_module(module_path)) 33 | # https://git.io/JvlNL 34 | for name in vars(module).keys(): 35 | # noinspection PyBroadException 36 | try: 37 | handler, group = getattr(module, name).handler 38 | 39 | if isinstance(handler, Handler) and isinstance(group, int): 40 | client.add_handler(handler, group) 41 | LOGGER.info( 42 | '[{}] [LOAD] {}("{}") in group {} from "{}"'.format( 43 | client.session_name, 44 | type(handler).__name__, 45 | name, 46 | group, 47 | module_path, 48 | ) 49 | ) 50 | lded_count += 1 51 | except Exception: 52 | pass 53 | await status_message.edit(f"installed {lded_count} commands / plugins") 54 | except Exception as error: 55 | await status_message.edit(f"ERROR: {error}") 56 | -------------------------------------------------------------------------------- /pyrobot/plugins/memes/aesthetic.py: -------------------------------------------------------------------------------- 1 | from pyrogram import Client, filters 2 | from pyrobot import COMMAND_HAND_LER 3 | from pyrobot.helper_functions.cust_p_filters import f_onw_fliter 4 | 5 | 6 | def aesthetify(string): 7 | PRINTABLE_ASCII = range(0x21, 0x7F) 8 | for c in string: 9 | c = ord(c) 10 | if c in PRINTABLE_ASCII: 11 | c += 0xFF00 - 0x20 12 | elif c == ord(" "): 13 | c = 0x3000 14 | yield chr(c) 15 | 16 | 17 | @Client.on_message(filters.command(["ae"], COMMAND_HAND_LER) & f_onw_fliter) 18 | async def aesthetic(client, message): 19 | status_message = await message.reply_text("...") 20 | text = "".join(str(e) for e in message.command[1:]) 21 | text = "".join(aesthetify(text)) 22 | await status_message.edit(text) 23 | -------------------------------------------------------------------------------- /pyrobot/plugins/memes/dart.py: -------------------------------------------------------------------------------- 1 | from pyrogram import Client, filters 2 | from pyrobot import COMMAND_HAND_LER 3 | from pyrobot.helper_functions.cust_p_filters import f_onw_fliter 4 | 5 | # EMOJI CONSTANTS 6 | DART_E_MOJI = "🎯" 7 | # EMOJI CONSTANTS 8 | 9 | 10 | @Client.on_message(filters.command(["throw", "dart"], COMMAND_HAND_LER) & f_onw_fliter) 11 | async def throw_dart(client, message): 12 | """ /throw an @AnimatedDart """ 13 | rep_mesg_id = message.id 14 | if message.reply_to_message: 15 | rep_mesg_id = message.reply_to_message.id 16 | await client.send_dice( 17 | chat_id=message.chat.id, 18 | emoji=DART_E_MOJI, 19 | disable_notification=True, 20 | reply_to_message_id=rep_mesg_id, 21 | ) 22 | -------------------------------------------------------------------------------- /pyrobot/plugins/memes/dice.py: -------------------------------------------------------------------------------- 1 | from pyrogram import Client, filters 2 | from pyrobot import COMMAND_HAND_LER 3 | from pyrobot.helper_functions.cust_p_filters import f_onw_fliter 4 | 5 | # EMOJI CONSTANTS 6 | DICE_E_MOJI = "🎲" 7 | # EMOJI CONSTANTS 8 | 9 | 10 | @Client.on_message(filters.command(["roll", "dice"], COMMAND_HAND_LER) & f_onw_fliter) 11 | async def roll_dice(client, message): 12 | """ @RollADie """ 13 | rep_mesg_id = message.id 14 | if message.reply_to_message: 15 | rep_mesg_id = message.reply_to_message.id 16 | await client.send_dice( 17 | chat_id=message.chat.id, 18 | emoji=DICE_E_MOJI, 19 | disable_notification=True, 20 | reply_to_message_id=rep_mesg_id, 21 | ) 22 | -------------------------------------------------------------------------------- /pyrobot/plugins/memes/runs.py: -------------------------------------------------------------------------------- 1 | import random 2 | from pyrogram import Client, filters 3 | from pyrobot import COMMAND_HAND_LER 4 | from pyrobot.helper_functions.cust_p_filters import f_onw_fliter 5 | 6 | 7 | RUN_STRINGS = [ 8 | "ഇരുട്ട് നിറഞ്ഞ എന്റെ ഈ ജീവിതത്തിലേക്ക് ഒരു തകർച്ചയെ \ 9 | ഓർമ്മിപ്പിക്കാൻ എന്തിന് ഈ ഓട്ടക്കാലണ ആയി നീ വന്നു", 10 | "നമ്മൾ നമ്മൾ പോലുമറിയാതെ അധോലോകം ആയി മാറിക്കഴിഞ്ഞിരിക്കുന്നു ഷാജിയേട്ടാ...", 11 | "എന്നെ ചീത്ത വിളിക്കു... വേണമെങ്കിൽ നല്ല ഇടി ഇടിക്കു... പക്ഷെ ഉപദേശിക്കരുത്.....", 12 | "ഓ ബ്ലഡി ഗ്രാമവാസീസ്!", 13 | "സീ മാഗ്ഗി ഐ ആം ഗോയിങ് ടു പേ ദി ബിൽ.", 14 | "പോരുന്നോ എന്റെ കൂടെ!", 15 | "തള്ളെ കലിപ്പ് തീരണില്ലല്ലോ!!", 16 | "ശബരിമല ശാസ്താവാണെ ഹരിഹരസുതനാണെ ഇത് ചെയ്തവനെ ഞാൻ പൂട്ടും നല്ല മണിച്ചിത്രത്താഴിട്ട് പൂട്ടും .", 17 | "ഞാൻ കണ്ടു...!! കിണ്ടി... കിണ്ടി...!", 18 | "മോന്തയ്ക്കിട്ട് കൊടുത്തിട്ട് ഒന്ന് എടുത്ത് കാണിച്ചുകൊടുക്ക് അപ്പോൾ കാണും ISI മാർക്ക് ", 19 | "ഡേവീസേട്ട, കിങ്ഫിഷറിണ്ടാ... ചിൽഡ്...! .", 20 | "പാതിരാത്രിക്ക് നിന്റെ അച്ഛൻ ഉണ്ടാക്കി വെച്ചിരിക്കുന്നോ പൊറോട്ടയും ചിക്കനും....", 21 | "ഇത് ഞങ്ങളുടെ പണിസാധനങ്ങളാ രാജാവേ.", 22 | "കളിക്കല്ലേ കളിച്ചാൽ ഞാൻ തീറ്റിക്കുമെ പുളിമാങ്ങ....", 23 | "മ്മക്ക് ഓരോ ബിയറാ കാച്ചിയാലോ...", 24 | "ഓ പിന്നെ നീ ഒക്കെ പ്രേമിക്കുമ്പോൾ അത് പ്രണയം.... നമ്മൾ ഒക്കെ പ്രേമിക്കുമ്പോൾ അത് കമ്പി...", 25 | "കള്ളടിക്കുന്നവനല്ലേ കരിമീനിന്റെ സ്വാദറിയു.....", 26 | "ഡാ വിജയാ നമുക്കെന്താ ഈ ബുദ്ധി നേരത്തെ തോന്നാതിരുന്നത്...!", 27 | "ഇത്രേം കാലം എവിടെ ആയിരുന്നു....!", 28 | "ദൈവമേ എന്നെ മാത്രം രക്ഷിക്കണേ....", 29 | "എനിക്കറിയാം ഇവന്റെ അച്ഛന്റെ പേര് ഭവാനിയമ്മ എന്നാ....", 30 | "ഡാ ദാസാ... ഏതാ ഈ അലവലാതി.....", 31 | "ഉപ്പുമാവിന്റെ ഇംഗ്ലീഷ് സാൾട് മംഗോ ട്രീ.....", 32 | "മക്കളെ.. രാജസ്ഥാൻ മരുഭൂമിയിലേക്ക് മണല് കയറ്റിവിടാൻ നോക്കല്ലേ.....", 33 | "നിന്റെ അച്ഛനാടാ പോൾ ബാർബർ....", 34 | "കാർ എൻജിൻ ഔട്ട് കംപ്ലീറ്റ്‌ലി.....", 35 | "ഇത് കണ്ണോ അതോ കാന്തമോ...", 36 | "നാലാമത്തെ പെഗ്ഗിൽ ഐസ്‌ക്യൂബ്സ് വീഴുന്നതിനു മുൻപ് ഞാൻ അവിടെ എത്തും.....", 37 | "അവളെ ഓർത്ത് കുടിച്ച കല്ലും നനഞ്ഞ മഴയും വേസ്റ്റ്....", 38 | "എന്നോട് പറ ഐ ലവ് യൂ ന്ന്....", 39 | "അല്ല ഇതാര് വാര്യംപിള്ളിയിലെ മീനാക്ഷി അല്ലയോ... എന്താ മോളെ സ്കൂട്ടറില്.... ", 40 | # Shonen 41 | 'Why did the Shonen hero cross the road? To train on the other side! 💪⚔️🚶‍♂️', 42 | 'How do Shonen heroes make friends? By saving the world, of course! 🌍❤️💪', 43 | 'Why don’t Shonen heroes use elevators? They prefer to take the stairs to greatness! 🏆🚶‍♂️✨', 44 | 'What’s a Shonen hero’s favorite type of music? Anything with a good training montage! 🎶💪📈', 45 | # Shojo 46 | 'What did the Shojo girl say on her date? "I’m falling for you, just like in the manga!" 💕🌹📚', 47 | 'Why did the Shojo character blush? Because her favorite romance anime just got a season two! 😳❤️🎬', 48 | 'How do Shojo characters stay cool? They have a fan club! 🎐💖🌸', 49 | 'What’s a Shojo character’s favorite exercise? Heart skips! ❤️💓💪', 50 | # Isekai 51 | 'Why did the Isekai protagonist get a job? To make a name in another world! ✨🌍💼', 52 | 'What’s an Isekai hero’s favorite hobby? Exploring new dimensions! 🗺️🔮🌌', 53 | 'Why don’t Isekai heroes need maps? They always find their way to adventure! 🌟🗺️🚀', 54 | 'What do you call an Isekai hero who loves cooking? A master of dimension cuisine! 🍲🔮🌟', 55 | # Mecha 56 | 'Why did the Mecha pilot go to therapy? To work through his robot issues! 🤖💔🛠️', 57 | 'What’s a Mecha pilot’s favorite drink? Mega-sized cola! 🥤🤖🚀', 58 | 'How do Mecha robots stay in shape? With a lot of heavy lifting! 🤖💪🏋️‍♂️', 59 | 'What’s a Mecha’s favorite pastime? Power moves and epic battles! ⚔️🤖💥', 60 | # Sports 61 | 'Why did the sports anime character get kicked out of the gym? Too many dramatic moments! 🏋️‍♂️🎭😂', 62 | 'What’s a sports anime fan’s favorite snack? Energy bars and victory cheers! 🏅🍫🎉', 63 | 'How do sports anime characters stay focused? They always have their game face on! 😎🏆⚽', 64 | 'Why did the sports anime star bring a ladder to the game? To reach new heights! 🏀🚀🏆', 65 | # Horror 66 | 'Why don’t horror anime characters use elevators? They’re afraid of unexpected scares! 🕯️😱🚪', 67 | 'What’s a horror anime character’s favorite dessert? Scary-sweet treats! 🍰👻💀', 68 | 'How do horror anime characters stay calm? By telling themselves it’s just fiction! 📚😅👻', 69 | 'Why did the horror anime protagonist go to the therapist? Too many nightmares! 😨🛏️🔮', 70 | # Slice of Life 71 | 'Why did the Slice of Life character bring an umbrella? To be prepared for all life’s showers! ☔🏠🌦️', 72 | 'What’s a Slice of Life character’s favorite hobby? Collecting everyday moments! 📚✨☕', 73 | 'How do Slice of Life characters stay organized? With lots of cute planners! 📅🎀✏️', 74 | 'Why did the Slice of Life character adopt a pet? To add a little more cuteness to their routine! 🐾💖📅', 75 | # Mystery 76 | 'Why did the detective in the mystery anime bring a pencil? To draw his conclusions! ✏️🔍🕵️‍♂️', 77 | 'What’s a mystery anime character’s favorite drink? Clue-ade! 🍹🔎🕵️‍♀️', 78 | 'How do mystery anime characters solve crimes? By piecing together the plot! 🧩🔍📜', 79 | 'Why was the mystery anime character always calm? They always had the answers! 😎🕵️‍♂️📚', 80 | # Fantasy 81 | 'Why did the fantasy character get a pet dragon? For a fiery sidekick! 🐉🔥👯', 82 | 'What’s a fantasy hero’s favorite tool? A wand with endless possibilities! 🪄✨🔮', 83 | 'How do fantasy characters relax? By reading ancient scrolls! 📜🌟💤', 84 | 'Why did the wizard visit the library? To find magical reads! 📚✨🧙', 85 | # Romance 86 | 'Why did the romance anime couple go to the park? For some heart-to-heart conversations! 🌹💬🌳', 87 | 'What’s a romance anime character’s favorite pickup line? "Are you a magic spell? Because you make my heart flutter!" 💕✨🧙‍♂️', 88 | 'Why did the romance anime character bring a ladder? To reach new heights in love! 💖🚀🌹', 89 | 'How do romance anime characters keep their relationships fresh? With regular sweet gestures! 💌🌟🍫', 90 | # Comedy 91 | 'Why did the comedy anime character go to the bank? To make a deposit of laughs! 😂🏦💰', 92 | 'What’s a comedy anime character’s favorite snack? Chuckle chips! 😂🍟😋', 93 | 'How do comedy anime characters stay upbeat? They always find the punchline! 😂🔍✨', 94 | 'Why did the comedy anime star cross the road? To get to the joke side! 😂🚶‍♂️🎭', 95 | # Drama 96 | 'Why did the drama anime character start a blog? To share their emotional outbursts! 😭💻📝', 97 | 'What’s a drama anime character’s favorite drink? Tears and tension tea! 😢☕🔥', 98 | 'How do drama anime characters keep their cool? By practicing their emotional monologues! 🎭😌🗣️', 99 | 'Why did the drama anime character carry a diary? To pen down every dramatic moment! 📖🎭✨', 100 | # Sci-Fi 101 | 'Why did the sci-fi hero bring a spaceship to the party? To add some futuristic fun! 🚀🎉🪐', 102 | 'What’s a sci-fi fan’s favorite accessory? Holographic sunglasses! 🕶️🌌✨', 103 | 'How do sci-fi characters keep track of time? With a personal space-time calculator! ⏳🔭🚀', 104 | 'Why did the sci-fi character visit the moon? To have a stellar weekend! 🌕🚀🎉', 105 | # Historical 106 | 'Why did the historical anime character visit the museum? To relive the epic battles! 🏛️⚔️🛡️', 107 | 'What’s a historical anime fan’s favorite hobby? Re-enacting classic scenes! 🎭🏰🔍', 108 | 'How do historical anime characters stay in shape? With ancient martial arts! 🥋🏹🏆', 109 | 'Why did the historical figure start a band? To revive some classic tunes! 🎸🎻📜', 110 | # Supernatural 111 | 'Why don’t supernatural characters play hide and seek? They’re always found out by their auras! 🌟👻🔮', 112 | 'What’s a supernatural being’s favorite game? Spirit tag! 👻🏃‍♂️💨', 113 | 'How do supernatural characters stay fit? By floating through their daily workout! ✨🧘‍♂️🌙', 114 | 'Why did the supernatural character get a pet? To add some magic to their life! 🐾✨🔮', 115 | # Action 116 | 'Why did the action star bring a parachute? For some high-flying stunts! 🪂💥🚀', 117 | 'What’s an action hero’s favorite type of music? Anything with a fast beat! 🎶🔥🏆', 118 | 'How do action stars stay fit? By practicing their fight scenes! 🥋💪⚔️', 119 | 'Why did the action hero carry a map? To plan his next big adventure! 🗺️🚀🔥', 120 | # Adventure 121 | 'Why did the adventurer bring a compass? To find their way to excitement! 🧭🌍🚀', 122 | 'What’s an adventurer’s favorite snack? Trail mix and treasure treats! 🏞️🍫💎', 123 | 'How do adventurers stay motivated? By dreaming of epic quests! 🗺️✨🏆', 124 | 'Why did the adventurer climb the mountain? To see the view from the top of the world! 🏔️🌟👀', 125 | # Music 126 | 'Why did the music anime character join a band? To hit the high notes! 🎸🎶🌟', 127 | 'What’s a musician’s favorite anime? One with a lot of musical battles! 🎤🎻🎹', 128 | 'How do music characters stay in tune? By practicing their scales every day! 🎵🎸📈', 129 | 'Why did the music anime character write a song? To compose their feelings! 🎶❤️📝', 130 | # Psychological 131 | 'Why did the psychological anime character see a therapist? To unravel their mind’s mysteries! 🧠🛋️🔍', 132 | 'What’s a psychological anime fan’s favorite activity? Analyzing character motivations! 🤔📚🔍', 133 | 'How do psychological anime characters handle stress? By overthinking everything! 😅🧠📈', 134 | 'Why did the psychological anime character start a journal? To document their inner chaos! 📓🧠💭', 135 | ] 136 | 137 | 138 | @Client.on_message(filters.command("runs", COMMAND_HAND_LER) & f_onw_fliter) 139 | async def runs(_, message): 140 | """ /runs strings """ 141 | effective_string = random.choice(RUN_STRINGS) 142 | if message.reply_to_message: 143 | await message.reply_to_message.reply_text(effective_string) 144 | else: 145 | await message.reply_text(effective_string) 146 | -------------------------------------------------------------------------------- /pyrobot/plugins/miss_rose_sgnak/antichannelpin.py: -------------------------------------------------------------------------------- 1 | from pyrogram import filters 2 | from pyrogram.types import Message 3 | from pyrobot import COMMAND_HAND_LER, A_PIN_MESSAGE_ID 4 | from pyrobot.pyrobot import PyroBot 5 | 6 | 7 | @PyroBot.on_message(filters.service) 8 | async def on_new_pin_message(client: PyroBot, message: Message): 9 | if message.pinned_message and message.pinned_message.id != A_PIN_MESSAGE_ID: 10 | original_pinned_message = await client.get_messages( 11 | chat_id=message.chat.id, message_ids=A_PIN_MESSAGE_ID 12 | ) 13 | await original_pinned_message.pin(disable_notification=True) 14 | -------------------------------------------------------------------------------- /pyrobot/plugins/notes/get.py: -------------------------------------------------------------------------------- 1 | from pyrogram import Client, filters 2 | from pyrobot import COMMAND_HAND_LER, DB_URI, TG_URI 3 | from pyrobot.helper_functions.msg_types import get_file_id 4 | 5 | if DB_URI is not None: 6 | import pyrobot.helper_functions.sql_helpers.notes_sql as sql 7 | 8 | 9 | async def get_note_with_command(message, note_name): 10 | note_d = sql.get_note(message.chat.id, note_name) 11 | if not note_d: 12 | return 13 | note_message_id = note_d.d_message_id 14 | note_message = await message._client.get_messages( 15 | chat_id=TG_URI, message_ids=note_message_id, replies=0 16 | ) 17 | n_m = message 18 | if message.reply_to_message: 19 | n_m = message.reply_to_message 20 | # 🥺 check two conditions 🤔🤔 21 | if note_message.media: 22 | _, file_id = get_file_id(note_message) 23 | caption = note_message.caption 24 | if caption: 25 | caption = caption.html 26 | if not caption: 27 | caption = "" 28 | await n_m.reply_cached_media( 29 | file_id=file_id, 30 | caption=caption, 31 | reply_markup=note_message.reply_markup, 32 | ) 33 | else: 34 | caption = note_message.text 35 | if caption: 36 | caption = caption.html 37 | if not caption: 38 | caption = "" 39 | disable_web_page_preview = True 40 | if "gra.ph" in caption or "youtu" in caption: 41 | disable_web_page_preview = False 42 | await n_m.reply_text( 43 | text=caption, 44 | disable_web_page_preview=disable_web_page_preview, 45 | reply_markup=note_message.reply_markup, 46 | ) 47 | 48 | 49 | @Client.on_message( 50 | filters.command(["getnote", "get"], COMMAND_HAND_LER) & filters.incoming 51 | ) 52 | async def get_note(_, message): 53 | note_name = " ".join(message.command[1:]) 54 | await get_note_with_command(message, note_name) 55 | 56 | 57 | @Client.on_message(filters.regex(pattern=r"#(\w+)") & filters.incoming) 58 | async def get_hash_tag_note(_, message): 59 | note_name = message.matches[0].group(1) 60 | await get_note_with_command(message, note_name) 61 | -------------------------------------------------------------------------------- /pyrobot/plugins/notes/others.py: -------------------------------------------------------------------------------- 1 | from pyrogram import Client, filters 2 | from pyrobot import COMMAND_HAND_LER, DB_URI, MAX_MESSAGE_LENGTH, TG_URI 3 | from pyrobot.helper_functions.cust_p_filters import admin_fliter 4 | 5 | if DB_URI is not None: 6 | import pyrobot.helper_functions.sql_helpers.notes_sql as sql 7 | 8 | 9 | @Client.on_message( 10 | filters.command(["clearnote", "clear"], COMMAND_HAND_LER) & admin_fliter 11 | ) 12 | async def clear_note(_, message): 13 | status_message = await message.reply_text("checking 🤔🙄🙄", quote=True) 14 | note_name = " ".join(message.command[1:]) 15 | sql.rm_note(message.chat.id, note_name) 16 | await status_message.edit_text( 17 | f"note {note_name} deleted from current chat." 18 | ) 19 | 20 | 21 | @Client.on_message(filters.command(["listnotes", "notes"], COMMAND_HAND_LER)) 22 | async def list_note(_, message): 23 | status_message = await message.reply_text("checking 🤔🙄🙄", quote=True) 24 | 25 | note_list = sql.get_all_chat_notes(message.chat.id) 26 | 27 | msg = "Notes in {}:\n".format("the current chat") 28 | msg_p = msg 29 | 30 | for note in note_list: 31 | note_name = " - #{}\n".format(note.name) 32 | if len(msg) + len(note_name) > MAX_MESSAGE_LENGTH: 33 | await message.reply_text(msg) 34 | msg = "" 35 | msg += note_name 36 | 37 | if msg == msg_p: 38 | await status_message.edit_text("ഇൗ ചാറ്റിൽ കുറിപ്പുകളൊന്നുമില്ല.") 39 | 40 | elif len(msg) != 0: 41 | await message.reply_text(msg) 42 | await status_message.delete() 43 | 44 | 45 | @Client.on_message(filters.command(["getnoformat"], COMMAND_HAND_LER)) 46 | async def get_no_format_note(_, message): 47 | note_name = " ".join(message.command[1:]) 48 | note_d = sql.get_note(message.chat.id, note_name) 49 | if not note_d: 50 | return 51 | note_message_id = note_d.d_message_id 52 | note_store_cid = str(TG_URI)[4:] 53 | await message.reply_text(f"https://t.me/c/{note_store_cid}/{note_message_id}") 54 | -------------------------------------------------------------------------------- /pyrobot/plugins/notes/save.py: -------------------------------------------------------------------------------- 1 | from pyrogram import Client, filters 2 | from pyrogram.types import InlineKeyboardMarkup 3 | from pyrobot import COMMAND_HAND_LER, DB_URI, TG_URI 4 | from pyrobot.helper_functions.cust_p_filters import admin_fliter 5 | from pyrobot.helper_functions.msg_types import get_note_type, Types 6 | 7 | if DB_URI is not None: 8 | import pyrobot.helper_functions.sql_helpers.notes_sql as sql 9 | 10 | 11 | @Client.on_message( 12 | filters.command(["savenote", "save"], COMMAND_HAND_LER) & admin_fliter 13 | ) 14 | async def save_note(client, message): 15 | status_message = await message.reply_text("checking 🤔🙄🙄", quote=True) 16 | if message.reply_to_message and message.reply_to_message.reply_markup is not None: 17 | fwded_mesg = await message.reply_to_message.forward( 18 | chat_id=TG_URI, disable_notification=True 19 | ) 20 | chat_id = message.chat.id 21 | note_name = " ".join(message.command[1:]) 22 | note_message_id = fwded_mesg.id 23 | sql.add_note_to_db(chat_id, note_name, note_message_id) 24 | await status_message.edit_text( 25 | f"note {note_name} added" 26 | # f"{message.chat.title}" 27 | ) 28 | else: 29 | note_name, text, data_type, content, buttons = get_note_type(message, 2) 30 | 31 | if data_type is None: 32 | await status_message.edit_text("🤔 maybe note text is empty") 33 | return 34 | 35 | if not note_name: 36 | await status_message.edit_text( 37 | "എന്തിന്ന് ഉള്ള മറുപടി ആണ് എന്ന് വ്യക്തം ആക്കിയില്ല 🤔" 38 | ) 39 | return 40 | 41 | # construct message using the above parameters 42 | fwded_mesg = None 43 | reply_markup = None 44 | if len(buttons) > 0: 45 | reply_markup = InlineKeyboardMarkup(buttons) 46 | if data_type in (Types.BUTTON_TEXT, Types.TEXT): 47 | fwded_mesg = await client.send_message( 48 | chat_id=TG_URI, 49 | text=text, 50 | disable_web_page_preview=True, 51 | disable_notification=True, 52 | reply_to_message_id=1, 53 | reply_markup=reply_markup, 54 | ) 55 | elif data_type is not None: 56 | fwded_mesg = await client.send_cached_media( 57 | chat_id=TG_URI, 58 | file_id=content, 59 | caption=text, 60 | disable_notification=True, 61 | reply_to_message_id=1, 62 | reply_markup=reply_markup, 63 | ) 64 | 65 | # save to db 🤔 66 | if fwded_mesg is not None: 67 | chat_id = message.chat.id 68 | note_message_id = fwded_mesg.id 69 | sql.add_note_to_db(chat_id, note_name, note_message_id) 70 | await status_message.edit_text( 71 | f"note {note_name} added" 72 | # f"{message.chat.title}" 73 | ) 74 | else: 75 | await status_message.edit_text("🥺 this might be an error 🤔") 76 | -------------------------------------------------------------------------------- /pyrobot/plugins/restrictions/ban.py: -------------------------------------------------------------------------------- 1 | from pyrogram import Client, filters 2 | from pyrobot import COMMAND_HAND_LER 3 | from pyrobot.helper_functions.extract_user import extract_user 4 | from pyrobot.helper_functions.string_handling import extract_time 5 | from pyrobot.helper_functions.cust_p_filters import admin_fliter 6 | 7 | 8 | @Client.on_message(filters.command("ban", COMMAND_HAND_LER) & admin_fliter) 9 | async def ban_user(_, message): 10 | user_id, user_first_name, _ = extract_user(message) 11 | 12 | try: 13 | await message.chat.ban_member(user_id=user_id) 14 | except Exception as error: 15 | await message.reply_text(str(error)) 16 | else: 17 | if str(user_id).lower().startswith("@"): 18 | await message.reply_text( 19 | "വേറെ ഒരാളും പൊടി പാറിപ്പിക്കുന്നു..! " 20 | f"{user_first_name}" 21 | " നെ വിലക്കിയിരിക്കുന്നു." 22 | ) 23 | else: 24 | await message.reply_text( 25 | "വേറെ ഒരാളും പൊടി പാറിപ്പിക്കുന്നു..! " 26 | f"" 27 | f"{user_first_name}" 28 | "" 29 | " നെ വിലക്കിയിരിക്കുന്നു." 30 | ) 31 | 32 | 33 | @Client.on_message(filters.command("tban", COMMAND_HAND_LER) & admin_fliter) 34 | async def temp_ban_user(_, message): 35 | if not len(message.command) > 1: 36 | return 37 | 38 | user_id, user_first_name, _ = extract_user(message) 39 | 40 | until_date_val = extract_time(message.command[1]) 41 | if until_date_val is None: 42 | await message.reply_text( 43 | ( 44 | "അസാധുവായ സമയ തരം വ്യക്തമാക്കി. " 45 | "പ്രതീക്ഷിച്ചതു m, h, or d, കിട്ടിയത്: {}" 46 | ).format(message.command[1][-1]) 47 | ) 48 | return 49 | 50 | try: 51 | await message.chat.ban_member(user_id=user_id, until_date=until_date_val) 52 | except Exception as error: 53 | await message.reply_text(str(error)) 54 | else: 55 | if str(user_id).lower().startswith("@"): 56 | await message.reply_text( 57 | "വേറെ ഒരാളും പൊടി പാറിപ്പിക്കുന്നു..! " 58 | f"{user_first_name}" 59 | f" banned for {message.command[1]}!" 60 | ) 61 | else: 62 | await message.reply_text( 63 | "വേറെ ഒരാളും പൊടി പാറിപ്പിക്കുന്നു..! " 64 | f"" 65 | "ലവനെ" 66 | "" 67 | f" banned for {message.command[1]}!" 68 | ) 69 | -------------------------------------------------------------------------------- /pyrobot/plugins/restrictions/restrict.py: -------------------------------------------------------------------------------- 1 | from pyrogram import Client, filters 2 | from pyrogram.types import ChatPermissions 3 | from pyrobot import COMMAND_HAND_LER 4 | from pyrobot.helper_functions.extract_user import extract_user 5 | from pyrobot.helper_functions.string_handling import extract_time 6 | from pyrobot.helper_functions.cust_p_filters import admin_fliter 7 | 8 | 9 | @Client.on_message(filters.command("mute", COMMAND_HAND_LER) & admin_fliter) 10 | async def mute_user(_, message): 11 | user_id, user_first_name, _ = extract_user(message) 12 | 13 | try: 14 | await message.chat.restrict_member( 15 | user_id=user_id, permissions=ChatPermissions() 16 | ) 17 | except Exception as error: 18 | await message.reply_text(str(error)) 19 | else: 20 | if str(user_id).lower().startswith("@"): 21 | await message.reply_text( 22 | "👍🏻 " f"{user_first_name}" " ലവന്റെ വായടച്ചിട്ടുണ്ട്! 🤐" 23 | ) 24 | else: 25 | await message.reply_text( 26 | "👍🏻 " 27 | f"" 28 | "ലവന്റെ" 29 | "" 30 | " വായടച്ചിട്ടുണ്ട്! 🤐" 31 | ) 32 | 33 | 34 | @Client.on_message(filters.command("tmute", COMMAND_HAND_LER) & admin_fliter) 35 | async def temp_mute_user(_, message): 36 | if not len(message.command) > 1: 37 | return 38 | 39 | user_id, user_first_name, _ = extract_user(message) 40 | 41 | until_date_val = extract_time(message.command[1]) 42 | if until_date_val is None: 43 | await message.reply_text( 44 | ( 45 | "അസാധുവായ സമയ തരം വ്യക്തമാക്കി. " 46 | "പ്രതീക്ഷിച്ചതു m, h, or d, കിട്ടിയത്: {}" 47 | ).format(message.command[1][-1]) 48 | ) 49 | return 50 | 51 | try: 52 | await message.chat.restrict_member( 53 | user_id=user_id, permissions=ChatPermissions(), until_date=until_date_val 54 | ) 55 | except Exception as error: 56 | await message.reply_text(str(error)) 57 | else: 58 | if str(user_id).lower().startswith("@"): 59 | await message.reply_text( 60 | "കുറച്ചുനേരം മിണ്ടാതിരിക്ക്! 😠" 61 | f"{user_first_name}" 62 | f" muted for {message.command[1]}!" 63 | ) 64 | else: 65 | await message.reply_text( 66 | "കുറച്ചുനേരം മിണ്ടാതിരിക്ക്! 😠" 67 | f"" 68 | "ലവന്റെ" 69 | "" 70 | " വായ " 71 | f" muted for {message.command[1]}!" 72 | ) 73 | -------------------------------------------------------------------------------- /pyrobot/plugins/restrictions/unban.py: -------------------------------------------------------------------------------- 1 | from pyrogram import Client, filters 2 | from pyrobot import COMMAND_HAND_LER 3 | from pyrobot.helper_functions.extract_user import extract_user 4 | from pyrobot.helper_functions.cust_p_filters import admin_fliter 5 | 6 | 7 | @Client.on_message( 8 | filters.command(["unban", "unmute"], COMMAND_HAND_LER) & admin_fliter 9 | ) 10 | async def un_ban_user(_, message): 11 | user_id, user_first_name, _ = extract_user(message) 12 | 13 | try: 14 | await message.chat.unban_member(user_id=user_id) 15 | except Exception as error: 16 | await message.reply_text(str(error)) 17 | else: 18 | if str(user_id).lower().startswith("@"): 19 | await message.reply_text( 20 | "ശരി, മാറ്റിയിട്ടുണ്ട്... ഇനി " 21 | f"{user_first_name} ക്ക് " 22 | " ഗ്രൂപ്പിൽ ചേരാൻ കഴിയും!" 23 | ) 24 | else: 25 | await message.reply_text( 26 | "ശരി, മാറ്റിയിട്ടുണ്ട്... ഇനി " 27 | f"" 28 | f"{user_first_name}" 29 | " ക്ക് " 30 | " ഗ്രൂപ്പിൽ ചേരാൻ കഴിയും!" 31 | ) 32 | -------------------------------------------------------------------------------- /pyrobot/plugins/tlifers/get.py: -------------------------------------------------------------------------------- 1 | import re 2 | from pyrogram import filters 3 | from pyrogram.types import Message 4 | from pyrobot import TG_URI 5 | from pyrobot.pyrobot import PyroBot 6 | from pyrobot.helper_functions.msg_types import get_file_id 7 | 8 | 9 | @PyroBot.on_message((filters.incoming), group=2) 10 | async def watch_all_messages(client: PyroBot, message: Message): 11 | to_match = message.text or message.caption or "" 12 | flt_list = client.filterstore.get(str(message.chat.id), []) 13 | for flt in flt_list: 14 | pattern = r"( |^|[^\w])" + re.escape(flt) + r"( |$|[^\w])" 15 | if re.search(pattern, to_match, flags=re.IGNORECASE): 16 | flt_message = await client.get_messages( 17 | chat_id=TG_URI, message_ids=flt_list[flt], replies=0 18 | ) 19 | n_m = message 20 | if message.reply_to_message: 21 | n_m = message.reply_to_message 22 | # 🥺 check two conditions 🤔🤔 23 | if flt_message.media: 24 | _, file_id = get_file_id(flt_message) 25 | caption = flt_message.caption 26 | if caption: 27 | caption = caption.html 28 | if not caption: 29 | caption = "" 30 | await n_m.reply_cached_media( 31 | file_id=file_id, 32 | caption=caption, 33 | reply_markup=flt_message.reply_markup, 34 | ) 35 | else: 36 | caption = flt_message.text 37 | if caption: 38 | caption = caption.html 39 | if not caption: 40 | caption = "" 41 | disable_web_page_preview = True 42 | if "gra.ph" in caption or "youtu" in caption: 43 | disable_web_page_preview = False 44 | await n_m.reply_text( 45 | text=caption, 46 | disable_web_page_preview=disable_web_page_preview, 47 | reply_markup=flt_message.reply_markup, 48 | ) 49 | break 50 | -------------------------------------------------------------------------------- /pyrobot/plugins/tlifers/others.py: -------------------------------------------------------------------------------- 1 | import json 2 | from pyrogram import filters 3 | from pyrobot import COMMAND_HAND_LER, MAX_MESSAGE_LENGTH, TG_IRU_S_M_ID, TG_URI 4 | from pyrobot.pyrobot import PyroBot 5 | from pyrobot.helper_functions.cust_p_filters import admin_fliter 6 | 7 | 8 | @PyroBot.on_message(filters.command(["clearfilter"], COMMAND_HAND_LER) & admin_fliter) 9 | async def clear_filter(client: PyroBot, message): 10 | status_message = await message.reply_text("checking 🤔🙄🙄", quote=True) 11 | flt_name = " ".join(message.command[1:]) 12 | flt_list = client.filterstore.get(str(message.chat.id), []) 13 | flt_list.pop(flt_name) 14 | await client.save_public_store(TG_IRU_S_M_ID, json.dumps(client.filterstore)) 15 | await status_message.edit_text( 16 | f"filter {flt_name} deleted from current chat." 17 | ) 18 | 19 | 20 | @PyroBot.on_message(filters.command(["listfilters", "filters"], COMMAND_HAND_LER)) 21 | async def list_filters(client: PyroBot, message): 22 | status_message = await message.reply_text("checking 🤔🙄🙄", quote=True) 23 | flt_list = client.filterstore.get(str(message.chat.id), []) 24 | msg = "Filters in {}:\n".format("the current chat") 25 | msg_p = msg 26 | for flt in flt_list: 27 | flt_name = " - {}\n".format(flt) 28 | if len(msg) + len(flt_name) > MAX_MESSAGE_LENGTH: 29 | await message.reply_text(msg) 30 | msg = "" 31 | msg += f"{flt_name}" 32 | if msg == msg_p: 33 | await status_message.edit_text("ഇൗ ചാറ്റിൽ കുറിപ്പുകളൊന്നുമില്ല.") 34 | 35 | elif len(msg) != 0: 36 | await message.reply_text(msg) 37 | await status_message.delete() 38 | 39 | 40 | @PyroBot.on_message(filters.command(["getfsnormat"], COMMAND_HAND_LER)) 41 | async def get_no_format_tlifer(client: PyroBot, message): 42 | flt_name = " ".join(message.command[1:]) 43 | flt_list = client.filterstore.get(str(message.chat.id), []) 44 | for flt in flt_list: 45 | if flt == flt_name: 46 | flt_message_id = flt_list[flt] 47 | flt_store_cid = str(TG_URI)[4:] 48 | await message.reply_text(f"https://t.me/c/{flt_store_cid}/{flt_message_id}") 49 | break 50 | -------------------------------------------------------------------------------- /pyrobot/plugins/tlifers/save.py: -------------------------------------------------------------------------------- 1 | import json 2 | from pyrogram import filters 3 | from pyrogram.types import InlineKeyboardMarkup 4 | from pyrobot import COMMAND_HAND_LER, TG_URI, TG_IRU_S_M_ID 5 | from pyrobot.pyrobot import PyroBot 6 | from pyrobot.helper_functions.cust_p_filters import admin_fliter 7 | from pyrobot.helper_functions.msg_types import get_note_type, Types 8 | 9 | 10 | @PyroBot.on_message( 11 | filters.command(["savefilter", "filter"], COMMAND_HAND_LER) & admin_fliter 12 | ) 13 | async def save_filter(client: PyroBot, message): 14 | status_message = await message.reply_text("checking 🤔🙄🙄", quote=True) 15 | if message.reply_to_message and message.reply_to_message.reply_markup is not None: 16 | fwded_mesg = await message.reply_to_message.forward( 17 | chat_id=TG_URI, disable_notification=True 18 | ) 19 | chat_id = message.chat.id 20 | filter_kw = " ".join(message.command[1:]) 21 | fm_id = fwded_mesg.id 22 | 23 | client.filterstore[str(chat_id)][filter_kw] = fm_id 24 | await client.save_public_store(TG_IRU_S_M_ID, json.dumps(client.filterstore)) 25 | 26 | await status_message.edit_text( 27 | f"filter {filter_kw} added" 28 | # f"{message.chat.title}" 29 | ) 30 | 31 | else: 32 | filter_kw, text, data_type, content, buttons = get_note_type(message, 2) 33 | 34 | if data_type is None: 35 | await status_message.edit_text("🤔 maybe note text is empty") 36 | return 37 | 38 | if not filter_kw: 39 | await status_message.edit_text( 40 | "എന്തിന്ന് ഉള്ള മറുപടി ആണ് എന്ന് വ്യക്തം ആക്കിയില്ല 🤔" 41 | ) 42 | return 43 | 44 | # construct message using the above parameters 45 | fwded_mesg = None 46 | reply_markup = None 47 | if len(buttons) > 0: 48 | reply_markup = InlineKeyboardMarkup(buttons) 49 | if data_type in (Types.BUTTON_TEXT, Types.TEXT): 50 | fwded_mesg = await client.send_message( 51 | chat_id=TG_URI, 52 | text=text, 53 | disable_web_page_preview=True, 54 | disable_notification=True, 55 | reply_to_message_id=1, 56 | reply_markup=reply_markup, 57 | ) 58 | elif data_type is not None: 59 | fwded_mesg = await client.send_cached_media( 60 | chat_id=TG_URI, 61 | file_id=content, 62 | caption=text, 63 | disable_notification=True, 64 | reply_to_message_id=1, 65 | reply_markup=reply_markup, 66 | ) 67 | 68 | # save to db 🤔 69 | if fwded_mesg is not None: 70 | chat_id = message.chat.id 71 | fm_id = fwded_mesg.id 72 | 73 | client.filterstore[str(chat_id)][filter_kw] = fm_id 74 | await client.save_public_store( 75 | TG_IRU_S_M_ID, json.dumps(client.filterstore) 76 | ) 77 | 78 | await status_message.edit_text( 79 | f"filter {filter_kw} added" 80 | # f"{message.chat.title}" 81 | ) 82 | else: 83 | await status_message.edit_text("🥺 this might be an error 🤔") 84 | -------------------------------------------------------------------------------- /pyrobot/plugins/tools/eval.py: -------------------------------------------------------------------------------- 1 | """Evaluate Python Code inside Telegram 2 | Syntax: .eval PythonCode""" 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | import html 8 | import io 9 | import sys 10 | import traceback 11 | from pyrogram import Client, filters 12 | from pyrobot import MAX_MESSAGE_LENGTH, COMMAND_HAND_LER 13 | from pyrobot.helper_functions.cust_p_filters import sudo_filter 14 | 15 | 16 | @Client.on_message(filters.command("eval", COMMAND_HAND_LER) & sudo_filter) 17 | async def eval(client, message): 18 | status_message = await message.reply_text("Processing ...") 19 | cmd = message.text.split(" ", maxsplit=1)[1] 20 | 21 | reply_to_ = message 22 | if message.reply_to_message: 23 | reply_to_ = message.reply_to_message 24 | 25 | old_stderr = sys.stderr 26 | old_stdout = sys.stdout 27 | redirected_output = sys.stdout = io.StringIO() 28 | redirected_error = sys.stderr = io.StringIO() 29 | stdout, stderr, exc = None, None, None 30 | 31 | try: 32 | await aexec(cmd, client, message) 33 | except Exception: 34 | exc = traceback.format_exc() 35 | 36 | stdout = redirected_output.getvalue() 37 | stderr = redirected_error.getvalue() 38 | sys.stdout = old_stdout 39 | sys.stderr = old_stderr 40 | 41 | evaluation = "" 42 | if exc: 43 | evaluation = exc 44 | elif stdout or stderr: 45 | evaluation = f"{stderr}\n\n{stdout}" 46 | else: 47 | evaluation = "Success" 48 | 49 | final_output = "EVAL: " 50 | final_output += f"{cmd}\n\n" 51 | final_output += "OUTPUT:\n" 52 | final_output += f'{evaluation.strip().replace("<", "<").replace(">", "&rt;").replace("&", "&")} \n' 53 | 54 | if len(final_output) > MAX_MESSAGE_LENGTH: 55 | with io.BytesIO(str.encode(final_output)) as out_file: 56 | out_file.name = "eval.text" 57 | await reply_to_.reply_document( 58 | document=out_file, 59 | caption=cmd[: MAX_MESSAGE_LENGTH // 4 - 1], 60 | disable_notification=True, 61 | quote=True, 62 | ) 63 | else: 64 | await reply_to_.reply_text(final_output, quote=True) 65 | await status_message.delete() 66 | 67 | 68 | async def aexec(code, client, message): 69 | exec( 70 | "async def __aexec(client, message): " 71 | + "".join(f"\n {l_}" for l_ in code.split("\n")) 72 | ) 73 | return await locals()["__aexec"](client, message) 74 | -------------------------------------------------------------------------------- /pyrobot/plugins/tools/exec.py: -------------------------------------------------------------------------------- 1 | """Execute GNU/Linux commands inside Telegram 2 | Syntax: .exec Code""" 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | 8 | import asyncio 9 | from io import BytesIO 10 | from pyrogram import Client, filters 11 | from pyrobot import MAX_MESSAGE_LENGTH, COMMAND_HAND_LER 12 | from pyrobot.helper_functions.cust_p_filters import sudo_filter 13 | 14 | 15 | @Client.on_message(filters.command("exec", COMMAND_HAND_LER) & sudo_filter) 16 | async def execution(_, message): 17 | status_message = await message.reply_text("Processing ...") 18 | # DELAY_BETWEEN_EDITS = 0.3 19 | # PROCESS_RUN_TIME = 100 20 | cmd = message.text.split(" ", maxsplit=1)[1] 21 | 22 | reply_to_ = message 23 | if message.reply_to_message: 24 | reply_to_ = message.reply_to_message 25 | 26 | # start_time = time.time() + PROCESS_RUN_TIME 27 | process = await asyncio.create_subprocess_shell( 28 | cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE 29 | ) 30 | stdout, stderr = await process.communicate() 31 | e = stderr.decode() 32 | if not e: 33 | e = "😂" 34 | o = stdout.decode() 35 | if not o: 36 | o = "😐" 37 | 38 | OUTPUT = "" 39 | OUTPUT += f"QUERY:\nCommand:\n{cmd} \n" 40 | OUTPUT += f"PID: {process.pid}\n\n" 41 | OUTPUT += f"stderr: \n{e}\n\n" 42 | OUTPUT += f"stdout: \n{o}" 43 | 44 | if len(OUTPUT) > MAX_MESSAGE_LENGTH: 45 | with BytesIO(str.encode(OUTPUT)) as out_file: 46 | out_file.name = "exec.text" 47 | await reply_to_.reply_document( 48 | document=out_file, 49 | caption=cmd[: MAX_MESSAGE_LENGTH // 4 - 1], 50 | disable_notification=True, 51 | quote=True, 52 | ) 53 | else: 54 | await reply_to_.reply_text(OUTPUT, quote=True) 55 | 56 | await status_message.delete() 57 | -------------------------------------------------------------------------------- /pyrobot/plugins/tools/google.py: -------------------------------------------------------------------------------- 1 | """ 2 | Google Search 3 | Syntax: .google query 4 | """ 5 | 6 | import aiohttp 7 | from pyrogram import Client, filters 8 | from pyrogram.types import Message, InputMediaDocument 9 | from pyrobot import Config, COMMAND_HAND_LER 10 | from pyrobot.helper_functions.cust_p_filters import sudo_filter 11 | 12 | 13 | @Client.on_message( 14 | filters.command("google", COMMAND_HAND_LER) & 15 | sudo_filter 16 | ) 17 | async def google_(_, message: Message): 18 | if len(message.command) <= 1: 19 | return await message.reply_text( 20 | "എന്ത് ചെയ്യണം എന്ന് പറഞ്ഞില്ല!" 21 | ) 22 | query = message.text.split(" ", maxsplit=1)[1] 23 | async with aiohttp.ClientSession() as requests: 24 | input_url = "https://bots.shrimadhavuk.me/search/" 25 | headers = {"USER-AGENT": "UseTGBot"} 26 | data = { 27 | "q": query, 28 | "app_id": Config.USE_TG_BOT_APP_ID 29 | } 30 | reponse = await requests.get( 31 | input_url, 32 | params=data, 33 | headers=headers 34 | ) 35 | response = await reponse.json() 36 | text = f"Query: {query}\n" 37 | for result in response.get("results", []): 38 | title = result.get("title") 39 | url = result.get("url") 40 | # description = result.get("description") 41 | # image = result.get("image") 42 | text += f" 👉🏻 {title} \n\n" 43 | await message.reply_text( 44 | text, 45 | quote=True, 46 | disable_web_page_preview=True 47 | ) 48 | 49 | 50 | @Client.on_message( 51 | filters.command("gim", COMMAND_HAND_LER) & 52 | sudo_filter 53 | ) 54 | async def google_image_(_, message: Message): 55 | if len(message.command) <= 1: 56 | return await message.reply_text( 57 | "എന്ത് ചെയ്യണം എന്ന് പറഞ്ഞില്ല!" 58 | ) 59 | query = message.text.split(" ", maxsplit=1)[1] 60 | async with aiohttp.ClientSession() as requests: 61 | input_url = "https://bots.shrimadhavuk.me/search/" 62 | headers = {"USER-AGENT": "UseTGBot"} 63 | data = { 64 | "q": query, 65 | "app_id": Config.USE_TG_BOT_APP_ID, 66 | "p": "GoogleImages" 67 | } 68 | reponse = await requests.get( 69 | input_url, 70 | params=data, 71 | headers=headers 72 | ) 73 | response = await reponse.json() 74 | text = f"Query: {query}\n" 75 | url_list = [] 76 | for result in response.get("results", []): 77 | if len(url_list) > 9: 78 | break 79 | caption = result.get("description") 80 | image_url = result.get("url") 81 | url_list.append( 82 | InputMediaDocument( 83 | media=image_url, 84 | caption=caption 85 | ) 86 | ) 87 | await message.reply_media_group( 88 | url_list, 89 | quote=True 90 | ) 91 | -------------------------------------------------------------------------------- /pyrobot/plugins/tools/json.py: -------------------------------------------------------------------------------- 1 | """Get Detailed info about any message 2 | Syntax: .json""" 3 | 4 | import os 5 | from pyrogram import Client, filters 6 | from pyrobot import COMMAND_HAND_LER 7 | from pyrobot.helper_functions.cust_p_filters import sudo_filter 8 | 9 | 10 | @Client.on_message(filters.command("json", COMMAND_HAND_LER) & sudo_filter) 11 | async def jsonify(_, message): 12 | the_real_message = None 13 | reply_to_id = None 14 | 15 | if message.reply_to_message: 16 | the_real_message = message.reply_to_message 17 | else: 18 | the_real_message = message 19 | 20 | try: 21 | await message.reply_text(f"{the_real_message}") 22 | except Exception as e: 23 | with open("json.text", "w+", encoding="utf8") as out_file: 24 | out_file.write(str(the_real_message)) 25 | await message.reply_document( 26 | document="json.text", 27 | caption=str(e), 28 | disable_notification=True, 29 | reply_to_message_id=reply_to_id, 30 | ) 31 | os.remove("json.text") 32 | -------------------------------------------------------------------------------- /pyrobot/plugins/tools/pastebin.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2020 by SpEcHiDe@Github, < https://github.com/SpEcHiDe >. 2 | # 3 | # This file is part of < https://github.com/SpEcHiDe/PyroGramBot > project, 4 | # and is released under the 5 | # "GNU Affero General Public License v3.0 License Agreement". 6 | # Please see < https://github.com/SpEcHiDe/PyroGramBot/raw/master/COPYING > 7 | # 8 | """IX.IO pastebin like site 9 | Syntax: .paste""" 10 | 11 | import io 12 | import aiohttp 13 | from json import loads 14 | from json.decoder import JSONDecodeError 15 | import os 16 | from urllib.parse import urlparse 17 | from pyrogram import Client, filters 18 | from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup, Message 19 | from pyrobot import COMMAND_HAND_LER, TMP_DOWNLOAD_DIRECTORY, paste_bin_store_s 20 | 21 | 22 | @Client.on_message(filters.command("paste", COMMAND_HAND_LER)) 23 | async def paste_bin(client: Client, message: Message): 24 | status_message = await message.reply_text("...", quote=True) 25 | downloaded_file_name = None 26 | 27 | # first we need to get the data to be pasted 28 | if ( 29 | message.reply_to_message and 30 | message.reply_to_message.document and 31 | "text/" in message.reply_to_message.document.mime_type 32 | ): 33 | file_obj = io.BytesIO() 34 | downloaded_file_name = "" 35 | async for chunk in client.stream_media(message.reply_to_message): 36 | file_obj.write(chunk) 37 | downloaded_file_name = file_obj.getvalue().decode("UTF-8") 38 | elif ( 39 | message.reply_to_message and 40 | not message.reply_to_message.media 41 | ): 42 | downloaded_file_name = message.reply_to_message.text.html 43 | # elif len(message.command) > 1: 44 | # downloaded_file_name = " ".join(message.command[1:]) 45 | else: 46 | await status_message.edit("എന്ത് ചെയ്യണം എന്ന് പറഞ്ഞില്ല") 47 | return 48 | 49 | if downloaded_file_name is None: 50 | await status_message.edit("എന്ത് ചെയ്യണം എന്ന് പറഞ്ഞില്ല") 51 | return 52 | 53 | json_paste_data = {"content": downloaded_file_name} 54 | 55 | chosen_store = "pasty" 56 | if len(message.command) == 2: 57 | chosen_store = message.command[1] 58 | 59 | # get the required pastebin URI 60 | paste_store_ = paste_bin_store_s.get(chosen_store) 61 | 62 | if not paste_store_: 63 | av_kys = ", ".join(paste_bin_store_s.keys()) 64 | await status_message.edit(f"available keys: {av_kys}") 65 | return 66 | 67 | paste_store_url = paste_store_.get("URL") 68 | paste_store_base_url_rp = urlparse(paste_store_url) 69 | 70 | # the pastebin sites, respond with only the "key" 71 | # we need to prepend the BASE_URL of the appropriate site 72 | paste_store_base_url = ( 73 | paste_store_base_url_rp.scheme + "://" + paste_store_base_url_rp.netloc 74 | ) 75 | 76 | async with aiohttp.ClientSession() as session: 77 | response_d = await session.post( 78 | paste_store_url, json=json_paste_data, headers=paste_store_.get("HEADERS") 79 | ) 80 | response_jn = await response_d.text() 81 | # print(response_jn) 82 | try: 83 | response_jn = loads(response_jn.strip()) 84 | except JSONDecodeError: 85 | # some sites, do not have JSON response 86 | pass 87 | 88 | rk = paste_store_.get("RAV") 89 | pkr = response_jn 90 | if rk: 91 | rkp = rk.split(".") 92 | for kp in rkp: 93 | pkr = pkr.get(kp) 94 | elif not rk: 95 | pkr = pkr[1:] 96 | required_url = paste_store_base_url + "/" + pkr 97 | 98 | kr = paste_store_.get("AVDTS") 99 | reply_markup = None 100 | if kr: 101 | kor = response_jn.get(kr) 102 | reply_markup = InlineKeyboardMarkup( 103 | [ 104 | [ 105 | InlineKeyboardButton( 106 | text="❌ delete paste ❎", callback_data=f"pb_{kor}_{pkr}" 107 | ) 108 | ] 109 | ] 110 | ) 111 | 112 | # finally, edit the bot sent message 113 | await status_message.delete() 114 | await message.reply_text( 115 | required_url, 116 | quote=True, 117 | reply_markup=reply_markup, 118 | disable_web_page_preview=True, 119 | ) 120 | -------------------------------------------------------------------------------- /pyrobot/plugins/tools/ping.py: -------------------------------------------------------------------------------- 1 | """Telegram Ping / Pong Speed 2 | Syntax: .ping""" 3 | 4 | import time 5 | from pyrogram import Client, filters, __version__ 6 | from pyrogram.raw.all import layer 7 | from pyrobot import COMMAND_HAND_LER 8 | from pyrobot.helper_functions.cust_p_filters import f_onw_fliter 9 | 10 | # -- Constants -- # 11 | ALIVE = ( 12 | "ചത്തിട്ടില്ലാ..." 13 | f"\nL{layer}" 14 | f"\nv{__version__}" 15 | ) 16 | HELP = "CAADAgAD6AkAAowucAABsFGHedLEzeUWBA" 17 | REPO = "User / Bot is available on GitHub:\n" "https://github.com/SpEcHiDe/PyroGramBot" 18 | # -- Constants End -- # 19 | 20 | 21 | @Client.on_message(filters.command(["alive", "start"], COMMAND_HAND_LER) & f_onw_fliter) 22 | async def check_alive(_, message): 23 | await message.reply_text(ALIVE) 24 | 25 | 26 | @Client.on_message(filters.command("help", COMMAND_HAND_LER) & f_onw_fliter) 27 | async def help_me(_, message): 28 | await message.reply_sticker(HELP) 29 | 30 | 31 | @Client.on_message(filters.command("ping", COMMAND_HAND_LER) & f_onw_fliter) 32 | async def ping(_, message): 33 | start_t = time.time() 34 | rm = await message.reply_text("...") 35 | end_t = time.time() 36 | time_taken_s = (end_t - start_t) * 1000 37 | await rm.edit(f"Pong!\n{time_taken_s:.3f} ms") 38 | 39 | 40 | @Client.on_message(filters.command("repo", COMMAND_HAND_LER) & f_onw_fliter) 41 | async def repo(_, message): 42 | await message.reply_text(REPO) 43 | -------------------------------------------------------------------------------- /pyrobot/plugins/up_utils/download.py: -------------------------------------------------------------------------------- 1 | """Download Telegram Media 2 | Syntax: .download""" 3 | 4 | import asyncio 5 | import math 6 | import os 7 | import time 8 | from datetime import datetime 9 | from pySmartDL import SmartDL 10 | from pyrogram import Client, filters 11 | from pyrobot import COMMAND_HAND_LER, LOGGER, TMP_DOWNLOAD_DIRECTORY 12 | from pyrobot.helper_functions.display_progress_dl_up import ( 13 | progress_for_pyrogram, 14 | humanbytes, 15 | ) 16 | from pyrobot.helper_functions.cust_p_filters import sudo_filter 17 | 18 | 19 | @Client.on_message(filters.command("download", COMMAND_HAND_LER) & sudo_filter) 20 | async def down_load_media(client, sms): 21 | message = await sms.reply_text("...", quote=True) 22 | if not os.path.isdir(TMP_DOWNLOAD_DIRECTORY): 23 | os.makedirs(TMP_DOWNLOAD_DIRECTORY) 24 | if sms.reply_to_message is not None: 25 | start_t = datetime.now() 26 | download_location = TMP_DOWNLOAD_DIRECTORY + "/" 27 | c_time = time.time() 28 | the_real_download_location = await client.download_media( 29 | message=sms.reply_to_message, 30 | file_name=download_location, 31 | progress=progress_for_pyrogram, 32 | progress_args=("trying to download", message, c_time), 33 | ) 34 | end_t = datetime.now() 35 | ms = (end_t - start_t).seconds 36 | await message.edit( 37 | f"Downloaded to {the_real_download_location} in {ms} seconds" 38 | ) 39 | elif len(sms.command) > 1: 40 | start_t = datetime.now() 41 | the_url_parts = " ".join(sms.command[1:]) 42 | url = the_url_parts.strip() 43 | custom_file_name = os.path.basename(url) 44 | if "|" in the_url_parts: 45 | url, custom_file_name = the_url_parts.split("|") 46 | url = url.strip() 47 | custom_file_name = custom_file_name.strip() 48 | download_file_path = os.path.join(TMP_DOWNLOAD_DIRECTORY, custom_file_name) 49 | downloader = SmartDL(url, download_file_path, progress_bar=False) 50 | downloader.start(blocking=False) 51 | c_time = time.time() 52 | while not downloader.isFinished(): 53 | total_length = downloader.filesize if downloader.filesize else None 54 | downloaded = downloader.get_dl_size() 55 | display_message = "" 56 | now = time.time() 57 | diff = now - c_time 58 | percentage = downloader.get_progress() * 100 59 | speed = downloader.get_speed() 60 | elapsed_time = round(diff) * 1000 61 | progress_str = "[{0}{1}]\nProgress: {2}%".format( 62 | "".join(["█" for i in range(math.floor(percentage / 5))]), 63 | "".join(["░" for i in range(20 - math.floor(percentage / 5))]), 64 | round(percentage, 2), 65 | ) 66 | estimated_total_time = downloader.get_eta(human=True) 67 | try: 68 | current_message = "trying to download\n" 69 | current_message += f"URL: {url}\n" 70 | current_message += f"File Name: {custom_file_name}\n" 71 | current_message += f"{progress_str}\n" 72 | current_message += ( 73 | f"{humanbytes(downloaded)} of {humanbytes(total_length)}\n" 74 | ) 75 | current_message += f"ETA: {estimated_total_time}" 76 | if round(diff % 10.00) == 0 and current_message != display_message: 77 | await message.edit( 78 | disable_web_page_preview=True, text=current_message 79 | ) 80 | display_message = current_message 81 | await asyncio.sleep(10) 82 | except Exception as e: 83 | LOGGER.info(str(e)) 84 | pass 85 | if os.path.exists(download_file_path): 86 | end_t = datetime.now() 87 | ms = (end_t - start_t).seconds 88 | await message.edit( 89 | f"Downloaded to {download_file_path} in {ms} seconds" 90 | ) 91 | else: 92 | await message.edit("Reply to a Telegram Media, to download it to local server.") 93 | -------------------------------------------------------------------------------- /pyrobot/plugins/up_utils/gDrive.py: -------------------------------------------------------------------------------- 1 | """Google Drive Plugins 2 | Syntax: .gdrive""" 3 | 4 | import math 5 | import httplib2 6 | import os 7 | import time 8 | from googleapiclient.discovery import build 9 | from googleapiclient.http import MediaFileUpload 10 | from mimetypes import guess_type 11 | from oauth2client.client import OAuth2WebServerFlow 12 | from pyrogram import Client, filters 13 | from pyrobot import ( 14 | COMMAND_HAND_LER, 15 | DB_URI, 16 | G_DRIVE_CLIENT_ID, 17 | G_DRIVE_CLIENT_SECRET, 18 | LOGGER, 19 | TMP_DOWNLOAD_DIRECTORY, 20 | ) 21 | from pyrobot.helper_functions.display_progress_dl_up import progress_for_pyrogram 22 | from pyrobot.helper_functions.cust_p_filters import sudo_filter 23 | 24 | if DB_URI is not None: 25 | import pyrobot.helper_functions.sql_helpers.gDrive_sql as sql 26 | 27 | # Check https://developers.google.com/drive/scopes for all available scopes 28 | OAUTH_SCOPE = "https://www.googleapis.com/auth/drive" 29 | # Redirect URI for installed apps, can be left as is 30 | REDIRECT_URI = "urn:ietf:wg:oauth:2.0:oob" 31 | # global variable to indicate mimeType of directories in gDrive 32 | G_DRIVE_DIR_MIME_TYPE = "application/vnd.google-apps.folder" 33 | # global variable to save the state of the authentication FLOW 34 | flow = None 35 | 36 | 37 | @Client.on_message(filters.command("gdrive", COMMAND_HAND_LER) & sudo_filter) 38 | async def g_drive_commands(client, message): 39 | status_message = await message.reply_text("...", quote=True) 40 | if len(message.command) > 1: 41 | current_recvd_command = message.command[1] 42 | if current_recvd_command == "setup": 43 | await g_drive_setup(message) 44 | elif current_recvd_command == "reset": 45 | sql.clear_credential(message.from_user.id) 46 | await status_message.edit_text(text="cleared saved credentials") 47 | elif current_recvd_command == "confirm": 48 | if len(message.command) == 3: 49 | await AskUserToVisitLinkAndGiveCode(status_message, message.command[2]) 50 | else: 51 | await status_message.edit_text(text="please give auth_code correctly") 52 | elif current_recvd_command == "search": 53 | # The gDrive table stores the user's access and refresh tokens, and is 54 | # created automatically when the authorization flow completes for the first 55 | # time. 56 | creds = sql.get_credential(message.from_user.id) 57 | # If there are no (valid) credentials available, throw error 58 | if not creds or not creds.invalid: 59 | if creds and creds.refresh_token: 60 | creds.refresh(get_new_http_instance()) 61 | # Save the credentials for the next run 62 | sql.set_credential(message.from_user.id, creds) 63 | # 64 | if len(message.command) > 2: 65 | search_query = " ".join(message.command[2:]) 66 | message_string = "gDrive Search Query:" 67 | message_string += f"{search_query}\n\n" 68 | message_string += "Results:\n" 69 | message_string += await search_g_drive(creds, search_query) 70 | await status_message.edit_text( 71 | text=message_string, disable_web_page_preview=True 72 | ) 73 | else: 74 | await status_message.edit_text( 75 | "syntax:\n" 76 | f"{COMMAND_HAND_LER}gdrive search (QUERY) " 77 | ) 78 | else: 79 | await status_message.edit_text( 80 | "invalid credentials, supplied??!\n" 81 | f"use {COMMAND_HAND_LER}gdrive reset " 82 | "to clear saved credentials" 83 | ) 84 | return 85 | else: 86 | await status_message.edit_text( 87 | text=f"please run {COMMAND_HAND_LER}gdrive setup first" 88 | ) 89 | elif current_recvd_command == "upload": 90 | # The gDrive table stores the user's access and refresh tokens, and is 91 | # created automatically when the authorization flow completes for the first 92 | # time. 93 | creds = sql.get_credential(message.from_user.id) 94 | # If there are no (valid) credentials available, throw error 95 | if not creds or not creds.invalid: 96 | if creds and creds.refresh_token: 97 | creds.refresh(get_new_http_instance()) 98 | # Save the credentials for the next run 99 | sql.set_credential(message.from_user.id, creds) 100 | # 101 | if len(message.command) > 2: 102 | upload_file_name = " ".join(message.command[2:]) 103 | if not os.path.exists(upload_file_name): 104 | await status_message.edit_text( 105 | "invalid file path provided?" 106 | ) 107 | return 108 | gDrive_file_id = await gDrive_upload_file( 109 | creds, upload_file_name, status_message 110 | ) 111 | reply_message_text = "" 112 | if gDrive_file_id is not None: 113 | reply_message_text += "Uploaded to " + gDrive_file_id + "" 117 | else: 118 | reply_message_text += "failed to upload.. check logs?" 119 | await status_message.edit_text( 120 | text=reply_message_text, disable_web_page_preview=True 121 | ) 122 | elif message.reply_to_message is not None: 123 | if not os.path.isdir(TMP_DOWNLOAD_DIRECTORY): 124 | os.makedirs(TMP_DOWNLOAD_DIRECTORY) 125 | download_location = TMP_DOWNLOAD_DIRECTORY + "/" 126 | c_time = time.time() 127 | the_real_download_location = await client.download_media( 128 | message=message.reply_to_message, 129 | file_name=download_location, 130 | progress=progress_for_pyrogram, 131 | progress_args=( 132 | "trying to download", 133 | status_message, 134 | c_time, 135 | ), 136 | ) 137 | await status_message.edit( 138 | f"Downloaded to {the_real_download_location}" 139 | ) 140 | if not os.path.exists(the_real_download_location): 141 | await message.edit_text("invalid file path provided?") 142 | return 143 | gDrive_file_id = await gDrive_upload_file( 144 | creds, the_real_download_location, status_message 145 | ) 146 | reply_message_text = "" 147 | if gDrive_file_id is not None: 148 | reply_message_text += "Uploaded to " + gDrive_file_id + "" 152 | else: 153 | reply_message_text += "failed to upload.. check logs?" 154 | os.remove(the_real_download_location) 155 | await status_message.edit_text( 156 | text=reply_message_text, disable_web_page_preview=True 157 | ) 158 | else: 159 | await status_message.edit_text( 160 | "syntax:\n" 161 | f"{COMMAND_HAND_LER}gdrive upload (file name) " 162 | ) 163 | else: 164 | await status_message.edit_text( 165 | "invalid credentials, supplied??!\n" 166 | f"use {COMMAND_HAND_LER}gdrive reset " 167 | "to clear saved credentials" 168 | ) 169 | return 170 | else: 171 | await status_message.edit_text( 172 | text=f"please run {COMMAND_HAND_LER}gdrive setup first" 173 | ) 174 | else: 175 | await status_message.edit_text(text="type correctly") 176 | 177 | 178 | async def g_drive_setup(message): 179 | # The gDrive table stores the user's access and refresh tokens, and is 180 | # created automatically when the authorization flow completes for the first 181 | # time. 182 | creds = sql.get_credential(message.from_user.id) 183 | # If there are no (valid) credentials available, let the user log in. 184 | if not creds or not creds.invalid: 185 | if creds and creds.refresh_token: 186 | creds.refresh(get_new_http_instance()) 187 | # Save the credentials for the next run 188 | sql.set_credential(message.from_user.id, creds) 189 | # 190 | await message.edit_text(text="gDrive authentication credentials, refreshed") 191 | else: 192 | global flow 193 | # Run through the OAuth flow and retrieve credentials 194 | flow = OAuth2WebServerFlow( 195 | G_DRIVE_CLIENT_ID, 196 | G_DRIVE_CLIENT_SECRET, 197 | OAUTH_SCOPE, 198 | redirect_uri=REDIRECT_URI, 199 | ) 200 | authorize_url = flow.step1_get_authorize_url() 201 | reply_string = f"please visit {authorize_url} and " 202 | reply_string += "send back " 203 | reply_string += ( 204 | f"{COMMAND_HAND_LER}gdrive confirm (RECEIVED_CODE)" 205 | ) 206 | await message.edit_text(text=reply_string, disable_web_page_preview=True) 207 | else: 208 | await message.edit_text(text="don't type this command -_-") 209 | 210 | 211 | async def AskUserToVisitLinkAndGiveCode(message, code): 212 | creds = None 213 | global flow 214 | if flow is None: 215 | await message.edit_text( 216 | text=f"run {COMMAND_HAND_LER}gdrive setup first." 217 | ) 218 | return 219 | await message.edit_text(text="checking received code ...") 220 | creds = flow.step2_exchange(code) 221 | # 222 | # Save the credentials for the next run 223 | sql.set_credential(message.reply_to_message.from_user.id, creds) 224 | # 225 | await message.edit_text(text="saved gDrive authentication credentials") 226 | # clear the global variable once the authentication FLOW is finished 227 | flow = None 228 | 229 | 230 | async def search_g_drive(creds, search_query): 231 | service = build("drive", "v3", credentials=creds, cache_discovery=False) 232 | # 233 | query = "name contains '{}'".format(search_query) 234 | page_token = None 235 | # Call the Drive v3 API 236 | results = ( 237 | service.files() 238 | .list( 239 | q=query, 240 | spaces="drive", 241 | fields="nextPageToken, files(id, name)", 242 | pageToken=page_token, 243 | ) 244 | .execute() 245 | ) 246 | items = results.get("files", []) 247 | message_string = "" 248 | if not items: 249 | message_string = "no files found in your gDrive?" 250 | else: 251 | for item in items: 252 | message_string += "👉 " 255 | message_string += item.get("name") 256 | message_string += "" 257 | message_string += "\n" 258 | return message_string 259 | 260 | 261 | async def gDrive_upload_file(creds, file_path, message): 262 | # Create Google Drive service instance 263 | service = build("drive", "v3", credentials=creds, cache_discovery=False) 264 | # getting the mime type of the file 265 | mime_type = guess_type(file_path)[0] 266 | mime_type = mime_type if mime_type else "text/plain" 267 | # File body description 268 | media_body = MediaFileUpload( 269 | file_path, mimetype=mime_type, chunksize=150 * 1024 * 1024, resumable=True 270 | ) 271 | file_name = os.path.basename(file_path) 272 | body = { 273 | "name": file_name, 274 | "description": "Uploaded using PyrogramUserBot gDrive v7", 275 | "mimeType": mime_type, 276 | } 277 | # Insert a file 278 | u_file_obj = service.files().create(body=body, media_body=media_body) 279 | response = None 280 | display_message = "" 281 | while response is None: 282 | status, response = u_file_obj.next_chunk() 283 | # await asyncio.sleep(5) 284 | if status: 285 | percentage = int(status.progress() * 100) 286 | progress_str = "[{0}{1}]\nProgress: {2}%\n".format( 287 | "".join(["█" for i in range(math.floor(percentage / 5))]), 288 | "".join(["░" for i in range(20 - math.floor(percentage / 5))]), 289 | round(percentage, 2), 290 | ) 291 | current_message = ( 292 | f"uploading to gDrive\nFile Name: {file_name}\n{progress_str}" 293 | ) 294 | if display_message != current_message: 295 | try: 296 | await message.edit_text(current_message) 297 | display_message = current_message 298 | except Exception as e: 299 | LOGGER.info(str(e)) 300 | pass 301 | # Permissions body description: anyone who has link can upload 302 | # Other permissions can be found at https://developers.google.com/drive/v3/reference/permissions 303 | # permissions = { 304 | # "role": "reader", 305 | # "type": "anyone", 306 | # "value": None, 307 | # "withLink": True 308 | # } 309 | # try: 310 | # # Insert new permissions 311 | # service.permissions().insert(fileId=file_id, body=permissions).execute() 312 | # except: 313 | # pass 314 | file_id = response.get("id") 315 | return file_id 316 | 317 | 318 | # https://github.com/googleapis/google-api-python-client/blob/master/docs/thread_safety.md 319 | # Create a new Http() object for every request 320 | def build_request(http, *args, **kwargs): 321 | new_http = httplib2.Http() 322 | return apiclient.http.HttpRequest(new_http, *args, **kwargs) 323 | 324 | 325 | def get_new_http_instance(): 326 | return httplib2.Http() 327 | -------------------------------------------------------------------------------- /pyrobot/plugins/up_utils/thumbnail.py: -------------------------------------------------------------------------------- 1 | """ThumbNail utilities, © @AnyDLBot 2 | Available Commands: 3 | .savethumbnail 4 | .clearthumbnail 5 | .getthumbnail""" 6 | 7 | import os 8 | import time 9 | from hachoir.metadata import extractMetadata 10 | from hachoir.parser import createParser 11 | from PIL import Image 12 | from pyrogram import Client, filters 13 | from pyrobot import COMMAND_HAND_LER, TMP_DOWNLOAD_DIRECTORY 14 | from pyrobot.helper_functions.cust_p_filters import sudo_filter 15 | from pyrobot.helper_functions.display_progress_dl_up import progress_for_pyrogram 16 | 17 | 18 | thumb_image_path = TMP_DOWNLOAD_DIRECTORY + "/thumb_image.jpg" 19 | 20 | 21 | @Client.on_message(filters.command("savethumbnail", COMMAND_HAND_LER) & sudo_filter) 22 | async def save_thumb_nail(client, message): 23 | status_message = await message.reply_text("...") 24 | if message.reply_to_message is not None: 25 | if not os.path.isdir(TMP_DOWNLOAD_DIRECTORY): 26 | os.makedirs(TMP_DOWNLOAD_DIRECTORY) 27 | download_location = TMP_DOWNLOAD_DIRECTORY + "/" 28 | c_time = time.time() 29 | downloaded_file_name = await client.download_media( 30 | message=message.reply_to_message, 31 | file_name=download_location, 32 | progress=progress_for_pyrogram, 33 | progress_args=("trying to download", status_message, c_time), 34 | ) 35 | # https://stackoverflow.com/a/21669827/4723940 36 | Image.open(downloaded_file_name).convert("RGB").save(downloaded_file_name) 37 | metadata = extractMetadata(createParser(downloaded_file_name)) 38 | height = 0 39 | if metadata.has("height"): 40 | height = metadata.get("height") 41 | # resize image 42 | # ref: https://t.me/PyrogramChat/44663 43 | img = Image.open(downloaded_file_name) 44 | # https://stackoverflow.com/a/37631799/4723940 45 | # img.thumbnail((320, 320)) 46 | img.resize((320, height)) 47 | img.save(thumb_image_path, "JPEG") 48 | # https://pillow.readthedocs.io/en/3.1.x/reference/Image.html#create-thumbnails 49 | os.remove(downloaded_file_name) 50 | await status_message.edit( 51 | "Custom video / file thumbnail saved. " 52 | "This image will be used in the upload, " 53 | "till .clearthumbnail." 54 | ) 55 | else: 56 | await status_message.edit("Reply to a photo to save custom thumbnail") 57 | 58 | 59 | @Client.on_message(filters.command("clearthumbnail", COMMAND_HAND_LER) & sudo_filter) 60 | async def clear_thumb_nail(client, message): 61 | status_message = await message.reply_text("...") 62 | if os.path.exists(thumb_image_path): 63 | os.remove(thumb_image_path) 64 | await status_message.edit("✅ Custom thumbnail cleared succesfully.") 65 | 66 | 67 | @Client.on_message(filters.command("getthumbnail", COMMAND_HAND_LER) & sudo_filter) 68 | async def get_thumb_nail(client, message): 69 | status_message = await message.reply_text("...") 70 | if message.reply_to_message is not None: 71 | """reply_to_message = message.reply_to_message 72 | thumb_image_file_id = None 73 | file_di_ref = None 74 | if reply_to_message.document is not None: 75 | thumb_image_file_id = reply_to_message.document.thumbs[0].file_id 76 | file_di_ref = reply_to_message.document.file_ref 77 | if reply_to_message.video is not None: 78 | thumb_image_file_id = reply_to_message.video.thumbs[0].file_id 79 | file_di_ref = reply_to_message.video.file_ref 80 | if thumb_image_file_id is not None: 81 | print(thumb_image_file_id) 82 | print(file_di_ref) 83 | download_location = TMP_DOWNLOAD_DIRECTORY + "/" 84 | c_time = time.time() 85 | downloaded_file_name = await client.download_media( 86 | message=thumb_image_file_id, 87 | file_ref=file_di_ref, 88 | file_name=download_location, 89 | progress=progress_for_pyrogram, 90 | progress_args=( 91 | "trying to download", message, c_time 92 | ) 93 | ) 94 | print(downloaded_file_name) 95 | await client.send_document( 96 | chat_id=message.chat.id, 97 | document=downloaded_file_name, 98 | disable_notification=True, 99 | reply_to_message_id=message.id 100 | ) 101 | os.remove(downloaded_file_name) 102 | await message.delete()""" 103 | await status_message.edit("issues") 104 | elif os.path.exists(thumb_image_path): 105 | caption_str = ( 106 | "Currently Saved Thumbnail. " "Clear with .clearthumbnail" 107 | ) 108 | await message.reply_document( 109 | document=thumb_image_path, caption=caption_str, disable_notification=True 110 | ) 111 | await status_message.delete() 112 | else: 113 | await status_message.edit( 114 | "Reply .gethumbnail as a reply to a media" 115 | ) 116 | -------------------------------------------------------------------------------- /pyrobot/plugins/up_utils/upload.py: -------------------------------------------------------------------------------- 1 | """Upload Local Media to Telegram 2 | Syntax: .upload* *""" 3 | 4 | import os 5 | import time 6 | from datetime import datetime 7 | from pyrogram import Client, filters 8 | from pyrobot import COMMAND_HAND_LER 9 | from pyrobot.helper_functions.check_if_thumb_exists import is_thumb_image_exists 10 | from pyrobot.helper_functions.cust_p_filters import sudo_filter 11 | from pyrobot.helper_functions.display_progress_dl_up import progress_for_pyrogram 12 | 13 | 14 | @Client.on_message(filters.command("uploadasdoc", COMMAND_HAND_LER) & sudo_filter) 15 | async def upload_as_document(client, message): 16 | status_message = await message.reply_text("...") 17 | if " " in message.text: 18 | recvd_command, local_file_name = message.text.split(" ", 1) 19 | if os.path.exists(local_file_name): 20 | thumb_image_path = await is_thumb_image_exists(local_file_name) 21 | start_t = datetime.now() 22 | c_time = time.time() 23 | doc_caption = os.path.basename(local_file_name) 24 | await message.reply_document( 25 | document=local_file_name, 26 | thumb=thumb_image_path, 27 | caption=doc_caption, 28 | disable_notification=True, 29 | reply_to_message_id=message.id, 30 | progress=progress_for_pyrogram, 31 | progress_args=("trying to upload", status_message, c_time), 32 | ) 33 | end_t = datetime.now() 34 | ms = (end_t - start_t).seconds 35 | await status_message.edit(f"Uploaded in {ms} seconds") 36 | else: 37 | await status_message.edit("404: media not found") 38 | else: 39 | await status_message.edit( 40 | f"{COMMAND_HAND_LER}uploadasdoc FILE_PATH to upload to current Telegram chat" 41 | ) 42 | 43 | 44 | @Client.on_message(filters.command("uploadasvideo", COMMAND_HAND_LER) & sudo_filter) 45 | async def upload_as_video(client, message): 46 | status_message = await message.reply_text("...") 47 | if " " in message.text: 48 | recvd_command, local_file_name = message.text.split(" ", 1) 49 | if os.path.exists(local_file_name): 50 | thumb_image_path = await is_thumb_image_exists(local_file_name) 51 | start_t = datetime.now() 52 | c_time = time.time() 53 | doc_caption = os.path.basename(local_file_name) 54 | await message.reply_video( 55 | video=local_file_name, 56 | caption=doc_caption, 57 | # duration=, 58 | # width=, 59 | # height=, 60 | thumb=thumb_image_path, 61 | supports_streaming=True, 62 | disable_notification=True, 63 | reply_to_message_id=message.id, 64 | progress=progress_for_pyrogram, 65 | progress_args=("trying to upload", status_message, c_time), 66 | ) 67 | end_t = datetime.now() 68 | ms = (end_t - start_t).seconds 69 | await status_message.edit(f"Uploaded in {ms} seconds") 70 | else: 71 | await status_message.edit("404: media not found") 72 | else: 73 | await status_message.edit( 74 | f"{COMMAND_HAND_LER}uploadasvideo FILE_PATH to upload to current Telegram chat" 75 | ) 76 | 77 | 78 | @Client.on_message(filters.command("uploadasphoto", COMMAND_HAND_LER) & sudo_filter) 79 | async def upload_as_photo(client, message): 80 | status_message = await message.reply_text("...") 81 | if " " in message.text: 82 | _, local_file_name = message.text.split(" ", 1) 83 | # thumb_image_path = await is_thumb_image_exists(local_file_name) 84 | if os.path.exists(local_file_name): 85 | start_t = datetime.now() 86 | c_time = time.time() 87 | doc_caption = os.path.basename(local_file_name) 88 | await message.reply_photo( 89 | photo=local_file_name, 90 | caption=doc_caption, 91 | # ttl_seconds=, 92 | disable_notification=True, 93 | reply_to_message_id=message.id, 94 | progress=progress_for_pyrogram, 95 | progress_args=("trying to upload", status_message, c_time), 96 | ) 97 | end_t = datetime.now() 98 | ms = (end_t - start_t).seconds 99 | await status_message.edit(f"Uploaded in {ms} seconds") 100 | else: 101 | await status_message.edit("404: media not found") 102 | else: 103 | await status_message.edit( 104 | f"{COMMAND_HAND_LER}uploadasphoto FILE_PATH to upload to current Telegram chat" 105 | ) 106 | -------------------------------------------------------------------------------- /pyrobot/plugins/warns/get_warns.py: -------------------------------------------------------------------------------- 1 | from pyrogram import filters 2 | from pyrogram.types import Message 3 | from pyrobot import COMMAND_HAND_LER 4 | from pyrobot.pyrobot import PyroBot 5 | from pyrobot.helper_functions.cust_p_filters import admin_fliter 6 | 7 | 8 | @PyroBot.on_message(filters.command(["warns"], COMMAND_HAND_LER) & admin_fliter) 9 | async def check_warns_of_user(client: PyroBot, msg: Message): 10 | replied = msg.reply_to_message 11 | if not replied: 12 | return 13 | 14 | chat_id = str(msg.chat.id) 15 | if chat_id not in client.warndatastore: 16 | client.warndatastore[chat_id] = {} 17 | DATA = client.warndatastore[chat_id] 18 | W_S = client.warnsettingsstore[chat_id] 19 | WARN_LIMIT = W_S["WARN_LIMIT"] 20 | 21 | user_id = str(replied.from_user.id) 22 | mention = f"" 23 | mention += replied.from_user.first_name 24 | mention += "" 25 | 26 | if replied.from_user.is_self: 27 | return 28 | 29 | if DATA.get(user_id): 30 | w_c = DATA[user_id]["limit"] # warn counts 31 | reason = "\n".join(DATA[user_id]["reason"]) 32 | reply_msg = ( 33 | "#WARNINGS\n" 34 | f"User: {mention}\n" 35 | f"Warn Counts: {w_c}/{WARN_LIMIT} Warnings\n" 36 | f"Reason: {reason}" 37 | ) 38 | await msg.reply(reply_msg) 39 | else: 40 | await msg.reply("Warnings not Found.") 41 | -------------------------------------------------------------------------------- /pyrobot/plugins/warns/reset_warn.py: -------------------------------------------------------------------------------- 1 | import json 2 | from pyrogram import filters 3 | from pyrogram.types import Message 4 | from pyrobot import COMMAND_HAND_LER, WARN_DATA_ID 5 | from pyrobot.pyrobot import PyroBot 6 | from pyrobot.helper_functions.cust_p_filters import admin_fliter 7 | 8 | 9 | @PyroBot.on_message(filters.command(["resetwarn"], COMMAND_HAND_LER) & admin_fliter) 10 | async def reset_all_warns(client: PyroBot, msg: Message): 11 | replied = msg.reply_to_message 12 | if not replied: 13 | return 14 | user_id = str(replied.from_user.id) 15 | if replied.from_user.is_self: 16 | return 17 | chat_id = str(msg.chat.id) 18 | if chat_id not in client.warndatastore: 19 | client.warndatastore[chat_id] = {} 20 | DATA = client.warndatastore[chat_id] 21 | if DATA.get(user_id): 22 | DATA.pop(user_id) 23 | await msg.reply("All Warns are removed for this User.") 24 | else: 25 | await msg.reply("User already not have any warn.") 26 | client.warndatastore[chat_id] = DATA 27 | await client.save_public_store(WARN_DATA_ID, json.dumps(client.warndatastore)) 28 | -------------------------------------------------------------------------------- /pyrobot/plugins/warns/set_warn_limit.py: -------------------------------------------------------------------------------- 1 | import json 2 | from pyrogram import filters 3 | from pyrogram.types import Message 4 | from pyrobot import COMMAND_HAND_LER, WARN_SETTINGS_ID 5 | from pyrobot.pyrobot import PyroBot 6 | from pyrobot.helper_functions.cust_p_filters import sudo_filter 7 | 8 | 9 | @PyroBot.on_message( 10 | filters.command(["warnlimit", "setwarn"], COMMAND_HAND_LER) & sudo_filter 11 | ) 12 | async def set_warn_mode_and_limit(client: PyroBot, msg: Message): 13 | if len(msg.command) <= 1: 14 | await msg.reply("Input not found!") 15 | return 16 | chat_id = str(msg.chat.id) 17 | WARN_MODE = "kick" 18 | WARN_LIMIT = 5 19 | _, args = msg.text.split(maxsplit=1) 20 | if "ban" in args.lower(): 21 | WARN_MODE = "ban" 22 | await msg.reply("Warning Mode Updated to Ban") 23 | elif "kick" in args.lower(): 24 | WARN_MODE = "kick" 25 | await msg.reply("Warning Mode Updated to Kick") 26 | elif "mute" in args.lower(): 27 | WARN_MODE = "mute" 28 | await msg.reply("Warning Mode Updated to Mute") 29 | elif args.isnumeric(): 30 | input_ = int(args) 31 | WARN_LIMIT = input_ 32 | await msg.reply(f"Warn limit Updated to {input_} Warns.") 33 | else: 34 | await msg.reply("Invalid arguments, Exiting...") 35 | client.warnsettingsstore[chat_id] = { 36 | "WARN_LIMIT": WARN_LIMIT, 37 | "WARN_MODE": WARN_MODE, 38 | } 39 | await client.save_public_store( 40 | WARN_SETTINGS_ID, json.dumps(client.warnsettingsstore) 41 | ) 42 | -------------------------------------------------------------------------------- /pyrobot/plugins/warns/warn_user.py: -------------------------------------------------------------------------------- 1 | import json 2 | import time 3 | from pyrogram import filters 4 | from pyrogram.types import ( 5 | Message, 6 | ChatPermissions, 7 | InlineKeyboardMarkup, 8 | InlineKeyboardButton, 9 | ) 10 | from pyrobot import COMMAND_HAND_LER, WARN_DATA_ID, WARN_SETTINGS_ID 11 | from pyrobot.pyrobot import PyroBot 12 | from pyrobot.helper_functions.admin_check import ( 13 | admin_check, # TODO: remove in next version 14 | ) 15 | from pyrobot.helper_functions.cust_p_filters import admin_fliter 16 | from datetime import datetime, timedelta 17 | 18 | 19 | @PyroBot.on_message( 20 | filters.command(["warnuser", "warn"], COMMAND_HAND_LER) & admin_fliter 21 | ) 22 | async def warn_user(client: PyroBot, msg: Message): 23 | chat_id = str(msg.chat.id) 24 | 25 | replied = msg.reply_to_message 26 | if not replied: 27 | return 28 | 29 | if chat_id not in client.warndatastore: 30 | client.warndatastore[chat_id] = {} 31 | 32 | DATA = client.warndatastore[chat_id] 33 | 34 | user_id = str(replied.from_user.id) 35 | mention = f"" 36 | mention += replied.from_user.first_name 37 | mention += "" 38 | 39 | if replied.from_user.is_self: 40 | await msg.reply_text("ഞാൻ സ്വയം താക്കീത്‌ നൽകാൻ പോകുന്നില്ല") 41 | return 42 | 43 | if await admin_check(replied): 44 | await msg.reply("User is Admin, Cannot Warn.") 45 | return 46 | 47 | if len(msg.command) < 2: 48 | await msg.reply("Give a reason to warn him.") 49 | return 50 | 51 | _, reason = msg.text.split(maxsplit=1) 52 | 53 | if chat_id not in client.warnsettingsstore: 54 | client.warnsettingsstore[chat_id] = {"WARN_LIMIT": 5, "WARN_MODE": "kick"} 55 | w_s = client.warnsettingsstore[chat_id] 56 | w_l = w_s["WARN_LIMIT"] 57 | w_m = w_s["WARN_MODE"] 58 | 59 | keyboard = InlineKeyboardMarkup( 60 | [ 61 | [ 62 | InlineKeyboardButton( 63 | "ഈ താക്കീത്‌ നീക്കംചെയ്യുക", 64 | callback_data=f"rmwarn_{user_id}_{msg.from_user.id}", 65 | ) 66 | ] 67 | ] 68 | ) 69 | 70 | if not DATA.get(user_id): 71 | w_d = {"limit": 1, "reason": [reason]} 72 | DATA[user_id] = w_d # warning data 73 | reply_text = f"#Warned\n{mention} has 1/{w_l} warnings.\n" 74 | reply_text += f"Reason: {reason}" 75 | await replied.reply_text(reply_text, reply_markup=keyboard) 76 | else: 77 | p_l = DATA[user_id]["limit"] # previous limit 78 | nw_l = p_l + 1 # new limit 79 | if nw_l >= w_l: 80 | if w_m == "ban": 81 | await msg.chat.ban_member(int(user_id)) 82 | exec_str = "BANNED" 83 | elif w_m == "kick": 84 | await msg.chat.ban_member(int(user_id), until_date=datetime.now() + timedelta(seconds=75)) 85 | exec_str = "KICKED" 86 | elif w_m == "mute": 87 | await msg.chat.restrict_member(int(user_id), ChatPermissions()) 88 | exec_str = "MUTED" 89 | reason = "\n".join(DATA[user_id]["reason"]) + "\n" + str(reason) 90 | await msg.reply( 91 | f"#WARNED_{exec_str}\n" 92 | f"{exec_str} User: {mention}\n" 93 | f"Warn Counts: {w_l}/{w_l} Warnings\n" 94 | f"Reason: {reason}" 95 | ) 96 | DATA.pop(user_id) 97 | 98 | else: 99 | DATA[user_id]["limit"] = nw_l 100 | DATA[user_id]["reason"].append(reason) 101 | r_t = f"#Warned\n{mention} has {nw_l}/{w_l} warnings.\n" 102 | r_t += f"Reason: {reason}" # r_t = reply text 103 | await replied.reply_text(r_t, reply_markup=keyboard) 104 | 105 | client.warndatastore[chat_id] = DATA 106 | await client.save_public_store(WARN_DATA_ID, json.dumps(client.warndatastore)) 107 | client.warnsettingsstore[chat_id] = w_s 108 | await client.save_public_store( 109 | WARN_SETTINGS_ID, json.dumps(client.warnsettingsstore) 110 | ) 111 | -------------------------------------------------------------------------------- /pyrobot/plugins/welcome/new_users.py: -------------------------------------------------------------------------------- 1 | from pyrogram import filters 2 | from pyrobot import DB_URI, TG_URI 3 | from pyrobot.pyrobot import PyroBot 4 | from pyrobot.helper_functions.msg_types import get_file_id 5 | from pyrobot.helper_functions.string_handling import format_welcome_caption 6 | 7 | if DB_URI is not None: 8 | import pyrobot.helper_functions.sql_helpers.welcome_sql as sql 9 | 10 | 11 | async def delete_prev_welcome(message, previous_w_message_id): 12 | await message._client.delete_messages( 13 | chat_id=message.chat.id, message_ids=previous_w_message_id, revoke=True 14 | ) 15 | 16 | 17 | async def get_note_with_command(message): 18 | note_d = sql.get_current_welcome_settings(message.chat.id) 19 | if not note_d: 20 | return 21 | # 22 | note_message_id = int(note_d.f_mesg_id) 23 | note_message = await message._client.get_messages( 24 | chat_id=TG_URI, message_ids=note_message_id, replies=0 25 | ) 26 | n_m = message 27 | # 🥺 check two conditions 🤔🤔 28 | for c_m in message.new_chat_members: 29 | if note_d.should_clean_welcome: 30 | await delete_prev_welcome(message, int(note_d.previous_welcome)) 31 | if note_message.media: 32 | _, file_id = get_file_id(note_message) 33 | caption = note_message.caption 34 | if caption: 35 | caption = format_welcome_caption(caption.html, c_m) 36 | n_m = await n_m.reply_cached_media( 37 | file_id=file_id, 38 | caption=caption, 39 | reply_markup=note_message.reply_markup, 40 | ) 41 | else: 42 | caption = note_message.text 43 | if caption: 44 | caption = format_welcome_caption(caption.html, c_m) 45 | disable_web_page_preview = True 46 | if "gra.ph" in caption or "youtu" in caption: 47 | disable_web_page_preview = False 48 | n_m = await n_m.reply_text( 49 | text=caption, 50 | disable_web_page_preview=disable_web_page_preview, 51 | reply_markup=note_message.reply_markup, 52 | ) 53 | # 54 | sql.update_previous_welcome(message.chat.id, n_m.id) 55 | 56 | 57 | @PyroBot.on_message(filters.new_chat_members) 58 | async def new_welcome(_, message): 59 | await get_note_with_command(message) 60 | -------------------------------------------------------------------------------- /pyrobot/plugins/welcome/others.py: -------------------------------------------------------------------------------- 1 | from pyrogram import Client, filters 2 | from pyrobot import ( 3 | COMMAND_HAND_LER, 4 | DB_URI, 5 | ) 6 | from pyrobot.helper_functions.cust_p_filters import admin_fliter 7 | 8 | if DB_URI is not None: 9 | import pyrobot.helper_functions.sql_helpers.welcome_sql as sql 10 | 11 | 12 | @Client.on_message( 13 | filters.command(["clearwelcome", "resetwelcome"], COMMAND_HAND_LER) & admin_fliter 14 | ) 15 | async def clear_note(_, message): 16 | status_message = await message.reply_text("checking 🤔🙄🙄", quote=True) 17 | sql.rm_welcome_setting(message.chat.id) 18 | await status_message.edit_text("welcome message cleared from current chat.") 19 | -------------------------------------------------------------------------------- /pyrobot/plugins/welcome/save.py: -------------------------------------------------------------------------------- 1 | from pyrogram import Client, filters 2 | from pyrogram.types import InlineKeyboardMarkup 3 | from pyrobot import COMMAND_HAND_LER, DB_URI, TG_URI 4 | from pyrobot.helper_functions.cust_p_filters import admin_fliter 5 | from pyrobot.helper_functions.msg_types import get_note_type, Types 6 | 7 | if DB_URI is not None: 8 | import pyrobot.helper_functions.sql_helpers.welcome_sql as sql 9 | 10 | 11 | @Client.on_message( 12 | filters.command(["savewelcome", "setwelcome"], COMMAND_HAND_LER) & admin_fliter 13 | ) 14 | async def save_note(client, message): 15 | status_message = await message.reply_text("checking 🤔🙄🙄", quote=True) 16 | if len(message.command) == 2: 17 | chat_id = message.chat.id 18 | note_message_id = int(message.command[1]) 19 | sql.add_welcome_setting(chat_id, False, 0, note_message_id) 20 | await status_message.edit_text("welcome message saved") 21 | elif message.reply_to_message and message.reply_to_message.reply_markup is not None: 22 | fwded_mesg = await message.reply_to_message.forward( 23 | chat_id=TG_URI, disable_notification=True 24 | ) 25 | chat_id = message.chat.id 26 | note_message_id = fwded_mesg.id 27 | sql.add_welcome_setting(chat_id, True, 0, note_message_id) 28 | await status_message.edit_text("welcome message saved") 29 | else: 30 | note_name, text, data_type, content, buttons = get_note_type(message, 1) 31 | 32 | if data_type is None: 33 | await status_message.edit_text("🤔 maybe welcome text is empty") 34 | return 35 | 36 | # construct message using the above parameters 37 | fwded_mesg = None 38 | reply_markup = None 39 | if len(buttons) > 0: 40 | reply_markup = InlineKeyboardMarkup(buttons) 41 | if data_type in (Types.BUTTON_TEXT, Types.TEXT): 42 | fwded_mesg = await client.send_message( 43 | chat_id=TG_URI, 44 | text=text, 45 | disable_web_page_preview=True, 46 | disable_notification=True, 47 | reply_to_message_id=1, 48 | reply_markup=reply_markup, 49 | ) 50 | elif data_type is not None: 51 | fwded_mesg = await client.send_cached_media( 52 | chat_id=TG_URI, 53 | file_id=content, 54 | caption=text, 55 | disable_notification=True, 56 | reply_to_message_id=1, 57 | reply_markup=reply_markup, 58 | ) 59 | 60 | # save to db 🤔 61 | if fwded_mesg is not None: 62 | chat_id = message.chat.id 63 | note_message_id = fwded_mesg.id 64 | sql.add_welcome_setting(chat_id, bool(note_name), 0, note_message_id) 65 | await status_message.edit_text("welcome message saved") 66 | -------------------------------------------------------------------------------- /pyrobot/pyrobot.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # (c) Shrimadhav U K 4 | 5 | import json 6 | from collections import defaultdict 7 | from typing import Dict, List, Union 8 | from pyrogram import Client, __version__ 9 | from pyrogram.errors import MessageNotModified 10 | from pyrogram.raw.all import layer 11 | from pyrogram.enums import ParseMode 12 | from pyrobot import ( 13 | APP_ID, 14 | API_HASH, 15 | LAYER_UPDATE_INTERVAL, 16 | LOGGER, 17 | # OWNER_ID, 18 | # SUDO_USERS, 19 | TG_COMPANION_BOT, 20 | TMP_DOWNLOAD_DIRECTORY, 21 | TG_URI, 22 | TG_IRU_S_M_ID, 23 | WARN_DATA_ID, 24 | WARN_SETTINGS_ID, 25 | ) 26 | from pyrobot.helper_functions.scheme import check_feed 27 | try: 28 | from pyrobot.a import A 29 | except ImportError: 30 | pass 31 | 32 | 33 | class PyroBot(Client): 34 | filterstore: Dict[str, Dict[str, str]] = defaultdict(dict) 35 | warndatastore: Dict[str, Dict[str, Union[str, int, List[str]]]] = defaultdict(dict) 36 | warnsettingsstore: Dict[str, str] = defaultdict(dict) 37 | 38 | def __init__(self): 39 | name = self.__class__.__name__.lower() 40 | super().__init__( 41 | name="PyroGramBot", 42 | plugins=dict(root=f"{name}/plugins"), 43 | workdir=TMP_DOWNLOAD_DIRECTORY, 44 | api_id=APP_ID, 45 | api_hash=API_HASH, 46 | bot_token=TG_COMPANION_BOT, 47 | parse_mode=ParseMode.HTML, 48 | sleep_threshold=60, 49 | # in_memory=True, 50 | skip_updates=False, 51 | ) 52 | 53 | async def start(self): 54 | await super().start() 55 | usr_bot_me = self.me 56 | self.filterstore = await self.load_public_store(TG_IRU_S_M_ID) 57 | self.warndatastore = await self.load_public_store(WARN_DATA_ID) 58 | self.warnsettingsstore = await self.load_public_store(WARN_SETTINGS_ID) 59 | if LAYER_UPDATE_INTERVAL: 60 | await check_feed(self) 61 | LOGGER.info( 62 | f"PyroGramBot based on Pyrogram v{__version__} " 63 | f"(Layer {layer}) started on @{usr_bot_me.username}. " 64 | "Hi." 65 | ) 66 | try: 67 | self.B, self.C = await A(self).start() 68 | except NameError: 69 | pass 70 | 71 | 72 | async def stop(self, *args): 73 | await self.save_public_store(TG_IRU_S_M_ID, json.dumps(self.filterstore)) 74 | await self.save_public_store(WARN_DATA_ID, json.dumps(self.warndatastore)) 75 | await self.save_public_store( 76 | WARN_SETTINGS_ID, json.dumps(self.warnsettingsstore) 77 | ) 78 | await super().stop() 79 | LOGGER.info("PyroGramBot stopped. Bye.") 80 | 81 | async def load_public_store(self, message_id: int) -> Dict: 82 | if message_id != 0: 83 | _check_message = await self.get_messages( 84 | chat_id=TG_URI, message_ids=message_id, replies=0 85 | ) 86 | if _check_message: 87 | return json.loads(_check_message.text) 88 | return {} 89 | 90 | async def save_public_store(self, message_id: int, text: str): 91 | if message_id != 0: 92 | try: 93 | await self.edit_message_text( 94 | chat_id=TG_URI, 95 | message_id=message_id, 96 | text=f"{text}", 97 | disable_web_page_preview=True, 98 | ) 99 | except MessageNotModified: 100 | pass 101 | -------------------------------------------------------------------------------- /pyrobot/sample_config.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # GetSongsBot 4 | # Copyright (C) 2021 The Authors 5 | 6 | # This program is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU Affero General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU Affero General Public License for more details. 15 | 16 | # You should have received a copy of the GNU Affero General Public License 17 | # along with this program. If not, see . 18 | 19 | import os 20 | from dotenv import load_dotenv 21 | 22 | 23 | # apparently, no error appears even if the path does not exists 24 | load_dotenv("config.env") 25 | 26 | 27 | class Config: 28 | LOGGER = True 29 | # The Telegram API things 30 | APP_ID = int(os.environ.get("APP_ID", 12345)) 31 | API_HASH = os.environ.get("API_HASH", None) 32 | # Get these values from my.telegram.org 33 | TG_COMPANION_BOT = os.environ.get("TG_BOT_TOKEN_BF_HER", None) 34 | # maximum message length in Telegram 35 | MAX_MESSAGE_LENGTH = 4096 36 | # specify command handler that should be used for the plugins 37 | # this should be a valid "regex" pattern 38 | COMMAND_HAND_LER = os.environ.get("COMMAND_HAND_LER", "/") 39 | # This is required for the plugins involving the file system. 40 | TMP_DOWNLOAD_DIRECTORY = os.environ.get("TMP_DOWNLOAD_DIRECTORY", "./DLS/") 41 | # For Databases 42 | # can be None in which case plugins requiring 43 | # DataBase would not work 44 | DB_URI = os.environ.get("DATABASE_URL", None) 45 | # @NoOneCares 46 | TG_URI = int(os.environ.get("TELEGRAM_URL", "-100")) 47 | # gDrive variables 48 | G_DRIVE_CLIENT_ID = os.environ.get("G_DRIVE_CLIENT_ID", None) 49 | G_DRIVE_CLIENT_SECRET = os.environ.get("G_DRIVE_CLIENT_SECRET", None) 50 | # SuDo User 51 | OWNER_ID = int(os.environ.get("OWNER_ID", "7351948")) 52 | # Array to store users who are authorized to use the bot 53 | SUDO_USERS = set(int(x) for x in os.environ.get("SUDO_USERS", "").split()) 54 | # the maximum number of 'selectable' messages in Telegram 55 | TG_MAX_SELECT_LEN = 100 56 | # for bakanup purposes 57 | TG_IRU_S_M_ID = int(os.environ.get("TG_IRU_S_M_ID", "0")) 58 | WARN_DATA_ID = int(os.environ.get("WARN_DATA_ID", "0")) 59 | WARN_SETTINGS_ID = int(os.environ.get("WARN_SETTINGS_ID", "0")) 60 | # message_id for the Pinned Message 61 | A_PIN_MESSAGE_ID = int(os.environ.get("A_PIN_MESSAGE_ID", "3")) 62 | # 63 | LAYER_FEED_CHAT = os.environ.get("LAYER_FEED_CHAT", None) 64 | LAYER_UPDATE_INTERVAL = os.environ.get("LAYER_UPDATE_INTERVAL", None) 65 | LAYER_UPDATE_MESSAGE_CAPTION = os.environ.get("LAYER_UPDATE_MESSAGE_CAPTION", None) 66 | # 67 | TE_LEGRA_PH_DOMAIN = os.environ.get( 68 | "TE_LEGRA_PH_DOMAIN", 69 | "https://te.legra.ph" 70 | ) 71 | # get this by sending /getapi to useTGBot.t.me 72 | USE_TG_BOT_APP_ID = os.environ.get( 73 | "USE_TG_BOT_APP_ID" 74 | ) 75 | 76 | 77 | class Production(Config): 78 | LOGGER = False 79 | 80 | 81 | class Development(Config): 82 | LOGGER = True 83 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | # Pyrogram 2 | pyrotgfork==2.1.33.8 3 | PyTgCrypto==1.2.7 4 | 5 | # GET / POST network requests 6 | asyncio==3.4.3 7 | aiohttp==3.9.4 8 | 9 | # scrapping tools [not used anywhere] 10 | beautifulsoup4==4.9.1 11 | 12 | # get metadata from files 13 | hachoir==3.1.1 14 | 15 | # process(, and resize) images 16 | Pillow==10.3.0 17 | 18 | # SQL DataBase ReQuirements 19 | psycopg2-binary==2.8.6 20 | sqlalchemy==1.3.23 21 | 22 | # download remote files 23 | pySmartDL==1.3.4 24 | 25 | # Google Drive ReQuirements 26 | google-api-python-client==2.36.0 27 | google-auth-httplib2==0.1.0 28 | google-auth-oauthlib==0.4.6 29 | oauth2client==4.1.3 30 | 31 | # for reading .env files 32 | python-dotenv==0.10 33 | 34 | # for interacting with te.legra.ph 35 | telegraph==2.2.0 36 | -------------------------------------------------------------------------------- /runtime.txt: -------------------------------------------------------------------------------- 1 | python-3.9.19 --------------------------------------------------------------------------------