├── Dockerfile ├── LICENCE ├── Procfile ├── README.md ├── app.py ├── config.py ├── database.py ├── main.py ├── plugins ├── broadcast.py ├── commands.py ├── db.py ├── public.py ├── regix.py ├── settings.py ├── test.py ├── unequeify.py └── utils.py ├── requirements.txt ├── run cmd.txt ├── runtime.txt └── script.py /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.10.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 /VJ-Forward-Bot 10 | WORKDIR /VJ-Forward-Bot 11 | COPY . /VJ-Forward-Bot 12 | CMD gunicorn app:app & python3 main.py 13 | -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | Mozilla Public License Version 2.0 2 | ================================== 3 | 4 | 1. Definitions 5 | -------------- 6 | 7 | 1.1. "Contributor" 8 | means each individual or legal entity that creates, contributes to 9 | the creation of, or owns Covered Software. 10 | 11 | 1.2. "Contributor Version" 12 | means the combination of the Contributions of others (if any) used 13 | by a Contributor and that particular Contributor's Contribution. 14 | 15 | 1.3. "Contribution" 16 | means Covered Software of a particular Contributor. 17 | 18 | 1.4. "Covered Software" 19 | means Source Code Form to which the initial Contributor has attached 20 | the notice in Exhibit A, the Executable Form of such Source Code 21 | Form, and Modifications of such Source Code Form, in each case 22 | including portions thereof. 23 | 24 | 1.5. "Incompatible With Secondary Licenses" 25 | means 26 | 27 | (a) that the initial Contributor has attached the notice described 28 | in Exhibit B to the Covered Software; or 29 | 30 | (b) that the Covered Software was made available under the terms of 31 | version 1.1 or earlier of the License, but not also under the 32 | terms of a Secondary License. 33 | 34 | 1.6. "Executable Form" 35 | means any form of the work other than Source Code Form. 36 | 37 | 1.7. "Larger Work" 38 | means a work that combines Covered Software with other material, in 39 | a separate file or files, that is not Covered Software. 40 | 41 | 1.8. "License" 42 | means this document. 43 | 44 | 1.9. "Licensable" 45 | means having the right to grant, to the maximum extent possible, 46 | whether at the time of the initial grant or subsequently, any and 47 | all of the rights conveyed by this License. 48 | 49 | 1.10. "Modifications" 50 | means any of the following: 51 | 52 | (a) any file in Source Code Form that results from an addition to, 53 | deletion from, or modification of the contents of Covered 54 | Software; or 55 | 56 | (b) any new file in Source Code Form that contains any Covered 57 | Software. 58 | 59 | 1.11. "Patent Claims" of a Contributor 60 | means any patent claim(s), including without limitation, method, 61 | process, and apparatus claims, in any patent Licensable by such 62 | Contributor that would be infringed, but for the grant of the 63 | License, by the making, using, selling, offering for sale, having 64 | made, import, or transfer of either its Contributions or its 65 | Contributor Version. 66 | 67 | 1.12. "Secondary License" 68 | means either the GNU General Public License, Version 2.0, the GNU 69 | Lesser General Public License, Version 2.1, the GNU Affero General 70 | Public License, Version 3.0, or any later versions of those 71 | licenses. 72 | 73 | 1.13. "Source Code Form" 74 | means the form of the work preferred for making modifications. 75 | 76 | 1.14. "You" (or "Your") 77 | means an individual or a legal entity exercising rights under this 78 | License. For legal entities, "You" includes any entity that 79 | controls, is controlled by, or is under common control with You. For 80 | purposes of this definition, "control" means (a) the power, direct 81 | or indirect, to cause the direction or management of such entity, 82 | whether by contract or otherwise, or (b) ownership of more than 83 | fifty percent (50%) of the outstanding shares or beneficial 84 | ownership of such entity. 85 | 86 | 2. License Grants and Conditions 87 | -------------------------------- 88 | 89 | 2.1. Grants 90 | 91 | Each Contributor hereby grants You a world-wide, royalty-free, 92 | non-exclusive license: 93 | 94 | (a) under intellectual property rights (other than patent or trademark) 95 | Licensable by such Contributor to use, reproduce, make available, 96 | modify, display, perform, distribute, and otherwise exploit its 97 | Contributions, either on an unmodified basis, with Modifications, or 98 | as part of a Larger Work; and 99 | 100 | (b) under Patent Claims of such Contributor to make, use, sell, offer 101 | for sale, have made, import, and otherwise transfer either its 102 | Contributions or its Contributor Version. 103 | 104 | 2.2. Effective Date 105 | 106 | The licenses granted in Section 2.1 with respect to any Contribution 107 | become effective for each Contribution on the date the Contributor first 108 | distributes such Contribution. 109 | 110 | 2.3. Limitations on Grant Scope 111 | 112 | The licenses granted in this Section 2 are the only rights granted under 113 | this License. No additional rights or licenses will be implied from the 114 | distribution or licensing of Covered Software under this License. 115 | Notwithstanding Section 2.1(b) above, no patent license is granted by a 116 | Contributor: 117 | 118 | (a) for any code that a Contributor has removed from Covered Software; 119 | or 120 | 121 | (b) for infringements caused by: (i) Your and any other third party's 122 | modifications of Covered Software, or (ii) the combination of its 123 | Contributions with other software (except as part of its Contributor 124 | Version); or 125 | 126 | (c) under Patent Claims infringed by Covered Software in the absence of 127 | its Contributions. 128 | 129 | This License does not grant any rights in the trademarks, service marks, 130 | or logos of any Contributor (except as may be necessary to comply with 131 | the notice requirements in Section 3.4). 132 | 133 | 2.4. Subsequent Licenses 134 | 135 | No Contributor makes additional grants as a result of Your choice to 136 | distribute the Covered Software under a subsequent version of this 137 | License (see Section 10.2) or under the terms of a Secondary License (if 138 | permitted under the terms of Section 3.3). 139 | 140 | 2.5. Representation 141 | 142 | Each Contributor represents that the Contributor believes its 143 | Contributions are its original creation(s) or it has sufficient rights 144 | to grant the rights to its Contributions conveyed by this License. 145 | 146 | 2.6. Fair Use 147 | 148 | This License is not intended to limit any rights You have under 149 | applicable copyright doctrines of fair use, fair dealing, or other 150 | equivalents. 151 | 152 | 2.7. Conditions 153 | 154 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted 155 | in Section 2.1. 156 | 157 | 3. Responsibilities 158 | ------------------- 159 | 160 | 3.1. Distribution of Source Form 161 | 162 | All distribution of Covered Software in Source Code Form, including any 163 | Modifications that You create or to which You contribute, must be under 164 | the terms of this License. You must inform recipients that the Source 165 | Code Form of the Covered Software is governed by the terms of this 166 | License, and how they can obtain a copy of this License. You may not 167 | attempt to alter or restrict the recipients' rights in the Source Code 168 | Form. 169 | 170 | 3.2. Distribution of Executable Form 171 | 172 | If You distribute Covered Software in Executable Form then: 173 | 174 | (a) such Covered Software must also be made available in Source Code 175 | Form, as described in Section 3.1, and You must inform recipients of 176 | the Executable Form how they can obtain a copy of such Source Code 177 | Form by reasonable means in a timely manner, at a charge no more 178 | than the cost of distribution to the recipient; and 179 | 180 | (b) You may distribute such Executable Form under the terms of this 181 | License, or sublicense it under different terms, provided that the 182 | license for the Executable Form does not attempt to limit or alter 183 | the recipients' rights in the Source Code Form under this License. 184 | 185 | 3.3. Distribution of a Larger Work 186 | 187 | You may create and distribute a Larger Work under terms of Your choice, 188 | provided that You also comply with the requirements of this License for 189 | the Covered Software. If the Larger Work is a combination of Covered 190 | Software with a work governed by one or more Secondary Licenses, and the 191 | Covered Software is not Incompatible With Secondary Licenses, this 192 | License permits You to additionally distribute such Covered Software 193 | under the terms of such Secondary License(s), so that the recipient of 194 | the Larger Work may, at their option, further distribute the Covered 195 | Software under the terms of either this License or such Secondary 196 | License(s). 197 | 198 | 3.4. Notices 199 | 200 | You may not remove or alter the substance of any license notices 201 | (including copyright notices, patent notices, disclaimers of warranty, 202 | or limitations of liability) contained within the Source Code Form of 203 | the Covered Software, except that You may alter any license notices to 204 | the extent required to remedy known factual inaccuracies. 205 | 206 | 3.5. Application of Additional Terms 207 | 208 | You may choose to offer, and to charge a fee for, warranty, support, 209 | indemnity or liability obligations to one or more recipients of Covered 210 | Software. However, You may do so only on Your own behalf, and not on 211 | behalf of any Contributor. You must make it absolutely clear that any 212 | such warranty, support, indemnity, or liability obligation is offered by 213 | You alone, and You hereby agree to indemnify every Contributor for any 214 | liability incurred by such Contributor as a result of warranty, support, 215 | indemnity or liability terms You offer. You may include additional 216 | disclaimers of warranty and limitations of liability specific to any 217 | jurisdiction. 218 | 219 | 4. Inability to Comply Due to Statute or Regulation 220 | --------------------------------------------------- 221 | 222 | If it is impossible for You to comply with any of the terms of this 223 | License with respect to some or all of the Covered Software due to 224 | statute, judicial order, or regulation then You must: (a) comply with 225 | the terms of this License to the maximum extent possible; and (b) 226 | describe the limitations and the code they affect. Such description must 227 | be placed in a text file included with all distributions of the Covered 228 | Software under this License. Except to the extent prohibited by statute 229 | or regulation, such description must be sufficiently detailed for a 230 | recipient of ordinary skill to be able to understand it. 231 | 232 | 5. Termination 233 | -------------- 234 | 235 | 5.1. The rights granted under this License will terminate automatically 236 | if You fail to comply with any of its terms. However, if You become 237 | compliant, then the rights granted under this License from a particular 238 | Contributor are reinstated (a) provisionally, unless and until such 239 | Contributor explicitly and finally terminates Your grants, and (b) on an 240 | ongoing basis, if such Contributor fails to notify You of the 241 | non-compliance by some reasonable means prior to 60 days after You have 242 | come back into compliance. Moreover, Your grants from a particular 243 | Contributor are reinstated on an ongoing basis if such Contributor 244 | notifies You of the non-compliance by some reasonable means, this is the 245 | first time You have received notice of non-compliance with this License 246 | from such Contributor, and You become compliant prior to 30 days after 247 | Your receipt of the notice. 248 | 249 | 5.2. If You initiate litigation against any entity by asserting a patent 250 | infringement claim (excluding declaratory judgment actions, 251 | counter-claims, and cross-claims) alleging that a Contributor Version 252 | directly or indirectly infringes any patent, then the rights granted to 253 | You by any and all Contributors for the Covered Software under Section 254 | 2.1 of this License shall terminate. 255 | 256 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all 257 | end user license agreements (excluding distributors and resellers) which 258 | have been validly granted by You or Your distributors under this License 259 | prior to termination shall survive termination. 260 | 261 | ************************************************************************ 262 | * * 263 | * 6. Disclaimer of Warranty * 264 | * ------------------------- * 265 | * * 266 | * Covered Software is provided under this License on an "as is" * 267 | * basis, without warranty of any kind, either expressed, implied, or * 268 | * statutory, including, without limitation, warranties that the * 269 | * Covered Software is free of defects, merchantable, fit for a * 270 | * particular purpose or non-infringing. The entire risk as to the * 271 | * quality and performance of the Covered Software is with You. * 272 | * Should any Covered Software prove defective in any respect, You * 273 | * (not any Contributor) assume the cost of any necessary servicing, * 274 | * repair, or correction. This disclaimer of warranty constitutes an * 275 | * essential part of this License. No use of any Covered Software is * 276 | * authorized under this License except under this disclaimer. * 277 | * * 278 | ************************************************************************ 279 | 280 | ************************************************************************ 281 | * * 282 | * 7. Limitation of Liability * 283 | * -------------------------- * 284 | * * 285 | * Under no circumstances and under no legal theory, whether tort * 286 | * (including negligence), contract, or otherwise, shall any * 287 | * Contributor, or anyone who distributes Covered Software as * 288 | * permitted above, be liable to You for any direct, indirect, * 289 | * special, incidental, or consequential damages of any character * 290 | * including, without limitation, damages for lost profits, loss of * 291 | * goodwill, work stoppage, computer failure or malfunction, or any * 292 | * and all other commercial damages or losses, even if such party * 293 | * shall have been informed of the possibility of such damages. This * 294 | * limitation of liability shall not apply to liability for death or * 295 | * personal injury resulting from such party's negligence to the * 296 | * extent applicable law prohibits such limitation. Some * 297 | * jurisdictions do not allow the exclusion or limitation of * 298 | * incidental or consequential damages, so this exclusion and * 299 | * limitation may not apply to You. * 300 | * * 301 | ************************************************************************ 302 | 303 | 8. Litigation 304 | ------------- 305 | 306 | Any litigation relating to this License may be brought only in the 307 | courts of a jurisdiction where the defendant maintains its principal 308 | place of business and such litigation shall be governed by laws of that 309 | jurisdiction, without reference to its conflict-of-law provisions. 310 | Nothing in this Section shall prevent a party's ability to bring 311 | cross-claims or counter-claims. 312 | 313 | 9. Miscellaneous 314 | ---------------- 315 | 316 | This License represents the complete agreement concerning the subject 317 | matter hereof. If any provision of this License is held to be 318 | unenforceable, such provision shall be reformed only to the extent 319 | necessary to make it enforceable. Any law or regulation which provides 320 | that the language of a contract shall be construed against the drafter 321 | shall not be used to construe this License against a Contributor. 322 | 323 | 10. Versions of the License 324 | --------------------------- 325 | 326 | 10.1. New Versions 327 | 328 | Mozilla Foundation is the license steward. Except as provided in Section 329 | 10.3, no one other than the license steward has the right to modify or 330 | publish new versions of this License. Each version will be given a 331 | distinguishing version number. 332 | 333 | 10.2. Effect of New Versions 334 | 335 | You may distribute the Covered Software under the terms of the version 336 | of the License under which You originally received the Covered Software, 337 | or under the terms of any subsequent version published by the license 338 | steward. 339 | 340 | 10.3. Modified Versions 341 | 342 | If you create software not governed by this License, and you want to 343 | create a new license for such software, you may create and use a 344 | modified version of this License if you rename the license and remove 345 | any references to the name of the license steward (except to note that 346 | such modified license differs from this License). 347 | 348 | 10.4. Distributing Source Code Form that is Incompatible With Secondary 349 | Licenses 350 | 351 | If You choose to distribute Source Code Form that is Incompatible With 352 | Secondary Licenses under the terms of this version of the License, the 353 | notice described in Exhibit B of this License must be attached. 354 | 355 | Exhibit A - Source Code Form License Notice 356 | ------------------------------------------- 357 | 358 | This Source Code Form is subject to the terms of the Mozilla Public 359 | License, v. 2.0. If a copy of the MPL was not distributed with this 360 | file, You can obtain one at http://mozilla.org/MPL/2.0/. 361 | 362 | If it is not possible or desirable to put the notice in a particular 363 | file, then You may include the notice in a location (such as a LICENSE 364 | file in a relevant directory) where a recipient would be likely to look 365 | for such a notice. 366 | 367 | You may add additional accurate notices of copyright ownership. 368 | 369 | Exhibit B - "Incompatible With Secondary Licenses" Notice 370 | --------------------------------------------------------- 371 | 372 | This Source Code Form is "Incompatible With Secondary Licenses", as 373 | defined by the Mozilla Public License, v. 2.0. 374 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | worker: python3 main.py 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # VJ Forward Bot 2 | 3 | Auto Restart All User Forwarding After Bot Restarted. 4 | 5 | ![Typing SVG](https://readme-typing-svg.herokuapp.com/?lines=Welcome+To+VJ+Forward+Bot+!) 6 | 7 | ## How To Deploy [Video Tutorial](https://youtu.be/A-iIh_5WAlk) 8 | 9 | ## Features 10 | 11 | - [x] Public Forward (Bot) 12 | - [x] Private Forward (User Bot) 13 | - [x] Custom Caption 14 | - [x] Custom Button 15 | - [x] Skip Duplicate Messages 16 | - [x] Skip Messages Based On Extensions & Keywords & Size 17 | - [x] Filter Type Of Messages 18 | - [x] Auto Restart Pending Task After Bot Restart 19 | 20 | 21 | To Know About All Features, Join My Update Channel. 22 | 23 | ## Commands 24 | 25 | ``` 26 | start - check I'm alive 27 | forward - forward messages 28 | unequify - delete duplicate media messages in chats 29 | settings - configure your settings 30 | stop - stop your ongoing tasks 31 | reset - reset your settings 32 | restart - restart server (owner only) 33 | resetall - reset all users settings (owner only) 34 | broadcast - broadcast a message to all your users (owner only) 35 | ``` 36 | 37 | ## Variables 38 | 39 | * `API_ID` API Id from my.telegram.org 40 | * `API_HASH` API Hash from my.telegram.org 41 | * `BOT_TOKEN` Bot token from @BotFather 42 | * `BOT_OWNER` Telegram Account Id of Owner. 43 | * `DATABASE_URI` Database uri from [MongoDB](https://mongodb.com) Watch [Video Tutorial](https://youtu.be/DAHRmFdw99o) 44 | 45 | ## Credits 46 | 47 | * [Tech VJ](https://youtube.com/@Tech_VJ) 48 | -------------------------------------------------------------------------------- /app.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | app = Flask(__name__) 3 | 4 | @app.route('/') 5 | def hello_world(): 6 | return 'Hello from Koyeb' 7 | 8 | 9 | if __name__ == "__main__": 10 | app.run() 11 | -------------------------------------------------------------------------------- /config.py: -------------------------------------------------------------------------------- 1 | # Don't Remove Credit Tg - @VJ_Botz 2 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 3 | # Ask Doubt on telegram @KingVJ01 4 | 5 | from os import environ 6 | 7 | class Config: 8 | API_ID = int(environ.get("API_ID", "")) 9 | API_HASH = environ.get("API_HASH", "") 10 | BOT_TOKEN = environ.get("BOT_TOKEN", "") 11 | BOT_SESSION = environ.get("BOT_SESSION", "vjbot") 12 | DATABASE_URI = environ.get("DATABASE_URI", "") 13 | DATABASE_NAME = environ.get("DATABASE_NAME", "vj-forward-bot") 14 | BOT_OWNER = int(environ.get("BOT_OWNER", "")) 15 | 16 | # Don't Remove Credit Tg - @VJ_Botz 17 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 18 | # Ask Doubt on telegram @KingVJ01 19 | 20 | class temp(object): 21 | lock = {} 22 | CANCEL = {} 23 | forwardings = 0 24 | BANNED_USERS = [] 25 | IS_FRWD_CHAT = [] 26 | 27 | # Don't Remove Credit Tg - @VJ_Botz 28 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 29 | # Ask Doubt on telegram @KingVJ01 30 | -------------------------------------------------------------------------------- /database.py: -------------------------------------------------------------------------------- 1 | import motor.motor_asyncio 2 | from config import Config 3 | 4 | class Db: 5 | 6 | def __init__(self, uri, database_name): 7 | self._client = motor.motor_asyncio.AsyncIOMotorClient(uri) 8 | self.db = self._client[database_name] 9 | self.bot = self.db.bots 10 | self.userbot = self.db.userbot 11 | self.col = self.db.users 12 | self.nfy = self.db.notify 13 | self.chl = self.db.channels 14 | 15 | def new_user(self, id, name): 16 | return dict( 17 | id = id, 18 | name = name, 19 | ban_status=dict( 20 | is_banned=False, 21 | ban_reason="", 22 | ), 23 | ) 24 | 25 | async def add_user(self, id, name): 26 | user = self.new_user(id, name) 27 | await self.col.insert_one(user) 28 | 29 | async def is_user_exist(self, id): 30 | user = await self.col.find_one({'id':int(id)}) 31 | return bool(user) 32 | 33 | async def total_users_count(self): 34 | count = await self.col.count_documents({}) 35 | return count 36 | 37 | async def total_users_bots_count(self): 38 | bcount = await self.bot.count_documents({}) 39 | count = await self.col.count_documents({}) 40 | return count, bcount 41 | 42 | async def remove_ban(self, id): 43 | ban_status = dict( 44 | is_banned=False, 45 | ban_reason='' 46 | ) 47 | await self.col.update_one({'id': id}, {'$set': {'ban_status': ban_status}}) 48 | 49 | async def ban_user(self, user_id, ban_reason="No Reason"): 50 | ban_status = dict( 51 | is_banned=True, 52 | ban_reason=ban_reason 53 | ) 54 | await self.col.update_one({'id': user_id}, {'$set': {'ban_status': ban_status}}) 55 | 56 | async def get_ban_status(self, id): 57 | default = dict( 58 | is_banned=False, 59 | ban_reason='' 60 | ) 61 | user = await self.col.find_one({'id':int(id)}) 62 | if not user: 63 | return default 64 | return user.get('ban_status', default) 65 | 66 | async def get_all_users(self): 67 | return self.col.find({}) 68 | 69 | async def delete_user(self, user_id): 70 | await self.col.delete_many({'id': int(user_id)}) 71 | 72 | async def get_banned(self): 73 | users = self.col.find({'ban_status.is_banned': True}) 74 | b_users = [user['id'] async for user in users] 75 | return b_users 76 | 77 | async def update_configs(self, id, configs): 78 | await self.col.update_one({'id': int(id)}, {'$set': {'configs': configs}}) 79 | 80 | async def get_configs(self, id): 81 | default = { 82 | 'caption': None, 83 | 'duplicate': True, 84 | 'forward_tag': False, 85 | 'min_size': 0, 86 | 'max_size': 0, 87 | 'extension': None, 88 | 'keywords': None, 89 | 'protect': None, 90 | 'button': None, 91 | 'db_uri': None, 92 | 'filters': { 93 | 'poll': True, 94 | 'text': True, 95 | 'audio': True, 96 | 'voice': True, 97 | 'video': True, 98 | 'photo': True, 99 | 'document': True, 100 | 'animation': True, 101 | 'sticker': True 102 | } 103 | } 104 | user = await self.col.find_one({'id':int(id)}) 105 | if user: 106 | return user.get('configs', default) 107 | return default 108 | 109 | async def add_bot(self, datas): 110 | if not await self.is_bot_exist(datas['user_id']): 111 | await self.bot.insert_one(datas) 112 | 113 | async def remove_bot(self, user_id): 114 | await self.bot.delete_many({'user_id': int(user_id)}) 115 | 116 | async def get_bot(self, user_id: int): 117 | bot = await self.bot.find_one({'user_id': user_id}) 118 | return bot if bot else None 119 | 120 | async def is_bot_exist(self, user_id): 121 | bot = await self.bot.find_one({'user_id': user_id}) 122 | return bool(bot) 123 | 124 | async def add_userbot(self, datas): 125 | if not await self.is_userbot_exist(datas['user_id']): 126 | await self.userbot.insert_one(datas) 127 | 128 | async def remove_userbot(self, user_id): 129 | await self.userbot.delete_many({'user_id': int(user_id)}) 130 | 131 | async def get_userbot(self, user_id: int): 132 | bot = await self.userbot.find_one({'user_id': user_id}) 133 | return bot if bot else None 134 | 135 | async def is_userbot_exist(self, user_id): 136 | bot = await self.userbot.find_one({'user_id': user_id}) 137 | return bool(bot) 138 | 139 | async def in_channel(self, user_id: int, chat_id: int) -> bool: 140 | channel = await self.chl.find_one({"user_id": int(user_id), "chat_id": int(chat_id)}) 141 | return bool(channel) 142 | 143 | async def add_channel(self, user_id: int, chat_id: int, title, username): 144 | channel = await self.in_channel(user_id, chat_id) 145 | if channel: 146 | return False 147 | return await self.chl.insert_one({"user_id": user_id, "chat_id": chat_id, "title": title, "username": username}) 148 | 149 | async def remove_channel(self, user_id: int, chat_id: int): 150 | channel = await self.in_channel(user_id, chat_id ) 151 | if not channel: 152 | return False 153 | return await self.chl.delete_many({"user_id": int(user_id), "chat_id": int(chat_id)}) 154 | 155 | async def get_channel_details(self, user_id: int, chat_id: int): 156 | return await self.chl.find_one({"user_id": int(user_id), "chat_id": int(chat_id)}) 157 | 158 | async def get_user_channels(self, user_id: int): 159 | channels = self.chl.find({"user_id": int(user_id)}) 160 | return [channel async for channel in channels] 161 | 162 | async def get_filters(self, user_id): 163 | filters = [] 164 | filter = (await self.get_configs(user_id))['filters'] 165 | for k, v in filter.items(): 166 | if v == False: 167 | filters.append(str(k)) 168 | return filters 169 | 170 | async def add_frwd(self, user_id): 171 | return await self.nfy.insert_one({'user_id': int(user_id)}) 172 | 173 | async def rmve_frwd(self, user_id=0, all=False): 174 | data = {} if all else {'user_id': int(user_id)} 175 | return await self.nfy.delete_many(data) 176 | 177 | async def get_all_frwd(self): 178 | return self.nfy.find({}) 179 | 180 | async def forwad_count(self): 181 | c = await self.nfy.count_documents({}) 182 | return c 183 | 184 | async def is_forwad_exit(self, user): 185 | u = await self.nfy.find_one({'user_id': user}) 186 | return bool(u) 187 | 188 | async def get_forward_details(self, user_id): 189 | defult = { 190 | 'chat_id': None, 191 | 'forward_id': None, 192 | 'toid': None, 193 | 'last_id': None, 194 | 'limit': None, 195 | 'msg_id': None, 196 | 'start_time': None, 197 | 'fetched': 0, 198 | 'offset': 0, 199 | 'deleted': 0, 200 | 'total': 0, 201 | 'duplicate': 0, 202 | 'skip': 0, 203 | 'filtered' :0 204 | } 205 | user = await self.nfy.find_one({'user_id': int(user_id)}) 206 | if user: 207 | return user.get('details', defult) 208 | return defult 209 | 210 | async def update_forward(self, user_id, details): 211 | await self.nfy.update_one({'user_id': user_id}, {'$set': {'details': details}}) 212 | 213 | db = Db(Config.DATABASE_URI, Config.DATABASE_NAME) 214 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | # Don't Remove Credit Tg - @VJ_Botz 2 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 3 | # Ask Doubt on telegram @KingVJ01 4 | 5 | import asyncio, logging 6 | from config import Config 7 | from pyrogram import Client as VJ, idle 8 | from typing import Union, Optional, AsyncGenerator 9 | from logging.handlers import RotatingFileHandler 10 | from plugins.regix import restart_forwards 11 | 12 | # Don't Remove Credit Tg - @VJ_Botz 13 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 14 | # Ask Doubt on telegram @KingVJ01 15 | 16 | if __name__ == "__main__": 17 | VJBot = VJ( 18 | "VJ-Forward-Bot", 19 | bot_token=Config.BOT_TOKEN, 20 | api_id=Config.API_ID, 21 | api_hash=Config.API_HASH, 22 | sleep_threshold=120, 23 | plugins=dict(root="plugins") 24 | ) 25 | async def iter_messages( 26 | self, 27 | chat_id: Union[int, str], 28 | limit: int, 29 | offset: int = 0, 30 | ) -> Optional[AsyncGenerator["types.Message", None]]: 31 | """Iterate through a chat sequentially. 32 | This convenience method does the same as repeatedly calling :meth:`~pyrogram.Client.get_messages` in a loop, thus saving 33 | you from the hassle of setting up boilerplate code. It is useful for getting the whole chat messages with a 34 | single call. 35 | Parameters: 36 | chat_id (``int`` | ``str``): 37 | Unique identifier (int) or username (str) of the target chat. 38 | For your personal cloud (Saved Messages) you can simply use "me" or "self". 39 | For a contact that exists in your Telegram address book you can use his phone number (str). 40 | 41 | limit (``int``): 42 | Identifier of the last message to be returned. 43 | 44 | offset (``int``, *optional*): 45 | Identifier of the first message to be returned. 46 | Defaults to 0. 47 | Returns: 48 | ``Generator``: A generator yielding :obj:`~pyrogram.types.Message` objects. 49 | Example: 50 | .. code-block:: python 51 | for message in app.iter_messages("pyrogram", 1, 15000): 52 | print(message.text) 53 | """ 54 | current = offset 55 | while True: 56 | new_diff = min(200, limit - current) 57 | if new_diff <= 0: 58 | return 59 | messages = await self.get_messages(chat_id, list(range(current, current+new_diff+1))) 60 | for message in messages: 61 | yield message 62 | current += 1 63 | 64 | async def main(): 65 | await VJBot.start() 66 | bot_info = await VJBot.get_me() 67 | await restart_forwards(VJBot) 68 | print("Bot Started.") 69 | await idle() 70 | 71 | asyncio.get_event_loop().run_until_complete(main()) 72 | 73 | # Don't Remove Credit Tg - @VJ_Botz 74 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 75 | # Ask Doubt on telegram @KingVJ01 76 | -------------------------------------------------------------------------------- /plugins/broadcast.py: -------------------------------------------------------------------------------- 1 | # Don't Remove Credit Tg - @VJ_Botz 2 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 3 | # Ask Doubt on telegram @KingVJ01 4 | 5 | from pyrogram.errors import InputUserDeactivated, UserNotParticipant, FloodWait, UserIsBlocked, PeerIdInvalid 6 | from database import db 7 | from pyrogram import Client, filters 8 | from config import Config 9 | import asyncio 10 | import datetime 11 | import time 12 | import logging 13 | 14 | # Don't Remove Credit Tg - @VJ_Botz 15 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 16 | # Ask Doubt on telegram @KingVJ01 17 | 18 | logger = logging.getLogger(__name__) 19 | logger.setLevel(logging.INFO) 20 | 21 | # Don't Remove Credit Tg - @VJ_Botz 22 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 23 | # Ask Doubt on telegram @KingVJ01 24 | 25 | async def broadcast_messages(user_id, message): 26 | try: 27 | await message.copy(chat_id=user_id) 28 | return True, "Success" 29 | except FloodWait as e: 30 | await asyncio.sleep(e.value) 31 | return await broadcast_messages(user_id, message) 32 | except InputUserDeactivated: 33 | await db.delete_user(int(user_id)) 34 | logging.info(f"{user_id}-Removed from Database, since deleted account.") 35 | return False, "Deleted" 36 | except UserIsBlocked: 37 | logging.info(f"{user_id} -Blocked the bot.") 38 | return False, "Blocked" 39 | except PeerIdInvalid: 40 | await db.delete_user(int(user_id)) 41 | logging.info(f"{user_id} - PeerIdInvalid") 42 | return False, "Error" 43 | except Exception as e: 44 | return False, "Error" 45 | 46 | # Don't Remove Credit Tg - @VJ_Botz 47 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 48 | # Ask Doubt on telegram @KingVJ01 49 | 50 | 51 | @Client.on_message(filters.command("broadcast") & filters.user(Config.BOT_OWNER) & filters.reply) 52 | async def verupikkals(bot, message): 53 | users = await db.get_all_users() 54 | b_msg = message.reply_to_message 55 | sts = await message.reply_text( 56 | text='Broadcasting your messages...' 57 | ) 58 | start_time = time.time() 59 | total_users = await db.total_users_count() 60 | done = 0 61 | blocked = 0 62 | deleted = 0 63 | failed =0 64 | 65 | # Don't Remove Credit Tg - @VJ_Botz 66 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 67 | # Ask Doubt on telegram @KingVJ01 68 | 69 | success = 0 70 | async for user in users: 71 | if 'id' in user: 72 | pti, sh = await broadcast_messages(int(user['id']), b_msg) 73 | if pti: 74 | success += 1 75 | elif pti == False: 76 | if sh == "Blocked": 77 | blocked += 1 78 | elif sh == "Deleted": 79 | deleted += 1 80 | elif sh == "Error": 81 | failed += 1 82 | done += 1 83 | if not done % 20: 84 | await sts.edit(f"Broadcast in progress:\n\nTotal Users {total_users}\nCompleted: {done} / {total_users}\nSuccess: {success}\nBlocked: {blocked}\nDeleted: {deleted}") 85 | else: 86 | # Handle the case where 'id' key is missing in the user dictionary 87 | done += 1 88 | failed += 1 89 | if not done % 20: 90 | try: 91 | await sts.edit(f"Broadcast in progress:\n\nTotal Users {total_users}\nCompleted: {done} / {total_users}\nSuccess: {success}\nBlocked: {blocked}\nDeleted: {deleted}") 92 | except: 93 | pass 94 | 95 | time_taken = datetime.timedelta(seconds=int(time.time()-start_time)) 96 | 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}") 97 | 98 | # Don't Remove Credit Tg - @VJ_Botz 99 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 100 | # Ask Doubt on telegram @KingVJ01 101 | 102 | -------------------------------------------------------------------------------- /plugins/commands.py: -------------------------------------------------------------------------------- 1 | # Don't Remove Credit Tg - @VJ_Botz 2 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 3 | # Ask Doubt on telegram @KingVJ01 4 | 5 | import os 6 | import sys 7 | import asyncio 8 | from database import Db, db 9 | from config import Config, temp 10 | from script import Script 11 | from pyrogram import Client, filters 12 | from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup, InputMediaDocument 13 | import psutil 14 | import time as time 15 | from os import environ, execle, system 16 | 17 | START_TIME = time.time() 18 | 19 | # Don't Remove Credit Tg - @VJ_Botz 20 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 21 | # Ask Doubt on telegram @KingVJ01 22 | 23 | main_buttons = [[ 24 | InlineKeyboardButton('❣️ ᴅᴇᴠᴇʟᴏᴘᴇʀ ❣️', url='https://t.me/kingvj01') 25 | ],[ 26 | InlineKeyboardButton('🔍 sᴜᴘᴘᴏʀᴛ ɢʀᴏᴜᴘ', url='https://t.me/vj_bot_disscussion'), 27 | InlineKeyboardButton('🤖 ᴜᴘᴅᴀᴛᴇ ᴄʜᴀɴɴᴇʟ', url='https://t.me/vj_botz') 28 | ],[ 29 | InlineKeyboardButton('💝 sᴜʙsᴄʀɪʙᴇ ᴍʏ ʏᴏᴜᴛᴜʙᴇ ᴄʜᴀɴɴᴇʟ', url='https://youtube.com/@Tech_VJ') 30 | ],[ 31 | InlineKeyboardButton('👨‍💻 ʜᴇʟᴘ', callback_data='help'), 32 | InlineKeyboardButton('💁 ᴀʙᴏᴜᴛ', callback_data='about') 33 | ],[ 34 | InlineKeyboardButton('⚙ sᴇᴛᴛɪɴɢs', callback_data='settings#main') 35 | ]] 36 | 37 | # Don't Remove Credit Tg - @VJ_Botz 38 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 39 | # Ask Doubt on telegram @KingVJ01 40 | 41 | @Client.on_message(filters.private & filters.command(['start'])) 42 | async def start(client, message): 43 | user = message.from_user 44 | if not await db.is_user_exist(user.id): 45 | await db.add_user(user.id, user.first_name) 46 | reply_markup = InlineKeyboardMarkup(main_buttons) 47 | await client.send_message( 48 | chat_id=message.chat.id, 49 | reply_markup=reply_markup, 50 | text=Script.START_TXT.format(message.from_user.first_name)) 51 | 52 | # Don't Remove Credit Tg - @VJ_Botz 53 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 54 | # Ask Doubt on telegram @KingVJ01 55 | 56 | @Client.on_message(filters.private & filters.command(['restart']) & filters.user(Config.BOT_OWNER)) 57 | async def restart(client, message): 58 | msg = await message.reply_text(text="Trying to restarting.....") 59 | await asyncio.sleep(5) 60 | await msg.edit("Server restarted successfully ✅") 61 | system("git pull -f && pip3 install --no-cache-dir -r requirements.txt") 62 | execle(sys.executable, sys.executable, "main.py", environ) 63 | 64 | # Don't Remove Credit Tg - @VJ_Botz 65 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 66 | # Ask Doubt on telegram @KingVJ01 67 | 68 | @Client.on_callback_query(filters.regex(r'^help')) 69 | async def helpcb(bot, query): 70 | buttons = [[ 71 | InlineKeyboardButton('🤔 ʜᴏᴡ ᴛᴏ ᴜsᴇ ᴍᴇ ❓', callback_data='how_to_use') 72 | ],[ 73 | InlineKeyboardButton('Aʙᴏᴜᴛ ✨️', callback_data='about'), 74 | InlineKeyboardButton('⚙ Sᴇᴛᴛɪɴɢs', callback_data='settings#main') 75 | ],[ 76 | InlineKeyboardButton('• back', callback_data='back') 77 | ]] 78 | reply_markup = InlineKeyboardMarkup(buttons) 79 | await query.message.edit_text(text=Script.HELP_TXT, reply_markup=reply_markup) 80 | 81 | # Don't Remove Credit Tg - @VJ_Botz 82 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 83 | # Ask Doubt on telegram @KingVJ01 84 | 85 | @Client.on_callback_query(filters.regex(r'^how_to_use')) 86 | async def how_to_use(bot, query): 87 | buttons = [[InlineKeyboardButton('• back', callback_data='help')]] 88 | reply_markup = InlineKeyboardMarkup(buttons) 89 | await query.message.edit_text( 90 | text=Script.HOW_USE_TXT, 91 | reply_markup=reply_markup, 92 | disable_web_page_preview=True 93 | ) 94 | 95 | # Don't Remove Credit Tg - @VJ_Botz 96 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 97 | # Ask Doubt on telegram @KingVJ01 98 | 99 | @Client.on_callback_query(filters.regex(r'^back')) 100 | async def back(bot, query): 101 | reply_markup = InlineKeyboardMarkup(main_buttons) 102 | await query.message.edit_text( 103 | reply_markup=reply_markup, 104 | text=Script.START_TXT.format(query.from_user.first_name)) 105 | 106 | # Don't Remove Credit Tg - @VJ_Botz 107 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 108 | # Ask Doubt on telegram @KingVJ01 109 | 110 | @Client.on_callback_query(filters.regex(r'^about')) 111 | async def about(bot, query): 112 | buttons = [[ 113 | InlineKeyboardButton('• back', callback_data='help'), 114 | InlineKeyboardButton('Stats ✨️', callback_data='status') 115 | ]] 116 | reply_markup = InlineKeyboardMarkup(buttons) 117 | await query.message.edit_text( 118 | text=Script.ABOUT_TXT, 119 | reply_markup=reply_markup, 120 | disable_web_page_preview=True 121 | ) 122 | 123 | # Don't Remove Credit Tg - @VJ_Botz 124 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 125 | # Ask Doubt on telegram @KingVJ01 126 | 127 | @Client.on_callback_query(filters.regex(r'^status')) 128 | async def status(bot, query): 129 | users_count, bots_count = await db.total_users_bots_count() 130 | forwardings = await db.forwad_count() 131 | upt = await get_bot_uptime(START_TIME) 132 | buttons = [[ 133 | InlineKeyboardButton('• back', callback_data='help'), 134 | InlineKeyboardButton('System Stats ✨️', callback_data='systm_sts'), 135 | ]] 136 | reply_markup = InlineKeyboardMarkup(buttons) 137 | await query.message.edit_text( 138 | text=Script.STATUS_TXT.format(upt, users_count, bots_count, forwardings), 139 | reply_markup=reply_markup, 140 | disable_web_page_preview=True, 141 | ) 142 | 143 | # Don't Remove Credit Tg - @VJ_Botz 144 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 145 | # Ask Doubt on telegram @KingVJ01 146 | 147 | @Client.on_callback_query(filters.regex(r'^systm_sts')) 148 | async def sys_status(bot, query): 149 | buttons = [[InlineKeyboardButton('• back', callback_data='help')]] 150 | ram = psutil.virtual_memory().percent 151 | cpu = psutil.cpu_percent() 152 | disk_usage = psutil.disk_usage('/') 153 | total_space = disk_usage.total / (1024**3) # Convert to GB 154 | used_space = disk_usage.used / (1024**3) # Convert to GB 155 | free_space = disk_usage.free / (1024**3) 156 | text = f""" 157 | ╔════❰ sᴇʀᴠᴇʀ sᴛᴀᴛs ❱═❍⊱❁۪۪ 158 | ║╭━━━━━━━━━━━━━━━➣ 159 | ║┣⪼ ᴛᴏᴛᴀʟ ᴅɪsᴋ sᴘᴀᴄᴇ: {total_space:.2f} GB 160 | ║┣⪼ ᴜsᴇᴅ: {used_space:.2f} GB 161 | ║┣⪼ ꜰʀᴇᴇ: {free_space:.2f} GB 162 | ║┣⪼ ᴄᴘᴜ: {cpu}% 163 | ║┣⪼ ʀᴀᴍ: {ram}% 164 | ║╰━━━━━━━━━━━━━━━➣ 165 | ╚══════════════════❍⊱❁۪۪ 166 | """ 167 | reply_markup = InlineKeyboardMarkup(buttons) 168 | await query.message.edit_text( 169 | text, 170 | reply_markup=reply_markup, 171 | disable_web_page_preview=True, 172 | ) 173 | 174 | # Don't Remove Credit Tg - @VJ_Botz 175 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 176 | # Ask Doubt on telegram @KingVJ01 177 | 178 | async def get_bot_uptime(start_time): 179 | # Calculate the uptime in seconds 180 | uptime_seconds = int(time.time() - start_time) 181 | uptime_minutes = uptime_seconds // 60 182 | uptime_hours = uptime_minutes // 60 183 | uptime_days = uptime_hours // 24 184 | uptime_weeks = uptime_days // 7 185 | uptime_string = "" 186 | if uptime_hours != 0: 187 | uptime_string += f" {uptime_hours % 24}H" 188 | if uptime_minutes != 0: 189 | uptime_string += f" {uptime_minutes % 60}M" 190 | uptime_string += f" {uptime_seconds % 60} Sec" 191 | return uptime_string 192 | 193 | # Don't Remove Credit Tg - @VJ_Botz 194 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 195 | # Ask Doubt on telegram @KingVJ01 196 | -------------------------------------------------------------------------------- /plugins/db.py: -------------------------------------------------------------------------------- 1 | # Don't Remove Credit Tg - @VJ_Botz 2 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 3 | # Ask Doubt on telegram @KingVJ01 4 | 5 | import asyncio 6 | import motor.motor_asyncio 7 | 8 | # Don't Remove Credit Tg - @VJ_Botz 9 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 10 | # Ask Doubt on telegram @KingVJ01 11 | 12 | class MongoDB: 13 | def __init__(self, uri, db_name, collection): 14 | self.uri = uri 15 | self.db_name = db_name 16 | self.collection = collection 17 | self.client = None 18 | self.db = None 19 | self.files = None 20 | 21 | async def connect(self): 22 | self.client = motor.motor_asyncio.AsyncIOMotorClient(self.uri) 23 | self.db = self.client[self.db_name] 24 | self.files = self.db[self.collection] 25 | 26 | async def close(self): 27 | if self.client: 28 | self.client.close() 29 | 30 | async def add_file(self, file_id): 31 | file = {"file_id": file_id} 32 | return await self.files.insert_one(file) 33 | 34 | async def is_file_exit(self, file_id): 35 | f = await self.files.find_one({"file_id": file_id}) 36 | return bool(f) 37 | 38 | async def get_all_files(self): 39 | return self.files.find({}) 40 | 41 | async def drop_all(self): 42 | return await self.files.drop() 43 | 44 | # Don't Remove Credit Tg - @VJ_Botz 45 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 46 | # Ask Doubt on telegram @KingVJ01 47 | 48 | async def connect_user_db(user_id, uri, chat): 49 | chat = f"{user_id}{chat}" 50 | dbname = f"{user_id}-Forward-Bot" 51 | db = MongoDB(uri, dbname, chat) 52 | try: 53 | await db.connect() 54 | except Exception as e: 55 | print(e) 56 | return False, db 57 | return True, db 58 | 59 | # Don't Remove Credit Tg - @VJ_Botz 60 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 61 | # Ask Doubt on telegram @KingVJ01 62 | -------------------------------------------------------------------------------- /plugins/public.py: -------------------------------------------------------------------------------- 1 | # Don't Remove Credit Tg - @VJ_Botz 2 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 3 | # Ask Doubt on telegram @KingVJ01 4 | 5 | import re 6 | import asyncio 7 | from .utils import STS 8 | from database import Db, db 9 | from config import temp 10 | from script import Script 11 | from pyrogram import Client, filters, enums 12 | from pyrogram.errors import FloodWait 13 | from pyrogram.errors.exceptions.not_acceptable_406 import ChannelPrivate as PrivateChat 14 | from pyrogram.errors.exceptions.bad_request_400 import ChannelInvalid, ChatAdminRequired, UsernameInvalid, UsernameNotModified, ChannelPrivate 15 | from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup, CallbackQuery, KeyboardButton, ReplyKeyboardMarkup, ReplyKeyboardRemove 16 | 17 | # Don't Remove Credit Tg - @VJ_Botz 18 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 19 | # Ask Doubt on telegram @KingVJ01 20 | 21 | @Client.on_message(filters.private & filters.command(["forward"])) 22 | async def run(bot, message): 23 | buttons = [] 24 | btn_data = {} 25 | user_id = message.from_user.id 26 | _bot = await db.get_bot(user_id) 27 | if not _bot: 28 | _bot = await db.get_userbot(user_id) 29 | if not _bot: 30 | return await message.reply("You didn't added any bot. Please add a bot using /settings !") 31 | channels = await db.get_user_channels(user_id) 32 | if not channels: 33 | return await message.reply_text("please set a to channel in /settings before forwarding") 34 | if len(channels) > 1: 35 | for channel in channels: 36 | buttons.append([KeyboardButton(f"{channel['title']}")]) 37 | btn_data[channel['title']] = channel['chat_id'] 38 | buttons.append([KeyboardButton("cancel")]) 39 | _toid = await bot.ask(message.chat.id, Script.TO_MSG.format(_bot['name'], _bot['username']), reply_markup=ReplyKeyboardMarkup(buttons, one_time_keyboard=True, resize_keyboard=True)) 40 | if _toid.text.startswith(('/', 'cancel')): 41 | return await message.reply_text(Script.CANCEL, reply_markup=ReplyKeyboardRemove()) 42 | to_title = _toid.text 43 | toid = btn_data.get(to_title) 44 | if not toid: 45 | return await message.reply_text("wrong channel choosen !", reply_markup=ReplyKeyboardRemove()) 46 | else: 47 | toid = channels[0]['chat_id'] 48 | to_title = channels[0]['title'] 49 | fromid = await bot.ask(message.chat.id, Script.FROM_MSG, reply_markup=ReplyKeyboardRemove()) 50 | if fromid.text and fromid.text.startswith('/'): 51 | await message.reply(Script.CANCEL) 52 | return 53 | if fromid.text and not fromid.forward_date: 54 | regex = re.compile("(https://)?(t\.me/|telegram\.me/|telegram\.dog/)(c/)?(\d+|[a-zA-Z_0-9]+)/(\d+)$") 55 | match = regex.match(fromid.text.replace("?single", "")) 56 | if not match: 57 | return await message.reply('Invalid link') 58 | chat_id = match.group(4) 59 | last_msg_id = int(match.group(5)) 60 | if chat_id.isnumeric(): 61 | chat_id = int(("-100" + chat_id)) 62 | elif fromid.forward_from_chat.type in [enums.ChatType.CHANNEL, 'supergroup']: 63 | last_msg_id = fromid.forward_from_message_id 64 | chat_id = fromid.forward_from_chat.username or fromid.forward_from_chat.id 65 | if last_msg_id == None: 66 | 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**") 67 | else: 68 | await message.reply_text("**invalid !**") 69 | return 70 | try: 71 | title = (await bot.get_chat(chat_id)).title 72 | # except ChannelInvalid: 73 | #return await fromid.reply("**Given source chat is copyrighted channel/group. you can't forward messages from there**") 74 | except (PrivateChat, ChannelPrivate, ChannelInvalid): 75 | title = "private" if fromid.text else fromid.forward_from_chat.title 76 | except (UsernameInvalid, UsernameNotModified): 77 | return await message.reply('Invalid Link specified.') 78 | except Exception as e: 79 | return await message.reply(f'Errors - {e}') 80 | skipno = await bot.ask(message.chat.id, Script.SKIP_MSG) 81 | if skipno.text.startswith('/'): 82 | await message.reply(Script.CANCEL) 83 | return 84 | forward_id = f"{user_id}-{skipno.id}" 85 | buttons = [[ 86 | InlineKeyboardButton('Yes', callback_data=f"start_public_{forward_id}"), 87 | InlineKeyboardButton('No', callback_data="close_btn") 88 | ]] 89 | reply_markup = InlineKeyboardMarkup(buttons) 90 | await message.reply_text( 91 | text=Script.DOUBLE_CHECK.format(botname=_bot['name'], botuname=_bot['username'], from_chat=title, to_chat=to_title, skip=skipno.text), 92 | disable_web_page_preview=True, 93 | reply_markup=reply_markup 94 | ) 95 | STS(forward_id).store(chat_id, toid, int(skipno.text), int(last_msg_id)) 96 | 97 | # Don't Remove Credit Tg - @VJ_Botz 98 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 99 | # Ask Doubt on telegram @KingVJ01 100 | -------------------------------------------------------------------------------- /plugins/regix.py: -------------------------------------------------------------------------------- 1 | # Don't Remove Credit Tg - @VJ_Botz 2 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 3 | # Ask Doubt on telegram @KingVJ01 4 | 5 | import os 6 | import sys 7 | import math 8 | import time, re 9 | import asyncio 10 | import logging 11 | import random 12 | from .utils import STS 13 | from database import Db, db 14 | from .test import CLIENT, get_client, iter_messages 15 | from config import Config, temp 16 | from script import Script 17 | from pyrogram import Client, filters 18 | from pyrogram.errors import FloodWait, MessageNotModified 19 | from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup, CallbackQuery, Message 20 | from .db import connect_user_db 21 | from pyrogram.types import Message 22 | 23 | # Don't Remove Credit Tg - @VJ_Botz 24 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 25 | # Ask Doubt on telegram @KingVJ01 26 | 27 | CLIENT = CLIENT() 28 | logger = logging.getLogger(__name__) 29 | logger.setLevel(logging.INFO) 30 | TEXT = Script.TEXT 31 | 32 | # Don't Remove Credit Tg - @VJ_Botz 33 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 34 | # Ask Doubt on telegram @KingVJ01 35 | 36 | @Client.on_callback_query(filters.regex(r'^start_public')) 37 | async def pub_(bot, message): 38 | user = message.from_user.id 39 | temp.CANCEL[user] = False 40 | frwd_id = message.data.split("_")[2] 41 | if temp.lock.get(user) and str(temp.lock.get(user))=="True": 42 | return await message.answer("please wait until previous task complete", show_alert=True) 43 | sts = STS(frwd_id) 44 | if not sts.verify(): 45 | await message.answer("your are clicking on my old button", show_alert=True) 46 | return await message.message.delete() 47 | i = sts.get(full=True) 48 | if i.TO in temp.IS_FRWD_CHAT: 49 | return await message.answer("In Target chat a task is progressing. please wait until task complete", show_alert=True) 50 | m = await msg_edit(message.message, "verifying your data's, please wait.") 51 | _bot, caption, forward_tag, datas, protect, button = await sts.get_data(user) 52 | filter = datas['filters'] 53 | max_size = datas['max_size'] 54 | min_size = datas['min_size'] 55 | keyword = datas['keywords'] 56 | exten = datas['extensions'] 57 | keywords = "" 58 | extensions = "" 59 | if keyword: 60 | for key in keyword: 61 | keywords += f"{key}|" 62 | keywords = keywords.rstrip("|") 63 | else: 64 | keywords = None 65 | if exten: 66 | for ext in exten: 67 | extensions += f"{ext}|" 68 | extensions = extensions.rstrip("|") 69 | else: 70 | extensions = None 71 | if not _bot: 72 | return await msg_edit(m, "You didn't added any bot. Please add a bot using /settings !", wait=True) 73 | if _bot['is_bot'] == True: 74 | data = _bot['token'] 75 | else: 76 | data = _bot['session'] 77 | try: 78 | il = True if _bot['is_bot'] == True else False 79 | client = await get_client(data, is_bot=il) 80 | await client.start() 81 | except Exception as e: 82 | return await m.edit(e) 83 | await msg_edit(m, "processing..") 84 | try: 85 | await client.get_messages(sts.get("FROM"), sts.get("limit")) 86 | except: 87 | 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) 88 | return await stop(client, user) 89 | try: 90 | k = await client.send_message(i.TO, "Testing") 91 | await k.delete() 92 | except: 93 | 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) 94 | return await stop(client, user) 95 | user_have_db = False 96 | dburi = datas['db_uri'] 97 | if dburi is not None: 98 | connected, user_db = await connect_user_db(user, dburi, i.TO) 99 | if not connected: 100 | await msg_edit(m, "Cannot Connected Your db Errors Found Dup files Have Been Skipped after Restart") 101 | else: 102 | user_have_db = True 103 | temp.forwardings += 1 104 | await db.add_frwd(user) 105 | await send(client, user, "Fᴏʀᴡᴀᴅɪɴɢ sᴛᴀʀᴛᴇᴅ🔥") 106 | sts.add(time=True) 107 | sleep = 1 if _bot['is_bot'] else 10 108 | await msg_edit(m, "processing...") 109 | temp.IS_FRWD_CHAT.append(i.TO) 110 | temp.lock[user] = locked = True 111 | dup_files = [] 112 | if locked: 113 | try: 114 | MSG = [] 115 | pling=0 116 | await edit(user, m, 'ᴘʀᴏɢʀᴇssɪɴɢ', 5, sts) 117 | async for message in iter_messages(client, chat_id=sts.get("FROM"), limit=sts.get("limit"), offset=sts.get("skip"), filters=filter, max_size=max_size): 118 | if await is_cancelled(client, user, m, sts): 119 | if user_have_db: 120 | await user_db.drop_all() 121 | await user_db.close() 122 | return 123 | if pling %20 == 0: 124 | await edit(user, m, 'ᴘʀᴏɢʀᴇssɪɴɢ', 5, sts) 125 | pling += 1 126 | sts.add('fetched') 127 | if message == "DUPLICATE": 128 | sts.add('duplicate') 129 | continue 130 | elif message == "FILTERED": 131 | sts.add('filtered') 132 | continue 133 | elif message.empty or message.service: 134 | sts.add('deleted') 135 | continue 136 | elif message.document and await extension_filter(extensions, message.document.file_name): 137 | sts.add('filtered') 138 | continue 139 | elif message.document and await keyword_filter(keywords, message.document.file_name): 140 | sts.add('filtered') 141 | continue 142 | elif message.document and await size_filter(max_size, min_size, message.document.file_size): 143 | sts.add('filtered') 144 | continue 145 | elif message.document and message.document.file_id in dup_files: 146 | sts.add('duplicate') 147 | continue 148 | if message.document and datas['skip_duplicate']: 149 | dup_files.append(message.document.file_id) 150 | if user_have_db: 151 | await user_db.add_file(message.document.file_id) 152 | if forward_tag: 153 | MSG.append(message.id) 154 | notcompleted = len(MSG) 155 | completed = sts.get('total') - sts.get('fetched') 156 | if ( notcompleted >= 100 157 | or completed <= 100): 158 | await forward(user, client, MSG, m, sts, protect) 159 | sts.add('total_files', notcompleted) 160 | await asyncio.sleep(10) 161 | MSG = [] 162 | else: 163 | new_caption = custom_caption(message, caption) 164 | details = {"msg_id": message.id, "media": media(message), "caption": new_caption, 'button': button, "protect": protect} 165 | await copy(user, client, details, m, sts) 166 | sts.add('total_files') 167 | await asyncio.sleep(sleep) 168 | except Exception as e: 169 | await msg_edit(m, f'ERROR:\n{e}', wait=True) 170 | print(e) 171 | if user_have_db: 172 | await user_db.drop_all() 173 | await user_db.close() 174 | temp.IS_FRWD_CHAT.remove(sts.TO) 175 | return await stop(client, user) 176 | temp.IS_FRWD_CHAT.remove(sts.TO) 177 | await send(client, user, "🎉 ғᴏʀᴡᴀᴅɪɴɢ ᴄᴏᴍᴘʟᴇᴛᴇᴅ") 178 | await edit(user, m, 'ᴄᴏᴍᴘʟᴇᴛᴇᴅ', "completed", sts) 179 | if user_have_db: 180 | await user_db.drop_all() 181 | await user_db.close() 182 | await stop(client, user) 183 | 184 | # Don't Remove Credit Tg - @VJ_Botz 185 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 186 | # Ask Doubt on telegram @KingVJ01 187 | 188 | async def copy(user, bot, msg, m, sts): 189 | try: 190 | if msg.get("media") and msg.get("caption"): 191 | await bot.send_cached_media( 192 | chat_id=sts.get('TO'), 193 | file_id=msg.get("media"), 194 | caption=msg.get("caption"), 195 | reply_markup=msg.get('button'), 196 | protect_content=msg.get("protect")) 197 | else: 198 | await bot.copy_message( 199 | chat_id=sts.get('TO'), 200 | from_chat_id=sts.get('FROM'), 201 | caption=msg.get("caption"), 202 | message_id=msg.get("msg_id"), 203 | reply_markup=msg.get('button'), 204 | protect_content=msg.get("protect")) 205 | except FloodWait as e: 206 | await edit(user, m, 'ᴘʀᴏɢʀᴇssɪɴɢ', e.value, sts) 207 | await asyncio.sleep(e.value) 208 | await edit(user, m, 'ᴘʀᴏɢʀᴇssɪɴɢ', 5, sts) 209 | await copy(user, bot, msg, m, sts) 210 | except Exception as e: 211 | print(e) 212 | sts.add('deleted') 213 | 214 | # Don't Remove Credit Tg - @VJ_Botz 215 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 216 | # Ask Doubt on telegram @KingVJ01 217 | 218 | async def forward(user, bot, msg, m, sts, protect): 219 | try: 220 | await bot.forward_messages( 221 | chat_id=sts.get('TO'), 222 | from_chat_id=sts.get('FROM'), 223 | protect_content=protect, 224 | message_ids=msg) 225 | except FloodWait as e: 226 | await edit(user, m, 'ᴘʀᴏɢʀᴇssɪɴɢ', e.value, sts) 227 | await asyncio.sleep(e.value) 228 | await edit(user, m, 'ᴘʀᴏɢʀᴇssɪɴɢ', 5, sts) 229 | await forward(bot, msg, m, sts, protect) 230 | 231 | # Don't Remove Credit Tg - @VJ_Botz 232 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 233 | # Ask Doubt on telegram @KingVJ01 234 | 235 | async def msg_edit(msg, text, button=None, wait=None): 236 | try: 237 | return await msg.edit(text, reply_markup=button) 238 | except MessageNotModified: 239 | pass 240 | except FloodWait as e: 241 | if wait: 242 | await asyncio.sleep(e.value) 243 | return await msg_edit(msg, text, button, wait) 244 | 245 | # Don't Remove Credit Tg - @VJ_Botz 246 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 247 | # Ask Doubt on telegram @KingVJ01 248 | 249 | async def edit(user, msg, title, status, sts): 250 | i = sts.get(full=True) 251 | status = 'Forwarding' if status == 5 else f"sleeping {status} s" if str(status).isnumeric() else status 252 | percentage = "{:.0f}".format(float(i.fetched)*100/float(i.total)) 253 | text = TEXT.format(i.fetched, i.total_files, i.duplicate, i.deleted, i.skip, i.filtered, status, percentage, title) 254 | await update_forward(user_id=user, last_id=None, start_time=i.start, limit=i.limit, chat_id=i.FROM, toid=i.TO, forward_id=None, msg_id=msg.id, fetched=i.fetched, deleted=i.deleted, total=i.total_files, duplicate=i.duplicate, skip=i.skip, filterd=i.filtered) 255 | now = time.time() 256 | diff = int(now - i.start) 257 | speed = sts.divide(i.fetched, diff) 258 | elapsed_time = round(diff) * 1000 259 | time_to_completion = round(sts.divide(i.total - i.fetched, int(speed))) * 1000 260 | estimated_total_time = elapsed_time + time_to_completion 261 | progress = "●{0}{1}".format( 262 | ''.join(["●" for i in range(math.floor(int(percentage) / 4))]), 263 | ''.join(["○" for i in range(24 - math.floor(int(percentage) / 4))])) 264 | button = [[InlineKeyboardButton(progress, f'fwrdstatus#{status}#{estimated_total_time}#{percentage}#{i.id}')]] 265 | estimated_total_time = TimeFormatter(milliseconds=estimated_total_time) 266 | estimated_total_time = estimated_total_time if estimated_total_time != '' else '0 s' 267 | if status in ["cancelled", "completed"]: 268 | button.append([InlineKeyboardButton('• ᴄᴏᴍᴘʟᴇᴛᴇᴅ ​•', url='https://t.me/VJ_BOTZ')]) 269 | else: 270 | button.append([InlineKeyboardButton('• ᴄᴀɴᴄᴇʟ', 'terminate_frwd')]) 271 | await msg_edit(msg, text, InlineKeyboardMarkup(button)) 272 | 273 | # Don't Remove Credit Tg - @VJ_Botz 274 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 275 | # Ask Doubt on telegram @KingVJ01 276 | 277 | async def is_cancelled(client, user, msg, sts): 278 | if temp.CANCEL.get(user)==True: 279 | if sts.TO in temp.IS_FRWD_CHAT: 280 | temp.IS_FRWD_CHAT.remove(sts.TO) 281 | await edit(user, msg, 'ᴄᴀɴᴄᴇʟʟᴇᴅ', "cancelled", sts) 282 | await send(client, user, "❌ ғᴏʀᴡᴀᴅɪɴɢ ᴄᴀɴᴄᴇʟʟᴇᴅ") 283 | await stop(client, user) 284 | return True 285 | return False 286 | 287 | # Don't Remove Credit Tg - @VJ_Botz 288 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 289 | # Ask Doubt on telegram @KingVJ01 290 | 291 | async def stop(client, user): 292 | try: 293 | await client.stop() 294 | except: 295 | pass 296 | await db.rmve_frwd(user) 297 | temp.forwardings -= 1 298 | temp.lock[user] = False 299 | 300 | # Don't Remove Credit Tg - @VJ_Botz 301 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 302 | # Ask Doubt on telegram @KingVJ01 303 | 304 | async def send(bot, user, text): 305 | try: 306 | await bot.send_message(user, text=text) 307 | except: 308 | pass 309 | 310 | # Don't Remove Credit Tg - @VJ_Botz 311 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 312 | # Ask Doubt on telegram @KingVJ01 313 | 314 | def custom_caption(msg, caption): 315 | if msg.media: 316 | if (msg.video or msg.document or msg.audio or msg.photo): 317 | media = getattr(msg, msg.media.value, None) 318 | if media: 319 | file_name = getattr(media, 'file_name', '') 320 | file_size = getattr(media, 'file_size', '') 321 | fcaption = getattr(msg, 'caption', '') 322 | if fcaption: 323 | fcaption = fcaption.html 324 | if caption: 325 | return caption.format(filename=file_name, size=get_size(file_size), caption=fcaption) 326 | return fcaption 327 | return None 328 | 329 | # Don't Remove Credit Tg - @VJ_Botz 330 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 331 | # Ask Doubt on telegram @KingVJ01 332 | 333 | def get_size(size): 334 | units = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB"] 335 | size = float(size) 336 | i = 0 337 | while size >= 1024.0 and i < len(units): 338 | i += 1 339 | size /= 1024.0 340 | return "%.2f %s" % (size, units[i]) 341 | 342 | # Don't Remove Credit Tg - @VJ_Botz 343 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 344 | # Ask Doubt on telegram @KingVJ01 345 | 346 | async def keyword_filter(keywords, file_name): 347 | if keywords is None: 348 | return False 349 | if re.search(keywords, file_name): 350 | return False 351 | else: 352 | return True 353 | 354 | # Don't Remove Credit Tg - @VJ_Botz 355 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 356 | # Ask Doubt on telegram @KingVJ01 357 | 358 | async def extension_filter(extensions, file_name): 359 | if extensions is None: 360 | return False 361 | if not re.search(extensions, file_name): 362 | return False 363 | else: 364 | return True 365 | 366 | # Don't Remove Credit Tg - @VJ_Botz 367 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 368 | # Ask Doubt on telegram @KingVJ01 369 | 370 | async def size_filter(max_size, min_size, file_size): 371 | file_size = file_size / 1024 / 1024 372 | if max_size and min_size == 0: 373 | return False 374 | if max_size == 0: 375 | return file_size < min_size 376 | if min_size == 0: 377 | return file_size > max_size 378 | if not min_size <= file_size <= max_size: 379 | return True 380 | else: 381 | return False 382 | 383 | # Don't Remove Credit Tg - @VJ_Botz 384 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 385 | # Ask Doubt on telegram @KingVJ01 386 | 387 | def media(msg): 388 | if msg.media: 389 | media = getattr(msg, msg.media.value, None) 390 | if media: 391 | return getattr(media, 'file_id', None) 392 | return None 393 | 394 | # Don't Remove Credit Tg - @VJ_Botz 395 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 396 | # Ask Doubt on telegram @KingVJ01 397 | 398 | def TimeFormatter(milliseconds: int) -> str: 399 | seconds, milliseconds = divmod(int(milliseconds), 1000) 400 | minutes, seconds = divmod(seconds, 60) 401 | hours, minutes = divmod(minutes, 60) 402 | days, hours = divmod(hours, 24) 403 | tmp = ((str(days) + "d, ") if days else "") + \ 404 | ((str(hours) + "h, ") if hours else "") + \ 405 | ((str(minutes) + "m, ") if minutes else "") + \ 406 | ((str(seconds) + "s, ") if seconds else "") + \ 407 | ((str(milliseconds) + "ms, ") if milliseconds else "") 408 | return tmp[:-2] 409 | 410 | # Don't Remove Credit Tg - @VJ_Botz 411 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 412 | # Ask Doubt on telegram @KingVJ01 413 | 414 | def retry_btn(id): 415 | return InlineKeyboardMarkup([[InlineKeyboardButton('♻️ RETRY ♻️', f"start_public_{id}")]]) 416 | 417 | # Don't Remove Credit Tg - @VJ_Botz 418 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 419 | # Ask Doubt on telegram @KingVJ01 420 | 421 | @Client.on_callback_query(filters.regex(r'^terminate_frwd$')) 422 | async def terminate_frwding(bot, m): 423 | user_id = m.from_user.id 424 | temp.lock[user_id] = False 425 | temp.CANCEL[user_id] = True 426 | await m.answer("Forwarding cancelled !", show_alert=True) 427 | 428 | # Don't Remove Credit Tg - @VJ_Botz 429 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 430 | # Ask Doubt on telegram @KingVJ01 431 | 432 | @Client.on_callback_query(filters.regex(r'^fwrdstatus')) 433 | async def status_msg(bot, msg): 434 | _, status, est_time, percentage, frwd_id = msg.data.split("#") 435 | sts = STS(frwd_id) 436 | if not sts.verify(): 437 | fetched, forwarded, remaining = 0 438 | else: 439 | fetched, limit, forwarded = sts.get('fetched'), sts.get('limit'), sts.get('total_files') 440 | remaining = limit - fetched 441 | est_time = TimeFormatter(milliseconds=est_time) 442 | start_time = sts.get('start') 443 | uptime = await get_bot_uptime(start_time) 444 | total = sts.get('limit') - sts.get('fetched') 445 | time_to_comple = await complete_time(total) 446 | est_time = est_time if (est_time != '' or status not in ['completed', 'cancelled']) else '0 s' 447 | return await msg.answer(PROGRESS.format(percentage, fetched, forwarded, remaining, status, time_to_comple, uptime), show_alert=True) 448 | 449 | # Don't Remove Credit Tg - @VJ_Botz 450 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 451 | # Ask Doubt on telegram @KingVJ01 452 | 453 | @Client.on_callback_query(filters.regex(r'^close_btn$')) 454 | async def close(bot, update): 455 | await update.answer() 456 | await update.message.delete() 457 | 458 | # Don't Remove Credit Tg - @VJ_Botz 459 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 460 | # Ask Doubt on telegram @KingVJ01 461 | 462 | @Client.on_message(filters.private & filters.command(['stop'])) 463 | async def stop_forward(client, message): 464 | user_id = message.from_user.id 465 | sts = await message.reply('Stoping...') 466 | await asyncio.sleep(0.5) 467 | if not await db.is_forwad_exit(message.from_user.id): 468 | return await sts.edit('**No Ongoing Forwards To Cancel**') 469 | temp.lock[user_id] = False 470 | temp.CANCEL[user_id] = True 471 | mst = await db.get_forward_details(user_id) 472 | msg = await client.get_messages(user_id, mst['msg_id']) 473 | link = f"tg://openmessage?user_id={6648261085}&message_id={mst['msg_id']}" 474 | await sts.edit(f"Successfully Canceled ", disable_web_page_preview=True) 475 | 476 | # Don't Remove Credit Tg - @VJ_Botz 477 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 478 | # Ask Doubt on telegram @KingVJ01 479 | 480 | async def restart_pending_forwads(bot, user): 481 | user = user['user_id'] 482 | settings = await db.get_forward_details(user) 483 | try: 484 | skiping = settings['offset'] 485 | fetch = settings['fetched'] - settings['skip'] 486 | temp.forwardings += 1 487 | forward_id = await store_vars(user) 488 | sts = STS(forward_id) 489 | if settings['chat_id'] is None: 490 | return await db.rmve_frwd(user) 491 | temp.forwardings -= 1 492 | if not sts.verify(): 493 | temp.forwardings -=1 494 | return 495 | sts.add('fetched', value=fetch) 496 | sts.add('duplicate', value=settings['duplicate']) 497 | sts.add('filtered', value=settings['filtered']) 498 | sts.add('deleted', value=settings['deleted']) 499 | sts.add('total_files', value=settings['total']) 500 | m = await bot.get_messages(user, settings['msg_id'])# 501 | _bot, caption, forward_tag, datas, protect, button = await sts.get_data(user) 502 | i = sts.get(full=True) 503 | filter = datas['filters'] 504 | max_size = datas['max_size'] 505 | min_size = datas['min_size'] 506 | keyword = datas['keywords'] 507 | exten = datas['extensions'] 508 | keywords = "" 509 | extensions = "" 510 | if keyword: 511 | for key in keyword: 512 | keywords += f"{key}|" 513 | keywords = keywords.rstrip("|") 514 | else: 515 | keywords = None 516 | if exten: 517 | for ext in exten: 518 | extensions += f"{ext}|" 519 | extensions = extensions.rstrip("|") 520 | else: 521 | extensions = None 522 | if not _bot: 523 | return await msg_edit(m, "You didn't added any bot. Please add a bot using /settings !", wait=True) 524 | if _bot['is_bot'] == True: 525 | data = _bot['token'] 526 | else: 527 | data = _bot['session'] 528 | try: 529 | il = True if _bot['is_bot'] == True else False 530 | client = await get_client(data, is_bot=il) 531 | await client.start() 532 | except Exception as e: 533 | return await m.edit(e) 534 | try: 535 | await msg_edit(m, "processing..") 536 | except: 537 | return await db.rmve_frwd(user) 538 | try: 539 | await client.get_messages(sts.get("FROM"), sts.get("limit")) 540 | except: 541 | 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(firwd_id), True) 542 | return await stop(client, user) 543 | try: 544 | k = await client.send_message(i.TO, "Testing") 545 | await k.delete() 546 | except: 547 | await msg_edit(m, f"**Please Make Your [UserBot / Bot](t.me/{_bot['username']}) Admin In Target Channel With Full Permissions**", retry_btn(forward_id), True) 548 | return await stop(client, user) 549 | except: 550 | return await db.rmve_frwd(user) 551 | user_have_db = False 552 | dburi = datas['db_uri'] 553 | if dburi is not None: 554 | connected, user_db = await connect_user_db(user, dburi, i.TO) 555 | if not connected: 556 | await msg_edit(m, "Cannot Connected Your db Errors Found Dup files Have Been Skipped after Restart") 557 | else: 558 | user_have_db = True 559 | try: 560 | start = settings['start_time'] 561 | except KeyError: 562 | start = None 563 | sts.add(time=True, start_time=start) 564 | sleep = 1 if _bot['is_bot'] else 10 565 | #await msg_edit(m, "processing...") 566 | temp.IS_FRWD_CHAT.append(i.TO) 567 | temp.lock[user] = locked = True 568 | dup_files = [] 569 | if user_have_db and datas['skip_duplicate']: 570 | old_files = await user_db.get_all_files() 571 | async for ofile in old_files: 572 | dup_files.append(ofile["file_id"]) 573 | if locked: 574 | try: 575 | MSG = [] 576 | pling=0 577 | await edit(user, m, 'ᴘʀᴏɢʀᴇssɪɴɢ', 5, sts) 578 | async for message in iter_messages(client, chat_id=sts.get("FROM"), limit=sts.get("limit"), offset=skiping, filters=filter, max_size=max_size): 579 | if await is_cancelled(client, user, m, sts): 580 | if user_have_db: 581 | await user_db.drop_all() 582 | await user_db.close() 583 | return 584 | return 585 | if pling %20 == 0: 586 | await edit(user, m, 'ᴘʀᴏɢʀᴇssɪɴɢ', 5, sts) 587 | pling += 1 588 | sts.add('fetched') 589 | if message == "DUPLICATE": 590 | sts.add('duplicate') 591 | continue 592 | elif message == "FILTERED": 593 | sts.add('filtered') 594 | continue 595 | elif message.empty or message.service: 596 | sts.add('deleted') 597 | continue 598 | elif message.document and await extension_filter(extensions, message.document.file_name): 599 | sts.add('filtered') 600 | continue 601 | elif message.document and await keyword_filter(keywords, message.document.file_name): 602 | sts.add('filtered') 603 | continue 604 | elif message.document and await size_filter(max_size, min_size, message.document.file_size): 605 | sts.add('filtered') 606 | continue 607 | elif message.document and message.document.file_id in dup_files: 608 | sts.add('duplicate') 609 | continue 610 | if message.document and datas['skip_duplicate']: 611 | dup_files.append(message.document.file_id) 612 | if user_have_db: 613 | await user_db.add_file(message.document.file_id) 614 | if forward_tag: 615 | MSG.append(message.id) 616 | notcompleted = len(MSG) 617 | completed = sts.get('total') - sts.get('fetched') 618 | if ( notcompleted >= 100 619 | or completed <= 100): 620 | await forward(user, client, MSG, m, sts, protect) 621 | sts.add('total_files', notcompleted) 622 | await asyncio.sleep(10) 623 | MSG = [] 624 | else: 625 | new_caption = custom_caption(message, caption) 626 | details = {"msg_id": message.id, "media": media(message), "caption": new_caption, 'button': button, "protect": protect} 627 | await copy(user, client, details, m, sts) 628 | sts.add('total_files') 629 | await asyncio.sleep(sleep) 630 | except Exception as e: 631 | await msg_edit(m, f'ERROR:\n{e}', wait=True) 632 | if user_have_db: 633 | await user_db.drop_all() 634 | await user_db.close() 635 | temp.IS_FRWD_CHAT.remove(sts.TO) 636 | return await stop(client, user) 637 | temp.IS_FRWD_CHAT.remove(sts.TO) 638 | await send(client, user, "🎉 ғᴏʀᴡᴀᴅɪɴɢ ᴄᴏᴍᴘʟᴇᴛᴇᴅ") 639 | if user_have_db: 640 | await user_db.drop_all() 641 | await user_db.close() 642 | await edit(user, m, 'ᴄᴏᴍᴘʟᴇᴛᴇᴅ', "completed", sts) 643 | await stop(client, user) 644 | 645 | # Don't Remove Credit Tg - @VJ_Botz 646 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 647 | # Ask Doubt on telegram @KingVJ01 648 | 649 | async def store_vars(user_id): 650 | settings = await db.get_forward_details(user_id) 651 | fetch = settings['fetched'] 652 | forward_id = f'{user_id}-{fetch}' 653 | print(fetch) 654 | STS(id=forward_id).store(settings['chat_id'], settings['toid'], settings['skip'], settings['limit']) 655 | return forward_id 656 | 657 | # Don't Remove Credit Tg - @VJ_Botz 658 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 659 | # Ask Doubt on telegram @KingVJ01 660 | 661 | async def restart_forwards(client): 662 | users = await db.get_all_frwd() 663 | count = await db.forwad_count() 664 | tasks = [] 665 | async for user in users: 666 | tasks.append(restart_pending_forwads(client, user)) 667 | random_seconds = random.randint(0, 300) 668 | minutes = random_seconds // 60 669 | seconds = random_seconds % 60 670 | await asyncio.gather(*tasks) 671 | print('Done') 672 | 673 | # Don't Remove Credit Tg - @VJ_Botz 674 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 675 | # Ask Doubt on telegram @KingVJ01 676 | 677 | async def update_forward(user_id, chat_id, start_time, toid, last_id, limit, forward_id, msg_id, fetched, total, duplicate, deleted, skip, filterd): 678 | details = { 679 | 'chat_id': chat_id, 680 | 'toid': toid, 681 | 'forward_id': forward_id, 682 | 'last_id': last_id, 683 | 'limit': limit, 684 | 'msg_id': msg_id, 685 | 'start_time': start_time, 686 | 'fetched': fetched, 687 | 'offset': fetched, 688 | 'deleted': deleted, 689 | 'total': total, 690 | 'duplicate': duplicate, 691 | 'skip': skip, 692 | 'filtered':filterd 693 | } 694 | await db.update_forward(user_id, details) 695 | 696 | # Don't Remove Credit Tg - @VJ_Botz 697 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 698 | # Ask Doubt on telegram @KingVJ01 699 | 700 | async def get_bot_uptime(start_time): 701 | # Calculate the uptime in seconds 702 | uptime_seconds = int(time.time() - start_time) 703 | uptime_minutes = uptime_seconds // 60 704 | uptime_hours = uptime_minutes // 60 705 | uptime_days = uptime_hours // 24 706 | uptime_weeks = uptime_days // 7 707 | uptime_string = "" 708 | if uptime_weeks != 0: 709 | uptime_string += f"{uptime_weeks % 7}w, " 710 | if uptime_days != 0: 711 | uptime_string += f"{uptime_days % 24}d, " 712 | if uptime_hours != 0: 713 | uptime_string += f"{uptime_hours % 24}h, " 714 | if uptime_minutes != 0: 715 | uptime_string += f"{uptime_minutes % 60}m, " 716 | uptime_string += f"{uptime_seconds % 60}s" 717 | return uptime_string 718 | 719 | # Don't Remove Credit Tg - @VJ_Botz 720 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 721 | # Ask Doubt on telegram @KingVJ01 722 | 723 | async def complete_time(total_files, files_per_minute=30): 724 | minutes_required = total_files / files_per_minute 725 | seconds_required = minutes_required * 60 726 | weeks = seconds_required // (7 * 24 * 60 * 60) 727 | days = (seconds_required % (7 * 24 * 60 * 60)) // (24 * 60 * 60) 728 | hours = (seconds_required % (24 * 60 * 60)) // (60 * 60) 729 | minutes = (seconds_required % (60 * 60)) // 60 730 | seconds = seconds_required % 60 731 | time_format = "" 732 | if weeks > 0: 733 | time_format += f"{int(weeks)}w, " 734 | if days > 0: 735 | time_format += f"{int(days)}d, " 736 | if hours > 0: 737 | time_format += f"{int(hours)}h, " 738 | if minutes > 0: 739 | time_format += f"{int(minutes)}m, " 740 | if seconds > 0: 741 | time_format += f"{int(seconds)}s" 742 | return time_format 743 | 744 | # Don't Remove Credit Tg - @VJ_Botz 745 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 746 | # Ask Doubt on telegram @KingVJ01 747 | -------------------------------------------------------------------------------- /plugins/settings.py: -------------------------------------------------------------------------------- 1 | # Don't Remove Credit Tg - @VJ_Botz 2 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 3 | # Ask Doubt on telegram @KingVJ01 4 | 5 | import asyncio 6 | from database import Db, db 7 | from script import Script 8 | from pyrogram import Client, filters 9 | from .test import get_configs, update_configs, CLIENT, parse_buttons 10 | from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup 11 | from .db import connect_user_db 12 | 13 | CLIENT = CLIENT() 14 | 15 | # Don't Remove Credit Tg - @VJ_Botz 16 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 17 | # Ask Doubt on telegram @KingVJ01 18 | 19 | @Client.on_message(filters.command('settings')) 20 | async def settings(client, message): 21 | await message.reply_text( 22 | "Hᴇʀᴇ Is Tʜᴇ Sᴇᴛᴛɪɴɢs Pᴀɴᴇʟ⚙\n\nᴄʜᴀɴɢᴇ ʏᴏᴜʀ sᴇᴛᴛɪɴɢs ᴀs ʏᴏᴜʀ ᴡɪsʜ 👇", 23 | reply_markup=main_buttons() 24 | ) 25 | 26 | # Don't Remove Credit Tg - @VJ_Botz 27 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 28 | # Ask Doubt on telegram @KingVJ01 29 | 30 | @Client.on_callback_query(filters.regex(r'^settings')) 31 | async def settings_query(bot, query): 32 | user_id = query.from_user.id 33 | i, type = query.data.split("#") 34 | buttons = [[InlineKeyboardButton('back', callback_data="settings#main")]] 35 | if type=="main": 36 | await query.message.edit_text( 37 | "Hᴇʀᴇ Is Tʜᴇ Sᴇᴛᴛɪɴɢs Pᴀɴᴇʟ⚙\n\nᴄʜᴀɴɢᴇ ʏᴏᴜʀ sᴇᴛᴛɪɴɢs ᴀs ʏᴏᴜʀ ᴡɪsʜ 👇", 38 | reply_markup=main_buttons()) 39 | elif type=="extra": 40 | await query.message.edit_text( 41 | "Hᴇʀᴇ Is Tʜᴇ Exᴛʀᴀ Sᴇᴛᴛɪɴɢs Pᴀɴᴇʟ⚙", 42 | reply_markup=extra_buttons()) 43 | elif type=="bots": 44 | buttons = [] 45 | _bot = await db.get_bot(user_id) 46 | usr_bot = await db.get_userbot(user_id) 47 | if _bot is not None: 48 | buttons.append([InlineKeyboardButton(_bot['name'], 49 | callback_data=f"settings#editbot")]) 50 | else: 51 | buttons.append([InlineKeyboardButton('✚ Add bot ✚', 52 | callback_data="settings#addbot")]) 53 | if usr_bot is not None: 54 | buttons.append([InlineKeyboardButton(usr_bot['name'], 55 | callback_data=f"settings#edituserbot")]) 56 | else: 57 | buttons.append([InlineKeyboardButton('✚ Add User bot ✚', 58 | callback_data="settings#adduserbot")]) 59 | buttons.append([InlineKeyboardButton('back', 60 | callback_data="settings#main")]) 61 | await query.message.edit_text( 62 | "My Bots\n\nYou can manage your bots in here", 63 | reply_markup=InlineKeyboardMarkup(buttons)) 64 | 65 | elif type=="addbot": 66 | await query.message.delete() 67 | bot = await CLIENT.add_bot(bot, query) 68 | if bot != True: return 69 | await query.message.reply_text( 70 | "bot token successfully added to db", 71 | reply_markup=InlineKeyboardMarkup(buttons)) 72 | 73 | elif type=="adduserbot": 74 | await query.message.delete() 75 | user = await CLIENT.add_session(bot, query) 76 | if user != True: return 77 | await query.message.reply_text( 78 | "session successfully added to db", 79 | reply_markup=InlineKeyboardMarkup(buttons)) 80 | 81 | elif type=="channels": 82 | buttons = [] 83 | channels = await db.get_user_channels(user_id) 84 | for channel in channels: 85 | buttons.append([InlineKeyboardButton(f"{channel['title']}", 86 | callback_data=f"settings#editchannels_{channel['chat_id']}")]) 87 | buttons.append([InlineKeyboardButton('✚ Add Channel ✚', 88 | callback_data="settings#addchannel")]) 89 | buttons.append([InlineKeyboardButton('back', 90 | callback_data="settings#main")]) 91 | await query.message.edit_text( 92 | "My Channels\n\nyou can manage your target chats in here", 93 | reply_markup=InlineKeyboardMarkup(buttons)) 94 | 95 | elif type=="addchannel": 96 | await query.message.delete() 97 | chat_ids = await bot.ask(chat_id=query.from_user.id, text="❪ SET TARGET CHAT ❫\n\nForward a message from Your target chat\n/cancel - cancel this process") 98 | if chat_ids.text=="/cancel": 99 | return await chat_ids.reply_text( 100 | "process canceled", 101 | reply_markup=InlineKeyboardMarkup(buttons)) 102 | elif not chat_ids.forward_date: 103 | return await chat_ids.reply("**This is not a forward message**") 104 | else: 105 | chat_id = chat_ids.forward_from_chat.id 106 | title = chat_ids.forward_from_chat.title 107 | username = chat_ids.forward_from_chat.username 108 | username = "@" + username if username else "private" 109 | chat = await db.add_channel(user_id, chat_id, title, username) 110 | await query.message.reply_text( 111 | "Successfully updated" if chat else "This channel already added", 112 | reply_markup=InlineKeyboardMarkup(buttons)) 113 | 114 | elif type=="editbot": 115 | bot = await db.get_bot(user_id) 116 | TEXT = Script.BOT_DETAILS if bot['is_bot'] else Script.USER_DETAILS 117 | buttons = [[InlineKeyboardButton('❌ Remove ❌', callback_data=f"settings#removebot") 118 | ], 119 | [InlineKeyboardButton('back', callback_data="settings#bots")]] 120 | await query.message.edit_text( 121 | TEXT.format(bot['name'], bot['id'], bot['username']), 122 | reply_markup=InlineKeyboardMarkup(buttons)) 123 | 124 | elif type=="edituserbot": 125 | bot = await db.get_userbot(user_id) 126 | TEXT = Script.USER_DETAILS 127 | buttons = [[InlineKeyboardButton('❌ Remove ❌', callback_data=f"settings#removeuserbot") 128 | ], 129 | [InlineKeyboardButton('back', callback_data="settings#bots")]] 130 | await query.message.edit_text( 131 | TEXT.format(bot['name'], bot['id'], bot['username']), 132 | reply_markup=InlineKeyboardMarkup(buttons)) 133 | 134 | elif type=="removebot": 135 | await db.remove_bot(user_id) 136 | await query.message.edit_text( 137 | "successfully updated", 138 | reply_markup=InlineKeyboardMarkup(buttons)) 139 | 140 | elif type=="removeuserbot": 141 | await db.remove_userbot(user_id) 142 | await query.message.edit_text( 143 | "successfully updated", 144 | reply_markup=InlineKeyboardMarkup(buttons)) 145 | 146 | elif type.startswith("editchannels"): 147 | chat_id = type.split('_')[1] 148 | chat = await db.get_channel_details(user_id, chat_id) 149 | buttons = [[InlineKeyboardButton('❌ Remove ❌', callback_data=f"settings#removechannel_{chat_id}") 150 | ], 151 | [InlineKeyboardButton('back', callback_data="settings#channels")]] 152 | await query.message.edit_text( 153 | f"📄 CHANNEL DETAILS\n\n- TITLE: {chat['title']}\n- CHANNEL ID: {chat['chat_id']}\n- USERNAME: {chat['username']}", 154 | reply_markup=InlineKeyboardMarkup(buttons)) 155 | 156 | elif type.startswith("removechannel"): 157 | chat_id = type.split('_')[1] 158 | await db.remove_channel(user_id, chat_id) 159 | await query.message.edit_text( 160 | "successfully updated", 161 | reply_markup=InlineKeyboardMarkup(buttons)) 162 | 163 | elif type=="caption": 164 | buttons = [] 165 | data = await get_configs(user_id) 166 | caption = data['caption'] 167 | if caption is None: 168 | buttons.append([InlineKeyboardButton('✚ Add Caption ✚', 169 | callback_data="settings#addcaption")]) 170 | else: 171 | buttons.append([InlineKeyboardButton('See Caption', 172 | callback_data="settings#seecaption")]) 173 | buttons[-1].append(InlineKeyboardButton('🗑️ Delete Caption', 174 | callback_data="settings#deletecaption")) 175 | buttons.append([InlineKeyboardButton('back', 176 | callback_data="settings#main")]) 177 | await query.message.edit_text( 178 | "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", 179 | reply_markup=InlineKeyboardMarkup(buttons)) 180 | 181 | elif type=="seecaption": 182 | data = await get_configs(user_id) 183 | buttons = [[InlineKeyboardButton('🖋️ Edit Caption', 184 | callback_data="settings#addcaption") 185 | ],[ 186 | InlineKeyboardButton('back', 187 | callback_data="settings#caption")]] 188 | await query.message.edit_text( 189 | f"YOUR CUSTOM CAPTION\n\n{data['caption']}", 190 | reply_markup=InlineKeyboardMarkup(buttons)) 191 | 192 | elif type=="deletecaption": 193 | await update_configs(user_id, 'caption', None) 194 | await query.message.edit_text( 195 | "successfully updated", 196 | reply_markup=InlineKeyboardMarkup(buttons)) 197 | 198 | elif type=="addcaption": 199 | await query.message.delete() 200 | caption = await bot.ask(query.message.chat.id, "Send your custom caption\n/cancel - cancel this process") 201 | if caption.text=="/cancel": 202 | return await caption.reply_text( 203 | "process canceled !", 204 | reply_markup=InlineKeyboardMarkup(buttons)) 205 | try: 206 | caption.text.format(filename='', size='', caption='') 207 | except KeyError as e: 208 | return await caption.reply_text( 209 | f"wrong filling {e} used in your caption. change it", 210 | reply_markup=InlineKeyboardMarkup(buttons)) 211 | await update_configs(user_id, 'caption', caption.text) 212 | await caption.reply_text( 213 | "successfully updated", 214 | reply_markup=InlineKeyboardMarkup(buttons)) 215 | 216 | elif type=="button": 217 | buttons = [] 218 | button = (await get_configs(user_id))['button'] 219 | if button is None: 220 | buttons.append([InlineKeyboardButton('✚ Add Button ✚', 221 | callback_data="settings#addbutton")]) 222 | else: 223 | buttons.append([InlineKeyboardButton('👀 See Button', 224 | callback_data="settings#seebutton")]) 225 | buttons[-1].append(InlineKeyboardButton('🗑️ Remove Button ', 226 | callback_data="settings#deletebutton")) 227 | buttons.append([InlineKeyboardButton('back', 228 | callback_data="settings#main")]) 229 | await query.message.edit_text( 230 | "CUSTOM BUTTON\n\nYou can set a inline button to messages.\n\nFORMAT:\n`[Forward bot][buttonurl:https://t.me/mychannelurl]`\n", 231 | reply_markup=InlineKeyboardMarkup(buttons)) 232 | 233 | elif type=="addbutton": 234 | await query.message.delete() 235 | ask = await bot.ask(user_id, text="**Send your custom button.\n\nFORMAT:**\n`[forward bot][buttonurl:https://t.me/url]`\n") 236 | button = parse_buttons(ask.text.html) 237 | if not button: 238 | return await ask.reply("**INVALID BUTTON**") 239 | await update_configs(user_id, 'button', ask.text.html) 240 | await ask.reply("**Successfully button added**", 241 | reply_markup=InlineKeyboardMarkup(buttons)) 242 | 243 | elif type=="seebutton": 244 | button = (await get_configs(user_id))['button'] 245 | button = parse_buttons(button, markup=False) 246 | button.append([InlineKeyboardButton("back", "settings#button")]) 247 | await query.message.edit_text( 248 | "**YOUR CUSTOM BUTTON**", 249 | reply_markup=InlineKeyboardMarkup(button)) 250 | 251 | elif type=="deletebutton": 252 | await update_configs(user_id, 'button', None) 253 | await query.message.edit_text( 254 | "**Successfully button deleted**", 255 | reply_markup=InlineKeyboardMarkup(buttons)) 256 | 257 | elif type=="database": 258 | buttons = [] 259 | db_uri = (await get_configs(user_id))['db_uri'] 260 | if db_uri is None: 261 | buttons.append([InlineKeyboardButton('✚ Add Mongo Url ', 262 | callback_data="settings#addurl")]) 263 | else: 264 | buttons.append([InlineKeyboardButton('👀 See Url', 265 | callback_data="settings#seeurl")]) 266 | buttons[-1].append(InlineKeyboardButton('❌ Remove Url ', 267 | callback_data="settings#deleteurl")) 268 | buttons.append([InlineKeyboardButton('back', 269 | callback_data="settings#main")]) 270 | await query.message.edit_text( 271 | "DATABASE\n\nDatabase is required for store your duplicate messages permenant. other wise stored duplicate media may be disappeared when after bot restart.", 272 | reply_markup=InlineKeyboardMarkup(buttons)) 273 | 274 | elif type=="addurl": 275 | await query.message.delete() 276 | uri = await bot.ask(user_id, "please send your mongodb url.\n\nget your Mongodb url from [MangoDb](https://mongodb.com)", disable_web_page_preview=True) 277 | if uri.text=="/cancel": 278 | return await uri.reply_text( 279 | "process canceled !", 280 | reply_markup=InlineKeyboardMarkup(buttons)) 281 | if not uri.text.startswith("mongodb+srv://") and not uri.text.endswith("majority"): 282 | return await uri.reply("Invalid Mongodb Url", 283 | reply_markup=InlineKeyboardMarkup(buttons)) 284 | connect, udb = await connect_user_db(user_id, uri.text, "test") 285 | if connect: 286 | await udb.drop_all() 287 | await udb.close() 288 | else: 289 | return await uri.reply("Invalid Mongodb Url Cant Connect With This Uri", 290 | reply_markup=InlineKeyboardMarkup(buttons)) 291 | await update_configs(user_id, 'db_uri', uri.text) 292 | await uri.reply("**Successfully database url added**", 293 | reply_markup=InlineKeyboardMarkup(buttons)) 294 | 295 | elif type=="seeurl": 296 | db_uri = (await get_configs(user_id))['db_uri'] 297 | await query.answer(f"DATABASE URL: {db_uri}", show_alert=True) 298 | 299 | elif type=="deleteurl": 300 | await update_configs(user_id, 'db_uri', None) 301 | await query.message.edit_text( 302 | "**Successfully your database url deleted**", 303 | reply_markup=InlineKeyboardMarkup(buttons)) 304 | 305 | elif type=="filters": 306 | await query.message.edit_text( 307 | "💠 CUSTOM FILTERS 💠\n\n**configure the type of messages which you want forward**", 308 | reply_markup=await filters_buttons(user_id)) 309 | 310 | elif type=="nextfilters": 311 | await query.edit_message_reply_markup( 312 | reply_markup=await next_filters_buttons(user_id)) 313 | 314 | elif type.startswith("updatefilter"): 315 | i, key, value = type.split('-') 316 | if value=="True": 317 | await update_configs(user_id, key, False) 318 | else: 319 | await update_configs(user_id, key, True) 320 | if key in ['poll', 'protect', 'voice', 'animation', 'sticker', 'duplicate']: 321 | return await query.edit_message_reply_markup( 322 | reply_markup=await next_filters_buttons(user_id)) 323 | await query.edit_message_reply_markup( 324 | reply_markup=await filters_buttons(user_id)) 325 | 326 | elif type.startswith("file_size"): 327 | settings = await get_configs(user_id) 328 | size = settings.get('min_size', 0) 329 | await query.message.edit_text( 330 | f'SIZE LIMIT\n\nyou can set file Minimum size limit to forward\n\nfiles with greater than `{size} MB` will forward', 331 | reply_markup=size_button(size)) 332 | 333 | elif type.startswith("maxfile_size"): 334 | settings = await get_configs(user_id) 335 | size = settings.get('max_size', 0) 336 | await query.message.edit_text( 337 | f'Max SIZE LIMIT\n\nyou can set file Maximum size limit to forward\n\nfiles with less than `{size} MB` will forward', 338 | reply_markup=maxsize_button(size)) 339 | 340 | elif type.startswith("update_size"): 341 | size = int(query.data.split('-')[1]) 342 | if 0 < size > 4000: 343 | return await query.answer("size limit exceeded", show_alert=True) 344 | await update_configs(user_id, 'min_size', size) 345 | i, limit = size_limit((await get_configs(user_id))['size_limit']) 346 | await query.message.edit_text( 347 | f'SIZE LIMIT\n\nyou can set file Minimum size limit to forward\n\nfiles with greater than `{size} MB` will forward', 348 | reply_markup=size_button(size)) 349 | 350 | elif type.startswith("maxupdate_size"): 351 | size = int(query.data.split('-')[1]) 352 | if 0 < size > 4000: 353 | return await query.answer("size limit exceeded", show_alert=True) 354 | await update_configs(user_id, 'max_size', size) 355 | i, limit = size_limit((await get_configs(user_id))['size_limit']) 356 | await query.message.edit_text( 357 | f'Max SIZE LIMIT\n\nyou can set file Maximum size limit to forward\n\nfiles with less than `{size} MB` will forward', 358 | reply_markup=maxsize_button(size)) 359 | 360 | elif type.startswith('update_limit'): 361 | i, limit, size = type.split('-') 362 | limit, sts = size_limit(limit) 363 | await update_configs(user_id, 'size_limit', limit) 364 | await query.message.edit_text( 365 | f'SIZE LIMIT\n\nyou can set file size limit to forward\n\nStatus: files with {sts} `{size} MB` will forward', 366 | reply_markup=size_button(int(size))) 367 | 368 | elif type == "add_extension": 369 | await query.message.delete() 370 | ext = await bot.ask(user_id, text="**please send your extensions (seperete by space)**") 371 | if ext.text == '/cancel': 372 | return await ext.reply_text( 373 | "process canceled", 374 | reply_markup=InlineKeyboardMarkup(buttons)) 375 | extensions = ext.text.split(" ") 376 | extension = (await get_configs(user_id))['extension'] 377 | if extension: 378 | for extn in extensions: 379 | extension.append(extn) 380 | else: 381 | extension = extensions 382 | await update_configs(user_id, 'extension', extension) 383 | buttons = [] 384 | buttons.append([InlineKeyboardButton('back', 385 | callback_data="settings#get_extension")]) 386 | await ext.reply_text( 387 | f"**successfully updated**", 388 | reply_markup=InlineKeyboardMarkup(buttons)) 389 | 390 | elif type == "get_extension": 391 | extensions = (await get_configs(user_id))['extension'] 392 | btn = [] 393 | text = "" 394 | if extensions: 395 | text += "**🕹 Extensions**" 396 | for ext in extensions: 397 | text += f"\n-{ext}" 398 | else: 399 | text += "** No Extensions Here**" 400 | btn.append([InlineKeyboardButton('✚ Add', 'settings#add_extension')]) 401 | btn.append([InlineKeyboardButton('Remove All', 'settings#rmve_all_extension')]) 402 | btn.append([InlineKeyboardButton('back', 'settings#extra')]) 403 | await query.message.edit_text( 404 | text=f"EXTENSIONS\n\n**Files with these extiontions will not forward**\n\n{text}", 405 | reply_markup=InlineKeyboardMarkup(btn)) 406 | 407 | elif type == "rmve_all_extension": 408 | await update_configs(user_id, 'extension', None) 409 | buttons = [] 410 | buttons.append([InlineKeyboardButton('back', 411 | callback_data="settings#get_extension")]) 412 | await query.message.edit_text(text="**successfully deleted**", 413 | reply_markup=InlineKeyboardMarkup(buttons)) 414 | elif type == "add_keyword": 415 | await query.message.delete() 416 | ask = await bot.ask(user_id, text="**please send the keywords (seperete by space Like:- English 1080p Hdrip)**") 417 | if ask.text == '/cancel': 418 | return await ask.reply_text( 419 | "process canceled", 420 | reply_markup=InlineKeyboardMarkup(buttons)) 421 | keywords = ask.text.split(" ") 422 | keyword = (await get_configs(user_id))['keywords'] 423 | if keyword: 424 | for word in keywords: 425 | keyword.append(word) 426 | else: 427 | keyword = keywords 428 | await update_configs(user_id, 'keywords', keyword) 429 | buttons = [] 430 | buttons.append([InlineKeyboardButton('back', 431 | callback_data="settings#get_keyword")]) 432 | await ask.reply_text( 433 | f"**successfully updated**", 434 | reply_markup=InlineKeyboardMarkup(buttons)) 435 | 436 | elif type == "get_keyword": 437 | keywords = (await get_configs(user_id))['keywords'] 438 | btn = [] 439 | text = "" 440 | if keywords: 441 | text += "**🔖 Keywords:**" 442 | for key in keywords: 443 | text += f"\n-{key}" 444 | else: 445 | text += "**You didn't Added Any Keywords**" 446 | btn.append([InlineKeyboardButton('✚ Add', 'settings#add_keyword')]) 447 | btn.append([InlineKeyboardButton('Remove all', 'settings#rmve_all_keyword')]) 448 | btn.append([InlineKeyboardButton('Back', 'settings#extra')]) 449 | await query.message.edit_text( 450 | text=f"Keywords\n\n**Files with these keywords in file name only forwad**\n\n{text}", 451 | reply_markup=InlineKeyboardMarkup(btn)) 452 | 453 | elif type == "rmve_all_keyword": 454 | await update_configs(user_id, 'keywords', None) 455 | buttons = [] 456 | buttons.append([InlineKeyboardButton('back', 457 | callback_data="settings#get_keyword")]) 458 | await query.message.edit_text(text="**successfully deleted All Keywords**", 459 | reply_markup=InlineKeyboardMarkup(buttons)) 460 | elif type.startswith("alert"): 461 | alert = type.split('_')[1] 462 | await query.answer(alert, show_alert=True) 463 | 464 | # Don't Remove Credit Tg - @VJ_Botz 465 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 466 | # Ask Doubt on telegram @KingVJ01 467 | 468 | def extra_buttons(): 469 | buttons = [[ 470 | InlineKeyboardButton('💾 Mɪɴ Sɪᴢᴇ Lɪᴍɪᴛ', 471 | callback_data=f'settings#file_size') 472 | ],[ 473 | InlineKeyboardButton('💾 Mᴀx Sɪᴢᴇ Lɪᴍɪᴛ', 474 | callback_data=f'settings#maxfile_size ') 475 | ],[ 476 | InlineKeyboardButton('🚥 Keywords', 477 | callback_data=f'settings#get_keyword'), 478 | InlineKeyboardButton('🕹 Extensions', 479 | callback_data=f'settings#get_extension') 480 | ],[ 481 | InlineKeyboardButton('⫷ Bᴀᴄᴋ', 482 | callback_data=f'settings#main') 483 | ]] 484 | return InlineKeyboardMarkup(buttons) 485 | 486 | # Don't Remove Credit Tg - @VJ_Botz 487 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 488 | # Ask Doubt on telegram @KingVJ01 489 | 490 | def main_buttons(): 491 | buttons = [[ 492 | InlineKeyboardButton('🤖 Bᴏᴛs', 493 | callback_data=f'settings#bots'), 494 | InlineKeyboardButton('🏷 Cʜᴀɴɴᴇʟs', 495 | callback_data=f'settings#channels') 496 | ],[ 497 | InlineKeyboardButton('🖋️ Cᴀᴘᴛɪᴏɴ', 498 | callback_data=f'settings#caption'), 499 | InlineKeyboardButton('⏹ Bᴜᴛᴛᴏɴ', 500 | callback_data=f'settings#button') 501 | ],[ 502 | InlineKeyboardButton('🕵‍♀ Fɪʟᴛᴇʀs 🕵‍♀', 503 | callback_data=f'settings#filters'), 504 | InlineKeyboardButton('🗃 MᴏɴɢᴏDB', 505 | callback_data=f'settings#database') 506 | ],[ 507 | InlineKeyboardButton('Exᴛʀᴀ Sᴇᴛᴛɪɴɢs 🧪', 508 | callback_data=f'settings#extra') 509 | ],[ 510 | InlineKeyboardButton('⫷ Bᴀᴄᴋ', 511 | callback_data=f'help') 512 | ]] 513 | return InlineKeyboardMarkup(buttons) 514 | 515 | # Don't Remove Credit Tg - @VJ_Botz 516 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 517 | # Ask Doubt on telegram @KingVJ01 518 | 519 | def size_limit(limit): 520 | if str(limit) == "None": 521 | return None, "" 522 | elif str(limit) == "True": 523 | return True, "more than" 524 | else: 525 | return False, "less than" 526 | 527 | # Don't Remove Credit Tg - @VJ_Botz 528 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 529 | # Ask Doubt on telegram @KingVJ01 530 | 531 | def extract_btn(datas): 532 | i = 0 533 | btn = [] 534 | if datas: 535 | for data in datas: 536 | if i >= 3: 537 | i = 0 538 | if i == 0: 539 | btn.append([InlineKeyboardButton(data, f'settings#alert_{data}')]) 540 | i += 1 541 | continue 542 | elif i > 0: 543 | btn[-1].append(InlineKeyboardButton(data, f'settings#alert_{data}')) 544 | i += 1 545 | return btn 546 | 547 | # Don't Remove Credit Tg - @VJ_Botz 548 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 549 | # Ask Doubt on telegram @KingVJ01 550 | 551 | def maxsize_button(size): 552 | buttons = [[ 553 | InlineKeyboardButton('💾 Max Size Limit', 554 | callback_data=f'noth') 555 | ],[ 556 | InlineKeyboardButton('+1', 557 | callback_data=f'settings#maxupdate_size-{size + 1}'), 558 | InlineKeyboardButton('-1', 559 | callback_data=f'settings#maxupdate_size_-{size - 1}') 560 | ],[ 561 | InlineKeyboardButton('+5', 562 | callback_data=f'settings#maxupdate_size-{size + 5}'), 563 | InlineKeyboardButton('-5', 564 | callback_data=f'settings#maxupdate_size_-{size - 5}') 565 | ],[ 566 | InlineKeyboardButton('+10', 567 | callback_data=f'settings#maxupdate_size-{size + 10}'), 568 | InlineKeyboardButton('-10', 569 | callback_data=f'settings#maxupdate_size_-{size - 10}') 570 | ],[ 571 | InlineKeyboardButton('+50', 572 | callback_data=f'settings#maxupdate_size-{size + 50}'), 573 | InlineKeyboardButton('-50', 574 | callback_data=f'settings#maxupdate_size_-{size - 50}') 575 | ],[ 576 | InlineKeyboardButton('+100', 577 | callback_data=f'settings#maxupdate_size-{size + 100}'), 578 | InlineKeyboardButton('-100', 579 | callback_data=f'settings#maxupdate_size_-{size - 100}') 580 | ],[ 581 | InlineKeyboardButton('back', 582 | callback_data="settings#extra") 583 | ]] 584 | return InlineKeyboardMarkup(buttons) 585 | 586 | # Don't Remove Credit Tg - @VJ_Botz 587 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 588 | # Ask Doubt on telegram @KingVJ01 589 | 590 | def size_button(size): 591 | buttons = [[ 592 | InlineKeyboardButton('💾 Min Size Limit', 593 | callback_data=f'noth') 594 | ],[ 595 | InlineKeyboardButton('+1', 596 | callback_data=f'settings#update_size-{size + 1}'), 597 | InlineKeyboardButton('-1', 598 | callback_data=f'settings#update_size_-{size - 1}') 599 | ],[ 600 | InlineKeyboardButton('+5', 601 | callback_data=f'settings#update_size-{size + 5}'), 602 | InlineKeyboardButton('-5', 603 | callback_data=f'settings#update_size_-{size - 5}') 604 | ],[ 605 | InlineKeyboardButton('+10', 606 | callback_data=f'settings#update_size-{size + 10}'), 607 | InlineKeyboardButton('-10', 608 | callback_data=f'settings#update_size_-{size - 10}') 609 | ],[ 610 | InlineKeyboardButton('+50', 611 | callback_data=f'settings#update_size-{size + 50}'), 612 | InlineKeyboardButton('-50', 613 | callback_data=f'settings#update_size_-{size - 50}') 614 | ],[ 615 | InlineKeyboardButton('+100', 616 | callback_data=f'settings#update_size-{size + 100}'), 617 | InlineKeyboardButton('-100', 618 | callback_data=f'settings#update_size_-{size - 100}') 619 | ],[ 620 | InlineKeyboardButton('back', 621 | callback_data="settings#extra") 622 | ]] 623 | return InlineKeyboardMarkup(buttons) 624 | 625 | # Don't Remove Credit Tg - @VJ_Botz 626 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 627 | # Ask Doubt on telegram @KingVJ01 628 | 629 | async def filters_buttons(user_id): 630 | filter = await get_configs(user_id) 631 | filters = filter['filters'] 632 | buttons = [[ 633 | InlineKeyboardButton('🏷️ Forward tag', 634 | callback_data=f'settings_#updatefilter-forward_tag-{filter["forward_tag"]}'), 635 | InlineKeyboardButton('✅' if filter['forward_tag'] else '❌', 636 | callback_data=f'settings#updatefilter-forward_tag-{filter["forward_tag"]}') 637 | ],[ 638 | InlineKeyboardButton('🖍️ Texts', 639 | callback_data=f'settings_#updatefilter-text-{filters["text"]}'), 640 | InlineKeyboardButton('✅' if filters['text'] else '❌', 641 | callback_data=f'settings#updatefilter-text-{filters["text"]}') 642 | ],[ 643 | InlineKeyboardButton('📁 Documents', 644 | callback_data=f'settings_#updatefilter-document-{filters["document"]}'), 645 | InlineKeyboardButton('✅' if filters['document'] else '❌', 646 | callback_data=f'settings#updatefilter-document-{filters["document"]}') 647 | ],[ 648 | InlineKeyboardButton('🎞️ Videos', 649 | callback_data=f'settings_#updatefilter-video-{filters["video"]}'), 650 | InlineKeyboardButton('✅' if filters['video'] else '❌', 651 | callback_data=f'settings#updatefilter-video-{filters["video"]}') 652 | ],[ 653 | InlineKeyboardButton('📷 Photos', 654 | callback_data=f'settings_#updatefilter-photo-{filters["photo"]}'), 655 | InlineKeyboardButton('✅' if filters['photo'] else '❌', 656 | callback_data=f'settings#updatefilter-photo-{filters["photo"]}') 657 | ],[ 658 | InlineKeyboardButton('🎧 Audios', 659 | callback_data=f'settings_#updatefilter-audio-{filters["audio"]}'), 660 | InlineKeyboardButton('✅' if filters['audio'] else '❌', 661 | callback_data=f'settings#updatefilter-audio-{filters["audio"]}') 662 | ],[ 663 | InlineKeyboardButton('⫷ back', 664 | callback_data="settings#main"), 665 | InlineKeyboardButton('next ⫸', 666 | callback_data="settings#nextfilters") 667 | ]] 668 | return InlineKeyboardMarkup(buttons) 669 | 670 | # Don't Remove Credit Tg - @VJ_Botz 671 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 672 | # Ask Doubt on telegram @KingVJ01 673 | 674 | async def next_filters_buttons(user_id): 675 | filter = await get_configs(user_id) 676 | filters = filter['filters'] 677 | buttons = [[ 678 | ],[ 679 | InlineKeyboardButton('🎤 Voices', 680 | callback_data=f'settings_#updatefilter-voice-{filters["voice"]}'), 681 | InlineKeyboardButton('✅' if filters['voice'] else '❌', 682 | callback_data=f'settings#updatefilter-voice-{filters["voice"]}') 683 | ],[ 684 | InlineKeyboardButton('🎭 Animations', 685 | callback_data=f'settings_#updatefilter-animation-{filters["animation"]}'), 686 | InlineKeyboardButton('✅' if filters['animation'] else '❌', 687 | callback_data=f'settings#updatefilter-animation-{filters["animation"]}') 688 | ],[ 689 | InlineKeyboardButton('🃏 Stickers', 690 | callback_data=f'settings_#updatefilter-sticker-{filters["sticker"]}'), 691 | InlineKeyboardButton('✅' if filters['sticker'] else '❌', 692 | callback_data=f'settings#updatefilter-sticker-{filters["sticker"]}') 693 | ],[ 694 | InlineKeyboardButton('▶️ Skip duplicate', 695 | callback_data=f'settings_#updatefilter-duplicate-{filter["duplicate"]}'), 696 | InlineKeyboardButton('✅' if filter['duplicate'] else '❌', 697 | callback_data=f'settings#updatefilter-duplicate-{filter["duplicate"]}') 698 | ],[ 699 | InlineKeyboardButton('📊 Poll', 700 | callback_data=f'settings_#updatefilter-poll-{filters["poll"]}'), 701 | InlineKeyboardButton('✅' if filters['poll'] else '❌', 702 | callback_data=f'settings#updatefilter-poll-{filters["poll"]}') 703 | ],[ 704 | InlineKeyboardButton('🔒 Secure message', 705 | callback_data=f'settings_#updatefilter-protect-{filter["protect"]}'), 706 | InlineKeyboardButton('✅' if filter['protect'] else '❌', 707 | callback_data=f'settings#updatefilter-protect-{filter["protect"]}') 708 | ],[ 709 | InlineKeyboardButton('⫷ back', 710 | callback_data="settings#filters"), 711 | InlineKeyboardButton('End ⫸', 712 | callback_data="settings#main") 713 | ]] 714 | return InlineKeyboardMarkup(buttons) 715 | 716 | # Don't Remove Credit Tg - @VJ_Botz 717 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 718 | # Ask Doubt on telegram @KingVJ01 719 | -------------------------------------------------------------------------------- /plugins/test.py: -------------------------------------------------------------------------------- 1 | # Don't Remove Credit Tg - @VJ_Botz 2 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 3 | # Ask Doubt on telegram @KingVJ01 4 | 5 | import os 6 | import re 7 | import sys 8 | import asyncio 9 | import logging 10 | from database import Db, db 11 | from config import Config, temp 12 | from pyrogram import Client, filters 13 | from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup, CallbackQuery, Message 14 | from pyrogram.errors.exceptions.bad_request_400 import AccessTokenExpired, AccessTokenInvalid 15 | from pyrogram.errors import FloodWait 16 | from config import Config 17 | from script import Script 18 | from typing import Union, Optional, AsyncGenerator 19 | from pyrogram.errors import ( 20 | ApiIdInvalid, 21 | PhoneNumberInvalid, 22 | PhoneCodeInvalid, 23 | PhoneCodeExpired, 24 | SessionPasswordNeeded, 25 | PasswordHashInvalid 26 | ) 27 | 28 | # Don't Remove Credit Tg - @VJ_Botz 29 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 30 | # Ask Doubt on telegram @KingVJ01 31 | 32 | logger = logging.getLogger(__name__) 33 | logger.setLevel(logging.INFO) 34 | 35 | BTN_URL_REGEX = re.compile(r"(\[([^\[]+?)]\[buttonurl:/{0,2}(.+?)(:same)?])") 36 | 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" 37 | SESSION_STRING_SIZE = 351 38 | 39 | # Don't Remove Credit Tg - @VJ_Botz 40 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 41 | # Ask Doubt on telegram @KingVJ01 42 | 43 | class CLIENT: 44 | def __init__(self): 45 | self.api_id = Config.API_ID 46 | self.api_hash = Config.API_HASH 47 | 48 | def user_session(self, data): 49 | return Client("USERBOT", self.api_id, self.api_hash, session_string=data) 50 | 51 | async def add_bot(self, bot, message): 52 | user_id = int(message.from_user.id) 53 | msg = await bot.ask(chat_id=user_id, text=BOT_TOKEN_TEXT) 54 | if msg.text=='/cancel': 55 | return await msg.reply('process cancelled !') 56 | elif not msg.forward_date: 57 | return await msg.reply_text("This is not a forward message") 58 | elif str(msg.forward_from.id) != "93372553": 59 | return await msg.reply_text("This message was not forward from bot father") 60 | bot_token = re.findall(r'\d[0-9]{8,10}:[0-9A-Za-z_-]{35}', msg.text, re.IGNORECASE) 61 | bot_token = bot_token[0] if bot_token else None 62 | if not bot_token: 63 | return await msg.reply_text("There is no bot token in that message") 64 | try: 65 | _client = Client("BOT", Config.API_ID, Config.API_HASH, bot_token=bot_token, in_memory=True) 66 | client = await _client.start() 67 | except Exception as e: 68 | await msg.reply_text(f"BOT ERROR: `{e}`") 69 | return 70 | _bot = _client.me 71 | details = { 72 | 'id': _bot.id, 73 | 'is_bot': True, 74 | 'user_id': user_id, 75 | 'name': _bot.first_name, 76 | 'token': bot_token, 77 | 'username': _bot.username 78 | } 79 | await db.add_bot(details) 80 | return True 81 | 82 | async def add_session(self, bot, message): 83 | user_id = int(message.from_user.id) 84 | 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." 85 | await bot.send_message(user_id, text=text) 86 | phone_number_msg = await bot.ask(chat_id=user_id, text="Please send your phone number which includes country code\nExample: +13124562345") 87 | if phone_number_msg.text=='/cancel': 88 | return await phone_number_msg.reply('process cancelled !') 89 | phone_number = phone_number_msg.text 90 | client = Client(":memory:", Config.API_ID, Config.API_HASH) 91 | await client.connect() 92 | await phone_number_msg.reply("Sending OTP...") 93 | try: 94 | code = await client.send_code(phone_number) 95 | phone_code_msg = await bot.ask(user_id, "Please check for an OTP in official telegram account. If you got it, send OTP here after reading the below format. \n\nIf OTP is `12345`, **please send it as** `1 2 3 4 5`.\n\n**Enter /cancel to cancel The Procces**", filters=filters.text, timeout=600) 96 | except PhoneNumberInvalid: 97 | await phone_number_msg.reply('`PHONE_NUMBER` **is invalid.**') 98 | return 99 | if phone_code_msg.text=='/cancel': 100 | return await phone_code_msg.reply('process cancelled !') 101 | try: 102 | phone_code = phone_code_msg.text.replace(" ", "") 103 | await client.sign_in(phone_number, code.phone_code_hash, phone_code) 104 | except PhoneCodeInvalid: 105 | await phone_code_msg.reply('**OTP is invalid.**') 106 | return 107 | except PhoneCodeExpired: 108 | await phone_code_msg.reply('**OTP is expired.**') 109 | return 110 | except SessionPasswordNeeded: 111 | two_step_msg = await bot.ask(user_id, '**Your account has enabled two-step verification. Please provide the password.\n\nEnter /cancel to cancel The Procces**', filters=filters.text, timeout=300) 112 | if two_step_msg.text=='/cancel': 113 | return await two_step_msg.reply('process cancelled !') 114 | try: 115 | password = two_step_msg.text 116 | await client.check_password(password=password) 117 | except PasswordHashInvalid: 118 | await two_step_msg.reply('**Invalid Password Provided**') 119 | return 120 | string_session = await client.export_session_string() 121 | await client.disconnect() 122 | if len(string_session) < SESSION_STRING_SIZE: 123 | return await msg.reply('invalid session sring') 124 | try: 125 | _client = Client("USERBOT", self.api_id, self.api_hash, session_string=string_session) 126 | client = await _client.start() 127 | except Exception as e: 128 | return 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': string_session, 136 | 'username': user.username 137 | } 138 | await db.add_userbot(details) 139 | return True 140 | 141 | # Don't Remove Credit Tg - @VJ_Botz 142 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 143 | # Ask Doubt on telegram @KingVJ01 144 | 145 | @Client.on_message(filters.private & filters.command('reset')) 146 | async def forward_tag(bot, m): 147 | default = await db.get_configs("01") 148 | await db.update_configs(m.from_user.id, default) 149 | await m.reply("successfully settings reseted ✔️") 150 | 151 | # Don't Remove Credit Tg - @VJ_Botz 152 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 153 | # Ask Doubt on telegram @KingVJ01 154 | 155 | @Client.on_message(filters.command('resetall') & filters.user(Config.BOT_OWNER)) 156 | async def resetall(bot, message): 157 | users = await db.get_all_users() 158 | sts = await message.reply("**processing**") 159 | TEXT = "total: {}\nsuccess: {}\nfailed: {}\nexcept: {}" 160 | total = success = failed = already = 0 161 | ERRORS = [] 162 | async for user in users: 163 | user_id = user['id'] 164 | default = await get_configs(user_id) 165 | default['db_uri'] = None 166 | total += 1 167 | if total %10 == 0: 168 | await sts.edit(TEXT.format(total, success, failed, already)) 169 | try: 170 | await db.update_configs(user_id, default) 171 | success += 1 172 | except Exception as e: 173 | ERRORS.append(e) 174 | failed += 1 175 | if ERRORS: 176 | await message.reply(ERRORS[:100]) 177 | await sts.edit("completed\n" + TEXT.format(total, success, failed, already)) 178 | 179 | # Don't Remove Credit Tg - @VJ_Botz 180 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 181 | # Ask Doubt on telegram @KingVJ01 182 | 183 | async def get_configs(user_id): 184 | configs = await db.get_configs(user_id) 185 | return configs 186 | 187 | # Don't Remove Credit Tg - @VJ_Botz 188 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 189 | # Ask Doubt on telegram @KingVJ01 190 | 191 | async def update_configs(user_id, key, value): 192 | current = await db.get_configs(user_id) 193 | if key in ['caption', 'duplicate', 'db_uri', 'forward_tag', 'protect', 'min_size', 'max_size', 'extension', 'keywords', 'button']: 194 | current[key] = value 195 | else: 196 | current['filters'][key] = value 197 | await db.update_configs(user_id, current) 198 | 199 | # Don't Remove Credit Tg - @VJ_Botz 200 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 201 | # Ask Doubt on telegram @KingVJ01 202 | 203 | async def iter_messages( 204 | self, 205 | chat_id: Union[int, str], 206 | limit: int, 207 | offset: int = 0, 208 | filters: dict = None, 209 | max_size: int = None, 210 | ) -> Optional[AsyncGenerator["types.Message", None]]: 211 | current = offset 212 | dup_files = [] 213 | while True: 214 | new_diff = min(200, limit - current) 215 | if new_diff <= 0: 216 | return 217 | 218 | messages = await self.get_messages(chat_id, list(range(current, current + new_diff + 1))) 219 | for message in messages: 220 | if any(getattr(message, media_type, False) for media_type in filters): 221 | yield "FILTERED" 222 | else: 223 | yield message 224 | 225 | current += 1 226 | 227 | # Don't Remove Credit Tg - @VJ_Botz 228 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 229 | # Ask Doubt on telegram @KingVJ01 230 | 231 | async def get_client(bot_token, is_bot=True): 232 | if is_bot: 233 | return Client("BOT", Config.API_ID, Config.API_HASH, bot_token=bot_token, in_memory=True) 234 | else: 235 | return Client("USERBOT", Config.API_ID, Config.API_HASH, session_string=bot_token) 236 | 237 | # Don't Remove Credit Tg - @VJ_Botz 238 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 239 | # Ask Doubt on telegram @KingVJ01 240 | 241 | def parse_buttons(text, markup=True): 242 | buttons = [] 243 | for match in BTN_URL_REGEX.finditer(text): 244 | n_escapes = 0 245 | to_check = match.start(1) - 1 246 | while to_check > 0 and text[to_check] == "\\": 247 | n_escapes += 1 248 | to_check -= 1 249 | 250 | if n_escapes % 2 == 0: 251 | if bool(match.group(4)) and buttons: 252 | buttons[-1].append(InlineKeyboardButton( 253 | text=match.group(2), 254 | url=match.group(3).replace(" ", ""))) 255 | else: 256 | buttons.append([InlineKeyboardButton( 257 | text=match.group(2), 258 | url=match.group(3).replace(" ", ""))]) 259 | if markup and buttons: 260 | buttons = InlineKeyboardMarkup(buttons) 261 | return buttons if buttons else None 262 | 263 | # Don't Remove Credit Tg - @VJ_Botz 264 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 265 | # Ask Doubt on telegram @KingVJ01 266 | -------------------------------------------------------------------------------- /plugins/unequeify.py: -------------------------------------------------------------------------------- 1 | # Don't Remove Credit Tg - @VJ_Botz 2 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 3 | # Ask Doubt on telegram @KingVJ01 4 | 5 | import re, asyncio 6 | from database import Db, db 7 | from config import temp 8 | from .test import CLIENT, get_client 9 | from script import Script 10 | import base64 11 | from pyrogram.file_id import FileId 12 | from pyrogram import Client, filters, enums 13 | from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup 14 | import struct 15 | 16 | # Don't Remove Credit Tg - @VJ_Botz 17 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 18 | # Ask Doubt on telegram @KingVJ01 19 | 20 | CLIENT = CLIENT() 21 | COMPLETED_BTN = InlineKeyboardMarkup( 22 | [[ 23 | InlineKeyboardButton('💟 sᴜᴘᴘᴏʀᴛ ɢʀᴏᴜᴘ 💟', url='https://t.me/VJ_Bot_Disscussion') 24 | ],[ 25 | InlineKeyboardButton('💠 ᴜᴘᴅᴀᴛᴇ ᴄʜᴀɴɴᴇʟ 💠', url='https://t.me/vj_botz') 26 | ]] 27 | ) 28 | CANCEL_BTN = InlineKeyboardMarkup([[InlineKeyboardButton('• ᴄᴀɴᴄᴇʟ', 'terminate_frwd')]]) 29 | 30 | # Don't Remove Credit Tg - @VJ_Botz 31 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 32 | # Ask Doubt on telegram @KingVJ01 33 | 34 | def encode_file_id(s: bytes) -> str: 35 | r = b"" 36 | n = 0 37 | 38 | for i in s + bytes([22]) + bytes([4]): 39 | if i == 0: 40 | n += 1 41 | else: 42 | if n: 43 | r += b"\x00" + bytes([n]) 44 | n = 0 45 | 46 | r += bytes([i]) 47 | 48 | return base64.urlsafe_b64encode(r).decode().rstrip("=") 49 | 50 | # Don't Remove Credit Tg - @VJ_Botz 51 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 52 | # Ask Doubt on telegram @KingVJ01 53 | 54 | def unpack_new_file_id(new_file_id): 55 | """Return file_id""" 56 | decoded = FileId.decode(new_file_id) 57 | file_id = encode_file_id( 58 | struct.pack( 59 | "Need userbot to do this process. Please add a userbot using /settings") 81 | target = await client.ask(user_id, text="**Forward the last message from target chat or send last message link.**\n/cancel - `cancel this process`") 82 | if target.text and target.text.startswith("/"): 83 | return await message.reply("**process cancelled !**") 84 | elif target.text: 85 | regex = re.compile("(https://)?(t\.me/|telegram\.me/|telegram\.dog/)(c/)?(\d+|[a-zA-Z_0-9]+)/(\d+)$") 86 | match = regex.match(target.text.replace("?single", "")) 87 | if not match: 88 | return await message.reply('**Invalid link**') 89 | chat_id = match.group(4) 90 | last_msg_id = int(match.group(5)) 91 | if chat_id.isnumeric(): 92 | chat_id = int(("-100" + chat_id)) 93 | elif target.forward_from_chat.type in [enums.ChatType.CHANNEL, 'supergroup']: 94 | last_msg_id = target.forward_from_message_id 95 | chat_id = target.forward_from_chat.username or target.forward_from_chat.id 96 | else: 97 | return await message.reply_text("**invalid !**") 98 | confirm = await client.ask(user_id, text="**send /yes to start the process and /no to cancel this process**") 99 | if confirm.text.lower() == '/no': 100 | return await confirm.reply("**process cancelled !**") 101 | sts = await confirm.reply("`processing..`") 102 | il = False 103 | data = _bot['session'] 104 | try: 105 | bot = await get_client(data, is_bot=il) 106 | await bot.start() 107 | except Exception as e: 108 | return await sts.edit(e) 109 | try: 110 | k = await bot.send_message(chat_id, text="testing") 111 | await k.delete() 112 | except: 113 | await sts.edit(f"**please make your [userbot](t.me/{_bot['username']}) admin in target chat with full permissions**") 114 | return await bot.stop() 115 | MESSAGES = [] 116 | DUPLICATE = [] 117 | total=deleted=0 118 | temp.lock[user_id] = True 119 | temp.CANCEL[user_id] = False 120 | try: 121 | await sts.edit(Script.DUPLICATE_TEXT.format(total, deleted, "ᴘʀᴏɢʀᴇssɪɴɢ"), reply_markup=CANCEL_BTN) 122 | async for message in bot.search_messages(chat_id=chat_id, filter=enums.MessagesFilter.DOCUMENT): 123 | if temp.CANCEL.get(user_id) == True: 124 | await sts.edit(Script.DUPLICATE_TEXT.format(total, deleted, "ᴄᴀɴᴄᴇʟʟᴇᴅ"), reply_markup=COMPLETED_BTN) 125 | return await bot.stop() 126 | file = message.document 127 | file_id = unpack_new_file_id(file.file_id) 128 | if file_id in MESSAGES: 129 | DUPLICATE.append(message.id) 130 | else: 131 | MESSAGES.append(file_id) 132 | total += 1 133 | if total %1000 == 0: 134 | await sts.edit(Script.DUPLICATE_TEXT.format(total, deleted, "ᴘʀᴏɢʀᴇssɪɴɢ"), reply_markup=CANCEL_BTN) 135 | if len(DUPLICATE) >= 100: 136 | await bot.delete_messages(chat_id, DUPLICATE) 137 | deleted += 100 138 | await sts.edit(Script.DUPLICATE_TEXT.format(total, deleted, "ᴘʀᴏɢʀᴇssɪɴɢ"), reply_markup=CANCEL_BTN) 139 | DUPLICATE = [] 140 | if DUPLICATE: 141 | await bot.delete_messages(chat_id, DUPLICATE) 142 | deleted += len(DUPLICATE) 143 | except Exception as e: 144 | temp.lock[user_id] = False 145 | await sts.edit(f"**ERROR**\n`{e}`") 146 | return await bot.stop() 147 | temp.lock[user_id] = False 148 | await sts.edit(Script.DUPLICATE_TEXT.format(total, deleted, "ᴄᴏᴍᴘʟᴇᴛᴇᴅ"), reply_markup=COMPLETED_BTN) 149 | await bot.stop() 150 | 151 | # Don't Remove Credit Tg - @VJ_Botz 152 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 153 | # Ask Doubt on telegram @KingVJ01 154 | -------------------------------------------------------------------------------- /plugins/utils.py: -------------------------------------------------------------------------------- 1 | # Don't Remove Credit Tg - @VJ_Botz 2 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 3 | # Ask Doubt on telegram @KingVJ01 4 | 5 | import time as tm 6 | from database import Db, db 7 | from .test import parse_buttons 8 | 9 | STATUS = {} 10 | 11 | # Don't Remove Credit Tg - @VJ_Botz 12 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 13 | # Ask Doubt on telegram @KingVJ01 14 | 15 | class STS: 16 | def __init__(self, id): 17 | self.id = id 18 | self.data = STATUS 19 | 20 | def verify(self): 21 | return self.data.get(self.id) 22 | 23 | def store(self, From, to, skip, limit): 24 | self.data[self.id] = {"FROM": From, 'TO': to, 'total_files': 0, 'skip': skip, 'limit': limit, 25 | 'fetched': skip, 'filtered': 0, 'deleted': 0, 'duplicate': 0, 'total': limit, 'start': 0} 26 | self.get(full=True) 27 | return STS(self.id) 28 | 29 | def get(self, value=None, full=False): 30 | values = self.data.get(self.id) 31 | if not full: 32 | return values.get(value) 33 | for k, v in values.items(): 34 | setattr(self, k, v) 35 | return self 36 | 37 | def add(self, key=None, value=1, time=False, start_time=None): 38 | if time: 39 | return self.data[self.id].update({'start': tm.time() if start_time is None else start_time}) 40 | self.data[self.id].update({key: self.get(key) + value}) 41 | 42 | def divide(self, no, by): 43 | by = 1 if int(by) == 0 else by 44 | return int(no) / by 45 | 46 | async def get_data(self, user_id): 47 | bot = await db.get_bot(user_id) 48 | if bot is None: 49 | bot = await db.get_userbot(user_id) 50 | k, filters = self, await db.get_filters(user_id) 51 | size, configs = None, await db.get_configs(user_id) 52 | if configs['duplicate']: 53 | duplicate = True 54 | else: 55 | duplicate = False 56 | try: 57 | min = configs['min_size'] 58 | max = configs['max_size'] 59 | except: 60 | min = 0 61 | max = 0 62 | button = parse_buttons(configs['button'] if configs['button'] else '') 63 | return bot, configs['caption'], configs['forward_tag'], {'filters': filters, 64 | 'keywords': configs['keywords'], 'min_size': min, 'max_size': max, 'extensions': configs['extension'], 'skip_duplicate': duplicate, 'db_uri': configs['db_uri']}, configs['protect'], button 65 | 66 | # Don't Remove Credit Tg - @VJ_Botz 67 | # Subscribe YouTube Channel For Amazing Bot https://youtube.com/@Tech_VJ 68 | # Ask Doubt on telegram @KingVJ01 69 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | pyrofork 2 | tgcrypto 3 | pyropatch 4 | humanize 5 | motor==2.5.1 6 | TgCrypto 7 | Dnspython 8 | pymongo[srv]==3.12.3 9 | umongo==3.0.1 10 | psutil 11 | pytz 12 | Flask==1.1.2 13 | gunicorn==20.1.0 14 | Jinja2==3.0.3 15 | werkzeug==2.0.2 16 | itsdangerous==2.0.1 17 | -------------------------------------------------------------------------------- /run cmd.txt: -------------------------------------------------------------------------------- 1 | gunicorn app:app & python3 main.py 2 | -------------------------------------------------------------------------------- /runtime.txt: -------------------------------------------------------------------------------- 1 | python-3.10.8 2 | -------------------------------------------------------------------------------- /script.py: -------------------------------------------------------------------------------- 1 | import os 2 | from config import Config 3 | 4 | class Script(object): 5 | START_TXT = """ʜɪ {} 6 | 7 | ɪ'ᴍ ᴀ ᴀᴅᴠᴀɴᴄᴇᴅ ꜰᴏʀᴡᴀʀᴅ ʙᴏᴛ 8 | ɪ ᴄᴀɴ ꜰᴏʀᴡᴀʀᴅ ᴀʟʟ ᴍᴇssᴀɢᴇ ꜰʀᴏᴍ ᴏɴᴇ ᴄʜᴀɴɴᴇʟ ᴛᴏ ᴀɴᴏᴛʜᴇʀ ᴄʜᴀɴɴᴇʟ 9 | 10 | **ᴄʟɪᴄᴋ ʜᴇʟᴘ ʙᴜᴛᴛᴏɴ ᴛᴏ ᴋɴᴏᴡ ᴍᴏʀᴇ ᴀʙᴏᴜᴛ ᴍᴇ**""" 11 | HELP_TXT = """🔆 Help 12 | 13 | **📚 Available commands:** 14 | ⏣ __/start - check I'm alive__ 15 | ⏣ __/forward - forward messages__ 16 | ⏣ __/settings - configure your settings__ 17 | ⏣ __ /unequify - delete duplicate media messages in chats__ 18 | ⏣ __ /stop - stop your ongoing tasks__ 19 | ⏣ __ /reset - reset your settings__ 20 | 21 | 💢 Features: 22 | ► __Forward message from public channel to your channel without admin permission. if the channel is private need admin permission, if you can't give admin permission then use userbot, but in userbot there is a chance to get your account ban so use fake account__ 23 | ► __custom caption__ 24 | ► __custom button__ 25 | ► __skip duplicate messages__ 26 | ► __filter type of messages__ 27 | """ 28 | 29 | HOW_USE_TXT = """⚠️ Before Forwarding: 30 | ► __add a bot or userbot__ 31 | ► __add atleast one to channel__ `(your bot/userbot must be admin in there)` 32 | ► __You can add chats or bots by using /settings__ 33 | ► __if the **From Channel** is private your userbot must be member in there or your bot must need admin permission in there also__ 34 | ► __Then use /forward to forward messages__ 35 | 36 | ► ʜᴏᴡ ᴛᴏ ᴜsᴇ ᴍᴇ [ᴛᴜᴛᴏʀɪᴀʟ ᴠɪᴅᴇᴏ](https://youtu.be/wO1FE-lf35I)""" 37 | 38 | ABOUT_TXT = """ 39 | ╔════❰ ғᴏʀᴡᴀʀᴅ ʙᴏᴛ ❱═❍⊱❁۪۪ 40 | ║╭━━━━━━━━━━━━━━━➣ 41 | ║┣⪼📃ʙᴏᴛ : [Fᴏʀᴡᴀᴅ Bᴏᴛ](https://t.me/VJForwardBot) 42 | ║┣⪼👦Cʀᴇᴀᴛᴏʀ : [Kɪɴɢ VJ 👑](https://t.me/kingvj01) 43 | ║┣⪼🤖Uᴘᴅᴀᴛᴇ : [VJ Bᴏᴛᴢ](https://t.me/vj_botz) 44 | ║┣⪼📡Hᴏsᴛᴇᴅ ᴏɴ : Sᴜᴘᴇʀ Fᴀsᴛ 45 | ║┣⪼🗣️Lᴀɴɢᴜᴀɢᴇ : Pʏᴛʜᴏɴ3 46 | ║┣⪼📚Lɪʙʀᴀʀʏ : Pʏʀᴏɢʀᴀᴍ Gᴀᴛʜᴇʀ 2.11.0 47 | ║┣⪼🗒️Vᴇʀsɪᴏɴ : 0.18.3 48 | ║╰━━━━━━━━━━━━━━━➣ 49 | ╚══════════════════❍⊱❁۪۪ 50 | """ 51 | STATUS_TXT = """ 52 | ╔════❰ ʙᴏᴛ sᴛᴀᴛᴜs ❱═❍⊱❁۪۪ 53 | ║╭━━━━━━━━━━━━━━━➣ 54 | ║┣⪼**⏳ ʙᴏᴛ ᴜᴘᴛɪᴍᴇ:**`{}` 55 | ║┃ 56 | ║┣⪼**👱 Tᴏᴛᴀʟ Usᴇʀs:** `{}` 57 | ║┃ 58 | ║┣⪼**🤖 Tᴏᴛᴀʟ Bᴏᴛ:** `{}` 59 | ║┃ 60 | ║┣⪼**🔃 Fᴏʀᴡᴀʀᴅɪɴɢs:** `{}` 61 | ║┃ 62 | ║╰━━━━━━━━━━━━━━━➣ 63 | ╚══════════════════❍⊱❁۪۪ 64 | """ 65 | FROM_MSG = "❪ SET SOURCE CHAT ❫\n\nForward the last message or last message link of source chat.\n/cancel - cancel this process" 66 | TO_MSG = "❪ CHOOSE TARGET CHAT ❫\n\nChoose your target chat from the given buttons.\n/cancel - Cancel this process" 67 | 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" 68 | CANCEL = "Process Cancelled Succefully !" 69 | BOT_DETAILS = "📄 BOT DETAILS\n\n➣ NAME: {}\n➣ BOT ID: {}\n➣ USERNAME: @{}" 70 | USER_DETAILS = "📄 USERBOT DETAILS\n\n➣ NAME: {}\n➣ USER ID: {}\n➣ USERNAME: @{}" 71 | 72 | TEXT = """ 73 | ╔════❰ ғᴏʀᴡᴀʀᴅ sᴛᴀᴛᴜs ❱═❍⊱❁۪۪ 74 | ║╭━━━━━━━━━━━━━━━➣ 75 | ║┣⪼🕵 ғᴇᴄʜᴇᴅ Msɢ : {} 76 | ║┃ 77 | ║┣⪼✅ sᴜᴄᴄᴇғᴜʟʟʏ Fᴡᴅ : {} 78 | ║┃ 79 | ║┣⪼👥 ᴅᴜᴘʟɪᴄᴀᴛᴇ Msɢ : {} 80 | ║┃ 81 | ║┣⪼🗑 ᴅᴇʟᴇᴛᴇᴅ Msɢ : {} 82 | ║┃ 83 | ║┣⪼🪆 Sᴋɪᴘᴘᴇᴅ Msɢ : {} 84 | ║┃ 85 | ║┣⪼🔁 Fɪʟᴛᴇʀᴇᴅ Msɢ : {} 86 | ║┃ 87 | ║┣⪼📊 Cᴜʀʀᴇɴᴛ Sᴛᴀᴛᴜs: {} 88 | ║┃ 89 | ║┣⪼𖨠 Pᴇʀᴄᴇɴᴛᴀɢᴇ: {} % 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 | 115 | SETTINGS_TXT = """change your settings as your wish""" 116 | --------------------------------------------------------------------------------