You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Plugin SDK for Teleton Agent — TypeScript types and utilities for building plugins that interact with Telegram and the TON blockchain.
Install
npm install @teleton-agent/sdk
The package ships type definitions and the PluginSDKError class. It has an optional peer dependency on better-sqlite3 (used only if your plugin needs a database).
Quick Start
A Teleton plugin is a module that exports a tools function and, optionally, manifest, start, and migrate.
importtype{PluginSDK,SimpleToolDef,PluginManifest}from"@teleton-agent/sdk";exportconstmanifest: PluginManifest={name: "greeting",version: "1.0.0",description: "Sends a greeting with the bot's TON balance",};exportconsttools=(sdk: PluginSDK): SimpleToolDef[]=>[{name: "greeting_hello",description: "Greet the user and show the bot wallet balance",parameters: {type: "object",properties: {name: {type: "string",description: "User's name"},},required: ["name"],},asyncexecute(params,context){constbalance=awaitsdk.ton.getBalance();consttext=`Hello ${params.name}! Bot balance: ${balance?.balance??"unknown"} TON`;awaitsdk.telegram.sendMessage(String(context.chatId),text);return{success: true,data: {greeting: text}};},},];
Place the compiled plugin at ~/.teleton/plugins/<name>/index.js and register it in config.yaml:
plugins:
greeting:
enabled: true
Plugin Lifecycle
The core platform loads plugins in a defined order. Each export is optional except tools.
The tools export can be either a static array or a factory function receiving the SDK. The start function receives a context object with db, config, pluginConfig, and log. The SDK object passed to tools is frozen -- plugins cannot modify or extend it. Each plugin receives its own isolated database (if migrate is exported) and a sanitized config object with no API keys.
Event Hooks
Plugins can export onMessage and onCallbackQuery to react to Telegram events directly, without going through the LLM agentic loop. These hooks are fire-and-forget — errors are caught per plugin and logged, so a failing hook never blocks message processing or other plugins.
onMessage
Called for every incoming message (DMs and groups), after the message is stored to the feed database. This fires regardless of whether the agent will respond to the message.
Called when a user presses an inline keyboard button. The data string is split on : into action (first segment) and params (remaining segments). You must call event.answer() to dismiss the loading spinner on the user's client.
importtype{PluginCallbackEvent}from"@teleton-agent/sdk";exportasyncfunctiononCallbackQuery(event: PluginCallbackEvent){// Button data format: "myplugin:action:param1:param2"if(event.action!=="myplugin")return;// Not for this pluginconst[subAction, ...args]=event.params;if(subAction==="confirm"){awaitevent.answer("Confirmed!",false);// Toast notification// ... handle the confirmation}else{awaitevent.answer("Unknown action",true);// Alert popup}}
Tip: Namespace your callback data with your plugin name (e.g. "casino:bet:100") so multiple plugins can coexist without action collisions. All registered onCallbackQuery hooks receive every callback event — filter by event.action to handle only your own buttons.
API Reference
Core
PluginSDK
Root SDK object passed to plugin functions.
Property
Type
Description
version
string
SDK version (semver)
ton
TonSDK
TON blockchain operations
telegram
TelegramSDK
Telegram messaging and user operations
secrets
SecretsSDK
Secure access to plugin secrets (API keys, tokens)
storage
StorageSDK | null
Simple key-value storage (null if no DB)
db
Database | null
Isolated SQLite database (null if no migrate exported)
config
Record<string, unknown>
Sanitized app config (no secrets)
pluginConfig
Record<string, unknown>
Plugin-specific config from config.yaml
log
PluginLogger
Prefixed logger
bot
BotSDK | null
Bot inline mode SDK (null if not configured — see Bot SDK)
PluginLogger
All methods auto-prefix output with the plugin name.
Method
Description
info(...args)
Informational message
warn(...args)
Warning
error(...args)
Error
debug(...args)
Debug (visible only when DEBUG or VERBOSE is set)
PluginSDKError
import{PluginSDKError}from"@teleton-agent/sdk";
Extends Error with a code property for programmatic handling.
Property
Type
Description
name
"PluginSDKError"
Always "PluginSDKError"
code
SDKErrorCode
Machine-readable error code
message
string
Human-readable description
SDKErrorCode
typeSDKErrorCode=|"BRIDGE_NOT_CONNECTED"// Telegram bridge not ready|"WALLET_NOT_INITIALIZED"// TON wallet not configured|"INVALID_ADDRESS"// Malformed TON address|"SECRET_NOT_FOUND"// Required secret not configured|"OPERATION_FAILED";// Generic failure
Dual DEX aggregator supporting STON.fi and DeDust. Compares quotes in parallel and recommends the best execution.
DexSDK
Method
Returns
Description
quote(params)
Promise<DexQuoteResult>
Compare quotes from both DEXes
quoteSTONfi(params)
Promise<DexSingleQuote | null>
Quote from STON.fi only
quoteDeDust(params)
Promise<DexSingleQuote | null>
Quote from DeDust only
swap(params)
Promise<DexSwapResult>
Swap via best DEX (or forced)
swapSTONfi(params)
Promise<DexSwapResult>
Swap on STON.fi
swapDeDust(params)
Promise<DexSwapResult>
Swap on DeDust
// Get best quote for swapping 10 TON → USDTconstusdt="EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs";constquote=awaitsdk.ton.dex.quote({fromAsset: "ton",toAsset: usdt,amount: 10,slippage: 0.01,// 1%});console.log(`Best: ${quote.recommended} → ${quote.stonfi?.expectedOutput??"N/A"} USDT`);// Execute the swapconstresult=awaitsdk.ton.dex.swap({fromAsset: "ton",toAsset: usdt,amount: 10});
DexQuoteParams
Field
Type
Description
fromAsset
string
"ton" or jetton master address
toAsset
string
"ton" or jetton master address
amount
number
Amount in human-readable units
slippage
number?
Tolerance (0.01 = 1%, default: 0.01)
DexQuoteResult
Field
Type
Description
stonfi
DexSingleQuote | null
STON.fi quote
dedust
DexSingleQuote | null
DeDust quote
recommended
"stonfi" | "dedust"
Best DEX for this trade
savings
string
Savings vs the other DEX
DexSingleQuote
Field
Type
Description
dex
"stonfi" | "dedust"
DEX name
expectedOutput
string
Expected output amount
minOutput
string
Minimum after slippage
rate
string
Exchange rate
priceImpact
string?
Price impact percentage
fee
string
Fee amount
poolType
string?
Pool type (DeDust: "volatile" or "stable")
DexSwapParams
Extends DexQuoteParams with:
Field
Type
Description
dex
"stonfi" | "dedust"?
Force a specific DEX (omit for auto)
DexSwapResult
Field
Type
Description
dex
"stonfi" | "dedust"
DEX used
fromAsset
string
Source asset
toAsset
string
Destination asset
amountIn
string
Amount sent
expectedOutput
string
Expected output
minOutput
string
Minimum after slippage
slippage
string
Slippage used
DNS — sdk.ton.dns
Manage .ton domains: check availability, resolve addresses, participate in auctions, and link domains to wallets.
DnsSDK
Method
Returns
Description
check(domain)
Promise<DnsCheckResult>
Check availability, owner, auction status
resolve(domain)
Promise<DnsResolveResult | null>
Resolve domain to wallet address
getAuctions(limit?)
Promise<DnsAuction[]>
List active auctions
startAuction(domain)
Promise<DnsAuctionResult>
Start auction for an available domain
bid(domain, amount)
Promise<DnsBidResult>
Place bid on active auction
link(domain, address)
Promise<void>
Link domain to wallet address
unlink(domain)
Promise<void>
Remove wallet link
setSiteRecord(domain, adnlAddress)
Promise<void>
Set TON Site (ADNL) record on a domain
// Check if a domain is availableconstinfo=awaitsdk.ton.dns.check("mybot.ton");if(info.available){constresult=awaitsdk.ton.dns.startAuction("mybot.ton");sdk.log.info(`Auction started for ${result.domain}`);}else{sdk.log.info(`Domain owned by ${info.owner}`);}// Resolve a domainconstresolved=awaitsdk.ton.dns.resolve("alice.ton");if(resolved){awaitsdk.ton.sendTON(resolved.walletAddress!,1,"Hello from plugin");}// Set a TON Site ADNL recordawaitsdk.ton.dns.setSiteRecord("mysite.ton","aabbccdd...64hex");
DnsCheckResult
Field
Type
Description
domain
string
Domain name (e.g. "example.ton")
available
boolean
Whether the domain is available
owner
string?
Current owner address
nftAddress
string?
NFT address of the domain
walletAddress
string?
Linked wallet address
auction
object?
Active auction: { bids, lastBid, endTime } (reserved, not yet populated)
// Gift marketplace flow: browse → check value → make offerconstgifts=awaitsdk.telegram.getMyGifts(10);for(constgiftofgifts){sdk.log.info(`Gift ${gift.id} from user ${gift.fromId}, worth ${gift.starsAmount} stars`);}// Check NFT gift valueconstvalue=awaitsdk.telegram.getUniqueGiftValue("CryptoBot-42");if(value?.floorPrice){sdk.log.info(`Floor: ${value.floorPrice}${value.currency}`);}// Transfer a collectibleconstresult=awaitsdk.telegram.transferCollectible(gift.messageId!,targetUserId);sdk.log.info(`Transferred to ${result.transferredTo}, paid: ${result.paidTransfer}`);
Stories & Advanced
Method
Returns
Description
sendStory(mediaPath, opts?)
Promise<number | null>
Post a story
setTyping(chatId)
Promise<void>
Show typing indicator
InlineButton
Field
Type
Description
text
string
Button label text
callback_data
string
Callback data sent when pressed
SendMessageOptions
Field
Type
Description
replyToId
number?
Message ID to reply to
inlineKeyboard
InlineButton[][]?
Inline keyboard rows
EditMessageOptions
Field
Type
Description
inlineKeyboard
InlineButton[][]?
Updated keyboard (omit to keep)
DiceResult
Field
Type
Description
value
number
Result value (range depends on emoticon)
messageId
number
Message ID of the dice
TelegramUser
Field
Type
Description
id
number
Telegram user ID
username
string?
Username (without @)
firstName
string?
First name
isBot
boolean
Whether the user is a bot
SimpleMessage
Field
Type
Description
id
number
Message ID
text
string
Message text
senderId
number
Sender user ID
senderUsername
string?
Sender username
timestamp
Date
Message timestamp
ChatInfo
Field
Type
Description
id
string
Chat ID
title
string
Chat title or user's first name
type
"private" | "group" | "supergroup" | "channel"
Chat type
membersCount
number?
Number of members
username
string?
Chat username (if public)
description
string?
Chat/channel description
UserInfo
Field
Type
Description
id
number
Telegram user ID
firstName
string
First name
lastName
string?
Last name
username
string?
Username without @
isBot
boolean
Whether the user is a bot
ResolvedPeer
Field
Type
Description
id
number
Entity ID
type
"user" | "chat" | "channel"
Entity type
username
string?
Username if available
title
string?
Title or first name
MediaSendOptions
Field
Type
Description
caption
string?
Media caption text
replyToId
number?
Message ID to reply to
inlineKeyboard
InlineButton[][]?
Inline keyboard
duration
number?
Duration in seconds (video/voice)
width
number?
Width in pixels (video)
height
number?
Height in pixels (video)
PollOptions
Field
Type
Description
isAnonymous
boolean?
Anonymous voters (default: true)
multipleChoice
boolean?
Allow multiple answers (default: false)
StarGift
Field
Type
Description
id
string
Gift ID
starsAmount
number
Cost in Telegram Stars
availableAmount
number?
Remaining available
totalAmount
number?
Total supply
ReceivedGift
Field
Type
Description
id
string
Gift ID
fromId
number?
Sender user ID
date
number
Unix timestamp
starsAmount
number
Stars value
saved
boolean
Whether saved to profile
messageId
number?
Associated message ID
Dialog
Field
Type
Description
id
string | null
Chat ID
title
string
Chat title or name
type
"dm" | "group" | "channel"
Chat type
unreadCount
number
Unread messages
unreadMentionsCount
number
Unread mentions
isPinned
boolean
Whether pinned
isArchived
boolean
Whether archived
lastMessageDate
number | null
Last message (unix timestamp)
lastMessage
string | null
Last message preview
StarsTransaction
Field
Type
Description
id
string
Transaction ID
amount
number
Amount (+received, -spent)
date
number
Unix timestamp
peer
string?
Peer info
description
string?
Description
TransferResult
Field
Type
Description
msgId
number
Message ID of transferred gift
transferredTo
string
Recipient identifier
paidTransfer
boolean
Whether it cost Stars
starsSpent
string?
Stars spent (if paid)
CollectibleInfo
Field
Type
Description
type
"username" | "phone"
Collectible type
value
string
Username or phone number
purchaseDate
string
ISO 8601 date
currency
string
Fiat currency
amount
string?
Fiat amount
cryptoCurrency
string?
Crypto currency (e.g. "TON")
cryptoAmount
string?
Crypto amount
url
string?
Fragment URL
UniqueGift
Field
Type
Description
id
string
Gift ID
giftId
string
Collection gift ID
slug
string
URL slug
title
string
Gift title
num
number
Number in collection
owner
object
{ id?, name?, address?, username? }
giftAddress
string?
TON address of the NFT
attributes
Array
[{ type, name, rarityPercent? }]
availability
object?
{ total, remaining }
nftLink
string
Link to NFT page
GiftValue
Field
Type
Description
slug
string
NFT slug
initialSaleDate
string?
First sale (ISO 8601)
initialSaleStars
string?
First sale price in Stars
lastSaleDate
string?
Last sale (ISO 8601)
lastSalePrice
string?
Last sale price
floorPrice
string?
Floor price
averagePrice
string?
Average price
listedCount
number?
Number listed
currency
string?
Currency
GiftOfferOptions
Field
Type
Description
duration
number?
Offer validity in seconds (default: 86400, min: 21600)
Secrets
SecretsSDK
Secure access to plugin secrets (API keys, tokens, credentials). Resolution order: environment variable > secrets store (/plugin set) > pluginConfig.
Method
Returns
Description
get(key)
string | undefined
Get secret value
require(key)
string
Get secret, throws SECRET_NOT_FOUND if missing
has(key)
boolean
Check if a secret is configured
constapiKey=sdk.secrets.get("api_key");if(!apiKey)return{success: false,error: "API key not configured"};
SecretDeclaration
Used in PluginManifest.secrets to declare required secrets.
Field
Type
Description
required
boolean
Whether the plugin needs this secret to function
description
string
Human-readable description
env
string?
Environment variable name override
Storage
StorageSDK
Simple key-value storage for plugins. Uses an auto-created _kv table in the plugin's isolated DB. No migrate() export needed. Values are JSON-serialized with optional TTL.
Method
Returns
Description
get<T>(key)
T | undefined
Get value (undefined if missing or expired)
set<T>(key, value, opts?)
void
Set value (optional { ttl: ms } for expiration)
delete(key)
boolean
Delete a key (true if existed)
has(key)
boolean
Check if key exists and is not expired
clear()
void
Delete all keys
// Simple counterconstcount=sdk.storage.get<number>("visits")??0;sdk.storage.set("visits",count+1);// Cache with 5-minute TTLsdk.storage.set("api_result",data,{ttl: 300_000});
Bot SDK (sdk.bot)
The Bot SDK enables plugins to handle Telegram inline queries and button callbacks. It is lazy-loaded — sdk.bot is null unless the plugin declares bot capabilities in its manifest.
To enable the Bot SDK, add a bot field to your manifest:
Bot's username (getter, empty string if unavailable)
onInlineQuery(handler)
void
Register handler for inline queries
onCallback(pattern, handler)
void
Register handler for button callbacks (glob pattern)
onChosenResult(handler)
void
Register handler for chosen inline results
editInlineMessage(inlineMessageId, text, opts?)
Promise<void>
Edit an inline message
keyboard(rows)
BotKeyboard
Build a keyboard with auto-prefixed callback data
onInlineQuery(handler)
Register a handler for inline queries. The handler receives the query text (with plugin prefix already stripped) and must return an array of InlineResult objects.
Build an inline keyboard with auto-prefixed callback data. Returns a BotKeyboard object with dual output formats.
Parameter
Type
Required
Description
rows
ButtonDef[][]
Yes
Array of button rows
constkb=sdk.bot.keyboard([[{text: "Buy",callback: "buy:123",style: "success"},{text: "Sell",callback: "sell:123",style: "danger"},],[{text: "Details",url: "https://example.com"}],]);// Use with GramJS (styled colors)consttlMarkup=kb.toTL();// Use with Grammy Bot API (no colors, but wider compatibility)constgrammyKb=kb.toGrammy();
BotManifest
Field
Type
Description
inline
boolean?
Enable inline query handling
callbacks
boolean?
Enable callback query handling
rateLimits
object?
{ inlinePerMinute?: number, callbackPerMinute?: number }
BotKeyboard
Field / Method
Type
Description
rows
ButtonDef[][]
Raw button definitions (with prefixed callbacks)
toGrammy()
unknown
Grammy InlineKeyboard (Bot API, no colors)
toTL()
unknown
GramJS TL ReplyInlineMarkup (MTProto, with colors)