mirror of
https://github.com/Steffo99/greed.git
synced 2024-11-24 14:54:18 +00:00
Improve logging system (and rename ChatWorker class to Worker)
This commit is contained in:
parent
013ffc8bbc
commit
68eafef656
2 changed files with 55 additions and 13 deletions
18
core.py
18
core.py
|
@ -48,8 +48,8 @@ def main():
|
|||
sys.exit(1)
|
||||
log.debug("Bot token is valid!")
|
||||
|
||||
# Create a dictionary linking the chat ids to the ChatWorker objects
|
||||
# {"1234": <ChatWorker>}
|
||||
# Create a dictionary linking the chat ids to the Worker objects
|
||||
# {"1234": <Worker>}
|
||||
chat_workers = {}
|
||||
|
||||
# Current update offset; if None it will get the last 100 unparsed messages
|
||||
|
@ -85,7 +85,7 @@ def main():
|
|||
log.debug(f"Received request to stop {old_worker.name}")
|
||||
old_worker.stop("request")
|
||||
# Initialize a new worker for the chat
|
||||
new_worker = worker.ChatWorker(bot, update.message.chat)
|
||||
new_worker = worker.Worker(bot, update.message.chat)
|
||||
# Start the worker
|
||||
log.debug(f"Starting {new_worker.name}")
|
||||
new_worker.start()
|
||||
|
@ -105,11 +105,11 @@ def main():
|
|||
continue
|
||||
# If the message contains the "Cancel" string defined in the strings file...
|
||||
if update.message.text == strings.menu_cancel:
|
||||
log.debug(f"Forwarding CancelSignal to {worker}")
|
||||
log.debug(f"Forwarding CancelSignal to {receiving_worker}")
|
||||
# Send a CancelSignal to the worker instead of the update
|
||||
receiving_worker.queue.put(worker.CancelSignal())
|
||||
else:
|
||||
log.debug(f"Forwarding message to {worker}")
|
||||
log.debug(f"Forwarding message to {receiving_worker}")
|
||||
# Forward the update to the worker
|
||||
receiving_worker.queue.put(update)
|
||||
# If the update is a inline keyboard press...
|
||||
|
@ -118,20 +118,20 @@ def main():
|
|||
receiving_worker = chat_workers.get(update.callback_query.from_user.id)
|
||||
# Ensure a worker exists for the chat
|
||||
if receiving_worker is None:
|
||||
log.debug(f"Received a callback query in a chat without worker: {update.message.chat.id}")
|
||||
log.debug(f"Received a callback query in a chat without worker: {update.callback_query.from_user.id}")
|
||||
# Suggest that the user restarts the chat with /start
|
||||
bot.send_message(update.callback_query.from_user.id, strings.error_no_worker_for_chat)
|
||||
# Skip the update
|
||||
continue
|
||||
# Check if the pressed inline key is a cancel button
|
||||
if update.callback_query.data == "cmd_cancel":
|
||||
log.debug(f"Forwarding CancelSignal to {worker}")
|
||||
log.debug(f"Forwarding CancelSignal to {receiving_worker}")
|
||||
# Forward a CancelSignal to the worker
|
||||
receiving_worker.queue.put(worker.CancelSignal())
|
||||
# Notify the Telegram client that the inline keyboard press has been received
|
||||
bot.answer_callback_query(update.callback_query.id)
|
||||
else:
|
||||
log.debug(f"Forwarding callback query to {worker}")
|
||||
log.debug(f"Forwarding callback query to {receiving_worker}")
|
||||
# Forward the update to the worker
|
||||
receiving_worker.queue.put(update)
|
||||
# If the update is a precheckoutquery, ensure it hasn't expired before forwarding it
|
||||
|
@ -151,7 +151,7 @@ def main():
|
|||
log.error("pre-checkout query expired before an answer could be sent!")
|
||||
# Go to the next update
|
||||
continue
|
||||
log.debug(f"Forwarding pre-checkout query to {worker}")
|
||||
log.debug(f"Forwarding pre-checkout query to {receiving_worker}")
|
||||
# Forward the update to the worker
|
||||
receiving_worker.queue.put(update)
|
||||
# If there were any updates...
|
||||
|
|
50
worker.py
50
worker.py
|
@ -14,6 +14,14 @@ import traceback
|
|||
from html import escape
|
||||
import requests
|
||||
import importlib
|
||||
import logging
|
||||
|
||||
try:
|
||||
import coloredlogs
|
||||
except ImportError:
|
||||
coloredlogs = None
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
language = configloader.config["Config"]["language"]
|
||||
strings = importlib.import_module("strings." + language)
|
||||
|
@ -31,7 +39,7 @@ class CancelSignal:
|
|||
pass
|
||||
|
||||
|
||||
class ChatWorker(threading.Thread):
|
||||
class Worker(threading.Thread):
|
||||
"""A worker for a single conversation. A new one is created every time the /start command is sent."""
|
||||
|
||||
def __init__(self, bot: utils.DuckBot, chat: telegram.Chat, *args, **kwargs):
|
||||
|
@ -41,11 +49,12 @@ class ChatWorker(threading.Thread):
|
|||
self.bot: utils.DuckBot = bot
|
||||
self.chat: telegram.Chat = chat
|
||||
# Open a new database session
|
||||
log.debug(f"Opening new database session for {self.name}")
|
||||
self.session = db.Session()
|
||||
# Get the user db data from the users and admin tables
|
||||
self.user: Optional[db.User] = None
|
||||
self.admin: Optional[db.Admin] = None
|
||||
# The sending pipe is stored in the ChatWorker class, allowing the forwarding of messages to the chat process
|
||||
# The sending pipe is stored in the Worker class, allowing the forwarding of messages to the chat process
|
||||
self.queue = queuem.Queue()
|
||||
# The current active invoice payload; reject all invoices with a different payload
|
||||
self.invoice_payload = None
|
||||
|
@ -56,13 +65,19 @@ class ChatWorker(threading.Thread):
|
|||
self.sentry_client = raven.Client(configloader.config["Error Reporting"]["sentry_token"],
|
||||
release=raven.fetch_git_sha(os.path.dirname(__file__)),
|
||||
environment="Dev" if __debug__ else "Prod")
|
||||
log.debug("Sentry: enabled")
|
||||
else:
|
||||
self.sentry_client = None
|
||||
log.debug("Sentry: disabled")
|
||||
|
||||
def __repr__(self):
|
||||
return f"<{self.__class__.__qualname__} {self.chat.id}>"
|
||||
|
||||
# noinspection PyBroadException
|
||||
def run(self):
|
||||
"""The conversation code."""
|
||||
# Welcome the user to the bot
|
||||
log.debug("Starting conversation")
|
||||
self.bot.send_message(self.chat.id, strings.conversation_after_start)
|
||||
# Get the user db data from the users and admin tables
|
||||
self.user = self.session.query(db.User).filter(db.User.user_id == self.chat.id).one_or_none()
|
||||
|
@ -91,6 +106,9 @@ class ChatWorker(threading.Thread):
|
|||
self.session.add(self.admin)
|
||||
# Commit the transaction
|
||||
self.session.commit()
|
||||
log.info(f"Created new user: {self.user}")
|
||||
if will_be_owner:
|
||||
log.warning(f"User was auto-promoted to Admin as no other admins existed: {self.user}")
|
||||
# Capture exceptions that occour during the conversation
|
||||
try:
|
||||
# If the user is not an admin, send him to the user menu
|
||||
|
@ -123,8 +141,8 @@ class ChatWorker(threading.Thread):
|
|||
self.join()
|
||||
|
||||
def update_user(self) -> db.User:
|
||||
"""Update the user data.
|
||||
Note that this method will cause crashes if used in different threads with sqlite databases."""
|
||||
"""Update the user data."""
|
||||
log.debug("Fetching updated user data from the database")
|
||||
self.user = self.session.query(db.User).filter(db.User.user_id == self.chat.id).one_or_none()
|
||||
return self.user
|
||||
|
||||
|
@ -150,6 +168,7 @@ class ChatWorker(threading.Thread):
|
|||
items: List[str],
|
||||
cancellable: bool = False) -> Union[str, CancelSignal]:
|
||||
"""Continue getting updates until until one of the strings contained in the list is received as a message."""
|
||||
log.debug("Waiting for a specific message...")
|
||||
while True:
|
||||
# Get the next update
|
||||
update = self.__receive_next_update()
|
||||
|
@ -175,6 +194,7 @@ class ChatWorker(threading.Thread):
|
|||
|
||||
def __wait_for_regex(self, regex: str, cancellable: bool = False) -> Union[str, CancelSignal]:
|
||||
"""Continue getting updates until the regex finds a match in a message, then return the first capture group."""
|
||||
log.debug("Waiting for a regex...")
|
||||
while True:
|
||||
# Get the next update
|
||||
update = self.__receive_next_update()
|
||||
|
@ -200,6 +220,7 @@ class ChatWorker(threading.Thread):
|
|||
cancellable: bool = False) -> Union[telegram.PreCheckoutQuery, CancelSignal]:
|
||||
"""Continue getting updates until a precheckoutquery is received.
|
||||
The payload is checked by the core before forwarding the message."""
|
||||
log.debug("Waiting for a PreCheckoutQuery...")
|
||||
while True:
|
||||
# Get the next update
|
||||
update = self.__receive_next_update()
|
||||
|
@ -215,6 +236,7 @@ class ChatWorker(threading.Thread):
|
|||
|
||||
def __wait_for_successfulpayment(self) -> telegram.SuccessfulPayment:
|
||||
"""Continue getting updates until a successfulpayment is received."""
|
||||
log.debug("Waiting for a SuccessfulPayment...")
|
||||
while True:
|
||||
# Get the next update
|
||||
update = self.__receive_next_update()
|
||||
|
@ -229,6 +251,7 @@ class ChatWorker(threading.Thread):
|
|||
|
||||
def __wait_for_photo(self, cancellable: bool = False) -> Union[List[telegram.PhotoSize], CancelSignal]:
|
||||
"""Continue getting updates until a photo is received, then return it."""
|
||||
log.debug("Waiting for a photo...")
|
||||
while True:
|
||||
# Get the next update
|
||||
update = self.__receive_next_update()
|
||||
|
@ -248,6 +271,7 @@ class ChatWorker(threading.Thread):
|
|||
def __wait_for_inlinekeyboard_callback(self, cancellable: bool = True) \
|
||||
-> Union[telegram.CallbackQuery, CancelSignal]:
|
||||
"""Continue getting updates until an inline keyboard callback is received, then return it."""
|
||||
log.debug("Waiting for a CallbackQuery...")
|
||||
while True:
|
||||
# Get the next update
|
||||
update = self.__receive_next_update()
|
||||
|
@ -265,6 +289,7 @@ class ChatWorker(threading.Thread):
|
|||
|
||||
def __user_select(self) -> Union[db.User, CancelSignal]:
|
||||
"""Select an user from the ones in the database."""
|
||||
log.debug("Waiting for a user selection...")
|
||||
# Find all the users in the database
|
||||
users = self.session.query(db.User).order_by(db.User.user_id).all()
|
||||
# Create a list containing all the keyboard button strings
|
||||
|
@ -294,6 +319,7 @@ class ChatWorker(threading.Thread):
|
|||
def __user_menu(self):
|
||||
"""Function called from the run method when the user is not an administrator.
|
||||
Normal bot actions should be placed here."""
|
||||
log.debug("Displaying __user_menu")
|
||||
# Loop used to returning to the menu after executing a command
|
||||
while True:
|
||||
# Create a keyboard with the user main menu
|
||||
|
@ -334,6 +360,7 @@ class ChatWorker(threading.Thread):
|
|||
|
||||
def __order_menu(self):
|
||||
"""User menu to order products from the shop."""
|
||||
log.debug("Displaying __order_menu")
|
||||
# Get the products list from the db
|
||||
products = self.session.query(db.Product).filter_by(deleted=False).all()
|
||||
# Create a dict to be used as 'cart'
|
||||
|
@ -530,6 +557,7 @@ class ChatWorker(threading.Thread):
|
|||
|
||||
def __order_status(self):
|
||||
"""Display the status of the sent orders."""
|
||||
log.debug("Displaying __order_status")
|
||||
# Find the latest orders
|
||||
orders = self.session.query(db.Order) \
|
||||
.filter(db.Order.user == self.user) \
|
||||
|
@ -546,6 +574,7 @@ class ChatWorker(threading.Thread):
|
|||
|
||||
def __add_credit_menu(self):
|
||||
"""Add more credit to the account."""
|
||||
log.debug("Displaying __add_credit_menu")
|
||||
# Create a payment methods keyboard
|
||||
keyboard = list()
|
||||
# Add the supported payment methods to the keyboard
|
||||
|
@ -577,6 +606,7 @@ class ChatWorker(threading.Thread):
|
|||
|
||||
def __add_credit_cc(self):
|
||||
"""Add money to the wallet through a credit card payment."""
|
||||
log.debug("Displaying __add_credit_cc")
|
||||
# Create a keyboard to be sent later
|
||||
keyboard = [[telegram.KeyboardButton(str(utils.Price("10.00")))],
|
||||
[telegram.KeyboardButton(str(utils.Price("25.00")))],
|
||||
|
@ -672,11 +702,13 @@ class ChatWorker(threading.Thread):
|
|||
|
||||
def __bot_info(self):
|
||||
"""Send information about the bot."""
|
||||
log.debug("Displaying __bot_info")
|
||||
self.bot.send_message(self.chat.id, strings.bot_info)
|
||||
|
||||
def __admin_menu(self):
|
||||
"""Function called from the run method when the user is an administrator.
|
||||
Administrative bot actions should be placed here."""
|
||||
log.debug("Displaying __admin_menu")
|
||||
# Loop used to return to the menu after executing a command
|
||||
while True:
|
||||
# Create a keyboard with the admin main menu based on the admin permissions specified in the db
|
||||
|
@ -733,6 +765,7 @@ class ChatWorker(threading.Thread):
|
|||
|
||||
def __products_menu(self):
|
||||
"""Display the admin menu to select a product to edit."""
|
||||
log.debug("Displaying __products_menu")
|
||||
# Get the products list from the db
|
||||
products = self.session.query(db.Product).filter_by(deleted=False).all()
|
||||
# Create a list of product names
|
||||
|
@ -769,6 +802,7 @@ class ChatWorker(threading.Thread):
|
|||
|
||||
def __edit_product_menu(self, product: Optional[db.Product] = None):
|
||||
"""Add a product to the database or edit an existing one."""
|
||||
log.debug("Displaying __edit_product_menu")
|
||||
# Create an inline keyboard with a single skip button
|
||||
cancel = telegram.InlineKeyboardMarkup([[telegram.InlineKeyboardButton(strings.menu_skip,
|
||||
callback_data="cmd_cancel")]])
|
||||
|
@ -857,6 +891,7 @@ class ChatWorker(threading.Thread):
|
|||
self.bot.send_message(self.chat.id, strings.success_product_edited)
|
||||
|
||||
def __delete_product_menu(self):
|
||||
log.debug("Displaying __delete_product_menu")
|
||||
# Get the products list from the db
|
||||
products = self.session.query(db.Product).filter_by(deleted=False).all()
|
||||
# Create a list of product names
|
||||
|
@ -884,6 +919,7 @@ class ChatWorker(threading.Thread):
|
|||
|
||||
def __orders_menu(self):
|
||||
"""Display a live flow of orders."""
|
||||
log.debug("Displaying __orders_menu")
|
||||
# Create a cancel and a stop keyboard
|
||||
stop_keyboard = telegram.InlineKeyboardMarkup([[telegram.InlineKeyboardButton(strings.menu_stop,
|
||||
callback_data="cmd_cancel")]])
|
||||
|
@ -975,6 +1011,7 @@ class ChatWorker(threading.Thread):
|
|||
|
||||
def __create_transaction(self):
|
||||
"""Edit manually the credit of an user."""
|
||||
log.debug("Displaying __create_transaction")
|
||||
# Make the admin select an user
|
||||
user = self.__user_select()
|
||||
# Allow the cancellation of the operation
|
||||
|
@ -1017,6 +1054,7 @@ class ChatWorker(threading.Thread):
|
|||
|
||||
def __help_menu(self):
|
||||
"""Help menu. Allows the user to ask for assistance, get a guide or see some info about the bot."""
|
||||
log.debug("Displaying __help_menu")
|
||||
# Create a keyboard with the user help menu
|
||||
keyboard = [[telegram.KeyboardButton(strings.menu_guide)],
|
||||
[telegram.KeyboardButton(strings.menu_contact_shopkeeper)],
|
||||
|
@ -1044,6 +1082,7 @@ class ChatWorker(threading.Thread):
|
|||
|
||||
def __transaction_pages(self):
|
||||
"""Display the latest transactions, in pages."""
|
||||
log.debug("Displaying __transaction_pages")
|
||||
# Page number
|
||||
page = 0
|
||||
# Create and send a placeholder message to be populated
|
||||
|
@ -1098,6 +1137,7 @@ class ChatWorker(threading.Thread):
|
|||
|
||||
def __transactions_file(self):
|
||||
"""Generate a .csv file containing the list of all transactions."""
|
||||
log.debug("Generating __transaction_file")
|
||||
# Retrieve all the transactions
|
||||
transactions = self.session.query(db.Transaction).order_by(db.Transaction.transaction_id.asc()).all()
|
||||
# Create the file if it doesn't exists
|
||||
|
@ -1143,6 +1183,7 @@ class ChatWorker(threading.Thread):
|
|||
|
||||
def __add_admin(self):
|
||||
"""Add an administrator to the bot."""
|
||||
log.debug("Displaying __add_admin")
|
||||
# Let the admin select an administrator to promote
|
||||
user = self.__user_select()
|
||||
# Allow the cancellation of the operation
|
||||
|
@ -1207,6 +1248,7 @@ class ChatWorker(threading.Thread):
|
|||
|
||||
def __graceful_stop(self, stop_trigger: StopSignal):
|
||||
"""Handle the graceful stop of the thread."""
|
||||
log.debug("Gracefully stopping the conversation")
|
||||
# If the session has expired...
|
||||
if stop_trigger.reason == "timeout":
|
||||
# Notify the user that the session has expired and remove the keyboard
|
||||
|
|
Loading…
Reference in a new issue