mirror of
https://github.com/Steffo99/greed.git
synced 2024-11-21 21:44:19 +00:00
Handle telegram exceptions
This commit is contained in:
parent
410aa99018
commit
2ba2be62ff
3 changed files with 94 additions and 23 deletions
4
core.py
4
core.py
|
@ -4,13 +4,13 @@ import time
|
||||||
import strings
|
import strings
|
||||||
import worker
|
import worker
|
||||||
import configloader
|
import configloader
|
||||||
|
import utils
|
||||||
|
|
||||||
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!"""
|
||||||
|
|
||||||
# Create a bot instance
|
# Create a bot instance
|
||||||
bot = telegram.Bot(configloader.config["Telegram"]["token"])
|
bot = utils.DuckBot(configloader.config["Telegram"]["token"])
|
||||||
|
|
||||||
# Test the specified token
|
# Test the specified token
|
||||||
try:
|
try:
|
||||||
|
|
73
utils.py
73
utils.py
|
@ -1,6 +1,6 @@
|
||||||
import telegram
|
import telegram
|
||||||
import telegram.error
|
import telegram.error
|
||||||
|
import time
|
||||||
from configloader import config
|
from configloader import config
|
||||||
from strings import currency_format_string, currency_symbol
|
from strings import currency_format_string, currency_symbol
|
||||||
import typing
|
import typing
|
||||||
|
@ -66,6 +66,7 @@ class Price:
|
||||||
return Price(Price(other).value - self.value)
|
return Price(Price(other).value - self.value)
|
||||||
|
|
||||||
def __rmul__(self, other):
|
def __rmul__(self, other):
|
||||||
|
|
||||||
return self.__mul__(other)
|
return self.__mul__(other)
|
||||||
|
|
||||||
def __iadd__(self, other):
|
def __iadd__(self, other):
|
||||||
|
@ -85,3 +86,73 @@ class Price:
|
||||||
self.value //= other
|
self.value //= other
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
|
||||||
|
def catch_telegram_errors(func):
|
||||||
|
"""Decorator, can be applied to any function to retry in case of Telegram errors."""
|
||||||
|
def result_func(*args, **kwargs):
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
func(*args, **kwargs)
|
||||||
|
except telegram.error.Unauthorized:
|
||||||
|
print(f"Unauthorized to call {func.__name__}(), skipping.")
|
||||||
|
break
|
||||||
|
except telegram.error.TimedOut:
|
||||||
|
print(f"Timed out while calling {func.__name__}(), retrying in 1 sec...")
|
||||||
|
time.sleep(1)
|
||||||
|
except telegram.error.NetworkError:
|
||||||
|
print(f"Network error while calling {func.__name__}(), retrying in 5 secs...")
|
||||||
|
time.sleep(5)
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
return result_func
|
||||||
|
|
||||||
|
|
||||||
|
class DuckBot:
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self.bot = telegram.Bot(*args, **kwargs)
|
||||||
|
|
||||||
|
@catch_telegram_errors
|
||||||
|
def send_message(self, *args, **kwargs):
|
||||||
|
self.bot.send_message(*args, **kwargs)
|
||||||
|
|
||||||
|
@catch_telegram_errors
|
||||||
|
def edit_message_text(self, *args, **kwargs):
|
||||||
|
self.bot.edit_message_text(*args, **kwargs)
|
||||||
|
|
||||||
|
@catch_telegram_errors
|
||||||
|
def edit_message_caption(self, *args, **kwargs):
|
||||||
|
self.bot.edit_message_caption(*args, **kwargs)
|
||||||
|
|
||||||
|
@catch_telegram_errors
|
||||||
|
def edit_message_reply_markup(self, *args, **kwargs):
|
||||||
|
self.bot.edit_message_reply_markup(*args, **kwargs)
|
||||||
|
|
||||||
|
@catch_telegram_errors
|
||||||
|
def get_updates(self, *args, **kwargs):
|
||||||
|
self.bot.get_updates(*args, **kwargs)
|
||||||
|
|
||||||
|
@catch_telegram_errors
|
||||||
|
def get_me(self, *args, **kwargs):
|
||||||
|
self.bot.get_me(*args, **kwargs)
|
||||||
|
|
||||||
|
@catch_telegram_errors
|
||||||
|
def answer_callback_query(self, *args, **kwargs):
|
||||||
|
self.bot.answer_callback_query(*args, **kwargs)
|
||||||
|
|
||||||
|
@catch_telegram_errors
|
||||||
|
def answer_pre_checkout_query(self, *args, **kwargs):
|
||||||
|
self.bot.answer_pre_checkout_query(*args, **kwargs)
|
||||||
|
|
||||||
|
@catch_telegram_errors
|
||||||
|
def send_invoice(self, *args, **kwargs):
|
||||||
|
self.bot.send_invoice(*args, **kwargs)
|
||||||
|
|
||||||
|
@catch_telegram_errors
|
||||||
|
def get_file(self, *args, **kwargs):
|
||||||
|
self.bot.get_file(*args, **kwargs)
|
||||||
|
|
||||||
|
@catch_telegram_errors
|
||||||
|
def send_chat_action(self, *args, **kwargs):
|
||||||
|
self.bot.send_chat_action(*args, **kwargs)
|
||||||
|
|
||||||
|
# TODO: add more methods
|
40
worker.py
40
worker.py
|
@ -9,7 +9,7 @@ import sys
|
||||||
import queue as queuem
|
import queue as queuem
|
||||||
import database as db
|
import database as db
|
||||||
import re
|
import re
|
||||||
from utils import Price
|
import utils
|
||||||
from html import escape
|
from html import escape
|
||||||
|
|
||||||
class StopSignal:
|
class StopSignal:
|
||||||
|
@ -27,17 +27,17 @@ class CancelSignal:
|
||||||
class ChatWorker(threading.Thread):
|
class ChatWorker(threading.Thread):
|
||||||
"""A worker for a single conversation. A new one is created every time the /start command is sent."""
|
"""A worker for a single conversation. A new one is created every time the /start command is sent."""
|
||||||
|
|
||||||
def __init__(self, bot: telegram.Bot, chat: telegram.Chat, *args, **kwargs):
|
def __init__(self, bot: utils.DuckBot, chat: telegram.Chat, *args, **kwargs):
|
||||||
# Initialize the thread
|
# Initialize the thread
|
||||||
super().__init__(name=f"ChatThread {chat.first_name}", *args, **kwargs)
|
super().__init__(name=f"ChatThread {chat.first_name}", *args, **kwargs)
|
||||||
# Store the bot and chat info inside the class
|
# Store the bot and chat info inside the class
|
||||||
self.bot = bot
|
self.bot: utils.DuckBot = bot
|
||||||
self.chat = chat
|
self.chat: telegram.Chat = chat
|
||||||
# Open a new database session
|
# Open a new database session
|
||||||
self.session = db.Session()
|
self.session = db.Session()
|
||||||
# Get the user db data from the users and admin tables
|
# Get the user db data from the users and admin tables
|
||||||
self.user = None
|
self.user: typing.Optional[db.User] = None
|
||||||
self.admin = None
|
self.admin: typing.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 ChatWorker class, allowing the forwarding of messages to the chat process
|
||||||
self.queue = queuem.Queue()
|
self.queue = queuem.Queue()
|
||||||
# The current active invoice payload; reject all invoices with a different payload
|
# The current active invoice payload; reject all invoices with a different payload
|
||||||
|
@ -216,7 +216,7 @@ class ChatWorker(threading.Thread):
|
||||||
[telegram.KeyboardButton(strings.menu_bot_info)]]
|
[telegram.KeyboardButton(strings.menu_bot_info)]]
|
||||||
# Send the previously created keyboard to the user (ensuring it can be clicked only 1 time)
|
# Send the previously created keyboard to the user (ensuring it can be clicked only 1 time)
|
||||||
self.bot.send_message(self.chat.id,
|
self.bot.send_message(self.chat.id,
|
||||||
strings.conversation_open_user_menu.format(credit=Price(self.user.credit)),
|
strings.conversation_open_user_menu.format(credit=utils.Price(self.user.credit)),
|
||||||
reply_markup=telegram.ReplyKeyboardMarkup(keyboard, one_time_keyboard=True),
|
reply_markup=telegram.ReplyKeyboardMarkup(keyboard, one_time_keyboard=True),
|
||||||
parse_mode="HTML")
|
parse_mode="HTML")
|
||||||
# Wait for a reply from the user
|
# Wait for a reply from the user
|
||||||
|
@ -295,7 +295,7 @@ class ChatWorker(threading.Thread):
|
||||||
caption=product.text(style="image", cart_qty=cart[callback.message.message_id][1]), parse_mode="HTML", reply_markup=product_inline_keyboard)
|
caption=product.text(style="image", cart_qty=cart[callback.message.message_id][1]), parse_mode="HTML", reply_markup=product_inline_keyboard)
|
||||||
# Create the cart summary
|
# Create the cart summary
|
||||||
product_list = ""
|
product_list = ""
|
||||||
total_cost = Price(0)
|
total_cost = utils.Price(0)
|
||||||
for product_id in cart:
|
for product_id in cart:
|
||||||
if cart[product_id][1] > 0:
|
if cart[product_id][1] > 0:
|
||||||
product_list += cart[product_id][0].text(style="short", cart_qty=cart[product_id][1]) + "\n"
|
product_list += cart[product_id][0].text(style="short", cart_qty=cart[product_id][1]) + "\n"
|
||||||
|
@ -332,7 +332,7 @@ class ChatWorker(threading.Thread):
|
||||||
caption=product.text(style="image", cart_qty=cart[callback.message.message_id][1]), parse_mode="HTML", reply_markup=product_inline_keyboard)
|
caption=product.text(style="image", cart_qty=cart[callback.message.message_id][1]), parse_mode="HTML", reply_markup=product_inline_keyboard)
|
||||||
# Create the cart summary
|
# Create the cart summary
|
||||||
product_list = ""
|
product_list = ""
|
||||||
total_cost = Price(0)
|
total_cost = utils.Price(0)
|
||||||
for product_id in cart:
|
for product_id in cart:
|
||||||
if cart[product_id][1] > 0:
|
if cart[product_id][1] > 0:
|
||||||
product_list += cart[product_id][0].text(style="short", cart_qty=cart[product_id][1]) + "\n"
|
product_list += cart[product_id][0].text(style="short", cart_qty=cart[product_id][1]) + "\n"
|
||||||
|
@ -447,10 +447,10 @@ class ChatWorker(threading.Thread):
|
||||||
def __add_credit_cc(self):
|
def __add_credit_cc(self):
|
||||||
"""Add money to the wallet through a credit card payment."""
|
"""Add money to the wallet through a credit card payment."""
|
||||||
# Create a keyboard to be sent later
|
# Create a keyboard to be sent later
|
||||||
keyboard = [[telegram.KeyboardButton(str(Price("10.00")))],
|
keyboard = [[telegram.KeyboardButton(str(utils.Price("10.00")))],
|
||||||
[telegram.KeyboardButton(str(Price("25.00")))],
|
[telegram.KeyboardButton(str(utils.Price("25.00")))],
|
||||||
[telegram.KeyboardButton(str(Price("50.00")))],
|
[telegram.KeyboardButton(str(utils.Price("50.00")))],
|
||||||
[telegram.KeyboardButton(str(Price("100.00")))],
|
[telegram.KeyboardButton(str(utils.Price("100.00")))],
|
||||||
[telegram.KeyboardButton(strings.menu_cancel)]]
|
[telegram.KeyboardButton(strings.menu_cancel)]]
|
||||||
# Boolean variable to check if the user has cancelled the action
|
# Boolean variable to check if the user has cancelled the action
|
||||||
cancelled = False
|
cancelled = False
|
||||||
|
@ -468,13 +468,13 @@ class ChatWorker(threading.Thread):
|
||||||
cancelled = True
|
cancelled = True
|
||||||
continue
|
continue
|
||||||
# Convert the amount to an integer
|
# Convert the amount to an integer
|
||||||
value = Price(selection)
|
value = utils.Price(selection)
|
||||||
# Ensure the amount is within the range
|
# Ensure the amount is within the range
|
||||||
if value > Price(int(configloader.config["Credit Card"]["max_amount"])):
|
if value > utils.Price(int(configloader.config["Credit Card"]["max_amount"])):
|
||||||
self.bot.send_message(self.chat.id, strings.error_payment_amount_over_max.format(max_amount=Price(configloader.config["Payments"]["max_amount"])))
|
self.bot.send_message(self.chat.id, strings.error_payment_amount_over_max.format(max_amount=utils.Price(configloader.config["Payments"]["max_amount"])))
|
||||||
continue
|
continue
|
||||||
elif value < Price(int(configloader.config["Credit Card"]["min_amount"])):
|
elif value < utils.Price(int(configloader.config["Credit Card"]["min_amount"])):
|
||||||
self.bot.send_message(self.chat.id, strings.error_payment_amount_under_min.format(min_amount=Price(configloader.config["Payments"]["min_amount"])))
|
self.bot.send_message(self.chat.id, strings.error_payment_amount_under_min.format(min_amount=utils.Price(configloader.config["Payments"]["min_amount"])))
|
||||||
continue
|
continue
|
||||||
break
|
break
|
||||||
# If the user cancelled the action...
|
# If the user cancelled the action...
|
||||||
|
@ -637,7 +637,7 @@ class ChatWorker(threading.Thread):
|
||||||
self.bot.send_message(self.chat.id, strings.ask_product_price, parse_mode="HTML")
|
self.bot.send_message(self.chat.id, strings.ask_product_price, parse_mode="HTML")
|
||||||
# Display the current name if you're editing an existing product
|
# Display the current name if you're editing an existing product
|
||||||
if product:
|
if product:
|
||||||
self.bot.send_message(self.chat.id, strings.edit_current_value.format(value=(str(Price(product.price)) if product.price is not None else 'Non in vendita')), parse_mode="HTML", reply_markup=cancel)
|
self.bot.send_message(self.chat.id, strings.edit_current_value.format(value=(str(utils.Price(product.price)) if product.price is not None else 'Non in vendita')), parse_mode="HTML", reply_markup=cancel)
|
||||||
# Wait for an answer
|
# Wait for an answer
|
||||||
price = self.__wait_for_regex(r"([0-9]{1,3}(?:[.,][0-9]{1,2})?|[Xx])", cancellable=True)
|
price = self.__wait_for_regex(r"([0-9]{1,3}(?:[.,][0-9]{1,2})?|[Xx])", cancellable=True)
|
||||||
# If the price is skipped
|
# If the price is skipped
|
||||||
|
@ -646,7 +646,7 @@ class ChatWorker(threading.Thread):
|
||||||
elif price.lower() == "x":
|
elif price.lower() == "x":
|
||||||
price = None
|
price = None
|
||||||
else:
|
else:
|
||||||
price = Price(price)
|
price = utils.Price(price)
|
||||||
# Ask for the product image
|
# Ask for the product image
|
||||||
self.bot.send_message(self.chat.id, strings.ask_product_image, reply_markup=cancel)
|
self.bot.send_message(self.chat.id, strings.ask_product_image, reply_markup=cancel)
|
||||||
# Wait for an answer
|
# Wait for an answer
|
||||||
|
|
Loading…
Reference in a new issue