ESC
Start typing to search...

Plugin SDK Reference

Complete single-page reference for every namespace, method, and type in the @teleton-agent/sdk package.

1. Overview & Interface

The PluginSDK is a frozen, immutable object passed to every plugin via the tools(sdk) export. All properties are readonly. The object is created once per plugin at load time and cannot be modified at runtime.

@teleton-agent/sdk
interface PluginSDK {
  version: string
  readonly ton: TonSDK
  readonly telegram: TelegramSDK
  readonly db: Database | null
  readonly config: Record<string, unknown>
  readonly pluginConfig: Record<string, unknown>
  readonly secrets: SecretsSDK
  readonly storage: StorageSDK | null
  readonly log: PluginLogger
}
PropertyTypeDescription
versionstringSDK version (SemVer). Always "1.0.0".
tonTonSDKTON blockchain: wallet, balance, transfers, jettons, NFTs, payment verification.
telegramTelegramSDKTelegram messaging, media, moderation, stars & gifts. Runtime-only.
dbDatabase | nullPlugin-isolated SQLite database (better-sqlite3). null if the plugin does not export migrate().
configRecord<string, unknown>Sanitized application config. Excludes all API keys and secrets.
pluginConfigRecord<string, unknown>Plugin-specific config from config.yaml merged with manifest defaultConfig.
secretsSecretsSDKSecure access to API keys, tokens, and credentials.
storageStorageSDK | nullKey-value storage with optional TTL. Always available (auto-creates _kv table).
logPluginLoggerPrefixed logger: [plugin:name] message.
botBotSDK | nullBot SDK for inline mode — null if bot not configured or plugin manifest does not declare a BotManifest (bot: { inline?: true, callbacks?: true }).

Sub-namespaces: sdk.ton exposes two nested namespaces: sdk.ton.dex (DEX quotes and swaps on STON.fi and DeDust) and sdk.ton.dns (.ton domain management). These are always available when sdk.ton is available.

sdk.db vs sdk.storage: sdk.db is null if the plugin does not export migrate(), but sdk.storage works regardless — it auto-creates its own _kv table without needing a migration.

2. sdk.version

Type: string. Always "1.0.0" (SemVer). Use this for compatibility checks in your plugin manifest via the sdkVersion field.

Example
if (sdk.version !== '1.0.0') {
  sdk.log.warn('Untested SDK version: ' + sdk.version);
}

3. sdk.ton : TON Blockchain

Type: TonSDK. Provides full access to TON blockchain operations including wallet management, token transfers, NFTs, and payment verification.

Core Methods

MethodReturnsDescription
getAddress()string | nullBot's wallet address. Returns null if wallet is not initialized.
getBalance(address?)Promise<TonBalance>Balance in TON + nanoTON. Omit address for the bot's own wallet.
getPrice()Promise<TonPrice>Current TON/USD price. Cached for 30 seconds.
sendTON(to, amount, comment?)Promise<TonSendResult>Transfer TON to an address. IRREVERSIBLE.
getTransactions(address, limit?)Promise<TonTransaction[]>Recent transactions. limit: 1–50, default 10.
validateAddress(address)booleanCheck if a TON address is valid.
toNano(amount)bigintConvert TON amount to nanoTON.
fromNano(nano)stringConvert nanoTON to TON string.

Jetton Methods

MethodReturnsDescription
getJettonBalances(ownerAddress?)Promise<...>Token balances for the owner. Defaults to bot wallet.
getJettonInfo(jettonAddress)Promise<...>Jetton metadata: name, symbol, decimals, total supply.
sendJetton(jettonAddress, to, amount, opts?)Promise<...>Transfer jetton tokens to another address.
getJettonWalletAddress(ownerAddress, jettonAddress)Promise<string>Resolve the jetton wallet address for an owner.

NFT Methods

MethodReturnsDescription
getNftItems(ownerAddress?)Promise<...>List NFTs owned by the address. Defaults to bot wallet.
getNftInfo(nftAddress)Promise<...>Metadata for a single NFT.

Payment Verification

verifyPayment(params)
const result = await sdk.ton.verifyPayment({
  amount: '1.5',        // expected TON amount (1% tolerance)
  memo: 'order-123',    // expected transaction memo
  timeWindow: 600,      // seconds to look back (default: 600 = 10min)
  senderAddress: '...'  // optional: expected sender
});

// result: {
//   verified: boolean,
//   txHash: string,
//   amount: string,
//   playerWallet: string,
//   date: Date,
//   secondsAgo: number,
//   error?: string
// }

Payment verification checks recent transactions for: amount match (1% tolerance), memo match, time window (10 minutes default), and replay protection via the used_transactions table.

TON Error Codes

ErrorMeaning
WALLET_NOT_INITIALIZEDBot wallet has not been set up or activated.
INVALID_ADDRESSThe provided TON address is malformed.
OPERATION_FAILEDGeneric blockchain operation failure.
TON usage example
const balance = await sdk.ton.getBalance();
sdk.log.info(`Wallet balance: ${balance.balance} TON`);

const price = await sdk.ton.getPrice();
sdk.log.info(`TON/USD: $${price.usd}`);

if (sdk.ton.validateAddress(userAddr)) {
  const result = await sdk.ton.sendTON(userAddr, '0.5', 'reward');
  sdk.log.info(`Sent 0.5 TON, hash: ${result.hash}`);
}

4. sdk.telegram : Telegram Messaging

Type: TelegramSDK. Provides access to the full Telegram API via GramJS. Only available at runtime (not during plugin loading). Call sdk.telegram.isAvailable() to check.

Basic Messaging

MethodReturnsDescription
sendMessage(chatId, text, opts?)messageIdSend a text message. opts may include parseMode, replyTo, buttons.
editMessage(chatId, messageId, text, opts?)messageIdEdit an existing message.
sendReaction(chatId, messageId, emoji)voidReact to a message with an emoji.
sendDice(chatId, emoticon, replyToId?){value, messageId}Send an animated dice/slot/bowling message.

Media

MethodReturnsDescription
sendPhoto(chatId, photo, opts?)messageIdSend a photo. photo can be a file path or Buffer.
sendVideo(chatId, video, opts?)messageIdSend a video file.
sendVoice(chatId, voice, opts?)messageIdSend a voice message. Must be OGG/Opus format.
sendFile(chatId, file, opts?)messageIdSend a document. Use opts.fileName to set the name.
sendGif(chatId, gif, opts?)messageIdSend an animated GIF.
sendSticker(chatId, sticker)messageIdSend a sticker. Must be WEBP format.
downloadMedia(chatId, messageId)Buffer | nullDownload media from a message. Returns null if no media.

Message Queries

MethodReturnsDescription
getMessages(chatId, limit?)Message[]Fetch recent messages from a chat.
searchMessages(chatId, query, limit?)Message[]Search messages in a chat by text query.
getReplies(chatId, messageId, limit?)Message[]Get replies to a specific message.

Message Management

MethodReturnsDescription
deleteMessage(chatId, messageId, revoke?)voidDelete a message. revoke deletes for everyone.
forwardMessage(fromChatId, toChatId, messageId)messageIdForward a message between chats.
pinMessage(chatId, messageId, opts?)voidPin a message in a chat.
scheduleMessage(chatId, text, scheduleDate)messageIdSchedule a message for future delivery.

Chat & User Info

MethodReturnsDescription
getChatInfo(chatId){id, title, type, membersCount?, username?}Get chat metadata.
getUserInfo(userId){id, firstName, lastName?, username?, isBot}Get user profile info.
resolveUsername(username){id, type, username?, title?}Resolve a @username to an entity.
getParticipants(chatId, limit?)Participant[]List chat/group members.
getMe()UserGet the bot's own user profile.

Interactive

MethodReturnsDescription
createPoll(chatId, question, answers, opts?)messageIdCreate a poll. opts may include isAnonymous, multipleChoice.
createQuiz(chatId, question, answers, correctIndex, explanation?)messageIdCreate a quiz with a correct answer.

Moderation

MethodReturnsDescription
banUser(chatId, userId)voidBan a user from a chat.
unbanUser(chatId, userId)voidUnban a previously banned user.
muteUser(chatId, userId, untilDate?)voidMute a user. untilDate is a Unix timestamp; omit for permanent mute.

Stars & Gifts

MethodReturnsDescription
getStarsBalance()numberGet the bot's Telegram Stars balance.
sendGift(userId, giftId, opts?)voidSend a gift to a user.
getAvailableGifts()Gift[]List all gifts available for purchase.
getMyGifts(limit?)Gift[]List gifts the bot has received.
getResaleGifts(limit?)Gift[]Browse gifts available for resale.
buyResaleGift(giftId)voidPurchase a resale gift.
sendStory(mediaPath, opts?)voidPost a story to the bot's profile.

Advanced

MethodReturnsDescription
setTyping(chatId)voidShow "typing..." indicator in a chat.
getRawClient()TelegramClientAccess the raw GramJS TelegramClient instance for unsupported operations.
isAvailable()booleanCheck if the Telegram client is connected and ready.
Telegram usage example
// Send a message with inline buttons
const msgId = await sdk.telegram.sendMessage(chatId, 'Choose an option:', {
  buttons: [
    [{ text: 'Option A', data: 'opt_a' }],
    [{ text: 'Option B', data: 'opt_b' }]
  ]
});

// Download and re-send media
const buffer = await sdk.telegram.downloadMedia(chatId, mediaMessageId);
if (buffer) {
  await sdk.telegram.sendPhoto(chatId, buffer, { caption: 'Downloaded!' });
}

// Moderation
await sdk.telegram.muteUser(chatId, spammerId, Math.floor(Date.now() / 1000) + 3600);

5. sdk.db : Plugin Database

Type: better-sqlite3.Database | null

  • null if the plugin does not export a migrate() function.
  • Location: ~/.teleton/plugins/data/{plugin-name}.db
  • Each plugin gets its own isolated SQLite database.
  • The API is synchronous (better-sqlite3).

To enable sdk.db, your plugin must export a migrate function that creates the required tables:

Enabling sdk.db
export const migrate = (db: any) => {
  db.exec(`
    CREATE TABLE IF NOT EXISTS scores (
      user_id INTEGER PRIMARY KEY,
      points INTEGER NOT NULL DEFAULT 0,
      updated_at TEXT DEFAULT (datetime('now'))
    )
  `);
};

// Then in your tool:
export const tools = (sdk: PluginSDK): SimpleToolDef[] => [{
  name: 'get_score',
  description: 'Get user score',
  async execute(params, context) {
    const row = sdk.db!.prepare('SELECT points FROM scores WHERE user_id = ?')
      .get(context.senderId);
    return { success: true, data: row ?? { points: 0 } };
  }
}];

6. sdk.config : Application Config

Type: Record<string, unknown>

A sanitized view of the application configuration. Useful for checking the LLM provider or admin list.

Included Fields

KeyDescription
agent.providerLLM provider name (e.g. "openai", "anthropic")
agent.modelModel identifier (e.g. "gpt-4o")
agent.max_tokensMaximum token limit for responses
telegram.admin_idsArray of Telegram user IDs with admin privileges

Excluded Fields (never exposed)

api_key, api_hash, phone, session data, all API tokens. Use sdk.secrets for sensitive values.

7. sdk.pluginConfig : Plugin-Specific Config

Type: Record<string, unknown>

Config values specific to your plugin, sourced from config.yaml under the plugins.{plugin_name_with_underscores} key. The final config is produced by merging the plugin manifest's defaultConfig with the user's overrides (user values take precedence).

config.yaml
plugins:
  my_plugin:
    max_retries: 5
    api_endpoint: "https://api.example.com"
Reading plugin config
const maxRetries = (sdk.pluginConfig.max_retries as number) ?? 3;
const endpoint = sdk.pluginConfig.api_endpoint as string;

8. sdk.secrets : Secure API Keys

Type: SecretsSDK

Secure, multi-source secret resolution. Values are resolved in this order:

  1. Environment variable: {PLUGINNAME}_KEY
  2. Value set via /plugin set command
  3. sdk.pluginConfig[key]
  4. undefined
MethodReturnsDescription
get(key)string | undefinedGet a secret value. Returns undefined if not found.
require(key)stringGet a secret value or throw PluginSDKError if missing.
has(key)booleanCheck if a secret is available.
Secrets usage example
// Safe check
if (sdk.secrets.has('api_key')) {
  const key = sdk.secrets.get('api_key');
  // use key...
}

// Fail fast
const key = sdk.secrets.require('api_key'); // throws if missing

9. sdk.storage : Key-Value Storage

Type: StorageSDK | null

A simple key-value store backed by SQLite. Always available, the SDK auto-creates a _kv table. Supports optional TTL for automatic expiration.

MethodReturnsDescription
get<T>(key)T | undefinedGet a value by key. Returns undefined if not found or expired.
set<T>(key, value, opts?)voidSet a key-value pair. opts.ttl in milliseconds.
delete(key)booleanDelete a key. Returns true if the key existed.
has(key)booleanCheck if a key exists and is not expired.
clear()voidRemove all key-value pairs.
Storage usage example
// Cache a value for 5 minutes
sdk.storage!.set('api_response', data, { ttl: 5 * 60 * 1000 });

// Retrieve it
const cached = sdk.storage!.get<ApiResponse>('api_response');
if (cached) {
  return { success: true, data: cached };
}

// Check and delete
if (sdk.storage!.has('old_key')) {
  sdk.storage!.delete('old_key');
}

10. sdk.log : Prefixed Logger

Type: PluginLogger

All log output is automatically prefixed with [plugin:{name}]. Four log levels are available:

MethodDescription
info(message, ...args)General informational messages.
warn(message, ...args)Warnings that do not halt execution.
error(message, ...args)Errors and exceptions.
debug(message, ...args)Verbose debug output (usually hidden in production).
Logger output
sdk.log.info('Processing payment');
// Output: [plugin:my-plugin] Processing payment

sdk.log.error('Payment failed', { code: 'TIMEOUT', retries: 3 });
// Output: [plugin:my-plugin] Payment failed { code: 'TIMEOUT', retries: 3 }

11. PluginManifest

Every plugin must export a manifest object that describes the plugin's metadata, dependencies, and configuration schema.

PluginManifest interface
interface PluginManifest {
  name: string            // lowercase, alphanumeric + hyphens, 1-64 chars
  version: string         // semver (e.g. "1.0.0")
  author?: string         // plugin author name
  description?: string    // max 256 characters
  dependencies?: string[] // required modules: ["deals", "ton"]
  defaultConfig?: Record<string, unknown>  // default config values
  sdkVersion?: string     // required SDK version range (e.g. ">=1.0.0")
  secrets?: Record<string, SecretDeclaration>  // declared secrets
}
Manifest example
export const manifest: PluginManifest = {
  name: 'price-alerts',
  version: '2.1.0',
  author: 'team@example.com',
  description: 'TON price alerts with configurable thresholds',
  dependencies: ['ton'],
  sdkVersion: '>=1.0.0',
  defaultConfig: {
    check_interval: 60,
    threshold_pct: 5
  },
  secrets: {
    webhook_url: {
      required: false,
      description: 'Optional webhook for external notifications'
    }
  }
};

12. Tool Definition (SimpleToolDef)

Each tool returned by tools(sdk) must conform to the SimpleToolDef interface:

SimpleToolDef interface
interface SimpleToolDef {
  name: string                              // unique tool name
  description: string                       // what the tool does (shown to LLM)
  parameters?: Record<string, unknown>      // JSON Schema for input params
  execute: (params: any, context: PluginToolContext) => Promise<ToolResult>
  scope?: "always" | "dm-only" | "group-only" | "admin-only"
  category?: "data-bearing" | "action"
}

interface ToolResult {
  success: boolean
  data?: unknown
  error?: string
}

interface PluginToolContext {
  chatId: string
  senderId: number
  isGroup: boolean
  bridge: unknown       // raw TelegramBridge (prefer sdk.telegram)
  db: unknown           // raw database (prefer sdk.db)
  config?: unknown
}

Scope Values

ScopeDescription
"always"Available in all contexts (default).
"dm-only"Only available in direct messages.
"group-only"Only available in group chats.
"admin-only"Restricted to admin users (from telegram.admin_ids).

Category Values

CategoryDescription
"data-bearing"Tool returns data for the LLM to reason about.
"action"Tool performs an action with side effects.
Complete tool example
export const tools = (sdk: PluginSDK): SimpleToolDef[] => [{
  name: 'check_balance',
  description: 'Check TON balance for the bot wallet or a given address',
  scope: 'always',
  category: 'data-bearing',
  parameters: {
    type: 'object',
    properties: {
      address: {
        type: 'string',
        description: 'TON address to check (optional, defaults to bot wallet)'
      }
    }
  },
  async execute(params, context) {
    try {
      const balance = await sdk.ton.getBalance(params.address);
      return {
        success: true,
        data: { balance: balance.balance, nano: balance.nanoBalance }
      };
    } catch (err) {
      sdk.log.error('Balance check failed', err);
      return { success: false, error: String(err) };
    }
  }
}];

13. Lifecycle Hooks

Plugins can export optional lifecycle hooks to react to system events:

HookSignatureWhen Called
start(context) => Promise<void>After Telegram client connects. Use for initialization.
stop() => Promise<void>On graceful shutdown. Use for cleanup.
onMessage(event) => Promise<void>On every incoming message. Use to react to messages passively.
onCallbackQuery(event) => Promise<void>When a user clicks an inline button. Use for interactive flows.
Lifecycle hooks example
import type { PluginSDK } from '@teleton-agent/sdk';

export const start = async (ctx: { sdk: PluginSDK; log: PluginLogger }) => {
  ctx.log.info('Plugin started, initializing cache...');
  ctx.sdk.storage?.set('boot_time', Date.now());
};

export const stop = async () => {
  // Cleanup timers, connections, etc.
};

export const onMessage = async (event: {
  chatId: string;
  senderId: number;
  text: string;
  messageId: number;
}) => {
  // React to messages without being called as a tool
  if (event.text.includes('gm')) {
    await sdk.telegram.sendReaction(event.chatId, event.messageId, '👋');
  }
};

export const onCallbackQuery = async (event: {
  chatId: string;
  senderId: number;
  data: string;
  messageId: number;
}) => {
  // Handle inline button presses
  if (event.data === 'opt_a') {
    await sdk.telegram.editMessage(event.chatId, event.messageId, 'You chose A!');
  }
};