mirror of
https://github.com/Steffo99/greed.git
synced 2024-11-25 07:14:18 +00:00
Some new stuff, but now the keyboard disappears?
This commit is contained in:
parent
c3d3cb2c3e
commit
4658cdc31b
4 changed files with 141 additions and 46 deletions
22
database.py
22
database.py
|
@ -7,6 +7,7 @@ import telegram
|
||||||
import strings
|
import strings
|
||||||
import requests
|
import requests
|
||||||
from html import escape
|
from html import escape
|
||||||
|
from utils import Price
|
||||||
|
|
||||||
# Create a (lazy) database engine
|
# Create a (lazy) database engine
|
||||||
engine = create_engine(configloader.config["Database"]["engine"])
|
engine = create_engine(configloader.config["Database"]["engine"])
|
||||||
|
@ -83,15 +84,24 @@ class Product(TableDeclarativeBase):
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.text()
|
return self.text()
|
||||||
|
|
||||||
def text(self, one_row:bool=False, cart_qty:int=None):
|
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 one_row:
|
if style == "short":
|
||||||
return f"{escape(self.name)} - {strings.currency_format_string.format(symbol=strings.currency_symbol, value=self.price / (10 ** int(configloader.config['Payments']['currency_exp'])))}"
|
return f"{cart_qty}x {escape(self.name)} - {str(Price(self.price) * cart_qty)}"
|
||||||
|
elif style == "full":
|
||||||
return f"<b>{escape(self.name)}</b>\n" \
|
return f"<b>{escape(self.name)}</b>\n" \
|
||||||
f"{escape(self.description)}\n" \
|
f"{escape(self.description)}\n" \
|
||||||
f"<i>{strings.in_stock_format_string.format(quantity=self.stock) if self.stock is not None else ''}</i>\n" \
|
f"<i>{strings.in_stock_format_string.format(quantity=self.stock) if self.stock is not None else ''}</i>\n" \
|
||||||
f"{strings.currency_format_string.format(symbol=strings.currency_symbol, value=self.price / (10 ** int(configloader.config['Payments']['currency_exp'])))}\n" \
|
f"{str(Price(self.price))}\n" \
|
||||||
f"<b>{strings.in_cart_format_string.format(quantity=cart_qty) if cart_qty is not None else ''}</b>"
|
f"<b>{strings.in_cart_format_string.format(quantity=cart_qty) if cart_qty is not None else ''}</b>"
|
||||||
|
elif style == "image":
|
||||||
|
return f"{escape(self.name)}\n" \
|
||||||
|
f"{escape(self.description)}\n" \
|
||||||
|
f"{strings.in_stock_format_string.format(quantity=self.stock) if self.stock is not None else ''}\n" \
|
||||||
|
f"{strings.currency_format_string.format(symbol=strings.currency_symbol, value=self.price / (10 ** int(configloader.config['Payments']['currency_exp'])))}\n" \
|
||||||
|
f"{strings.in_cart_format_string.format(quantity=cart_qty) if cart_qty is not None else ''}"
|
||||||
|
else:
|
||||||
|
raise ValueError("style is not an accepted value")
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return f"<Product {self.name}>"
|
return f"<Product {self.name}>"
|
||||||
|
@ -101,13 +111,13 @@ class Product(TableDeclarativeBase):
|
||||||
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": str(self),
|
"text": self.text(),
|
||||||
"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": str(self),
|
"caption": self.text(style="image"),
|
||||||
"parse_mode": "HTML"})
|
"parse_mode": "HTML"})
|
||||||
return r.json()
|
return r.json()
|
||||||
|
|
||||||
|
|
|
@ -42,7 +42,7 @@ conversation_cart_actions = "Quando hai finito di aggiungere prodotti al carrell
|
||||||
|
|
||||||
# Conversation: confirm the cart contents
|
# Conversation: confirm the cart contents
|
||||||
conversation_confirm_cart = "Il tuo carrello contiene questi prodotti:\n" \
|
conversation_confirm_cart = "Il tuo carrello contiene questi prodotti:\n" \
|
||||||
"{product_list}\n" \
|
"{product_list}" \
|
||||||
"\n" \
|
"\n" \
|
||||||
"Totale: {total_cost}\n" \
|
"Totale: {total_cost}\n" \
|
||||||
"Procedere?"
|
"Procedere?"
|
||||||
|
|
80
utils.py
Normal file
80
utils.py
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
from configloader import config
|
||||||
|
from strings import currency_format_string, currency_symbol
|
||||||
|
import typing
|
||||||
|
|
||||||
|
class Price:
|
||||||
|
def __init__(self, value:typing.Union[int, float, str, "Price"]=0):
|
||||||
|
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
|
||||||
|
self.value = int(value.replace(".", "").replace(",", ""))
|
||||||
|
elif isinstance(value, Price):
|
||||||
|
# Copy self
|
||||||
|
self.value = value.value
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return currency_format_string.format(symbol=currency_symbol, value="{0:.2f}".format(self.value / (10 ** int(config["Payments"]["currency_exp"]))))
|
||||||
|
|
||||||
|
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):
|
||||||
|
return Price(self.value * Price(other).value)
|
||||||
|
|
||||||
|
def __floordiv__(self, other):
|
||||||
|
return Price(self.value // Price(other).value)
|
||||||
|
|
||||||
|
def __radd__(self, other):
|
||||||
|
self.__add__(other)
|
||||||
|
|
||||||
|
def __rsub__(self, other):
|
||||||
|
return Price(Price(other).value - self.value)
|
||||||
|
|
||||||
|
def __rmul__(self, other):
|
||||||
|
self.__mul__(other)
|
||||||
|
|
||||||
|
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):
|
||||||
|
self.value *= Price(other).value
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __ifloordiv__(self, other):
|
||||||
|
self.value //= Price(other).value
|
||||||
|
return self
|
||||||
|
|
71
worker.py
71
worker.py
|
@ -1,7 +1,6 @@
|
||||||
import threading
|
import threading
|
||||||
import typing
|
import typing
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
import telegram
|
import telegram
|
||||||
import strings
|
import strings
|
||||||
|
@ -10,6 +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
|
||||||
from html import escape
|
from html import escape
|
||||||
|
|
||||||
class StopSignal:
|
class StopSignal:
|
||||||
|
@ -232,7 +232,7 @@ class ChatWorker(threading.Thread):
|
||||||
products = self.session.query(db.Product).all()
|
products = self.session.query(db.Product).all()
|
||||||
# Create a dict to be used as 'cart'
|
# Create a dict to be used as 'cart'
|
||||||
# The key is the message id of the product list
|
# The key is the message id of the product list
|
||||||
cart = {} # type: typing.Dict[typing.List[db.Product, int]]
|
cart: typing.Dict[typing.List[db.Product, int]] = {}
|
||||||
# Initialize the products list
|
# Initialize the products list
|
||||||
for product in products:
|
for product in products:
|
||||||
# If the product is not for sale, don't display it
|
# If the product is not for sale, don't display it
|
||||||
|
@ -279,13 +279,16 @@ class ChatWorker(threading.Thread):
|
||||||
text=product.text(cart_qty=cart[callback.message.message_id][1]), parse_mode="HTML", reply_markup=product_inline_keyboard)
|
text=product.text(cart_qty=cart[callback.message.message_id][1]), parse_mode="HTML", reply_markup=product_inline_keyboard)
|
||||||
else:
|
else:
|
||||||
self.bot.edit_message_caption(chat_id=self.chat.id, message_id=callback.message.message_id,
|
self.bot.edit_message_caption(chat_id=self.chat.id, message_id=callback.message.message_id,
|
||||||
caption=product.text(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)
|
||||||
try:
|
# Create the cart summary
|
||||||
|
product_list = ""
|
||||||
|
total_cost = Price(0)
|
||||||
|
for product_id in cart:
|
||||||
|
if cart[product_id][1] > 0:
|
||||||
|
product_list += cart[product_id][0].text(style="short", cart_qty=cart[product_id][1]) + "\n"
|
||||||
|
total_cost += cart[product_id][0].price * cart[product_id][1]
|
||||||
self.bot.edit_message_text(chat_id=self.chat.id, message_id=final.message_id,
|
self.bot.edit_message_text(chat_id=self.chat.id, message_id=final.message_id,
|
||||||
text=strings.conversation_cart_actions, reply_markup=final_inline_keyboard)
|
text=strings.conversation_confirm_cart.format(product_list=product_list, total_cost=str(total_cost), reply_markup=final_inline_keyboard))
|
||||||
except telegram.error.BadRequest:
|
|
||||||
# Telegram returns an error if the message is not edited
|
|
||||||
pass
|
|
||||||
# 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":
|
||||||
# Get the selected product
|
# Get the selected product
|
||||||
|
@ -313,13 +316,18 @@ class ChatWorker(threading.Thread):
|
||||||
text=product.text(cart_qty=cart[callback.message.message_id][1]), parse_mode="HTML", reply_markup=product_inline_keyboard)
|
text=product.text(cart_qty=cart[callback.message.message_id][1]), parse_mode="HTML", reply_markup=product_inline_keyboard)
|
||||||
else:
|
else:
|
||||||
self.bot.edit_message_caption(chat_id=self.chat.id, message_id=callback.message.message_id,
|
self.bot.edit_message_caption(chat_id=self.chat.id, message_id=callback.message.message_id,
|
||||||
caption=product.text(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)
|
||||||
try:
|
# Create the cart summary
|
||||||
|
product_list = ""
|
||||||
|
total_cost = Price(0)
|
||||||
|
for product_id in cart:
|
||||||
|
if cart[product_id][1] > 0:
|
||||||
|
product_list += cart[product_id][0].text(style="short", cart_qty=cart[product_id][1]) + "\n"
|
||||||
|
total_cost += cart[product_id][0].price * cart[product_id][1]
|
||||||
self.bot.edit_message_text(chat_id=self.chat.id, message_id=final.message_id,
|
self.bot.edit_message_text(chat_id=self.chat.id, message_id=final.message_id,
|
||||||
text=strings.conversation_cart_actions, reply_markup=final_inline_keyboard)
|
text=strings.conversation_confirm_cart.format(product_list=product_list,
|
||||||
except telegram.error.BadRequest:
|
total_cost=str(total_cost),
|
||||||
# Telegram returns an error if the message is not edited
|
reply_markup=final_inline_keyboard))
|
||||||
pass
|
|
||||||
# If the done button has been pressed...
|
# If the done button has been pressed...
|
||||||
elif callback.data == "cart_done":
|
elif callback.data == "cart_done":
|
||||||
# End the loop
|
# End the loop
|
||||||
|
@ -364,7 +372,6 @@ class ChatWorker(threading.Thread):
|
||||||
# Notify the user of the order result
|
# Notify the user of the order result
|
||||||
self.bot.send_message(self.chat.id, strings.success_order_created)
|
self.bot.send_message(self.chat.id, strings.success_order_created)
|
||||||
|
|
||||||
|
|
||||||
def __order_status(self):
|
def __order_status(self):
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@ -406,10 +413,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(strings.currency_format_string.format(symbol=strings.currency_symbol, value="10"))],
|
keyboard = [[telegram.KeyboardButton(str(Price("10.00")))],
|
||||||
[telegram.KeyboardButton(strings.currency_format_string.format(symbol=strings.currency_symbol, value="25"))],
|
[telegram.KeyboardButton(str(Price("25.00")))],
|
||||||
[telegram.KeyboardButton(strings.currency_format_string.format(symbol=strings.currency_symbol, value="50"))],
|
[telegram.KeyboardButton(str(Price("50.00")))],
|
||||||
[telegram.KeyboardButton(strings.currency_format_string.format(symbol=strings.currency_symbol, value="100"))],
|
[telegram.KeyboardButton(str(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
|
||||||
|
@ -427,13 +434,13 @@ class ChatWorker(threading.Thread):
|
||||||
cancelled = True
|
cancelled = True
|
||||||
continue
|
continue
|
||||||
# Convert the amount to an integer
|
# Convert the amount to an integer
|
||||||
value = int(float(selection.replace(",", ".")) * (10 ** int(configloader.config["Payments"]["currency_exp"])))
|
value = Price(selection)
|
||||||
# Ensure the amount is within the range
|
# Ensure the amount is within the range
|
||||||
if value > int(configloader.config["Payments"]["max_amount"]):
|
if value > int(configloader.config["Payments"]["max_amount"]):
|
||||||
self.bot.send_message(self.chat.id, strings.error_payment_amount_over_max.format(max_amount=strings.currency_format_string.format(symbol=strings.currency_symbol, value=configloader.config["Payments"]["max_amount"])))
|
self.bot.send_message(self.chat.id, strings.error_payment_amount_over_max.format(max_amount=Price(configloader.config["Payments"]["max_amount"])))
|
||||||
continue
|
continue
|
||||||
elif value < int(configloader.config["Payments"]["min_amount"]):
|
elif value < int(configloader.config["Payments"]["min_amount"]):
|
||||||
self.bot.send_message(self.chat.id, strings.error_payment_amount_under_min.format(min_amount=strings.currency_format_string.format(symbol=strings.currency_symbol, value=configloader.config["Payments"]["min_amount"])))
|
self.bot.send_message(self.chat.id, strings.error_payment_amount_under_min.format(min_amount=Price(configloader.config["Payments"]["min_amount"])))
|
||||||
continue
|
continue
|
||||||
break
|
break
|
||||||
# If the user cancelled the action...
|
# If the user cancelled the action...
|
||||||
|
@ -443,11 +450,11 @@ class ChatWorker(threading.Thread):
|
||||||
# Set the invoice active invoice payload
|
# Set the invoice active invoice payload
|
||||||
self.invoice_payload = str(uuid.uuid4())
|
self.invoice_payload = str(uuid.uuid4())
|
||||||
# Create the price array
|
# Create the price array
|
||||||
prices = [telegram.LabeledPrice(label=strings.payment_invoice_label, amount=value)]
|
prices = [telegram.LabeledPrice(label=strings.payment_invoice_label, amount=int(value))]
|
||||||
# If the user has to pay a fee when using the credit card, add it to the prices list
|
# If the user has to pay a fee when using the credit card, add it to the prices list
|
||||||
fee_percentage = float(configloader.config["Credit Card"]["fee_percentage"]) / 100
|
fee_percentage = Price(configloader.config["Credit Card"]["fee_percentage"]) // 100
|
||||||
fee_fixed = int(configloader.config["Credit Card"]["fee_fixed"])
|
fee_fixed = Price(configloader.config["Credit Card"]["fee_fixed"])
|
||||||
total_fee = int(value * fee_percentage) + fee_fixed
|
total_fee = value * fee_percentage + fee_fixed
|
||||||
if total_fee > 0:
|
if total_fee > 0:
|
||||||
prices.append(telegram.LabeledPrice(label=strings.payment_invoice_fee_label, amount=int(total_fee)))
|
prices.append(telegram.LabeledPrice(label=strings.payment_invoice_fee_label, amount=int(total_fee)))
|
||||||
else:
|
else:
|
||||||
|
@ -459,7 +466,7 @@ class ChatWorker(threading.Thread):
|
||||||
# The amount is valid, send the invoice
|
# The amount is valid, send the invoice
|
||||||
self.bot.send_invoice(self.chat.id,
|
self.bot.send_invoice(self.chat.id,
|
||||||
title=strings.payment_invoice_title,
|
title=strings.payment_invoice_title,
|
||||||
description=strings.payment_invoice_description.format(amount=strings.currency_format_string.format(symbol=strings.currency_symbol, value=value / (10 ** int(configloader.config["Payments"]["currency_exp"])))),
|
description=strings.payment_invoice_description.format(amount=str(value)),
|
||||||
payload=self.invoice_payload,
|
payload=self.invoice_payload,
|
||||||
provider_token=configloader.config["Credit Card"]["credit_card_token"],
|
provider_token=configloader.config["Credit Card"]["credit_card_token"],
|
||||||
start_parameter="tempdeeplink", # TODO: no idea on how deeplinks should work
|
start_parameter="tempdeeplink", # TODO: no idea on how deeplinks should work
|
||||||
|
@ -490,7 +497,7 @@ class ChatWorker(threading.Thread):
|
||||||
transaction.payment_email = successfulpayment.order_info.email
|
transaction.payment_email = successfulpayment.order_info.email
|
||||||
transaction.payment_phone = successfulpayment.order_info.phone_number
|
transaction.payment_phone = successfulpayment.order_info.phone_number
|
||||||
# Add the credit to the user account
|
# Add the credit to the user account
|
||||||
self.user.credit += successfulpayment.total_amount - total_fee
|
self.user.credit += successfulpayment.total_amount - int(total_fee)
|
||||||
# Add and commit the transaction
|
# Add and commit the transaction
|
||||||
self.session.add(transaction)
|
self.session.add(transaction)
|
||||||
self.session.commit()
|
self.session.commit()
|
||||||
|
@ -587,7 +594,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=(strings.currency_format_string.format(symbol=strings.currency_symbol, value=(product.price / (10 ** int(configloader.config["Payments"]["currency_exp"]))))) 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(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
|
||||||
|
@ -596,19 +603,17 @@ class ChatWorker(threading.Thread):
|
||||||
elif price.lower() == "x":
|
elif price.lower() == "x":
|
||||||
price = None
|
price = None
|
||||||
else:
|
else:
|
||||||
price = int(float(price.replace(",", ".")) * (10 ** int(configloader.config["Payments"]["currency_exp"])))
|
price = 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
|
||||||
photo_list = self.__wait_for_photo(cancellable=True)
|
photo_list = self.__wait_for_photo(cancellable=True)
|
||||||
# TODO: ask for boolean status
|
|
||||||
# If a new product is being added...
|
# If a new product is being added...
|
||||||
if not product:
|
if not product:
|
||||||
# Create the db record for the product
|
# Create the db record for the product
|
||||||
# TODO: add the boolean status
|
|
||||||
product = db.Product(name=name,
|
product = db.Product(name=name,
|
||||||
description=description,
|
description=description,
|
||||||
price=price,
|
price=int(price),
|
||||||
boolean_product=False)
|
boolean_product=False)
|
||||||
# Add the record to the database
|
# Add the record to the database
|
||||||
self.session.add(product)
|
self.session.add(product)
|
||||||
|
|
Loading…
Reference in a new issue