├── start.sh
├── heroku.yml
├── main.py
├── Procfile
├── requirements.txt
├── Dockerfile
├── config.py
├── logging.conf
├── LICENSE
├── app.json
├── plugins
├── utils.py
├── broadcast.py
├── commands.py
├── unequify.py
├── public.py
├── test.py
├── regix.py
└── settings.py
├── bot.py
├── README.md
├── translation.py
└── database.py
/start.sh:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/heroku.yml:
--------------------------------------------------------------------------------
1 | build:
2 | docker:
3 | worker: Dockerfile
4 |
--------------------------------------------------------------------------------
/main.py:
--------------------------------------------------------------------------------
1 | from bot import Bot
2 |
3 | app = Bot()
4 | app.run()
5 |
--------------------------------------------------------------------------------
/Procfile:
--------------------------------------------------------------------------------
1 | devgagan1: python main.py
2 | devgagan2: cp __init__.py src/devgagan/ && cd src && python -m devgagan
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | pyrofork
2 | motor
3 | TgCrypto
4 | Dnspython
5 | https://devgagan.in/wp-content/uploads/2023/12/gagantelethon.zip
6 | cryptg
7 | tgcrypto
8 | Pyrogram==2.0.93
9 | psutil
10 | opencv-python-headless
11 | python-decouple
12 | requests
13 | speedtest-cli
14 | pymongo
15 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM python:3.8-slim-buster
2 |
3 | RUN apt update && apt upgrade -y
4 | RUN apt install git -y
5 | COPY requirements.txt /requirements.txt
6 |
7 | RUN cd /
8 | RUN pip3 install -U pip && pip3 install -U -r requirements.txt
9 | RUN mkdir /fwdbot
10 | WORKDIR /fwdbot
11 | COPY start.sh /start.sh
12 | CMD ["/bin/bash", "/start.sh"]
13 |
--------------------------------------------------------------------------------
/config.py:
--------------------------------------------------------------------------------
1 | from os import environ
2 |
3 | class Config:
4 | API_ID = environ.get("API_ID", "577678")
5 | API_HASH = environ.get("API_HASH", "d2c6e01uuiuiouioiuiou0fc6d7a1be")
6 | BOT_TOKEN = environ.get("BOT_TOKEN", "70955...")
7 | BOT_SESSION = environ.get("BOT_SESSION", "bot")
8 | DATABASE_URI = environ.get("DATABASE", "mongodb+srv://chhjgjkkjhkjhkjh@cluster0.xowzpr4.mongodb.net/")
9 | DATABASE_NAME = environ.get("DATABASE_NAME", "forward-bot")
10 | BOT_OWNER_ID = [int(id) for id in environ.get("BOT_OWNER_ID", '6964148334').split()]
11 |
12 | class temp(object):
13 | lock = {}
14 | CANCEL = {}
15 | forwardings = 0
16 | BANNED_USERS = []
17 | IS_FRWD_CHAT = []
18 |
19 |
--------------------------------------------------------------------------------
/logging.conf:
--------------------------------------------------------------------------------
1 | [loggers]
2 | keys=root
3 |
4 | [handlers]
5 | keys=consoleHandler,fileHandler
6 |
7 | [formatters]
8 | keys=consoleFormatter,fileFormatter
9 |
10 | [logger_root]
11 | level=DEBUG
12 | handlers=consoleHandler,fileHandler
13 |
14 | [handler_consoleHandler]
15 | class=StreamHandler
16 | level=INFO
17 | formatter=consoleFormatter
18 | args=(sys.stdout,)
19 |
20 | [handler_fileHandler]
21 | class=FileHandler
22 | level=ERROR
23 | formatter=fileFormatter
24 | args=('TelegramBot.log','w',)
25 |
26 | [formatter_consoleFormatter]
27 | format=%(asctime)s - %(lineno)d - %(name)s - %(module)s - %(levelname)s - %(message)s
28 | datefmt=%I:%M:%S %p
29 |
30 | [formatter_fileFormatter]
31 | format=[%(asctime)s:%(name)s:%(lineno)d:%(levelname)s] %(message)s
32 | datefmt=%m/%d/%Y %I:%M:%S %p
33 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 |
2 | MIT License
3 |
4 | Copyright (c) 2024 devgaganin (as on GitHub)
5 |
6 | Permission is hereby granted, free of charge, to any person obtaining a copy
7 | of this software and associated documentation files (the "Software"), to deal
8 | in the Software without restriction, including without limitation the rights
9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | copies of the Software, and to permit persons to whom the Software is
11 | furnished to do so, subject to the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be included in all
14 | copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | SOFTWARE.
23 |
--------------------------------------------------------------------------------
/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Auto-Forward-Bot-V2",
3 | "description": "Telegram's Best Smart Plugin File Forward Bot",
4 | "logo": "https://telegra.ph/file/5c3499c3b2ec282cf0c4e.jpg",
5 | "stack": "container",
6 | "keywords": [
7 | "telegram",
8 | "best",
9 | "file",
10 | "forward",
11 | "bot"
12 | ],
13 | "success_url": "https://t.me/devggn",
14 | "website": "https://github.com/devgaganin/Auto-Forward-Bot-V2",
15 | "repository": "https://github.com/devgaganin/Auto-Forward-Bot-V2",
16 | "env": {
17 | "API_ID": {
18 | "description": "Get this value from https://my.telegram.org or @UseTGSBot",
19 | "value": ""
20 | },
21 | "API_HASH": {
22 | "description": "Get this value from https://my.telegram.org or @UseTGSBot",
23 | "value": ""
24 | },
25 | "BOT_TOKEN": {
26 | "description": "Your bot token from @BotFather",
27 | "value": ""
28 | },
29 | "BOT_OWNER_ID": {
30 | "description": "Enter Your Telegram id",
31 | "value": ""
32 | },
33 | "BANNED_USERS": {
34 | "description": "If you want to add a caption to the forwarded file, enter it here",
35 | "required": false
36 | },
37 | "DATABASE_NAME": {
38 | "description": "Type Of filters (document , audio , photo , video , animation)",
39 | "value": "document"
40 | },
41 | "DATABASE_URI": {
42 | "description": "pyrogram string Seccion - https://replit.com/@JijinR/PyroSessionString?v=1",",
43 | "value": ""
44 | }
45 | },
46 | "formation": {
47 | "worker": {
48 | "quantity": 1,
49 | "size": "free"
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/plugins/utils.py:
--------------------------------------------------------------------------------
1 | import time as tm
2 | from database import db
3 | from .test import parse_buttons
4 |
5 | STATUS = {}
6 |
7 | class STS:
8 | def __init__(self, id):
9 | self.id = id
10 | self.data = STATUS
11 |
12 | def verify(self):
13 | return self.data.get(self.id)
14 |
15 | def store(self, From, to, skip, limit):
16 | self.data[self.id] = {"FROM": From, 'TO': to, 'total_files': 0, 'skip': skip, 'limit': limit,
17 | 'fetched': skip, 'filtered': 0, 'deleted': 0, 'duplicate': 0, 'total': limit, 'start': 0}
18 | self.get(full=True)
19 | return STS(self.id)
20 |
21 | def get(self, value=None, full=False):
22 | values = self.data.get(self.id)
23 | if not full:
24 | return values.get(value)
25 | for k, v in values.items():
26 | setattr(self, k, v)
27 | return self
28 |
29 | def add(self, key=None, value=1, time=False):
30 | if time:
31 | return self.data[self.id].update({'start': tm.time()})
32 | self.data[self.id].update({key: self.get(key) + value})
33 |
34 | def divide(self, no, by):
35 | by = 1 if int(by) == 0 else by
36 | return int(no) / by
37 |
38 | async def get_data(self, user_id):
39 | bot = await db.get_bot(user_id)
40 | k, filters = self, await db.get_filters(user_id)
41 | size, configs = None, await db.get_configs(user_id)
42 | if configs['duplicate']:
43 | duplicate = [configs['db_uri'], self.TO]
44 | else:
45 | duplicate = False
46 | button = parse_buttons(configs['button'] if configs['button'] else '')
47 | if configs['file_size'] != 0:
48 | size = [configs['file_size'], configs['size_limit']]
49 | return bot, configs['caption'], configs['forward_tag'], {'chat_id': k.FROM, 'limit': k.limit, 'offset': k.skip, 'filters': filters,
50 | 'keywords': configs['keywords'], 'media_size': size, 'extensions': configs['extension'], 'skip_duplicate': duplicate}, configs['protect'], button
51 |
--------------------------------------------------------------------------------
/bot.py:
--------------------------------------------------------------------------------
1 | import asyncio
2 | import logging
3 | import logging.config
4 | from database import db
5 | from config import Config
6 | from pyrogram import Client, __version__
7 | from pyrogram.raw.all import layer
8 | from pyrogram.enums import ParseMode
9 | from pyrogram.errors import FloodWait
10 |
11 | logging.config.fileConfig('logging.conf')
12 | logging.getLogger().setLevel(logging.INFO)
13 | logging.getLogger("pyrogram").setLevel(logging.ERROR)
14 |
15 | class Bot(Client):
16 | def __init__(self):
17 | super().__init__(
18 | Config.BOT_SESSION,
19 | api_hash=Config.API_HASH,
20 | api_id=Config.API_ID,
21 | plugins={
22 | "root": "plugins"
23 | },
24 | workers=50,
25 | bot_token=Config.BOT_TOKEN
26 | )
27 | self.log = logging
28 |
29 | async def start(self):
30 | await super().start()
31 | me = await self.get_me()
32 | logging.info(f"{me.first_name} with for pyrogram v{__version__} (Layer {layer}) started on @{me.username}.")
33 | self.id = me.id
34 | self.username = me.username
35 | self.first_name = me.first_name
36 | self.set_parse_mode(ParseMode.DEFAULT)
37 | text = "**๏[-ิ_•ิ]๏ bot restarted !**"
38 | logging.info(text)
39 | success = failed = 0
40 | users = await db.get_all_frwd()
41 | async for user in users:
42 | chat_id = user['user_id']
43 | try:
44 | await self.send_message(chat_id, text)
45 | success += 1
46 | except FloodWait as e:
47 | await asyncio.sleep(e.value + 1)
48 | await self.send_message(chat_id, text)
49 | success += 1
50 | except Exception:
51 | failed += 1
52 | # await self.send_message("venombotsupport", text)
53 | if (success + failed) != 0:
54 | await db.rmve_frwd(all=True)
55 | logging.info(f"Restart message status"
56 | f"success: {success}"
57 | f"failed: {failed}")
58 |
59 | async def stop(self, *args):
60 | msg = f"@{self.username} stopped. Bye."
61 | await super().stop()
62 | logging.info(msg)
--------------------------------------------------------------------------------
/plugins/broadcast.py:
--------------------------------------------------------------------------------
1 | import asyncio
2 | import time, datetime
3 | from database import db
4 | from config import Config
5 | from pyrogram import Client, filters
6 | from pyrogram.errors import InputUserDeactivated, FloodWait, UserIsBlocked
7 |
8 | @Client.on_message(filters.command("broadcast") & filters.user(Config.BOT_OWNER_ID) & filters.reply)
9 | async def broadcast (bot, message):
10 | users = await db.get_all_users()
11 | b_msg = message.reply_to_message
12 | sts = await message.reply_text(
13 | text='Broadcasting your messages...'
14 | )
15 | start_time = time.time()
16 | total_users, k = await db.total_users_bots_count()
17 | done = 0
18 | blocked = 0
19 | deleted = 0
20 | failed = 0
21 | success = 0
22 | async for user in users:
23 | pti, sh = await broadcast_messages(int(user['id']), b_msg, bot.log)
24 | if pti:
25 | success += 1
26 | await asyncio.sleep(2)
27 | elif pti == False:
28 | if sh == "Blocked":
29 | blocked+=1
30 | elif sh == "Deleted":
31 | deleted += 1
32 | elif sh == "Error":
33 | failed += 1
34 | done += 1
35 | if not done % 20:
36 | await sts.edit(f"Broadcast in progress:\n\nTotal Users {total_users}\nCompleted: {done} / {total_users}\nSuccess: {success}\nBlocked: {blocked}\nDeleted: {deleted}")
37 | time_taken = datetime.timedelta(seconds=int(time.time()-start_time))
38 | await sts.edit(f"Broadcast Completed:\nCompleted in {time_taken} seconds.\n\nTotal Users {total_users}\nCompleted: {done} / {total_users}\nSuccess: {success}\nBlocked: {blocked}\nDeleted: {deleted}")
39 |
40 | async def broadcast_messages(user_id, message, log):
41 | try:
42 | await message.copy(chat_id=user_id)
43 | return True, "Success"
44 | except FloodWait as e:
45 | await asyncio.sleep(e.x)
46 | return await broadcast_messages(user_id, message, log)
47 | except InputUserDeactivated:
48 | await db.delete_user(int(user_id))
49 | log.info(f"{user_id}-Removed from Database, since deleted account.")
50 | return False, "Deleted"
51 | except UserIsBlocked:
52 | log.info(f"{user_id} -Blocked the bot.")
53 | return False, "Blocked"
54 | except Exception as e:
55 | return False, "Error"
56 |
57 |
58 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Advance Content Saver Bot
2 |
3 | Advance Telegram Bot that can work as Message/Files Forwarder from Restricted or Non-Restricted Channels/Groups/Bots. It can save files even if the channel have restrictions of forwarding or else.
4 |
5 | ## What can this bot do
6 | - Forward public/private channel messages for private user login needed as forward method
7 | - Save/clone individual post by its post link
8 | - Can save multiple post link via `batch` command up to 10K
9 |
10 | ## Deployment Methods
11 |
12 | ### VPS Deployment
13 |
14 | 1. **Initial Setup**:
15 | -`config.py` files with your variables.
16 |
17 | 2. **Clone and Run**:
18 | - Clone your forked and edited repository:
19 | ```bash
20 | git clone forked_edited_repo_link
21 | ```
22 | - Navigate to the repository directory:
23 | ```bash
24 | cd repo_name
25 | ```
26 | - Run the bot:
27 | ```bash
28 | python main.py
29 | ```
30 |
31 | ### Heroku Deployment
32 |
33 | 1. **Setup on Heroku**:
34 | - Go to [Heroku Dashboard](https://dashboard.heroku.com) and create a new app.
35 | - Connect your GitHub repository to Heroku.
36 | - Search and deploy the forked repository.
37 | - Back to the app view and refresh the page.
38 |
39 | 2. **Configure Dynos**:
40 | - Configure dynos for `devagagan1` and `devgagan2`.
41 |
42 | ## Additional Notes
43 |
44 | - Make sure to replace `forked_edited_repo_link` with the link to your forked and edited repository.
45 | - Replace `repo_name` with the name of your repository.
46 | - Update the `__init__.py` and `config.py` files with your bot's specific variables before deployment.
47 | - Ensure that your bot is configured correctly according to the requirements of the Telegram Bot API.
48 |
49 | ## Commands Available in Bot - [TEAM SPY](https://t.me/dev_gagan)
50 |
51 | - ```/start``` - to start the bot
52 | - ```/cancel``` - to cancel the onging /batch task
53 | - ```/stats``` - to viewing the statics of bot
54 | - `/forward or /fwd` - to start forward
55 | - `restart` - to restart the bot
56 | - `/resetall` - to reset unlink all other users / bot
57 | - `/broadcast` - send bulk message to all users who ever have started the bot
58 | - `/help` - get help about other commands
59 |
60 | ## Support
61 |
62 | [
](https://instagram.com/devagagn.in)
63 | [
](https://youtube.com/@dev_gagan)
64 | [
](https://t.me/dev_gagan)
65 | [
](https://github.com/devgaganin)
66 | [
](https://devgagan.in)
67 |
68 | ## Terms of USE / Modification
69 | Visit [Terms](https://github.com/devgaganin/Save-Restricted-Content-Bot-Repo/blob/main/TERMS_OF_USE.md) and accept the guidelines.
70 |
71 | ## Contributing
72 |
73 | Contributions are welcome! Please feel free to submit issues or pull requests.
74 |
75 | ## License
76 |
77 | This project is licensed under the [MIT License](LICENSE).
78 |
--------------------------------------------------------------------------------
/plugins/commands.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 | import asyncio
4 | from database import db, mongodb_version
5 | from config import Config, temp
6 | from platform import python_version
7 | from translation import Translation
8 | from pyrogram import Client, filters, enums, __version__ as pyrogram_version
9 | from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup, InputMediaDocument
10 |
11 | main_buttons = [[
12 | InlineKeyboardButton('Main Channel', url='https://t.me/dev_gagan')
13 | ],[
14 | InlineKeyboardButton('📜 sᴜᴘᴘᴏʀᴛ ɢʀᴏᴜᴘ ', url='https://t.me/dev_gagan'),
15 | InlineKeyboardButton('🤖 ᴜᴘᴅᴀᴛᴇ ᴄʜᴀɴɴᴇʟ ', url='https://t.me/dev_gagan')
16 | ],[
17 | InlineKeyboardButton('🙋♂️ ʜᴇʟᴘ', callback_data='help'),
18 | InlineKeyboardButton('💁♂️ ᴀʙᴏᴜᴛ ', callback_data='about')
19 | ],[
20 | InlineKeyboardButton('⚙️ sᴇᴛᴛɪɴɢs ⚙️', callback_data='settings#main')
21 | ]]
22 | #===================Start Function===================#
23 |
24 | @Client.on_message(filters.private & filters.command(['start']))
25 | async def start(client, message):
26 | user = message.from_user
27 | if not await db.is_user_exist(user.id):
28 | await db.add_user(user.id, user.first_name)
29 | reply_markup = InlineKeyboardMarkup(main_buttons)
30 | await client.send_message(
31 | chat_id=message.chat.id,
32 | reply_markup=InlineKeyboardMarkup(main_buttons),
33 | text=Translation.START_TXT.format(message.from_user.first_name))
34 |
35 | #==================Restart Function==================#
36 |
37 | @Client.on_message(filters.private & filters.command(['restart']) & filters.user(Config.BOT_OWNER_ID))
38 | async def restart(client, message):
39 | msg = await message.reply_text(
40 | text="Trying to restarting....."
41 | )
42 | await asyncio.sleep(5)
43 | await msg.edit("Server restarted successfully ✅")
44 | os.execl(sys.executable, sys.executable, *sys.argv)
45 |
46 | #==================Callback Functions==================#
47 |
48 | @Client.on_callback_query(filters.regex(r'^help'))
49 | async def helpcb(bot, query):
50 | await query.message.edit_text(
51 | text=Translation.HELP_TXT,
52 | reply_markup=InlineKeyboardMarkup(
53 | [[
54 | InlineKeyboardButton('ʜᴏᴡ ᴛᴏ ᴜsᴇ ᴍᴇ ❓', callback_data='how_to_use')
55 | ],[
56 | InlineKeyboardButton('⚙️ sᴇᴛᴛɪɴɢs ', callback_data='settings#main'),
57 | InlineKeyboardButton('📜 sᴛᴀᴛᴜs ', callback_data='status')
58 | ],[
59 | InlineKeyboardButton('↩ ʙᴀᴄᴋ', callback_data='back')
60 | ]]
61 | ))
62 |
63 | @Client.on_callback_query(filters.regex(r'^how_to_use'))
64 | async def how_to_use(bot, query):
65 | await query.message.edit_text(
66 | text=Translation.HOW_USE_TXT,
67 | reply_markup=InlineKeyboardMarkup([[InlineKeyboardButton('↩ Back', callback_data='help')]]),
68 | disable_web_page_preview=True
69 | )
70 |
71 | @Client.on_callback_query(filters.regex(r'^back'))
72 | async def back(bot, query):
73 | reply_markup = InlineKeyboardMarkup(main_buttons)
74 | await query.message.edit_text(
75 | reply_markup=reply_markup,
76 | text=Translation.START_TXT.format(
77 | query.from_user.first_name))
78 |
79 | @Client.on_callback_query(filters.regex(r'^about'))
80 | async def about(bot, query):
81 | await query.message.edit_text(
82 | text=Translation.ABOUT_TXT.format(my_name='Public Forward',python_version=python_version(),pyrogram_version=pyrogram_version,mongodb_version=await mongodb_version()),
83 | reply_markup=InlineKeyboardMarkup([[InlineKeyboardButton('↩ Back', callback_data='back')]]),
84 | disable_web_page_preview=True,
85 | parse_mode=enums.ParseMode.HTML,
86 | )
87 |
88 | @Client.on_callback_query(filters.regex(r'^status'))
89 | async def status(bot, query):
90 | users_count, bots_count = await db.total_users_bots_count()
91 | total_channels = await db.total_channels()
92 | await query.message.edit_text(
93 | text=Translation.STATUS_TXT.format(users_count, bots_count, temp.forwardings, total_channels, temp.BANNED_USERS ),
94 | reply_markup=InlineKeyboardMarkup([[InlineKeyboardButton('↩ Back', callback_data='help')]]),
95 | parse_mode=enums.ParseMode.HTML,
96 | disable_web_page_preview=True,
97 | )
98 |
--------------------------------------------------------------------------------
/plugins/unequify.py:
--------------------------------------------------------------------------------
1 | import re, asyncio
2 | from database import db
3 | from config import temp
4 | from .test import CLIENT , start_clone_bot
5 | from translation import Translation
6 | from pyrogram import Client, filters
7 | #from pyropatch.utils import unpack_new_file_id
8 | from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup
9 |
10 | CLIENT = CLIENT()
11 | COMPLETED_BTN = InlineKeyboardMarkup(
12 | [
13 | [InlineKeyboardButton('⚡ Support', url='https://t.me/dev_gagan')],
14 | [InlineKeyboardButton('📢 Updates', url='https://t.me/dev_gagan')]
15 | ]
16 | )
17 |
18 | CANCEL_BTN = InlineKeyboardMarkup([[InlineKeyboardButton('• ᴄᴀɴᴄᴇʟ', 'terminate_frwd')]])
19 |
20 | @Client.on_message(filters.command("unequify") & filters.private)
21 | async def unequify(client, message):
22 | user_id = message.from_user.id
23 | temp.CANCEL[user_id] = False
24 | if temp.lock.get(user_id) and str(temp.lock.get(user_id))=="True":
25 | return await message.reply("**please wait until previous task complete**")
26 | _bot = await db.get_bot(user_id)
27 | if not _bot or _bot['is_bot']:
28 | return await message.reply("Need userbot to do this process. Please add a userbot using /settings")
29 | target = await client.ask(user_id, text="**Forward the last message from target chat or send last message link.**\n/cancel - `cancel this process`")
30 | if target.text.startswith("/"):
31 | return await message.reply("**process cancelled !**")
32 | elif target.text:
33 | regex = re.compile(r"(https://)?(t\.me/|telegram\.me/|telegram\.dog/)(c/)?(\d+|[a-zA-Z_0-9]+)/(\d+)$")
34 | match = regex.match(target.text.replace("?single", ""))
35 | if not match:
36 | return await message.reply('**Invalid link**')
37 | chat_id = match.group(4)
38 | last_msg_id = int(match.group(5))
39 | if chat_id.isnumeric():
40 | chat_id = int(("-100" + chat_id))
41 | elif fromid.forward_from_chat.type in ['channel', 'supergroup']:
42 | last_msg_id = target.forward_from_message_id
43 | chat_id = target.forward_from_chat.username or target.forward_from_chat.id
44 | else:
45 | return await message.reply_text("**invalid !**")
46 | confirm = await client.ask(user_id, text="**send /yes to start the process and /no to cancel this process**")
47 | if confirm.text.lower() == '/no':
48 | return await confirm.reply("**process cancelled !**")
49 | sts = await confirm.reply("`processing..`")
50 | try:
51 | bot = await start_clone_bot(CLIENT.client(_bot))
52 | except Exception as e:
53 | return await sts.edit(e)
54 | try:
55 | k = await bot.send_message(chat_id, text="testing")
56 | await k.delete()
57 | except:
58 | await sts.edit(f"**please make your [userbot](t.me/{_bot['username']}) admin in target chat with full permissions**")
59 | return await bot.stop()
60 | MESSAGES = []
61 | DUPLICATE = []
62 | total=deleted=0
63 | temp.lock[user_id] = True
64 | try:
65 | await sts.edit(Translation.DUPLICATE_TEXT.format(total, deleted, "ᴘʀᴏɢʀᴇssɪɴɢ"), reply_markup=CANCEL_BTN)
66 | async for message in bot.search_messages(chat_id=chat_id, filter="document"):
67 | if temp.CANCEL.get(user_id) == True:
68 | await sts.edit(Translation.DUPLICATE_TEXT.format(total, deleted, "ᴄᴀɴᴄᴇʟʟᴇᴅ"), reply_markup=COMPLETED_BTN)
69 | return await bot.stop()
70 | file = message.document
71 | file_id = unpack_new_file_id(file.file_id)
72 | if file_id in MESSAGES:
73 | DUPLICATE.append(message.id)
74 | else:
75 | MESSAGES.append(file_id)
76 | total += 1
77 | if total %10000 == 0:
78 | await sts.edit(Translation.DUPLICATE_TEXT.format(total, deleted, "ᴘʀᴏɢʀᴇssɪɴɢ"), reply_markup=CANCEL_BTN)
79 | if len(DUPLICATE) >= 100:
80 | await bot.delete_messages(chat_id, DUPLICATE)
81 | deleted += 100
82 | await sts.edit(Translation.DUPLICATE_TEXT.format(total, deleted, "ᴘʀᴏɢʀᴇssɪɴɢ"), reply_markup=CANCEL_BTN)
83 | DUPLICATE = []
84 | if DUPLICATE:
85 | await bot.delete_messages(chat_id, DUPLICATE)
86 | deleted += len(DUPLICATE)
87 | except Exception as e:
88 | temp.lock[user_id] = False
89 | await sts.edit(f"**ERROR**\n`{e}`")
90 | return await bot.stop()
91 | temp.lock[user_id] = False
92 | await sts.edit(Translation.DUPLICATE_TEXT.format(total, deleted, "ᴄᴏᴍᴘʟᴇᴛᴇᴅ"), reply_markup=COMPLETED_BTN)
93 | await bot.stop()
94 |
95 |
--------------------------------------------------------------------------------
/plugins/public.py:
--------------------------------------------------------------------------------
1 | import re
2 | import asyncio
3 | from .utils import STS
4 | from database import db
5 | from config import temp
6 | from translation import Translation
7 | from pyrogram import Client, filters, enums
8 | from pyrogram.errors import FloodWait
9 | from pyrogram.errors.exceptions.not_acceptable_406 import ChannelPrivate as PrivateChat
10 | from pyrogram.errors.exceptions.bad_request_400 import ChannelInvalid, ChatAdminRequired, UsernameInvalid, UsernameNotModified, ChannelPrivate
11 | from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup, CallbackQuery, KeyboardButton, ReplyKeyboardMarkup, ReplyKeyboardRemove
12 |
13 | #===================Run Function===================#
14 |
15 | @Client.on_message(filters.private & filters.command(["fwd", "forward"]))
16 | async def run(bot, message):
17 | buttons = []
18 | btn_data = {}
19 | user_id = message.from_user.id
20 | _bot = await db.get_bot(user_id)
21 | if not _bot:
22 | return await message.reply("You didn't added any bot. Please add a bot using /settings !")
23 | channels = await db.get_user_channels(user_id)
24 | if not channels:
25 | return await message.reply_text("please set a to channel in /settings before forwarding")
26 | if len(channels) > 1:
27 | for channel in channels:
28 | buttons.append([KeyboardButton(f"{channel['title']}")])
29 | btn_data[channel['title']] = channel['chat_id']
30 | buttons.append([KeyboardButton("cancel")])
31 | _toid = await bot.ask(message.chat.id, Translation.TO_MSG.format(_bot['name'], _bot['username']), reply_markup=ReplyKeyboardMarkup(buttons, one_time_keyboard=True, resize_keyboard=True))
32 | if _toid.text.startswith(('/', 'cancel')):
33 | return await message.reply_text(Translation.CANCEL, reply_markup=ReplyKeyboardRemove())
34 | to_title = _toid.text
35 | toid = btn_data.get(to_title)
36 | if not toid:
37 | return await message.reply_text("wrong channel choosen !", reply_markup=ReplyKeyboardRemove())
38 | else:
39 | toid = channels[0]['chat_id']
40 | to_title = channels[0]['title']
41 | fromid = await bot.ask(message.chat.id, Translation.FROM_MSG, reply_markup=ReplyKeyboardRemove())
42 | if fromid.text and fromid.text.startswith('/'):
43 | await message.reply(Translation.CANCEL)
44 | return
45 | if fromid.text and not fromid.forward_date:
46 | regex = re.compile(r"(https://)?(t\.me/|telegram\.me/|telegram\.dog/)(c/)?(\d+|[a-zA-Z_0-9]+)/(\d+)$")
47 | match = regex.match(fromid.text.replace("?single", ""))
48 | if not match:
49 | return await message.reply('Invalid link')
50 | chat_id = match.group(4)
51 | last_msg_id = int(match.group(5))
52 | if chat_id.isnumeric():
53 | chat_id = int(("-100" + chat_id))
54 | elif fromid.forward_from_chat.type in [enums.ChatType.CHANNEL]:
55 | last_msg_id = fromid.forward_from_message_id
56 | chat_id = fromid.forward_from_chat.username or fromid.forward_from_chat.id
57 | if last_msg_id == None:
58 | return await message.reply_text("**This may be a forwarded message from a group and sended by anonymous admin. instead of this please send last message link from group**")
59 | else:
60 | await message.reply_text("**invalid !**")
61 | return
62 | try:
63 | title = (await bot.get_chat(chat_id)).title
64 | # except ChannelInvalid:
65 | #return await fromid.reply("**Given source chat is copyrighted channel/group. you can't forward messages from there**")
66 | except (PrivateChat, ChannelPrivate, ChannelInvalid):
67 | title = "private" if fromid.text else fromid.forward_from_chat.title
68 | except (UsernameInvalid, UsernameNotModified):
69 | return await message.reply('Invalid Link specified.')
70 | except Exception as e:
71 | return await message.reply(f'Errors - {e}')
72 | skipno = await bot.ask(message.chat.id, Translation.SKIP_MSG)
73 | if skipno.text.startswith('/'):
74 | await message.reply(Translation.CANCEL)
75 | return
76 | forward_id = f"{user_id}-{skipno.id}"
77 | buttons = [[
78 | InlineKeyboardButton('Yes', callback_data=f"start_public_{forward_id}"),
79 | InlineKeyboardButton('No', callback_data="close_btn")
80 | ]]
81 | reply_markup = InlineKeyboardMarkup(buttons)
82 | await message.reply_text(
83 | text=Translation.DOUBLE_CHECK.format(botname=_bot['name'], botuname=_bot['username'], from_chat=title, to_chat=to_title, skip=skipno.text),
84 | disable_web_page_preview=True,
85 | reply_markup=reply_markup
86 | )
87 | STS(forward_id).store(chat_id, toid, int(skipno.text), int(last_msg_id))
88 |
--------------------------------------------------------------------------------
/translation.py:
--------------------------------------------------------------------------------
1 | import os
2 | from config import Config
3 |
4 | class Translation(object):
5 | START_TXT = """ʜᴇʟʟᴏ {}
6 |
7 | ɪ'ᴍ ᴀ ᴘᴏᴡᴇʀғᴜʟʟ ᴀᴜᴛᴏ ғᴏʀᴡᴀʀᴅ ʙᴏᴛ
8 |
9 | ɪ ᴄᴀɴ ғᴏʀᴡᴀʀᴅ ᴀʟʟ ᴍᴇssᴀɢᴇ ғʀᴏᴍ ᴏɴᴇ ᴄʜᴀɴɴᴇʟ ᴛᴏ ᴀɴᴏᴛʜᴇʀ ᴄʜᴀɴɴᴇʟ ➜ ᴡɪᴛʜ ᴍᴏʀᴇ ғᴇᴀᴛᴜʀᴇs.
10 | ᴄʟɪᴄᴋ ʜᴇʟᴘ ʙᴜᴛᴛᴏɴ ᴛᴏ ᴋɴᴏᴡ ᴍᴏʀᴇ ᴀʙᴏᴜᴛ ᴍᴇ"""
11 |
12 |
13 | HELP_TXT = """🔆 HELP
14 |
15 | **📚 Available commands:**
16 | ⏣ __/start - check I'm alive__
17 | ⏣ __/forward - forward messages__
18 | ⏣ __/unequify - delete duplicate messages in channels__
19 | ⏣ __/settings - configure your settings__
20 | ⏣ __/reset - reset your settings__
21 |
22 | 💢 Features:
23 | ► __Forward message from public channel to your channel without admin permission. if the channel is private need admin permission__
24 | ► __Forward message from private channel to your channel by using userbot(user must be member in there)__
25 | ► __custom caption__
26 | ► __custom button__
27 | ► __support restricted chats__
28 | ► __skip duplicate messages__
29 | ► __filter type of messages__
30 | ► __skip messages based on extensions & keywords & size__
31 | """
32 |
33 | HOW_USE_TXT = """⚠️ Before Forwarding:
34 | ► __add a bot or userbot__
35 | ► __add atleast one to channel__ `(your bot/userbot must be admin in there)`
36 | ► __You can add chats or bots by using /settings__
37 | ► __if the **From Channel** is private your userbot must be member in there or your bot must need admin permission in there also__
38 | ► __Then use /forward to forward messages__"""
39 |
40 | ABOUT_TXT = """╭──────❰ 🤖 Bot Details ❱──────〄
41 | │
42 | │ 🤖 Mʏ Nᴀᴍᴇ : Dev Gagan Botᴛ
43 | │ 👨💻 ᴅᴇᴠᴘʟᴏᴇʀ : Team SPY
44 | │ 🤖 ᴜᴘᴅᴀᴛᴇ : devgagan
45 | │ 📡 ʜᴏsᴛ ᴏɴ : Dev Gagan Host
46 | │ 🗣️ ʟᴀɴɢᴜᴀɢᴇ : ᴘʏᴛʜᴏɴ 3
47 | {python_version}
48 | │ 📚 ʟɪʙʀᴀʀʏ : ᴘʏʀᴏɢʀᴀᴍ
49 | ╰────────────────────⍟"""
50 |
51 | STATUS_TXT = """╭──────❪ 🤖 Bot Status ❫─────⍟
52 | │
53 | ├👨 ᴜsᴇʀs : {}
54 | │
55 | ├🤖 ʙᴏᴛs : {}
56 | │
57 | ├📣 ᴄʜᴀɴɴᴇʟ : {}
58 | ╰───────────────────⍟"""
59 |
60 | FROM_MSG = "❪ SET SOURCE CHAT ❫\n\nForward the last message or last message link of source chat.\n/cancel - cancel this process"
61 | TO_MSG = "❪ CHOOSE TARGET CHAT ❫\n\nChoose your target chat from the given buttons.\n/cancel - Cancel this process"
62 | SKIP_MSG = "❪ SET MESSAGE SKIPING NUMBER ❫\n\nSkip the message as much as you enter the number and the rest of the message will be forwarded\nDefault Skip Number = 0\neg: You enter 0 = 0 message skiped\n You enter 5 = 5 message skiped\n/cancel - cancel this process"
63 | CANCEL = "Process Cancelled Succefully !"
64 | BOT_DETAILS = "📄 BOT DETAILS\n\n➣ NAME: {}\n➣ BOT ID: {}\n➣ USERNAME: @{}"
65 | USER_DETAILS = "📄 USERBOT DETAILS\n\n➣ NAME: {}\n➣ USER ID: {}\n➣ USERNAME: @{}"
66 |
67 | TEXT = """╭────❰ Forwarded Status ❱────❍
68 | ┃
69 | ┣⊸🕵 ғᴇᴄʜᴇᴅ ᴍsɢ : {}
70 | ┣⊸✅ sᴜᴄᴄᴇғᴜʟʟʏ ғᴡᴅ : {}
71 | ┣⊸👥 ᴅᴜᴘʟɪᴄᴀᴛᴇ ᴍsɢ : {}
72 | ┣⊸🗑️ ᴅᴇʟᴇᴛᴇᴅ ᴍsɢ : {}
73 | ┣⊸🪆 sᴋɪᴘᴘᴇᴅ ᴍsɢ : {}
74 | ┣⊸📊 sᴛᴀᴛᴜs : {}
75 | ┣⊸⏳ ᴘʀᴏɢʀᴇss : {} %
76 | ┣⊸⏰ ᴇᴛᴀ : {}
77 | ┃
78 | ╰────⌊ {} ⌉───❍"""
79 |
80 | TEXT1 = """╭─❰ Forwarded Status ❱─❍
81 | ┃
82 | ┣⊸🕵𝙁𝙚𝙘𝙝𝙚𝙙 𝙈𝙨𝙜 : {}
83 | ┣⊸✅𝙎𝙪𝙘𝙘𝙚𝙛𝙪𝙡𝙮 𝙁𝙬𝙙 : {}
84 | ┣⊸👥𝘿𝙪𝙥𝙡𝙞𝙘𝙖𝙩𝙚 𝙈𝙨𝙜: {}
85 | ┣⊸🗑𝘿𝙚𝙡𝙚𝙩𝙚𝙙 𝙈𝙨𝙜: {}
86 | ┣⊸🪆𝙎𝙠𝙞𝙥𝙥𝙚𝙙 : {}
87 | ┣⊸📊𝙎𝙩𝙖𝙩𝙨 : {}
88 | ┣⊸⏳𝙋𝙧𝙤𝙜𝙧𝙚𝙨𝙨 : {}
89 | ┣⊸𝙀𝙏𝘼 : {}
90 | ┃
91 | ╰─⌊ {} ⌉─❍"""
92 |
93 | DUPLICATE_TEXT = """
94 | ╔════❰ ᴜɴᴇǫᴜɪғʏ sᴛᴀᴛᴜs ❱═❍⊱❁۪۪
95 | ║╭━━━━━━━━━━━━━━━➣
96 | ║┣⪼ ғᴇᴛᴄʜᴇᴅ ғɪʟᴇs: {}
97 | ║┃
98 | ║┣⪼ ᴅᴜᴘʟɪᴄᴀᴛᴇ ᴅᴇʟᴇᴛᴇᴅ: {}
99 | ║╰━━━━━━━━━━━━━━━➣
100 | ╚════❰ {} ❱══❍⊱❁۪۪
101 | """
102 | DOUBLE_CHECK = """DOUBLE CHECKING ⚠️
103 | Before forwarding the messages Click the Yes button only after checking the following
104 |
105 | ★ YOUR BOT: [{botname}](t.me/{botuname})
106 | ★ FROM CHANNEL: `{from_chat}`
107 | ★ TO CHANNEL: `{to_chat}`
108 | ★ SKIP MESSAGES: `{skip}`
109 |
110 | ° [{botname}](t.me/{botuname}) must be admin in **TARGET CHAT** (`{to_chat}`)
111 | ° If the **SOURCE CHAT** is private your userbot must be member or your bot must be admin in there also
112 |
113 | If the above is checked then the yes button can be clicked"""
114 |
--------------------------------------------------------------------------------
/database.py:
--------------------------------------------------------------------------------
1 | from os import environ
2 | from config import Config
3 | import motor.motor_asyncio
4 | from pymongo import MongoClient
5 |
6 | async def mongodb_version():
7 | x = MongoClient(Config.DATABASE_URI)
8 | mongodb_version = x.server_info()['version']
9 | return mongodb_version
10 |
11 | class Database:
12 |
13 | def __init__(self, uri, database_name):
14 | self._client = motor.motor_asyncio.AsyncIOMotorClient(uri)
15 | self.db = self._client[database_name]
16 | self.bot = self.db.bots
17 | self.col = self.db.users
18 | self.nfy = self.db.notify
19 | self.chl = self.db.channels
20 |
21 | def new_user(self, id, name):
22 | return dict(
23 | id = id,
24 | name = name,
25 | ban_status=dict(
26 | is_banned=False,
27 | ban_reason="",
28 | ),
29 | )
30 |
31 | async def add_user(self, id, name):
32 | user = self.new_user(id, name)
33 | await self.col.insert_one(user)
34 |
35 | async def is_user_exist(self, id):
36 | user = await self.col.find_one({'id':int(id)})
37 | return bool(user)
38 |
39 | async def total_users_bots_count(self):
40 | bcount = await self.bot.count_documents({})
41 | count = await self.col.count_documents({})
42 | return count, bcount
43 |
44 | async def total_channels(self):
45 | count = await self.chl.count_documents({})
46 | return count
47 |
48 | async def remove_ban(self, id):
49 | ban_status = dict(
50 | is_banned=False,
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_reason="No Reason"):
56 | ban_status = dict(
57 | is_banned=True,
58 | ban_reason=ban_reason
59 | )
60 | await self.col.update_one({'id': user_id}, {'$set': {'ban_status': ban_status}})
61 |
62 | async def get_ban_status(self, id):
63 | default = dict(
64 | is_banned=False,
65 | ban_reason=''
66 | )
67 | user = await self.col.find_one({'id':int(id)})
68 | if not user:
69 | return default
70 | return user.get('ban_status', default)
71 |
72 | async def get_all_users(self):
73 | return self.col.find({})
74 |
75 | async def delete_user(self, user_id):
76 | await self.col.delete_many({'id': int(user_id)})
77 |
78 | async def get_banned(self):
79 | users = self.col.find({'ban_status.is_banned': True})
80 | b_users = [user['id'] async for user in users]
81 | return b_users
82 |
83 | async def update_configs(self, id, configs):
84 | await self.col.update_one({'id': int(id)}, {'$set': {'configs': configs}})
85 |
86 | async def get_configs(self, id):
87 | default = {
88 | 'caption': None,
89 | 'duplicate': True,
90 | 'forward_tag': False,
91 | 'file_size': 0,
92 | 'size_limit': None,
93 | 'extension': None,
94 | 'keywords': None,
95 | 'protect': None,
96 | 'button': None,
97 | 'db_uri': None,
98 | 'filters': {
99 | 'poll': True,
100 | 'text': True,
101 | 'audio': True,
102 | 'voice': True,
103 | 'video': True,
104 | 'photo': True,
105 | 'document': True,
106 | 'animation': True,
107 | 'sticker': True
108 | }
109 | }
110 | user = await self.col.find_one({'id':int(id)})
111 | if user:
112 | return user.get('configs', default)
113 | return default
114 |
115 | async def add_bot(self, datas):
116 | if not await self.is_bot_exist(datas['user_id']):
117 | await self.bot.insert_one(datas)
118 |
119 | async def remove_bot(self, user_id):
120 | await self.bot.delete_many({'user_id': int(user_id)})
121 |
122 | async def get_bot(self, user_id: int):
123 | bot = await self.bot.find_one({'user_id': user_id})
124 | return bot if bot else None
125 |
126 | async def is_bot_exist(self, user_id):
127 | bot = await self.bot.find_one({'user_id': user_id})
128 | return bool(bot)
129 |
130 | async def in_channel(self, user_id: int, chat_id: int) -> bool:
131 | channel = await self.chl.find_one({"user_id": int(user_id), "chat_id": int(chat_id)})
132 | return bool(channel)
133 |
134 | async def add_channel(self, user_id: int, chat_id: int, title, username):
135 | channel = await self.in_channel(user_id, chat_id)
136 | if channel:
137 | return False
138 | return await self.chl.insert_one({"user_id": user_id, "chat_id": chat_id, "title": title, "username": username})
139 |
140 | async def remove_channel(self, user_id: int, chat_id: int):
141 | channel = await self.in_channel(user_id, chat_id )
142 | if not channel:
143 | return False
144 | return await self.chl.delete_many({"user_id": int(user_id), "chat_id": int(chat_id)})
145 |
146 | async def get_channel_details(self, user_id: int, chat_id: int):
147 | return await self.chl.find_one({"user_id": int(user_id), "chat_id": int(chat_id)})
148 |
149 | async def get_user_channels(self, user_id: int):
150 | channels = self.chl.find({"user_id": int(user_id)})
151 | return [channel async for channel in channels]
152 |
153 | async def get_filters(self, user_id):
154 | filters = []
155 | filter = (await self.get_configs(user_id))['filters']
156 | for k, v in filter.items():
157 | if v == False:
158 | filters.append(str(k))
159 | return filters
160 |
161 | async def add_frwd(self, user_id):
162 | return await self.nfy.insert_one({'user_id': int(user_id)})
163 |
164 | async def rmve_frwd(self, user_id=0, all=False):
165 | data = {} if all else {'user_id': int(user_id)}
166 | return await self.nfy.delete_many(data)
167 |
168 | async def get_all_frwd(self):
169 | return self.nfy.find({})
170 |
171 | db = Database(Config.DATABASE_URI, Config.DATABASE_NAME)
172 |
--------------------------------------------------------------------------------
/plugins/test.py:
--------------------------------------------------------------------------------
1 | import os
2 | import re
3 | import sys
4 | import typing
5 | import asyncio
6 | import logging
7 | from database import db
8 | from config import Config, temp
9 | from pyrogram import Client, filters
10 | from pyrogram.raw.all import layer
11 | from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup, CallbackQuery, Message
12 | from pyrogram.errors.exceptions.bad_request_400 import AccessTokenExpired, AccessTokenInvalid
13 | from pyrogram.errors import FloodWait
14 | from config import Config
15 | from translation import Translation
16 |
17 | from typing import Union, Optional, AsyncGenerator
18 |
19 | logger = logging.getLogger(__name__)
20 | logger.setLevel(logging.INFO)
21 |
22 | BTN_URL_REGEX = re.compile(r"(\[([^\[]+?)]\[buttonurl:/{0,2}(.+?)(:same)?])")
23 | BOT_TOKEN_TEXT = "1) create a bot using @BotFather\n2) Then you will get a message with bot token\n3) Forward that message to me"
24 | SESSION_STRING_SIZE = 351
25 |
26 | async def start_clone_bot(FwdBot, data=None):
27 | await FwdBot.start()
28 | #
29 | async def iter_messages(
30 | self,
31 | chat_id: Union[int, str],
32 | limit: int,
33 | offset: int = 0,
34 | search: str = None,
35 | filter: "types.TypeMessagesFilter" = None,
36 | ) -> Optional[AsyncGenerator["types.Message", None]]:
37 | """Iterate through a chat sequentially.
38 | This convenience method does the same as repeatedly calling :meth:`~pyrogram.Client.get_messages` in a loop, thus saving
39 | you from the hassle of setting up boilerplate code. It is useful for getting the whole chat messages with a
40 | single call.
41 | Parameters:
42 | chat_id (``int`` | ``str``):
43 | Unique identifier (int) or username (str) of the target chat.
44 | For your personal cloud (Saved Messages) you can simply use "me" or "self".
45 | For a contact that exists in your Telegram address book you can use his phone number (str).
46 |
47 | limit (``int``):
48 | Identifier of the last message to be returned.
49 |
50 | offset (``int``, *optional*):
51 | Identifier of the first message to be returned.
52 | Defaults to 0.
53 | Returns:
54 | ``Generator``: A generator yielding :obj:`~pyrogram.types.Message` objects.
55 | Example:
56 | .. code-block:: python
57 | for message in app.iter_messages("pyrogram", 1, 15000):
58 | print(message.text)
59 | """
60 | current = offset
61 | while True:
62 | new_diff = min(200, limit - current)
63 | if new_diff <= 0:
64 | return
65 | messages = await self.get_messages(chat_id, list(range(current, current+new_diff+1)))
66 | for message in messages:
67 | yield message
68 | current += 1
69 | #
70 | FwdBot.iter_messages = iter_messages
71 | return FwdBot
72 |
73 | class CLIENT:
74 | def __init__(self):
75 | self.api_id = Config.API_ID
76 | self.api_hash = Config.API_HASH
77 |
78 | def client(self, data, user=None):
79 | if user == None and data.get('is_bot') == False:
80 | return Client("USERBOT", self.api_id, self.api_hash, session_string=data.get('session'))
81 | elif user == True:
82 | return Client("USERBOT", self.api_id, self.api_hash, session_string=data)
83 | elif user != False:
84 | data = data.get('token')
85 | return Client("BOT", self.api_id, self.api_hash, bot_token=data, in_memory=True)
86 |
87 | async def add_bot(self, bot, message):
88 | user_id = int(message.from_user.id)
89 | msg = await bot.ask(chat_id=user_id, text=BOT_TOKEN_TEXT)
90 | if msg.text=='/cancel':
91 | return await msg.reply('process cancelled !')
92 | elif not msg.forward_date:
93 | return await msg.reply_text("This is not a forward message")
94 | elif str(msg.forward_from.id) != "93372553":
95 | return await msg.reply_text("This message was not forward from bot father")
96 | bot_token = re.findall(r'\d[0-9]{8,10}:[0-9A-Za-z_-]{35}', msg.text, re.IGNORECASE)
97 | bot_token = bot_token[0] if bot_token else None
98 | if not bot_token:
99 | return await msg.reply_text("There is no bot token in that message")
100 | try:
101 | _client = await start_clone_bot(self.client(bot_token, False), True)
102 | except Exception as e:
103 | await msg.reply_text(f"BOT ERROR: `{e}`")
104 | _bot = _client.me
105 | details = {
106 | 'id': _bot.id,
107 | 'is_bot': True,
108 | 'user_id': user_id,
109 | 'name': _bot.first_name,
110 | 'token': bot_token,
111 | 'username': _bot.username
112 | }
113 | await db.add_bot(details)
114 | return True
115 |
116 | async def add_session(self, bot, message):
117 | user_id = int(message.from_user.id)
118 | text = "⚠️ DISCLAIMER ⚠️\n\nyou can use your session for forward message from private chat to another chat.\nPlease add your pyrogram session with your own risk. Their is a chance to ban your account. My developer is not responsible if your account may get banned."
119 | await bot.send_message(user_id, text=text)
120 | msg = await bot.ask(chat_id=user_id, text="send your pyrogram session.\nGet it from trusted sources.\n\n/cancel - cancel the process")
121 | if msg.text=='/cancel':
122 | return await msg.reply('process cancelled !')
123 | elif len(msg.text) < SESSION_STRING_SIZE:
124 | return await msg.reply('invalid session sring')
125 | try:
126 | client = await start_clone_bot(self.client(msg.text, True), True)
127 | except Exception as e:
128 | await msg.reply_text(f"USER BOT ERROR: `{e}`")
129 | user = client.me
130 | details = {
131 | 'id': user.id,
132 | 'is_bot': False,
133 | 'user_id': user_id,
134 | 'name': user.first_name,
135 | 'session': msg.text,
136 | 'username': user.username
137 | }
138 | await db.add_bot(details)
139 | return True
140 |
141 | @Client.on_message(filters.private & filters.command('reset'))
142 | async def forward_tag(bot, m):
143 | default = await db.get_configs("01")
144 | temp.CONFIGS[m.from_user.id] = default
145 | await db.update_configs(m.from_user.id, default)
146 | await m.reply("successfully settings reseted ✔️")
147 |
148 | @Client.on_message(filters.command('resetall') & filters.user(Config.BOT_OWNER_ID))
149 | async def resetall(bot, message):
150 | users = await db.get_all_users()
151 | sts = await message.reply("**processing**")
152 | TEXT = "total: {}\nsuccess: {}\nfailed: {}\nexcept: {}"
153 | total = success = failed = already = 0
154 | ERRORS = []
155 | async for user in users:
156 | user_id = user['id']
157 | default = await get_configs(user_id)
158 | default['db_uri'] = None
159 | total += 1
160 | if total %10 == 0:
161 | await sts.edit(TEXT.format(total, success, failed, already))
162 | try:
163 | await db.update_configs(user_id, default)
164 | success += 1
165 | except Exception as e:
166 | ERRORS.append(e)
167 | failed += 1
168 | if ERRORS:
169 | await message.reply(ERRORS[:100])
170 | await sts.edit("completed\n" + TEXT.format(total, success, failed, already))
171 |
172 | async def get_configs(user_id):
173 | #configs = temp.CONFIGS.get(user_id)
174 | #if not configs:
175 | configs = await db.get_configs(user_id)
176 | #temp.CONFIGS[user_id] = configs
177 | return configs
178 |
179 | async def update_configs(user_id, key, value):
180 | current = await db.get_configs(user_id)
181 | if key in ['caption', 'duplicate', 'db_uri', 'forward_tag', 'protect', 'file_size', 'size_limit', 'extension', 'keywords', 'button']:
182 | current[key] = value
183 | else:
184 | current['filters'][key] = value
185 | # temp.CONFIGS[user_id] = value
186 | await db.update_configs(user_id, current)
187 |
188 | def parse_buttons(text, markup=True):
189 | buttons = []
190 | for match in BTN_URL_REGEX.finditer(text):
191 | n_escapes = 0
192 | to_check = match.start(1) - 1
193 | while to_check > 0 and text[to_check] == "\\":
194 | n_escapes += 1
195 | to_check -= 1
196 |
197 | if n_escapes % 2 == 0:
198 | if bool(match.group(4)) and buttons:
199 | buttons[-1].append(InlineKeyboardButton(
200 | text=match.group(2),
201 | url=match.group(3).replace(" ", "")))
202 | else:
203 | buttons.append([InlineKeyboardButton(
204 | text=match.group(2),
205 | url=match.group(3).replace(" ", ""))])
206 | if markup and buttons:
207 | buttons = InlineKeyboardMarkup(buttons)
208 | return buttons if buttons else None
209 |
--------------------------------------------------------------------------------
/plugins/regix.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 | import math
4 | import time
5 | import asyncio
6 | import logging
7 | from .utils import STS
8 | from database import db
9 | from .test import CLIENT , start_clone_bot
10 | from config import Config, temp
11 | from translation import Translation
12 | from pyrogram import Client, filters
13 | #from pyropatch.utils import unpack_new_file_id
14 | from pyrogram.errors import FloodWait, MessageNotModified, RPCError
15 | from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup, CallbackQuery, Message
16 |
17 | CLIENT = CLIENT()
18 | logger = logging.getLogger(__name__)
19 | logger.setLevel(logging.INFO)
20 | TEXT = Translation.TEXT
21 |
22 | @Client.on_callback_query(filters.regex(r'^start_public'))
23 | async def pub_(bot, message):
24 | user = message.from_user.id
25 | temp.CANCEL[user] = False
26 | frwd_id = message.data.split("_")[2]
27 | if temp.lock.get(user) and str(temp.lock.get(user))=="True":
28 | return await message.answer("please wait until previous task complete", show_alert=True)
29 | sts = STS(frwd_id)
30 | if not sts.verify():
31 | await message.answer("your are clicking on my old button", show_alert=True)
32 | return await message.message.delete()
33 | i = sts.get(full=True)
34 | if i.TO in temp.IS_FRWD_CHAT:
35 | return await message.answer("In Target chat a task is progressing. please wait until task complete", show_alert=True)
36 | m = await msg_edit(message.message, "verifying your data's, please wait.")
37 | _bot, caption, forward_tag, data, protect, button = await sts.get_data(user)
38 | if not _bot:
39 | return await msg_edit(m, "You didn't added any bot. Please add a bot using /settings !", wait=True)
40 | try:
41 | client = await start_clone_bot(CLIENT.client(_bot))
42 | except Exception as e:
43 | return await m.edit(e)
44 | await msg_edit(m, "processing..")
45 | try:
46 | await client.get_messages(sts.get("FROM"), sts.get("limit"))
47 | except:
48 | await msg_edit(m, f"**Source chat may be a private channel / group. Use userbot (user must be member over there) or if Make Your [Bot](t.me/{_bot['username']}) an admin over there**", retry_btn(frwd_id), True)
49 | return await stop(client, user)
50 | try:
51 | k = await client.send_message(i.TO, "Testing")
52 | await k.delete()
53 | except:
54 | await msg_edit(m, f"**Please Make Your [UserBot / Bot](t.me/{_bot['username']}) Admin In Target Channel With Full Permissions**", retry_btn(frwd_id), True)
55 | return await stop(client, user)
56 | temp.forwardings += 1
57 | await db.add_frwd(user)
58 | await send(client, user, "ғᴏʀᴡᴀʀᴅɪɴɢ sᴛᴀʀᴛᴇᴅ Dev Gagan")
59 | sts.add(time=True)
60 | sleep = 1 if _bot['is_bot'] else 10
61 | await msg_edit(m, "Processing...")
62 | temp.IS_FRWD_CHAT.append(i.TO)
63 | temp.lock[user] = locked = True
64 | if locked:
65 | try:
66 | MSG = []
67 | pling=0
68 | await edit(m, 'Progressing', 10, sts)
69 | print(f"Starting Forwarding Process... From :{sts.get('FROM')} To: {sts.get('TO')} Totel: {sts.get('limit')} stats : {sts.get('skip')})")
70 | async for message in client.iter_messages(
71 | client,
72 | chat_id=sts.get('FROM'),
73 | limit=int(sts.get('limit')),
74 | offset=int(sts.get('skip')) if sts.get('skip') else 0
75 | ):
76 | if await is_cancelled(client, user, m, sts):
77 | return
78 | if pling %20 == 0:
79 | await edit(m, 'Progressing', 10, sts)
80 | pling += 1
81 | sts.add('fetched')
82 | if message == "DUPLICATE":
83 | sts.add('duplicate')
84 | continue
85 | elif message == "FILTERED":
86 | sts.add('filtered')
87 | continue
88 | if message.empty or message.service:
89 | sts.add('deleted')
90 | continue
91 | if forward_tag:
92 | MSG.append(message.id)
93 | notcompleted = len(MSG)
94 | completed = sts.get('total') - sts.get('fetched')
95 | if ( notcompleted >= 100
96 | or completed <= 100):
97 | await forward(client, MSG, m, sts, protect)
98 | sts.add('total_files', notcompleted)
99 | await asyncio.sleep(10)
100 | MSG = []
101 | else:
102 | new_caption = custom_caption(message, caption)
103 | details = {"msg_id": message.id, "media": media(message), "caption": new_caption, 'button': button, "protect": protect}
104 | await copy(client, details, m, sts)
105 | sts.add('total_files')
106 | await asyncio.sleep(sleep)
107 | except Exception as e:
108 | await msg_edit(m, f'ERROR:\n{e}', wait=True)
109 | temp.IS_FRWD_CHAT.remove(sts.TO)
110 | return await stop(client, user)
111 | temp.IS_FRWD_CHAT.remove(sts.TO)
112 | await send(client, user, "🎉 ғᴏʀᴡᴀᴅɪɴɢ ᴄᴏᴍᴘʟᴇᴛᴇᴅ 🥀 SUPPORT🥀")
113 | await edit(m, 'Completed', "completed", sts)
114 | await stop(client, user)
115 |
116 | async def copy(bot, msg, m, sts):
117 | try:
118 | if msg.get("media") and msg.get("caption"):
119 | await bot.send_cached_media(
120 | chat_id=sts.get('TO'),
121 | file_id=msg.get("media"),
122 | caption=msg.get("caption"),
123 | reply_markup=msg.get('button'),
124 | protect_content=msg.get("protect"))
125 | else:
126 | await bot.copy_message(
127 | chat_id=sts.get('TO'),
128 | from_chat_id=sts.get('FROM'),
129 | caption=msg.get("caption"),
130 | message_id=msg.get("msg_id"),
131 | reply_markup=msg.get('button'),
132 | protect_content=msg.get("protect"))
133 | except FloodWait as e:
134 | await edit(m, 'Progressing', e.value, sts)
135 | await asyncio.sleep(e.value)
136 | await edit(m, 'Progressing', 10, sts)
137 | await copy(bot, msg, m, sts)
138 | except Exception as e:
139 | print(e)
140 | sts.add('deleted')
141 |
142 | async def forward(bot, msg, m, sts, protect):
143 | try:
144 | await bot.forward_messages(
145 | chat_id=sts.get('TO'),
146 | from_chat_id=sts.get('FROM'),
147 | protect_content=protect,
148 | message_ids=msg)
149 | except FloodWait as e:
150 | await edit(m, 'Progressing', e.value, sts)
151 | await asyncio.sleep(e.value)
152 | await edit(m, 'Progressing', 10, sts)
153 | await forward(bot, msg, m, sts, protect)
154 |
155 | PROGRESS = """
156 | 📈 Percetage: {0} %
157 |
158 | ♻️ Feched: {1}
159 |
160 | ♻️ Fowarded: {2}
161 |
162 | ♻️ Remaining: {3}
163 |
164 | ♻️ Stataus: {4}
165 |
166 | ⏳️ ETA: {5}
167 | """
168 |
169 | async def msg_edit(msg, text, button=None, wait=None):
170 | try:
171 | return await msg.edit(text, reply_markup=button)
172 | except MessageNotModified:
173 | pass
174 | except FloodWait as e:
175 | if wait:
176 | await asyncio.sleep(e.value)
177 | return await msg_edit(msg, text, button, wait)
178 |
179 | async def edit(msg, title, status, sts):
180 | i = sts.get(full=True)
181 | status = 'Forwarding' if status == 10 else f"Sleeping {status} s" if str(status).isnumeric() else status
182 | percentage = "{:.0f}".format(float(i.fetched)*100/float(i.total))
183 |
184 | now = time.time()
185 | diff = int(now - i.start)
186 | speed = sts.divide(i.fetched, diff)
187 | elapsed_time = round(diff) * 1000
188 | time_to_completion = round(sts.divide(i.total - i.fetched, int(speed))) * 1000
189 | estimated_total_time = elapsed_time + time_to_completion
190 | progress = "◉{0}{1}".format(
191 | ''.join(["◉" for i in range(math.floor(int(percentage) / 10))]),
192 | ''.join(["◎" for i in range(10 - math.floor(int(percentage) / 10))]))
193 | button = [[InlineKeyboardButton(title, f'fwrdstatus#{status}#{estimated_total_time}#{percentage}#{i.id}')]]
194 | estimated_total_time = TimeFormatter(milliseconds=estimated_total_time)
195 | estimated_total_time = estimated_total_time if estimated_total_time != '' else '0 s'
196 |
197 | text = TEXT.format(i.fetched, i.total_files, i.duplicate, i.deleted, i.skip, status, percentage, estimated_total_time, progress)
198 | if status in ["cancelled", "completed"]:
199 | button.append(
200 | [InlineKeyboardButton('Support', url='https://t.me/dev_gagan'),
201 | InlineKeyboardButton('Updates', url='https://t.me/dev_gagan')]
202 | )
203 | else:
204 | button.append([InlineKeyboardButton('• ᴄᴀɴᴄᴇʟ', 'terminate_frwd')])
205 | await msg_edit(msg, text, InlineKeyboardMarkup(button))
206 |
207 | async def is_cancelled(client, user, msg, sts):
208 | if temp.CANCEL.get(user)==True:
209 | temp.IS_FRWD_CHAT.remove(sts.TO)
210 | await edit(msg, "Cancelled", "completed", sts)
211 | await send(client, user, "❌ Forwarding Process Cancelled")
212 | await stop(client, user)
213 | return True
214 | return False
215 |
216 | async def stop(client, user):
217 | try:
218 | await client.stop()
219 | except:
220 | pass
221 | await db.rmve_frwd(user)
222 | temp.forwardings -= 1
223 | temp.lock[user] = False
224 |
225 | async def send(bot, user, text):
226 | try:
227 | await bot.send_message(user, text=text)
228 | except:
229 | pass
230 |
231 | def custom_caption(msg, caption):
232 | if msg.media:
233 | if (msg.video or msg.document or msg.audio or msg.photo):
234 | media = getattr(msg, msg.media.value, None)
235 | if media:
236 | file_name = getattr(media, 'file_name', '')
237 | file_size = getattr(media, 'file_size', '')
238 | fcaption = getattr(msg, 'caption', '')
239 | if fcaption:
240 | fcaption = fcaption.html
241 | if caption:
242 | return caption.format(filename=file_name, size=get_size(file_size), caption=fcaption)
243 | return fcaption
244 | return None
245 |
246 | def get_size(size):
247 | units = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB"]
248 | size = float(size)
249 | i = 0
250 | while size >= 1024.0 and i < len(units):
251 | i += 1
252 | size /= 1024.0
253 | return "%.2f %s" % (size, units[i])
254 |
255 | def media(msg):
256 | if msg.media:
257 | media = getattr(msg, msg.media.value, None)
258 | if media:
259 | return getattr(media, 'file_id', None)
260 | return None
261 |
262 | def TimeFormatter(milliseconds: int) -> str:
263 | seconds, milliseconds = divmod(int(milliseconds), 1000)
264 | minutes, seconds = divmod(seconds, 60)
265 | hours, minutes = divmod(minutes, 60)
266 | days, hours = divmod(hours, 24)
267 | tmp = ((str(days) + "d, ") if days else "") + \
268 | ((str(hours) + "h, ") if hours else "") + \
269 | ((str(minutes) + "m, ") if minutes else "") + \
270 | ((str(seconds) + "s, ") if seconds else "") + \
271 | ((str(milliseconds) + "ms, ") if milliseconds else "")
272 | return tmp[:-2]
273 |
274 | def retry_btn(id):
275 | return InlineKeyboardMarkup([[InlineKeyboardButton('♻️ RETRY ♻️', f"start_public_{id}")]])
276 |
277 | @Client.on_callback_query(filters.regex(r'^terminate_frwd$'))
278 | async def terminate_frwding(bot, m):
279 | user_id = m.from_user.id
280 | temp.lock[user_id] = False
281 | temp.CANCEL[user_id] = True
282 | await m.answer("Forwarding cancelled !", show_alert=True)
283 |
284 | @Client.on_callback_query(filters.regex(r'^fwrdstatus'))
285 | async def status_msg(bot, msg):
286 | _, status, est_time, percentage, frwd_id = msg.data.split("#")
287 | sts = STS(frwd_id)
288 | if not sts.verify():
289 | fetched, forwarded, remaining = 0
290 | else:
291 | fetched, forwarded = sts.get('fetched'), sts.get('total_files')
292 | remaining = fetched - forwarded
293 | est_time = TimeFormatter(milliseconds=est_time)
294 | est_time = est_time if (est_time != '' or status not in ['completed', 'cancelled']) else '0 s'
295 | return await msg.answer(PROGRESS.format(percentage, fetched, forwarded, remaining, status, est_time), show_alert=True)
296 |
297 | @Client.on_callback_query(filters.regex(r'^close_btn$'))
298 | async def close(bot, update):
299 | await update.answer()
300 | await update.message.delete()
301 |
--------------------------------------------------------------------------------
/plugins/settings.py:
--------------------------------------------------------------------------------
1 | import asyncio
2 | from database import db
3 | from translation import Translation
4 | from pyrogram import Client, filters
5 | from .test import get_configs, update_configs, CLIENT, parse_buttons
6 | from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup
7 |
8 | CLIENT = CLIENT()
9 |
10 | @Client.on_message(filters.command('settings'))
11 | async def settings(client, message):
12 | await message.delete()
13 | await message.reply_text(
14 | "change your settings as your wish",
15 | reply_markup=main_buttons()
16 | )
17 |
18 | @Client.on_callback_query(filters.regex(r'^settings'))
19 | async def settings_query(bot, query):
20 | user_id = query.from_user.id
21 | i, type = query.data.split("#")
22 | buttons = [[InlineKeyboardButton('↩ Back', callback_data="settings#main")]]
23 |
24 | if type=="main":
25 | await query.message.edit_text(
26 | "change your settings as your wish",
27 | reply_markup=main_buttons())
28 |
29 | elif type=="bots":
30 | buttons = []
31 | _bot = await db.get_bot(user_id)
32 | if _bot is not None:
33 | buttons.append([InlineKeyboardButton(_bot['name'],
34 | callback_data=f"settings#editbot")])
35 | else:
36 | buttons.append([InlineKeyboardButton('✚ Add bot ✚',
37 | callback_data="settings#addbot")])
38 | buttons.append([InlineKeyboardButton('✚ Add User bot ✚',
39 | callback_data="settings#adduserbot")])
40 | buttons.append([InlineKeyboardButton('↩ Back',
41 | callback_data="settings#main")])
42 | await query.message.edit_text(
43 | "My Bots\n\nYou can manage your bots in here",
44 | reply_markup=InlineKeyboardMarkup(buttons))
45 |
46 | elif type=="addbot":
47 | await query.message.delete()
48 | bot = await CLIENT.add_bot(bot, query)
49 | if bot != True: return
50 | await query.message.reply_text(
51 | "bot token successfully added to db",
52 | reply_markup=InlineKeyboardMarkup(buttons))
53 |
54 | elif type=="adduserbot":
55 | await query.message.delete()
56 | user = await CLIENT.add_session(bot, query)
57 | if user != True: return
58 | await query.message.reply_text(
59 | "session successfully added to db",
60 | reply_markup=InlineKeyboardMarkup(buttons))
61 |
62 | elif type=="channels":
63 | buttons = []
64 | channels = await db.get_user_channels(user_id)
65 | for channel in channels:
66 | buttons.append([InlineKeyboardButton(f"{channel['title']}",
67 | callback_data=f"settings#editchannels_{channel['chat_id']}")])
68 | buttons.append([InlineKeyboardButton('✚ Add Channel ✚',
69 | callback_data="settings#addchannel")])
70 | buttons.append([InlineKeyboardButton('↩ Back',
71 | callback_data="settings#main")])
72 | await query.message.edit_text(
73 | "My Channels\n\nyou can manage your target chats in here",
74 | reply_markup=InlineKeyboardMarkup(buttons))
75 |
76 | elif type=="addchannel":
77 | await query.message.delete()
78 | try:
79 | text = await bot.send_message(user_id, "❪ SET TARGET CHAT ❫\n\nForward a message from Your target chat\n/cancel - cancel this process")
80 | chat_ids = await bot.listen(chat_id=user_id, timeout=300)
81 | if chat_ids.text=="/cancel":
82 | await chat_ids.delete()
83 | return await text.edit_text(
84 | "process canceled",
85 | reply_markup=InlineKeyboardMarkup(buttons))
86 | elif not chat_ids.forward_date:
87 | await chat_ids.delete()
88 | return await text.edit_text("**This is not a forward message**")
89 | else:
90 | chat_id = chat_ids.forward_from_chat.id
91 | title = chat_ids.forward_from_chat.title
92 | username = chat_ids.forward_from_chat.username
93 | username = "@" + username if username else "private"
94 | chat = await db.add_channel(user_id, chat_id, title, username)
95 | await chat_ids.delete()
96 | await text.edit_text(
97 | "Successfully updated" if chat else "This channel already added",
98 | reply_markup=InlineKeyboardMarkup(buttons))
99 | except asyncio.exceptions.TimeoutError:
100 | await text.edit_text('Process has been automatically cancelled', reply_markup=InlineKeyboardMarkup(buttons))
101 |
102 | elif type=="editbot":
103 | bot = await db.get_bot(user_id)
104 | TEXT = Translation.BOT_DETAILS if bot['is_bot'] else Translation.USER_DETAILS
105 | buttons = [[InlineKeyboardButton('❌ Remove ❌', callback_data=f"settings#removebot")
106 | ],
107 | [InlineKeyboardButton('↩ Back', callback_data="settings#bots")]]
108 | await query.message.edit_text(
109 | TEXT.format(bot['name'], bot['id'], bot['username']),
110 | reply_markup=InlineKeyboardMarkup(buttons))
111 |
112 | elif type=="removebot":
113 | await db.remove_bot(user_id)
114 | await query.message.edit_text(
115 | "successfully updated",
116 | reply_markup=InlineKeyboardMarkup(buttons))
117 |
118 | elif type.startswith("editchannels"):
119 | chat_id = type.split('_')[1]
120 | chat = await db.get_channel_details(user_id, chat_id)
121 | buttons = [[InlineKeyboardButton('❌ Remove ❌', callback_data=f"settings#removechannel_{chat_id}")
122 | ],
123 | [InlineKeyboardButton('↩ Back', callback_data="settings#channels")]]
124 | await query.message.edit_text(
125 | f"📄 CHANNEL DETAILS\n\n- TITLE: {chat['title']}\n- CHANNEL ID: {chat['chat_id']}\n- USERNAME: {chat['username']}",
126 | reply_markup=InlineKeyboardMarkup(buttons))
127 |
128 | elif type.startswith("removechannel"):
129 | chat_id = type.split('_')[1]
130 | await db.remove_channel(user_id, chat_id)
131 | await query.message.edit_text(
132 | "successfully updated",
133 | reply_markup=InlineKeyboardMarkup(buttons))
134 |
135 | elif type=="caption":
136 | buttons = []
137 | data = await get_configs(user_id)
138 | caption = data['caption']
139 | if caption is None:
140 | buttons.append([InlineKeyboardButton('✚ Add Caption ✚',
141 | callback_data="settings#addcaption")])
142 | else:
143 | buttons.append([InlineKeyboardButton('See Caption',
144 | callback_data="settings#seecaption")])
145 | buttons[-1].append(InlineKeyboardButton('🗑️ Delete Caption',
146 | callback_data="settings#deletecaption"))
147 | buttons.append([InlineKeyboardButton('↩ Back',
148 | callback_data="settings#main")])
149 | await query.message.edit_text(
150 | "CUSTOM CAPTION\n\nYou can set a custom caption to videos and documents. Normaly use its default caption\n\nAVAILABLE FILLINGS:\n- {filename} : Filename\n- {size} : File size\n- {caption} : default caption",
151 | reply_markup=InlineKeyboardMarkup(buttons))
152 |
153 | elif type=="seecaption":
154 | data = await get_configs(user_id)
155 | buttons = [[InlineKeyboardButton('🖋️ Edit Caption',
156 | callback_data="settings#addcaption")
157 | ],[
158 | InlineKeyboardButton('↩ Back',
159 | callback_data="settings#caption")]]
160 | await query.message.edit_text(
161 | f"YOUR CUSTOM CAPTION\n\n{data['caption']}",
162 | reply_markup=InlineKeyboardMarkup(buttons))
163 |
164 | elif type=="deletecaption":
165 | await update_configs(user_id, 'caption', None)
166 | await query.message.edit_text(
167 | "successfully updated",
168 | reply_markup=InlineKeyboardMarkup(buttons))
169 |
170 | elif type=="addcaption":
171 | await query.message.delete()
172 | try:
173 | text = await bot.send_message(query.message.chat.id, "Send your custom caption\n/cancel - cancel this process")
174 | caption = await bot.listen(chat_id=user_id, timeout=300)
175 | if caption.text=="/cancel":
176 | await caption.delete()
177 | return await text.edit_text(
178 | "process canceled !",
179 | reply_markup=InlineKeyboardMarkup(buttons))
180 | try:
181 | caption.text.format(filename='', size='', caption='')
182 | except KeyError as e:
183 | await caption.delete()
184 | return await text.edit_text(
185 | f"wrong filling {e} used in your caption. change it",
186 | reply_markup=InlineKeyboardMarkup(buttons))
187 | await update_configs(user_id, 'caption', caption.text)
188 | await caption.delete()
189 | await text.edit_text(
190 | "successfully updated",
191 | reply_markup=InlineKeyboardMarkup(buttons))
192 | except asyncio.exceptions.TimeoutError:
193 | await text.edit_text('Process has been automatically cancelled', reply_markup=InlineKeyboardMarkup(buttons))
194 |
195 | elif type=="button":
196 | buttons = []
197 | button = (await get_configs(user_id))['button']
198 | if button is None:
199 | buttons.append([InlineKeyboardButton('✚ Add Button ✚',
200 | callback_data="settings#addbutton")])
201 | else:
202 | buttons.append([InlineKeyboardButton('👀 See Button',
203 | callback_data="settings#seebutton")])
204 | buttons[-1].append(InlineKeyboardButton('🗑️ Remove Button ',
205 | callback_data="settings#deletebutton"))
206 | buttons.append([InlineKeyboardButton('↩ Back',
207 | callback_data="settings#main")])
208 | await query.message.edit_text(
209 | "CUSTOM BUTTON\n\nYou can set a inline button to messages.\n\nFORMAT:\n`[Forward bot][buttonurl:https://t.me/devgaganbot]`\n",
210 | reply_markup=InlineKeyboardMarkup(buttons))
211 |
212 | elif type=="addbutton":
213 | await query.message.delete()
214 | try:
215 | txt = await bot.send_message(user_id, text="**Send your custom button.\n\nFORMAT:**\n`[forward bot][buttonurl:https://t.me/devgaganbot]`\n")
216 | ask = await bot.listen(chat_id=user_id, timeout=300)
217 | button = parse_buttons(ask.text.html)
218 | if not button:
219 | await ask.delete()
220 | return await txt.edit_text("**INVALID BUTTON**")
221 | await update_configs(user_id, 'button', ask.text.html)
222 | await ask.delete()
223 | await txt.edit_text("**Successfully button added**",
224 | reply_markup=InlineKeyboardMarkup(buttons))
225 | except asyncio.exceptions.TimeoutError:
226 | await txt.edit_text('Process has been automatically cancelled', reply_markup=InlineKeyboardMarkup(buttons))
227 |
228 | elif type=="seebutton":
229 | button = (await get_configs(user_id))['button']
230 | button = parse_buttons(button, markup=False)
231 | button.append([InlineKeyboardButton("↩ Back", "settings#button")])
232 | await query.message.edit_text(
233 | "**YOUR CUSTOM BUTTON**",
234 | reply_markup=InlineKeyboardMarkup(button))
235 |
236 | elif type=="deletebutton":
237 | await update_configs(user_id, 'button', None)
238 | await query.message.edit_text(
239 | "**Successfully button deleted**",
240 | reply_markup=InlineKeyboardMarkup(buttons))
241 |
242 | elif type=="database":
243 | buttons = []
244 | db_uri = (await get_configs(user_id))['db_uri']
245 | if db_uri is None:
246 | buttons.append([InlineKeyboardButton('✚ Add Url ✚',
247 | callback_data="settings#addurl")])
248 | else:
249 | buttons.append([InlineKeyboardButton('👀 See Url',
250 | callback_data="settings#seeurl")])
251 | buttons[-1].append(InlineKeyboardButton('🗑️ Remove Url ',
252 | callback_data="settings#deleteurl"))
253 | buttons.append([InlineKeyboardButton('↩ Back',
254 | callback_data="settings#main")])
255 | await query.message.edit_text(
256 | "DATABASE\n\nDatabase is required for store your duplicate messages permenant. other wise stored duplicate media may be disappeared when after bot restart.",
257 | reply_markup=InlineKeyboardMarkup(buttons))
258 |
259 | elif type=="addurl":
260 | await query.message.delete()
261 | uri = await bot.ask(user_id, "please send your mongodb url.\n\nget your Mongodb url from [here](https://mongodb.com)", disable_web_page_preview=True)
262 | if uri.text=="/cancel":
263 | return await uri.reply_text(
264 | "process canceled !",
265 | reply_markup=InlineKeyboardMarkup(buttons))
266 | if not uri.text.startswith("mongodb+srv://") and not uri.text.endswith("majority"):
267 | return await uri.reply("Invalid Mongodb Url",
268 | reply_markup=InlineKeyboardMarkup(buttons))
269 | await update_configs(user_id, 'db_uri', uri.text)
270 | await uri.reply("**Successfully database url added**",
271 | reply_markup=InlineKeyboardMarkup(buttons))
272 |
273 | elif type=="seeurl":
274 | db_uri = (await get_configs(user_id))['db_uri']
275 | await query.answer(f"DATABASE URL: {db_uri}", show_alert=True)
276 |
277 | elif type=="deleteurl":
278 | await update_configs(user_id, 'db_uri', None)
279 | await query.message.edit_text(
280 | "**Successfully your database url deleted**",
281 | reply_markup=InlineKeyboardMarkup(buttons))
282 |
283 | elif type=="filters":
284 | await query.message.edit_text(
285 | "💠 CUSTOM FILTERS 💠\n\n**configure the type of messages which you want forward**",
286 | reply_markup=await filters_buttons(user_id))
287 |
288 | elif type=="nextfilters":
289 | await query.edit_message_reply_markup(
290 | reply_markup=await next_filters_buttons(user_id))
291 |
292 | elif type.startswith("updatefilter"):
293 | i, key, value = type.split('-')
294 | if value=="True":
295 | await update_configs(user_id, key, False)
296 | else:
297 | await update_configs(user_id, key, True)
298 | if key in ['poll', 'protect']:
299 | return await query.edit_message_reply_markup(
300 | reply_markup=await next_filters_buttons(user_id))
301 | await query.edit_message_reply_markup(
302 | reply_markup=await filters_buttons(user_id))
303 |
304 | elif type.startswith("file_size"):
305 | settings = await get_configs(user_id)
306 | size = settings.get('file_size', 0)
307 | i, limit = size_limit(settings['size_limit'])
308 | await query.message.edit_text(
309 | f'SIZE LIMIT\n\nyou can set file size limit to forward\n\nStatus: files with {limit} `{size} MB` will forward',
310 | reply_markup=size_button(size))
311 |
312 | elif type.startswith("update_size"):
313 | size = int(query.data.split('-')[1])
314 | if 0 < size > 2000:
315 | return await query.answer("size limit exceeded", show_alert=True)
316 | await update_configs(user_id, 'file_size', size)
317 | i, limit = size_limit((await get_configs(user_id))['size_limit'])
318 | await query.message.edit_text(
319 | f'SIZE LIMIT\n\nyou can set file size limit to forward\n\nStatus: files with {limit} `{size} MB` will forward',
320 | reply_markup=size_button(size))
321 |
322 | elif type.startswith('update_limit'):
323 | i, limit, size = type.split('-')
324 | limit, sts = size_limit(limit)
325 | await update_configs(user_id, 'size_limit', limit)
326 | await query.message.edit_text(
327 | f'SIZE LIMIT\n\nyou can set file size limit to forward\n\nStatus: files with {sts} `{size} MB` will forward',
328 | reply_markup=size_button(int(size)))
329 |
330 | elif type == "add_extension":
331 | await query.message.delete()
332 | ext = await bot.ask(user_id, text="**please send your extensions (seperete by space)**")
333 | if ext.text == '/cancel':
334 | return await ext.reply_text(
335 | "process canceled",
336 | reply_markup=InlineKeyboardMarkup(buttons))
337 | extensions = ext.text.split(" ")
338 | extension = (await get_configs(user_id))['extension']
339 | if extension:
340 | for extn in extensions:
341 | extension.append(extn)
342 | else:
343 | extension = extensions
344 | await update_configs(user_id, 'extension', extension)
345 | await ext.reply_text(
346 | f"**successfully updated**",
347 | reply_markup=InlineKeyboardMarkup(buttons))
348 |
349 | elif type == "get_extension":
350 | extensions = (await get_configs(user_id))['extension']
351 | btn = extract_btn(extensions)
352 | btn.append([InlineKeyboardButton('✚ ADD ✚', 'settings#add_extension')])
353 | btn.append([InlineKeyboardButton('Remove all', 'settings#rmve_all_extension')])
354 | btn.append([InlineKeyboardButton('↩ Back', 'settings#main')])
355 | await query.message.edit_text(
356 | text='EXTENSIONS\n\n**Files with these extiontions will not forward**',
357 | reply_markup=InlineKeyboardMarkup(btn))
358 |
359 | elif type == "rmve_all_extension":
360 | await update_configs(user_id, 'extension', None)
361 | await query.message.edit_text(text="**successfully deleted**",
362 | reply_markup=InlineKeyboardMarkup(buttons))
363 | elif type == "add_keyword":
364 | await query.message.delete()
365 | ask = await bot.ask(user_id, text="**please send the keywords (seperete by space)**")
366 | if ask.text == '/cancel':
367 | return await ask.reply_text(
368 | "process canceled",
369 | reply_markup=InlineKeyboardMarkup(buttons))
370 | keywords = ask.text.split(" ")
371 | keyword = (await get_configs(user_id))['keywords']
372 | if keyword:
373 | for word in keywords:
374 | keyword.append(word)
375 | else:
376 | keyword = keywords
377 | await update_configs(user_id, 'keywords', keyword)
378 | await ask.reply_text(
379 | f"**successfully updated**",
380 | reply_markup=InlineKeyboardMarkup(buttons))
381 |
382 | elif type == "get_keyword":
383 | keywords = (await get_configs(user_id))['keywords']
384 | btn = extract_btn(keywords)
385 | btn.append([InlineKeyboardButton('✚ ADD ✚', 'settings#add_keyword')])
386 | btn.append([InlineKeyboardButton('Remove all', 'settings#rmve_all_keyword')])
387 | btn.append([InlineKeyboardButton('↩ Back', 'settings#main')])
388 | await query.message.edit_text(
389 | text='KEYWORDS\n\n**File with these keywords in file name will forwad**',
390 | reply_markup=InlineKeyboardMarkup(btn))
391 |
392 | elif type == "rmve_all_keyword":
393 | await update_configs(user_id, 'keywords', None)
394 | await query.message.edit_text(text="**successfully deleted**",
395 | reply_markup=InlineKeyboardMarkup(buttons))
396 | elif type.startswith("alert"):
397 | alert = type.split('_')[1]
398 | await query.answer(alert, show_alert=True)
399 |
400 | def main_buttons():
401 | buttons = [[
402 | InlineKeyboardButton('🤖 Bᴏᴛs',
403 | callback_data=f'settings#bots'),
404 | InlineKeyboardButton('🏷 Cʜᴀɴɴᴇʟs',
405 | callback_data=f'settings#channels')
406 | ],[
407 | InlineKeyboardButton('🖋️ Cᴀᴘᴛɪᴏɴ',
408 | callback_data=f'settings#caption'),
409 | InlineKeyboardButton('🗃 MᴏɴɢᴏDB',
410 | callback_data=f'settings#database')
411 | ],[
412 | InlineKeyboardButton('🕵♀ Fɪʟᴛᴇʀs 🕵♀',
413 | callback_data=f'settings#filters'),
414 | InlineKeyboardButton('⏹ Bᴜᴛᴛᴏɴ',
415 | callback_data=f'settings#button')
416 | ],[
417 | InlineKeyboardButton('Exᴛʀᴀ Sᴇᴛᴛɪɴɢs 🧪',
418 | callback_data='settings#nextfilters')
419 | ],[
420 | InlineKeyboardButton('⫷ Bᴀᴄᴋ', callback_data='back')
421 | ]]
422 | return InlineKeyboardMarkup(buttons)
423 |
424 | def size_limit(limit):
425 | if str(limit) == "None":
426 | return None, ""
427 | elif str(limit) == "True":
428 | return True, "more than"
429 | else:
430 | return False, "less than"
431 |
432 | def extract_btn(datas):
433 | i = 0
434 | btn = []
435 | if datas:
436 | for data in datas:
437 | if i >= 5:
438 | i = 0
439 | if i == 0:
440 | btn.append([InlineKeyboardButton(data, f'settings#alert_{data}')])
441 | i += 1
442 | continue
443 | elif i > 0:
444 | btn[-1].append(InlineKeyboardButton(data, f'settings#alert_{data}'))
445 | i += 1
446 | return btn
447 |
448 | def size_button(size):
449 | buttons = [[
450 | InlineKeyboardButton('+',
451 | callback_data=f'settings#update_limit-True-{size}'),
452 | InlineKeyboardButton('=',
453 | callback_data=f'settings#update_limit-None-{size}'),
454 | InlineKeyboardButton('-',
455 | callback_data=f'settings#update_limit-False-{size}')
456 | ],[
457 | InlineKeyboardButton('+1',
458 | callback_data=f'settings#update_size-{size + 1}'),
459 | InlineKeyboardButton('-1',
460 | callback_data=f'settings#update_size_-{size - 1}')
461 | ],[
462 | InlineKeyboardButton('+5',
463 | callback_data=f'settings#update_size-{size + 5}'),
464 | InlineKeyboardButton('-5',
465 | callback_data=f'settings#update_size_-{size - 5}')
466 | ],[
467 | InlineKeyboardButton('+10',
468 | callback_data=f'settings#update_size-{size + 10}'),
469 | InlineKeyboardButton('-10',
470 | callback_data=f'settings#update_size_-{size - 10}')
471 | ],[
472 | InlineKeyboardButton('+50',
473 | callback_data=f'settings#update_size-{size + 50}'),
474 | InlineKeyboardButton('-50',
475 | callback_data=f'settings#update_size_-{size - 50}')
476 | ],[
477 | InlineKeyboardButton('+100',
478 | callback_data=f'settings#update_size-{size + 100}'),
479 | InlineKeyboardButton('-100',
480 | callback_data=f'settings#update_size_-{size - 100}')
481 | ],[
482 | InlineKeyboardButton('↩ Back',
483 | callback_data="settings#main")
484 | ]]
485 | return InlineKeyboardMarkup(buttons)
486 |
487 | async def filters_buttons(user_id):
488 | filter = await get_configs(user_id)
489 | filters = filter['filters']
490 | buttons = [[
491 | InlineKeyboardButton('🏷️ Forward tag',
492 | callback_data=f'settings_#updatefilter-forward_tag-{filter["forward_tag"]}'),
493 | InlineKeyboardButton('✅' if filter['forward_tag'] else '❌',
494 | callback_data=f'settings#updatefilter-forward_tag-{filter["forward_tag"]}')
495 | ],[
496 | InlineKeyboardButton('🖍️ Texts',
497 | callback_data=f'settings_#updatefilter-text-{filters["text"]}'),
498 | InlineKeyboardButton('✅' if filters['text'] else '❌',
499 | callback_data=f'settings#updatefilter-text-{filters["text"]}')
500 | ],[
501 | InlineKeyboardButton('📁 Documents',
502 | callback_data=f'settings_#updatefilter-document-{filters["document"]}'),
503 | InlineKeyboardButton('✅' if filters['document'] else '❌',
504 | callback_data=f'settings#updatefilter-document-{filters["document"]}')
505 | ],[
506 | InlineKeyboardButton('🎞️ Videos',
507 | callback_data=f'settings_#updatefilter-video-{filters["video"]}'),
508 | InlineKeyboardButton('✅' if filters['video'] else '❌',
509 | callback_data=f'settings#updatefilter-video-{filters["video"]}')
510 | ],[
511 | InlineKeyboardButton('📷 Photos',
512 | callback_data=f'settings_#updatefilter-photo-{filters["photo"]}'),
513 | InlineKeyboardButton('✅' if filters['photo'] else '❌',
514 | callback_data=f'settings#updatefilter-photo-{filters["photo"]}')
515 | ],[
516 | InlineKeyboardButton('🎧 Audios',
517 | callback_data=f'settings_#updatefilter-audio-{filters["audio"]}'),
518 | InlineKeyboardButton('✅' if filters['audio'] else '❌',
519 | callback_data=f'settings#updatefilter-audio-{filters["audio"]}')
520 | ],[
521 | InlineKeyboardButton('🎤 Voices',
522 | callback_data=f'settings_#updatefilter-voice-{filters["voice"]}'),
523 | InlineKeyboardButton('✅' if filters['voice'] else '❌',
524 | callback_data=f'settings#updatefilter-voice-{filters["voice"]}')
525 | ],[
526 | InlineKeyboardButton('🎭 Animations',
527 | callback_data=f'settings_#updatefilter-animation-{filters["animation"]}'),
528 | InlineKeyboardButton('✅' if filters['animation'] else '❌',
529 | callback_data=f'settings#updatefilter-animation-{filters["animation"]}')
530 | ],[
531 | InlineKeyboardButton('🃏 Stickers',
532 | callback_data=f'settings_#updatefilter-sticker-{filters["sticker"]}'),
533 | InlineKeyboardButton('✅' if filters['sticker'] else '❌',
534 | callback_data=f'settings#updatefilter-sticker-{filters["sticker"]}')
535 | ],[
536 | InlineKeyboardButton('▶️ Skip duplicate',
537 | callback_data=f'settings_#updatefilter-duplicate-{filter["duplicate"]}'),
538 | InlineKeyboardButton('✅' if filter['duplicate'] else '❌',
539 | callback_data=f'settings#updatefilter-duplicate-{filter["duplicate"]}')
540 | ],[
541 | InlineKeyboardButton('⫷ back',
542 | callback_data="settings#main")
543 | ]]
544 | return InlineKeyboardMarkup(buttons)
545 |
546 | async def next_filters_buttons(user_id):
547 | filter = await get_configs(user_id)
548 | filters = filter['filters']
549 | buttons = [[
550 | InlineKeyboardButton('📊 Poll',
551 | callback_data=f'settings_#updatefilter-poll-{filters["poll"]}'),
552 | InlineKeyboardButton('✅' if filters['poll'] else '❌',
553 | callback_data=f'settings#updatefilter-poll-{filters["poll"]}')
554 | ],[
555 | InlineKeyboardButton('🔒 Secure message',
556 | callback_data=f'settings_#updatefilter-protect-{filter["protect"]}'),
557 | InlineKeyboardButton('✅' if filter['protect'] else '❌',
558 | callback_data=f'settings#updatefilter-protect-{filter["protect"]}')
559 | ],[
560 | InlineKeyboardButton('🛑 size limit',
561 | callback_data='settings#file_size')
562 | ],[
563 | InlineKeyboardButton('💾 Extension',
564 | callback_data='settings#get_extension')
565 | ],[
566 | InlineKeyboardButton('♦️ keywords ♦️',
567 | callback_data='settings#get_keyword')
568 | ],[
569 | InlineKeyboardButton('⫷ back',
570 | callback_data="settings#main")
571 | ]]
572 | return InlineKeyboardMarkup(buttons)
573 |
574 |
--------------------------------------------------------------------------------