d4rktg 0.0.1__py3-none-any.whl → 0.0.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ from .db import db
d4rk/Database/db.py ADDED
@@ -0,0 +1,52 @@
1
+ # src/Database/Mongodb/db.py
2
+
3
+ from pymongo import MongoClient , server_api
4
+
5
+ from d4rk.Logs import setup_logger
6
+
7
+ logger = setup_logger(__name__)
8
+
9
+ class Database:
10
+ client = None
11
+ db = None
12
+ is_connected = False
13
+ def __init__(self):
14
+ pass
15
+
16
+ def connect(self,name: str, collections: list = [],DATABASE_URL: str =None):
17
+ self.database_name = name
18
+ self._collections = collections
19
+ self._DATABASE_URL = DATABASE_URL
20
+ if not DATABASE_URL:
21
+ logger.warning("DATABASE_URL is not set in the environment variables.")
22
+ exit()
23
+ try:
24
+ self.client = MongoClient(DATABASE_URL,server_api=server_api.ServerApi('1'))
25
+ self.db = self.client[self.database_name]
26
+ self._load_custom_collections()
27
+ self.is_connected = True
28
+ except Exception as e:
29
+ logger.warning(f"Failed to connect to MongoDB: {e}")
30
+ exit()
31
+ if self.is_connected:
32
+ try:
33
+ self.client.admin.command('ping')
34
+ logger.info(f"successfully connected to {name} Mongodb!")
35
+ except Exception as e:
36
+ logger.warning(f"Something Went Wrong While Connecting To Database! : {e}")
37
+ self.is_connected = False
38
+ exit()
39
+
40
+ def _load_custom_collections(self):
41
+ for CustomClass in self._collections:
42
+ collection_name = str(CustomClass.__name__).title()
43
+ base_collection = self.get_collection(collection_name)
44
+ instance = CustomClass(base_collection)
45
+ setattr(self, collection_name, instance)
46
+ logger.info(f"Loaded collection: {collection_name}")
47
+
48
+ def get_collection(self, collection):
49
+ return self.db[collection]
50
+
51
+ db = Database()
52
+
@@ -0,0 +1,2 @@
1
+ from ._bot import BotManager
2
+ from ._scheduler import scheduler
d4rk/Handlers/_bot.py ADDED
@@ -0,0 +1,239 @@
1
+ import os
2
+ import sys
3
+ import asyncio
4
+ import traceback
5
+ from datetime import datetime, timedelta
6
+
7
+ from pyrogram import Client
8
+ from pyrogram.errors import FloodWait
9
+ from pyrogram.types import BotCommand
10
+ from pyrogram.errors.exceptions.bad_request_400 import AccessTokenExpired
11
+
12
+ from d4rk.Logs import setup_logger
13
+
14
+ logger = setup_logger(__name__)
15
+
16
+ class BotManager(Client):
17
+ _bot: Client = None
18
+ _web = True
19
+ _bot_info = None
20
+ _is_connected = False
21
+ _flood_data = {}
22
+ TOKEN_INDEX = 0
23
+ _loop = None
24
+ _scheduler_thread = None
25
+
26
+ def create_client(self,token_index):
27
+ super().__init__(
28
+ name=self.app_name,
29
+ api_id=self.api_id,
30
+ api_hash=self.api_hash,
31
+ bot_token=self.tokens[token_index],
32
+ plugins=self.plugins,
33
+ in_memory=True
34
+ )
35
+
36
+ def load_flood_data(self):
37
+ if not os.path.exists('flood.txt'):
38
+ return self._flood_data
39
+
40
+ with open('flood.txt', "r") as f:
41
+ for line in f:
42
+ if ":" not in line:
43
+ continue
44
+ try:
45
+ token_index, until = map(int, line.strip().split(":"))
46
+ if token_index not in self._flood_data or until > self._flood_data[token_index]:
47
+ self._flood_data[token_index] = until
48
+ except:
49
+ continue
50
+
51
+ async def handle_flood_wait(self, wait_time: int):
52
+ logger.info(f"FloodWait: Sleeping for {wait_time} seconds.")
53
+
54
+ await asyncio.sleep(wait_time)
55
+ try:await super().start()
56
+ except AccessTokenExpired:pass
57
+
58
+ def _safe_async(self, coro_func):
59
+ if self._loop:asyncio.run_coroutine_threadsafe(coro_func(), self._loop)
60
+ else:logger.error("Event loop is not set for _safe_async")
61
+
62
+ def get_token_index_from_flood_file(self):
63
+ if not os.path.exists("flood.txt"):
64
+ return 0
65
+ now = int(datetime.now(self.TZ).timestamp())
66
+ available = []
67
+ non_flooded = []
68
+ for i in range(len(self.tokens)):
69
+ until = self._flood_data.get(i, 0)
70
+ if until <= now:
71
+ logger.info(f"Token {i} is not in flood.")
72
+ self._flood_data.pop(i, None)
73
+ self.save_flood_data()
74
+ non_flooded.append((i))
75
+ else:
76
+ available.append((i, until))
77
+ if non_flooded != []:
78
+ return non_flooded[0]
79
+ logger.warning("All tokens are in flood. Picking the one with the soonest expiry.")
80
+ available.sort(key=lambda x: x[1])
81
+ return available[0][0]
82
+ def save_flood_data(self):
83
+ with open('flood.txt', "w") as f:
84
+ for token_index, until in self._flood_data.items():
85
+ f.write(f"{token_index}:{until}\n")
86
+
87
+ async def change_token(self, token_index,wait_time):
88
+ flood_until = datetime.now(self.TZ) + timedelta(seconds=wait_time)
89
+ self._flood_data[token_index] = int(flood_until.timestamp())
90
+ self.save_flood_data()
91
+ try:
92
+ self.TOKEN_INDEX = token_index + 1
93
+ self.create_client(self.TOKEN_INDEX)
94
+ await asyncio.sleep(2)
95
+ except:
96
+ self.TOKEN_INDEX = 0
97
+ self.create_client(self.TOKEN_INDEX)
98
+ await self.handle_flood_wait(wait_time)
99
+
100
+ async def setup_webserver(self):
101
+ self._web_runner = await self.web_server.setup_web_server(8443)
102
+
103
+ async def powerup(self):
104
+ self.font = self.db.Settings.get(key="font",datatype=str)
105
+ self.sudo_users = self.db.Settings.get(key="sudo_users",datatype=list,default=[])
106
+ if not self.font:
107
+ logger.info("Font not set, defaulting to font 1")
108
+ self.db.Settings.set("font", "1")
109
+ self.font = 1
110
+
111
+ self.load_flood_data()
112
+ self.TOKEN_INDEX= self.get_token_index_from_flood_file()
113
+ self.create_client(self.TOKEN_INDEX)
114
+ max_retries = 3
115
+ for attempt in range(max_retries):
116
+ try:
117
+ self._loop = asyncio.get_running_loop()
118
+ logger.info(f'Starting bot client... (attempt {attempt + 1}/{max_retries})')
119
+ if not self._is_connected:
120
+ await asyncio.wait_for(super().start(), timeout=60.0)
121
+ self._bot_info = await super().get_me()
122
+ logger.info(f"Bot Client > {self._bot_info.first_name} - @{self._bot_info.username} Started")
123
+ await self.setup_commands()
124
+ self._is_connected = True
125
+ await self.start_scheduler()
126
+ await self.handle_restart()
127
+ break
128
+
129
+ except asyncio.TimeoutError:
130
+ logger.error(f"Bot start timed out (attempt {attempt + 1})")
131
+ if attempt < max_retries - 1:
132
+ logger.info("Retrying in 10 seconds...")
133
+ await asyncio.sleep(10)
134
+
135
+ except FloodWait as e:
136
+ logger.error(f"FloodWait: {e.value} seconds")
137
+ await self.change_token(self.TOKEN_INDEX,e.value)
138
+
139
+ except AccessTokenExpired:
140
+ logger.error(f"Access token expired (attempt {attempt + 1})")
141
+ await self.change_token(self.TOKEN_INDEX, 60)
142
+ except Exception as e:
143
+ logger.error(f"Error starting Client (attempt {attempt + 1}): {e}")
144
+ logger.error(traceback.format_exc())
145
+ if attempt < max_retries - 1:
146
+ logger.info("Retrying in 10 seconds...")
147
+ await asyncio.sleep(10)
148
+ else:
149
+ logger.error("Failed to start bot after all retry attempts")
150
+
151
+ await self.setup_webserver()
152
+
153
+ async def powerdown(self, *args):
154
+ logger.info("Initiating APP to stop...")
155
+ self.stop_scheduler()
156
+ today = self.TZ_now.strftime("%Y-%m-%d")
157
+ if hasattr(self, '_web_runner') and self._web_runner:
158
+ await self.web_server.cleanup()
159
+ if self._is_connected:
160
+ logger.info("Stopping bot client...")
161
+ try:
162
+ if self.LOGS:
163
+ await self.send_message(chat_id=self.LOGS, text=f"{self._bot_info.mention} stopping...")
164
+ await self.send_document(chat_id=self.LOGS, document=f"logs/log-{today}.txt")
165
+ await self.send_message(chat_id=self.LOGS, text=f"{self._bot_info.mention} stopped successfully!")
166
+ except Exception as e:
167
+ logger.error(f"Error sending stop notification: {e}")
168
+ logger.info(f"{self._bot_info.first_name} - @{self._bot_info.username} Stoped")
169
+ await super().stop()
170
+ sys.exit(0)
171
+
172
+ async def reboot(self):
173
+ try:
174
+ logger.info("Initiating APP to reboot...")
175
+ self.stop_scheduler()
176
+ today = self.TZ_now.strftime("%Y-%m-%d")
177
+ if hasattr(self, '_web_runner') and self._web_runner:
178
+ await self.web_server.cleanup()
179
+ if self._is_connected:
180
+ try:
181
+ if self.LOGS:
182
+ await self.send_message(chat_id=self.LOGS, text=f"{self._bot_info.mention} rebooting...")
183
+ await self.send_document(chat_id=self.LOGS, document=f"logs/log-{today}.txt")
184
+ logger.info(f"{self._bot_info.first_name} - @{self._bot_info.username} is rebooting")
185
+ except Exception as e:
186
+ logger.error(f"Error sending reboot notification: {e}")
187
+ await super().stop()
188
+ self._is_connected = False
189
+ await asyncio.sleep(2)
190
+
191
+ logger.info("Restarting process...")
192
+ os.execl(sys.executable, sys.executable, *sys.argv)
193
+ except Exception as e:
194
+ logger.error(f"Error during reboot: {e}")
195
+ os.execl(sys.executable, sys.executable, *sys.argv)
196
+
197
+ async def handle_restart(self):
198
+ if os.path.exists('restart.txt'):
199
+ with open('restart.txt', 'r') as file:
200
+ data = file.read().split()
201
+ chat_id = int(data[0])
202
+ Message_id = int(data[1])
203
+ try:await self.send_message(chat_id=self.LOGS, text=f"{self._bot_info.mention} restarted successfully!")
204
+ except Exception as e:logger.error(f"Failed to send restart notification: {e}")
205
+ try:await self.edit_message_text(chat_id=chat_id,message_id=Message_id, text="Bot restarted successfully!")
206
+ except:
207
+ await self.send_message(chat_id=chat_id, text="Bot restarted successfully!",reply_to_message_id=Message_id-1,)
208
+ await self.delete_messages(chat_id=chat_id,message_ids=Message_id)
209
+ os.remove('restart.txt')
210
+ else:
211
+ try:await self.send_message(chat_id=self.LOGS, text=f"{self._bot_info.mention} started successfully!")
212
+ except Exception as e:logger.error(f"Failed to send start notification: {e}")
213
+
214
+
215
+ async def setup_commands(self,set_commands=False):
216
+ if set_commands:
217
+ commands = await super().get_bot_commands()
218
+ if commands == []:
219
+ b_index = self.TOKEN_INDEX + 1
220
+ bot_commands = [
221
+ BotCommand("start", f"{b_index} Start the bot"),
222
+ BotCommand("help", f"{b_index} Get help"),
223
+ BotCommand("logs", f"{b_index} Get logs (Admin only)"),
224
+ BotCommand("reboot", f"{b_index} Reboot the bot (Admin only)")
225
+ ]
226
+ await super().set_bot_commands(bot_commands)
227
+
228
+ async def send_logs(self):
229
+ logger.info("Sending yesterday logs...")
230
+ if not self._is_connected:
231
+ logger.warning("Bot is not connected")
232
+ if self._is_connected:
233
+
234
+ yesterday = (self.TZ_now - timedelta(days=1)).strftime("%Y-%m-%d")
235
+ try:
236
+ m = await self.send_document(chat_id=self.LOGS, document=f"logs/log-{yesterday}.txt")
237
+ logger.info(f"Logs sent to {m.chat.first_name} - @{m.chat.username}")
238
+ except Exception as e:
239
+ logger.error(f"Error sending logs: {e}")
@@ -0,0 +1,33 @@
1
+ import schedule
2
+ import threading
3
+ import time
4
+ from datetime import datetime, timedelta
5
+
6
+ from d4rk.Logs import setup_logger
7
+ logger = setup_logger(__name__)
8
+
9
+ class scheduler:
10
+ async def start_scheduler(self):
11
+ if self._scheduler_thread and self._scheduler_thread.is_alive():
12
+ return
13
+ schedule.clear()
14
+ schedule.every().day.at(time_str="00:01",tz="Asia/Kolkata").do(lambda: self._safe_async(self.send_logs))
15
+ self._stop_scheduler.clear()
16
+ self._scheduler_thread = threading.Thread(target=self._run_scheduler, daemon=True)
17
+ self._scheduler_thread.start()
18
+ logger.info("Background scheduler started for log maintenance")
19
+
20
+ def stop_scheduler(self):
21
+ if self._scheduler_thread:
22
+ self._stop_scheduler.set()
23
+ self._scheduler_thread.join(timeout=5)
24
+ logger.info("Background scheduler stopped")
25
+
26
+ def _run_scheduler(self):
27
+ while not self._stop_scheduler.is_set():
28
+ try:
29
+ schedule.run_pending()
30
+ time.sleep(60) # Check every minute
31
+ except Exception as e:
32
+ if logger:
33
+ logger.error(f"Scheduler error: {e}")
d4rk/Logs/__init__.py ADDED
@@ -0,0 +1 @@
1
+ from ._logger import *
d4rk/Logs/_logger.py ADDED
@@ -0,0 +1,70 @@
1
+ # src/Log/_logger_config.py
2
+
3
+ import os
4
+ import logging
5
+ from datetime import datetime, timezone, timedelta
6
+ from logging.handlers import TimedRotatingFileHandler
7
+
8
+ def get_timezone_offset(time_zone: str = "00:00") -> timezone:
9
+ if time_zone:
10
+ try:
11
+ hours, minutes = time_zone.split(':')
12
+ if hours.startswith("-"):
13
+ return timezone(timedelta(hours=-int(hours), minutes=-int(minutes)))
14
+ else:
15
+ return timezone(timedelta(hours=int(hours), minutes=int(minutes)))
16
+ except ValueError:
17
+ raise ValueError(f"Invalid TIME_ZONE format: {time_zone}")
18
+ return timezone(timedelta(hours=0))
19
+
20
+ TZ = get_timezone_offset(os.getenv("TIME_ZONE", "05:30"))
21
+
22
+ class TimeZoneFormatter(logging.Formatter):
23
+ def converter(self, timestamp):
24
+ dt = datetime.fromtimestamp(timestamp, tz=timezone.utc)
25
+ return dt.astimezone(TZ).timetuple()
26
+
27
+ def formatTime(self, record, datefmt=None):
28
+ dt = datetime.fromtimestamp(record.created, tz=timezone.utc)
29
+ time_zone_time = dt.astimezone(TZ)
30
+ if datefmt:
31
+ return time_zone_time.strftime(datefmt)
32
+ else:
33
+ return time_zone_time.strftime('%Y-%m-%d %H:%M:%S %z')
34
+
35
+ def setup_logger(name=__name__, log_level=logging.INFO):
36
+ log_dir = "logs"
37
+ if not os.path.exists(log_dir):
38
+ os.makedirs(log_dir)
39
+
40
+ logger = logging.getLogger(name)
41
+ logger.setLevel(log_level)
42
+
43
+ if logger.handlers:
44
+ return logger
45
+
46
+ time_zone_now = datetime.now(TZ)
47
+
48
+ file_handler = TimedRotatingFileHandler(
49
+ filename=os.path.join(log_dir, f"log-{time_zone_now.strftime('%Y-%m-%d')}.txt"),
50
+ when='midnight',
51
+ interval=1,
52
+ backupCount=30
53
+ )
54
+ file_handler.setLevel(log_level)
55
+
56
+ console_handler = logging.StreamHandler()
57
+ console_handler.setLevel(log_level)
58
+
59
+ # Create formatter with Sri Lanka timezone
60
+ formatter = TimeZoneFormatter(
61
+ '%(asctime)s - %(name)s - %(funcName)s:%(lineno)d - %(levelname)s - %(message)s',
62
+ datefmt='%Y-%m-%d %H:%M:%S'
63
+ )
64
+
65
+ file_handler.setFormatter(formatter)
66
+ console_handler.setFormatter(formatter)
67
+ logger.addHandler(file_handler)
68
+ logger.addHandler(console_handler)
69
+ logger.propagate = False
70
+ return logger
@@ -0,0 +1,2 @@
1
+ from ._commands import command , find_command
2
+ from ._movie_title import movie_finder
@@ -0,0 +1,66 @@
1
+ import re
2
+ import requests
3
+
4
+ from typing import Union
5
+ from functools import wraps
6
+ from pyrogram import Client, filters
7
+
8
+ from d4rk.Logs import setup_logger
9
+
10
+ logger = setup_logger(__name__)
11
+
12
+ command_registry = []
13
+
14
+ def command(command: Union[str, list], description: str):
15
+ def decorator(func):
16
+ command_registry.append({
17
+ "command": command,
18
+ "description": description,
19
+ "handler": func
20
+ })
21
+ logger.info(f"Registered command: {command} - {description}")
22
+ @Client.on_message(filters.command(command))
23
+ @wraps(func)
24
+ async def wrapper(client, message):
25
+ return await func(client, message)
26
+
27
+ return wrapper
28
+ return decorator
29
+
30
+ class CommandAI:
31
+ def __init__(self):
32
+ self.api_key = "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"
33
+ self.api_url = "https://api-inference.huggingface.co/models/facebook/bart-large-mnli"
34
+ self.headers = {"Authorization": "Bearer " + self.api_key}
35
+
36
+ def __post(self,payload):
37
+ response = requests.post(self.api_url, headers=self.headers, json=payload)
38
+ if response.status_code != 200:
39
+ print(f"❌ Error {response.status_code}: {response.text}")
40
+ return None
41
+ return response.json()
42
+
43
+ def extract_username(self, query: str):
44
+ match = re.search(r'@[\w\d_]+', query)
45
+ return match.group(0) if match else None
46
+
47
+ def get_command(self,user_query):
48
+ labels = [entry["description"] for entry in command_registry]
49
+ response = self.__post(
50
+ payload={
51
+ "inputs": user_query,
52
+ "parameters": {"candidate_labels": labels},
53
+ }
54
+ )
55
+ print(response)
56
+ if response is None:return None
57
+ best_label = response["labels"][0]
58
+ if best_label is None:
59
+ logger.error("No matching command found for the user query.")
60
+ return None
61
+ for entry in command_registry:
62
+ if entry["description"] == best_label:
63
+ return entry["command"] if isinstance(entry["command"], str) else entry["command"][0]
64
+ return None
65
+
66
+ find_command = CommandAI()
@@ -0,0 +1,26 @@
1
+ # src/models/_movie_title.py
2
+
3
+ from rapidfuzz import process
4
+
5
+ class FindMovie:
6
+ def __init__(self):
7
+ self.movie_titles = []
8
+ self.movie_docs = []
9
+
10
+ def load_movie_titles(self, titles :list):
11
+ self.movie_docs = titles
12
+ self.movie_titles = [doc["title"] for doc in self.movie_docs]
13
+
14
+ def get_match_one(self,user_input):
15
+ match, _, _ = process.extractOne(user_input, self.movie_titles)
16
+ return match
17
+
18
+ def get_match_doc(self,match):
19
+ return next((doc for doc in self.movie_docs if doc["title"] == match), None)
20
+
21
+ def get_match(self, user_input :str,limit :int=3):
22
+ matches = process.extract(user_input, self.movie_titles, limit=limit)
23
+ return matches
24
+
25
+ movie_finder = FindMovie()
26
+
d4rk/Utils/__init__.py ADDED
@@ -0,0 +1,6 @@
1
+ from ._terminal import clear_terminal
2
+ from ._ip import check_public_ip_reachable , get_public_ip
3
+ from ._decorators import authorize
4
+ from ._delete import delete
5
+ from ._ractions import Reacts
6
+ from ._fonts import FontMessageMixin , get_font
@@ -0,0 +1,76 @@
1
+ import os
2
+ import asyncio
3
+ import functools
4
+
5
+ from typing import Union
6
+ from pyrogram import Client
7
+ from d4rk.Logs import setup_logger
8
+ from pyrogram.types import Message , CallbackQuery , ChatPrivileges
9
+
10
+ from ._delete import delete
11
+ from ._ractions import Reacts
12
+
13
+ logger = setup_logger(__name__)
14
+
15
+ OWNER = os.getenv("OWNER", 7859877609)
16
+
17
+ def authorize(sudo=True,admin=False,delete_command=True, react=True,react_emoji:Reacts=Reacts.fire,alert=True,permission=None):
18
+ def decorator(func):
19
+ @functools.wraps(func)
20
+ async def wrapper(client: Client, message: Union[Message,CallbackQuery]):
21
+ try:
22
+ user = message.from_user
23
+ if not user:
24
+ logger.warning(f"Unauthorized access attempt from non-user message: {message}")
25
+ me = await client.get_me()
26
+ is_admin = False
27
+ if admin:
28
+ if message.chat.type.name.lower() in ["group","supergroup"]:
29
+ role = await client.get_chat_member(message.chat.id, user.id)
30
+ myrole = await client.get_chat_member(message.chat.id, me.id)
31
+ if (user_admin:= role.status.name.lower() in ["creator", "administrator","owner"]) and (i_am_admin:=myrole.status.name.lower() in ["creator", "administrator","owner"]):
32
+ if permission:
33
+ privileges = getattr(role, "privileges", None)
34
+ myprivileges = getattr(myrole, "privileges", None)
35
+ if privileges and myprivileges:
36
+ has_permission = getattr(privileges, permission, False)
37
+ has_my_permission = getattr(myprivileges, permission, False)
38
+ if has_permission and has_my_permission:
39
+ is_admin = True
40
+ else:
41
+ msg = ""
42
+ if not has_permission and not has_my_permission:msg = f"❌ Neither you nor I "
43
+ elif not has_permission:msg = f"❌ You don't"
44
+ elif not has_my_permission:msg = f"❌ I don't"
45
+ msg += f" have the required permission: `{permission}`."
46
+ return await client.send_alert(message=message,text=msg)
47
+ else:is_admin = True
48
+ else:
49
+ if alert:
50
+ return await client.send_alert(message=message,text="❌ This command can only be used in groups.")
51
+
52
+ authorized = user.id == OWNER or (sudo and user.id in client.sudo_users) or is_admin
53
+ if react and isinstance(message, Message):
54
+ try:await message.react(react_emoji)
55
+ except:pass
56
+
57
+ if not authorized:
58
+ logger.warning(f"Unauthorized {func.__name__} request from user {user.id} @{user.username}")
59
+ if alert:
60
+ m = await client.send_alert(message=message,text="❌ Unauthorized access.")
61
+ if react and isinstance(message, Message):
62
+ await message.react(Reacts.shit)
63
+ if m:await delete(client, message.chat.id, m.id, timeout=5)
64
+
65
+ else:
66
+ logger.info(f"Authorized user {user.id} executing {func.__name__}")
67
+ await func(client, message)
68
+ if delete_command:
69
+ try:await delete(client, message.chat.id, message.id, timeout=5)
70
+ except:pass
71
+ except Exception as e:
72
+ logger.error(f"Error in {func.__name__}: {e}")
73
+ return
74
+ return wrapper
75
+ return decorator
76
+
d4rk/Utils/_delete.py ADDED
@@ -0,0 +1,16 @@
1
+ import asyncio
2
+
3
+ from pyrogram import Client
4
+ from d4rk.Logs import setup_logger
5
+
6
+ logger = setup_logger(__name__)
7
+
8
+ async def delete(client: Client, chat_id: int, message_id: int,timeout: int=3):
9
+ await asyncio.create_task(delete_message_worker(client,chat_id, message_id, timeout))
10
+
11
+ async def delete_message_worker(client,chat_id, message_id, timeout):
12
+ await asyncio.sleep(timeout)
13
+ try:
14
+ await client.delete_messages(chat_id=chat_id, message_ids=message_id)
15
+ except Exception as e:
16
+ logger.error(f"Error deleting message {message_id}: {e}")
d4rk/Utils/_fonts.py ADDED
@@ -0,0 +1,82 @@
1
+ # src/Utils/_fonts.py
2
+
3
+ import re
4
+
5
+ from typing import Union
6
+
7
+ from pyrogram import Client
8
+ from pyrogram.enums import ParseMode
9
+ from pyrogram.types import Message, CallbackQuery
10
+
11
+ __font1 = {'a' : 'ᴀ','b' : 'ʙ','c' : 'ᴄ','d' : 'ᴅ','e' : 'ᴇ','f' : 'ғ','g' : 'ɢ','h' : 'ʜ','i' : 'ɪ','j' : 'ᴊ','k' : 'ᴋ','l' : 'ʟ','m' : 'ᴍ','n' : 'ɴ','o' : 'ᴏ','p' : 'ᴘ','q' : 'ǫ','r' : 'ʀ','s' : 's','t' : 'ᴛ','u' : 'ᴜ','v' : 'ᴠ','w' : 'ᴡ','x' : 'x','y' : 'ʏ','z' : 'ᴢ','1' : '𝟷','2' : '𝟸','3' : '𝟹','4' : '𝟺','5' : '𝟻','6' : '𝟼','7' : '𝟽','8' : '𝟾','9' : '𝟿','0' : '𝟶'}
12
+ __font2 = {'a':'𝐚','b':'𝐛','c':'𝐜','d':'𝐝','e':'𝐞','f':'𝐟','g':'𝐠','h':'𝐡','i':'𝐢','j':'𝐣','k':'𝐤','l':'𝐥','m':'𝐦','n':'𝐧','o':'𝐨','p':'𝐩','q':'𝐪','r':'𝐫','s':'𝐬','t':'𝐭','u':'𝐮','v':'𝐯','w':'𝐰','x':'𝐱','y':'𝐲','z':'𝐳','1':'𝟏','2':'𝟐','3':'𝟑','4':'𝟒','5':'𝟓','6':'𝟔','7':'𝟕','8':'𝟖','9':'𝟗','0':'𝟎'}
13
+ __font3 = {'a':'𝒶','b':'𝒷','c':'𝒸','d':'𝒹','e':'ℯ','f':'𝒻','g':'𝑔','h':'𝒽','i':'𝒾','j':'𝒿','k':'𝓀','l':'𝓁','m':'𝓂','n':'𝓃','o':'𝑜','p':'𝓅','q':'𝓆','r':'𝓇','s':'𝓈','t':'𝓉','u':'𝓊','v':'𝓋','w':'𝓌','x':'𝓍','y':'𝓎','z':'𝓏','1':'𝟣','2':'𝟤','3':'𝟥','4':'𝟦','5':'𝟧','6':'𝟨','7':'𝟩','8':'𝟪','9':'𝟫','0':'𝟢'}
14
+ __font4 = {'a':'𝓐','b':'𝓑','c':'𝓒','d':'𝓓','e':'𝓔','f':'𝓕','g':'𝓖','h':'𝓗','i':'𝓘','j':'𝓙','k':'𝓚','l':'𝓛','m':'𝓜','n':'𝓝','o':'𝓞','p':'𝓟','q':'𝓠','r':'𝓡','s':'𝓢','t':'𝓣','u':'𝓤','v':'𝓥','w':'𝓦','x':'𝓧','y':'𝓨','z':'𝓩','1':'𝟙','2':'𝟚','3':'𝟛','4':'𝟜','5':'𝟝','6':'𝟞','7':'𝟟','8':'𝟠','9':'𝟡','0':'𝟘'}
15
+ __font5 = {'a':'🅰','b':'🅱','c':'🅲','d':'🅳','e':'🅴','f':'🅵','g':'🅶','h':'🅷','i':'🅸','j':'🅹','k':'🅺','l':'🅻','m':'🅼','n':'🅽','o':'🅾','p':'🅿','q':'🆀','r':'🆁','s':'🆂','t':'🆃','u':'🆄','v':'🆅','w':'🆆','x':'🆇','y':'🆈','z':'🆉','1':'➊','2':'➋','3':'➌','4':'➍','5':'➎','6':'➏','7':'➐','8':'➑','9':'➒','0':'⓿'}
16
+ __font6 = {'a':'𝕒','b':'𝕓','c':'𝕔','d':'𝕕','e':'𝕖','f':'𝕗','g':'𝕘','h':'𝕙','i':'𝕚','j':'𝕛','k':'𝕜','l':'𝕝','m':'𝕞','n':'𝕟','o':'𝕠','p':'𝕡','q':'𝕢','r':'𝕣','s':'𝕤','t':'𝕥','u':'𝕦','v':'𝕧','w':'𝕨','x':'𝕩','y':'𝕪','z':'𝕫','1':'𝟙','2':'𝟚','3':'𝟛','4':'𝟜','5':'𝟝','6':'𝟞','7':'𝟟','8':'𝟠','9':'𝟡','0':'𝟘'}
17
+
18
+ class FontMessageMixin(Client):
19
+ async def send_message(self, chat_id :Union[int, str], text :str, parse_mode=None, *args, **kwargs):
20
+ return await super().send_message(chat_id=chat_id, text=get_font(text=text, font=self.font), parse_mode=ParseMode.HTML, *args, **kwargs)
21
+
22
+ async def send_photo(self, chat_id:Union[int, str], photo :str, caption :str=None, parse_mode=None, *args, **kwargs):
23
+ return await super().send_photo(chat_id=chat_id, photo=photo, caption=get_font(text=caption, font=self.font), parse_mode=ParseMode.HTML, *args, **kwargs)
24
+
25
+ async def edit_message_text(self, chat_id: Union[int, str], message_id: int, text :str, parse_mode=None, *args, **kwargs):
26
+ return await super().edit_message_text(chat_id=chat_id, message_id=message_id, text=get_font(text=text, font=self.font), parse_mode=ParseMode.HTML, *args, **kwargs)
27
+
28
+ async def edit_message_caption(self, chat_id :Union[int, str], message_id : int, caption :str, parse_mode=None, *args, **kwargs):
29
+ return await super().edit_message_caption(chat_id=chat_id, message_id=message_id, caption=get_font(text=caption, font=self.font), parse_mode=ParseMode.HTML, *args, **kwargs)
30
+
31
+ async def edit_inline_text(self, inline_message_id: int, text :str, parse_mode=None, *args, **kwargs):
32
+ return await super().edit_inline_text(inline_message_id, text=get_font(text=text, font=self.font), parse_mode=ParseMode.HTML, *args, **kwargs)
33
+
34
+ async def send_document(self, chat_id :Union[int, str], document, caption :str=None, parse_mode=None, *args, **kwargs):
35
+ return await super().send_document(chat_id, document, caption=get_font(text=caption, font=self.font), parse_mode=ParseMode.HTML, *args, **kwargs)
36
+
37
+ async def send_video(self, chat_id :Union[int,str], video, caption :str=None, parse_mode=None, *args, **kwargs):
38
+ return await super().send_video(chat_id, video, caption=get_font(text=caption, font=self.font), parse_mode=ParseMode.HTML, *args, **kwargs)
39
+
40
+ async def send_audio(self, chat_id :Union[int,str], audio, caption :str=None, parse_mode=None, *args, **kwargs):
41
+ return await super().send_audio(chat_id, audio, caption=get_font(text=caption, font=self.font), parse_mode=ParseMode.HTML, *args, **kwargs)
42
+
43
+ async def send_voice(self, chat_id :Union[int,str], voice, caption :str=None, parse_mode=None, *args, **kwargs):
44
+ return await super().send_voice(chat_id, voice, caption=get_font(text=caption, font=self.font), parse_mode=ParseMode.HTML, *args, **kwargs)
45
+
46
+ async def send_alert(self,message:Union[Message,CallbackQuery], text :str):
47
+ if isinstance(message, Message):
48
+ return await message.reply(text)
49
+ elif isinstance(message, CallbackQuery):
50
+ return await message.answer(text, show_alert=True)
51
+
52
+ def get_font(text: str, font: int = 1):
53
+ if int(font) ==0:return text
54
+ font_name = f"__font{font}"
55
+ font_style: dict = globals().get(font_name, None)
56
+ if not text:
57
+ return text
58
+ if font_style is None:
59
+ return text
60
+
61
+ def convert(match):
62
+ if match.group("tag"):
63
+ return match.group("tag") # Preserve HTML tags
64
+ elif match.group("braced"):
65
+ return match.group("braced") # Preserve {placeholders}
66
+ elif match.group("command"):
67
+ return match.group("command") # Preserve /commands
68
+ elif match.group("mention"):
69
+ return match.group("mention")
70
+ else:
71
+ content = match.group("text")
72
+ return "".join(font_style.get(char, char) for char in content)
73
+
74
+ pattern = (
75
+ r"(?P<tag><[^>]+>)" # HTML tags
76
+ r"|(?P<braced>\{[^}]+\})" # Braced placeholders
77
+ r"|(?P<command>/\w+)" # /commands
78
+ r"|(?P<mention>@[\w_]+)" # @usernames (mentions)
79
+ r"|(?P<text>\w+)" # Regular words
80
+ )
81
+
82
+ return re.sub(pattern, convert, text.lower(), flags=re.IGNORECASE)
d4rk/Utils/_ip.py ADDED
@@ -0,0 +1,21 @@
1
+ import requests
2
+ import socket
3
+ import asyncio
4
+
5
+ def get_public_ip():
6
+ try:
7
+ response = requests.get('https://api.ipify.org?format=json')
8
+ ip_data = response.json()
9
+ ip = ip_data['ip']
10
+ return ip
11
+ except requests.RequestException as e:
12
+ return None
13
+
14
+
15
+ async def check_public_ip_reachable(ip: str, port: int = 8080):
16
+ await asyncio.sleep(1)
17
+ try:
18
+ sock = socket.create_connection((ip, port), timeout=5)
19
+ return True
20
+ except (socket.timeout, socket.error):
21
+ return False
@@ -0,0 +1,92 @@
1
+ import re
2
+ import PTN
3
+ import unicodedata
4
+
5
+ from typing import List, Optional
6
+ from dataclasses import dataclass, field
7
+
8
+ @dataclass
9
+ class Movie:
10
+ title: str
11
+ normalized_title: str
12
+ year: Optional[int]
13
+ resolution: Optional[str]
14
+ quality: Optional[str]
15
+ codec: Optional[str]
16
+ extra: List[str] = field(default_factory=list)
17
+ split: bool = False
18
+ part: Optional[int] = None
19
+
20
+ class MovieParser:
21
+ def __parse_file_name(self, file_name: str):
22
+ file_name = re.sub(r'@[\w_]+', '', file_name.lower()).strip()
23
+ file_name = file_name.replace('_', ' ')
24
+ self.file_name = file_name
25
+ self.extension = self.file_name.split('.')[-1]
26
+ self.movie = PTN.parse(self.file_name)
27
+ self.tags = []
28
+
29
+ def fallback_title(self, file_name):
30
+ name = re.sub(r'\.\w{2,4}$', '', file_name)
31
+ name = name.replace('_', ' ').replace('.', ' ')
32
+ name = re.sub(r'@\w+', '', name)
33
+ name = unicodedata.normalize('NFKC', name)
34
+ tags_pattern = r'\b(480p|720p|1080p|2160p|BR_Rip|WEBRip|HDRip|x264|x265|HEVC|AAC|DD\+?5\.1|[0-9]+MB|1GB|2GB|Tamil|Telugu|Hindi|English|Dubbed|HDTV|WEB-DL|BluRay|Blu-ray|YTS|YIFY|fps)\b'
35
+ name = re.sub(tags_pattern, '', name, flags=re.IGNORECASE)
36
+ name = re.split(r'\b(S\d+|Ep\d+)\b', name, 1)[0]
37
+ name = re.sub(r'\s+', ' ', name).strip()
38
+ return name
39
+
40
+ def fallback_year(self, file_name):
41
+ years = re.findall(r'(?<!\d)(19\d{2}|20\d{2})(?!\d)', file_name)
42
+ return int(years[0]) if years else None
43
+
44
+ def _fix_roman_numerals(self, text: str) -> str:
45
+ roman_pattern = r'\b(i{1,3}|iv|v|vi{0,3}|ix|x{1,3}|xl|l|li{0,3}|lx|xc|c|ci{0,3}|cd|d|dc|cm|m|m{1,3})\b'
46
+ return re.sub(roman_pattern, lambda m: m.group(0).upper(), text, flags=re.IGNORECASE)
47
+
48
+ def extract(self, file_name: str) -> Movie:
49
+
50
+ self.__parse_file_name(file_name)
51
+ title = self.movie.get('title', '').replace('.', ' ').strip()
52
+ if not title:title = self.fallback_title(self.file_name)
53
+ else:title = title.replace('.', ' ').strip()
54
+
55
+ year = self.movie.get('year') or self.fallback_year(self.file_name)
56
+ if year and str(year) in title:title = title.replace(str(year), '').strip()
57
+
58
+ resolution = self.movie.get('resolution')
59
+ quality = self.movie.get('quality')
60
+ codec = self.movie.get('codec')
61
+ if codec:codec = codec.replace('H', 'x').replace('.', '')
62
+
63
+ extra = []
64
+ tag_keywords = {
65
+ 'psa': 'PSA', 'pahe': 'Pahe', 'galaxyrg': 'GalaxyRG',
66
+ 'imax': 'IMAX', '60fps': '60FPS'
67
+ }
68
+ for k, v in tag_keywords.items():
69
+ if k in self.file_name:
70
+ extra.append(v)
71
+
72
+ if any(k in self.file_name for k in ['dolby', 'atmos']):
73
+ extra.append('DolbyAtmos')
74
+
75
+ match = re.search(r'\.(\d{3})$', file_name)
76
+ split = match is not None
77
+ part = int(match.group(1)) if split else None
78
+
79
+ return Movie(
80
+ title=self._fix_roman_numerals(title.title()),
81
+ normalized_title=re.sub(r'[^a-z0-9&\+]+', ' ', title.lower()).strip(),
82
+ year=year,
83
+ resolution=resolution,
84
+ quality=quality,
85
+ codec=codec,
86
+ extra=extra,
87
+ split=split,
88
+ part=part
89
+ )
90
+
91
+ parser = MovieParser()
92
+
@@ -0,0 +1,79 @@
1
+ from dataclasses import dataclass
2
+
3
+ @dataclass
4
+ class Reacts:
5
+ like: str = "👍"
6
+ unlike: str = "👎"
7
+ heart: str = "❤"
8
+ fire: str = "🔥"
9
+ face_with_hearts: str = "🥰"
10
+ bye: str = "👏"
11
+ beaming_face: str = "😁"
12
+ thinking: str = "🤔"
13
+ exploding: str = "🤯"
14
+ screaming: str = "😱"
15
+ anger: str = "🤬"
16
+ crying: str = "😢"
17
+ party_popper: str = "🎉"
18
+ star_struck: str = "🤩"
19
+ vomiting: str = "🤮"
20
+ shit: str = "💩"
21
+ folded_hands: str = "🙏"
22
+ ok_hand: str = "👌"
23
+ dove: str = "🕊"
24
+ lightning: str = "⚡"
25
+ clown: str = "🤡"
26
+ yawning: str = "🥱"
27
+ woozy: str = "🥴"
28
+ woow: str = "😍"
29
+ whale: str = "🐳"
30
+ heart_on_fire: str = "❤‍🔥"
31
+ dark_moon: str = "🌚"
32
+ hot_dog: str = "🌭"
33
+ hundred: str = "💯"
34
+ rolling_on_the_floor_laughing: str = "🤣"
35
+ banana: str = "🍌"
36
+ trophy: str = "🏆"
37
+ broken_heart: str = "💔"
38
+ raised_eyebrow: str = "🤨"
39
+ neutral_face: str = "😐"
40
+ strawberry: str = "🍓"
41
+ champagne: str = "🍾"
42
+ kiss: str = "💋"
43
+ middle_finger: str = "🖕"
44
+ smiling_devil: str = "😈"
45
+ sleeping: str = "😴"
46
+ crying_loudly: str = "😭"
47
+ nerd_face: str = "🤓"
48
+ ghost: str = "👻"
49
+ technologist: str = "👨‍💻"
50
+ eyes: str = "👀"
51
+ jack_o_lantern: str = "🎃"
52
+ see_no_evil: str = "🙈"
53
+ angel: str = "😇"
54
+ fearful_face: str = "😨"
55
+ handshake: str = "🤝"
56
+ writing_hand: str = "✍"
57
+ hugging_face: str = "🤗"
58
+ saluting_face: str = "🫡"
59
+ santa_claus: str = "🎅"
60
+ christmas_tree: str = "🎄"
61
+ snowman: str = "☃"
62
+ nail_polish: str = "💅"
63
+ zany_face: str = "🤪"
64
+ moai: str = "🗿"
65
+ cool: str = "🆒"
66
+ heart_with_arrow: str = "💘"
67
+ hear_no_evil: str = "🙉"
68
+ unicorn: str = "🦄"
69
+ face_blowing_kiss: str = "😘"
70
+ pill: str = "💊"
71
+ speak_no_evil: str = "🙊"
72
+ sunglasses: str = "😎"
73
+ alien: str = "👾"
74
+ man_shrugging: str = "🤷‍♂"
75
+ woman_shrugging: str = "🤷‍♀"
76
+
77
+
78
+
79
+
@@ -0,0 +1,7 @@
1
+ import os
2
+
3
+ def clear_terminal():
4
+ if os.name == 'nt':
5
+ os.system('cls')
6
+ else:
7
+ os.system('clear')
d4rk/__init__.py ADDED
@@ -0,0 +1,5 @@
1
+ from .Database import db
2
+ from .Handlers import BotManager, scheduler
3
+ from .Logs import setup_logger , get_timezone_offset
4
+ from .Models import command, find_command, movie_finder
5
+ from .Utils import *
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: d4rktg
3
- Version: 0.0.1
3
+ Version: 0.0.2
4
4
  Summary: A module for create with easy and fast
5
5
  Author: D4rkShell
6
6
  Author-email: premiumqtrst@gmail.com
@@ -0,0 +1,23 @@
1
+ d4rk/__init__.py,sha256=Xq5qqX-FwosTJBLrSPF53FPD6fEISH750hbImZpSu5k,202
2
+ d4rk/Database/__init__.py,sha256=TQB5D8PBDCq80jPq6rsC2G939yYYKTh_bCcOWsZ-nA8,18
3
+ d4rk/Database/db.py,sha256=MHy3vJRyDMxFbuw8iIw2HZ26cQdT98MBz6LUi1wnpW0,1854
4
+ d4rk/Handlers/__init__.py,sha256=lO1b7Tnu3GWgwcJmX5qepiNqaBBi6qTgjj0SlzVUyEA,63
5
+ d4rk/Handlers/_bot.py,sha256=A4zPQexHScxIajtKMbsjH1ohgVssukeMaL0jKhgpiPI,10502
6
+ d4rk/Handlers/_scheduler.py,sha256=AyqexO4nxZlIzRfU9vWTfJtTWQVQmP4de7GRPg-3JkA,1236
7
+ d4rk/Logs/__init__.py,sha256=mXWD5jXnyH3_AvS7K_ki3iw5BpoEAvrDFbmr-iEFNnY,22
8
+ d4rk/Logs/_logger.py,sha256=C09OA5wUpl5oLp7oAs5P2CrpBKkcHxjUYetCtHJ1Kqk,2379
9
+ d4rk/Models/__init__.py,sha256=84TdTKOHM5fr3T_B77tqgRTnLk5UtaEcGYY0mdLSUNo,85
10
+ d4rk/Models/_commands.py,sha256=YbV6LQgMlTnbtaPRI_yRWWACQVgEBrlPclU-l51yH4Q,2248
11
+ d4rk/Models/_movie_title.py,sha256=s4nUjMTe7th7gNkLy1wDiEZV-OcBvx3ZoV7gVeP9428,788
12
+ d4rk/Utils/__init__.py,sha256=rLJgLDUQqt8e0o1FdXu0RKmkYpV8sd8mfEzYewBXIy4,242
13
+ d4rk/Utils/_decorators.py,sha256=DjrzX3W6nGmrShH2KT4Zm4vfczj3HJv0CGb8Z-9tZ40,4093
14
+ d4rk/Utils/_delete.py,sha256=gSmQAENGmM5XCCJfPuJ4vRMSxtixYG0vYdocU-2TSFg,581
15
+ d4rk/Utils/_fonts.py,sha256=1NxszvAB7kZMMSUBUYFb0vwvqtJFNpFlO04e3jYteGY,6669
16
+ d4rk/Utils/_ip.py,sha256=KJJW2QSngshIVWCO5YPXF1wj4IPQzVN5oFofpfzlU5w,559
17
+ d4rk/Utils/_movie_parser.py,sha256=eiy1YVSu9fgk9pIXnMojJkWhW6SbdewKGF0ATk2qZoA,3440
18
+ d4rk/Utils/_ractions.py,sha256=wOVPyoFnbDuMgoP6NF_gLO1DYcfhERC0trdAK1jWSE8,2170
19
+ d4rk/Utils/_terminal.py,sha256=Anu4OcffY3v6LMOrCskP1cHrJIliomo1Hjownbhh2sQ,125
20
+ d4rktg-0.0.2.dist-info/METADATA,sha256=QJ5UOauEFMY4S2B0pv9EXjhKEjWZP7JjmK_98fq3FNw,923
21
+ d4rktg-0.0.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
22
+ d4rktg-0.0.2.dist-info/top_level.txt,sha256=qs1qTnKWImmGi7E0FoJS0OAEOHoVZA9vHRS3Pm6ncAo,5
23
+ d4rktg-0.0.2.dist-info/RECORD,,
@@ -0,0 +1 @@
1
+ d4rk
@@ -1,4 +0,0 @@
1
- d4rktg-0.0.1.dist-info/METADATA,sha256=9R-llUIL7azgsymexAccpIGBWYxioHE0ECDJr_91dMs,923
2
- d4rktg-0.0.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
3
- d4rktg-0.0.1.dist-info/top_level.txt,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
4
- d4rktg-0.0.1.dist-info/RECORD,,
@@ -1 +0,0 @@
1
-
File without changes