/**
* Pump Fun Agent Tracker Bot — Telegram Bot & Commands
*
* Grammy bot with commands:
* /start — Welcome message
* /status — Show tracker stats
* /wallet — Check developer wallet balance
* /claims — Show recent claims summary
* /buybacks — Show recent buybacks summary
* /help — Command reference
*
* Sends real-time notifications for:
* - Agent payments received
* - Creator fee claims
* - Token buybacks by developer wallet
*/
import { Bot } from 'grammy';
import type { Context } from 'grammy';
import type { AgentMonitor } from './monitor.js';
import type { BotConfig } from './types.js';
import {
formatBuybackNotification,
formatClaimNotification,
formatHelp,
formatPaymentNotification,
formatStatus,
formatWalletBalance,
} from './formatters.js';
export function createBot(config: BotConfig, monitor: AgentMonitor): Bot {
const bot = new Bot(config.telegramToken);
// ── /start ────────────────────────────────────────────────────────────
bot.command('start', async (ctx: Context) => {
await ctx.reply(
`Pump Fun Agent Tracker\n\n` +
`Monitoring agent token:\n${config.agentTokenMint}\n\n` +
`Developer wallet:\n${config.developerWallet}\n\n` +
`Use /help for commands.`,
{ parse_mode: 'HTML' },
);
});
// ── /status ───────────────────────────────────────────────────────────
bot.command('status', async (ctx: Context) => {
const msg = formatStatus(monitor.state, {
agentMint: config.agentTokenMint,
devWallet: config.developerWallet,
});
await ctx.reply(msg, { parse_mode: 'HTML' });
});
// ── /wallet ───────────────────────────────────────────────────────────
bot.command('wallet', async (ctx: Context) => {
try {
const snapshot = await monitor.getWalletBalance();
await ctx.reply(formatWalletBalance(snapshot), {
parse_mode: 'HTML',
link_preview_options: { is_disabled: true },
});
} catch (err) {
await ctx.reply('Failed to fetch wallet balance. Try again later.');
}
});
// ── /claims ───────────────────────────────────────────────────────────
bot.command('claims', async (ctx: Context) => {
const { totalClaims, totalClaimsSol, lastClaim } = monitor.state;
let msg = `Claims Summary\n\n`;
msg += `Total Claims: ${totalClaims}\n`;
msg += `Total SOL Claimed: ${totalClaimsSol.toFixed(4)} SOL\n`;
if (lastClaim) {
msg += `\nLast Claim:\n`;
msg += formatClaimNotification(lastClaim);
} else {
msg += `\nNo claims detected yet.`;
}
await ctx.reply(msg, {
parse_mode: 'HTML',
link_preview_options: { is_disabled: true },
});
});
// ── /buybacks ─────────────────────────────────────────────────────────
bot.command('buybacks', async (ctx: Context) => {
const { totalBuybacks, totalBuybackSol, lastBuyback } = monitor.state;
let msg = `Buybacks Summary\n\n`;
msg += `Total Buybacks: ${totalBuybacks}\n`;
msg += `Total SOL Spent: ${totalBuybackSol.toFixed(4)} SOL\n`;
if (lastBuyback) {
msg += `\nLast Buyback:\n`;
msg += formatBuybackNotification(lastBuyback);
} else {
msg += `\nNo buybacks detected yet.`;
}
await ctx.reply(msg, {
parse_mode: 'HTML',
link_preview_options: { is_disabled: true },
});
});
// ── /help ─────────────────────────────────────────────────────────────
bot.command('help', async (ctx: Context) => {
await ctx.reply(formatHelp(), { parse_mode: 'HTML' });
});
return bot;
}
/**
* Send a notification to all configured chat IDs.
* Used by the monitor callbacks to push real-time alerts.
*/
export async function broadcastMessage(
bot: Bot,
chatIds: number[],
message: string,
): Promise {
for (const chatId of chatIds) {
try {
await bot.api.sendMessage(chatId, message, {
parse_mode: 'HTML',
link_preview_options: { is_disabled: true },
});
} catch (err) {
console.error(`[Bot] Failed to send to chat ${chatId}:`, err);
}
}
}