ParadoxStudios Logo ParadoxDevelopment

📘 Complete Project Documentation: ParadoxHeartbeat

Welcome to the ultimate documentation for project ParadoxHeartbeat. This documentation is designed to be understood by everyone – from complete beginners to experienced developers. We will explain every file, every piece of logic, and every setting.


📂 1. File Structure

Here is an overview of what you can find where:

ParadoxHeartbeat/
├── config.js           # ⚙️ Main configuration (passwords, IDs, parameters)
├── index.js            # 🧠 BRAIN of the whole bot and web (everything happens here)
├── support.js          # 👥 List of support members (who, when, what they do)
├── tags.js             # 🏷️ Saved quick responses (tags)
├── public/             # 🖼️ Files for web (images, CSS)
├── views/              # 📄 HTML templates for web (EJS)
│   ├── admin/          # Admin panel pages
│   └── transcript.ejs  # Template for chat history
└── ...                 # Other files (package.json, README)

⚙️ 2. Configuration (config.js)

This file controls the bot's behavior. Never share it!

Variable Description
discordToken "Password" for the bot. Allows it to login.
applicationId Application ID (from Discord Developer Portal).
guildId ID of your Discord server.
botOwnerId Your ID (for commands like /forcereset).
logChannelId ID of the channel where bot writes logs (deleted tickets, transcripts).
errorChannelId ID of the channel for error reports (when something goes wrong).
ultimateRoleId ID of "Admin" role for web panel.
supportRoleId ID of "Support" role. These people see tickets.
responseTimeoutMs Time (ms) after which bot stops waiting for clicks (e.g. 60000 = 1 minute).
autoWipeClosedIntervalMs How often (ms) tickets from "Closed" category are wiped.
ticketDb / panelDb Database credentials (MySQL).
web.port Port where web panel runs (e.g. 37015).
web.baseURL Public web address (for transcript links).
openaiApiKey Key for ChatGPT (if AI is enabled).
categories Button settings for tickets (Ark, Minecraft, Billing...).

🚦 System Status & Error Codes

Here is an overview of states and error codes you might encounter.

System States

Green Operational Everything is working as expected. The bot is responsive, threads are managed correctly.

Orange Degraded The system is functioning, but with issues.

  • Non-critical errors (e.g. Code 401, 499)
  • Bot unresponsive for short periods

Red Outage / CPR Critical failure. This state triggers an automatic restart sequence.

Error Code Reference

Code Severity Description
1006 Critical Connection Reset / Timeout. If persists, restart is triggered.
401 Warning Switch Panel Failed. Internal error.
402 Warning Thread Create Failed. Discord API limit or permissions.
404 Warning Roster Update Failed. Database sync issue.
504 Monitor SSH Connection Failed. The bot might be running but monitoring is down.

🧠 3. Main Logic (index.js)

This is the most important file. It does absolutely everything. Let's break it down into parts.

A. Initialization and Helper Functions

At the beginning of the file, the bot loads libraries and prepares tools.

Function embed(title, description, color) This function creates "cards" (embeds) for Discord. Feature: Automatically loads the logo from icon.png and inserts it into every message.

function embed(title, description, color) {
  var e = { title: title, description: description, color: color }
  if (logoBuffer) { // If we have a loaded logo
    e.thumbnail = { url: 'attachment://icon.png' } // Attach as thumbnail
    e.footer = { text: 'Heroic Gamez', icon_url: 'attachment://icon.png' } // Also in footer
  }
  return e
}

Function openaiChat(messages) Communicates with ChatGPT. Contains protection (System Instruction) that commands AI:

  1. Be nice and professional.
  2. Never offer solutions (that is human work).
  3. Only confirm message receipt.

B. Ticket Lifecycle

How does it work when someone clicks a button?

1. Category Selection (ticketpanel)

User types /ticketpanel and bot sends buttons (defined in config.categories). When a user clicks a button (e.g. "Ark"):

  1. Bot checks if it's a specific category (Ark/Minecraft have extra version buttons).
  2. If not, displays a Modal (form).

2. Filling the Form (Modal)

User fills in:

  • Server ID / Email
  • Issue description
  • (Optionally game name)

3. Channel Creation

Once the user submits the form:

  1. Bot creates a new channel (e.g. ark-pepa-12345).
  2. Sets permissions: Only user and Support role can see it.
  3. Saves ticket to database (INSERT INTO tickets).
  4. AI Welcome: Bot sends form data to ChatGPT and says: "Write a nice welcome message for this user, mention their issue, but do not solve it."
  5. Sends the result to the channel as an embed.

4. Closing the Ticket (/closerequest)

When finished:

  1. Support or user types /closerequest.
  2. Bot asks for a reason and sends Accept / Deny buttons.
  3. Upon approval:
    • Ticket is marked as closed.
    • Channel is moved to "Closed" category.
    • Transcript (chat history) is generated and link is saved.
    • User receives a DM with the link and option to rate support (1-5 stars).

C. The Monitoring Loop 🔄

The bot doesn't sleep; it constantly checks things in the background.

1. Auto Wipe Closed Loop

This loop runs every X minutes (based on config.autoWipeClosedIntervalMs).

  • Scans all channels in database marked as "closed".
  • If a channel has been closed longer than the set interval, it deletes it permanently.
  • This keeps Discord clean.

2. Reminder Loop

This loop checks for inactivity in tickets.

  • If support hasn't replied for more than X hours -> Reminder.
  • If user hasn't replied -> Reminder.

D. The CPR Logic (Self-Healing & SSH Restart) 🚑

This is critical for stability. The bot is connected to an external monitoring system that can revive it even from clinical death.

Detecting Error 1006 (Connection Reset) This error means Discord "severed" the connection and the bot is stuck in an inconsistent state. The bot recognizes this and calls for help.

client.on('error', async function(err){
  if (err.code === 1006) {
    // 1. Report error to log channel (so admin knows)
    await client.createMessage(chId, { embeds: [embed('System Alert', 'Connection reset (1006)...', 0xE67E22)] })
    
    // 2. Call external "CPR" server (Remote Restart)
    // This request tells the server: "I am stuck, restart my process via SSH/Docker!"
    http.get('http://136.243.104.68:50030/restart', ...) 
  }
})

SSH Restart (How it works on the other side?) When the bot calls the URL above:

  1. External monitoring server receives the signal.
  2. Executes command (e.g. via SSH or Docker) to kill the bot process.
  3. Automatically starts the bot again.
  4. Bot wakes up clean and ready to work.

E. Web Dashboard (express)

The index.js file also runs a web server.

Admin Panel (/admin)

  • Displays statistics (ticket count, support activity).
  • Allows changing settings (Role IDs, Channels) without restarting the bot.
  • Security: Uses bcrypt for passwords and session for login.

Transcripts (/transcripts/:id)

  • Public page (no password) displaying chat history.
  • Pulls data from ticket_messages table.

🗄️ 4. Database (MySQL)

All data is stored in these tables:

Table Description
tickets Main list of tickets (ID, owner, status, channel).
ticket_messages Archive of every message for transcripts.
ticket_status Last time someone wrote (for coloring tickets in panel).
ticket_details Details from form (Server ID, issue description).
ticket_ratings Support ratings from users (stars).
agent_activity Logs when support was "On Duty" or in voice channel.
support_duty Current status of support (On Duty? Yes/No).
panel_users Accounts for logging into admin panel.
settings Dynamic bot settings (editable from web).
tags_meta / tag_usage Statistics on tag usage.

🤖 5. Bot Commands (Slash Commands)

  • /ticketpanel: Creates a panel with buttons for creating tickets.
  • /help: Lists commands.
  • /closerequest: Requests ticket closure (with reason and delay).
  • /wipeclosed: Immediately deletes all channels in "Closed" category.
  • /onduty / /offduty: Toggles your status (counts towards payroll/stats).
  • /agentstatus: Shows how many hours you worked this week.
  • /switchpanel: Switches ticket category (e.g. from "Ark" to "Billing") and renames channel.
  • /tag [name]: Sends a pre-prepared response.
  • /notes: Creates a private thread in the ticket for support notes.
  • /forceerror: (Test only) Simulates a bot error.
  • /forcereset: (Owner only) Restarts bot via API.

🔌 API Usage & Integrations

This document details how the bot communicates with the outside world. We don't use "black-box" libraries for API calls – we manage everything ourselves using native Node.js modules (https, http) for maximum control and speed.

🧠 1. OpenAI (ChatGPT) Integration

This is the brain of the bot for text generation. We don't want the bot to sound robotic, but it also mustn't "hallucinate" technical solutions.

A. The Wrapper (openaiChat)

Instead of installing a huge openai library, we use a lightweight function that sends a clean HTTP POST request.

How it works:

  1. Builds JSON with model and parameters (temperature, penalty).
  2. Sends request to api.openai.com.
  3. Returns only the clean text response.
async function openaiChat(messages) {
  // 1. Prepare request body
  var body = JSON.stringify({
    model: config.openaiModel, // e.g. "gpt-3.5-turbo"
    messages: messages,        // Conversation history
    temperature: 0.9           // Creativity
  })

  // 2. Send request (Native HTTPS)
  return await new Promise(function(resolve){
    var req = https.request({ 
      method: 'POST', 
      hostname: 'api.openai.com', 
      path: '/v1/chat/completions', 
      headers: { 
        'Authorization': 'Bearer ' + config.openaiApiKey 
      } 
    }, function(res){
      // ... process response ...
    })
    req.write(body)
    req.end()
  })
}

B. Safety & Sanitization (sanitizeAndValidateAi)

AI likes to give advice, even when it shouldn't. We want humans to provide support. Therefore, the bot has a "censor".

Logic:

  1. Checks if text contains banned words (fix, solution, try to).
  2. If yes -> Discards it and tries again or uses fallback.
  3. Removes Markdown code blocks (so bot doesn't send code).
function sanitizeAndValidateAi(text) {
  var lower = text.toLowerCase()
  // Banned words implying technical advice
  var banned = ['fix', 'resolve', 'solution', 'steps', 'guide', 'you can do']
  
  for (var i=0; i<banned.length; i++) { 
    if (lower.indexOf(banned[i]) >= 0) { 
      console.warn('AI output violated no-solutions rule')
      return null // Reject response
    } 
  }
  return text.trim()
}

C. Uniqueness Engine (getUniqueAi)

To prevent the bot from sending the same thing to everyone ("Hello, how can I help?"), we have a uniqueness system.

How it works:

  1. Generates a response.
  2. Creates a Hash (fingerprint) of it.
  3. Checks memory (aiRecent) if we sent this recently.
  4. If yes -> Discards it and tries generating another variant (up to 6 attempts).
async function getUniqueAi(type, fn){
  for (var attempt=0; attempt<6; attempt++) {
    var out = await fn() // Call AI
    var h = hashText(out) // Create hash
    if (seen(type, h)) continue // If seen, try again
    
    remember(type, h) // Remember new phrase
    return out
  }
  return null // Fallback
}

🚑 2. The CPR API (External Monitoring)

The bot doesn't run in a vacuum. It is connected to an external "Mothership" server (136.243.104.68) that watches over its health.

A. Error Reporting (sendErrorReport)

When an error occurs (e.g. Discord API fails), the bot "snitches" to the monitoring server.

// Sending error to external API
var req = http.request({
  method: 'POST',
  hostname: '136.243.104.68',
  port: 50030,
  path: '/api/error/' + codeVal, // E.g. /api/error/404
}, function(res){})
req.write(JSON.stringify({ message: fullMsg }))
req.end()

B. The Kill Switch (Remote Restart)

If the bot detects a critical error (Error 1006 - Connection Reset), it knows it's over. Instead of "hanging", it calls for a restart.

Why external API? Because the bot cannot restart itself (a process cannot turn itself on when it is off).

client.on('error', async function(err){
  if (err.code === 1006) {
    // "Hello, I'm stuck. Restart me!"
    http.get('http://136.243.104.68:50030/restart', ...) 
  }
})

🖼️ 3. Discord API (Eris & Attachments)

Here is an example of how we work with the Discord API, specifically file uploads (Logo), which was a key requirement.

Direct File Upload

Instead of sending a URL link (which can expire or be slow), we send the image binary data directly in the request body.

// Load file into memory at start (Buffer)
var logoBuffer = fs.readFileSync(__dirname + '/public/icon.png')

// Sending message with attachment
await client.createMessage(channelId, { 
  embeds: [{ 
    title: 'Hello',
    // Reference attachment using internal protocol
    thumbnail: { url: 'attachment://icon.png' } 
  }] 
}, { 
  file: logoBuffer, // Binary data
  name: 'icon.png'  // Filename in attachment
})

This way, the logo loads instantly and doesn't depend on any external webserver.


Status API Reference

Base URL: http://136.243.104.68:50030/

Status Model

  • status: overall state (Green green, Orange orange, Red red)
  • heartbeatStatus: monitor/SSH state (Green green, Red red)
  • botStatus: derived from consecutive failures (Green green, Orange orange, Red red)
  • lastCheck: ISO timestamp
  • isResetting: restart in progress flag

Endpoints

GET /api/status

Returns the current overall status and heartbeat component state.

Response example:

{
  "status": "green",
  "heartbeatStatus": "green",
  "botStatus": "green",
  "lastCheck": "2025-01-01T12:00:00.000Z",
  "isResetting": false,
  "consecutiveFailures": 0
}

GET /api/config

Returns UI configuration values consumed by the frontend.

Response example:

{ "iconPath": "/assets/icon.png" }

GET /api/history/:year/:month

Returns daily summary for the month (for calendar/history views).

  • Params: year (YYYY), month (MM 1–12)

Response example:

[
  { "date": "2025-12-01", "status": "green" },
  { "date": "2025-12-02", "status": "red" }
]

GET /api/day/:date

Returns all records for a specific day.

  • Params: date (YYYY-MM-DD)

Response example:

[
  { "id": 10, "type": "restart_request", "status": "red", "code": 1006, "message": "API Restart Requested", "timestamp": "2025-12-02T08:15:00Z" },
  { "id": 11, "type": "restart_complete", "status": "green", "code": null, "message": "Restart Completed Successfully", "timestamp": "2025-12-02T08:16:15Z" }
]

GET /api/incidents

Returns recent incidents (Orange orange or Red red, newest first). Use on History page.

Response example:

[
  { "id": 21, "type": "heartbeat_fail", "status": "orange", "code": 1006, "message": "Connection Failed (Attempt 10)", "timestamp": "2025-12-05T11:00:00Z" },
  { "id": 22, "type": "cpr_start", "status": "red", "code": 1006, "message": "CPR (Forced Restart) Initiated", "timestamp": "2025-12-05T11:05:00Z" }
]

GET /api/errors/:code

Returns recent incidents for a specific error code.

  • Params: code (e.g., 401, 1006, 499)

Response example:

[
  { "id": 31, "type": "reported_error", "status": "orange", "code": 401, "message": "Switch Panel Failed", "timestamp": "2025-12-09T09:30:00Z" }
  ]

POST /api/error/:code

Reports a non-restart error from external systems. Treated as Orange orange (degraded).

Body:

{ "message": "Optional detailed error description" }

POST or GET /restart

Initiates a forced restart (CPR) when the bot is unreachable (1006). Red Red status is reserved for this action.

Response example: 202 Accepted { "status": "accepted", "message": "Reset sequence initiated" }

Error Codes

  • 1006 Critical Connection Reset (critical, restart-triggering)
  • 401 Warning Switch Panel Failed
  • 402 Warning Thread Create Failed
  • 403 Warning Voice Update Failed
  • 404 Warning Roster Update Failed
  • 405 Warning Interaction Failed
  • 499 Warning General Error