mirror of
https://github.com/Steffo99/greed.git
synced 2024-11-25 23:24:19 +00:00
Enable logging in core
This commit is contained in:
parent
3caa5ef179
commit
641c2736bd
3 changed files with 51 additions and 7 deletions
|
@ -17,6 +17,7 @@ A customizable Telegram shop bot, developed as a project for the final exam.
|
||||||
|
|
||||||
1. Download the project files through `git clone https://github.com/Steffo99/greed.git` or [this link](https://github.com/Steffo99/greed/archive/master.zip).
|
1. Download the project files through `git clone https://github.com/Steffo99/greed.git` or [this link](https://github.com/Steffo99/greed/archive/master.zip).
|
||||||
2. Install the project requirements with `pip install -r requirements.txt`
|
2. Install the project requirements with `pip install -r requirements.txt`
|
||||||
|
3. _Optional: run `pip install coloredlogs` to have colored logging output._
|
||||||
3. Run `python -OO core.py` to generate the configuration file.
|
3. Run `python -OO core.py` to generate the configuration file.
|
||||||
4. Open the config folder and edit the `config.ini` file following the contained instructions.
|
4. Open the config folder and edit the `config.ini` file following the contained instructions.
|
||||||
Ensure the `is_template` field is set to `no`.
|
Ensure the `is_template` field is set to `no`.
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
# Config file parameters
|
# Config file parameters
|
||||||
[Config]
|
[Config]
|
||||||
; Config file version. DO NOT EDIT THIS!
|
; Config file version. DO NOT EDIT THIS!
|
||||||
version = 14
|
version = 15
|
||||||
; Set this to no when you are done editing the file
|
; Set this to no when you are done editing the file
|
||||||
is_template = yes
|
is_template = yes
|
||||||
; Language code for string file
|
; Language code for string file
|
||||||
|
@ -75,3 +75,12 @@ full_order_info = no
|
||||||
; Optional sentry token: get the token at https://sentry.io/ or ask @Steffo for one
|
; Optional sentry token: get the token at https://sentry.io/ or ask @Steffo for one
|
||||||
; Needed to automatically report bugs found by the users in the code.
|
; Needed to automatically report bugs found by the users in the code.
|
||||||
sentry_token = https://00000000000000000000000000000000:00000000000000000000000000000000@sentry.io/0000000
|
sentry_token = https://00000000000000000000000000000000:00000000000000000000000000000000@sentry.io/0000000
|
||||||
|
|
||||||
|
# Logging settings
|
||||||
|
[Logging]
|
||||||
|
; The output format for the messages printed to the console
|
||||||
|
; See https://docs.python.org/3/library/logging.html#logrecord-attributes for information about the {}-attributes
|
||||||
|
format = {asctime} | {threadName} | {name} | {message}
|
||||||
|
; Logging level: ignore all log entries with a level lower than the specified one
|
||||||
|
; Valid options are FATAL, ERROR, WARNING, INFO, and DEBUG
|
||||||
|
level = INFO
|
||||||
|
|
44
core.py
44
core.py
|
@ -5,6 +5,12 @@ import configloader
|
||||||
import utils
|
import utils
|
||||||
import threading
|
import threading
|
||||||
import importlib
|
import importlib
|
||||||
|
import logging
|
||||||
|
|
||||||
|
try:
|
||||||
|
import coloredlogs
|
||||||
|
except ImportError:
|
||||||
|
coloredlogs = None
|
||||||
|
|
||||||
language = configloader.config["Config"]["language"]
|
language = configloader.config["Config"]["language"]
|
||||||
strings = importlib.import_module("strings." + language)
|
strings = importlib.import_module("strings." + language)
|
||||||
|
@ -12,20 +18,35 @@ strings = importlib.import_module("strings." + language)
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
"""The core code of the program. Should be run only in the main process!"""
|
"""The core code of the program. Should be run only in the main process!"""
|
||||||
|
|
||||||
# Rename the main thread for presentation purposes
|
# Rename the main thread for presentation purposes
|
||||||
threading.current_thread().name = "Core"
|
threading.current_thread().name = "Core"
|
||||||
|
|
||||||
|
# Setup logging
|
||||||
|
log = logging.getLogger("core")
|
||||||
|
logging.root.setLevel(configloader.config["Logging"]["level"])
|
||||||
|
stream_handler = logging.StreamHandler()
|
||||||
|
if coloredlogs is not None:
|
||||||
|
stream_handler.formatter = coloredlogs.ColoredFormatter(configloader.config["Logging"]["format"], style="{")
|
||||||
|
else:
|
||||||
|
stream_handler.formatter = logging.Formatter(configloader.config["Logging"]["format"], style="{")
|
||||||
|
logging.root.handlers.clear()
|
||||||
|
logging.root.addHandler(stream_handler)
|
||||||
|
log.debug("Logging setup successfully!")
|
||||||
|
|
||||||
|
# Ignore most python-telegram-bot logs, as they are useless most of the time
|
||||||
|
logging.getLogger("telegram").setLevel("ERROR")
|
||||||
|
|
||||||
# Create a bot instance
|
# Create a bot instance
|
||||||
bot = utils.DuckBot(configloader.config["Telegram"]["token"])
|
bot = utils.DuckBot(configloader.config["Telegram"]["token"])
|
||||||
|
|
||||||
# Test the specified token
|
# Test the specified token
|
||||||
|
log.debug("Testing bot token...")
|
||||||
try:
|
try:
|
||||||
bot.get_me()
|
bot.get_me()
|
||||||
except telegram.error.Unauthorized:
|
except telegram.error.Unauthorized:
|
||||||
print("The token you have entered in the config file is invalid.\n"
|
logging.fatal("The token you have entered in the config file is invalid. Fix it, then restart greed.")
|
||||||
"Fix it, then restart this script.")
|
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
log.debug("Bot token is valid!")
|
||||||
|
|
||||||
# Create a dictionary linking the chat ids to the ChatWorker objects
|
# Create a dictionary linking the chat ids to the ChatWorker objects
|
||||||
# {"1234": <ChatWorker>}
|
# {"1234": <ChatWorker>}
|
||||||
|
@ -35,11 +56,12 @@ def main():
|
||||||
next_update = None
|
next_update = None
|
||||||
|
|
||||||
# Notify on the console that the bot is starting
|
# Notify on the console that the bot is starting
|
||||||
print("greed-bot is now starting!")
|
log.info("greed is starting!")
|
||||||
|
|
||||||
# Main loop of the program
|
# Main loop of the program
|
||||||
while True:
|
while True:
|
||||||
# Get a new batch of 100 updates and mark the last 100 parsed as read
|
# Get a new batch of 100 updates and mark the last 100 parsed as read
|
||||||
|
log.debug("Getting updates from Telegram")
|
||||||
updates = bot.get_updates(offset=next_update,
|
updates = bot.get_updates(offset=next_update,
|
||||||
timeout=int(configloader.config["Telegram"]["long_polling_timeout"]))
|
timeout=int(configloader.config["Telegram"]["long_polling_timeout"]))
|
||||||
# Parse all the updates
|
# Parse all the updates
|
||||||
|
@ -48,20 +70,24 @@ def main():
|
||||||
if update.message is not None:
|
if update.message is not None:
|
||||||
# Ensure the message has been sent in a private chat
|
# Ensure the message has been sent in a private chat
|
||||||
if update.message.chat.type != "private":
|
if update.message.chat.type != "private":
|
||||||
|
log.debug(f"Received a message from a non-private chat: {update.message.chat.id}")
|
||||||
# Notify the chat
|
# Notify the chat
|
||||||
bot.send_message(update.message.chat.id, strings.error_nonprivate_chat)
|
bot.send_message(update.message.chat.id, strings.error_nonprivate_chat)
|
||||||
# Skip the update
|
# Skip the update
|
||||||
continue
|
continue
|
||||||
# If the message is a start command...
|
# If the message is a start command...
|
||||||
if isinstance(update.message.text, str) and update.message.text == "/start":
|
if isinstance(update.message.text, str) and update.message.text == "/start":
|
||||||
|
log.info(f"Received /start from: {update.message.chat.id}")
|
||||||
# Check if a worker already exists for that chat
|
# Check if a worker already exists for that chat
|
||||||
old_worker = chat_workers.get(update.message.chat.id)
|
old_worker = chat_workers.get(update.message.chat.id)
|
||||||
# If it exists, gracefully stop the worker
|
# If it exists, gracefully stop the worker
|
||||||
if old_worker:
|
if old_worker:
|
||||||
|
log.debug(f"Received request to stop {old_worker.name}")
|
||||||
old_worker.stop("request")
|
old_worker.stop("request")
|
||||||
# Initialize a new worker for the chat
|
# Initialize a new worker for the chat
|
||||||
new_worker = worker.ChatWorker(bot, update.message.chat)
|
new_worker = worker.ChatWorker(bot, update.message.chat)
|
||||||
# Start the worker
|
# Start the worker
|
||||||
|
log.debug(f"Starting {new_worker.name}")
|
||||||
new_worker.start()
|
new_worker.start()
|
||||||
# Store the worker in the dictionary
|
# Store the worker in the dictionary
|
||||||
chat_workers[update.message.chat.id] = new_worker
|
chat_workers[update.message.chat.id] = new_worker
|
||||||
|
@ -71,6 +97,7 @@ def main():
|
||||||
receiving_worker = chat_workers.get(update.message.chat.id)
|
receiving_worker = chat_workers.get(update.message.chat.id)
|
||||||
# Ensure a worker exists for the chat and is alive
|
# Ensure a worker exists for the chat and is alive
|
||||||
if receiving_worker is None or not receiving_worker.is_alive():
|
if receiving_worker is None or not receiving_worker.is_alive():
|
||||||
|
log.debug(f"Received a message in a chat without worker: {update.message.chat.id}")
|
||||||
# Suggest that the user restarts the chat with /start
|
# Suggest that the user restarts the chat with /start
|
||||||
bot.send_message(update.message.chat.id, strings.error_no_worker_for_chat,
|
bot.send_message(update.message.chat.id, strings.error_no_worker_for_chat,
|
||||||
reply_markup=telegram.ReplyKeyboardRemove())
|
reply_markup=telegram.ReplyKeyboardRemove())
|
||||||
|
@ -78,9 +105,11 @@ def main():
|
||||||
continue
|
continue
|
||||||
# If the message contains the "Cancel" string defined in the strings file...
|
# If the message contains the "Cancel" string defined in the strings file...
|
||||||
if update.message.text == strings.menu_cancel:
|
if update.message.text == strings.menu_cancel:
|
||||||
|
log.debug(f"Forwarding CancelSignal to {worker}")
|
||||||
# Send a CancelSignal to the worker instead of the update
|
# Send a CancelSignal to the worker instead of the update
|
||||||
receiving_worker.queue.put(worker.CancelSignal())
|
receiving_worker.queue.put(worker.CancelSignal())
|
||||||
else:
|
else:
|
||||||
|
log.debug(f"Forwarding message to {worker}")
|
||||||
# Forward the update to the worker
|
# Forward the update to the worker
|
||||||
receiving_worker.queue.put(update)
|
receiving_worker.queue.put(update)
|
||||||
# If the update is a inline keyboard press...
|
# If the update is a inline keyboard press...
|
||||||
|
@ -89,17 +118,20 @@ def main():
|
||||||
receiving_worker = chat_workers.get(update.callback_query.from_user.id)
|
receiving_worker = chat_workers.get(update.callback_query.from_user.id)
|
||||||
# Ensure a worker exists for the chat
|
# Ensure a worker exists for the chat
|
||||||
if receiving_worker is None:
|
if receiving_worker is None:
|
||||||
|
log.debug(f"Received a callback query in a chat without worker: {update.message.chat.id}")
|
||||||
# Suggest that the user restarts the chat with /start
|
# Suggest that the user restarts the chat with /start
|
||||||
bot.send_message(update.callback_query.from_user.id, strings.error_no_worker_for_chat)
|
bot.send_message(update.callback_query.from_user.id, strings.error_no_worker_for_chat)
|
||||||
# Skip the update
|
# Skip the update
|
||||||
continue
|
continue
|
||||||
# Check if the pressed inline key is a cancel button
|
# Check if the pressed inline key is a cancel button
|
||||||
if update.callback_query.data == "cmd_cancel":
|
if update.callback_query.data == "cmd_cancel":
|
||||||
|
log.debug(f"Forwarding CancelSignal to {worker}")
|
||||||
# Forward a CancelSignal to the worker
|
# Forward a CancelSignal to the worker
|
||||||
receiving_worker.queue.put(worker.CancelSignal())
|
receiving_worker.queue.put(worker.CancelSignal())
|
||||||
# Notify the Telegram client that the inline keyboard press has been received
|
# Notify the Telegram client that the inline keyboard press has been received
|
||||||
bot.answer_callback_query(update.callback_query.id)
|
bot.answer_callback_query(update.callback_query.id)
|
||||||
else:
|
else:
|
||||||
|
log.debug(f"Forwarding callback query to {worker}")
|
||||||
# Forward the update to the worker
|
# Forward the update to the worker
|
||||||
receiving_worker.queue.put(update)
|
receiving_worker.queue.put(update)
|
||||||
# If the update is a precheckoutquery, ensure it hasn't expired before forwarding it
|
# If the update is a precheckoutquery, ensure it hasn't expired before forwarding it
|
||||||
|
@ -110,14 +142,16 @@ def main():
|
||||||
if receiving_worker is None or \
|
if receiving_worker is None or \
|
||||||
update.pre_checkout_query.invoice_payload != receiving_worker.invoice_payload:
|
update.pre_checkout_query.invoice_payload != receiving_worker.invoice_payload:
|
||||||
# Notify the user that the invoice has expired
|
# Notify the user that the invoice has expired
|
||||||
|
log.debug(f"Received a pre-checkout query for an expired invoice in: {update.message.chat.id}")
|
||||||
try:
|
try:
|
||||||
bot.answer_pre_checkout_query(update.pre_checkout_query.id,
|
bot.answer_pre_checkout_query(update.pre_checkout_query.id,
|
||||||
ok=False,
|
ok=False,
|
||||||
error_message=strings.error_invoice_expired)
|
error_message=strings.error_invoice_expired)
|
||||||
except telegram.error.BadRequest:
|
except telegram.error.BadRequest:
|
||||||
print(f"ERROR: pre_checkout_query expired before an answer could be sent")
|
log.error("pre-checkout query expired before an answer could be sent!")
|
||||||
# Go to the next update
|
# Go to the next update
|
||||||
continue
|
continue
|
||||||
|
log.debug(f"Forwarding pre-checkout query to {worker}")
|
||||||
# Forward the update to the worker
|
# Forward the update to the worker
|
||||||
receiving_worker.queue.put(update)
|
receiving_worker.queue.put(update)
|
||||||
# If there were any updates...
|
# If there were any updates...
|
||||||
|
|
Loading…
Reference in a new issue