Merge branch 'dev' into production
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
const { Client, GatewayIntentBits, ActivityType } = require("discord.js");
|
const { Client, GatewayIntentBits, ActivityType } = require("discord.js");
|
||||||
const http = require("http");
|
const http = require("https");
|
||||||
|
|
||||||
const TOKEN = process.env.DISCORD_BOT_TOKEN;
|
const TOKEN = process.env.DISCORD_BOT_TOKEN;
|
||||||
const TARGET_USER_ID = process.env.DISCORD_USER_ID || "172037532370862080";
|
const TARGET_USER_ID = process.env.DISCORD_USER_ID || "172037532370862080";
|
||||||
@@ -18,6 +18,53 @@ const client = new Client({
|
|||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const steamCoverCache = new Map();
|
||||||
|
|
||||||
|
function fetchJSON(url) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const req = http.get(url, { timeout: 5000 }, (res) => {
|
||||||
|
if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
||||||
|
return fetchJSON(res.headers.location).then(resolve).catch(reject);
|
||||||
|
}
|
||||||
|
if (res.statusCode !== 200) {
|
||||||
|
res.resume();
|
||||||
|
return reject(new Error(`HTTP ${res.statusCode}`));
|
||||||
|
}
|
||||||
|
let data = "";
|
||||||
|
res.on("data", (chunk) => (data += chunk));
|
||||||
|
res.on("end", () => {
|
||||||
|
try { resolve(JSON.parse(data)); } catch (e) { reject(e); }
|
||||||
|
});
|
||||||
|
});
|
||||||
|
req.on("error", reject);
|
||||||
|
req.on("timeout", () => { req.destroy(); reject(new Error("timeout")); });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function resolveGameCover(name) {
|
||||||
|
if (steamCoverCache.has(name)) return steamCoverCache.get(name);
|
||||||
|
try {
|
||||||
|
const search = await fetchJSON(`https://store.steampowered.com/api/storesearch/?term=${encodeURIComponent(name)}&l=english&cc=us`);
|
||||||
|
const items = search?.response?.items;
|
||||||
|
if (!items || items.length === 0) {
|
||||||
|
steamCoverCache.set(name, null);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const match = items.find((i) => i.name.toLowerCase() === name.toLowerCase()) || items[0];
|
||||||
|
const appId = match.id;
|
||||||
|
const details = await fetchJSON(`https://store.steampowered.com/api/appdetails/?appids=${appId}&cc=us`);
|
||||||
|
const appData = details?.[String(appId)]?.data;
|
||||||
|
const coverUrl = appData?.header_image || null;
|
||||||
|
steamCoverCache.set(name, coverUrl);
|
||||||
|
console.log(`Resolved cover for "${name}": ${coverUrl}`);
|
||||||
|
return coverUrl;
|
||||||
|
} catch (e) {
|
||||||
|
console.error(`Steam lookup failed for "${name}":`, e.message);
|
||||||
|
steamCoverCache.set(name, null);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let cachedData = {
|
let cachedData = {
|
||||||
discord_status: "offline",
|
discord_status: "offline",
|
||||||
listening_to_spotify: false,
|
listening_to_spotify: false,
|
||||||
@@ -25,52 +72,58 @@ let cachedData = {
|
|||||||
activities: [],
|
activities: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
function updatePresence(guild) {
|
async function updatePresence(guild) {
|
||||||
const member = guild.members.cache.get(TARGET_USER_ID);
|
const member = guild.members.cache.get(TARGET_USER_ID);
|
||||||
if (!member || !member.presence) return;
|
if (!member || !member.presence) return;
|
||||||
|
|
||||||
const presence = member.presence;
|
const presence = member.presence;
|
||||||
cachedData.discord_status = presence.status || "offline";
|
cachedData.discord_status = presence.status || "offline";
|
||||||
|
|
||||||
cachedData.activities = presence.activities
|
const activities = presence.activities
|
||||||
? presence.activities
|
? presence.activities.filter((a) => a.type !== ActivityType.Custom)
|
||||||
.filter((a) => a.type !== ActivityType.Custom)
|
|
||||||
.map((a) => {
|
|
||||||
const entry = {
|
|
||||||
name: a.name,
|
|
||||||
type: a.type,
|
|
||||||
details: a.details || null,
|
|
||||||
state: a.state || null,
|
|
||||||
image: null,
|
|
||||||
applicationId: a.applicationId || null,
|
|
||||||
};
|
|
||||||
if (a.applicationId && a.assets?.largeImage) {
|
|
||||||
const imgKey = a.assets.largeImage;
|
|
||||||
entry.image = imgKey.startsWith("mp:external")
|
|
||||||
? `https://media.discordapp.net/${imgKey.replace("mp:", "")}`
|
|
||||||
: `https://cdn.discordapp.com/app-assets/${a.applicationId}/${imgKey}.png`;
|
|
||||||
}
|
|
||||||
if (a.assets) {
|
|
||||||
entry.assets = {
|
|
||||||
large_image: a.assets.largeImage || null,
|
|
||||||
large_text: a.assets.largeText || null,
|
|
||||||
small_image: a.assets.smallImage || null,
|
|
||||||
small_text: a.assets.smallText || null,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (a.timestamps) {
|
|
||||||
entry.timestamps = {
|
|
||||||
start: a.timestamps.start?.toISOString() || null,
|
|
||||||
end: a.timestamps.end?.toISOString() || null,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return entry;
|
|
||||||
})
|
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
const spotifyActivity = presence.activities
|
const entries = [];
|
||||||
? presence.activities.find((a) => a.type === ActivityType.Listening && a.name === "Spotify")
|
for (const a of activities) {
|
||||||
: null;
|
const entry = {
|
||||||
|
name: a.name,
|
||||||
|
type: a.type,
|
||||||
|
details: a.details || null,
|
||||||
|
state: a.state || null,
|
||||||
|
image: null,
|
||||||
|
applicationId: a.applicationId || null,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (a.applicationId && a.assets?.largeImage) {
|
||||||
|
const imgKey = a.assets.largeImage;
|
||||||
|
entry.image = imgKey.startsWith("mp:external")
|
||||||
|
? `https://media.discordapp.net/${imgKey.replace("mp:", "")}`
|
||||||
|
: `https://cdn.discordapp.com/app-assets/${a.applicationId}/${imgKey}.png`;
|
||||||
|
} else if (a.type === ActivityType.Playing && !entry.image) {
|
||||||
|
const steamCover = await resolveGameCover(a.name);
|
||||||
|
if (steamCover) entry.image = steamCover;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (a.assets) {
|
||||||
|
entry.assets = {
|
||||||
|
large_image: a.assets.largeImage || null,
|
||||||
|
large_text: a.assets.largeText || null,
|
||||||
|
small_image: a.assets.smallImage || null,
|
||||||
|
small_text: a.assets.smallText || null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (a.timestamps) {
|
||||||
|
entry.timestamps = {
|
||||||
|
start: a.timestamps.start?.toISOString() || null,
|
||||||
|
end: a.timestamps.end?.toISOString() || null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
entries.push(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
cachedData.activities = entries;
|
||||||
|
|
||||||
|
const spotifyActivity = activities.find((a) => a.type === ActivityType.Listening && a.name === "Spotify");
|
||||||
|
|
||||||
if (spotifyActivity && spotifyActivity.syncId) {
|
if (spotifyActivity && spotifyActivity.syncId) {
|
||||||
cachedData.listening_to_spotify = true;
|
cachedData.listening_to_spotify = true;
|
||||||
@@ -89,9 +142,9 @@ function updatePresence(guild) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateAll() {
|
async function updateAll() {
|
||||||
for (const guild of client.guilds.cache.values()) {
|
for (const guild of client.guilds.cache.values()) {
|
||||||
updatePresence(guild);
|
await updatePresence(guild);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,7 +165,7 @@ client.on("presenceUpdate", () => {
|
|||||||
updateAll();
|
updateAll();
|
||||||
});
|
});
|
||||||
|
|
||||||
const server = http.createServer((req, res) => {
|
const server = require("http").createServer((req, res) => {
|
||||||
if (req.method === "GET" && req.url === "/presence") {
|
if (req.method === "GET" && req.url === "/presence") {
|
||||||
res.writeHead(200, { "Content-Type": "application/json" });
|
res.writeHead(200, { "Content-Type": "application/json" });
|
||||||
res.end(JSON.stringify({ data: cachedData }));
|
res.end(JSON.stringify({ data: cachedData }));
|
||||||
|
|||||||
@@ -62,6 +62,14 @@ const nextConfig: NextConfig = {
|
|||||||
protocol: "https",
|
protocol: "https",
|
||||||
hostname: "media.discordapp.net",
|
hostname: "media.discordapp.net",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
protocol: "https",
|
||||||
|
hostname: "store.steampowered.com",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
protocol: "https",
|
||||||
|
hostname: "cdn.akamai.steamstatic.com",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
protocol: "https",
|
protocol: "https",
|
||||||
hostname: "cms.dk0.dev",
|
hostname: "cms.dk0.dev",
|
||||||
|
|||||||
Reference in New Issue
Block a user