import axios from 'axios'; import { logger } from './index.js'; /** * Discord Webhook Service * Sends notifications for copy and close operations */ export class DiscordWebhook { constructor(webhookUrl) { this.webhookUrl = webhookUrl; } /** * Send notification to Discord */ async sendNotification(embed) { if (!this.webhookUrl) { logger.debug('Discord webhook URL not configured, skipping notification'); return; } try { const response = await axios.post( this.webhookUrl, { embeds: [embed], username: 'Byreal Sniper Bot', avatar_url: 'https://i.imgur.com/4M34hi2.png', // Optional bot avatar }, { timeout: 10000, headers: { 'Content-Type': 'application/json', }, } ); if (response.status === 204) { logger.success('Discord notification sent successfully'); } } catch (error) { logger.error('Failed to send Discord notification:', error.message); if (error.response) { logger.error('Discord API response:', error.response.data); } } } /** * Send copy success notification */ async notifyCopySuccess({ parentPosition, poolAddress, poolName, tokenA, tokenB, copyUsdValue, txSignature, }) { const embed = { title: '✅ Position Copied Successfully', color: 0x00ff00, // Green timestamp: new Date().toISOString(), fields: [ { name: '📋 Parent Position', value: `[${parentPosition.slice(0, 8)}...${parentPosition.slice(-8)}](https://solscan.io/account/${parentPosition})`, inline: true, }, { name: '🏊 Pool', value: `${poolName}\n[${poolAddress.slice(0, 8)}...](https://solscan.io/account/${poolAddress})`, inline: true, }, { name: '💰 Copy Amount', value: `$${copyUsdValue.toFixed(2)}`, inline: true, }, { name: '💱 Token A', value: `${tokenA.amount} ${tokenA.symbol || ''}\n($${tokenA.valueUsd || 0})`, inline: true, }, { name: '💱 Token B', value: `${tokenB.amount} ${tokenB.symbol || ''}\n($${tokenB.valueUsd || 0})`, inline: true, }, ], footer: { text: 'Byreal Sniper Bot', }, }; if (txSignature) { embed.fields.push({ name: '🔗 Transaction', value: `[View on Solscan](https://solscan.io/tx/${txSignature})`, inline: false, }); } await this.sendNotification(embed); } /** * Send close success notification */ async notifyCloseSuccess({ positionAddress, poolName, closedAt, reason, txSignature, }) { const embed = { title: '🔴 Position Closed', color: 0xff0000, // Red timestamp: new Date().toISOString(), fields: [ { name: '📋 Position', value: `[${positionAddress.slice(0, 8)}...${positionAddress.slice(-8)}](https://solscan.io/account/${positionAddress})`, inline: true, }, { name: '🏊 Pool', value: poolName || 'Unknown', inline: true, }, { name: '📝 Reason', value: reason || 'Manual close', inline: false, }, ], footer: { text: 'Byreal Sniper Bot', }, }; if (txSignature) { embed.fields.push({ name: '🔗 Transaction', value: `[View on Solscan](https://solscan.io/tx/${txSignature})`, inline: false, }); } await this.sendNotification(embed); } /** * Send error notification */ async notifyError({ operation, positionAddress, error, }) { const embed = { title: '⚠️ Operation Failed', color: 0xffa500, // Orange timestamp: new Date().toISOString(), fields: [ { name: '❌ Operation', value: operation, inline: true, }, { name: '📋 Position', value: `[${positionAddress.slice(0, 8)}...${positionAddress.slice(-8)}](https://solscan.io/account/${positionAddress})`, inline: true, }, { name: '🔴 Error', value: error.slice(0, 1000), // Discord field limit is 1024 inline: false, }, ], footer: { text: 'Byreal Sniper Bot', }, }; await this.sendNotification(embed); } } export default DiscordWebhook;