import TelegramBot from 'node-telegram-bot-api';
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
interface UserSession {
mcpClient: Client;
transport: any;
availableTools: any[];
}
// Execute MCP tool with parameters
export async function executeMCPTool(
session: UserSession,
toolName: string,
parameters: Record<string, any> = {}
): Promise<any> {
try {
const result = await session.mcpClient.callTool({
name: toolName,
arguments: parameters,
});
return result;
} catch (error) {
throw new Error(`Failed to execute ${toolName}: ${error}`);
}
}
// Wallet command handlers
export function setupWalletHandlers(bot: TelegramBot, userSessions: Map<number, UserSession>) {
// /wallet create
bot.onText(/\/wallet create/, async (msg) => {
const chatId = msg.chat.id;
const userId = msg.from?.id || 0;
try {
const session = userSessions.get(userId);
if (!session) {
bot.sendMessage(chatId, 'β Please start the bot first with /start');
return;
}
const result = await executeMCPTool(session, 'create_wallet');
bot.sendMessage(chatId, `β
*Wallet Created Successfully!*\n\nπ *Address:* \`${result.content[0]?.text || 'N/A'}\`\n\nβ οΈ *Important:* Save your private key securely!`, {
parse_mode: 'Markdown'
});
} catch (error) {
bot.sendMessage(chatId, `β Error creating wallet: ${error}`);
}
});
// /wallet address
bot.onText(/\/wallet address/, async (msg) => {
const chatId = msg.chat.id;
const userId = msg.from?.id || 0;
try {
const session = userSessions.get(userId);
if (!session) {
bot.sendMessage(chatId, 'β Please start the bot first with /start');
return;
}
try {
const result = await executeMCPTool(session, 'get_current_wallet');
const walletText = result.content[0]?.text || '';
// Extract address from response
const addressMatch = walletText.match(/Address:\s*(0x[a-fA-F0-9]{40})/i);
if (addressMatch) {
bot.sendMessage(chatId, `π¦ *Your Wallet Address:*\n\n\`${addressMatch[1]}\``, {
parse_mode: 'Markdown'
});
} else {
bot.sendMessage(chatId, 'β No wallet found. Please create or import a wallet first using `/wallet create` or `/wallet import <mnemonic>`', {
parse_mode: 'Markdown'
});
}
} catch (error) {
bot.sendMessage(chatId, 'β No wallet found. Please create or import a wallet first using `/wallet create` or `/wallet import <mnemonic>`', {
parse_mode: 'Markdown'
});
}
} catch (error) {
bot.sendMessage(chatId, `β Error getting wallet address: ${error}`);
}
});
// /wallet import <mnemonic or private key>
bot.onText(/\/wallet import (.+)/, async (msg, match) => {
const chatId = msg.chat.id;
const userId = msg.from?.id || 0;
const input = match?.[1]?.trim();
if (!input) {
bot.sendMessage(chatId, 'β Please provide a mnemonic phrase or private key:\n\n`/wallet import your mnemonic phrase here`\nor\n`/wallet import 0xYourPrivateKey`', {
parse_mode: 'Markdown'
});
return;
}
try {
const session = userSessions.get(userId);
if (!session) {
bot.sendMessage(chatId, 'β Please start the bot first with /start');
return;
}
// Detect if it's a private key (starts with 0x and is 66 chars) or mnemonic
const isPrivateKey = input.startsWith('0x') && input.length === 66;
const params = isPrivateKey
? { privateKey: input }
: { mnemonic: input };
const result = await executeMCPTool(session, 'import_wallet', params);
bot.sendMessage(chatId, `β
*Wallet Imported Successfully!*\n\n${result.content[0]?.text || 'Wallet imported'}`, {
parse_mode: 'Markdown'
});
} catch (error) {
bot.sendMessage(chatId, `β Error importing wallet: ${error}`);
}
});
// /wallet list - List all wallets
bot.onText(/\/wallet list$/, async (msg) => {
const chatId = msg.chat.id;
const userId = msg.from?.id || 0;
try {
const session = userSessions.get(userId);
if (!session) {
bot.sendMessage(chatId, 'β Please start the bot first with /start');
return;
}
const result = await executeMCPTool(session, 'list_wallets');
bot.sendMessage(chatId, `π *Your Wallets:*\n\n${result.content[0]?.text || 'No wallets found'}`, {
parse_mode: 'Markdown'
});
} catch (error) {
bot.sendMessage(chatId, `β Error listing wallets: ${error}`);
}
});
// /wallet set <address> - Set current wallet
bot.onText(/\/wallet set (.+)/, async (msg, match) => {
const chatId = msg.chat.id;
const userId = msg.from?.id || 0;
const address = match?.[1]?.trim();
if (!address) {
bot.sendMessage(chatId, 'β Please provide a wallet address:\n\n`/wallet set 0xYourAddress`', {
parse_mode: 'Markdown'
});
return;
}
try {
const session = userSessions.get(userId);
if (!session) {
bot.sendMessage(chatId, 'β Please start the bot first with /start');
return;
}
const result = await executeMCPTool(session, 'set_current_wallet', { address });
bot.sendMessage(chatId, `β
*Current Wallet Set!*\n\n${result.content[0]?.text || 'Wallet set successfully'}`, {
parse_mode: 'Markdown'
});
} catch (error) {
bot.sendMessage(chatId, `β Error setting wallet: ${error}`);
}
});
// /wallet current - Get current wallet
bot.onText(/\/wallet current$/, async (msg) => {
const chatId = msg.chat.id;
const userId = msg.from?.id || 0;
try {
const session = userSessions.get(userId);
if (!session) {
bot.sendMessage(chatId, 'β Please start the bot first with /start');
return;
}
const result = await executeMCPTool(session, 'get_current_wallet');
bot.sendMessage(chatId, `π *Current Wallet:*\n\n${result.content[0]?.text || 'No current wallet'}`, {
parse_mode: 'Markdown'
});
} catch (error) {
bot.sendMessage(chatId, `β Error getting current wallet: ${error}`);
}
});
}
// Balance command handlers
export function setupBalanceHandlers(bot: TelegramBot, userSessions: Map<number, UserSession>) {
// /balance
bot.onText(/\/balance$/, async (msg) => {
const chatId = msg.chat.id;
const userId = msg.from?.id || 0;
try {
const session = userSessions.get(userId);
if (!session) {
bot.sendMessage(chatId, 'β Please start the bot first with /start');
return;
}
// Try to list wallets first
try {
const walletsResult = await executeMCPTool(session, 'list_wallets');
const walletsText = walletsResult.content[0]?.text || '';
// Extract first wallet address
const addressMatch = walletsText.match(/(?:β\s+|\s{2})(0x[a-fA-F0-9]+)/);
if (!addressMatch) {
bot.sendMessage(chatId, 'β No wallet found. Please create or import a wallet first using `/wallet create` or `/wallet import <mnemonic>`', {
parse_mode: 'Markdown'
});
return;
}
const walletAddress = addressMatch[1];
// Get balance for the wallet address
const result = await executeMCPTool(session, 'get_balance', { address: walletAddress });
bot.sendMessage(chatId, `π° *Your Balance:*\n\n${result.content[0]?.text || 'Unable to fetch balance'}`, {
parse_mode: 'Markdown'
});
} catch (error) {
bot.sendMessage(chatId, 'β No wallet found. Please create or import a wallet first using `/wallet create` or `/wallet import <mnemonic>`', {
parse_mode: 'Markdown'
});
}
} catch (error) {
bot.sendMessage(chatId, `β Error getting balance: ${error}`);
}
});
// /balance <address>
bot.onText(/\/balance (.+)/, async (msg, match) => {
const chatId = msg.chat.id;
const userId = msg.from?.id || 0;
const address = match?.[1];
if (!address) {
bot.sendMessage(chatId, 'β Please provide an address: `/balance 0x...`', {
parse_mode: 'Markdown'
});
return;
}
try {
const session = userSessions.get(userId);
if (!session) {
bot.sendMessage(chatId, 'β Please start the bot first with /start');
return;
}
const result = await executeMCPTool(session, 'get_balance', { address });
bot.sendMessage(chatId, `π° *Balance for ${address}:*\n\n${result.content[0]?.text || 'Unable to fetch balance'}`, {
parse_mode: 'Markdown'
});
} catch (error) {
bot.sendMessage(chatId, `β Error getting balance: ${error}`);
}
});
}
// Network command handlers
export function setupNetworkHandlers(bot: TelegramBot, userSessions: Map<number, UserSession>) {
// /gas - Get current gas prices
bot.onText(/\/gas$/, async (msg) => {
const chatId = msg.chat.id;
const userId = msg.from?.id || 0;
try {
const session = userSessions.get(userId);
if (!session) {
bot.sendMessage(chatId, 'β Please start the bot first with /start');
return;
}
const result = await executeMCPTool(session, 'get_network_info');
const networkText = result.content[0]?.text || '';
// Extract gas price from network info
const gasPriceMatch = networkText.match(/Gas Price:\s*([^\n]+)/i);
const blockMatch = networkText.match(/Latest Block:\s*([^\n]+)/i);
const chainMatch = networkText.match(/Chain ID:\s*([^\n]+)/i);
let response = 'β½ *Current Gas Information:*\n\n';
if (gasPriceMatch) {
response += `π¨ **Gas Price:** ${gasPriceMatch[1]}\n`;
}
if (blockMatch) {
response += `π **Latest Block:** ${blockMatch[1]}\n`;
}
if (chainMatch) {
response += `π **Chain ID:** ${chainMatch[1]}\n`;
}
response += '\nπ‘ *Tip: Lower gas prices mean cheaper transactions!*';
bot.sendMessage(chatId, response, {
parse_mode: 'Markdown'
});
} catch (error) {
bot.sendMessage(chatId, `β Error getting gas information: ${error}`);
}
});
// /network - Get full network information
bot.onText(/\/network$/, async (msg) => {
const chatId = msg.chat.id;
const userId = msg.from?.id || 0;
try {
const session = userSessions.get(userId);
if (!session) {
bot.sendMessage(chatId, 'β Please start the bot first with /start');
return;
}
const result = await executeMCPTool(session, 'get_network_info');
bot.sendMessage(chatId, `π *Network Information:*\n\n${result.content[0]?.text || 'Unable to fetch network info'}`, {
parse_mode: 'Markdown'
});
} catch (error) {
bot.sendMessage(chatId, `β Error getting network information: ${error}`);
}
});
}
// Transaction query handlers
export function setupTransactionQueryHandlers(bot: TelegramBot, userSessions: Map<number, UserSession>) {
// /tx <hash> - Get transaction details
bot.onText(/\/tx (.+)/, async (msg, match) => {
const chatId = msg.chat.id;
const userId = msg.from?.id || 0;
const txHash = match?.[1]?.trim();
if (!txHash) {
bot.sendMessage(chatId, 'β Please provide a transaction hash:\n\n`/tx 0xYourTransactionHash`', {
parse_mode: 'Markdown'
});
return;
}
try {
const session = userSessions.get(userId);
if (!session) {
bot.sendMessage(chatId, 'β Please start the bot first with /start');
return;
}
const result = await executeMCPTool(session, 'get_transaction', { hash: txHash });
bot.sendMessage(chatId, `π *Transaction Details:*\n\n${result.content[0]?.text || 'Transaction not found'}`, {
parse_mode: 'Markdown'
});
} catch (error) {
bot.sendMessage(chatId, `β Error getting transaction: ${error}`);
}
});
// /block <number or hash> - Get block information
bot.onText(/\/block (.+)/, async (msg, match) => {
const chatId = msg.chat.id;
const userId = msg.from?.id || 0;
const blockId = match?.[1]?.trim();
if (!blockId) {
bot.sendMessage(chatId, 'β Please provide a block number or hash:\n\n`/block 12345` or `/block 0xBlockHash`', {
parse_mode: 'Markdown'
});
return;
}
try {
const session = userSessions.get(userId);
if (!session) {
bot.sendMessage(chatId, 'β Please start the bot first with /start');
return;
}
// Check if it's a number or hash
const params = blockId.startsWith('0x')
? { hash: blockId }
: { number: parseInt(blockId) };
const result = await executeMCPTool(session, 'get_block', params);
bot.sendMessage(chatId, `π§± *Block Information:*\n\n${result.content[0]?.text || 'Block not found'}`, {
parse_mode: 'Markdown'
});
} catch (error) {
bot.sendMessage(chatId, `β Error getting block: ${error}`);
}
});
// /estimate <to> <amount> - Estimate gas for transaction
bot.onText(/\/estimate (.+) (.+)/, async (msg, match) => {
const chatId = msg.chat.id;
const userId = msg.from?.id || 0;
const to = match?.[1]?.trim();
const amount = match?.[2]?.trim();
if (!to || !amount) {
bot.sendMessage(chatId, 'β Please provide recipient and amount:\n\n`/estimate 0xRecipient 1.5`', {
parse_mode: 'Markdown'
});
return;
}
try {
const session = userSessions.get(userId);
if (!session) {
bot.sendMessage(chatId, 'β Please start the bot first with /start');
return;
}
const result = await executeMCPTool(session, 'estimate_gas', { to, amount });
bot.sendMessage(chatId, `β½ *Gas Estimation:*\n\n${result.content[0]?.text || 'Unable to estimate gas'}`, {
parse_mode: 'Markdown'
});
} catch (error) {
bot.sendMessage(chatId, `β Error estimating gas: ${error}`);
}
});
}