Files
echoes-of-the-ash/docs/development/BOT_MODULE.md
Joan 861f3b8a36 Add visual progress bars and refactor handler modules
- Implement visual HP/Stamina/XP bars using Unicode characters (██░)
- Refactor handlers.py (1308 → 377 lines) into specialized modules:
  * action_handlers.py - World interaction and status display
  * inventory_handlers.py - Inventory management
  * combat_handlers.py - Combat actions
  * profile_handlers.py - Character stats with visual bars
  * corpse_handlers.py - Looting system
  * pickup_handlers.py - Item collection
- Add utility functions: create_progress_bar(), format_stat_bar()
- Organize all documentation into docs/ structure
- Create comprehensive documentation index with navigation
- Add UI examples showing before/after visual improvements
2025-10-19 00:23:44 +02:00

7.5 KiB

Bot Module Documentation

Overview

The bot module contains all the Telegram bot logic for "Echoes of the Ashes" RPG game.

Module Structure

Core Modules

  • handlers.py (14KB) - Main message router and utility functions
  • database.py - Database operations and queries
  • keyboards.py - Telegram keyboard layouts
  • logic.py - Game logic and calculations
  • combat.py (17KB) - Combat system implementation
  • spawn_manager.py - Enemy spawning system
  • utils.py - Utility functions and decorators

Handler Modules (Refactored)

Organized by functionality for better maintainability:

action_handlers.py (14KB, 367 lines)

World interaction and inspection

  • get_player_status_text() - Player status display
  • handle_inspect_area() - Inspect locations
  • handle_attack_wandering() - Attack wandering enemies
  • handle_inspect_interactable() - Inspect objects
  • handle_action() - Perform actions
  • handle_main_menu() - Main menu navigation
  • handle_move_menu() - Movement menu
  • handle_move() - Travel between locations

inventory_handlers.py (13KB, 355 lines)

Inventory management system

  • handle_inventory_menu() - Show inventory
  • handle_inventory_item() - Item details
  • handle_inventory_use() - Use consumables
  • handle_inventory_drop() - Drop items
  • handle_inventory_equip() - Equip gear
  • handle_inventory_unequip() - Unequip gear

pickup_handlers.py (5.1KB, 135 lines)

Item collection from world

  • handle_pickup_menu() - Pickup options
  • handle_pickup() - Pick up items

combat_handlers.py (6.3KB, 171 lines)

Combat action handlers

  • handle_combat_attack() - Attack enemy
  • handle_combat_flee() - Flee combat
  • handle_combat_use_item_menu() - Combat items menu
  • handle_combat_use_item() - Use item in combat
  • handle_combat_back() - Return to combat menu

profile_handlers.py (6.0KB, 147 lines)

Character progression

  • handle_profile() - View profile
  • handle_spend_points_menu() - Stat points menu
  • handle_spend_point() - Allocate stat points

corpse_handlers.py (8.2KB, 234 lines)

Looting system

  • handle_loot_player_corpse() - Loot player corpses
  • handle_take_corpse_item() - Take items from player corpse
  • handle_scavenge_npc_corpse() - Scavenge NPC corpses
  • handle_scavenge_corpse_item() - Take items from NPC corpse

Architecture

Message Flow

Telegram Update
    ↓
main.py (Application setup)
    ↓
handlers.py::button_handler (Router)
    ↓
Specialized Handler (action_handlers, combat_handlers, etc.)
    ↓
database.py (Data operations)
    ↓
keyboards.py (UI response)
    ↓
Response to User

Handler Pattern

All handlers follow a consistent pattern:

async def handle_action(query, user_id: int, player: dict, data: list = None):
    """
    Handle specific action.
    
    Args:
        query: Telegram callback query object
        user_id: Telegram user ID
        player: Player data dict
        data: Optional list of parameters from callback_data
    """
    # 1. Validate input
    # 2. Perform action
    # 3. Update database
    # 4. Send response with send_or_edit_with_image()

Shared Utilities

send_or_edit_with_image()

Central function for sending/editing messages with images:

await send_or_edit_with_image(
    query,
    text="Message text",
    reply_markup=keyboard,
    image_path="/path/to/image.png"  # Optional
)

Features:

  • Smooth image transitions
  • Image caching for performance
  • Automatic fallback to text-only
  • Edit vs. send detection

Design Principles

1. Separation of Concerns

Each module handles a specific domain:

  • Handlers → User interaction
  • Database → Data persistence
  • Logic → Game rules
  • Combat → Battle mechanics

2. Single Responsibility

Each function has one clear purpose:

  • handle_inventory_use() - Use items
  • handle_combat_attack() - Attack in combat
  • One giant function that does everything

3. Consistency

All handlers:

  • Accept same parameter pattern
  • Use async/await
  • Handle errors gracefully
  • Log important actions

4. Error Handling

Centralized in button_handler:

try:
    await handle_specific_action(...)
except Exception as e:
    logger.error(f"Error: {e}", exc_info=True)
    await query.answer("An error occurred.", show_alert=True)

Adding New Handlers

Step 1: Create Handler Function

# In appropriate *_handlers.py module
async def handle_new_action(query, user_id: int, player: dict, data: list):
    """Handle new action."""
    await query.answer()
    
    # Your logic here
    
    from .handlers import send_or_edit_with_image
    await send_or_edit_with_image(
        query,
        text="Action result",
        reply_markup=some_keyboard()
    )

Step 2: Import in handlers.py

from .your_handlers import handle_new_action

Step 3: Add Route

# In button_handler()
elif action_type == "new_action":
    await handle_new_action(query, user_id, player, data)

Step 4: Create Keyboard Button

# In keyboards.py
InlineKeyboardButton(
    "New Action",
    callback_data="new_action:param1:param2"
)

Testing

Manual Testing

  1. Start the bot: python main.py
  2. Send /start to the bot
  3. Test each button action
  4. Check logs for errors

Unit Testing (Future)

# tests/test_handlers.py
async def test_handle_inventory_use():
    result = await handle_inventory_use(
        mock_query,
        user_id=123,
        player=mock_player,
        data=['inventory_use', '1']
    )
    assert result is not None

Performance Considerations

Image Caching

Images are cached after first upload:

cached_file_id = await database.get_cached_image(image_path)
if not cached_file_id:
    # Upload and cache
    await database.cache_image(image_path, file_id)

Database Queries

  • Use indexed lookups (user_id, location_id)
  • Batch operations where possible
  • Async all the way

Memory

  • Don't store large objects in memory
  • Use database for persistence
  • Clean up old data periodically

Debugging

Enable Debug Logging

# In main.py
logging.basicConfig(
    level=logging.DEBUG,  # Change from INFO
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)

Common Issues

Issue: Handler not being called

  • Check callback_data format: "action_type:param1:param2"
  • Verify route exists in button_handler()
  • Check import statement

Issue: Image not showing

  • Verify image path exists
  • Check image cache
  • Look for upload errors in logs

Issue: Database errors

  • Check player exists
  • Verify foreign key relationships
  • Look at database schema

Migration Guide

From Old handlers.py

Old code in backup files:

  • backups/handlers_original.py
  • backups/handlers.py.old

All functionality preserved, just reorganized into modules.

Future Enhancements

  1. Type Hints - Add proper type annotations
  2. Unit Tests - Test coverage for all handlers
  3. Decorators - Common validations (requires_combat, requires_stamina)
  4. Base Classes - Handler base class with common functionality
  5. Event System - Decouple actions from responses

Contributing

When adding new features:

  1. Choose the appropriate handler module (or create new one)
  2. Follow the existing pattern
  3. Add docstrings
  4. Log important actions
  5. Handle errors gracefully
  6. Update this documentation

Questions?

See also:

  • REFACTORING_NOTES.md - Refactoring details
  • HANDLER_REFACTORING_SUMMARY.md - Before/after comparison
  • README.md - Project overview