Avatar Ilya Mateyko

go.astrophena.name/tools Module

GitHub repository | Commit (3a5506b)

Personal tools.

starlet

Install this program:

$ go install go.astrophena.name/tools/cmd/starlet@latest

Contents

Starlet is a Telegram bot runner powered by Starlark.

It fetches bot logic written in Starlark from a specified GitHub Gist, executes it within a Starlark interpreter, and handles incoming Telegram updates via webhooks. Starlet provides a sandboxed environment with pre-defined modules for interacting with Telegram, Gemini AI, key-value caching, and more.

When a Telegram update arrives, Starlet parses it, converts the JSON payload into a Starlark dictionary, and invokes the handle function defined in the user's bot.star script. Errors during execution are caught and reported back to the bot owner via Telegram, using a customizable error template.

Starlet is designed for deployment on platforms like Render and includes features like self-pinging to prevent idling on free tiers.

Usage

$ starlet [flags...]

Starlark Environment

The following built-in functions and modules are available within the bot.star script:

config: A struct containing bot configuration information.
	- bot_id (int): The Telegram user ID of the bot itself.
	- bot_username (str): The Telegram username of the bot itself.
	- owner_id (int): The Telegram user ID of the designated bot owner.
	- version (str): The version string of the running Starlet instance.

debug: A module providing debugging utilities.
	- stack() -> str: Returns the current Starlark call stack as a formatted string.
	- go_stack() -> str: Returns the current Go runtime call stack as a string.

fail(err: str): Causes the current Starlark execution to fail with the provided error message.
    This error will be reported back to the bot owner.

files: A module for accessing other files within the same GitHub Gist.
	- read(name: str) -> str: Reads the content of the file named name from the Gist.
	  Raises an error if the file does not exist in the Gist.

gemini: A module for interacting with Google's Gemini API (requires GEMINI_KEY env var).
	- generate_content(model: str, contents: list[str], system_instructions: str | None = None, unsafe: bool = False) -> list[list[str]]:
	  Generates content using the specified Gemini model.
	  - model: Name of the Gemini model (e.g., "gemini-1.5-flash").
	  - contents: A list of strings representing the conversation history or prompt parts.
	    Odd-indexed elements are treated as user input, even-indexed as model output (if len > 1).
	  - system_instructions: Optional system prompt to guide the model's behavior.
	  - unsafe: If True, disables safety filters (use with caution).
	  Returns a list of candidate responses, where each candidate is a list of text parts (strings).

kvcache: A module providing a simple in-memory key-value cache with time-to-live (TTL).
	- get(key: str) -> value | None: Retrieves the value for key. Returns None if the key
	  doesn't exist or has expired. Accessing a key resets its TTL.
	- set(key: str, value: any): Stores value under key, overwriting any existing entry.
	  Setting a key resets its TTL. TTL is based on last access/modification time.

markdown: A module for Markdown processing.
	- convert(s: str) -> dict: Converts the input Markdown string s into a dictionary
	  representing a Telegram message with entities, suitable for use with the
	  telegram.call method (e.g., as part of the args for sendMessage).

module(name: str, **members): Creates a new Starlark module object.

struct(**fields): Creates a new Starlark struct object.

telegram: A module for interacting with the Telegram Bot API.
	- call(method: str, args: dict) -> any: Makes a call to the specified Telegram Bot API
	  method with the given args dictionary. Returns the decoded JSON response
	  from the Telegram API as a Starlark value (usually a dict or list).

time: The standard Starlark time module. Provides functions for time manipulation,
      formatting, and parsing. See https://pkg.go.dev/go.starlark.net/lib/time#Module
      for detailed documentation.

Starlet Standard Library

Additionally, Starlet provides a small standard library that can be loaded from within bot.star using the @starlet// prefix:

load("@starlet//convcache.star", "convcache")
load("@starlet//tg.star", "tg")

Modules available:

convcache: Provides functions (get, append, reset) to manage simple conversation
           histories per chat ID using the kvcache.

tg: Provides helper functions built on top of telegram.call:
    - forward_message(to: int|str, from_chat_id: int|str, message_id: int): Forwards a message.
    - send_message(to: int|str, text: str, reply_markup: dict = {}, link_preview: bool = False):
      Sends a message, automatically handling Markdown conversion via markdown.convert
      and splitting long messages (>4096 chars) into multiple chunks.

GitHub Gist Structure

The bot's code and configuration reside in a GitHub Gist, which must contain:

Any other files (e.g., .json, .txt) can be included and accessed from bot.star using:

files.read("filename.ext")

Entry Point

The bot.star script *must* define a function named handle that accepts a single argument:

def handle(update):
    # update is a Starlark dictionary representing the received Telegram update JSON.
    # Bot logic goes here.
    pass

Optionally, you can define an on_load function:

def on_load():
    # Code here runs every time the bot code is loaded from the Gist,
    # including the initial startup and after a reload via the debug interface.
    # Useful for setup tasks like setting bot commands.
    pass

Starlet calls handle for each incoming Telegram update webhook. It calls on_load after successfully loading or reloading the code from the Gist.

Environment Variables

Starlet is configured using environment variables:

Required:

Optional:

Debug Interface

When not in production mode, or when accessed by the authenticated bot owner in production mode, Starlet provides a debug interface at /debug:

Authentication for the debug interface in production mode uses Telegram Login Widget. The bot owner must authenticate via Telegram. The login callback URL should be set to https://<your-bot-host>/login in BotFather (/setdomain).