Skip to content

KnightNiwrem/grammy-menu-message

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

149 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

grammY Menu Message

A lightweight, type-safe menu system for grammY Telegram bots built with Deno. Create declarative inline keyboards with automatic callback routing, persistent navigation history, and media support.

Features

  • Declarative Menu Building โ€” Define menu builders using a chainable builder API
  • Automatic Callback Routing โ€” Callbacks are handled internally with zero manual routing code
  • Media Support โ€” Create menus with photos, videos, animations, audio, or documents
  • Navigation History โ€” Built-in tracking of menu navigation per message
  • Type-Safe โ€” Full TypeScript support with proper type inference
  • Storage Flexibility โ€” Pluggable storage adapters for persistence (memory, Redis, etc.)
  • Middleware Integration โ€” Seamless integration with grammY's middleware system

Installation

import { MenuBuilder, MenuRegistry } from "jsr:@your-scope/grammy-menu-message";

Note: This library is currently in development and not yet published to JSR.

Quick Start

import { Bot } from "https://lib.deno.dev/x/grammy@v1/mod.ts";
import { MenuBuilder, MenuRegistry } from "./src/mod.ts";

const bot = new Bot(Deno.env.get("BOT_TOKEN")!);
const registry = new MenuRegistry();

// Define a menu builder
const mainMenu = new MenuBuilder("Welcome! Choose an option:")
  .cb("Say Hello", async (ctx) => {
    await ctx.reply("Hello! ๐Ÿ‘‹");
  })
  .cb("Show Info", async (ctx) => {
    await ctx.reply("This is a grammY menu example.");
  })
  .row()
  .url("GitHub", "https://github.com")
  .url("Documentation", "https://grammy.dev");

// Register the builder
registry.register("main", mainMenu);

// Use the registry middleware
bot.use(registry.middleware());

// Send the menu
bot.command("start", async (ctx) => {
  const menu = registry.menu("main");
  await ctx.reply("Loading menu...", { reply_markup: menu });
});

bot.start();

Core Concepts

MenuBuilder

A MenuBuilder is a declarative builder for defining menu structure. It supports:

  • Callback buttons (.cb()) โ€” Buttons with handler functions
  • URL buttons (.url()) โ€” Direct links to websites
  • Web App buttons (.webApp()) โ€” Open Telegram Web Apps
  • Inline query buttons (.switchInline(), .switchInlineCurrent(), .switchInlineChosen())
  • Special buttons โ€” Login, copy text, game, and payment buttons
  • Row control (.row()) โ€” Start a new button row

MenuRegistry

The MenuRegistry manages menu builders and handles callback routing:

  • Register builders โ€” registry.register("id", builder)
  • Render menus โ€” registry.menu("id") creates a new menu instance
  • Middleware โ€” registry.middleware() handles callbacks automatically
  • Storage โ€” Configurable storage adapters for persistence across restarts

Menu Types

Different menu types support different media attachments:

  • MenuBuilder โ€” Text-only menus
  • PhotoMenuBuilder โ€” Menus with photos
  • VideoMenuBuilder โ€” Menus with videos
  • AnimationMenuBuilder โ€” Menus with GIF/animations
  • AudioMenuBuilder โ€” Menus with audio files
  • DocumentMenuBuilder โ€” Menus with documents

You can convert between types using chainable methods like .photo(), .video(), etc.

Examples

Basic Menu with Callbacks

const menu = new MenuBuilder("Choose an action:")
  .cb("Option 1", async (ctx) => {
    await ctx.reply("You selected option 1");
  })
  .cb("Option 2", async (ctx) => {
    await ctx.reply("You selected option 2");
  })
  .row()
  .cb("Back", async (ctx) => {
    await ctx.reply("Going back...");
  });

registry.register("basic", menu);

Menu with Media

const photoMenu = new MenuBuilder("Check out this image!")
  .photo("https://picsum.photos/800/600")
  .cb("Like", async (ctx) => {
    await ctx.reply("Thanks for liking!");
  })
  .cb("Share", async (ctx) => {
    await ctx.reply("Sharing...");
  });

registry.register("photo", photoMenu);

Menu with Mixed Button Types

const mixedMenu = new MenuBuilder("Explore options:")
  .cb("Settings", async (ctx) => {
    await ctx.reply("Opening settings...");
  })
  .row()
  .url("Website", "https://example.com")
  .webApp("Web App", "https://app.example.com")
  .row()
  .switchInline("Share Bot", "check out this bot");

registry.register("mixed", mixedMenu);

Custom Storage

import { MenuRegistry } from "./src/mod.ts";

// Use custom storage adapters for persistence
const registry = new MenuRegistry({
  keyPrefix: "mybot",
  menuStorage: new RedisAdapter(),
  navigationStorage: new RedisAdapter(),
});

API Reference

MenuBuilder Methods

Button Methods

  • .cb(label, handler, payload?) โ€” Add callback button with handler
  • .rawCb(label, callbackData) โ€” Add raw callback button (manual routing)
  • .url(text, url) โ€” Add URL button
  • .webApp(text, url) โ€” Add Web App button
  • .login(text, loginUrl) โ€” Add login button
  • .switchInline(text, query?) โ€” Add inline query button
  • .switchInlineCurrent(text, query?) โ€” Add inline query button (current chat)
  • .switchInlineChosen(text, query?) โ€” Add inline query button (chosen chat filter)
  • .copyText(text, copyText) โ€” Add copy text button
  • .game(text) โ€” Add game button
  • .pay(text) โ€” Add payment button

Layout Methods

  • .row() โ€” Start a new button row
  • .addText(text) โ€” Set or replace menu text

Media Methods

  • .photo(photo) โ€” Convert to PhotoMenuBuilder
  • .video(video) โ€” Convert to VideoMenuBuilder
  • .animation(animation) โ€” Convert to AnimationMenuBuilder
  • .audio(audio) โ€” Convert to AudioMenuBuilder
  • .document(document) โ€” Convert to DocumentMenuBuilder

MenuRegistry Methods

  • register(templateId, builder) โ€” Register a menu builder
  • get(templateId) โ€” Retrieve a registered builder
  • has(templateId) โ€” Check if builder exists
  • menu(templateId) โ€” Render a menu from builder
  • middleware() โ€” Get the middleware function

Development

This project uses Deno. Available tasks:

deno task fmt      # Format code
deno task lint     # Lint code
deno task test     # Run tests
deno task check    # Type-check code
deno task ok       # Run all checks (fmt + lint + test + check)

Always run deno task ok before committing.

Contributing

Contributions are welcome! Please:

  1. Create a feature branch from main
  2. Make your changes with appropriate tests
  3. Run deno task ok to ensure all checks pass
  4. Submit a pull request

License

MIT License โ€” see LICENSE for details.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

โšก