Skip to content

Commit 4ff08f3

Browse files
Merge pull request #167 from MothScientist/fix
Fix
2 parents 9b29748 + c2aeeba commit 4ff08f3

16 files changed

+233
-97
lines changed

budget_graph/bot.py

Lines changed: 71 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,17 @@
1313

1414
sys_path.append('../')
1515
from budget_graph.logger import setup_logger
16-
from budget_graph.time_checking import timeit
16+
# from budget_graph.time_checking import timeit
1717
from budget_graph.create_csv import CsvFileWithTable
1818
from budget_graph.registration_service import user_registration
1919
from budget_graph.dictionary import Stickers, receive_translation
2020
from budget_graph.encryption import getting_hash, get_salt, logging_hash
2121
from budget_graph.user_cache_structure import UserLanguageCache, UserRegistrationStatusCache
2222
from budget_graph.db_manager import DatabaseQueries, connect_db, close_db, connect_defer_close_db
23-
from budget_graph.helpers import get_category_button_labels, get_bot_commands, get_category_translate, \
24-
get_timezone_buttons
2523
from budget_graph.validation import date_validation, value_validation, description_validation, username_validation, \
2624
password_validation, category_validation
25+
from budget_graph.helpers import StorageMsgIdForDeleteAfterOperation, get_category_button_labels, get_bot_commands, \
26+
get_category_translate, get_timezone_buttons, get_language_buttons
2727

2828

2929
load_dotenv() # Load environment variables from .env file
@@ -103,13 +103,10 @@ def group_settings_get_buttons(message):
103103
reply_markup=markup_1)
104104

105105

106-
@timeit
107-
def reply_buttons(message):
106+
@connect_defer_close_db
107+
def reply_buttons(db_connection, message):
108108
telegram_id: int = message.from_user.id
109-
connection = connect_db()
110-
bot_db = DatabaseQueries(connection)
111-
res: str = bot_db.get_username_by_telegram_id(telegram_id)
112-
close_db(connection)
109+
res: str = db_connection.get_username_by_telegram_id(telegram_id)
113110
if res:
114111
reply_menu_buttons_register(message)
115112
else:
@@ -162,6 +159,32 @@ def get_my_id(message) -> None:
162159
f"telegram ID: {message.from_user.id}")
163160

164161

162+
@bot.message_handler(commands=['del_msg_transaction'])
163+
@connect_defer_close_db
164+
def del_msg_transaction(db_connection, message) -> None:
165+
telegram_id: int = message.from_user.id
166+
chat_id: int = message.chat.id
167+
user_language: str = check_user_language(telegram_id)
168+
169+
reg_res: bool = user_is_registered(telegram_id)
170+
if not reg_res:
171+
bot.send_message(chat_id, receive_translation(user_language, 'not_register'))
172+
bot.send_sticker(chat_id, Stickers.get_sticker_by_id('id_5'))
173+
logger_bot.info(f'[del_msg_transaction] Unregistered user interaction. TelegramID: {logging_hash(telegram_id)}')
174+
reply_menu_buttons_not_register(message)
175+
return
176+
177+
old_feature_status: bool = db_connection.get_feature_status_del_msg_after_transaction(telegram_id)
178+
res: bool = db_connection.change_feature_status_del_msg_after_transaction(telegram_id)
179+
if res and old_feature_status: # the functionality was enabled
180+
bot.send_message(chat_id, receive_translation(user_language, 'del_msg_transaction_of'))
181+
elif res and not old_feature_status: # the functionality was disabled
182+
bot.send_message(chat_id, receive_translation(user_language, 'del_msg_transaction_on'))
183+
else:
184+
bot.send_message(chat_id, f"{receive_translation(user_language, 'del_msg_transaction_on')}\n"
185+
f"{receive_translation(user_language, 'contact_support')}")
186+
187+
165188
@bot.message_handler(commands=['project_github'])
166189
def project_github(message) -> None:
167190
telegram_id: int = message.from_user.id
@@ -229,19 +252,7 @@ def callback_query_change_timezone(call):
229252
def change_language(message) -> None:
230253
telegram_id: int = message.from_user.id
231254
user_language: str = check_user_language(telegram_id)
232-
markup_1 = InlineKeyboardMarkup(row_width=2)
233-
# button_labels: dict = {'English': 'en', 'Español': 'es', 'Русский': 'ru', 'Français': 'fr', ...} - cache
234-
235-
# TODO - cache
236-
markup_1.add(InlineKeyboardButton('English', callback_data='change_language_en'))
237-
markup_1.add(InlineKeyboardButton('Español', callback_data='change_language_es'))
238-
markup_1.add(InlineKeyboardButton('Русский', callback_data='change_language_ru'))
239-
markup_1.add(InlineKeyboardButton('Français', callback_data='change_language_fr'))
240-
markup_1.add(InlineKeyboardButton('Deutsch', callback_data='change_language_de'))
241-
markup_1.add(InlineKeyboardButton('Islenskur', callback_data='change_language_is'))
242-
markup_1.add(InlineKeyboardButton('Português', callback_data='change_language_pt'))
243-
markup_1.add(InlineKeyboardButton('қазақ', callback_data='change_language_kk'))
244-
255+
markup_1 = InlineKeyboardMarkup(get_language_buttons())
245256
bot.send_message(message.chat.id, f"{receive_translation(user_language, 'choose_language')}:",
246257
reply_markup=markup_1)
247258

@@ -304,17 +315,17 @@ def get_my_token(db_connection, message, user_language: str) -> None:
304315
bot.send_message(message.chat.id, token)
305316

306317

307-
def add_income(message, user_language: str) -> None:
308-
bot.send_message(message.chat.id, f"{receive_translation(user_language, "enter_income")}:")
309-
bot.register_next_step_handler(message, process_add_date_for_transfer, False)
310-
311-
312-
def add_expense(message, user_language: str) -> None:
313-
bot.send_message(message.chat.id, f"{receive_translation(user_language, "enter_expense")}:")
314-
bot.register_next_step_handler(message, process_add_date_for_transfer, True)
318+
@connect_defer_close_db
319+
def start_transaction(db_connection, message, user_language: str, is_negative: bool) -> None:
320+
text_key: str = 'enter_expense' if is_negative else 'enter_income'
321+
msg = bot.send_message(message.chat.id, f"{receive_translation(user_language, text_key)}:")
322+
feature_is_active: bool = db_connection.get_feature_status_del_msg_after_transaction(message.from_user.id)
323+
msg_del_obj = StorageMsgIdForDeleteAfterOperation(feature_is_active)
324+
msg_del_obj.append(msg.id)
325+
bot.register_next_step_handler(message, process_add_date_for_transfer, is_negative, msg_del_obj)
315326

316327

317-
def process_add_date_for_transfer(message, is_negative: bool) -> None:
328+
def process_add_date_for_transfer(message, is_negative: bool, msg_del_obj: StorageMsgIdForDeleteAfterOperation) -> None:
318329
"""
319330
Adds income and expense to the database.
320331
Accepts an unvalidated value,
@@ -324,9 +335,11 @@ def process_add_date_for_transfer(message, is_negative: bool) -> None:
324335
message:
325336
is_negative (bool): False if X > 0 (add_income), True if X < 0 (add_expense)
326337
X = 0 - will be rejected by the validator
338+
msg_del_obj:
327339
328340
Returns: None
329341
"""
342+
msg_del_obj.append(message.message_id)
330343
telegram_id: int = message.from_user.id
331344
user_language: str = check_user_language(telegram_id)
332345
value: str = message.text
@@ -338,14 +351,17 @@ def process_add_date_for_transfer(message, is_negative: bool) -> None:
338351

339352
if value:
340353
value *= -1 if is_negative else 1
341-
bot.send_message(message.chat.id, f"{receive_translation(user_language, "set_date")} (DD/MM/YYYY)",
342-
reply_markup=markup_1)
343-
bot.register_next_step_handler(message, process_add_category_for_transfer, value, user_language)
354+
msg = bot.send_message(message.chat.id, f"{receive_translation(user_language, "set_date")} (DD/MM/YYYY)",
355+
reply_markup=markup_1)
356+
msg_del_obj.append(msg.id)
357+
bot.register_next_step_handler(message, process_add_category_for_transfer, value, user_language, msg_del_obj)
344358
else:
345359
bot.send_message(message.chat.id, receive_translation(user_language, 'invalid_value'))
346360

347361

348-
def process_add_category_for_transfer(message, value: int, user_language: str) -> None:
362+
def process_add_category_for_transfer(message, value: int, user_language: str,
363+
msg_del_obj: StorageMsgIdForDeleteAfterOperation) -> None:
364+
msg_del_obj.append(message.message_id)
349365
markup_1 = ReplyKeyboardMarkup(resize_keyboard=True, row_width=4)
350366
button_labels: tuple = get_category_button_labels(user_language)
351367
buttons: list = [KeyboardButton(label) for label in button_labels] # assembling buttons from the tuple above
@@ -355,24 +371,30 @@ def process_add_category_for_transfer(message, value: int, user_language: str) -
355371
record_date_is_valid: bool = asyncio_run(date_validation(record_date)) # DD/MM/YYYY
356372

357373
if record_date_is_valid:
358-
bot.send_message(message.chat.id, f"{receive_translation(user_language, "select_category")}:",
359-
reply_markup=markup_1)
360-
bot.register_next_step_handler(message, process_add_description_for_transfer, value, record_date, user_language)
374+
msg = bot.send_message(message.chat.id, f"{receive_translation(user_language, "select_category")}:",
375+
reply_markup=markup_1)
376+
msg_del_obj.append(msg.id)
377+
bot.register_next_step_handler(message, process_add_description_for_transfer, value,
378+
record_date, user_language, msg_del_obj)
361379
else:
362380
bot.send_message(message.chat.id, receive_translation(user_language, 'invalid_date'))
363381
reply_buttons(message)
364382

365383

366-
def process_add_description_for_transfer(message, value: int, record_date: str, user_language: str) -> None:
384+
def process_add_description_for_transfer(message, value: int, record_date: str, user_language: str,
385+
msg_del_obj: StorageMsgIdForDeleteAfterOperation) -> None:
386+
msg_del_obj.append(message.message_id)
367387
markup_1 = ReplyKeyboardMarkup(resize_keyboard=True, row_width=4)
368388
btn1 = KeyboardButton(receive_translation(user_language, 'no_description'))
369389
markup_1.add(btn1)
370390
category: str = message.text
371391

372392
if category_validation(user_language, category):
373-
bot.send_message(message.chat.id, receive_translation(user_language, 'add_description'),
374-
reply_markup=markup_1)
375-
bot.register_next_step_handler(message, process_transfer_final, value, record_date, category, user_language)
393+
msg = bot.send_message(message.chat.id, receive_translation(user_language, 'add_description'),
394+
reply_markup=markup_1)
395+
msg_del_obj.append(msg.id)
396+
bot.register_next_step_handler(message, process_transfer_final, value, record_date,
397+
category, user_language, msg_del_obj)
376398
else:
377399
bot.send_message(message.chat.id, receive_translation(user_language, 'invalid_category'))
378400
reply_buttons(message)
@@ -386,8 +408,10 @@ def process_transfer_final(
386408
value: int,
387409
record_date: str,
388410
category: str,
389-
user_language: str
411+
user_language: str,
412+
msg_del_obj: StorageMsgIdForDeleteAfterOperation
390413
) -> None:
414+
msg_del_obj.append(message.message_id)
391415
telegram_id: int = message.from_user.id
392416
description: str = message.text
393417

@@ -396,7 +420,8 @@ def process_transfer_final(
396420

397421
if description_validation(description):
398422
if db_connection.add_transaction_to_db(value, record_date, category, description, telegram_id=telegram_id):
399-
bot.send_message(message.chat.id, f"{receive_translation(user_language, 'entry_add_success')}!")
423+
bot.send_message(chat_id := message.chat.id, f"{receive_translation(user_language, 'entry_add_success')}!")
424+
msg_del_obj.delete_messages(bot, chat_id)
400425
else:
401426
bot.send_message(message.chat.id, f"{receive_translation(user_language, 'entry_add_error')}\n"
402427
f"{receive_translation(user_language, 'contact_support')}")
@@ -896,9 +921,9 @@ def text(message) -> None:
896921
if message.text == f"📖 {receive_translation(user_language, 'view_table')}":
897922
view_table(message, res, user_language)
898923
elif message.text == f"📈 {receive_translation(user_language, 'add_income')}":
899-
add_income(message, user_language)
924+
start_transaction(message, user_language, False)
900925
elif message.text == f"📉 {receive_translation(user_language, 'add_expense')}":
901-
add_expense(message, user_language)
926+
start_transaction(message, user_language, True)
902927
elif message.text == f"❌ {receive_translation(user_language, 'del_record')}":
903928
delete_record(message, user_language)
904929
elif message.text == f"🗃️ {receive_translation(user_language, 'get_csv')}":

budget_graph/db_manager.py

Lines changed: 65 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -522,6 +522,42 @@ def check_limit_users_in_group(self, group_id: int) -> bool:
522522
f"group id: {group_id}")
523523
return False # to prevent a user from being written to a non-existent group
524524

525+
def get_feature_status_del_msg_after_transaction(self, telegram_id: int) -> bool:
526+
"""
527+
Checks the status whether the function is enabled
528+
for the operation of the StorageMsgIdForDeleteAfterOperation class
529+
"""
530+
try:
531+
with self.__conn as conn:
532+
with conn.cursor() as cur:
533+
cur.execute(
534+
read_sql_file('get_feature_status_del_msg_after_transaction'),
535+
{'telegram_id': telegram_id}
536+
)
537+
return bool(cur.fetchone()[0])
538+
539+
except (DatabaseError, TypeError) as err:
540+
logger_database.error(f"[DB_QUERY] {str(err)}, "
541+
f"telegram_id: {telegram_id}")
542+
return False # default value - False
543+
544+
def change_feature_status_del_msg_after_transaction(self, telegram_id: int) -> bool:
545+
try:
546+
with self.__conn as conn:
547+
with conn.cursor() as cur:
548+
cur.execute(
549+
read_sql_file('change_feature_status_del_msg_after_transaction'),
550+
{'telegram_id': telegram_id}
551+
)
552+
conn.commit()
553+
logger_database.info(f"[del_msg_transaction] successfully changed for {logging_hash(telegram_id)}")
554+
return True
555+
556+
except (DatabaseError, TypeError) as err:
557+
logger_database.error(f"[DB_QUERY] {str(err)}, "
558+
f"telegram_id: {telegram_id}")
559+
return False
560+
525561
@timeit
526562
def get_user_language(self, telegram_id: int) -> str:
527563
"""
@@ -782,38 +818,44 @@ def delete_group_with_users(self, group_id: int) -> bool: # TODO REFERENCES IN
782818
with self.__conn as conn:
783819
with conn.cursor() as cur:
784820
cur.execute("""
785-
DELETE FROM
786-
"budget_graph"."users"
787-
WHERE
788-
"telegram_id" IN
789-
(
790-
SELECT
821+
-- users
822+
WITH
823+
telegram_id_users AS (
824+
SELECT
791825
u."telegram_id"
792826
FROM
793827
"budget_graph"."users" u
794-
INNER JOIN
828+
JOIN
795829
"budget_graph"."users_groups" u_g
796830
ON
797831
u."telegram_id" = u_g."telegram_id"
798832
WHERE
799833
u_g."group_id" = %s::smallint
800-
);
834+
)
801835
802-
DELETE FROM
803-
"budget_graph"."users_groups"
804-
WHERE
805-
"group_id" = %s::smallint;
806-
807-
DELETE FROM
808-
"budget_graph"."groups"
809-
WHERE
810-
"id" = %s::smallint;
811-
812-
DELETE FROM
813-
"budget_graph"."monetary_transactions"
814-
WHERE
815-
"group_id" = %s::smallint
816-
""", (group_id, group_id, group_id, group_id,))
836+
DELETE FROM
837+
"budget_graph"."users"
838+
WHERE
839+
"telegram_id" = ANY(ARRAY(SELECT "telegram_id" FROM telegram_id_users));
840+
841+
-- groups
842+
DELETE FROM
843+
"budget_graph"."groups"
844+
WHERE
845+
"id" = %s::smallint;
846+
847+
-- users_groups
848+
DELETE FROM
849+
"budget_graph"."users_groups"
850+
WHERE
851+
"group_id" = %s::smallint;
852+
853+
-- monetary_transactions
854+
DELETE FROM
855+
"budget_graph"."monetary_transactions"
856+
WHERE
857+
"group_id" = %s::smallint;
858+
""", (*([group_id]*4),))
817859

818860
conn.commit()
819861
logger_database.info(f'[DB_QUERY] Group #{group_id} has been completely deleted')

budget_graph/helpers.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,27 @@
99
from budget_graph.dictionary import receive_translation
1010

1111

12+
class StorageMsgIdForDeleteAfterOperation:
13+
"""
14+
Stores message IDs that are deleted after a transaction is completed (the option is enabled in the user settings)
15+
"""
16+
def __init__(self, feature_is_active=None):
17+
self.feature_is_active: bool = feature_is_active
18+
self.msg_id_to_delete: list = []
19+
20+
def append(self, msg_id: int):
21+
if self.feature_is_active:
22+
self.msg_id_to_delete.append(msg_id)
23+
24+
def delete_messages(self, bot, chat_id: int) -> None:
25+
"""
26+
:param bot: bot object through which messages can be deleted
27+
:param chat_id:
28+
"""
29+
if self.feature_is_active:
30+
bot.delete_messages(chat_id, self.msg_id_to_delete)
31+
32+
1233
@cache
1334
def get_category_button_labels(user_language: str) -> tuple:
1435
"""
@@ -37,6 +58,20 @@ def get_category_button_labels(user_language: str) -> tuple:
3758
)
3859

3960

61+
@cache
62+
def get_language_buttons() -> tuple:
63+
return (
64+
[InlineKeyboardButton('English', callback_data='change_language_en')],
65+
[InlineKeyboardButton('Español', callback_data='change_language_es')],
66+
[InlineKeyboardButton('Русский', callback_data='change_language_ru')],
67+
[InlineKeyboardButton('Français', callback_data='change_language_fr')],
68+
[InlineKeyboardButton('Deutsch', callback_data='change_language_de')],
69+
[InlineKeyboardButton('Islenskur', callback_data='change_language_is')],
70+
[InlineKeyboardButton('Português', callback_data='change_language_pt')],
71+
[InlineKeyboardButton('қазақ', callback_data='change_language_kk')]
72+
)
73+
74+
4075
@cache
4176
def get_timezone_buttons() -> tuple:
4277
buttons_timezone_negative: tuple = (
@@ -95,6 +130,7 @@ def get_bot_commands() -> list:
95130
return [
96131
BotCommand('start', 'Start'),
97132
BotCommand('change_language', 'Change Language'),
133+
BotCommand('del_msg_transaction', '[ON/OFF] Delete messages after successful transaction'),
98134
BotCommand('change_timezone', 'Change Time zone'),
99135
BotCommand('get_my_id', 'Get my Telegram ID'),
100136
BotCommand('premium', 'Premium'),

0 commit comments

Comments
 (0)