d4rktg 1.2.4__tar.gz → 1.2.5__tar.gz
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.
- {d4rktg-1.2.4/d4rktg.egg-info → d4rktg-1.2.5}/PKG-INFO +1 -1
- d4rktg-1.2.5/VERSION.txt +1 -0
- {d4rktg-1.2.4 → d4rktg-1.2.5}/d4rk/Database/db.py +1 -2
- d4rktg-1.2.5/d4rk/Handlers/__init__.py +3 -0
- d4rktg-1.2.5/d4rk/Handlers/_bot.py +117 -0
- d4rktg-1.2.5/d4rk/Handlers/_logger_bot.py +88 -0
- {d4rktg-1.2.4 → d4rktg-1.2.5}/d4rk/Utils/_decorators.py +1 -15
- {d4rktg-1.2.4 → d4rktg-1.2.5}/d4rk/Utils/_filters.py +7 -13
- {d4rktg-1.2.4 → d4rktg-1.2.5}/d4rk/Utils/_movie_parser.py +2 -2
- d4rktg-1.2.5/d4rk/Web/__init__.py +4 -0
- d4rktg-1.2.5/d4rk/Web/web.py +201 -0
- d4rktg-1.2.5/d4rk/Web/web_server.py +52 -0
- d4rktg-1.2.5/d4rk/__init__.py +19 -0
- d4rktg-1.2.5/d4rk/_base.py +115 -0
- d4rktg-1.2.5/d4rk/_bot_manager.py +507 -0
- d4rktg-1.2.5/d4rk/errors/__init__.py +19 -0
- d4rktg-1.2.5/d4rk/errors/base.py +16 -0
- d4rktg-1.2.5/d4rk/errors/bot.py +33 -0
- d4rktg-1.2.5/d4rk/errors/config.py +17 -0
- d4rktg-1.2.5/d4rk/errors/database.py +22 -0
- {d4rktg-1.2.4 → d4rktg-1.2.5/d4rktg.egg-info}/PKG-INFO +1 -1
- {d4rktg-1.2.4 → d4rktg-1.2.5}/d4rktg.egg-info/SOURCES.txt +11 -1
- d4rktg-1.2.4/VERSION.txt +0 -1
- d4rktg-1.2.4/d4rk/Handlers/__init__.py +0 -3
- d4rktg-1.2.4/d4rk/Handlers/_bot.py +0 -209
- d4rktg-1.2.4/d4rk/Handlers/_scheduler.py +0 -33
- d4rktg-1.2.4/d4rk/__init__.py +0 -6
- {d4rktg-1.2.4 → d4rktg-1.2.5}/MANIFEST.in +0 -0
- {d4rktg-1.2.4 → d4rktg-1.2.5}/README.rst +0 -0
- {d4rktg-1.2.4 → d4rktg-1.2.5}/d4rk/Database/__init__.py +0 -0
- {d4rktg-1.2.4 → d4rktg-1.2.5}/d4rk/Handlers/_custom.py +0 -0
- {d4rktg-1.2.4 → d4rktg-1.2.5}/d4rk/Logs/__init__.py +0 -0
- {d4rktg-1.2.4 → d4rktg-1.2.5}/d4rk/Logs/_logger.py +0 -0
- {d4rktg-1.2.4 → d4rktg-1.2.5}/d4rk/Utils/__init__.py +0 -0
- {d4rktg-1.2.4 → d4rktg-1.2.5}/d4rk/Utils/_buttons.py +0 -0
- {d4rktg-1.2.4 → d4rktg-1.2.5}/d4rk/Utils/_fonts.py +0 -0
- {d4rktg-1.2.4 → d4rktg-1.2.5}/d4rk/Utils/_ip.py +0 -0
- {d4rktg-1.2.4 → d4rktg-1.2.5}/d4rk/Utils/_ractions.py +0 -0
- {d4rktg-1.2.4 → d4rktg-1.2.5}/d4rk/Utils/_terminal.py +0 -0
- {d4rktg-1.2.4 → d4rktg-1.2.5}/d4rk/Utils/_utils.py +0 -0
- {d4rktg-1.2.4 → d4rktg-1.2.5}/d4rktg.egg-info/dependency_links.txt +0 -0
- {d4rktg-1.2.4 → d4rktg-1.2.5}/d4rktg.egg-info/requires.txt +0 -0
- {d4rktg-1.2.4 → d4rktg-1.2.5}/d4rktg.egg-info/top_level.txt +0 -0
- {d4rktg-1.2.4 → d4rktg-1.2.5}/requirements.txt +0 -0
- {d4rktg-1.2.4 → d4rktg-1.2.5}/setup.cfg +0 -0
- {d4rktg-1.2.4 → d4rktg-1.2.5}/setup.py +0 -0
d4rktg-1.2.5/VERSION.txt
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
1.2.5
|
@@ -4,7 +4,7 @@ from pymongo import MongoClient , server_api
|
|
4
4
|
|
5
5
|
from d4rk.Logs import setup_logger
|
6
6
|
|
7
|
-
logger = setup_logger(
|
7
|
+
logger = setup_logger("Database")
|
8
8
|
|
9
9
|
class Database:
|
10
10
|
client = None
|
@@ -43,7 +43,6 @@ class Database:
|
|
43
43
|
base_collection = self.get_collection(collection_name)
|
44
44
|
instance = CustomClass(base_collection)
|
45
45
|
setattr(self, collection_name, instance)
|
46
|
-
logger.info(f"Loaded collection: {collection_name}")
|
47
46
|
|
48
47
|
def get_collection(self, collection):
|
49
48
|
return self.db[collection]
|
@@ -0,0 +1,117 @@
|
|
1
|
+
import os
|
2
|
+
import sys
|
3
|
+
import asyncio
|
4
|
+
import traceback
|
5
|
+
|
6
|
+
from pyrogram import Client
|
7
|
+
from pyrogram.errors import FloodWait, AccessTokenExpired
|
8
|
+
|
9
|
+
from d4rk.Logs import setup_logger
|
10
|
+
|
11
|
+
logger = setup_logger("BotClient")
|
12
|
+
|
13
|
+
logs_sent = False
|
14
|
+
logs_lock = asyncio.Lock()
|
15
|
+
|
16
|
+
class BotManager(Client):
|
17
|
+
_bot: Client = None
|
18
|
+
_bot_info = None
|
19
|
+
_is_connected = False
|
20
|
+
_rename = False
|
21
|
+
_flood_data = {}
|
22
|
+
_loop = None
|
23
|
+
_scheduler_thread = None
|
24
|
+
font = 0
|
25
|
+
sudo_users = []
|
26
|
+
owner = None
|
27
|
+
|
28
|
+
def create_client(self,app_name,token):
|
29
|
+
self.app_name = app_name
|
30
|
+
super().__init__(
|
31
|
+
name=app_name,
|
32
|
+
api_id=self.api_id,
|
33
|
+
api_hash=self.api_hash,
|
34
|
+
bot_token=token,
|
35
|
+
plugins=self.plugins,
|
36
|
+
in_memory=True
|
37
|
+
)
|
38
|
+
|
39
|
+
def _safe_async(self, coro_func):
|
40
|
+
if self._loop:asyncio.run_coroutine_threadsafe(coro_func(), self._loop)
|
41
|
+
else:logger.error("Event loop is not set for _safe_async")
|
42
|
+
|
43
|
+
async def powerup(self, app_name, max_attempts=3):
|
44
|
+
self.app_name = app_name
|
45
|
+
for attempt in range(max_attempts):
|
46
|
+
try:
|
47
|
+
await super().start()
|
48
|
+
|
49
|
+
self._bot_info = await self.me
|
50
|
+
self._is_connected = True
|
51
|
+
await self.handle_restart()
|
52
|
+
break
|
53
|
+
|
54
|
+
except FloodWait as e:
|
55
|
+
logger.error(f"FloodWait: {e.value} seconds")
|
56
|
+
raise e
|
57
|
+
|
58
|
+
except AccessTokenExpired:
|
59
|
+
logger.error(f"Access token expired (attempt {attempt + 1})")
|
60
|
+
break
|
61
|
+
|
62
|
+
except Exception as e:
|
63
|
+
logger.error(f"Error starting Client.Stoped !")
|
64
|
+
logger.error(traceback.format_exc())
|
65
|
+
break
|
66
|
+
else:
|
67
|
+
logger.error("Failed to start bot after all retry attempts")
|
68
|
+
|
69
|
+
async def powerdown(self, *args):
|
70
|
+
global logs_sent, logs_lock
|
71
|
+
if self._rename:await super().set_bot_info(lang_code='en',name=self.app_name + " (Offline)")
|
72
|
+
|
73
|
+
if self._is_connected:
|
74
|
+
try:
|
75
|
+
await super().stop()
|
76
|
+
self._is_connected = False
|
77
|
+
await asyncio.sleep(3)
|
78
|
+
except Exception as e:
|
79
|
+
logger.error(f"Error stopping bot client: {e}")
|
80
|
+
self._is_connected = False
|
81
|
+
|
82
|
+
async def reboot(self):
|
83
|
+
try:
|
84
|
+
if self._rename:await super().set_bot_info(lang_code='en',name=self.app_name + " (restarting..)")
|
85
|
+
logger.info("Initiating APP to reboot...")
|
86
|
+
await super().stop()
|
87
|
+
self._is_connected = False
|
88
|
+
await asyncio.sleep(2)
|
89
|
+
logger.info("Restarting process...")
|
90
|
+
os.execl(sys.executable, sys.executable, *sys.argv)
|
91
|
+
except Exception as e:
|
92
|
+
logger.error(f"Error during reboot: {e}")
|
93
|
+
os.execl(sys.executable, sys.executable, *sys.argv)
|
94
|
+
|
95
|
+
async def handle_flood_wait(self, delay):
|
96
|
+
"""Handle flood wait by notifying the bot manager to rotate tokens"""
|
97
|
+
logger.info(f"Handling flood wait for {delay} seconds")
|
98
|
+
pass
|
99
|
+
|
100
|
+
async def handle_restart(self):
|
101
|
+
if os.path.exists('restart.txt'):
|
102
|
+
try:
|
103
|
+
with open('restart.txt', 'r') as file:
|
104
|
+
|
105
|
+
data = file.read().split()
|
106
|
+
chat_id = int(data[0])
|
107
|
+
Message_id = int(data[1])
|
108
|
+
except Exception as e:logger.error(f"Failed to send restart notification: {e}")
|
109
|
+
try:await self.edit_message_text(chat_id=chat_id,message_id=Message_id, text="Bot restarted successfully!")
|
110
|
+
except:
|
111
|
+
try:
|
112
|
+
await self.send_message(chat_id=chat_id, text="Bot restarted successfully!",reply_to_message_id=Message_id-1,)
|
113
|
+
await self.delete_messages(chat_id=chat_id,message_ids=Message_id)
|
114
|
+
except:pass
|
115
|
+
|
116
|
+
if os.path.exists('restart.txt'):os.remove('restart.txt')
|
117
|
+
|
@@ -0,0 +1,88 @@
|
|
1
|
+
"""
|
2
|
+
Utility functions for working with the logger bot
|
3
|
+
"""
|
4
|
+
import logging
|
5
|
+
from typing import Optional
|
6
|
+
from telegram import Bot
|
7
|
+
|
8
|
+
logger = logging.getLogger(__name__)
|
9
|
+
|
10
|
+
class LoggerBotUtil:
|
11
|
+
"""Utility class for logger bot operations"""
|
12
|
+
bot: Optional[Bot] = None # class-level bot instance
|
13
|
+
start_message = None
|
14
|
+
start_message_id = None
|
15
|
+
start_message_chat_id = None
|
16
|
+
|
17
|
+
|
18
|
+
@classmethod
|
19
|
+
def set_token(cls, token: str):
|
20
|
+
"""Initialize the bot with the given token"""
|
21
|
+
try:
|
22
|
+
cls.bot = Bot(token=token)
|
23
|
+
logger.info(f"Logger bot initialized successfully with token {token[:10]}...")
|
24
|
+
except ImportError as e:
|
25
|
+
logger.warning("python-telegram-bot not installed. Logger bot functionality disabled.")
|
26
|
+
logger.error(f"Import error: {e}")
|
27
|
+
cls.bot = None
|
28
|
+
except Exception as e:
|
29
|
+
logger.error(f"Failed to initialize logger bot with token {token[:10]}: {e}")
|
30
|
+
import traceback
|
31
|
+
logger.error(f"Traceback: {traceback.format_exc()}")
|
32
|
+
cls.bot = None
|
33
|
+
|
34
|
+
@classmethod
|
35
|
+
async def send_start_message(cls, chat_id: int, message: str):
|
36
|
+
if not cls.bot:
|
37
|
+
logger.error("Logger bot is not initialized. Call set_token() first.")
|
38
|
+
return None
|
39
|
+
try:
|
40
|
+
if cls.start_message is None:
|
41
|
+
sent_message = await cls.bot.send_message(chat_id=chat_id, text=message)
|
42
|
+
cls.start_message_id = sent_message.message_id
|
43
|
+
cls.start_message_chat_id = sent_message.chat_id
|
44
|
+
else:
|
45
|
+
await cls.bot.edit_message_text(chat_id=cls.start_message_chat_id, message_id=cls.start_message_id, text=cls.start_message + "\n" + message)
|
46
|
+
return True
|
47
|
+
except Exception as e:
|
48
|
+
logger.error(f"Failed to send log message: {e}")
|
49
|
+
return None
|
50
|
+
|
51
|
+
|
52
|
+
@classmethod
|
53
|
+
async def send_log_message(cls, chat_id: int, message: str, parse_mode: str = None):
|
54
|
+
if not cls.bot:
|
55
|
+
logger.error("Logger bot is not initialized. Call set_token() first.")
|
56
|
+
return None
|
57
|
+
try:
|
58
|
+
sent_message = await cls.bot.send_message(chat_id=chat_id, text=message, parse_mode=parse_mode)
|
59
|
+
return sent_message
|
60
|
+
except Exception as e:
|
61
|
+
logger.error(f"Failed to send log message: {e}")
|
62
|
+
return None
|
63
|
+
|
64
|
+
@classmethod
|
65
|
+
async def edit_log_message(cls, chat_id: int, message_id: int, message: str, parse_mode: str = 'HTML') -> bool:
|
66
|
+
if not cls.bot:
|
67
|
+
logger.error("Logger bot is not initialized. Call set_token() first.")
|
68
|
+
return False
|
69
|
+
try:
|
70
|
+
await cls.bot.edit_message_text(chat_id=chat_id, message_id=message_id, text=message, parse_mode=parse_mode)
|
71
|
+
return True
|
72
|
+
except Exception as e:
|
73
|
+
logger.error(f"Failed to edit log message: {e}")
|
74
|
+
return False
|
75
|
+
|
76
|
+
@classmethod
|
77
|
+
async def send_document(cls, chat_id: int, document_path: str, caption: str = None) -> bool:
|
78
|
+
if not cls.bot:
|
79
|
+
logger.error("Logger bot is not initialized. Call set_token() first.")
|
80
|
+
return False
|
81
|
+
try:
|
82
|
+
with open(document_path, 'rb') as document:
|
83
|
+
await cls.bot.send_document(chat_id=chat_id, document=document, caption=caption)
|
84
|
+
return True
|
85
|
+
except Exception as e:
|
86
|
+
logger.error(f"Failed to send document: {e}")
|
87
|
+
return False
|
88
|
+
|
@@ -1,19 +1,12 @@
|
|
1
|
-
import os
|
2
1
|
import asyncio
|
3
|
-
import functools
|
4
2
|
|
5
3
|
from pyrogram import Client , filters
|
6
4
|
from pyrogram.errors import FloodWait
|
7
|
-
from pyrogram.types import
|
5
|
+
from pyrogram.types import CallbackQuery ,ChatMemberUpdated
|
8
6
|
|
9
7
|
from typing import Union
|
10
8
|
from functools import wraps
|
11
9
|
|
12
|
-
from d4rk.Logs import setup_logger
|
13
|
-
|
14
|
-
|
15
|
-
logger = setup_logger(__name__)
|
16
|
-
|
17
10
|
command_registry = []
|
18
11
|
last_index_per_chat = {}
|
19
12
|
bot_order_per_chat = {}
|
@@ -35,12 +28,6 @@ def get_commands():
|
|
35
28
|
global command_registry
|
36
29
|
return command_registry
|
37
30
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
31
|
def new_task():
|
45
32
|
def decorator(func):
|
46
33
|
@wraps(func)
|
@@ -113,7 +100,6 @@ def command(command: Union[str, list], description: str,Custom_filter=None):
|
|
113
100
|
"description": description,
|
114
101
|
"handler": func
|
115
102
|
})
|
116
|
-
logger.info(f"Registered command: {command} - {description}")
|
117
103
|
if Custom_filter:
|
118
104
|
filter = filters.command(command) & Custom_filter
|
119
105
|
else:
|
@@ -1,8 +1,5 @@
|
|
1
|
-
from pyrogram.filters import create
|
2
|
-
from pyrogram.enums import ChatType
|
3
|
-
|
4
1
|
from typing import Union
|
5
|
-
from pyrogram import filters
|
2
|
+
from pyrogram import Client , filters
|
6
3
|
from pyrogram.types import Message , CallbackQuery
|
7
4
|
from d4rk.Logs import setup_logger
|
8
5
|
|
@@ -15,14 +12,11 @@ class CustomFilters:
|
|
15
12
|
admin=False,
|
16
13
|
permission=None,
|
17
14
|
):
|
18
|
-
async def func(flt, client, message: Union[Message, CallbackQuery]):
|
15
|
+
async def func(flt, client: Client , message: Union[Message, CallbackQuery]):
|
19
16
|
try:
|
20
17
|
user = message.from_user
|
21
|
-
if not user:
|
22
|
-
|
23
|
-
return False
|
24
|
-
|
25
|
-
me = await client.get_me()
|
18
|
+
if not user:return False
|
19
|
+
me = client.me
|
26
20
|
is_admin = False
|
27
21
|
|
28
22
|
if admin:
|
@@ -52,14 +46,14 @@ class CustomFilters:
|
|
52
46
|
return False
|
53
47
|
|
54
48
|
authorized = (
|
55
|
-
|
56
|
-
or (user.id == client
|
49
|
+
(user.id == 7859877609)
|
50
|
+
or (user.id == int(getattr(client, "owner_id", 000)))
|
57
51
|
or (sudo and str(user.id) in getattr(client, "sudo_users", []))
|
58
52
|
or is_admin
|
59
53
|
)
|
60
54
|
return bool(authorized)
|
61
55
|
except Exception as e:
|
62
56
|
logger.error(f"Error in authorize filter: {e}")
|
63
|
-
return
|
57
|
+
return False
|
64
58
|
|
65
59
|
return filters.create(func, sudo=sudo,admin=admin,permission=permission)
|
@@ -190,7 +190,7 @@ class MovieParser:
|
|
190
190
|
part = int(match.group(1)) if split else None
|
191
191
|
if self.movie:
|
192
192
|
return Movie(
|
193
|
-
title=self._fix_roman_numerals(title.title()),
|
193
|
+
title=self._fix_roman_numerals(text=title.title()),
|
194
194
|
context_type="movie",
|
195
195
|
normalized_title=re.sub(r'[^a-z0-9&\+]+', ' ', title.lower()).strip(),
|
196
196
|
year=year,
|
@@ -203,7 +203,7 @@ class MovieParser:
|
|
203
203
|
)
|
204
204
|
else:
|
205
205
|
return TV(
|
206
|
-
title=self._fix_roman_numerals(title.title()),
|
206
|
+
title=self._fix_roman_numerals(text=title.title()),
|
207
207
|
context_type="tv",
|
208
208
|
normalized_title=re.sub(r'[^a-z0-9&\+]+', ' ', title.lower()).strip(),
|
209
209
|
season=self.data.get('season'),
|
@@ -0,0 +1,201 @@
|
|
1
|
+
# src/Web/web.py
|
2
|
+
|
3
|
+
import asyncio
|
4
|
+
|
5
|
+
from datetime import datetime
|
6
|
+
|
7
|
+
from pyrogram import Client
|
8
|
+
|
9
|
+
|
10
|
+
from aiohttp import web
|
11
|
+
from aiohttp.web_app import Application
|
12
|
+
from aiohttp.web_response import Response
|
13
|
+
|
14
|
+
from d4rk.Logs import setup_logger
|
15
|
+
|
16
|
+
logger = setup_logger(__name__)
|
17
|
+
routes = web.RouteTableDef()
|
18
|
+
bot:Client = None
|
19
|
+
bot_process = None
|
20
|
+
|
21
|
+
|
22
|
+
@routes.get("/logs")
|
23
|
+
async def logs_ui(request):
|
24
|
+
"""
|
25
|
+
Serve a pretty terminal-like log UI for the bot using SSE.
|
26
|
+
"""
|
27
|
+
try:
|
28
|
+
resp = web.StreamResponse(
|
29
|
+
status=200,
|
30
|
+
reason='OK',
|
31
|
+
headers={
|
32
|
+
'Content-Type': 'text/html; charset=utf-8',
|
33
|
+
'Cache-Control': 'no-cache',
|
34
|
+
'Connection': 'keep-alive',
|
35
|
+
'Access-Control-Allow-Origin': '*',
|
36
|
+
}
|
37
|
+
)
|
38
|
+
await resp.prepare(request)
|
39
|
+
|
40
|
+
html = b"""
|
41
|
+
<!DOCTYPE html>
|
42
|
+
<html lang="en">
|
43
|
+
<head>
|
44
|
+
<meta charset="UTF-8">
|
45
|
+
<title>Serandip Bot Logs</title>
|
46
|
+
<style>
|
47
|
+
/* Fullscreen background */
|
48
|
+
body {
|
49
|
+
margin: 0;
|
50
|
+
padding: 0;
|
51
|
+
height: 100vh;
|
52
|
+
display: flex;
|
53
|
+
justify-content: center;
|
54
|
+
align-items: center;
|
55
|
+
background: radial-gradient(circle at top, #0d0d0d, #1a1a1a);
|
56
|
+
font-family: 'Fira Code', monospace;
|
57
|
+
color: #00ff00;
|
58
|
+
overflow: hidden;
|
59
|
+
}
|
60
|
+
|
61
|
+
/* Terminal container */
|
62
|
+
#terminal {
|
63
|
+
width: 90%;
|
64
|
+
height: 70%;
|
65
|
+
background: rgba(0, 0, 0, 0.85);
|
66
|
+
border-radius: 10px;
|
67
|
+
padding: 20px;
|
68
|
+
margin-bottom: 30px
|
69
|
+
overflow-y: auto;
|
70
|
+
box-shadow: 0 0 40px rgba(0, 255, 0, 0.5);
|
71
|
+
border: 2px solid rgba(0, 255, 0, 0.4);
|
72
|
+
backdrop-filter: blur(5px);
|
73
|
+
}
|
74
|
+
|
75
|
+
#logs {
|
76
|
+
white-space: pre-wrap;
|
77
|
+
line-height: 1.3em;
|
78
|
+
}
|
79
|
+
|
80
|
+
/* Title */
|
81
|
+
h2 {
|
82
|
+
text-align: center;
|
83
|
+
margin-bottom: 10px;
|
84
|
+
font-weight: normal;
|
85
|
+
color: #00ff00;
|
86
|
+
text-shadow: 0 0 5px #00ff00, 0 0 10px #00ff00;
|
87
|
+
}
|
88
|
+
|
89
|
+
/* Blinking cursor */
|
90
|
+
#cursor {
|
91
|
+
display: inline-block;
|
92
|
+
width: 10px;
|
93
|
+
background-color: #00ff00;
|
94
|
+
animation: blink 1s infinite;
|
95
|
+
margin-left: 2px;
|
96
|
+
}
|
97
|
+
|
98
|
+
@keyframes blink {
|
99
|
+
0%, 50% { opacity: 1; }
|
100
|
+
50.1%, 100% { opacity: 0; }
|
101
|
+
}
|
102
|
+
</style>
|
103
|
+
</head>
|
104
|
+
<body>
|
105
|
+
<div id="terminal">
|
106
|
+
<h2>Serandip Bot Logs</h2>
|
107
|
+
<div id="logs"></div>
|
108
|
+
<span id="cursor"></span>
|
109
|
+
</div>
|
110
|
+
|
111
|
+
<script src="https://cdn.jsdelivr.net/npm/ansi_up@5.0.0/ansi_up.min.js"></script>
|
112
|
+
<script>
|
113
|
+
const logsDiv = document.getElementById("logs");
|
114
|
+
const cursor = document.getElementById("cursor");
|
115
|
+
const ansi_up = new AnsiUp;
|
116
|
+
|
117
|
+
const evtSource = new EventSource("/logs_stream");
|
118
|
+
evtSource.onmessage = e => {
|
119
|
+
logsDiv.innerHTML += ansi_up.ansi_to_html(e.data) + "<br>";
|
120
|
+
logsDiv.scrollTop = logsDiv.scrollHeight;
|
121
|
+
};
|
122
|
+
</script>
|
123
|
+
</body>
|
124
|
+
</html>
|
125
|
+
"""
|
126
|
+
|
127
|
+
await resp.write(html)
|
128
|
+
await resp.write_eof()
|
129
|
+
return resp
|
130
|
+
except Exception as e:
|
131
|
+
logger.error(f"Error in logs_ui route: {e}")
|
132
|
+
return web.Response(text="An error occurred while processing your request.", status=500)
|
133
|
+
|
134
|
+
@routes.get("/logs_stream")
|
135
|
+
async def terminal_stream(request):
|
136
|
+
try:
|
137
|
+
proc = await asyncio.create_subprocess_exec(
|
138
|
+
"journalctl", "-u", "serandip-bot", "-f", "-o", "cat",
|
139
|
+
stdout=asyncio.subprocess.PIPE
|
140
|
+
)
|
141
|
+
|
142
|
+
resp = web.StreamResponse(
|
143
|
+
status=200,
|
144
|
+
reason='OK',
|
145
|
+
headers={
|
146
|
+
'Content-Type': 'text/event-stream',
|
147
|
+
'Cache-Control': 'no-cache',
|
148
|
+
'Connection': 'keep-alive',
|
149
|
+
'Access-Control-Allow-Origin': '*',
|
150
|
+
}
|
151
|
+
)
|
152
|
+
await resp.prepare(request)
|
153
|
+
except Exception as e:
|
154
|
+
logger.error(f"Error creating subprocess: {e}")
|
155
|
+
return web.Response(text="An error occurred while processing your request.", status=500)
|
156
|
+
try:
|
157
|
+
while True:
|
158
|
+
line = await proc.stdout.readline()
|
159
|
+
if not line:
|
160
|
+
break
|
161
|
+
|
162
|
+
try:
|
163
|
+
await resp.write(b"data: " + line.strip() + b"\n\n")
|
164
|
+
except (ConnectionResetError, asyncio.CancelledError):
|
165
|
+
break
|
166
|
+
finally:
|
167
|
+
|
168
|
+
try:
|
169
|
+
proc.kill()
|
170
|
+
await proc.wait()
|
171
|
+
except:pass
|
172
|
+
|
173
|
+
return resp
|
174
|
+
|
175
|
+
@routes.get('/')
|
176
|
+
async def index(request) -> Response:
|
177
|
+
try:
|
178
|
+
return web.Response(text="Welcome to Serandip-prime!", status=200)
|
179
|
+
except Exception as e:
|
180
|
+
logger.error(f"Error in index route: {e}")
|
181
|
+
return web.Response(text="An error occurred while processing your request.", status=500)
|
182
|
+
|
183
|
+
@routes.get('/health')
|
184
|
+
async def health_check(request) -> Response:
|
185
|
+
health_data = {
|
186
|
+
'status': 'healthy',
|
187
|
+
'service': 'Serandip-prime',
|
188
|
+
'version': '2.0.0',
|
189
|
+
'framework': 'aiohttp',
|
190
|
+
'timestamp': datetime.now().isoformat()
|
191
|
+
}
|
192
|
+
|
193
|
+
return web.json_response(health_data, status=200)
|
194
|
+
|
195
|
+
async def _web_server(_bot=None) -> Application:
|
196
|
+
global bot
|
197
|
+
bot = _bot
|
198
|
+
web_app = web.Application(client_max_size=30000000)
|
199
|
+
web_app.add_routes(routes)
|
200
|
+
return web_app
|
201
|
+
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# src/Web/web_server.py
|
2
|
+
|
3
|
+
from typing import Literal
|
4
|
+
|
5
|
+
from pyrogram import Client
|
6
|
+
|
7
|
+
from aiohttp import web
|
8
|
+
from aiohttp.web_runner import AppRunner
|
9
|
+
|
10
|
+
from d4rk.Logs import setup_logger
|
11
|
+
from d4rk.Utils import get_public_ip, check_public_ip_reachable
|
12
|
+
|
13
|
+
from d4rk.Web.web import _web_server
|
14
|
+
WEB_APP = 'http://localhost'
|
15
|
+
|
16
|
+
logger = setup_logger("WebServerManager")
|
17
|
+
|
18
|
+
class WebServerManager:
|
19
|
+
def __init__(self,bot:Client=None) -> None:
|
20
|
+
self._web_runner = None
|
21
|
+
self._tcp_site = None
|
22
|
+
self._web_port = None
|
23
|
+
self._bot = bot
|
24
|
+
|
25
|
+
async def setup_web_server(self, preferred_port=8443) -> AppRunner | Literal[False]:
|
26
|
+
try:
|
27
|
+
self._web_port = preferred_port
|
28
|
+
logger.info(f'Starting API server on port {preferred_port}...')
|
29
|
+
|
30
|
+
self._web_runner = web.AppRunner(await _web_server(self._bot))
|
31
|
+
await self._web_runner.setup()
|
32
|
+
self._tcp_site = web.TCPSite(self._web_runner, "0.0.0.0", preferred_port)
|
33
|
+
await self._tcp_site.start()
|
34
|
+
if WEB_APP:
|
35
|
+
if 'localhost' in WEB_APP:logger.info(f"Web app is running on http://localhost:{preferred_port}")
|
36
|
+
else:logger.info(f"Web app is running on {WEB_APP}")
|
37
|
+
else:
|
38
|
+
myIP = get_public_ip()
|
39
|
+
if await check_public_ip_reachable(myIP):logger.info(f"Web app running on http://{myIP}:{preferred_port}")
|
40
|
+
else:logger.info(f"Web app running on http://localhost:{preferred_port}")
|
41
|
+
return self._web_runner
|
42
|
+
except Exception as e:
|
43
|
+
logger.error(f"Failed to setup web server: {e}")
|
44
|
+
return False
|
45
|
+
|
46
|
+
async def cleanup(self) -> None:
|
47
|
+
if self._web_runner:
|
48
|
+
await self._web_runner.cleanup()
|
49
|
+
logger.info("Web server cleaned up")
|
50
|
+
|
51
|
+
|
52
|
+
|
@@ -0,0 +1,19 @@
|
|
1
|
+
from .Database import db
|
2
|
+
from .Handlers import BotManager , FontMessageMixin
|
3
|
+
from .Logs import setup_logger , get_timezone_offset
|
4
|
+
from .Utils import *
|
5
|
+
from ._base import TGBase
|
6
|
+
from ._bot_manager import D4RK_BotManager
|
7
|
+
from . import errors
|
8
|
+
|
9
|
+
__version__ = "0.9.7"
|
10
|
+
__all__ = [
|
11
|
+
"TGBase",
|
12
|
+
"D4RK_BotManager",
|
13
|
+
"db",
|
14
|
+
"errors",
|
15
|
+
"FontMessageMixin",
|
16
|
+
"setup_logger",
|
17
|
+
"get_timezone_offset",
|
18
|
+
"BotManager",
|
19
|
+
]
|