-
Notifications
You must be signed in to change notification settings - Fork 7
Made Logger working #1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -115,4 +115,6 @@ dmypy.json | |
|
|
||
| # Logs | ||
| *.session | ||
| *.session-journal | ||
| *.session-journal | ||
| logs | ||
| database | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,49 @@ | ||||||||||||||||||||||||
| import logging | ||||||||||||||||||||||||
| import os | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| class LoggingFormatter(logging.Formatter): | ||||||||||||||||||||||||
| # Colors | ||||||||||||||||||||||||
| black = "\x1b[30m" | ||||||||||||||||||||||||
| red = "\x1b[31m" | ||||||||||||||||||||||||
| green = "\x1b[32m" | ||||||||||||||||||||||||
| yellow = "\x1b[33m" | ||||||||||||||||||||||||
| blue = "\x1b[34m" | ||||||||||||||||||||||||
| gray = "\x1b[38m" | ||||||||||||||||||||||||
| # Styles | ||||||||||||||||||||||||
| reset = "\x1b[0m" | ||||||||||||||||||||||||
| bold = "\x1b[1m" | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| COLORS = { | ||||||||||||||||||||||||
| logging.DEBUG: gray + bold, | ||||||||||||||||||||||||
| logging.INFO: blue + bold, | ||||||||||||||||||||||||
| logging.WARNING: yellow + bold, | ||||||||||||||||||||||||
| logging.ERROR: red, | ||||||||||||||||||||||||
| logging.CRITICAL: red + bold, | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| def format(self, record): | ||||||||||||||||||||||||
| log_color = self.COLORS[record.levelno] | ||||||||||||||||||||||||
| format = "(black){asctime}(reset) (levelcolor){levelname}(reset) (green){name}(reset) {message}" | ||||||||||||||||||||||||
| format = format.replace("(black)", self.black + self.bold) | ||||||||||||||||||||||||
| format = format.replace("(reset)", self.reset) | ||||||||||||||||||||||||
| format = format.replace("(levelcolor)", log_color) | ||||||||||||||||||||||||
| format = format.replace("(green)", self.green + self.bold) | ||||||||||||||||||||||||
|
Comment on lines
+26
to
+30
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
We can make use of |
||||||||||||||||||||||||
| formatter = logging.Formatter(format, "%Y-%m-%d %H:%M:%S", style="{") | ||||||||||||||||||||||||
| return formatter.format(record) | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| def setlogger(name): | ||||||||||||||||||||||||
| '''Setup the logger''' | ||||||||||||||||||||||||
| logger = logging.getLogger(name) | ||||||||||||||||||||||||
| logger.setLevel(logging.INFO) | ||||||||||||||||||||||||
| console_handler = logging.StreamHandler() | ||||||||||||||||||||||||
| console_handler.setFormatter(LoggingFormatter()) | ||||||||||||||||||||||||
| os.makedirs(f'./logs', exist_ok=True) | ||||||||||||||||||||||||
| file_handler = logging.FileHandler(f'./logs/{name}.log', 'a', 'utf-8') | ||||||||||||||||||||||||
| file_handler_formatter = logging.Formatter( | ||||||||||||||||||||||||
| "[{asctime}] [{levelname}] {name}: {message}", "%Y-%m-%d %H:%M:%S", style="{" | ||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||
| file_handler.setFormatter(file_handler_formatter) | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| logger.addHandler(console_handler) | ||||||||||||||||||||||||
| logger.addHandler(file_handler) | ||||||||||||||||||||||||
| return logger | ||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,30 +1,55 @@ | ||
| import datetime | ||
| from datetime import datetime | ||
| import json | ||
| import os | ||
| from typing import Optional | ||
|
|
||
| from logging import Logger | ||
|
|
||
| class Storage: | ||
| def __init__(self) -> None: | ||
| def __init__(self, logger : Logger) -> None: | ||
| """Stores the information for each user""" | ||
| # Load the shelve db if possible | ||
| self.storage = {} | ||
| if os.path.isfile(f"./database/storage.json"): | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. BASE_DIR = os.path.dirname(os.path.abspath(__file__))
...
path = os.path.join(BASE_DIR,"database","storage.json")Filepath can be improved using the snippet above instead of |
||
| logger.info("Loading stored events...") | ||
| with open("./database/storage.json", "r") as storageFile: | ||
| try: | ||
| self.storage = json.load(storageFile) | ||
| except Exception as e: | ||
| logger.error("Error loading stored events", {str(e)}) | ||
| self.storage = {} | ||
| else: | ||
| logger.info("No saved events found.") | ||
| self.storage = {} | ||
| os.makedirs("./database", exist_ok=True) | ||
| with open("./database/storage.json", "w") as storageFile: | ||
| json.dump(self.storage, storageFile, indent=4) | ||
|
|
||
|
Comment on lines
+11
to
+25
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is not an ideal solution for writing into a file. In this case it might be better to use something like |
||
|
|
||
| def add_event(self, chat_id: int, event_name: str, event_time: str) -> datetime.datetime: | ||
| def add_event(self, chat_id: int, event_name: str, event_time: str) -> datetime: | ||
| """ | ||
| Throws ValueError when the format is incorrect | ||
| """ | ||
| chat_id = str(chat_id) | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe we can create another class to deal with the file logging (opening/closing/writing to files ) and only write to the file when the destructor of the class is called. |
||
| if chat_id not in self.storage: | ||
| self.storage[chat_id] = {} | ||
| deadline = datetime.datetime.strptime( | ||
| deadline = datetime.strptime( | ||
| event_time, '%d/%m/%Y %H:%M') | ||
| self.storage[chat_id][event_name] = deadline | ||
| self.storage[chat_id][event_name] = event_time | ||
| with open("./database/storage.json", "w") as storageFile: | ||
| json.dump(self.storage, storageFile, indent=4) | ||
| return deadline | ||
|
|
||
| def get_events(self, chat_id: int, event_name) -> Optional[datetime.datetime]: | ||
| return self.storage.get(chat_id, {}).get(event_name, None) | ||
| def get_events(self, chat_id: int, event_name) -> Optional[datetime]: | ||
| chat_id = str(chat_id) | ||
| event_time = self.storage.get(chat_id, {}).get(event_name, None) | ||
| if event_time is None: return None | ||
| return datetime.strptime(event_time, '%d/%m/%Y %H:%M') | ||
|
|
||
| def delete_event(self, chat_id: int, event_name: str) -> bool: | ||
| chat_id = str(chat_id) | ||
| try: | ||
| del self.storage[chat_id][event_name] | ||
| with open("./database/storage.json", "w") as storageFile: | ||
| json.dump(self.storage, storageFile, indent=4) | ||
| return True | ||
| except: | ||
| return False | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,16 +1,18 @@ | ||
| import os | ||
| import datetime | ||
| import logging | ||
|
|
||
| from time import sleep | ||
| from Storage import Storage | ||
| from Logger import setlogger | ||
| from dotenv import load_dotenv | ||
| from pyrogram import filters | ||
| from pyrogram.types import Message | ||
| from pyrogram.client import Client | ||
| from constants import CALLBACK_DICT, ERROR_CMD_MSG, ZERO_TIME_DELTA, TIMER_FORMAT, EVENT_ENDED_FORMAT, POLLING_INTERVAL, TIME_FORMAT, CANCEL_MSG, ERROR_CANCEL_MSG, EVENT_CANCELLED_FORMAT, CMD_START, CMD_DEFAULT, CMD_CANCEL, CMD_TIMER, BOT_NAME, LOGGER_FORMAT | ||
| from constants import CALLBACK_DICT, ERROR_CMD_MSG, ZERO_TIME_DELTA, TIMER_FORMAT, EVENT_ENDED_FORMAT, POLLING_INTERVAL, CANCEL_MSG, ERROR_CANCEL_MSG, EVENT_CANCELLED_FORMAT, CMD_START, CMD_DEFAULT, CMD_CANCEL, CMD_TIMER, BOT_NAME, FOOTER | ||
|
|
||
| load_dotenv() | ||
| storage = Storage() | ||
| logger = setlogger(BOT_NAME) | ||
| storage = Storage(logger) | ||
|
|
||
| app = Client( | ||
| BOT_NAME, | ||
|
|
@@ -19,20 +21,16 @@ | |
| bot_token=os.environ.get("BOT_TOKEN", ""), | ||
| ) | ||
|
|
||
| logging.basicConfig(format=LOGGER_FORMAT) | ||
| logger = logging.getLogger(__name__) | ||
|
|
||
|
|
||
| @app.on_message(filters.command(CMD_START)) | ||
| async def start(_, message): | ||
| async def start(_, message: Message): | ||
| await message.reply( | ||
| text=CALLBACK_DICT[CMD_START].get_msg(), | ||
| reply_markup=CALLBACK_DICT[CMD_START].get_markup() | ||
| ) | ||
|
|
||
|
|
||
| @app.on_message(filters.command(CMD_CANCEL)) | ||
| async def cancel(_, message): | ||
| async def cancel(_, message: Message): | ||
| try: | ||
| _, event_name = message.text.split(' ', 1) | ||
| if not storage.delete_event(message.chat.id, event_name): | ||
|
|
@@ -47,7 +45,7 @@ async def cancel(_, message): | |
|
|
||
|
|
||
| @app.on_message(filters.command(CMD_TIMER)) | ||
| async def start_timer(_, message): | ||
| async def start_timer(_, message: Message): | ||
| """The main method for the timer message""" | ||
| try: | ||
| # [command, date, time, event_name] | ||
|
|
@@ -68,15 +66,18 @@ async def start_timer(_, message): | |
|
|
||
| await refresh_msg(msg, deadline, event_name) | ||
|
|
||
| except (ValueError, TypeError): | ||
| except (ValueError, TypeError) as e: | ||
| logger.error(str(e)) | ||
| await message.reply(text=ERROR_CMD_MSG) | ||
|
|
||
|
|
||
| async def refresh_msg(msg, deadline: datetime.datetime, event_name: str): | ||
| async def refresh_msg(msg: Message, deadline: datetime.datetime, event_name: str): | ||
| """Updates the event message until it is pass the deadline""" | ||
| sleep_time = max(POLLING_INTERVAL, 5) | ||
| while True: | ||
| sleep(POLLING_INTERVAL) | ||
| sleep(sleep_time) | ||
| time_left = deadline - datetime.datetime.now() | ||
| if not time_left.days and time_left.seconds < 10: sleep_time = 1 | ||
| if storage.get_events(msg.chat.id, event_name) is None: | ||
| format = EVENT_CANCELLED_FORMAT | ||
| logger.info(f"Event {event_name} was cancelled") | ||
|
|
@@ -87,21 +88,24 @@ async def refresh_msg(msg, deadline: datetime.datetime, event_name: str): | |
| break | ||
| event_string = get_event_string(time_left, event_name) | ||
| await msg.edit(event_string) | ||
| logger.info(f"Event {event_name} updated for {time_left}") | ||
| # logger.info(f"Event {event_name} updated for {time_left}") | ||
| await msg.edit(format.format(event_name=event_name)) | ||
|
|
||
|
|
||
| def get_event_string(time: datetime.timedelta, event_name: str): | ||
| """Get the string format for event message""" | ||
| return TIMER_FORMAT.format(time=get_time_string(time), event_name=event_name) | ||
| return TIMER_FORMAT.format(time=get_time_string(time), event_name=event_name, footer = FOOTER) | ||
|
|
||
|
|
||
| def get_time_string(time: datetime.timedelta): | ||
| hours = time.seconds // 3600 | ||
| minutes = (time.seconds % 3600) // 60 | ||
| seconds = time.seconds % 60 | ||
| return TIME_FORMAT.format(days=time.days, hours=hours, minutes=minutes, seconds=seconds) | ||
|
|
||
| time_string = "" | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The formatting logic here can be handled by changing the |
||
| if time.days: time_string += f"{time.days}**d** " | ||
| minutes, seconds = divmod(time.seconds, 60) | ||
| hours, minutes = divmod(minutes, 60) | ||
| if hours: time_string += f"{hours}**h** {minutes}**m** {seconds}**s**" | ||
| elif minutes: time_string += f"{minutes}**m** {seconds}**s**" | ||
| else: time_string += f"{seconds}**s**" | ||
| return time_string | ||
|
|
||
| @app.on_callback_query() | ||
| async def callback(_, query) -> None: | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -19,17 +19,20 @@ | |
| CMD_CANCEL = 'cancel' | ||
|
|
||
| # Logger Format | ||
| LOGGER_FORMAT = '%(asctime)s %(clientip)-15s %(user)-8s %(message)s' | ||
| # LOGGER_FORMAT = '%(asctime)s %(clientip)-15s %(user)-8s %(message)s' | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe we can remove commented code. If we have a need to retrieve it, we can look at the previous versions of the code. |
||
|
|
||
| # Interval to edit the message (Default 30 seconds) | ||
| POLLING_INTERVAL = 10 | ||
| POLLING_INTERVAL = 1 | ||
| FOOTER = "My developer is the greatest" | ||
|
|
||
| # Format for Display | ||
| TIMER_FORMAT = "**{event_name}**\n⏳{time}\nThis updates every " + \ | ||
| str(POLLING_INTERVAL) + " seconds." | ||
| EVENT_ENDED_FORMAT = "{event_name} has already ended :(" | ||
| # TIMER_FORMAT = "**{event_name}**\n⏳{time}\nThis updates every " + \ | ||
| # str(POLLING_INTERVAL) + " seconds." | ||
| TIMER_FORMAT = "**{event_name}**\n\n⏳{time}\n\n__{footer}__" | ||
|
|
||
| EVENT_ENDED_FORMAT = "{event_name} has already ended!" | ||
| EVENT_CANCELLED_FORMAT = "{event_name} is cancelled :(" | ||
| TIME_FORMAT = "{days} Days, {hours} Hours, {minutes} Minutes {seconds} Seconds left" | ||
| # TIME_FORMAT = "{days} Days, {hours} Hours, {minutes} Minutes {seconds} Seconds left" | ||
|
|
||
| START_MSG = f'Welcome to the {BOT_NAME} bot, feel free to look around' | ||
| CANCEL_MSG = "{event_name} is cancelled." | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we can add the
/at the back to denote directories