mirror of
https://github.com/Steffo99/greed.git
synced 2024-11-22 05:54:18 +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
|
# 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)
|
![](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
|
## 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`
|
2. Install the project requirements with `pip install -r requirements.txt`
|
||||||
3. _Optional: run `pip install coloredlogs` to have colored logging output._
|
3. _Optional: run `pip install coloredlogs` to have colored logging output._
|
||||||
3. Run `python -OO core.py` to generate the configuration file.
|
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.
|
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.
|
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
|
## 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.
|
`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
|
## Forks
|
||||||
|
|
||||||
> Please note that @Steffo99, the developer of `greed`, does not endorse any of these forks.
|
> Please note that @Steffo99, the developer of `greed`, does not endorse any of these forks.
|
||||||
|
|
|
@ -5,18 +5,20 @@
|
||||||
# Config file parameters
|
# Config file parameters
|
||||||
[Config]
|
[Config]
|
||||||
; Config file version. DO NOT EDIT THIS!
|
; Config file version. DO NOT EDIT THIS!
|
||||||
version = 15
|
version = 17
|
||||||
; Set this to no when you are done editing the file
|
; Set this to no when you are done editing the file
|
||||||
is_template = yes
|
is_template = yes
|
||||||
; Language code for string file
|
; Language code for string file
|
||||||
; Available languages:
|
; Available languages:
|
||||||
; it_IT - Italian, by Steffo
|
; it_IT - Italian, by Steffo
|
||||||
; en_US - English, by https://github.com/DarrenWestwood (incomplete, please improve it!)
|
; 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
|
language = it_IT
|
||||||
|
|
||||||
# Telegram bot parameters
|
# Telegram bot parameters
|
||||||
[Telegram]
|
[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_______________
|
token = 123456789:YOUR_TOKEN_GOES_HERE_______________
|
||||||
; Time in seconds before a conversation (thread) with no new messages expires
|
; 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
|
; 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.
|
; 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...)
|
; It has a value of 2 in most currencies (EUR, USD, GBP...)
|
||||||
currency_exp = 2
|
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 payment settings
|
||||||
[Credit Card]
|
[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_
|
credit_card_token = 123456789:YOUR_TOKEN_HERE_
|
||||||
; Minimum wallet payment accepted (in miniumum currency units, $1.00 = 100 units)
|
; Minimum wallet payment accepted (in miniumum currency units, $1.00 = 100 units)
|
||||||
min_amount = 1000
|
min_amount = 1000
|
||||||
; Maximum wallet payment accepted (in miniumum currency units, $1.00 = 100 units)
|
; Maximum wallet payment accepted (in miniumum currency units, $1.00 = 100 units)
|
||||||
max_amount = 10000
|
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
|
; 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:
|
; The formula for determining the total cost is:
|
||||||
; cost = added_funds + added_funds * fee_percentage / 100 + fee_fixed
|
; 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
|
; 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
|
; The full order information includes the order number and the timestamp of the order placement
|
||||||
full_order_info = no
|
full_order_info = no
|
||||||
|
; Allow balance refill during the order checkout in case of unsufficient balance
|
||||||
|
refill_on_checkout = yes
|
||||||
|
|
||||||
# Exception reporting settings
|
# Exception reporting settings
|
||||||
[Error Reporting]
|
[Error Reporting]
|
||||||
|
|
|
@ -12,11 +12,12 @@ log = logging.getLogger(__name__)
|
||||||
if not os.path.isfile("config/config.ini"):
|
if not os.path.isfile("config/config.ini"):
|
||||||
log.debug("Creating config.ini from template_config.ini")
|
log.debug("Creating config.ini from template_config.ini")
|
||||||
# Open the template file and create the config file
|
# 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
|
# Copy the template file to the config file
|
||||||
config_file.write(template_file.read())
|
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
|
# Find the template version number
|
||||||
config = configparser.ConfigParser()
|
config = configparser.ConfigParser()
|
||||||
config.read_file(template_file)
|
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}")
|
log.debug(f"Template is version {template_version}")
|
||||||
|
|
||||||
# Overwrite the template config with the values in the config
|
# 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.read_file(config_file)
|
||||||
config_version = int(config["Config"]["version"])
|
config_version = int(config["Config"]["version"])
|
||||||
log.debug(f"Config is version {template_version}")
|
log.debug(f"Config is version {template_version}")
|
||||||
|
@ -44,7 +45,7 @@ if template_version > config_version:
|
||||||
# Update the config version
|
# Update the config version
|
||||||
config["Config"]["version"] = str(template_version)
|
config["Config"]["version"] = str(template_version)
|
||||||
# Save the file
|
# 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...")
|
log.debug("Writing merged config file...")
|
||||||
config.write(config_file)
|
config.write(config_file)
|
||||||
# Notify the user and quit
|
# Notify the user and quit
|
||||||
|
|
2
core.py
2
core.py
|
@ -76,7 +76,7 @@ def main():
|
||||||
# Skip the update
|
# Skip the update
|
||||||
continue
|
continue
|
||||||
# If the message is a start command...
|
# 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}")
|
log.info(f"Received /start from: {update.message.chat.id}")
|
||||||
# Check if a worker already exists for that chat
|
# Check if a worker already exists for that chat
|
||||||
old_worker = chat_workers.get(update.message.chat.id)
|
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"] != \
|
if config["Error Reporting"]["sentry_token"] != \
|
||||||
"https://00000000000000000000000000000000:00000000000000000000000000000000@sentry.io/0000000":
|
"https://00000000000000000000000000000000:00000000000000000000000000000000@sentry.io/0000000":
|
||||||
import raven
|
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"],
|
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")
|
environment="Dev" if __debug__ else "Prod")
|
||||||
else:
|
else:
|
||||||
sentry_client = None
|
sentry_client = None
|
||||||
|
@ -53,7 +57,7 @@ class Price:
|
||||||
return f"<Price of value {self.value}>"
|
return f"<Price of value {self.value}>"
|
||||||
|
|
||||||
def __str__(self):
|
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(
|
value="{0:.2f}".format(
|
||||||
self.value / (10 ** int(config["Payments"]["currency_exp"]))))
|
self.value / (10 ** int(config["Payments"]["currency_exp"]))))
|
||||||
|
|
||||||
|
|
186
worker.py
186
worker.py
|
@ -166,8 +166,9 @@ class Worker(threading.Thread):
|
||||||
while True:
|
while True:
|
||||||
# Get the next update
|
# Get the next update
|
||||||
update = self.__receive_next_update()
|
update = self.__receive_next_update()
|
||||||
# Ensure the update isn't a CancelSignal
|
# If a CancelSignal is received...
|
||||||
if isinstance(update, CancelSignal):
|
if isinstance(update, CancelSignal):
|
||||||
|
# And the wait is cancellable...
|
||||||
if cancellable:
|
if cancellable:
|
||||||
# Return the CancelSignal
|
# Return the CancelSignal
|
||||||
return update
|
return update
|
||||||
|
@ -192,10 +193,15 @@ class Worker(threading.Thread):
|
||||||
while True:
|
while True:
|
||||||
# Get the next update
|
# Get the next update
|
||||||
update = self.__receive_next_update()
|
update = self.__receive_next_update()
|
||||||
# Ensure the update isn't a CancelSignal
|
# If a CancelSignal is received...
|
||||||
if cancellable and isinstance(update, CancelSignal):
|
if isinstance(update, CancelSignal):
|
||||||
|
# And the wait is cancellable...
|
||||||
|
if cancellable:
|
||||||
# Return the CancelSignal
|
# Return the CancelSignal
|
||||||
return update
|
return update
|
||||||
|
else:
|
||||||
|
# Ignore the signal
|
||||||
|
continue
|
||||||
# Ensure the update contains a message
|
# Ensure the update contains a message
|
||||||
if update.message is None:
|
if update.message is None:
|
||||||
continue
|
continue
|
||||||
|
@ -218,10 +224,15 @@ class Worker(threading.Thread):
|
||||||
while True:
|
while True:
|
||||||
# Get the next update
|
# Get the next update
|
||||||
update = self.__receive_next_update()
|
update = self.__receive_next_update()
|
||||||
# Ensure the update isn't a CancelSignal
|
# If a CancelSignal is received...
|
||||||
if cancellable and isinstance(update, CancelSignal):
|
if isinstance(update, CancelSignal):
|
||||||
|
# And the wait is cancellable...
|
||||||
|
if cancellable:
|
||||||
# Return the CancelSignal
|
# Return the CancelSignal
|
||||||
return update
|
return update
|
||||||
|
else:
|
||||||
|
# Ignore the signal
|
||||||
|
continue
|
||||||
# Ensure the update contains a precheckoutquery
|
# Ensure the update contains a precheckoutquery
|
||||||
if update.pre_checkout_query is None:
|
if update.pre_checkout_query is None:
|
||||||
continue
|
continue
|
||||||
|
@ -249,10 +260,15 @@ class Worker(threading.Thread):
|
||||||
while True:
|
while True:
|
||||||
# Get the next update
|
# Get the next update
|
||||||
update = self.__receive_next_update()
|
update = self.__receive_next_update()
|
||||||
# Ensure the update isn't a CancelSignal
|
# If a CancelSignal is received...
|
||||||
if cancellable and isinstance(update, CancelSignal):
|
if isinstance(update, CancelSignal):
|
||||||
|
# And the wait is cancellable...
|
||||||
|
if cancellable:
|
||||||
# Return the CancelSignal
|
# Return the CancelSignal
|
||||||
return update
|
return update
|
||||||
|
else:
|
||||||
|
# Ignore the signal
|
||||||
|
continue
|
||||||
# Ensure the update contains a message
|
# Ensure the update contains a message
|
||||||
if update.message is None:
|
if update.message is None:
|
||||||
continue
|
continue
|
||||||
|
@ -262,17 +278,22 @@ class Worker(threading.Thread):
|
||||||
# Return the photo array
|
# Return the photo array
|
||||||
return update.message.photo
|
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]:
|
-> Union[telegram.CallbackQuery, CancelSignal]:
|
||||||
"""Continue getting updates until an inline keyboard callback is received, then return it."""
|
"""Continue getting updates until an inline keyboard callback is received, then return it."""
|
||||||
log.debug("Waiting for a CallbackQuery...")
|
log.debug("Waiting for a CallbackQuery...")
|
||||||
while True:
|
while True:
|
||||||
# Get the next update
|
# Get the next update
|
||||||
update = self.__receive_next_update()
|
update = self.__receive_next_update()
|
||||||
# Ensure the update isn't a CancelSignal
|
# If a CancelSignal is received...
|
||||||
if cancellable and isinstance(update, CancelSignal):
|
if isinstance(update, CancelSignal):
|
||||||
|
# And the wait is cancellable...
|
||||||
|
if cancellable:
|
||||||
# Return the CancelSignal
|
# Return the CancelSignal
|
||||||
return update
|
return update
|
||||||
|
else:
|
||||||
|
# Ignore the signal
|
||||||
|
continue
|
||||||
# Ensure the update is a CallbackQuery
|
# Ensure the update is a CallbackQuery
|
||||||
if update.callback_query is None:
|
if update.callback_query is None:
|
||||||
continue
|
continue
|
||||||
|
@ -428,16 +449,12 @@ class Worker(threading.Thread):
|
||||||
message_id=callback.message.message_id,
|
message_id=callback.message.message_id,
|
||||||
caption=product.text(cart_qty=cart[callback.message.message_id][1]),
|
caption=product.text(cart_qty=cart[callback.message.message_id][1]),
|
||||||
reply_markup=product_inline_keyboard)
|
reply_markup=product_inline_keyboard)
|
||||||
# Create the cart summary
|
|
||||||
product_list = ""
|
self.bot.edit_message_text(
|
||||||
total_cost = utils.Price(0)
|
chat_id=self.chat.id,
|
||||||
for product_id in cart:
|
message_id=final_msg.message_id,
|
||||||
if cart[product_id][1] > 0:
|
text=strings.conversation_confirm_cart.format(product_list=self.__get_cart_summary(cart),
|
||||||
product_list += cart[product_id][0].text(style="short", cart_qty=cart[product_id][1]) + "\n"
|
total_cost=str(self.__get_cart_value(cart))),
|
||||||
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)
|
reply_markup=final_inline_keyboard)
|
||||||
# If the Remove from cart button has been pressed...
|
# If the Remove from cart button has been pressed...
|
||||||
elif callback.data == "cart_remove":
|
elif callback.data == "cart_remove":
|
||||||
|
@ -476,16 +493,12 @@ class Worker(threading.Thread):
|
||||||
message_id=callback.message.message_id,
|
message_id=callback.message.message_id,
|
||||||
caption=product.text(cart_qty=cart[callback.message.message_id][1]),
|
caption=product.text(cart_qty=cart[callback.message.message_id][1]),
|
||||||
reply_markup=product_inline_keyboard)
|
reply_markup=product_inline_keyboard)
|
||||||
# Create the cart summary
|
|
||||||
product_list = ""
|
self.bot.edit_message_text(
|
||||||
total_cost = utils.Price(0)
|
chat_id=self.chat.id,
|
||||||
for product_id in cart:
|
message_id=final_msg.message_id,
|
||||||
if cart[product_id][1] > 0:
|
text=strings.conversation_confirm_cart.format(product_list=self.__get_cart_summary(cart),
|
||||||
product_list += cart[product_id][0].text(style="short", cart_qty=cart[product_id][1]) + "\n"
|
total_cost=str(self.__get_cart_value(cart))),
|
||||||
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)
|
reply_markup=final_inline_keyboard)
|
||||||
# If the done button has been pressed...
|
# If the done button has been pressed...
|
||||||
elif callback.data == "cart_done":
|
elif callback.data == "cart_done":
|
||||||
|
@ -505,22 +518,50 @@ class Worker(threading.Thread):
|
||||||
# Add the record to the session and get an ID
|
# Add the record to the session and get an ID
|
||||||
self.session.add(order)
|
self.session.add(order)
|
||||||
self.session.flush()
|
self.session.flush()
|
||||||
# For each product added to the cart, create a new OrderItem and get the total value
|
# For each product added to the cart, create a new OrderItem
|
||||||
value = 0
|
|
||||||
for product in cart:
|
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
|
# Create {quantity} new OrderItems
|
||||||
for i in range(0, cart[product][1]):
|
for i in range(0, cart[product][1]):
|
||||||
order_item = db.OrderItem(product=cart[product][0],
|
order_item = db.OrderItem(product=cart[product][0],
|
||||||
order_id=order.order_id)
|
order_id=order.order_id)
|
||||||
self.session.add(order_item)
|
self.session.add(order_item)
|
||||||
# Ensure the user has enough credit to make the purchase
|
# 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)
|
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
|
# Rollback all the changes
|
||||||
self.session.rollback()
|
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
|
# Create a new transaction and add it to the session
|
||||||
transaction = db.Transaction(user=self.user,
|
transaction = db.Transaction(user=self.user,
|
||||||
value=value,
|
value=value,
|
||||||
|
@ -532,6 +573,10 @@ class Worker(threading.Thread):
|
||||||
self.user.recalculate_credit()
|
self.user.recalculate_credit()
|
||||||
# Commit all the changes
|
# Commit all the changes
|
||||||
self.session.commit()
|
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
|
# 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,
|
self.bot.send_message(self.chat.id, strings.success_order_created.format(order=order.get_text(self.session,
|
||||||
user=True)))
|
user=True)))
|
||||||
|
@ -583,7 +628,8 @@ class Worker(threading.Thread):
|
||||||
self.bot.send_message(self.chat.id, strings.conversation_payment_method,
|
self.bot.send_message(self.chat.id, strings.conversation_payment_method,
|
||||||
reply_markup=telegram.ReplyKeyboardMarkup(keyboard, one_time_keyboard=True))
|
reply_markup=telegram.ReplyKeyboardMarkup(keyboard, one_time_keyboard=True))
|
||||||
# Wait for a reply from the user
|
# 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 the user has selected the Cash option...
|
||||||
if selection == strings.menu_cash:
|
if selection == strings.menu_cash:
|
||||||
# Go to the pay with cash function
|
# Go to the pay with cash function
|
||||||
|
@ -594,7 +640,7 @@ class Worker(threading.Thread):
|
||||||
# Go to the pay with credit card function
|
# Go to the pay with credit card function
|
||||||
self.__add_credit_cc()
|
self.__add_credit_cc()
|
||||||
# If the user has selected the Cancel option...
|
# If the user has selected the Cancel option...
|
||||||
elif selection == strings.menu_cancel:
|
elif isinstance(selection, CancelSignal):
|
||||||
# Send him back to the previous menu
|
# Send him back to the previous menu
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -602,11 +648,9 @@ class Worker(threading.Thread):
|
||||||
"""Add money to the wallet through a credit card payment."""
|
"""Add money to the wallet through a credit card payment."""
|
||||||
log.debug("Displaying __add_credit_cc")
|
log.debug("Displaying __add_credit_cc")
|
||||||
# Create a keyboard to be sent later
|
# Create a keyboard to be sent later
|
||||||
keyboard = [[telegram.KeyboardButton(str(utils.Price("10.00")))],
|
presets = list(map(lambda s: s.strip(" "), configloader.config["Credit Card"]["payment_presets"].split('|')))
|
||||||
[telegram.KeyboardButton(str(utils.Price("25.00")))],
|
keyboard = [[telegram.KeyboardButton(str(utils.Price(preset)))] for preset in presets]
|
||||||
[telegram.KeyboardButton(str(utils.Price("50.00")))],
|
keyboard.append([telegram.KeyboardButton(strings.menu_cancel)])
|
||||||
[telegram.KeyboardButton(str(utils.Price("100.00")))],
|
|
||||||
[telegram.KeyboardButton(strings.menu_cancel)]]
|
|
||||||
# Boolean variable to check if the user has cancelled the action
|
# Boolean variable to check if the user has cancelled the action
|
||||||
cancelled = False
|
cancelled = False
|
||||||
# Loop used to continue asking if there's an error during the input
|
# 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,
|
self.bot.send_message(self.chat.id, strings.payment_cc_amount,
|
||||||
reply_markup=telegram.ReplyKeyboardMarkup(keyboard, one_time_keyboard=True))
|
reply_markup=telegram.ReplyKeyboardMarkup(keyboard, one_time_keyboard=True))
|
||||||
# Wait until a valid amount is sent
|
# 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 the user cancelled the action
|
||||||
if selection == strings.menu_cancel:
|
if isinstance(selection, CancelSignal):
|
||||||
# Exit the loop
|
# Exit the loop
|
||||||
cancelled = True
|
cancelled = True
|
||||||
continue
|
continue
|
||||||
|
@ -627,31 +671,33 @@ class Worker(threading.Thread):
|
||||||
if value > utils.Price(int(configloader.config["Credit Card"]["max_amount"])):
|
if value > utils.Price(int(configloader.config["Credit Card"]["max_amount"])):
|
||||||
self.bot.send_message(self.chat.id,
|
self.bot.send_message(self.chat.id,
|
||||||
strings.error_payment_amount_over_max.format(
|
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
|
continue
|
||||||
elif value < utils.Price(int(configloader.config["Credit Card"]["min_amount"])):
|
elif value < utils.Price(int(configloader.config["Credit Card"]["min_amount"])):
|
||||||
self.bot.send_message(self.chat.id,
|
self.bot.send_message(self.chat.id,
|
||||||
strings.error_payment_amount_under_min.format(
|
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
|
continue
|
||||||
break
|
break
|
||||||
# If the user cancelled the action...
|
# If the user cancelled the action...
|
||||||
else:
|
else:
|
||||||
# Exit the function
|
# Exit the function
|
||||||
return
|
return
|
||||||
|
# Issue the payment invoice
|
||||||
|
self.__make_payment(amount=value)
|
||||||
|
|
||||||
|
def __make_payment(self, amount):
|
||||||
# Set the invoice active invoice payload
|
# Set the invoice active invoice payload
|
||||||
self.invoice_payload = str(uuid.uuid4())
|
self.invoice_payload = str(uuid.uuid4())
|
||||||
# Create the price array
|
# Create the price array
|
||||||
prices = [telegram.LabeledPrice(label=strings.payment_invoice_label, amount=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
|
# 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 = int(self.__get_total_fee(amount))
|
||||||
fee_fixed = int(configloader.config["Credit Card"]["fee_fixed"])
|
if fee > 0:
|
||||||
total_fee = value * fee_percentage + fee_fixed
|
prices.append(telegram.LabeledPrice(label=strings.payment_invoice_fee_label,
|
||||||
if total_fee > 0:
|
amount=fee))
|
||||||
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
|
|
||||||
# Create the invoice keyboard
|
# Create the invoice keyboard
|
||||||
inline_keyboard = telegram.InlineKeyboardMarkup([[telegram.InlineKeyboardButton(strings.menu_pay, pay=True)],
|
inline_keyboard = telegram.InlineKeyboardMarkup([[telegram.InlineKeyboardButton(strings.menu_pay, pay=True)],
|
||||||
[telegram.InlineKeyboardButton(strings.menu_cancel,
|
[telegram.InlineKeyboardButton(strings.menu_cancel,
|
||||||
|
@ -659,7 +705,7 @@ class Worker(threading.Thread):
|
||||||
# The amount is valid, send the invoice
|
# The amount is valid, send the invoice
|
||||||
self.bot.send_invoice(self.chat.id,
|
self.bot.send_invoice(self.chat.id,
|
||||||
title=strings.payment_invoice_title,
|
title=strings.payment_invoice_title,
|
||||||
description=strings.payment_invoice_description.format(amount=str(value)),
|
description=strings.payment_invoice_description.format(amount=str(amount)),
|
||||||
payload=self.invoice_payload,
|
payload=self.invoice_payload,
|
||||||
provider_token=configloader.config["Credit Card"]["credit_card_token"],
|
provider_token=configloader.config["Credit Card"]["credit_card_token"],
|
||||||
start_parameter="tempdeeplink",
|
start_parameter="tempdeeplink",
|
||||||
|
@ -669,7 +715,7 @@ class Worker(threading.Thread):
|
||||||
need_email=configloader.config["Credit Card"]["email_required"] == "yes",
|
need_email=configloader.config["Credit Card"]["email_required"] == "yes",
|
||||||
need_phone_number=configloader.config["Credit Card"]["phone_required"] == "yes",
|
need_phone_number=configloader.config["Credit Card"]["phone_required"] == "yes",
|
||||||
reply_markup=inline_keyboard)
|
reply_markup=inline_keyboard)
|
||||||
# Wait for the invoice
|
# Wait for the precheckout query
|
||||||
precheckoutquery = self.__wait_for_precheckoutquery(cancellable=True)
|
precheckoutquery = self.__wait_for_precheckoutquery(cancellable=True)
|
||||||
# Check if the user has cancelled the invoice
|
# Check if the user has cancelled the invoice
|
||||||
if isinstance(precheckoutquery, CancelSignal):
|
if isinstance(precheckoutquery, CancelSignal):
|
||||||
|
@ -681,10 +727,11 @@ class Worker(threading.Thread):
|
||||||
successfulpayment = self.__wait_for_successfulpayment()
|
successfulpayment = self.__wait_for_successfulpayment()
|
||||||
# Create a new database transaction
|
# Create a new database transaction
|
||||||
transaction = db.Transaction(user=self.user,
|
transaction = db.Transaction(user=self.user,
|
||||||
value=successfulpayment.total_amount - int(total_fee),
|
value=int(successfulpayment.total_amount) - fee,
|
||||||
provider="Credit Card",
|
provider="Credit Card",
|
||||||
telegram_charge_id=successfulpayment.telegram_payment_charge_id,
|
telegram_charge_id=successfulpayment.telegram_payment_charge_id,
|
||||||
provider_charge_id=successfulpayment.provider_payment_charge_id)
|
provider_charge_id=successfulpayment.provider_payment_charge_id)
|
||||||
|
|
||||||
if successfulpayment.order_info is not None:
|
if successfulpayment.order_info is not None:
|
||||||
transaction.payment_name = successfulpayment.order_info.name
|
transaction.payment_name = successfulpayment.order_info.name
|
||||||
transaction.payment_email = successfulpayment.order_info.email
|
transaction.payment_email = successfulpayment.order_info.email
|
||||||
|
@ -694,6 +741,17 @@ class Worker(threading.Thread):
|
||||||
# Commit all the changes
|
# Commit all the changes
|
||||||
self.session.commit()
|
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):
|
def __bot_info(self):
|
||||||
"""Send information about the bot."""
|
"""Send information about the bot."""
|
||||||
log.debug("Displaying __bot_info")
|
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,
|
self.bot.send_message(self.chat.id, strings.conversation_admin_select_product,
|
||||||
reply_markup=telegram.ReplyKeyboardMarkup(keyboard, one_time_keyboard=True))
|
reply_markup=telegram.ReplyKeyboardMarkup(keyboard, one_time_keyboard=True))
|
||||||
# Wait for a reply from the user
|
# 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 the user has selected the Cancel option...
|
||||||
if selection == strings.menu_cancel:
|
if isinstance(selection, CancelSignal):
|
||||||
# Exit the menu
|
# Exit the menu
|
||||||
return
|
return
|
||||||
# If the user has selected the Add Product option...
|
# 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,
|
self.bot.send_message(self.chat.id, strings.conversation_admin_select_product_to_delete,
|
||||||
reply_markup=telegram.ReplyKeyboardMarkup(keyboard, one_time_keyboard=True))
|
reply_markup=telegram.ReplyKeyboardMarkup(keyboard, one_time_keyboard=True))
|
||||||
# Wait for a reply from the user
|
# 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 selection == strings.menu_cancel:
|
if isinstance(selection, CancelSignal):
|
||||||
# Exit the menu
|
# Exit the menu
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
|
@ -1229,7 +1287,7 @@ class Worker(threading.Thread):
|
||||||
callback = self.__wait_for_inlinekeyboard_callback()
|
callback = self.__wait_for_inlinekeyboard_callback()
|
||||||
# Toggle the correct property
|
# Toggle the correct property
|
||||||
if callback.data == "toggle_edit_products":
|
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":
|
elif callback.data == "toggle_receive_orders":
|
||||||
admin.receive_orders = not admin.receive_orders
|
admin.receive_orders = not admin.receive_orders
|
||||||
elif callback.data == "toggle_create_transactions":
|
elif callback.data == "toggle_create_transactions":
|
||||||
|
|
Loading…
Reference in a new issue