From cf51573ab5cb36dde9a0387ee39d8aa898176718 Mon Sep 17 00:00:00 2001 From: Justin Oro Date: Fri, 4 Sep 2026 17:09:37 -0700 Subject: [PATCH] Share remembered facts across all members instead of per-user only --- README.md | 4 ++-- index.js | 17 ++++++++++------- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 410e26f..8c7e8fc 100644 --- a/README.md +++ b/README.md @@ -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 the best price without watching it, `!prices GAME NAME` to list every store it's on sale at, cheapest first, `!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, `!popular` to list games owned by more than one member, most owned first, `!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 the best price without watching it, `!prices GAME NAME` to list every store it's on sale at, cheapest first, `!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, `!popular` to list games owned by more than one member, most owned first, `!remember SOMETHING` to permanently teach it a fact that survives restarts and is shared with everyone it talks to, `!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. @@ -84,7 +84,7 @@ To change the token or key later, edit `.env` directly, then run the installer a - `!popular` combines both sources across every game, not just one at a time: anything self-reported with `!own`, plus a full scan of every Steam-linked member's library (Steam already hands back the game name with that lookup, so no extra API calls are needed). The same game reported through `!own` and detected in someone's Steam library counts as one shared total, not two. Games with only one confirmed owner are left out, and everything is sorted most-owned first. - 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. +- `!remember SOMETHING` teaches it a permanent fact - a name, a preference, a correction like "don't call me buddy", or something about another member mentioned by name - that it will always take into account, even after the bot restarts. These facts are shared: anyone can ask about a fact someone else taught it, not just the person who typed `!remember`. They're saved to `memory.yaml` under whoever typed the command, and never expire on their own, unlike the short-term memory described below. `!forget SOMETHING` removes one you personally taught it (it has to match what you typed to `!remember`, word for word - use `!memories` to see the exact wording it has stored under your name), and `!forget all` clears everything you've had it remember. 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. - It remembers each person's last 3 exchanges (their message and its reply) so it can follow up naturally and stick to things you told it earlier in the conversation, like a correction or a preference. This memory is per-person, kept only in the bot's running memory (not saved to disk), and forgotten automatically after 30 minutes of that person not mentioning it. - The bot's name, trigger word, and personality all come from `personality.yaml`. It ships set up as BMO from Adventure Time: diff --git a/index.js b/index.js index 07baf74..32afd13 100644 --- a/index.js +++ b/index.js @@ -123,17 +123,20 @@ function trimIncompleteSentence(text) { return trimmed.slice(0, lastEnd + 1); } -async function askAI(question, history, displayName, memories) { +async function askAI(question, history, displayName, allFacts) { const messages = [ { role: "system", content: AI_SYSTEM_PROMPT }, { role: "system", content: "The Discord member you are talking to is called " + displayName + ". Do not address them by any other name." } ]; - if (memories && memories.length > 0) { + if (allFacts && allFacts.length > 0) { messages.push({ role: "system", - content: "Permanent facts this member has asked you to remember about them - always follow these, " + - "they override your default personality habits:\n" + memories.map((m) => "- " + m).join("\n") + content: "Permanent facts Discord members have taught you with !remember - shared knowledge across " + + "everyone here, not private to whoever said them. Some are about the person who said them, some " + + "are about someone else they mentioned by name - use whichever ones are relevant, including when " + + "someone asks you what you know about a specific named person:\n" + + allFacts.map((f) => "- " + f).join("\n") }); } @@ -1322,7 +1325,7 @@ async function handleHelp(message, note) { "!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" + "!popular - list games owned by more than one member, most owned first\n" + - "!remember SOMETHING - permanently teach me a fact about you (a name, a preference), I'll remember it across restarts\n" + + "!remember SOMETHING - permanently teach me a fact (about you or someone else by name), shared with everyone and remembered across restarts\n" + "!forget SOMETHING - make me forget something you had me remember (must match exactly), or !forget all\n" + "!memories - show everything I remember about you\n" + "!check - run the sale check right now instead of waiting for the daily run\n" + @@ -1582,8 +1585,8 @@ client.on("messageCreate", async (message) => { const displayName = (message.member && message.member.displayName) || message.author.globalName || message.author.username; const memory = loadMemory(); - const memories = memory[message.author.id] || []; - const aiReply = await askAI(content, history, displayName, memories); + const allFacts = Object.values(memory).reduce((acc, list) => acc.concat(list || []), []); + const aiReply = await askAI(content, history, displayName, allFacts); if (aiReply) { await message.reply(aiReply); rememberAiExchange(message.author.id, content, aiReply);