Implement unified Telegram bot interface with Login/Logout and fix callback_data limits
**Interface improvements:** - Add persistent Login/Logout buttons to main menu - Help button now updates text above menu (not separate message) - Expired token automatically transforms menu to Login state - Consolidate linking success messages (single welcome + menu) **Fix callback_data length limits (Telegram 64-byte limit):** - Truncate client/supplier names to 40 chars in callback_data - Use full names for API calls but truncated for buttons - Fix pagination buttons for long partner names (30 chars limit) - Search entities by prefix match to handle truncated names **Treasury improvements:** - Show ALL bank/cash accounts (removed 5-item limit) - Remove unnecessary Refresh/Export buttons from treasury views - Handle "Message is not modified" error gracefully **Bug fixes:** - Fix Markdown parsing errors (replace <cod> with `CODUL_TAU`) - Fix silent errors when token expires (show user-friendly message) - Fix Button_data_invalid errors on pagination and details 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -187,7 +187,8 @@ def get_menu_message(
|
||||
|
||||
def create_main_menu(
|
||||
company_name: Optional[str] = None,
|
||||
company_cui: Optional[str] = None
|
||||
company_cui: Optional[str] = None,
|
||||
is_authenticated: bool = True
|
||||
) -> InlineKeyboardMarkup:
|
||||
"""
|
||||
Create main menu keyboard (Level 1) with financial options.
|
||||
@@ -197,6 +198,7 @@ def create_main_menu(
|
||||
Args:
|
||||
company_name: Active company name, or None if no company selected
|
||||
company_cui: Company fiscal code (CUI), or None
|
||||
is_authenticated: Whether user is authenticated (affects Login/Logout button)
|
||||
|
||||
Returns:
|
||||
InlineKeyboardMarkup with main menu buttons
|
||||
@@ -240,48 +242,52 @@ def create_main_menu(
|
||||
]
|
||||
])
|
||||
|
||||
# Row 5: Help button (full width)
|
||||
keyboard.append([
|
||||
InlineKeyboardButton("Help", callback_data="action:help")
|
||||
])
|
||||
# Row 5: Help/Logout buttons (authenticated) or Login button (non-authenticated)
|
||||
if is_authenticated:
|
||||
keyboard.append([
|
||||
InlineKeyboardButton("Help", callback_data="action:help"),
|
||||
InlineKeyboardButton("Logout", callback_data="action:logout")
|
||||
])
|
||||
else:
|
||||
keyboard.append([
|
||||
InlineKeyboardButton("Login", callback_data="action:login")
|
||||
])
|
||||
|
||||
return InlineKeyboardMarkup(keyboard)
|
||||
|
||||
|
||||
def create_action_buttons(current_view: str, show_export: bool = True, show_back: bool = False) -> InlineKeyboardMarkup:
|
||||
def create_action_buttons(current_view: str, show_export: bool = True, show_back: bool = False, show_refresh: bool = True) -> InlineKeyboardMarkup:
|
||||
"""
|
||||
Create action buttons for responses (Refresh, Export, Back, Menu).
|
||||
|
||||
Layout (buttons made wide by message text padding):
|
||||
[Refresh] [Export] (if show_export=True)
|
||||
[Refresh] [Export] (if show_refresh=True and show_export=True)
|
||||
[Refresh] (if show_refresh=True and show_export=False)
|
||||
[Înapoi] (if show_back=True, full width)
|
||||
[Menu] (full width)
|
||||
|
||||
Or:
|
||||
[Refresh] (if show_export=False)
|
||||
[Înapoi] (if show_back=True, full width)
|
||||
[Menu] (full width)
|
||||
[Menu] (full width, always shown)
|
||||
|
||||
Args:
|
||||
current_view: View identifier for refresh callback (e.g., "sold", "clienti")
|
||||
show_export: Whether to show Export button
|
||||
show_back: Whether to show Back button to list
|
||||
show_refresh: Whether to show Refresh button
|
||||
|
||||
Returns:
|
||||
InlineKeyboardMarkup with action buttons
|
||||
"""
|
||||
keyboard = []
|
||||
|
||||
# Row 1: Refresh and optionally Export
|
||||
if show_export:
|
||||
keyboard.append([
|
||||
InlineKeyboardButton("Refresh", callback_data=f"action:refresh:{current_view}"),
|
||||
InlineKeyboardButton("Export", callback_data=f"action:export:{current_view}")
|
||||
])
|
||||
else:
|
||||
keyboard.append([
|
||||
InlineKeyboardButton("Refresh", callback_data=f"action:refresh:{current_view}")
|
||||
])
|
||||
# Row 1: Refresh and optionally Export (only if show_refresh is True)
|
||||
if show_refresh:
|
||||
if show_export:
|
||||
keyboard.append([
|
||||
InlineKeyboardButton("Refresh", callback_data=f"action:refresh:{current_view}"),
|
||||
InlineKeyboardButton("Export", callback_data=f"action:export:{current_view}")
|
||||
])
|
||||
else:
|
||||
keyboard.append([
|
||||
InlineKeyboardButton("Refresh", callback_data=f"action:refresh:{current_view}")
|
||||
])
|
||||
|
||||
# Row 2: Back to List (if show_back is True)
|
||||
if show_back:
|
||||
@@ -344,10 +350,15 @@ def create_client_list_keyboard(clients: List[Dict], max_items: int = 10, page:
|
||||
balance_str = f"{balance:,.0f}" if balance else "0"
|
||||
|
||||
button_text = f"{client_name} - {balance_str} RON"
|
||||
|
||||
# Limit callback_data to 64 bytes (Telegram limit)
|
||||
# Use only first 40 chars of name to stay within limit
|
||||
safe_name = client_name[:40] if len(client_name) > 40 else client_name
|
||||
|
||||
keyboard.append([
|
||||
InlineKeyboardButton(
|
||||
button_text,
|
||||
callback_data=f"details:client:{client_name}:0" # name:page
|
||||
callback_data=f"details:client:{safe_name}:0" # name:page
|
||||
)
|
||||
])
|
||||
|
||||
@@ -417,10 +428,15 @@ def create_supplier_list_keyboard(suppliers: List[Dict], max_items: int = 10, pa
|
||||
balance_str = f"{balance:,.0f}" if balance else "0"
|
||||
|
||||
button_text = f"{supplier_name} - {balance_str} RON"
|
||||
|
||||
# Limit callback_data to 64 bytes (Telegram limit)
|
||||
# Use only first 40 chars of name to stay within limit
|
||||
safe_name = supplier_name[:40] if len(supplier_name) > 40 else supplier_name
|
||||
|
||||
keyboard.append([
|
||||
InlineKeyboardButton(
|
||||
button_text,
|
||||
callback_data=f"details:supplier:{supplier_name}:0" # name:page
|
||||
callback_data=f"details:supplier:{safe_name}:0" # name:page
|
||||
)
|
||||
])
|
||||
|
||||
@@ -480,6 +496,9 @@ def create_invoice_list_keyboard(
|
||||
"""
|
||||
keyboard = []
|
||||
|
||||
# Limit partner_name to 30 chars for Telegram callback_data limit (64 bytes)
|
||||
safe_partner_name = partner_name[:30] if len(partner_name) > 30 else partner_name
|
||||
|
||||
# Calculate pagination
|
||||
total_invoices = len(invoices)
|
||||
total_pages = (total_invoices + max_items - 1) // max_items # Ceiling division
|
||||
@@ -517,7 +536,7 @@ def create_invoice_list_keyboard(
|
||||
# Previous button
|
||||
if page > 0:
|
||||
nav_buttons.append(
|
||||
InlineKeyboardButton("< Anterior", callback_data=f"invoices_page:{partner_type}:{partner_name}:{page-1}")
|
||||
InlineKeyboardButton("< Anterior", callback_data=f"invoices_page:{partner_type}:{safe_partner_name}:{page-1}")
|
||||
)
|
||||
|
||||
# Page indicator (non-clickable)
|
||||
@@ -528,7 +547,7 @@ def create_invoice_list_keyboard(
|
||||
# Next button
|
||||
if page < total_pages - 1:
|
||||
nav_buttons.append(
|
||||
InlineKeyboardButton("Următor >", callback_data=f"invoices_page:{partner_type}:{partner_name}:{page+1}")
|
||||
InlineKeyboardButton("Următor >", callback_data=f"invoices_page:{partner_type}:{safe_partner_name}:{page+1}")
|
||||
)
|
||||
|
||||
keyboard.append(nav_buttons)
|
||||
|
||||
Reference in New Issue
Block a user