Skip to content

Commit 6f43dc5

Browse files
authored
Merge pull request #17 from YeetCode-devs/staging/hakimi
iSort, coloured logging, add_license.py improvement, module system, pyrogram
2 parents 99cce57 + 20f62d6 commit 6f43dc5

File tree

8 files changed

+256
-39
lines changed

8 files changed

+256
-39
lines changed

modules/start.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# SPDX-License-Identifier: GPL-3.0-only
2+
#
3+
# This program is free software: you can redistribute it and/or modify
4+
# it under the terms of the GNU General Public License as published by
5+
# the Free Software Foundation, version 3 of the License.
6+
#
7+
# This program is distributed in the hope that it will be useful,
8+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
9+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10+
# GNU General Public License for more details.
11+
#
12+
# You should have received a copy of the GNU General Public License
13+
# along with this program. If not, see <https://www.gnu.org/licenses/>.
14+
#
15+
# Copyright (c) 2024, YeetCode Developers <[email protected]>
16+
17+
from pyrogram import filters
18+
from pyrogram.client import Client
19+
from pyrogram.handlers import MessageHandler
20+
from pyrogram.types import Message
21+
22+
from src.Module import ModuleBase
23+
24+
25+
class Module(ModuleBase):
26+
def on_load(self, app: Client):
27+
app.add_handler(MessageHandler(start, filters.command("start")))
28+
29+
def on_shutdown(self, app: Client):
30+
pass
31+
32+
33+
async def start(app: Client, message: Message):
34+
await message.reply("Hello!")

poetry.lock

Lines changed: 81 additions & 29 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,9 @@ package-mode = false
1010
[tool.poetry.dependencies]
1111
python = "^3.10"
1212
g4f = {extras = ["all"], version = "^0.2.5.4"}
13-
telethon = "^1.34.0"
1413
python-dotenv = "^1.0.1"
14+
pyrogram = {url = "https://github.com/KurimuzonAkuma/pyrogram/archive/refs/heads/dev.zip"}
15+
tgcrypto = "^1.2.5"
1516

1617
[tool.poetry.group.dev.dependencies]
1718
black = "^24.3.0"

scripts/add_license.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141

4242

4343
def add_license_header(file_path) -> StatusIsOk:
44-
with open(file_path, "r+") as f:
44+
with open(file_path, "r+", encoding="utf-8") as f:
4545
content = f.read()
4646
if license_header in content:
4747
return False

src/Bot.py

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,13 @@
1717
from os import getenv
1818

1919
from dotenv import load_dotenv
20-
from telethon import TelegramClient, events
20+
from pyrogram.client import Client
21+
22+
from .Module import load_modules
2123

2224

2325
def main() -> None:
24-
load_dotenv()
26+
load_dotenv(override=True)
2527

2628
api_id = getenv("API_ID", 0)
2729
api_hash = getenv("API_HASH", "")
@@ -30,10 +32,7 @@ def main() -> None:
3032
if not all([api_id, api_hash, bot_token]):
3133
raise ValueError("Could not get all required credentials from env!")
3234

33-
app = TelegramClient("app", int(api_id), api_hash).start(bot_token=bot_token)
34-
35-
@app.on(events.NewMessage(incoming=True, pattern="/start"))
36-
async def start(event):
37-
await event.reply("Hello!")
35+
app = Client("app", int(api_id), api_hash, bot_token=bot_token)
3836

39-
app.run_until_disconnected()
37+
loaded_modules = load_modules(app)
38+
app.run()

src/Logging.py

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# SPDX-License-Identifier: GPL-3.0-only
2+
#
3+
# This program is free software: you can redistribute it and/or modify
4+
# it under the terms of the GNU General Public License as published by
5+
# the Free Software Foundation, version 3 of the License.
6+
#
7+
# This program is distributed in the hope that it will be useful,
8+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
9+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10+
# GNU General Public License for more details.
11+
#
12+
# You should have received a copy of the GNU General Public License
13+
# along with this program. If not, see <https://www.gnu.org/licenses/>.
14+
#
15+
# Copyright (c) 2024, YeetCode Developers <[email protected]>
16+
17+
import logging
18+
import os
19+
20+
GLOBAL_DEBUG: bool = False
21+
if os.getenv("TGBOT_DEBUG") is not None:
22+
GLOBAL_DEBUG = True
23+
24+
log_additional_args: dict = {"filename": "bot.log", "level": logging.INFO}
25+
if GLOBAL_DEBUG:
26+
log_additional_args.clear()
27+
log_additional_args.update({"level": logging.DEBUG})
28+
29+
30+
#
31+
# Adapted from https://stackoverflow.com/a/56944256
32+
#
33+
class ColouredFormatter(logging.Formatter):
34+
35+
grey = "\x1b[38;20m"
36+
yellow = "\x1b[33;20m"
37+
red = "\x1b[31;20m"
38+
green = "\x1b[0;32m"
39+
blue = "\x1b[0;34m"
40+
bold_red = "\x1b[31;1m"
41+
reset = "\x1b[0m"
42+
format_str = "%(asctime)s [%(levelname)s] %(name)s: %(message)s"
43+
44+
FORMATS = {
45+
logging.DEBUG: blue + format_str + reset,
46+
logging.INFO: green + format_str + reset,
47+
logging.WARNING: yellow + format_str + reset,
48+
logging.ERROR: red + format_str + reset,
49+
logging.CRITICAL: bold_red + format_str + reset,
50+
}
51+
52+
def format(self, record: logging.LogRecord):
53+
log_fmt = self.FORMATS.get(record.levelno)
54+
formatter = logging.Formatter(log_fmt)
55+
return formatter.format(record)
56+
57+
58+
logging.basicConfig(format="%(asctime)s [%(levelname)s] %(name)s: %(message)s", **log_additional_args)
59+
60+
61+
for handler in logging.root.handlers:
62+
if issubclass(logging.StreamHandler, type(handler)):
63+
logging.root.removeHandler(handler)
64+
65+
_sh = logging.StreamHandler()
66+
_sh.setFormatter(ColouredFormatter())
67+
logging.root.addHandler(_sh)
68+
logging.getLogger(__name__).info("Coloured log output initialized")

src/Module.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# SPDX-License-Identifier: GPL-3.0-only
2+
#
3+
# This program is free software: you can redistribute it and/or modify
4+
# it under the terms of the GNU General Public License as published by
5+
# the Free Software Foundation, version 3 of the License.
6+
#
7+
# This program is distributed in the hope that it will be useful,
8+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
9+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10+
# GNU General Public License for more details.
11+
#
12+
# You should have received a copy of the GNU General Public License
13+
# along with this program. If not, see <https://www.gnu.org/licenses/>.
14+
#
15+
# Copyright (c) 2024, YeetCode Developers <[email protected]>
16+
17+
import atexit
18+
import logging
19+
from abc import ABC, abstractmethod
20+
from importlib import import_module
21+
from pathlib import Path
22+
23+
from pyrogram.client import Client
24+
25+
log: logging.Logger = logging.getLogger(__name__)
26+
27+
28+
class ModuleBase(ABC):
29+
@abstractmethod
30+
def on_load(self, app: Client):
31+
pass
32+
33+
@abstractmethod
34+
def on_shutdown(self, app: Client):
35+
pass
36+
37+
38+
def load_modules(app: Client) -> list[object]:
39+
loaded_modules: list[object] = []
40+
41+
log.info("Searching for modules")
42+
modules: list[Path] = list(Path("modules").rglob("*.py"))
43+
log.info(f"Found {len(modules)} modules")
44+
45+
for module in modules:
46+
log.info(f"Loading module '{module}'")
47+
48+
mdl = import_module(f"modules.{module.name.removesuffix('.py')}")
49+
50+
if not hasattr(mdl, "Module"):
51+
log.error(f"Module '{module}' does not have a Module class, cannot load")
52+
continue
53+
54+
if not issubclass(mdl.Module, ModuleBase):
55+
log.warning(f"Module '{module}' does not inherit from ModuleBase class")
56+
57+
mdl.Module().on_load(app)
58+
atexit.register(mdl.Module().on_shutdown, app)
59+
60+
loaded_modules.append(mdl)
61+
62+
return loaded_modules

src/__main__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#
1515
# Copyright (c) 2024, YeetCode Developers <[email protected]>
1616

17+
from . import Logging
1718
from .Bot import main
1819

1920
if __name__ == "__main__":

0 commit comments

Comments
 (0)