├── .github └── workflows │ └── codeql-analysis.yml ├── Dockerfile ├── Procfile ├── README.md ├── app.json ├── bot.py ├── configs.py ├── handlers ├── add_user_to_db.py ├── broadcast_handlers.py ├── check_user_status.py ├── database.py ├── force_sub_handler.py ├── helpers.py ├── save_media.py └── send_file.py ├── heroku.yml └── requirements.txt /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ main ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ main ] 20 | schedule: 21 | - cron: '28 2 * * 2' 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | runs-on: ubuntu-latest 27 | permissions: 28 | actions: read 29 | contents: read 30 | security-events: write 31 | 32 | strategy: 33 | fail-fast: false 34 | matrix: 35 | language: [ 'python' ] 36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] 37 | # Learn more: 38 | # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed 39 | 40 | steps: 41 | - name: Checkout repository 42 | uses: actions/checkout@v2 43 | 44 | # Initializes the CodeQL tools for scanning. 45 | - name: Initialize CodeQL 46 | uses: github/codeql-action/init@v1 47 | with: 48 | languages: ${{ matrix.language }} 49 | # If you wish to specify custom queries, you can do so here or in a config file. 50 | # By default, queries listed here will override any specified in a config file. 51 | # Prefix the list here with "+" to use these queries and those in the config file. 52 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 53 | 54 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 55 | # If this step fails, then you should remove it and run the build manually (see below) 56 | - name: Autobuild 57 | uses: github/codeql-action/autobuild@v1 58 | 59 | # ℹ️ Command-line programs to run using the OS shell. 60 | # 📚 https://git.io/JvXDl 61 | 62 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 63 | # and modify them (or add more) to build your code if your project 64 | # uses a compiled language 65 | 66 | #- run: | 67 | # make bootstrap 68 | # make release 69 | 70 | - name: Perform CodeQL Analysis 71 | uses: github/codeql-action/analyze@v1 72 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:18.04 2 | 3 | ENV DEBIAN_FRONTEND=noninteractive 4 | 5 | WORKDIR /app 6 | 7 | RUN apt-get update 8 | RUN echo y | apt-get install locales 9 | RUN echo y | apt install build-essential 10 | RUN apt -qq install -y --no-install-recommends \ 11 | curl \ 12 | git \ 13 | gnupg2 \ 14 | wget 15 | 16 | RUN set -ex; \ 17 | apt-get update \ 18 | && apt-get install -y --no-install-recommends \ 19 | busybox \ 20 | git \ 21 | python3 \ 22 | python3-dev \ 23 | python3-pip \ 24 | python3-lxml \ 25 | pv \ 26 | && apt-get autoclean \ 27 | && apt-get autoremove \ 28 | && rm -rf /var/lib/apt/lists/* 29 | 30 | RUN pip3 install setuptools wheel yarl multidict 31 | COPY requirements.txt . 32 | RUN pip3 install -r requirements.txt 33 | RUN dpkg-reconfigure locales 34 | COPY . /app 35 | 36 | CMD ["python3", "bot.py"] 37 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | worker: python3 bot.py -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PyroFilesStoreBot 2 | This is Telegram Parmanent Files Store Bot by [@AbirHasan2005](https://github.com/AbirHasan2005). 3 | 4 | * **Language:** [Python3](https://www.python.org) 5 | * **Library:** [Pyrogram](https://docs.pyrogram.org) 6 | 7 | ### Features: 8 | - In PM Just Forward or Send any file it will save on Database & give you the Access Link. 9 | - In Channel Add Bot as Admin with Edit Rights. When you will send any file or media in Channel it will Edit the Broadcast Message with Saved Link Button. 10 | - You can also Broadcast anythings via this Bot. 11 | - You can also Do Force Sub to a Channel to allow access the Bot. 12 | - Save Multiple Files in Same Link. (Batch) 13 | 14 | ### Demo Bot: 15 | 16 | 17 | ## Configs: 18 | - `API_ID` - Get this from [@TeleORG_Bot](https://t.me/TeleORG_Bot) 19 | - `API_HASH` - Get this from [@TeleORG_Bot](https://t.me/TeleORG_Bot) 20 | - `BOT_TOKEN` - Get this from [@BotFather](https://t.me/BotFather) 21 | - `BOT_USERNAME` - You Bot Username. *(Without [@])* 22 | - `DB_CHANNEL` - A Telegram Channel ID. 23 | - Make a Channel for Storing Files. We will use that as Database. Channel must be Private! Else you will be Copyright by [Telegram DMCA](https://t.me/dmcatelegram)! 24 | - `BOT_OWNER` - Bot Owner UserID 25 | - Put your Telegram UserID for doing Broadcast. 26 | - `DATABASE_URL` - MongoDB Database URI 27 | - This for Saving UserIDs. When you will Broadcast, bot will forward the Broadcast to DB Users. 28 | - `UPDATES_CHANNEL` - Force Sub Channel ID *(Optional)* 29 | - ID of a Channel which you want to do Force Sub to use the bot. 30 | - `LOG_CHANNEL` - Logs Channel ID 31 | - This for some getting user info. If any new User added to DB, Bot will send Log to that Logs Channel. You can use same DB Channel ID. 32 | - `FORWARD_AS_COPY` - Value should be `True` or `False` *(Optional)* 33 | - If `True` all messages will be forwarder *As Copy*. If `False` all messages will be forwarder with Forward Tag. 34 | - `BROADCAST_AS_COPY` - Value should be `True` or `False` *(Optional)* 35 | - Broadcast with Forward Tag or as Copy.*(Without Forward Tag)* 36 | - `BANNED_USERS` - Banned unwanted members 37 | - Put all banned user IDs & Separate with space. 38 | - `BANNED_CHAT_IDS` - All Banned Channel IDs *(Optional)* 39 | - Put all banned channel IDs & Separate with space. 40 | 41 | ### Video Tutorial: 42 | [![YouTube](https://img.shields.io/badge/YouTube-Video%20Tutorial-red?logo=youtube)](https://youtu.be/hHu2RaePhwI) 43 | 44 | ### Deploy Now: 45 | [![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy?template=https://github.com/AbirHasan2005/PyroFilesStoreBot) 46 | 47 | ## Commands: 48 | ``` 49 | start - start the bot 50 | clear_batch - Clear User Batch Files 51 | status - Show number of users in DB [Owner Only] 52 | broadcast - Broadcast replied message to DB Users [Owner Only] 53 | ban_user - [user_id] [ban_duration] [ban_reason] Ban Any User [Owner Only] 54 | unban_user - [user_id] Unban Any User [Owner Only] 55 | banned_users - Get All Banned Users [Owner Only] 56 | ``` 57 | 58 | ### Support Group: 59 | 60 | 61 | ### Follow on: 62 |

63 | 64 |

65 |

66 | 67 |

68 |

69 | 70 |

71 |

72 | 73 |

74 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Telegram Files Store Bot", 3 | "description": "A Telegram Files Store Bot in Pyrogram by @AbirHasan2005", 4 | "keywords": [ 5 | "telegram", 6 | "files", 7 | "store", 8 | "bot" 9 | ], 10 | "repository": "https://github.com/AbirHasan2005/PyroFilesStoreBot", 11 | "website": "https://telegram.dog/AbirHasan2005", 12 | "success_url": "https://t.me/Discovery_Updates", 13 | "env": { 14 | "API_ID": { 15 | "description": "Get this value from my.telegram.org or @TeleORG_Bot" 16 | }, 17 | "API_HASH": { 18 | "description": "Get this value from my.telegram.org or @TeleORG_Bot" 19 | }, 20 | "BOT_TOKEN": { 21 | "description": "Get this from @BotFather XD" 22 | }, 23 | "BOT_USERNAME": { 24 | "description": "Your Bot Username which you sent to @BotFather (Without [@])" 25 | }, 26 | "DB_CHANNEL": { 27 | "description": "The Channel ID which will be used as Database. Example: -100123456789" 28 | }, 29 | "BOT_OWNER": { 30 | "description": "Bot Owner UserID" 31 | }, 32 | "DATABASE_URL": { 33 | "description": "MongoDB Database URI for Saving UserID for Broadcast." 34 | }, 35 | "UPDATES_CHANNEL": { 36 | "description": "ID of a Channel which you want to do Force Sub to use the bot. Example: -100123456789", 37 | "required": false 38 | }, 39 | "LOG_CHANNEL": { 40 | "description": "Logs Channel ID for some Tracking XD. Example: -100123456789" 41 | }, 42 | "BANNED_USERS": { 43 | "description": "Banned unwanted members", 44 | "required": false 45 | }, 46 | "BANNED_CHAT_IDS": { 47 | "description": "Banned unwanted channel IDs", 48 | "required": false 49 | }, 50 | "BROADCAST_AS_COPY": { 51 | "description": "Broadcast with Forward Tag or as Copy(Without Forward Tag). Value should be True/False !!", 52 | "required": false 53 | }, 54 | "FORWARD_AS_COPY": { 55 | "description": "If True all messages will be forwarder As Copy. If False all messages will be forwarder with Forward Tag. Value should be True/False !!", 56 | "required": false 57 | } 58 | }, 59 | "buildpacks": [ 60 | { 61 | "url": "heroku/python" 62 | } 63 | ], 64 | "formation": { 65 | "worker": { 66 | "quantity": 1, 67 | "size": "free" 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /bot.py: -------------------------------------------------------------------------------- 1 | # (c) @AbirHasan2005 2 | 3 | import os 4 | import asyncio 5 | import traceback 6 | from binascii import ( 7 | Error 8 | ) 9 | from pyrogram import ( 10 | Client, 11 | enums, 12 | filters 13 | ) 14 | from pyrogram.errors import ( 15 | UserNotParticipant, 16 | FloodWait, 17 | QueryIdInvalid 18 | ) 19 | from pyrogram.types import ( 20 | InlineKeyboardMarkup, 21 | InlineKeyboardButton, 22 | CallbackQuery, 23 | Message 24 | ) 25 | from configs import Config 26 | from handlers.database import db 27 | from handlers.add_user_to_db import add_user_to_database 28 | from handlers.send_file import send_media_and_reply 29 | from handlers.helpers import b64_to_str, str_to_b64 30 | from handlers.check_user_status import handle_user_status 31 | from handlers.force_sub_handler import ( 32 | handle_force_sub, 33 | get_invite_link 34 | ) 35 | from handlers.broadcast_handlers import main_broadcast_handler 36 | from handlers.save_media import ( 37 | save_media_in_channel, 38 | save_batch_media_in_channel 39 | ) 40 | 41 | MediaList = {} 42 | 43 | Bot = Client( 44 | name=Config.BOT_USERNAME, 45 | in_memory=True, 46 | bot_token=Config.BOT_TOKEN, 47 | api_id=Config.API_ID, 48 | api_hash=Config.API_HASH 49 | ) 50 | 51 | 52 | @Bot.on_message(filters.private) 53 | async def _(bot: Client, cmd: Message): 54 | await handle_user_status(bot, cmd) 55 | 56 | 57 | @Bot.on_message(filters.command("start") & filters.private) 58 | async def start(bot: Client, cmd: Message): 59 | 60 | if cmd.from_user.id in Config.BANNED_USERS: 61 | await cmd.reply_text("Sorry, You are banned.") 62 | return 63 | if Config.UPDATES_CHANNEL is not None: 64 | back = await handle_force_sub(bot, cmd) 65 | if back == 400: 66 | return 67 | 68 | usr_cmd = cmd.text.split("_", 1)[-1] 69 | if usr_cmd == "/start": 70 | await add_user_to_database(bot, cmd) 71 | await cmd.reply_text( 72 | Config.HOME_TEXT.format(cmd.from_user.first_name, cmd.from_user.id), 73 | disable_web_page_preview=True, 74 | reply_markup=InlineKeyboardMarkup( 75 | [ 76 | [ 77 | InlineKeyboardButton("Support Group", url="https://t.me/JoinOT"), 78 | InlineKeyboardButton("Bots Channel", url="https://t.me/Discovery_Updates") 79 | ], 80 | [ 81 | InlineKeyboardButton("About Bot", callback_data="aboutbot"), 82 | InlineKeyboardButton("About Dev", callback_data="aboutdevs") 83 | ] 84 | ] 85 | ) 86 | ) 87 | else: 88 | try: 89 | try: 90 | file_id = int(b64_to_str(usr_cmd).split("_")[-1]) 91 | except (Error, UnicodeDecodeError): 92 | file_id = int(usr_cmd.split("_")[-1]) 93 | GetMessage = await bot.get_messages(chat_id=Config.DB_CHANNEL, message_ids=file_id) 94 | message_ids = [] 95 | if GetMessage.text: 96 | message_ids = GetMessage.text.split(" ") 97 | _response_msg = await cmd.reply_text( 98 | text=f"**Total Files:** `{len(message_ids)}`", 99 | quote=True, 100 | disable_web_page_preview=True 101 | ) 102 | else: 103 | message_ids.append(int(GetMessage.id)) 104 | for i in range(len(message_ids)): 105 | await send_media_and_reply(bot, user_id=cmd.from_user.id, file_id=int(message_ids[i])) 106 | except Exception as err: 107 | await cmd.reply_text(f"Something went wrong!\n\n**Error:** `{err}`") 108 | 109 | 110 | @Bot.on_message((filters.document | filters.video | filters.audio) & ~filters.chat(Config.DB_CHANNEL)) 111 | async def main(bot: Client, message: Message): 112 | 113 | if message.chat.type == enums.ChatType.PRIVATE: 114 | 115 | await add_user_to_database(bot, message) 116 | 117 | if Config.UPDATES_CHANNEL is not None: 118 | back = await handle_force_sub(bot, message) 119 | if back == 400: 120 | return 121 | 122 | if message.from_user.id in Config.BANNED_USERS: 123 | await message.reply_text("Sorry, You are banned!\n\nContact [Support Group](https://t.me/JoinOT)", 124 | disable_web_page_preview=True) 125 | return 126 | 127 | if Config.OTHER_USERS_CAN_SAVE_FILE is False: 128 | return 129 | 130 | await message.reply_text( 131 | text="**Choose an option from below:**", 132 | reply_markup=InlineKeyboardMarkup([ 133 | [InlineKeyboardButton("Save in Batch", callback_data="addToBatchTrue")], 134 | [InlineKeyboardButton("Get Sharable Link", callback_data="addToBatchFalse")] 135 | ]), 136 | quote=True, 137 | disable_web_page_preview=True 138 | ) 139 | elif message.chat.type == enums.ChatType.CHANNEL: 140 | if (message.chat.id == int(Config.LOG_CHANNEL)) or (message.chat.id == int(Config.UPDATES_CHANNEL)) or message.forward_from_chat or message.forward_from: 141 | return 142 | elif int(message.chat.id) in Config.BANNED_CHAT_IDS: 143 | await bot.leave_chat(message.chat.id) 144 | return 145 | else: 146 | pass 147 | 148 | try: 149 | forwarded_msg = await message.forward(Config.DB_CHANNEL) 150 | file_er_id = str(forwarded_msg.id) 151 | share_link = f"https://t.me/{Config.BOT_USERNAME}?start=AbirHasan2005_{str_to_b64(file_er_id)}" 152 | CH_edit = await bot.edit_message_reply_markup(message.chat.id, message.id, 153 | reply_markup=InlineKeyboardMarkup([[InlineKeyboardButton( 154 | "Get Sharable Link", url=share_link)]])) 155 | if message.chat.username: 156 | await forwarded_msg.reply_text( 157 | f"#CHANNEL_BUTTON:\n\n[{message.chat.title}](https://t.me/{message.chat.username}/{CH_edit.id}) Channel's Broadcasted File's Button Added!") 158 | else: 159 | private_ch = str(message.chat.id)[4:] 160 | await forwarded_msg.reply_text( 161 | f"#CHANNEL_BUTTON:\n\n[{message.chat.title}](https://t.me/c/{private_ch}/{CH_edit.id}) Channel's Broadcasted File's Button Added!") 162 | except FloodWait as sl: 163 | await asyncio.sleep(sl.value) 164 | await bot.send_message( 165 | chat_id=int(Config.LOG_CHANNEL), 166 | text=f"#FloodWait:\nGot FloodWait of `{str(sl.value)}s` from `{str(message.chat.id)}` !!", 167 | disable_web_page_preview=True 168 | ) 169 | except Exception as err: 170 | await bot.leave_chat(message.chat.id) 171 | await bot.send_message( 172 | chat_id=int(Config.LOG_CHANNEL), 173 | text=f"#ERROR_TRACEBACK:\nGot Error from `{str(message.chat.id)}` !!\n\n**Traceback:** `{err}`", 174 | disable_web_page_preview=True 175 | ) 176 | 177 | 178 | @Bot.on_message(filters.private & filters.command("broadcast") & filters.user(Config.BOT_OWNER) & filters.reply) 179 | async def broadcast_handler_open(_, m: Message): 180 | await main_broadcast_handler(m, db) 181 | 182 | 183 | @Bot.on_message(filters.private & filters.command("status") & filters.user(Config.BOT_OWNER)) 184 | async def sts(_, m: Message): 185 | total_users = await db.total_users_count() 186 | await m.reply_text( 187 | text=f"**Total Users in DB:** `{total_users}`", 188 | quote=True 189 | ) 190 | 191 | 192 | @Bot.on_message(filters.private & filters.command("ban_user") & filters.user(Config.BOT_OWNER)) 193 | async def ban(c: Client, m: Message): 194 | 195 | if len(m.command) == 1: 196 | await m.reply_text( 197 | f"Use this command to ban any user from the bot.\n\n" 198 | f"Usage:\n\n" 199 | f"`/ban_user user_id ban_duration ban_reason`\n\n" 200 | f"Eg: `/ban_user 1234567 28 You misused me.`\n" 201 | f"This will ban user with id `1234567` for `28` days for the reason `You misused me`.", 202 | quote=True 203 | ) 204 | return 205 | 206 | try: 207 | user_id = int(m.command[1]) 208 | ban_duration = int(m.command[2]) 209 | ban_reason = ' '.join(m.command[3:]) 210 | ban_log_text = f"Banning user {user_id} for {ban_duration} days for the reason {ban_reason}." 211 | try: 212 | await c.send_message( 213 | user_id, 214 | f"You are banned to use this bot for **{ban_duration}** day(s) for the reason __{ban_reason}__ \n\n" 215 | f"**Message from the admin**" 216 | ) 217 | ban_log_text += '\n\nUser notified successfully!' 218 | except: 219 | traceback.print_exc() 220 | ban_log_text += f"\n\nUser notification failed! \n\n`{traceback.format_exc()}`" 221 | 222 | await db.ban_user(user_id, ban_duration, ban_reason) 223 | print(ban_log_text) 224 | await m.reply_text( 225 | ban_log_text, 226 | quote=True 227 | ) 228 | except: 229 | traceback.print_exc() 230 | await m.reply_text( 231 | f"Error occoured! Traceback given below\n\n`{traceback.format_exc()}`", 232 | quote=True 233 | ) 234 | 235 | 236 | @Bot.on_message(filters.private & filters.command("unban_user") & filters.user(Config.BOT_OWNER)) 237 | async def unban(c: Client, m: Message): 238 | 239 | if len(m.command) == 1: 240 | await m.reply_text( 241 | f"Use this command to unban any user.\n\n" 242 | f"Usage:\n\n`/unban_user user_id`\n\n" 243 | f"Eg: `/unban_user 1234567`\n" 244 | f"This will unban user with id `1234567`.", 245 | quote=True 246 | ) 247 | return 248 | 249 | try: 250 | user_id = int(m.command[1]) 251 | unban_log_text = f"Unbanning user {user_id}" 252 | try: 253 | await c.send_message( 254 | user_id, 255 | f"Your ban was lifted!" 256 | ) 257 | unban_log_text += '\n\nUser notified successfully!' 258 | except: 259 | traceback.print_exc() 260 | unban_log_text += f"\n\nUser notification failed! \n\n`{traceback.format_exc()}`" 261 | await db.remove_ban(user_id) 262 | print(unban_log_text) 263 | await m.reply_text( 264 | unban_log_text, 265 | quote=True 266 | ) 267 | except: 268 | traceback.print_exc() 269 | await m.reply_text( 270 | f"Error occurred! Traceback given below\n\n`{traceback.format_exc()}`", 271 | quote=True 272 | ) 273 | 274 | 275 | @Bot.on_message(filters.private & filters.command("banned_users") & filters.user(Config.BOT_OWNER)) 276 | async def _banned_users(_, m: Message): 277 | 278 | all_banned_users = await db.get_all_banned_users() 279 | banned_usr_count = 0 280 | text = '' 281 | 282 | async for banned_user in all_banned_users: 283 | user_id = banned_user['id'] 284 | ban_duration = banned_user['ban_status']['ban_duration'] 285 | banned_on = banned_user['ban_status']['banned_on'] 286 | ban_reason = banned_user['ban_status']['ban_reason'] 287 | banned_usr_count += 1 288 | text += f"> **user_id**: `{user_id}`, **Ban Duration**: `{ban_duration}`, " \ 289 | f"**Banned on**: `{banned_on}`, **Reason**: `{ban_reason}`\n\n" 290 | reply_text = f"Total banned user(s): `{banned_usr_count}`\n\n{text}" 291 | if len(reply_text) > 4096: 292 | with open('banned-users.txt', 'w') as f: 293 | f.write(reply_text) 294 | await m.reply_document('banned-users.txt', True) 295 | os.remove('banned-users.txt') 296 | return 297 | await m.reply_text(reply_text, True) 298 | 299 | 300 | @Bot.on_message(filters.private & filters.command("clear_batch")) 301 | async def clear_user_batch(bot: Client, m: Message): 302 | MediaList[f"{str(m.from_user.id)}"] = [] 303 | await m.reply_text("Cleared your batch files successfully!") 304 | 305 | 306 | @Bot.on_callback_query() 307 | async def button(bot: Client, cmd: CallbackQuery): 308 | 309 | cb_data = cmd.data 310 | if "aboutbot" in cb_data: 311 | await cmd.message.edit( 312 | Config.ABOUT_BOT_TEXT, 313 | disable_web_page_preview=True, 314 | reply_markup=InlineKeyboardMarkup( 315 | [ 316 | [ 317 | InlineKeyboardButton("Source Codes of Bot", 318 | url="https://github.com/AbirHasan2005/PyroFilesStoreBot") 319 | ], 320 | [ 321 | InlineKeyboardButton("Go Home", callback_data="gotohome"), 322 | InlineKeyboardButton("About Dev", callback_data="aboutdevs") 323 | ] 324 | ] 325 | ) 326 | ) 327 | 328 | elif "aboutdevs" in cb_data: 329 | await cmd.message.edit( 330 | Config.ABOUT_DEV_TEXT, 331 | disable_web_page_preview=True, 332 | reply_markup=InlineKeyboardMarkup( 333 | [ 334 | [ 335 | InlineKeyboardButton("Source Codes of Bot", 336 | url="https://github.com/AbirHasan2005/PyroFilesStoreBot") 337 | ], 338 | [ 339 | InlineKeyboardButton("About Bot", callback_data="aboutbot"), 340 | InlineKeyboardButton("Go Home", callback_data="gotohome") 341 | ] 342 | ] 343 | ) 344 | ) 345 | 346 | elif "gotohome" in cb_data: 347 | await cmd.message.edit( 348 | Config.HOME_TEXT.format(cmd.message.chat.first_name, cmd.message.chat.id), 349 | disable_web_page_preview=True, 350 | reply_markup=InlineKeyboardMarkup( 351 | [ 352 | [ 353 | InlineKeyboardButton("Support Group", url="https://t.me/JoinOT"), 354 | InlineKeyboardButton("Bots Channel", url="https://t.me/Discovery_Updates") 355 | ], 356 | [ 357 | InlineKeyboardButton("About Bot", callback_data="aboutbot"), 358 | InlineKeyboardButton("About Dev", callback_data="aboutdevs") 359 | ] 360 | ] 361 | ) 362 | ) 363 | 364 | elif "refreshForceSub" in cb_data: 365 | if Config.UPDATES_CHANNEL: 366 | if Config.UPDATES_CHANNEL.startswith("-100"): 367 | channel_chat_id = int(Config.UPDATES_CHANNEL) 368 | else: 369 | channel_chat_id = Config.UPDATES_CHANNEL 370 | try: 371 | user = await bot.get_chat_member(channel_chat_id, cmd.message.chat.id) 372 | if user.status == "kicked": 373 | await cmd.message.edit( 374 | text="Sorry Sir, You are Banned to use me. Contact my [Support Group](https://t.me/JoinOT).", 375 | disable_web_page_preview=True 376 | ) 377 | return 378 | except UserNotParticipant: 379 | invite_link = await get_invite_link(channel_chat_id) 380 | await cmd.message.edit( 381 | text="**You Still Didn't Join ☹️, Please Join My Updates Channel to use this Bot!**\n\n" 382 | "Due to Overload, Only Channel Subscribers can use the Bot!", 383 | reply_markup=InlineKeyboardMarkup( 384 | [ 385 | [ 386 | InlineKeyboardButton("🤖 Join Updates Channel", url=invite_link.invite_link) 387 | ], 388 | [ 389 | InlineKeyboardButton("🔄 Refresh 🔄", callback_data="refreshmeh") 390 | ] 391 | ] 392 | ) 393 | ) 394 | return 395 | except Exception: 396 | await cmd.message.edit( 397 | text="Something went Wrong. Contact my [Support Group](https://t.me/JoinOT).", 398 | disable_web_page_preview=True 399 | ) 400 | return 401 | await cmd.message.edit( 402 | text=Config.HOME_TEXT.format(cmd.message.chat.first_name, cmd.message.chat.id), 403 | disable_web_page_preview=True, 404 | reply_markup=InlineKeyboardMarkup( 405 | [ 406 | [ 407 | InlineKeyboardButton("Support Group", url="https://t.me/JoinOT"), 408 | InlineKeyboardButton("Bots Channel", url="https://t.me/Discovery_Updates") 409 | ], 410 | [ 411 | InlineKeyboardButton("About Bot", callback_data="aboutbot"), 412 | InlineKeyboardButton("About Dev", callback_data="aboutdevs") 413 | ] 414 | ] 415 | ) 416 | ) 417 | 418 | elif cb_data.startswith("ban_user_"): 419 | user_id = cb_data.split("_", 2)[-1] 420 | if Config.UPDATES_CHANNEL is None: 421 | await cmd.answer("Sorry Sir, You didn't Set any Updates Channel!", show_alert=True) 422 | return 423 | if not int(cmd.from_user.id) == Config.BOT_OWNER: 424 | await cmd.answer("You are not allowed to do that!", show_alert=True) 425 | return 426 | try: 427 | await bot.kick_chat_member(chat_id=int(Config.UPDATES_CHANNEL), user_id=int(user_id)) 428 | await cmd.answer("User Banned from Updates Channel!", show_alert=True) 429 | except Exception as e: 430 | await cmd.answer(f"Can't Ban Him!\n\nError: {e}", show_alert=True) 431 | 432 | elif "addToBatchTrue" in cb_data: 433 | if MediaList.get(f"{str(cmd.from_user.id)}", None) is None: 434 | MediaList[f"{str(cmd.from_user.id)}"] = [] 435 | file_id = cmd.message.reply_to_message.id 436 | MediaList[f"{str(cmd.from_user.id)}"].append(file_id) 437 | await cmd.message.edit("File Saved in Batch!\n\n" 438 | "Press below button to get batch link.", 439 | reply_markup=InlineKeyboardMarkup([ 440 | [InlineKeyboardButton("Get Batch Link", callback_data="getBatchLink")], 441 | [InlineKeyboardButton("Close Message", callback_data="closeMessage")] 442 | ])) 443 | 444 | elif "addToBatchFalse" in cb_data: 445 | await save_media_in_channel(bot, editable=cmd.message, message=cmd.message.reply_to_message) 446 | 447 | elif "getBatchLink" in cb_data: 448 | message_ids = MediaList.get(f"{str(cmd.from_user.id)}", None) 449 | if message_ids is None: 450 | await cmd.answer("Batch List Empty!", show_alert=True) 451 | return 452 | await cmd.message.edit("Please wait, generating batch link ...") 453 | await save_batch_media_in_channel(bot=bot, editable=cmd.message, message_ids=message_ids) 454 | MediaList[f"{str(cmd.from_user.id)}"] = [] 455 | 456 | elif "closeMessage" in cb_data: 457 | await cmd.message.delete(True) 458 | 459 | try: 460 | await cmd.answer() 461 | except QueryIdInvalid: pass 462 | 463 | 464 | Bot.run() 465 | -------------------------------------------------------------------------------- /configs.py: -------------------------------------------------------------------------------- 1 | # (c) @AbirHasan2005 2 | 3 | import os 4 | 5 | 6 | class Config(object): 7 | API_ID = int(os.environ.get("API_ID", "0")) 8 | API_HASH = os.environ.get("API_HASH") 9 | BOT_TOKEN = os.environ.get("BOT_TOKEN") 10 | BOT_USERNAME = os.environ.get("BOT_USERNAME") 11 | DB_CHANNEL = int(os.environ.get("DB_CHANNEL", "-100")) 12 | BOT_OWNER = int(os.environ.get("BOT_OWNER", "1445283714")) 13 | DATABASE_URL = os.environ.get("DATABASE_URL") 14 | UPDATES_CHANNEL = os.environ.get("UPDATES_CHANNEL", "") 15 | LOG_CHANNEL = os.environ.get("LOG_CHANNEL", None) 16 | BANNED_USERS = set(int(x) for x in os.environ.get("BANNED_USERS", "1234567890").split()) 17 | FORWARD_AS_COPY = bool(os.environ.get("FORWARD_AS_COPY", True)) 18 | BROADCAST_AS_COPY = bool(os.environ.get("BROADCAST_AS_COPY", False)) 19 | BANNED_CHAT_IDS = list(set(int(x) for x in os.environ.get("BANNED_CHAT_IDS", "-1001362659779 -1001255795497").split())) 20 | OTHER_USERS_CAN_SAVE_FILE = bool(os.environ.get("OTHER_USERS_CAN_SAVE_FILE", True)) 21 | ABOUT_BOT_TEXT = f""" 22 | This is Permanent Files Store Bot! 23 | Send me any file I will save it in my Database. Also works for channel. Add me to channel as Admin with Edit Permission, I will add Save Uploaded File in Channel & add Sharable Button Link. 24 | 25 | 🤖 **My Name:** [Files Store Bot](https://t.me/{BOT_USERNAME}) 26 | 27 | 📝 **Language:** [Python3](https://www.python.org) 28 | 29 | 📚 **Library:** [Pyrogram](https://docs.pyrogram.org) 30 | 31 | 📡 **Hosted on:** [Heroku](https://heroku.com) 32 | 33 | 🧑🏻‍💻 **Developer:** @AbirHasan2005 34 | 35 | 👥 **Support Group:** [Linux Repositories](https://t.me/DevsZone) 36 | 37 | 📢 **Updates Channel:** [Discovery Projects](https://t.me/Discovery_Updates) 38 | """ 39 | ABOUT_DEV_TEXT = f""" 40 | 🧑🏻‍💻 **Developer:** @AbirHasan2005 41 | 42 | Developer is Super Noob. Just Learning from Official Docs. Please Donate the developer for Keeping the Service Alive. 43 | 44 | Also remember that developer will Delete Adult Contents from Database. So better don't Store Those Kind of Things. 45 | 46 | [Donate Now](https://www.paypal.me/AbirHasan2005) (PayPal) 47 | """ 48 | HOME_TEXT = """ 49 | Hi, [{}](tg://user?id={})\n\nThis is Permanent **File Store Bot**. 50 | 51 | Send me any file I will give you a permanent Sharable Link. I Support Channel Also! Check **About Bot** Button. 52 | """ 53 | -------------------------------------------------------------------------------- /handlers/add_user_to_db.py: -------------------------------------------------------------------------------- 1 | # (c) @AbirHasan2005 2 | 3 | from configs import Config 4 | from handlers.database import db 5 | from pyrogram import Client 6 | from pyrogram.types import Message 7 | 8 | 9 | async def add_user_to_database(bot: Client, cmd: Message): 10 | if not await db.is_user_exist(cmd.from_user.id): 11 | await db.add_user(cmd.from_user.id) 12 | if Config.LOG_CHANNEL is not None: 13 | await bot.send_message( 14 | int(Config.LOG_CHANNEL), 15 | f"#NEW_USER: \n\nNew User [{cmd.from_user.first_name}](tg://user?id={cmd.from_user.id}) started @{Config.BOT_USERNAME} !!" 16 | ) 17 | -------------------------------------------------------------------------------- /handlers/broadcast_handlers.py: -------------------------------------------------------------------------------- 1 | # (c) @AbirHasan2005 2 | 3 | import time 4 | import string 5 | import random 6 | import datetime 7 | import aiofiles 8 | import asyncio 9 | import traceback 10 | import aiofiles.os 11 | from configs import Config 12 | from pyrogram.errors import ( 13 | FloodWait, 14 | InputUserDeactivated, 15 | UserIsBlocked, 16 | PeerIdInvalid 17 | ) 18 | 19 | broadcast_ids = {} 20 | 21 | 22 | async def send_msg(user_id, message): 23 | try: 24 | if Config.BROADCAST_AS_COPY is False: 25 | await message.forward(chat_id=user_id) 26 | elif Config.BROADCAST_AS_COPY is True: 27 | await message.copy(chat_id=user_id) 28 | return 200, None 29 | except FloodWait as e: 30 | await asyncio.sleep(e.value) 31 | return send_msg(user_id, message) 32 | except InputUserDeactivated: 33 | return 400, f"{user_id} : deactivated\n" 34 | except UserIsBlocked: 35 | return 400, f"{user_id} : blocked the bot\n" 36 | except PeerIdInvalid: 37 | return 400, f"{user_id} : user id invalid\n" 38 | except Exception as e: 39 | return 500, f"{user_id} : {traceback.format_exc()}\n" 40 | 41 | 42 | async def main_broadcast_handler(m, db): 43 | all_users = await db.get_all_users() 44 | broadcast_msg = m.reply_to_message 45 | while True: 46 | broadcast_id = ''.join([random.choice(string.ascii_letters) for i in range(3)]) 47 | if not broadcast_ids.get(broadcast_id): 48 | break 49 | out = await m.reply_text( 50 | text=f"Broadcast Started! You will be notified with log file when all the users are notified." 51 | ) 52 | start_time = time.time() 53 | total_users = await db.total_users_count() 54 | done = 0 55 | failed = 0 56 | success = 0 57 | broadcast_ids[broadcast_id] = dict( 58 | total=total_users, 59 | current=done, 60 | failed=failed, 61 | success=success 62 | ) 63 | async with aiofiles.open('broadcast.txt', 'w') as broadcast_log_file: 64 | async for user in all_users: 65 | sts, msg = await send_msg( 66 | user_id=int(user['id']), 67 | message=broadcast_msg 68 | ) 69 | if msg is not None: 70 | await broadcast_log_file.write(msg) 71 | if sts == 200: 72 | success += 1 73 | else: 74 | failed += 1 75 | if sts == 400: 76 | await db.delete_user(user['id']) 77 | done += 1 78 | if broadcast_ids.get(broadcast_id) is None: 79 | break 80 | else: 81 | broadcast_ids[broadcast_id].update( 82 | dict( 83 | current=done, 84 | failed=failed, 85 | success=success 86 | ) 87 | ) 88 | if broadcast_ids.get(broadcast_id): 89 | broadcast_ids.pop(broadcast_id) 90 | completed_in = datetime.timedelta(seconds=int(time.time() - start_time)) 91 | await asyncio.sleep(3) 92 | await out.delete() 93 | if failed == 0: 94 | await m.reply_text( 95 | text=f"broadcast completed in `{completed_in}`\n\nTotal users {total_users}.\nTotal done {done}, {success} success and {failed} failed.", 96 | quote=True 97 | ) 98 | else: 99 | await m.reply_document( 100 | document='broadcast.txt', 101 | caption=f"broadcast completed in `{completed_in}`\n\nTotal users {total_users}.\nTotal done {done}, {success} success and {failed} failed.", 102 | quote=True 103 | ) 104 | await aiofiles.os.remove('broadcast.txt') 105 | -------------------------------------------------------------------------------- /handlers/check_user_status.py: -------------------------------------------------------------------------------- 1 | # (c) Mr. Vishal & @AbirHasan2005 2 | 3 | import datetime 4 | from configs import Config 5 | from handlers.database import Database 6 | 7 | db = Database(Config.DATABASE_URL, Config.BOT_USERNAME) 8 | 9 | 10 | async def handle_user_status(bot, cmd): 11 | chat_id = cmd.from_user.id 12 | if not await db.is_user_exist(chat_id): 13 | await db.add_user(chat_id) 14 | await bot.send_message( 15 | Config.LOG_CHANNEL, 16 | f"#NEW_USER: \n\nNew User [{cmd.from_user.first_name}](tg://user?id={cmd.from_user.id}) started @{Config.BOT_USERNAME} !!" 17 | ) 18 | 19 | ban_status = await db.get_ban_status(chat_id) 20 | if ban_status["is_banned"]: 21 | if ( 22 | datetime.date.today() - datetime.date.fromisoformat(ban_status["banned_on"]) 23 | ).days > ban_status["ban_duration"]: 24 | await db.remove_ban(chat_id) 25 | else: 26 | await cmd.reply_text("You are Banned to Use This Bot 🥺", quote=True) 27 | return 28 | await cmd.continue_propagation() 29 | -------------------------------------------------------------------------------- /handlers/database.py: -------------------------------------------------------------------------------- 1 | # (c) @AbirHasan2005 2 | 3 | import datetime 4 | import motor.motor_asyncio 5 | from configs import Config 6 | 7 | 8 | class Database: 9 | 10 | def __init__(self, uri, database_name): 11 | self._client = motor.motor_asyncio.AsyncIOMotorClient(uri) 12 | self.db = self._client[database_name] 13 | self.col = self.db.users 14 | 15 | def new_user(self, id): 16 | return dict( 17 | id=id, 18 | join_date=datetime.date.today().isoformat(), 19 | ban_status=dict( 20 | is_banned=False, 21 | ban_duration=0, 22 | banned_on=datetime.date.max.isoformat(), 23 | ban_reason='' 24 | ) 25 | ) 26 | 27 | async def add_user(self, id): 28 | user = self.new_user(id) 29 | await self.col.insert_one(user) 30 | 31 | async def is_user_exist(self, id): 32 | user = await self.col.find_one({'id': int(id)}) 33 | return True if user else False 34 | 35 | async def total_users_count(self): 36 | count = await self.col.count_documents({}) 37 | return count 38 | 39 | async def get_all_users(self): 40 | all_users = self.col.find({}) 41 | return all_users 42 | 43 | async def delete_user(self, user_id): 44 | await self.col.delete_many({'id': int(user_id)}) 45 | 46 | async def remove_ban(self, id): 47 | ban_status = dict( 48 | is_banned=False, 49 | ban_duration=0, 50 | banned_on=datetime.date.max.isoformat(), 51 | ban_reason='' 52 | ) 53 | await self.col.update_one({'id': id}, {'$set': {'ban_status': ban_status}}) 54 | 55 | async def ban_user(self, user_id, ban_duration, ban_reason): 56 | ban_status = dict( 57 | is_banned=True, 58 | ban_duration=ban_duration, 59 | banned_on=datetime.date.today().isoformat(), 60 | ban_reason=ban_reason 61 | ) 62 | await self.col.update_one({'id': user_id}, {'$set': {'ban_status': ban_status}}) 63 | 64 | async def get_ban_status(self, id): 65 | default = dict( 66 | is_banned=False, 67 | ban_duration=0, 68 | banned_on=datetime.date.max.isoformat(), 69 | ban_reason='' 70 | ) 71 | user = await self.col.find_one({'id': int(id)}) 72 | return user.get('ban_status', default) 73 | 74 | async def get_all_banned_users(self): 75 | banned_users = self.col.find({'ban_status.is_banned': True}) 76 | return banned_users 77 | 78 | 79 | db = Database(Config.DATABASE_URL, Config.BOT_USERNAME) 80 | -------------------------------------------------------------------------------- /handlers/force_sub_handler.py: -------------------------------------------------------------------------------- 1 | # (c) @AbirHasan2005 2 | 3 | import asyncio 4 | from typing import ( 5 | Union 6 | ) 7 | from configs import Config 8 | from pyrogram import Client 9 | from pyrogram.errors import FloodWait, UserNotParticipant 10 | from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton, Message 11 | 12 | 13 | async def get_invite_link(bot: Client, chat_id: Union[str, int]): 14 | try: 15 | invite_link = await bot.create_chat_invite_link(chat_id=chat_id) 16 | return invite_link 17 | except FloodWait as e: 18 | print(f"Sleep of {e.value}s caused by FloodWait ...") 19 | await asyncio.sleep(e.value) 20 | return await get_invite_link(bot, chat_id) 21 | 22 | 23 | async def handle_force_sub(bot: Client, cmd: Message): 24 | if Config.UPDATES_CHANNEL and Config.UPDATES_CHANNEL.startswith("-100"): 25 | channel_chat_id = int(Config.UPDATES_CHANNEL) 26 | elif Config.UPDATES_CHANNEL and (not Config.UPDATES_CHANNEL.startswith("-100")): 27 | channel_chat_id = Config.UPDATES_CHANNEL 28 | else: 29 | return 200 30 | try: 31 | user = await bot.get_chat_member(chat_id=channel_chat_id, user_id=cmd.from_user.id) 32 | if user.status == "kicked": 33 | await bot.send_message( 34 | chat_id=cmd.from_user.id, 35 | text="Sorry Sir, You are Banned to use me. Contact my [Support Group](https://t.me/JoinOT).", 36 | disable_web_page_preview=True 37 | ) 38 | return 400 39 | except UserNotParticipant: 40 | try: 41 | invite_link = await get_invite_link(bot, chat_id=channel_chat_id) 42 | except Exception as err: 43 | print(f"Unable to do Force Subscribe to {Config.UPDATES_CHANNEL}\n\nError: {err}") 44 | return 200 45 | await bot.send_message( 46 | chat_id=cmd.from_user.id, 47 | text="**Please Join My Updates Channel to use this Bot!**\n\n" 48 | "Due to Overload, Only Channel Subscribers can use the Bot!", 49 | reply_markup=InlineKeyboardMarkup( 50 | [ 51 | [ 52 | InlineKeyboardButton("🤖 Join Updates Channel", url=invite_link.invite_link) 53 | ], 54 | [ 55 | InlineKeyboardButton("🔄 Refresh 🔄", callback_data="refreshForceSub") 56 | ] 57 | ] 58 | ) 59 | ) 60 | return 400 61 | except Exception: 62 | await bot.send_message( 63 | chat_id=cmd.from_user.id, 64 | text="Something went Wrong. Contact my [Support Group](https://t.me/JoinOT).", 65 | disable_web_page_preview=True 66 | ) 67 | return 200 68 | return 200 69 | -------------------------------------------------------------------------------- /handlers/helpers.py: -------------------------------------------------------------------------------- 1 | # (c) @ballicipluck & @AbirHasan2005 2 | 3 | from base64 import standard_b64encode, standard_b64decode 4 | 5 | 6 | def str_to_b64(__str: str) -> str: 7 | str_bytes = __str.encode('ascii') 8 | bytes_b64 = standard_b64encode(str_bytes) 9 | b64 = bytes_b64.decode('ascii') 10 | return b64 11 | 12 | 13 | def b64_to_str(b64: str) -> str: 14 | bytes_b64 = b64.encode('ascii') 15 | bytes_str = standard_b64decode(bytes_b64) 16 | __str = bytes_str.decode('ascii') 17 | return __str 18 | -------------------------------------------------------------------------------- /handlers/save_media.py: -------------------------------------------------------------------------------- 1 | # (c) @AbirHasan2005 2 | 3 | import asyncio 4 | from configs import Config 5 | from pyrogram import Client 6 | from pyrogram.types import ( 7 | Message, 8 | InlineKeyboardMarkup, 9 | InlineKeyboardButton 10 | ) 11 | from pyrogram.errors import FloodWait 12 | from handlers.helpers import str_to_b64 13 | 14 | 15 | async def forward_to_channel(bot: Client, message: Message, editable: Message): 16 | try: 17 | __SENT = await message.forward(Config.DB_CHANNEL) 18 | return __SENT 19 | except FloodWait as sl: 20 | if sl.value > 45: 21 | await asyncio.sleep(sl.value) 22 | await bot.send_message( 23 | chat_id=int(Config.LOG_CHANNEL), 24 | text=f"#FloodWait:\nGot FloodWait of `{str(sl.value)}s` from `{str(editable.chat.id)}` !!", 25 | disable_web_page_preview=True, 26 | reply_markup=InlineKeyboardMarkup( 27 | [ 28 | [InlineKeyboardButton("Ban User", callback_data=f"ban_user_{str(editable.chat.id)}")] 29 | ] 30 | ) 31 | ) 32 | return await forward_to_channel(bot, message, editable) 33 | 34 | 35 | async def save_batch_media_in_channel(bot: Client, editable: Message, message_ids: list): 36 | try: 37 | message_ids_str = "" 38 | for message in (await bot.get_messages(chat_id=editable.chat.id, message_ids=message_ids)): 39 | sent_message = await forward_to_channel(bot, message, editable) 40 | if sent_message is None: 41 | continue 42 | message_ids_str += f"{str(sent_message.id)} " 43 | await asyncio.sleep(2) 44 | SaveMessage = await bot.send_message( 45 | chat_id=Config.DB_CHANNEL, 46 | text=message_ids_str, 47 | disable_web_page_preview=True, 48 | reply_markup=InlineKeyboardMarkup([[ 49 | InlineKeyboardButton("Delete Batch", callback_data="closeMessage") 50 | ]]) 51 | ) 52 | share_link = f"https://t.me/{Config.BOT_USERNAME}?start=AbirHasan2005_{str_to_b64(str(SaveMessage.id))}" 53 | await editable.edit( 54 | f"**Batch Files Stored in my Database!**\n\nHere is the Permanent Link of your files: {share_link} \n\n" 55 | f"Just Click the link to get your files!", 56 | reply_markup=InlineKeyboardMarkup( 57 | [[InlineKeyboardButton("Open Link", url=share_link)], 58 | [InlineKeyboardButton("Bots Channel", url="https://t.me/Discovery_Updates"), 59 | InlineKeyboardButton("Support Group", url="https://t.me/JoinOT")]] 60 | ), 61 | disable_web_page_preview=True 62 | ) 63 | await bot.send_message( 64 | chat_id=int(Config.LOG_CHANNEL), 65 | text=f"#BATCH_SAVE:\n\n[{editable.reply_to_message.from_user.first_name}](tg://user?id={editable.reply_to_message.from_user.id}) Got Batch Link!", 66 | disable_web_page_preview=True, 67 | reply_markup=InlineKeyboardMarkup([[InlineKeyboardButton("Open Link", url=share_link)]]) 68 | ) 69 | except Exception as err: 70 | await editable.edit(f"Something Went Wrong!\n\n**Error:** `{err}`") 71 | await bot.send_message( 72 | chat_id=int(Config.LOG_CHANNEL), 73 | text=f"#ERROR_TRACEBACK:\nGot Error from `{str(editable.chat.id)}` !!\n\n**Traceback:** `{err}`", 74 | disable_web_page_preview=True, 75 | reply_markup=InlineKeyboardMarkup( 76 | [ 77 | [InlineKeyboardButton("Ban User", callback_data=f"ban_user_{str(editable.chat.id)}")] 78 | ] 79 | ) 80 | ) 81 | 82 | 83 | async def save_media_in_channel(bot: Client, editable: Message, message: Message): 84 | try: 85 | forwarded_msg = await message.forward(Config.DB_CHANNEL) 86 | file_er_id = str(forwarded_msg.id) 87 | await forwarded_msg.reply_text( 88 | f"#PRIVATE_FILE:\n\n[{message.from_user.first_name}](tg://user?id={message.from_user.id}) Got File Link!", 89 | disable_web_page_preview=True) 90 | share_link = f"https://t.me/{Config.BOT_USERNAME}?start=AbirHasan2005_{str_to_b64(file_er_id)}" 91 | await editable.edit( 92 | "**Your File Stored in my Database!**\n\n" 93 | f"Here is the Permanent Link of your file: {share_link} \n\n" 94 | "Just Click the link to get your file!", 95 | reply_markup=InlineKeyboardMarkup( 96 | [[InlineKeyboardButton("Open Link", url=share_link)], 97 | [InlineKeyboardButton("Bots Channel", url="https://t.me/Discovery_Updates"), 98 | InlineKeyboardButton("Support Group", url="https://t.me/JoinOT")]] 99 | ), 100 | disable_web_page_preview=True 101 | ) 102 | except FloodWait as sl: 103 | if sl.value > 45: 104 | print(f"Sleep of {sl.value}s caused by FloodWait ...") 105 | await asyncio.sleep(sl.value) 106 | await bot.send_message( 107 | chat_id=int(Config.LOG_CHANNEL), 108 | text="#FloodWait:\n" 109 | f"Got FloodWait of `{str(sl.value)}s` from `{str(editable.chat.id)}` !!", 110 | disable_web_page_preview=True, 111 | reply_markup=InlineKeyboardMarkup( 112 | [ 113 | [InlineKeyboardButton("Ban User", callback_data=f"ban_user_{str(editable.chat.id)}")] 114 | ] 115 | ) 116 | ) 117 | await save_media_in_channel(bot, editable, message) 118 | except Exception as err: 119 | await editable.edit(f"Something Went Wrong!\n\n**Error:** `{err}`") 120 | await bot.send_message( 121 | chat_id=int(Config.LOG_CHANNEL), 122 | text="#ERROR_TRACEBACK:\n" 123 | f"Got Error from `{str(editable.chat.id)}` !!\n\n" 124 | f"**Traceback:** `{err}`", 125 | disable_web_page_preview=True, 126 | reply_markup=InlineKeyboardMarkup( 127 | [ 128 | [InlineKeyboardButton("Ban User", callback_data=f"ban_user_{str(editable.chat.id)}")] 129 | ] 130 | ) 131 | ) 132 | -------------------------------------------------------------------------------- /handlers/send_file.py: -------------------------------------------------------------------------------- 1 | # (c) @AbirHasan2005 2 | 3 | import asyncio 4 | from configs import Config 5 | from pyrogram import Client 6 | from pyrogram.types import Message 7 | from pyrogram.errors import FloodWait 8 | from handlers.helpers import str_to_b64 9 | 10 | 11 | async def reply_forward(message: Message, file_id: int): 12 | try: 13 | await message.reply_text( 14 | f"**Here is Sharable Link of this file:**\n" 15 | f"https://t.me/{Config.BOT_USERNAME}?start=AbirHasan2005_{str_to_b64(str(file_id))}\n\n" 16 | f"__To Retrive the Stored File, just open the link!__", 17 | disable_web_page_preview=True, quote=True) 18 | except FloodWait as e: 19 | await asyncio.sleep(e.value) 20 | await reply_forward(message, file_id) 21 | 22 | 23 | async def media_forward(bot: Client, user_id: int, file_id: int): 24 | try: 25 | if Config.FORWARD_AS_COPY is True: 26 | return await bot.copy_message(chat_id=user_id, from_chat_id=Config.DB_CHANNEL, 27 | message_id=file_id) 28 | elif Config.FORWARD_AS_COPY is False: 29 | return await bot.forward_messages(chat_id=user_id, from_chat_id=Config.DB_CHANNEL, 30 | message_ids=file_id) 31 | except FloodWait as e: 32 | await asyncio.sleep(e.value) 33 | return media_forward(bot, user_id, file_id) 34 | 35 | 36 | async def send_media_and_reply(bot: Client, user_id: int, file_id: int): 37 | sent_message = await media_forward(bot, user_id, file_id) 38 | await reply_forward(message=sent_message, file_id=file_id) 39 | await asyncio.sleep(2) 40 | -------------------------------------------------------------------------------- /heroku.yml: -------------------------------------------------------------------------------- 1 | build: 2 | docker: 3 | worker: Dockerfile 4 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | Pyrogram 2 | TgCrypto 3 | motor 4 | aiofiles 5 | dnspython 6 | psutil 7 | --------------------------------------------------------------------------------