chore: Add initial configuration files and code structure
This commit is contained in:
commit
127a7dc87f
13 changed files with 1761 additions and 0 deletions
49
.eslintrc.json
Normal file
49
.eslintrc.json
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
{
|
||||||
|
"extends": "eslint:recommended",
|
||||||
|
"env": {
|
||||||
|
"node": true,
|
||||||
|
"es6": true
|
||||||
|
},
|
||||||
|
"parserOptions": {
|
||||||
|
"ecmaVersion": 2021
|
||||||
|
},
|
||||||
|
"rules": {
|
||||||
|
"arrow-spacing": ["warn", { "before": true, "after": true }],
|
||||||
|
"brace-style": ["error", "stroustrup", { "allowSingleLine": true }],
|
||||||
|
"comma-dangle": ["error", "always-multiline"],
|
||||||
|
"comma-spacing": "error",
|
||||||
|
"comma-style": "error",
|
||||||
|
"curly": ["error", "multi-line", "consistent"],
|
||||||
|
"dot-location": ["error", "property"],
|
||||||
|
"handle-callback-err": "off",
|
||||||
|
"indent": ["error", "tab"],
|
||||||
|
"keyword-spacing": "error",
|
||||||
|
"max-nested-callbacks": ["error", { "max": 4 }],
|
||||||
|
"max-statements-per-line": ["error", { "max": 2 }],
|
||||||
|
"no-console": "off",
|
||||||
|
"no-empty-function": "error",
|
||||||
|
"no-floating-decimal": "error",
|
||||||
|
"no-inline-comments": "error",
|
||||||
|
"no-lonely-if": "error",
|
||||||
|
"no-multi-spaces": "error",
|
||||||
|
"no-multiple-empty-lines": ["error", { "max": 2, "maxEOF": 1, "maxBOF": 0 }],
|
||||||
|
"no-shadow": ["error", { "allow": ["err", "resolve", "reject"] }],
|
||||||
|
"no-trailing-spaces": ["error"],
|
||||||
|
"no-var": "error",
|
||||||
|
"object-curly-spacing": ["error", "always"],
|
||||||
|
"prefer-const": "error",
|
||||||
|
"quotes": ["error", "single"],
|
||||||
|
"semi": ["error", "always"],
|
||||||
|
"space-before-blocks": "error",
|
||||||
|
"space-before-function-paren": ["error", {
|
||||||
|
"anonymous": "never",
|
||||||
|
"named": "never",
|
||||||
|
"asyncArrow": "always"
|
||||||
|
}],
|
||||||
|
"space-in-parens": "error",
|
||||||
|
"space-infix-ops": "error",
|
||||||
|
"space-unary-ops": "error",
|
||||||
|
"spaced-comment": "error",
|
||||||
|
"yoda": "error"
|
||||||
|
}
|
||||||
|
}
|
132
.gitignore
vendored
Normal file
132
.gitignore
vendored
Normal file
|
@ -0,0 +1,132 @@
|
||||||
|
node_modules
|
||||||
|
config.json
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
.pnpm-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
|
||||||
|
|
||||||
|
# Microbundle cache
|
||||||
|
.rpt2_cache/
|
||||||
|
.rts2_cache_cjs/
|
||||||
|
.rts2_cache_es/
|
||||||
|
.rts2_cache_umd/
|
||||||
|
|
||||||
|
# Optional REPL history
|
||||||
|
.node_repl_history
|
||||||
|
|
||||||
|
# Output of 'npm pack'
|
||||||
|
*.tgz
|
||||||
|
|
||||||
|
# Yarn Integrity file
|
||||||
|
.yarn-integrity
|
||||||
|
|
||||||
|
# dotenv environment variable files
|
||||||
|
.env
|
||||||
|
.env.development.local
|
||||||
|
.env.test.local
|
||||||
|
.env.production.local
|
||||||
|
.env.local
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
# 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.*
|
13
bot.py
Normal file
13
bot.py
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
import sys
|
||||||
|
|
||||||
|
def process_message(message):
|
||||||
|
# Replace this with your actual processing logic
|
||||||
|
return f"Processed message: {message}"
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
if len(sys.argv) > 1:
|
||||||
|
user_message = sys.argv[1]
|
||||||
|
result = process_message(user_message)
|
||||||
|
print(result)
|
||||||
|
else:
|
||||||
|
print("No message provided")
|
35
commands/utility/whitelist.js
Normal file
35
commands/utility/whitelist.js
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
const { SlashCommandBuilder } = require('discord.js');
|
||||||
|
const { ownerID } = require('../../config.json');
|
||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
data: new SlashCommandBuilder()
|
||||||
|
.setName('whitelist')
|
||||||
|
.setDescription('Add user to whitelist')
|
||||||
|
.addUserOption(option =>
|
||||||
|
option
|
||||||
|
.setName('target')
|
||||||
|
.setDescription('The member to whitelist')
|
||||||
|
.setRequired(true)),
|
||||||
|
async execute(interaction) {
|
||||||
|
const userID = interaction.user.id; // This is the ID of the user who triggered the interaction
|
||||||
|
if (userID.localeCompare(ownerID)) {
|
||||||
|
const target = interaction.options.getUser('target');
|
||||||
|
const config = JSON.parse(fs.readFileSync('config.json', 'utf-8'));
|
||||||
|
|
||||||
|
if (!config.whitelist.includes(target.id)) {
|
||||||
|
// Add the user ID to the whitelist
|
||||||
|
config.whitelist.push(target.id);
|
||||||
|
|
||||||
|
// Write the updated JSON back to the config file
|
||||||
|
fs.writeFileSync('config.json', JSON.stringify(config, null, 2));
|
||||||
|
|
||||||
|
await interaction.reply('User has been added to the whitelist!');
|
||||||
|
} else {
|
||||||
|
await interaction.reply('User is already in the whitelist!');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
await interaction.reply('You\'r not the owner of the bot!');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
7
config.example.json
Normal file
7
config.example.json
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"token": "bot-token",
|
||||||
|
"clientId": "bot-id",
|
||||||
|
"guildId": "server-id",
|
||||||
|
"ownerId": "owner-id",
|
||||||
|
"whitelist": []
|
||||||
|
}
|
46
deploy-commands.js
Normal file
46
deploy-commands.js
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
const { REST, Routes } = require('discord.js');
|
||||||
|
const { clientId, guildId, token } = require('./config.json');
|
||||||
|
const fs = require('node:fs');
|
||||||
|
const path = require('node:path');
|
||||||
|
|
||||||
|
const commands = [];
|
||||||
|
// Grab all the command folders from the commands directory you created earlier
|
||||||
|
const foldersPath = path.join(__dirname, 'commands');
|
||||||
|
const commandFolders = fs.readdirSync(foldersPath);
|
||||||
|
|
||||||
|
for (const folder of commandFolders) {
|
||||||
|
// Grab all the command files from the commands directory you created earlier
|
||||||
|
const commandsPath = path.join(foldersPath, folder);
|
||||||
|
const commandFiles = fs.readdirSync(commandsPath).filter(file => file.endsWith('.js'));
|
||||||
|
// Grab the SlashCommandBuilder#toJSON() output of each command's data for deployment
|
||||||
|
for (const file of commandFiles) {
|
||||||
|
const filePath = path.join(commandsPath, file);
|
||||||
|
const command = require(filePath);
|
||||||
|
if ('data' in command && 'execute' in command) {
|
||||||
|
commands.push(command.data.toJSON());
|
||||||
|
} else {
|
||||||
|
console.log(`[WARNING] The command at ${filePath} is missing a required "data" or "execute" property.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Construct and prepare an instance of the REST module
|
||||||
|
const rest = new REST().setToken(token);
|
||||||
|
|
||||||
|
// and deploy your commands!
|
||||||
|
(async () => {
|
||||||
|
try {
|
||||||
|
console.log(`Started refreshing ${commands.length} application (/) commands.`);
|
||||||
|
|
||||||
|
// The put method is used to fully refresh all commands in the guild with the current set
|
||||||
|
const data = await rest.put(
|
||||||
|
Routes.applicationGuildCommands(clientId, guildId),
|
||||||
|
{ body: commands },
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log(`Successfully reloaded ${data.length} application (/) commands.`);
|
||||||
|
} catch (error) {
|
||||||
|
// And of course, make sure you catch and log any errors!
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
})();
|
26
events/interactionCreate.js
Normal file
26
events/interactionCreate.js
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
const { Events } = require('discord.js');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name: Events.InteractionCreate,
|
||||||
|
async execute(interaction) {
|
||||||
|
if (!interaction.isChatInputCommand()) return;
|
||||||
|
|
||||||
|
const command = interaction.client.commands.get(interaction.commandName);
|
||||||
|
|
||||||
|
if (!command) {
|
||||||
|
console.error(`No command matching ${interaction.commandName} was found.`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await command.execute(interaction);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
if (interaction.replied || interaction.deferred) {
|
||||||
|
await interaction.followUp({ content: 'There was an error while executing this command!', ephemeral: true });
|
||||||
|
} else {
|
||||||
|
await interaction.reply({ content: 'There was an error while executing this command!', ephemeral: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
51
events/messageCreate.js
Normal file
51
events/messageCreate.js
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
const { Events } = require('discord.js');
|
||||||
|
const fs = require('fs');
|
||||||
|
const { exec } = require('child_process');
|
||||||
|
const { logAction } = require('../logs/logger.js'); // Import logAction
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name: Events.MessageCreate,
|
||||||
|
async execute(message) {
|
||||||
|
// Ignore messages sent by the bot itself
|
||||||
|
if (message.author.bot) return;
|
||||||
|
|
||||||
|
const config = JSON.parse(fs.readFileSync('config.json', 'utf-8'));
|
||||||
|
|
||||||
|
// Check if the message is a DM or if the bot is mentioned in a guild message
|
||||||
|
const isDM = message.guild === null;
|
||||||
|
const isMentioned = message.mentions.has(message.client.user);
|
||||||
|
|
||||||
|
if (isDM || isMentioned) {
|
||||||
|
// Get the content of the message and remove the bot mention if in a guild
|
||||||
|
const botMention = isDM ? '' : `<@${message.client.user.id}>`;
|
||||||
|
const messageContent = message.content.replace(botMention, '').trim();
|
||||||
|
|
||||||
|
// Log the message
|
||||||
|
console.log(`[LOG] User ${message.author.tag} asked: ${messageContent}`);
|
||||||
|
logAction(`${message.author.tag}: ${messageContent}`, message.author.id, message.author.tag);
|
||||||
|
|
||||||
|
if (config.whitelist.includes(message.author.id)) {
|
||||||
|
exec(`python3 bot.py "${messageContent}"`, (error, stdout, stderr) => {
|
||||||
|
if (error) {
|
||||||
|
console.error(`Error executing script: ${error.message}`);
|
||||||
|
logAction(`GPT: ${error.message}`, message.author.id, message.author.tag);
|
||||||
|
return message.reply("There was an error processing your request.");
|
||||||
|
}
|
||||||
|
if (stderr) {
|
||||||
|
console.error(`Script error: ${stderr}`);
|
||||||
|
logAction(`GPT: ${stderr}`, message.author.id, message.author.tag);
|
||||||
|
return message.reply("There was an error processing your request.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log the output from the Python script
|
||||||
|
logAction(`GPT: ${stdout.trim()}`, message.author.id, message.author.tag);
|
||||||
|
|
||||||
|
// Reply with the output from the Python script
|
||||||
|
message.reply(stdout.trim());
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
message.reply("Not whitelisted");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
9
events/ready.js
Normal file
9
events/ready.js
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
const { Events } = require('discord.js');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name: Events.ClientReady,
|
||||||
|
once: true,
|
||||||
|
execute(client) {
|
||||||
|
console.log(`Ready! Logged in as ${client.user.tag}`);
|
||||||
|
},
|
||||||
|
};
|
57
index.js
Normal file
57
index.js
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
const fs = require('node:fs');
|
||||||
|
const path = require('node:path');
|
||||||
|
const { Client, Collection, GatewayIntentBits, Partials } = require('discord.js');
|
||||||
|
const { token } = require('./config.json');
|
||||||
|
|
||||||
|
// Create a new client instance
|
||||||
|
const client = new Client({
|
||||||
|
intents:
|
||||||
|
[
|
||||||
|
GatewayIntentBits.Guilds,
|
||||||
|
GatewayIntentBits.GuildMessages,
|
||||||
|
GatewayIntentBits.MessageContent,
|
||||||
|
GatewayIntentBits.GuildMembers,
|
||||||
|
GatewayIntentBits.DirectMessages,
|
||||||
|
GatewayIntentBits.MessageContent
|
||||||
|
],
|
||||||
|
partials: [
|
||||||
|
Partials.Channel,
|
||||||
|
Partials.Message
|
||||||
|
]
|
||||||
|
});
|
||||||
|
client.commands = new Collection();
|
||||||
|
|
||||||
|
// Grab all the command folders from the commands directory you created earlier
|
||||||
|
const foldersPath = path.join(__dirname, 'commands');
|
||||||
|
const commandFolders = fs.readdirSync(foldersPath);
|
||||||
|
|
||||||
|
for (const folder of commandFolders) {
|
||||||
|
const commandsPath = path.join(foldersPath, folder);
|
||||||
|
const commandFiles = fs.readdirSync(commandsPath).filter(file => file.endsWith('.js'));
|
||||||
|
for (const file of commandFiles) {
|
||||||
|
const filePath = path.join(commandsPath, file);
|
||||||
|
const command = require(filePath);
|
||||||
|
// Set a new item in the Collection with the key as the command name and the value as the exported module
|
||||||
|
if ('data' in command && 'execute' in command) {
|
||||||
|
client.commands.set(command.data.name, command);
|
||||||
|
} else {
|
||||||
|
console.log(`[WARNING] The command at ${filePath} is missing a required "data" or "execute" property.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const eventsPath = path.join(__dirname, 'events');
|
||||||
|
const eventFiles = fs.readdirSync(eventsPath).filter(file => file.endsWith('.js'));
|
||||||
|
|
||||||
|
for (const file of eventFiles) {
|
||||||
|
const filePath = path.join(eventsPath, file);
|
||||||
|
const event = require(filePath);
|
||||||
|
if (event.once) {
|
||||||
|
client.once(event.name, (...args) => event.execute(...args));
|
||||||
|
} else {
|
||||||
|
client.on(event.name, (...args) => event.execute(...args));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log in to Discord with your client's token
|
||||||
|
client.login(token);
|
16
logs/logger.js
Normal file
16
logs/logger.js
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
|
function logAction(action, userid, username) {
|
||||||
|
const date = new Date();
|
||||||
|
const formattedDate = `${date.getDate()}${date.getMonth() + 1}${date.getFullYear()}`;
|
||||||
|
const logMessage = `${new Date().toISOString()} ${action}\n`;
|
||||||
|
fs.appendFile(`./logs/${userid}_${username}_${formattedDate}.txt`, logMessage, (err) => {
|
||||||
|
if (err) {
|
||||||
|
console.error(`Failed to log action: ${err}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
logAction
|
||||||
|
};
|
1302
package-lock.json
generated
Normal file
1302
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
18
package.json
Normal file
18
package.json
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
{
|
||||||
|
"name": "ai-discord-bot",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1",
|
||||||
|
"start": "node index.js"
|
||||||
|
},
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"description": "",
|
||||||
|
"dependencies": {
|
||||||
|
"discord.js": "^14.15.3"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"eslint": "^9.4.0"
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue