2018-04-05 07:30:32 +00:00
|
|
|
import telegram
|
|
|
|
import telegram.error
|
2018-04-05 07:57:59 +00:00
|
|
|
import time
|
2018-02-28 19:07:40 +00:00
|
|
|
from configloader import config
|
|
|
|
import typing
|
2018-04-16 09:15:54 +00:00
|
|
|
import os
|
|
|
|
import sys
|
2020-02-04 13:13:14 +00:00
|
|
|
import importlib
|
2020-04-01 01:19:01 +00:00
|
|
|
import logging
|
|
|
|
import traceback
|
|
|
|
|
|
|
|
|
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
|
2020-02-04 13:13:14 +00:00
|
|
|
|
|
|
|
language = config["Config"]["language"]
|
2020-02-05 02:23:13 +00:00
|
|
|
try:
|
|
|
|
strings = importlib.import_module("strings." + language)
|
|
|
|
except ModuleNotFoundError:
|
|
|
|
print("The strings file you specified in the config file does not exist.")
|
|
|
|
sys.exit(1)
|
2018-04-16 09:15:54 +00:00
|
|
|
|
|
|
|
if config["Error Reporting"]["sentry_token"] != \
|
|
|
|
"https://00000000000000000000000000000000:00000000000000000000000000000000@sentry.io/0000000":
|
|
|
|
import raven
|
|
|
|
|
|
|
|
sentry_client = raven.Client(config["Error Reporting"]["sentry_token"],
|
|
|
|
release=raven.fetch_git_sha(os.path.dirname(__file__)),
|
|
|
|
environment="Dev" if __debug__ else "Prod")
|
|
|
|
else:
|
|
|
|
sentry_client = None
|
2018-02-28 19:07:40 +00:00
|
|
|
|
2018-04-05 08:34:14 +00:00
|
|
|
|
2018-02-28 19:07:40 +00:00
|
|
|
class Price:
|
2018-05-03 07:24:04 +00:00
|
|
|
"""The base class for the prices in greed.
|
|
|
|
Its int value is in minimum units, while its float and str values are in decimal format.int("""
|
2020-03-13 01:54:23 +00:00
|
|
|
|
|
|
|
def __init__(self, value: typing.Union[int, float, str, "Price"] = 0):
|
2018-02-28 19:07:40 +00:00
|
|
|
if isinstance(value, int):
|
|
|
|
# Keep the value as it is
|
|
|
|
self.value = int(value)
|
|
|
|
elif isinstance(value, float):
|
|
|
|
# Convert the value to minimum units
|
|
|
|
self.value = int(value * (10 ** int(config["Payments"]["currency_exp"])))
|
|
|
|
elif isinstance(value, str):
|
|
|
|
# Remove decimal points, then cast to int
|
2018-03-06 16:39:02 +00:00
|
|
|
self.value = int(float(value.replace(",", ".")) * (10 ** int(config["Payments"]["currency_exp"])))
|
2018-02-28 19:07:40 +00:00
|
|
|
elif isinstance(value, Price):
|
|
|
|
# Copy self
|
|
|
|
self.value = value.value
|
|
|
|
|
2018-03-22 09:33:50 +00:00
|
|
|
def __repr__(self):
|
|
|
|
return f"<Price of value {self.value}>"
|
|
|
|
|
2018-02-28 19:07:40 +00:00
|
|
|
def __str__(self):
|
2020-02-04 13:13:14 +00:00
|
|
|
return strings.currency_format_string.format(symbol=strings.currency_symbol,
|
2020-03-13 01:54:23 +00:00
|
|
|
value="{0:.2f}".format(
|
|
|
|
self.value / (10 ** int(config["Payments"]["currency_exp"]))))
|
2018-02-28 19:07:40 +00:00
|
|
|
|
|
|
|
def __int__(self):
|
|
|
|
return self.value
|
|
|
|
|
|
|
|
def __float__(self):
|
|
|
|
return self.value / (10 ** int(config["Payments"]["currency_exp"]))
|
|
|
|
|
|
|
|
def __ge__(self, other):
|
|
|
|
return self.value >= Price(other).value
|
|
|
|
|
|
|
|
def __le__(self, other):
|
|
|
|
return self.value <= Price(other).value
|
|
|
|
|
|
|
|
def __eq__(self, other):
|
|
|
|
return self.value == Price(other).value
|
|
|
|
|
|
|
|
def __gt__(self, other):
|
|
|
|
return self.value > Price(other).value
|
|
|
|
|
|
|
|
def __lt__(self, other):
|
|
|
|
return self.value < Price(other).value
|
|
|
|
|
|
|
|
def __add__(self, other):
|
|
|
|
return Price(self.value + Price(other).value)
|
|
|
|
|
|
|
|
def __sub__(self, other):
|
|
|
|
return Price(self.value - Price(other).value)
|
|
|
|
|
|
|
|
def __mul__(self, other):
|
2018-03-22 09:33:50 +00:00
|
|
|
return Price(int(self.value * other))
|
2018-02-28 19:07:40 +00:00
|
|
|
|
|
|
|
def __floordiv__(self, other):
|
2018-03-22 09:33:50 +00:00
|
|
|
return Price(int(self.value // other))
|
2018-02-28 19:07:40 +00:00
|
|
|
|
|
|
|
def __radd__(self, other):
|
2018-03-22 09:33:50 +00:00
|
|
|
return self.__add__(other)
|
2018-02-28 19:07:40 +00:00
|
|
|
|
|
|
|
def __rsub__(self, other):
|
|
|
|
return Price(Price(other).value - self.value)
|
|
|
|
|
|
|
|
def __rmul__(self, other):
|
2018-04-05 07:57:59 +00:00
|
|
|
|
2018-03-22 09:33:50 +00:00
|
|
|
return self.__mul__(other)
|
2018-02-28 19:07:40 +00:00
|
|
|
|
|
|
|
def __iadd__(self, other):
|
|
|
|
self.value += Price(other).value
|
|
|
|
return self
|
|
|
|
|
|
|
|
def __isub__(self, other):
|
|
|
|
self.value -= Price(other).value
|
|
|
|
return self
|
|
|
|
|
|
|
|
def __imul__(self, other):
|
2018-03-22 09:33:50 +00:00
|
|
|
self.value *= other
|
|
|
|
self.value = int(self.value)
|
2018-02-28 19:07:40 +00:00
|
|
|
return self
|
|
|
|
|
|
|
|
def __ifloordiv__(self, other):
|
2018-03-22 09:33:50 +00:00
|
|
|
self.value //= other
|
2018-02-28 19:07:40 +00:00
|
|
|
return self
|
2018-04-05 07:30:32 +00:00
|
|
|
|
2018-04-05 07:57:59 +00:00
|
|
|
|
2018-04-16 10:18:42 +00:00
|
|
|
def telegram_html_escape(string: str):
|
2020-03-13 01:54:23 +00:00
|
|
|
return string.replace("<", "<") \
|
|
|
|
.replace(">", ">") \
|
|
|
|
.replace("&", "&") \
|
|
|
|
.replace('"', """)
|
2018-04-16 10:18:42 +00:00
|
|
|
|
|
|
|
|
2018-04-05 07:57:59 +00:00
|
|
|
def catch_telegram_errors(func):
|
|
|
|
"""Decorator, can be applied to any function to retry in case of Telegram errors."""
|
2020-03-13 01:54:23 +00:00
|
|
|
|
2018-04-05 07:57:59 +00:00
|
|
|
def result_func(*args, **kwargs):
|
|
|
|
while True:
|
|
|
|
try:
|
2018-04-05 08:34:14 +00:00
|
|
|
return func(*args, **kwargs)
|
|
|
|
# Bot was blocked by the user
|
2018-04-05 07:57:59 +00:00
|
|
|
except telegram.error.Unauthorized:
|
2020-04-01 01:19:01 +00:00
|
|
|
log.debug(f"Unauthorized to call {func.__name__}(), skipping.")
|
2018-04-05 07:57:59 +00:00
|
|
|
break
|
2018-04-05 08:34:14 +00:00
|
|
|
# Telegram API didn't answer in time
|
2018-04-05 07:57:59 +00:00
|
|
|
except telegram.error.TimedOut:
|
2020-04-01 01:19:01 +00:00
|
|
|
log.warning(f"Timed out while calling {func.__name__}(),"
|
|
|
|
f" retrying in {config['Telegram']['timed_out_pause']} secs...")
|
2018-05-03 07:24:04 +00:00
|
|
|
time.sleep(int(config["Telegram"]["timed_out_pause"]))
|
2018-04-05 08:34:14 +00:00
|
|
|
# Telegram is not reachable
|
2018-09-18 23:23:04 +00:00
|
|
|
except telegram.error.NetworkError as error:
|
2020-04-01 01:19:01 +00:00
|
|
|
log.error(f"Network error while calling {func.__name__}(),"
|
|
|
|
f" retrying in {config['Telegram']['error_pause']} secs...\n"
|
|
|
|
f"Full error: {error.message}")
|
2018-05-03 07:24:04 +00:00
|
|
|
time.sleep(int(config["Telegram"]["error_pause"]))
|
2018-04-05 08:34:14 +00:00
|
|
|
# Unknown error
|
2018-04-26 06:15:18 +00:00
|
|
|
except telegram.error.TelegramError as error:
|
2018-04-26 06:16:39 +00:00
|
|
|
if error.message.lower() in ["bad gateway", "invalid server response"]:
|
2020-04-01 01:19:01 +00:00
|
|
|
log.warning(f"Bad Gateway while calling {func.__name__}(),"
|
|
|
|
f" retrying in {config['Telegram']['error_pause']} secs...")
|
2018-05-03 07:24:04 +00:00
|
|
|
time.sleep(int(config["Telegram"]["error_pause"]))
|
2018-04-26 06:15:18 +00:00
|
|
|
elif error.message.lower() == "timed out":
|
2020-04-01 01:19:01 +00:00
|
|
|
log.warning(f"Timed out while calling {func.__name__}(),"
|
|
|
|
f" retrying in {config['Telegram']['timed_out_pause']} secs...")
|
2018-05-03 07:24:04 +00:00
|
|
|
time.sleep(int(config["Telegram"]["timed_out_pause"]))
|
2018-04-26 06:15:18 +00:00
|
|
|
else:
|
2020-04-01 01:19:01 +00:00
|
|
|
log.error(f"Telegram error while calling {func.__name__}(),"
|
|
|
|
f" retrying in {config['Telegram']['error_pause']} secs...\n"
|
|
|
|
f"Full error: {error.message}")
|
2018-04-26 06:15:18 +00:00
|
|
|
# Send the error to the Sentry server
|
2020-03-13 01:54:23 +00:00
|
|
|
if sentry_client is not None:
|
2018-04-26 06:15:18 +00:00
|
|
|
sentry_client.captureException(exc_info=sys.exc_info())
|
2020-04-01 01:19:01 +00:00
|
|
|
else:
|
|
|
|
traceback.print_exception(*sys.exc_info())
|
2018-05-03 07:24:04 +00:00
|
|
|
time.sleep(int(config["Telegram"]["error_pause"]))
|
2020-03-13 01:54:23 +00:00
|
|
|
|
2018-04-05 07:57:59 +00:00
|
|
|
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):
|
2018-04-19 07:55:36 +00:00
|
|
|
# All messages are sent in HTML parse mode
|
|
|
|
return self.bot.send_message(parse_mode="HTML", *args, **kwargs)
|
2018-04-05 07:57:59 +00:00
|
|
|
|
|
|
|
@catch_telegram_errors
|
|
|
|
def edit_message_text(self, *args, **kwargs):
|
2018-04-19 07:55:36 +00:00
|
|
|
# All messages are sent in HTML parse mode
|
|
|
|
return self.bot.edit_message_text(parse_mode="HTML", *args, **kwargs)
|
2018-04-05 07:57:59 +00:00
|
|
|
|
|
|
|
@catch_telegram_errors
|
|
|
|
def edit_message_caption(self, *args, **kwargs):
|
2018-04-19 07:55:36 +00:00
|
|
|
# All messages are sent in HTML parse mode
|
|
|
|
return self.bot.edit_message_caption(parse_mode="HTML", *args, **kwargs)
|
2018-04-05 07:57:59 +00:00
|
|
|
|
|
|
|
@catch_telegram_errors
|
|
|
|
def edit_message_reply_markup(self, *args, **kwargs):
|
2018-04-05 08:34:14 +00:00
|
|
|
return self.bot.edit_message_reply_markup(*args, **kwargs)
|
2018-04-05 07:57:59 +00:00
|
|
|
|
|
|
|
@catch_telegram_errors
|
|
|
|
def get_updates(self, *args, **kwargs):
|
2018-04-05 08:34:14 +00:00
|
|
|
return self.bot.get_updates(*args, **kwargs)
|
2018-04-05 07:57:59 +00:00
|
|
|
|
|
|
|
@catch_telegram_errors
|
|
|
|
def get_me(self, *args, **kwargs):
|
2018-04-05 08:34:14 +00:00
|
|
|
return self.bot.get_me(*args, **kwargs)
|
2018-04-05 07:57:59 +00:00
|
|
|
|
|
|
|
@catch_telegram_errors
|
|
|
|
def answer_callback_query(self, *args, **kwargs):
|
2018-04-05 08:34:14 +00:00
|
|
|
return self.bot.answer_callback_query(*args, **kwargs)
|
2018-04-05 07:57:59 +00:00
|
|
|
|
|
|
|
@catch_telegram_errors
|
|
|
|
def answer_pre_checkout_query(self, *args, **kwargs):
|
2018-04-05 08:34:14 +00:00
|
|
|
return self.bot.answer_pre_checkout_query(*args, **kwargs)
|
2018-04-05 07:57:59 +00:00
|
|
|
|
|
|
|
@catch_telegram_errors
|
|
|
|
def send_invoice(self, *args, **kwargs):
|
2018-04-05 08:34:14 +00:00
|
|
|
return self.bot.send_invoice(*args, **kwargs)
|
2018-04-05 07:57:59 +00:00
|
|
|
|
|
|
|
@catch_telegram_errors
|
|
|
|
def get_file(self, *args, **kwargs):
|
2018-04-05 08:34:14 +00:00
|
|
|
return self.bot.get_file(*args, **kwargs)
|
2018-04-05 07:57:59 +00:00
|
|
|
|
|
|
|
@catch_telegram_errors
|
|
|
|
def send_chat_action(self, *args, **kwargs):
|
2018-04-05 08:34:14 +00:00
|
|
|
return self.bot.send_chat_action(*args, **kwargs)
|
2018-04-05 07:57:59 +00:00
|
|
|
|
2018-04-16 10:28:00 +00:00
|
|
|
@catch_telegram_errors
|
|
|
|
def delete_message(self, *args, **kwargs):
|
|
|
|
return self.bot.delete_message(*args, **kwargs)
|
|
|
|
|
2018-04-19 07:37:42 +00:00
|
|
|
@catch_telegram_errors
|
|
|
|
def send_document(self, *args, **kwargs):
|
|
|
|
return self.bot.send_document(*args, **kwargs)
|
|
|
|
|
2018-05-03 07:24:04 +00:00
|
|
|
# More methods can be added here
|
2018-05-04 13:46:07 +00:00
|
|
|
|
|
|
|
|
|
|
|
def boolmoji(boolean: bool):
|
|
|
|
return strings.emoji_yes if boolean else strings.emoji_no
|