Remove bulk-link Discord command, keep terminal-only linking

This commit is contained in:
Justin Oro
2026-08-30 17:05:56 -07:00
parent 46a98f6737
commit 94649c97ef
2 changed files with 3 additions and 78 deletions
+2 -10
View File
@@ -1,6 +1,6 @@
# Discord Game Price Bot
A Discord bot for your server. Type `!watch GAME NAME` in any channel to start tracking a PC game's price, `!remove GAME NAME` (or `!unwatch GAME NAME`) to stop, `!list` to see what is being watched, `!price GAME NAME` to check a price without watching it, `!history GAME NAME` to see every recorded sale over the past 12 months with date and price, `!predict GAME NAME` to guess when it might go on sale next, `!info GAME NAME` to see its platforms, multiplayer/co-op/cross-play info, a link to its store page, and how many people on the server own it, `!own GAME NAME` to mark a game as owned by you, `!unown GAME NAME` to remove it from your owned list, `!my-steam-profile LINK` to link your Steam profile so `!info` can count you automatically, `!bulk-link` to link several members' Steam profiles at once if you already have them, `!who GAME NAME` to list who has confirmed they own a game, `!remember SOMETHING` to permanently teach it a fact about you that survives restarts, `!forget SOMETHING` (or `!forget all`) to make it forget something you had it remember, `!memories` to see everything it remembers about you, `!check` to run the sale check on demand, `!shops` to see which stores it covers. Once a day it checks the best current price across PC stores for everything watched and posts in the channel it was added from when something is on sale, with the title, sale price, and which store it is at.
A Discord bot for your server. Type `!watch GAME NAME` in any channel to start tracking a PC game's price, `!remove GAME NAME` (or `!unwatch GAME NAME`) to stop, `!list` to see what is being watched, `!price GAME NAME` to check a price without watching it, `!history GAME NAME` to see every recorded sale over the past 12 months with date and price, `!predict GAME NAME` to guess when it might go on sale next, `!info GAME NAME` to see its platforms, multiplayer/co-op/cross-play info, a link to its store page, and how many people on the server own it, `!own GAME NAME` to mark a game as owned by you, `!unown GAME NAME` to remove it from your owned list, `!my-steam-profile LINK` to link your Steam profile so `!info` can count games you own automatically, `!who GAME NAME` to list who has confirmed they own a game, `!remember SOMETHING` to permanently teach it a fact about you that survives restarts, `!forget SOMETHING` (or `!forget all`) to make it forget something you had it remember, `!memories` to see everything it remembers about you, `!check` to run the sale check on demand, `!shops` to see which stores it covers. Once a day it checks the best current price across PC stores for everything watched and posts in the channel it was added from when something is on sale, with the title, sale price, and which store it is at.
Mention the bot's trigger word anywhere in a message (that doesn't start with `!`) and, if Ollama is installed and running (and `AI_ENABLED` isn't turned off), it thinks up a short, in-character answer to whatever you said using a free AI model running right on your own computer - no API key, no cost, nothing sent anywhere over the internet. Without Ollama running (or with `AI_ENABLED=false`), it doesn't reply to mentions at all. It never fires on `!` commands, even ones that happen to contain the trigger word. The bot's name, trigger word, and personality are entirely up to you - edit `personality.yaml` to set them (see [Notes](#notes)); it ships pre-filled as BMO from Adventure Time as an example.
@@ -80,15 +80,7 @@ To change the token or key later, edit `.env` directly, then run the installer a
- The join greeting posts to whichever channel is named `general` in your server. To use a different channel, change `GREETING_CHANNEL_NAME` near the top of `index.js` to that channel's name (no `#`) and restart it (re-run the installer, or `pm2 restart discord-game-price-bot`). If no channel with that name exists, nothing posts and `bot.error.log` will note it.
- `!info` gets platforms and Multiplayer/Co-Op/Cross-Play from that game's Steam store page when it has one. For non-Steam games (GOG/Epic exclusives etc.) there is no such lookup available, so "not listed" there means "couldn't confirm it," not "definitely single-player only."
- The ownership count on `!info` combines two sources: anyone who ran `!own` for that game, plus anyone who linked their Steam profile with `!my-steam-profile` and actually owns it there. Discord itself has no concept of game ownership, so there is no fully automatic way to get this - `!own` needs people to mark their own games by hand, and `!my-steam-profile` only auto-detects Steam copies (not GOG/Epic) and only if that person's Steam profile has "game details" set to public. Everything self-reported through `!own` is stored in `owned.json`; Steam profile links are stored in `steamlinks.json`, both in this folder, in plain text.
- If you don't want to make each person run `!my-steam-profile` themselves, there are two ways to link them yourself:
- From the terminal (the easiest way if you already know your server members as Steam friends): run `bash install.sh --link-steam-profiles` (macOS/Linux) or `powershell -ExecutionPolicy Bypass -File install.ps1 -LinkSteamProfiles` (Windows). The first time, it asks for your own Steam profile once (to read your friends list from - it needs your Steam friends list privacy set to public, and remembers your profile after that so it won't ask again). Then it lists your Discord server's members for you to pick one by number, lists your Steam friends for you to pick one by number, links them, and asks if you want to link another (defaults to no). This does not restart the bot, so there is no need to run the installer again afterward.
- From Discord, use `!bulk-link` instead: type `!bulk-link`, then on the following lines list one real @mention and Steam profile URL per line, like:
```
!bulk-link
@Alex https://steamcommunity.com/id/alexname
@Sam https://steamcommunity.com/profiles/76561198012345678
```
Each @mention has to actually be picked from Discord's autocomplete popup while typing (so it turns into a real link, shown highlighted) - plain typed text like `@Alex` that was never selected from that popup will not be recognized, and that line will be skipped. It replies back with who it managed to link and which lines it could not, so you can fix and re-run just those.
- If you don't want to make each person run `!my-steam-profile` themselves, you can link them yourself from the terminal (the easiest way if you already know your server members as Steam friends): run `bash install.sh --link-steam-profiles` (macOS/Linux) or `powershell -ExecutionPolicy Bypass -File install.ps1 -LinkSteamProfiles` (Windows). The first time, it asks for your own Steam profile once (to read your friends list from - it needs your Steam friends list privacy set to public, and remembers your profile after that so it won't ask again). Then it lists your Discord server's members for you to pick one by number, lists your Steam friends for you to pick one by number, links them, and asks if you want to link another (defaults to no). This does not restart the bot, so there is no need to run the installer again afterward. This is terminal-only by design - there's no Discord command for it, so members can't link (or relink) each other's profiles themselves.
- The bot only replies to mentions of its trigger word if Ollama is installed, running, and has the model in `OLLAMA_MODEL` downloaded - without that, mentioning it gets no reply at all, and nothing is logged (check `bot.error.log` if you expect it to be working and it isn't replying). Each person can only trigger an AI answer once every 5 seconds; asking again sooner (or the Ollama request failing) just gets no reply rather than a second request, which keeps a burst of questions from bogging down your computer generating several replies at once.
- `!remember SOMETHING` teaches it a permanent fact about you - a name, a preference, a correction like "don't call me buddy" - that it will always take into account when it replies to you, even after the bot restarts. These facts are saved to `memory.yaml`, per person, and never expire on their own, unlike the short-term memory described below. `!forget SOMETHING` removes one again (it has to match what you typed to `!remember`, word for word - use `!memories` to see the exact wording it has stored), and `!forget all` clears everything it remembers about you at once. Each person can have up to 20 things remembered at a time.
- `OLLAMA_MODEL` in `.env` controls which model it uses (`llama3.2` by default - small, fast, and good enough for short in-character replies). To use a different one, pull it yourself with `ollama pull MODEL_NAME`, set `OLLAMA_MODEL=MODEL_NAME` in `.env`, and restart it (re-run the installer, or `pm2 restart discord-game-price-bot`). Bigger models give better answers but take longer to reply and use more of your computer's memory.
+1 -68
View File
@@ -993,69 +993,6 @@ async function handleLinkSteam(message, input) {
);
}
async function handleBulkLink(message, body) {
if (!STEAM_API_KEY) {
await message.reply("Steam linking is not set up on this bot yet - it needs a Steam Web API key added to .env.");
return;
}
const lines = body.split("\n").map((l) => l.trim()).filter((l) => l.length > 0);
if (lines.length === 0) {
await message.reply(
"Usage: type !bulk-link, then on new lines list one @member and Steam profile per line, like:\n" +
"!bulk-link\n" +
"@user1 https://steamcommunity.com/id/name1\n" +
"@user2 https://steamcommunity.com/id/name2"
);
return;
}
const links = loadSteamLinks();
const linked = [];
const failed = [];
for (const line of lines) {
const mentionMatch = line.match(/<@!?(\d+)>/);
if (!mentionMatch) {
failed.push(line + " (no @mention found)");
continue;
}
const userId = mentionMatch[1];
const rest = line.replace(/<@!?\d+>/, "").trim();
if (!rest) {
failed.push(line + " (no Steam profile given)");
continue;
}
let steamId;
try {
steamId = await resolveSteamId(rest);
} catch (err) {
failed.push(line + " (lookup failed)");
continue;
}
if (!steamId) {
failed.push(line + " (Steam profile not found)");
continue;
}
links[userId] = steamId;
linked.push("<@" + userId + ">");
}
saveSteamLinks(links);
let summary = "Linked " + linked.length + " member" + (linked.length === 1 ? "" : "s") + ".";
if (failed.length > 0) {
summary += "\nCould not link:\n" + failed.map((f) => "- " + f).join("\n");
}
await message.reply({ content: summary, allowedMentions: { users: [] } });
}
async function handleRemove(message, query, commandName) {
if (!query) {
await message.reply("Usage: " + (commandName || "!remove") + " GAME NAME");
@@ -1174,8 +1111,7 @@ async function handleHelp(message) {
"!info GAME - show price, platforms, multiplayer/co-op/cross-play info, a store link, and how many people here own it\n" +
"!own GAME - mark a game as owned by you\n" +
"!unown GAME - remove a game from your owned list\n" +
"!my-steam-profile LINK - link your Steam profile so !info can count you automatically\n" +
"!bulk-link - link multiple members' Steam profiles at once, one @member and link per line\n" +
"!my-steam-profile LINK - link your Steam profile so !info can count games you own automatically\n" +
"!who GAME - list who has confirmed they own a game\n" +
"!remember SOMETHING - permanently teach me a fact about you (a name, a preference), I'll remember it across restarts\n" +
"!forget SOMETHING - make me forget something you had me remember (must match exactly), or !forget all\n" +
@@ -1342,9 +1278,6 @@ client.on("messageCreate", async (message) => {
await handleForget(message, content.slice(8).trim());
} else if (lower === "!memories") {
await handleMemories(message);
} else if (lower.startsWith("!bulk-link")) {
const lines = content.split("\n");
await handleBulkLink(message, lines.slice(1).join("\n"));
} else if (lower === "!check") {
await handleCheck(message, client);
} else if (lower === "!list") {