initialized discord bot
@ -1,59 +0,0 @@
|
||||
name: Build and Deploy
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
Deploy Web:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
container:
|
||||
image: catthehacker/ubuntu:act-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Use Nodejs
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
- run: npm install
|
||||
working-directory: ./web
|
||||
- run: npm run build
|
||||
working-directory: ./web
|
||||
|
||||
- uses: docker/setup-qemu-action@v2
|
||||
- uses: docker/setup-buildx-action@v2
|
||||
|
||||
- uses: docker/login-action@v2
|
||||
with:
|
||||
registry: gitea.xeovalyte.dev
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
|
||||
- run: docker buildx build -t gitea.xeovalyte.dev/xeovalyte/polarcraft-web:latest --load --platform=linux/amd64 ./web
|
||||
- run: docker push gitea.xeovalyte.dev/xeovalyte/polarcraft-web:latest
|
||||
|
||||
Deploy Discord Bot:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
container:
|
||||
image: catthehacker/ubuntu:act-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Use Nodejs
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
- run: npm install
|
||||
working-directory: ./discord-bot
|
||||
|
||||
- uses: docker/setup-qemu-action@v2
|
||||
- uses: docker/setup-buildx-action@v2
|
||||
|
||||
- uses: docker/login-action@v2
|
||||
with:
|
||||
registry: gitea.xeovalyte.dev
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
|
||||
- run: docker buildx build -t gitea.xeovalyte.dev/xeovalyte/polarcraft-discord:latest --load --platform=linux/amd64 ./discord-bot
|
||||
- run: docker push gitea.xeovalyte.dev/xeovalyte/polarcraft-discord:latest
|
Before Width: | Height: | Size: 663 KiB |
Before Width: | Height: | Size: 601 KiB |
Before Width: | Height: | Size: 899 KiB |
BIN
assets/logo.png
Before Width: | Height: | Size: 984 KiB |
Before Width: | Height: | Size: 6.0 KiB |
@ -1,49 +0,0 @@
|
||||
{
|
||||
"extends": "eslint:recommended",
|
||||
"env": {
|
||||
"node": true,
|
||||
"es6": true
|
||||
},
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 2021
|
||||
},
|
||||
"rules": {
|
||||
"arrow-spacing": ["warn", { "before": true, "after": true }],
|
||||
"brace-style": ["error", "1tbs", { "allowSingleLine": true }],
|
||||
"comma-dangle": ["error", "always-multiline"],
|
||||
"comma-spacing": "error",
|
||||
"comma-style": "error",
|
||||
"curly": ["error", "multi-line", "consistent"],
|
||||
"dot-location": ["error", "property"],
|
||||
"handle-callback-err": "off",
|
||||
"indent": ["error", 2],
|
||||
"keyword-spacing": "error",
|
||||
"max-nested-callbacks": ["error", { "max": 4 }],
|
||||
"max-statements-per-line": ["error", { "max": 2 }],
|
||||
"no-console": "off",
|
||||
"no-empty-function": "error",
|
||||
"no-floating-decimal": "error",
|
||||
"no-inline-comments": "error",
|
||||
"no-lonely-if": "error",
|
||||
"no-multi-spaces": "error",
|
||||
"no-multiple-empty-lines": ["error", { "max": 2, "maxEOF": 1, "maxBOF": 0 }],
|
||||
"no-shadow": ["error", { "allow": ["err", "resolve", "reject"] }],
|
||||
"no-trailing-spaces": ["error"],
|
||||
"no-var": "error",
|
||||
"object-curly-spacing": ["error", "always"],
|
||||
"prefer-const": "error",
|
||||
"quotes": ["error", "single"],
|
||||
"semi": ["error", "always"],
|
||||
"space-before-blocks": "error",
|
||||
"space-before-function-paren": ["error", {
|
||||
"anonymous": "never",
|
||||
"named": "never",
|
||||
"asyncArrow": "always"
|
||||
}],
|
||||
"space-in-parens": "error",
|
||||
"space-infix-ops": "error",
|
||||
"space-unary-ops": "error",
|
||||
"spaced-comment": "error",
|
||||
"yoda": "error"
|
||||
}
|
||||
}
|
2
discord-bot/.gitignore
vendored
@ -1,2 +0,0 @@
|
||||
.env
|
||||
node_modules
|
@ -1,11 +0,0 @@
|
||||
FROM node:18-alpine
|
||||
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN npm install
|
||||
|
||||
EXPOSE 3000
|
||||
|
||||
CMD [ "node", "index.js" ]
|
@ -1,24 +0,0 @@
|
||||
const { SlashCommandBuilder, PermissionFlagsBits } = require('discord.js');
|
||||
|
||||
module.exports = {
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('clear')
|
||||
.setDescription('Clear 1-100 messages')
|
||||
.setDefaultMemberPermissions(PermissionFlagsBits.Administrator)
|
||||
.addNumberOption(option => option
|
||||
.setName('amount')
|
||||
.setDescription('The amount of messages to clear')
|
||||
.setRequired(true)),
|
||||
|
||||
async execute({ interaction, createEmbed, client }) {
|
||||
const amount = interaction.options.getNumber('amount');
|
||||
|
||||
if (amount < 1 || amount > 100) return await interaction.reply({ embeds: [createEmbed.basic('The amount must be between 1-100')] });
|
||||
|
||||
const channel = client.channels.cache.get(interaction.channelId);
|
||||
|
||||
channel.bulkDelete(amount);
|
||||
|
||||
await interaction.reply({ embeds: [createEmbed.basic(`Cleared **${amount}** messages`)], ephemeral: true });
|
||||
},
|
||||
};
|
@ -1,173 +0,0 @@
|
||||
const { SlashCommandBuilder } = require('discord.js');
|
||||
const { QueryType } = require('discord-player');
|
||||
|
||||
module.exports = {
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('music')
|
||||
.setDescription('Play and configure music')
|
||||
.addSubcommand(subcommand => subcommand
|
||||
.setName('play')
|
||||
.setDescription('Play music')
|
||||
.addStringOption(option => option
|
||||
.setName('link-or-query')
|
||||
.setDescription('The song or link you want to play')
|
||||
.setRequired(true)))
|
||||
.addSubcommand(subcommand => subcommand
|
||||
.setName('skip')
|
||||
.setDescription('Skip current song'))
|
||||
.addSubcommand(subcommand => subcommand
|
||||
.setName('stop')
|
||||
.setDescription('Stop current queue'))
|
||||
.addSubcommand(subcommand => subcommand
|
||||
.setName('pause')
|
||||
.setDescription('Pause the current song'))
|
||||
.addSubcommand(subcommand => subcommand
|
||||
.setName('shuffle')
|
||||
.setDescription('Shuffle the queue'))
|
||||
.addSubcommand(subcommand => subcommand
|
||||
.setName('resume')
|
||||
.setDescription('Resume the current song'))
|
||||
.addSubcommand(subcommand => subcommand
|
||||
.setName('filter')
|
||||
.setDescription('Apply a filter')
|
||||
.addStringOption(option => option
|
||||
.setName('type')
|
||||
.setDescription('Type of filter')
|
||||
.setRequired(true)
|
||||
.addChoices(
|
||||
{ name: 'Bassboost', value: 'bassboost_low' },
|
||||
{ name: 'Nightcore', value: 'nightcore' },
|
||||
)))
|
||||
.addSubcommand(subcommand => subcommand
|
||||
.setName('volume')
|
||||
.setDescription('Set volume')
|
||||
.addNumberOption(option => option
|
||||
.setName('percentage')
|
||||
.setDescription('The percentage of the volume between 1 and 100')
|
||||
.setRequired(true))),
|
||||
|
||||
async execute({ interaction, createEmbed, client }) {
|
||||
if (!interaction.member.voice.channelId) return await interaction.reply({ embeds: [createEmbed.basic('You are not in a voice channel!')], ephemeral: true });
|
||||
if (interaction.guild.members.me.voice.channelId && interaction.member.voice.channelId !== interaction.guild.members.me.voice.channelId) {
|
||||
return await interaction.reply({ embeds: [createEmbed.basic('You are not in my voice channel!')], ephemeral: true });
|
||||
}
|
||||
|
||||
|
||||
if (interaction.options.getSubcommand() === 'play') {
|
||||
const query = interaction.options.getString('link-or-query');
|
||||
|
||||
const queue = client.player.createQueue(interaction.guild, {
|
||||
ytdlOptions: {
|
||||
filter: 'audioonly',
|
||||
highWaterMark: 1 << 30,
|
||||
dlChunkSize: 0,
|
||||
},
|
||||
metadata: {
|
||||
channel: interaction.channel,
|
||||
},
|
||||
});
|
||||
|
||||
// verify vc connection
|
||||
try {
|
||||
if (!queue.connection) await queue.connect(interaction.member.voice.channel);
|
||||
} catch {
|
||||
queue.destroy();
|
||||
return await interaction.reply({ embeds: [createEmbed.basic('Could not join your voice channel!')], ephemeral: true });
|
||||
}
|
||||
|
||||
await interaction.reply({ embeds: [createEmbed.basic('Searching...')], ephemeral: true });
|
||||
|
||||
const searchResult = await client.player.search(query, {
|
||||
requestedBy: interaction.user,
|
||||
searchEngine: QueryType.AUTO,
|
||||
});
|
||||
if (!searchResult || !searchResult.tracks[0]) return await interaction.editReply({ embeds: [createEmbed.basic(`Track or playlist **${query}** not found!`)], ephemeral: true });
|
||||
|
||||
searchResult.playlist ? queue.addTracks(searchResult.tracks) : queue.addTrack(searchResult.tracks[0]);
|
||||
if (!queue.playing) await queue.play();
|
||||
|
||||
if (!searchResult.playlist) await interaction.editReply({ embeds: [createEmbed.basic(`Adding track **${searchResult.tracks[0].title}**`)], ephemeral: false });
|
||||
else await interaction.editReply({ embeds: [createEmbed.basic(`Adding **${searchResult.playlist.tracks.length}** tracks from playlist **${searchResult.playlist.title}**`)], ephemeral: false });
|
||||
|
||||
} else if (interaction.options.getSubcommand() === 'skip') {
|
||||
|
||||
const queue = client.player.getQueue(interaction.guild);
|
||||
|
||||
if (!queue || !queue.playing) return await interaction.reply({ embeds: [createEmbed.basic('No music is being played')] });
|
||||
|
||||
const currentTrack = queue.nowPlaying();
|
||||
const success = queue.skip();
|
||||
|
||||
await interaction.reply({ embeds: [createEmbed.basic(success ? `Skipped **${currentTrack.title}**` : 'Something went wrong')] });
|
||||
} else if (interaction.options.getSubcommand() === 'volume') {
|
||||
const vol = interaction.options.getNumber('percentage');
|
||||
|
||||
const queue = client.player.getQueue(interaction.guild);
|
||||
|
||||
if (!queue || !queue.playing) return await interaction.reply({ embeds: [createEmbed.basic('No music is being played')] });
|
||||
|
||||
if (vol < 1 || vol > 100) return interaction.reply({ embeds: [createEmbed.basic('Volume must be between 1 and 100')] });
|
||||
|
||||
const success = queue.setVolume(vol);
|
||||
|
||||
await interaction.reply({ embeds: [createEmbed.basic(success ? `Volume set to **${vol}%**` : 'Something went wrong')] });
|
||||
} else if (interaction.options.getSubcommand() === 'stop') {
|
||||
|
||||
const queue = client.player.getQueue(interaction.guild);
|
||||
|
||||
if (!queue || !queue.playing) return await interaction.reply({ embeds: [createEmbed.basic('No music is being played')] });
|
||||
|
||||
queue.destroy();
|
||||
|
||||
await interaction.reply({ embeds: [createEmbed.basic('Stopped the player')] });
|
||||
} else if (interaction.options.getSubcommand() === 'pause') {
|
||||
|
||||
const queue = client.player.getQueue(interaction.guild);
|
||||
|
||||
if (!queue || !queue.playing) return await interaction.reply({ embeds: [createEmbed.basic('No music is being played')] });
|
||||
|
||||
queue.setPaused(true);
|
||||
|
||||
await interaction.reply({ embeds: [createEmbed.basic('Paused the player')] });
|
||||
} else if (interaction.options.getSubcommand() === 'resume') {
|
||||
|
||||
const queue = client.player.getQueue(interaction.guild);
|
||||
|
||||
if (!queue || !queue.playing) return await interaction.reply({ embeds: [createEmbed.basic('No music is being played')] });
|
||||
|
||||
queue.setPaused(false);
|
||||
|
||||
await interaction.reply({ embeds: [createEmbed.basic('Resumed the player')] });
|
||||
} else if (interaction.options.getSubcommand() === 'filter') {
|
||||
const filter = interaction.options.getString('type');
|
||||
|
||||
const queue = client.player.getQueue(interaction.guild);
|
||||
|
||||
if (!queue || !queue.playing) return await interaction.reply({ embeds: [createEmbed.basic('No music is being played')] });
|
||||
|
||||
if (filter === 'bassboost_low') {
|
||||
await queue.setFilters({
|
||||
bassboost_low: !queue.getFiltersEnabled().includes('bassboost_low'),
|
||||
normalizer2: !queue.getFiltersEnabled().includes('bassboost_low'),
|
||||
});
|
||||
}
|
||||
|
||||
if (filter === 'nightcore') {
|
||||
await queue.setFilters({
|
||||
nightcore: !queue.getFiltersEnabled().includes('nightcore'),
|
||||
});
|
||||
}
|
||||
|
||||
await interaction.reply({ embeds: [createEmbed.basic(queue.getFiltersEnabled().includes(filter) ? `Applied **${filter}** filter` : `Removed **${filter}** filter`)] });
|
||||
} else if (interaction.options.getSubcommand() === 'shuffle') {
|
||||
|
||||
const queue = client.player.getQueue(interaction.guild);
|
||||
|
||||
if (!queue || !queue.playing) return await interaction.reply({ embeds: [createEmbed.basic('No music is being played')] });
|
||||
|
||||
const success = queue.shuffle();
|
||||
|
||||
await interaction.reply({ embeds: [createEmbed.basic(success ? 'The queue has been shuffled' : 'Something went wrong')] });
|
||||
}
|
||||
},
|
||||
};
|
@ -1,12 +0,0 @@
|
||||
const { SlashCommandBuilder } = require('discord.js');
|
||||
|
||||
module.exports = {
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('ping')
|
||||
.setDescription('Replies with Pong!'),
|
||||
|
||||
async execute({ interaction, createEmbed, client }) {
|
||||
const reply = await interaction.reply({ embeds: [createEmbed.basic(`Websocket heartbeat: **${client.ws.ping}ms**\n Roundtrip latency: **Pinging...**`)], fetchReply: true, ephemeral: true });
|
||||
interaction.editReply({ embeds: [createEmbed.basic(`Websocket heartbeat: **${client.ws.ping}ms**\n Roundtrip latency: **${reply.createdTimestamp - interaction.createdTimestamp}ms**`)], emphemeral: true });
|
||||
},
|
||||
};
|
@ -1,70 +0,0 @@
|
||||
const { SlashCommandBuilder, PermissionsBitField, ChannelType, ButtonStyle, ButtonBuilder, ActionRowBuilder } = require('discord.js');
|
||||
|
||||
module.exports = {
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('ticket')
|
||||
.setDescription('Commands for managing a ticket')
|
||||
.addSubcommand(subcommand => subcommand
|
||||
.setName('create')
|
||||
.setDescription('Create a ticket with you and moderators'))
|
||||
.addSubcommand(subcommand => subcommand
|
||||
.setName('close')
|
||||
.setDescription('Close current ticket')),
|
||||
|
||||
async execute({ interaction, createEmbed }) {
|
||||
if (interaction.options.getSubcommand() === 'create') {
|
||||
await interaction.reply({ embeds: [createEmbed.basic('Creating ticket channel...')], fetchReply: true, ephemeral: true });
|
||||
|
||||
const ticketChannel = await interaction.member.guild.channels.create({
|
||||
name: `ticket-${interaction.user.username}`,
|
||||
type: ChannelType.GuildText,
|
||||
parent: process.env.TICKET_CATEGORY_ID,
|
||||
permissionOverwrites: [
|
||||
{
|
||||
id: interaction.guild.id,
|
||||
deny: [PermissionsBitField.Flags.ViewChannel],
|
||||
},
|
||||
{
|
||||
id: process.env.MODERATOR_ROLE_ID,
|
||||
allow: [PermissionsBitField.Flags.ViewChannel],
|
||||
},
|
||||
{
|
||||
id: interaction.user.id,
|
||||
allow: [PermissionsBitField.Flags.ViewChannel],
|
||||
},
|
||||
],
|
||||
|
||||
});
|
||||
|
||||
await ticketChannel.send({ embeds: [createEmbed.basic(`${interaction.user} created a ticket`)] });
|
||||
|
||||
interaction.editReply({ embeds: [createEmbed.basic(`${ticketChannel} has been created`)], emphemeral: true });
|
||||
|
||||
} else if (interaction.options.getSubcommand() === 'close') {
|
||||
|
||||
if (!interaction.channel.name.startsWith('ticket')) return interaction.reply({ embeds: [createEmbed.basic('You must execute this command inside a ticket channel')], emphemeral: true });
|
||||
|
||||
const cancelRow = new ActionRowBuilder()
|
||||
.addComponents(
|
||||
new ButtonBuilder()
|
||||
.setCustomId('cancel')
|
||||
.setLabel('Cancel')
|
||||
.setStyle(ButtonStyle.Danger),
|
||||
);
|
||||
|
||||
const closeMessage = await interaction.reply({ embeds: [createEmbed.basic('Closing ticket in 10 seconds...')], components: [cancelRow] });
|
||||
const collector = closeMessage.createMessageComponentCollector();
|
||||
|
||||
const timeout = setTimeout(() => {
|
||||
try {
|
||||
interaction.channel.delete();
|
||||
} catch {}
|
||||
}, 10000);
|
||||
|
||||
collector.on('collect', async () => {
|
||||
clearTimeout(timeout);
|
||||
await closeMessage.edit({ embeds: [createEmbed.basic('Ticket closing has been stopped')], components: [] });
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
@ -1,17 +0,0 @@
|
||||
const { Events } = require('discord.js');
|
||||
|
||||
module.exports = {
|
||||
name: Events.MessageCreate,
|
||||
async execute({ log }, message) {
|
||||
|
||||
if (message.channelId === process.env.MINECRAFT_CHANNEL_ID && !message.author.bot) {
|
||||
await fetch(process.env.WEB_HOST + '/api/minecraft/message/chattominecraft', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
content: message.content,
|
||||
discordId: message.author.id,
|
||||
}),
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
@ -1,17 +0,0 @@
|
||||
const { Events, EmbedBuilder } = require('discord.js');
|
||||
|
||||
module.exports = {
|
||||
name: Events.GuildMemberAdd,
|
||||
async execute({ client, log }, member) {
|
||||
log.Info(`${member.user.username} has joined`);
|
||||
|
||||
const newMemberEmbed = new EmbedBuilder()
|
||||
.setTitle(`${member.user.username} has joined!`)
|
||||
.setDescription(`Welcome ${member} to the **Polarcraft** Discord server!`)
|
||||
.setColor(process.env.EMBED_COLOR)
|
||||
.setThumbnail(member.user.avatarURL());
|
||||
|
||||
const channel = await client.channels.cache.get(process.env.LOG_CHANNEL_ID);
|
||||
channel.send({ embeds: [newMemberEmbed] });
|
||||
},
|
||||
};
|
@ -1,15 +0,0 @@
|
||||
const { Events, EmbedBuilder } = require('discord.js');
|
||||
|
||||
module.exports = {
|
||||
name: Events.GuildMemberRemove,
|
||||
async execute({ client, log }, member) {
|
||||
log.Info(`${member.user.username} has left`);
|
||||
|
||||
const newMemberEmbed = new EmbedBuilder()
|
||||
.setTitle(`${member.user.username} has left!`)
|
||||
.setColor(process.env.EMBED_COLOR);
|
||||
|
||||
const channel = await client.channels.cache.get(process.env.LOG_CHANNEL_ID);
|
||||
channel.send({ embeds: [newMemberEmbed] });
|
||||
},
|
||||
};
|
@ -1,26 +0,0 @@
|
||||
const { Events } = require('discord.js');
|
||||
|
||||
module.exports = {
|
||||
name: Events.InteractionCreate,
|
||||
async execute({ log, createEmbed, client }, interaction) {
|
||||
|
||||
if (interaction.isChatInputCommand()) {
|
||||
const command = interaction.client.commands.get(interaction.commandName);
|
||||
|
||||
if (!command) {
|
||||
return log.Error(`No command matching ${interaction.commandName} was found`);
|
||||
} else {
|
||||
log.Info(`${interaction.user.username} executed command ${interaction.commandName}`);
|
||||
}
|
||||
|
||||
try {
|
||||
await command.execute({ interaction, log, createEmbed, client });
|
||||
} catch (error) {
|
||||
log.Error(error);
|
||||
|
||||
await interaction.reply({ content: 'There was an error while executing this command!', ephemeral: true });
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
};
|
@ -1,11 +0,0 @@
|
||||
const { Events } = require('discord.js');
|
||||
|
||||
module.exports = {
|
||||
name: Events.ClientReady,
|
||||
once: true,
|
||||
execute({ client, log }) {
|
||||
log.Info(`Ready! Logged in as ${client.user.tag}`);
|
||||
|
||||
require('../functions/registerCommands.js').registerCommands(log);
|
||||
},
|
||||
};
|
@ -1,12 +0,0 @@
|
||||
const { EmbedBuilder } = require('discord.js');
|
||||
const dotenv = require('dotenv');
|
||||
|
||||
dotenv.config();
|
||||
|
||||
const basic = (description) => {
|
||||
return new EmbedBuilder()
|
||||
.setColor(process.env.EMBED_COLOR)
|
||||
.setDescription(description);
|
||||
};
|
||||
|
||||
module.exports = { basic };
|
@ -1,31 +0,0 @@
|
||||
const { EmbedBuilder } = require('discord.js');
|
||||
|
||||
const registerEvents = ({ client }) => {
|
||||
|
||||
client.player.on('trackStart', (queue, track) => {
|
||||
|
||||
const nowPlayingEmbed = new EmbedBuilder()
|
||||
.setTitle('Now Playing')
|
||||
.setColor(process.env.EMBED_COLOR)
|
||||
.setDescription(`[${track.title}](${track.url})`)
|
||||
.addFields(
|
||||
{ name: 'Requested By', value: `${track.requestedBy}`, inline: true },
|
||||
{ name: 'Duration', value: track.duration, inline: true },
|
||||
{ name: 'Queue', value: `${queue.tracks.length} song(s)`, inline: true },
|
||||
{ name: 'Author', value: track.author, inline: true },
|
||||
{ name: 'Source', value: track.source, inline: true },
|
||||
{ name: 'Volume', value: `${queue.options.initialVolume}%`, inline: true },
|
||||
{ name: 'Filters', value: queue.getFiltersEnabled().join('\n') || 'No filters active', inline: false },
|
||||
)
|
||||
.setThumbnail(track.thumbnail)
|
||||
.setTimestamp();
|
||||
|
||||
queue.metadata.channel.send({ embeds: [nowPlayingEmbed] });
|
||||
});
|
||||
|
||||
client.player.on('error', (queue, error) => {
|
||||
console.log(error);
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = { registerEvents };
|
@ -1,48 +0,0 @@
|
||||
const { REST, Routes } = require('discord.js');
|
||||
const fs = require('node:fs');
|
||||
const dotenv = require('dotenv');
|
||||
|
||||
dotenv.config();
|
||||
|
||||
const registerCommands = (log) => {
|
||||
const commands = [];
|
||||
// Grab all the command files from the commands directory you created earlier
|
||||
const commandFiles = fs.readdirSync('./commands').filter(file => file.endsWith('.js'));
|
||||
|
||||
// Grab the SlashCommandBuilder#toJSON() output of each command's data for deployment
|
||||
for (const file of commandFiles) {
|
||||
const command = require(`../commands/${file}`);
|
||||
commands.push(command.data.toJSON());
|
||||
}
|
||||
|
||||
// Construct and prepare an instance of the REST module
|
||||
const rest = new REST({ version: '10' }).setToken(process.env.DISCORD_TOKEN);
|
||||
|
||||
// and deploy your commands!
|
||||
(async () => {
|
||||
try {
|
||||
log.Info(`Started refreshing ${commands.length} application (/) commands.`);
|
||||
|
||||
// The put method is used to fully refresh all commands in the guild with the current set
|
||||
let data = null;
|
||||
if (process.env.GUILD_ID !== 'production') {
|
||||
data = await rest.put(
|
||||
Routes.applicationGuildCommands(process.env.CLIENT_ID, process.env.GUILD_ID),
|
||||
{ body: commands },
|
||||
);
|
||||
} else {
|
||||
data = await rest.put(
|
||||
Routes.applicationGuildCommands(process.env.CLIENT_ID),
|
||||
{ body: commands },
|
||||
);
|
||||
}
|
||||
|
||||
log.Info(`Successfully reloaded ${data.length} application (/) commands.`);
|
||||
} catch (error) {
|
||||
// And of course, make sure you catch and log any errors!
|
||||
log.Error(error);
|
||||
}
|
||||
})();
|
||||
};
|
||||
|
||||
module.exports = { registerCommands };
|
@ -1,76 +0,0 @@
|
||||
const chalk = require('chalk');
|
||||
const { Client, GatewayIntentBits, Collection } = require('discord.js');
|
||||
const { Player } = require('discord-player');
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
const dotenv = require('dotenv');
|
||||
const express = require('express');
|
||||
|
||||
const createEmbed = require('./functions/createEmbed.js');
|
||||
dotenv.config();
|
||||
|
||||
const log = {
|
||||
Info: (message) => console.log(chalk.blue('INFO'), message),
|
||||
Error: (message) => console.log(chalk.red('ERROR'), message),
|
||||
Warn: (message) => console.log(chalk.hex('#FFA500'), message),
|
||||
};
|
||||
|
||||
// Register client and music events
|
||||
const client = new Client({ intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildVoiceStates, GatewayIntentBits.GuildMembers, GatewayIntentBits.GuildMessages, GatewayIntentBits.MessageContent] });
|
||||
client.player = new Player(client);
|
||||
|
||||
require('./functions/player.js').registerEvents({ client, createEmbed });
|
||||
|
||||
|
||||
// Express
|
||||
const app = express();
|
||||
app.use(express.json());
|
||||
|
||||
const minecraftRoute = require('./routes/Minecraft');
|
||||
const userRoute = require('./routes/User');
|
||||
const teamRoute = require('./routes/Team');
|
||||
|
||||
app.use('/minecraft', minecraftRoute);
|
||||
app.use('/user', userRoute);
|
||||
app.use('/team', teamRoute);
|
||||
|
||||
app.listen('4000', () => {
|
||||
log.Info('Express app running');
|
||||
});
|
||||
|
||||
|
||||
// Command handling
|
||||
client.commands = new Collection();
|
||||
|
||||
const commandsPath = path.join(__dirname, 'commands');
|
||||
const commandFiles = fs.readdirSync(commandsPath).filter(file => file.endsWith('.js'));
|
||||
|
||||
for (const file of commandFiles) {
|
||||
const filePath = path.join(commandsPath, file);
|
||||
const command = require(filePath);
|
||||
|
||||
if ('data' in command && 'execute' in command) {
|
||||
client.commands.set(command.data.name, command);
|
||||
} else {
|
||||
log.Warn(`The command at ${filePath} is missing a required "data" or "execute" property.`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Event handling
|
||||
const eventsPath = path.join(__dirname, 'events');
|
||||
const eventFiles = fs.readdirSync(eventsPath).filter(file => file.endsWith('.js'));
|
||||
|
||||
for (const file of eventFiles) {
|
||||
const filePath = path.join(eventsPath, file);
|
||||
const event = require(filePath);
|
||||
if (event.once) {
|
||||
client.once(event.name, (...args) => event.execute({ client, log, createEmbed }, ...args));
|
||||
} else {
|
||||
client.on(event.name, (...args) => event.execute({ client, log, createEmbed }, ...args));
|
||||
}
|
||||
}
|
||||
|
||||
client.login(process.env.DISCORD_TOKEN);
|
||||
|
||||
module.exports.client = client;
|
3323
discord-bot/package-lock.json
generated
@ -1,25 +0,0 @@
|
||||
{
|
||||
"type": "commonjs",
|
||||
"name": "discord-bot",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"lint": "eslint .",
|
||||
"lint-fix": "eslint . --fix"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@discord-player/extractor": "^3.0.2",
|
||||
"@discordjs/opus": "^0.8.0",
|
||||
"chalk": "^4.1.2",
|
||||
"discord-player": "^5.3.2",
|
||||
"discord.js": "^14.7.1",
|
||||
"distube": "^4.0.4",
|
||||
"dotenv": "^16.0.3",
|
||||
"eslint": "^8.29.0",
|
||||
"express": "^4.18.2",
|
||||
"ytdl-core": "^4.11.4"
|
||||
}
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
const { WebhookClient, EmbedBuilder } = require('discord.js');
|
||||
const express = require('express');
|
||||
|
||||
const router = express.Router();
|
||||
const webhookClient = new WebhookClient({ url: process.env.MINECRAFT_WEBHOOK_URL });
|
||||
|
||||
router.post('/sendchatmessage', (req, res) => {
|
||||
const { content, username, avatarURL } = req.body;
|
||||
|
||||
if (!username || !content || !avatarURL) return res.status(400).send({ error: 'Content, username and avatar_url are required' });
|
||||
|
||||
webhookClient.send({
|
||||
content: content,
|
||||
username: username,
|
||||
avatarURL: avatarURL,
|
||||
});
|
||||
|
||||
res.send({ data: 'Test data' });
|
||||
});
|
||||
|
||||
router.post('/sendgamemessage', (req, res) => {
|
||||
const { content, avatarURL } = req.body;
|
||||
|
||||
if (!content || !avatarURL) return res.status(400).send({ error: 'Content, username and avatar_url are required' });
|
||||
|
||||
const messageEmbed = new EmbedBuilder()
|
||||
.setColor(process.env.EMBED_COLOR)
|
||||
.setAuthor({ name: content, iconURL: avatarURL });
|
||||
|
||||
webhookClient.send({
|
||||
embeds: [messageEmbed],
|
||||
username: 'Server',
|
||||
});
|
||||
|
||||
res.send({ data: 'Test data' });
|
||||
});
|
||||
|
||||
module.exports = router;
|
@ -1,145 +0,0 @@
|
||||
const express = require('express');
|
||||
const index = require('../index');
|
||||
const { PermissionsBitField, ChannelType } = require('discord.js');
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
router.post('/createchannels', async (req, res) => {
|
||||
const { name, discordId } = req.body;
|
||||
|
||||
if (!name || !discordId) return res.status(400).send({ error: 'Name en discordId zijn vereist' });
|
||||
|
||||
try {
|
||||
const guild = await index.client.guilds.fetch(process.env.GUILD_ID);
|
||||
|
||||
const category = await guild.channels.fetch(process.env.TEAM_CATEGORY_ID);
|
||||
|
||||
const member = await guild.members.fetch(discordId);
|
||||
|
||||
const textChannel = await guild.channels.create({
|
||||
name: name,
|
||||
type: ChannelType.GuildText,
|
||||
parent: category,
|
||||
permissionOverwrites: [
|
||||
{
|
||||
id: guild.id,
|
||||
deny: [PermissionsBitField.Flags.ViewChannel],
|
||||
},
|
||||
{
|
||||
id: member.id,
|
||||
allow: [PermissionsBitField.Flags.ViewChannel],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const voiceChannel = await guild.channels.create({
|
||||
name: name,
|
||||
type: ChannelType.GuildVoice,
|
||||
parent: category,
|
||||
permissionOverwrites: [
|
||||
{
|
||||
id: guild.id,
|
||||
deny: [PermissionsBitField.Flags.ViewChannel],
|
||||
},
|
||||
{
|
||||
id: member.id,
|
||||
allow: [PermissionsBitField.Flags.ViewChannel],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
res.send({ textChannel, voiceChannel });
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
return res.status(500).send({ error: 'Error tijdens het maken van discord channels' });
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
router.post('/deletechannels', async (req, res) => {
|
||||
const { textChannelId, voiceChannelId } = req.body;
|
||||
|
||||
if (!textChannelId, !voiceChannelId) return res.status(400).send({ error: 'textChannelId en voiceChannelId zijn vereist' });
|
||||
|
||||
try {
|
||||
const guild = await index.client.guilds.fetch(process.env.GUILD_ID);
|
||||
|
||||
const textChannel = await guild.channels.fetch(textChannelId);
|
||||
const voiceChannel = await guild.channels.fetch(voiceChannelId);
|
||||
|
||||
await textChannel.delete();
|
||||
await voiceChannel.delete();
|
||||
|
||||
res.send({ status: 'success' });
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
return res.status(500).send({ error: 'Error tijdens het verwijderen van discord channels' });
|
||||
}
|
||||
});
|
||||
|
||||
router.post('/removeteammember', async (req, res) => {
|
||||
const { textChannelId, voiceChannelId, discordId } = req.body;
|
||||
|
||||
if (!textChannelId, !voiceChannelId, !discordId) return res.status(400).send({ error: 'textChannelId, voiceChannelId en discordId zijn vereist' });
|
||||
|
||||
try {
|
||||
const guild = await index.client.guilds.fetch(process.env.GUILD_ID);
|
||||
const member = await guild.members.fetch(discordId);
|
||||
|
||||
const textChannel = await guild.channels.fetch(textChannelId);
|
||||
const voiceChannel = await guild.channels.fetch(voiceChannelId);
|
||||
|
||||
await textChannel.permissionOverwrites.delete(member);
|
||||
await voiceChannel.permissionOverwrites.delete(member);
|
||||
|
||||
res.send({ status: 'success' });
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
return res.status(500).send({ error: 'Error tijdens het verwijderen van een team member' });
|
||||
}
|
||||
});
|
||||
|
||||
router.post('/addteammember', async (req, res) => {
|
||||
const { textChannelId, voiceChannelId, discordId } = req.body;
|
||||
|
||||
if (!textChannelId, !voiceChannelId, !discordId) return res.status(400).send({ error: 'textChannelId, voiceChannelId en discordId zijn vereist' });
|
||||
|
||||
try {
|
||||
const guild = await index.client.guilds.fetch(process.env.GUILD_ID);
|
||||
const member = await guild.members.fetch(discordId);
|
||||
|
||||
const textChannel = await guild.channels.fetch(textChannelId);
|
||||
const voiceChannel = await guild.channels.fetch(voiceChannelId);
|
||||
|
||||
await textChannel.permissionOverwrites.edit(member, { ViewChannel: true });
|
||||
await voiceChannel.permissionOverwrites.edit(member, { ViewChannel: true });
|
||||
|
||||
res.send({ status: 'success' });
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
return res.status(500).send({ error: 'Error tijdens het toevoegen van team member' });
|
||||
}
|
||||
});
|
||||
|
||||
router.post('/edit', async (req, res) => {
|
||||
const { textChannelId, voiceChannelId, name } = req.body;
|
||||
|
||||
if (!textChannelId, !voiceChannelId, !name) return res.status(400).send({ error: 'textChannelId, voiceChannelId en name zijn vereist' });
|
||||
|
||||
try {
|
||||
const guild = await index.client.guilds.fetch(process.env.GUILD_ID);
|
||||
|
||||
const textChannel = await guild.channels.fetch(textChannelId);
|
||||
const voiceChannel = await guild.channels.fetch(voiceChannelId);
|
||||
|
||||
await textChannel.edit({ name: name });
|
||||
await voiceChannel.edit({ name: name });
|
||||
|
||||
res.send({ status: 'success' });
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
return res.status(500).send({ error: 'Error tijds het veranderen van Discord channel naam' });
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
@ -1,47 +0,0 @@
|
||||
const express = require('express');
|
||||
const index = require('../index');
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
router.post('/changenickname', async (req, res) => {
|
||||
const { nickname, discordId } = req.body;
|
||||
|
||||
if (!nickname || !discordId) return res.status(400).send({ error: 'Nickname en discordId zijn vereist' });
|
||||
|
||||
const nick = nickname.length > 32 ? nickname.slice(0, 32) : nickname;
|
||||
|
||||
try {
|
||||
const guild = await index.client.guilds.fetch(process.env.GUILD_ID);
|
||||
|
||||
const member = await guild.members.fetch(discordId);
|
||||
|
||||
await member.edit({ nick: nick });
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
return res.status(500).send({ error: 'Error tijds het veranderen van de nickname' });
|
||||
}
|
||||
|
||||
res.send({ status: 'success' });
|
||||
});
|
||||
|
||||
router.post('/ban', async (req, res) => {
|
||||
const { discordId, reason } = req.body;
|
||||
|
||||
if (!reason || !discordId) return res.status(400).send({ error: 'Reason en discordId zijn vereist' });
|
||||
|
||||
try {
|
||||
const guild = await index.client.guilds.fetch(process.env.GUILD_ID);
|
||||
|
||||
const member = await guild.members.fetch(discordId);
|
||||
|
||||
await member.ban({ deleteMessageSeconds: 24 * 3600, reason: reason });
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
return res.status(500).send({ error: 'Error tijds het bannen van gebruiker' });
|
||||
}
|
||||
|
||||
res.send({ status: 'success' });
|
||||
});
|
||||
|
||||
|
||||
module.exports = router;
|
@ -1,17 +1,49 @@
|
||||
{
|
||||
"extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"plugins": ["@typescript-eslint"],
|
||||
"root": true,
|
||||
"extends": "eslint:recommended",
|
||||
"env": {
|
||||
"node": true,
|
||||
"es6": true
|
||||
},
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 2021,
|
||||
"sourceType": "module"
|
||||
"ecmaVersion": 2021
|
||||
},
|
||||
"rules": {
|
||||
"no-console": "off"
|
||||
}
|
||||
"arrow-spacing": ["warn", { "before": true, "after": true }],
|
||||
"brace-style": ["error", "stroustrup", { "allowSingleLine": true }],
|
||||
"comma-dangle": ["error", "always-multiline"],
|
||||
"comma-spacing": "error",
|
||||
"comma-style": "error",
|
||||
"curly": ["error", "multi-line", "consistent"],
|
||||
"dot-location": ["error", "property"],
|
||||
"handle-callback-err": "off",
|
||||
"indent": ["error", "tab"],
|
||||
"keyword-spacing": "error",
|
||||
"max-nested-callbacks": ["error", { "max": 4 }],
|
||||
"max-statements-per-line": ["error", { "max": 2 }],
|
||||
"no-console": "off",
|
||||
"no-empty-function": "error",
|
||||
"no-floating-decimal": "error",
|
||||
"no-inline-comments": "error",
|
||||
"no-lonely-if": "error",
|
||||
"no-multi-spaces": "error",
|
||||
"no-multiple-empty-lines": ["error", { "max": 2, "maxEOF": 1, "maxBOF": 0 }],
|
||||
"no-shadow": ["error", { "allow": ["err", "resolve", "reject"] }],
|
||||
"no-trailing-spaces": ["error"],
|
||||
"no-var": "error",
|
||||
"object-curly-spacing": ["error", "always"],
|
||||
"prefer-const": "error",
|
||||
"quotes": ["error", "single"],
|
||||
"semi": ["error", "always"],
|
||||
"space-before-blocks": "error",
|
||||
"space-before-function-paren": ["error", {
|
||||
"anonymous": "never",
|
||||
"named": "never",
|
||||
"asyncArrow": "always"
|
||||
}],
|
||||
"space-in-parens": "error",
|
||||
"space-infix-ops": "error",
|
||||
"space-unary-ops": "error",
|
||||
"spaced-comment": "error",
|
||||
"yoda": "error"
|
||||
}
|
||||
}
|
||||
|
12
discordbot/index.js
Normal file
@ -0,0 +1,12 @@
|
||||
const { Client, Events, GatewayIntentBits } = require('discord.js');
|
||||
const dotenv = require('dotenv');
|
||||
|
||||
dotenv.config();
|
||||
|
||||
const client = new Client({ intents: [GatewayIntentBits.Guilds] });
|
||||
|
||||
client.once(Events.ClientReady, c => {
|
||||
console.log(`Ready! Logged in as ${c.user.tag}`);
|
||||
});
|
||||
|
||||
client.login(process.env.DISCORD_TOKEN);
|
4513
discordbot/package-lock.json
generated
@ -2,26 +2,17 @@
|
||||
"name": "discordbot",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.ts",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"dev": "tsx watch src/index.ts",
|
||||
"start": "node dist/index.js",
|
||||
"build": "tsup src/index.ts --minify",
|
||||
"lint": "eslint \"**/*.{ts, tsx}\"",
|
||||
"eslint-fix": "eslint . --fix"
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "",
|
||||
"author": "Xeovalyte",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"discord.js": "^14.11.0",
|
||||
"discord.js": "^14.12.1",
|
||||
"dotenv": "^16.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@typescript-eslint/eslint-plugin": "^5.60.1",
|
||||
"@typescript-eslint/parser": "^5.60.1",
|
||||
"eslint": "^8.43.0",
|
||||
"tsup": "^7.1.0",
|
||||
"tsx": "^3.12.7",
|
||||
"typescript": "^5.1.3"
|
||||
"eslint": "^8.46.0"
|
||||
}
|
||||
}
|
||||
|
@ -1,24 +0,0 @@
|
||||
import { ChatInputCommandInteraction, SlashCommandBuilder, Client, PermissionFlagsBits } from "discord.js";
|
||||
import { basicEmbed } from '../createEmbed'
|
||||
|
||||
export const data = new SlashCommandBuilder()
|
||||
.setName("clear")
|
||||
.setDescription("Clear 1-100 messages")
|
||||
.setDefaultMemberPermissions(PermissionFlagsBits.Administrator)
|
||||
.addNumberOption(option => option
|
||||
.setName('amount')
|
||||
.setDescription('The amount of messages to creat')
|
||||
.setRequired(true)
|
||||
)
|
||||
|
||||
export async function execute({ interaction }: { interaction: ChatInputCommandInteraction, client: Client }) {
|
||||
const amount = interaction.options.getNumber('amount');
|
||||
|
||||
if (!amount || !interaction.channel || amount < 1 || amount > 100) return await interaction.reply({ embeds: [basicEmbed('The amount must be between 1-100')] });
|
||||
|
||||
if (interaction.channel.isDMBased()) return await interaction.reply({ embeds: [basicEmbed('This command can only be executed inside a guild')], ephemeral: true })
|
||||
|
||||
interaction.channel.bulkDelete(amount)
|
||||
|
||||
await interaction.reply({ embeds: [basicEmbed(`Cleared **${amount}** messages`)], ephemeral: true })
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
import { ChatInputCommandInteraction, SlashCommandBuilder, Client } from "discord.js";
|
||||
import { basicEmbed } from '../createEmbed'
|
||||
|
||||
export const data = new SlashCommandBuilder()
|
||||
.setName("ping")
|
||||
.setDescription("Replies with Pong!");
|
||||
|
||||
export async function execute({ interaction, client }: { interaction: ChatInputCommandInteraction, client: Client }) {
|
||||
const reply = await interaction.reply({ embeds: [basicEmbed(`Websocket heartbeat: **${client.ws.ping}ms**\n Roundtrip latency: **Pinging...**`)], fetchReply: true, ephemeral: true });
|
||||
interaction.editReply({ embeds: [basicEmbed(`Websocket heartbeat: **${client.ws.ping}ms**\n Roundtrip latency: **${reply.createdTimestamp - interaction.createdTimestamp}ms**`)] });
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
import { ColorResolvable } from "discord.js";
|
||||
import dotenv from "dotenv";
|
||||
|
||||
dotenv.config();
|
||||
|
||||
const { DISCORD_TOKEN, DISCORD_CLIENT_ID, DISCORD_GUILD_ID } = process.env;
|
||||
|
||||
if (!DISCORD_TOKEN || !DISCORD_CLIENT_ID) {
|
||||
throw new Error("Missing environment variables");
|
||||
}
|
||||
|
||||
const EMBED_COLOR: ColorResolvable = '#0080ff'
|
||||
|
||||
export const config = {
|
||||
DISCORD_TOKEN,
|
||||
DISCORD_CLIENT_ID,
|
||||
DISCORD_GUILD_ID,
|
||||
EMBED_COLOR
|
||||
};
|
||||
|
@ -1,8 +0,0 @@
|
||||
import { EmbedBuilder } from 'discord.js'
|
||||
import { config } from './config'
|
||||
|
||||
export const basicEmbed = (description: string) => {
|
||||
return new EmbedBuilder()
|
||||
.setColor(config.EMBED_COLOR)
|
||||
.setDescription(description);
|
||||
};
|
@ -1,23 +0,0 @@
|
||||
import { Events, Client, BaseInteraction } from 'discord.js'
|
||||
|
||||
export const name = Events.InteractionCreate
|
||||
|
||||
export const execute = async ({ client }: { client: Client }, interaction: BaseInteraction) => {
|
||||
if (interaction.isChatInputCommand()) {
|
||||
const command = interaction.client.commands.get(interaction.commandName);
|
||||
|
||||
if (!command) {
|
||||
return console.error(`No command matching ${interaction.commandName} was found`);
|
||||
} else {
|
||||
console.info(`${interaction.user.username} executed command ${interaction.commandName}`);
|
||||
}
|
||||
|
||||
try {
|
||||
await command.execute({ interaction, client });
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
|
||||
await interaction.reply({ content: 'There was an error while executing this command!', ephemeral: true });
|
||||
}
|
||||
}
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
import { Events, Client } from 'discord.js'
|
||||
import registerCommands from '../registerCommands'
|
||||
|
||||
export const name = Events.ClientReady
|
||||
|
||||
export const once = true
|
||||
|
||||
export const execute = async ({ client }: { client: Client }) => {
|
||||
if (!client.user) throw Error('No client.user')
|
||||
|
||||
console.log(`Ready! Logged in as ${client.user.tag}`)
|
||||
|
||||
registerCommands()
|
||||
}
|
@ -1,48 +0,0 @@
|
||||
import { Client, GatewayIntentBits, Collection } from 'discord.js'
|
||||
import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
import { config } from './config'
|
||||
|
||||
type Command = {
|
||||
data: any
|
||||
}
|
||||
|
||||
const client = new Client({
|
||||
intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMembers, GatewayIntentBits.GuildMessages, GatewayIntentBits.MessageContent]
|
||||
})
|
||||
|
||||
|
||||
client.commands = new Collection()
|
||||
|
||||
const commandsPath = path.join(__dirname, 'commands');
|
||||
const commandFiles = fs.readdirSync(commandsPath).filter(file => file.endsWith('.ts'));
|
||||
|
||||
(async () => {
|
||||
for (const file of commandFiles) {
|
||||
const filePath = path.join(commandsPath, file);
|
||||
const command = await import(filePath) as Command
|
||||
|
||||
if ('data' in command && 'execute' in command) {
|
||||
client.commands.set(command.data.name, command);
|
||||
} else {
|
||||
console.error(`The command at ${filePath} is missing a required "data" or "execute" property.`);
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const eventsPath = path.join(__dirname, 'events');
|
||||
const eventFiles = fs.readdirSync(eventsPath).filter(file => file.endsWith('.ts'));
|
||||
|
||||
(async () => {
|
||||
for (const file of eventFiles) {
|
||||
const filePath = path.join(eventsPath, file);
|
||||
const event = await import(filePath);
|
||||
if (event.once) {
|
||||
client.once(event.name, (...args) => event.execute({ client }, ...args));
|
||||
} else {
|
||||
client.on(event.name, (...args) => event.execute({ client }, ...args));
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
client.login(config.DISCORD_TOKEN);
|
@ -1,54 +0,0 @@
|
||||
import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
import { REST, Routes } from 'discord.js'
|
||||
import { config } from './config'
|
||||
|
||||
type Command = {
|
||||
data: any
|
||||
}
|
||||
|
||||
export default async () => {
|
||||
const commands = []
|
||||
|
||||
const commandsPath = path.join(__dirname, 'commands')
|
||||
const commandFiles = fs.readdirSync(commandsPath).filter(file => file.endsWith('ts'))
|
||||
|
||||
for (const file of commandFiles) {
|
||||
const filePath = path.join(commandsPath, file)
|
||||
const command = await import(filePath) as Command
|
||||
|
||||
if ('data' in command && 'execute' in command) {
|
||||
commands.push(command.data.toJSON())
|
||||
} else {
|
||||
console.error(`The command at ${filePath} is missing a required "data" or "execute" property`)
|
||||
}
|
||||
}
|
||||
|
||||
const rest = new REST().setToken(config.DISCORD_TOKEN)
|
||||
|
||||
try {
|
||||
console.log(`Started refreshing ${commands.length} application (/) commands.`);
|
||||
|
||||
// The put method is used to fully refresh all commands in the guild with the current set
|
||||
if (config.DISCORD_GUILD_ID) {
|
||||
const data: any = await rest.put(
|
||||
Routes.applicationGuildCommands(config.DISCORD_CLIENT_ID, config.DISCORD_GUILD_ID),
|
||||
{ body: commands },
|
||||
);
|
||||
|
||||
console.log(`Successfully reloaded ${data.length} guild (/) commands.`);
|
||||
} else {
|
||||
|
||||
const data: any = await rest.put(
|
||||
Routes.applicationCommands(config.DISCORD_CLIENT_ID),
|
||||
{ body: commands },
|
||||
);
|
||||
|
||||
console.log(`Successfully reloaded ${data.length} global (/) commands.`);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
// And of course, make sure you catch and log any errors!
|
||||
console.error(error);
|
||||
}
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"module": "commonjs",
|
||||
"rootDir": "./src",
|
||||
"outDir": "./dist",
|
||||
"removeComments": true,
|
||||
"resolveJsonModule": true,
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"strict": true,
|
||||
"strictNullChecks": true,
|
||||
"skipLibCheck": true,
|
||||
"compilerOptions": {
|
||||
"module": "commonjs"
|
||||
}
|
||||
}
|
||||
}
|
11
discordbot/types.d.ts
vendored
@ -1,11 +0,0 @@
|
||||
import { Collection } from 'discord.js'
|
||||
|
||||
declare module 'discord.js' {
|
||||
export interface Client {
|
||||
commands: Collection<unknown, any>
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
type CreateEmbed = (message: string) => void
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
{
|
||||
"root": true,
|
||||
"extends": ["@nuxt/eslint-config", "plugin:tailwindcss/recommended"],
|
||||
"rules": {
|
||||
"vue/max-attributes-per-line": ["error", {
|
||||
"singleline": {
|
||||
"max": 4
|
||||
},
|
||||
"multiline": {
|
||||
"max": 2
|
||||
}
|
||||
}],
|
||||
"vue/multi-word-component-names": "off",
|
||||
"vue/singleline-html-element-content-newline": "off",
|
||||
"no-undef": "off"
|
||||
}
|
||||
}
|
9
web/.gitignore
vendored
@ -1,9 +0,0 @@
|
||||
node_modules
|
||||
*.log*
|
||||
.nuxt
|
||||
.nitro
|
||||
.cache
|
||||
.output
|
||||
.env
|
||||
dist
|
||||
.DS_Store
|
@ -1,2 +0,0 @@
|
||||
shamefully-hoist=true
|
||||
strict-peer-dependencies=false
|
@ -1,9 +0,0 @@
|
||||
FROM node:18-alpine
|
||||
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
COPY .output .
|
||||
|
||||
EXPOSE 3000
|
||||
|
||||
CMD [ "node", "server/index.mjs" ]
|
@ -1,42 +0,0 @@
|
||||
# Nuxt 3 Minimal Starter
|
||||
|
||||
Look at the [Nuxt 3 documentation](https://nuxt.com/docs/getting-started/introduction) to learn more.
|
||||
|
||||
## Setup
|
||||
|
||||
Make sure to install the dependencies:
|
||||
|
||||
```bash
|
||||
# yarn
|
||||
yarn install
|
||||
|
||||
# npm
|
||||
npm install
|
||||
|
||||
# pnpm
|
||||
pnpm install
|
||||
```
|
||||
|
||||
## Development Server
|
||||
|
||||
Start the development server on `http://localhost:3000`
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
|
||||
## Production
|
||||
|
||||
Build the application for production:
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
|
||||
Locally preview production build:
|
||||
|
||||
```bash
|
||||
npm run preview
|
||||
```
|
||||
|
||||
Check out the [deployment documentation](https://nuxt.com/docs/getting-started/deployment) for more information.
|
@ -1,5 +0,0 @@
|
||||
<template>
|
||||
<div class="h-screen w-full overflow-y-auto">
|
||||
<NuxtLayout />
|
||||
</div>
|
||||
</template>
|
@ -1,17 +0,0 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@layer components {
|
||||
.sidebar-item {
|
||||
@apply font-medium text-lg w-full px-4 py-2 rounded hover:bg-white hover:bg-opacity-5 hover:cursor-pointer flex items-center
|
||||
}
|
||||
|
||||
.highlight {
|
||||
@apply bg-primary bg-opacity-20 px-2 rounded
|
||||
}
|
||||
}
|
||||
|
||||
.router-link-active {
|
||||
background-color: rgba(0, 0, 0, 20%)
|
||||
}
|
Before Width: | Height: | Size: 11 MiB |
@ -1,43 +0,0 @@
|
||||
<template>
|
||||
<div class="flex w-full flex-col items-center text-primary">
|
||||
<div v-if="!user.minecraft.uuid" class="flex flex-col items-center">
|
||||
<p class="mb-10 max-w-xl text-sm sm:text-base">
|
||||
Je bent momenteel niet gewhitelist. Om toegang te krijgen tot de Minecraft server moet je in Minecraft naar de
|
||||
server met het ip <span class="highlight">play.polarcraft.xeovalyte.com</span> gaan. Vervolgens krijg je een code
|
||||
te zien, vul deze code hieronder in.
|
||||
</p>
|
||||
<div class="flex gap-2">
|
||||
<Input v-model="code">Code</Input>
|
||||
<Button @click="submitCode">Submit</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
definePageMeta({
|
||||
middleware: ["auth"]
|
||||
})
|
||||
|
||||
const user = useState('user')
|
||||
const code = ref('')
|
||||
|
||||
const submitCode = async () => {
|
||||
try {
|
||||
const response = await $fetch('/api/minecraft/whitelist', {
|
||||
method: 'POST',
|
||||
body: {
|
||||
code: code.value
|
||||
}
|
||||
})
|
||||
|
||||
user.value.minecraft.uuid = response.uuid
|
||||
user.value.minecraft.username = response.username
|
||||
|
||||
useToast().success('Succesvol gewhitelist')
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
useToast().error(e.statusMessage)
|
||||
}
|
||||
}
|
||||
</script>
|
@ -1,19 +0,0 @@
|
||||
<template>
|
||||
<div class="flex items-center border-b border-b-gray-700 bg-neutral-800 px-5">
|
||||
<h1 class="text-2xl font-bold text-primary">Polarcraft</h1>
|
||||
<Button outline class="ml-auto" @click="logout">Logout</Button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const logout = async () => {
|
||||
try {
|
||||
await useFetch('/api/auth/logout')
|
||||
|
||||
navigateTo('/login')
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
alert(err.statusMessage)
|
||||
}
|
||||
}
|
||||
</script>
|
@ -1,35 +0,0 @@
|
||||
<template>
|
||||
<div class="flex flex-col gap-1 bg-neutral-800 px-2 pb-14 pt-5 text-gray-300">
|
||||
<NuxtLink to="/" class="sidebar-item">
|
||||
<Icon size="1.5em" name="ph:house" class="mr-3" />
|
||||
Home
|
||||
</NuxtLink>
|
||||
<NuxtLink to="/team" class="sidebar-item">
|
||||
<Icon size="1.5em" name="ph:users-three" class="mr-3" />
|
||||
Team
|
||||
</NuxtLink>
|
||||
<NuxtLink to="/player-store" class="sidebar-item">
|
||||
<Icon size="1.5em" name="ph:storefront" class="mr-3" />
|
||||
Player stores
|
||||
</NuxtLink>
|
||||
<NuxtLink to="/map" class="sidebar-item">
|
||||
<Icon size="1.5em" name="ph:map-trifold" class="mr-3" />
|
||||
Map
|
||||
</NuxtLink>
|
||||
<div v-if="user && user.role.admin" class="mt-auto space-y-1">
|
||||
<h2 class="ml-2 text-gray-400">Moderation</h2>
|
||||
<NuxtLink to="/mod/users" class="sidebar-item">
|
||||
<Icon size="1.5em" name="ph:users-three" class="mr-3" />
|
||||
Users
|
||||
</NuxtLink>
|
||||
<NuxtLink to="/mod/server" class="sidebar-item">
|
||||
<Icon size="1.5em" name="ph:cpu" class="mr-3" />
|
||||
Server Control
|
||||
</NuxtLink>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const user = useState('user')
|
||||
</script>
|
@ -1,180 +0,0 @@
|
||||
<template>
|
||||
<Modal v-if="modalOpen" title="Invite team member" @close="modalOpen = false" @submit="modalOpen = false">
|
||||
<div>
|
||||
<h2 class="text-lg font-bold text-primary">Users</h2>
|
||||
<div class="h-48 space-y-3 overflow-y-auto">
|
||||
<div v-for="unaffiliatedUser in unaffilatedUsers" :key="unaffiliatedUser._id.toString()" class="flex items-center rounded bg-neutral-700 px-3 py-1 text-gray-200">
|
||||
{{ unaffiliatedUser.username }}
|
||||
<Button v-if="!unaffiliatedUser.teamInvites.includes(user.team.id)" class="ml-auto" @click="inviteUser(unaffiliatedUser)">Invite</Button>
|
||||
<Button v-else class="ml-auto" type="danger" @click="cancelInvite(unaffiliatedUser)">Cancel Invite</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
<Modal v-if="editTeamModal.open" title="Edit team" @close="editTeamModal.open = false" @submit="editTeam">
|
||||
<Input v-model="editTeamModal.name" background-class="bg-neutral-800" class="w-full max-w-sm">Naam / Prefix</Input>
|
||||
<Colorpicker v-model="editTeamModal.color" input-background-class="bg-neutral-800" class="w-full max-w-sm" />
|
||||
</Modal>
|
||||
<div class="mx-auto my-10 max-w-2xl">
|
||||
<h2 class="mb-2 text-xl font-bold text-primary">Team Information</h2>
|
||||
<div class="rounded border-[1px] border-primary px-5 py-2 text-primary">
|
||||
<table class="w-full table-auto">
|
||||
<tbody class="divide-y divide-gray-700">
|
||||
<tr>
|
||||
<td class="py-3">Name</td>
|
||||
<td class="font-bold">{{ team.name }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="py-3">Color</td>
|
||||
<td class="font-bold">{{ team.color }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="py-3">ID</td>
|
||||
<td class="font-bold">{{ team._id }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="mb-10 mt-5 flex justify-center gap-x-3">
|
||||
<Button type="danger" @click="leaveTeam">Leave Team</Button>
|
||||
<Button v-if="user.team.admin" @click="openTeamModal">Edit Team</Button>
|
||||
</div>
|
||||
<h2 class="mb-2 text-xl font-bold text-primary">Team Members</h2>
|
||||
<div class="space-y-5 rounded border-[1px] border-primary p-5 text-primary">
|
||||
<div v-for="teamMember in teamMembers" :key="teamMember._id" class="flex h-12 items-center rounded bg-neutral-800 px-5 font-bold text-gray-200">
|
||||
{{ teamMember.username }}
|
||||
<span v-if="teamMember.team.admin" class="ml-3 text-sm text-gray-400">Admin</span>
|
||||
<div v-if="user.team.admin" class="ml-auto">
|
||||
<Button v-if="!teamMember.team.admin && teamMember._id !== user._id" @click="promoteUser(teamMember)">Promote</Button>
|
||||
<Button v-if="teamMember.team.admin && teamMember._id !== user._id" @click="demoteUser(teamMember)">Demote</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="user.team.admin" class="rounded border-2 border-dashed border-neutral-500 bg-neutral-800 p-3 text-center font-bold text-gray-200 hover:cursor-pointer hover:bg-neutral-700" @click="modalOpen = true">
|
||||
Invite new member
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const user = useState('user');
|
||||
const { data: team } = await useFetch('/api/team');
|
||||
const { data: teamMembers } = await useFetch('/api/team/members');
|
||||
const { data: unaffilatedUsers } = await useFetch('/api/team/unaffiliated')
|
||||
|
||||
const modalOpen = ref(false)
|
||||
|
||||
const editTeamModal = ref({
|
||||
open: false,
|
||||
name: '',
|
||||
color: '',
|
||||
})
|
||||
|
||||
const openTeamModal = () => {
|
||||
editTeamModal.value.name = team.value.name
|
||||
editTeamModal.value.color = team.value.color
|
||||
editTeamModal.value.open = true
|
||||
}
|
||||
|
||||
const leaveTeam = async () => {
|
||||
try {
|
||||
await $fetch('/api/team/leave');
|
||||
|
||||
user.value.team = null;
|
||||
|
||||
useToast().success('Succesvol team verlaten')
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
useToast().error(e.statusMessage)
|
||||
}
|
||||
}
|
||||
|
||||
const editTeam = async () => {
|
||||
try {
|
||||
await $fetch('/api/team/edit', {
|
||||
method: 'POST',
|
||||
body: {
|
||||
name: editTeamModal.value.name,
|
||||
color: editTeamModal.value.color,
|
||||
}
|
||||
});
|
||||
|
||||
team.value.name = editTeamModal.value.name
|
||||
team.value.color = editTeamModal.value.color
|
||||
|
||||
useToast().success('Team is succesvol bewerkt')
|
||||
|
||||
editTeamModal.value.open = false
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
useToast().error(e.statusMessage)
|
||||
}
|
||||
}
|
||||
|
||||
const inviteUser = async (usr) => {
|
||||
try {
|
||||
await $fetch('/api/team/invite', {
|
||||
method: 'POST',
|
||||
body: { id: usr._id }
|
||||
});
|
||||
|
||||
usr.teamInvites.push(user.value.team.id);
|
||||
|
||||
useToast().success(`Invited ${usr.username} succesvol`)
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
useToast().error(e.statusMessage)
|
||||
}
|
||||
}
|
||||
|
||||
const cancelInvite = async (usr) => {
|
||||
try {
|
||||
await $fetch('/api/team/cancelinvite', {
|
||||
method: 'POST',
|
||||
body: { id: usr._id }
|
||||
});
|
||||
|
||||
usr.teamInvites = usr.teamInvites.filter(a => a !== user.value.team.id);
|
||||
|
||||
useToast().success('Succesvol invite geannuleerd')
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
useToast().error(e.statusMessage)
|
||||
}
|
||||
}
|
||||
|
||||
const promoteUser = async (usr) => {
|
||||
try {
|
||||
await $fetch('/api/team/promote', {
|
||||
method: 'POST',
|
||||
body: { userId: usr._id }
|
||||
});
|
||||
|
||||
usr.team.admin = true
|
||||
|
||||
console.log(usr)
|
||||
console.log(teamMembers)
|
||||
|
||||
useToast().success('Succesvol gebruiker gepromoveerd')
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
useToast().error(e.statusMessage)
|
||||
}
|
||||
}
|
||||
|
||||
const demoteUser = async (usr) => {
|
||||
try {
|
||||
await $fetch('/api/team/demote', {
|
||||
method: 'POST',
|
||||
body: { userId: usr._id }
|
||||
});
|
||||
|
||||
usr.team.admin = false
|
||||
|
||||
useToast().success('Succesvol gebruiker gedegradeerd')
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
useToast().error(e.statusMessage)
|
||||
}
|
||||
}
|
||||
</script>
|
@ -1,84 +0,0 @@
|
||||
<template>
|
||||
<p class="my-10 text-center text-gray-300">
|
||||
Je zit momenteel niet in een team. Maak een team aan of wacht tot dat je geinvite wordt
|
||||
</p>
|
||||
<div class="mb-5 flex w-full justify-center gap-10">
|
||||
<span
|
||||
class="font-bold text-primary hover:cursor-pointer" :class="{ 'underline underline-offset-4': !createTeam.show }"
|
||||
@click="createTeam.show = false"
|
||||
>
|
||||
Team Invites
|
||||
</span>
|
||||
<span
|
||||
class="font-bold text-primary hover:cursor-pointer" :class="{ 'underline underline-offset-4': createTeam.show }"
|
||||
@click="createTeam.show = true"
|
||||
>
|
||||
Create Team
|
||||
</span>
|
||||
</div>
|
||||
<div v-if="!createTeam.show" class="text-center text-gray-300">
|
||||
<h2 v-if="!user.teamInvites.length">You don't have any team invites</h2>
|
||||
<div v-else class="mx-auto flex max-w-lg flex-col">
|
||||
<div v-for="team in filteredTeams" :key="team._id" class="flex items-center rounded bg-neutral-800 px-3 py-1 text-left" @click="acceptInvite(team)">
|
||||
<span>
|
||||
{{ team.name }}
|
||||
</span>
|
||||
<Button class="ml-auto">Accept</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="flex w-full flex-col items-center gap-5">
|
||||
<Input v-model="createTeam.name" class="w-full max-w-sm">Naam</Input>
|
||||
<Colorpicker v-model="createTeam.color" class="w-full max-w-sm" />
|
||||
<Button @click="handleCreateTeam">Create Team</Button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const user = useState('user')
|
||||
|
||||
const teams = await $fetch('/api/team/all')
|
||||
|
||||
const filteredTeams = computed(() => {
|
||||
return teams.filter(a => user.value.teamInvites.includes(a._id));
|
||||
})
|
||||
|
||||
const createTeam = ref({
|
||||
show: false,
|
||||
name: '',
|
||||
color: '',
|
||||
})
|
||||
|
||||
const acceptInvite = async (team) => {
|
||||
try {
|
||||
const response = await $fetch('/api/team/acceptinvite', {
|
||||
method: 'POST',
|
||||
body: { teamId: team._id }
|
||||
})
|
||||
|
||||
user.value.team = { id: response._id, admin: false }
|
||||
|
||||
useToast().success('Succesvol lid geworden van het team')
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
useToast().error(e.statusMessage)
|
||||
}
|
||||
}
|
||||
|
||||
const handleCreateTeam = async () => {
|
||||
try {
|
||||
const response = await $fetch('/api/team/create', {
|
||||
method: 'POST',
|
||||
body: { teamName: createTeam.value.name, teamColor: createTeam.value.color }
|
||||
})
|
||||
|
||||
user.value.team = { id: response.insertedId.toString(), admin: true }
|
||||
|
||||
useToast().success('Succesvol team gemaakt')
|
||||
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
useToast().error(e.statusMessage)
|
||||
}
|
||||
}
|
||||
</script>
|
@ -1,3 +0,0 @@
|
||||
<template>
|
||||
<NuxtPage />
|
||||
</template>
|
@ -1,20 +0,0 @@
|
||||
<template>
|
||||
<div class="h-full bg-neutral-900">
|
||||
<div v-if="user" class="hidden h-full grid-cols-desktoplayout grid-rows-desktoplayout sm:grid">
|
||||
<LayoutNavbar class="col-span-2" />
|
||||
<LayoutSidebar v-if="user.minecraft.uuid" class="" />
|
||||
<div class="overflow-y-auto px-10" :class="{ 'col-span-2': !user.minecraft.uuid }">
|
||||
<NuxtPage />
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="user" class="h-full sm:hidden">
|
||||
<div class="overflow-y-auto p-2">
|
||||
<NuxtPage />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const user = useState('user')
|
||||
</script>
|
@ -1,17 +0,0 @@
|
||||
export default defineNuxtRouteMiddleware(async (to) => {
|
||||
if (process.server) return;
|
||||
|
||||
try {
|
||||
const user = await $fetch('/api/auth/user')
|
||||
useState('user', () => user)
|
||||
|
||||
if (to.meta.moderator && !user.role.moderator) return navigateTo('/')
|
||||
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
|
||||
useState('user', () => null)
|
||||
|
||||
return navigateTo('/login')
|
||||
}
|
||||
})
|
@ -1,40 +0,0 @@
|
||||
// https://nuxt.com/docs/api/configuration/nuxt-config
|
||||
export default defineNuxtConfig({
|
||||
extends: 'node_modules/@xeovalyte/nuxt-xvui',
|
||||
devtools: true,
|
||||
ssr: false,
|
||||
modules: [
|
||||
'@nuxtjs/tailwindcss',
|
||||
'@xeovalyte/nuxt-xvtoast',
|
||||
'nuxt-icon',
|
||||
'@nuxtjs/tailwindcss',
|
||||
'@vueuse/nuxt',
|
||||
'@nuxt/image-edge'
|
||||
],
|
||||
app: {
|
||||
head: {
|
||||
link: [
|
||||
{ rel: "apple-touch-icon", sizes: "180x180", href: "/apple-touch-icon.png" },
|
||||
{ rel: "icon", sizes: "32x32", type: "image/png", href: "/favicon-32x32.png" },
|
||||
{ rel: "icon", sizes: "16x16", type: "image/png", href: "/favicon-16x16.png" },
|
||||
{ rel: "manifest", href: "/site.webmanifest" },
|
||||
]
|
||||
}
|
||||
},
|
||||
runtimeConfig: {
|
||||
discordId: '',
|
||||
discordSecret: '',
|
||||
discordHost: '',
|
||||
jwtSecret: '',
|
||||
dbUrl: '',
|
||||
mineckerHost: '',
|
||||
mineckerApiKey: '',
|
||||
rconPassword: '',
|
||||
rconPort: '25575',
|
||||
rconHost: 'localhost',
|
||||
redirectURI: 'http://localhost:3000/api/auth',
|
||||
public: {
|
||||
redirectUrl: 'https://discord.com/api/oauth2/authorize?client_id=1052974736432443432&redirect_uri=http%3A%2F%2Flocalhost%3A3000%2Fapi%2Fauth&response_type=code&scope=identify',
|
||||
}
|
||||
}
|
||||
})
|
25302
web/package-lock.json
generated
@ -1,36 +0,0 @@
|
||||
{
|
||||
"name": "nuxt-app",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "nuxt build",
|
||||
"dev": "nuxt dev",
|
||||
"generate": "nuxt generate",
|
||||
"preview": "nuxt preview",
|
||||
"postinstall": "nuxt prepare",
|
||||
"lint": "eslint .",
|
||||
"lint-fix": "eslint . --fix"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nuxt/devtools": "^0.5.0",
|
||||
"@nuxt/image-edge": "^1.0.0-28059208.2abef1b",
|
||||
"@nuxtjs/eslint-module": "^4.1.0",
|
||||
"@nuxtjs/tailwindcss": "^6.7.0",
|
||||
"@types/node": "^20",
|
||||
"@vueuse/components": "^10.1.2",
|
||||
"@vueuse/core": "^10.1.2",
|
||||
"@vueuse/nuxt": "^10.1.2",
|
||||
"eslint": "^8.41.0",
|
||||
"eslint-plugin-tailwindcss": "^3.12.0",
|
||||
"nuxt": "^3.5.0",
|
||||
"nuxt-icon": "^0.4.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@nuxt/eslint-config": "^0.1.1",
|
||||
"@xeovalyte/nuxt-xvtoast": "^1.1.3",
|
||||
"@xeovalyte/nuxt-xvui": "git+https://gitea.xeovalyte.dev/xeovalyte/nuxt-xvui.git",
|
||||
"jsonwebtoken": "^9.0.0",
|
||||
"minecraft-server-util": "^5.4.2",
|
||||
"mongodb": "^5.5.0",
|
||||
"socket.io-client": "^4.6.1"
|
||||
}
|
||||
}
|
@ -1,120 +0,0 @@
|
||||
<template>
|
||||
<div class="flex h-full w-full flex-col text-primary">
|
||||
<h1 class="mb-10 mt-20 text-center text-5xl font-bold">Welkom, {{ user.username }}</h1>
|
||||
<Whitelist v-if="!user.minecraft.uuid" />
|
||||
<div v-else class="flex w-full flex-wrap justify-evenly gap-10">
|
||||
<div class="flex w-full max-w-xl flex-col gap-3">
|
||||
<h2 class="text-xl font-bold text-primary">Discord Information <span v-if="!user.useMinecraftUsername" class="font-normal">(Default)</span></h2>
|
||||
<div class="flex gap-3">
|
||||
<img :src="'https://cdn.discordapp.com/avatars/' + user.discord.id + '/' + user.discord.avatarHash + '.png'" class="aspect-square w-24 rounded shadow">
|
||||
<div class="flex w-full rounded border-2 border-primary p-4">
|
||||
<ul class="my-auto">
|
||||
<li>Username: <b>{{ user.discord.username }}</b></li>
|
||||
<li>ID: <b>{{ user.discord.id }}</b></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-2 flex w-full justify-center gap-4">
|
||||
<Button @click="refreshDiscordUsername">
|
||||
Refresh Username
|
||||
</Button>
|
||||
<Button v-if="user.useMinecraftUsername" @click="setDefaultUsername('discord')">
|
||||
Set Default
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex w-full max-w-xl flex-col gap-3">
|
||||
<h2 class="text-xl font-bold text-primary">Minecraft Information <span v-if="user.useMinecraftUsername" class="font-normal">(Default)</span></h2>
|
||||
<div class="flex gap-3">
|
||||
<img :src="'https://api.mineatar.io/face/' + user.minecraft.uuid + '?scale=16'" class="aspect-square w-24 rounded shadow">
|
||||
<div class="flex w-full rounded border-2 border-primary p-4">
|
||||
<ul class="my-auto">
|
||||
<li>Username: <b>{{ user.minecraft.username }}</b></li>
|
||||
<li>UUID: <b>{{ user.minecraft.uuid }}</b></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-2 flex w-full justify-center gap-4">
|
||||
<Button type="danger" @click="removeWhitelist">
|
||||
Remove from whitelist
|
||||
</Button>
|
||||
<Button @click="refreshMinecraftUsername">
|
||||
Refresh Username
|
||||
</Button>
|
||||
<Button v-if="!user.useMinecraftUsername" @click="setDefaultUsername('minecraft')">
|
||||
Set Default
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
definePageMeta({
|
||||
middleware: ["auth"],
|
||||
})
|
||||
|
||||
useHead({ title: 'Polarcraft' })
|
||||
|
||||
const user = useState('user')
|
||||
|
||||
const refreshMinecraftUsername = async () => {
|
||||
try {
|
||||
const response = await $fetch('/api/minecraft/refreshusername')
|
||||
|
||||
user.value.minecraft.username = response.username
|
||||
|
||||
useToast().success('Gebruikersnaam is ververst')
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
|
||||
useToast().error(e.statusMessage)
|
||||
}
|
||||
}
|
||||
|
||||
const refreshDiscordUsername = async () => {
|
||||
try {
|
||||
const response = await $fetch('/api/discord/refreshusername')
|
||||
|
||||
user.value.discord.username = response.username
|
||||
|
||||
useToast().success('Gebruikersnaam is ververst')
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
|
||||
useToast().error(e.statusMessage)
|
||||
}
|
||||
}
|
||||
|
||||
const removeWhitelist = async () => {
|
||||
try {
|
||||
await $fetch('/api/minecraft/removewhitelist')
|
||||
|
||||
user.value.minecraft.uuid = null
|
||||
user.value.minecraft.username = null
|
||||
|
||||
useToast().success('Minecraft is niet meer gekoppeld')
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
useToast().error(e.statusMessage)
|
||||
}
|
||||
}
|
||||
|
||||
const setDefaultUsername = async (type) => {
|
||||
try {
|
||||
const response = await $fetch('/api/auth/user/setdefaultusername', {
|
||||
method: 'POST',
|
||||
body: { type: type }
|
||||
})
|
||||
|
||||
user.value.username = response.username
|
||||
user.value.useMinecraftUsername = type === 'discord' ? false : true
|
||||
|
||||
useToast().success(`${ type === 'discord' ? 'Discord' : 'Minecraft' } is nu de standaard gebruikersnaam`)
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
useToast().error(e.statusMessage)
|
||||
}
|
||||
}
|
||||
</script>
|
@ -1,26 +0,0 @@
|
||||
<template>
|
||||
<div class="flex h-screen w-full flex-col items-center justify-center bg-transparent px-2 text-primary">
|
||||
<h1 class="mb-5 text-center text-3xl font-bold sm:text-5xl">Polarcraft S5</h1>
|
||||
<p class="mb-10 max-w-xl text-sm sm:text-base">
|
||||
<b>Welkom bij Polarcraft seizoen 5!</b> Start door in te loggen met Discord en vervolgens je account te koppelen met Minecraft. Als je problemen hebt maak dan in Discord een ticket aan door het <span class="highlight">/ticket</span> commando uit te voeren.
|
||||
</p>
|
||||
<Button @click="navigateTo(config.public.redirectUrl, { external: true })">
|
||||
Log in with Discord
|
||||
<Icon size="1.6em" name="ic:baseline-discord" />
|
||||
</Button>
|
||||
|
||||
<div class="absolute left-0 top-0 -z-10 h-screen w-full overflow-hidden">
|
||||
<nuxt-img src="/pictures/diamond_wall.webp" class="h-full w-full scale-105 object-cover blur-[1px] brightness-75" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
definePageMeta({
|
||||
layout: 'blank'
|
||||
})
|
||||
|
||||
useHead({ title: 'Login | Polarcraft' })
|
||||
|
||||
const config = useRuntimeConfig()
|
||||
</script>
|
@ -1,13 +0,0 @@
|
||||
<template>
|
||||
<div class="-mx-10 h-full">
|
||||
<iframe src="https://squaremap-demo.jpenilla.xyz/" class="h-full w-full" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
definePageMeta({
|
||||
middleware: ["auth"]
|
||||
})
|
||||
|
||||
useHead({ title: 'Map | Polarcraft' })
|
||||
</script>
|
@ -1,14 +0,0 @@
|
||||
<template>
|
||||
<div class="mt-5">
|
||||
Server Control
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
definePageMeta({
|
||||
middleware: ["auth"],
|
||||
moderator: true
|
||||
})
|
||||
|
||||
useHead({ title: 'Server Control | Polarcraft' })
|
||||
</script>
|
@ -1,78 +0,0 @@
|
||||
<template>
|
||||
<div class="mt-5">
|
||||
<Modal v-if="actionModal.open" :title="actionModal.title" @click="actionModal.func">
|
||||
<Select v-model="actionModal.reason" :options="[{ name: 'Griefing', value: 'griefing' }]">Reason</Select>
|
||||
</Modal>
|
||||
<h1 class="text-2xl font-bold text-primary">
|
||||
Users
|
||||
</h1>
|
||||
<div class="mx-auto my-10 max-w-4xl">
|
||||
<h2 class="mb-2 text-xl font-bold text-primary">User Actions</h2>
|
||||
<div class="w-full rounded border-[1px] border-primary px-3 py-1 text-left">
|
||||
<table class="w-full table-auto divide-y divide-neutral-500 rounded">
|
||||
<tr class="border-b-2 border-primary text-gray-200">
|
||||
<th class="py-1">Username</th>
|
||||
<th class="py-1">Team</th>
|
||||
<th class="py-1">MC Linked</th>
|
||||
<th class="py-1">Actions</th>
|
||||
</tr>
|
||||
<tr v-for="user in users" :key="user._id" class="text-gray-200">
|
||||
<td class="py-1">{{ user.username }}</td>
|
||||
<td class="py-1">{{ user.team ? teams.filter(a => a._id === user.team.id)[0].name : '-' }}</td>
|
||||
<td class="py-1">{{ user.minecraft.uuid ? 'True ' : 'False' }}</td>
|
||||
<td class="space-x-2 py-1">
|
||||
<span v-if="currentUser.role.admin" class="rounded bg-red-700 px-2 text-red-300 hover:cursor-pointer hover:bg-red-800" @click="openModal(user, 'ban', ban(user))">Ban</span>
|
||||
<span class="rounded bg-orange-700 px-2 text-orange-300 hover:cursor-pointer hover:bg-orange-800">Suspend</span>
|
||||
<span class="rounded bg-yellow-700 px-2 text-yellow-300 hover:cursor-pointer hover:bg-yellow-800">Warn</span>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
definePageMeta({
|
||||
middleware: ["auth"],
|
||||
moderator: true
|
||||
})
|
||||
|
||||
useHead({ title: 'Users | Polarcraft' })
|
||||
|
||||
const currentUser = useState('user')
|
||||
|
||||
const { data: users } = useFetch('/api/auth/getusers')
|
||||
const { data: teams } = useFetch('/api/team/all')
|
||||
|
||||
const actionModal = ref({
|
||||
open: false,
|
||||
title: '',
|
||||
reason: '',
|
||||
userId: '',
|
||||
func: null,
|
||||
})
|
||||
|
||||
const openModal = (user, type, func) => {
|
||||
if (type === 'ban') actionModal.value.title = 'Ban User'
|
||||
if (type === 'suspend') actionModal.value.title = 'Suspend User'
|
||||
if (type === 'warn') actionModal.value.title = 'Warn User'
|
||||
|
||||
actionModal.value.func = func
|
||||
actionModal.value.userId = user._id;
|
||||
actionModal.value.open = true
|
||||
}
|
||||
|
||||
const ban = async (user) => {
|
||||
try {
|
||||
const response = await $fetch(`/api/auth/user/${user._id}/ban`, {
|
||||
reason: "reason"
|
||||
})
|
||||
|
||||
user = response;
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
useToast().error('Error banning user')
|
||||
}
|
||||
}
|
||||
</script>
|
@ -1,158 +0,0 @@
|
||||
<template>
|
||||
<div class="mt-5 text-primary">
|
||||
<Modal
|
||||
v-if="storeModal.open"
|
||||
title="Create new store"
|
||||
:delete="!storeModal.new"
|
||||
@submit="submitModal"
|
||||
@delete="deleteStore"
|
||||
@close="storeModal.open = false"
|
||||
>
|
||||
<Input v-model="storeModal.name" background-class="bg-neutral-800">Store name</Input>
|
||||
<div class="flex gap-x-5">
|
||||
<Input v-model="storeModal.coords.x" background-class="bg-neutral-800">Coordinate X</Input>
|
||||
<Input v-model="storeModal.coords.z" background-class="bg-neutral-800">Coordinate Z</Input>
|
||||
</div>
|
||||
<h2 class="font-bold">Items</h2>
|
||||
<div class="max-h-56 space-y-3 overflow-scroll">
|
||||
<div v-for="item, index in storeModal.items" :key="item.name" class="flex gap-2">
|
||||
<Input v-model="item.displayName" background-class="bg-neutral-800">Name</Input>
|
||||
<Input v-model="item.price" background-class="bg-neutral-800">Price per quantity</Input>
|
||||
<Button type="danger" @click="storeModal.items.splice(index, 1)"><Icon size="1.5em" name="ph:trash" class="-my-2" /></Button>
|
||||
</div>
|
||||
</div>
|
||||
<Button class="" @click="storeModal.items.push({ displayName: '', price: '' })">Add Item</Button>
|
||||
</Modal>
|
||||
<h1 class="pb-10 text-2xl font-bold text-primary">
|
||||
Player Stores
|
||||
</h1>
|
||||
<div class="fixed bottom-10 right-10">
|
||||
<Button class="fixed" @click="openStoreModal(true)">
|
||||
Create Store
|
||||
</Button>
|
||||
</div>
|
||||
<div class="flex flex-wrap gap-10">
|
||||
<div v-for="store in stores" :key="store._id.toString()" class="h-min w-96 rounded-lg bg-neutral-800 text-gray-200 shadow relative">
|
||||
<div v-if="store.ownerId === user._id" class="absolute right-5 top-5 hover:cursor-pointer" @click="openStoreModal(false, store)">
|
||||
<Icon name="ph:pencil-simple" size="1.6em" />
|
||||
</div>
|
||||
<div class="p-5 pb-3">
|
||||
<div class="flex items-center">
|
||||
<img :src="'https://api.mineatar.io/face/' + store.owner.minecraft.uuid + '?scale=3'" class="mr-2 aspect-square rounded">
|
||||
{{ store.owner.username }}
|
||||
</div>
|
||||
<h2 class="text-2xl font-bold text-primary">
|
||||
{{ store.name }}
|
||||
<span class="text-sm font-medium">({{ store.coords.x }}, {{ store.coords.z }})</span>
|
||||
</h2>
|
||||
</div>
|
||||
<h2 class="ml-3 text-xl font-bold">Items</h2>
|
||||
<div class="divide-y divide-neutral-600 rounded-lg bg-neutral-700 px-4 pt-2 pb-2">
|
||||
<div v-for="item in store.items" :key="item.displayname" class="py-1">
|
||||
<b>{{ item.displayName }}</b> for <b>{{ item.price }}</b>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
definePageMeta({
|
||||
middleware: ["auth"]
|
||||
})
|
||||
|
||||
const user = useState('user')
|
||||
|
||||
useHead({ title: 'Player Stores | Polarcraft' })
|
||||
|
||||
const { data: stores } = await useFetch('/api/store');
|
||||
|
||||
const storeModal = ref({
|
||||
open: false,
|
||||
name: '',
|
||||
new: false,
|
||||
id: '',
|
||||
coords: {
|
||||
x: '',
|
||||
z: '',
|
||||
},
|
||||
items: [
|
||||
{ displayName: '', price: '' }
|
||||
],
|
||||
})
|
||||
|
||||
const openStoreModal = (newStore, store) => {
|
||||
storeModal.value.open = true;
|
||||
|
||||
if (newStore === true) {
|
||||
storeModal.value.name = '';
|
||||
storeModal.value.items = [
|
||||
{ displayName: '', price: '' }
|
||||
];
|
||||
|
||||
storeModal.value.coords = { x: '', z: '' }
|
||||
|
||||
storeModal.value.new = true
|
||||
} else {
|
||||
storeModal.value.new = false
|
||||
|
||||
storeModal.value.items = []
|
||||
store.items.forEach(item => {
|
||||
storeModal.value.items.push(item);
|
||||
})
|
||||
|
||||
storeModal.value.name = store.name
|
||||
storeModal.value.id = store._id
|
||||
storeModal.value.coords.x = store.coords.x
|
||||
storeModal.value.coords.z = store.coords.z
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const submitModal = async () => {
|
||||
try {
|
||||
const store = await $fetch('/api/store', {
|
||||
method: 'POST',
|
||||
body: { name: storeModal.value.name, coords: storeModal.value.coords, items: storeModal.value.items, id: storeModal.value.id || null }
|
||||
})
|
||||
|
||||
if (storeModal.value.id) {
|
||||
const oldStore = stores.value.filter(a => a._id === storeModal.value.id)[0]
|
||||
|
||||
oldStore.name = storeModal.value.name;
|
||||
oldStore.coords = storeModal.value.coords;
|
||||
oldStore.items = storeModal.value.items;
|
||||
} else {
|
||||
store.owner = user
|
||||
stores.value.push(store)
|
||||
}
|
||||
|
||||
storeModal.value.open = false
|
||||
|
||||
useToast().success('Succesvol store aangemaakt')
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
useToast().error(e.statusMessage)
|
||||
}
|
||||
}
|
||||
|
||||
const deleteStore = async () => {
|
||||
try {
|
||||
await $fetch('/api/store', {
|
||||
method: 'DELETE',
|
||||
body: { id: storeModal.value.id }
|
||||
})
|
||||
|
||||
|
||||
|
||||
stores.value = stores.value.filter(a => a._id !== storeModal.value.id)
|
||||
|
||||
storeModal.value.open = false
|
||||
useToast().success('Succesvol store verwijderd')
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
useToast().error(e.statusMessage)
|
||||
}
|
||||
}
|
||||
</script>
|
@ -1,19 +0,0 @@
|
||||
<template>
|
||||
<div class="mt-5">
|
||||
<h1 class="text-2xl font-bold text-primary">
|
||||
Team
|
||||
</h1>
|
||||
<TeamNone v-if="!user.team" />
|
||||
<TeamDefault v-else />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const user = useState('user')
|
||||
|
||||
definePageMeta({
|
||||
middleware: ["auth"]
|
||||
})
|
||||
|
||||
useHead({ title: 'Team | Polarcraft' })
|
||||
</script>
|
Before Width: | Height: | Size: 28 KiB |
Before Width: | Height: | Size: 174 KiB |
Before Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 825 B |
Before Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 103 KiB |
@ -1 +0,0 @@
|
||||
{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"}
|
@ -1,13 +0,0 @@
|
||||
export default defineEventHandler(async (event) => {
|
||||
const user = await getAuth(event)
|
||||
|
||||
if (!user.role.moderator) return createError({ statusCode: 403, statusMessage: 'Gebruiker heeft geen toegang tot alle gebruikers' })
|
||||
|
||||
const usersColl = db.collection('users');
|
||||
|
||||
const cursor = usersColl.find()
|
||||
|
||||
const users = await cursor.toArray()
|
||||
|
||||
return users
|
||||
});
|
@ -1,52 +0,0 @@
|
||||
export default defineEventHandler(async (event) => {
|
||||
const { code } = getQuery(event)
|
||||
|
||||
const config = useRuntimeConfig()
|
||||
|
||||
if (!code) return sendRedirect(event, '/', 302)
|
||||
|
||||
try {
|
||||
const tokenResponseData = await $fetch('https://discord.com/api/oauth2/token', {
|
||||
method: 'POST',
|
||||
body: new URLSearchParams({
|
||||
client_id: config.discordId,
|
||||
client_secret: config.discordSecret,
|
||||
code: code,
|
||||
grant_type: 'authorization_code',
|
||||
redirect_uri: config.redirectURI,
|
||||
scope: 'identify',
|
||||
}).toString(),
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded'
|
||||
}
|
||||
})
|
||||
|
||||
const userResult = await $fetch('https://discord.com/api/users/@me', {
|
||||
headers: {
|
||||
authorization: `Bearer ${tokenResponseData.access_token}`
|
||||
}
|
||||
})
|
||||
|
||||
const coll = db.collection('users')
|
||||
|
||||
const doc = {
|
||||
discord: {
|
||||
id: userResult.id,
|
||||
username: userResult.username,
|
||||
avatarHash: userResult.avatar || null
|
||||
},
|
||||
}
|
||||
|
||||
await coll.updateOne({ 'discord.id': userResult.id }, { $set: doc, $setOnInsert: { minecraft: { uuid: null, username: null }, teamInvites: [], role: {} } }, { upsert: true })
|
||||
|
||||
const token = createToken(tokenResponseData.access_token, tokenResponseData.refresh_token, tokenResponseData.expires_in, userResult.id )
|
||||
|
||||
setCookie(event, 'jwt', token, { httpOnly: true, maxAge: tokenResponseData.expires_in * 1000 })
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
|
||||
throw createError({ statusCode: 500, statusMessage: 'Error tijdens het genereren van JWT token'})
|
||||
}
|
||||
|
||||
return sendRedirect(event, '/', 302)
|
||||
});
|
@ -1,25 +0,0 @@
|
||||
import { ObjectId } from "mongodb";
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const { reason } = await readBody(event)
|
||||
|
||||
const currentUser = await getAuth(event)
|
||||
const userId = event.context.params.id;
|
||||
|
||||
if (!reason) return createError({ statusCode: 400, statusMessage: 'Reason is vereist' })
|
||||
if (!currentUser.role.admin) return createError({ statusCode: 403, statusMessage: 'Geen toegang om gebruiker te bannen' })
|
||||
|
||||
const usersColl = db.collection('users')
|
||||
const user = usersColl.findOneAndUpdate({ _id: new ObjectId(userId)}, { $set: { banned: { reason: reason, date: new Date() } } })
|
||||
|
||||
if (!user.value) return createError({ statusCode: 500, statusMessage: 'Error tijdens het updaten van de gebruiker' })
|
||||
|
||||
await $fetch(config.discordHost + '/user/ban', {
|
||||
method: 'POST',
|
||||
body: { reason: reason, discordId: user.discord.id }
|
||||
})
|
||||
|
||||
await sendRconCommand(`ban ${user.value.minecraft.uuid} ${reason}`)
|
||||
|
||||
return user
|
||||
});
|
@ -1,5 +0,0 @@
|
||||
export default defineEventHandler(async (event) => {
|
||||
const user = await getAuth(event)
|
||||
|
||||
return user
|
||||
});
|
@ -1,16 +0,0 @@
|
||||
import { ObjectId } from "mongodb";
|
||||
import { applyUsername } from "~/server/utils/auth";
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const { type } = await readBody(event)
|
||||
const auth = await getAuth(event)
|
||||
|
||||
const usersColl = db.collection('users')
|
||||
|
||||
const username = type === 'discord' ? auth.discord.username : auth.minecraft.username
|
||||
|
||||
const newUser = await usersColl.findOneAndUpdate({ _id: new ObjectId(auth._id) }, { $set: { username: username, useMinecraftUsername: type === 'discord' ? false : true } }, { returnDocument: 'after' })
|
||||
await applyUsername(newUser.value)
|
||||
|
||||
return newUser.value
|
||||
});
|
@ -1,26 +0,0 @@
|
||||
export default defineEventHandler(async (event) => {
|
||||
const user = await getAuth(event)
|
||||
|
||||
const userResult = await $fetch('https://discord.com/api/users/@me', {
|
||||
headers: {
|
||||
authorization: `Bearer ${user.accessToken}`
|
||||
}
|
||||
})
|
||||
|
||||
const coll = db.collection('users')
|
||||
|
||||
const doc = {
|
||||
discord: {
|
||||
id: userResult.id,
|
||||
username: userResult.username,
|
||||
avatarHash: userResult.avatar || null
|
||||
},
|
||||
username: user.useMinecraftUsername ? user.minecraft.username : userResult.username
|
||||
}
|
||||
|
||||
const newUser = await coll.findOneAndUpdate({ 'discord.id': userResult.id }, { $set: doc }, { returnDocument: 'after' })
|
||||
|
||||
applyUsername(newUser)
|
||||
|
||||
return doc.discord
|
||||
});
|
@ -1,22 +0,0 @@
|
||||
export default defineEventHandler(async (event) => {
|
||||
const { content, uuid } = await readBody(event);
|
||||
|
||||
const config = useRuntimeConfig();
|
||||
|
||||
const usersColl = db.collection('users')
|
||||
const user = await usersColl.findOne({ 'minecraft.uuid': uuid })
|
||||
|
||||
const response = await getUsernameWithTeam(user);
|
||||
|
||||
await $fetch(config.discordHost + '/minecraft/sendchatmessage', {
|
||||
method: 'POST',
|
||||
body: {
|
||||
username: response.usernameWithoutStyle,
|
||||
// avatarURL: 'https://cdn.discordapp.com/avatars/' + doc.discord.id + '/' + doc.discord.avatarHash + '.png',
|
||||
avatarURL: 'https://api.mineatar.io/face/' + user.minecraft.uuid + '?scale=16',
|
||||
content: content,
|
||||
}
|
||||
})
|
||||
|
||||
return { code: 'success' }
|
||||
});
|
@ -1,21 +0,0 @@
|
||||
import { getUsernameWithTeam } from "~/server/utils/auth";
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const { discordId, content } = await readBody(event);
|
||||
|
||||
const coll = db.collection('users');
|
||||
const user = await coll.findOne({ 'discord.id': discordId });
|
||||
|
||||
const response = await getUsernameWithTeam(user)
|
||||
|
||||
let tellraw;
|
||||
if (user.team) {
|
||||
tellraw = `["",{"text":"DC","color":"gray"},{"text":" ${user.username} ["},{"text":"${response.team.name}","color":"${response.team.color}"},{"text":"] > ${content}"}]`
|
||||
} else {
|
||||
tellraw = `["",{"text":"DC","color":"gray"},{"text":" ${user.username} > ${content}"}]`
|
||||
}
|
||||
|
||||
await sendRconCommand(`tellraw @a ${tellraw}`)
|
||||
|
||||
return { status: 'success' }
|
||||
});
|
@ -1,18 +0,0 @@
|
||||
export default defineEventHandler(async (event) => {
|
||||
const { content, uuid } = await readBody(event);
|
||||
|
||||
const config = useRuntimeConfig();
|
||||
|
||||
const coll = db.collection('users')
|
||||
const doc = await coll.findOne({ 'minecraft.uuid': uuid })
|
||||
|
||||
await $fetch(config.discordHost + '/minecraft/sendgamemessage', {
|
||||
method: 'POST',
|
||||
body: {
|
||||
avatarURL: 'https://api.mineatar.io/face/' + doc.minecraft.uuid + '?scale=16',
|
||||
content: content,
|
||||
}
|
||||
})
|
||||
|
||||
return { status: 'success' }
|
||||
});
|
@ -1,14 +0,0 @@
|
||||
export default defineEventHandler(async (event) => {
|
||||
const user = await getAuth(event)
|
||||
|
||||
if (!auth.minecraft.uuid) throw createError({ errorCode: 400, statusMessage: 'Geen Minecraft account is gekoppeld' })
|
||||
|
||||
const minecraftProfile = await $fetch(`https://sessionserver.mojang.com/session/minecraft/profile/${user.minecraft.uuid}`)
|
||||
|
||||
const usersColl = db.collection('users')
|
||||
const newUser = await usersColl.findOneAndUpdate({ 'minecraft.uuid': auth.minecraft.uuid }, { $set: { 'minecraft.username': minecraftProfile.name, username: user.useMinecraftUsername ? minecraftProfile.name : user.discord.username } }, { returnDocument: 'after' })
|
||||
|
||||
applyUsername(newUser.value)
|
||||
|
||||
return newUser.value
|
||||
});
|
@ -1,13 +0,0 @@
|
||||
export default defineEventHandler(async (event) => {
|
||||
const auth = await getAuth(event)
|
||||
|
||||
if (!auth.minecraft.uuid) throw createError({ errorCode: 400, statusMessage: 'Geen Minecraft account is gekoppeld' })
|
||||
|
||||
const whitelistColl = db.collection('whitelist')
|
||||
await whitelistColl.deleteOne({ uuid: auth.minecraft.uuid })
|
||||
|
||||
const usersColl = db.collection('users')
|
||||
await usersColl.findOneAndUpdate({ 'minecraft.uuid': auth.minecraft.uuid }, { $set: { 'minecraft.uuid': null, 'minecraft.username': null } })
|
||||
|
||||
return { code: 'success' }
|
||||
});
|
@ -1,38 +0,0 @@
|
||||
import { getUsernameWithTeam } from "~/server/utils/auth";
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const { uuid } = await readBody(event)
|
||||
|
||||
const coll = db.collection('whitelist')
|
||||
const usersColl = db.collection('users')
|
||||
|
||||
const doc = await coll.findOne({ uuid: uuid })
|
||||
|
||||
if (doc && !doc.verified) return { code: doc.code, verified: false }
|
||||
|
||||
if (doc && doc.verified) {
|
||||
const user = await usersColl.findOne({ 'minecraft.uuid': uuid });
|
||||
|
||||
const response = await getUsernameWithTeam(user);
|
||||
|
||||
return { verified: true, username: response.username, usernameWithoutStyle: response.usernameWithoutStyle }
|
||||
}
|
||||
|
||||
await coll.createIndex({ code: 1 }, { unique: true })
|
||||
|
||||
const code = await insertDoc(coll, uuid)
|
||||
|
||||
return { code: code.toString(), verified: false }
|
||||
});
|
||||
|
||||
const insertDoc = async (coll, uuid) => {
|
||||
try {
|
||||
const code = Math.floor(100000 + Math.random() * 900000)
|
||||
await coll.insertOne({ uuid: uuid, verified: false, code: code.toString() })
|
||||
|
||||
return code;
|
||||
} catch (e) {
|
||||
const code = await insertDoc(coll, uuid)
|
||||
return code;
|
||||
}
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
export default defineEventHandler(async (event) => {
|
||||
const { code } = await readBody(event)
|
||||
|
||||
if (!code) throw createError({ statusCode: 400, statusMessage: 'Code is vereist'})
|
||||
|
||||
const auth = await getAuth(event)
|
||||
|
||||
const whitelistColl = db.collection('whitelist')
|
||||
const whitelistDoc = await whitelistColl.findOne({ code: code.toString() })
|
||||
|
||||
if (!whitelistDoc) throw createError({ statusCode: 400, statusMessage: 'Code is niet gevonden, join eerste de Minecraft server' })
|
||||
if (whitelistDoc && whitelistDoc.verified) throw createError({ statusCode: 400, statusMessage: 'Already verified' })
|
||||
|
||||
await whitelistColl.updateOne({ code: code.toString() }, { $set: { verified: true } })
|
||||
|
||||
const minecraftProfile = await $fetch(`https://sessionserver.mojang.com/session/minecraft/profile/${whitelistDoc.uuid}`)
|
||||
|
||||
const usersColl = db.collection('users')
|
||||
await usersColl.updateOne({ 'discord.id': auth.discord.id }, { $set: { 'minecraft.uuid': whitelistDoc.uuid, 'minecraft.username': minecraftProfile.name } })
|
||||
|
||||
|
||||
return { uuid: whitelistDoc.uuid, verified: false, username: minecraftProfile.name }
|
||||
});
|
@ -1,17 +0,0 @@
|
||||
import { ObjectId } from "mongodb";
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const { id } = await readBody(event)
|
||||
|
||||
const user = await getAuth(event)
|
||||
|
||||
const storesColl = db.collection('stores');
|
||||
|
||||
const result = await storesColl.deleteOne({ _id: new ObjectId(id) })
|
||||
|
||||
if (result.deletedCount === 1) {
|
||||
return result
|
||||
} else {
|
||||
throw createError({ statusCode: 500, statusMessage: 'Error tijdens het verwijderen van de store' })
|
||||
}
|
||||
});
|
@ -1,21 +0,0 @@
|
||||
export default defineEventHandler(async () => {
|
||||
|
||||
const storesColl = db.collection('stores');
|
||||
|
||||
const cursor = storesColl.aggregate([
|
||||
{
|
||||
$lookup:
|
||||
{
|
||||
from: "users",
|
||||
localField: "ownerId",
|
||||
foreignField: "_id",
|
||||
as: "owner",
|
||||
},
|
||||
},
|
||||
{
|
||||
$unwind: "$owner",
|
||||
},
|
||||
])
|
||||
|
||||
return await cursor.toArray()
|
||||
});
|
@ -1,13 +0,0 @@
|
||||
import { ObjectId } from "mongodb";
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const { coords, name, items, id } = await readBody(event)
|
||||
|
||||
const user = await getAuth(event)
|
||||
|
||||
const storesColl = db.collection('stores');
|
||||
|
||||
const { value: store } = await storesColl.findOneAndUpdate({ _id: id ? new ObjectId(id) : new ObjectId() }, { $set: { coords: coords, name: name, items: items, ownerId: user._id, } }, { returnDocument: 'after', upsert: true })
|
||||
|
||||
return store
|
||||
});
|
@ -1,24 +0,0 @@
|
||||
import { ObjectId } from 'mongodb'
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const { teamId } = await readBody(event);
|
||||
|
||||
const user = await getAuth(event)
|
||||
const config = useRuntimeConfig()
|
||||
|
||||
const teamsColl = db.collection('teams')
|
||||
const usersColl = db.collection('users')
|
||||
|
||||
const team = await teamsColl.findOneAndUpdate({ _id: new ObjectId(teamId) }, { $inc: { count: 1 } })
|
||||
|
||||
if (!team.value) return createError({ statusCode: 500, statusMessage: 'Team niet gevonden'})
|
||||
|
||||
await $fetch(config.discordHost + '/team/addteammember', {
|
||||
method: 'POST',
|
||||
body: { voiceChannelId: team.value.voiceChannelId, textChannelId: team.value.textChannelId, discordId: user.discord.id }
|
||||
})
|
||||
|
||||
await usersColl.updateOne({ _id: new ObjectId(user._id) }, { $set: { 'team.id': teamId, 'team.admin': false }})
|
||||
|
||||
return team
|
||||
});
|
@ -1,8 +0,0 @@
|
||||
export default defineEventHandler(async () => {
|
||||
const teamsColl = db.collection('teams')
|
||||
|
||||
const cursor = teamsColl.find();
|
||||
const teams = await cursor.toArray()
|
||||
|
||||
return teams
|
||||
});
|
@ -1,16 +0,0 @@
|
||||
import { ObjectId } from 'mongodb'
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const { id } = await readBody(event);
|
||||
|
||||
const user = await getAuth(event)
|
||||
|
||||
const teamsColl = db.collection('teams')
|
||||
const usersColl = db.collection('users')
|
||||
|
||||
const team = await teamsColl.findOne({ _id: new ObjectId(user.team.id) })
|
||||
|
||||
usersColl.updateOne({ _id: new ObjectId(id) }, { $pull: { teamInvites: team._id.toString() } })
|
||||
|
||||
return team
|
||||
});
|
@ -1,28 +0,0 @@
|
||||
export default defineEventHandler(async (event) => {
|
||||
const { teamName, teamColor } = await readBody(event);
|
||||
|
||||
if (!teamName || !teamColor) return createError({ statusCode: 400, statusMessage: 'Team naam en kleur zijn vereist' })
|
||||
if (!isHexColor(teamColor)) return createError({ statusCode: 400, statusMessage: 'Team kleur is geen gelidige kleurencode' })
|
||||
if (!verifyUsername(teamName)) return createError({ statusCode: 400, statusMessage: 'Team naam moet alfanumeriek zijn en mag maximaal 20 tekens lang zijn' })
|
||||
|
||||
const user = await getAuth(event)
|
||||
const config = useRuntimeConfig()
|
||||
|
||||
if (user.team) return createError({ statusCode: 400, statusMessage: 'Gebruiker is al in een team' })
|
||||
|
||||
const teamsColl = db.collection('teams')
|
||||
const usersColl = db.collection('users')
|
||||
|
||||
if (await teamsColl.findOne({ name: { $regex: new RegExp(teamName, "i") } })) return createError({ statusCode: 400, statusMessage: 'Team naam bestaat al' })
|
||||
|
||||
const discordResponse = await $fetch(config.discordHost + '/team/createchannels', {
|
||||
method: 'POST',
|
||||
body: { name: teamName, discordId: user.discord.id }
|
||||
})
|
||||
|
||||
const response = await teamsColl.insertOne({ name: teamName, color: teamColor, count: 1, textChannelId: discordResponse.textChannel.id, voiceChannelId: discordResponse.voiceChannel.id })
|
||||
|
||||
await usersColl.findOneAndUpdate({ 'discord.id': user.discord.id }, { $set: { 'team.id': response.insertedId.toString(), 'team.admin': true } })
|
||||
|
||||
return response;
|
||||
});
|
@ -1,14 +0,0 @@
|
||||
import { ObjectId } from 'mongodb'
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const { userId } = await readBody(event)
|
||||
|
||||
const user = await getAuth(event)
|
||||
|
||||
if (!user.team.admin) return createError({ statusCode: 403, statusMessage: 'Admin team rol vereist' })
|
||||
|
||||
const usersColl = db.collection('users')
|
||||
await usersColl.findOneAndUpdate({ _id: new ObjectId(userId) }, { $set: { 'team.admin': false } });
|
||||
|
||||
return { status: 'Success' }
|
||||
});
|
@ -1,29 +0,0 @@
|
||||
import { ObjectId } from 'mongodb'
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const { name, color } = await readBody(event);
|
||||
|
||||
if (!name || !color) return createError({ statusCode: 400, statusMessage: 'Team naam en kleur zijn vereist' })
|
||||
if (!isHexColor(color)) return createError({ statusCode: 400, statusMessage: 'Team kleur is geen goede kleurencode' })
|
||||
if (!verifyUsername(name)) return createError({ statusCode: 400, statusMessage: 'Team naam moet alfanumeriek zijn en mag maximaal 20 tekens lang zijn' })
|
||||
|
||||
const user = await getAuth(event)
|
||||
const config = useRuntimeConfig()
|
||||
|
||||
if (!user.team.admin) return createError({ statusCode: 403, statusMessage: 'Admin team rol vereist' })
|
||||
|
||||
const teamsColl = db.collection('teams')
|
||||
|
||||
const team = await teamsColl.findOne({ _id: new ObjectId(user.team.id) });
|
||||
|
||||
if (team.name !== name && await teamsColl.findOne({ name: { $regex: new RegExp(name, "i") } })) return createError({ statusCode: 400, statusMessage: 'Team naam bestaat al' })
|
||||
|
||||
await $fetch(config.discordHost + '/team/edit', {
|
||||
method: 'POST',
|
||||
body: { voiceChannelId: team.voiceChannelId, textChannelId: team.textChannelId, name: name }
|
||||
})
|
||||
|
||||
await teamsColl.updateOne({ _id: new ObjectId(user.team.id) }, { $set: { name: name, color: color } })
|
||||
|
||||
return team
|
||||
});
|