tele-menu 3.0.1
Telegram menu system
License: MIT
Requires Python: >=3.9
Keywords: telegram, menu, telemenu, tele_menu, telegram_menu, tg_menu, tg, scene, scenes, tools, telebot, telegram_bot, pytelegrambotapi

Telegram menu
=========================

A toolkit for creating scene-based Telegram bots using `pyTelegramBotAPI`. This module simplifies the management of user states, menus, and database interactions using `db_attribute` as the ORM.

# Table of contents

* [Table of contents](#table-of-contents)
* [Installation](#installation)
* [Setup](#setup)
* [Usage Example](#usage-example)
* [Database & User Management (ORM)](#database--user-management-orm)
    * [The User Class](#the-user-class)
    * [Using User class](#using-user-class)

# Installation

```bash
pip install tele_menu
```
or
```bash
pip install git+https://github.com/shutkanos/TeleMenu.git
```

[Back to table of contents](#table-of-contents)

# Setup

To start, you need to configure the database connection and register the bot instance.

```python
import telebot
from tele_menu import sql_register, bot_register

# Database credentials
host = "localhost"
db_user = "root"
password = "password"
database = "my_bot_db"
token = "YOUR_BOT_TOKEN"

# 1. Initialize Database Connection
sql_register(host=host, user=db_user, password=password, database=database)

# 2. Register Bot Instance
bot_register(telebot.TeleBot(token))
```

[Back to table of contents](#table-of-contents)

# Usage Example

Below is a complete example of a bot with a Main Menu, a Profile scene, and a Nickname change scene.

```python
import random
import traceback
import requests.exceptions
from tele_menu import (
    SceneManager, Scene, TextMessage, PhotoMessage, Button, 
    SendSceneAction, CallMethodAction, Data, Log, User
)

# Assume setup (sql_register, bot_register) is done here

@SceneManager.decorator_register(name="main_menu")
class MainMenuScene(Scene):
    def build(self):
        self.add_message(TextMessage(
            content=f"Hello, {self.user.nameuser}!",
            buttons=[
                [
                    Button("Profile", action=SendSceneAction(scene_name='profile', context="Navigated from Main Menu!")),
                ],
                [
                    Button("About Bot", action=SendSceneAction(scene_name='bot_info'))
                ]
            ]
        ))

@SceneManager.decorator_register(name="profile")
class ProfileScene(Scene):
    def build(self):
        self.add_message(TextMessage(
            content=f"👤 User Profile:\n"
                    f"ID: {self.user.id}\n"
                    f"Nickname: {self.user.nameuser}\n"
                    f"{self.context if self.context else ''}",
            buttons=[
                [
                    Button("Change Nickname", action=SendSceneAction(scene_name='change_nickname')),
                    Button("Set random Nickname", action=CallMethodAction(method_name='random_nick', kwargs={'salt': '(auto)'}))
                ],
                [
                    Button("Back", action=SendSceneAction(scene_name='main_menu'))
                ]
            ]
        ))

    def random_nick(self, salt=""):
        self.user.nameuser = f"User{salt}#{random.randint(1000, 9999)}"
        # Refresh the scene to show changes
        self.user.set_scene("profile")

@SceneManager.decorator_register(name="change_nickname")
class ChangeNickname(Scene):
    def build(self):
        self.add_message(TextMessage(
            content=f"{self.context if self.context else ''}Please enter your new nickname:",
            buttons=[[Button("Cancel", action=SendSceneAction(scene_name='profile'))]]
        ))

    def input(self, text):
        if len(text) > 64:
            self.user.set_scene("change_nickname", context="Nickname length is limited to 64 characters. ")
            return
        
        self.user.nameuser = text
        self.user.set_scene("profile", context="Nickname changed successfully!")

@SceneManager.decorator_register(name="bot_info")
class InfoScene(Scene):
    def build(self):
        self.add_message(PhotoMessage(content=open("test_photo.png", 'rb')))
        self.add_message(TextMessage(
            content="Bot developed using tele_menu.",
            buttons=[[Button("Back", action=SendSceneAction(scene_name='main_menu'))]]
        ))

# Start Polling
while True:
    try:
        Data.bot.polling(none_stop=True, interval=0)
    except Exception as e:
        Log.error(traceback.format_exc())
        break
    except requests.exceptions.SSLError:
        pass
    else:
        break
```

[Back to table of contents](#table-of-contents)

# Database & User Management (ORM)

`tele_menu` uses **db_attribute** as its ORM. The `User` class is pre-defined and inherits from `DbAttribute`. Changes to user attributes are automatically synchronized with the database (unless manual dump mode is enabled).

## The User Class
The internal `User` class structure includes these basic fields:
- `nameuser` (str)
- `tgUsername` (str)
- `registerData` (date)
- `rank` (str)

## Using User class

**1. Finding a User**
You can retrieve a user by ID or by searching specific fields using Python logical operators (`&`, `|` instead of `and`, `or`).

```python
# Get user by Telegram ID (Primary Key)
owner_id = 123456789
user = User.get(owner_id)

# Find user by specific attribute
found_user = User.get(User.nameuser == "JohnDoe")

# Find user with complex logic
admin_user = User.get((User.rank == "Admin") & (User.nameuser == "noname"))

# Get all users with rank 'User'
users_list = User.gets(User.rank == "User")
```

**2. Modifying Data**
Simply assign values to attributes. The ORM handles the SQL `UPDATE` operations automatically.

```python
# Update a simple field
user.nameuser = "NewName"
user.rank = "Owner"

# If the field is a mutable container (like a list or dict defined as DbField)
# modifications are tracked automatically.
```
[Back to table of contents](#table-of-contents)