| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193 |
- 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;
|