Compare commits

..

1 Commits

Author SHA1 Message Date
admin_rb 7da2c782af Initial commit 2026-04-06 14:10:15 +02:00
116 changed files with 16 additions and 14603 deletions
-6
View File
@@ -1,6 +0,0 @@
D_ID=
D_Token=
D_Prefix=
D_GuildID=
M_URI=
M_DB=
-2
View File
@@ -1,2 +0,0 @@
.env
node_modules
+16
View File
@@ -0,0 +1,16 @@
MIT No Attribution
Copyright <YEAR> <COPYRIGHT HOLDER>
Permission is hereby granted, free of charge, to any person obtaining a copy of this
software and associated documentation files (the "Software"), to deal in the Software
without restriction, including without limitation the rights to use, copy, modify,
merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-58
View File
@@ -1,60 +1,2 @@
# Arthonor-Neo # Arthonor-Neo
**Moderately Advanced Discord Bot for Friends**
Arthonor-Neo is a custom Discord bot designed for community interaction and fun. It offers an assortment of commands for economy, social engagement, and server utilities. Suitable for small to medium-sized friend servers.
---
## Features
### Core Interactions
- Nut Economy - Earn and spend nuts in server activities
- Honor System - Show recognition and respect to users
- Quotes - Save and retrieve memorable quotes
### Community & Events
- Automatic Welcome Messages - Greet new members
- Birthday System - Track and celebrate birthdays
- Automatic Birthday Cards - Personalized greetings on birthdays
- Wednesday Frog - Midweek server event
- WGE Core - With Periodic Reminders and Random Function
---
## Differences from Version 2.x
### Legacy Commands (Chat Messages)
These have been mostly removed with certain exceptions:
- Quoteadd has been kept due to the technicaly incompabiltity of replying using slash commands.
- Nuts has been kept due to frequent usage
### Removed
- Timers
- Weather commands
- GIF commands
- Certain developer tools
- WOKCommands Core
- Friday Reminder
### Upgraded to Slash Commands
- Nuts
- Quotes
- Honor
- Birthdays
- Role Selection
- Channel Selection
- Meme Commands
- WGE (NEW)
### New and Improved
- Interactive Honor Menu
- Interactive Quote Menu
### Database Migration
Some structurual changes to the command schema, and removal of predefined schematics
- Tables renamed with prefixes, e.g. items_, config_, stats_, etc.
- Split tables into a relation-ish style
- made some data more machine-friendly
-3
View File
@@ -1,3 +0,0 @@
### ToDo?:
- quotes filter by channel
- event creation + reminder 1h before
Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 242 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 914 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 411 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 371 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 254 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 176 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 167 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 158 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 158 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 536 KiB

-18
View File
@@ -1,18 +0,0 @@
const { EmbedBuilder } = require("discord.js")
function delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); }
module.exports = {
name: 'abort',
description: 'abort it',
async execute(interaction) {
const embed = new EmbedBuilder()
.setTitle('Aborted')
.setDescription('Schließe Menü. . .')
await interaction.update({
embeds: [embed],
components: []
})
await delay(2000).then(interaction.deleteReply())
}
}
-42
View File
@@ -1,42 +0,0 @@
const { EmbedBuilder, ActionRowBuilder, ButtonBuilder, ButtonStyle, MessageFlags } = require("discord.js")
const { mClient } = require("../..")
require('dotenv').config()
module.exports = {
name: 'honor_menu_dishonor',
description: 'honor menu dishonor button',
async execute(interaction) {
let description = interaction.message.embeds[0].data.description
const target = await interaction.guild.members.fetch(description.split(' ')[0].slice(2, -1))
const db = mClient.db(process.env.M_DB)
const honorsColl = db.collection('items_honors')
const reason = `[-] Quick Menu Dishonor`
const theirHonorLevel = await honorsColl.findOne({ userID: target.id })
const embed = new EmbedBuilder()
.setTitle('- Honor Menu WIP -')
.setDescription(`Willst du wirklich <@${target.id}> einen **Dishonor** geben?\r\n[Grund: ${reason}]`)
.addFields({
name: 'Current Honor Level', value: `${theirHonorLevel?.honors ?? 0} (**-1**)`
})
.setThumbnail(target.displayAvatarURL())
const row = new ActionRowBuilder()
.addComponents(
new ButtonBuilder()
.setLabel('✔')
.setCustomId('honor_multi_confirm')
.setStyle(ButtonStyle.Success)
)
.addComponents(
new ButtonBuilder()
.setLabel('✖')
.setCustomId('abort')
.setStyle(ButtonStyle.Danger)
)
await interaction.reply({
embeds: [embed],
components: [row],
flags: MessageFlags.Ephemeral
})
}
}
-36
View File
@@ -1,36 +0,0 @@
const { EmbedBuilder, ActionRowBuilder, ButtonBuilder, ButtonStyle, MessageFlags } = require("discord.js")
const { mClient } = require("../..")
require('dotenv').config()
module.exports = {
name: 'honor_menu_history',
description: 'show selected user honor history',
async execute(interaction) {
let description = interaction.message.embeds[0].data.description
const target = await interaction.guild.members.fetch(description.split(' ')[0].slice(2, -1))
const db = mClient.db(process.env.M_DB)
const reasonsColl = db.collection('history_honors')
const history = await reasonsColl.findOne({ userID: target.user.id })
const embed = new EmbedBuilder()
.setTitle('- Honor History -')
.setDescription(`${target} - ${target.user.globalName ?? target.user.username}`)
.setThumbnail(target.displayAvatarURL())
.setFooter({text: 'Um zurückzukehren, wähle einen Nutzer!'})
if(history){
let temp = ''
history.reasons.forEach(reason => {
temp += reason + '\r\n'
})
embed.setDescription(temp)
}
const select = await interaction.message.components[0]
const row = await interaction.message.components[1]
await interaction.reply({
embeds: [embed],
components: [select, row],
flags: MessageFlags.Ephemeral
})
}
}
-43
View File
@@ -1,43 +0,0 @@
const { EmbedBuilder, ButtonBuilder, ActionRowBuilder, ButtonStyle, MessageFlags } = require("discord.js")
const { mClient } = require("../..")
require('dotenv').config()
module.exports = {
name: 'honor_menu_honor',
description: 'honor menu honor button',
async execute(interaction) {
let description = interaction.message.embeds[0].data.description
const target = await interaction.guild.members.fetch(description.split(' ')[0].slice(2, -1))
const db = mClient.db(process.env.M_DB)
const honorsColl = db.collection('items_honors')
const reason = `[+] Quick Menu Honor`
const theirHonorLevel = await honorsColl.findOne({ userID: target.id })
const embed = new EmbedBuilder()
.setTitle('- Honor Up! -')
.setDescription(`Willst du wirklich <@${target.id}> einen **Honor** geben?\r\n[Grund: ${reason}]`)
.addFields({
name: 'Current Honor Level', value: `${theirHonorLevel?.honors ?? 0} (**+1**)`
})
.setThumbnail(target.displayAvatarURL())
//.setFooter('Honor Level updaten sich nicht automatisch!')
const row = new ActionRowBuilder()
.addComponents(
new ButtonBuilder()
.setLabel('✔')
.setCustomId('honor_multi_confirm')
.setStyle(ButtonStyle.Success)
)
.addComponents(
new ButtonBuilder()
.setLabel('✖')
.setCustomId('abort')
.setStyle(ButtonStyle.Danger)
)
await interaction.reply({
embeds: [embed],
components: [row],
flags: MessageFlags.Ephemeral
})
}
}
-68
View File
@@ -1,68 +0,0 @@
const { EmbedBuilder, MessageFlags } = require("discord.js")
const { mClient } = require("../..")
require('dotenv').config()
module.exports = {
name: 'honor_multi_confirm',
description: 'confirm it',
async execute(interaction) {
let temp = await interaction.message.embeds[0].description.split(' ')
var honor
if (temp[5] === '**Honor**') {
honor = 1
} else if (temp[5] === '**Dishonor**'){
honor = -1
} else {
return await interaction.reply({content: "ERROR", flags: MessageFlags.Ephemeral})
}
const target = await interaction.message.guild.members.fetch(temp[3].slice(2, -1))
temp = interaction.message.embeds[0].description.split('\r\n')
const reason = `${temp[1].slice(7, -1)}`
const db = mClient.db(process.env.M_DB)
const honorsColl = db.collection('items_honors')
const reasonsColl = db.collection('history_honors')
const theirHonorLevel = await honorsColl.findOne({ userID: target.id })
await honorsColl.findOneAndUpdate({ userID: target.id }, { $inc: { honors: honor } }, { upsert: true })
await reasonsColl.findOneAndUpdate({ userID: target.id }, { $push: { reasons: reason } }, { upsert: true })
var description
var newTitle
var newDescription
var newThumbnail
if (honor == 1) {
description = `Willst du wirklich <@${target.id}> einen **Honor** geben?\r\n[Grund: ${reason}]`
newTitle = 'Honor Up!'
newDescription = `<@${target.id}> erhält einen Honor!\r\n[Grund: ${reason}]`
newThumbnail = "https://cdn.discordapp.com/emojis/748176295535443968.webp"
} else {
description = `Willst du wirklich <@${target.id}> einen **Dishonor** geben?\r\n[Grund: ${reason}]`
newTitle = 'Honor Down!'
newDescription = `<@${target.id}> erhält einen Dishonor!\r\n[Grund: ${reason}]`
newThumbnail = "https://cdn.discordapp.com/emojis/748176295132790786.webp"
}
const embed = new EmbedBuilder()
.setTitle('- Honor Menu -')
.setDescription(description)
.addFields({
name: 'Current Honor Level', value: `${theirHonorLevel?.honors ?? 0} ${honor == 1 ? '(**+1**)' : '(**-1**)'}`
})
.setThumbnail(target.displayAvatarURL())
await interaction.update({
embeds: [embed]
})
const newEmbed = new EmbedBuilder()
.setTitle(newTitle)
.setDescription(newDescription)
.setThumbnail(newThumbnail)
await interaction.followUp({
embeds: [newEmbed],
components: [],
})
}
}
-35
View File
@@ -1,35 +0,0 @@
const { EmbedBuilder } = require("discord.js")
const { mClient } = require("../..")
require('dotenv').config()
module.exports = {
name: 'honors_leaderboard_left',
description: 'navigate a page up',
async execute(interaction) {
const db = mClient.db(process.env.M_DB)
const honorsColl = db.collection('items_honors')
const min = 0
let skip = interaction.message.embeds[0].data.description.split('.')
skip = Number(skip[0]) - 6
skip = skip - ( skip % 5)
if ( skip < 4){
skip = 0
}
const honorsData = await honorsColl.find().sort({ honors: -1 }).skip(skip).limit(5).toArray()
let fields
honorsData.forEach((data) => {
skip++
fields = (fields ? fields : '') + (`${skip}. <@${data.userID}> : ${data.honors} Honors\r\n`)
})
const embed = new EmbedBuilder()
.setTitle('Honors Leaderboard')
.setThumbnail(interaction.guild.iconURL({ dynamic: true}))
.setDescription(fields.toString())
.setColor('#5865F2') // Discord's blurple color
.setFooter({ text: 'Use ◄ ► to navigate' });
await interaction.update({
embeds: [embed],
components: [interaction.message.components[0]]
})
}
}
-33
View File
@@ -1,33 +0,0 @@
const { EmbedBuilder } = require("discord.js")
const { mClient } = require("../..")
require('dotenv').config()
module.exports = {
name: 'honors_leaderboard_right',
description: 'navigate a page down',
async execute(interaction) {
const db = mClient.db(process.env.M_DB)
const honorsColl = db.collection('items_honors')
const max = await honorsColl.countDocuments()
let skip = interaction.message.embeds[0].data.description.split('.')
skip = Number(skip[0]) +4
if (skip >= (max - (max % 5))) {
skip = max - (max % 5)
}
const honorsData = await honorsColl.find().sort({ honors: -1 }).skip(skip).limit(5).toArray()
let fields
honorsData.forEach((data) => {
skip++
fields = (fields ? fields : '') + (`${skip}. <@${data.userID}> : ${data.honors} Honors\r\n`)
})
const embed = new EmbedBuilder()
.setTitle('Honors Leaderboard')
.setThumbnail(interaction.guild.iconURL({ dynamic: true}))
.setDescription(fields.toString())
.setColor('#5865F2') // Discord's blurple color
.setFooter({ text: 'Use ◄ ► to navigate' });
await interaction.update({
embeds: [embed],
components: [interaction.message.components[0]]
})
}
}
-44
View File
@@ -1,44 +0,0 @@
const { EmbedBuilder } = require("discord.js")
const { mClient } = require("../..")
require('dotenv').config()
module.exports = {
name: 'honors_leaderboard_self',
description: 'navigate to your own placement',
async execute(interaction) {
const db = mClient.db(process.env.M_DB)
const honorsColl = db.collection('items_honors')
const self = await honorsColl.find({ userID: interaction.user.id }).toArray()
const all = await honorsColl.find().sort({ honors: -1 }).toArray()
function findIndex(){
let index = 0
for (let i = 0; i < all.length; i++) {
index++
if (all[i].honors === self[0].honors) {
return index
}
}
}
let selfIndex = findIndex()
let skip = selfIndex - ( selfIndex % 5)
const honorsData = await honorsColl.find().sort({ honors: -1 }).skip(skip).limit(5).toArray()
let fields
honorsData.forEach((data) => {
skip++
fields = (fields ? fields : '') + (`${skip}. <@${data.userID}> : ${data.honors} Honors\r\n`)
})
const embed = new EmbedBuilder()
.setTitle('Honors Leaderboard')
.setThumbnail(interaction.guild.iconURL({ dynamic: true}))
.setDescription(fields.toString())
.setColor('#5865F2') // Discord's blurple color
.setFooter({ text: 'Use ◄ ► to navigate' });
await interaction.update({
embeds: [embed],
components: [interaction.message.components[0]]
})
}
}
-14
View File
@@ -1,14 +0,0 @@
const {
showNutHub
} = require("../../utils/nutHub");
module.exports = {
name: "nutHubBack",
async execute(interaction) {
return showNutHub(
interaction
);
}
};
-19
View File
@@ -1,19 +0,0 @@
const {
showNutBlackjack
} = require(
"../../utils/nutBlackjack"
);
module.exports = {
name:
"nutHubBlackjack",
async execute(
interaction
) {
return showNutBlackjack(
interaction
);
}
};
-20
View File
@@ -1,20 +0,0 @@
const {
startNutBlackjack
} = require(
"../../utils/nutBlackjack"
);
module.exports = {
name:
"nutBlackjackBet10",
async execute(
interaction
) {
return startNutBlackjack(
interaction,
10
);
}
};
-20
View File
@@ -1,20 +0,0 @@
const {
startNutBlackjack
} = require(
"../../utils/nutBlackjack"
);
module.exports = {
name:
"nutBlackjackBet100",
async execute(
interaction
) {
return startNutBlackjack(
interaction,
100
);
}
};
-20
View File
@@ -1,20 +0,0 @@
const {
startNutBlackjack
} = require(
"../../utils/nutBlackjack"
);
module.exports = {
name:
"nutBlackjackBet1000",
async execute(
interaction
) {
return startNutBlackjack(
interaction,
1000
);
}
};
-20
View File
@@ -1,20 +0,0 @@
const {
startNutBlackjack
} = require(
"../../utils/nutBlackjack"
);
module.exports = {
name:
"nutBlackjackBet50",
async execute(
interaction
) {
return startNutBlackjack(
interaction,
50
);
}
};
-20
View File
@@ -1,20 +0,0 @@
const {
startNutBlackjack
} = require(
"../../utils/nutBlackjack"
);
module.exports = {
name:
"nutBlackjackBet500",
async execute(
interaction
) {
return startNutBlackjack(
interaction,
500
);
}
};
-19
View File
@@ -1,19 +0,0 @@
const {
doubleNutBlackjack
} = require(
"../../utils/nutBlackjack"
);
module.exports = {
name:
"nutBlackjackDouble",
async execute(
interaction
) {
return doubleNutBlackjack(
interaction
);
}
};
-19
View File
@@ -1,19 +0,0 @@
const {
hitNutBlackjack
} = require(
"../../utils/nutBlackjack"
);
module.exports = {
name:
"nutBlackjackHit",
async execute(
interaction
) {
return hitNutBlackjack(
interaction
);
}
};
-19
View File
@@ -1,19 +0,0 @@
const {
standNutBlackjack
} = require(
"../../utils/nutBlackjack"
);
module.exports = {
name:
"nutBlackjackStand",
async execute(
interaction
) {
return standNutBlackjack(
interaction
);
}
};
-19
View File
@@ -1,19 +0,0 @@
const {
showNutHeist
} = require(
"../../utils/nutHeist"
);
module.exports = {
name:
"nutHubHeist",
async execute(
interaction
) {
return showNutHeist(
interaction
);
}
};
-22
View File
@@ -1,22 +0,0 @@
// buttons/nutHub/heistHack.js
const {
performNutHeist
} = require(
"../../utils/nutHeist"
);
module.exports = {
name:
"nutHeistHack",
async execute(
interaction
) {
return performNutHeist(
interaction,
"Hack"
);
}
};
-22
View File
@@ -1,22 +0,0 @@
// buttons/nutHub/heistSmash.js
const {
performNutHeist
} = require(
"../../utils/nutHeist"
);
module.exports = {
name:
"nutHeistSmash",
async execute(
interaction
) {
return performNutHeist(
interaction,
"Smash"
);
}
};
-22
View File
@@ -1,22 +0,0 @@
// buttons/nutHub/heistSneak.js
const {
performNutHeist
} = require(
"../../utils/nutHeist"
);
module.exports = {
name:
"nutHeistSneak",
async execute(
interaction
) {
return performNutHeist(
interaction,
"Sneak"
);
}
};
-21
View File
@@ -1,21 +0,0 @@
// buttons/nutHub/roulette.js
const {
showNutRoulette
} = require(
"../../utils/nutRoulette"
);
module.exports = {
name:
"nutHubRoulette",
async execute(
interaction
) {
return showNutRoulette(
interaction
);
}
};
-15
View File
@@ -1,15 +0,0 @@
// buttons/nutHub/rouletteBet10.js
const {
chooseNutRouletteBet
} = require("../../utils/nutRoulette");
module.exports = {
name: "nutRouletteBet10",
async execute(interaction) {
return chooseNutRouletteBet(
interaction,
10
);
}
};
-15
View File
@@ -1,15 +0,0 @@
// buttons/nutHub/rouletteBet100.js
const {
chooseNutRouletteBet
} = require("../../utils/nutRoulette");
module.exports = {
name: "nutRouletteBet100",
async execute(interaction) {
return chooseNutRouletteBet(
interaction,
100
);
}
};
-15
View File
@@ -1,15 +0,0 @@
// buttons/nutHub/rouletteBet1000.js
const {
chooseNutRouletteBet
} = require("../../utils/nutRoulette");
module.exports = {
name: "nutRouletteBet1000",
async execute(interaction) {
return chooseNutRouletteBet(
interaction,
1000
);
}
};
-15
View File
@@ -1,15 +0,0 @@
// buttons/nutHub/rouletteBet50.js
const {
chooseNutRouletteBet
} = require("../../utils/nutRoulette");
module.exports = {
name: "nutRouletteBet50",
async execute(interaction) {
return chooseNutRouletteBet(
interaction,
50
);
}
};
-15
View File
@@ -1,15 +0,0 @@
// buttons/nutHub/rouletteBet500.js
const {
chooseNutRouletteBet
} = require("../../utils/nutRoulette");
module.exports = {
name: "nutRouletteBet500",
async execute(interaction) {
return chooseNutRouletteBet(
interaction,
500
);
}
};
-15
View File
@@ -1,15 +0,0 @@
// buttons/nutHub/rouletteBlack.js
const {
playNutRoulette
} = require("../../utils/nutRoulette");
module.exports = {
name: "nutRouletteBlack",
async execute(interaction) {
return playNutRoulette(
interaction,
"Black"
);
}
};
-15
View File
@@ -1,15 +0,0 @@
// buttons/nutHub/rouletteEven.js
const {
playNutRoulette
} = require("../../utils/nutRoulette");
module.exports = {
name: "nutRouletteEven",
async execute(interaction) {
return playNutRoulette(
interaction,
"Even"
);
}
};
-15
View File
@@ -1,15 +0,0 @@
// buttons/nutHub/rouletteOdd.js
const {
playNutRoulette
} = require("../../utils/nutRoulette");
module.exports = {
name: "nutRouletteOdd",
async execute(interaction) {
return playNutRoulette(
interaction,
"Odd"
);
}
};
-15
View File
@@ -1,15 +0,0 @@
// buttons/nutHub/rouletteRed.js
const {
playNutRoulette
} = require("../../utils/nutRoulette");
module.exports = {
name: "nutRouletteRed",
async execute(interaction) {
return playNutRoulette(
interaction,
"Red"
);
}
};
-15
View File
@@ -1,15 +0,0 @@
// buttons/nutHub/rouletteZero.js
const {
playNutRoulette
} = require("../../utils/nutRoulette");
module.exports = {
name: "nutRouletteZero",
async execute(interaction) {
return playNutRoulette(
interaction,
"Zero"
);
}
};
-19
View File
@@ -1,19 +0,0 @@
const {
playNutSlots
} = require(
"../../utils/nutSlots"
);
module.exports = {
name:
"nutSlotsBet10",
async execute(
interaction
) {
return playNutSlots(
interaction,
10
);
}
};
-19
View File
@@ -1,19 +0,0 @@
const {
playNutSlots
} = require(
"../../utils/nutSlots"
);
module.exports = {
name:
"nutSlotsBet100",
async execute(
interaction
) {
return playNutSlots(
interaction,
100
);
}
};
-19
View File
@@ -1,19 +0,0 @@
const {
playNutSlots
} = require(
"../../utils/nutSlots"
);
module.exports = {
name:
"nutSlotsBet1000",
async execute(
interaction
) {
return playNutSlots(
interaction,
1000
);
}
};
-19
View File
@@ -1,19 +0,0 @@
const {
playNutSlots
} = require(
"../../utils/nutSlots"
);
module.exports = {
name:
"nutSlotsBet50",
async execute(
interaction
) {
return playNutSlots(
interaction,
50
);
}
};
-19
View File
@@ -1,19 +0,0 @@
const {
playNutSlots
} = require(
"../../utils/nutSlots"
);
module.exports = {
name:
"nutSlotsBet500",
async execute(
interaction
) {
return playNutSlots(
interaction,
500
);
}
};
-21
View File
@@ -1,21 +0,0 @@
// buttons/nutHub/slots.js
const {
showNutSlots
} = require(
"../../utils/nutSlots"
);
module.exports = {
name:
"nutHubSlots",
async execute(
interaction
) {
return showNutSlots(
interaction
);
}
};
-46
View File
@@ -1,46 +0,0 @@
const { EmbedBuilder } = require("discord.js")
const { mClient } = require("../..")
require('dotenv').config()
module.exports = {
name: 'nuts_give_confirm',
description: 'confirm it',
async execute(interaction) {
const from = interaction.user
let temp = await interaction.message.embeds[0].description.split(' ')
const amount = Number(temp[3].split('**')[1])
const to = await interaction.message.guild.members.fetch(temp[6].slice(2,-1))
const db = mClient.db(process.env.M_DB)
const nutsColl = db.collection('items_nuts')
await nutsColl.findOneAndUpdate({ userId: from.id }, { $inc: { nuts: -amount } }, { upsert: true })
await nutsColl.findOneAndUpdate({ userId: to.id }, { $inc: { nuts: +amount } }, { upsert: true })
const yourBalance = await nutsColl.findOne({ userId: from.id })
const theirBalance = await nutsColl.findOne({ userId: to.id })
const embed = new EmbedBuilder()
.setTitle('Gib Nuss!')
.setDescription(`Willst du wirklich **${amount}** Nüsse an <@${to.id}> senden?`)
.addFields(
{ name: `Your Current Balance`, value: `${yourBalance.nuts} (**-${amount}**)`, inline: true },
{ name: `Your New Balance`, value: `${yourBalance.nuts - amount}`, inline: true },
{ name: '\u200B', value: '\u200B' },
{ name: `Their Current Balance`, value: `${theirBalance.nuts} (**+${amount}**)`, inline: true },
{ name: `Their New Balance`, value: `${theirBalance.nuts + amount}`, inline: true }
)
.setThumbnail(to.displayAvatarURL())
await interaction.update({
embeds: [ embed ]
})
const newEmbed = new EmbedBuilder()
.setTitle('Gib Nuss!')
.setDescription(`Du hast **${amount}** Nuts an <@${to.id}> gesendet!`)
.setFooter({ text: 'Um den gleichen Wert zu senden, klick einfach nochmal auf ✔' })
.setThumbnail(to.user.displayAvatarURL())
await interaction.followUp({
embeds: [ newEmbed ],
components: [],
})
}
}
-42
View File
@@ -1,42 +0,0 @@
const { EmbedBuilder } = require("discord.js")
const { mClient } = require("../..")
module.exports = {
name: 'nuts_give_fake',
description: 'fake it',
async execute(interaction) {
const from = interaction.user
let temp = await interaction.message.embeds[0].description.split(' ')
const amount = Number(temp[3].split('**')[1])
const to = await interaction.message.guild.members.fetch(temp[6].slice(2,-1))
const db = mClient.db(process.env.M_DB)
const nutsColl = db.collection('items_nuts')
const yourBalance = await nutsColl.findOne({ userId: from.id })
const theirBalance = await nutsColl.findOne({ userId: to.id })
const embed = new EmbedBuilder()
.setTitle('Gib Nuss!')
.setDescription(`Willst du wirklich **${amount}** Nüsse an <@${to.id}> senden?`)
.addFields(
{ name: `Your Current Balance`, value: `${yourBalance.nuts} (**-${amount}**)`, inline: true },
{ name: `Your New Balance`, value: `${yourBalance.nuts - amount}`, inline: true },
{ name: '\u200B', value: '\u200B' },
{ name: `Their Current Balance`, value: `${theirBalance.nuts} (**+${amount}**)`, inline: true },
{ name: `Their New Balance`, value: `${theirBalance.nuts + amount}`, inline: true }
)
.setThumbnail(to.displayAvatarURL())
await interaction.update({
embeds: [ embed ]
})
const newEmbed = new EmbedBuilder()
.setTitle('Du Fieser Gauner!')
.setDescription(`Yee Haw! Was tust du hier?!`)
.setThumbnail(interaction.client.user.displayAvatarURL())
await interaction.followUp({
embeds: [ newEmbed ],
components: [],
})
}
}
-35
View File
@@ -1,35 +0,0 @@
const { EmbedBuilder } = require("discord.js")
const { mClient } = require("../..")
require('dotenv').config()
module.exports = {
name: 'nuts_leaderboard_left',
description: 'navigate a page up',
async execute(interaction) {
const db = mClient.db(process.env.M_DB)
const nutsColl = db.collection('items_nuts')
const min = 0
let skip = interaction.message.embeds[0].data.description.split('.')
skip = Number(skip[0]) - 6
skip = skip - ( skip % 5)
if ( skip < 4){
skip = 0
}
const nutsData = await nutsColl.find({ nuts: { $gt: 0 } }).sort({ nuts: -1 }).skip(skip).limit(5).toArray()
let fields
nutsData.forEach((data) => {
skip++
fields = (fields ? fields : '') + (`${skip}. <@${data.userId}> : ${data.nuts} Nuts\r\n`)
})
const embed = new EmbedBuilder()
.setTitle('Nuts Leaderboard')
.setThumbnail(interaction.guild.iconURL({ dynamic: true}))
.setDescription(fields.toString())
.setColor('#5865F2') // Discord's blurple color
.setFooter({ text: 'Use ◄ ► to navigate' });
await interaction.update({
embeds: [embed],
components: [interaction.message.components[0]]
})
}
}
-33
View File
@@ -1,33 +0,0 @@
const { EmbedBuilder } = require("discord.js")
const { mClient } = require("../..")
require('dotenv').config()
module.exports = {
name: 'nuts_leaderboard_right',
description: 'navigate a page down',
async execute(interaction) {
const db = mClient.db(process.env.M_DB)
const nutsColl = db.collection('items_nuts')
const max = await nutsColl.countDocuments({ nuts: { $gt: 0 } })
let skip = interaction.message.embeds[0].data.description.split('.')
skip = Number(skip[0]) +4
if (skip >= (max - (max % 5))) {
skip = max - (max % 5)
}
const nutsData = await nutsColl.find({ nuts: { $gt: 0 } }).sort({ nuts: -1 }).skip(skip).limit(5).toArray()
let fields
nutsData.forEach((data) => {
skip++
fields = (fields ? fields : '') + (`${skip}. <@${data.userId}> : ${data.nuts} Nuts\r\n`)
})
const embed = new EmbedBuilder()
.setTitle('Nuts Leaderboard')
.setThumbnail(interaction.guild.iconURL({ dynamic: true}))
.setDescription(fields.toString())
.setColor('#5865F2') // Discord's blurple color
.setFooter({ text: 'Use ◄ ► to navigate' });
await interaction.update({
embeds: [embed],
components: [interaction.message.components[0]]
})
}
}
-43
View File
@@ -1,43 +0,0 @@
const { EmbedBuilder } = require("discord.js")
const { mClient } = require("../..")
require('dotenv').config()
module.exports = {
name: 'nuts_leaderboard_self',
description: 'navigate to your own placement',
async execute(interaction) {
const db = mClient.db(process.env.M_DB)
const nutsColl = db.collection('items_nuts')
const self = await nutsColl.find({ userId: interaction.user.id }).toArray()
const all = await nutsColl.find({ nuts: { $gt: 0 } }).sort({ nuts: -1 }).toArray()
function findIndex(){
let index = 0
for (let i = 0; i < all.length; i++) {
index++
if (all[i].nuts === self[0].nuts) {
return index - 1
}
}
}
let selfIndex = findIndex()
let skip = selfIndex - ( selfIndex % 5)
const nutsData = await nutsColl.find({ nuts: { $gt: 0 } }).sort({ nuts: -1 }).skip(skip).limit(5).toArray()
let fields
nutsData.forEach((data) => {
skip++
fields = (fields ? fields : '') + (`${skip}. <@${data.userId}> : ${data.nuts} Nuts\r\n`)
})
const embed = new EmbedBuilder()
.setTitle('Nuts Leaderboard')
.setThumbnail(interaction.guild.iconURL({ dynamic: true}))
.setDescription(fields.toString())
.setColor('#5865F2') // Discord's blurple color
.setFooter({ text: 'Use ◄ ► to navigate' });
await interaction.update({
embeds: [embed],
components: [interaction.message.components[0]]
})
}
}
-19
View File
@@ -1,19 +0,0 @@
const { quotesLeaderboard } = require("../../commands/slash/applications/quotes");
module.exports = {
name: 'quotes_leaderboard_left',
description: 'Navigate left through the quotes leaderboard',
async execute(interaction) {
await interaction.deferReply();
// Parse current page from footer
let page = 1;
const footerText = interaction.message.embeds[0]?.footer?.text || '';
const match = footerText.match(/Page (\d+) of (\d+)/);
if (match) page = Number(match[1]);
page--; // move to previous page
await quotesLeaderboard(interaction, page);
}
};
@@ -1,19 +0,0 @@
const { quotesLeaderboard } = require("../../commands/slash/applications/quotes");
module.exports = {
name: 'quotes_leaderboard_right',
description: 'Navigate right through the quotes leaderboard',
async execute(interaction) {
await interaction.deferReply();
// Parse current page from footer
let page = 1;
const footerText = interaction.message.embeds[0]?.footer?.text || '';
const match = footerText.match(/Page (\d+) of (\d+)/);
if (match) page = Number(match[1]);
page++; // move to next page
await quotesLeaderboard(interaction, page);
}
};
-82
View File
@@ -1,82 +0,0 @@
const { EmbedBuilder, UserSelectMenuBuilder, ActionRowBuilder, MessageFlags } = require("discord.js")
const { mClient } = require("../..")
require('dotenv').config()
module.exports = {
name: 'quotes_list_left',
description: 'navigate left',
async execute(interaction) {
let oldEmbed = interaction.message.embeds[0].data
if (!oldEmbed.footer) { return interaction.reply({ content: 'Please use a Filter', flags: MessageFlags.Ephemeral }) }
if (oldEmbed.fields && oldEmbed.fields[0].value == 0) { return interaction.reply({ content: 'This User has no Quotes', flags: MessageFlags.Ephemeral }) }
let userID = interaction.message.embeds[0].data.footer.text.split(' ')[5].slice(2, -1)
const target = await interaction.guild.members.fetch(userID)
if (!target) { return interaction.reply({ content: 'Please use a Filter', flags: MessageFlags.Ephemeral }) }
const db = mClient.db(process.env.M_DB)
const quotesColl = db.collection('items_quotes')
const quotes = await quotesColl.find({ userID: target.id }).toArray()
var index = Number((await interaction.message.embeds[0].data.title).split(' ')[2].slice(1))
if (!index) { index = 0 }
index = index - 2
if (index < 1) { index = 0 }
const found = quotes[index]
const guild = await interaction.client.guilds.fetch(found.guildID)
const channel = await guild.channels.fetch(found.channelID)
const message = await channel.messages.fetch(found.messageID)
var msgData = {
content: message.content,
author: await message.author,
reference: await message.reference,
embed: await message.embeds ? 'embed' : null
} // debug
if (msgData.reference) {
let refMessage = await channel.messages.fetch(msgData.reference.messageId)
var refData = {
content: refMessage.content,
author: await refMessage.author,
embed: await refMessage.embeds.length > 0 ? 'embed' : null
}
}
var description = ''
if (refData) {
description += `> ${refData.author}`
if (refData.embed) {
description += '*an embed / a command* '
} else {
description += refData.content
}
description += `\r\n↳`
}
let messageLink = `https://discord.com/channels/${found.guildID}/${found.channelID}/${found.messageID}`
description += `${msgData.author} ${msgData.content}\r\n\r\n${messageLink}`
let timestamp = new Date(message.createdTimestamp)
let footer = timestamp.toDateString().toString()
const embed = new EmbedBuilder()
.setThumbnail(msgData.author.displayAvatarURL())
.setURL(messageLink)
.setTitle(`- Quote #${index + 1} -`)
.setDescription(description)
.setFooter({ text: (`${footer} #${found.messageID} <@${found.userID}>`) })
const select = new ActionRowBuilder()
.addComponents(
new UserSelectMenuBuilder()
.setCustomId('quotes_list_mentionable')
.setPlaceholder('Filter By User')
)
const row = interaction.message.components[1]
await interaction.update({
embeds: [embed],
components: [select, row]
})
}
}
-82
View File
@@ -1,82 +0,0 @@
const { EmbedBuilder, UserSelectMenuBuilder, ActionRowBuilder, MessageFlags } = require("discord.js")
const { mClient } = require("../..")
require('dotenv').config()
module.exports = {
name: 'quotes_list_right',
description: 'navigate right',
async execute(interaction) {
let oldEmbed = interaction.message.embeds[0].data
if (!oldEmbed.footer) { return interaction.reply({ content: 'Please use a Filter', flags: MessageFlags.Ephemeral }) }
if (oldEmbed.fields && oldEmbed.fields[0].value == 0) { return interaction.reply({ content: 'This User has no Quotes', flags: MessageFlags.Ephemeral }) }
let userID = interaction.message.embeds[0].data.footer.text.split(' ')[5].slice(2, -1)
const target = await interaction.guild.members.fetch(userID)
if (!target) { return interaction.reply({ content: 'Please use a Filter', flags: MessageFlags.Ephemeral }) }
const db = mClient.db(process.env.M_DB)
const quotesColl = db.collection('items_quotes')
const quotes = await quotesColl.find({ userID: target.id }).toArray()
var index = Number((await interaction.message.embeds[0].data.title).split(' ')[2].slice(1))
if (!index) { index = 0 }
//if (index >= quotes.length){ return interaction.reply({content: 'Max Quotes Reached', flags: MessageFlags.Ephemeral})}
if (index >= quotes.length) { index-- }
// will automatically reflect the next index due to how arrays think
const found = quotes[index]
const guild = await interaction.client.guilds.fetch(found.guildID)
const channel = await guild.channels.fetch(found.channelID)
const message = await channel.messages.fetch(found.messageID)
var msgData = {
content: message.content,
author: await message.author,
reference: await message.reference,
embed: await message.embeds ? 'embed' : null
} // debug
if (msgData.reference) {
let refMessage = await channel.messages.fetch(msgData.reference.messageId)
var refData = {
content: refMessage.content,
author: await refMessage.author,
embed: await refMessage.embeds.length > 0 ? 'embed' : null
}
}
var description = ''
if (refData) {
description += `> ${refData.author}`
if (refData.embed) {
description += '*an embed / a command* '
} else {
description += refData.content
}
description += `\r\n↳`
}
let messageLink = `https://discord.com/channels/${found.guildID}/${found.channelID}/${found.messageID}`
description += `${msgData.author} ${msgData.content}\r\n\r\n${messageLink}`
let timestamp = new Date(message.createdTimestamp)
let footer = timestamp.toDateString().toString()
const embed = new EmbedBuilder()
.setThumbnail(msgData.author.displayAvatarURL())
.setURL(messageLink)
.setTitle(`- Quote #${index + 1} -`)
.setDescription(description)
.setFooter({ text: (`${footer} #${found.messageID} <@${found.userID}>`) })
const select = new ActionRowBuilder()
.addComponents(
new UserSelectMenuBuilder()
.setCustomId('quotes_list_mentionable')
.setPlaceholder('Filter By User')
)
const row = interaction.message.components[1]
await interaction.update({
embeds: [embed],
components: [select, row]
})
}
}
-9
View File
@@ -1,9 +0,0 @@
const ping = require("../../../utils/ping")
module.exports = {
name: 'ping',
description: 'dev tool',
aliases: ['p'],
async execute(message, args) {
return ping(message, args, 'legacy')
}}
-158
View File
@@ -1,158 +0,0 @@
const {
EmbedBuilder
} = require("discord.js");
const {
getNutBalance
} = require(
"../../../utils/nutEconomy"
);
function delay(ms) {
return new Promise(
resolve =>
setTimeout(
resolve,
ms
)
);
}
module.exports = {
name:
"checknuts",
description:
"get nuts",
aliases: [
"chn"
],
async execute(
message,
args
) {
let target =
message.author;
let addressing =
"Du hast";
if (
args[0]
) {
if (
!args[0].startsWith("<@")
||
!args[0].endsWith(">")
) {
return message.reply(
"Invalid User Specification"
);
}
try {
target =
await message.guild.members.fetch(
args[0].slice(
2,
-1
)
);
} catch (error) {
return message.reply({
content:
`User: ${args[0]} nicht gefunden!`
})
.then(
async msg => {
await delay(
4000
);
message.delete();
msg.delete();
}
);
}
if (
target.user.id !==
message.author.id
) {
if (
!target.user.globalName
) {
addressing =
`${target.user.username} hat`;
}
else {
addressing =
`${target.user.globalName} hat`;
}
}
}
const targetId =
target.id
??
target.user?.id;
const balance =
await getNutBalance(
targetId
);
let content;
if (
balance <=
0
) {
content =
`${addressing} noch keine Nüsse gesammelt :(`;
}
else {
content =
`${addressing} bereits **${balance}** Nüsse gesammelt!`;
}
const embed =
new EmbedBuilder()
.setTitle(
"Checknuts!"
)
.setThumbnail(
target.displayAvatarURL()
)
.setDescription(
content
);
return message.reply({
embeds: [
embed
]
});
}
};
-88
View File
@@ -1,88 +0,0 @@
const {
EmbedBuilder
} = require("discord.js");
const {
getCooldown,
getUnixTime
} = require(
"../../../utils/nutEconomy"
);
module.exports = {
name:
"nutsCooldown",
description:
"shows nut cooldown",
aliases: [
"ncd"
],
async execute(
message,
args
) {
const cooldown =
await getCooldown(
message.author.id,
"nut"
);
const now =
getUnixTime();
let content =
"Du kannst wieder nussen! :)";
let thumbnail =
"https://cdn-icons-png.flaticon.com/512/7451/7451659.png";
let title =
"Go Nuts!";
if (
cooldown >
now
) {
content =
`<t:${cooldown}:R> kannst du wieder nussen! ;)`;
thumbnail =
"https://cdn.discordapp.com/attachments/1152723542836772914/1152987472788193361/No-nuts-PhotoRoom.png-PhotoRoom.png";
title =
"To Nut or Not to Nut...";
}
const embed =
new EmbedBuilder()
.setThumbnail(
thumbnail
)
.setTitle(
title
)
.setDescription(
content
);
await message.reply({
embeds: [
embed
]
});
}
};
-131
View File
@@ -1,131 +0,0 @@
const {
getCollections,
changeNuts,
claimCooldown,
randomInteger
} = require(
"../../../utils/nutEconomy"
);
module.exports = {
name:
"nuts",
description:
"get nuts",
aliases: [
"n",
"nget",
"nutsget"
],
async execute(
message,
args
) {
const {
db
} =
getCollections();
const nutsStatsColl =
db.collection(
"stats_nuts"
);
const cooldown =
await claimCooldown(
message.author.id,
"nut",
60 * 60
);
if (
!cooldown.claimed
) {
return message.reply({
content:
`Du kannst erst <t:${cooldown.next}:R> wieder nussen :(`
});
}
const amount =
randomInteger(
0,
9
);
await changeNuts(
message.author.id,
amount
);
await nutsStatsColl.findOneAndUpdate(
{
userId:
message.author.id
},
{
$inc: {
[`stat.${amount}`]:
1
}
},
{
upsert:
true
}
);
await nutsStatsColl.findOneAndUpdate(
{
userId:
"__global"
},
{
$inc: {
[`stat.${amount}`]:
1
}
},
{
upsert:
true
}
);
const formatNut =
amount =>
`${amount === 1 ? "eine" : amount} ${amount === 1 ? "Nuss" : "Nüsse"}`;
let nutText =
`Du hast ${formatNut(amount)} bekommen! :chestnut:`;
if (
amount <=
0
) {
nutText =
"Du hast keine Nuss bekommen! :yoink_help:";
}
return message.reply({
content:
nutText
});
}
};
-224
View File
@@ -1,224 +0,0 @@
const {
EmbedBuilder
} = require("discord.js");
const {
getNutBalance,
transferNuts
} = require(
"../../../utils/nutEconomy"
);
function delay(ms) {
return new Promise(
resolve =>
setTimeout(
resolve,
ms
)
);
}
module.exports = {
name:
"givenuts",
description:
"give nuts",
aliases: [
"gn"
],
async execute(
message,
args
) {
const from =
message.author;
if (
args[0] ==
`<@${message.author.id}>`
||
!args[0]
) {
return message.reply({
content:
"Du musst einen anderen User erwähnen"
})
.then(
async msg => {
await delay(
4000
);
message.delete();
msg.delete();
}
);
}
if (
!args[0].startsWith("<@")
||
!args[0].endsWith(">")
) {
return message.reply(
"Invalid User Specification"
);
}
let target;
try {
target =
await message.guild.members.fetch(
args[0].slice(
2,
-1
)
);
} catch (error) {
return message.reply({
content:
`User: ${args[0]} nicht gefunden!`
})
.then(
async msg => {
await delay(
4000
);
message.delete();
msg.delete();
}
);
}
const to =
target;
let amount;
try {
amount =
Number(
args[1]
);
} catch (error) {
console.log(
error
);
return message.reply({
content:
"Du musst eine Menge angeben!"
})
.then(
async msg => {
await delay(
4000
);
message.delete();
msg.delete();
}
);
}
if (
!amount
) {
return message.reply({
content:
"Danke für nichts, du Hu-"
});
}
const yourBalance =
await getNutBalance(
from.id
);
if (
amount <=
0
) {
return message.reply({
content:
`Hier wird nicht geklaut >:(\r\nDas gibt **${yourBalance}** Abzug!`
});
}
if (
yourBalance <
amount
) {
return message.reply({
content:
"Du kannst nicht mehr geben als du hast!"
});
}
let content =
`Du sendest **${amount}** Nüsse an <@${target.id}>`;
if (
amount ==
1
) {
content =
`Du sendest **eine** Nuss an <@${target.id}>`;
}
await transferNuts(
from.id,
to.id,
amount
);
const embed =
new EmbedBuilder()
.setTitle(
"Gib Nuss!"
)
.setDescription(
content
)
.setThumbnail(
target.displayAvatarURL()
);
return message.reply({
embeds: [
embed
]
});
}
};
-162
View File
@@ -1,162 +0,0 @@
const {
EmbedBuilder,
ActionRowBuilder,
ButtonBuilder,
ButtonStyle
} = require("discord.js");
const {
getCollections
} = require(
"../../../utils/nutEconomy"
);
module.exports = {
name:
"nutsleaderboard",
description:
"show nuts leaderboard",
aliases: [
"nl",
"tn",
"topnuts"
],
async execute(
message,
args
) {
const row =
new ActionRowBuilder()
.addComponents(
new ButtonBuilder()
.setLabel(
"◄"
)
.setStyle(
ButtonStyle.Primary
)
.setCustomId(
"nuts_leaderboard_left"
),
new ButtonBuilder()
.setLabel(
"🧑"
)
.setStyle(
ButtonStyle.Primary
)
.setCustomId(
"nuts_leaderboard_self"
),
new ButtonBuilder()
.setLabel(
"►"
)
.setStyle(
ButtonStyle.Primary
)
.setCustomId(
"nuts_leaderboard_right"
)
);
const {
nuts
} =
getCollections();
const skip =
0;
const nutsData =
await nuts
.find({
nuts: {
$gt:
0
}
})
.sort({
nuts:
-1
})
.skip(
skip
)
.limit(
5
)
.toArray();
let fields;
let placements =
skip +
1;
nutsData.forEach(
data => {
fields =
(
fields
?
fields
:
""
)
+
`${placements}. <@${data.userId}> : ${data.nuts} Nuts\r\n`;
placements++;
}
);
const embed =
new EmbedBuilder()
.setTitle(
"Nuts Leaderboard"
)
.setThumbnail(
message.guild.iconURL({
dynamic:
true
})
)
.setDescription(
fields.toString()
)
.setColor(
"#5865F2"
)
.setFooter({
text:
"Use ◄ ► to navigate"
});
await message.reply({
embeds: [
embed
],
components: [
row
]
});
}
};
-130
View File
@@ -1,130 +0,0 @@
const { EmbedBuilder, MessageFlags } = require("discord.js");
const { client, mClient } = require("../../../index");
module.exports = {
name: "quotesadd",
description: "Adds a quote to the database by replying to a message.",
aliases: ["qadd", "qa"],
async execute(message) {
// Ensure the command is used as a reply.
if (!message.reference?.messageId) {
return message.reply({
content: "Please reply to the message you want to quote.",
flags: MessageFlags.Ephemeral
});
}
const { messageId, channelId, guildId } = message.reference;
let quoteMessage;
let quoteChannel;
let quoteGuild;
try {
quoteGuild = await client.guilds.fetch(guildId);
quoteChannel = await quoteGuild.channels.fetch(channelId);
quoteMessage = await quoteChannel.messages.fetch(messageId);
} catch (err) {
console.error(err);
return message.reply({
content: "Unable to fetch the quoted message.",
flags: MessageFlags.Ephemeral
});
}
const msgData = {
content: quoteMessage.content,
author: quoteMessage.author,
hasEmbed: quoteMessage.embeds.length > 0,
reference: quoteMessage.reference
};
// Only populated if the quoted message is itself a reply.
let refData = null;
if (msgData.reference?.messageId) {
try {
const refGuild = await client.guilds.fetch(msgData.reference.guildId);
const refChannel = await refGuild.channels.fetch(msgData.reference.channelId);
const refMessage = await refChannel.messages.fetch(msgData.reference.messageId);
refData = {
content: refMessage.content,
author: refMessage.author,
hasEmbed: refMessage.embeds.length > 0
};
} catch (err) {
// Referenced message couldn't be fetched (deleted, inaccessible, etc.)
refData = null;
}
}
const db = mClient.db(process.env.M_DB);
const quotesColl = db.collection("items_quotes");
const existing = await quotesColl.findOne({
messageID: quoteMessage.id
});
if (existing) {
return message.reply({
content: "Quote already exists in the database.",
flags: MessageFlags.Ephemeral
});
}
await quotesColl.findOneAndUpdate(
{ messageID: quoteMessage.id },
{
$set: {
messageID: quoteMessage.id,
channelID: quoteChannel.id,
guildID: quoteGuild.id,
userID: msgData.author.id,
count: 0
}
},
{ upsert: true }
);
const messageLink = `https://discord.com/channels/${quoteGuild.id}/${quoteChannel.id}/${quoteMessage.id}`;
let description = "";
// Only show the replied-to message if one actually exists.
if (refData) {
description += `> ${refData.author} `;
if (refData.hasEmbed) {
description += "*an embed / a command*";
} else if (refData.content) {
description += refData.content;
} else {
description += "*no text*";
}
description += "\n↳ ";
}
description += `${msgData.author} `;
if (msgData.hasEmbed && !msgData.content) {
description += "*an embed / a command*";
} else {
description += msgData.content || "*no text*";
}
description += `\n\n${messageLink}`;
const footer = `${new Date(quoteMessage.createdTimestamp).toDateString()} #${quoteMessage.id}`;
const embed = new EmbedBuilder()
.setTitle("- Quote Added! -")
.setURL(messageLink)
.setThumbnail(msgData.author.displayAvatarURL())
.setDescription(description)
.setFooter({ text: footer });
return message.reply({ embeds: [embed] });
}
};
-201
View File
@@ -1,201 +0,0 @@
const { SlashCommandBuilder, EmbedBuilder, MessageFlags, PermissionFlagsBits } = require('discord.js')
const { mClient } = require('../../../index')
const { configDotenv } = require('dotenv')
configDotenv()
function isValidDate(day, month) {
// Define the number of days in each month
const daysInMonth = {
1: 31, // January
2: 28, // February (fuck leap years, me an' the homies hate that)
3: 31, // March
4: 30, // April
5: 31, // May
6: 30, // June
7: 31, // July
8: 31, // August
9: 30, // September
10: 31, // October
11: 30, // November
12: 31 // December
};
// Check if month is valid
if (month < 1 || month > 12) {
return false;
}
// Check if day is valid for the given month
if (day < 1 || day > daysInMonth[month]) {
return false;
}
return true;
}
function getNextBirthdayTimestamp(day, month) {
const now = new Date();
now.setHours(0, 0, 0, 0);
const currentYear = now.getFullYear();
let nextBirthday = new Date(currentYear, month - 1, day);
// If birthday already passed this year → next year
if (nextBirthday < now) {
nextBirthday.setFullYear(currentYear + 1);
}
return Math.floor(nextBirthday.getTime() / 1000); // Unix timestamp
}
async function birthdaySet(interaction) {
let target = await interaction.options.getUser('user')
let day = await interaction.options.getNumber('day')
let month = await interaction.options.getNumber('month')
if (!isValidDate(day, month)) {
return interaction.reply({ content: "Invalid Date", flags: MessageFlags.Ephemeral })
}
const db = mClient.db(process.env.M_DB)
const bdayColl = db.collection('items_birthdays')
const found = await bdayColl.findOne({ userID: target.id })
if (found) {
return interaction.reply({ content: "Already in Database", flags: MessageFlags.Ephemeral })
}
const data = {
guildID: interaction.guild.id,
userID: target.id,
day: day,
month: month
}
const res = await bdayColl.insertOne(data)
if (res.acknowledged) {
return interaction.reply({ content: "Birthday added successfully!" })
} else {
return interaction.reply({ content: "There was an issue!" })
}
}
async function birthdayDelete(interaction) {
var target = await interaction.options.getUser('user')
const guild = await interaction.client.guilds.cache.get(interaction.guild.id)
if (!target) {
target = await guild.members.cache.get(await interaction.options.getString('id'))
}
if (!target) {
return interaction.reply({ content: "Invalid or No User specified!" })
}
const db = mClient.db(process.env.M_DB)
const bdayColl = db.collection('items_birthdays')
const found = await bdayColl.findOne({ userID: target.id })
if (!found) {
return interaction.reply({ content: "Not yet in Database", flags: MessageFlags.Ephemeral })
}
const res = await bdayColl.deleteOne({ userID: target.id })
if (res.acknowledged) {
return interaction.reply({ content: "Birthday removed successfully!" })
} else {
return interaction.reply({ content: "There was an issue!" })
}
}
async function birthdayGet(interaction) {
const target = await interaction.options.getUser('user');
const db = mClient.db(process.env.M_DB);
const bdayColl = db.collection('items_birthdays');
const found = await bdayColl.findOne({ userID: target.id });
if (!found) {
return interaction.reply({
content: "Date not in Database!",
flags: MessageFlags.Ephemeral
});
}
const now = new Date();
const isToday =
now.getDate() === found.day &&
now.getMonth() === (found.month - 1);
let description;
if (isToday) {
description = `IS TODAY! 🥳🎉`;
} else {
const unix = getNextBirthdayTimestamp(found.day, found.month);
description = `is on the <t:${unix}:d>\n<t:${unix}:R>`;
}
const embed = new EmbedBuilder()
.setThumbnail(target.displayAvatarURL())
.setTitle(`${target.globalName ?? target.username}'s Birthday...`)
.setDescription(description);
await interaction.reply({ embeds: [embed] });
}
module.exports = {
data: new SlashCommandBuilder()
.setName('birthday')
.setDescription('manage birthdays')
.setDefaultMemberPermissions(PermissionFlagsBits.ManageRoles)
.addSubcommand(s =>
s
.setName('set')
.setDescription('set date to birthday')
.addUserOption((option) =>
option
.setName('user')
.setDescription('user')
.setRequired(true))
.addNumberOption((option) =>
option
.setName('day')
.setDescription('day')
.setMinValue(1)
.setMaxValue(31)
.setRequired(true))
.addNumberOption((option) =>
option
.setName('month')
.setDescription('month')
.setMinValue(1)
.setMaxValue(12)
.setRequired(true))
)
.addSubcommand(s =>
s
.setName('delete')
.setDescription('deletes a birthday')
.addUserOption((option) =>
option.setName('user').setDescription('user'))
.addStringOption((option) =>
option.setName('id').setDescription('ID')))
.addSubcommand(s =>
s
.setName('get')
.setDescription('gets a birthday')
.addUserOption((option) =>
option.setName('user').setDescription('user').setRequired(true))),
async execute(interaction) {
switch (interaction.options._subcommand) {
case 'set':
birthdaySet(interaction)
break;
case 'delete':
birthdayDelete(interaction)
break;
case 'get':
birthdayGet(interaction)
break;
default:
break;
}
}
}
-172
View File
@@ -1,172 +0,0 @@
const { SlashCommandBuilder, Events, MessageFlags, PermissionFlagsBits } = require('discord.js')
const { mClient } = require('../../../index')
require('dotenv').config()
async function channelSet(interaction) {
const channel = interaction.options.getChannel('channel')
const purpose = interaction.options.getString('purpose')
const db = mClient.db(process.env.M_DB)
const channelColl = db.collection('config_channels')
await channelColl.findOneAndUpdate({
$and: [{ guildID: channel.guild.id }, { purpose: purpose }]
}, {
$set: {
guildID: channel.guild.id,
purpose: purpose,
channelID: channel.id
}
}, {
upsert: true
})
return await interaction.editReply({
content: `Set <#${channel.id}> as ${purpose} channel!`, flags: MessageFlags.Ephemeral
})
}
async function channelSimulate(interaction) {
const purpose = interaction.options.getString('purpose')
switch (purpose) {
case 'birthday':
await interaction.client.emit('Birthday', interaction.member)
break;
case 'wge':
// For Future WGE Use
break;
case 'welcome':
await interaction.client.emit(Events.GuildMemberAdd, interaction.member)
break;
case 'logs':
// For Future Log Use
break;
default:
break;
}
return interaction.reply({ content: 'Done.', flags: MessageFlags.Ephemeral })
}
async function roleSet(interaction) {
const guild = interaction.guild
const purpose = interaction.options.getString('purpose')
const role = interaction.options.getRole('role')
const db = mClient.db(process.env.M_DB)
const roleColl = db.collection('config_roles')
switch (purpose) {
case 'birthday':
await roleColl.findOneAndUpdate({
$and: [{ guildID: guild.id }, { purpose: purpose }]
}, {
$set: {
guildID: guild.id,
purpose: purpose,
roleID: role.id
}
}, {
upsert: true
})
break;
default:
break;
}
return await interaction.editReply({
content: `Set ${role} as default role for ${purpose} in ${guild} `
})
}
module.exports = {
data: new SlashCommandBuilder()
.setName('config')
.setDescription('configurations for admins')
.setDefaultMemberPermissions(PermissionFlagsBits.ManageChannels)
.addSubcommandGroup(sg =>
sg
.setName('channels')
.setDescription('configure channels for certain events')
.addSubcommand(s =>
s.setName('set')
.setDescription('sets channel')
.addStringOption(o =>
o.setName('purpose').setDescription('set the purpose').setRequired(true).addChoices({
name: 'birthday',
value: 'birthday'
}, {
name: 'welcome',
value: 'welcome'
}, {
name: 'logs',
value: 'logs'
}, {
name: 'wge',
value: 'wge'
}))
.addChannelOption(c =>
c.setName('channel').setDescription('choose a channel').setRequired(true)
)
)
.addSubcommand(s =>
s
.setName('simulate')
.setDescription('simulate a specific channel event')
.addStringOption(o =>
o.setName('purpose').setDescription('set the purpose').setRequired(true).addChoices({
name: 'birthday',
value: 'birthday'
}, {
name: 'welcome',
value: 'welcome'
}, {
name: 'logs',
value: 'logs'
}, {
name: 'wge',
value: 'wge'
}))
)
)
.addSubcommandGroup(sg =>
sg.setName('roles')
.setDescription('configure roles for certain events')
.addSubcommand(s =>
s.setName('set')
.setDescription('sets roles')
.addStringOption(o =>
o.setName('purpose').setDescription('set the purpose').setRequired(true).addChoices({
name: 'birthday',
value: 'birthday'
}))
.addRoleOption(c =>
c.setName('role').setDescription('choose a role').setRequired(true)
)
)
),
async execute(interaction) {
switch (interaction.options._group) {
case 'channels':
switch (interaction.options._subcommand) {
case 'set':
await interaction.deferReply()
channelSet(interaction)
break;
case 'simulate':
channelSimulate(interaction)
break;
default:
break;
}
case 'roles':
switch (interaction.options._subcommand) {
case 'set':
await interaction.deferReply()
roleSet(interaction)
break;
default:
break;
}
default:
break;
}
}
}
-11
View File
@@ -1,11 +0,0 @@
const { SlashCommandBuilder } = require("discord.js");
const ping = require("../../../utils/ping");
module.exports = {
data: new SlashCommandBuilder()
.setName('ping')
.setDescription('dev tool'),
async execute(interaction) {
return ping(interaction, null, 'slash')
}
}
-249
View File
@@ -1,249 +0,0 @@
const {
SlashCommandBuilder,
EmbedBuilder,
ActionRowBuilder,
ButtonBuilder,
ButtonStyle,
MessageFlags
} = require('discord.js');
const { mClient } = require('../../..');
require('dotenv').config()
const db = mClient.db(process.env.M_DB); // adjust if needed
const coll = db.collection('items_ezd');
module.exports = {
data: new SlashCommandBuilder()
.setName('ezd')
.setDescription('Ersti Zweiti Dritti')
.addSubcommand(s =>
s
.setName('show')
.setDescription('Zeigt die täglichen Erster/Zweiter/Dritter Ergebnisse')
)
.addSubcommand(s =>
s
.setName('leaderboard')
.setDescription('Zeigt die Rangliste der EZDler')
)
,
async execute(interaction, client) {
switch (interaction.options._subcommand) {
case 'show':
ezdShow(interaction)
break;
case 'leaderboard':
ezdLeaderboard(interaction)
break;
default:
break;
}
}
};
// helper: shift YYYY-MM-DD safely
function shiftDate(dateStr, offset) {
const d = new Date(dateStr);
d.setDate(d.getDate() + offset);
return d.toISOString().slice(0, 10);
}
async function ezdShow(interaction) {
const today = new Date().toLocaleDateString("en-CA", {
timeZone: "Europe/Berlin"
});
let currentDate = today;
const sessionId = `${interaction.user.id}-${Date.now()}`;
const buildEmbed = (doc, date) => {
const p = doc?.positions || {};
const line = (emoji, label, key) => {
const v = p[key];
return `${emoji} **${label}:** ${v ? `<@${v.userId}>` : "—"}`;
};
const germanDate = new Date(`${date}T00:00:00`).toLocaleDateString("de-DE", {
timeZone: "Europe/Berlin",
day: "2-digit",
month: "2-digit",
year: "numeric"
});
return new EmbedBuilder()
.setTitle(`📊 EZD Übersicht – ${germanDate}`)
.setColor(0x2b2d31)
.setDescription([
line("🥇", "Ersti", "Ersti"),
line("🥈", "Zweiti", "Zweiti"),
line("🥉", "Dritti", "Dritti")
].join("\n"));
};
const buildButtons = (date) => {
return new ActionRowBuilder().addComponents(
new ButtonBuilder()
.setCustomId(`ezd_prev`)
.setLabel("⬅️")
.setStyle(ButtonStyle.Secondary),
new ButtonBuilder()
.setCustomId(`ezd_next`)
.setLabel("➡️")
.setStyle(ButtonStyle.Secondary)
);
};
const doc = await coll.findOne({ date: currentDate });
const msg = await interaction.reply({
embeds: [buildEmbed(doc, currentDate)],
components: [buildButtons(currentDate)]
});
const collector = msg.createMessageComponentCollector({
time: 1000 * 60 * 10 // 10 minutes
});
collector.on("collect", async (btn) => {
if (btn.user.id !== interaction.user.id) {
return btn.reply({
content: "❌ Diese Buttons gehören nicht dir.",
flags: MessageFlags.Ephemeral
});
}
if (btn.customId === `ezd_prev`) {
currentDate = shiftDate(currentDate, -1);
}
if (btn.customId === `ezd_next`) {
currentDate = shiftDate(currentDate, 1);
}
const newDoc = await coll.findOne({ date: currentDate });
await btn.update({
embeds: [buildEmbed(newDoc, currentDate)],
components: [buildButtons(currentDate)]
});
});
}
async function ezdLeaderboard(interaction) {
const docs = await coll.find({}).toArray();
const leaderboard = new Map();
const add = (entry, place) => {
if (!entry?.userId) return;
if (!leaderboard.has(entry.userId)) {
leaderboard.set(entry.userId, {
userId: entry.userId,
points: 0,
Ersti: 0,
Zweiti: 0,
Dritti: 0
});
}
const user = leaderboard.get(entry.userId);
user[place]++;
if (place === "Ersti") user.points += 3;
if (place === "Zweiti") user.points += 2;
if (place === "Dritti") user.points += 1;
};
for (const doc of docs) {
const p = doc.positions || {};
add(p.Ersti, "Ersti");
add(p.Zweiti, "Zweiti");
add(p.Dritti, "Dritti");
}
const users = [...leaderboard.values()].sort((a, b) => {
if (b.points !== a.points) return b.points - a.points;
if (b.Ersti !== a.Ersti) return b.Ersti - a.Ersti;
if (b.Zweiti !== a.Zweiti) return b.Zweiti - a.Zweiti;
return b.Dritti - a.Dritti;
});
const PAGE_SIZE = 5;
const pageCount = Math.max(1, Math.ceil(users.length / PAGE_SIZE));
let page = 0;
const buildEmbed = () => {
const slice = users.slice(
page * PAGE_SIZE,
page * PAGE_SIZE + PAGE_SIZE
);
const description = slice.length
? slice.map((u, i) => {
const rank = page * PAGE_SIZE + i + 1;
return `**#${rank}** <@${u.userId}> — **${u.points}**
🥇 ${u.Ersti} · 🥈 ${u.Zweiti} · 🥉 ${u.Dritti}`;
}).join("\n\n")
: "—";
return new EmbedBuilder()
.setTitle("🏆 EZD Rangliste")
.setColor(0x2b2d31)
.setDescription(description);
};
const buildButtons = () =>
new ActionRowBuilder().addComponents(
new ButtonBuilder()
.setCustomId("ezd_lb_prev")
.setLabel("⬅️")
.setStyle(ButtonStyle.Secondary),
new ButtonBuilder()
.setCustomId("ezd_lb_page")
.setLabel(`${page + 1}/${pageCount}`)
.setStyle(ButtonStyle.Primary)
.setDisabled(true),
new ButtonBuilder()
.setCustomId("ezd_lb_next")
.setLabel("➡️")
.setStyle(ButtonStyle.Secondary)
);
const msg = await interaction.reply({
embeds: [buildEmbed()],
components: [buildButtons()]
});
const collector = msg.createMessageComponentCollector({
time: 1000 * 60 * 10
});
collector.on("collect", async btn => {
if (btn.user.id !== interaction.user.id) {
return btn.reply({
content: "❌ Diese Buttons gehören nicht dir.",
flags: MessageFlags.Ephemeral
});
}
if (btn.customId === "ezd_lb_prev")
page = (page - 1 + pageCount) % pageCount;
if (btn.customId === "ezd_lb_next")
page = (page + 1) % pageCount;
await btn.update({
embeds: [buildEmbed()],
components: [buildButtons()]
});
});
}
-277
View File
@@ -1,277 +0,0 @@
const { SlashCommandBuilder, EmbedBuilder, ActionRowBuilder, ButtonBuilder, ButtonStyle, UserSelectMenuBuilder, MessageFlags } = require('discord.js')
const { mClient } = require('../../../index')
require('dotenv').config()
function delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); }
async function honorHoner(interaction) {
const db = mClient.db(process.env.M_DB)
const honorsColl = db.collection('items_honors')
const target = await interaction.options.getUser('target')
const reason = `[+] ${await interaction.options.getString('reason') ?? 'No reason provided'}`
const theirHonorLevel = await honorsColl.findOne({ userID: target.id })
const embed = new EmbedBuilder()
.setTitle('- Honor Up! -')
.setDescription(`Willst du wirklich <@${target.id}> einen **Honor** geben?\r\n[Grund: ${reason}]`)
.addFields({
name: 'Current Honor Level', value: `${theirHonorLevel?.honors ?? 0} (**+1**)`
})
.setThumbnail(target.displayAvatarURL())
const row = new ActionRowBuilder()
.addComponents(
new ButtonBuilder()
.setLabel('✔')
.setCustomId('honor_multi_confirm')
.setStyle(ButtonStyle.Success)
)
.addComponents(
new ButtonBuilder()
.setLabel('✖')
.setCustomId('abort')
.setStyle(ButtonStyle.Danger)
)
await interaction.reply({
embeds: [embed],
components: [row],
flags: MessageFlags.Ephemeral
})
}
async function honerDishonor(interaction) {
const db = mClient.db(process.env.M_DB)
const honorsColl = db.collection('items_honors')
const target = await interaction.options.getUser('target')
const reason = `[-] ${await interaction.options.getString('reason') ?? 'No reason provided'}`
const theirHonorLevel = await honorsColl.findOne({ userID: target.id })
const embed = new EmbedBuilder()
.setTitle('- Honor Down! -')
.setDescription(`Willst du wirklich <@${target.id}> einen **Dishonor** geben?\r\n[Grund: ${reason}]`)
.addFields({
name: 'Current Honor Level', value: `${theirHonorLevel?.honors ?? 0} (**-1**)`
})
.setThumbnail(target.displayAvatarURL())
const row = new ActionRowBuilder()
.addComponents(
new ButtonBuilder()
.setLabel('✔')
.setCustomId('honor_multi_confirm')
.setStyle(ButtonStyle.Success)
)
.addComponents(
new ButtonBuilder()
.setLabel('✖')
.setCustomId('abort')
.setStyle(ButtonStyle.Danger)
)
await interaction.reply({
embeds: [embed],
components: [row],
flags: MessageFlags.Ephemeral
})
}
async function honorLeaderboard(interaction) {
const row = new ActionRowBuilder()
.addComponents(
new ButtonBuilder()
.setLabel('◄')
.setStyle(ButtonStyle.Primary)
.setCustomId('honors_leaderboard_left')
)
.addComponents(
new ButtonBuilder()
.setLabel('🧑')
.setStyle(ButtonStyle.Primary)
.setCustomId('honors_leaderboard_self')
)
.addComponents(
new ButtonBuilder()
.setLabel('►')
.setStyle(ButtonStyle.Primary)
.setCustomId('honors_leaderboard_right')
)
const db = mClient.db(process.env.M_DB)
const honorsColl = db.collection('items_honors')
let skip = 0
const honorsData = await honorsColl.find({}).sort({ honors: -1 }).skip(skip).limit(5).toArray()
let fields
let placements = skip + 1
honorsData.forEach((data) => {
fields = (fields ? fields : '') + (`${placements}. <@${data.userID}> : ${data.honors} Honors\r\n`)
})
const embed = new EmbedBuilder()
.setTitle('- Honors Leaderboard -')
.setThumbnail(interaction.guild.iconURL({ dynamic: true}))
.setDescription(fields.toString())
.setColor('#5865F2') // Discord's blurple color
.setFooter({ text: 'Use ◄ ► to navigate' });
await interaction.editReply({
embeds: [embed],
components: [row]
})
}
async function honorHistory(interaction) {
var target = interaction.options.getUser('target')
if (!target) { target = interaction.user }
const db = mClient.db(process.env.M_DB)
const reasonsColl = db.collection('history_honors')
var history = await reasonsColl.findOne({ userID: target.id })
const embed = new EmbedBuilder()
.setTitle('- Honor History -')
.setThumbnail(target.displayAvatarURL())
if (history) {
let temp = ''
history.reasons.forEach(reason => {
temp += reason + '\r\n'
})
embed.setDescription(temp)
}
await interaction.editReply({ embeds: [embed] })
}
async function honorCheck(interaction) {
var target = interaction.options.getUser('target')
if (!target) { target = interaction.user }
const db = mClient.db(process.env.M_DB)
const honorsColl = db.collection('items_honors')
var honors = await honorsColl.findOne({ userID: target.id })
honors = honors?.honors ?? 0
if (!honors) {
content = 'Choose a goddamn side!'
} else {
if (honors === -20) {
content = 'They are is at max Dishonor Level! <:lowhonor:748176295132790786> <:lowhonor:748176295132790786> <:lowhonor:748176295132790786>\n```' + `You don't get to live a bad life and have good things happen to you. - Arthur M.` + '```'
} else if (honors === 20) {
content = 'They are at max Honor Level! <:highhonor:748176295535443968> <:highhonor:748176295535443968> <:highhonor:748176295535443968>'
} else if (honors > 0) {
content = 'real good, boah, REAL GOOD! <:highhonor:748176295535443968>'
} else if (honors < 0) {
content = 'What happened to Loyalty?! <:lowhonor:748176295132790786>'
}
}
const embed = new EmbedBuilder()
.setTitle('- Honor Check -')
.setDescription(`${target} has an Honor Level of **${honors}**!`)
.setThumbnail(target.displayAvatarURL())
.addFields({ name: '\u200B', value: `*${content}*` })
await interaction.editReply({
embeds: [embed]
})
}
async function honorMenu(interaction) {
const embed = new EmbedBuilder()
.setTitle('- Honor Menu! -')
.setDescription('- Choose someone from the Select Menu below\r\n- Choose Honor Dishonor, or History\r\n- Confirm your Selection')
.setThumbnail(interaction.guild.iconURL())
const row = new ActionRowBuilder()
.addComponents(
new ButtonBuilder()
.setEmoji(`<:honorhigh:748176295535443968>`)
.setCustomId('honor_menu_honor')
.setStyle(ButtonStyle.Success)
)
.addComponents(
new ButtonBuilder()
.setEmoji(`<:honorlow:748176295132790786>`)
.setCustomId('honor_menu_dishonor')
.setStyle(ButtonStyle.Danger)
)
.addComponents(
new ButtonBuilder()
.setEmoji(`❌`)
.setCustomId('abort')
.setStyle(ButtonStyle.Secondary)
)
.addComponents(
new ButtonBuilder()
.setEmoji('📜')
.setCustomId('honor_menu_history')
.setStyle(ButtonStyle.Secondary)
)
const select = new ActionRowBuilder()
.addComponents(
new UserSelectMenuBuilder()
.setCustomId('honor_menu_mentionable')
.setPlaceholder('Select a User')
)
await interaction.reply({
embeds: [embed],
components: [select, row],
flags: MessageFlags.Ephemeral
})
}
module.exports = {
data: new SlashCommandBuilder()
.setName('honors')
.setDescription('rund um den honor')
.addSubcommand(s =>
s
.setName('honor')
.setDescription('honor someone')
.addUserOption(o => o.setName('target').setDescription('wer wird gehonored?').setRequired(true))
.addStringOption(o => o.setName('reason').setDescription('reason?'))
)
.addSubcommand(s =>
s
.setName('dishonor')
.setDescription('dishonor someone')
.addUserOption(o => o.setName('target').setDescription('wer wird gehonored?').setRequired(true))
.addStringOption(o => o.setName('reason').setDescription('reason?'))
)
.addSubcommand(s =>
s
.setName('check')
.setDescription("check someone's honor level")
.addUserOption(o => o.setName('target').setDescription('wer wird gehonored?'))
)
.addSubcommand(s =>
s
.setName('history')
.setDescription("check someone's honor history")
.addUserOption(o => o.setName('target').setDescription('von wem?'))
)
.addSubcommand(s =>
s
.setName('leaderboard')
.setDescription("check who's got the most honor")
)
.addSubcommand(s =>
s
.setName('menu')
.setDescription('WIP universal honor menu'))
,
async execute(interaction) {
switch (interaction.options._subcommand) {
case 'honor':
honorHoner(interaction)
break;
case 'dishonor':
honerDishonor(interaction)
break;
case 'leaderboard':
await interaction.deferReply()
honorLeaderboard(interaction)
break;
case 'history':
await interaction.deferReply()
honorHistory(interaction)
break;
case 'check':
await interaction.deferReply()
honorCheck(interaction)
break;
case 'menu':
honorMenu(interaction)
break;
default:
break;
}
}
}
-32
View File
@@ -1,32 +0,0 @@
// commands/applications/nutHub.js
const {
SlashCommandBuilder
} = require("discord.js");
const {
showNutHub
} = require(
"../../../utils/nutHub"
);
module.exports = {
data:
new SlashCommandBuilder()
.setName(
"nuthub"
)
.setDescription(
"open the Nut Hub"
),
async execute(
interaction
) {
return showNutHub(
interaction
);
}
};
File diff suppressed because it is too large Load Diff
-426
View File
@@ -1,426 +0,0 @@
/*
Random By Year?
Counter (Leaderboard)
*/
const { SlashCommandBuilder, EmbedBuilder, ActionRowBuilder, ButtonBuilder, ButtonStyle, UserSelectMenuBuilder, MessageFlags } = require('discord.js')
const { mClient, client } = require('../../../index')
require('dotenv').config()
function delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); }
async function quotesAdd(interaction) {
const messageLink = await interaction.options.getString('link')
//regex for message link
const regex = /^https:\/\/discord\.com\/channels\/\d+\/\d+\/\d+$/
if (!messageLink.match(regex)) {
return await interaction.editReply({
content: `Invalid URL`,
flags: MessageFlags.Ephemeral
})
}
let temp = messageLink.split('/')
let messageID = temp[temp.length - 1]
let channelID = temp[temp.length - 2]
let guildID = temp[temp.length - 3]
const guild = await client.guilds.fetch(guildID)
const channel = await guild.channels.fetch(channelID)
const message = await channel.messages.fetch(messageID)
let refData = null
let msgData = {
content: message.content,
author: message.author,
reference: message.reference,
embed: message.embeds.length > 0 ? 'embed' : null
}
if (msgData.reference) {
let refGuild = await client.guilds.fetch(msgData.reference.guildId)
let refChannel = await refGuild.channels.fetch(msgData.reference.channelId)
let refMessage = await refChannel.messages.fetch(msgData.reference.messageId)
refData = {
content: refMessage.content,
author: await refMessage.author,
embed: await refMessage.embeds.length > 0 ? 'embed' : null
}
}
const db = mClient.db(process.env.M_DB)
const quotesColl = db.collection('items_quotes')
const found = await quotesColl.findOne({ messageID: messageID })
if (found) { return await interaction.editReply({ content: 'Quote Already in Database', flags: MessageFlags.Ephemeral }) }
await quotesColl.findOneAndUpdate({ messageID: messageID }, {
$set: {
messageID: messageID,
channelID: channelID,
guildID: guildID,
userID: msgData.author.id,
count: 0
}
}, { upsert: true })
var description = ''
if (refData) {
description += `> ${refData.author}`
if (refData.embed) {
description += '*an embed / a command* '
} else {
description += refData.content
}
description += `\r\n↳`
}
description += `${msgData.author} ${msgData.content}\r\n\r\n${messageLink}`
let timestamp = new Date(message.createdTimestamp)
let footer = timestamp.toDateString().toString()
const embed = new EmbedBuilder()
.setThumbnail(msgData.author.displayAvatarURL())
.setURL(messageLink)
.setTitle(`- Quote Added! -`)
.setDescription(description)
.setFooter({ text: (footer + ' #' + messageID) })
await interaction.editReply({ embeds: [embed] })
}
async function quotesRemove(interaction) {
const id = await interaction.options.getString('id')
const db = mClient.db(process.env.M_DB)
const quotesColl = db.collection('items_quotes')
const found = await quotesColl.findOne({ messageID: id })
if (!found) { return await interaction.reply({ content: 'ID not found!', flags: MessageFlags.Ephemeral }) }
await quotesColl.deleteOne({ messageID: id })
return interaction.reply({ content: 'Quote successfully removed!', flags: MessageFlags.Ephemeral })
}
async function quotesSearch(interaction) {
const id = interaction.options.getString("id")
const db = mClient.db(process.env.M_DB)
const quotesColl = db.collection('items_quotes')
const found = await quotesColl.findOne({ messageID: id })
if (!found) { return await interaction.reply({ content: 'ID not found!', flags: MessageFlags.Ephemeral }) }
const guild = await client.guilds.fetch(found.guildID)
const channel = await guild.channels.fetch(found.channelID)
const message = await channel.messages.fetch(found.messageID)
let refData = null
let msgData = {
content: message.content,
author: message.author,
reference: message.reference,
embed: message.embeds.length > 0 ? 'embed' : null
}
quotesColl.findOneAndUpdate({
messageID: found.messageID
}, {
$inc: { count: 1 }
}, {
upsert: true
})
if (msgData.reference?.messageId) {
let refGuild = await client.guilds.fetch(msgData.reference.guildId)
let refChannel = await refGuild.channels.fetch(msgData.reference.channelId)
let refMessage = await refChannel.messages.fetch(msgData.reference.messageId)
refData = {
content: refMessage.content,
author: await refMessage.author,
embed: await refMessage.embeds.length > 0 ? 'embed' : null
}
}
var description = ''
if (refData) {
description += `> ${refData.author}`
if (refData.embed) {
description += '*an embed / a command* '
} else {
description += refData.content
}
description += `\r\n↳`
}
let messageLink = `https://discord.com/channels/${found.guildID}/${found.channelID}/${found.messageID}`
description += `${msgData.author} ${msgData.content}\r\n\r\n${messageLink}`
let timestamp = new Date(message.createdTimestamp)
let footer = timestamp.toDateString().toString()
const embed = new EmbedBuilder()
.setThumbnail(msgData.author.displayAvatarURL())
.setURL(messageLink)
.setTitle(`- Quote Found! -`)
.setDescription(description)
.setFooter({ text: (footer + ' #' + found.messageID) })
await interaction.reply({ embeds: [embed] })
}
async function quotesRandom(interaction) {
var user = interaction.options.getUser("by")
const db = mClient.db(process.env.M_DB)
const quotesColl = db.collection('items_quotes')
var rdm
if (user) {
rdm = await quotesColl.aggregate([
{
$match: { userID: user.id },
}, {
$sample: { size: 1 }
}
]).toArray()
} else {
rdm = await quotesColl.aggregate([
{
$sample: { size: 1 }
}
]).toArray()
}
const found = rdm[0]
if (!found) { return await interaction.reply({ content: 'No Entry Found.', flags: MessageFlags.Ephemeral }) }
const guild = await client.guilds.fetch(found.guildID)
const channel = await guild.channels.fetch(found.channelID)
const message = await channel.messages.fetch(found.messageID)
let refData = null
let msgData = {
content: message.content,
author: message.author,
reference: message.reference,
embed: message.embeds.length > 0 ? 'embed' : null
}
quotesColl.findOneAndUpdate({
messageID: found.messageID
}, {
$inc: { count: 1 }
}, {
upsert: true
})
if (msgData.reference?.messageId) {
let refGuild = await client.guilds.fetch(msgData.reference.guildId)
let refChannel = await refGuild.channels.fetch(msgData.reference.channelId)
let refMessage = await refChannel.messages.fetch(msgData.reference.messageId)
refData = {
content: refMessage.content,
author: await refMessage.author,
embed: await refMessage.embeds.length > 0 ? 'embed' : null
}
}
var description = ''
if (refData) {
description += `> ${refData.author}`
if (refData.embed) {
description += '*an embed / a command* '
} else {
description += refData.content
}
description += `\r\n↳`
}
let messageLink = `https://discord.com/channels/${found.guildID}/${found.channelID}/${found.messageID}`
description += `${msgData.author} ${msgData.content}\r\n\r\n${messageLink}`
let timestamp = new Date(message.createdTimestamp)
let footer = timestamp.toDateString().toString()
const embed = new EmbedBuilder()
.setThumbnail(msgData.author.displayAvatarURL())
.setURL(messageLink)
.setTitle(`- Quote Found! -`)
.setDescription(description)
.setFooter({ text: (footer + ' #' + found.messageID) })
await interaction.reply({ embeds: [embed] })
}
async function quotesList(interaction) {
const embed = new EmbedBuilder()
.setTitle('- Quotes List! -')
.setDescription('- Choose someone below to filter by User')
.setThumbnail(interaction.guild.iconURL())
const row = new ActionRowBuilder()
.addComponents(
new ButtonBuilder()
.setEmoji(`◀`)
.setCustomId('quotes_list_left')
.setStyle(ButtonStyle.Primary)
)
.addComponents(
new ButtonBuilder()
.setEmoji(`❌`)
.setCustomId('abort')
.setStyle(ButtonStyle.Primary)
)
.addComponents(
new ButtonBuilder()
.setEmoji(`▶`)
.setCustomId('quotes_list_right')
.setStyle(ButtonStyle.Primary)
)
const select = new ActionRowBuilder()
.addComponents(
new UserSelectMenuBuilder()
.setCustomId('quotes_list_mentionable')
.setPlaceholder('Filter by User')
)
await interaction.reply({
embeds: [embed],
components: [select, row],
flags: MessageFlags.Ephemeral
})
}
async function quotesLeaderboard(interaction, page = 1, limit = 5) {
const db = mClient.db(process.env.M_DB);
const quotesColl = db.collection('items_quotes');
// Total quotes in the guild
const totalQuotes = await quotesColl.countDocuments({ guildID: interaction.guild.id, count: { $gte: 0 } });
const totalPages = Math.ceil(totalQuotes / limit);
// Clamp page
if (page < 1) page = 1;
if (page > totalPages) page = totalPages;
const skip = (page - 1) * limit;
// Fetch quotes for this page
const quotesData = await quotesColl
.find({ guildID: interaction.guild.id, count: { $gte: 0 } })
.sort({ count: -1, messageID: -1 })
.skip(skip)
.limit(limit)
.toArray();
// Fallback if no quotes exist
if (!quotesData.length) {
return interaction.editReply({ content: 'No quotes found for this server yet!', components: [] });
}
const guild = await client.guilds.fetch(interaction.guild.id);
// Fetch all messages concurrently
const fields = await Promise.all(
quotesData.map(async (data, index) => {
try {
const channel = await guild.channels.fetch(data.channelID);
const message = await channel.messages.fetch(data.messageID);
const timestamp = new Date(message.createdTimestamp).toLocaleString('en-US', {
dateStyle: 'medium',
timeStyle: 'short'
});
return {
name: `${skip + index + 1}.`,
value: `[#${data.messageID}](https://discord.com/channels/${data.guildID}/${data.channelID}/${data.messageID})\n**by** <@${data.userID}> • ⭐ ${data.count}\n_Posted on **${timestamp}**_\n${message.content}`
};
} catch {
return {
name: `${skip + index + 1}.`,
value: 'Message unavailable.'
};
}
})
);
// Build embed
const embed = new EmbedBuilder()
.setTitle('- Quotes Leaderboard -')
.setThumbnail(guild.iconURL({ dynamic: true }))
.setFields(fields)
.setColor('#5865F2')
.setFooter({ text: `Use ◄ ► to navigate • Page ${page} of ${totalPages}` });
// Build buttons
const row = new ActionRowBuilder()
.addComponents(
new ButtonBuilder()
.setLabel('◄')
.setStyle(ButtonStyle.Primary)
.setCustomId('quotes_leaderboard_left')
.setDisabled(page === 1),
new ButtonBuilder()
.setLabel('X')
.setStyle(ButtonStyle.Danger)
.setCustomId('abort'),
new ButtonBuilder()
.setLabel('►')
.setStyle(ButtonStyle.Primary)
.setCustomId('quotes_leaderboard_right')
.setDisabled(page === totalPages)
);
await interaction.editReply({ embeds: [embed], components: [row] });
}
module.exports = {
data: new SlashCommandBuilder()
.setName('quotes')
.setDescription('quoten pfoten')
.addSubcommand(s =>
s
.setName('add')
.setDescription('add a new quote either by replying to a message or inserting the message link')
.addStringOption(o => o.setName('link').setDescription('insert message link').setRequired(true)))
.addSubcommand(s =>
s
.setName('remove')
.setDescription('remove a quote')
.addStringOption(o => o.setName('id').setDescription('insert message ID').setRequired(true)))
.addSubcommand(s =>
s
.setName('search')
.setDescription('search by ID')
.addStringOption(o => o.setName('id').setDescription('insert message ID').setRequired(true)))
.addSubcommand(s =>
s
.setName('random')
.setDescription('get a random quote')
.addUserOption(o => o.setName('by').setDescription('by user')))
.addSubcommand(s =>
s
.setName('leaderboard')
.setDescription('see the most used quotes'))
.addSubcommand(s =>
s
.setName('list')
.setDescription('[ADMIN] list all quotes')),
async execute(interaction) {
switch (interaction.options._subcommand) {
case 'add':
await interaction.deferReply()
await quotesAdd(interaction)
break;
case 'remove':
awaitquotesRemove(interaction)
break;
case 'search':
awaitquotesSearch(interaction)
break;
case 'random':
awaitquotesRandom(interaction)
break;
case 'leaderboard':
await interaction.deferReply()
awaitquotesLeaderboard(interaction)
break;
case 'list':
await quotesList(interaction)
break;
default:
break;
}
}
},
module.exports.quotesLeaderboard = quotesLeaderboard
-576
View File
@@ -1,576 +0,0 @@
const { ModalBuilder, UserSelectMenuBuilder, TextInputBuilder, EmbedBuilder, TimestampStyles, time, StringSelectMenuBuilder, messageLink } = require("@discordjs/builders");
const { ActionRowBuilder, ButtonBuilder, ButtonStyle, ComponentType } = require('discord.js')
const { SlashCommandBuilder, LabelBuilder, TextInputStyle, MessageFlags } = require("discord.js");
const { mClient } = require("../../..");
const db = mClient.db('neo-db')
require('dotenv').config()
function getRemaining(timer) {
if (timer.state !== 'running') {
return timer.timeRemaining;
}
return Math.max(
0,
timer.timeRemaining - (Date.now() - timer.startedAt)
);
}
function ordinal(n) {
const s = ["th", "st", "nd", "rd"];
const v = n % 100;
return n + (s[(v - 20) % 10] || s[v] || s[0]);
}
async function wgeStatus(interaction) {
const wgeColl = db.collection('timer_wge')
const historyColl = db.collection('history_wge');
const results = await wgeColl.findOne({
$and: [
{ debug: false },
{ state: 'running' }
]
})
if (!results) {
return interaction.editReply({
content: 'No WGE Session currently running!',
flags: MessageFlags.Ephemeral
})
}
const historyCount = await historyColl.countDocuments();
const iteration = historyCount + 1;
const createdAt = new Date(results.createdAt)
const expiresAt = new Date(createdAt.getTime() + Number(results.timeRemaining))
const embed = new EmbedBuilder()
.setTitle(`✨ [${ordinal(iteration)}] World's Greatest Expert ✨`)
.setDescription(
`<@${results.assignedUser}> is currently the **World's Greatest Expert** in **${results.topic}**.\n\n` +
`They must submit their entry <t:${Math.floor(expiresAt.getTime() / 1000)}:R>.\n\n` +
`Thank you, <@${results.assigningUser}>, for the nomination!`
)
return interaction.editReply({
embeds: [embed]
})
}
async function wgeStart(interaction) {
const defaultUsers = []
defaultUsers.push(process.env.D_ID)
const participantColl = db.collection('items_wge')
const participants = await participantColl.find({
status: true
}).toArray()
const wgeBaseModal = new ModalBuilder()
.setCustomId('wgeBaseModal')
.setTitle("World's Greatest Expert")
const options = [
{
label: 'RANDOM',
value: process.env.D_ID
}
];
const participantOptions = await Promise.all(
participants.map(async (participant) => {
try {
const member = await interaction.guild.members.fetch(participant.userID);
return {
label: `${member.displayName} (${member.user.username})`,
value: participant.userID
};
} catch {
return null;
}
})
);
options.push(
...participantOptions.filter(option => option !== null)
);
// Using StringSelectMenuBuilder because UserSelectMenuBuilder cannot be filtered for now
const wgeUserCustInput = new StringSelectMenuBuilder()
.setCustomId('wgeCustUserInput')
.setMaxValues(1)
.setMinValues(1)
.setRequired(true)
.addOptions(options)
const wgeTopicInput = new TextInputBuilder()
.setCustomId('wgeTopicInput')
.setStyle(TextInputStyle.Short)
.setRequired(true)
const wgeTopicLabel = new LabelBuilder()
.setLabel("And WHAT are they an expert in?")
.setDescription('Tell me!')
.setTextInputComponent(wgeTopicInput)
const wgeCustUserLabel = new LabelBuilder()
.setLabel('WHO is your greatest expert?')
.setDescription('Select them')
.setStringSelectMenuComponent(wgeUserCustInput)
wgeBaseModal
.addLabelComponents(wgeCustUserLabel, wgeTopicLabel)
await interaction.showModal(wgeBaseModal)
}
async function wgeStop(interaction) {
;
const wgeColl = db.collection('timer_wge');
const timer = await wgeColl.findOne({
debug: false
});
if (!timer) {
return interaction.reply({
content: 'Timer not found.',
flags: MessageFlags.Ephemeral
});
}
// freeze remaining time
const remaining = getRemaining(timer);
await wgeColl.updateOne(
{ _id: timer._id },
{
$set: {
timeRemaining: remaining,
startedAt: null,
state: 'stopped'
}
}
);
await interaction.reply({
content: `🛑 Timer stopped for **${timer.topic}**`
});
}
async function wgeResume(interaction) {
;
const wgeColl = db.collection('timer_wge');
const timer = await wgeColl.findOne({
debug: false
});
if (!timer) {
return interaction.reply({
content: 'Timer not found.',
flags: MessageFlags.Ephemeral
});
}
// freeze remaining time
const remaining = getRemaining(timer);
await wgeColl.updateOne(
{ _id: timer._id },
{
$set: {
timeRemaining: remaining,
startedAt: Date.now(),
state: 'running'
}
}
);
await interaction.reply({
content: `🟢 Timer resumed for **${timer.topic}**`
});
}
async function wgeForfeit(interaction) {
;
const wgeColl = db.collection('timer_wge');
const participateColl = db.collection('items_wge');
// Get active WGE
const wge = await wgeColl.findOne({
debug: false,
state: 'running'
});
if (!wge) {
return interaction.reply({
content: 'No active WGE is currently running.',
flags: MessageFlags.Ephemeral
});
}
// Sanity check: only assigned user can forfeit
if (wge.assignedUser !== interaction.user.id) {
return interaction.reply({
content: `Only <@${wge.assignedUser}> can forfeit the current WGE.`,
flags: MessageFlags.Ephemeral
});
}
// Pick a new participant, excluding current assignee
const [randomParticipant] = await participateColl.aggregate([
{
$match: {
status: true,
userID: { $ne: interaction.user.id }
}
},
{ $sample: { size: 1 } }
]).toArray();
if (!randomParticipant) {
return interaction.reply({
content: 'No one else is participating!',
flags: MessageFlags.Ephemeral
});
}
const member = await interaction.guild.members.fetch(
randomParticipant.userID
);
const nextUser = member.user;
const WGE_DURATION = 7 * 24 * 60 * 60 * 1000; // 7 days
await wgeColl.deleteOne({ _id: wge._id });
await wgeColl.insertOne({
assignedUser: nextUser.id,
assigningUser: wge.assigningUser,
startedAt: Date.now(),
timeRemaining: WGE_DURATION,
state: 'running',
notifications: {
half: false,
day24: false,
complete: false
},
topic: wge.topic,
debug: false,
createdAt: new Date()
});
return interaction.reply({
content: `${interaction.user} has forfeited! <@${nextUser.id}> is the next expert! Their topic is **${wge.topic}**`
});
}
async function wgeParticipate(interaction) {
const status = interaction.options.getBoolean('status')
const user = interaction.user
const coll = db.collection('items_wge')
// Choice was false, ergo not participating
await coll.findOneAndUpdate({
userID: user.id
}, {
$set: {
userID: user.id,
status: status
}
}, {
upsert: true
})
interaction.reply({
content: `You are ${status ? '' : 'no longer '}participating!`,
flags: MessageFlags.Ephemeral
})
}
async function listParticipants(interaction) {
;
const participantColl = db.collection('items_wge');
const participants = await participantColl.find({}).toArray();
const participating = participants
.filter(p => p.status)
.map(p => `<@${p.userID}>`);
const notParticipating = participants
.filter(p => !p.status)
.map(p => `<@${p.userID}>`);
const embed = new EmbedBuilder()
.setTitle('WGE Participation')
.setColor(0x5865F2)
.addFields(
{
name: `Participating (${participating.length})`,
value: participating.join('\n') || 'No participants found.',
inline: true
},
{
name: `Not Participating (${notParticipating.length})`,
value: notParticipating.join('\n') || 'No entries found.',
inline: true
}
)
.setFooter({
text: `Total entries: ${participants.length}`
})
await interaction.reply({
embeds: [embed],
flags: MessageFlags.Ephemeral
});
}
function formatDuration(ms) {
if (ms == null) return "unknown"
const totalSeconds = Math.floor(ms / 1000)
const days = Math.floor(totalSeconds / 86400)
const hours = Math.floor((totalSeconds % 86400) / 3600)
const minutes = Math.floor((totalSeconds % 3600) / 60)
const seconds = totalSeconds % 60
const parts = []
if (days) parts.push(`${days}d`)
if (hours) parts.push(`${hours}h`)
if (minutes) parts.push(`${minutes}m`)
if (seconds && !days) parts.push(`${seconds}s`) // only show seconds for short durations
return parts.length ? parts.join(" ") : "0s"
}
async function listHistory(interaction) {
const historyColl = db.collection('history_wge')
const wgeHistory = await historyColl
.find({ debug: false })
.sort({ createdAt: -1 })
.toArray()
if (!wgeHistory.length) {
return interaction.reply({
content: "No WGE history found.",
flags: MessageFlags.Ephemeral
})
}
const buildEmbed = (entry, index) => {
const createdTs = entry.createdAt
? Math.floor(new Date(entry.createdAt).getTime() / 1000)
: null
return new EmbedBuilder()
.setTitle(`WGE History (${index + 1}/${wgeHistory.length})`)
.setDescription(
`<@${entry.assignedUser}> was the **World's Greatest Expert** of...\n\n` +
`**${entry.topic}**\n\n` +
`Assigned by: <@${entry.assigningUser}>\n` +
(createdTs ? `Created: <t:${createdTs}:R>` : "")
)
.setColor(0x2b2d31)
}
let page = 0
const row = new ActionRowBuilder().addComponents(
new ButtonBuilder()
.setCustomId('prev')
.setLabel('◀')
.setStyle(ButtonStyle.Secondary)
.setDisabled(true),
new ButtonBuilder()
.setCustomId('next')
.setLabel('▶')
.setStyle(ButtonStyle.Secondary)
.setDisabled(wgeHistory.length <= 1)
)
const msg = await interaction.reply({
embeds: [buildEmbed(wgeHistory[page], page)],
components: [row],
fetchReply: true
})
const collector = msg.createMessageComponentCollector({
componentType: ComponentType.Button,
time: 120000
})
collector.on('collect', async i => {
if (i.user.id !== interaction.user.id) {
return i.reply({
content: "You can't control this pagination.",
flags: MessageFlags.Ephemeral
})
}
if (i.customId === 'prev') {
page--
} else if (i.customId === 'next') {
page++
}
page = Math.max(0, Math.min(page, wgeHistory.length - 1))
row.components[0].setDisabled(page === 0)
row.components[1].setDisabled(page === wgeHistory.length - 1)
await i.update({
embeds: [buildEmbed(wgeHistory[page], page)],
components: [row]
})
})
collector.on('end', async () => {
const disabledRow = new ActionRowBuilder().addComponents(
row.components.map(btn => ButtonBuilder.from(btn).setDisabled(true))
)
await msg.edit({ components: [disabledRow] }).catch(() => { })
})
}
async function wgeList(interaction) {
const thing = interaction.options.getString('thing');
switch (thing) {
case 'participants': {
listParticipants(interaction)
break;
}
case 'history':
listHistory(interaction)
break;
default:
break;
}
}
module.exports = {
data: new SlashCommandBuilder()
.setName('wge')
.setDescription('welcome to worlds greatest experts')
// Show Status of Current Running WGE Session
.addSubcommand(s =>
s
.setName('status')
.setDescription('display current wge session')
)
// Assign Someone to be the next Expert
.addSubcommand(s =>
s
.setName('start')
.setDescription('start a new wge session')
)
// Stop the current Session Timer
.addSubcommand(s =>
s
.setName('stop')
.setDescription('stop the current wge session')
)
// Resume the current Session Timer
.addSubcommand(s =>
s
.setName('resume')
.setDescription('resume the current wge session timer')
)
// Forfeit your current expert topic and assign someone else
.addSubcommand(s =>
s
.setName('forfeit')
.setDescription('forfeit your current topic')
)
// List the previous WGE Sessions
.addSubcommand(s =>
s
.setName('history')
.setDescription('display the previous wge sessions')
)
// List the previous WGE Sessions
.addSubcommand(s =>
s
.setName('participate')
.setDescription('do you want to play a game?')
.addBooleanOption(o =>
o.setName('status')
.setDescription('choose')
.setRequired(true)
)
)
// List the previous WGE Sessions
.addSubcommand(s =>
s
.setName('list')
.setDescription('list things')
.addStringOption(o =>
o.setName('thing')
.setDescription('what would you like to be listed')
.addChoices(
{ name: 'participants', value: 'participants' },
{ name: 'history', value: 'history' }
)
)
)
,
async execute(interaction) {
switch (interaction.options._subcommand) {
case 'status':
await interaction.deferReply()
wgeStatus(interaction)
break;
case 'start':
wgeStart(interaction)
break;
case 'stop':
wgeStop(interaction)
break;
case 'pause':
wgePause(interaction)
break;
case 'resume':
wgeResume(interaction)
break;
case 'forfeit':
wgeForfeit(interaction)
break;
case 'history':
wgeHistory(interaction)
break;
case 'participate':
wgeParticipate(interaction)
break;
case 'list':
wgeList(interaction)
default:
break;
}
}
}
-48
View File
@@ -1,48 +0,0 @@
const { SlashCommandBuilder } = require("discord.js");
function askMyBalls() {
var answers = [
'Absolut, so wie die Authorität unserer Göttin!',
'Unterschätze es nicht, genauso wie Roberts sexual frustration',
'Ich zedaz so',
'Ohne Zweifel',
'Definitiv, Alter',
'Microsoft Outlook gut',
'Das Sternzeichen von Yeesh leuchtet heute stark dafür',
'Mit "Support-Desk am Freitagabend" - Wahrscheinlichkeit',
'Ist die Nase von Julius wunderschön und lang?',
'So wahr wie Laviis Zeichnkünste',
'nein...ja... vielleicht?',
'Frag mich nachher nochmal',
'Die Kalkulationen sind leicht off, ich aber nicht ;)',
'Ich sag es dir ein ander Mal',
'Ohne schwere Zeiten, würden wir die guten Zeiten nie schätzen',
'Genauso schlecht wie eine Ketzerei gegen den Olymp',
'Hat der Fuchs uns jemals verraten?...**hust**',
'Mein Outlook ist abgeschmiert, mist',
'Ich bezweifle es. A propos, zweifel nicht an den Götter und ihren Priester!',
'Meine Quellen sagen.... WARNUNG! Artikel 17: Die betroffene Person hat das Recht, von dem Verantwortlichen zu verlangen, dass sie betreffende personenbezogene Daten unverzüglich gelöscht werden, und der Verantwortliche ist verpflichtet, personenbezogene Daten unverzüglich zu löschen, sofern einer der folgenden Gründe zutrifft **ERROR ERROR ERROR**?'
]
const r = Math.floor(Math.random() * answers.length)
return answers[r]
}
function delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); }
module.exports = {
data: new SlashCommandBuilder()
.setName('8ball')
.setDescription('dont ask me anything')
.addSubcommand(s =>
s.setName('ask')
.setDescription('what do you want to know?')
.addStringOption(
o => o.setName('question').setDescription('ask away!').setRequired(true))
),
async execute(interaction) {
await interaction.deferReply()
await delay(2000)
await interaction.editReply(askMyBalls())
}
}
-83
View File
@@ -1,83 +0,0 @@
const { SlashCommandBuilder, EmbedBuilder, AttachmentBuilder } = require("discord.js");
async function doggoPet(interaction) {
const responses = [
'*loving growl*',
'Danke, das habe ich gebraucht.',
'Ein bisschen tiefer.',
'Ja, genau dort!'
]
var r = Math.floor(Math.random() * responses.length)
const filePath = `assets/Command_Doggo/doggo.jpg`;
const attachment = new AttachmentBuilder(filePath);
const embed = new EmbedBuilder()
.setDescription(responses[r])
.setColor(0x51267)
.setTimestamp()
.setImage(`attachment://doggo.jpg`);
await interaction.editReply({
embeds: [embed],
files: [attachment]
})
}
async function doggoFeed(interaction) {
await interaction.editReply({
content: 'WIP placeholder'
})
}
async function doggoWalk(interaction) {
await interaction.editReply({
content: 'WIP placeholder'
})
}
async function doggoPlay(interaction) {
await interaction.editReply({
content: 'WIP placeholder'
})
}
module.exports = {
data: new SlashCommandBuilder()
.setName('doggo')
.setDescription('who is a good boi?')
.addSubcommand(s =>
s.setName('pet')
.setDescription('beeg scratch')
)
.addSubcommand(s =>
s.setName('feed')
.setDescription('tastge')
)
.addSubcommand(s =>
s.setName('walk')
.setDescription('walkies')
)
.addSubcommand(s =>
s.setName('play')
.setDescription('fun time')
),
async execute(interaction) {
await interaction.deferReply()
switch (interaction.options._subcommand) {
case 'pet':
doggoPet(interaction)
break;
case 'feed':
doggoFeed(interaction)
break;
case 'walk':
doggoWalk(interaction)
break;
case 'play':
doggoPlay(interaction)
break;
default:
break;
}
}
}
-72
View File
@@ -1,72 +0,0 @@
const { SlashCommandBuilder, AttachmentBuilder, EmbedBuilder } = require('discord.js');
const fs = require('fs/promises');
const path = require('path');
async function makeDay() {
const date = new Date();
const tage = [
'sonntag',
'montag',
'dienstag',
'mittwoch',
'donnerstag',
'freitag',
'samstag'
];
const text = {
montag: 'Es ist Montag meine Münmler!',
mittwoch: 'Es ist Mittwoch meine Kerle!',
donnerstag: 'Es ist nicht mehr Mittwoch meine Kerle!',
freitag: 'Es ist Freitag meine Kerl*innen!',
default: 'Es ist nicht Mittwoch, meine Kerle...'
};
const today = tage[date.getDay()];
let files;
try {
files = await fs.readdir('assets/Command_Mittwoch');
} catch (err) {
console.error(err);
return { filename: 'nicht_mittwoch', content: text.default };
}
const available = files.map(f => path.parse(f).name);
if (available.includes(today)) {
return {
filename: today,
content: text[today] || text.default
};
}
return {
filename: 'nicht_mittwoch',
content: text.default
};
}
module.exports = {
data: new SlashCommandBuilder()
.setName('mittwoch')
.setDescription('mittwoch vibes'),
async execute(interaction) {
const { filename, content } = await makeDay();
const filePath = `assets/Command_Mittwoch/${filename}.jpg`;
const attachment = new AttachmentBuilder(filePath);
const embed = new EmbedBuilder()
.setTitle(content)
.setColor(0x51267)
.setTimestamp()
.setImage(`attachment://${filename}.jpg`);
await interaction.reply({
embeds: [embed],
files: [attachment]
});
}
};
-118
View File
@@ -1,118 +0,0 @@
const { createCanvas, loadImage } = require('canvas');
const path = require('node:path');
const { mClient } = require('..');
require('dotenv').configDotenv();
module.exports = {
name: "Birthday",
once: false,
async execute(member, client) {
console.log(`${member.user.username} hat Geburtstag!`)
let found = null
const db = mClient.db(process.env.M_DB)
const channelsColl = db.collection('config_channels')
found = await channelsColl.find(
{
$and: [
{ guildID: member.guild.id },
{ purpose: 'birthday' }
]
}
).toArray()
if (!found) {
return console.log('Channel not yet set for birthday!')
}
const guild = client.guilds.cache.get(found[0].guildID);
const channel = guild.channels.cache.get(found[0].channelID);
const roleColl = db.collection('config_roles')
// Bot Role MUST BE above birthday Role, Birthday Role MUST BE above user roles
found = await roleColl.findOne({guildID: member.guild.id})
const role = guild.roles.cache.get(found.roleID)
try {
await member.roles.add(role)
} catch (error) {
console.error(error)
}
// Create Canvas
let canvasWidth = 600;
let canvasHeight = 250;
const canvas = new createCanvas(canvasWidth, canvasHeight);
const ctx = canvas.getContext('2d');
// Draw Initial Image
const background = await loadImage(
path.join(__dirname, '../assets/Event_Birthday/bdbanner.png')
);
let x = 0;
let y = 0;
ctx.drawImage(background, x, y);
// Create Profile Picture
const pfp = await loadImage(
member.user.displayAvatarURL({
extension: 'jpg',
size: 64,
})
);
// Draw Profile Picture on Top of Background
x = canvas.width / 2 - pfp.width / 2;
y = 20;
ctx.drawImage(pfp, x, y);
// Set styles for text
ctx.font = '35px sans-serif';
ctx.fillStyle = '#FFC0CB'; // Pink text
ctx.strokeStyle = '#000000'; // Black outline
ctx.lineWidth = 3; // Thickness of the outline
let text = `Alles Gute zum Geburtstag! `;
let textWidth = ctx.measureText(text).width;
let textX = canvas.width / 2 - textWidth / 2;
let textHeight = 35; // Approximate height of the text
// Draw the text with black outline
ctx.strokeText(text, textX, 60 + pfp.height);
ctx.fillText(text, textX, 60 + pfp.height);
// requires apt-get install ttf-ancient-fonts
ctx.font = '30px "Noto Emoji", "Segoe UI Emoji", "Apple Color Emoji", sans-serif';
text = `🎉 ${member.user.globalName ? member.user.globalName : member.user.username} 🎉`;
textWidth = ctx.measureText(text).width;
textX = canvas.width / 2 - textWidth / 2;
textHeight = 30; // Approximate height of the text
// Draw the second line of text with black outline
ctx.strokeText(text, textX, 100 + pfp.height);
ctx.fillText(text, textX, 100 + pfp.height);
const banner = canvas.toBuffer();
channel.send({
content: `@here`,
files: [{
attachment: banner,
name: 'banner.png',
description: 'a birthday banner'
}]
});
// mark as notified
const bdayColl = db.collection('items_birthdays')
await bdayColl.findOneAndUpdate({
userID: member.user.id
}, {
$set: {
notified: true
}
},{
upsert: true
})
}
};
-106
View File
@@ -1,106 +0,0 @@
const { Events, ActivityType } = require('discord.js');
const { mClient } = require('..');
module.exports = {
name: Events.ClientReady,
once: true,
async execute(client) {
console.log(`Ready! Logged in as ${client.user.tag}`);
// Set presence
client.user.setPresence({
activities: [{
name: 'Red Dead Depression',
type: ActivityType.Streaming,
url: 'https://twitch.tv/desq_blocki'
}],
status: 'online'
});
// Check if a date matches today
const isToday = (day, month) => {
const today = new Date();
return today.getDate() === day && today.getMonth() === month - 1;
};
// Handle birthdays
const handleBirthdays = async () => {
const guild = await client.guilds.fetch(process.env.D_GuildID);
const db = mClient.db(process.env.M_DB);
const bdayColl = db.collection('items_birthdays');
const allBirthdays = await bdayColl.find().toArray();
const bdayMap = new Map(allBirthdays.map(b => [b.userID, b]));
// Fetch birthday role
const bdayRoleColl = db.collection('config_roles');
const bdayRoleEntry = await bdayRoleColl.findOne({ guildID: guild.id });
const birthdayRoleID = bdayRoleEntry?.roleID;
if (!birthdayRoleID) {
console.warn("No birthday role configured in DB");
return;
}
// Fetch all guild members
const members = await guild.members.fetch();
for (const member of members.values()) {
const dbEntry = bdayMap.get(member.id);
if (!dbEntry) continue;
// Reset notification once the birthday has passed
if (!isToday(dbEntry.day, dbEntry.month) && dbEntry.notified) {
await bdayColl.updateOne(
{ userID: member.id },
{ $set: { notified: false } }
);
dbEntry.notified = false; // keep local copy in sync
console.log(`Removed notified flag for ${member.user.tag}`);
}
// Skip if already handled today
if (dbEntry.notified && isToday(dbEntry.day, dbEntry.month)) {
console.log(`${member.user.username} was already notified today, skipping...`);
continue;
}
if (isToday(dbEntry.day, dbEntry.month)) {
await member.roles.add(birthdayRoleID).catch(err =>
console.error(`Failed to add birthday role to ${member.user.tag}:`, err)
);
console.log(`Added birthday role to ${member.user.tag}`);
client.emit('Birthday', member);
// mark as notified
await bdayColl.updateOne(
{ userID: member.id },
{ $set: { notified: true } }
);
} else {
if (member.roles.cache.has(birthdayRoleID)) {
await member.roles.remove(birthdayRoleID).catch(err =>
console.error(`Failed to remove birthday role from ${member.user.tag}:`, err)
);
console.log(`Removed birthday role from ${member.user.tag}`);
}
}
}
};
await handleBirthdays();
// First check after restart
client.emit('timerCheck');
// Check every minute
setInterval(() => {
client.emit('timerCheck');
}, 60_000);
}
};
-93
View File
@@ -1,93 +0,0 @@
const { Events } = require('discord.js');
const { createCanvas, loadImage } = require('canvas');
const path = require('node:path');
const { mClient } = require('..');
require('dotenv').configDotenv();
module.exports = {
name: Events.GuildMemberAdd,
once: false,
async execute(member, client) {
console.log(`${member.user.username} joined the Server`);
const db = mClient.db(process.env.M_DB)
const channelsColl = db.collection('config_channels')
const found = await channelsColl.find(
{
$and: [
{ guildID: member.guild.id },
{ purpose: 'welcome' }
]
}
).toArray()
if (!found) {
return console.log('Channel not yet set for welcome!')
}
const guild = client.guilds.cache.get(found[0].guildID);
const channel = guild.channels.cache.get(found[0].channelID);
// Create Canvas
let canvasWidth = 600;
let canvasHeight = 250;
const canvas = new createCanvas(canvasWidth, canvasHeight);
const ctx = canvas.getContext('2d');
// Draw Initial Image
const background = await loadImage(
path.join(__dirname, '../assets/Event_MemberJoin/background.png')
);
let x = 0;
let y = 0;
ctx.drawImage(background, x, y);
// Create Profile Picture
const pfp = await loadImage(
member.user.displayAvatarURL({
extension: 'jpg',
size: 64,
})
);
// Draw Profile Picture on Top of Background
x = canvas.width / 2 - pfp.width / 2;
y = 20;
ctx.drawImage(pfp, x, y);
// Set styles for text
ctx.font = '35px sans-serif';
ctx.fillStyle = '#ffffff'; // White text
ctx.strokeStyle = '#000000'; // Black outline
ctx.lineWidth = 3; // Thickness of the outline
let text = `Willkommen, ${member.user.globalName?member.user.globalName:member.user.username}!`;
let textWidth = ctx.measureText(text).width;
let textX = canvas.width / 2 - textWidth / 2;
let textHeight = 35; // Approximate height of the text
// Draw the text with black outline
ctx.strokeText(text, textX, 60 + pfp.height);
ctx.fillText(text, textX, 60 + pfp.height);
ctx.font = '30px sans-serif';
text = `auf dem ✨Olymp✨`;
textWidth = ctx.measureText(text).width;
textX = canvas.width / 2 - textWidth / 2;
textHeight = 30; // Approximate height of the text
// Draw the second line of text with black outline
ctx.strokeText(text, textX, 100 + pfp.height);
ctx.fillText(text, textX, 100 + pfp.height);
const banner = canvas.toBuffer();
channel.send({
content: `Bitte guck einmal in die <#455023824791011338> ${member}`,
files: [{
attachment: banner,
name: 'banner.png',
description: 'a welcome banner'
}]
});
}
};
-316
View File
@@ -1,316 +0,0 @@
const { Events, MessageFlags, Message } = require('discord.js')
const { mClient } = require('../index')
require('dotenv').config()
module.exports = {
name: Events.InteractionCreate,
once: false,
async execute(interaction) {
if (interaction.isChatInputCommand()) { commandHandler(interaction) }
if (interaction.isButton()) { buttonHandler(interaction) }
if (interaction.isMentionableSelectMenu()) { selectMenuHandler(interaction) }
if (interaction.isUserSelectMenu()) { selectMenuHandler(interaction) }
if (interaction.isModalSubmit()) { modalSubmitHandler(interaction) }
}
}
async function commandHandler(interaction) {
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!', flags: MessageFlags.Ephemeral });
} else {
await interaction.reply({ content: 'There was an error while executing this command!', flags: MessageFlags.Ephemeral });
}
}
}
async function buttonHandler(interaction) {
const button = interaction.client.buttons.get(interaction.customId)
if (!button) { return }// console.error(`No button registered matching ${interaction.customId}.`) }
try {
await button.execute(interaction);
} catch (error) {
console.error(error);
if (interaction.replied || interaction.deferred) {
await interaction.followUp({ content: 'There was an error while executing this command!', flags: MessageFlags.Ephemeral });
} else {
await interaction.reply({ content: 'There was an error while executing this command!', flags: MessageFlags.Ephemeral });
}
}
}
async function selectMenuHandler(interaction) {
const selectMenu = interaction.client.selectMenus.get(interaction.customId)
if (!selectMenu) { return console.error(`No Select Menu registered matching ${interaction.customId}.`) }
try {
await selectMenu.execute(interaction);
} catch (error) {
console.error(error);
if (interaction.replied || interaction.deferred) {
await interaction.followUp({ content: 'There was an error while executing this command!', flags: MessageFlags.Ephemeral });
} else {
await interaction.reply({ content: 'There was an error while executing this command!', flags: MessageFlags.Ephemeral });
}
}
}
function getParticipantWeight(participant, now = Date.now()) {
// Never selected before -> strongly favor them
if (!participant.lastExpertAt) {
return 6;
}
const lastExpertAt = new Date(participant.lastExpertAt).getTime();
// Invalid date safety
if (Number.isNaN(lastExpertAt)) {
return 6;
}
const daysSinceExpert = Math.max(
0,
(now - lastExpertAt) / (24 * 60 * 60 * 1000)
);
/*
* Square-root weighting prevents very old participants
* from completely dominating the random selection.
*
* Examples:
* 1 day -> ~1.41
* 7 days -> ~2.83
* 30 days -> ~5.57
*/
return Math.sqrt(daysSinceExpert + 1);
}
function weightedRandom(participants) {
if (!participants.length) {
return null;
}
const weightedParticipants = participants.map(participant => ({
participant,
weight: getParticipantWeight(participant)
}));
const totalWeight = weightedParticipants.reduce(
(total, item) => total + item.weight,
0
);
let random = Math.random() * totalWeight;
for (const item of weightedParticipants) {
random -= item.weight;
if (random <= 0) {
return item.participant;
}
}
// Floating-point safety fallback
return weightedParticipants[weightedParticipants.length - 1].participant;
}
async function selectWgeParticipant(
participateColl,
{ excludeUserId = null } = {}
) {
const filter = {
status: true
};
if (excludeUserId) {
filter.userID = {
$ne: excludeUserId
};
}
const participants = await participateColl
.find(filter)
.toArray();
return weightedRandom(participants);
}
async function modalSubmitHandler(interaction) {
if (interaction.customId !== 'wgeBaseModal') {
return;
}
const db = mClient.db('neo-db');
const wgeColl = db.collection('timer_wge');
const participateColl = db.collection('items_wge');
const historyWgeColl = db.collection('history_wge');
const assigningUser = interaction.user;
const selectedUserId =
interaction.components[0].component.values[0];
const topic = interaction.fields.getTextInputValue(
'wgeTopicInput'
);
const defaultAssignedTime =
7 * 24 * 60 * 60 * 1000;
// Get current running WGE so the previous expert
// cannot immediately be selected again.
const prevWge = await wgeColl.findOne({
debug: false,
state: 'running'
});
let assignedUser;
let selectedParticipant;
// ---------------------------------
// RANDOM / WEIGHTED ASSIGNMENT
// ---------------------------------
if (selectedUserId === process.env.D_ID) {
selectedParticipant = await selectWgeParticipant(
participateColl,
{
excludeUserId: prevWge?.assignedUser
}
);
if (!selectedParticipant) {
return interaction.reply({
content:
'There are no eligible participants available.',
flags: MessageFlags.Ephemeral
});
}
const member = await interaction.guild.members
.fetch(selectedParticipant.userID)
.catch(() => null);
if (!member) {
return interaction.reply({
content:
'The selected participant could not be found in the server.',
flags: MessageFlags.Ephemeral
});
}
assignedUser = member.user;
}
// ---------------------------------
// MANUAL ASSIGNMENT
// ---------------------------------
else {
selectedParticipant = await participateColl.findOne({
userID: selectedUserId,
status: true
});
if (!selectedParticipant) {
return interaction.reply({
content:
`<@${selectedUserId}> is not participating!`,
flags: MessageFlags.Ephemeral
});
}
if (
prevWge &&
selectedUserId === prevWge.assignedUser
) {
return interaction.reply({
content:
`<@${selectedUserId}> was the previous expert and cannot be selected again this round.`,
flags: MessageFlags.Ephemeral
});
}
assignedUser = {
id: selectedUserId
};
}
// ---------------------------------
// ARCHIVE PREVIOUS WGE
// ---------------------------------
if (prevWge) {
await historyWgeColl.insertOne({
assignedUser: prevWge.assignedUser,
assigningUser: prevWge.assigningUser,
timeRemaining: prevWge.timeRemaining,
topic: prevWge.topic,
debug: prevWge.debug,
createdAt: prevWge.createdAt
});
console.log({
message: 'Saved previous WGE to history_wge',
data: prevWge
});
await wgeColl.deleteOne({
_id: prevWge._id
});
}
const assignedAt = new Date();
// ---------------------------------
// UPDATE PARTICIPANT STATISTICS
// ---------------------------------
await participateColl.updateOne(
{
_id: selectedParticipant._id
},
{
$set: {
lastExpertAt: assignedAt
},
$inc: {
expertCount: 1
}
}
);
// ---------------------------------
// CREATE NEW WGE
// ---------------------------------
await wgeColl.insertOne({
assignedUser: assignedUser.id,
assigningUser: assigningUser.id,
startedAt: Date.now(),
timeRemaining: defaultAssignedTime,
state: 'running',
notifications: {
half: false,
day24: false,
complete: false
},
topic,
debug: false,
createdAt: assignedAt
});
return interaction.reply({
content:
`<@${assignedUser.id}> is the next expert! Their topic is **${topic}**`
});
}
-120
View File
@@ -1,120 +0,0 @@
const { Events, MessageFlags } = require("discord.js");
require("dotenv").config();
const { client, mClient } = require("../index");
const prefix = process.env.D_Prefix;
const db = mClient.db(process.env.M_DB);
module.exports = {
name: Events.MessageCreate,
once: false,
async execute(message) {
if (message.author.bot) return;
/*
* Daily number handler
* Only does something if Ersti/Zweiti/Dritti is detected.
*/
if (message.channelId === "353902391021535244") {
const numbered = {
Erst: "Ersti",
Zweit: "Zweiti",
Dritt: "Dritti"
};
const found = Object.entries(numbered).find(([stem]) =>
new RegExp(`\\b\\w*${stem}\\w*\\b`, "i").test(message.content)
)?.[1];
if (found) {
const numberedColl = db.collection("items_ezd");
const today = new Date().toLocaleDateString("en-CA", {
timeZone: "Europe/Berlin"
});
await numberedColl.findOneAndUpdate(
{ date: today },
{
$setOnInsert: {
date: today,
positions: {
Ersti: null,
Zweiti: null,
Dritti: null
}
}
},
{ upsert: true }
);
const result = await numberedColl.findOneAndUpdate(
{
date: today,
[`positions.${found}`]: null,
"positions.Ersti.userId": { $ne: message.author.id },
"positions.Zweiti.userId": { $ne: message.author.id },
"positions.Dritti.userId": { $ne: message.author.id }
},
{
$set: {
[`positions.${found}`]: {
userId: message.author.id,
timestamp: new Date()
}
}
},
{
returnDocument: "after"
}
);
if (!result) {
await message.reply({
content:
"❌ Du kannst pro Tag nur eine Position beanspruchen, und sie muss noch frei sein.",
flags: MessageFlags.Ephemeral
});
} else {
await message.reply({
content: `✅ Du bist ${found}! :point_right: :point_left: <@${message.author.id}>`,
flags: MessageFlags.Ephemeral
});
}
// Only stop here if this was actually a number claim.
return;
}
}
/*
* Command handler
*/
if (!message.content.startsWith(prefix)) return;
const args = message.content
.slice(prefix.length)
.trim()
.split(/ +/);
const cmd = args.shift().toLowerCase();
let command = client.legacyCommands.get(cmd);
if (!command) {
command = client.aliases.get(cmd)
? client.legacyCommands.get(client.aliases.get(cmd))
: null;
}
if (!command) return;
try {
await command.execute(message, args);
} catch (error) {
console.error(error);
}
}
};
-57
View File
@@ -1,57 +0,0 @@
// const { Events, ClientVoiceManager } = require("discord.js");
// const { getVoiceConnection } = require('@discordjs/voice')
// require('dotenv').config()
// const { client } = require('../index');
// const users_in_afk = new Set();
// const delay = ms => new Promise(res => setTimeout(res, ms));
// var connected = false;
// const url = `https://www.youtube.com/watch?v=Y4ySxP_IptE`
// module.exports = {
// name: Events.VoiceStateUpdate,
// once: false,
// async execute(oldState, newState) {
// const guild = client.guilds.cache.get(process.env.D_GuildID)
// const afk_channel = await guild.channels.fetch(guild.afkChannelId)
// const real_afk_channel = await guild.channels.fetch(process.env.D_RealTartarosID)
// const user = await guild.members.fetch(newState.id)
// if(user.id === process.env.D_ID) return;
// // if someone enters the official AFK channel, move them to one where we can play music
// if (newState.channelId === afk_channel.id) {
// await delay(1000)
// await user.voice.setChannel(real_afk_channel)
// users_in_afk.add(newState.id)
// console.log(`${users_in_afk.size} User(s) in Channel`)
// if (!connected) {
// connected = true;
// await client.distube.voices.join(real_afk_channel)
// await user.voice.setSuppressed(true) // mute user?
// await guild.members.me.voice.setSuppressed(false); //unmute self
// await client.distube.play(real_afk_channel, url, {
// member: newState.member,
// })
// //const queue = client.distube.getQueue(guild)
// //await queue.setRepeatMode(2)
// //await queue.setVolume(40)
// }
// }
// // if someone leaves the real AFK channel, remove them from cache but wait for rejoiners, then leave if none are here
// if (oldState.channelId === real_afk_channel.id && oldState.channelId != newState.channelId) {
// users_in_afk.delete(oldState.id)
// const user = await guild.members.fetch(oldState.id)
// //await user.voice.setMute(false) //kann nicht unmuten wenn disconnected ist
// }
// if (users_in_afk.size === 0) {
// //leave if empty
// await client.distube.voices.leave(real_afk_channel)
// connected = false;
// }
// }
// }
-34
View File
@@ -1,34 +0,0 @@
async function getMember(client, userId) {
const guild = client.guilds.cache.get(process.env.D_GuildID);
if (!guild) return null;
// cache first (fast path)
let member = guild.members.cache.get(userId);
// fallback (safe path)
if (!member) {
member = await guild.members.fetch(userId).catch(() => null);
}
return member;
}
module.exports = {
name: 'timer24h',
async execute(timer, client) {
const member = await getMember(client, timer.assignedUser);
if (!member) return;
console.log(`⚠️ Less than 24 hours remaining for ${member.user.username}, notifying...`)
try {
await member.send(
`⚠️ Less than 24 hours remaining for: **${timer.topic}**`
)
console.log('notified successfully!')
} catch (e) {
console.error(`notification failed, errorr ${e}`)
}
}
};
-76
View File
@@ -1,76 +0,0 @@
const { mClient } = require('../index');
function getRemaining(timer) {
if (timer.state !== 'running') {
return timer.timeRemaining;
}
return Math.max(
0,
timer.timeRemaining - (Date.now() - timer.startedAt)
);
}
module.exports = {
name: 'timerCheck',
async execute(client) {
const db = mClient.db('neo-db');
const wgeColl = db.collection('timer_wge');
const timers = await wgeColl.find({
state: { $ne: 'stopped' }
}).toArray();
const now = Date.now();
for (const timer of timers) {
const remaining = getRemaining(timer);
// -------------------------
// HALF TIME (3.5 days left)
// -------------------------
if (
!timer.notifications?.half &&
remaining <= 3.5 * 24 * 60 * 60 * 1000
) {
await wgeColl.updateOne(
{ _id: timer._id },
{ $set: { "notifications.half": true } }
);
client.emit('timerHalf', timer);
}
// -------------------------
// 24H WARNING
// -------------------------
if (
!timer.notifications?.day24 &&
remaining <= 24 * 60 * 60 * 1000
) {
await wgeColl.updateOne(
{ _id: timer._id },
{ $set: { "notifications.day24": true } }
);
client.emit('timer24h', timer);
}
// -------------------------
// COMPLETION
// -------------------------
if (
!timer.notifications?.complete &&
remaining <= 0
) {
await wgeColl.updateOne(
{ _id: timer._id },
{ $set: { "notifications.complete": true } }
);
client.emit('timerComplete', timer);
}
}
}
};
-245
View File
@@ -1,245 +0,0 @@
const { mClient } = require('..');
async function getMember(client, userId) {
const guild = client.guilds.cache.get(process.env.D_GuildID);
if (!guild) {
return null;
}
return (
guild.members.cache.get(userId) ||
await guild.members.fetch(userId).catch(() => null)
);
}
function getParticipantWeight(participant, now = Date.now()) {
// Never selected before -> strongly favor them
if (!participant.lastExpertAt) {
return 6;
}
const lastExpertAt = new Date(participant.lastExpertAt).getTime();
// Invalid date safety
if (Number.isNaN(lastExpertAt)) {
return 6;
}
const daysSinceExpert = Math.max(
0,
(now - lastExpertAt) / (24 * 60 * 60 * 1000)
);
/*
* Square-root weighting prevents very old participants
* from completely dominating the random selection.
*
* Examples:
* 1 day -> ~1.41
* 7 days -> ~2.83
* 30 days -> ~5.57
*/
return Math.sqrt(daysSinceExpert + 1);
}
function weightedRandom(participants) {
if (!participants.length) {
return null;
}
const weightedParticipants = participants.map(participant => ({
participant,
weight: getParticipantWeight(participant)
}));
const totalWeight = weightedParticipants.reduce(
(total, item) => total + item.weight,
0
);
let random = Math.random() * totalWeight;
for (const item of weightedParticipants) {
random -= item.weight;
if (random <= 0) {
return item.participant;
}
}
// Floating-point safety fallback
return weightedParticipants[weightedParticipants.length - 1].participant;
}
async function selectWgeParticipant(
participateColl,
{ excludeUserId = null } = {}
) {
const filter = {
status: true
};
if (excludeUserId) {
filter.userID = {
$ne: excludeUserId
};
}
const participants = await participateColl
.find(filter)
.toArray();
return weightedRandom(participants);
}
module.exports = {
name: 'timerComplete',
async execute(timer, client) {
const db = mClient.db('neo-db');
const wgeColl = db.collection('timer_wge');
const participateColl = db.collection('items_wge');
const channelColl = db.collection('config_channels');
const guild = client.guilds.cache.get(process.env.D_GuildID);
if (!guild) {
console.error('timerComplete: Guild not found.');
return;
}
// Notify previous expert
const previousMember = await getMember(
client,
timer.assignedUser
);
if (previousMember) {
await previousMember.send(
`❌ Timer ran out: **${timer.topic}** ...Re-assigning randomly...`
).catch(error => {
console.error(
`timerComplete: Failed to DM ${timer.assignedUser}:`,
error
);
});
}
// Get configured WGE channel
const wgeChannelConfig = await channelColl.findOne({
purpose: 'wge',
guildID: process.env.D_GuildID
});
if (!wgeChannelConfig) {
console.error(
'timerComplete: WGE channel configuration not found.'
);
return;
}
const channel = client.channels.cache.get(
wgeChannelConfig.channelID
);
if (!channel) {
console.error('timerComplete: WGE channel not found.');
return;
}
// Weighted random selection.
// Previous expert is explicitly excluded.
const selectedParticipant = await selectWgeParticipant(
participateColl,
{
excludeUserId: timer.assignedUser
}
);
if (!selectedParticipant) {
await channel.send({
content:
`The timer for <@${timer.assignedUser}> has run out, ` +
`but there are currently no other participating users available for reassignment.`
});
return;
}
const newMember = await guild.members
.fetch(selectedParticipant.userID)
.catch(() => null);
if (!newMember) {
console.error(
`timerComplete: Could not fetch selected user ${selectedParticipant.userID}.`
);
return;
}
const newAssignedUser = newMember.user;
const wgeDefaultAssignedTime =
7 * 24 * 60 * 60 * 1000;
const assignedAt = new Date();
/*
* Update participant statistics.
*
* $inc automatically creates expertCount with value 1
* if the field does not exist yet.
*/
await participateColl.updateOne(
{
_id: selectedParticipant._id
},
{
$set: {
lastExpertAt: assignedAt
},
$inc: {
expertCount: 1
}
}
);
// Remove the expired timer
await wgeColl.deleteOne({
_id: timer._id
});
// Create new timer
await wgeColl.insertOne({
assignedUser: newAssignedUser.id,
assigningUser: timer.assigningUser,
startedAt: Date.now(),
timeRemaining: wgeDefaultAssignedTime,
state: 'running',
notifications: {
half: false,
day24: false,
complete: false
},
topic: timer.topic,
debug: false,
createdAt: assignedAt
});
await channel.send({
content:
`The timer for <@${timer.assignedUser}> has run out! Assigning randomly...\n` +
`<@${newAssignedUser.id}> is the next expert! Their topic is still **${timer.topic}**`
});
console.log(
`timerComplete: Reassigned WGE from ${timer.assignedUser} to ${newAssignedUser.id}.`
);
}
};
-33
View File
@@ -1,33 +0,0 @@
async function getMember(client, userId) {
const guild = client.guilds.cache.get(process.env.D_GuildID);
if (!guild) return null;
// cache first (fast path)
let member = guild.members.cache.get(userId);
// fallback (safe path)
if (!member) {
member = await guild.members.fetch(userId).catch(() => null);
}
return member;
}
module.exports = {
name: 'timerHalf',
async execute(timer, client) {
const member = await getMember(client, timer.assignedUser);
if (!member) return;
console.log(`⏳ Half time reached for: ${member.user.username}, notifying...`)
try {
await member.send(
`⏳ Half time reached for: **${timer.topic}**`
)
console.log('notified successfully!')
} catch (e) {
console.error(`notification failed, errorr ${e}`)
}
}
};
-20
View File
@@ -1,20 +0,0 @@
const fs = require('node:fs')
const path = require('node:path')
module.exports = (client) => {
const buttonsFoldersPath = path.join(__dirname, '../buttons');
const buttonsFolders = fs.readdirSync(buttonsFoldersPath);
let buttonCount = 0
for (const folder of buttonsFolders) {
const buttonsPath = path.join(buttonsFoldersPath, folder);
const buttonsFiles = fs.readdirSync(buttonsPath).filter(file => file.endsWith('.js'));
for (const file of buttonsFiles) {
buttonCount++
const filePath = path.join(buttonsPath, file);
const button = require(filePath);
if ('execute' in button) {
client.buttons.set(button.name, button);
}
}
}
console.log(`[${buttonCount}] Button(s) initialized`)
}

Some files were not shown because too many files have changed in this diff Show More