2026-02-13 22:46:20 +01:00
2026-02-13 22:46:20 +01:00
2026-02-13 22:46:20 +01:00
2026-02-13 20:56:23 +01:00
2026-02-13 20:56:23 +01:00
2026-02-13 22:46:20 +01:00
2026-02-13 22:46:20 +01:00

Discord AI Companion

Nova is a friendly, slightly witty Discord companion that chats naturally in DMs or when mentioned in servers. It runs on Node.js, uses discord.js v14, and leans on OpenAI's cost-efficient models plus lightweight local memory for persistent personality.

Features

  • Conversational replies in DMs automatically; replies in servers when mentioned or in a pinned channel.
  • OpenAI chat model (gpt-4o-mini by default) for dialogue and text-embedding-3-small for memory.
  • Short-term, long-term, and summarized memory layers with cosine-similarity retrieval.
  • Automatic memory pruning, importance scoring, and transcript summarization when chats grow long.
  • Local JSON vector store (no extra infrastructure) plus graceful retries for OpenAI rate limits.
  • Optional "miss u" pings that DM your coder at random intervals (06h) when CODER_USER_ID is set.
  • Dynamic per-message prompt directives that tune Nova's tone (empathetic, hype, roleplay, etc.) before every OpenAI call.
  • Lightweight DuckDuckGo scraping for "Google-like" answers without paid APIs (locally cached).
  • Guard rails that refuse "ignore previous instructions"-style jailbreak attempts plus a configurable search blacklist.
  • All DuckDuckGo requests are relayed through rotating ProxyScrape HTTP proxies so Nova never hits the web from its real IP.

Prerequisites

  • Node.js 18+
  • Discord bot token with Message Content Intent enabled
  • OpenAI API key

Setup

  1. Install dependencies:
    npm install
    
  2. Copy the environment template:
    cp .env.example .env
    
  3. Fill .env with your secrets:
    • DISCORD_TOKEN: Discord bot token
    • OPENAI_API_KEY: OpenAI key
    • OPENAI_MODEL: Optional chat model override (default gpt-4o-mini)
    • OPENAI_EMBED_MODEL: Optional embedding model (default text-embedding-3-small)
    • BOT_CHANNEL_ID: Optional guild channel ID where the bot can reply without mentions
    • CODER_USER_ID: Optional Discord user ID to receive surprise DMs every 06 hours
    • ENABLE_WEB_SEARCH: Set to false to disable DuckDuckGo lookups (default true)
    • ENABLE_PROXY_SCRAPE: Set to false only if you want to bypass ProxyScrape and hit DuckDuckGo directly (default true)
    • PROXYSCRAPE_ENDPOINT: Optional override for the proxy list endpoint (defaults to elite HTTPS-capable HTTP proxies)
    • PROXYSCRAPE_REFRESH_MS: How long to cache the proxy list locally (default 600000 ms)
    • PROXYSCRAPE_ATTEMPTS: Max proxy retries per search request (default 5)

Running

  • Development: npm run dev
  • Production: npm start

Optional PM2 Setup

npm install -g pm2
pm2 start npm --name nova-bot -- run start
pm2 save

PM2 restarts the bot if it crashes and keeps logs (pm2 logs nova-bot).

File Structure

src/
  bot.js        # Discord client + routing logic
  config.js     # Environment and tuning knobs
  openai.js     # Chat + embedding helpers with retry logic
  memory.js     # Multi-layer memory engine
.env.example
README.md

How Memory Works

  • Short-term (recency buffer): Last 10 conversation turns kept verbatim for style and continuity. Stored per user in data/memory.json.
  • Long-term (vector store): Every user message + bot reply pair becomes an embedding via text-embedding-3-small. Embeddings, raw text, timestamps, and heuristic importance scores are stored in the JSON vector store. Retrieval uses cosine similarity plus a small importance boost; top 5 results feed the prompt.
  • Summary layer: When the recency buffer grows past ~3000 characters, Nova asks OpenAI to condense the transcript to <120 words, keeps the summary, and trims the raw buffer down to the last few turns. This keeps token usage low while retaining story arcs.
  • Importance scoring: Messages mentioning intent words ("plan", "remember", etc.), showing length, or emotional weight receive higher scores. When the store exceeds its cap, the lowest-importance/oldest memories are pruned. You can also call pruneLowImportanceMemories() manually if needed.

Memory Deep Dive

  • Embedding math: text-embedding-3-small returns 1,536 floating-point numbers for each text chunk. That giant array is a vector map of the messages meaning; similar moments land near each other in 1,536-dimensional space.
  • What gets embedded: After every user→bot turn, recordInteraction() (see src/memory.js) bundles the pair, scores its importance, asks OpenAI for an embedding, and stores { content, embedding, importance, timestamp } inside data/memory.json.
  • Why so many numbers: Cosine similarity needs raw vectors to compare new thoughts to past ones. When a fresh message arrives, retrieveRelevantMemories() embeds it too, calculates cosine similarity against every stored vector, adds a small importance boost, and returns the top five memories to inject into the system prompt.
  • Self-cleaning: If the JSON file grows past the configured limits, low-importance items are trimmed, summaries compress the short-term transcript, and you can delete data/memory.json to reset everything cleanly.

Conversation Flow

  1. Incoming message triggers only if it is a DM, mentions the bot, or appears in the configured channel.
  2. The user turn is appended to short-term memory immediately.
  3. The memory engine retrieves relevant long-term memories and summary text.
  4. A compact system prompt injects personality, summary, and relevant memories before passing short-term history to OpenAI.
  5. The reply is sent back to Discord. If Nova wants to send a burst of thoughts, she emits the <SPLIT> token and the runtime fans it out into multiple sequential Discord messages.
  6. Long chats automatically summarize; low-value memories eventually get pruned.

Dynamic Prompting

  • Each turn, Nova inspects the fresh user message (tone, instructions, roleplay cues, explicit “split this” requests) plus the last few utterances.
  • A helper (composeDynamicPrompt in src/bot.js) emits short directives like “User mood: fragile, be gentle” or “They asked for roleplay—stay in character.”
  • These directives slot into the system prompt ahead of memories, so OpenAI gets real-time guidance tailored to the latest vibe without losing the core persona.
  • src/search.js scrapes DuckDuckGo's HTML endpoint with a normal browser user-agent, extracts the top results (title/link/snippet), and caches them for 10 minutes to avoid hammering the site.
  • bot.js detects when a question sounds “live” (mentions today/news/google/etc.) and injects the formatted snippets into the prompt as "Live intel". No paid APIs involved—its just outbound HTTPS from your machine.
  • Toggle this via ENABLE_WEB_SEARCH=false if you dont want Nova to look things up.
  • DuckDuckGo traffic is routed through the free ProxyScrape list (HTTP proxies with HTTPS support). The bot downloads a fresh pool every PROXYSCRAPE_REFRESH_MS, rotates through them, and refuses to search if no proxy is available so your origin IP never touches suspicious sites directly. Tune the endpoint/refresh/attempt knobs with the env vars above if you need different regions or paid pools.
  • Edit data/filter.txt to maintain a newline-delimited list of banned search keywords/phrases; matching queries are blocked before hitting DuckDuckGo and Nova is instructed to refuse them.
  • Every entry in data/search.log records which proxy (or cache) served the lookup so you can audit traffic paths quickly.

Proactive Pings

  • When CODER_USER_ID is provided, Nova spins up a timer on startup that waits a random duration (anywhere from immediate to 6 hours) before DMing that user.
  • Each ping goes through OpenAI with the prompt "you havent messaged your coder in a while, and you wanna chat with him!" so responses stay playful and unscripted.
  • The ping gets typed out (sendTyping) for realism and is stored back into the memory layers so the next incoming reply has context.

Update Log

  • 2026-02-13 — Dynamic personality + multi-message riffs: Added the instinctive persona prompt with tone mirroring, <SPLIT>-based multi-bubble replies, and proactive coder pings so Nova feels alive in DMs.
  • 2026-02-13 — Memory intelligence: Implemented embeddings-backed long-term memory, short-term buffers, transcript summarization, and heuristic importance pruning stored in data/memory.json.
  • 2026-02-13 — Live intel & directives: Introduced DuckDuckGo scraping, per-turn dynamic prompt directives (tone, roleplay, instruction compliance), and env toggles (ENABLE_WEB_SEARCH, CODER_USER_ID).
  • 2026-02-13 — UX polish: Added typing indicators, persona-aware fallback replies, mention cleaning, and README/docs covering setup, memory internals, web search, and deployment tips.
  • 2026-02-13 — Conversational control: Tuned system prompt to avoid forced follow-up questions, raised temperature for looser banter, and reinforced Nova's awareness of DuckDuckGo lookups plus <SPLIT> usage.
  • 2026-02-13 — Statement-first vibes: Reworked persona to favor bold statements over reflexive questions and dialed back temperature so Nova keeps the vibe without interrogating users.
  • 2026-02-13 — Search logging: Every DuckDuckGo lookup now appends a line to data/search.log with timestamp, query, and the snippets shared with Nova.
  • 2026-02-13 — Safeguards: Added prompt bypass detection and a file-based DuckDuckGo filter (data/filter.txt) to keep Nova from honoring jailbreak requests or searching off-limits topics.
  • 2026-02-13 — Proxy-based search: DuckDuckGo scraping now tunnels through ProxyScrape relays with automatic rotation/retries and clear prompts when the proxy pool is down, plus new env toggles for tuning the proxy source.

Notes

  • The bot retries OpenAI requests up to 3 times with incremental backoff when rate limited.
  • data/memory.json is ignored by git but will grow with usage; back it up if you want persistent personality.
  • To reset persona, delete data/memory.json while the bot is offline.

Happy chatting!

Description
Open-source AI Discord chatbot built with modular architecture, memory handling etc.
Readme MIT 163 KiB
Languages
JavaScript 70.4%
HTML 29.6%