dexscreener-obs/main.js

292 lines
9.4 KiB
JavaScript
Raw Normal View History

2024-03-02 14:57:32 -05:00
require('dotenv').config();
const axios = require('axios');
const WebSocket = require('ws');
const crypto = require('crypto');
2024-03-02 16:04:10 -05:00
const fs = require('fs');
const path = require('path');
2024-03-02 17:12:06 -05:00
const { Client, GatewayIntentBits, EmbedBuilder } = require('discord.js');
const Discord = require('discord.js');
const readline = require('readline');
2024-03-02 16:04:10 -05:00
const envPath = path.resolve(__dirname, '.env');
2024-03-02 14:57:32 -05:00
const obsWebSocketUrl = 'ws://localhost:4444';
const password = process.env.OBS_PASSWORD;
2024-03-02 16:04:10 -05:00
const discordToken = process.env.DISCORD_TOKEN;
2024-03-02 19:12:59 -05:00
const controlChannelId = process.env.controlChannelId;
2024-03-02 16:04:10 -05:00
let currentToken = process.env.tokenAddress;
2024-03-02 17:12:06 -05:00
let statsMessageId = process.env.STATS_MESSAGE_ID || null;
const statsChannelId = process.env.STATS_CHANNEL_ID;
2024-03-02 16:04:10 -05:00
2024-03-02 14:57:32 -05:00
const ws = new WebSocket(obsWebSocketUrl);
2024-03-02 16:04:10 -05:00
const discordClient = new Client({ intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages, GatewayIntentBits.MessageContent] });
let calculationInterval;
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
rl.on('line', (input) => {
console.log(`Received input: ${input}`);
updateTokenAndRestart(input.trim());
});
function updateTokenAndRestart(newToken) {
currentToken = newToken;
console.log(`Token updated to: ${newToken}`);
updateEnvToken(newToken);
restartCalculations();
}
2024-03-02 16:04:10 -05:00
function restartCalculations() {
if (calculationInterval) {
clearInterval(calculationInterval);
}
calculationInterval = setInterval(checkStatusAndUpdateOBS, 5000);
}
let obsVisibilityState = {
makingMoney: null
};
2024-03-02 16:04:10 -05:00
discordClient.login(discordToken);
2024-03-02 17:12:06 -05:00
discordClient.on('ready', async () => {
console.log(`Logged in as ${discordClient.user.tag}!`);
restartCalculations();
const statsChannel = await discordClient.channels.fetch(statsChannelId);
if (statsChannel) {
try {
const existingMessage = await statsChannel.messages.fetch(process.env.STATS_MESSAGE_ID);
console.log(`Using existing stats message ID: ${process.env.STATS_MESSAGE_ID}`);
} catch (error) {
console.log(`Existing stats message ID is invalid or the message was not found. Sending a new message.`);
const sentMessage = await statsChannel.send({ embeds: [createStatsEmbed()] });
console.log(`New stats message sent, ID: ${sentMessage.id}`);
updateEnvValue('STATS_MESSAGE_ID', sentMessage.id);
}
}
2024-03-02 16:04:10 -05:00
});
2024-03-02 17:12:06 -05:00
function updateEnvValue(key, value) {
fs.readFile(envPath, 'utf8', function(err, data) {
if (err) {
return console.error('Error reading .env file:', err);
}
const envLines = data.split('\n');
const updatedLines = envLines.map(line => line.startsWith(`${key}=`) ? `${key}=${value}` : line);
if (!updatedLines.some(line => line.startsWith(`${key}=`))) {
updatedLines.push(`${key}=${value}`);
}
fs.writeFile(envPath, updatedLines.join('\n'), 'utf8', function(err) {
if (err) {
return console.error('Error writing to .env file:', err);
}
console.log(`${key} updated in .env file.`);
});
});
}
function createStatsEmbed(tokenData = {}) {
const formatNumber = (num) => new Intl.NumberFormat().format(num);
const marketCapFormatted = tokenData.marketCap ? `$${formatNumber(tokenData.marketCap)}` : 'N/A';
const priceFormatted = tokenData.price ? `$${formatNumber(tokenData.price)}` : 'N/A';
const liquidityFormatted = tokenData.liquidity ? `$${formatNumber(tokenData.liquidity)}` : 'N/A';
const priceChangeFormatted = tokenData.priceChange ? `${formatNumber(tokenData.priceChange)}%` : 'N/A';
const embed = new EmbedBuilder()
.setTitle(`${tokenData.name} $${tokenData.symbol} Token Stats`)
.setURL(`https://dexscreener.com/solana/${currentToken}`)
.setColor('#0099ff')
.addFields(
{ name: 'Market Cap', value: marketCapFormatted, inline: true },
{ name: 'Price (USD)', value: `$${tokenData.price || 'N/A'}`, inline: true },
{ name: 'Liquidity (USD)', value: liquidityFormatted, inline: true },
{ name: 'Status', value: tokenData.profitStatus ? 'Making Money 📈💰' : 'Losing Money 📉😡', inline: false },
{ name: 'Price Change (5 Min)', value: priceChangeFormatted, inline: true }
);
return embed;
}
function calculateProfitStatus(tokenData) {
return tokenData.priceChange && tokenData.priceChange.m5 > 0;
}
const updateStatsMessage = async (tokenData) => {
const statsChannel = await discordClient.channels.fetch(statsChannelId);
2024-03-02 18:58:18 -05:00
if (!statsChannel) {
console.log("Stats channel not found.");
return;
}
2024-03-02 17:12:06 -05:00
if (statsMessageId) {
try {
const message = await statsChannel.messages.fetch(statsMessageId);
await message.edit({ embeds: [createStatsEmbed(tokenData)] });
} catch (error) {
console.error('Error updating stats message, sending a new one:', error);
sendNewStatsMessage(statsChannel, tokenData);
}
} else {
sendNewStatsMessage(statsChannel, tokenData);
}
};
const sendNewStatsMessage = async (statsChannel, tokenData) => {
try {
const sentMessage = await statsChannel.send({ embeds: [createStatsEmbed(tokenData)] });
statsMessageId = sentMessage.id;
console.log(`New stats message sent, ID: ${statsMessageId}`);
updateEnvValue('STATS_MESSAGE_ID', statsMessageId);
} catch (error) {
console.error('Error sending new stats message:', error);
}
};
2024-03-02 16:04:10 -05:00
discordClient.on('messageCreate', message => {
if (message.channel.id === controlChannelId) {
if (message.content.startsWith('!token ')) {
const newToken = message.content.split(' ')[1];
if (newToken) {
currentToken = newToken;
updateEnvToken(newToken);
restartCalculations();
message.reply('Token updated successfully. Restarting calculations.');
}
}
}
});
function updateEnvToken(newToken) {
fs.readFile(envPath, 'utf8', (err, data) => {
if (err) {
console.error('Error reading .env file', err);
return;
}
let updatedData = data.split('\n').map(line => {
if (line.startsWith('tokenAddress=')) {
return `tokenAddress=${newToken}`;
}
return line;
}).join('\n');
fs.writeFile(envPath, updatedData, 'utf8', (err) => {
if (err) console.error('Error writing to .env file', err);
});
});
}
2024-03-02 14:57:32 -05:00
ws.on('open', () => {
console.log('Connected to OBS WebSocket server.');
ws.send(JSON.stringify({
'request-type': 'GetAuthRequired',
'message-id': 'authRequired'
}));
});
ws.on('message', (data) => {
const message = JSON.parse(data.toString());
if (message['message-id'] === 'authRequired') {
if (message.authRequired) {
const auth = generateAuth(password, message.salt, message.challenge);
ws.send(JSON.stringify({
'request-type': 'Authenticate',
'message-id': 'authenticate',
'auth': auth
}));
} else {
setInterval(updateOBSTextVisibility, 60000);
}
} else if (message['message-id'] === 'authenticate') {
if (message.status === 'ok') {
setInterval(updateOBSTextVisibility, 5000);
} else {
console.error('Authentication failed:', message.error);
}
} else if (message['message-id'] === 'setText') {
if (message.status === 'ok') {
console.log('Text updated successfully in OBS.');
} else {
console.error('Failed to update text in OBS:', message.error);
}
}
});
ws.on('error', (error) => {
console.error('WebSocket error:', error);
});
const setSourceVisibility = (sourceName, visible) => {
const message = {
'request-type': 'SetSourceRender',
'source': sourceName,
'render': visible,
'message-id': 'setSourceVisibility'
};
ws.send(JSON.stringify(message));
};
2024-03-02 18:58:18 -05:00
const updateOBSTextVisibility = () => {
const makingMoney = obsVisibilityState.makingMoney;
2024-03-02 18:58:18 -05:00
setSourceVisibility("TextGreen", makingMoney);
setSourceVisibility("TextRed", !makingMoney);
setSourceVisibility("Making", makingMoney);
setSourceVisibility("Losing", !makingMoney);
2024-03-02 14:57:32 -05:00
};
2024-03-02 17:12:06 -05:00
const checkStatusAndUpdateOBS = async () => {
try {
const response = await axios.get(`https://api.dexscreener.com/latest/dex/tokens/${currentToken}`);
if (response.data.pairs && response.data.pairs.length > 0) {
const pair = response.data.pairs[0];
const tokenData = {
name: pair.baseToken.name,
symbol: pair.baseToken.symbol,
marketCap: pair.fdv,
price: pair.priceUsd,
liquidity: pair.liquidity.usd,
profitStatus: pair.priceChange.m5 > 0,
priceChange: pair.priceChange.m5
};
2024-03-02 18:58:18 -05:00
obsVisibilityState.makingMoney = tokenData.profitStatus;
2024-03-02 17:12:06 -05:00
const priceFormatted = tokenData.price ? `$${parseFloat(tokenData.price).toFixed(8)}` : 'N/A';
console.log(`Price Change (Last 5 Minutes): ${pair.priceChange.m5}%`);
console.log('Making Money:', tokenData.profitStatus);
setSourceVisibility("TextGreen", tokenData.profitStatus);
setSourceVisibility("TextRed", !tokenData.profitStatus);
updateStatsMessage(tokenData);
2024-03-02 18:58:18 -05:00
updateOBSTextVisibility();
2024-03-02 17:12:06 -05:00
} else {
console.log('No pairs data found for the token address.');
}
} catch (error) {
console.error('Error fetching data:', error);
}
2024-03-02 14:57:32 -05:00
};
function generateAuth(password, salt, challenge) {
const secret = crypto.createHash('sha256').update(password + salt).digest('base64');
const authResponse = crypto.createHash('sha256').update(secret + challenge).digest('base64');
return authResponse;
}
setInterval(checkStatusAndUpdateOBS, 5000);