Welp, thats a start

This commit is contained in:
Christopher Cookman 2025-06-17 00:31:40 -06:00
commit 58b2ee9f9c
16 changed files with 2723 additions and 0 deletions

131
.gitignore vendored Normal file
View file

@ -0,0 +1,131 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional stylelint cache
.stylelintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variable files
.env
.env.*
!.env.example
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next
out
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# vuepress v2.x temp and cache directory
.temp
.cache
# vitepress build output
**/.vitepress/dist
# vitepress cache directory
**/.vitepress/cache
# Docusaurus cache and generated files
.docusaurus
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*
storage/
database.db
snowflakes.*

14
TODO Normal file
View file

@ -0,0 +1,14 @@
# TODO List
## Commands
[X] /add
[X] /remove
[X] /lookup
[X] /serverconfig
[ ] ~~active~~
[X] mode
[X] logchannel
[X] fullscan
- Possibly impliment rate limiting on this?
## Features
[X] Automated actions against users (Configured per-server)

149
commands.js Normal file
View file

@ -0,0 +1,149 @@
const Discord = require("discord.js");
const adminGuildCommands = [
{
name: "add",
description: "Add a bad actor to the list",
options: [
{
name: "user",
type: 6, // USER type
description: "The user to add",
required: true
},
{
name: "comment",
type: 3, // STRING type
description: "Comment, reason, or any other context for the addition",
required: true
},
{
name: "attachment1",
type: 11, // ATTACHMENT type
description: "Optional attachment 1",
required: false
},
{
name: "attachment2",
type: 11,
description: "Optional attachment 2",
required: false
},
{
name: "attachment3",
type: 11,
description: "Optional attachment 3",
required: false
},
{
name: "attachment4",
type: 11,
description: "Optional attachment 4",
required: false
}
]
},
{
name: "remove",
description: "Remove a bad actor from the list",
options: [
{
name: "user",
type: 6, // USER type
description: "The user to remove",
required: true
},
{
name: "confirmation",
type: Discord.ApplicationCommandOptionType.String,
description: "Type 'yes im absolutely sure' to confirm removal",
required: true
}
]
}
]
const globalCommands = [
{
name: "lookup",
description: "Look up a bad actor by their User ID",
options: [
{
name: "user",
type: 6, // USER type
description: "The user to look up",
required: true
}
]
},
{
name: "serverconfig",
description: "Configure server settings for bad actor management",
type: 1, // CHAT_INPUT type
default_member_permissions: Discord.PermissionFlagsBits.Administrator.toString(),
options: [
{
name: "mode",
description: "Set the mode for bad actor management in this server",
type: 1, // SUB_COMMAND type
options: [
{
name: "mode",
type: 3, // STRING type
description: "Choose 'strict' or 'lenient' mode",
required: true,
choices: [
{
name: "Auto-Kick",
value: "kick"
},
{
name: "Auto-Ban",
value: "ban"
},
{
name: "Warn Only",
value: "warn"
},
{
name: "No Action",
value: "none"
}
]
}
]
},
{
name: "logchannel",
description: "Set the channel for logging bad actor actions",
type: 1, // SUB_COMMAND type
options: [
{
name: "channel",
type: 7, // CHANNEL type
description: "The channel to log actions in",
required: true
}
]
},
{
name: "view",
description: "View the current server configuration for bad actor management",
type: 1 // SUB_COMMAND type
},
{
name: "fullscan",
description: "Perform a full scan of the server for bad actors",
type: 1 // SUB_COMMAND type
}
]
}
]
// Define commands
module.exports = {
adminGuildCommands: adminGuildCommands,
globalCommands: globalCommands
}

View file

@ -0,0 +1,77 @@
module.exports = (member, db, client) => {
if (!member.guild) return; // Ignore DMs
if (member.user.bot) return; // Ignore bots
db.get("SELECT * FROM guildConfigs WHERE guildId = ?", [member.guild.id], (err, config) => {
if (err) {
console.error("Database error:", err);
return;
}
if (!config) {
console.warn(`No configuration found for guild ${member.guild.id}`);
return;
}
db.get("SELECT * FROM badActors WHERE id = ?", [member.id], (err, row) => {
if (err) {
console.error("Database error:", err);
return;
}
if (row) {
switch (config.mode) {
case "none":
return; // Literally do nothing lol
break;
case "warn": // Send a warning message in the logChannel
break; // Just break, we send warning for everything else anyways
case "kick": // Kick the user
member.kick("Listed bad actor").then(() => {
client.channels.fetch(config.logChannelId).then(logChan => {
logChan.send({
content: `User <@${member.id}> has been kicked for being a bad actor. Reported by <@${row.reportedBy}>.`
}).catch(console.error);
});
}
).catch(err => {
console.error("Failed to kick user:", err);
});
break;
case "ban": // Ban the user
member.ban({ reason: "Listed bad actor" }).then(() => {
client.channels.fetch(config.logChannelId).then(logChan => {
logChan.send({
content: `User <@${member.id}> has been banned for being a bad actor. Reported by <@${row.reportedBy}>.`
}).catch(console.error);
});
}).catch(err => {
console.error("Failed to ban user:", err);
});
break;
}
client.channels.fetch(config.logChannelId).then(channel => {
if (!channel) {
console.warn(`Log channel ${config.logChannelId} not found in guild ${member.guild.id}`);
return;
}
channel.send({
embeds: [{
title: "Bad Actor Detected",
description: `User <@${member.id}> has joined the server and is listed as a bad actor.`,
fields: [
{ name: "Reported By", value: `<@${row.reportedBy}>`, inline: true },
{ name: "Comment", value: row.comment || "No comment provided", inline: false },
{ name: "Timestamp", value: `<t:${Math.floor(row.timestamp / 1000)}>`, inline: true }
],
color: 0xff0000
}],
files: row.attachments ? JSON.parse(row.attachments).map(file => ({
attachment: path.join(__dirname, '../../storage', file),
name: file
})) : []
}).catch(console.error);
}).catch(console.error);
}
});
});
}

159
index.js Normal file
View file

@ -0,0 +1,159 @@
require("dotenv").config();
const sqlite3 = require('sqlite3').verbose();
const db = new sqlite3.Database(process.env.DB_PATH || 'database.db');
require('./migrations')(db).then(() => {
console.log('All migrations completed successfully');
}).catch(err => {
console.error('Error running migrations:', err);
});
const Discord = require("discord.js");
const fs = require('fs');
const path = require("path");
const client = new Discord.Client({
intents: [
Discord.IntentsBitField.Flags.Guilds,
Discord.IntentsBitField.Flags.GuildModeration,
Discord.IntentsBitField.Flags.GuildMembers
]
})
// Presence Loop
var presenceCounter = 0;
var presenceInterval = 60; // Default to 60 seconds, can be adjusted in presence.json
const presenceLoop = () => {
console.log("Doing presence update...");
try {
const presenceConfig = JSON.parse(fs.readFileSync('presence.json', 'utf8'));
if (!presenceConfig.presenceList || !Array.isArray(presenceConfig.presenceList) || presenceConfig.presenceList.length === 0) {
console.warn('Presence list is empty or not defined in presence.json');
return;
}
if (!presenceConfig.presenceList[presenceCounter]) {
presenceCounter = 0; // Reset counter if out of bounds
}
presenceInterval = presenceConfig.interval || 60; // Use interval from config or default to 60 seconds
console.log(`Setting presence to: ${presenceConfig.presenceList[presenceCounter]}; Counter: ${presenceCounter}; Waiting ${presenceInterval} seconds for next update...`);
client.user.setPresence({
activities: [
{
name: presenceConfig.presenceList[presenceCounter],
type: Discord.ActivityType.Custom
}
]
});
if (presenceConfig.random) {
console.log("Random presence enabled, selecting a random presence for next update.");
presenceCounter = Math.floor(Math.random() * presenceConfig.presenceList.length);
} else {
presenceCounter++;
}
} catch (err) {
console.error('Failed to load presence config:', err);
}
setTimeout(presenceLoop, presenceInterval * 1000); // Wait for the specified interval before next update
}
client.once("ready", async () => {
console.log(`Logged in as ${client.user.username} (${client.user.id})`);
// Register commands
const commands = require("./commands.js")
await (async () => {
const rest = new Discord.REST({ version: '10' }).setToken(client.token);
try {
//Global
console.log(`Registering global commands`);
await rest.put(Discord.Routes.applicationCommands(client.user.id), { body: commands.globalCommands })
console.log(`Registered global commands successfully`);
} catch (error) {
console.error(error);
}
if (process.env.REFRESH_ADMIN_GUILD_COMMANDS === 'true') {
// Unregister guild commands from all guilds
try {
console.log(`Unregistering guild commands from all guilds`);
const guilds = await client.guilds.fetch();
console.log(`Found ${guilds.size} guilds to unregister commands from`);
if (guilds.size === 0) {
console.log(`No guilds found, skipping unregistration`);
return;
}
for (const guild of guilds.values()) {
await rest.put(Discord.Routes.applicationGuildCommands(client.user.id, guild.id), { body: [] });
}
console.log(`Unregistered guild commands from all guilds successfully`);
} catch (error) {
console.error(error);
}
try {
//Guild
console.log(`Registering guild commands for admin guild`);
await rest.put(Discord.Routes.applicationGuildCommands(client.user.id, process.env.ADMIN_GUILD), { body: commands.adminGuildCommands })
console.log(`Registered guild commands for admin guild successfully`);
} catch (error) {
console.error(error);
}
}
})();
presenceLoop();
});
client.on("error", (error) => {
console.error("An error occurred:", error);
});
client.on("interactionCreate", async (interaction) => {
switch (interaction.type) {
case Discord.InteractionType.ApplicationCommand:
const command = require(`./interactions/commands/${interaction.commandName}.js`);
if (command) {
try {
await command.execute(interaction, db, client);
} catch (error) {
console.error(`Error executing command ${interaction.commandName}:`, error);
await interaction.reply({ content: 'There was an error while executing this command!', ephemeral: true });
}
} else {
console.warn(`Command ${interaction.commandName} not found.`);
await interaction.reply({ content: 'This command does not exist.', ephemeral: true });
}
break;
case Discord.InteractionType.ModalSubmit:
const modal = require(`./interactions/modals/${interaction.customId}.js`)(interaction, db, client);
if (modal) {
try {
await modal.execute();
} catch (error) {
console.error(`Error executing modal ${interaction.customId}:`, error);
await interaction.reply({ content: 'There was an error while processing this modal!', ephemeral: true });
}
} else {
console.warn(`Modal ${interaction.customId} not found.`);
await interaction.reply({ content: 'This modal does not exist.', ephemeral: true });
}
break;
default:
console.warn(`Unhandled interaction type: ${interaction.type}`);
await interaction.reply({ content: 'This interaction type is not supported.', ephemeral: true });
break;
}
});
client.on("guildMemberAdd", async (member) => {
require("./eventHandlers/guildMemberAdd.js")(member, db, client);
});
client.login(process.env.TOKEN).then(() => {
console.log("Bot is online!");
}).catch(err => {
console.error("Failed to log in:", err);
});

View file

@ -0,0 +1,87 @@
const fs = require('fs');
const path = require('path');
const https = require('https');
const execute = async (interaction, db, client) => {
if (!interaction.isCommand()) return;
await interaction.deferReply({ ephemeral: true }).catch(console.error);
db.get("SELECT * FROM badActors WHERE id = ?", [interaction.options.getUser("user")], async (err, row) => {
if (err) {
console.error("Database error:", err);
return interaction.editReply({ content: "An error occurred while accessing the database.", ephemeral: true });
}
if (row) {
return interaction.editReply({ content: "That user is already listed!", ephemeral: true });
} else {
const user = interaction.options.getUser("user").id;
const comment = interaction.options.getString("comment") || "No comment provided";
const attachments = [];
for (let i = 1; i <= 4; i++) {
const attachment = interaction.options.getAttachment(`attachment${i}`);
if (attachment) {
const url = attachment.url;
const filename = `${Date.now()}_${attachment.name}`;
const filepath = path.join(__dirname, '../../storage', filename);
// Await the download before continuing
await new Promise((resolve, reject) => {
const file = fs.createWriteStream(filepath);
https.get(url, (response) => {
response.pipe(file);
file.on('finish', () => {
file.close(resolve);
});
}).on('error', (err) => {
fs.unlink(filepath, () => { });
console.error('Error downloading attachment:', err);
reject(err);
});
});
attachments.push(filename);
}
}
const timestamp = Date.now();
db.run("INSERT INTO badActors (id, reportedBy, comment, timestamp, attachments) VALUES (?, ?, ?, ?, ?)", [
user,
interaction.user.id,
comment,
timestamp,
JSON.stringify(attachments)
], function (err) {
if (err) {
console.error("Database error:", err);
return interaction.editReply({ content: "An error occurred while adding the user to the list.", ephemeral: true });
}
const embed = {
title: "Bad Actor Added",
description: `User <@${user}> has been added to the bad actors list.`,
fields: [
{ name: "Reported By", value: `<@${interaction.user.id}>`, inline: true },
{ name: "Comment", value: comment, inline: false },
{ name: "Timestamp", value: new Date(timestamp).toLocaleString(), inline: true }
],
color: 0xff0000
};
interaction.editReply({ embeds: [embed], files: attachments.map(file => path.join(__dirname, '../../storage', file))});
client.channels.fetch(process.env.ADMIN_LOG_CHANNEL).then(logChan => {
logChan.send({
embeds: [embed],
content: `User <@${user}> has been added to the bad actors list by <@${interaction.user.id}>.`,
files: attachments.map(file => ({
attachment: path.join(__dirname, '../../storage', file),
name: file
}))
}).catch(console.error);
})
});
}
});
}
module.exports = { execute };

View file

@ -0,0 +1,36 @@
const path = require('path');
const execute = async (interaction, db, client) => {
if (!interaction.isCommand()) return;
await interaction.deferReply({ ephemeral: true }).catch(console.error);
interaction.options.getUser("user").id
db.get("SELECT * FROM badActors WHERE id = ?", [interaction.options.getUser("user").id], async (err, row) => {
if (row) {
const embed = {
title: "Bad Actor Found",
description: `User <@${row.id}> is in the bad actors list.`,
fields: [
{ name: "Reported By", value: `<@${row.reportedBy}>`, inline: true },
{ name: "Comment", value: row.comment || "No comment provided", inline: false },
{ name: "Timestamp", value: `<t:${Math.floor(new Date(row.timestamp)/1000)}>`, inline: true }
],
color: 0x00ff00
};
let files = [];
if (row.attachments) {
try {
const attachments = JSON.parse(row.attachments);
files = attachments.map(file => path.join(__dirname, '../../storage', file));
} catch (e) {
files = [];
}
}
interaction.editReply({ embeds: [embed], files });
} else {
interaction.editReply({ content: "That user is not listed as a bad actor.", ephemeral: true });
}
});
}
module.exports = { execute };

View file

@ -0,0 +1,48 @@
const fs = require('fs');
const path = require('path');
const execute = async (interaction, db, client) => {
if (!interaction.isCommand()) return;
await interaction.deferReply({ ephemeral: true }).catch(console.error);
db.get("SELECT * FROM badActors WHERE id = ?", [interaction.options.getUser("user").id], async (err, row) => {
if (err) {
console.error("Database error:", err);
return interaction.editReply({ content: "An error occurred while accessing the database.", ephemeral: true });
}
if (row) {
const user = interaction.options.getUser("user").id;
const confirmation = interaction.options.getString("confirmation");
if (confirmation !== "yes im absolutely sure") {
return interaction.editReply({ content: "You must type 'yes im absolutely sure' to confirm removal.", ephemeral: true });
}
db.run("DELETE FROM badActors WHERE id = ?", [user], function (err) {
if (err) {
console.error("Database error:", err);
return interaction.editReply({ content: "An error occurred while removing the user from the list.", ephemeral: true });
}
row.attachments = JSON.parse(row.attachments || "[]");
if (row.attachments && Array.isArray(row.attachments)) {
for (const filePath of row.attachments) {
fs.unlink(path.join(__dirname, "../../storage", filePath), (err) => {
if (err) {
console.error(`Failed to delete attachment: ${filePath}`, err);
}
});
}
}
interaction.editReply({ content: `User <@${user}> has been removed from the bad actors list.`, ephemeral: true });
client.channels.fetch(process.env.ADMIN_LOG_CHANNEL).then(logChan => {
logChan.send({
content: `User <@${user}> has been removed from the bad actors list by <@${interaction.user.id}>.`
}).catch(console.error);
})
});
} else {
return interaction.editReply({ content: "That user is not listed as a bad actor.", ephemeral: true });
}
});
}
module.exports = { execute };

View file

@ -0,0 +1,112 @@
const Discord = require("discord.js");
const allowedChannelTypes = [Discord.ChannelType.GuildAnnouncement, Discord.ChannelType.GuildText, Discord.ChannelType.GuildVoice]
const execute = async (interaction, db, client) => {
if (!interaction.isCommand()) return;
if (!interaction.guild) return interaction.reply({ content: "This command can only be used in a server.", ephemeral: true });
await interaction.deferReply({ ephemeral: true }).catch(console.error);
var conf;
await db.get("SELECT * FROM guildConfigs WHERE guildId = ?", [interaction.guild.id], async (err, row) => {
if (err) {
console.error("Database error:", err);
return interaction.editReply({ content: "An error occurred while accessing the database.", ephemeral: true });
}
if (!row) {
db.run("INSERT INTO guildConfigs (guildId, mode) VALUES (?, ?)", [interaction.guild.id, "none"], function (err) {
if (err) {
console.error("Database error:", err);
returninteraction.editReply({ content: "An error occurred while initializing server configuration.", ephemeral: true });
}
conf = { guildId: interaction.guild.id, mode: "none" };
});
}
conf = row;
const subcommand = interaction.options.getSubcommand();
console.log(`Subcommand: ${subcommand}`);
switch (subcommand) {
case "mode": // Activate or deactivate automatic bad-actor management
const newMode = interaction.options.getString("mode");
if (!["none", "warn", "kick", "ban"].includes(newMode)) {
return interaction.editReply({ content: "Invalid mode selected. Choose from: none, warn, kick, ban.", ephemeral: true });
}
db.run("UPDATE guildConfigs SET mode = ? WHERE guildId = ?", [newMode, interaction.guild.id], function (err) {
if (err) {
console.error("Database error:", err);
return interaction.editReply({ content: "An error occurred while updating the mode.", ephemeral: true });
}
conf.mode = newMode;
return interaction.editReply({ content: `Bad actor management mode set to **${newMode}**.`, ephemeral: true });
});
break;
case "logchannel": // Set the log channel for bad actor actions
const logChannel = interaction.options.getChannel("channel");
if (!logChannel || !allowedChannelTypes.includes(logChannel.type)) {
return interaction.editReply({ content: "Please select a valid text channel for logging.", ephemeral: true });
}
db.run("UPDATE guildConfigs SET logChannelId = ? WHERE guildId = ?", [logChannel.id, interaction.guild.id], function (err) {
if (err) {
console.error("Database error:", err);
return interaction.editReply({ content: "An error occurred while updating the log channel.", ephemeral: true });
}
conf.logChannelId = logChannel.id;
return interaction.editReply({ content: `Log channel set to <#${logChannel.id}>.`, ephemeral: true });
});
break;
case "view": // View current server configuration
if (!conf) {
return interaction.editReply({ content: "No configuration found for this server.", ephemeral: true });
}
const mode = conf.mode || "none";
const logChannelId = conf.logChannelId ? `<#${conf.logChannelId}>` : "Not set";
const embed = new Discord.EmbedBuilder()
.setTitle("Server Configuration")
.addFields(
{ name: "Bad Actor Management Mode", value: mode, inline: true },
{ name: "Log Channel", value: logChannelId, inline: true }
)
.setColor("#0099ff")
.setTimestamp();
return interaction.editReply({ embeds: [embed], ephemeral: true });
break;
case "fullscan": // Perform a full scan of the server for bad actors
if (!conf.logChannelId) {
return interaction.editReply({ content: "Log channel is not set. Please set it using `/serverconfig logchannel`.", ephemeral: true });
}
const logChannelFullScan = await client.channels.fetch(conf.logChannelId).catch(console.error);
if (!logChannelFullScan || !allowedChannelTypes.includes(logChannelFullScan.type)) {
return interaction.editReply({ content: "Log channel is not valid or not found.", ephemeral: true });
}
const members = await interaction.guild.members.fetch();
const badActors = [];
for (const member of members.values()) {
if (member.user.bot) continue; // Ignore bots
const actor = await new Promise((resolve, reject) => {
db.get("SELECT * FROM badActors WHERE id = ?", [member.user.id], (err, row) => {
if (err) {
console.error("Database error:", err);
return resolve(null);
}
resolve(row);
});
});
if (actor) {
badActors.push(actor);
}
}
if (badActors.length === 0) {
return interaction.editReply({ content: "No bad actors found in the server.", ephemeral: true });
}
let message = `Bad Actor Scan Results\nFound ${badActors.length} bad actors in the server.\n\n`;
badActors.forEach(actor => {
message += `ID: ${actor.id}\nUser: <@${actor.id}>\nReported by: <@${actor.reportedBy}>\nDate: <t:${Math.floor(actor.timestamp / 1000)}:F>\nComment: ${actor.comment}\n\n`;
});
logChannelFullScan.send({ content: message }).catch(console.error);
return interaction.editReply({ content: "Full scan completed. Check the log channel for results.", ephemeral: true });
break;
}
});
}
module.exports = { execute };

69
migrations.js Normal file
View file

@ -0,0 +1,69 @@
const fs = require('fs');
const path = require('path');
module.exports = async function runMigrations(db) {
// Promisify db.run and db.get for easier async/await usage
function run(sql, params = []) {
return new Promise((resolve, reject) => {
db.run(sql, params, function (err) {
if (err) reject(err);
else resolve(this);
});
});
}
function get(sql, params = []) {
return new Promise((resolve, reject) => {
db.get(sql, params, function (err, row) {
if (err) reject(err);
else resolve(row);
});
});
}
function all(sql, params = []) {
return new Promise((resolve, reject) => {
db.all(sql, params, function (err, rows) {
if (err) reject(err);
else resolve(rows);
});
});
}
// 1. Ensure migrations table exists
await run(`
CREATE TABLE IF NOT EXISTS migrations (
name TEXT PRIMARY KEY,
run_at DATETIME DEFAULT CURRENT_TIMESTAMP
)
`);
// 2. Read all migration files
const migrationsDir = path.join(__dirname, 'migrations');
let files = [];
try {
files = fs.readdirSync(migrationsDir)
.filter(f => f.endsWith('.js'))
.sort();
} catch (e) {
// If directory doesn't exist, skip
if (e.code !== 'ENOENT') throw e;
}
// 3. Get already run migrations
const appliedRows = await all('SELECT name FROM migrations');
const applied = new Set(appliedRows.map(row => row.name));
// 4. Run pending migrations
for (const file of files) {
if (!applied.has(file)) {
const migration = require(path.join(migrationsDir, file));
if (typeof migration !== 'function') {
throw new Error(`Migration file ${file} does not export a function`);
}
console.log(`Running migration: ${file}`);
await migration(db, run, get, all);
// Mark migration as applied
await run('INSERT INTO migrations (name) VALUES (?)', [file]);
console.log(`Migration applied: ${file}`);
}
}
};

View file

@ -0,0 +1,7 @@
module.exports = async function createTables(db, run, get, all) {
// Create badActors table
run("CREATE TABLE IF NOT EXISTS badActors (id TEXT PRIMARY KEY, reportedBy TEXT, comment TEXT, timestamp INTEGER, attachments BLOB)");
// Create guildConfigs table
run("CREATE TABLE IF NOT EXISTS guildConfigs (guildId TEXT PRIMARY KEY, mode TEXT DEFAULT 'warn', logChannelId TEXT)")
}

View file

@ -0,0 +1,12 @@
module.exports = async function importOldData(db, run, get, all) {
oldData = require("../snowflakes.json");
for (const key in oldData) {
let actor = oldData[key][0];
run("INSERT INTO badActors (id, reportedBy, comment, timestamp) VALUES (?, ?, ?, ?)", [
key,
actor.author.id,
actor.orig_content,
new Date(actor.created_at).getTime()
])
}
}

1782
package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

18
package.json Normal file
View file

@ -0,0 +1,18 @@
{
"name": "satanic-security",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"type": "commonjs",
"dependencies": {
"discord.js": "^14.20.0",
"dotenv": "^16.5.0",
"sqlite3": "^5.1.7"
}
}

13
presence.json Normal file
View file

@ -0,0 +1,13 @@
{
"interval": 60,
"random": true,
"presenceList": [
"Praise yourself!",
"Happy pride month 🌈",
"I'll protect you",
"You are loved",
"You are not alone",
"You are enough",
"You are valid"
]
}

9
util/embedGen.js Normal file
View file

@ -0,0 +1,9 @@
module.exports = {
listBadActor: (row) => {
return {
embeds: [{
}]
}
}
}