├── .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 | [](https://youtu.be/hHu2RaePhwI)
43 |
44 | ### Deploy Now:
45 | [](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 |
--------------------------------------------------------------------------------