added reminder cmd + fixed custom channel
All checks were successful
release-tag / release-image (push) Successful in 22s

This commit is contained in:
2025-01-14 19:22:44 +01:00
parent 3852ff922e
commit 76525b73c9
6 changed files with 239 additions and 81 deletions

View File

@@ -17,20 +17,31 @@ export class DmService {
// todo // todo
} }
async reminderPrivate(member: GuildMember) {
console.log("reminder");
try {
const content = `hey, kleine erinnerung :)\nbitte stelle dich auf <#${config.discord.channelIdIntroduction}> vor, damit du alle kanaele ansehen und nutzen kannst! <3`
await client.users.send(member, content);
} catch (error) {
const channels = client.channels;
const channel = channels.cache.get(config.discord.channelIdNotification);
if (channel?.isTextBased() && channel?.isSendable()) {
await channel.send(`konnte keine private nachricht an <@${member.user.id}> senden`);
}
console.error("error while sending a welcome msg:", error);
}
}
async welcomePrivate(member: GuildMember) { async welcomePrivate(member: GuildMember) {
console.log("welcome private"); console.log("welcome private");
try { try {
await client.users.send(member, dmWelcomeContent); await client.users.send(member, dmWelcomeContent);
} catch (error) { } catch (error) {
console.error("error while sending a welcome msg:", error); const channels = client.channels;
} const channel = channels.cache.get(config.discord.channelIdNotification);
} if (channel?.isTextBased() && channel?.isSendable()) {
await channel.send(`konnte keine private nachricht an <@${member.user.id}> senden`);
async welcomePrivateTest() { }
console.log("welcomePrivateTest()");
try {
await client.users.send(config.discord.myId, dmWelcomeContent);
} catch (error) {
console.error("error while sending a welcome msg:", error); console.error("error while sending a welcome msg:", error);
} }
} }
@@ -66,6 +77,11 @@ export class DmService {
try { try {
await client.users.send(member, dmAcceptedContent); await client.users.send(member, dmAcceptedContent);
} catch (error) { } catch (error) {
const channels = client.channels;
const channel = channels.cache.get(config.discord.channelIdNotification);
if (channel?.isTextBased() && channel?.isSendable()) {
await channel.send(`konnte keine private nachricht an <@${member.user.id}> senden`);
}
console.error("error while sending a accept msg:", error); console.error("error while sending a accept msg:", error);
} }
} }

View File

@@ -10,9 +10,9 @@ import {
type GuildMember, type GuildMember,
type Interaction, type Interaction,
type OmitPartialGroupDMChannel, type OmitPartialGroupDMChannel,
VoiceState, type VoiceState,
VoiceChannel, type VoiceChannel,
StageChannel, type StageChannel,
Events, Events,
type VoiceBasedChannel, type VoiceBasedChannel,
} from "discord.js"; } from "discord.js";
@@ -22,26 +22,36 @@ export class DynamicChannelService {
// todo // todo
} }
async createChannel(oldState: VoiceState, newState: VoiceState, channel: VoiceBasedChannel): Promise<StageChannel | VoiceChannel> { async createChannel(
console.log("createChannel()"); oldState: VoiceState,
newState: VoiceState,
channel: VoiceBasedChannel,
): Promise<StageChannel | VoiceChannel> {
//console.log("createChannel()");
const newChannel = await channel.clone({ const newChannel = await channel.clone({
name: channel.name + " " + newState.member?.displayName, name: `${channel.name.substring(2)}; ${newState.member?.displayName}`,
position: channel.position position: 100,
}); });
return newChannel; return newChannel;
} }
async deleteChannel(oldState: VoiceState, newState: VoiceState, newChannel: StageChannel | VoiceChannel, channelListeners: Map<any, any>, channelListener: (oldState: VoiceState, newState: VoiceState) => void) { async deleteChannel(
oldState: VoiceState,
console.log("deleteChannel()"); newState: VoiceState,
newChannel: StageChannel | VoiceChannel,
if (oldState.channelId === newChannel.id || newState.channelId === newChannel.id) { // biome-ignore lint/suspicious/noExplicitAny: <explanation>
channelListeners: Map<any, any>,
channelListener: (oldState: VoiceState, newState: VoiceState) => void,
) {
//console.log("deleteChannel()");
if (
oldState.channelId === newChannel.id ||
newState.channelId === newChannel.id
) {
if (newChannel.members.size === 0) { if (newChannel.members.size === 0) {
newChannel.delete() newChannel.delete().catch(console.error);
.catch(console.error);
client.removeListener(Events.VoiceStateUpdate, channelListener); client.removeListener(Events.VoiceStateUpdate, channelListener);
channelListeners.delete(newChannel.id); channelListeners.delete(newChannel.id);

View File

@@ -13,6 +13,8 @@ import {
type CacheType, type CacheType,
type GuildMember, type GuildMember,
type Interaction, type Interaction,
GuildMemberRoleManager,
type APIInteractionGuildMember,
} from "discord.js"; } from "discord.js";
import { DmService } from "actions/dm/dm.service.ts"; import { DmService } from "actions/dm/dm.service.ts";
import { Commands, type CommandsType } from "commands/index.ts"; import { Commands, type CommandsType } from "commands/index.ts";
@@ -35,7 +37,6 @@ export class GreetingService {
} }
async handleChatInputCommand(interaction: ChatInputCommandInteraction<CacheType>) { async handleChatInputCommand(interaction: ChatInputCommandInteraction<CacheType>) {
console.log("accept");
const commandName = interaction.commandName as CommandsType; const commandName = interaction.commandName as CommandsType;
switch (commandName) { switch (commandName) {
case Commands.Enum.accept: case Commands.Enum.accept:
@@ -44,6 +45,9 @@ export class GreetingService {
case Commands.Enum.welcome: case Commands.Enum.welcome:
await this.welcomeCommand(interaction); await this.welcomeCommand(interaction);
return; return;
case Commands.Enum.reminder:
await this.reminderCommand(interaction);
return;
default: default:
break; break;
} }
@@ -78,10 +82,9 @@ export class GreetingService {
//console.log(input); //console.log(input);
// permission check // permission check
const userIdCommand = interaction.user.id; if (await this.checkPermission(interaction.member) !== true) {
if (userIdCommand !== config.discord.myId) {
await interaction.reply({ await interaction.reply({
content: "you have no permission for that command", content: "du hast keine rechte fuer diesen befehl",
ephemeral: true, ephemeral: true,
}); });
return; return;
@@ -100,14 +103,19 @@ export class GreetingService {
return; return;
} }
const username = (await guild.members.fetch(userId)).user.username;
console.log(username);
// get member from id
const member = await guild.members.fetch(userId); const member = await guild.members.fetch(userId);
const username = member.user.username;
console.log(username);
const role = config.discord.roleStudy; if (await this.checkRole(member) === true) {
await interaction.reply({
content: `${member.user.username} hat die rolle *lernende:r* schon!`,
ephemeral: true,
});
return;
}
await member.roles.add(role); await member.roles.add(config.discord.roleStudy);
await interaction.reply({ await interaction.reply({
content: `die rolle *lernende:r* wurde erfolgreich an ${member.user.username} vergeben`, content: `die rolle *lernende:r* wurde erfolgreich an ${member.user.username} vergeben`,
@@ -180,25 +188,77 @@ export class GreetingService {
return greetContent[getRandomInt(0, greetContent.length - 1)]; return greetContent[getRandomInt(0, greetContent.length - 1)];
} }
async welcomeCommand( async reminderCommand(
interaction: ChatInputCommandInteraction<CacheType> interaction: ChatInputCommandInteraction<CacheType>) {
) { console.log("remind user");
console.log("accept user");
// get the string option // get the string option
const input = interaction.options.getString("input") || ""; const input = interaction.options.getString("input") || "";
// return the value // return the value
//console.log(input); //console.log(input);
// permission check try {
const userIdCommand = interaction.user.id; // permission check
if (userIdCommand !== config.discord.myId) { if (await this.checkPermission(interaction.member) !== true) {
await interaction.reply({
content: "du hast keine rechte fuer diesen befehl",
ephemeral: true,
});
return;
}
// get user id from mentioning
const userId = input.replace(/[<@!>]/g, "");
console.log(userId.toString());
const guild = interaction.guild;
if (!guild) {
await interaction.reply({
content: "command can only be used on a server",
ephemeral: true,
});
return;
}
const member = await guild.members.fetch(userId);
const username = member.user.username;
console.log(username);
if (await this.checkRole(member) === true) {
await interaction.reply({
content: `${member.user.username} hat die rolle *lernende:r* schon!`,
ephemeral: true,
});
return;
}
await this.dmService.reminderPrivate(member);
//await member.roles.add(config.discord.roleStudy);
await interaction.reply({ await interaction.reply({
content: "you have no permission for that command", content: `${member.user.username} wurde erfolgrich remindet`,
ephemeral: true,
});
this.dmService.acceptDm(member);
} catch (error) {
console.error("error while reminding:", error);
await interaction.reply({
content:
"Es gab einen Fehler beim reminden. Stelle sicher, dass du einen gültigen User erwähnt hast.",
ephemeral: true, ephemeral: true,
}); });
return;
} }
}
async welcomeCommand(
interaction: ChatInputCommandInteraction<CacheType>
) {
console.log("welcome user");
// get the string option
const input = interaction.options.getString("input") || "";
// return the value
//console.log(input);
try { try {
// get user id from mentioning // get user id from mentioning
@@ -213,14 +273,27 @@ export class GreetingService {
return; return;
} }
const username = (await guild.members.fetch(userId)).user.username; if (await this.checkPermission(interaction.member) !== true) {
console.log(username); await interaction.reply({
content: "du hast keine rechte fuer diesen befehl",
ephemeral: true,
});
return;
}
// get member from id // get member from id
const member = await guild.members.fetch(userId); const member = await guild.members.fetch(userId);
const username = member.user.username;
console.log(username);
const welcomeContent = getWelcomeContent(member); const welcomeContent = getWelcomeContent(member);
if (await this.checkRole(member) === true) {
await interaction.reply({
content: `${member.user.username} wurde schon begruesst!`,
ephemeral: true,
});
}
try { try {
const channels = client.channels; const channels = client.channels;
@@ -240,6 +313,7 @@ export class GreetingService {
content: `erfolgreich welcome command: ${member.user.username}`, content: `erfolgreich welcome command: ${member.user.username}`,
ephemeral: true, ephemeral: true,
}); });
} catch (error) { } catch (error) {
console.error("fehler bei welcome command", error); console.error("fehler bei welcome command", error);
await interaction.reply({ await interaction.reply({
@@ -249,4 +323,26 @@ export class GreetingService {
}); });
} }
} }
async checkPermission(member: GuildMember | APIInteractionGuildMember | null) {
let permission = false;
if (member?.roles instanceof GuildMemberRoleManager) {
if (member.roles.cache.has(config.discord.roleAdmin) || member.roles.cache.has(config.discord.roleMod)) {
console.log("user has permission");
permission = true;
}
}
return permission;
}
async checkRole(member: GuildMember) {
let hasRole = false;
if (member?.roles instanceof GuildMemberRoleManager) {
if (member.roles.cache.has(config.discord.roleAdmin) || member.roles.cache.has(config.discord.roleMod)) {
console.log("user has permission");
hasRole = true;
}
}
return hasRole;
}
} }

View File

@@ -1,7 +1,7 @@
import { SlashCommandBuilder, userMention } from "discord.js"; import { SlashCommandBuilder, userMention } from "discord.js";
import { z } from "zod"; import { z } from "zod";
export const Commands = z.enum(["giessen", "medikamente", "hilfe", "accept", "welcome", "embed", "message"]); export const Commands = z.enum(["giessen", "medikamente", "hilfe", "accept", "welcome", "embed", "message", "reminder"]);
export const CommandsMeta: Record<z.output<typeof Commands>, { description: string }> = { export const CommandsMeta: Record<z.output<typeof Commands>, { description: string }> = {
giessen: { giessen: {
@@ -24,6 +24,9 @@ export const CommandsMeta: Record<z.output<typeof Commands>, { description: stri
}, },
message: { message: {
description: "admin use only" description: "admin use only"
},
reminder: {
description: "admin use only"
} }
} }
@@ -76,6 +79,13 @@ export default function getCommands() {
option.setName('input') option.setName('input')
.setDescription('input for bot') .setDescription('input for bot')
.setRequired(true)), .setRequired(true)),
new SlashCommandBuilder()
.setName(Commands.Enum.reminder)
.setDescription(CommandsMeta.reminder.description)
.addStringOption(option =>
option.setName('input')
.setDescription('input for bot')
.setRequired(true)),
].map((command) => command.toJSON()); ].map((command) => command.toJSON());

View File

@@ -22,6 +22,8 @@ export default {
vchannelIdForGroup: process.env.DISCORD_VCHANNEL_ID_FOR_GROUP || "", vchannelIdForGroup: process.env.DISCORD_VCHANNEL_ID_FOR_GROUP || "",
// roles // roles
roleStudy: process.env.PEOPLE || "", roleStudy: process.env.PEOPLE || "",
roleMod: process.env.MOD || "",
roleAdmin: process.env.ADMIN || "",
// other // other
applicationId: process.env.DISCORD_APPLICATION_ID || "", applicationId: process.env.DISCORD_APPLICATION_ID || "",

View File

@@ -4,7 +4,7 @@ import {
Client, Client,
Events, Events,
IntentsBitField, IntentsBitField,
VoiceState, type VoiceState,
type ButtonInteraction, type ButtonInteraction,
type CacheType, type CacheType,
type ChatInputCommandInteraction, type ChatInputCommandInteraction,
@@ -48,7 +48,7 @@ export default class DiscordController extends EventEmitter {
this.customMessageService = new CustomMessageService(); this.customMessageService = new CustomMessageService();
this.dynamicChannelService = new DynamicChannelService(); this.dynamicChannelService = new DynamicChannelService();
var channelListeners = new Map(); let channelListeners = new Map();
// log when running // log when running
client.once("ready", async () => { client.once("ready", async () => {
await this.setActivity(); await this.setActivity();
@@ -70,32 +70,48 @@ export default class DiscordController extends EventEmitter {
await this.greetingService.welcome(member); await this.greetingService.welcome(member);
}); });
client.on(Events.VoiceStateUpdate, async (oldState: VoiceState, newState: VoiceState) => { client.on(
// check if user joined a vc Events.VoiceStateUpdate,
if (!oldState.channelId && newState.channelId || oldState.channelId != newState.channelId) { async (oldState: VoiceState, newState: VoiceState) => {
// check if right vc // check if user joined a vc
if (newState.channelId === config.discord.vchannelIdForTwo || newState.channelId === config.discord.vchannelIdForThree || newState.channelId === config.discord.vchannelIdForFour || newState.channelId === config.discord.vchannelIdForGroup) { if (
const channel = newState.channel; (!oldState.channelId && newState.channelId) ||
if (!channel) { oldState.channelId !== newState.channelId
console.error("channel not found"); ) {
return; // check if right vc
} if (
try { newState.channelId === config.discord.vchannelIdForTwo ||
// create new channel with same settings newState.channelId === config.discord.vchannelIdForThree ||
/*const newChannel = await channel.clone({ newState.channelId === config.discord.vchannelIdForFour ||
newState.channelId === config.discord.vchannelIdForGroup
) {
const channel = newState.channel;
if (!channel) {
console.error("channel not found");
return;
}
try {
// create new channel with same settings
/*const newChannel = await channel.clone({
name: channel.name + "; " + newState.member?.displayName, name: channel.name + "; " + newState.member?.displayName,
position: channel.position position: channel.position
});*/ });*/
const newChannel = await this.dynamicChannelService.createChannel(
oldState,
newState,
channel,
);
const newChannel = await this.dynamicChannelService.createChannel(oldState, newState, channel); // move user in new channel
await newState.setChannel(newChannel);
// move user in new channel // create specific listener for channel
await newState.setChannel(newChannel); const channelListener = async (
oldState: VoiceState,
// create specific listener for channel newState: VoiceState,
const channelListener = async (oldState: VoiceState, newState: VoiceState) => { ) => {
/*if (oldState.channelId === newChannel.id || newState.channelId === newChannel.id) { /*if (oldState.channelId === newChannel.id || newState.channelId === newChannel.id) {
// check if channel empty // check if channel empty
if (newChannel.members.size === 0) { if (newChannel.members.size === 0) {
newChannel.delete() newChannel.delete()
@@ -105,22 +121,27 @@ export default class DiscordController extends EventEmitter {
channelListeners.delete(newChannel.id); channelListeners.delete(newChannel.id);
} }
}*/ }*/
channelListeners = await this.dynamicChannelService.deleteChannel(oldState, newState, newChannel, channelListeners, channelListener); channelListeners =
await this.dynamicChannelService.deleteChannel(
oldState,
newState,
newChannel,
channelListeners,
channelListener,
);
};
// save listener in map
channelListeners.set(newChannel.id, channelListener);
}; // add listener
// save listener in map client.on(Events.VoiceStateUpdate, channelListener);
channelListeners.set(newChannel.id, channelListener); } catch (error) {
console.error("error while duplicating channel", error);
// add listener }
client.on(Events.VoiceStateUpdate, channelListener);
} catch (error) {
console.error("error while duplicating channel", error);
} }
} }
} },
}); );
} }
async setActivity() { async setActivity() {
@@ -191,6 +212,9 @@ export default class DiscordController extends EventEmitter {
case Commands.Enum.message: case Commands.Enum.message:
await this.customMessageService.handleInteraction(interaction); await this.customMessageService.handleInteraction(interaction);
return; return;
case Commands.Enum.reminder:
await this.greetingService.handleInteraction(interaction);
return;
default: default:
break; break;
} }