Skip to main content

Async Python client for the Polis Bot API

Project description

polis-bot

Async Python client for the Polis Bot API — build bots for the Polis messaging platform.

Requires Python 3.10+. Built on httpx and Pydantic v2.

Installation

pip install polis-bot

Quick Start

from polis_bot import PolisBot, CommandContext, MessageContext

bot = PolisBot("your-bot-token")

@bot.command("/start", description="Start the bot")
async def start(ctx: CommandContext):
    await ctx.reply(f"Hello {ctx.user.display_name}! 🤖")

@bot.command("/help", description="Show help")
async def help_cmd(ctx: CommandContext):
    await ctx.reply("**Commands:**\n- /start — Greet\n- /help — This message")

@bot.on_message
async def echo(ctx: MessageContext):
    await ctx.reply(f"You said: {ctx.message.content}")

bot.run()

Commands are auto-registered with the server on startup. The bot long-polls for updates and dispatches them to the matching handler.

Command Arguments

Handler type hints drive automatic argument parsing — like FastAPI for bots:

@bot.command("/add", description="Add two numbers")
async def add(ctx: CommandContext, a: int, b: int):
    await ctx.reply(f"{a + b}")
    # /add 3 5 → "8"

The last str parameter absorbs remaining tokens:

@bot.command("/echo", description="Echo text")
async def echo_cmd(ctx: CommandContext, text: str):
    await ctx.reply(text)
    # /echo hello world → "hello world"

Supported types: str, int, float, bool, UUID, and Pydantic BaseModel subclasses. Invalid input returns a usage hint automatically.

File Handling

Files are attachments on messages — any message (text, command, or standalone) can carry one or more files. Each attachment is an Attachment object with metadata properties and download() / download_to() methods — no need to manage URLs.

Sending files

# File-only message
await bot.send_file(user_id, "image.png", content_type="image/png")

# Text + file in one message
await bot.send_file(user_id, "report.pdf",
                    content_type="application/pdf",
                    content="Here's the report you requested")

# Via context
@bot.command("/cat", description="Send a cat picture")
async def cat(ctx: CommandContext):
    await ctx.reply_file("cat.jpg", content_type="image/jpeg")

Receiving files

File attachments are available on any message via ctx.message.attachments. Each Attachment exposes metadata and can download itself:

@bot.on_message
async def handle(ctx: MessageContext):
    if ctx.message.attachments:
        for att in ctx.message.attachments:
            print(f"{att.file_name} ({att.content_type}, {att.size_bytes} bytes)")
            # Download to memory
            data = await att.download()
            # or save to disk
            await att.download_to(f"downloads/{att.file_name}")
        await ctx.reply(f"Got {len(ctx.message.attachments)} file(s)")
    elif ctx.message.content:
        await ctx.reply(f"You said: {ctx.message.content}")

Commands can also have file attachments:

@bot.command("/analyze", description="Analyze an uploaded file")
async def analyze(ctx: CommandContext):
    if not ctx.message.attachments:
        await ctx.reply("Please attach a file to analyze")
        return
    att = ctx.message.attachments[0]
    data = await att.download()
    await ctx.reply(f"Analyzed **{att.file_name}**: {len(data)} bytes")

Lifecycle Events

from polis_bot import EventContext

@bot.on_event("BotStarted")
async def on_started(ctx: EventContext):
    await bot.send_message(ctx.user.id, "Welcome! Send /help to get started.")

@bot.on_event("BotBlocked")
async def on_blocked(ctx: EventContext):
    print(f"{ctx.user.display_name} blocked the bot")

Event types: BotStarted, BotBlocked, BotUnblocked, BotAdded, BotRemoved.

Startup Hook

@bot.on_startup
async def setup():
    print(f"Running as @{bot.me.botname} ({bot.me.display_name})")

bot.me is populated before startup hooks run and returns cached BotInfo.

Error Handling

The SDK raises typed exceptions for API errors:

from polis_bot import PolisApiError, PolisAuthError, PolisRateLimitError

try:
    await bot.send_message(user_id, "hello")
except PolisAuthError:
    print("Invalid bot token")
except PolisRateLimitError as e:
    print(f"Rate limited — retry after {e.retry_after}s")
except PolisApiError as e:
    print(f"HTTP {e.status_code}: {e.message}")
Exception HTTP Status
PolisAuthError 401
PolisForbiddenError 403
PolisNotFoundError 404
PolisRateLimitError 429
PolisApiError Any non-2xx

Async Context Manager

async with PolisBot("token") as bot:
    me = await bot.get_me()
    await bot.send_message(user_id, "Hello!")
# client is automatically closed

Low-Level Polling

For full control over the update loop instead of using decorators:

async with PolisBot("token") as bot:
    async for update in bot.poll(limit=20, timeout=30):
        print(update.type, update.user.display_name)

        if update.message and update.message.is_command:
            await bot.send_message(update.user.id, "Got your command!")

Updates are automatically acknowledged after each batch. Disable with auto_ack=False:

async for update in bot.poll(auto_ack=False):
    # manually acknowledge
    await bot.acknowledge_updates([update.update_id])

API Reference

PolisBot(token, base_url="https://polis.krlv.org", *, timeout=30.0)

Properties

Property Description
me Cached BotInfo (available after start() / run())

Decorators

Decorator Description
@bot.command(name, *, description=None) Register a /command handler with auto-parsed args
@bot.on_message Handle non-command messages (text and/or file attachments)
@bot.on_event(event_type) Handle lifecycle events (BotStarted, etc.)
@bot.on_startup Run a coroutine once on startup

Methods

Method Description
run(*, register_commands=True) Start polling and dispatch (blocking)
start(*, register_commands=True) Async version of run()
get_me() Get bot info
send_message(user_id, content, *, reply_to_id=None) Send a text message
send_file(user_id, file, *, filename=None, content_type=..., content=None) Upload a file (optionally with text)
get_updates(*, limit, offset, timeout) Fetch pending updates
acknowledge_updates(update_ids) Mark updates as delivered
set_commands(commands) Register bot commands
poll(*, limit=20, timeout=30, auto_ack=True) Async generator for long-polling
close() Close the HTTP client

Context Objects

Class Available On Key Properties
CommandContext @bot.command user, message, args, bot, reply(), reply_file()
MessageContext @bot.on_message Same as CommandContext
EventContext @bot.on_event user, bot

Attachment

Each file attachment on ctx.message.attachments is an Attachment instance:

Property / Method Description
file_name Original filename
content_type MIME type
size_bytes File size in bytes
id Server-assigned file ID
download() Download and return raw bytes
download_to(dest) Download and save to disk, returns Path

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

polis_bot-0.3.0.tar.gz (27.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

polis_bot-0.3.0-py3-none-any.whl (16.4 kB view details)

Uploaded Python 3

File details

Details for the file polis_bot-0.3.0.tar.gz.

File metadata

  • Download URL: polis_bot-0.3.0.tar.gz
  • Upload date:
  • Size: 27.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for polis_bot-0.3.0.tar.gz
Algorithm Hash digest
SHA256 fc5c9a57b23a8faf1de5cfa29710c8dfb2d3481966b3dc6829a9a62d13bbdccb
MD5 73a70b49601233a9fb11f116d2331090
BLAKE2b-256 4e2d166273f579986158832499f92a8f803543f72fe5a72b19448da4e2be8201

See more details on using hashes here.

Provenance

The following attestation bundles were made for polis_bot-0.3.0.tar.gz:

Publisher: sdk-release.yml on roman-right/Polis

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file polis_bot-0.3.0-py3-none-any.whl.

File metadata

  • Download URL: polis_bot-0.3.0-py3-none-any.whl
  • Upload date:
  • Size: 16.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for polis_bot-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 3ecb43f534f26761461089b844bde6c8e49ec47f46daf6642d9de1fd8e57f6f4
MD5 b18c79390d921149f6f9e44694a3b27a
BLAKE2b-256 3b15f7bdcd4351082ecdd469180dfcd1ed9e59be71e2930eb8561d0788718b03

See more details on using hashes here.

Provenance

The following attestation bundles were made for polis_bot-0.3.0-py3-none-any.whl:

Publisher: sdk-release.yml on roman-right/Polis

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page