1
Fork 0
mirror of https://github.com/Steffo99/greed.git synced 2025-03-13 12:17:27 +00:00
This commit is contained in:
pzhuk 2020-04-15 14:29:18 +03:00 committed by GitHub
commit bfc4ff6430
Signed by: github
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 164 additions and 91 deletions

View file

@ -1,10 +1,12 @@
import importlib
import sys
import threading
import telegram
import worker
import configloader
import utils
import threading
import importlib
import worker
language = configloader.config["Config"]["language"]
strings = importlib.import_module("strings." + language)

View file

@ -1,13 +1,17 @@
import typing
from sqlalchemy import create_engine, Column, ForeignKey, UniqueConstraint
from sqlalchemy import Integer, BigInteger, String, Text, LargeBinary, DateTime, Boolean
from sqlalchemy.orm import sessionmaker, relationship, backref
from sqlalchemy.ext.declarative import declarative_base
import configloader
import telegram
import requests
import utils
import datetime
import importlib
import typing
import requests
import telegram
from sqlalchemy import (BigInteger, Boolean, Column, DateTime, ForeignKey,
Integer, LargeBinary, String, Text, UniqueConstraint,
create_engine)
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import backref, relationship, sessionmaker
import configloader
import utils
language = configloader.config["Config"]["language"]
strings = importlib.import_module("strings." + language)
@ -31,9 +35,10 @@ class User(TableDeclarativeBase):
first_name = Column(String, nullable=False)
last_name = Column(String)
username = Column(String)
last_seen = Column(DateTime, default=datetime.datetime.utcnow)
# Current wallet credit
credit = Column(Integer, nullable=False)
credit = Column(Integer, nullable=False, server_default=0)
# Extra table parameters
__tablename__ = "users"
@ -46,8 +51,6 @@ class User(TableDeclarativeBase):
self.first_name = telegram_chat.first_name
self.last_name = telegram_chat.last_name
self.username = telegram_chat.username
# The starting wallet value is 0
self.credit = 0
def __str__(self):
"""Describe the user in the best way possible given the available data."""

View file

@ -115,8 +115,13 @@ conversation_live_orders_start = "You are in <b>Live Orders</b> mode\n" \
conversation_open_help_menu = "What kind of help do you need?"
# Conversation: confirm promotion to admin
conversation_confirm_admin_promotion = "Are you sure you want to promote this user to 💼 Manager?\n" \
"It is an irreversible action!"
conversation_confirm_admin_promotion = "Are you sure you want to promote this user to 💼 Manager?\n"
# Conversation: remove administrator
conversation_admin_dismissal_menu = "🗑 Remove Manager"
# Conversation: administrator removal confirmation
conversation_confirm_admin_dismissal = "🗑 User was successfully removed from the Managers!"
# Conversation: switching to user mode
conversation_switch_to_user_mode = " You are switching to 👤 Customer mode.\n" \
@ -296,6 +301,9 @@ downloading_image = "I'm downloading your photo!\n" \
"It might take a while... Please be patient!\n" \
"I won't be able to answer you while I'm downloading."
downloading_image_failed = "Something went wrong with the image upload handling" \
"Please, try again"
# Edit product: current value
edit_current_value = "The current value is:\n" \
"<pre>{value}</pre>\n" \

View file

@ -115,8 +115,13 @@ conversation_live_orders_start = "Sei in modalità di <b>Ricezione Ordini</b>!\n
conversation_open_help_menu = "Che tipo di assistenza desideri ricevere?"
# Conversation: confirm promotion to admin
conversation_confirm_admin_promotion = "Sei sicuro di voler promuovere questo utente a 💼 Gestore?\n" \
"E' un'azione irreversibile!"
conversation_confirm_admin_promotion = "Sei sicuro di voler promuovere questo utente a 💼 Gestore?\n"
# Conversation: remove administrator
conversation_admin_dismissal_menu = "🗑 Elimina Gestore"
# Conversation: administrator removal confirmation
conversation_confirm_admin_dismissal = "🗑 Gestore eliminato con successo!"
# Conversation: switching to user mode
conversation_switch_to_user_mode = "Stai passando alla modalità 👤 Cliente.\n" \
@ -297,6 +302,10 @@ downloading_image = "Sto scaricando la tua foto!\n" \
"Potrei metterci un po'... Abbi pazienza!\n" \
"Non sarò in grado di risponderti durante il download."
# There was an error reading the image the user sent
downloading_image_failed = "Si è verificato un errore durante il download dell'immagine...\n" \
"Riprova, per favore!"
# Edit product: current value
edit_current_value = "Il valore attuale è:\n" \
"<pre>{value}</pre>\n" \

View file

@ -111,8 +111,13 @@ conversation_live_orders_start = "Вы в режиме <b>Новые заказ
conversation_open_help_menu = "Чем могу Вам помочь?"
# Conversation: confirm promotion to admin
conversation_confirm_admin_promotion = "Вы уверены, что хотите повысить этого пользователя до 💼 Менеджера?\n" \
"Это действие невозможно отменить!"
conversation_confirm_admin_promotion = "Вы уверены, что хотите повысить этого пользователя до 💼 Менеджера?\n"
# Conversation: remove administrator
conversation_admin_dismissal_menu = "🗑 Удалить менеджера"
# Conversation: administrator removal confirmation
conversation_confirm_admin_dismissal = "🗑 Менеджер удален"
# Conversation: switching to user mode
conversation_switch_to_user_mode = " Вы перешли в режим 👤 Покупателя.\n" \
@ -265,7 +270,7 @@ ask_refund_reason = " Сообщите причину возврата сред
# Edit credit: notes?
ask_transaction_notes = " Добавьте сообщение к транзакции.\n" \
" Сообщение будет доступно 👤 Покупателю после пополнения/списания средств" \
" и 💼 Администратору в логах транзакций."
" и 💼 Менеджеру в логах транзакций."
# Edit credit: amount?
ask_credit = "Вы хотите изменить баланс Покупателя?\n" \
@ -294,6 +299,9 @@ downloading_image = "Я загружаю фото!\n" \
"Это может занять некоторое время...!\n" \
"Я не смогу отвечать, пока идет загрузка."
downloading_image_failed = "Ошибка при загрузке изображения" \
"Попробуйте еще раз..."
# Edit product: current value
edit_current_value = "Текущее значение:\n" \
"<pre>{value}</pre>\n" \

View file

@ -111,8 +111,13 @@ conversation_live_orders_start = "Ви в режимі <b>Свіжі Замов
conversation_open_help_menu = "Як можемо Вам допомогти?"
# Conversation: confirm promotion to admin
conversation_confirm_admin_promotion = "Ви впевнені, що хочете підвищити цього користувача до 💼 Менеджера?\n" \
"Цю дію неможливо відмінити!"
conversation_confirm_admin_promotion = "Ви впевнені, що хочете підвищити цього користувача до 💼 Менеджера?\n"
# Conversation: remove administrator
conversation_admin_dismissal_menu = "🗑 Видалити менеджера"
# Conversation: administrator removal confirmation
conversation_confirm_admin_dismissal = "🗑 Администратора видалено"
# Conversation: switching to user mode
conversation_switch_to_user_mode = " Ви перейшли в режим 👤 Замовника.\n" \
@ -265,7 +270,7 @@ ask_refund_reason = " Напишіть причину повернення ко
# Edit credit: notes?
ask_transaction_notes = " Додайте повідомлення до транзакції.\n" \
"👤 Повідомлення буде доступне замовнику після поповнення/списання" \
" і 💼 Адміністратору в логах транзакцій."
" і 💼 Менеджеру в логах транзакцій."
# Edit credit: amount?
ask_credit = "Як ви хочете змінити баланс замовника?\n" \
@ -294,6 +299,9 @@ downloading_image = "Я завантажую фото!\n" \
"Може зайняти деякий час... Майте терпіння!\n" \
"Я не зможу відповідати, поки йде завантаження."
downloading_image_failed = "Помилка при завантаженні зображення" \
"Спробуйте ще раз..."
# Edit product: current value
edit_current_value = "Поточне значення:\n" \
"<pre>{value}</pre>\n" \

View file

@ -1,11 +1,13 @@
import telegram
import telegram.error
import time
from configloader import config
import typing
import importlib
import os
import sys
import importlib
import time
import typing
import telegram
import telegram.error
from configloader import config
language = config["Config"]["language"]
try:

153
worker.py
View file

@ -1,19 +1,23 @@
import threading
from typing import *
import uuid
import datetime
import telegram
import configloader
import sys
import queue as queuem
import database as db
import re
import utils
import os
import traceback
from html import escape
import requests
import importlib
import os
import queue as queuem
import re
import sys
import threading
import traceback
import uuid
from datetime import datetime
from html import escape
from operator import attrgetter
from typing import Dict, List, Optional, Union
import requests
import telegram
from sqlalchemy import or_
import configloader
import database as db
import utils
language = configloader.config["Config"]["language"]
strings = importlib.import_module("strings." + language)
@ -91,6 +95,10 @@ class ChatWorker(threading.Thread):
self.session.add(self.admin)
# Commit the transaction
self.session.commit()
else:
# Update users last seen date
self.user.last_seen = datetime.now()
self.session.commit()
# Capture exceptions that occour during the conversation
try:
# If the user is not an admin, send him to the user menu
@ -200,7 +208,7 @@ class ChatWorker(threading.Thread):
if match is None:
continue
# Return the first capture group
return match.group(1)
return match.group(1) or match.group(2)
def __wait_for_precheckoutquery(self,
cancellable: bool = False) -> Union[telegram.PreCheckoutQuery, CancelSignal]:
@ -258,8 +266,21 @@ class ChatWorker(threading.Thread):
# Ensure the message contains a photo
if update.message.photo is None:
continue
# Return the photo array
return update.message.photo
# If photo message has been sent
if len(update.message.photo) > 0:
# Find object with maximum width
photo = max(update.message.photo, key=attrgetter('width'))
break
else:
self.bot.send_message(self.chat.id, strings.downloading_image_failed)
continue
# If a photo has been sent...
# Notify the user that the bot is downloading the image and might be inactive for a while
self.bot.send_message(self.chat.id, strings.downloading_image)
self.bot.send_chat_action(self.chat.id, action="upload_photo")
# Return file object associated with the photo
return self.bot.get_file(photo.file_id)
def __wait_for_inlinekeyboard_callback(self, cancellable: bool = False) \
-> Union[telegram.CallbackQuery, CancelSignal]:
@ -286,8 +307,11 @@ class ChatWorker(threading.Thread):
def __user_select(self) -> Union[db.User, CancelSignal]:
"""Select an user from the ones in the database."""
# Find all the users in the database
users = self.session.query(db.User).order_by(db.User.user_id).all()
# Find first five users in the database
users = self.session.query(db.User) \
.order_by(db.User.last_seen.desc()) \
.limit(5) \
.all()
# Create a list containing all the keyboard button strings
keyboard_buttons = [[strings.menu_cancel]]
# Add to the list all the users
@ -300,12 +324,14 @@ class ChatWorker(threading.Thread):
# Send the keyboard
self.bot.send_message(self.chat.id, strings.conversation_admin_select_user, reply_markup=keyboard)
# Wait for a reply
reply = self.__wait_for_regex("user_([0-9]+)", cancellable=True)
reply = self.__wait_for_regex("user_([0-9]+)|\@(.*)", cancellable=True)
# Propagate CancelSignals
if isinstance(reply, CancelSignal):
return reply
# Find the user in the database
user = self.session.query(db.User).filter_by(user_id=int(reply)).one_or_none()
user = self.session.query(db.User) \
.filter(or_(db.User.user_id==reply, db.User.username==reply)) \
.one_or_none()
# Ensure the user exists
if not user:
self.bot.send_message(self.chat.id, strings.error_user_does_not_exist)
@ -492,7 +518,7 @@ class ChatWorker(threading.Thread):
notes = self.__wait_for_regex(r"(.*)", cancellable=True)
# Create a new Order
order = db.Order(user=self.user,
creation_date=datetime.datetime.now(),
creation_date=datetime.now(),
notes=notes if not isinstance(notes, CancelSignal) else "")
# Add the record to the session and get an ID
self.session.add(order)
@ -805,7 +831,7 @@ class ChatWorker(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, cancellable = True)
selection = self.__wait_for_specific_message(product_names, cancellable=True)
# If the user has selected the Cancel option...
if isinstance(selection, CancelSignal):
# Exit the menu
@ -875,10 +901,7 @@ class ChatWorker(threading.Thread):
price = None
else:
price = utils.Price(price)
# Ask for the product image
self.bot.send_message(self.chat.id, strings.ask_product_image, reply_markup=cancel)
# Wait for an answer
photo_list = self.__wait_for_photo(cancellable=True)
# If a new product is being added...
if not product:
# Create the db record for the product
@ -895,20 +918,13 @@ class ChatWorker(threading.Thread):
product.name = name if not isinstance(name, CancelSignal) else product.name
product.description = description if not isinstance(description, CancelSignal) else product.description
product.price = int(price) if not isinstance(price, CancelSignal) else product.price
# If a photo has been sent...
if isinstance(photo_list, list):
# Find the largest photo id
largest_photo = photo_list[0]
for photo in photo_list[1:]:
if photo.width > largest_photo.width:
largest_photo = photo
# Get the file object associated with the photo
photo_file = self.bot.get_file(largest_photo.file_id)
# Notify the user that the bot is downloading the image and might be inactive for a while
self.bot.send_message(self.chat.id, strings.downloading_image)
self.bot.send_chat_action(self.chat.id, action="upload_photo")
# Set the image for that product
product.set_image(photo_file)
# Ask for the product image
self.bot.send_message(self.chat.id, strings.ask_product_image, reply_markup=cancel)
# Wait for an answer
photo = self.__wait_for_photo(cancellable=True)
# Set the image for that product
if not isinstance(photo, CancelSignal):
product.set_image(photo)
# Commit the session changes
self.session.commit()
# Notify the user
@ -927,7 +943,7 @@ class ChatWorker(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, cancellable = True)
selection = self.__wait_for_specific_message(product_names, cancellable=True)
if isinstance(selection, CancelSignal):
# Exit the menu
return
@ -988,7 +1004,7 @@ class ChatWorker(threading.Thread):
# If the user pressed the complete order button, complete the order
if update.data == "order_complete":
# Mark the order as complete
order.delivery_date = datetime.datetime.now()
order.delivery_date = datetime.now()
# Commit the transaction
self.session.commit()
# Update order message
@ -1011,7 +1027,7 @@ class ChatWorker(threading.Thread):
self.bot.delete_message(self.chat.id, reason_msg.message_id)
continue
# Mark the order as refunded
order.refund_date = datetime.datetime.now()
order.refund_date = datetime.now()
# Save the refund reason
order.refund_reason = reply
# Refund the credit, reverting the old transaction
@ -1199,66 +1215,83 @@ class ChatWorker(threading.Thread):
# Delete the created file
os.remove(f"transactions_{self.chat.id}.csv")
def __add_admin(self):
def __select_or_create_admin(self):
"""Add an administrator to the bot."""
# Let the admin select an administrator to promote
user = self.__user_select()
# Allow the cancellation of the operation
if isinstance(user, CancelSignal):
return
return None, None
# Check if the user is already an administrator
admin = self.session.query(db.Admin).filter_by(user_id=user.user_id).one_or_none()
# Return if user is not an admin
if admin is None:
# Create the keyboard to be sent
keyboard = telegram.ReplyKeyboardMarkup([[strings.emoji_yes, strings.emoji_no]], one_time_keyboard=True)
# Ask for confirmation
# Ask for confirmation on the admin deletion
self.bot.send_message(self.chat.id, strings.conversation_confirm_admin_promotion, reply_markup=keyboard)
# Wait for an answer
selection = self.__wait_for_specific_message([strings.emoji_yes, strings.emoji_no])
# Proceed only if the answer is yes
if selection == strings.emoji_no:
return
return None, None
# Create a new admin
admin = db.Admin(user=user,
edit_products=False,
receive_orders=False,
create_transactions=False,
is_owner=False,
display_on_help=False)
edit_products=False,
receive_orders=False,
create_transactions=False,
is_owner=False,
display_on_help=False)
self.session.add(admin)
self.session.commit()
return user
def __add_admin(self):
"""Add an administrator to the bot."""
user = self.__select_or_create_admin()
if (user is None or user.admin is None):
return
# Send the empty admin message and record the id
message = self.bot.send_message(self.chat.id, strings.admin_properties.format(name=str(admin.user)))
message = self.bot.send_message(self.chat.id, strings.admin_properties.format(name=str(user)))
# Start accepting edits
while True:
# Create the inline keyboard with the admin status
inline_keyboard = telegram.InlineKeyboardMarkup([
[telegram.InlineKeyboardButton(f"{utils.boolmoji(admin.edit_products)} {strings.prop_edit_products}",
callback_data="toggle_edit_products")],
callback_data="toggle_edit_products")],
[telegram.InlineKeyboardButton(f"{utils.boolmoji(admin.receive_orders)} {strings.prop_receive_orders}",
callback_data="toggle_receive_orders")],
callback_data="toggle_receive_orders")],
[telegram.InlineKeyboardButton(
f"{utils.boolmoji(admin.create_transactions)} {strings.prop_create_transactions}",
callback_data="toggle_create_transactions")],
[telegram.InlineKeyboardButton(
f"{utils.boolmoji(admin.display_on_help)} {strings.prop_display_on_help}",
callback_data="toggle_display_on_help")],
[telegram.InlineKeyboardButton(strings.conversation_admin_dismissal_menu, callback_data="cmd_remove")],
[telegram.InlineKeyboardButton(strings.menu_done, callback_data="cmd_done")]
])
# Update the inline keyboard
self.bot.edit_message_reply_markup(message_id=message.message_id,
chat_id=self.chat.id,
reply_markup=inline_keyboard)
chat_id=self.chat.id,
reply_markup=inline_keyboard)
# Wait for an user answer
callback = self.__wait_for_inlinekeyboard_callback()
# Toggle the correct property
if callback.data == "toggle_edit_products":
admin.edit_products = not admin.edit_products1
admin.edit_products = not admin.edit_products
elif callback.data == "toggle_receive_orders":
admin.receive_orders = not admin.receive_orders
elif callback.data == "toggle_create_transactions":
admin.create_transactions = not admin.create_transactions
elif callback.data == "toggle_display_on_help":
admin.display_on_help = not admin.display_on_help
elif callback.data == "cmd_remove":
self.session.delete(user.admin)
message = self.bot.send_message(self.chat.id, strings.conversation_confirm_admin_dismissal)
break
elif callback.data == "cmd_done":
break
self.session.commit()