mirror of
https://github.com/Steffo99/greed.git
synced 2024-11-21 21:44:19 +00:00
Merge branch 'master' into logging
This commit is contained in:
commit
8d3b492822
8 changed files with 1016 additions and 87 deletions
24
README.md
24
README.md
|
@ -1,6 +1,6 @@
|
|||
# greed
|
||||
|
||||
A customizable Telegram shop bot, developed as a project for the final exam.
|
||||
A [customizable](/config/template_config.ini), [multilanguage](/strings) Telegram shop bot with [Telegram Payments support](https://core.telegram.org/bots/payments)!
|
||||
|
||||
![](https://img.shields.io/badge/version-beta-blue.svg) ![](https://img.shields.io/badge/maintenance-passively--maintained-yellowgreen)
|
||||
|
||||
|
@ -15,7 +15,7 @@ A customizable Telegram shop bot, developed as a project for the final exam.
|
|||
|
||||
## Installation
|
||||
|
||||
1. Download the project files through `git clone https://github.com/Steffo99/greed.git` or [this link](https://github.com/Steffo99/greed/archive/master.zip).
|
||||
1. Download the project files through `git clone https://github.com/Steffo99/greed.git` (recommended) or [this link](https://github.com/Steffo99/greed/archive/master.zip).
|
||||
2. Install the project requirements with `pip install -r requirements.txt`
|
||||
3. _Optional: run `pip install coloredlogs` to have colored logging output._
|
||||
3. Run `python -OO core.py` to generate the configuration file.
|
||||
|
@ -32,10 +32,30 @@ All the bot features are available through Telegram.
|
|||
As the administrator, you can add new products, check the placed orders, create new transactions and generate .csv log files.
|
||||
Users will be able to add credit to their wallet, place orders and contact you in case they require assistance.
|
||||
|
||||
## Updating
|
||||
|
||||
### Through `git`
|
||||
|
||||
If you downloaded `greed` through `git`, you can update it by running:
|
||||
|
||||
```
|
||||
git stash
|
||||
git pull
|
||||
git stash pop
|
||||
```
|
||||
|
||||
### By redownloading the zip file
|
||||
|
||||
If you downloaded `greed` through the zip archive, you can update it by redownloading [the latest version](https://github.com/Steffo99/greed/archive/master.zip) and by moving your `config.ini` and `database.sqlite` (if applicable) files to the new folder.
|
||||
|
||||
## Documentation
|
||||
|
||||
`greed` currently does not have a documentation page, but you can try to read the [paper](https://docs.google.com/document/d/1f4MKVr0B7RSQfWTSa_6ZO0LM4nPpky_GX_qdls3EHtQ/edit?usp=sharing) (in Italian) I wrote for my final Scuola Superiore exam about it.
|
||||
|
||||
## Help!
|
||||
|
||||
If you find a bug, have an idea for a new feature or just require help with `greed`, please [post an issue](https://github.com/Steffo99/greed/issues/new) on GitHub, or, if GitHub is blocked in your country, join [our Telegram group](https://t.me/greed_project) and send a message there.
|
||||
|
||||
## Forks
|
||||
|
||||
> Please note that @Steffo99, the developer of `greed`, does not endorse any of these forks.
|
||||
|
|
|
@ -5,18 +5,20 @@
|
|||
# Config file parameters
|
||||
[Config]
|
||||
; Config file version. DO NOT EDIT THIS!
|
||||
version = 15
|
||||
version = 17
|
||||
; Set this to no when you are done editing the file
|
||||
is_template = yes
|
||||
; Language code for string file
|
||||
; Available languages:
|
||||
; it_IT - Italian, by Steffo
|
||||
; en_US - English, by https://github.com/DarrenWestwood (incomplete, please improve it!)
|
||||
; ua_UK - Ukrainian, by https://github.com/pzhuk
|
||||
; ru_RU - Russian, by https://github.com/pzhuk
|
||||
language = it_IT
|
||||
|
||||
# Telegram bot parameters
|
||||
[Telegram]
|
||||
; Your bot token goes here. Get one from @BotFather!
|
||||
; Your bot token goes here. Get one from https://t.me/BotFather!
|
||||
token = 123456789:YOUR_TOKEN_GOES_HERE_______________
|
||||
; Time in seconds before a conversation (thread) with no new messages expires
|
||||
; A lower value reduces memory usage, but can be inconvenient for the users
|
||||
|
@ -41,15 +43,23 @@ currency = EUR
|
|||
; Currency exp parameter. You can find that on https://core.telegram.org/bots/payments/currencies.json.
|
||||
; It has a value of 2 in most currencies (EUR, USD, GBP...)
|
||||
currency_exp = 2
|
||||
; Currency symbol which is show to the client users when displaying prices and transaction values
|
||||
; If not defined here, default language specific currency symbol from strings would be used
|
||||
currency_symbol = "€"
|
||||
|
||||
# Credit card payment settings
|
||||
[Credit Card]
|
||||
; Provider token: get the token by linking the payment provider with @BotFather
|
||||
; Telegram Payments provider token obtainable at https://t.me/BotFather in the bot's Payments menu
|
||||
; If empty, credit card payments are disabled.
|
||||
# credit_card_token =
|
||||
credit_card_token = 123456789:YOUR_TOKEN_HERE_
|
||||
; Minimum wallet payment accepted (in miniumum currency units, $1.00 = 100 units)
|
||||
min_amount = 1000
|
||||
; Maximum wallet payment accepted (in miniumum currency units, $1.00 = 100 units)
|
||||
max_amount = 10000
|
||||
; The preset selections that can be made when adding credit to the wallet with a credit card
|
||||
; Presets are pipe-separated |, and should never be outside the bounds provided by the min_amount and max_amount options
|
||||
payment_presets = 10.00 | 25.00 | 50.00 | 100.00
|
||||
; Make the user pay a extra fee when adding credit to the wallet with a credit card
|
||||
; The formula for determining the total cost is:
|
||||
; cost = added_funds + added_funds * fee_percentage / 100 + fee_fixed
|
||||
|
@ -69,6 +79,8 @@ phone_required = yes
|
|||
; Display the full order information to the customers instead of the shortened version
|
||||
; The full order information includes the order number and the timestamp of the order placement
|
||||
full_order_info = no
|
||||
; Allow balance refill during the order checkout in case of unsufficient balance
|
||||
refill_on_checkout = yes
|
||||
|
||||
# Exception reporting settings
|
||||
[Error Reporting]
|
||||
|
|
|
@ -12,11 +12,12 @@ log = logging.getLogger(__name__)
|
|||
if not os.path.isfile("config/config.ini"):
|
||||
log.debug("Creating config.ini from template_config.ini")
|
||||
# Open the template file and create the config file
|
||||
with open("config/template_config.ini") as template_file, open("config/config.ini", "w") as config_file:
|
||||
with open("config/template_config.ini", encoding="utf8") as template_file, \
|
||||
open("config/config.ini", "w", encoding="utf8") as config_file:
|
||||
# Copy the template file to the config file
|
||||
config_file.write(template_file.read())
|
||||
|
||||
with open("config/template_config.ini") as template_file:
|
||||
with open("config/template_config.ini", encoding="utf8") as template_file:
|
||||
# Find the template version number
|
||||
config = configparser.ConfigParser()
|
||||
config.read_file(template_file)
|
||||
|
@ -24,7 +25,7 @@ with open("config/template_config.ini") as template_file:
|
|||
log.debug(f"Template is version {template_version}")
|
||||
|
||||
# Overwrite the template config with the values in the config
|
||||
with open("config/config.ini") as config_file:
|
||||
with open("config/config.ini", encoding="utf8") as config_file:
|
||||
config.read_file(config_file)
|
||||
config_version = int(config["Config"]["version"])
|
||||
log.debug(f"Config is version {template_version}")
|
||||
|
@ -44,7 +45,7 @@ if template_version > config_version:
|
|||
# Update the config version
|
||||
config["Config"]["version"] = str(template_version)
|
||||
# Save the file
|
||||
with open("config/config.ini", "w") as config_file:
|
||||
with open("config/config.ini", "w", encoding="utf8") as config_file:
|
||||
log.debug("Writing merged config file...")
|
||||
config.write(config_file)
|
||||
# Notify the user and quit
|
||||
|
|
2
core.py
2
core.py
|
@ -76,7 +76,7 @@ def main():
|
|||
# Skip the update
|
||||
continue
|
||||
# If the message is a start command...
|
||||
if isinstance(update.message.text, str) and update.message.text == "/start":
|
||||
if isinstance(update.message.text, str) and update.message.text.startswith("/start"):
|
||||
log.info(f"Received /start from: {update.message.chat.id}")
|
||||
# Check if a worker already exists for that chat
|
||||
old_worker = chat_workers.get(update.message.chat.id)
|
||||
|
|
417
strings/ru_RU.py
Normal file
417
strings/ru_RU.py
Normal file
|
@ -0,0 +1,417 @@
|
|||
# Strings / localization file for greed
|
||||
# Can be edited, but DON'T REMOVE THE REPLACEMENT FIELDS (words surrounded by {curly braces})
|
||||
|
||||
# Part of the translation by https://github.com/pzhuk
|
||||
|
||||
# Currency symbol
|
||||
currency_symbol = "₽"
|
||||
|
||||
# Positioning of the currency symbol
|
||||
currency_format_string = "{value} {symbol}"
|
||||
|
||||
# Quantity of a product in stock
|
||||
in_stock_format_string = "{quantity} доступно"
|
||||
|
||||
# Copies of a product in cart
|
||||
in_cart_format_string = "{quantity} в корзине"
|
||||
|
||||
# Product information
|
||||
product_format_string = "<b>{name}</b>\n" \
|
||||
"{description}\n" \
|
||||
"{price}\n" \
|
||||
"<b>{cart}</b>"
|
||||
|
||||
# Order number, displayed in the order info
|
||||
order_number = "Заказ #{id}"
|
||||
|
||||
# Order info string, shown to the admins
|
||||
order_format_string = "Покупатель {user}\n" \
|
||||
"Создано {date}\n" \
|
||||
"\n" \
|
||||
"{items}\n" \
|
||||
"ИТОГО: <b>{value}</b>\n" \
|
||||
"\n" \
|
||||
"Сообщение: {notes}\n"
|
||||
|
||||
# Order info string, shown to the user
|
||||
user_order_format_string = "{status_emoji} <b>Заказ {status_text}</b>\n" \
|
||||
"{items}\n" \
|
||||
"Итого: <b>{value}</b>\n" \
|
||||
"\n" \
|
||||
"Сообщение: {notes}\n"
|
||||
|
||||
# Transaction page is loading
|
||||
loading_transactions = "<i>Загружаю транзакции...\n" \
|
||||
"Это займет несколько секунд.</i>"
|
||||
|
||||
# Transactions page
|
||||
transactions_page = "Страница <b>{page}</b>:\n" \
|
||||
"\n" \
|
||||
"{transactions}"
|
||||
|
||||
# transactions.csv caption
|
||||
csv_caption = "Файл 📄 .csv сгенерирован, и содержит все транзакции из базы данных бота.\n" \
|
||||
"Вы можете открыть этот файт с помощью LibreOffice Calc, чтобы просмотреть детали."
|
||||
|
||||
# Conversation: the start command was sent and the bot should welcome the user
|
||||
conversation_after_start = "Привет!\n" \
|
||||
"Добро пожаловать в greed!\n" \
|
||||
"Это 🅱️ <b>Бета</b> версия программы.\n" \
|
||||
"Программа полностью готова к использованию, но могут быть баги.\n" \
|
||||
"Если нашли баг - сообщите тут: https://github.com/Steffo99/greed/issues."
|
||||
|
||||
# Conversation: to send an inline keyboard you need to send a message with it
|
||||
conversation_open_user_menu = "Что бы Вы хотели сделать?\n" \
|
||||
"💰 У вас в кошельке <b>{credit}</b>.\n" \
|
||||
"\n" \
|
||||
"<i>Выберите опцию из вариантов на клавиатуре.\n" \
|
||||
"Если клавиатуры не видно - её можно активировать кнопкой с квардатами внизу</i>."
|
||||
|
||||
# Conversation: like above, but for administrators
|
||||
conversation_open_admin_menu = "Вы 💼 <b>Менеджер</b> этого магазина!\n" \
|
||||
"Что бы Вы хотели сделать?\n" \
|
||||
"\n" \
|
||||
"<i>Выберите опцию из вариантов на клавиатуре.\n" \
|
||||
"Если клавиатуры не видно - её можно активировать кнопкой с квардатами внизу</i>."
|
||||
|
||||
# Conversation: select a payment method
|
||||
conversation_payment_method = "Как бы Вы хотели пополнить ваш кошелек?"
|
||||
|
||||
# Conversation: select a product to edit
|
||||
conversation_admin_select_product = "✏️ Какой продукт необходимо отредактировать?"
|
||||
|
||||
# Conversation: select a product to delete
|
||||
conversation_admin_select_product_to_delete = "❌ Какой продукт необходимо удалить?"
|
||||
|
||||
# Conversation: select a user to edit
|
||||
conversation_admin_select_user = "Выберите пользователя для редактирования."
|
||||
|
||||
# Conversation: click below to pay for the purchase
|
||||
conversation_cart_actions = "<i>Добавьте продукты в корзину с помощью кнопки Добавить." \
|
||||
" Когда сделаете Ваш выбор, возвращайтесь к этому сообщению" \
|
||||
" и нажмите кнопку Готово.</i>"
|
||||
|
||||
# Conversation: confirm the cart contents
|
||||
conversation_confirm_cart = "🛒 Продукты у Вас в корзине:\n" \
|
||||
"{product_list}" \
|
||||
"Итого: <b>{total_cost}</b>\n" \
|
||||
"\n" \
|
||||
"<i>Нажмите Готово, чтобы продолжить.\n" \
|
||||
"Если передумали - выберите Отмена.</i>"
|
||||
|
||||
# Conversation: the user activated the live orders mode
|
||||
conversation_live_orders_start = "Вы в режиме <b>Новые заказы</b>\n" \
|
||||
"Все новые заказы появятся в этом чате в режиме реального времени," \
|
||||
" и их можно отметить ✅ Выполнено" \
|
||||
" или ✴️ Возвращено в случае возврата денег.\n" \
|
||||
"\n" \
|
||||
"<i>Нажмите Стоп в этом чате, чтобы остановить этот режим.</i>"
|
||||
|
||||
# Conversation: help menu has been opened
|
||||
conversation_open_help_menu = "Чем могу Вам помочь?"
|
||||
|
||||
# Conversation: confirm promotion to admin
|
||||
conversation_confirm_admin_promotion = "Вы уверены, что хотите повысить этого пользователя до 💼 Менеджера?\n" \
|
||||
"Это действие невозможно отменить!"
|
||||
|
||||
# Conversation: switching to user mode
|
||||
conversation_switch_to_user_mode = " Вы перешли в режим 👤 Покупателя.\n" \
|
||||
"Если хотите вернутся в режим 💼 Менеджера, рестартуйте с помощью команды /start."
|
||||
|
||||
# Notification: the conversation has expired
|
||||
conversation_expired = "🕐 За долгое время я не получил ни одного сообщения, поэтому я прекратил общение" \
|
||||
" чтобы сохранить ресурсы.\n" \
|
||||
"Чтобы начать снова, пришлите команду /start ."
|
||||
|
||||
# User menu: order
|
||||
menu_order = "🛒 Заказать"
|
||||
|
||||
# User menu: order status
|
||||
menu_order_status = "🛍 Мои заказы"
|
||||
|
||||
# User menu: add credit
|
||||
menu_add_credit = "💵 Пополнить кошелек"
|
||||
|
||||
# User menu: bot info
|
||||
menu_bot_info = "ℹ️ Информация о боте"
|
||||
|
||||
# User menu: cash
|
||||
menu_cash = "💵 Наличными"
|
||||
|
||||
# User menu: credit card
|
||||
menu_credit_card = "💳 Кредитной картой"
|
||||
|
||||
# Admin menu: products
|
||||
menu_products = "📝️ Продукты"
|
||||
|
||||
# Admin menu: orders
|
||||
menu_orders = "📦 Заказы"
|
||||
|
||||
# Menu: transactions
|
||||
menu_transactions = "💳 Список транзакций"
|
||||
|
||||
# Menu: edit credit
|
||||
menu_edit_credit = "💰 Создать транзакцию"
|
||||
|
||||
# Admin menu: go to user mode
|
||||
menu_user_mode = "👤 Режим покупателя"
|
||||
|
||||
# Admin menu: add product
|
||||
menu_add_product = "✨ Новый продукт"
|
||||
|
||||
# Admin menu: delete product
|
||||
menu_delete_product = "❌ Удалить продукт"
|
||||
|
||||
# Menu: cancel
|
||||
menu_cancel = "🔙 Отмена"
|
||||
|
||||
# Menu: skip
|
||||
menu_skip = "⏭ Пропустить"
|
||||
|
||||
# Menu: done
|
||||
menu_done = "✅️ Готово"
|
||||
|
||||
# Menu: pay invoice
|
||||
menu_pay = "💳 Заплатить"
|
||||
|
||||
# Menu: complete
|
||||
menu_complete = "✅ Готово"
|
||||
|
||||
# Menu: refund
|
||||
menu_refund = "✴️ Возврат средств"
|
||||
|
||||
# Menu: stop
|
||||
menu_stop = "🛑 Стоп"
|
||||
|
||||
# Menu: add to cart
|
||||
menu_add_to_cart = "➕ Добавить"
|
||||
|
||||
# Menu: remove from cart
|
||||
menu_remove_from_cart = "➖ Удалить"
|
||||
|
||||
# Menu: help menu
|
||||
menu_help = "❓ Помощь"
|
||||
|
||||
# Menu: guide
|
||||
menu_guide = "📖 Инструкция"
|
||||
|
||||
# Menu: next page
|
||||
menu_next = "▶️ Следующая"
|
||||
|
||||
# Menu: previous page
|
||||
menu_previous = "◀️ Предыдущая"
|
||||
|
||||
# Menu: contact the shopkeeper
|
||||
menu_contact_shopkeeper = "👨💼 Контакты"
|
||||
|
||||
# Menu: generate transactions .csv file
|
||||
menu_csv = "📄 .csv"
|
||||
|
||||
# Menu: edit admins list
|
||||
menu_edit_admins = "🏵 Изменить менеджеров"
|
||||
|
||||
# Emoji: unprocessed order
|
||||
emoji_not_processed = "*️⃣"
|
||||
|
||||
# Emoji: completed order
|
||||
emoji_completed = "✅"
|
||||
|
||||
# Emoji: refunded order
|
||||
emoji_refunded = "✴️"
|
||||
|
||||
# Emoji: yes
|
||||
emoji_yes = "✅"
|
||||
|
||||
# Emoji: no
|
||||
emoji_no = "🚫"
|
||||
|
||||
# Text: unprocessed order
|
||||
text_not_processed = "ожидает"
|
||||
|
||||
# Text: completed order
|
||||
text_completed = "выполнен"
|
||||
|
||||
# Text: refunded order
|
||||
text_refunded = "возмещен"
|
||||
|
||||
# Add product: name?
|
||||
ask_product_name = "Как назовем продукт?"
|
||||
|
||||
# Add product: description?
|
||||
ask_product_description = "Каким будет описание продукта?"
|
||||
|
||||
# Add product: price?
|
||||
ask_product_price = "Какова будет цена?\n" \
|
||||
"Введите <code>X</code> если продукт сейчас недоступен."
|
||||
|
||||
# Add product: image?
|
||||
ask_product_image = "🖼 Добавим фото продукта?\n" \
|
||||
"\n" \
|
||||
"<i>Пришлите фото, или Пропустите этот шаг.</i>"
|
||||
|
||||
ask_product_category = "Выберите категорию товара"
|
||||
|
||||
# Order product: notes?
|
||||
ask_order_notes = "Оставить заметку к этом заказу?\n" \
|
||||
"💼 Заметка будет доступна Менеджеру магазина.\n" \
|
||||
"\n" \
|
||||
"<i>Напишите Ваше сообщение, или выберите Пропустить," \
|
||||
" чтобы не оставлять заметку.</i>"
|
||||
|
||||
# Refund product: reason?
|
||||
ask_refund_reason = " Сообщите причину возврата средств.\n" \
|
||||
" Причина будет видна 👤 Покупателю."
|
||||
|
||||
# Edit credit: notes?
|
||||
ask_transaction_notes = " Добавьте сообщение к транзакции.\n" \
|
||||
" Сообщение будет доступно 👤 Покупателю после пополнения/списания средств" \
|
||||
" и 💼 Администратору в логах транзакций."
|
||||
|
||||
# Edit credit: amount?
|
||||
ask_credit = "Вы хотите изменить баланс Покупателя?\n" \
|
||||
"\n" \
|
||||
"<i>Напишите сообщение и укажите сумму.\n" \
|
||||
"Используйте </i><code>+</code><i> чтобы пополнить счет," \
|
||||
" и знак </i><code>-</code><i> чтобы списать средства.</i>"
|
||||
|
||||
# Header for the edit admin message
|
||||
admin_properties = "<b>Доступы пользователя {name}:</b>"
|
||||
|
||||
# Edit admin: can edit products?
|
||||
prop_edit_products = "Редактировать продукты"
|
||||
|
||||
# Edit admin: can receive orders?
|
||||
prop_receive_orders = "Получать заказы"
|
||||
|
||||
# Edit admin: can create transactions?
|
||||
prop_create_transactions = "Управлять транзакциями"
|
||||
|
||||
# Edit admin: show on help message?
|
||||
prop_display_on_help = "Показывать покупателям"
|
||||
|
||||
# Thread has started downloading an image and might be unresponsive
|
||||
downloading_image = "Я загружаю фото!\n" \
|
||||
"Это может занять некоторое время...!\n" \
|
||||
"Я не смогу отвечать, пока идет загрузка."
|
||||
|
||||
# Edit product: current value
|
||||
edit_current_value = "Текущее значение:\n" \
|
||||
"<pre>{value}</pre>\n" \
|
||||
"\n" \
|
||||
"<i>Нажмите Пропустить, чтобы оставить значение без изменений.</i>"
|
||||
|
||||
# Payment: cash payment info
|
||||
payment_cash = "Вы можете пополнить счет наличными в торговых точках.\n" \
|
||||
"Рассчитайтесь и сообщение Менеджеру следующее значение:\n" \
|
||||
"<b>{user_cash_id}</b>"
|
||||
|
||||
# Payment: invoice amount
|
||||
payment_cc_amount = "На какую сумму пополнить Ваш кошелек?\n" \
|
||||
"\n" \
|
||||
"<i>Выберите сумму из предложеных значений, или введите вручную в сообщении.</i>"
|
||||
|
||||
# Payment: add funds invoice title
|
||||
payment_invoice_title = "Пополнение"
|
||||
|
||||
# Payment: add funds invoice description
|
||||
payment_invoice_description = "Оплата этого счета добавит {amount} в Ваш кошелек."
|
||||
|
||||
# Payment: label of the labeled price on the invoice
|
||||
payment_invoice_label = "Платеж"
|
||||
|
||||
# Payment: label of the labeled price on the invoice
|
||||
payment_invoice_fee_label = "Сбор за пополнение"
|
||||
|
||||
# Notification: order has been placed
|
||||
notification_order_placed = "Получен новый заказ:\n" \
|
||||
"{order}"
|
||||
|
||||
# Notification: order has been completed
|
||||
notification_order_completed = "Выш заказ успешно выполнен!\n" \
|
||||
"{order}"
|
||||
|
||||
# Notification: order has been refunded
|
||||
notification_order_refunded = "Ваш заказ отменен. Средства возвращены в Ваш кошелек!\n" \
|
||||
"{order}"
|
||||
|
||||
# Notification: a manual transaction was applied
|
||||
notification_transaction_created = "ℹ️ Новая транзакция в Вашем кошельке:\n" \
|
||||
"{transaction}"
|
||||
|
||||
# Refund reason
|
||||
refund_reason = "Причина возврата:\n" \
|
||||
"{reason}"
|
||||
|
||||
# Info: informazioni sul bot
|
||||
bot_info = 'Этот бот использует <a href="https://github.com/Steffo99/greed">greed</a>,' \
|
||||
' фреймворк разработан @Steffo для платежей Телеграм и выпущен под лицензией' \
|
||||
' <a href="https://github.com/Steffo99/greed/blob/master/LICENSE.txt">' \
|
||||
'Affero General Public License 3.0</a>.\n'
|
||||
|
||||
# Help: guide
|
||||
help_msg = "Инструкция к greed доступна по этому адресу:\n" \
|
||||
"https://docs.google.com/document/d/1f4MKVr0B7RSQfWTSa_6ZO0LM4nPpky_GX_qdls3EHtQ/"
|
||||
|
||||
# Help: contact shopkeeper
|
||||
contact_shopkeeper = "Следующие сотрудники доступны сейчас и могут помочь:\n" \
|
||||
"{shopkeepers}\n" \
|
||||
"<i>Выберите одного из них и напишите в Телеграм чат.</i>"
|
||||
|
||||
# Success: product has been added/edited to the database
|
||||
success_product_edited = "✅ Продукт успешно создан/обновлен!"
|
||||
|
||||
# Success: product has been added/edited to the database
|
||||
success_product_deleted = "✅ Продукт успешно удален!"
|
||||
|
||||
# Success: order has been created
|
||||
success_order_created = "✅ Заказ успешно создан!\n" \
|
||||
"\n" \
|
||||
"{order}"
|
||||
|
||||
# Success: order was marked as completed
|
||||
success_order_completed = "✅ Ваш заказ #{order_id} был успешно выполнен."
|
||||
|
||||
# Success: order was refunded successfully
|
||||
success_order_refunded = "✴️ Средства по заказу #{order_id} были возвращены."
|
||||
|
||||
# Success: transaction was created successfully
|
||||
success_transaction_created = "✅ Транзакция успешно создана!\n" \
|
||||
"{transaction}"
|
||||
|
||||
# Error: message received not in a private chat
|
||||
error_nonprivate_chat = "⚠️ Этот бот работает только в частных чатах."
|
||||
|
||||
# Error: a message was sent in a chat, but no worker exists for that chat.
|
||||
# Suggest the creation of a new worker with /start
|
||||
error_no_worker_for_chat = "⚠️ Общение с ботом было прервано.\n" \
|
||||
"Чтобы начать снова, воспользуйтесь командой /start "
|
||||
|
||||
# Error: add funds amount over max
|
||||
error_payment_amount_over_max = "⚠️ Максимальная сумма одной транзакции {max_amount}."
|
||||
|
||||
# Error: add funds amount under min
|
||||
error_payment_amount_under_min = "⚠️ Минимальная сумма одной транзакции {min_amount}."
|
||||
|
||||
# Error: the invoice has expired and can't be paid
|
||||
error_invoice_expired = "⚠️ Время действия инвойса завершено. Если все еще хотите пополнить счет - выберите" \
|
||||
" Пополнить счет в меню."
|
||||
|
||||
# Error: a product with that name already exists
|
||||
error_duplicate_name = "️⚠️ Продукт с таким именем уже существует."
|
||||
|
||||
# Error: not enough credit to order
|
||||
error_not_enough_credit = "⚠️ У Вас недостаточно средств, чтобы выполнить заказ."
|
||||
|
||||
# Error: order has already been cleared
|
||||
error_order_already_cleared = "⚠️ Этот заказ уже был выполнен ранее."
|
||||
|
||||
# Error: no orders have been placed, so none can be shown
|
||||
error_no_orders = "⚠️ Вы еще не сделали ни одного заказа, поэтому здесь пусто."
|
||||
|
||||
# Error: selected user does not exist
|
||||
error_user_does_not_exist = "⚠️ Нет такого пользователя."
|
||||
|
||||
# Fatal: conversation raised an exception
|
||||
fatal_conversation_exception = "☢️ Вот беда! <b>Ошибка</b> прервала наше общение\n" \
|
||||
"Владельцу бота будет сообщено об этой ошибке.\n" \
|
||||
"Чтобы начать общение заново, воспользуйтесь командой /start."
|
417
strings/uk_UA.py
Normal file
417
strings/uk_UA.py
Normal file
|
@ -0,0 +1,417 @@
|
|||
# Strings / localization file for greed
|
||||
# Can be edited, but DON'T REMOVE THE REPLACEMENT FIELDS (words surrounded by {curly braces})
|
||||
|
||||
# Part of the translation by https://github.com/pzhuk
|
||||
|
||||
# Currency symbol
|
||||
currency_symbol = "₴"
|
||||
|
||||
# Positioning of the currency symbol
|
||||
currency_format_string = "{value} {symbol}"
|
||||
|
||||
# Quantity of a product in stock
|
||||
in_stock_format_string = "{quantity} наявні"
|
||||
|
||||
# Copies of a product in cart
|
||||
in_cart_format_string = "{quantity} в кошику"
|
||||
|
||||
# Product information
|
||||
product_format_string = "<b>{name}</b>\n" \
|
||||
"{description}\n" \
|
||||
"{price}\n" \
|
||||
"<b>{cart}</b>"
|
||||
|
||||
# Order number, displayed in the order info
|
||||
order_number = "Замовлення #{id}"
|
||||
|
||||
# Order info string, shown to the admins
|
||||
order_format_string = "Користувач {user}\n" \
|
||||
"Створено {date}\n" \
|
||||
"\n" \
|
||||
"{items}\n" \
|
||||
"ЗАГАЛОМ: <b>{value}</b>\n" \
|
||||
"\n" \
|
||||
"Нотатка: {notes}\n"
|
||||
|
||||
# Order info string, shown to the user
|
||||
user_order_format_string = "{status_emoji} <b>Замовлення {status_text}</b>\n" \
|
||||
"{items}\n" \
|
||||
"Загалом: <b>{value}</b>\n" \
|
||||
"\n" \
|
||||
"Нотатка: {notes}\n"
|
||||
|
||||
# Transaction page is loading
|
||||
loading_transactions = "<i>Завантажую транзакції...\n" \
|
||||
"Зачекайте кілька секунд.</i>"
|
||||
|
||||
# Transactions page
|
||||
transactions_page = "Сторінка <b>{page}</b>:\n" \
|
||||
"\n" \
|
||||
"{transactions}"
|
||||
|
||||
# transactions.csv caption
|
||||
csv_caption = "Файл 📄 .csv, який має всі транзакції з бази даних бота було сгенеровано.\n" \
|
||||
"Можете відкрити файл за допомогою LibreOffice Calc, щоб переглянути деталі."
|
||||
|
||||
# Conversation: the start command was sent and the bot should welcome the user
|
||||
conversation_after_start = "Привіт!\n" \
|
||||
"Вітаю в greed!\n" \
|
||||
"Це 🅱️ <b>Бета</b> версія програми.\n" \
|
||||
"Програма повністю придатна до використання, але ще можуть бути баги.\n" \
|
||||
"Якщо знайшли баг - повідомте на https://github.com/Steffo99/greed/issues."
|
||||
|
||||
# Conversation: to send an inline keyboard you need to send a message with it
|
||||
conversation_open_user_menu = "Щоб ви хотіли зробити?\n" \
|
||||
"💰 У вас <b>{credit}</b> в гаманці.\n" \
|
||||
"\n" \
|
||||
"<i>Виберіть опцію з варіантів на клавіатурі.\n" \
|
||||
"Якщо клавіатури не видно - її можна активувати кнопкою з чотирма квадратами внизу</i>."
|
||||
|
||||
# Conversation: like above, but for administrators
|
||||
conversation_open_admin_menu = "Ви є 💼 <b>Менеджером</b> цього магазину!\n" \
|
||||
"Що б ви хотіли зробити?\n" \
|
||||
"\n" \
|
||||
"<i>Виберіть опцію з варіантів на клавіатурі.\n" \
|
||||
"Якщо клавіатури не видно - її можна активувати кнопкою з чотирма квадратами внизу</i>."
|
||||
|
||||
# Conversation: select a payment method
|
||||
conversation_payment_method = "Як би Ви хотіли поповнити гаманець?"
|
||||
|
||||
# Conversation: select a product to edit
|
||||
conversation_admin_select_product = "✏️ Який продукт потрібно редагувати?"
|
||||
|
||||
# Conversation: select a product to delete
|
||||
conversation_admin_select_product_to_delete = "❌ Який продукт потрібно видалит?"
|
||||
|
||||
# Conversation: select a user to edit
|
||||
conversation_admin_select_user = "Виберіть користувача для редагування."
|
||||
|
||||
# Conversation: click below to pay for the purchase
|
||||
conversation_cart_actions = "<i>Додайте продукти в кошик натисканням кнопки Додати." \
|
||||
" Коли зробите Ваш вибір, повертайтесь до цього повідомлення" \
|
||||
" і натисніть кнопку Готово.</i>"
|
||||
|
||||
# Conversation: confirm the cart contents
|
||||
conversation_confirm_cart = "🛒 У вас в кошику наступні продукти:\n" \
|
||||
"{product_list}" \
|
||||
"Всього: <b>{total_cost}</b>\n" \
|
||||
"\n" \
|
||||
"<i>Щоб продовжити натисніть Готово.\n" \
|
||||
"Якщо змінили свою думку - обирайте Відміна.</i>"
|
||||
|
||||
# Conversation: the user activated the live orders mode
|
||||
conversation_live_orders_start = "Ви в режимі <b>Свіжі Замовлення</b>\n" \
|
||||
"Всі нові замовення від покупців зʼявляться в цьому чаті в режимі живого часу," \
|
||||
" і ви зможете помічати їх ✅ Виконано" \
|
||||
" або ✴️ Повернути кошти покупцю.\n" \
|
||||
"\n" \
|
||||
"<i>Натисніть кнопку Стоп в цьому чаті, щоб зупинити цей режим.</i>"
|
||||
|
||||
# Conversation: help menu has been opened
|
||||
conversation_open_help_menu = "Як можемо Вам допомогти?"
|
||||
|
||||
# Conversation: confirm promotion to admin
|
||||
conversation_confirm_admin_promotion = "Ви впевнені, що хочете підвищити цього користувача до 💼 Менеджера?\n" \
|
||||
"Цю дію неможливо відмінити!"
|
||||
|
||||
# Conversation: switching to user mode
|
||||
conversation_switch_to_user_mode = " Ви перейшли в режим 👤 Замовника.\n" \
|
||||
"Якщо хочете повернутись в меню 💼 Менеджера, рестартуйте розмову з /start."
|
||||
|
||||
# Notification: the conversation has expired
|
||||
conversation_expired = "🕐 За довгий час я не отримав жодного повідомлення, тому я завершив розмову" \
|
||||
" щоб зберегти ресурси.\n" \
|
||||
"Щоб почату знову, надішліть команду /start ."
|
||||
|
||||
# User menu: order
|
||||
menu_order = "🛒 Замовлення"
|
||||
|
||||
# User menu: order status
|
||||
menu_order_status = "🛍 Мої замовлення"
|
||||
|
||||
# User menu: add credit
|
||||
menu_add_credit = "💵 Поповнити гаманець"
|
||||
|
||||
# User menu: bot info
|
||||
menu_bot_info = "ℹ️ Інформація про бот"
|
||||
|
||||
# User menu: cash
|
||||
menu_cash = "💵 Готівкою"
|
||||
|
||||
# User menu: credit card
|
||||
menu_credit_card = "💳 Кредитною картою"
|
||||
|
||||
# Admin menu: products
|
||||
menu_products = "📝️ Продукти"
|
||||
|
||||
# Admin menu: orders
|
||||
menu_orders = "📦 Замовлення"
|
||||
|
||||
# Menu: transactions
|
||||
menu_transactions = "💳 Список транзакцій"
|
||||
|
||||
# Menu: edit credit
|
||||
menu_edit_credit = "💰 Створити транзакцію"
|
||||
|
||||
# Admin menu: go to user mode
|
||||
menu_user_mode = "👤 Режим замовника"
|
||||
|
||||
# Admin menu: add product
|
||||
menu_add_product = "✨ Новий продукт"
|
||||
|
||||
# Admin menu: delete product
|
||||
menu_delete_product = "❌ Видалити продукт"
|
||||
|
||||
# Menu: cancel
|
||||
menu_cancel = "🔙 Відміна"
|
||||
|
||||
# Menu: skip
|
||||
menu_skip = "⏭ Пропустити"
|
||||
|
||||
# Menu: done
|
||||
menu_done = "✅️ Готово"
|
||||
|
||||
# Menu: pay invoice
|
||||
menu_pay = "💳 Заплатити"
|
||||
|
||||
# Menu: complete
|
||||
menu_complete = "✅ Готово"
|
||||
|
||||
# Menu: refund
|
||||
menu_refund = "✴️ Повернення коштів"
|
||||
|
||||
# Menu: stop
|
||||
menu_stop = "🛑 Стоп"
|
||||
|
||||
# Menu: add to cart
|
||||
menu_add_to_cart = "➕ Додати"
|
||||
|
||||
# Menu: remove from cart
|
||||
menu_remove_from_cart = "➖ Прибрати"
|
||||
|
||||
# Menu: help menu
|
||||
menu_help = "❓ Допомога"
|
||||
|
||||
# Menu: guide
|
||||
menu_guide = "📖 Інструкція"
|
||||
|
||||
# Menu: next page
|
||||
menu_next = "▶️ Наступна"
|
||||
|
||||
# Menu: previous page
|
||||
menu_previous = "◀️ Попередня"
|
||||
|
||||
# Menu: contact the shopkeeper
|
||||
menu_contact_shopkeeper = "👨💼 Контакти магазину"
|
||||
|
||||
# Menu: generate transactions .csv file
|
||||
menu_csv = "📄 .csv"
|
||||
|
||||
# Menu: edit admins list
|
||||
menu_edit_admins = "🏵 Редагувати менеджерів"
|
||||
|
||||
# Emoji: unprocessed order
|
||||
emoji_not_processed = "*️⃣"
|
||||
|
||||
# Emoji: completed order
|
||||
emoji_completed = "✅"
|
||||
|
||||
# Emoji: refunded order
|
||||
emoji_refunded = "✴️"
|
||||
|
||||
# Emoji: yes
|
||||
emoji_yes = "✅"
|
||||
|
||||
# Emoji: no
|
||||
emoji_no = "🚫"
|
||||
|
||||
# Text: unprocessed order
|
||||
text_not_processed = "очікує"
|
||||
|
||||
# Text: completed order
|
||||
text_completed = "завершено"
|
||||
|
||||
# Text: refunded order
|
||||
text_refunded = "повернуто"
|
||||
|
||||
# Add product: name?
|
||||
ask_product_name = "Як назвати продукт?"
|
||||
|
||||
# Add product: description?
|
||||
ask_product_description = "Який буде опис продукту?"
|
||||
|
||||
# Add product: price?
|
||||
ask_product_price = "Яка буде ціна?\n" \
|
||||
"Введіть <code>X</code> Якщо продукт зараз не продається."
|
||||
|
||||
# Add product: image?
|
||||
ask_product_image = "🖼 Яку картинку додати до продукта?\n" \
|
||||
"\n" \
|
||||
"<i>Надішліть фото, або Пропустіть цей крок.</i>"
|
||||
|
||||
ask_product_category = "Оберіть категорію товару"
|
||||
|
||||
# Order product: notes?
|
||||
ask_order_notes = "Залишити повідомлення разом з цією покупкою?\n" \
|
||||
"💼 Повідомлення буде доступне Менеджеру магазину.\n" \
|
||||
"\n" \
|
||||
"<i>Надішліть Ваше повідомлення, або натисність Пропустити" \
|
||||
" щоб не залишати повідомлення.</i>"
|
||||
|
||||
# Refund product: reason?
|
||||
ask_refund_reason = " Напишіть причину повернення коштів.\n" \
|
||||
"👤 Причина буде доступна замовнику."
|
||||
|
||||
# Edit credit: notes?
|
||||
ask_transaction_notes = " Додайте повідомлення до транзакції.\n" \
|
||||
"👤 Повідомлення буде доступне замовнику після поповнення/списання" \
|
||||
" і 💼 Адміністратору в логах транзакцій."
|
||||
|
||||
# Edit credit: amount?
|
||||
ask_credit = "Як ви хочете змінити баланс замовника?\n" \
|
||||
"\n" \
|
||||
"<i>Надішліть повідомлення з сумою.\n" \
|
||||
"Використовуйте </i><code>+</code><i> щоб поповнити рахунок," \
|
||||
" і знак </i><code>-</code><i> щоб списати кошти.</i>"
|
||||
|
||||
# Header for the edit admin message
|
||||
admin_properties = "<b>Доступи користувача {name}:</b>"
|
||||
|
||||
# Edit admin: can edit products?
|
||||
prop_edit_products = "Редагувати продукти"
|
||||
|
||||
# Edit admin: can receive orders?
|
||||
prop_receive_orders = "Отримувати замовлення"
|
||||
|
||||
# Edit admin: can create transactions?
|
||||
prop_create_transactions = "Керувати транзакціями"
|
||||
|
||||
# Edit admin: show on help message?
|
||||
prop_display_on_help = "Показувати замовнику"
|
||||
|
||||
# Thread has started downloading an image and might be unresponsive
|
||||
downloading_image = "Я завантажую фото!\n" \
|
||||
"Може зайняти деякий час... Майте терпіння!\n" \
|
||||
"Я не зможу відповідати, поки йде завантаження."
|
||||
|
||||
# Edit product: current value
|
||||
edit_current_value = "Поточне значення:\n" \
|
||||
"<pre>{value}</pre>\n" \
|
||||
"\n" \
|
||||
"<i>Натисність Пропустити під цим повідомленням, щоб залишити значення таким.</i>"
|
||||
|
||||
# Payment: cash payment info
|
||||
payment_cash = "Ви можете поповнити готівкою прямо в магазині.\n" \
|
||||
"Розрахуйтесь і дайте цей id менеджеру:\n" \
|
||||
"<b>{user_cash_id}</b>"
|
||||
|
||||
# Payment: invoice amount
|
||||
payment_cc_amount = "На яку сумму ви хочете поповнити гаманець?\n" \
|
||||
"\n" \
|
||||
"<i>Виберіть сумму із запропонованих значень, або введіть вручну в повідомленні.</i>"
|
||||
|
||||
# Payment: add funds invoice title
|
||||
payment_invoice_title = "Поповнення"
|
||||
|
||||
# Payment: add funds invoice description
|
||||
payment_invoice_description = "Оплата цього рахунку додасть {amount} в ваш гаманець."
|
||||
|
||||
# Payment: label of the labeled price on the invoice
|
||||
payment_invoice_label = "Платіж"
|
||||
|
||||
# Payment: label of the labeled price on the invoice
|
||||
payment_invoice_fee_label = "Оплата за поповнення"
|
||||
|
||||
# Notification: order has been placed
|
||||
notification_order_placed = "Отримано нове замовлення:\n" \
|
||||
"{order}"
|
||||
|
||||
# Notification: order has been completed
|
||||
notification_order_completed = "Ваше замовнення успішно завершено!\n" \
|
||||
"{order}"
|
||||
|
||||
# Notification: order has been refunded
|
||||
notification_order_refunded = "Ваше замовлення відмінено. Кошти повернуто!\n" \
|
||||
"{order}"
|
||||
|
||||
# Notification: a manual transaction was applied
|
||||
notification_transaction_created = "ℹ️ Нова транзакція в вашому гаманці:\n" \
|
||||
"{transaction}"
|
||||
|
||||
# Refund reason
|
||||
refund_reason = "Причина повернення:\n" \
|
||||
"{reason}"
|
||||
|
||||
# Info: informazioni sul bot
|
||||
bot_info = 'Цей бот використовує <a href="https://github.com/Steffo99/greed">greed</a>,' \
|
||||
' фреймворк розроблений @Steffo для платежів Телеграм випущений під ліцензією' \
|
||||
' <a href="https://github.com/Steffo99/greed/blob/master/LICENSE.txt">' \
|
||||
'Affero General Public License 3.0</a>.\n'
|
||||
|
||||
# Help: guide
|
||||
help_msg = "Інструкція по greed доступна за цією адресою:\n" \
|
||||
"https://docs.google.com/document/d/1f4MKVr0B7RSQfWTSa_6ZO0LM4nPpky_GX_qdls3EHtQ/"
|
||||
|
||||
# Help: contact shopkeeper
|
||||
contact_shopkeeper = "Наразі наступні працівники доступні і зможуть допомогти:\n" \
|
||||
"{shopkeepers}\n" \
|
||||
"<i>Виберіть когось одного і напишіть в Телеграм чат.</i>"
|
||||
|
||||
# Success: product has been added/edited to the database
|
||||
success_product_edited = "✅ Продукт успішно створено/оновлено!"
|
||||
|
||||
# Success: product has been added/edited to the database
|
||||
success_product_deleted = "✅ Продукт успішно видалено!"
|
||||
|
||||
# Success: order has been created
|
||||
success_order_created = "✅ Замовлення успішно надіслано!\n" \
|
||||
"\n" \
|
||||
"{order}"
|
||||
|
||||
# Success: order was marked as completed
|
||||
success_order_completed = "✅ Ваше замовлення #{order_id} було успішно проведено."
|
||||
|
||||
# Success: order was refunded successfully
|
||||
success_order_refunded = "✴️ Кошти по замовленню #{order_id} було відшкодовано."
|
||||
|
||||
# Success: transaction was created successfully
|
||||
success_transaction_created = "✅ Транзакцію успішно створено!\n" \
|
||||
"{transaction}"
|
||||
|
||||
# Error: message received not in a private chat
|
||||
error_nonprivate_chat = "⚠️ Цей бот працює тільки в приватних чатах."
|
||||
|
||||
# Error: a message was sent in a chat, but no worker exists for that chat.
|
||||
# Suggest the creation of a new worker with /start
|
||||
error_no_worker_for_chat = "⚠️ Спілкування з ботом було перервано.\n" \
|
||||
"Щоб почати знову, надішліть боту команду /start "
|
||||
|
||||
# Error: add funds amount over max
|
||||
error_payment_amount_over_max = "⚠️ Максимальна сума однієї транзакції {max_amount}."
|
||||
|
||||
# Error: add funds amount under min
|
||||
error_payment_amount_under_min = "⚠️ Мінімальна сума однієї транзакції {min_amount}."
|
||||
|
||||
# Error: the invoice has expired and can't be paid
|
||||
error_invoice_expired = "⚠️ Час дії інвойсу було вичерпано. Якщо все хочете додати кошти - виберіть Додати" \
|
||||
" кошти в меню."
|
||||
|
||||
# Error: a product with that name already exists
|
||||
error_duplicate_name = "️⚠️ Продукт з таким імʼям вже існує."
|
||||
|
||||
# Error: not enough credit to order
|
||||
error_not_enough_credit = "⚠️ У вас недостатньо коштів, щоб виконати замовлення."
|
||||
|
||||
# Error: order has already been cleared
|
||||
error_order_already_cleared = "⚠️ Це замовлення вже було опрацьовано раніше."
|
||||
|
||||
# Error: no orders have been placed, so none can be shown
|
||||
error_no_orders = "⚠️ Ви ще не зробили жодного замовлення, тому тут пусто."
|
||||
|
||||
# Error: selected user does not exist
|
||||
error_user_does_not_exist = "⚠️ Такого користувача не існує."
|
||||
|
||||
# Fatal: conversation raised an exception
|
||||
fatal_conversation_exception = "☢️ Ой лишенько! <b>Помилка</b> перервала нашу розмову\n" \
|
||||
"Про помилку було повідомлено власника бота.\n" \
|
||||
"Щоб почати розмову знову, надішліть команду /start."
|
10
utils.py
10
utils.py
|
@ -23,9 +23,13 @@ except ModuleNotFoundError:
|
|||
if config["Error Reporting"]["sentry_token"] != \
|
||||
"https://00000000000000000000000000000000:00000000000000000000000000000000@sentry.io/0000000":
|
||||
import raven
|
||||
|
||||
import raven.exceptions
|
||||
try:
|
||||
release = raven.fetch_git_sha(os.path.dirname(__file__))
|
||||
except raven.exceptions.InvalidGitRepository:
|
||||
release = "Unknown"
|
||||
sentry_client = raven.Client(config["Error Reporting"]["sentry_token"],
|
||||
release=raven.fetch_git_sha(os.path.dirname(__file__)),
|
||||
release=release,
|
||||
environment="Dev" if __debug__ else "Prod")
|
||||
else:
|
||||
sentry_client = None
|
||||
|
@ -53,7 +57,7 @@ class Price:
|
|||
return f"<Price of value {self.value}>"
|
||||
|
||||
def __str__(self):
|
||||
return strings.currency_format_string.format(symbol=strings.currency_symbol,
|
||||
return strings.currency_format_string.format(symbol=(config["Payments"]["currency_symbol"] or strings.currency_symbol),
|
||||
value="{0:.2f}".format(
|
||||
self.value / (10 ** int(config["Payments"]["currency_exp"]))))
|
||||
|
||||
|
|
206
worker.py
206
worker.py
|
@ -166,8 +166,9 @@ class Worker(threading.Thread):
|
|||
while True:
|
||||
# Get the next update
|
||||
update = self.__receive_next_update()
|
||||
# Ensure the update isn't a CancelSignal
|
||||
# If a CancelSignal is received...
|
||||
if isinstance(update, CancelSignal):
|
||||
# And the wait is cancellable...
|
||||
if cancellable:
|
||||
# Return the CancelSignal
|
||||
return update
|
||||
|
@ -192,10 +193,15 @@ class Worker(threading.Thread):
|
|||
while True:
|
||||
# Get the next update
|
||||
update = self.__receive_next_update()
|
||||
# Ensure the update isn't a CancelSignal
|
||||
if cancellable and isinstance(update, CancelSignal):
|
||||
# Return the CancelSignal
|
||||
return update
|
||||
# If a CancelSignal is received...
|
||||
if isinstance(update, CancelSignal):
|
||||
# And the wait is cancellable...
|
||||
if cancellable:
|
||||
# Return the CancelSignal
|
||||
return update
|
||||
else:
|
||||
# Ignore the signal
|
||||
continue
|
||||
# Ensure the update contains a message
|
||||
if update.message is None:
|
||||
continue
|
||||
|
@ -218,10 +224,15 @@ class Worker(threading.Thread):
|
|||
while True:
|
||||
# Get the next update
|
||||
update = self.__receive_next_update()
|
||||
# Ensure the update isn't a CancelSignal
|
||||
if cancellable and isinstance(update, CancelSignal):
|
||||
# Return the CancelSignal
|
||||
return update
|
||||
# If a CancelSignal is received...
|
||||
if isinstance(update, CancelSignal):
|
||||
# And the wait is cancellable...
|
||||
if cancellable:
|
||||
# Return the CancelSignal
|
||||
return update
|
||||
else:
|
||||
# Ignore the signal
|
||||
continue
|
||||
# Ensure the update contains a precheckoutquery
|
||||
if update.pre_checkout_query is None:
|
||||
continue
|
||||
|
@ -249,10 +260,15 @@ class Worker(threading.Thread):
|
|||
while True:
|
||||
# Get the next update
|
||||
update = self.__receive_next_update()
|
||||
# Ensure the update isn't a CancelSignal
|
||||
if cancellable and isinstance(update, CancelSignal):
|
||||
# Return the CancelSignal
|
||||
return update
|
||||
# If a CancelSignal is received...
|
||||
if isinstance(update, CancelSignal):
|
||||
# And the wait is cancellable...
|
||||
if cancellable:
|
||||
# Return the CancelSignal
|
||||
return update
|
||||
else:
|
||||
# Ignore the signal
|
||||
continue
|
||||
# Ensure the update contains a message
|
||||
if update.message is None:
|
||||
continue
|
||||
|
@ -262,17 +278,22 @@ class Worker(threading.Thread):
|
|||
# Return the photo array
|
||||
return update.message.photo
|
||||
|
||||
def __wait_for_inlinekeyboard_callback(self, cancellable: bool = True) \
|
||||
def __wait_for_inlinekeyboard_callback(self, cancellable: bool = False) \
|
||||
-> Union[telegram.CallbackQuery, CancelSignal]:
|
||||
"""Continue getting updates until an inline keyboard callback is received, then return it."""
|
||||
log.debug("Waiting for a CallbackQuery...")
|
||||
while True:
|
||||
# Get the next update
|
||||
update = self.__receive_next_update()
|
||||
# Ensure the update isn't a CancelSignal
|
||||
if cancellable and isinstance(update, CancelSignal):
|
||||
# Return the CancelSignal
|
||||
return update
|
||||
# If a CancelSignal is received...
|
||||
if isinstance(update, CancelSignal):
|
||||
# And the wait is cancellable...
|
||||
if cancellable:
|
||||
# Return the CancelSignal
|
||||
return update
|
||||
else:
|
||||
# Ignore the signal
|
||||
continue
|
||||
# Ensure the update is a CallbackQuery
|
||||
if update.callback_query is None:
|
||||
continue
|
||||
|
@ -428,17 +449,13 @@ class Worker(threading.Thread):
|
|||
message_id=callback.message.message_id,
|
||||
caption=product.text(cart_qty=cart[callback.message.message_id][1]),
|
||||
reply_markup=product_inline_keyboard)
|
||||
# Create the cart summary
|
||||
product_list = ""
|
||||
total_cost = utils.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_msg.message_id,
|
||||
text=strings.conversation_confirm_cart.format(product_list=product_list,
|
||||
total_cost=str(total_cost)),
|
||||
reply_markup=final_inline_keyboard)
|
||||
|
||||
self.bot.edit_message_text(
|
||||
chat_id=self.chat.id,
|
||||
message_id=final_msg.message_id,
|
||||
text=strings.conversation_confirm_cart.format(product_list=self.__get_cart_summary(cart),
|
||||
total_cost=str(self.__get_cart_value(cart))),
|
||||
reply_markup=final_inline_keyboard)
|
||||
# If the Remove from cart button has been pressed...
|
||||
elif callback.data == "cart_remove":
|
||||
# Get the selected product, ensuring it exists
|
||||
|
@ -476,17 +493,13 @@ class Worker(threading.Thread):
|
|||
message_id=callback.message.message_id,
|
||||
caption=product.text(cart_qty=cart[callback.message.message_id][1]),
|
||||
reply_markup=product_inline_keyboard)
|
||||
# Create the cart summary
|
||||
product_list = ""
|
||||
total_cost = utils.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_msg.message_id,
|
||||
text=strings.conversation_confirm_cart.format(product_list=product_list,
|
||||
total_cost=str(total_cost)),
|
||||
reply_markup=final_inline_keyboard)
|
||||
|
||||
self.bot.edit_message_text(
|
||||
chat_id=self.chat.id,
|
||||
message_id=final_msg.message_id,
|
||||
text=strings.conversation_confirm_cart.format(product_list=self.__get_cart_summary(cart),
|
||||
total_cost=str(self.__get_cart_value(cart))),
|
||||
reply_markup=final_inline_keyboard)
|
||||
# If the done button has been pressed...
|
||||
elif callback.data == "cart_done":
|
||||
# End the loop
|
||||
|
@ -505,22 +518,50 @@ class Worker(threading.Thread):
|
|||
# Add the record to the session and get an ID
|
||||
self.session.add(order)
|
||||
self.session.flush()
|
||||
# For each product added to the cart, create a new OrderItem and get the total value
|
||||
value = 0
|
||||
# For each product added to the cart, create a new OrderItem
|
||||
for product in cart:
|
||||
# Add the price multiplied by the quantity to the total price
|
||||
value -= cart[product][0].price * cart[product][1]
|
||||
# Create {quantity} new OrderItems
|
||||
for i in range(0, cart[product][1]):
|
||||
order_item = db.OrderItem(product=cart[product][0],
|
||||
order_id=order.order_id)
|
||||
self.session.add(order_item)
|
||||
# Ensure the user has enough credit to make the purchase
|
||||
if self.user.credit + value < 0:
|
||||
credit_required = self.__get_cart_value(cart) - self.user.credit
|
||||
# Notify user in case of insufficient credit
|
||||
if credit_required > 0:
|
||||
self.bot.send_message(self.chat.id, strings.error_not_enough_credit)
|
||||
# Suggest payment for missing credit value if configuration allows refill
|
||||
if configloader.config["Credit Card"]["credit_card_token"] != "" \
|
||||
and configloader.config["Appearance"]["refill_on_checkout"] == 'yes' \
|
||||
and credit_required <= utils.Price(int(configloader.config["Credit Card"]["max_amount"])) \
|
||||
and credit_required >= utils.Price(int(configloader.config["Credit Card"]["min_amount"])):
|
||||
self.__make_payment(utils.Price(credit_required))
|
||||
# If afer requested payment credit is still insufficient (either payment failure or cancel)
|
||||
if self.user.credit < self.__get_cart_value(cart):
|
||||
# Rollback all the changes
|
||||
self.session.rollback()
|
||||
return
|
||||
else:
|
||||
# User has credit and valid order, perform transaction now
|
||||
self.__order_transaction(order=order, value=-int(self.__get_cart_value(cart)))
|
||||
|
||||
@staticmethod
|
||||
def __get_cart_value(cart):
|
||||
# Calculate total items value in cart
|
||||
value = utils.Price(0)
|
||||
for product in cart:
|
||||
value += cart[product][0].price * cart[product][1]
|
||||
return value
|
||||
|
||||
@staticmethod
|
||||
def __get_cart_summary(cart):
|
||||
# Create the cart summary
|
||||
product_list = ""
|
||||
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"
|
||||
return product_list
|
||||
|
||||
def __order_transaction(self, order, value):
|
||||
# Create a new transaction and add it to the session
|
||||
transaction = db.Transaction(user=self.user,
|
||||
value=value,
|
||||
|
@ -532,6 +573,10 @@ class Worker(threading.Thread):
|
|||
self.user.recalculate_credit()
|
||||
# Commit all the changes
|
||||
self.session.commit()
|
||||
# Notify admins about new transation
|
||||
self.__order_notify_admins(order=order)
|
||||
|
||||
def __order_notify_admins(self, order):
|
||||
# Notify the user of the order result
|
||||
self.bot.send_message(self.chat.id, strings.success_order_created.format(order=order.get_text(self.session,
|
||||
user=True)))
|
||||
|
@ -583,7 +628,8 @@ class Worker(threading.Thread):
|
|||
self.bot.send_message(self.chat.id, strings.conversation_payment_method,
|
||||
reply_markup=telegram.ReplyKeyboardMarkup(keyboard, one_time_keyboard=True))
|
||||
# Wait for a reply from the user
|
||||
selection = self.__wait_for_specific_message([strings.menu_cash, strings.menu_credit_card, strings.menu_cancel])
|
||||
selection = self.__wait_for_specific_message([strings.menu_cash, strings.menu_credit_card, strings.menu_cancel],
|
||||
cancellable=True)
|
||||
# If the user has selected the Cash option...
|
||||
if selection == strings.menu_cash:
|
||||
# Go to the pay with cash function
|
||||
|
@ -594,7 +640,7 @@ class Worker(threading.Thread):
|
|||
# Go to the pay with credit card function
|
||||
self.__add_credit_cc()
|
||||
# If the user has selected the Cancel option...
|
||||
elif selection == strings.menu_cancel:
|
||||
elif isinstance(selection, CancelSignal):
|
||||
# Send him back to the previous menu
|
||||
return
|
||||
|
||||
|
@ -602,11 +648,9 @@ class Worker(threading.Thread):
|
|||
"""Add money to the wallet through a credit card payment."""
|
||||
log.debug("Displaying __add_credit_cc")
|
||||
# Create a keyboard to be sent later
|
||||
keyboard = [[telegram.KeyboardButton(str(utils.Price("10.00")))],
|
||||
[telegram.KeyboardButton(str(utils.Price("25.00")))],
|
||||
[telegram.KeyboardButton(str(utils.Price("50.00")))],
|
||||
[telegram.KeyboardButton(str(utils.Price("100.00")))],
|
||||
[telegram.KeyboardButton(strings.menu_cancel)]]
|
||||
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.append([telegram.KeyboardButton(strings.menu_cancel)])
|
||||
# Boolean variable to check if the user has cancelled the action
|
||||
cancelled = False
|
||||
# Loop used to continue asking if there's an error during the input
|
||||
|
@ -615,9 +659,9 @@ class Worker(threading.Thread):
|
|||
self.bot.send_message(self.chat.id, strings.payment_cc_amount,
|
||||
reply_markup=telegram.ReplyKeyboardMarkup(keyboard, one_time_keyboard=True))
|
||||
# Wait until a valid amount is sent
|
||||
selection = self.__wait_for_regex(r"([0-9]+(?:[.,][0-9]+)?|" + strings.menu_cancel + r")")
|
||||
selection = self.__wait_for_regex(r"([0-9]+(?:[.,][0-9]+)?|" + strings.menu_cancel + r")", cancellable=True)
|
||||
# If the user cancelled the action
|
||||
if selection == strings.menu_cancel:
|
||||
if isinstance(selection, CancelSignal):
|
||||
# Exit the loop
|
||||
cancelled = True
|
||||
continue
|
||||
|
@ -627,31 +671,33 @@ class Worker(threading.Thread):
|
|||
if value > utils.Price(int(configloader.config["Credit Card"]["max_amount"])):
|
||||
self.bot.send_message(self.chat.id,
|
||||
strings.error_payment_amount_over_max.format(
|
||||
max_amount=utils.Price(configloader.config["Payments"]["max_amount"])))
|
||||
max_amount=utils.Price(configloader.config["Credit Card"]["max_amount"]))
|
||||
)
|
||||
continue
|
||||
elif value < utils.Price(int(configloader.config["Credit Card"]["min_amount"])):
|
||||
self.bot.send_message(self.chat.id,
|
||||
strings.error_payment_amount_under_min.format(
|
||||
min_amount=utils.Price(configloader.config["Payments"]["min_amount"])))
|
||||
min_amount=utils.Price(configloader.config["Credit Card"]["min_amount"]))
|
||||
)
|
||||
continue
|
||||
break
|
||||
# If the user cancelled the action...
|
||||
else:
|
||||
# Exit the function
|
||||
return
|
||||
# Issue the payment invoice
|
||||
self.__make_payment(amount=value)
|
||||
|
||||
def __make_payment(self, amount):
|
||||
# Set the invoice active invoice payload
|
||||
self.invoice_payload = str(uuid.uuid4())
|
||||
# Create the price array
|
||||
prices = [telegram.LabeledPrice(label=strings.payment_invoice_label, amount=int(value))]
|
||||
prices = [telegram.LabeledPrice(label=strings.payment_invoice_label, amount=int(amount))]
|
||||
# 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_fixed = int(configloader.config["Credit Card"]["fee_fixed"])
|
||||
total_fee = value * fee_percentage + fee_fixed
|
||||
if total_fee > 0:
|
||||
prices.append(telegram.LabeledPrice(label=strings.payment_invoice_fee_label, amount=int(total_fee)))
|
||||
else:
|
||||
# Otherwise, set the fee to 0 to ensure no accidental discounts are applied
|
||||
total_fee = 0
|
||||
fee = int(self.__get_total_fee(amount))
|
||||
if fee > 0:
|
||||
prices.append(telegram.LabeledPrice(label=strings.payment_invoice_fee_label,
|
||||
amount=fee))
|
||||
# Create the invoice keyboard
|
||||
inline_keyboard = telegram.InlineKeyboardMarkup([[telegram.InlineKeyboardButton(strings.menu_pay, pay=True)],
|
||||
[telegram.InlineKeyboardButton(strings.menu_cancel,
|
||||
|
@ -659,7 +705,7 @@ class Worker(threading.Thread):
|
|||
# The amount is valid, send the invoice
|
||||
self.bot.send_invoice(self.chat.id,
|
||||
title=strings.payment_invoice_title,
|
||||
description=strings.payment_invoice_description.format(amount=str(value)),
|
||||
description=strings.payment_invoice_description.format(amount=str(amount)),
|
||||
payload=self.invoice_payload,
|
||||
provider_token=configloader.config["Credit Card"]["credit_card_token"],
|
||||
start_parameter="tempdeeplink",
|
||||
|
@ -669,7 +715,7 @@ class Worker(threading.Thread):
|
|||
need_email=configloader.config["Credit Card"]["email_required"] == "yes",
|
||||
need_phone_number=configloader.config["Credit Card"]["phone_required"] == "yes",
|
||||
reply_markup=inline_keyboard)
|
||||
# Wait for the invoice
|
||||
# Wait for the precheckout query
|
||||
precheckoutquery = self.__wait_for_precheckoutquery(cancellable=True)
|
||||
# Check if the user has cancelled the invoice
|
||||
if isinstance(precheckoutquery, CancelSignal):
|
||||
|
@ -681,10 +727,11 @@ class Worker(threading.Thread):
|
|||
successfulpayment = self.__wait_for_successfulpayment()
|
||||
# Create a new database transaction
|
||||
transaction = db.Transaction(user=self.user,
|
||||
value=successfulpayment.total_amount - int(total_fee),
|
||||
value=int(successfulpayment.total_amount) - fee,
|
||||
provider="Credit Card",
|
||||
telegram_charge_id=successfulpayment.telegram_payment_charge_id,
|
||||
provider_charge_id=successfulpayment.provider_payment_charge_id)
|
||||
|
||||
if successfulpayment.order_info is not None:
|
||||
transaction.payment_name = successfulpayment.order_info.name
|
||||
transaction.payment_email = successfulpayment.order_info.email
|
||||
|
@ -694,6 +741,17 @@ class Worker(threading.Thread):
|
|||
# Commit all the changes
|
||||
self.session.commit()
|
||||
|
||||
@staticmethod
|
||||
def __get_total_fee(amount):
|
||||
# Calculate a fee for the required amount
|
||||
fee_percentage = float(configloader.config["Credit Card"]["fee_percentage"]) / 100
|
||||
fee_fixed = int(configloader.config["Credit Card"]["fee_fixed"])
|
||||
total_fee = amount * fee_percentage + fee_fixed
|
||||
if total_fee > 0:
|
||||
return total_fee
|
||||
# Set the fee to 0 to ensure no accidental discounts are applied
|
||||
return 0
|
||||
|
||||
def __bot_info(self):
|
||||
"""Send information about the bot."""
|
||||
log.debug("Displaying __bot_info")
|
||||
|
@ -774,9 +832,9 @@ class Worker(threading.Thread):
|
|||
self.bot.send_message(self.chat.id, strings.conversation_admin_select_product,
|
||||
reply_markup=telegram.ReplyKeyboardMarkup(keyboard, one_time_keyboard=True))
|
||||
# Wait for a reply from the user
|
||||
selection = self.__wait_for_specific_message(product_names)
|
||||
selection = self.__wait_for_specific_message(product_names, cancellable = True)
|
||||
# If the user has selected the Cancel option...
|
||||
if selection == strings.menu_cancel:
|
||||
if isinstance(selection, CancelSignal):
|
||||
# Exit the menu
|
||||
return
|
||||
# If the user has selected the Add Product option...
|
||||
|
@ -898,8 +956,8 @@ class Worker(threading.Thread):
|
|||
self.bot.send_message(self.chat.id, strings.conversation_admin_select_product_to_delete,
|
||||
reply_markup=telegram.ReplyKeyboardMarkup(keyboard, one_time_keyboard=True))
|
||||
# Wait for a reply from the user
|
||||
selection = self.__wait_for_specific_message(product_names)
|
||||
if selection == strings.menu_cancel:
|
||||
selection = self.__wait_for_specific_message(product_names, cancellable = True)
|
||||
if isinstance(selection, CancelSignal):
|
||||
# Exit the menu
|
||||
return
|
||||
else:
|
||||
|
@ -1229,7 +1287,7 @@ class Worker(threading.Thread):
|
|||
callback = self.__wait_for_inlinekeyboard_callback()
|
||||
# Toggle the correct property
|
||||
if callback.data == "toggle_edit_products":
|
||||
admin.edit_products = not admin.edit_products
|
||||
admin.edit_products = not admin.edit_products1
|
||||
elif callback.data == "toggle_receive_orders":
|
||||
admin.receive_orders = not admin.receive_orders
|
||||
elif callback.data == "toggle_create_transactions":
|
||||
|
|
Loading…
Reference in a new issue