1
Fork 0
mirror of https://github.com/Steffo99/greed.git synced 2024-11-22 05:54:18 +00:00

Fully remove global strings files

This commit is contained in:
Steffo 2020-05-03 12:40:11 +02:00
parent 5c79ec7581
commit 6b3fb880cb
7 changed files with 209 additions and 138 deletions

View file

@ -46,7 +46,7 @@ def main():
log.debug("Bot token is valid!") log.debug("Bot token is valid!")
# Finding default language # Finding default language
default_language = configloader.config["Config"]["language"] default_language = configloader.config["Language"]["default_language"]
# Creating localization object # Creating localization object
default_loc = localization.Localization(language=default_language, fallback=default_language) default_loc = localization.Localization(language=default_language, fallback=default_language)

View file

@ -7,14 +7,11 @@ import configloader
import telegram import telegram
import requests import requests
import utils import utils
import importlib import localization
import logging import logging
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
language = configloader.config["Config"]["language"]
strings = importlib.import_module("strings." + language)
# Create a (lazy) database engine # Create a (lazy) database engine
engine = create_engine(configloader.config["Database"]["engine"]) engine = create_engine(configloader.config["Database"]["engine"])
@ -50,7 +47,8 @@ class User(TableDeclarativeBase):
self.first_name = telegram_user.first_name self.first_name = telegram_user.first_name
self.last_name = telegram_user.last_name self.last_name = telegram_user.last_name
self.username = telegram_user.username self.username = telegram_user.username
self.language = telegram_user.language_code if telegram_user.language_code else configloader.config["Language"]["default_language"] self.language = telegram_user.language_code if telegram_user.language_code else configloader.config["Language"][
"default_language"]
# The starting wallet value is 0 # The starting wallet value is 0
self.credit = 0 self.credit = 0
@ -111,40 +109,37 @@ class Product(TableDeclarativeBase):
# No __init__ is needed, the default one is sufficient # No __init__ is needed, the default one is sufficient
def __str__(self): def text(self, *, loc: localization.Localization, style: str = "full", cart_qty: int = None):
return self.text()
def text(self, style: str="full", cart_qty: int=None):
"""Return the product details formatted with Telegram HTML. The image is omitted.""" """Return the product details formatted with Telegram HTML. The image is omitted."""
if style == "short": if style == "short":
return f"{cart_qty}x {utils.telegram_html_escape(self.name)} - {str(utils.Price(self.price) * cart_qty)}" return f"{cart_qty}x {utils.telegram_html_escape(self.name)} - {str(utils.Price(self.price, loc) * cart_qty)}"
elif style == "full": elif style == "full":
if cart_qty is not None: if cart_qty is not None:
cart = strings.in_cart_format_string.format(quantity=cart_qty) cart = loc.get("in_cart_format_string", quantity=cart_qty)
else: else:
cart = '' cart = ''
return strings.product_format_string.format(name=utils.telegram_html_escape(self.name), return loc.get("product_format_string", name=utils.telegram_html_escape(self.name),
description=utils.telegram_html_escape(self.description), description=utils.telegram_html_escape(self.description),
price=str(utils.Price(self.price)), price=str(utils.Price(self.price, loc)),
cart=cart) cart=cart)
else: else:
raise ValueError("style is not an accepted value") raise ValueError("style is not an accepted value")
def __repr__(self): def __repr__(self):
return f"<Product {self.name}>" return f"<Product {self.name}>"
def send_as_message(self, chat_id: int) -> dict: def send_as_message(self, loc: localization.Localization, chat_id: int) -> dict:
"""Send a message containing the product data.""" """Send a message containing the product data."""
if self.image is None: if self.image is None:
r = requests.get(f"https://api.telegram.org/bot{configloader.config['Telegram']['token']}/sendMessage", r = requests.get(f"https://api.telegram.org/bot{configloader.config['Telegram']['token']}/sendMessage",
params={"chat_id": chat_id, params={"chat_id": chat_id,
"text": self.text(), "text": self.text(loc=loc),
"parse_mode": "HTML"}) "parse_mode": "HTML"})
else: else:
r = requests.post(f"https://api.telegram.org/bot{configloader.config['Telegram']['token']}/sendPhoto", r = requests.post(f"https://api.telegram.org/bot{configloader.config['Telegram']['token']}/sendPhoto",
files={"photo": self.image}, files={"photo": self.image},
params={"chat_id": chat_id, params={"chat_id": chat_id,
"caption": self.text(), "caption": self.text(loc=loc),
"parse_mode": "HTML"}) "parse_mode": "HTML"})
return r.json() return r.json()
@ -193,10 +188,10 @@ class Transaction(TableDeclarativeBase):
__tablename__ = "transactions" __tablename__ = "transactions"
__table_args__ = (UniqueConstraint("provider", "provider_charge_id"),) __table_args__ = (UniqueConstraint("provider", "provider_charge_id"),)
def __str__(self): def text(self, *, loc: localization.Localization):
string = f"<b>T{self.transaction_id}</b> | {str(self.user)} | {utils.Price(self.value)}" string = f"<b>T{self.transaction_id}</b> | {str(self.user)} | {utils.Price(self.value, loc)}"
if self.refunded: if self.refunded:
string += f" | {strings.emoji_refunded}" string += f" | {loc.get('emoji_refunded')}"
if self.provider: if self.provider:
string += f" | {self.provider}" string += f" | {self.provider}"
if self.notes: if self.notes:
@ -259,36 +254,38 @@ class Order(TableDeclarativeBase):
def __repr__(self): def __repr__(self):
return f"<Order {self.order_id} placed by User {self.user_id}>" return f"<Order {self.order_id} placed by User {self.user_id}>"
def get_text(self, session, user=False): def text(self, *, loc: localization.Localization, session, user=False):
joined_self = session.query(Order).filter_by(order_id=self.order_id).join(Transaction).one() joined_self = session.query(Order).filter_by(order_id=self.order_id).join(Transaction).one()
items = "" items = ""
for item in self.items: for item in self.items:
items += str(item) + "\n" items += str(item) + "\n"
if self.delivery_date is not None: if self.delivery_date is not None:
status_emoji = strings.emoji_completed status_emoji = loc.get("emoji_completed")
status_text = strings.text_completed status_text = loc.get("text_completed")
elif self.refund_date is not None: elif self.refund_date is not None:
status_emoji = strings.emoji_refunded status_emoji = loc.get("emoji_refunded")
status_text = strings.text_refunded status_text = loc.get("text_refunded")
else: else:
status_emoji = strings.emoji_not_processed status_emoji = loc.get("emoji_not_processed")
status_text = strings.text_not_processed status_text = loc.get("text_not_processed")
if user and configloader.config["Appearance"]["full_order_info"] == "no": if user and configloader.config["Appearance"]["full_order_info"] == "no":
return strings.user_order_format_string.format(status_emoji=status_emoji, return loc.get("user_order_format_string",
status_text=status_text, status_emoji=status_emoji,
items=items, status_text=status_text,
notes=self.notes, items=items,
value=str(utils.Price(-joined_self.transaction.value))) + \ notes=self.notes,
(strings.refund_reason.format(reason=self.refund_reason) if self.refund_date is not None else "") value=str(utils.Price(-joined_self.transaction.value, loc))) + \
(loc.get("refund_reason", reason=self.refund_reason) if self.refund_date is not None else "")
else: else:
return status_emoji + " " + \ return status_emoji + " " + \
strings.order_number.format(id=self.order_id) + "\n" + \ loc.get("order_number", id=self.order_id) + "\n" + \
strings.order_format_string.format(user=self.user.mention(), loc.get("order_format_string",
date=self.creation_date.isoformat(), user=self.user.mention(),
items=items, date=self.creation_date.isoformat(),
notes=self.notes if self.notes is not None else "", items=items,
value=str(utils.Price(-joined_self.transaction.value))) + \ notes=self.notes if self.notes is not None else "",
(strings.refund_reason.format(reason=self.refund_reason) if self.refund_date is not None else "") value=str(utils.Price(-joined_self.transaction.value, loc))) + \
(loc.get("refund_reason", reason=self.refund_reason) if self.refund_date is not None else "")
class OrderItem(TableDeclarativeBase): class OrderItem(TableDeclarativeBase):
@ -305,8 +302,8 @@ class OrderItem(TableDeclarativeBase):
# Extra table parameters # Extra table parameters
__tablename__ = "orderitems" __tablename__ = "orderitems"
def __str__(self): def text(self, *, loc: localization.Localization):
return f"{self.product.name} - {str(utils.Price(self.product.price))}" return f"{self.product.name} - {str(utils.Price(self.product.price, loc))}"
def __repr__(self): def __repr__(self):
return f"<OrderItem {self.item_id}>" return f"<OrderItem {self.item_id}>"

View file

@ -42,3 +42,6 @@ class Localization:
assert isinstance(string, str) assert isinstance(string, str)
formatter = IgnoreDict(**self.replacements, **kwargs) formatter = IgnoreDict(**self.replacements, **kwargs)
return string.format_map(formatter) return string.format_map(formatter)
def boolmoji(self, boolean: bool) -> str:
return self.get("emoji_yes") if boolean else self.get("emoji_no")

View file

@ -118,6 +118,9 @@ conversation_open_help_menu = "What kind of help do you need?"
conversation_confirm_admin_promotion = "Are you sure you want to promote this user to 💼 Manager?\n" \ conversation_confirm_admin_promotion = "Are you sure you want to promote this user to 💼 Manager?\n" \
"It is an irreversible action!" "It is an irreversible action!"
# Conversation: language select menu header
conversation_language_select = "Select a language:"
# Conversation: switching to user mode # Conversation: switching to user mode
conversation_switch_to_user_mode = " You are switching to 👤 Customer mode.\n" \ conversation_switch_to_user_mode = " You are switching to 👤 Customer mode.\n" \
"If you want to go back to the 💼 Manager menu, restart the conversation with /start." "If you want to go back to the 💼 Manager menu, restart the conversation with /start."
@ -214,6 +217,9 @@ menu_csv = "📄 .csv"
# Menu: edit admins list # Menu: edit admins list
menu_edit_admins = "🏵 Edit Managers" menu_edit_admins = "🏵 Edit Managers"
# Menu: language
menu_language = "🇬🇧 Language"
# Emoji: unprocessed order # Emoji: unprocessed order
emoji_not_processed = "*️⃣" emoji_not_processed = "*️⃣"

View file

@ -118,6 +118,9 @@ conversation_open_help_menu = "Che tipo di assistenza desideri ricevere?"
conversation_confirm_admin_promotion = "Sei sicuro di voler promuovere questo utente a 💼 Gestore?\n" \ conversation_confirm_admin_promotion = "Sei sicuro di voler promuovere questo utente a 💼 Gestore?\n" \
"E' un'azione irreversibile!" "E' un'azione irreversibile!"
# Conversation: language select menu header
conversation_language_select = "Scegli una lingua:"
# Conversation: switching to user mode # Conversation: switching to user mode
conversation_switch_to_user_mode = "Stai passando alla modalità 👤 Cliente.\n" \ conversation_switch_to_user_mode = "Stai passando alla modalità 👤 Cliente.\n" \
"Se vuoi riassumere il ruolo di 💼 Gestore, riavvia la conversazione con /start." "Se vuoi riassumere il ruolo di 💼 Gestore, riavvia la conversazione con /start."
@ -214,6 +217,9 @@ menu_csv = "📄 .csv"
# Menu: edit admins list # Menu: edit admins list
menu_edit_admins = "🏵 Modifica gestori" menu_edit_admins = "🏵 Modifica gestori"
# Menu: language
menu_language = "🇮🇹 Lingua"
# Emoji: unprocessed order # Emoji: unprocessed order
emoji_not_processed = "*️⃣" emoji_not_processed = "*️⃣"

View file

@ -8,18 +8,12 @@ import sys
import importlib import importlib
import logging import logging
import traceback import traceback
import localization
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
language = config["Config"]["language"]
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)
if config["Error Reporting"]["sentry_token"] != \ if config["Error Reporting"]["sentry_token"] != \
"https://00000000000000000000000000000000:00000000000000000000000000000000@sentry.io/0000000": "https://00000000000000000000000000000000:00000000000000000000000000000000@sentry.io/0000000":
import raven import raven
@ -39,7 +33,9 @@ class Price:
"""The base class for the prices in greed. """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(""" Its int value is in minimum units, while its float and str values are in decimal format.int("""
def __init__(self, value: typing.Union[int, float, str, "Price"] = 0): def __init__(self, value: typing.Union[int, float, str, "Price"], loc: localization.Localization):
# Keep a reference to the localization file
self.loc = loc
if isinstance(value, int): if isinstance(value, int):
# Keep the value as it is # Keep the value as it is
self.value = int(value) self.value = int(value)
@ -57,9 +53,9 @@ class Price:
return f"<Price of value {self.value}>" return f"<Price of value {self.value}>"
def __str__(self): def __str__(self):
return strings.currency_format_string.format(symbol=(config["Payments"]["currency_symbol"] or strings.currency_symbol), return self.loc.get("currency_format_string",
value="{0:.2f}".format( symbol=config["Payments"]["currency_symbol"],
self.value / (10 ** int(config["Payments"]["currency_exp"])))) value="{0:.2f}".format(self.value / (10 ** int(config["Payments"]["currency_exp"]))))
def __int__(self): def __int__(self):
return self.value return self.value
@ -68,48 +64,48 @@ class Price:
return self.value / (10 ** int(config["Payments"]["currency_exp"])) return self.value / (10 ** int(config["Payments"]["currency_exp"]))
def __ge__(self, other): def __ge__(self, other):
return self.value >= Price(other).value return self.value >= Price(other, self.loc).value
def __le__(self, other): def __le__(self, other):
return self.value <= Price(other).value return self.value <= Price(other, self.loc).value
def __eq__(self, other): def __eq__(self, other):
return self.value == Price(other).value return self.value == Price(other, self.loc).value
def __gt__(self, other): def __gt__(self, other):
return self.value > Price(other).value return self.value > Price(other, self.loc).value
def __lt__(self, other): def __lt__(self, other):
return self.value < Price(other).value return self.value < Price(other, self.loc).value
def __add__(self, other): def __add__(self, other):
return Price(self.value + Price(other).value) return Price(self.value + Price(other, self.loc).value, self.loc)
def __sub__(self, other): def __sub__(self, other):
return Price(self.value - Price(other).value) return Price(self.value - Price(other, self.loc).value, self.loc)
def __mul__(self, other): def __mul__(self, other):
return Price(int(self.value * other)) return Price(int(self.value * other), self.loc)
def __floordiv__(self, other): def __floordiv__(self, other):
return Price(int(self.value // other)) return Price(int(self.value // other), self.loc)
def __radd__(self, other): def __radd__(self, other):
return self.__add__(other) return self.__add__(other)
def __rsub__(self, other): def __rsub__(self, other):
return Price(Price(other).value - self.value) return Price(Price(other, self.loc).value - self.value, self.loc)
def __rmul__(self, other): def __rmul__(self, other):
return self.__mul__(other) return self.__mul__(other)
def __iadd__(self, other): def __iadd__(self, other):
self.value += Price(other).value self.value += Price(other, self.loc).value
return self return self
def __isub__(self, other): def __isub__(self, other):
self.value -= Price(other).value self.value -= Price(other, self.loc).value
return self return self
def __imul__(self, other): def __imul__(self, other):
@ -235,7 +231,3 @@ class DuckBot:
return self.bot.send_document(*args, **kwargs) return self.bot.send_document(*args, **kwargs)
# More methods can be added here # More methods can be added here
def boolmoji(boolean: bool):
return strings.emoji_yes if boolean else strings.emoji_no

201
worker.py
View file

@ -100,23 +100,8 @@ class Worker(threading.Thread):
log.info(f"Created new user: {self.user}") log.info(f"Created new user: {self.user}")
if will_be_owner: if will_be_owner:
log.warning(f"User was auto-promoted to Admin as no other admins existed: {self.user}") log.warning(f"User was auto-promoted to Admin as no other admins existed: {self.user}")
# Check if the user's language is enabled; if it isn't, change it to the default # Create the localization object
if self.user.language not in configloader.config["Language"]["enabled_languages"]: self.__create_localization()
log.debug(f"User's language '{self.user.language}' is not enabled, changing it to the default")
self.user.language = configloader.config["Language"]["default_language"]
self.session.commit()
# Create a Localization object
self.loc = localization.Localization(
language=self.user.language,
fallback=configloader.config["Language"]["fallback_language"],
replacements={
"user_string": str(self.user),
"user_mention": self.user.mention(),
"user_full_name": self.user.full_name,
"user_first_name": self.user.first_name,
"today": datetime.datetime.now().strftime("%a %d %b %Y"),
}
)
# Capture exceptions that occour during the conversation # Capture exceptions that occour during the conversation
# noinspection PyBroadException # noinspection PyBroadException
try: try:
@ -359,16 +344,23 @@ class Worker(threading.Thread):
keyboard = [[telegram.KeyboardButton(self.loc.get("menu_order"))], keyboard = [[telegram.KeyboardButton(self.loc.get("menu_order"))],
[telegram.KeyboardButton(self.loc.get("menu_order_status"))], [telegram.KeyboardButton(self.loc.get("menu_order_status"))],
[telegram.KeyboardButton(self.loc.get("menu_add_credit"))], [telegram.KeyboardButton(self.loc.get("menu_add_credit"))],
[telegram.KeyboardButton(self.loc.get("menu_language"))],
[telegram.KeyboardButton(self.loc.get("menu_help")), [telegram.KeyboardButton(self.loc.get("menu_help")),
telegram.KeyboardButton(self.loc.get("menu_bot_info"))]] telegram.KeyboardButton(self.loc.get("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,
self.loc.get("conversation_open_user_menu", credit=utils.Price(self.user.credit)), self.loc.get("conversation_open_user_menu",
credit=utils.Price(self.user.credit, self.loc)),
reply_markup=telegram.ReplyKeyboardMarkup(keyboard, one_time_keyboard=True)) reply_markup=telegram.ReplyKeyboardMarkup(keyboard, one_time_keyboard=True))
# Wait for a reply from the user # Wait for a reply from the user
selection = self.__wait_for_specific_message([self.loc.get("menu_order"), self.loc.get("menu_order_status"), selection = self.__wait_for_specific_message([
self.loc.get("menu_add_credit"), self.loc.get("menu_bot_info"), self.loc.get("menu_order"),
self.loc.get("menu_help")]) self.loc.get("menu_order_status"),
self.loc.get("menu_add_credit"),
self.loc.get("menu_language"),
self.loc.get("menu_help"),
self.loc.get("menu_bot_info"),
])
# After the user reply, update the user data # After the user reply, update the user data
self.update_user() self.update_user()
# If the user has selected the Order option... # If the user has selected the Order option...
@ -383,6 +375,10 @@ class Worker(threading.Thread):
elif selection == self.loc.get("menu_add_credit"): elif selection == self.loc.get("menu_add_credit"):
# Display the add credit menu # Display the add credit menu
self.__add_credit_menu() self.__add_credit_menu()
# If the user has selected the Language option...
elif selection == self.loc.get("menu_language"):
# Display the language menu
self.__language_menu()
# If the user has selected the Bot Info option... # If the user has selected the Bot Info option...
elif selection == self.loc.get("menu_bot_info"): elif selection == self.loc.get("menu_bot_info"):
# Display information about the bot # Display information about the bot
@ -410,24 +406,27 @@ class Worker(threading.Thread):
# Add the product to the cart # Add the product to the cart
cart[message['result']['message_id']] = [product, 0] cart[message['result']['message_id']] = [product, 0]
# Create the inline keyboard to add the product to the cart # Create the inline keyboard to add the product to the cart
inline_keyboard = telegram.InlineKeyboardMarkup([[telegram.InlineKeyboardButton(self.loc.get("menu_add_to_cart"), inline_keyboard = telegram.InlineKeyboardMarkup(
callback_data="cart_add")]]) [[telegram.InlineKeyboardButton(self.loc.get("menu_add_to_cart"), callback_data="cart_add")]]
)
# Edit the sent message and add the inline keyboard # Edit the sent message and add the inline keyboard
if product.image is None: if product.image is None:
self.bot.edit_message_text(chat_id=self.chat.id, self.bot.edit_message_text(chat_id=self.chat.id,
message_id=message['result']['message_id'], message_id=message['result']['message_id'],
text=product.text(), text=product.text(loc=self.loc),
reply_markup=inline_keyboard) reply_markup=inline_keyboard)
else: else:
self.bot.edit_message_caption(chat_id=self.chat.id, self.bot.edit_message_caption(chat_id=self.chat.id,
message_id=message['result']['message_id'], message_id=message['result']['message_id'],
caption=product.text(), caption=product.text(loc=self.loc),
reply_markup=inline_keyboard) reply_markup=inline_keyboard)
# Create the keyboard with the cancel button # Create the keyboard with the cancel button
inline_keyboard = telegram.InlineKeyboardMarkup([[telegram.InlineKeyboardButton(self.loc.get("menu_cancel"), inline_keyboard = telegram.InlineKeyboardMarkup([[telegram.InlineKeyboardButton(self.loc.get("menu_cancel"),
callback_data="cart_cancel")]]) callback_data="cart_cancel")]])
# Send a message containing the button to cancel or pay # Send a message containing the button to cancel or pay
final_msg = self.bot.send_message(self.chat.id, self.loc.get("conversation_cart_actions"), reply_markup=inline_keyboard) final_msg = self.bot.send_message(self.chat.id,
self.loc.get("conversation_cart_actions"),
reply_markup=inline_keyboard)
# Wait for user input # Wait for user input
while True: while True:
callback = self.__wait_for_inlinekeyboard_callback() callback = self.__wait_for_inlinekeyboard_callback()
@ -448,8 +447,10 @@ class Worker(threading.Thread):
# Create the product inline keyboard # Create the product inline keyboard
product_inline_keyboard = telegram.InlineKeyboardMarkup( product_inline_keyboard = telegram.InlineKeyboardMarkup(
[ [
[telegram.InlineKeyboardButton(self.loc.get("menu_add_to_cart"), callback_data="cart_add"), [telegram.InlineKeyboardButton(self.loc.get("menu_add_to_cart"),
telegram.InlineKeyboardButton(self.loc.get("menu_remove_from_cart"), callback_data="cart_remove")] callback_data="cart_add"),
telegram.InlineKeyboardButton(self.loc.get("menu_remove_from_cart"),
callback_data="cart_remove")]
]) ])
# Create the final inline keyboard # Create the final inline keyboard
final_inline_keyboard = telegram.InlineKeyboardMarkup( final_inline_keyboard = telegram.InlineKeyboardMarkup(
@ -461,19 +462,22 @@ class Worker(threading.Thread):
if product.image is None: if product.image is None:
self.bot.edit_message_text(chat_id=self.chat.id, self.bot.edit_message_text(chat_id=self.chat.id,
message_id=callback.message.message_id, message_id=callback.message.message_id,
text=product.text(cart_qty=cart[callback.message.message_id][1]), text=product.text(loc=self.loc,
cart_qty=cart[callback.message.message_id][1]),
reply_markup=product_inline_keyboard) reply_markup=product_inline_keyboard)
else: else:
self.bot.edit_message_caption(chat_id=self.chat.id, self.bot.edit_message_caption(chat_id=self.chat.id,
message_id=callback.message.message_id, message_id=callback.message.message_id,
caption=product.text(cart_qty=cart[callback.message.message_id][1]), caption=product.text(loc=self.loc,
cart_qty=cart[callback.message.message_id][1]),
reply_markup=product_inline_keyboard) reply_markup=product_inline_keyboard)
self.bot.edit_message_text( self.bot.edit_message_text(
chat_id=self.chat.id, chat_id=self.chat.id,
message_id=final_msg.message_id, message_id=final_msg.message_id,
text=self.loc.get("conversation_confirm_cart", product_list=self.__get_cart_summary(cart), text=self.loc.get("conversation_confirm_cart",
total_cost=str(self.__get_cart_value(cart))), product_list=self.__get_cart_summary(cart),
total_cost=str(self.__get_cart_value(cart))),
reply_markup=final_inline_keyboard) reply_markup=final_inline_keyboard)
# If the Remove from cart button has been pressed... # If the Remove from cart button has been pressed...
elif callback.data == "cart_remove": elif callback.data == "cart_remove":
@ -495,7 +499,8 @@ class Worker(threading.Thread):
callback_data="cart_remove")) callback_data="cart_remove"))
product_inline_keyboard = telegram.InlineKeyboardMarkup(product_inline_list) product_inline_keyboard = telegram.InlineKeyboardMarkup(product_inline_list)
# Create the final inline keyboard # Create the final inline keyboard
final_inline_list = [[telegram.InlineKeyboardButton(self.loc.get("menu_cancel"), callback_data="cart_cancel")]] final_inline_list = [[telegram.InlineKeyboardButton(self.loc.get("menu_cancel"),
callback_data="cart_cancel")]]
for product_id in cart: for product_id in cart:
if cart[product_id][1] > 0: if cart[product_id][1] > 0:
final_inline_list.append([telegram.InlineKeyboardButton(self.loc.get("menu_done"), final_inline_list.append([telegram.InlineKeyboardButton(self.loc.get("menu_done"),
@ -505,18 +510,22 @@ class Worker(threading.Thread):
# Edit the product message # Edit the product message
if product.image is None: if product.image is None:
self.bot.edit_message_text(chat_id=self.chat.id, message_id=callback.message.message_id, self.bot.edit_message_text(chat_id=self.chat.id, message_id=callback.message.message_id,
text=product.text(cart_qty=cart[callback.message.message_id][1]), text=product.text(loc=self.loc,
cart_qty=cart[callback.message.message_id][1]),
reply_markup=product_inline_keyboard) reply_markup=product_inline_keyboard)
else: else:
self.bot.edit_message_caption(chat_id=self.chat.id, self.bot.edit_message_caption(chat_id=self.chat.id,
message_id=callback.message.message_id, message_id=callback.message.message_id,
caption=product.text(cart_qty=cart[callback.message.message_id][1]), caption=product.text(loc=self.loc,
cart_qty=cart[callback.message.message_id][1]),
reply_markup=product_inline_keyboard) reply_markup=product_inline_keyboard)
self.bot.edit_message_text( self.bot.edit_message_text(
chat_id=self.chat.id, chat_id=self.chat.id,
message_id=final_msg.message_id, message_id=final_msg.message_id,
text=self.loc.get("conversation_confirm_cart", product_list=self.__get_cart_summary(cart), total_cost=str(self.__get_cart_value(cart))), text=self.loc.get("conversation_confirm_cart",
product_list=self.__get_cart_summary(cart),
total_cost=str(self.__get_cart_value(cart))),
reply_markup=final_inline_keyboard) reply_markup=final_inline_keyboard)
# If the done button has been pressed... # If the done button has been pressed...
elif callback.data == "cart_done": elif callback.data == "cart_done":
@ -551,10 +560,10 @@ class Worker(threading.Thread):
# Suggest payment for missing credit value if configuration allows refill # Suggest payment for missing credit value if configuration allows refill
if configloader.config["Credit Card"]["credit_card_token"] != "" \ if configloader.config["Credit Card"]["credit_card_token"] != "" \
and configloader.config["Appearance"]["refill_on_checkout"] == 'yes' \ and configloader.config["Appearance"]["refill_on_checkout"] == 'yes' \
and utils.Price(int(configloader.config["Credit Card"]["min_amount"])) <= \ and utils.Price(int(configloader.config["Credit Card"]["min_amount"]), self.loc) <= \
credit_required <= \ credit_required <= \
utils.Price(int(configloader.config["Credit Card"]["max_amount"])): utils.Price(int(configloader.config["Credit Card"]["max_amount"]), self.loc):
self.__make_payment(utils.Price(credit_required)) self.__make_payment(utils.Price(credit_required, self.loc))
# If afer requested payment credit is still insufficient (either payment failure or cancel) # If afer requested payment credit is still insufficient (either payment failure or cancel)
if self.user.credit < self.__get_cart_value(cart): if self.user.credit < self.__get_cart_value(cart):
# Rollback all the changes # Rollback all the changes
@ -563,21 +572,21 @@ class Worker(threading.Thread):
# User has credit and valid order, perform transaction now # User has credit and valid order, perform transaction now
self.__order_transaction(order=order, value=-int(self.__get_cart_value(cart))) self.__order_transaction(order=order, value=-int(self.__get_cart_value(cart)))
@staticmethod def __get_cart_value(self, cart):
def __get_cart_value(cart):
# Calculate total items value in cart # Calculate total items value in cart
value = utils.Price(0) value = utils.Price(0, self.loc)
for product in cart: for product in cart:
value += cart[product][0].price * cart[product][1] value += cart[product][0].price * cart[product][1]
return value return value
@staticmethod def __get_cart_summary(self, cart):
def __get_cart_summary(cart):
# Create the cart summary # Create the cart summary
product_list = "" product_list = ""
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(loc=self.loc,
style="short",
cart_qty=cart[product_id][1]) + "\n"
return product_list return product_list
def __order_transaction(self, order, value): def __order_transaction(self, order, value):
@ -597,7 +606,9 @@ class Worker(threading.Thread):
def __order_notify_admins(self, order): def __order_notify_admins(self, order):
# Notify the user of the order result # Notify the user of the order result
self.bot.send_message(self.chat.id, self.loc.get("success_order_created", order=order.get_text(self.session, user=True))) self.bot.send_message(self.chat.id, self.loc.get("success_order_created", order=order.text(loc=self.loc,
session=self.session,
user=True)))
# Notify the admins (in Live Orders mode) of the new order # Notify the admins (in Live Orders mode) of the new order
admins = self.session.query(db.Admin).filter_by(live_mode=True).all() admins = self.session.query(db.Admin).filter_by(live_mode=True).all()
# Create the order keyboard # Create the order keyboard
@ -609,7 +620,8 @@ class Worker(threading.Thread):
# Notify them of the new placed order # Notify them of the new placed order
for admin in admins: for admin in admins:
self.bot.send_message(admin.user_id, self.bot.send_message(admin.user_id,
f"{self.loc.get('notification_order_placed', order=order.get_text(self.session))}", self.loc.get('notification_order_placed',
order=order.text(loc=self.loc, session=self.session)),
reply_markup=order_keyboard) reply_markup=order_keyboard)
def __order_status(self): def __order_status(self):
@ -626,7 +638,7 @@ class Worker(threading.Thread):
self.bot.send_message(self.chat.id, self.loc.get("error_no_orders")) self.bot.send_message(self.chat.id, self.loc.get("error_no_orders"))
# Display the order status to the user # Display the order status to the user
for order in orders: for order in orders:
self.bot.send_message(self.chat.id, order.get_text(self.session, user=True)) self.bot.send_message(self.chat.id, order.text(loc=self.loc, session=self.session, user=True))
# TODO: maybe add a page displayer instead of showing the latest 5 orders # TODO: maybe add a page displayer instead of showing the latest 5 orders
def __add_credit_menu(self): def __add_credit_menu(self):
@ -667,7 +679,7 @@ class Worker(threading.Thread):
log.debug("Displaying __add_credit_cc") log.debug("Displaying __add_credit_cc")
# Create a keyboard to be sent later # Create a keyboard to be sent later
presets = list(map(lambda s: s.strip(" "), configloader.config["Credit Card"]["payment_presets"].split('|'))) presets = list(map(lambda s: s.strip(" "), configloader.config["Credit Card"]["payment_presets"].split('|')))
keyboard = [[telegram.KeyboardButton(str(utils.Price(preset)))] for preset in presets] keyboard = [[telegram.KeyboardButton(str(utils.Price(preset, self.loc)))] for preset in presets]
keyboard.append([telegram.KeyboardButton(self.loc.get("menu_cancel"))]) keyboard.append([telegram.KeyboardButton(self.loc.get("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
@ -684,15 +696,15 @@ class Worker(threading.Thread):
cancelled = True cancelled = True
continue continue
# Convert the amount to an integer # Convert the amount to an integer
value = utils.Price(selection) value = utils.Price(selection, self.loc)
# Ensure the amount is within the range # Ensure the amount is within the range
if value > utils.Price(int(configloader.config["Credit Card"]["max_amount"])): if value > utils.Price(int(configloader.config["Credit Card"]["max_amount"]), self.loc):
self.bot.send_message(self.chat.id, self.bot.send_message(self.chat.id,
self.loc.get("error_payment_amount_over_max", max_amount=utils.Price(configloader.config["Credit Card"]["max_amount"]))) self.loc.get("error_payment_amount_over_max", max_amount=utils.Price(configloader.config["Credit Card"]["max_amount"], self.loc)))
continue continue
elif value < utils.Price(int(configloader.config["Credit Card"]["min_amount"])): elif value < utils.Price(int(configloader.config["Credit Card"]["min_amount"]), self.loc):
self.bot.send_message(self.chat.id, self.bot.send_message(self.chat.id,
self.loc.get("error_payment_amount_under_min", min_amount=utils.Price(configloader.config["Credit Card"]["min_amount"]))) self.loc.get("error_payment_amount_under_min", min_amount=utils.Price(configloader.config["Credit Card"]["min_amount"], self.loc)))
continue continue
break break
# If the user cancelled the action... # If the user cancelled the action...
@ -904,7 +916,7 @@ class Worker(threading.Thread):
if product: if product:
self.bot.send_message(self.chat.id, self.bot.send_message(self.chat.id,
self.loc.get("edit_current_value", self.loc.get("edit_current_value",
value=(str(utils.Price(product.price)) value=(str(utils.Price(product.price, self.loc))
if product.price is not None else 'Non in vendita')), if product.price is not None else 'Non in vendita')),
reply_markup=cancel) reply_markup=cancel)
# Wait for an answer # Wait for an answer
@ -916,7 +928,7 @@ class Worker(threading.Thread):
elif price.lower() == "x": elif price.lower() == "x":
price = None price = None
else: else:
price = utils.Price(price) price = utils.Price(price, self.loc)
# Ask for the product image # Ask for the product image
self.bot.send_message(self.chat.id, self.loc.get("ask_product_image"), reply_markup=cancel) self.bot.send_message(self.chat.id, self.loc.get("ask_product_image"), reply_markup=cancel)
# Wait for an answer # Wait for an answer
@ -1007,7 +1019,7 @@ class Worker(threading.Thread):
# Create a message for every one of them # Create a message for every one of them
for order in orders: for order in orders:
# Send the created message # Send the created message
self.bot.send_message(self.chat.id, order.get_text(session=self.session), self.bot.send_message(self.chat.id, order.text(loc=self.loc, session=self.session),
reply_markup=order_keyboard) reply_markup=order_keyboard)
# Set the Live mode flag to True # Set the Live mode flag to True
self.admin.live_mode = True self.admin.live_mode = True
@ -1036,11 +1048,11 @@ class Worker(threading.Thread):
# Commit the transaction # Commit the transaction
self.session.commit() self.session.commit()
# Update order message # Update order message
self.bot.edit_message_text(order.get_text(session=self.session), chat_id=self.chat.id, self.bot.edit_message_text(order.text(loc=self.loc, session=self.session), chat_id=self.chat.id,
message_id=update.message.message_id) message_id=update.message.message_id)
# Notify the user of the completition # Notify the user of the completition
self.bot.send_message(order.user_id, self.bot.send_message(order.user_id,
self.loc.get("notification_order_completed", order=order.get_text(self.session, user=True))) self.loc.get("notification_order_completed", order=order.text(loc=self.loc, session=self.session, user=True)))
# If the user pressed the refund order button, refund the order... # If the user pressed the refund order button, refund the order...
elif update.data == "order_refund": elif update.data == "order_refund":
# Ask for a refund reason # Ask for a refund reason
@ -1064,13 +1076,14 @@ class Worker(threading.Thread):
# Commit the changes # Commit the changes
self.session.commit() self.session.commit()
# Update the order message # Update the order message
self.bot.edit_message_text(order.get_text(session=self.session), self.bot.edit_message_text(order.text(loc=self.loc, session=self.session),
chat_id=self.chat.id, chat_id=self.chat.id,
message_id=update.message.message_id) message_id=update.message.message_id)
# Notify the user of the refund # Notify the user of the refund
self.bot.send_message(order.user_id, self.bot.send_message(order.user_id,
self.loc.get("notification_order_refunded", order=order.get_text(self.session, self.loc.get("notification_order_refunded", order=order.text(loc=self.loc,
user=True))) session=self.session,
user=True)))
# Notify the admin of the refund # Notify the admin of the refund
self.bot.send_message(self.chat.id, self.loc.get("success_order_refunded", order_id=order.order_id)) self.bot.send_message(self.chat.id, self.loc.get("success_order_refunded", order_id=order.order_id))
@ -1093,7 +1106,7 @@ class Worker(threading.Thread):
if isinstance(reply, CancelSignal): if isinstance(reply, CancelSignal):
return return
# Convert the reply to a price object # Convert the reply to a price object
price = utils.Price(reply) price = utils.Price(reply, self.loc)
# Ask the user for notes # Ask the user for notes
self.bot.send_message(self.chat.id, self.loc.get("ask_transaction_notes"), reply_markup=cancel) self.bot.send_message(self.chat.id, self.loc.get("ask_transaction_notes"), reply_markup=cancel)
# Wait for an answer # Wait for an answer
@ -1279,15 +1292,15 @@ class Worker(threading.Thread):
while True: while True:
# Create the inline keyboard with the admin status # Create the inline keyboard with the admin status
inline_keyboard = telegram.InlineKeyboardMarkup([ inline_keyboard = telegram.InlineKeyboardMarkup([
[telegram.InlineKeyboardButton(f"{utils.boolmoji(admin.edit_products)} {self.loc.get('prop_edit_products')}", [telegram.InlineKeyboardButton(f"{self.loc.boolmoji(admin.edit_products)} {self.loc.get('prop_edit_products')}",
callback_data="toggle_edit_products")], callback_data="toggle_edit_products")],
[telegram.InlineKeyboardButton(f"{utils.boolmoji(admin.receive_orders)} {self.loc.get('prop_receive_orders')}", [telegram.InlineKeyboardButton(f"{self.loc.boolmoji(admin.receive_orders)} {self.loc.get('prop_receive_orders')}",
callback_data="toggle_receive_orders")], callback_data="toggle_receive_orders")],
[telegram.InlineKeyboardButton( [telegram.InlineKeyboardButton(
f"{utils.boolmoji(admin.create_transactions)} {self.loc.get('prop_create_transactions')}", f"{self.loc.boolmoji(admin.create_transactions)} {self.loc.get('prop_create_transactions')}",
callback_data="toggle_create_transactions")], callback_data="toggle_create_transactions")],
[telegram.InlineKeyboardButton( [telegram.InlineKeyboardButton(
f"{utils.boolmoji(admin.display_on_help)} {self.loc.get('prop_display_on_help')}", f"{self.loc.boolmoji(admin.display_on_help)} {self.loc.get('prop_display_on_help')}",
callback_data="toggle_display_on_help")], callback_data="toggle_display_on_help")],
[telegram.InlineKeyboardButton(self.loc.get('menu_done'), callback_data="cmd_done")] [telegram.InlineKeyboardButton(self.loc.get('menu_done'), callback_data="cmd_done")]
]) ])
@ -1310,6 +1323,60 @@ class Worker(threading.Thread):
break break
self.session.commit() self.session.commit()
def __language_menu(self):
"""Select a language."""
log.debug("Displaying __language_menu")
keyboard = []
options: Dict[str, str] = {}
# https://en.wikipedia.org/wiki/List_of_language_names
if "it" in configloader.config["Language"]["enabled_languages"]:
lang = "🇮🇹 Italiano"
keyboard.append([telegram.KeyboardButton(lang)])
options[lang] = "it"
if "en" in configloader.config["Language"]["enabled_languages"]:
lang = "🇬🇧 English"
keyboard.append([telegram.KeyboardButton(lang)])
options[lang] = "en"
if "ru" in configloader.config["Language"]["enabled_languages"]:
lang = "🇷🇺 Русский"
keyboard.append([telegram.KeyboardButton(lang)])
options[lang] = "ru"
if "uk" in configloader.config["Language"]["enabled_languages"]:
lang = "🇺🇦 Українська"
keyboard.append([telegram.KeyboardButton(lang)])
options[lang] = "uk"
# Send the previously created keyboard to the user (ensuring it can be clicked only 1 time)
self.bot.send_message(self.chat.id,
self.loc.get("conversation_language_select"),
reply_markup=telegram.ReplyKeyboardMarkup(keyboard, one_time_keyboard=True))
# Wait for an answer
response = self.__wait_for_specific_message(list(options.keys()))
# Set the language to the corresponding value
self.user.language = options[response]
# Commit the edit to the database
self.session.commit()
# Recreate the localization object
self.__create_localization()
def __create_localization(self):
# Check if the user's language is enabled; if it isn't, change it to the default
if self.user.language not in configloader.config["Language"]["enabled_languages"]:
log.debug(f"User's language '{self.user.language}' is not enabled, changing it to the default")
self.user.language = configloader.config["Language"]["default_language"]
self.session.commit()
# Create a new Localization object
self.loc = localization.Localization(
language=self.user.language,
fallback=configloader.config["Language"]["fallback_language"],
replacements={
"user_string": str(self.user),
"user_mention": self.user.mention(),
"user_full_name": self.user.full_name,
"user_first_name": self.user.first_name,
"today": datetime.datetime.now().strftime("%a %d %b %Y"),
}
)
def __graceful_stop(self, stop_trigger: StopSignal): def __graceful_stop(self, stop_trigger: StopSignal):
"""Handle the graceful stop of the thread.""" """Handle the graceful stop of the thread."""
log.debug("Gracefully stopping the conversation") log.debug("Gracefully stopping the conversation")