Skip to content

Commit a95756c

Browse files
committed
Added embed message option.
1 parent 89a810a commit a95756c

File tree

4 files changed

+121
-15
lines changed

4 files changed

+121
-15
lines changed

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ CHAIN="ethereum" # 詳細的名稱清單請參考 https://docs.dexscreener.com/a
1919
PAIR_HASH="0x646946F0518c6Ba27f1B2C6b4387EC6035bC42e3" # 範例交易對,使用的是 "丹 - PFPAsia" 的非官方交易對
2020
UPDATE_FREQUENCY = 5000 # in milliseconds
2121

22+
MESSAGE_TYPE="embed" # embed or text, default is text
23+
2224
# TASKs
2325
UPDATE_STATUS="on" # 讓機器人將價格反應在狀態上,"on" 開啟 or "off" 關閉
2426
BOARDCAST="off" # 讓機器人發送價格訊息到 "TARGET_CHANNEL_IDS" 指定的頻道,"on" 開啟 or "off" 關閉
@@ -33,6 +35,8 @@ CHAIN="ethereum" # For a detailed list of names, please refer to https://docs.de
3335
PAIR_HASH="0x646946F0518c6Ba27f1B2C6b4387EC6035bC42e3" # Example pair hash for the unofficial "Dan - PFPAsia" pair
3436
UPDATE_FREQUENCY = 5000 # in milliseconds
3537

38+
MESSAGE_TYPE="embed" # embed or text, default is text
39+
3640
# TASKs
3741
UPDATE_STATUS="on" # Enable or disable the bot's ability to reflect prices in status. Use "on" or "off".
3842
BOARDCAST="off" # Enable or disable the bot's ability to send price messages to the channels specified in "TARGET_CHANNEL_IDS". Use "on" or "off".
@@ -53,5 +57,6 @@ docker compose up -d --build
5357
Alternatively, you can run the source code using Node:
5458

5559
```sh
60+
npm i
5661
npm run start
5762
```

app.js

Lines changed: 107 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
1-
const { Client, GatewayIntentBits, ActivityType } = require('discord.js');
2-
const { fetchPrice } = require('./utils');
1+
const { Client, GatewayIntentBits, ActivityType, EmbedBuilder } = require('discord.js');
2+
const { fetchPairData } = require('./utils');
33

4-
const client = new Client({ intents: [GatewayIntentBits.Guilds] });
4+
const client = new Client({
5+
intents: [
6+
GatewayIntentBits.Guilds,
7+
GatewayIntentBits.GuildMessages,
8+
GatewayIntentBits.MessageContent,
9+
]
10+
});
511
const dotenv = require('dotenv');
612

713
dotenv.config()
@@ -25,18 +31,112 @@ const doTask = async () => {
2531
}
2632

2733
const updateStatus = async () => {
28-
const price = await fetchPrice()
29-
client.user.setActivity(`$${price}`, { type: ActivityType.Watching })
34+
const data = await fetchPairData()
35+
client.user.setActivity(`$${data.pair.priceUsd}`, { type: ActivityType.Watching })
3036
}
3137

3238
const boardcast = async () => {
33-
const price = await fetchPrice()
39+
const data = await fetchPairData()
3440

35-
// send message to all channels from specific channel ids
41+
// Send message to all channels from specific channel ids
3642
const channelIds = process.env.TARGET_CHANNEL_IDS.split(',');
3743

44+
if (process.env.MESSAGE_TYPE === 'text') {
45+
_boardcastText(channelIds, data);
46+
} else {
47+
_boardcastEmbed(channelIds, data);
48+
}
49+
}
50+
51+
const _boardcastEmbed = async (channelIds, data) => {
52+
for (const channelId of channelIds) {
53+
const channel = await client.channels.fetch(channelId);
54+
55+
// [Optional] Let the chaanel only keep latest message
56+
const messages = await channel.messages.fetch({ limit: 100 });
57+
58+
let targetMsg = null;
59+
// let ftPriceField = null;
60+
let m5ChangeField = null;
61+
62+
for (const message of messages.values()) {
63+
const embedMsg = message.embeds[0];
64+
if (embedMsg) {
65+
m5ChangeField = embedMsg.fields.find(field => field.name === '5M');
66+
67+
if (m5ChangeField) {
68+
targetMsg = message;
69+
break;
70+
}
71+
}
72+
}
73+
// If you can't see any message, please check the bot's permission
74+
// console.log(messages)
75+
76+
if (m5ChangeField &&
77+
m5ChangeField.value === `${data.pair.priceChange.m5}%`) {
78+
// same 5m change rate, no need to update
79+
continue;
80+
} else if (m5ChangeField && m5ChangeField.value !== `$${data.pair.priceChange.m5}`) {
81+
// price changed, delete the old message
82+
await targetMsg.delete();
83+
}
84+
85+
// no existed message or price changed, send a new message
86+
const embedMsg = new EmbedBuilder()
87+
.setTitle('丹 Price')
88+
.setURL(`https://dexscreener.com/${process.env.CHAIN}/${process.env.PAIR_HASH}`)
89+
.setAuthor({ name: 'mmq88x', iconURL: 'https://i.imgur.com/8NJckwc.png', url: 'https://twitter.com/mmq88x' })
90+
.setDescription(`這是現在 PFPAsia 最大流動池的即時價格監控機器人,你可以點擊標頭查看更詳細的資訊。\n` +
91+
`This is the realtime price monitoring bot of PFPAsia's largest pool now.` +
92+
`You can click on the title to view more detailed information.`)
93+
.addFields(
94+
{ name: '<:coin:1207027215414460416> FT Price', value: `$${data.pair.priceUsd}` },
95+
{ name: '5M', value: `${data.pair.priceChange.m5}%`, inline: true },
96+
{ name: '1H', value: `${data.pair.priceChange.h1}%`, inline: true },
97+
{ name: '6H', value: `${data.pair.priceChange.h6}%`, inline: true },
98+
{ name: '24H', value: `${data.pair.priceChange.h24}%`, inline: false },
99+
{ name: '<:liquidity:1207026179639484506> Liquidity', value: `$${data.pair.liquidity.usd}`, inline: true },
100+
{ name: '<:diamond:1207023588000010321> Market cap', value: `$${data.pair.fdv}`, inline: true },
101+
)
102+
.setThumbnail('https://i.imgur.com/ys8mjvO.png')
103+
.setFooter({ text: 'Update time', iconURL: 'https://i.imgur.com/ys8mjvO.png' })
104+
.setColor([227, 23, 13])
105+
.setTimestamp();
106+
107+
await channel.send({ embeds: [embedMsg] });
108+
109+
}
110+
}
111+
112+
const _boardcastText = async (channelIds, data) => {
38113
for (const channelId of channelIds) {
39114
const channel = await client.channels.fetch(channelId);
115+
116+
// [Optional] Let the chaanel only keep latest message
117+
const messages = await channel.messages.fetch({ limit: 100 });
118+
119+
// If you can't see any message, please check the bot's permission
120+
// console.log(messages)
121+
const price = data.pair.priceUsd;
122+
const priceSameMsg = messages.find(
123+
(msg) => msg.content === `丹 DAN Price: $${price}`
124+
);
125+
126+
if (priceSameMsg) {
127+
continue;
128+
}
129+
130+
const priceDifferentMsg = messages.find(
131+
(msg) => msg.content.startsWith('丹 DAN Price:') && msg.content !== `丹 DAN Price: $${price}`
132+
);
133+
134+
if (priceDifferentMsg) {
135+
await priceDifferentMsg.edit(`丹 DAN Price: ${price}`);
136+
continue;
137+
}
138+
139+
// No existed message from the bot, send a new message
40140
await channel.send(`丹 DAN Price: $${price}`);
41141
}
42142
}

example.env

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@ DISCORD_TOKEN="Your Token"
44
TARGET_CHANNEL_IDS="Your Channel ID" # Seperated by ',', ex: 123456789,987654321
55
CHAIN="ethereum" # Detail: https://docs.dexscreener.com/api/reference
66
PAIR_HASH="0x646946F0518c6Ba27f1B2C6b4387EC6035bC42e3"
7-
UPDATE_FREQUENCY = 5000 # in milliseconds
7+
UPDATE_FREQUENCY = 6000 # in milliseconds
8+
9+
MESSAGE_TYPE="embed" # embed or text, default is text
810

911
# TASKs
1012
UPDATE_STATUS="on" # on or off
11-
BOARDCAST="off" # on or off
13+
BOARDCAST="on" # on or off

utils.js

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,17 @@
1-
const fetchPrice = async () => {
2-
priceUsd = -1;
1+
const fetchPairData = async () => {
2+
let data = null;
33

44
try {
55
const response = await fetch(
66
`https://api.dexscreener.com/latest/dex/pairs/${process.env.CHAIN}/${process.env.PAIR_HASH}`
77
);
8-
const data = await response.json();
9-
priceUsd = data.pair.priceUsd;
8+
data = await response.json();
109

1110
} catch (error) {
1211
console.log(error);
1312
}
1413

15-
return priceUsd;
14+
return data;
1615
}
1716

18-
module.exports = { fetchPrice };
17+
module.exports = { fetchPairData };

0 commit comments

Comments
 (0)