Skip to content

bispo-daniel/morphereum-community-openai-ws

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Morphereum — MatrixCHAT WebSocket 💬⚡

A lightweight Socket.IO gateway that streams AI replies for the Morphereum community chat.

Provides two real-time flows: general chat and raid message generator, with per-IP rate limits, friendly block UX, and OpenAI-backed responses.


✨ What this WS does

  • Chat replies: receives a user message and streams back a witty MatrixCHAT reply from OpenAI. The assistant persona is set with a system prompt (humorous, meme-y).
  • Raid composer: given the daily raid context (platform + share message), returns a spicy, share-ready text for raiders.
  • Fair-use limits: 5 chat messages / 12h and 1 raid message / 18h per IP; graceful “blocked” events include a human-readable unlock time.
  • CORS-safe: allows frontends from https://localhost:5173 and https://morphereum.netlify.app.

🧱 Tech Stack

  • Runtime: Node.js + Express (for bootstrapping the HTTP server).
  • Realtime: Socket.IO server, room-addressing by socket.id.
  • AI: OpenAI Chat Completions API, configurable model via env, with max_tokens: 200 and temperature: 0.1.
  • Rate limiting: rate-limiter-flexible (memory store) for chat and raid flows.
  • Config & validation: dotenv + zod for strict environment parsing.

🔌 Events (Socket.IO)

Client → Server

  • "message"
    Payload: a string (the user’s chat text).
    Server runs OpenAI with a humorous “MatrixCHAT” prompt and responds to that socket room.

  • "raid-message"
    Payload (JSON):

    {
      platform: string;
      shareMessage: string;
    }

    Server crafts a meme-y call-to-action text for the given platform and base share copy.

Server → Client

  • "bot-message" — AI reply to "message". Payload: string.
  • "bot-raid-message" — AI reply to "raid-message". Payload: string.
  • "blocked" — chat flow limit reached. Payload:
    {
      "message": "Você atingiu o limite de mensagens. Tente novamente em X...",
      "unblockDateFormatted": "dd/mm/aaaa hh:mm:ss",
      "unblockDate": "2025-10-20T12:34:56.000Z"
    }
    The formatted time uses pt-BR and timezone America/Sao_Paulo.
  • "raid-blocked" — raid flow limit reached. Payload:
    {
      "unblockDateFormatted": "dd/mm/aaaa hh:mm:ss",
      "unblockDate": "2025-10-20T12:34:56.000Z"
    }
    Same localization as above.

🚦 Rate Limits

  • General chat ("message"): 5 messages per 12 hours per IP. On exceed, server emits "blocked" with the remaining wait time (humanized). The humanization uses an internal helper that renders hours/minutes/seconds in Portuguese (e.g., 2 horas e 15 minutos).
  • Raid composer ("raid-message"): 1 message per 18 hours per IP. On exceed, server emits "raid-blocked" with unlock timestamps.

🧠 OpenAI Behavior

  • Model: read from OPENAI_MODEL (e.g., gpt-4o-mini, gpt-4.1-mini, etc.).
  • Completion: max_tokens: 200, temperature: 0.1.
  • System persona: the assistant speaks with an irreverent, comedic tone tailored for the Morphereum community (prompt is in Portuguese).

🔐 Environment Variables

Validated on boot via zod; process exits if missing/invalid.

PORT=8081
IS_DEV_MODE=true
OPENAI_API_KEY=sk-...
OPENAI_MODEL=gpt-4o-mini

▶️ Getting Started

# 1) install deps
pnpm install        # or npm i / yarn

# 2) create .env
cp .env.example .env    # fill in PORT, OPENAI keys, etc.

# 3) run dev
pnpm dev

# 4) run prod
pnpm build && pnpm start
  • On boot, you’ll see [server] --> Running at http://localhost:<PORT>.
  • CORS allows https://localhost:5173 and https://morphereum.netlify.app. Adjust in server.ts if needed.

🧩 Client Usage (Examples)

Minimal client (browser)

<script src="https://cdn.socket.io/4.7.5/socket.io.min.js"></script>
<script>
  const socket = io("http://localhost:8081", {
    transports: ["websocket"],
    withCredentials: true,
  });

  // Send a chat message
  function sendChat(text) {
    socket.emit("message", text);
  }

  // Receive AI reply
  socket.on("bot-message", (reply) => {
    console.log("AI:", reply);
  });

  // Handle rate limit
  socket.on("blocked", ({ message, unblockDateFormatted }) => {
    alert(`${message}\nDesbloqueio: ${unblockDateFormatted}`);
  });

  // Raid composer
  function composeRaid(platform, shareMessage) {
    socket.emit("raid-message", { platform, shareMessage });
  }

  socket.on("bot-raid-message", (reply) => {
    console.log("RAID:", reply);
  });

  socket.on("raid-blocked", ({ unblockDateFormatted }) => {
    alert(`Raid composer locked. Try again at ${unblockDateFormatted}`);
  });
</script>

TypeScript client (snippet)

import { io } from "socket.io-client";

const socket = io("http://localhost:8081");

export function askMatrixChat(message: string) {
  socket.emit("message", message);
}

socket.on("bot-message", (text: string) => {
  // render in UI
});

export function askRaidCopy(platform: string, shareMessage: string) {
  socket.emit("raid-message", { platform, shareMessage });
}

socket.on("bot-raid-message", (text: string) => {
  // render in UI
});

socket.on("blocked", ({ message, unblockDateFormatted }) => {
  // set UI state to blocked until unblockDateFormatted
});

socket.on("raid-blocked", ({ unblockDateFormatted }) => {
  // show toast
});

🧭 Server Flow

Client "message" --> rateLimiter (5/12h) --> OpenAI chat.completions --> emit "bot-message"
Client "raid-message" --> raidLimiter (1/18h) --> OpenAI chat.completions --> emit "bot-raid-message"
Exceed limits --> emit "blocked" | "raid-blocked" with unlock dates (pt-BR, America/Sao_Paulo)
  • Socket rooms use socket.id; replies target the originating client only.

⚙️ Configuration Notes

  • CORS: update origin allowlist in server.ts for new frontends.
  • Formatting helper: formatMsToTime({ ms }) converts wait time into “X horas e Y minutos…”.
  • HTTP layer: Express + http.createServer bootstrap before attaching Socket.IO.

🧪 Status & Errors

  • Application logs a startup banner with host/port.
  • When a limiter rejects, the server does not throw; it emits structured block events with both a raw unblockDate and a localized unblockDateFormatted.

Made with 💚 for the $Morphereum community

About

A lightweight Socket.IO gateway that streams AI replies for the Morphereum community chat.

Topics

Resources

Stars

Watchers

Forks