Metadata-Version: 2.4
Name: shingram
Version: 0.2.0
Summary: Minimal Python wrapper for the Telegram Bot API; one Event, one router, sync and async.
Author: Ren Nouzumoto
Maintainer: Ren Nouzumoto
License: MIT
Project-URL: Homepage, https://github.com/nouzumoto/shingram
Project-URL: Documentation, https://nouzumoto.github.io/shingram/
Project-URL: Repository, https://github.com/nouzumoto/shingram
Project-URL: Issues, https://github.com/nouzumoto/shingram/issues
Project-URL: Bug Tracker, https://github.com/nouzumoto/shingram/issues
Keywords: telegram,bot,api,wrapper,telegram-bot,telegram-api,shingram,minimal,lightweight
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Communications :: Chat
Requires-Python: >=3.8
Description-Content-Type: text/markdown
Requires-Dist: httpx>=0.24.0
Provides-Extra: dev
Requires-Dist: pytest>=7.0.0; extra == "dev"

# Shingram

[![Channel](https://shields.io/badge/channel-subscribe-blue?logo=telegram&color=3faee8)](https://t.me/shingramnews)
[![Documentation](https://img.shields.io/badge/docs-here-red)](https://nouzumoto.github.io/shingram/)

Minimal Python wrapper for the Telegram Bot API. One `Event` shape for all update types, one router for handlers; sync and async share the same core (normalize, router, error handling). New Telegram methods work without changing the library.

## Features

- **Zero hardcoding** — All Telegram API methods work without library updates. Call `bot.send_message(...)`, `bot.send_photo(...)`, or any method; names are converted from `snake_case` to `camelCase` automatically.
- **Single Event type** — Every update (message, callback, inline, etc.) is normalized to one `Event`; the full payload stays in `event.raw`.
- **Full update coverage** — Commands, messages, edits, channel posts, inline queries, callbacks, joins, polls, reactions, business, webhooks.
- **Sync and async** — Use `bot.run()` with sync handlers or `bot.run_async()` with async handlers; same router and Event, shared core.
- **Long polling** — Offset and timeouts handled. Optional `timeout`, `limit`, `allowed_updates`, and `on_error` on `run()` / `run_async()`. Webhook examples with Flask and FastAPI.

## Installation

```bash
pip install shingram
```

## Documentation

Full documentation is available at: **[https://nouzumoto.github.io/shingram/](https://nouzumoto.github.io/shingram/)**

## Quick Start

```python
from shingram import Bot

bot = Bot("YOUR_BOT_TOKEN")

@bot.on("command:start")
def handle_start(event):
    bot.send_message(chat_id=event.chat_id, text="Hello!")

@bot.on("message")
def handle_message(event):
    bot.send_message(chat_id=event.chat_id, text=f"You wrote: {event.text}")

bot.run()
```

## Event types

- **Commands**: `command:start`, `command:help`, etc. Use `command` to catch all commands
- **Messages**: `message` for text messages
- **Edited messages**: `edited_message` for edited text messages
- **Channel posts**: `channel_post` and `edited_channel_post`
- **Inline queries**: `inline_query` for inline search queries
- **Callback queries**: `callback` for inline button clicks
- **Join/Leave events**: `join` and `leave` for group membership changes
- **Poll updates**: `poll` and `poll_answer` for poll-related events
- **Chat member updates**: `chat_member` and `my_chat_member`
- **Chat join requests**: `chat_join_request`
- **Business messages**: `business_message`, `edited_business_message`, `business_connection`
- **Message reactions**: `message_reaction` and `message_reaction_count`
- **Chat boosts**: `chat_boost` and `removed_chat_boost`
- **Shipping and payment**: `shipping_query`, `pre_checkout_query`
- **Chosen inline results**: `chosen_inline_result`

Handlers always get the same `Event`; the field that varies is `type` (and `name` for commands/callbacks).

## Event fields

```python
@dataclass
class Event:
    type: str                    # Event type: "command", "message", "callback", etc.
    name: str                    # Event name: "start" for commands, "" for others
    chat_id: int                 # Chat ID (0 if not applicable)
    user_id: int                 # User ID (0 if not applicable)
    text: str                    # Message text or callback data
    raw: dict                    # Complete raw data from Telegram API
    reply_to: Optional[int]      # ID of replied message (if present)
    chat_type: Optional[str]     # "private", "group", "supergroup", "channel"
    inline_query_id: Optional[str]    # For inline_query events
    callback_query_id: Optional[str]  # For callback_query events
    message_id: Optional[int]    # Message ID (if available)
    username: Optional[str]      # User username (if available)
    first_name: Optional[str]    # User first name (if available)
    chat_title: Optional[str]    # Chat title (for groups/channels)
    last_name: Optional[str]     # User last name (if available)
    language_code: Optional[str] # User language code (if available)
    content_type: Optional[str]  # "text", "photo", "document", etc. (for messages)
```

## Async

Use `async def` handlers and start the bot with `bot.run_async()`. For API calls use `await bot.async_client.send_message(...)` (and the other methods on `bot.async_client`).

```python
from shingram import Bot

bot = Bot("YOUR_BOT_TOKEN")

@bot.on("command:start")
async def handle_start(event):
    await bot.async_client.send_message(chat_id=event.chat_id, text="Hello!")

@bot.on("message")
async def handle_message(event):
    if event.text:
        await bot.async_client.send_message(chat_id=event.chat_id, text=f"You said: {event.text}")

bot.run_async()
```

You can pass optional polling options to `run()` and `run_async()`: `timeout` (default 30), `limit` (default 100), `allowed_updates` (list of update types, or `None` for all), and `on_error` (callback for polling-loop errors, e.g. `bot.run(on_error=logger.exception)`).

## Error Handling

```python
from shingram import Bot, TelegramAPIError

try:
    bot.send_message(chat_id=123, text="Test")
except TelegramAPIError as e:
    print(f"Telegram API error: {e}")
    print(f"Error code: {e.error_code}")
    print(f"Description: {e.description}")
```

## Examples

See the **`examples/`** directory in the repo. You can find plenty of examples there: **sync** (e.g. `echo_bot.py`, `inline_bot.py`, `keyboard_bot.py`, `webhook_flask.py`, `webhook_fastapi.py`) and **async** (e.g. `echo_bot_async.py`, `inline_bot_async.py`, `keyboard_bot_async.py`). Set your bot token in the file and run it.

## License

MIT License - see `LICENSE` for details.
