Merge remote-tracking branch 'origin/main'
# Conflicts: # README.md # commands/db/balance.js # commands/db/earn.js # commands/db/give.js # commands/db/test.js # commands/db/transfer.js # commands/moderation/kick.js # commands/moderation/mute.js # commands/moderation/temp_ban.js # commands/unique/quiz.js # commands/unique/timestamp.js # commands/utility/about.js # commands/utility/help.js # commands/utility/ping.js # events/tempban.js
This commit is contained in:
@@ -6,16 +6,16 @@ Im going to make a To-do List for the features i want to implement for my Bot.
|
|||||||
(I will try to make these tools optional, because alot of bots already feature them)
|
(I will try to make these tools optional, because alot of bots already feature them)
|
||||||
|
|
||||||
1. **Basic Commands**
|
1. **Basic Commands**
|
||||||
- [x] Add a `!help` command to display a list of available commands.
|
- [x] Add a `/help` command to display a list of available commands.
|
||||||
- [x] Implement a `!ping` command to check bot responsiveness.
|
- [x] Implement a `/ping` command to check bot responsiveness.
|
||||||
- [ **Only Text missing** ] Add a `!about` command to describe the bot's purpose.
|
- [x] Add an `/about` command to describe the bot's purpose.
|
||||||
|
|
||||||
2. **Role Management**
|
2. **Role Management**
|
||||||
- [ ] Set up a self-assignable role system (e.g., using reaction roles).
|
- [ ] Set up a self-assignable role system (e.g., using reaction roles).
|
||||||
- [ ] Allow admins to assign/remove roles with commands.
|
- [ ] Allow admins to assign/remove roles with commands.
|
||||||
|
|
||||||
3. **Moderation Tools**
|
3. **Moderation Tools**
|
||||||
- [ ] Add commands to mute, x kick, and x temp_ban users.
|
- [x] Add commands to mute, kick, and temp_ban users.
|
||||||
- [ ] Create an auto-moderation feature to delete spam or inappropriate content.
|
- [ ] Create an auto-moderation feature to delete spam or inappropriate content.
|
||||||
- [ ] Implement a warning system with a strike counter.
|
- [ ] Implement a warning system with a strike counter.
|
||||||
|
|
||||||
@@ -23,23 +23,23 @@ Im going to make a To-do List for the features i want to implement for my Bot.
|
|||||||
|
|
||||||
#### **Unique Features**
|
#### **Unique Features**
|
||||||
1. **Currency System**
|
1. **Currency System**
|
||||||
- [ ] Design a virtual currency system with basic commands:
|
- [x] Design a virtual currency system with basic commands:
|
||||||
- [ ] `!balance` to check currency balance.
|
- [x] `/balance` to check currency balance.
|
||||||
- [ ] `!earn` to earn daily or periodic rewards.
|
- [x] `/earn` to earn daily or periodic rewards.
|
||||||
- [ ] `!transfer @user [amount]` to send currency to another user.
|
- [x] `/transfer @user [amount]` to send currency to another user.
|
||||||
- [ ] Link the currency system to role management:
|
- [ ] Link the currency system to role management:
|
||||||
- [ ] Allow users to purchase roles using the currency with a command (e.g., `!buyrole [role name]`).
|
- [ ] Allow users to purchase roles using the currency with a command (e.g., `!buyrole [role name]`).
|
||||||
|
|
||||||
2. **Datetime Converter**
|
2. **Datetime Converter**
|
||||||
(Still in Progress, maybe i'll add visual Menu)
|
(Still in Progress, maybe i'll add visual Menu)
|
||||||
- [x] Create a command to convert timezones:
|
- [x] Create a command to convert timezones:
|
||||||
- [x] `!convert [time] [timezone] to [timezone]` to handle conversions.
|
- [x] `/timestamp [date]` to generate Discord timestamps.
|
||||||
- [x] Support both 12-hour and 24-hour formats.
|
- [x] Support multiple Discord timestamp formats.
|
||||||
- [ ] Include a command to display server-specific or user-specific timezones.
|
- [ ] Include a command to display server-specific or user-specific timezones.
|
||||||
|
|
||||||
3. **Minigames**
|
3. **Minigames**
|
||||||
- [ ] Implement a Quiz Game:
|
- [ ] Implement a Quiz Game:
|
||||||
- [ ] Add a `!quiz` command to start the game.
|
- [x] Add a `/quiz` command to start the game.
|
||||||
- [ ] Provide multiple-choice or true/false questions.
|
- [ ] Provide multiple-choice or true/false questions.
|
||||||
- [ ] Award virtual currency for correct answers.
|
- [ ] Award virtual currency for correct answers.
|
||||||
- [ ] Add a simple number-guessing game (e.g., `!guess [number]`).
|
- [ ] Add a simple number-guessing game (e.g., `!guess [number]`).
|
||||||
@@ -49,8 +49,8 @@ Im going to make a To-do List for the features i want to implement for my Bot.
|
|||||||
|
|
||||||
#### **Utility Tools**
|
#### **Utility Tools**
|
||||||
1. **Search and Lookups**
|
1. **Search and Lookups**
|
||||||
- [ ] Add a `!wiki [topic]` command to fetch summaries from Wikipedia.
|
- [x] Add a `/wiki [topic]` command to fetch summaries from Wikipedia.
|
||||||
- [ ] Include a `!weather [location]` command for weather updates.
|
- [x] Include a `/weather [location]` command for weather updates.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ module.exports = {
|
|||||||
.setName('user')
|
.setName('user')
|
||||||
.setDescription('User to check (defaults to you)')
|
.setDescription('User to check (defaults to you)')
|
||||||
.setRequired(false)),
|
.setRequired(false)),
|
||||||
|
category: 'Currency',
|
||||||
async execute(interaction) {
|
async execute(interaction) {
|
||||||
const dbClient = await connectToDatabase();
|
const dbClient = await connectToDatabase();
|
||||||
const db = dbClient.db('discord');
|
const db = dbClient.db('discord');
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ module.exports = {
|
|||||||
data: new SlashCommandBuilder()
|
data: new SlashCommandBuilder()
|
||||||
.setName('earn')
|
.setName('earn')
|
||||||
.setDescription('Claim your daily coin reward.'),
|
.setDescription('Claim your daily coin reward.'),
|
||||||
|
category: 'Currency',
|
||||||
async execute(interaction) {
|
async execute(interaction) {
|
||||||
const dbClient = await connectToDatabase();
|
const dbClient = await connectToDatabase();
|
||||||
const db = dbClient.db('discord');
|
const db = dbClient.db('discord');
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ module.exports = {
|
|||||||
.setName('target')
|
.setName('target')
|
||||||
.setDescription('User to receive coins')
|
.setDescription('User to receive coins')
|
||||||
.setRequired(true)),
|
.setRequired(true)),
|
||||||
|
category: 'Currency',
|
||||||
async execute(interaction) {
|
async execute(interaction) {
|
||||||
const dbClient = await connectToDatabase();
|
const dbClient = await connectToDatabase();
|
||||||
const target = interaction.options.getUser('target');
|
const target = interaction.options.getUser('target');
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ module.exports = {
|
|||||||
.setName('message')
|
.setName('message')
|
||||||
.setDescription('string to store')
|
.setDescription('string to store')
|
||||||
.setRequired(true)),
|
.setRequired(true)),
|
||||||
|
category: 'Database',
|
||||||
async execute(interaction) {
|
async execute(interaction) {
|
||||||
const dbClient = await connectToDatabase();
|
const dbClient = await connectToDatabase();
|
||||||
const message = interaction.options.getString('message');
|
const message = interaction.options.getString('message');
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ module.exports = {
|
|||||||
.setDescription('Amount of coins to send')
|
.setDescription('Amount of coins to send')
|
||||||
.setMinValue(1)
|
.setMinValue(1)
|
||||||
.setRequired(true)),
|
.setRequired(true)),
|
||||||
|
category: 'Currency',
|
||||||
async execute(interaction) {
|
async execute(interaction) {
|
||||||
const dbClient = await connectToDatabase();
|
const dbClient = await connectToDatabase();
|
||||||
const db = dbClient.db('discord');
|
const db = dbClient.db('discord');
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
const { SlashCommandBuilder } = require("discord.js");
|
const { SlashCommandBuilder, PermissionFlagsBits } = require("discord.js");
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
data: new SlashCommandBuilder()
|
data: new SlashCommandBuilder()
|
||||||
.setName("kick")
|
.setName("kick")
|
||||||
.setDescription("Kick misbehaving people")
|
.setDescription("Kick misbehaving people")
|
||||||
|
.setDefaultMemberPermissions(PermissionFlagsBits.KickMembers)
|
||||||
.addUserOption(option =>
|
.addUserOption(option =>
|
||||||
option
|
option
|
||||||
.setName('target')
|
.setName('target')
|
||||||
@@ -13,6 +14,7 @@ module.exports = {
|
|||||||
option
|
option
|
||||||
.setName('reason')
|
.setName('reason')
|
||||||
.setDescription('The reason of the kick')),
|
.setDescription('The reason of the kick')),
|
||||||
|
category: 'Moderation',
|
||||||
async execute(interaction) {
|
async execute(interaction) {
|
||||||
const user = interaction.options.getUser('target');
|
const user = interaction.options.getUser('target');
|
||||||
const reason = interaction.options.getString('reason') || 'No reason provided';
|
const reason = interaction.options.getString('reason') || 'No reason provided';
|
||||||
@@ -20,6 +22,13 @@ module.exports = {
|
|||||||
try {
|
try {
|
||||||
const member = await interaction.guild.members.fetch(user.id);
|
const member = await interaction.guild.members.fetch(user.id);
|
||||||
|
|
||||||
|
if (!member.kickable) {
|
||||||
|
return interaction.reply({
|
||||||
|
content: 'I cannot kick that member. Check my role permissions and hierarchy.',
|
||||||
|
ephemeral: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
await member.kick(reason);
|
await member.kick(reason);
|
||||||
|
|
||||||
await interaction.reply(`Kicked ${user.tag} for: ${reason}`);
|
await interaction.reply(`Kicked ${user.tag} for: ${reason}`);
|
||||||
|
|||||||
+20
-12
@@ -1,9 +1,10 @@
|
|||||||
const { SlashCommandBuilder } = require("discord.js");
|
const { SlashCommandBuilder, PermissionFlagsBits } = require("discord.js");
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
data: new SlashCommandBuilder()
|
data: new SlashCommandBuilder()
|
||||||
.setName("mute")
|
.setName("mute")
|
||||||
.setDescription("mute misbehaving people")
|
.setDescription("Temporarily mute a member with a timeout.")
|
||||||
|
.setDefaultMemberPermissions(PermissionFlagsBits.ModerateMembers)
|
||||||
.addUserOption(option =>
|
.addUserOption(option =>
|
||||||
option
|
option
|
||||||
.setName('target')
|
.setName('target')
|
||||||
@@ -12,27 +13,34 @@ module.exports = {
|
|||||||
.addIntegerOption(option =>
|
.addIntegerOption(option =>
|
||||||
option
|
option
|
||||||
.setName('duration')
|
.setName('duration')
|
||||||
.setDescription('The duration of the Mute (Minutes)')
|
.setDescription('The duration of the mute (in minutes)')
|
||||||
|
.setMinValue(1)
|
||||||
|
.setMaxValue(40320)
|
||||||
.setRequired(true))
|
.setRequired(true))
|
||||||
.addStringOption(option =>
|
.addStringOption(option =>
|
||||||
option
|
option
|
||||||
.setName('reason')
|
.setName('reason')
|
||||||
.setDescription('The reason of the mute')),
|
.setDescription('The reason of the mute')),
|
||||||
|
category: 'Moderation',
|
||||||
async execute(interaction) {
|
async execute(interaction) {
|
||||||
const target = interaction.options.getUser('target');
|
const target = interaction.options.getUser('target');
|
||||||
const duration = interaction.options.getInteger('duration');
|
const duration = interaction.options.getInteger('duration');
|
||||||
const reason = interaction.options.getString('reason');
|
const reason = interaction.options.getString('reason') || 'No reason provided';
|
||||||
|
|
||||||
// Convert duration to milliseconds
|
|
||||||
const durationMs = duration * 60 * 1000;
|
const durationMs = duration * 60 * 1000;
|
||||||
|
const member = await interaction.guild.members.fetch(target.id);
|
||||||
|
|
||||||
const guildmember = await interaction.guild.members.fetch(target.id);
|
if (!member.moderatable) {
|
||||||
|
return interaction.reply({
|
||||||
const muteRole = interaction.guild.roles.find(role => role.name === "mute");
|
content: 'I cannot mute that member. Check my role permissions and hierarchy.',
|
||||||
if (!muteRole) {
|
ephemeral: true,
|
||||||
|
});
|
||||||
} else {
|
|
||||||
await guildmember.roles.add(muteRole, reason);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await member.timeout(durationMs, reason);
|
||||||
|
|
||||||
|
return interaction.reply({
|
||||||
|
content: `Muted ${target.tag} for **${duration} minutes**. Reason: ${reason}`,
|
||||||
|
});
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -1,15 +1,16 @@
|
|||||||
const { SlashCommandBuilder, EmbedBuilder } = require("discord.js");
|
const { SlashCommandBuilder, EmbedBuilder, PermissionFlagsBits } = require("discord.js");
|
||||||
const { connectToDatabase } = require('../../db.js');
|
const { connectToDatabase } = require('../../db.js');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
mod: true,
|
mod: true,
|
||||||
data: new SlashCommandBuilder()
|
data: new SlashCommandBuilder()
|
||||||
.setName("temp-ban")
|
.setName("temp-ban")
|
||||||
.setDescription("Kick misbehaving people")
|
.setDescription("Temporarily ban a member.")
|
||||||
|
.setDefaultMemberPermissions(PermissionFlagsBits.BanMembers)
|
||||||
.addUserOption(option =>
|
.addUserOption(option =>
|
||||||
option
|
option
|
||||||
.setName('target')
|
.setName('target')
|
||||||
.setDescription('The member to kick')
|
.setDescription('The member to ban')
|
||||||
.setRequired(true))
|
.setRequired(true))
|
||||||
.addIntegerOption(option =>
|
.addIntegerOption(option =>
|
||||||
option
|
option
|
||||||
@@ -26,6 +27,7 @@ module.exports = {
|
|||||||
.setName('delete-messages')
|
.setName('delete-messages')
|
||||||
.setDescription('Number of days to delete messages')
|
.setDescription('Number of days to delete messages')
|
||||||
.setMinValue(0).setMaxValue(7)),
|
.setMinValue(0).setMaxValue(7)),
|
||||||
|
category: 'Moderation',
|
||||||
async execute(interaction) {
|
async execute(interaction) {
|
||||||
const user = interaction.options.getUser('target');
|
const user = interaction.options.getUser('target');
|
||||||
const reason = interaction.options.getString('reason');
|
const reason = interaction.options.getString('reason');
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ module.exports = {
|
|||||||
data: new SlashCommandBuilder()
|
data: new SlashCommandBuilder()
|
||||||
.setName("quiz")
|
.setName("quiz")
|
||||||
.setDescription("Start a cool quiz"),
|
.setDescription("Start a cool quiz"),
|
||||||
|
category: 'Games',
|
||||||
async execute(interaction) {
|
async execute(interaction) {
|
||||||
const item = quiz[Math.floor(Math.random() * quiz.length)];
|
const item = quiz[Math.floor(Math.random() * quiz.length)];
|
||||||
await interaction.reply({
|
await interaction.reply({
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ module.exports = {
|
|||||||
{ name: 'Full date and time with weekday', value: 'F' },
|
{ name: 'Full date and time with weekday', value: 'F' },
|
||||||
{ name: 'Relative time', value: 'R' },
|
{ name: 'Relative time', value: 'R' },
|
||||||
)),
|
)),
|
||||||
|
category: 'Utility',
|
||||||
async execute(interaction) {
|
async execute(interaction) {
|
||||||
// Get user input
|
// Get user input
|
||||||
const dateInput = interaction.options.getString('date');
|
const dateInput = interaction.options.getString('date');
|
||||||
|
|||||||
@@ -4,13 +4,14 @@ module.exports = {
|
|||||||
data: new SlashCommandBuilder()
|
data: new SlashCommandBuilder()
|
||||||
.setName('about')
|
.setName('about')
|
||||||
.setDescription('Information about this bot and creator'),
|
.setDescription('Information about this bot and creator'),
|
||||||
|
category: 'Utility',
|
||||||
async execute(interaction) {
|
async execute(interaction) {
|
||||||
const exampleEmbed = new EmbedBuilder()
|
const exampleEmbed = new EmbedBuilder()
|
||||||
.setColor(0x0099FF)
|
.setColor(0x0099FF)
|
||||||
.setTitle('About This Bot')
|
.setTitle('About This Bot')
|
||||||
.setDescription('A multipurpose Discord bot focused on helpful utilities, moderation, and fun community features.')
|
.setDescription('A multipurpose Discord bot focused on helpful utilities, moderation, and fun community features.')
|
||||||
.addFields(
|
.addFields(
|
||||||
{ name: 'Highlights', value: 'Timezone converter, quiz games, and a growing currency system.' },
|
{ name: 'Highlights', value: 'Timezone converter, quiz games, a currency system, and helpful utility tools.' },
|
||||||
{ name: 'Created By', value: interaction.client.user?.username ?? 'Bot Creator' }
|
{ name: 'Created By', value: interaction.client.user?.username ?? 'Bot Creator' }
|
||||||
)
|
)
|
||||||
.setImage('https://i.postimg.cc/QdZZwKVD/Screenshot-2024-12-25-005933.png')
|
.setImage('https://i.postimg.cc/QdZZwKVD/Screenshot-2024-12-25-005933.png')
|
||||||
|
|||||||
@@ -4,22 +4,37 @@ module.exports = {
|
|||||||
data: new SlashCommandBuilder()
|
data: new SlashCommandBuilder()
|
||||||
.setName('help')
|
.setName('help')
|
||||||
.setDescription('Gives all available commands.'),
|
.setDescription('Gives all available commands.'),
|
||||||
|
category: 'Utility',
|
||||||
async execute(interaction) {
|
async execute(interaction) {
|
||||||
const commandDetails = interaction.client.commands
|
const commandDetails = interaction.client.commands
|
||||||
.map(cmd => ({
|
.map(cmd => ({
|
||||||
name: cmd.data.name,
|
name: cmd.data.name,
|
||||||
description: cmd.data.description,
|
description: cmd.data.description,
|
||||||
|
category: cmd.category ?? 'Other',
|
||||||
}))
|
}))
|
||||||
.sort((a, b) => a.name.localeCompare(b.name));
|
.sort((a, b) => a.name.localeCompare(b.name));
|
||||||
|
|
||||||
const helpMessage = commandDetails
|
const grouped = commandDetails.reduce((acc, command) => {
|
||||||
.map(detail => `• **/${detail.name}** — ${detail.description}`)
|
const group = acc[command.category] ?? [];
|
||||||
.join('\n');
|
group.push(command);
|
||||||
|
acc[command.category] = group;
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
const fields = Object.keys(grouped)
|
||||||
|
.sort((a, b) => a.localeCompare(b))
|
||||||
|
.map((category) => ({
|
||||||
|
name: category,
|
||||||
|
value: grouped[category]
|
||||||
|
.map(command => `• **/${command.name}** — ${command.description}`)
|
||||||
|
.join('\n'),
|
||||||
|
}));
|
||||||
|
|
||||||
const embed = new EmbedBuilder()
|
const embed = new EmbedBuilder()
|
||||||
.setColor(0x4b7bec)
|
.setColor(0x4b7bec)
|
||||||
.setTitle('Available Commands')
|
.setTitle('Available Commands')
|
||||||
.setDescription(helpMessage)
|
.setDescription('Here is everything I can do right now.')
|
||||||
|
.addFields(fields)
|
||||||
.setFooter({ text: `${commandDetails.length} commands loaded.` });
|
.setFooter({ text: `${commandDetails.length} commands loaded.` });
|
||||||
|
|
||||||
await interaction.reply({ embeds: [embed], ephemeral: true });
|
await interaction.reply({ embeds: [embed], ephemeral: true });
|
||||||
|
|||||||
@@ -1,17 +1,25 @@
|
|||||||
const { SlashCommandBuilder } = require('discord.js');
|
const { SlashCommandBuilder, EmbedBuilder } = require('discord.js');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
data: new SlashCommandBuilder()
|
data: new SlashCommandBuilder()
|
||||||
.setName('ping')
|
.setName('ping')
|
||||||
.setDescription('Check the bot\'s responsiveness.'),
|
.setDescription('Check the bot\'s responsiveness.'),
|
||||||
|
category: 'Utility',
|
||||||
async execute(interaction) {
|
async execute(interaction) {
|
||||||
const sentAt = Date.now();
|
const sentAt = Date.now();
|
||||||
await interaction.reply({ content: 'Pinging...', ephemeral: true });
|
await interaction.reply({ content: 'Pinging...', ephemeral: true });
|
||||||
const latency = Date.now() - sentAt;
|
const latency = Date.now() - sentAt;
|
||||||
const apiLatency = Math.round(interaction.client.ws.ping);
|
const apiLatency = Math.round(interaction.client.ws.ping);
|
||||||
|
|
||||||
await interaction.editReply({
|
const embed = new EmbedBuilder()
|
||||||
content: `Pong! **${latency}ms** (API: **${apiLatency}ms**)`,
|
.setColor(0x34ace0)
|
||||||
});
|
.setTitle('Pong! 🏓')
|
||||||
|
.addFields(
|
||||||
|
{ name: 'Round Trip', value: `${latency}ms`, inline: true },
|
||||||
|
{ name: 'API Latency', value: `${apiLatency}ms`, inline: true },
|
||||||
|
)
|
||||||
|
.setTimestamp();
|
||||||
|
|
||||||
|
await interaction.editReply({ embeds: [embed], content: null });
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,10 +1,30 @@
|
|||||||
const { SlashCommandBuilder } = require('discord.js');
|
const { SlashCommandBuilder, EmbedBuilder } = require('discord.js');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
data: new SlashCommandBuilder()
|
data: new SlashCommandBuilder()
|
||||||
.setName('server')
|
.setName('server')
|
||||||
.setDescription('Provides information about the server.'),
|
.setDescription('Provides information about the server.'),
|
||||||
|
category: 'Utility',
|
||||||
async execute(interaction) {
|
async execute(interaction) {
|
||||||
await interaction.reply(`This server is ${interaction.guild.name} and has ${interaction.guild.memberCount} members.`);
|
const { guild } = interaction;
|
||||||
|
const owner = await guild.fetchOwner();
|
||||||
|
const created = `<t:${Math.floor(guild.createdTimestamp / 1000)}:F>`;
|
||||||
|
const boostCount = guild.premiumSubscriptionCount ?? 0;
|
||||||
|
|
||||||
|
const embed = new EmbedBuilder()
|
||||||
|
.setColor(0x2ecc71)
|
||||||
|
.setTitle(guild.name)
|
||||||
|
.setThumbnail(guild.iconURL({ size: 256 }))
|
||||||
|
.addFields(
|
||||||
|
{ name: 'Server ID', value: guild.id, inline: true },
|
||||||
|
{ name: 'Owner', value: owner.user.tag, inline: true },
|
||||||
|
{ name: 'Members', value: guild.memberCount.toString(), inline: true },
|
||||||
|
{ name: 'Boosts', value: boostCount.toString(), inline: true },
|
||||||
|
{ name: 'Created', value: created, inline: false },
|
||||||
|
)
|
||||||
|
.setFooter({ text: `Requested by ${interaction.user.tag}` })
|
||||||
|
.setTimestamp();
|
||||||
|
|
||||||
|
await interaction.reply({ embeds: [embed], ephemeral: true });
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -1,10 +1,32 @@
|
|||||||
const { SlashCommandBuilder } = require('discord.js');
|
const { SlashCommandBuilder, EmbedBuilder } = require('discord.js');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
data: new SlashCommandBuilder()
|
data: new SlashCommandBuilder()
|
||||||
.setName('user')
|
.setName('user')
|
||||||
.setDescription('Provides information about the user.'),
|
.setDescription('Provides information about a user.')
|
||||||
|
.addUserOption(option =>
|
||||||
|
option
|
||||||
|
.setName('target')
|
||||||
|
.setDescription('User to look up')
|
||||||
|
.setRequired(false)),
|
||||||
|
category: 'Utility',
|
||||||
async execute(interaction) {
|
async execute(interaction) {
|
||||||
await interaction.reply(`This command was run by ${interaction.user.username}, who joined on ${interaction.member.joinedAt}.`);
|
const target = interaction.options.getUser('target') ?? interaction.user;
|
||||||
|
const member = await interaction.guild.members.fetch(target.id);
|
||||||
|
|
||||||
|
const embed = new EmbedBuilder()
|
||||||
|
.setColor(0x4b7bec)
|
||||||
|
.setTitle(`${target.username}`)
|
||||||
|
.setThumbnail(target.displayAvatarURL({ size: 256 }))
|
||||||
|
.addFields(
|
||||||
|
{ name: 'Tag', value: target.tag, inline: true },
|
||||||
|
{ name: 'User ID', value: target.id, inline: true },
|
||||||
|
{ name: 'Account Created', value: `<t:${Math.floor(target.createdTimestamp / 1000)}:F>` },
|
||||||
|
{ name: 'Joined Server', value: member.joinedAt ? `<t:${Math.floor(member.joinedAt.getTime() / 1000)}:F>` : 'Unknown' },
|
||||||
|
)
|
||||||
|
.setFooter({ text: `Requested by ${interaction.user.tag}` })
|
||||||
|
.setTimestamp();
|
||||||
|
|
||||||
|
await interaction.reply({ embeds: [embed], ephemeral: true });
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
const { SlashCommandBuilder, EmbedBuilder } = require('discord.js');
|
||||||
|
|
||||||
|
const getJson = async (url) => {
|
||||||
|
const response = await fetch(url);
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`Request failed with status ${response.status}`);
|
||||||
|
}
|
||||||
|
return response.json();
|
||||||
|
};
|
||||||
|
|
||||||
|
const formatTemperature = (value, unit) => `${Math.round(value)}°${unit === 'fahrenheit' ? 'F' : 'C'}`;
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
data: new SlashCommandBuilder()
|
||||||
|
.setName('weather')
|
||||||
|
.setDescription('Get the current weather for a location.')
|
||||||
|
.addStringOption(option =>
|
||||||
|
option
|
||||||
|
.setName('location')
|
||||||
|
.setDescription('City or place name to look up')
|
||||||
|
.setRequired(true))
|
||||||
|
.addStringOption(option =>
|
||||||
|
option
|
||||||
|
.setName('units')
|
||||||
|
.setDescription('Temperature units')
|
||||||
|
.setRequired(false)
|
||||||
|
.addChoices(
|
||||||
|
{ name: 'Celsius', value: 'celsius' },
|
||||||
|
{ name: 'Fahrenheit', value: 'fahrenheit' },
|
||||||
|
)),
|
||||||
|
category: 'Utility',
|
||||||
|
async execute(interaction) {
|
||||||
|
const location = interaction.options.getString('location');
|
||||||
|
const units = interaction.options.getString('units') ?? 'celsius';
|
||||||
|
|
||||||
|
await interaction.deferReply();
|
||||||
|
|
||||||
|
try {
|
||||||
|
const geoUrl = `https://geocoding-api.open-meteo.com/v1/search?name=${encodeURIComponent(location)}&count=1&language=en&format=json`;
|
||||||
|
const geo = await getJson(geoUrl);
|
||||||
|
const match = geo.results?.[0];
|
||||||
|
|
||||||
|
if (!match) {
|
||||||
|
return interaction.editReply({ content: 'I could not find that location. Try a more specific place name.' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const weatherUrl = `https://api.open-meteo.com/v1/forecast?latitude=${match.latitude}&longitude=${match.longitude}¤t_weather=true&temperature_unit=${units}`;
|
||||||
|
const weather = await getJson(weatherUrl);
|
||||||
|
const current = weather.current_weather;
|
||||||
|
|
||||||
|
if (!current) {
|
||||||
|
return interaction.editReply({ content: 'Weather data is currently unavailable for that location.' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const embed = new EmbedBuilder()
|
||||||
|
.setColor(0x3498db)
|
||||||
|
.setTitle(`Weather for ${match.name}${match.country ? `, ${match.country}` : ''}`)
|
||||||
|
.addFields(
|
||||||
|
{ name: 'Temperature', value: formatTemperature(current.temperature, units), inline: true },
|
||||||
|
{ name: 'Wind Speed', value: `${Math.round(current.windspeed)} km/h`, inline: true },
|
||||||
|
{ name: 'Conditions', value: `Weather code: ${current.weathercode}`, inline: true },
|
||||||
|
)
|
||||||
|
.setFooter({ text: `Latitude ${match.latitude.toFixed(2)}, Longitude ${match.longitude.toFixed(2)}` })
|
||||||
|
.setTimestamp(new Date(current.time));
|
||||||
|
|
||||||
|
return interaction.editReply({ embeds: [embed] });
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
return interaction.editReply({ content: 'Something went wrong fetching the weather. Please try again later.' });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
const { SlashCommandBuilder, EmbedBuilder } = require('discord.js');
|
||||||
|
|
||||||
|
const MAX_DESCRIPTION = 4096;
|
||||||
|
|
||||||
|
const truncate = (text, maxLength) => {
|
||||||
|
if (!text) return 'No summary available.';
|
||||||
|
if (text.length <= maxLength) return text;
|
||||||
|
return `${text.slice(0, maxLength - 3)}...`;
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
data: new SlashCommandBuilder()
|
||||||
|
.setName('wiki')
|
||||||
|
.setDescription('Search Wikipedia for a topic.')
|
||||||
|
.addStringOption(option =>
|
||||||
|
option
|
||||||
|
.setName('topic')
|
||||||
|
.setDescription('Topic to search')
|
||||||
|
.setRequired(true)),
|
||||||
|
category: 'Utility',
|
||||||
|
async execute(interaction) {
|
||||||
|
const topic = interaction.options.getString('topic');
|
||||||
|
const endpoint = `https://en.wikipedia.org/api/rest_v1/page/summary/${encodeURIComponent(topic)}`;
|
||||||
|
|
||||||
|
await interaction.deferReply();
|
||||||
|
|
||||||
|
const response = await fetch(endpoint, {
|
||||||
|
headers: {
|
||||||
|
'User-Agent': 'DiscordBot/1.0 (https://github.com)',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
return interaction.editReply({
|
||||||
|
content: 'I could not find a Wikipedia summary for that topic.',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
|
||||||
|
const embed = new EmbedBuilder()
|
||||||
|
.setColor(0x8e44ad)
|
||||||
|
.setTitle(data.title ?? topic)
|
||||||
|
.setURL(data.content_urls?.desktop?.page ?? `https://en.wikipedia.org/wiki/${encodeURIComponent(topic)}`)
|
||||||
|
.setDescription(truncate(data.extract, MAX_DESCRIPTION))
|
||||||
|
.setFooter({ text: 'Source: Wikipedia' });
|
||||||
|
|
||||||
|
if (data.thumbnail?.source) {
|
||||||
|
embed.setThumbnail(data.thumbnail.source);
|
||||||
|
}
|
||||||
|
|
||||||
|
return interaction.editReply({ embeds: [embed] });
|
||||||
|
},
|
||||||
|
};
|
||||||
+24
-36
@@ -1,5 +1,5 @@
|
|||||||
const {Events} = require('discord.js');
|
const { Events } = require('discord.js');
|
||||||
const {connectToDatabase} = require('../db.js');
|
const { connectToDatabase } = require('../db.js');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
name: Events.ClientReady,
|
name: Events.ClientReady,
|
||||||
@@ -7,50 +7,38 @@ module.exports = {
|
|||||||
const dbClient = await connectToDatabase();
|
const dbClient = await connectToDatabase();
|
||||||
const db = dbClient.db('discord');
|
const db = dbClient.db('discord');
|
||||||
const tempBanCollection = db.collection('tempban');
|
const tempBanCollection = db.collection('tempban');
|
||||||
const scheduled = new Map();
|
|
||||||
async function unbanNow(banData) {
|
// 2) Define a function to schedule unbans
|
||||||
|
async function scheduleUnban(banData) {
|
||||||
|
const delay = banData.banTime - Date.now();
|
||||||
|
|
||||||
|
if (delay <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setTimeout(async () => {
|
||||||
try {
|
try {
|
||||||
const guild = await client.guilds.fetch(banData.guildId);
|
const guild = await client.guilds.fetch(banData.guildId);
|
||||||
await guild.bans.remove(banData.userId);
|
await guild.bans.remove(banData.userId);
|
||||||
await tempBanCollection.deleteOne({_id: banData._id});
|
|
||||||
|
await tempBanCollection.deleteOne({ _id: banData._id });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Error unbanning member:', err);
|
console.error('Error unbanning member:', err);
|
||||||
} finally {
|
|
||||||
scheduled.delete(String(banData._id));
|
|
||||||
}
|
}
|
||||||
|
}, delay);
|
||||||
}
|
}
|
||||||
|
|
||||||
function scheduleUnban(banData) {
|
|
||||||
const id = String(banData._id);
|
|
||||||
if (scheduled.has(id)) return;
|
|
||||||
|
|
||||||
const delay = banData.banTime - Date.now(); // assumes banTime is a future timestamp (ms)
|
|
||||||
if (delay <= 0) {
|
|
||||||
// already expired -> unban immediately
|
|
||||||
void unbanNow(banData);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const timeoutId = setTimeout(() => void unbanNow(banData), delay);
|
|
||||||
scheduled.set(id, timeoutId);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function scanAndSchedule() {
|
|
||||||
try {
|
try {
|
||||||
const now = Date.now();
|
const allBans = await tempBanCollection.find({}).toArray();
|
||||||
const futureBans = await tempBanCollection.find({banTime: {$gt: now}}).toArray();
|
allBans.forEach(scheduleUnban);
|
||||||
futureBans.forEach(scheduleUnban);
|
|
||||||
const expired = await tempBanCollection.find({banTime: {$lte: now}}).toArray();
|
tempBanCollection.watch().on('change', (change) => {
|
||||||
expired.forEach((b) => void unbanNow(b));
|
if (change.operationType === 'insert') {
|
||||||
|
const newBan = change.fullDocument;
|
||||||
|
scheduleUnban(newBan);
|
||||||
|
}
|
||||||
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Error scanning tempbans:', err);
|
console.error('Error setting up unban system:', err);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Initial load
|
|
||||||
await scanAndSchedule();
|
|
||||||
|
|
||||||
// Poll every 10 seconds for newly inserted bans
|
|
||||||
setInterval(scanAndSchedule, 10_000);
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
Reference in New Issue
Block a user