├── 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 | 
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 |
--------------------------------------------------------------------------------