Files
SupportBot/Structures/Database.js
T
2026-08-06 17:29:37 +01:00

248 lines
6.5 KiB
JavaScript

// SupportBot | JSON fallback adapter for environments without native addons (Pterodactyl)
const fs = require("fs");
const path = require("path");
const DB_PATH = path.join(__dirname, "../Data/supportbot.json");
function load() {
try {
const raw = fs.readFileSync(DB_PATH, "utf8");
return JSON.parse(raw);
} catch (e) {
return {
suggestions: {},
profiles: {},
tickets: {},
ticket_panel: [],
ticket_users: {},
settings: {},
};
}
}
function save(state) {
try {
fs.mkdirSync(path.dirname(DB_PATH), { recursive: true });
fs.writeFileSync(DB_PATH, JSON.stringify(state, null, 2), "utf8");
} catch (e) {
console.error("Failed to persist database:", e);
}
}
const state = load();
function ensureProfile(userId) {
if (!state.profiles[userId]) {
state.profiles[userId] = { user_id: userId, bio: "", timezone: "", clockedIn: 0 };
save(state);
}
}
function normaliseTicketRow(row) {
if (!row) return null;
let questionAnswers = [];
if (row.questionAnswers) {
try {
questionAnswers = JSON.parse(row.questionAnswers);
} catch {
questionAnswers = [];
}
}
return {
...row,
aiModeEnabled: Boolean(row.aiModeEnabled),
clockedIn: row.clockedIn !== undefined ? Boolean(row.clockedIn) : row.clockedIn,
questionAnswers,
};
}
module.exports = {
addTicket(ticket) {
const now = Date.now();
const id = ticket.id;
state.tickets[id] = state.tickets[id] || {};
const existing = state.tickets[id];
state.tickets[id] = {
ticket_id: id,
user_id: ticket.user,
subject: ticket.reason || "",
description: ticket.description || "",
created_at: existing.created_at || now,
updated_at: now,
department: ticket.department || "general",
priority: ticket.priority || "medium",
aiModeEnabled: ticket.aiModeEnabled ? 1 : 0,
aiChannelId: ticket.aiChannelId || null,
aiType: ticket.aiType || null,
aiEnabledBy: ticket.aiEnabledBy || null,
aiEnabledAt: ticket.aiEnabledAt || null,
questionAnswers: JSON.stringify(ticket.questionAnswers || []),
ticketName: ticket.name || null,
ticketNumber: ticket.number || null,
status: existing.status || "open",
};
save(state);
if (global.apiServer) {
global.apiServer.broadcast("ticket_created", {
ticket_id: id,
user_id: ticket.user,
subject: ticket.reason || "",
description: ticket.description || "",
department: ticket.department || "general",
priority: ticket.priority || "medium",
created_at: now,
});
}
},
updateTicketQuestionAnswers(ticketId, questionAnswers = []) {
const t = state.tickets[ticketId];
if (!t) return;
t.questionAnswers = JSON.stringify(questionAnswers);
t.updated_at = Date.now();
save(state);
},
updateTicketVoice(ticketId, voiceChannelId) {
const t = state.tickets[ticketId];
if (!t) return;
t.voiceChannelId = voiceChannelId;
t.updated_at = Date.now();
save(state);
},
updateTicketStatus(ticketId, status) {
const t = state.tickets[ticketId];
if (!t) return;
t.status = status;
t.updated_at = Date.now();
save(state);
if (global.apiServer && status === "closed") {
global.apiServer.broadcast("ticket_closed", { ticket_id: ticketId });
}
},
updateTicketDepartment(ticketId, department) {
const t = state.tickets[ticketId];
if (!t) return;
t.department = department;
t.updated_at = Date.now();
save(state);
},
updateTicketPriority(ticketId, priority) {
const t = state.tickets[ticketId];
if (!t) return;
t.priority = priority;
t.updated_at = Date.now();
save(state);
},
setTicketAIState(ticketId, aiState = {}) {
const t = state.tickets[ticketId];
if (!t) return;
t.aiModeEnabled = aiState.enabled ? 1 : 0;
t.aiChannelId = aiState.aiChannelId || null;
t.aiType = aiState.aiType || null;
t.aiEnabledBy = aiState.enabledBy || null;
t.aiEnabledAt = aiState.enabledAt ? new Date(aiState.enabledAt).getTime() : aiState.enabled ? Date.now() : null;
t.updated_at = Date.now();
save(state);
},
enableTicketAI(ticketId, aiChannelId = null, aiType = null, enabledBy = null) {
const t = state.tickets[ticketId];
if (!t) return;
t.aiModeEnabled = 1;
t.aiChannelId = aiChannelId;
t.aiType = aiType;
t.aiEnabledBy = enabledBy;
t.aiEnabledAt = Date.now();
t.updated_at = Date.now();
save(state);
},
disableTicketAI(ticketId) {
const t = state.tickets[ticketId];
if (!t) return;
t.aiModeEnabled = 0;
t.updated_at = Date.now();
save(state);
},
getTicket(ticketId) {
return normaliseTicketRow(state.tickets[ticketId]);
},
getTicketByAIChannel(aiChannelId) {
const keys = Object.keys(state.tickets);
for (const k of keys) {
const t = state.tickets[k];
if (t && t.aiChannelId === aiChannelId) return normaliseTicketRow(t);
}
return null;
},
getAllTickets() {
return Object.values(state.tickets).map(normaliseTicketRow);
},
saveTicketPanel(messageId, channelId) {
state.ticket_panel.push({ id: Date.now(), message_id: messageId, channel_id: channelId, created_at: Date.now() });
save(state);
},
getTicketPanel() {
const arr = state.ticket_panel;
if (!arr || arr.length === 0) return null;
return arr[arr.length - 1];
},
addUserToTicket(ticketId, userId) {
state.ticket_users[ticketId] = state.ticket_users[ticketId] || new Set();
// Sets cannot be serialized directly; store as array
const arr = new Set(state.ticket_users[ticketId]);
arr.add(userId);
state.ticket_users[ticketId] = Array.from(arr);
save(state);
},
removeUserFromTicket(ticketId, userId) {
const arr = new Set(state.ticket_users[ticketId] || []);
arr.delete(userId);
state.ticket_users[ticketId] = Array.from(arr);
save(state);
},
getTicketUsers(ticketId) {
return (state.ticket_users[ticketId] || []).slice();
},
getSettings() {
return state.settings || {};
},
getProfile(userId) {
ensureProfile(userId);
const row = state.profiles[userId];
if (!row) return { user_id: userId, bio: "", timezone: "", clockedIn: false };
return { ...row, clockedIn: Boolean(row.clockedIn) };
},
setClockedIn(userId, clockedIn) {
ensureProfile(userId);
state.profiles[userId].clockedIn = clockedIn ? 1 : 0;
save(state);
},
saveSettings(obj) {
state.settings = obj || {};
save(state);
},
};