You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用node-telegram-bot-api实现Telegram Bot的CA文本一键复制带通知

Telegram机器人实现Solana合约地址点击复制功能需求

我正在用Node.js的node-telegram-bot-api开发一款Telegram机器人,用来推送Solana区块链的交易通知。每条交易消息里包含合约地址(CA),我希望CA文本支持点击操作:用户点击后自动复制到剪贴板,同时Telegram顶部显示“已复制到剪贴板”的系统通知。

期望效果

我见过其他机器人实现了这个功能:消息中的CA(比如Ek4FUaAyinUT5whKyUMFv9qWpggud32P)可点击,点击后自动复制CA并弹出“CA已复制到剪贴板”的Telegram通知,我想在自己的机器人里复刻这个功能。

当前实现方案

目前我使用parse_mode: 'HTML',把CA用反引号包裹(比如Ek4FUaAyinUT5whKyUMFv9qWpggud32P)发送消息,还加了“(长按复制)”的提示。用户可以长按CA复制,触发Telegram的“已复制到剪贴板”通知,但我希望改成点击即可复制,不用长按。

以下是bot.js中构建和发送交易消息的相关代码:

const subscribeTrading = async (walletAddress, walletName, chatId) => {
    // ... (其他交易处理代码)

    let message = `<a href="https://solscan.io/account/${walletAddress}">${walletName}</a>\n\n`;
    if (['BUY', 'SELL'].includes(transactionType)) {
        if (Math.abs(solAmount) < 0.002) return;
        message += `${transactionType === 'BUY' ? '🟢' : '🔴'} ${transactionType === 'BUY' ? 'BOUGHT' : 'SOLD'} ${Math.abs(solAmount).toFixed(3)} SOL | ${formatTokenAmount(tokenAmount)} Tokens\n\n` +
                   `🚀 <b>Token:</b> ${tokenName} (${tokenSymbol})${pumpFunLink}\n\n` +
                   `📜 <b>CA:</b> \`${tokenAddress}\` (long-press to copy)`;
    } else if (['SENT_TOKEN', 'RECEIVED_TOKEN'].includes(transactionType)) {
        message += `📤 ${transactionType === 'SENT_TOKEN' ? 'SENT' : 'RECEIVED'} | ${formatTokenAmount(tokenAmount)} Tokens\n\n` +
                   `🚀 <b>Token:</b> ${tokenName} (${tokenSymbol})${pumpFunLink}\n\n` +
                   `📜 <b>CA:</b> \`${tokenAddress}\` (long-press to copy)`;
    } else {
        if (Math.abs(solTransferAmount) < 0.002) return;
        message += `📤 ${transactionType === 'SENT' ? 'SENT' : 'RECEIVED'} ${solTransferAmount.toFixed(3)} SOL`;
    }

    const timestamp = new Date().toLocaleTimeString('en-GB', { hour12: false });
    message += `\n\n🔗 <a href="https://solscan.io/tx/${signature}">Solscan</a> | ⏰ ${timestamp}`;

    const sub = subscriptions.get(walletAddress);
    if (sub) {
        for (const cid of sub.chatIds) {
            if (['BUY', 'SELL', 'SENT_TOKEN', 'RECEIVED_TOKEN'].includes(transactionType)) {
                try {
                    await sendTelegramMessage(cid, message, tokenName, tokenAddress);
                } catch (sendError) {
                    console.log(`Failed to send Telegram message to ${cid}:`, sendError.message);
                    await bot.sendMessage(cid, message, { parse_mode: 'HTML', disable_web_page_preview: true });
                }
            } else {
                await sendTelegramMessage(cid, message);
            }
        }
    }
};

const sendTelegramMessage = async (chatId, message, tokenName = null, tokenCA = null) => {
    try {
        const user = await new Promise((resolve, reject) => {
            db.get('SELECT settings FROM users WHERE chatId = ?', [chatId], (err, row) => {
                if (err) reject(err);
                resolve(row ? JSON.parse(row.settings) : { chartViewer: 'DEX Screener', buyBot: 'Trojan' });
            });
        });

        const options = { parse_mode: 'HTML', disable_web_page_preview: true };
        if (tokenName && tokenCA) {
            const chartLabel = user.chartViewer;
            const buyBotLabel = user.buyBot;
            const chartUrl = getChartUrl(tokenCA, user.chartViewer);
            const buttons = [];

            try {
                new URL(chartUrl);
                buttons.push({ text: `📊 ${chartLabel}`, url: chartUrl });
            } catch (urlError) {
                console.log(`Invalid chart URL for ${tokenCA}: ${chartUrl}`, urlError.message);
            }

            const botUsernames = {
                'Trojan': 'nestor_trojanbot',
                'GMGN': 'GMGN_sol03_bot',
                'MEVX': 'MevxTradingBot',
                'Bonk': 'bonkbot_bot',
            };

            if (buyBotLabel === 'BullX NEO') {
                const buyUrl = `https://neo.bullx.io/${encodeURIComponent(tokenCA)}`;
                try {
                    new URL(buyUrl);
                    buttons.push({ text: `💸 ${buyBotLabel}`, url: buyUrl });
                } catch (urlError) {
                    console.log(`Invalid buy URL for ${tokenCA}: ${buyUrl}`, urlError.message);
                }
            } else if (botUsernames[buyBotLabel]) {
                let deepLink = `https://t.me/${botUsernames[buyBotLabel]}?start=${encodeURIComponent(tokenCA)}`;
                try {
                    new URL(deepLink);
                    buttons.push({ text: `💸 ${buyBotLabel}`, url: deepLink });
                } catch (urlError) {
                    console.log(`Invalid deep link for ${buyBotLabel}: ${deepLink}`, urlError.message);
                    message += `\n\nIf the ${buyBotLabel} button fails, open @${botUsernames[buyBotLabel]} and send /start ${tokenCA}`;
                }
            }

            if (buttons.length > 0) {
                options.reply_markup = { inline_keyboard: [buttons] };
            }
        }

        await bot.sendMessage(chatId, message, options);
    } catch (error) {
        console.log(`Error sending message to ${chatId}:`, error.message);
    }
};

已尝试的方案

  • 使用Markdown格式:设置parse_mode: 'MARKDOWN',将CA用反引号包裹,示例代码:

    bot.sendMessage(chatId, "Hi. `Press me!`", { parse_mode: 'MARKDOWN' });
    

    这种方式让CA显示为等宽字体,但还是需要长按复制,无法实现点击复制。

  • 使用Inline键盘按钮:添加带callback_data的按钮,在回调中返回提示,示例代码:

    options.reply_markup = {
        inline_keyboard: [[{ text: `📜 CA: ${tokenCA}`, callback_data: `copy_ca_${tokenCA}` }]]
    };
    // 回调处理
    bot.on('callback_query', async (callbackQuery) => {
        const chatId = callbackQuery.message.chat.id.toString();
        const data = callbackQuery.data;
    
        if (data.startsWith('copy_ca_')) {
            const tokenCA = data.replace('copy_ca_', '');
            await bot.answerCallbackQuery(callbackQuery.id, { text: `CA copied: ${tokenCA}` });
        }
    });
    

    这种方式在消息下方添加了按钮,点击后弹出包含CA的通知,但用户还是需要长按通知里的CA才能复制,没法实现消息内CA直接点击复制。

问题

如何让交易消息中的CA文本支持单次点击,点击后自动复制到剪贴板并显示Telegram的“已复制到剪贴板”系统通知?通过node-telegram-bot-api能否实现?如果Telegram Bot API不允许程序化访问剪贴板,有没有其他方式实现类似的用户体验?

额外信息

  • node-telegram-bot-api版本:0.66.0
  • Node.js版本:20.19.0
  • 机器人运行环境:Raspberry Pi + systemd服务

内容的提问来源于stack exchange,提问作者Martin Wæhre

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 09:07:34