Compare commits
4 Commits
Author | SHA1 | Date |
---|---|---|
staticfanfare | d017d0d852 | |
staticfanfare | a49c3d635d | |
staticfanfare | 256f318713 | |
staticfanfare | 420fd837f6 |
|
@ -1,6 +1,7 @@
|
||||||
{
|
{
|
||||||
"env": {
|
"env": {
|
||||||
"browser": true,
|
"browser": false,
|
||||||
|
"node": true,
|
||||||
"es2021": true
|
"es2021": true
|
||||||
},
|
},
|
||||||
"extends": "eslint:recommended",
|
"extends": "eslint:recommended",
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
config.json
|
config.json
|
||||||
node_modules
|
node_modules
|
||||||
|
database.sqlite
|
||||||
|
|
|
@ -6,4 +6,11 @@ general purpose discord bot
|
||||||
|
|
||||||
install dependencies: `npm install`
|
install dependencies: `npm install`
|
||||||
|
|
||||||
|
initialize database: `node dbInit.js`
|
||||||
|
|
||||||
|
deploy commands: `node delpoy-commands.js`
|
||||||
|
|
||||||
start the bot: `node index.js`
|
start the bot: `node index.js`
|
||||||
|
|
||||||
|
## TODO
|
||||||
|
- Finish currency system
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
//Boring init stuff
|
||||||
|
const { SlashCommandBuilder } = require('discord.js');
|
||||||
|
const helpers = require('../../helpers.js');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
//Creates the slash command
|
||||||
|
data: new SlashCommandBuilder()
|
||||||
|
.setName('balance')
|
||||||
|
.setDescription('Shows your (or a specified user\'s) ospCoin balance.')
|
||||||
|
.addUserOption(option =>
|
||||||
|
option
|
||||||
|
.setName('user')
|
||||||
|
.setDescription('User\'s balance to display')),
|
||||||
|
//Executes said slash command
|
||||||
|
async execute(interaction) {
|
||||||
|
const target = interaction.options.getUser('user') ?? interaction.user;
|
||||||
|
return interaction.reply(`${target.tag} has ${helpers.getBalance(target.id)} ospCoin`);
|
||||||
|
},
|
||||||
|
};
|
|
@ -0,0 +1,24 @@
|
||||||
|
//Boring init stuff
|
||||||
|
const { SlashCommandBuilder } = require('discord.js');
|
||||||
|
const helpers = require('../../helpers.js');
|
||||||
|
const { Users } = require('../../dbObjects.js');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
//Creates the slash command
|
||||||
|
data: new SlashCommandBuilder()
|
||||||
|
.setName('inventory')
|
||||||
|
.setDescription('Shows your (or a specified user\'s) inventory.')
|
||||||
|
.addUserOption(option =>
|
||||||
|
option
|
||||||
|
.setName('user')
|
||||||
|
.setDescription('User\'s inventory to display')),
|
||||||
|
//Executes said slash command
|
||||||
|
async execute(interaction) {
|
||||||
|
const target = interaction.options.getUser('user') ?? interaction.user;
|
||||||
|
const user = await Users.findOne({ where: { user_id: target.id } });
|
||||||
|
const items = await user.getItems();
|
||||||
|
|
||||||
|
if (!items.length) return interaction.reply(`${target.tag} has no items ;v;`);
|
||||||
|
return interaction.reply(`${target.tag} currently has ${items.map(i => `${i.amount} ${i.item.name}`).join(', ')}`);
|
||||||
|
},
|
||||||
|
};
|
|
@ -0,0 +1,34 @@
|
||||||
|
//Boring init stuff
|
||||||
|
const { SlashCommandBuilder } = require('discord.js');
|
||||||
|
const { Users } = require('../../dbObjects.js');
|
||||||
|
const helpers = require('../../helpers.js');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
//Creates the slash command
|
||||||
|
cooldown: 1800,
|
||||||
|
data: new SlashCommandBuilder()
|
||||||
|
.setName('gamble')
|
||||||
|
.setDescription('bet a certain amount of ospCoin, and either win or lose that amount.')
|
||||||
|
.addIntegerOption(option =>
|
||||||
|
option
|
||||||
|
.setName('amount')
|
||||||
|
.setDescription('Amount of ospCoin to bet')
|
||||||
|
.setRequired(true)),
|
||||||
|
//Executes said slash command
|
||||||
|
async execute(interaction) {
|
||||||
|
const amount = interaction.options.getInteger('amount') ?? 0;
|
||||||
|
const target = interaction.user;
|
||||||
|
const id = target.id;
|
||||||
|
if(amount <= helpers.getBalance(id) && amount > 0) {
|
||||||
|
if(Boolean(Math.round(Math.random()))) {
|
||||||
|
helpers.addBalance(id, amount);
|
||||||
|
await interaction.reply(`You won **${amount}** ospCoin!`);
|
||||||
|
} else {
|
||||||
|
helpers.addBalance(id, -amount);
|
||||||
|
await interaction.reply(`You lost **${amount}** ospCoin :<`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
await interaction.reply(`Invalid amount!`);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
|
@ -0,0 +1,27 @@
|
||||||
|
const Sequelize = require('sequelize');
|
||||||
|
|
||||||
|
const sequelize = new Sequelize('database', 'username', 'password', {
|
||||||
|
host: 'localhost',
|
||||||
|
dialect: 'sqlite',
|
||||||
|
logging: false,
|
||||||
|
storage: 'database.sqlite',
|
||||||
|
});
|
||||||
|
|
||||||
|
const ItemShop = require('./models/ItemShop.js')(sequelize, Sequelize.DataTypes);
|
||||||
|
require('./models/Users.js')(sequelize, Sequelize.DataTypes);
|
||||||
|
require('./models/UserItems.js')(sequelize, Sequelize.DataTypes);
|
||||||
|
|
||||||
|
const force = process.argv.includes('--force') || process.argv.includes('-f');
|
||||||
|
|
||||||
|
sequelize.sync({ force }).then(async () => {
|
||||||
|
const shop = [
|
||||||
|
ItemShop.upsert({ name: 'Canary', desc: 'A little guy! :>', cost: 100 }),
|
||||||
|
ItemShop.upsert({ name: 'Brick', desc: 'For throwing at friends :3', cost: 3 }),
|
||||||
|
ItemShop.upsert({ name: 'Book', desc: 'Reading is good for the mind.', cost: 10 }),
|
||||||
|
];
|
||||||
|
|
||||||
|
await Promise.all(shop);
|
||||||
|
console.log('Database synced');
|
||||||
|
|
||||||
|
sequelize.close();
|
||||||
|
}).catch(console.error);
|
|
@ -0,0 +1,40 @@
|
||||||
|
const Sequelize = require('sequelize');
|
||||||
|
|
||||||
|
const sequelize = new Sequelize('database', 'username', 'password', {
|
||||||
|
host: 'localhost',
|
||||||
|
dialect: 'sqlite',
|
||||||
|
logging: false,
|
||||||
|
storage: 'database.sqlite',
|
||||||
|
});
|
||||||
|
|
||||||
|
const Users = require('./models/Users.js')(sequelize, Sequelize.DataTypes);
|
||||||
|
const ItemShop = require('./models/ItemShop.js')(sequelize, Sequelize.DataTypes);
|
||||||
|
const UserItems = require('./models/UserItems.js')(sequelize, Sequelize.DataTypes);
|
||||||
|
|
||||||
|
UserItems.belongsTo(ItemShop, { foreignKey: 'item_id', as: 'item' });
|
||||||
|
|
||||||
|
Reflect.defineProperty(Users.prototype, 'addItem', {
|
||||||
|
value: async item => {
|
||||||
|
const userItem = await UserItems.findOne({
|
||||||
|
where: { user_id: this.user_id, item_id: item.id },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (userItem) {
|
||||||
|
userItem.amount += 1;
|
||||||
|
return userItem.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
return UserItems.create({ user_id: this.user_id, item_id: item.id, amount: 1 });
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
Reflect.defineProperty(Users.prototype, 'getItems', {
|
||||||
|
value: () => {
|
||||||
|
return UserItems.findAll({
|
||||||
|
where: { user_id: this.user_id },
|
||||||
|
include: ['item'],
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = { Users, ItemShop, UserItems };
|
|
@ -1,5 +1,5 @@
|
||||||
const { REST, Routes } = require('discord.js');
|
const { REST, Routes } = require('discord.js');
|
||||||
const { clientID, guildID, token } = require('./config.json');
|
const { clientID, token } = require('./config.json');
|
||||||
const fs = require('node:fs');
|
const fs = require('node:fs');
|
||||||
const path = require('node:path');
|
const path = require('node:path');
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ const rest = new REST().setToken(token);
|
||||||
|
|
||||||
// The put method is used to fully refresh all commands in the guild with the current set
|
// The put method is used to fully refresh all commands in the guild with the current set
|
||||||
const data = await rest.put(
|
const data = await rest.put(
|
||||||
Routes.applicationGuildCommands(clientID, guildID),
|
Routes.applicationCommands(clientID),
|
||||||
{ body: commands },
|
{ body: commands },
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
const { Events, Collection } = require('discord.js');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name: Events.InteractionCreate,
|
||||||
|
async execute(interaction) {
|
||||||
|
if (!interaction.isChatInputCommand()) return;
|
||||||
|
const { cooldowns } = interaction.client;
|
||||||
|
|
||||||
|
const command = interaction.client.commands.get(interaction.commandName);
|
||||||
|
|
||||||
|
if (!command) {
|
||||||
|
console.error(`ERR: Command "${interaction.commandName}" not found`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!cooldowns.has(command.data.name)) {
|
||||||
|
cooldowns.set(command.data.name, new Collection());
|
||||||
|
}
|
||||||
|
|
||||||
|
const now = Date.now();
|
||||||
|
const timestamps = cooldowns.get(command.data.name);
|
||||||
|
const defaultCooldownDuration = 5;
|
||||||
|
const cooldownAmount = (command.cooldown ?? defaultCooldownDuration) * 1000;
|
||||||
|
if (timestamps.has(interaction.user.id)) {
|
||||||
|
const expirationTime = timestamps.get(interaction.user.id) + cooldownAmount;
|
||||||
|
if (now < expirationTime) {
|
||||||
|
const expiredTimestamp = Math.round(expirationTime / 1000);
|
||||||
|
return interaction.reply({ content: `Please wait for <t:${expiredTimestamp}:R> before using \`${command.data.name}\` again.`, ephemeral: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
timestamps.set(interaction.user.id, now);
|
||||||
|
setTimeout(() => timestamps.delete(interaction.user.id), cooldownAmount);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await command.execute(interaction);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
if (interaction.replied || interaction.deferred) {
|
||||||
|
await interaction.followUp({ content: `ERROR!`, ephemeral: true});
|
||||||
|
} else {
|
||||||
|
await interaction.reply({ content: `ERROR!`, ephemeral: true});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
const { Events } = require('discord.js');
|
||||||
|
const helpers = require('../helpers.js');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name: Events.MessageCreate,
|
||||||
|
async execute(message) {
|
||||||
|
if (message.author.bot) return;
|
||||||
|
if (Math.floor(Math.random() * 11) == 10) {
|
||||||
|
helpers.addBalance(message.author.id, 1);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
const { Events } = require('discord.js');
|
||||||
|
const { Users } = require('../dbObjects.js');
|
||||||
|
const helpers = require('../helpers.js');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name: Events.ClientReady,
|
||||||
|
once: true,
|
||||||
|
async execute(client) {
|
||||||
|
const storedBalances = await Users.findAll();
|
||||||
|
storedBalances.forEach(b => helpers.currency.set(b.user_id, b));
|
||||||
|
|
||||||
|
console.log(`Ready! Logged in as ${client.user.tag}`);
|
||||||
|
},
|
||||||
|
};
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"token": "BOT-TOKEN-GOES-HERE",
|
||||||
|
"clientID": "APP-ID-GOES-HERE",
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
const { Collection } = require('discord.js');
|
||||||
|
const { Users } = require('./dbObjects.js')
|
||||||
|
|
||||||
|
const currency = new Collection();
|
||||||
|
//Helper methods for currency stuff
|
||||||
|
module.exports = {
|
||||||
|
currency: currency,
|
||||||
|
addBalance: async function (id, amount) {
|
||||||
|
const user = currency.get(id);
|
||||||
|
|
||||||
|
if (user) {
|
||||||
|
user.balance += Number(amount);
|
||||||
|
return user.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
const newUser = await Users.create({ user_id: id, balance: amount });
|
||||||
|
currency.set(id, newUser);
|
||||||
|
|
||||||
|
return newUser;
|
||||||
|
},
|
||||||
|
getBalance: function (id) {
|
||||||
|
const user = currency.get(id);
|
||||||
|
return user ? user.balance : 0;
|
||||||
|
},
|
||||||
|
}
|
50
index.js
50
index.js
|
@ -1,13 +1,19 @@
|
||||||
//Boring boring initialization stuff
|
//Boring boring initialization stuff
|
||||||
const fs = require('node:fs');
|
const fs = require('node:fs');
|
||||||
const path = require('node:path');
|
const path = require('node:path');
|
||||||
const { Client, Collection, Events, GatewayIntentBits } = require('discord.js');
|
|
||||||
|
const { Op } = require('sequelize');
|
||||||
|
const { Users, ItemShop } = require('./dbObjects.js');
|
||||||
|
const helpers = require('./helpers.js');
|
||||||
|
|
||||||
|
const { Client, codeBlock, Collection, GatewayIntentBits } = require('discord.js');
|
||||||
const { token } = require('./config.json');
|
const { token } = require('./config.json');
|
||||||
|
|
||||||
const client = new Client({ intents: [GatewayIntentBits.Guilds] });
|
const client = new Client({ intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages] });
|
||||||
|
|
||||||
//Handle commands
|
//Handle commands
|
||||||
client.commands = new Collection();
|
client.commands = new Collection();
|
||||||
|
client.cooldowns = new Collection();
|
||||||
|
|
||||||
//Command Loading
|
//Command Loading
|
||||||
const foldersPath = path.join(__dirname, 'commands');
|
const foldersPath = path.join(__dirname, 'commands');
|
||||||
|
@ -17,7 +23,7 @@ for(const folder of commandFolders) {
|
||||||
const commandPath = path.join(foldersPath, folder);
|
const commandPath = path.join(foldersPath, folder);
|
||||||
const commandFiles = fs.readdirSync(commandPath).filter(file => file.endsWith('.js'));
|
const commandFiles = fs.readdirSync(commandPath).filter(file => file.endsWith('.js'));
|
||||||
for (const file of commandFiles) {
|
for (const file of commandFiles) {
|
||||||
const filePath = path.join(commandPath, file);
|
const filePath = path.join(commandPath, file);
|
||||||
const command = require(filePath);
|
const command = require(filePath);
|
||||||
if ('data' in command && 'execute' in command) {
|
if ('data' in command && 'execute' in command) {
|
||||||
client.commands.set(command.data.name, command);
|
client.commands.set(command.data.name, command);
|
||||||
|
@ -27,34 +33,18 @@ for(const folder of commandFolders) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Command Interaction Processing
|
//Event Loading
|
||||||
client.on(Events.InteractionCreate, async interaction => {
|
const eventsPath = path.join(__dirname, 'events');
|
||||||
if (!interaction.isChatInputCommand()) return;
|
const eventFiles = fs.readdirSync(eventsPath).filter(file => file.endsWith('.js'));
|
||||||
|
|
||||||
const command = interaction.client.commands.get(interaction.commandName);
|
for (const file of eventFiles) {
|
||||||
|
const filePath = path.join(eventsPath, file);
|
||||||
if (!command) {
|
const event = require(filePath);
|
||||||
console.error(`ERR: Command "${interaction.commandName}" not found`);
|
if (event.once) {
|
||||||
return;
|
client.once(event.name, (...args) => event.execute(...args));
|
||||||
|
} else {
|
||||||
|
client.on(event.name, (...args) => event.execute(...args));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
try {
|
|
||||||
await command.execute(interaction);
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error);
|
|
||||||
if (interaction.replied || interaction.deferred) {
|
|
||||||
await interaction.followUp({ content: `ERROR!`, ephermal: true});
|
|
||||||
} else {
|
|
||||||
await interaction.reply({ content: `ERROR!`, ephermal: true});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
//Log the bot in with the token supplied in config.json
|
|
||||||
client.once(Events.ClientReady, readyClient => {
|
|
||||||
console.log(`Ready! Logged in as ${readyClient.user.tag}`);
|
|
||||||
});
|
|
||||||
|
|
||||||
client.login(token);
|
client.login(token);
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
module.exports = (sequelize, DataTypes) => {
|
||||||
|
return sequelize.define('item_shop', {
|
||||||
|
name: {
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
unique: true,
|
||||||
|
},
|
||||||
|
desc: {
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
},
|
||||||
|
cost: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
allowNull: false,
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
timestamps: false,
|
||||||
|
});
|
||||||
|
};
|
|
@ -0,0 +1,14 @@
|
||||||
|
module.exports = (sequelize, DataTypes) => {
|
||||||
|
return sequelize.define('user_item', {
|
||||||
|
user_id: DataTypes.STRING,
|
||||||
|
item_id: DataTypes.INTEGER,
|
||||||
|
amount: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
allowNull: false,
|
||||||
|
'default': 0,
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
timestamps: false,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
module.exports = (sequelize, DataTypes) => {
|
||||||
|
return sequelize.define('users', {
|
||||||
|
user_id: {
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
primaryKey: true,
|
||||||
|
},
|
||||||
|
balance: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
defaultValue: 0,
|
||||||
|
allowNull: false,
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
timestamps: false,
|
||||||
|
});
|
||||||
|
};
|
File diff suppressed because it is too large
Load Diff
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "skopa",
|
"name": "skopa",
|
||||||
"version": "0.1.1",
|
"version": "0.2.0",
|
||||||
"description": "a general-purpose discord bot",
|
"description": "a general-purpose discord bot",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
@ -13,7 +13,9 @@
|
||||||
"author": "staticfanfare",
|
"author": "staticfanfare",
|
||||||
"license": "AGPL-3.0-or-later",
|
"license": "AGPL-3.0-or-later",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"discord.js": "^14.14.1"
|
"discord.js": "^14.14.1",
|
||||||
|
"sequelize": "^6.35.2",
|
||||||
|
"sqlite3": "^5.1.6"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"eslint": "^8.56.0"
|
"eslint": "^8.56.0"
|
||||||
|
|
Loading…
Reference in New Issue