Initial commit
This commit is contained in:
commit
2fe0082973
16 files changed
+2463
No files matched your search
@@ -0,0 +1,257 @@
|
||||
require("dotenv").config();
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const readline = require("node:readline/promises");
|
||||
const { stdin: input, stdout: output } = require("node:process");
|
||||
|
||||
const DISCORD_BOT_TOKEN = process.env.DISCORD_BOT_TOKEN;
|
||||
const STEAM_API_KEY = process.env.STEAM_API_KEY;
|
||||
const DISCORD_API = "https://discord.com/api/v10";
|
||||
const STEAMLINKS_PATH = path.join(__dirname, "steamlinks.json");
|
||||
const OWNER_CONFIG_PATH = path.join(__dirname, ".link-steam-profiles-owner.json");
|
||||
|
||||
if (!DISCORD_BOT_TOKEN) {
|
||||
console.error("DISCORD_BOT_TOKEN is not set in .env. Run bash install.sh first to set it up.");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (!STEAM_API_KEY) {
|
||||
console.error("STEAM_API_KEY is not set in .env. Run bash install.sh and provide a Steam Web API key first.");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
function loadSteamLinks() {
|
||||
if (!fs.existsSync(STEAMLINKS_PATH)) return {};
|
||||
return JSON.parse(fs.readFileSync(STEAMLINKS_PATH, "utf8"));
|
||||
}
|
||||
|
||||
function saveSteamLinks(links) {
|
||||
fs.writeFileSync(STEAMLINKS_PATH, JSON.stringify(links, null, 2) + "\n");
|
||||
}
|
||||
|
||||
function loadOwnerSteamId() {
|
||||
if (!fs.existsSync(OWNER_CONFIG_PATH)) return null;
|
||||
try {
|
||||
const data = JSON.parse(fs.readFileSync(OWNER_CONFIG_PATH, "utf8"));
|
||||
return data.steamId || null;
|
||||
} catch (err) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function saveOwnerSteamId(steamId) {
|
||||
fs.writeFileSync(OWNER_CONFIG_PATH, JSON.stringify({ steamId: steamId }, null, 2) + "\n");
|
||||
}
|
||||
|
||||
function parseSteamProfileInput(rawInput) {
|
||||
const trimmed = rawInput.trim();
|
||||
let m = trimmed.match(/steamcommunity\.com\/profiles\/(\d{17})/i);
|
||||
if (m) return { type: "id", value: m[1] };
|
||||
m = trimmed.match(/steamcommunity\.com\/id\/([^\/\s]+)/i);
|
||||
if (m) return { type: "vanity", value: m[1] };
|
||||
if (/^\d{17}$/.test(trimmed)) return { type: "id", value: trimmed };
|
||||
return { type: "vanity", value: trimmed };
|
||||
}
|
||||
|
||||
async function resolveSteamId(rawInput) {
|
||||
const parsed = parseSteamProfileInput(rawInput);
|
||||
if (parsed.type === "id") return parsed.value;
|
||||
|
||||
const url = "https://api.steampowered.com/ISteamUser/ResolveVanityURL/v0001/?key=" +
|
||||
STEAM_API_KEY + "&vanityurl=" + encodeURIComponent(parsed.value);
|
||||
const res = await fetch(url);
|
||||
if (!res.ok) throw new Error("resolve vanity url failed: " + res.status);
|
||||
const data = await res.json();
|
||||
if (data.response && data.response.success === 1) return data.response.steamid;
|
||||
return null;
|
||||
}
|
||||
|
||||
async function fetchBotGuilds() {
|
||||
const res = await fetch(DISCORD_API + "/users/@me/guilds", {
|
||||
headers: { Authorization: "Bot " + DISCORD_BOT_TOKEN }
|
||||
});
|
||||
if (!res.ok) throw new Error("Discord guild list failed: " + res.status);
|
||||
return res.json();
|
||||
}
|
||||
|
||||
async function fetchGuildMembers(guildId) {
|
||||
const res = await fetch(DISCORD_API + "/guilds/" + guildId + "/members?limit=1000", {
|
||||
headers: { Authorization: "Bot " + DISCORD_BOT_TOKEN }
|
||||
});
|
||||
if (!res.ok) throw new Error("Discord member list failed: " + res.status);
|
||||
return res.json();
|
||||
}
|
||||
|
||||
async function fetchFriendList(steamId) {
|
||||
const url = "https://api.steampowered.com/ISteamUser/GetFriendList/v0001/?key=" +
|
||||
STEAM_API_KEY + "&steamid=" + steamId + "&relationship=friend";
|
||||
const res = await fetch(url);
|
||||
if (res.status === 401) {
|
||||
throw new Error(
|
||||
"your Steam friends list is set to private. Go to Steam > Settings > Privacy Settings > " +
|
||||
"\"My friends list\" and set it to Public, then try again."
|
||||
);
|
||||
}
|
||||
if (!res.ok) throw new Error("Steam friend list failed: " + res.status);
|
||||
const data = await res.json();
|
||||
return (data.friendslist && data.friendslist.friends) || [];
|
||||
}
|
||||
|
||||
async function fetchPlayerSummaries(steamIds) {
|
||||
const url = "https://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0002/?key=" +
|
||||
STEAM_API_KEY + "&steamids=" + steamIds.join(",");
|
||||
const res = await fetch(url);
|
||||
if (!res.ok) throw new Error("Steam player summaries failed: " + res.status);
|
||||
const data = await res.json();
|
||||
return (data.response && data.response.players) || [];
|
||||
}
|
||||
|
||||
function memberDisplayName(member) {
|
||||
return member.nick || member.user.global_name || member.user.username;
|
||||
}
|
||||
|
||||
async function pickFromList(rl, label, items, formatItem) {
|
||||
console.log("");
|
||||
console.log(label);
|
||||
items.forEach((item, i) => {
|
||||
console.log(" " + (i + 1) + ". " + formatItem(item));
|
||||
});
|
||||
|
||||
while (true) {
|
||||
const answer = (await rl.question("Enter a number (or 0 to cancel): ")).trim();
|
||||
const n = parseInt(answer, 10);
|
||||
if (n === 0) return null;
|
||||
if (!isNaN(n) && n >= 1 && n <= items.length) return items[n - 1];
|
||||
console.log("Not a valid choice, try again.");
|
||||
}
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const rl = readline.createInterface({ input, output });
|
||||
|
||||
let ownerSteamId = loadOwnerSteamId();
|
||||
if (!ownerSteamId) {
|
||||
console.log("First, we need your own Steam profile so we can read your friends list from it.");
|
||||
const profileInput = await rl.question("Enter your Steam profile URL or SteamID: ");
|
||||
try {
|
||||
ownerSteamId = await resolveSteamId(profileInput);
|
||||
} catch (err) {
|
||||
console.error("Could not look up that Steam profile:", err.message);
|
||||
rl.close();
|
||||
process.exit(1);
|
||||
}
|
||||
if (!ownerSteamId) {
|
||||
console.error("Could not find a Steam profile matching that.");
|
||||
rl.close();
|
||||
process.exit(1);
|
||||
}
|
||||
saveOwnerSteamId(ownerSteamId);
|
||||
console.log("Saved, so you won't be asked again next time.");
|
||||
}
|
||||
|
||||
let guilds;
|
||||
try {
|
||||
guilds = await fetchBotGuilds();
|
||||
} catch (err) {
|
||||
console.error("Could not load your Discord server list:", err.message);
|
||||
rl.close();
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (!guilds || guilds.length === 0) {
|
||||
console.error("The bot is not in any Discord server yet.");
|
||||
rl.close();
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
let guild = guilds[0];
|
||||
if (guilds.length > 1) {
|
||||
guild = await pickFromList(rl, "Which Discord server?", guilds, (g) => g.name);
|
||||
if (!guild) {
|
||||
rl.close();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let friends;
|
||||
try {
|
||||
friends = await fetchFriendList(ownerSteamId);
|
||||
} catch (err) {
|
||||
console.error("Could not load your Steam friends list:", err.message);
|
||||
rl.close();
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (friends.length === 0) {
|
||||
console.error(
|
||||
"No Steam friends were returned. This usually means your Steam friends list privacy " +
|
||||
"is not set to public - check that in your Steam privacy settings and try again."
|
||||
);
|
||||
rl.close();
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
let friendProfiles = [];
|
||||
const friendIds = friends.map((f) => f.steamid);
|
||||
for (let i = 0; i < friendIds.length; i += 100) {
|
||||
const batch = friendIds.slice(i, i + 100);
|
||||
try {
|
||||
const summaries = await fetchPlayerSummaries(batch);
|
||||
friendProfiles.push(...summaries);
|
||||
} catch (err) {
|
||||
console.error("Could not load some friend profiles:", err.message);
|
||||
}
|
||||
}
|
||||
friendProfiles.sort((a, b) => (a.personaname || "").localeCompare(b.personaname || ""));
|
||||
|
||||
let keepGoing = true;
|
||||
while (keepGoing) {
|
||||
let members;
|
||||
try {
|
||||
members = await fetchGuildMembers(guild.id);
|
||||
} catch (err) {
|
||||
console.error("Could not load server members:", err.message);
|
||||
break;
|
||||
}
|
||||
|
||||
members = members.filter((m) => !m.user.bot);
|
||||
members.sort((a, b) => memberDisplayName(a).localeCompare(memberDisplayName(b)));
|
||||
|
||||
if (members.length === 0) {
|
||||
console.error("No members found in that server.");
|
||||
break;
|
||||
}
|
||||
|
||||
const currentLinks = loadSteamLinks();
|
||||
const member = await pickFromList(
|
||||
rl,
|
||||
"Which Discord server member?",
|
||||
members,
|
||||
(m) => memberDisplayName(m) + " (@" + m.user.username + ") - " +
|
||||
(currentLinks[m.user.id] ? "(linked)" : "(unlinked)")
|
||||
);
|
||||
if (!member) break;
|
||||
|
||||
const friend = await pickFromList(
|
||||
rl,
|
||||
"Which Steam profile?",
|
||||
friendProfiles,
|
||||
(f) => (f.personaname || "unknown") + " - " + f.profileurl
|
||||
);
|
||||
if (!friend) continue;
|
||||
|
||||
const links = loadSteamLinks();
|
||||
links[member.user.id] = friend.steamid;
|
||||
saveSteamLinks(links);
|
||||
|
||||
console.log("Linked " + memberDisplayName(member) + " to " + (friend.personaname || friend.steamid) + ".");
|
||||
|
||||
const again = (await rl.question("Link another? (y/N): ")).trim().toLowerCase();
|
||||
keepGoing = again === "y" || again === "yes";
|
||||
}
|
||||
|
||||
console.log("Done.");
|
||||
rl.close();
|
||||
}
|
||||
|
||||
main();
|
||||
Reference in new issue
Block a user