Add command logging and real-time dashboard updates
Integrates PostgreSQL for command logging and analytics, adds WebSocket support for real-time dashboard updates, and includes `pg` and `ws` dependencies. Replit-Commit-Author: Agent Replit-Commit-Session-Id: aed2e46d-25bb-4b73-81a1-bb9e8437c261 Replit-Commit-Checkpoint-Type: intermediate_checkpoint Replit-Commit-Event-Id: 2d4b80d0-ca85-407f-99c3-f3596476f525 Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/3bdfff67-975a-46ad-9845-fbb6b4a4c4b5/aed2e46d-25bb-4b73-81a1-bb9e8437c261/4ZEVdt6 Replit-Helium-Checkpoint-Created: true
This commit is contained in:
parent
32e44245df
commit
bc8a04825b
4 changed files with 517 additions and 12 deletions
4
.replit
4
.replit
|
|
@ -22,10 +22,6 @@ externalPort = 80
|
||||||
localPort = 8080
|
localPort = 8080
|
||||||
externalPort = 8080
|
externalPort = 8080
|
||||||
|
|
||||||
[[ports]]
|
|
||||||
localPort = 38859
|
|
||||||
externalPort = 3000
|
|
||||||
|
|
||||||
[workflows]
|
[workflows]
|
||||||
runButton = "Project"
|
runButton = "Project"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,8 @@ const { createClient } = require("@supabase/supabase-js");
|
||||||
const http = require("http");
|
const http = require("http");
|
||||||
const fs = require("fs");
|
const fs = require("fs");
|
||||||
const path = require("path");
|
const path = require("path");
|
||||||
|
const { Pool } = require("pg");
|
||||||
|
const WebSocket = require("ws");
|
||||||
|
|
||||||
// Dashboard HTML path
|
// Dashboard HTML path
|
||||||
const dashboardPath = path.join(__dirname, "public", "dashboard.html");
|
const dashboardPath = path.join(__dirname, "public", "dashboard.html");
|
||||||
|
|
@ -66,6 +68,181 @@ if (process.env.SUPABASE_URL && process.env.SUPABASE_SERVICE_ROLE) {
|
||||||
console.log("Supabase not configured - community features will be limited");
|
console.log("Supabase not configured - community features will be limited");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// POSTGRESQL DATABASE SETUP (Local database for analytics & config)
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
let pgPool = null;
|
||||||
|
if (process.env.DATABASE_URL) {
|
||||||
|
pgPool = new Pool({
|
||||||
|
connectionString: process.env.DATABASE_URL,
|
||||||
|
max: 10,
|
||||||
|
idleTimeoutMillis: 30000,
|
||||||
|
connectionTimeoutMillis: 2000,
|
||||||
|
});
|
||||||
|
pgPool.on('error', (err) => {
|
||||||
|
console.error('PostgreSQL pool error:', err.message);
|
||||||
|
});
|
||||||
|
console.log("PostgreSQL connected");
|
||||||
|
} else {
|
||||||
|
console.log("PostgreSQL not configured - using in-memory storage");
|
||||||
|
}
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// COMMAND LOGGING SYSTEM
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
async function logCommand(data) {
|
||||||
|
if (!pgPool) return;
|
||||||
|
try {
|
||||||
|
await pgPool.query(
|
||||||
|
`INSERT INTO command_logs (command_name, user_id, user_tag, guild_id, guild_name, channel_id, success, error_message, execution_time_ms)
|
||||||
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)`,
|
||||||
|
[
|
||||||
|
data.commandName,
|
||||||
|
data.userId,
|
||||||
|
data.userTag,
|
||||||
|
data.guildId,
|
||||||
|
data.guildName,
|
||||||
|
data.channelId,
|
||||||
|
data.success,
|
||||||
|
data.errorMessage || null,
|
||||||
|
data.executionTime || null
|
||||||
|
]
|
||||||
|
);
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Failed to log command:', err.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getCommandAnalytics(days = 7) {
|
||||||
|
if (!pgPool) return { commands: [], hourly: [], daily: [], topUsers: [] };
|
||||||
|
try {
|
||||||
|
const commandsResult = await pgPool.query(
|
||||||
|
`SELECT command_name, COUNT(*) as count,
|
||||||
|
SUM(CASE WHEN success THEN 1 ELSE 0 END) as success_count,
|
||||||
|
AVG(execution_time_ms) as avg_time
|
||||||
|
FROM command_logs
|
||||||
|
WHERE created_at > NOW() - INTERVAL '${days} days'
|
||||||
|
GROUP BY command_name
|
||||||
|
ORDER BY count DESC
|
||||||
|
LIMIT 20`
|
||||||
|
);
|
||||||
|
|
||||||
|
const hourlyResult = await pgPool.query(
|
||||||
|
`SELECT EXTRACT(HOUR FROM created_at) as hour, COUNT(*) as count
|
||||||
|
FROM command_logs
|
||||||
|
WHERE created_at > NOW() - INTERVAL '24 hours'
|
||||||
|
GROUP BY hour
|
||||||
|
ORDER BY hour`
|
||||||
|
);
|
||||||
|
|
||||||
|
const dailyResult = await pgPool.query(
|
||||||
|
`SELECT DATE(created_at) as date, COUNT(*) as count
|
||||||
|
FROM command_logs
|
||||||
|
WHERE created_at > NOW() - INTERVAL '${days} days'
|
||||||
|
GROUP BY date
|
||||||
|
ORDER BY date`
|
||||||
|
);
|
||||||
|
|
||||||
|
const topUsersResult = await pgPool.query(
|
||||||
|
`SELECT user_id, user_tag, COUNT(*) as command_count
|
||||||
|
FROM command_logs
|
||||||
|
WHERE created_at > NOW() - INTERVAL '${days} days'
|
||||||
|
GROUP BY user_id, user_tag
|
||||||
|
ORDER BY command_count DESC
|
||||||
|
LIMIT 10`
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
commands: commandsResult.rows,
|
||||||
|
hourly: hourlyResult.rows,
|
||||||
|
daily: dailyResult.rows,
|
||||||
|
topUsers: topUsersResult.rows
|
||||||
|
};
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Failed to get command analytics:', err.message);
|
||||||
|
return { commands: [], hourly: [], daily: [], topUsers: [] };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getTotalCommandCount() {
|
||||||
|
if (!pgPool) return 0;
|
||||||
|
try {
|
||||||
|
const result = await pgPool.query('SELECT COUNT(*) as count FROM command_logs');
|
||||||
|
return parseInt(result.rows[0].count) || 0;
|
||||||
|
} catch (err) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PostgreSQL-based server config functions
|
||||||
|
async function saveServerConfigToDB(guildId, config) {
|
||||||
|
if (!pgPool) return false;
|
||||||
|
try {
|
||||||
|
await pgPool.query(
|
||||||
|
`INSERT INTO server_config (guild_id, welcome_channel, goodbye_channel, modlog_channel, level_up_channel, auto_role, verified_role, updated_at)
|
||||||
|
VALUES ($1, $2, $3, $4, $5, $6, $7, NOW())
|
||||||
|
ON CONFLICT (guild_id) DO UPDATE SET
|
||||||
|
welcome_channel = EXCLUDED.welcome_channel,
|
||||||
|
goodbye_channel = EXCLUDED.goodbye_channel,
|
||||||
|
modlog_channel = EXCLUDED.modlog_channel,
|
||||||
|
level_up_channel = EXCLUDED.level_up_channel,
|
||||||
|
auto_role = EXCLUDED.auto_role,
|
||||||
|
verified_role = EXCLUDED.verified_role,
|
||||||
|
updated_at = NOW()`,
|
||||||
|
[guildId, config.welcome_channel, config.goodbye_channel, config.modlog_channel,
|
||||||
|
config.level_up_channel, config.auto_role, config.verified_role]
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Failed to save server config:', err.message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getServerConfigFromDB(guildId) {
|
||||||
|
if (!pgPool) return null;
|
||||||
|
try {
|
||||||
|
const result = await pgPool.query(
|
||||||
|
'SELECT * FROM server_config WHERE guild_id = $1',
|
||||||
|
[guildId]
|
||||||
|
);
|
||||||
|
return result.rows[0] || null;
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Failed to get server config:', err.message);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Federation mappings with PostgreSQL
|
||||||
|
async function saveFederationMappingToDB(guildId, roleId, roleName) {
|
||||||
|
if (!pgPool) return false;
|
||||||
|
try {
|
||||||
|
await pgPool.query(
|
||||||
|
`INSERT INTO federation_mappings (guild_id, role_id, role_name, linked_at)
|
||||||
|
VALUES ($1, $2, $3, NOW())
|
||||||
|
ON CONFLICT (guild_id, role_id) DO UPDATE SET role_name = EXCLUDED.role_name`,
|
||||||
|
[guildId, roleId, roleName]
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Failed to save federation mapping:', err.message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getFederationMappingsFromDB() {
|
||||||
|
if (!pgPool) return [];
|
||||||
|
try {
|
||||||
|
const result = await pgPool.query('SELECT * FROM federation_mappings ORDER BY linked_at DESC');
|
||||||
|
return result.rows;
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Failed to get federation mappings:', err.message);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
// SENTINEL: HEAT TRACKING SYSTEM (New)
|
// SENTINEL: HEAT TRACKING SYSTEM (New)
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
|
|
@ -588,24 +765,47 @@ client.on("interactionCreate", async (interaction) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const queueEntry = addToCommandQueue(`/${interaction.commandName} by ${interaction.user.tag}`, 'pending');
|
const queueEntry = addToCommandQueue(`/${interaction.commandName} by ${interaction.user.tag}`, 'pending');
|
||||||
|
const startTime = Date.now();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
console.log(`[Command] Executing: ${interaction.commandName}`);
|
console.log(`[Command] Executing: ${interaction.commandName}`);
|
||||||
await command.execute(interaction, supabase, client);
|
await command.execute(interaction, supabase, client);
|
||||||
console.log(`[Command] Completed: ${interaction.commandName}`);
|
const executionTime = Date.now() - startTime;
|
||||||
|
console.log(`[Command] Completed: ${interaction.commandName} (${executionTime}ms)`);
|
||||||
|
|
||||||
updateCommandQueue(queueEntry.id, 'completed');
|
updateCommandQueue(queueEntry.id, 'completed');
|
||||||
trackCommand(interaction.commandName);
|
trackCommand(interaction.commandName);
|
||||||
resetDailyAnalytics();
|
resetDailyAnalytics();
|
||||||
|
|
||||||
addActivity('command', {
|
const activityData = {
|
||||||
command: interaction.commandName,
|
command: interaction.commandName,
|
||||||
user: interaction.user.tag,
|
user: interaction.user.tag,
|
||||||
userId: interaction.user.id,
|
userId: interaction.user.id,
|
||||||
guild: interaction.guild?.name || 'DM',
|
guild: interaction.guild?.name || 'DM',
|
||||||
guildId: interaction.guildId,
|
guildId: interaction.guildId,
|
||||||
|
executionTime,
|
||||||
|
};
|
||||||
|
|
||||||
|
addActivity('command', activityData);
|
||||||
|
|
||||||
|
// Log to database
|
||||||
|
logCommand({
|
||||||
|
commandName: interaction.commandName,
|
||||||
|
userId: interaction.user.id,
|
||||||
|
userTag: interaction.user.tag,
|
||||||
|
guildId: interaction.guildId,
|
||||||
|
guildName: interaction.guild?.name || 'DM',
|
||||||
|
channelId: interaction.channelId,
|
||||||
|
success: true,
|
||||||
|
executionTime,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Broadcast via WebSocket
|
||||||
|
if (typeof wsBroadcast === 'function') {
|
||||||
|
wsBroadcast('command', activityData);
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
const executionTime = Date.now() - startTime;
|
||||||
console.error(`Error executing ${interaction.commandName}:`, error);
|
console.error(`Error executing ${interaction.commandName}:`, error);
|
||||||
|
|
||||||
updateCommandQueue(queueEntry.id, 'failed');
|
updateCommandQueue(queueEntry.id, 'failed');
|
||||||
|
|
@ -617,6 +817,19 @@ client.on("interactionCreate", async (interaction) => {
|
||||||
error: error.message,
|
error: error.message,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Log failed command to database
|
||||||
|
logCommand({
|
||||||
|
commandName: interaction.commandName,
|
||||||
|
userId: interaction.user.id,
|
||||||
|
userTag: interaction.user.tag,
|
||||||
|
guildId: interaction.guildId,
|
||||||
|
guildName: interaction.guild?.name || 'DM',
|
||||||
|
channelId: interaction.channelId,
|
||||||
|
success: false,
|
||||||
|
errorMessage: error.message,
|
||||||
|
executionTime,
|
||||||
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const errorEmbed = new EmbedBuilder()
|
const errorEmbed = new EmbedBuilder()
|
||||||
.setColor(0xff0000)
|
.setColor(0xff0000)
|
||||||
|
|
@ -758,8 +971,7 @@ const checkAdminAuth = (req) => {
|
||||||
return authHeader === `Bearer ${ADMIN_TOKEN}`;
|
return authHeader === `Bearer ${ADMIN_TOKEN}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
http
|
const httpServer = http.createServer((req, res) => {
|
||||||
.createServer((req, res) => {
|
|
||||||
res.setHeader("Access-Control-Allow-Origin", "*");
|
res.setHeader("Access-Control-Allow-Origin", "*");
|
||||||
res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
|
res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
|
||||||
res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
|
res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
|
||||||
|
|
@ -1988,14 +2200,111 @@ http
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Analytics endpoint with detailed command data
|
||||||
|
if (req.url === "/command-analytics" || req.url.startsWith("/command-analytics?")) {
|
||||||
|
(async () => {
|
||||||
|
try {
|
||||||
|
const url = new URL(req.url, `http://localhost:${healthPort}`);
|
||||||
|
const days = parseInt(url.searchParams.get('days') || '7');
|
||||||
|
const analytics = await getCommandAnalytics(days);
|
||||||
|
const totalCount = await getTotalCommandCount();
|
||||||
|
|
||||||
|
res.writeHead(200);
|
||||||
|
res.end(JSON.stringify({
|
||||||
|
success: true,
|
||||||
|
totalCommands: totalCount,
|
||||||
|
analytics,
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
}));
|
||||||
|
} catch (error) {
|
||||||
|
res.writeHead(500);
|
||||||
|
res.end(JSON.stringify({ success: false, error: error.message }));
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
res.writeHead(404);
|
res.writeHead(404);
|
||||||
res.end(JSON.stringify({ error: "Not found" }));
|
res.end(JSON.stringify({ error: "Not found" }));
|
||||||
})
|
|
||||||
.listen(healthPort, () => {
|
|
||||||
console.log(`Health check server running on port ${healthPort}`);
|
|
||||||
console.log(`Register commands at: POST http://localhost:${healthPort}/register-commands`);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// WEBSOCKET SERVER FOR REAL-TIME UPDATES
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
const wsClients = new Set();
|
||||||
|
const wss = new WebSocket.Server({ noServer: true });
|
||||||
|
|
||||||
|
wss.on('connection', (ws) => {
|
||||||
|
wsClients.add(ws);
|
||||||
|
console.log(`[WebSocket] Client connected. Total: ${wsClients.size}`);
|
||||||
|
|
||||||
|
ws.send(JSON.stringify({
|
||||||
|
type: 'init',
|
||||||
|
data: {
|
||||||
|
status: 'online',
|
||||||
|
guilds: client.guilds.cache.size,
|
||||||
|
commands: client.commands?.size || 0,
|
||||||
|
uptime: Math.floor(process.uptime()),
|
||||||
|
heatMapSize: heatMap.size,
|
||||||
|
activeTickets: activeTickets.size,
|
||||||
|
federationLinks: federationMappings.size,
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
ws.on('close', () => {
|
||||||
|
wsClients.delete(ws);
|
||||||
|
console.log(`[WebSocket] Client disconnected. Total: ${wsClients.size}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
ws.on('error', (err) => {
|
||||||
|
console.error('[WebSocket] Error:', err.message);
|
||||||
|
wsClients.delete(ws);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function wsBroadcast(type, data) {
|
||||||
|
const message = JSON.stringify({ type, data, timestamp: new Date().toISOString() });
|
||||||
|
for (const wsClient of wsClients) {
|
||||||
|
if (wsClient.readyState === WebSocket.OPEN) {
|
||||||
|
try {
|
||||||
|
wsClient.send(message);
|
||||||
|
} catch (err) {
|
||||||
|
console.error('[WebSocket] Broadcast error:', err.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setInterval(() => {
|
||||||
|
if (wsClients.size > 0) {
|
||||||
|
wsBroadcast('stats', {
|
||||||
|
guilds: client.guilds.cache.size,
|
||||||
|
commands: client.commands?.size || 0,
|
||||||
|
uptime: Math.floor(process.uptime()),
|
||||||
|
heatMapSize: heatMap.size,
|
||||||
|
activeTickets: activeTickets.size,
|
||||||
|
federationLinks: federationMappings.size,
|
||||||
|
memory: process.memoryUsage(),
|
||||||
|
cpu: getCpuUsage(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, 5000);
|
||||||
|
|
||||||
|
// Add WebSocket upgrade handling
|
||||||
|
httpServer.on('upgrade', (request, socket, head) => {
|
||||||
|
wss.handleUpgrade(request, socket, head, (ws) => {
|
||||||
|
wss.emit('connection', ws, request);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
httpServer.listen(healthPort, () => {
|
||||||
|
console.log(`Health check server running on port ${healthPort}`);
|
||||||
|
console.log(`WebSocket server available at ws://localhost:${healthPort}`);
|
||||||
|
console.log(`Register commands at: POST http://localhost:${healthPort}/register-commands`);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
// BOT LOGIN AND READY
|
// BOT LOGIN AND READY
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
|
|
|
||||||
184
package-lock.json
generated
Normal file
184
package-lock.json
generated
Normal file
|
|
@ -0,0 +1,184 @@
|
||||||
|
{
|
||||||
|
"name": "workspace",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"name": "workspace",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"pg": "^8.16.3",
|
||||||
|
"ws": "^8.18.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/pg": {
|
||||||
|
"version": "8.16.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/pg/-/pg-8.16.3.tgz",
|
||||||
|
"integrity": "sha512-enxc1h0jA/aq5oSDMvqyW3q89ra6XIIDZgCX9vkMrnz5DFTw/Ny3Li2lFQ+pt3L6MCgm/5o2o8HW9hiJji+xvw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"pg-connection-string": "^2.9.1",
|
||||||
|
"pg-pool": "^3.10.1",
|
||||||
|
"pg-protocol": "^1.10.3",
|
||||||
|
"pg-types": "2.2.0",
|
||||||
|
"pgpass": "1.0.5"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 16.0.0"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"pg-cloudflare": "^1.2.7"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"pg-native": ">=3.0.1"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"pg-native": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/pg-cloudflare": {
|
||||||
|
"version": "1.2.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.2.7.tgz",
|
||||||
|
"integrity": "sha512-YgCtzMH0ptvZJslLM1ffsY4EuGaU0cx4XSdXLRFae8bPP4dS5xL1tNB3k2o/N64cHJpwU7dxKli/nZ2lUa5fLg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"node_modules/pg-connection-string": {
|
||||||
|
"version": "2.9.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.9.1.tgz",
|
||||||
|
"integrity": "sha512-nkc6NpDcvPVpZXxrreI/FOtX3XemeLl8E0qFr6F2Lrm/I8WOnaWNhIPK2Z7OHpw7gh5XJThi6j6ppgNoaT1w4w==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/pg-int8": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==",
|
||||||
|
"license": "ISC",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/pg-pool": {
|
||||||
|
"version": "3.10.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.10.1.tgz",
|
||||||
|
"integrity": "sha512-Tu8jMlcX+9d8+QVzKIvM/uJtp07PKr82IUOYEphaWcoBhIYkoHpLXN3qO59nAI11ripznDsEzEv8nUxBVWajGg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"pg": ">=8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/pg-protocol": {
|
||||||
|
"version": "1.10.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.10.3.tgz",
|
||||||
|
"integrity": "sha512-6DIBgBQaTKDJyxnXaLiLR8wBpQQcGWuAESkRBX/t6OwA8YsqP+iVSiond2EDy6Y/dsGk8rh/jtax3js5NeV7JQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/pg-types": {
|
||||||
|
"version": "2.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz",
|
||||||
|
"integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"pg-int8": "1.0.1",
|
||||||
|
"postgres-array": "~2.0.0",
|
||||||
|
"postgres-bytea": "~1.0.0",
|
||||||
|
"postgres-date": "~1.0.4",
|
||||||
|
"postgres-interval": "^1.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/pgpass": {
|
||||||
|
"version": "1.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz",
|
||||||
|
"integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"split2": "^4.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/postgres-array": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/postgres-bytea": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/postgres-date": {
|
||||||
|
"version": "1.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz",
|
||||||
|
"integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/postgres-interval": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"xtend": "^4.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/split2": {
|
||||||
|
"version": "4.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",
|
||||||
|
"integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==",
|
||||||
|
"license": "ISC",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.x"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/ws": {
|
||||||
|
"version": "8.18.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz",
|
||||||
|
"integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"bufferutil": "^4.0.1",
|
||||||
|
"utf-8-validate": ">=5.0.2"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"bufferutil": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"utf-8-validate": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/xtend": {
|
||||||
|
"version": "4.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
|
||||||
|
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.4"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
16
package.json
Normal file
16
package.json
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
"name": "workspace",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "This is a starting point for making your own Discord bot using Python and the [discordpy](https://discordpy.readthedocs.io/) library. Read [their getting-started guides](https://discordpy.readthedocs.io/en/stable/#getting-started) to get the most out of this template.",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"pg": "^8.16.3",
|
||||||
|
"ws": "^8.18.3"
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue