REPOSITORY / ScuroNeko

Laniakea

Код, история изменений и документация проекта.
ACTIVE PUBLIC SOURCE
ScuroNeko 0ee0917af5 Update repo agent documentation rules
Require paired English and Russian docs when expanding project documentation
Clarify changelog handling for main repo changes and keep rc.12 notes aligned
Ignore local editor and Codex config directories in git
2026-03-26 23:06:05 +03:00
2026-03-23 12:59:18 +03:00
2026-03-25 18:07:41 +03:00
2026-03-25 18:07:41 +03:00
2026-03-23 13:19:39 +03:00
2026-03-25 18:07:41 +03:00
2026-03-25 18:07:41 +03:00
2026-01-29 11:50:03 +03:00
2026-03-26 18:35:35 +03:00
2026-03-25 18:07:41 +03:00
2026-03-25 18:07:41 +03:00
2026-03-25 18:07:41 +03:00
2026-03-25 18:07:41 +03:00

Laniakea

Laniakea

Go Version License: GPL-3.0 Gitea Release

A lightweight, easy-to-use, and performant Telegram Bot API wrapper for Go. It simplifies bot development with a clean plugin system, middleware support, automatic command generation, and built-in rate limiting.

На русском

Wiki


Features

  • Simple & Intuitive API: Designed for ease of use, based on practical examples.
  • Plugin System: Organize your bot's functionality into independent, reusable plugins.
  • Command Handling: Easily register commands and extract arguments.
  • Middleware Support: Run code before or after commands (e.g., logging, access control).
  • Automatic Command Generation: Generate help and command lists automatically.
  • Built-in Rate Limiting: Protect your bot from hitting Telegram API limits (supports retry_after handling).
  • Context-Aware: Pass custom database or state contexts to your handlers.
  • Fluent Interface: Chain methods for clean configuration (e.g., bot.ErrorTemplate(...).AddPlugins(...)).

📦 Installation

go get git.nix13.pw/scuroneko/laniakea

or

go get github.com/scuroneko/laniakea

🚀 Quick Start (with step-by-step explanation)

Here is a minimal echo/ping bot example with detailed comments.

package main

import (
	"log"

	"git.nix13.pw/scuroneko/laniakea" // Import the Laniakea library
)

// echo is a command handler function.
// It receives two parameters:
//   - ctx: the message context (contains info about the message, sender, chat, etc.)
//   - db: your custom database context (here we use NoDB, a placeholder for no database)
func echo(ctx *laniakea.MsgContext, db laniakea.NoDB) error {
	// Answer the user with the text they sent, without any command prefix.
	// ctx.Text contains the user's message with the command part stripped off.
	ctx.Answer(ctx.Text) // User input WITHOUT command
	return nil
}

func main() {
	// 1. Create bot options. Replace "TOKEN" with your actual bot token from @BotFather.
	opts := &laniakea.BotOpts{Token: "TOKEN"}

	// 2. Initialize a new bot instance.
	//    We use laniakea.NoDB as the database context type (no database needed for this example).
	bot, err := laniakea.NewBot[laniakea.NoDB](opts)
	if err != nil {
		log.Fatal(err)
	}
	// Ensure bot resources are cleaned up on exit.
	defer bot.Close()

	// 3. Create a new plugin named "ping".
	//    Plugins help group related commands and middlewares.
	p := laniakea.NewPlugin[laniakea.NoDB]("ping")

	// 4. Add a command to the plugin.
	//    p.NewCommand(echo, "echo") creates a command that triggers the 'echo' function on the "/echo" command.
	p.AddCommand(p.NewCommand(echo, "echo"))

	// 5. Add another command using an anonymous function (closure).
	//    This command simply replies "Pong" when the user sends "/ping".
	p.AddCommand(p.NewCommand(func(ctx *laniakea.MsgContext, db laniakea.NoDB) error {
		ctx.Answer("Pong")
		return nil
	}, "ping"))

	// 6. Configure the bot with a custom error template and add the plugin.
	//    ErrorTemplate sets a format string for errors (where %s will be replaced by the actual error).
	//    AddPlugins(p) registers our "ping" plugin with the bot.
	bot = bot.ErrorTemplate("Error\n\n%s").AddPlugins(p)

	// 7. Automatically generate commands like /start, /help, and a list of all registered commands.
	//    This is optional but very useful for most bots.
	if err := bot.AutoGenerateCommands(); err != nil {
		log.Println(err)
	}

	// 8. Start the bot, listening for updates (long polling).
	if err := bot.Run(); err != nil {
		log.Fatal(err)
	}
}

How It Works

  1. BotOpts: Holds configuration like the API token.
  2. NewBot[T]: Creates a bot instance. The type parameter T allows you to pass a custom database context (e.g., *sql.DB) that will be available in all handlers. Use laniakea.NoDB if you don't need it.
  3. NewPlugin: Creates a logical group for commands and middlewares.
  4. AddCommand: Registers a command. The first argument is the handler function (func(*MsgContext, T) error), the second is the command name (without the slash).
  5. Handler Functions: Receive *MsgContext (message details, methods like Answer) and your custom database context T, and return an error for centralized error handling.
  6. ErrorTemplate: Sets a template for error messages. The %s placeholder is replaced by the actual error.
  7. AutoGenerateCommands: Registers plugin-defined commands with Telegram across the supported scopes.
  8. Run(): Starts the bot's update polling loop and returns an error if startup or polling fails.
  9. A Bot instance is single-use. After Run() or RunWithContext() returns, create a new bot instance for the next session.

📖 Core Concepts

Plugins

Plugins are the main way to organize code. A plugin can have multiple commands and middlewares.

plugin := laniakea.NewPlugin[*MyDB]("admin")
plugin.AddCommand(plugin.NewCommand(banUser, "ban"))
bot.AddPlugins(plugin)

Commands

A command is a function that handles a specific bot command (e.g., /start).

func myHandler(ctx *laniakea.MsgContext, db *MyDB) error {
    // Access command arguments via ctx.Args ([]string)
    // Reply to the user: ctx.Answer("some text")
    return nil
}

MsgContext

Provides access to the incoming message and useful reply methods:

  • Answer(text string) *AnswerMessage: Sends a message with parse_mode none.
  • AnswerLong(text string) []*AnswerMessage: Splits long plain text into multiple messages.
  • AnswerMarkdown(text string) *AnswerMessage: Sends a message formatted with MarkdownV2 (you handle escaping).
  • Keyboard(text string, keyboard *InlineKeyboard) *AnswerMessage: Sends a message with parse_mode none and inline keyboard.
  • KeyboardLong(text string, keyboard *InlineKeyboard) []*AnswerMessage: Splits long plain text into multiple messages and attaches the keyboard to the final chunk.
  • KeyboardMarkdown(text string, keyboard *InlineKeyboard) *AnswerMessage: Sends a message formatted with MarkdownV2 (you handle escaping) and inline keyboard.
  • AnswerPhoto(photoId, text string) *AnswerMessage: Sends a message with photo with parse_mode none.
  • AnswerPhotoMarkdown(photoId, text string) *AnswerMessage: Sends a photo with MarkdownV2 caption (you handle escaping).
  • EditCallback(text string): Edits message with parse_mode none after clicking inline button.
  • EditCallbackMarkdown(text string): Edits a message formatted with MarkdownV2 (you handle escaping) after clicking inline button.
  • SendAction(action tgapi.ChatActionType): Sends a “typing”, “uploading photo”, etc., action.
  • Fields: Text, Args, From, FromID, Msg, InlineMsgId, CallbackQueryId, etc.
  • And more methods and fields!

tgapi: API and Uploader

tgapi provides two clients:

  • API for JSON requests (e.g., SendMessage, EditMessageText, methods using file_id/URL).
  • Uploader for multipart uploads (e.g., SendPhoto, SendDocument, SendVideo with binary files).

This split keeps method intent explicit: JSON-only calls go through API, file uploads go through Uploader.

For advanced cases, tgapi.NewRequest(...) and tgapi.NewUploaderRequest(...) remain public as low-level escape hatches. They are intentionally less safe than method-specific helpers: callers must supply the correct Telegram method name and compatible request/response types themselves.

Database Context

The T in NewBot[T] is a powerful feature. You can pass any type, but shared dependencies such as database pools should usually use a pointer type.

type MyDB struct { /* ... */ }
db := &MyDB{...}
bot, err := laniakea.NewBot[*MyDB](opts)
if err != nil {
    log.Fatal(err)
}
bot.DatabaseContext(db)

🧩 Middleware

Middleware are functions that run before a command handler. They are perfect for cross-cutting concerns like logging, access control, rate limiting, or modifying the context.

Signature

A middleware function has the same signature as a command handler, but it must return a bool:

func(ctx *MsgContext, db T) bool
  • If it returns true, the next middleware (or the command) will be executed.
  • If it returns false, the execution chain stops immediately (the command will not run).

Adding Middleware

Use AddMiddleware on a plugin to add one or more shared middleware functions. They are executed in the order they are added.

plugin := laniakea.NewPlugin[*MyDB]("admin")
plugin.AddMiddleware(laniakea.NewMiddleware("logging", loggingMiddleware))
plugin.AddMiddleware(laniakea.NewMiddleware("admin-only", adminOnlyMiddleware))
plugin.AddCommand(plugin.NewCommand(banUser, "ban"))

Example Middlewares

  1. Logging Middleware logs every command execution.
func loggingMiddleware(ctx *laniakea.MsgContext, db *MyDB) bool {
    log.Printf("User %d executed command: %s", ctx.FromID, ctx.Msg.Text)
    return true // continue to next middleware/command
}
  1. Admin-Only Middleware restricts access to users with a specific role.
func adminOnlyMiddleware(ctx *laniakea.MsgContext, db *MyDB) bool {
    if !db.IsAdmin(ctx.FromID) { // assume db has IsAdmin method
        ctx.Answer("⛔ Access denied. Admins only.")
        return false // stop execution
    }
    return true
}

Important Notes

  • Middleware can modify the MsgContext (e.g., add custom fields) before the command runs.

⚙️ Advanced Configuration

  • Inline Keyboards: Build keyboards using laniakea.NewInlineKeyboardJson, laniakea.NewInlineKeyboardBase64, or laniakea.NewInlineKeyboard. Bot.SetPayloadType(...) defines the default payload format, and InlineKeyboard.SetPayloadType(...) overrides it for one keyboard.
  • Rate Limiting: Pass a configured utils.RateLimiter via BotOpts to handle Telegram's rate limits gracefully.
  • Localization: L10n is safe for concurrent use once attached to the bot.
  • Custom Update Handlers: Use plugin.AddUpdateHandler(...) for Telegram update types that are not part of the command/payload flow.
  • Lifecycle: RunWithContext(...) does not call Close() for you. Shut the bot down explicitly, and create a fresh Bot for the next run.

Telegram Update Handling

  • Commands and payloads are handled through plugins.
  • Non-command updates can be routed with plugin.AddUpdateHandler(updateType, handler).
  • message, channel_post, and callback_query stay on the command/payload flow.
  • tgapi.Update exposes a derived Type field after JSON unmarshalling so handlers can inspect the effective update kind directly.

📝 License

This project is licensed under the GNU General Public License v3.0 — see the LICENSE file for details.

📚 Learn More

GoDoc

Wiki

Telegram Bot API

✅ Built with ❤️ by scuroneko
S
Description
No description provided
Readme GPL-3.0
1.7 MiB
1.2.0
Latest
2026-08-20 11:08:45 +03:00
Languages
Go 100%