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

Add and edit products

This commit is contained in:
Steffo 2018-02-01 10:06:40 +01:00
parent b7bdae4678
commit c97dcaeef4
WARNING! Although there is a key with this ID in the database it does not verify this commit! This commit is SUSPICIOUS.
GPG key ID: C27544372FBB445D
3 changed files with 111 additions and 26 deletions

View file

@ -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):

View file

@ -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."

View file

@ -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()