From 6883d8ff3da9afb118902ef8bd850356913ccd31 Mon Sep 17 00:00:00 2001 From: Stefano Pigozzi Date: Thu, 12 Apr 2018 10:21:11 +0200 Subject: [PATCH] Add Sentry integration and error handling --- config/template_config.ini | 9 +++++-- strings.py | 15 +++++++---- worker.py | 53 ++++++++++++++++++++++++++------------ 3 files changed, 53 insertions(+), 24 deletions(-) diff --git a/config/template_config.ini b/config/template_config.ini index 5480e64..cef2329 100644 --- a/config/template_config.ini +++ b/config/template_config.ini @@ -3,7 +3,7 @@ # Config file parameters [Config] ; Config file version. DO NOT EDIT THIS! -version = 10 +version = 11 ; Set this to no when you are done editing the file is_template = yes @@ -47,4 +47,9 @@ name_required = yes # Require the email of the user email_required = yes # Require the phone number of the user -phone_required = yes \ No newline at end of file +phone_required = yes + +# Exception reporting settings +[Error Reporting] +# Optional sentry token: get the token at https://sentry.io/ +sentry_token = https://00000000000000000000000000000000:00000000000000000000000000000000@sentry.io/0000000 \ No newline at end of file diff --git a/strings.py b/strings.py index 1b5c8a7..a97a02a 100644 --- a/strings.py +++ b/strings.py @@ -41,9 +41,6 @@ conversation_after_start = "Ciao!\n" \ conversation_open_user_menu = "Cosa vorresti fare?\n" \ "Hai {credit} sul portafoglio." -# Conversation: the same message as above but when the first has already been sent -conversation_open_user_menu_multiple = "Hai bisogno di qualcos'altro?" - # Conversation: like above, but for administrators conversation_open_admin_menu = "Sei un amministratore di greed!\n" \ "Cosa vorresti fare?" @@ -196,7 +193,8 @@ edit_current_value = "Il valore attuale è:\n" \ # Payment: cash payment info payment_cash = "Puoi pagare in contanti alla sede fisica del negozio.\n" \ - "Il gestore provvederà ad aggiungere credito al tuo account appena gli avrai consegnato i soldi." + "Paga alla cassa, e fornisci al gestore del negozio questo id:\n" \ + "{user_cash_id}" # Payment: invoice amount payment_cc_amount = "Quanti fondi vuoi aggiungere al tuo portafoglio?" @@ -290,4 +288,11 @@ error_order_already_cleared = "⚠️ Questo ordine è già stato processato." error_no_orders = "⚠️ Non hai ancora piazzato ordini, quindi non c'è niente da visualizzare!" # Error: selected user does not exist -error_user_does_not_exist = "⚠️ L'utente selezionato non esiste." \ No newline at end of file +error_user_does_not_exist = "⚠️ L'utente selezionato non esiste." + +# Fatal: conversation raised an exception +fatal_conversation_exception = "☢️ Il bot ha riscontrato un errore fatale durante l'esecuzione e ha interrotto la" \ + " conversazione.\n" \ + "L'errore è stato segnalato allo sviluppatore di greed," \ + " in modo che possa sistemarlo.\n" \ + "Per avviare una nuova conversazione, invia il comando /start." \ No newline at end of file diff --git a/worker.py b/worker.py index e50d8cc..dec9c8b 100644 --- a/worker.py +++ b/worker.py @@ -10,6 +10,7 @@ import queue as queuem import database as db import re import utils +import os from html import escape @@ -43,6 +44,15 @@ class ChatWorker(threading.Thread): self.queue = queuem.Queue() # The current active invoice payload; reject all invoices with a different payload self.invoice_payload = None + # The Sentry client for reporting errors encountered by the user + if configloader.config["Error Reporting"]["sentry_token"] != \ + "https://00000000000000000000000000000000:00000000000000000000000000000000@sentry.io/0000000": + import raven + 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") + else: + self.sentry_client = None def run(self): """The conversation code.""" @@ -59,17 +69,28 @@ class ChatWorker(threading.Thread): self.session.add(self.user) # Commit the transaction self.session.commit() - # If the user is not an admin, send him to the user menu - if self.admin is None: - self.__user_menu() - # If the user is an admin, send him to the admin menu - else: - # Clear the live orders flag - self.admin.live_mode = False - # Commit the change - self.session.commit() - # Open the admin menu - self.__admin_menu() + # Capture exceptions that occour during the conversation + try: + # If the user is not an admin, send him to the user menu + if self.admin is None: + self.__user_menu() + # If the user is an admin, send him to the admin menu + else: + # Clear the live orders flag + self.admin.live_mode = False + # Commit the change + self.session.commit() + # Open the admin menu + self.__admin_menu() + except Exception: + # Try to notify the user of the exception + try: + self.bot.send_message(self.chat.id, strings.fatal_conversation_exception) + except Exception: + pass + # If the Sentry integration is enabled, log the exception + if self.sentry_client is not None: + self.sentry_client.captureException() def stop(self, reason: str=""): """Gracefully stop the worker process""" @@ -473,7 +494,9 @@ class ChatWorker(threading.Thread): # If the user has selected the Cash option... if selection == strings.menu_cash: # Go to the pay with cash function - self.__add_credit_cash() + self.bot.send_message(self.chat.id, + strings.payment_cash.format(user_cash_id=self.user.identifiable_str()), + parse_mode="HTML") # If the user has selected the Credit Card option... elif selection == strings.menu_credit_card: # Go to the pay with credit card function @@ -483,10 +506,6 @@ class ChatWorker(threading.Thread): # Send him back to the previous menu return - def __add_credit_cash(self): - """Tell the user how to pay with cash at this shop.""" - self.bot.send_message(self.chat.id, strings.payment_cash) - def __add_credit_cc(self): """Add money to the wallet through a credit card payment.""" # Create a keyboard to be sent later @@ -867,7 +886,7 @@ class ChatWorker(threading.Thread): def __create_transaction(self): """Edit manually the credit of an user.""" # Find all the users in the database - users = self.session.query(db.User).all() + users = self.session.query(db.User).order_by(db.User.user_id).all() # Create a list containing all the keyboard button strings keyboard_buttons = [[strings.menu_cancel]] # Add to the list all the users