1
Fork 0
mirror of https://github.com/Steffo99/greed.git synced 2024-11-21 13:34:18 +00:00

First part of the order products

This commit is contained in:
Steffo 2018-02-15 10:37:51 +01:00
parent b0186edd42
commit 6686543cc7
3 changed files with 138 additions and 81 deletions

View file

@ -80,25 +80,36 @@ class Product(TableDeclarativeBase):
# No __init__ is needed, the default one is sufficient
def __str__(self, one_row=False):
def __str__(self):
return self.text()
def text(self, one_row:bool=False, cart_qty:int=None):
"""Return the product details formatted with Telegram HTML. The image is omitted."""
if one_row:
return f"{escape(self.name)} - {strings.currency_format_string.format(symbol=strings.currency_symbol, value=self.price)}"
return f"{escape(self.name)}\n" \
f"{escape(self.description)}\n\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)}"
return f"<b>{escape(self.name)}</b>\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"{strings.currency_format_string.format(symbol=strings.currency_symbol, value=self.price)}\n" \
f"<b>{strings.in_cart_format_string.format(quantity=cart_qty) if cart_qty is not None else ''}</b>"
def __repr__(self):
return f"<Product {self.name}>"
def send_as_message(self, chat_id: int) -> requests.Response:
def send_as_message(self, chat_id: int) -> dict:
"""Send a message containing the product data."""
r = requests.post(f"https://api.telegram.org/bot{configloader.config['Telegram']['token']}/sendPhoto",
files={"photo": self.image},
params={"chat_id": chat_id,
"caption": str(self)})
return r
if self.image is None:
r = requests.get(f"https://api.telegram.org/bot{configloader.config['Telegram']['token']}/sendMessage",
params={"chat_id": chat_id,
"text": str(self),
"parse_mode": "HTML"})
else:
r = requests.post(f"https://api.telegram.org/bot{configloader.config['Telegram']['token']}/sendPhoto",
files={"photo": self.image},
params={"chat_id": chat_id,
"caption": str(self),
"parse_mode": "HTML"})
return r.json()
def set_image(self, file: telegram.File):
"""Download an image from Telegram and store it in the image column.

View file

@ -11,6 +11,9 @@ currency_format_string = "{symbol} {value}"
# Quantity of a product in stock
in_stock_format_string = "{quantity} disponibili"
# Copies of a product in cart
in_cart_format_string = "{quantity} nel carrello"
# Conversation: the start command was sent and the bot should welcome the user
conversation_after_start = "Ciao!\n" \
"Benvenuto su greed!"
@ -34,6 +37,9 @@ conversation_admin_select_product = "Che prodotto vuoi modificare?"
# Conversation: add extra notes to the order
conversation_extra_notes = "Che messaggio vuoi lasciare insieme al tuo ordine?"
# Conversation: click below to pay for the purchase
conversation_cart_actions = "Quando hai finito di aggiungere prodotti al carrello, clicca uno dei pulsanti qui sotto!"
# Conversation: confirm the cart contents
conversation_confirm_cart = "Il tuo carrello contiene questi prodotti:\n" \
"{product_list}\n" \
@ -87,6 +93,12 @@ menu_done = "✅️ Fatto"
# Menu: pay invoice
menu_pay = "💳 Paga"
# Menu: add to cart
menu_add_to_cart = " Aggiungi"
# Menu: remove from cart
menu_remove_from_cart = " Rimuovi"
# Add product: name?
ask_product_name = "Come si deve chiamare il prodotto?"

174
worker.py
View file

@ -163,7 +163,7 @@ class ChatWorker(threading.Thread):
return update.message.successful_payment
def __wait_for_photo(self, cancellable:bool=False) -> typing.Union[typing.List[telegram.PhotoSize], CancelSignal]:
"""Continue getting updates until a photo is received, then download and return it."""
"""Continue getting updates until a photo is received, then return it."""
while True:
# Get the next update
update = self.__receive_next_update()
@ -180,6 +180,17 @@ class ChatWorker(threading.Thread):
# Return the photo array
return update.message.photo
def __wait_for_inlinekeyboard_callback(self) -> telegram.CallbackQuery:
"""Continue getting updates until an inline keyboard callback is received, then return it."""
while True:
# Get the next update
update = self.__receive_next_update()
# Ensure the update is a CallbackQuery
if update.callback_query is None:
continue
# Return the callbackquery
return update.callback_query
def __user_menu(self):
"""Function called from the run method when the user is not an administrator.
Normal bot actions should be placed here."""
@ -215,75 +226,98 @@ class ChatWorker(threading.Thread):
def __order_menu(self):
"""User menu to order products from the shop."""
raise NotImplementedError()
# # Create a list with the requested items
# order_items = []
# # Get the products list from the db
# products = self.session.query(db.Product).all()
# # TODO: this should be changed
# # Loop exit reason
# exit_reason = None
# # Ask for a list of products to order
# while True:
# # Create a list of product names
# product_names = [product.name for product in products]
# # Add a Cancel button at the end of the keyboard
# product_names.append(strings.menu_cancel)
# # If at least 1 product has been ordered, add a Done button at the start of the keyboard
# if len(order_items) > 0:
# product_names.insert(0, strings.menu_done)
# # Create a keyboard using the product names
# keyboard = [[telegram.KeyboardButton(product_name)] for product_name in product_names]
# # Wait for an answer
# selection = self.__wait_for_specific_message(product_names)
# # If the user selected the Cancel option...
# if selection == strings.menu_cancel:
# exit_reason = "Cancel"
# break
# # If the user selected the Done option...
# elif selection == strings.menu_done:
# exit_reason = "Done"
# break
# # If the user selected a product...
# else:
# # Find the selected product
# product = self.session.query(db.Product).filter_by(name=selection).one()
# # Add the product to the order_items list
# order_items.append(product)
# # Ask for extra notes
# self.bot.send_message(self.chat.id, strings.conversation_extra_notes)
# # Wait for an answer
# notes = self.__wait_for_regex("(.+)")
# # Create the confirmation message and find the total cost
# total_cost = 0
# product_list_string = ""
# for item in order_items:
# # Add to the string and the cost
# product_list_string += f"{str(item)}\n"
# total_cost += item.price
# # Send the confirmation message
# self.bot.send_message(self.chat.id, strings.conversation_confirm_cart.format(product_list=product_list_string, total_cost=strings.currency_format_string.format(symbol=strings.currency_symbol, value=(total_cost / (10 ** int(configloader.config["Payments"]["currency_exp"]))))))
# # TODO: wait for an answer
# # TODO: create a new transaction
# # TODO: test the code
# # TODO: everything
# # Create the order record and add it to the session
# order = db.Order(user=self.user,
# creation_date=datetime.datetime.now(),
# notes=notes)
# self.session.add(order)
# # Commit the session so the order record gets an id
# self.session.commit()
# # Create the orderitems for the selected products
# for item in order_items:
# item_record = db.OrderItem(product=item,
# order_id=order.order_id)
# # Add the created item to the session
# self.session.add(item_record)
# # Commit the session
# self.session.commit()
# # Send a confirmation to the user
# self.bot.send_message(self.chat.id, strings.success_order_created)
# Get the products list from the db
products = self.session.query(db.Product).all()
# Create a dict to be used as 'cart'
# The key is the message id of the product list
cart = {} # type: typing.Dict[typing.List[db.Product, int]]
# Initialize the products list
for product in products:
# If the product is not for sale, don't display it
if product.price is None:
continue
# Send the message without the keyboard to get the message id
message = product.send_as_message(self.chat.id)
# Add the product to the cart
cart[message['result']['message_id']] = [product, 0]
# Create the inline keyboard to add the product to the cart
inline_keyboard = telegram.InlineKeyboardMarkup([[telegram.InlineKeyboardButton(strings.menu_add_to_cart, callback_data="cart_add")]])
# Edit the sent message and add the inline keyboard
if product.image is None:
self.bot.edit_message_text(chat_id=self.chat.id, message_id=message['result']['message_id'], text=str(product), parse_mode="HTML", reply_markup=inline_keyboard)
else:
self.bot.edit_message_caption(chat_id=self.chat.id, message_id=message['result']['message_id'], caption=str(product), parse_mode="HTML", reply_markup=inline_keyboard)
# Create the keyboard with the cancel button
inline_keyboard = telegram.InlineKeyboardMarkup([[telegram.InlineKeyboardButton(strings.menu_cancel, callback_data="cart_cancel")]])
# Send a message containing the button to cancel or pay
final = self.bot.send_message(self.chat.id, strings.conversation_cart_actions, reply_markup=inline_keyboard)
# Wait for user input
while True:
callback = self.__wait_for_inlinekeyboard_callback()
# React to the user input
# If the cancel button has been pressed...
if callback.data == "cart_cancel":
# Stop waiting for user input and go back to the previous menu
return
# If a Add to Cart button has been pressed...
elif callback.data == "cart_add":
# Get the selected product
product = cart[callback.message.message_id][0]
# Add 1 copy to the cart
cart[callback.message.message_id][1] += 1
# Create the product inline keyboard
product_inline_keyboard = telegram.InlineKeyboardMarkup([[telegram.InlineKeyboardButton(strings.menu_add_to_cart, callback_data="cart_add")],
[telegram.InlineKeyboardButton(strings.menu_remove_from_cart, callback_data="cart_remove")]])
# Create the final inline keyboard
final_inline_keyboard = telegram.InlineKeyboardMarkup([[telegram.InlineKeyboardButton(strings.menu_cancel, callback_data="cart_cancel")],
[telegram.InlineKeyboardButton(strings.menu_done, callback_data="cart_done")]])
# Edit both the product and the final message
if product.image is None:
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]), parse_mode="HTML", reply_markup=product_inline_keyboard)
else:
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)
try:
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)
except telegram.error.BadRequest:
# Telegram returns an error if the message is not edited
pass
# If the Remove from cart button has been pressed...
elif callback.data == "cart_remove":
# Get the selected product
product = cart[callback.message.message_id][0]
# Remove 1 copy from the cart
if cart[callback.message.message_id][1] > 0:
cart[callback.message.message_id][1] -= 1
else:
continue
# Create the product inline keyboard
product_inline_list = [[telegram.InlineKeyboardButton(strings.menu_add_to_cart, callback_data="cart_add")]]
if cart[callback.message.message_id][1] > 0:
product_inline_list.append([telegram.InlineKeyboardButton(strings.menu_remove_from_cart, callback_data="cart_remove")])
product_inline_keyboard = telegram.InlineKeyboardMarkup(product_inline_list)
# Create the final inline keyboard
final_inline_list = [[telegram.InlineKeyboardButton(strings.menu_cancel, callback_data="cart_cancel")]]
for product_id in cart:
if cart[product_id][1] > 0:
final_inline_list.append([telegram.InlineKeyboardButton(strings.menu_done, callback_data="cart_done")])
break
final_inline_keyboard = telegram.InlineKeyboardMarkup(final_inline_list)
# Edit both the product and the final message
if product.image is None:
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]), parse_mode="HTML", reply_markup=product_inline_keyboard)
else:
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)
try:
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)
except telegram.error.BadRequest:
# Telegram returns an error if the message is not edited
pass
def __order_status(self):
raise NotImplementedError()