Compare commits

..

No commits in common. "56ec6a1ad839773ec5233eceeaef2fdec82558d1" and "0d757fef7947fb5096e910896eba92820c614fc3" have entirely different histories.

10 changed files with 59 additions and 152 deletions

View File

@ -1,7 +1,7 @@
import config from "config"; import config from "config";
import client from "lib/client"; import client from "lib/client";
import { getRandomInt } from "lib/utils"; import { getRandomInt } from "lib/utils";
import { } from "./dynamicVChannel.components.ts"; import { } from "./dynamicChannel.components.ts";
import { import {
Client, Client,
EmbedBuilder, EmbedBuilder,
@ -17,33 +17,35 @@ import {
type VoiceBasedChannel, type VoiceBasedChannel,
} from "discord.js"; } from "discord.js";
export class DynamicVChannelService { export class DynamicChannelService {
async handleInteraction(interaction: Interaction<CacheType>) { async handleInteraction(interaction: Interaction<CacheType>) {
// todo // todo
} }
async createVChannel( async createChannel(
oldState: VoiceState,
newState: VoiceState, newState: VoiceState,
channel: VoiceBasedChannel channel: VoiceBasedChannel,
): Promise<StageChannel | VoiceChannel> { ): Promise<StageChannel | VoiceChannel> {
//console.log("createChannel()"); //console.log("createChannel()");
const newVChannel = await channel.clone({ const newChannel = await channel.clone({
name: `${channel.name.substring(2)}; ${newState.member?.displayName}`, name: `${channel.name.substring(2)}; ${newState.member?.displayName}`,
position: 100, position: 100,
}); });
return newVChannel; return newChannel;
} }
async deleteVChannel( async deleteChannel(
oldState: VoiceState, oldState: VoiceState,
newState: VoiceState, newState: VoiceState,
newChannel: StageChannel | VoiceChannel, newChannel: StageChannel | VoiceChannel,
// biome-ignore lint/suspicious/noExplicitAny: <explanation> // biome-ignore lint/suspicious/noExplicitAny: <explanation>
channelListeners: Map<any, any>, channelListeners: Map<any, any>,
channelListener: (oldState: VoiceState, newState: VoiceState) => void channelListener: (oldState: VoiceState, newState: VoiceState) => void,
) { ) {
//console.log("deleteChannel()"); //console.log("deleteChannel()");
if ( if (
oldState.channelId === newChannel.id || oldState.channelId === newChannel.id ||
newState.channelId === newChannel.id newState.channelId === newChannel.id

View File

@ -133,7 +133,6 @@ export class GreetingService {
} }
} }
// unused
async greet() { async greet() {
client.user?.setActivity("guten morgen! :3", { type: 4 }); client.user?.setActivity("guten morgen! :3", { type: 4 });
console.log("set activity: awake"); console.log("set activity: awake");
@ -143,14 +142,13 @@ export class GreetingService {
const channels = client.channels; const channels = client.channels;
const channel = channels.cache.get(config.discord.channelIdOffTopic); const channel = channels.cache.get(config.discord.testChannel);
if (channel?.isTextBased && channel?.isSendable()) { if (channel?.isTextBased && channel?.isSendable()) {
await channel.send({ content: this.getContent(false) }); await channel.send({ content: this.getContent(false) });
} }
} }
// unused
async sleep() { async sleep() {
client.user?.setActivity("zzzzZZ..", { type: 4 }); client.user?.setActivity("zzzzZZ..", { type: 4 });
console.log("set activity: asleep"); console.log("set activity: asleep");
@ -160,7 +158,7 @@ export class GreetingService {
const channels = client.channels; const channels = client.channels;
const channel = channels.cache.get(config.discord.channelIdOffTopic); const channel = channels.cache.get(config.discord.testChannel);
if (channel?.isTextBased && channel?.isSendable()) { if (channel?.isTextBased && channel?.isSendable()) {
await channel.send({ content: this.getContent(true) }); await channel.send({ content: this.getContent(true) });
@ -169,15 +167,14 @@ export class GreetingService {
async newYear() { async newYear() {
client.user?.setActivity("frohes neues! :)", { type: 4 }); client.user?.setActivity("frohes neues! :)", { type: 4 });
console.log("set activity: happy new year"); console.log("set activity: happy new Year");
client.user?.setPresence({ client.user?.setPresence({
status: "online", status: "online",
}); });
// unused
/*const channels = client.channels; /*const channels = client.channels;
const channel = channels.cache.get(config.discord.channelIdOffTopic); const channel = channels.cache.get(config.discord.channelId);
if (channel?.isTextBased && channel?.isSendable()) { if (channel?.isTextBased && channel?.isSendable()) {
await channel.send({ content: "frohes neues! @everyone" }); await channel.send({ content: "frohes neues! @everyone" });

View File

@ -1,41 +0,0 @@
import config from "config";
import { Events, NewsChannel, type VoiceState } from "discord.js";
import client from "lib/client";
import EventEmitter from "node:events";
import { PomodoroService } from "actions/pomodoro/pomodoro.service";
export default class PomodoroController extends EventEmitter {
private pomodoroService: PomodoroService;
private activePomodoros = new Set<string>();
private pomodoroChannels = [config.discord.vchannelIdPomodoro25, config.discord.vchannelIdPomodoro50];
constructor() {
super();
this.pomodoroService = new PomodoroService();
client.on(Events.VoiceStateUpdate, async (oldState, newState) => {
const userId = newState.id;
const joinedPomodoroVC = newState.channelId != null && this.pomodoroChannels.includes(newState.channelId) &&
oldState.channelId !== newState.channelId;
const leftPomodoroVC = oldState.channelId != null && this.pomodoroChannels.includes(oldState.channelId) &&
newState.channelId !== config.discord.vchannelIdPomodoro25;
if (joinedPomodoroVC && !this.activePomodoros.has(userId)) {
const member = newState.member;
const vchannel = newState.channel;
if (!member || !vchannel) return;
this.activePomodoros.add(userId);
this.pomodoroService.startPomodoroLoop(member, vchannel);
}
if (leftPomodoroVC && this.activePomodoros.has(userId)) {
this.pomodoroService.stopPomodoro(userId);
this.activePomodoros.delete(userId);
}
});
}
}

View File

@ -1,67 +0,0 @@
import type { GuildMember, VoiceBasedChannel } from "discord.js";
import client from "lib/client";
import config from "config";
import { CustomMessageService } from "actions/customMessage/customMessage.service";
export class PomodoroService {
customMessageService: CustomMessageService;
private activeControllers = new Map<string, AbortController>();
constructor() {
this.customMessageService = new CustomMessageService();
}
public async startPomodoroLoop(member: GuildMember, vchannel: VoiceBasedChannel) {
const userId = member.id;
const controller = new AbortController();
this.activeControllers.set(userId, controller);
const minutesWork = vchannel.id === config.discord.vchannelIdPomodoro25 ? 25 : 50;
const minutesBreak = minutesWork / 5;//vchannel.id === config.discord.vchannelIdPomodoro25 ? 5 : 10;
const signal = controller.signal;
try {
while (!signal.aborted) {
await this.sendMessage(`<@${userId}> 🍅 **pomodoro gestartet!** ${minutesWork} minuten produktivitaet`);
const finishedWork = await this.sleep(minutesWork * 60 * 1000, signal);
if (!finishedWork) break;
await this.sendMessage(`<@${userId}> ☕ **pause!** ${minutesBreak} minuten chillen`);
const finishedBreak = await this.sleep(minutesBreak * 60 * 1000, signal);
if (!finishedBreak) break;
}
} catch (err) {
if ((err as Error).name !== "AbortError") {
console.error("pomodoro fehler:", err);
}
} finally {
this.activeControllers.delete(userId);
}
}
public stopPomodoro(userId: string) {
const controller = this.activeControllers.get(userId);
if (controller) {
controller.abort();
this.activeControllers.delete(userId);
}
}
private async sendMessage(text: string) {
const channel = client.channels.cache.get(config.discord.channelIdPomodoro);
if (channel?.isTextBased() && channel?.isSendable()) {
await channel.send(text);
}
}
private sleep(ms: number, signal: AbortSignal): Promise<boolean> {
return new Promise((resolve, reject) => {
const timeout = setTimeout(() => resolve(true), ms);
signal.addEventListener("abort", () => {
clearTimeout(timeout);
resolve(false);
});
});
}
}

View File

@ -1,6 +1,8 @@
export default { export default {
discord: { discord: {
version: 250508.2150, // test
testChannel: process.env.DISCORD_TEST_CHANNEL_ID || "",
// avocadi // avocadi
serverID: process.env.DISCORD_SERVER_ID || "", serverID: process.env.DISCORD_SERVER_ID || "",
// texxt channel // texxt channel
@ -13,14 +15,11 @@ export default {
channelIdIntroduction: process.env.DISCORD_CHANNEL_ID_INTRODUCTION || "", channelIdIntroduction: process.env.DISCORD_CHANNEL_ID_INTRODUCTION || "",
channelIdOffTopic: process.env.DISCORD_CHANNEL_ID_OFF_TOPIC || "", channelIdOffTopic: process.env.DISCORD_CHANNEL_ID_OFF_TOPIC || "",
channelIdHelp: process.env.DISCORD_CHANNEL_ID_HELP || "", channelIdHelp: process.env.DISCORD_CHANNEL_ID_HELP || "",
channelIdPomodoro: process.env.DISCORD_CHANNEL_ID_POMODORO || "", // voice channel#
// voice channel
vchannelIdForTwo: process.env.DISCORD_VCHANNEL_ID_FOR_TWO || "", vchannelIdForTwo: process.env.DISCORD_VCHANNEL_ID_FOR_TWO || "",
vchannelIdForThree: process.env.DISCORD_VCHANNEL_ID_FOR_THREE || "", vchannelIdForThree: process.env.DISCORD_VCHANNEL_ID_FOR_THREE || "",
vchannelIdForFour: process.env.DISCORD_VCHANNEL_ID_FOR_FOUR || "", vchannelIdForFour: process.env.DISCORD_VCHANNEL_ID_FOR_FOUR || "",
vchannelIdForGroup: process.env.DISCORD_VCHANNEL_ID_FOR_GROUP || "", vchannelIdForGroup: process.env.DISCORD_VCHANNEL_ID_FOR_GROUP || "",
vchannelIdPomodoro25: process.env.DISCORD_VCHANNEL_ID_POMODORO_25_5 || "",
vchannelIdPomodoro50: process.env.DISCORD_VCHANNEL_ID_POMODORO_50_10 || "",
// roles // roles
roleStudy: process.env.PEOPLE || "", roleStudy: process.env.PEOPLE || "",
roleMod: process.env.MOD || "", roleMod: process.env.MOD || "",

View File

@ -13,7 +13,7 @@ import {
} from "discord.js"; } from "discord.js";
import client from "lib/client"; import client from "lib/client";
import EventEmitter from "node:events"; import EventEmitter from "node:events";
import DiscordService from "discord.service"; import DiscordService from "services/discord.service";
import { WaterMeService } from "actions/waterMe/waterMe.service"; import { WaterMeService } from "actions/waterMe/waterMe.service";
import { MedicationService } from "actions/medication/medication.service"; import { MedicationService } from "actions/medication/medication.service";
import { HelpService } from "actions/help/help.service"; import { HelpService } from "actions/help/help.service";
@ -22,24 +22,24 @@ import { GreetingService } from "actions/greeting/greeting.service";
import { ActivityService } from "actions/activity/activity.service"; import { ActivityService } from "actions/activity/activity.service";
import { DmService } from "actions/dm/dm.service"; import { DmService } from "actions/dm/dm.service";
import { CustomMessageService } from "actions/customMessage/customMessage.service"; import { CustomMessageService } from "actions/customMessage/customMessage.service";
import { DynamicVChannelService } from "actions/dynamicVChannel/dynamicVChannel.service"; import { DynamicChannelService } from "actions/dynamicChannel/dynamicChannel.service";
import { ReactRolesService } from "actions/reactRole/reactRoles.service"; import { ReactRolesService } from "actions/reactRole/reactRoles.service";
import config from "config"; import config from "config";
export default class DiscordController extends EventEmitter { export default class DiscordController extends EventEmitter {
private discordService: DiscordService; private discordService!: DiscordService;
private waterMeService: WaterMeService; waterMeService: WaterMeService;
private greetingService: GreetingService; greetingService: GreetingService;
private medicationService: MedicationService; medicationService: MedicationService;
private helpService: HelpService; helpService: HelpService;
private supportService: SupportService; supportService: SupportService;
private activityService: ActivityService; activityService: ActivityService;
private dmService: DmService; dmService: DmService;
private customMessageService: CustomMessageService; customMessageService: CustomMessageService;
private channelListeners = new Map(); channelListeners = new Map();
private dynamicVChannelService: DynamicVChannelService; dynamicChannelService: DynamicChannelService;
private reactRolesService: ReactRolesService; reactRolesService: ReactRolesService;
version = 250222.2319;
constructor() { constructor() {
super(); super();
@ -53,7 +53,7 @@ export default class DiscordController extends EventEmitter {
this.activityService = new ActivityService(); this.activityService = new ActivityService();
this.dmService = new DmService(); this.dmService = new DmService();
this.customMessageService = new CustomMessageService(); this.customMessageService = new CustomMessageService();
this.dynamicVChannelService = new DynamicVChannelService(); this.dynamicChannelService = new DynamicChannelService();
this.reactRolesService = new ReactRolesService(); this.reactRolesService = new ReactRolesService();
client.on("messageReactionAdd", async (reaction, user) => { client.on("messageReactionAdd", async (reaction, user) => {
@ -106,19 +106,38 @@ export default class DiscordController extends EventEmitter {
return; return;
} }
try { try {
const newChannel = await this.dynamicVChannelService.createVChannel( // create new channel with same settings
/*const newChannel = await channel.clone({
name: channel.name + "; " + newState.member?.displayName,
position: channel.position
});*/
const newChannel = await this.dynamicChannelService.createChannel(
oldState,
newState, newState,
channel, channel,
); );
// move user in new channel // move user in new channel
await newState.setChannel(newChannel); await newState.setChannel(newChannel);
// create specific listener for channel // create specific listener for channel
const channelListener = async ( const channelListener = async (
oldState: VoiceState, oldState: VoiceState,
newState: VoiceState, newState: VoiceState,
) => { ) => {
/*if (oldState.channelId === newChannel.id || newState.channelId === newChannel.id) {
// check if channel empty
if (newChannel.members.size === 0) {
newChannel.delete()
.catch(console.error);
// delete listener for this channel
client.removeListener(Events.VoiceStateUpdate, channelListener);
channelListeners.delete(newChannel.id);
}
}*/
channelListeners = channelListeners =
await this.dynamicVChannelService.deleteVChannel( await this.dynamicChannelService.deleteChannel(
oldState, oldState,
newState, newState,
newChannel, newChannel,
@ -128,6 +147,7 @@ export default class DiscordController extends EventEmitter {
}; };
// save listener in map // save listener in map
channelListeners.set(newChannel.id, channelListener); channelListeners.set(newChannel.id, channelListener);
// add listener // add listener
client.on(Events.VoiceStateUpdate, channelListener); client.on(Events.VoiceStateUpdate, channelListener);
} catch (error) { } catch (error) {
@ -137,7 +157,7 @@ export default class DiscordController extends EventEmitter {
} }
}, },
); );
console.log(`----------------\nversion ${config.discord.version}\n----------------`); console.log(`----------------\nversion ${this.version}\n----------------`);
} }
async setActivity() { async setActivity() {

View File

@ -2,13 +2,10 @@ import "actions/waterMe/waterMe.task";
import "actions/greeting/greeting.task"; import "actions/greeting/greeting.task";
import "actions/medication/medication.task"; import "actions/medication/medication.task";
import "actions/drink/drink.task"; import "actions/drink/drink.task";
import DiscordController from "discord.controller"; import DiscordController from "controllers/discord.controller";
import PomodoroController from "actions/pomodoro/pomodoro.controller";
import "dotenv/config"; import "dotenv/config";
// bootstrap application // bootstrap application
const discordController = new DiscordController(); const discordController = new DiscordController();
const pomodoroController = new PomodoroController(); discordController.init();
discordController.init();