mirror of
https://github.com/Steffo99/greed.git
synced 2024-11-25 07:14:18 +00:00
Add and edit products
This commit is contained in:
parent
b7bdae4678
commit
c97dcaeef4
3 changed files with 111 additions and 26 deletions
29
database.py
29
database.py
|
@ -1,10 +1,11 @@
|
|||
from sqlalchemy import create_engine, Column, ForeignKey, UniqueConstraint, CheckConstraint
|
||||
from sqlalchemy import Integer, BigInteger, String, Numeric, Text
|
||||
from sqlalchemy import Integer, BigInteger, String, Text, LargeBinary
|
||||
from sqlalchemy.orm import sessionmaker, relationship
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
import configloader
|
||||
import telegram
|
||||
import strings
|
||||
import requests
|
||||
from html import escape
|
||||
|
||||
# Create a (lazy) database engine
|
||||
|
@ -67,8 +68,8 @@ class Product(TableDeclarativeBase):
|
|||
description = Column(Text)
|
||||
# Product price, if null product is not for sale
|
||||
price = Column(Integer)
|
||||
# Image filename
|
||||
image = Column(String)
|
||||
# Image data
|
||||
image = Column(LargeBinary)
|
||||
# Stock quantity, if null product has infinite stock
|
||||
stock = Column(Integer)
|
||||
|
||||
|
@ -79,15 +80,29 @@ class Product(TableDeclarativeBase):
|
|||
|
||||
def __str__(self):
|
||||
"""Return the product details formatted with Telegram HTML. The image is omitted."""
|
||||
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" \
|
||||
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)}"
|
||||
|
||||
def __repr__(self):
|
||||
return f"<Product {self.name}>"
|
||||
|
||||
# TODO: add get image (and set image?) method(s)
|
||||
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
|
||||
|
||||
def set_image(self, file: telegram.File):
|
||||
"""Download an image from Telegram and store it in the image column.
|
||||
This is a slow blocking function. Try to avoid calling it directly, use a thread if possible."""
|
||||
# Download the photo through a get request
|
||||
r = requests.get(file.file_path)
|
||||
# Store the photo in the database record
|
||||
self.image = r.content
|
||||
|
||||
|
||||
class Transaction(TableDeclarativeBase):
|
||||
|
|
18
strings.py
18
strings.py
|
@ -75,7 +75,20 @@ ask_product_name = "Come si deve chiamare il prodotto?"
|
|||
ask_product_description = "Quale deve essere la descrizione del prodotto?"
|
||||
|
||||
# Add product: price?
|
||||
ask_product_price = "Quanto deve costare il prodotto?"
|
||||
ask_product_price = "Quanto deve costare il prodotto?\n" \
|
||||
"Scrivi <code>skip</code> se vuoi che il prodotto non sia ancora in vendita."
|
||||
|
||||
# Add product: image?
|
||||
ask_product_image = "Che immagine vuoi che abbia il prodotto?"
|
||||
|
||||
# Thread has started downloading an image and might be unresponsive
|
||||
downloading_image = "Sto scaricando la tua foto!\n" \
|
||||
"Potrei metterci un po'... Abbi pazienza!\n" \
|
||||
"Non sarò in grado di risponderti durante il download."
|
||||
|
||||
# Edit product: current value
|
||||
edit_current_value = "Il valore attuale è:\n" \
|
||||
"<pre>{value}</pre>"
|
||||
|
||||
# Payment: cash payment info
|
||||
payment_cash = "Puoi pagare in contanti alla sede fisica del negozio.\n" \
|
||||
|
@ -105,6 +118,9 @@ bot_info = 'Questo bot utilizza <a href="https://github.com/Steffo99/greed">gree
|
|||
# Success: product has been added to the database
|
||||
success_product_added = "✅ Il prodotto è stato aggiunto con successo!"
|
||||
|
||||
# Success: product has been added to the database
|
||||
success_product_edited = "✅ Il prodotto è stato modificato con successo!"
|
||||
|
||||
# Error: message received not in a private chat
|
||||
error_nonprivate_chat = "⚠️ Questo bot funziona solo in chat private."
|
||||
|
||||
|
|
90
worker.py
90
worker.py
|
@ -8,6 +8,8 @@ import sys
|
|||
import queue as queuem
|
||||
import database as db
|
||||
import re
|
||||
import requests
|
||||
from html import escape
|
||||
|
||||
class StopSignal:
|
||||
"""A data class that should be sent to the worker when the conversation has to be stopped abnormally."""
|
||||
|
@ -144,6 +146,20 @@ class ChatWorker(threading.Thread):
|
|||
# Return the successfulpayment
|
||||
return update.message.successful_payment
|
||||
|
||||
def __wait_for_photo(self) -> typing.List[telegram.PhotoSize]:
|
||||
"""Continue getting updates until a photo is received, then download and return it."""
|
||||
while True:
|
||||
# Get the next update
|
||||
update = self.__receive_next_update()
|
||||
# Ensure the update contains a message
|
||||
if update.message is None:
|
||||
continue
|
||||
# Ensure the message contains a photo
|
||||
if update.message.photo is None:
|
||||
continue
|
||||
# Return the photo array
|
||||
return update.message.photo
|
||||
|
||||
def __user_menu(self):
|
||||
"""Function called from the run method when the user is not an administrator.
|
||||
Normal bot actions should be placed here."""
|
||||
|
@ -339,47 +355,85 @@ class ChatWorker(threading.Thread):
|
|||
# If the user has selected the Add Product option...
|
||||
if selection == strings.menu_add_product:
|
||||
# Open the add product menu
|
||||
self.__add_product_menu()
|
||||
self.__edit_product_menu()
|
||||
# If the user has selected a product
|
||||
else:
|
||||
# Find the selected product
|
||||
product = self.session.query(db.Product).filter_by(name=selection).one()
|
||||
# Open the edit menu for that specific product
|
||||
self.__edit_product_menu(selection)
|
||||
self.__edit_product_menu(product=product)
|
||||
|
||||
def __add_product_menu(self):
|
||||
"""Add a product to the database."""
|
||||
def __edit_product_menu(self, product: typing.Optional[db.Product]=None):
|
||||
"""Add a product to the database or edit an existing one."""
|
||||
# Ask for the product name until a valid product name is specified
|
||||
while True:
|
||||
# Ask the question to the user
|
||||
self.bot.send_message(self.chat.id, strings.ask_product_name)
|
||||
# Display the current name if you're editing an existing product
|
||||
if product:
|
||||
self.bot.send_message(self.chat.id, strings.edit_current_value.format(value=escape(product.name)), parse_mode="HTML")
|
||||
# Wait for an answer
|
||||
name = self.__wait_for_regex(r"(.*)")
|
||||
# Ensure a product with that name doesn't already exist
|
||||
if self.session.query(db.Product).filter_by(name=name).one_or_none() is None:
|
||||
if self.session.query(db.Product).filter_by(name=name).one_or_none() in [None, product]:
|
||||
# Exit the loop
|
||||
break
|
||||
self.bot.send_message(self.chat.id, strings.error_duplicate_name)
|
||||
# Ask for the product description
|
||||
self.bot.send_message(self.chat.id, strings.ask_product_description)
|
||||
# Display the current description if you're editing an existing product
|
||||
if product:
|
||||
self.bot.send_message(self.chat.id, strings.edit_current_value.format(value=escape(product.description)), parse_mode="HTML")
|
||||
# Wait for an answer
|
||||
description = self.__wait_for_regex(r"(.*)")
|
||||
# Ask for the product price
|
||||
self.bot.send_message(self.chat.id, strings.ask_product_price)
|
||||
# Display the current name if you're editing an existing 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")
|
||||
# Wait for an answer
|
||||
price = int(self.__wait_for_regex(r"([0-9]{1,3}(?:[.,][0-9]{1,2})?)").replace(".", "").replace(",", "")) * (
|
||||
10 ** int(configloader.config["Payments"]["currency_exp"]))
|
||||
# TODO: ask for product image
|
||||
# Create the db record for the product
|
||||
product = db.Product(name=name,
|
||||
description=description,
|
||||
price=price)
|
||||
# Add the record to the session, then commit
|
||||
self.session.add(product)
|
||||
price = self.__wait_for_regex(r"([0-9]{1,3}(?:[.,][0-9]{1,2})?|[Ss][Kk][Ii][Pp])")
|
||||
# If the price is skipped
|
||||
if price.lower() == "skip":
|
||||
price = None
|
||||
else:
|
||||
price = int(price.replace(".", "").replace(",", "")) * (10 ** int(configloader.config["Payments"]["currency_exp"]))
|
||||
# Ask for the product image
|
||||
self.bot.send_message(self.chat.id, strings.ask_product_image)
|
||||
# Wait for an answer
|
||||
photo_list = self.__wait_for_photo()
|
||||
# If a new product is being added...
|
||||
if not product:
|
||||
# Create the db record for the product
|
||||
product = db.Product(name=name,
|
||||
description=description,
|
||||
price=price)
|
||||
# Add the record to the database
|
||||
self.session.add(product)
|
||||
# If a product is being edited...
|
||||
else:
|
||||
# Edit the record with the new values
|
||||
product.name = name
|
||||
product.description = description
|
||||
product.price = price
|
||||
# Find the largest photo id
|
||||
largest_photo = photo_list[0]
|
||||
for photo in photo_list[1:]:
|
||||
if photo.width > largest_photo.width:
|
||||
largest_photo = photo
|
||||
# Get the file object associated with the photo
|
||||
photo_file = self.bot.get_file(largest_photo.file_id)
|
||||
# Notify the user that the bot is downloading the image and might be inactive for a while
|
||||
self.bot.send_message(self.chat.id, strings.downloading_image)
|
||||
self.bot.send_chat_action(self.chat.id, action="upload_photo")
|
||||
# Set the image for that product
|
||||
product.set_image(photo_file)
|
||||
self.session.commit()
|
||||
# Notify the user
|
||||
self.bot.send_message(self.chat.id, strings.success_product_added)
|
||||
|
||||
def __edit_product_menu(self, name: str):
|
||||
raise NotImplementedError()
|
||||
if product:
|
||||
self.bot.send_message(self.chat.id, strings.success_product_edited)
|
||||
else:
|
||||
self.bot.send_message(self.chat.id, strings.success_product_added)
|
||||
|
||||
def __orders_menu(self):
|
||||
raise NotImplementedError()
|
||||
|
|
Loading…
Reference in a new issue