From f1a02983e4dd29be6739c5d31a732aa69360753d Mon Sep 17 00:00:00 2001 From: brucewzj99 Date: Wed, 5 Jul 2023 10:05:33 +0800 Subject: [PATCH 1/3] move tracker range up --- bot/google_sheet.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/bot/google_sheet.py b/bot/google_sheet.py index 449a6b7..3daf196 100644 --- a/bot/google_sheet.py +++ b/bot/google_sheet.py @@ -20,10 +20,14 @@ f"Dropdown!{chr(i)}12:{chr(i)}19" for i in range(ord("A"), ord("K")) ] payment_main_range = "Dropdown!A12:J12" -quick_others_range = "Tracker!I3:J13" income_range = "Dropdown!L2:L9" overall_range = "!M13:O25" +tracker_range = "Tracker!B3:E3" +quick_add_range = "Tracker!G3:J3" +quick_others_range = "Tracker!I3:J13" +quick_transport_range = "Tracker!G3:H3" + def get_main_dropdown_value(sheet_id, entry_type): range = [] @@ -151,7 +155,7 @@ def get_trackers(sheet_id): result = ( sheets_api.spreadsheets() .values() - .get(spreadsheetId=sheet_id, range=f"Tracker!B3:E3") + .get(spreadsheetId=sheet_id, range=tracker_range) .execute() ) values = result.get("values", []) @@ -160,7 +164,7 @@ def get_trackers(sheet_id): def update_rows(sheet_id, day, new_row, first_row): values = [[day] + [new_row] * 2 + [first_row]] - range_name = "Tracker!B3:E3" + range_name = tracker_range body = {"values": values} request = ( sheets_api.spreadsheets() @@ -176,7 +180,7 @@ def update_rows(sheet_id, day, new_row, first_row): def row_incremental(sheet_id, entry_type): - range_name = "Tracker!B3:E3" + range_name = tracker_range response = ( sheets_api.spreadsheets() .values() @@ -202,7 +206,7 @@ def row_incremental(sheet_id, entry_type): def get_quick_add_settings(sheet_id, entry_type): - range_name = "Tracker!G3:J3" + range_name = quick_add_range response = ( sheets_api.spreadsheets() .values() @@ -226,7 +230,7 @@ def get_quick_add_settings(sheet_id, entry_type): def update_quick_add_settings(sheet_id, entry_type, payment, type): if entry_type == EntryType.TRANSPORT: - range_name = "Tracker!G3:H3" + range_name = quick_transport_range else: last_row = ( sheets_api.spreadsheets() From 244963647e0925c798a10f496c645df2845b880f Mon Sep 17 00:00:00 2001 From: Bruce Wang Date: Tue, 11 Jul 2023 15:21:50 +0800 Subject: [PATCH 2/3] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d50fda1..d4f9a51 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ pip install -r requirements.txt * Go to Google Cloud Platform * Set up Google Sheet API, download service account key * Retrieve Google Sheet API email -* Set up Firebase Realtime Database, download service account key +* Set up Firebase Firestore Database, download service account key * Retrieve your firebase database url * Set up telegram bot via [BotFather](https://t.me/BotFather) * Insert all of them into .env as follows, you can use py dotenv or set it as env variable in your venv From bfba390badf4c6c2602eb576c26e8f9bdc41182a Mon Sep 17 00:00:00 2001 From: brucewzj99 Date: Sun, 16 Jul 2023 04:04:44 +0800 Subject: [PATCH 3/3] v2.1.3 --- bot/common.py | 3 +- bot/firebase.py | 37 --------------- bot/firebase_config.py | 11 ----- bot/google_sheet.py | 56 ++++++++++++----------- bot/telegram_bot.py | 100 ++++++++++++++++++++++++----------------- bot/text_str.py | 20 ++++----- release_notes.md | 15 ++++++- test.py | 5 ++- 8 files changed, 121 insertions(+), 126 deletions(-) delete mode 100644 bot/firebase.py delete mode 100644 bot/firebase_config.py diff --git a/bot/common.py b/bot/common.py index 0b20b61..66f409c 100644 --- a/bot/common.py +++ b/bot/common.py @@ -21,6 +21,7 @@ class ConversationState(Enum): SUBPAYMENT, QUICK_ADD, QUICK_ADD_CATEGORY, + QUICK_ADD_TRANSPORT, CONFIG_SETUP, CONFIG_CATEGORY, CONFIG_SUBCATEGORY, @@ -31,4 +32,4 @@ class ConversationState(Enum): INCOME, WORK_PLACE, CPF, - ) = range(23) + ) = range(24) diff --git a/bot/firebase.py b/bot/firebase.py deleted file mode 100644 index 75d1293..0000000 --- a/bot/firebase.py +++ /dev/null @@ -1,37 +0,0 @@ -from bot.firebase_config import db - - -# new user setup -def new_user_setup(telegram_id, sheet_id): - ref = db.reference("/users/" + str(telegram_id)) - ref.update( - { - "sheet_id": sheet_id, - } - ) - - -# check if user exists -def check_if_user_exists(telegram_id): - ref = db.reference("/users/" + str(telegram_id)) - return ref.get() is not None - - -# get user sheet id -def get_user_sheet_id(telegram_id): - ref = db.reference("/users/" + str(telegram_id) + "/sheet_id") - return ref.get() - - -def get_all_user_id(): - ref = db.reference("/users/") - user_ids = [int(user_id) for user_id in ref.get().keys()] - return user_ids - - -def get_all_sheet_id(): - ref = db.reference("/users/") - sheet_ids = [] - for value in ref.get().values(): - sheet_ids.append(value["sheet_id"]) - return sheet_ids diff --git a/bot/firebase_config.py b/bot/firebase_config.py deleted file mode 100644 index 78c8606..0000000 --- a/bot/firebase_config.py +++ /dev/null @@ -1,11 +0,0 @@ -import firebase_admin -from firebase_admin import credentials -from firebase_admin import db -import os -import json -from firebase_admin.credentials import Certificate - -DATABASE_URL = os.getenv("DATABASE_URL") -FIREBASE_JSON = json.loads(os.environ["FIREBASE_JSON"]) - -firebase_admin.initialize_app(Certificate(FIREBASE_JSON), {"databaseURL": DATABASE_URL}) diff --git a/bot/google_sheet.py b/bot/google_sheet.py index 3daf196..4aa6a26 100644 --- a/bot/google_sheet.py +++ b/bot/google_sheet.py @@ -24,9 +24,13 @@ overall_range = "!M13:O25" tracker_range = "Tracker!B3:E3" +tracker_transport_1 = "G" +tracker_transport_2 = "H" +tracker_others_1 = "I" +tracker_others_2 = "J" quick_add_range = "Tracker!G3:J3" quick_others_range = "Tracker!I3:J13" -quick_transport_range = "Tracker!G3:H3" +quick_transport_range = "Tracker!G3:H13" def get_main_dropdown_value(sheet_id, entry_type): @@ -37,7 +41,6 @@ def get_main_dropdown_value(sheet_id, entry_type): range = others_main_range else: range = payment_main_range - # Make the request results = ( sheets_api.spreadsheets() .values() @@ -45,7 +48,6 @@ def get_main_dropdown_value(sheet_id, entry_type): .execute() ) - # Get the values from the result values = results.get("values", []) if entry_type == EntryType.TRANSPORT: @@ -64,7 +66,6 @@ def get_sub_dropdown_value(sheet_id, main_value, entry_type): range = others_sub_range else: range = payment_sub_range - # Make the request results = ( sheets_api.spreadsheets() .values() @@ -72,7 +73,6 @@ def get_sub_dropdown_value(sheet_id, main_value, entry_type): .execute() ) - # Get the values from the result value_ranges = results.get("valueRanges", []) dropdown = [] @@ -111,7 +111,6 @@ def get_new_row(sheet_id, month): def create_date(sheet_id, day, month, first_row): - # Update date in column A body = {"values": [[day]]} range_name = f"{month}!A{first_row}" sheets_api.spreadsheets().values().update( @@ -138,7 +137,6 @@ def create_entry(sheet_id, month, row_tracker, row_data): sheet_column_end = "G" data = [price] + remarks_list + [category, payment] - # Write the message to the Google Sheet body = {"values": [data]} range_name = ( f"{month}!{sheet_column_start}{row_tracker}:{sheet_column_end}{row_tracker}" @@ -230,20 +228,24 @@ def get_quick_add_settings(sheet_id, entry_type): def update_quick_add_settings(sheet_id, entry_type, payment, type): if entry_type == EntryType.TRANSPORT: - range_name = quick_transport_range + range_1 = tracker_transport_1 + range_2 = tracker_transport_2 else: - last_row = ( - sheets_api.spreadsheets() - .values() - .get( - spreadsheetId=sheet_id, - range="Tracker!I:J", - ) - .execute() - .get("values", []) + range_1 = tracker_others_1 + range_2 = tracker_others_2 + + last_row = ( + sheets_api.spreadsheets() + .values() + .get( + spreadsheetId=sheet_id, + range=f"Tracker!{range_1}:{range_2}", ) - last_row = len(last_row) + 1 - range_name = f"Tracker!I{last_row}:J{last_row}" + .execute() + .get("values", []) + ) + last_row = len(last_row) + 1 + range_name = f"Tracker!{range_1}{last_row}:{range_2}{last_row}" new_row = [payment, type] body = {"values": [new_row]} sheets_api.spreadsheets().values().update( @@ -254,8 +256,11 @@ def update_quick_add_settings(sheet_id, entry_type, payment, type): ).execute() -def get_quick_add_others(sheet_id): - range_name = quick_others_range +def get_quick_add_list(sheet_id, entry_type): + if entry_type == EntryType.TRANSPORT: + range_name = quick_transport_range + else: + range_name = quick_others_range response = ( sheets_api.spreadsheets() .values() @@ -264,11 +269,11 @@ def get_quick_add_others(sheet_id): ) values = response.get("values", []) - others_list = [] + settings_list = [] for other in values: merged_str = ", ".join(other) - others_list.append(merged_str) - return others_list + settings_list.append(merged_str) + return settings_list def get_day_transaction(sheet_id, month, date): @@ -359,6 +364,7 @@ def update_income(sheet_id, month, row_data): ).execute() return True + def get_overall(sheet_id, month): result = ( sheets_api.spreadsheets() @@ -366,4 +372,4 @@ def get_overall(sheet_id, month): .get(spreadsheetId=sheet_id, range=f"{month}{overall_range}") .execute() ) - return result.get("values", []) \ No newline at end of file + return result.get("values", []) diff --git a/bot/telegram_bot.py b/bot/telegram_bot.py index dd6cbaa..b94f6d0 100644 --- a/bot/telegram_bot.py +++ b/bot/telegram_bot.py @@ -1,5 +1,5 @@ import os -from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup +from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup, ParseMode from telegram.ext import ( CommandHandler, ConversationHandler, @@ -24,7 +24,10 @@ def create_inline_markup(list): keyboard_markup_list = [] for reply in list: - keyboard_markup_list.append([InlineKeyboardButton(reply, callback_data=reply)]) + if reply: + keyboard_markup_list.append( + [InlineKeyboardButton(reply, callback_data=reply)] + ) return InlineKeyboardMarkup(keyboard_markup_list) @@ -74,7 +77,7 @@ def start(update, context): ) return CS.RESET_UP else: - update.message.reply_text(SETUP_TEXT) + update.message.reply_text(SETUP_TEXT, parse_mode=ParseMode.HTML) return CS.SET_UP except Exception as e: update.message.reply_text(ERROR_TEXT) @@ -110,7 +113,7 @@ def reset_up(update, context) -> int: reply = update.callback_query.data update.callback_query.answer() if reply == "Yes": - update.callback_query.message.reply_text(SETUP_TEXT) + update.callback_query.message.reply_text(SETUP_TEXT, parse_mode=ParseMode.HTML) return CS.SET_UP else: update.callback_query.edit_message_text(END_TEXT, reply_markup=None) @@ -141,43 +144,35 @@ def config_handler(update, context) -> int: return ConversationHandler.END update.callback_query.edit_message_text(reply, reply_markup=None) if reply == "Change Google Sheet": - update.callback_query.message.reply_text(SETUP_TEXT) + update.callback_query.message.reply_text(SETUP_TEXT, parse_mode=ParseMode.HTML) return CS.SET_UP else: try: if reply == "Configure Quick Transport": context.user_data["config"] = EntryType.TRANSPORT - msg = f"This is your current Transport settings.\n" - setting_list = gs.get_quick_add_settings( - context.user_data["sheet_id"], context.user_data["config"] - ) - # Retrieve current settings - if setting_list == None: - msg = f"{msg}Default Payment: None\nDefault Type: None\n" - else: - msg = f"{msg}Default Payment: {setting_list[0]}\nDefault Type: {setting_list[1]}\n" - msg = f"{msg}Do you want to update it?" - update.callback_query.message.reply_text( - msg, reply_markup=create_inline_markup(["Yes", "No"]) - ) - return CS.CONFIG_SETUP + msg = QUICK_TRANSPORT_TEXT + limit = QUICK_TRANSPORT_LIMIT elif reply == "Configure Quick Others": context.user_data["config"] = EntryType.OTHERS msg = QUICK_OTHER_TEXT - setting_list = gs.get_quick_add_others(context.user_data["sheet_id"]) - keyboard_list = [] - if setting_list == None: - msg = f"{msg}No settings found\n" - else: - for setting in setting_list: - msg = f"{msg}{setting}\n" - if len(setting_list) < QUICK_OTHER_LIMIT: - keyboard_list.append("Add new") - keyboard_list.append("Cancel") - update.callback_query.message.reply_text( - msg, reply_markup=create_inline_markup(keyboard_list) - ) - return CS.CONFIG_SETUP + limit = QUICK_OTHER_LIMIT + + setting_list = gs.get_quick_add_list( + context.user_data["sheet_id"], context.user_data["config"] + ) + keyboard_list = [] + if setting_list == None: + msg = f"{msg}No settings found\n" + else: + for setting in setting_list: + msg = f"{msg}{setting}\n" + if len(setting_list) < limit: + keyboard_list.append("Add new") + keyboard_list.append("Cancel") + update.callback_query.message.reply_text( + msg, reply_markup=create_inline_markup(keyboard_list) + ) + return CS.CONFIG_SETUP except Exception as e: update.callback_query.message.reply_text(ERROR_TEXT) return ConversationHandler.END @@ -550,13 +545,24 @@ def add_transport(update, context): update.message.reply_text(QUICK_SETUP_TRANSPORT) return ConversationHandler.END else: - context.user_data["payment"] = setting_list[0] - context.user_data["category"] = setting_list[1] - update.message.reply_text( - f"Quick Add Transport\nDefault Payment: {setting_list[0]}\nDefault Type: {setting_list[1]}" - + "\n\nPlease enter as follow: [price],[start],[end]\n e.g. 2.11, Home, Work" + setting_list = gs.get_quick_add_list( + context.user_data["sheet_id"], context.user_data["entry_type"] ) - return CS.QUICK_ADD + if len(setting_list) == 1: + payment, category = setting_list[0].split(",") + context.user_data["payment"] = payment + context.user_data["category"] = category + update.message.reply_text( + f"Quick Add Transport\nDefault Payment: {payment}\nDefault Type: {category}" + + "\n\nPlease enter as follow: [price],[start],[end]\n e.g. 2.11, Home, Work" + ) + return CS.QUICK_ADD + else: + update.message.reply_text( + "Quick Add Transport, please choose your category.", + reply_markup=create_inline_markup(setting_list), + ) + return CS.QUICK_ADD_TRANSPORT def add_others(update, context): @@ -575,7 +581,9 @@ def add_others(update, context): update.message.reply_text(QUICK_SETUP_OTHER) return ConversationHandler.END else: - setting_list = gs.get_quick_add_others(context.user_data["sheet_id"]) + setting_list = gs.get_quick_add_list( + context.user_data["sheet_id"], context.user_data["entry_type"] + ) update.message.reply_text( "Quick Add Others, please choose your category.", reply_markup=create_inline_markup(setting_list), @@ -595,6 +603,17 @@ def quick_add_category(update, context) -> int: return CS.QUICK_ADD +def quick_add_transport(update, context) -> int: + reply = update.callback_query.data + context.user_data["payment"], context.user_data["category"] = reply.split(",") + update.callback_query.answer() + update.callback_query.edit_message_text( + f'Quick Add Transport\nDefault Payment: {context.user_data["payment"]}\nDefault Type: {context.user_data["category"]}' + + "\n\nPlease enter as follow: [price],[start],[end]\n e.g. 2.11, Home, Work" + ) + return CS.QUICK_ADD + + def quick_add(update, context) -> int: reply = update.message.text try: @@ -800,6 +819,7 @@ def setup_handlers(dispatcher): quick_add_states = { CS.QUICK_ADD: [MessageHandler(Filters.text & ~Filters.command, quick_add)], CS.QUICK_ADD_CATEGORY: [CallbackQueryHandler(quick_add_category)], + CS.QUICK_ADD_TRANSPORT: [CallbackQueryHandler(quick_add_transport)], } # Retrieve transaction-related states and handlers diff --git a/bot/text_str.py b/bot/text_str.py index 6a6b391..d8335a9 100644 --- a/bot/text_str.py +++ b/bot/text_str.py @@ -1,24 +1,22 @@ import os GOOGLE_API_EMAIL = os.getenv("GOOGLE_API_EMAIL") - +GOOGLE_TEMPLATE = "https://docs.google.com/spreadsheets/d/1-XVdaeq1qqnLqAttGGzki6NkavqpkKs1n6Elz4GDC2I/edit?usp=sharing" SETUP_TEXT = ( "Please set up your Google sheet by following the steps below.\n\n" - + "1. Go over to https://docs.google.com/spreadsheets/d/1dJgJk7YUoR0nYjNa_lgrMxpz-MehOo4SyfRitlasQo8/edit#gid=861838157\n" + + f"1. Head over to Expense Tracker Template\n" + "2. Go to File > Make a copy\n" - + "3. Go to File > Share > Share with others\n" - + "4. Add " + + "3. Edit the Dropdown sheet accordingly\n" + + "4. Go to File > Share > Share with others\n" + + "5. Add " + str(GOOGLE_API_EMAIL) + " as an editor\n" - + "5. Copy your Google Sheet URL and send it over\n" + + "6. Copy your entire Google Sheet URL and send it over\n" + "Example: https://docs.google.com/spreadsheets/d/abcd1234/edit\n" - + "6. Edit the Dropdown sheet accordingly\n" ) ERROR_TEXT = "There seems to be an error, please try again later." SUCCESS_LINK_TEXT = "Google sheet successfully linked! Please proceed to configure your Dropdown sheet.\nOnce completed, type /addentry to add your first entry!" -GSHEET_ERROR_TEXT = ( - "There seems to be an error linking your google sheet, please try again later." -) +GSHEET_ERROR_TEXT = f"There seems to be an error linking your google sheet, have you shared your Google Sheet with {GOOGLE_API_EMAIL} yet?" GSHEET_WRONG_TEXT = ( "That doesn't seem like a Google sheet link, are you sure? Try sending again." ) @@ -41,8 +39,10 @@ QUICK_SETUP_TRANSPORT = "You have not set up your quick add settings for transport yet, please do so by typing /config" QUICK_SETUP_OTHER = "You have not set up your quick add settings for others yet, please do so by typing /config" -QUICK_OTHER_LIMIT = 5 # max i can go is 10, but testing with 5 first +QUICK_OTHER_LIMIT = 5 +QUICK_TRANSPORT_LIMIT = 3 QUICK_OTHER_TEXT = f'This is your current Others settings.\nClick on "Add new" to add a new one (max {QUICK_OTHER_LIMIT}).\nIf you wish to update, you will have to do so manually in the "Tracker" sheet.\n\nPayment, Category\n' +QUICK_TRANSPORT_TEXT = f'This is your current Transport settings.\nClick on "Add new" to add a new one (max {QUICK_TRANSPORT_LIMIT}).\nIf you wish to update, you will have to do so manually in the "Tracker" sheet.\n\nPayment, Category\n' HELP_TEXT = ( "To get started, please type /start\n" diff --git a/release_notes.md b/release_notes.md index 195eaef..ccbc4af 100644 --- a/release_notes.md +++ b/release_notes.md @@ -70,4 +70,17 @@ ## Version 2.1.2 - Date 26 Jun 2023 ### Bug Fix 🛠️ - When new users join, new date entry was not created -- Quick add settings not sending proper error message when either transport or others has been configured \ No newline at end of file +- Quick add settings not sending proper error message when either transport or others has been configured + + +## Version 2.1.3 - Date 16 July 2023 +### Enhancement 🔥 +- Allows user to add multiple settings for quick transport + - If there is only one settings, that will be used as default (/addtransport only) +- Revamp Google Sheet with new looks + +### Bug Fix 🛠️ +- Skip empty cells for payment & category + +### For Developer 🧑‍💻 +- Remove firebase codes \ No newline at end of file diff --git a/test.py b/test.py index df94bd2..326e4a1 100644 --- a/test.py +++ b/test.py @@ -10,19 +10,22 @@ updater = Updater(token=TOKEN) dispatcher = updater.dispatcher + @app.route("/webhook", methods=["POST"]) def webhook(): update = Update.de_json(request.get_json(), updater.bot) dispatcher.process_update(update) return "OK" + @app.route("/") def index(): return "Bot is running!" + setup_handlers(dispatcher) if __name__ == "__main__": public_url = ngrok.connect(5000, "http").public_url updater.bot.set_webhook(url=f"{public_url}/webhook") - app.run(host="0.0.0.0", port=int(os.environ.get("PORT", 5000))) \ No newline at end of file + app.run(host="0.0.0.0", port=int(os.environ.get("PORT", 5000)))