cactus-coin/index.js

343 lines
10 KiB
JavaScript

const config = require("./config.json");
const Discord = require("discord.js");
const express = require("express");
const rest = new Discord.REST({
version: '10'
}).setToken(config.discord.token);
const fs = require("fs");
const path = require("path");
const colors = require("colors");
const client = new Discord.Client({
intents: [
"GuildMessages",
"GuildMembers",
"Guilds"
]
});
const app = express();
// Use sqlite3 for object storage, and create a database if it doesn't exist
const sqlite3 = require("sqlite3").verbose();
const db = new sqlite3.Database("./levels.db");
// Create table if it doesn't exist
db.run("CREATE TABLE IF NOT EXISTS levels (id TEXT, xp INTEGER, lvl INTEGER, totalXp INTEGER, msgCount INTEGER, tag TEXT)");
// update table if it does exist
// Check if tag column exists in the levels table
db.all("PRAGMA table_info(levels)", async (err, rows) => {
// Check if tag column exists
if (rows.filter(row => row.name === "tag").length === 0) {
// Add tag column
await db.run("ALTER TABLE levels ADD COLUMN tag TEXT");
}
});
client.on("ready", async () => {
console.log(`${colors.cyan("[INFO]")} Logged in as ${colors.green(client.user.tag)}`)
// Load Commands
console.log(`${colors.cyan("[INFO]")} Loading Commands...`)
const commands = require('./commands.json');
await (async () => {
try {
console.log(`${colors.cyan("[INFO]")} Registering Commands...`)
let start = Date.now()
// For every guild
for (const guild of client.guilds.cache.values()) {
let gStart = Date.now();
console.log(`${colors.cyan("[INFO]")} Registering Commands for ${colors.green(guild.name)}...`);
// Register commands
await rest.put(
Discord.Routes.applicationGuildCommands(client.user.id, guild.id), {
body: commands
},
);
console.log(`${colors.cyan("[INFO]")} Successfully registered commands for ${colors.green(guild.name)}. Took ${colors.green((Date.now() - gStart) / 1000)} seconds.`);
};
console.log(`${colors.cyan("[INFO]")} Successfully registered commands. Took ${colors.green((Date.now() - start) / 1000)} seconds.`);
} catch (error) {
console.error(error);
}
})();
// Log startup time in seconds
console.log(`${colors.cyan("[INFO]")} Startup took ${colors.green((Date.now() - initTime) / 1000)} seconds.`)
});
client.on("messageCreate", async message => {
if (message.author.bot) return;
if (message.channel.type === "DM") return;
if (config.discord.levels.blacklist.includes(message.channel.id)) return;
if (config.discord.levels.blacklist.includes(message.channel.parentId)) return;
// Calculate random xp
let xp = Math.floor(Math.random() * 10) + 15;
// If user is not in database, add them, {user: {xp = xp, lvl = 1, totalXp: xp, msgCount = 1}}
await db.get(`SELECT * FROM levels WHERE id = '${message.author.id}'`, async (err, row) => {
if (err) {
console.error(err);
}
if (!row) {
await db.run(`INSERT INTO levels (id, xp, lvl, totalXp, msgCount, tag) VALUES ('${message.author.id}', ${xp}, 1, ${xp}, 1, '${message.author.tag}')`); // Add user to database
}
});
// Get user data
await db.get(`SELECT * FROM levels WHERE id = '${message.author.id}'`, async (err, row) => {
if (err) {
console.error(err);
}
if (row) {
var data = row;
let lvl = data.lvl;
data.msgCount++;
// Cooldown
if (cooldowns[message.author.id] && new Date() - cooldowns[message.author.id] < config.discord.levels.cooldownMinutes * 60 * 1000) return await db.run(`UPDATE levels SET xp = ${data.xp}, lvl = ${data.lvl}, totalXp = ${data.totalXp}, msgCount = ${data.msgCount} WHERE id = '${message.author.id}'`);
cooldowns[message.author.id] = new Date();
data.xp += xp;
data.totalXp += xp;
// If user is in database, and xp is greater than or equal to the calculated level up XP, add 1 to lvl and add the remainder to xp
let lvlUpXp = eval(config.discord.levels.lvlUpEquation);
// Keep running level up equation until xp is less than the calculated level up xp
while (data.xp >= lvlUpXp) {
data.lvl++;
data.xp -= lvlUpXp;
lvlUpXp = eval(config.discord.levels.lvlUpEquation);
// use config.discord.levels.lvlUpMessage to send a message when the user levels up
message.channel.send(config.discord.levels.lvlUpMessage.replace("{user}", `<@${message.author.id}>`).replace("{lvl}", data.lvl)).then(msg => {
setTimeout(() => {
msg.delete();
}, 10000);
});
}
// Update database
await db.run(`UPDATE levels SET xp = ${data.xp}, lvl = ${data.lvl}, totalXp = ${data.totalXp}, msgCount = ${data.msgCount}, tag = '${message.author.tag}' WHERE id = '${message.author.id}'`);
}
});
});
client.on("interactionCreate", async interaction => {
if (!interaction.isCommand()) return;
switch (interaction.commandName) {
case "rank":
var user;
if (interaction.options.getMember("user")) {
user = interaction.options.getMember("user").user;
} else {
user = interaction.user;
}
// Get user data
await db.get(`SELECT * FROM levels WHERE id = '${user.id}'`, async (err, row) => {
if (err) {
console.error(err);
}
if (!row) return interaction.reply({
content: "This user has not sent any messages yet.",
ephemeral: true
});
if (row) {
var data = row;
let lvl = data.lvl;
let rank;
// Calculate rank
await db.all(`SELECT * FROM levels ORDER BY totalXp DESC`, async (err, rows) => {
if (err) {
console.error(err);
}
if (rows) {
let rank = 0;
for (let i = 0; i < rows.length; i++) {
if (rows[i].id === user.id) {
rank = i + 1;
break;
}
}
interaction.reply({
embeds: [{
title: `${user.tag}'s Rank`,
fields: [{
name: "Rank",
value: `#${rank}`,
inline: true
},
{
name: "Level",
value: data.lvl,
inline: true
},
{
name: "XP",
value: `${data.xp}/${eval(config.discord.levels.lvlUpEquation)}`,
},
{
name: "Total XP",
value: data.totalXp,
inline: true
},
{
name: "Messages Sent",
value: data.msgCount,
inline: true
}
],
color: 0x00ff00
}]
})
}
});
}
});
break;
case "leaderboard":
await db.all(`SELECT * FROM levels ORDER BY totalXp DESC`, async (err, rows) => {
if (err) {
console.error(err);
}
if (!rows) return interaction.reply({
content: "No one has sent any messages yet.",
ephemeral: true
});
if (rows) {
let leaderboard = [];
// Top 10
for (let i = 0; i < 10; i++) {
if (rows[i]) {
let user = await client.users.fetch(rows[i].id);
let lvl = rows[i].lvl;
leaderboard.push(`${i + 1}. <@${user.id}> - ${rows[i].xp}/${eval(config.discord.levels.lvlUpEquation)} L${rows[i].lvl} - ${rows[i].totalXp} XP - ${rows[i].msgCount} Messages`);
}
}
interaction.reply({
embeds: [{
title: "Leaderboard",
description: leaderboard.join("\n"),
color: 0x00ff00
}]
});
}
});
break;
case "givexp":
// Dont gotta check perms, done on discord
// Dont gotta check arguments, done on discord
// Get user data
await db.get(`SELECT * FROM levels WHERE id = '${interaction.options.getUser("user").id}'`, async (err, row) => {
if (err) {
console.error(err);
}
if (!row) return interaction.reply({ content: "This user has not sent any messages yet.", ephemeral: true });
if (row) {
var data = row;
let lvl = data.lvl;
data.xp += interaction.options.getInteger("amount");
data.totalXp += interaction.options.getInteger("amount");
// If user is in database, and xp is greater than or equal to the calculated level up XP, add 1 to lvl and add the remainder to xp
let lvlUpXp = eval(config.discord.levels.lvlUpEquation);
// Keep running level up equation until xp is less than the calculated level up xp
while (data.xp >= lvlUpXp) {
data.lvl++;
data.xp -= lvlUpXp;
lvlUpXp = eval(config.discord.levels.lvlUpEquation);
}
// Update database
await db.run(`UPDATE levels SET xp = ${data.xp}, lvl = ${data.lvl}, totalXp = ${data.totalXp}, msgCount = ${data.msgCount} WHERE id = '${interaction.options.getUser("user").id}'`);
interaction.reply({
content: `Gave ${interaction.options.getInteger("amount")} XP to ${interaction.options.getUser("user").tag}!`,
ephemeral: true
});
}
});
break;
};
});
app.get("/api/levels", async (req, res) => {
// Pretty much send the entire database
await db.all(`SELECT * FROM levels ORDER BY totalXp DESC`, async (err, rows) => {
if (err) {
console.error(err);
return res.sendStatus(500); // Internal server error
}
if (!rows) return res.sendStatus(204) // No content
if (rows) {
let output = row;
if (!output.tag) output.tag = "Unknown#0000";
return res.json(output);
}
});
});
app.get("/api/levels/:id", async (req, res) => {
// Get user data
await db.get(`SELECT * FROM levels WHERE id = '${req.params.id}'`, async (err, row) => {
if (err) {
console.error(err);
return res.sendStatus(500); // Internal server error
}
if (!row) return res.sendStatus(404) // Not found
if (row) {
let output = row;
if (!output.tag) output.tag = "Unknown#0000";
return res.json(output);
}
});
});
// Handle SIGINT gracefully
process.on('SIGINT', async () => {
await console.log(`${colors.cyan("[INFO]")} Stop received, exiting...`);
await client.user.setPresence({
status: "invisible",
activities: []
});
await client.destroy();
await console.log(`${colors.cyan("[INFO]")} Goodbye!`);
process.exit(0);
});
// Global error handler
/*process.on('uncaughtException', async (error) => {
await console.error(`${colors.red("[ERROR]")} Uncaught Exception: ${error}`);
if (client.user.tag) {
client.channels.fetch(config.discord.errorChannel).then(async channel => {
await channel.send({
embeds: [{
title: "Uncaught Exception",
description: `\`\`\`${error}\`\`\``,
color: 0xff0000
}]
});
});
}
});*/
if (config.api.enabled) {
// Start API
app.listen(config.api.port, () => {
console.log(`${colors.cyan("[INFO]")} API listening on port ${config.api.port}`);
});
}
// Global Variables
var cooldowns = {};
console.log(`${colors.cyan("[INFO]")} Starting...`)
// Start timer to see how long startup takes
const initTime = Date.now()
// Login to Discord
client.login(config.discord.token);