implement first interaction handling
This commit is contained in:
@@ -33,12 +33,15 @@
|
||||
"./db": "./dist/db/index.js",
|
||||
"./db/schema": "./dist/db/schema.js",
|
||||
"./entities/channels/channels.schema": "./dist/entities/channels/channels.schema.js",
|
||||
"./entities/channels/voice/voice-channels.service": "./dist/entities/channels/voice/voice-channels.service.js",
|
||||
"./entities/commands/commands.entity": "./dist/entities/commands/commands.entity.js",
|
||||
"./entities/commands/commands.schema": "./dist/entities/commands/commands.schema.js",
|
||||
"./entities/interactions/interactions.schema": "./dist/entities/interactions/interactions.schema.js",
|
||||
"./entities/messages/messages.service": "./dist/entities/messages/messages.service.js",
|
||||
"./entities/roles/roles.schema": "./dist/entities/roles/roles.schema.js",
|
||||
"./entities/roles/roles.service": "./dist/entities/roles/roles.service.js",
|
||||
"./features/dynamic-voice-channels/dynamic-voice-channels.schema": "./dist/features/dynamic-voice-channels/dynamic-voice-channels.schema.js",
|
||||
"./features/dynamic-voice-channels/dynamic-voice-channels.service": "./dist/features/dynamic-voice-channels/dynamic-voice-channels.service.js",
|
||||
"./features/greeting/greeting.service": "./dist/features/greeting/greeting.service.js",
|
||||
"./features/text-based-feature/text-based-feature": "./dist/features/text-based-feature/text-based-feature.js",
|
||||
"./features/text-based-feature/text-based-feature.schema": "./dist/features/text-based-feature/text-based-feature.schema.js",
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
import config from "config";
|
||||
import client from "lib/client";
|
||||
|
||||
export class GreetingService {
|
||||
async greet() {
|
||||
const channels = client.channels;
|
||||
|
||||
const channel = channels.cache.get(config.discord.channelIdBot);
|
||||
|
||||
if (channel?.isTextBased && channel?.isSendable()) {
|
||||
await channel.send({ content: "HALLOOOO" });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
import config from "config";
|
||||
import client from "lib/client";
|
||||
import { getRandomInt } from "lib/utils";
|
||||
import { } from "./dynamicVChannel.components.ts";
|
||||
import {
|
||||
Client,
|
||||
EmbedBuilder,
|
||||
type Message,
|
||||
type CacheType,
|
||||
type GuildMember,
|
||||
type Interaction,
|
||||
type OmitPartialGroupDMChannel,
|
||||
type VoiceState,
|
||||
type VoiceChannel,
|
||||
type StageChannel,
|
||||
Events,
|
||||
type VoiceBasedChannel,
|
||||
} from "discord.js";
|
||||
|
||||
export class DynamicVChannelService {
|
||||
async handleInteraction(interaction: Interaction<CacheType>) {
|
||||
// todo
|
||||
}
|
||||
|
||||
async createVChannel(
|
||||
newState: VoiceState,
|
||||
channel: VoiceBasedChannel
|
||||
): Promise<StageChannel | VoiceChannel> {
|
||||
//console.log("createChannel()");
|
||||
const newVChannel = await channel.clone({
|
||||
name: `${channel.name.substring(2)}; ${newState.member?.displayName}`,
|
||||
position: 100,
|
||||
});
|
||||
|
||||
return newVChannel;
|
||||
}
|
||||
|
||||
async deleteVChannel(
|
||||
oldState: VoiceState,
|
||||
newState: VoiceState,
|
||||
newChannel: StageChannel | VoiceChannel,
|
||||
// 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) {
|
||||
newChannel.delete().catch(console.error);
|
||||
|
||||
client.removeListener(Events.VoiceStateUpdate, channelListener);
|
||||
channelListeners.delete(newChannel.id);
|
||||
}
|
||||
}
|
||||
return channelListeners;
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
import { ButtonBuilder, ButtonStyle } from "discord.js";
|
||||
|
||||
export const yesButton = new ButtonBuilder()
|
||||
.setCustomId("yesMedication")
|
||||
.setLabel("ja")
|
||||
.setStyle(ButtonStyle.Primary);
|
||||
export const noButton = new ButtonBuilder()
|
||||
.setCustomId("noMedication")
|
||||
.setLabel("noch nicht")
|
||||
.setStyle(ButtonStyle.Secondary);
|
||||
@@ -1,250 +0,0 @@
|
||||
import { CronJob } from "cron";
|
||||
import { getRandomInt } from "lib/utils";
|
||||
import config from "config";
|
||||
import client from "lib/client";
|
||||
import {
|
||||
ActionRowBuilder,
|
||||
ButtonBuilder,
|
||||
type ButtonInteraction,
|
||||
ButtonStyle,
|
||||
type ChatInputCommandInteraction,
|
||||
type ModalSubmitInteraction,
|
||||
type CacheType,
|
||||
type Interaction,
|
||||
} from "discord.js";
|
||||
import { yesButton, noButton } from "./medication.components";
|
||||
import { db } from "db";
|
||||
import { usersTable } from "db/schema";
|
||||
import { eq } from "drizzle-orm";
|
||||
|
||||
export class MedicationService {
|
||||
medication: string;
|
||||
tookMedication: boolean;
|
||||
|
||||
constructor() {
|
||||
this.medication = "";
|
||||
this.tookMedication = false;
|
||||
}
|
||||
|
||||
async askMedication() {
|
||||
const channels = client.channels;
|
||||
|
||||
const channel = channels.cache.get(config.discord.channelIdBot);
|
||||
|
||||
// funkt noch nicht, beides
|
||||
|
||||
const row = new ActionRowBuilder().addComponents(yesButton);
|
||||
row.addComponents(noButton);
|
||||
|
||||
if (
|
||||
channel?.isTextBased &&
|
||||
channel?.isSendable() &&
|
||||
this.tookMedication === false
|
||||
) {
|
||||
await channel.send({
|
||||
content: "hast du schon deine medis genommen? :)",
|
||||
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
||||
components: [row as any],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
getMedication() {
|
||||
// todo
|
||||
}
|
||||
|
||||
setMedication() {
|
||||
const reply = this.getReply();
|
||||
console.log("medication");
|
||||
|
||||
// this.medication = input von user:in hier rein;
|
||||
return {
|
||||
reply,
|
||||
};
|
||||
}
|
||||
|
||||
getReply() {
|
||||
return "medication reminder";
|
||||
}
|
||||
|
||||
async handleInteraction(interaction: Interaction<CacheType>) {
|
||||
if (interaction.isModalSubmit()) {
|
||||
await this.handleModalSubmit(interaction);
|
||||
return;
|
||||
}
|
||||
|
||||
if (interaction.isChatInputCommand()) {
|
||||
await this.handleChatInputCommand(interaction);
|
||||
return;
|
||||
}
|
||||
if (interaction.isButton()) {
|
||||
await this.handleButton(interaction);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
async handleModalSubmit(interaction: ModalSubmitInteraction<CacheType>) {
|
||||
switch (interaction.customId) {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
async handleButton(interaction: ButtonInteraction<CacheType>) {
|
||||
console.log("button interaction");
|
||||
|
||||
const discordId = Number.parseInt(interaction.user.id);
|
||||
const name = interaction.user.displayName;
|
||||
|
||||
console.log(`userid: ${discordId}`);
|
||||
|
||||
try {
|
||||
const userId = await this.ensureUserExists(discordId, name);
|
||||
console.log(`userid: ${userId}`);
|
||||
|
||||
const tookMedication = interaction.customId === "yesMedication";
|
||||
|
||||
await interaction.reply({
|
||||
content: tookMedication
|
||||
? "das hast du toll gemacht <3 mach weiter so :3"
|
||||
: "das passiert mal... aber versuch sie heute noch zu nehmen, oki? :)",
|
||||
});
|
||||
await this.logMedication(userId, tookMedication);
|
||||
} catch (error) {
|
||||
console.error("error ensuring user exists:", error);
|
||||
await interaction.reply({
|
||||
content:
|
||||
"es gab einen fehler beim verarbeiten deiner anfrage :( versuch es bitte spaeter nochmal, oki? c:",
|
||||
ephemeral: true,
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
async handleChatInputCommand(
|
||||
interaction: ChatInputCommandInteraction<CacheType>,
|
||||
) {
|
||||
const result = this.setMedication();
|
||||
|
||||
const row = new ActionRowBuilder().addComponents(yesButton);
|
||||
row.addComponents(noButton);
|
||||
await interaction.reply({
|
||||
content: result.reply,
|
||||
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
||||
components: [row as any],
|
||||
});
|
||||
}
|
||||
|
||||
/**, um die Benutzerdaten in die Datenbank zu schreiben.
|
||||
* @param discordId unique user id
|
||||
* @param name name how the user wants to get called by avocadi
|
||||
* @param tookMedication if user took medication
|
||||
*/
|
||||
async logMedication(id: number, tookMedication: boolean) {
|
||||
try {
|
||||
await db
|
||||
.update(usersTable)
|
||||
.set({
|
||||
took_medication_today: Number(tookMedication),
|
||||
})
|
||||
.where(eq(usersTable.id, id));
|
||||
|
||||
/* await db.insert(usersTable).values({
|
||||
name: name,
|
||||
discord_id: discordId,
|
||||
took_medication_today: Number(tookMedication),
|
||||
}); */
|
||||
|
||||
console.log(`user with id ${id} saved`);
|
||||
} catch (error) {
|
||||
console.error("error while saving in db:", error);
|
||||
}
|
||||
}
|
||||
|
||||
async getNameByDiscordId(discordId: number): Promise<string | null> {
|
||||
const result = await db
|
||||
.select({
|
||||
name: usersTable.name,
|
||||
})
|
||||
.from(usersTable)
|
||||
.where(eq(usersTable.discord_id, discordId))
|
||||
.limit(1);
|
||||
|
||||
if (result.length > 0) {
|
||||
console.log("user found");
|
||||
return result[0].name;
|
||||
}
|
||||
console.log("name not found");
|
||||
return "";
|
||||
}
|
||||
|
||||
async findUserIdByDiscordId(discordId: number): Promise<number | null> {
|
||||
try {
|
||||
const result = await db
|
||||
.select({
|
||||
id: usersTable.id,
|
||||
})
|
||||
.from(usersTable)
|
||||
.where(eq(usersTable.discord_id, discordId))
|
||||
.limit(1);
|
||||
|
||||
if (result.length > 0) {
|
||||
console.log(`ID für Discord-ID ${discordId} gefunden: ${result[0].id}`);
|
||||
return result[0].id;
|
||||
}
|
||||
console.log(`no id for discordId ${discordId} found`);
|
||||
return null;
|
||||
} catch (error) {
|
||||
console.error(
|
||||
`error while calling id for discordId ${discordId}:`,
|
||||
error,
|
||||
);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async ensureUserExists(discordId: number, name: string): Promise<number> {
|
||||
try {
|
||||
const userId = await this.findUserIdByDiscordId(discordId);
|
||||
|
||||
if (userId !== null) {
|
||||
console.log(`entry for discordID ${discordId} already exists`);
|
||||
return userId;
|
||||
}
|
||||
|
||||
console.log(
|
||||
`found no entry for discordID ${discordId}. creating new entry`,
|
||||
);
|
||||
|
||||
const result = await db
|
||||
.insert(usersTable)
|
||||
.values({
|
||||
name: name,
|
||||
discord_id: discordId,
|
||||
})
|
||||
.onConflictDoNothing()
|
||||
.returning({
|
||||
id: usersTable.id,
|
||||
});
|
||||
|
||||
if (result.length > 0) {
|
||||
const newUserId = result[0].id;
|
||||
console.log(
|
||||
`new user with discordId ${discordId} and name ${name} created. id: ${newUserId}`,
|
||||
);
|
||||
return newUserId;
|
||||
}
|
||||
|
||||
// check again if user is now existing
|
||||
const newUserId = await this.findUserIdByDiscordId(discordId);
|
||||
if (newUserId !== null) {
|
||||
console.log(`user created in parallel. fetched id: ${newUserId}`);
|
||||
return newUserId;
|
||||
}
|
||||
throw new Error(`creating a new user for discordId ${discordId} failed`);
|
||||
} catch (error) {
|
||||
console.error("error while creating or calling the user:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
import { CronJob } from "cron";
|
||||
import { MedicationService } from "actions/medication/medication.service";
|
||||
|
||||
const medicationService = new MedicationService();
|
||||
|
||||
/*new CronJob(
|
||||
"0 0 19 * * *", // cronTime
|
||||
async () => {
|
||||
console.log("askMedication()");
|
||||
await medicationService.askMedication();
|
||||
}, // onTick
|
||||
null, // onComplete
|
||||
true, // start
|
||||
"Europe/Berlin", // timeZone
|
||||
);*/
|
||||
@@ -3,7 +3,7 @@ import z from "zod";
|
||||
export const TextChannelOptions = [
|
||||
"bump",
|
||||
"bot",
|
||||
"notification",
|
||||
"log",
|
||||
"testing",
|
||||
"news",
|
||||
"rules",
|
||||
|
||||
14
core/src/entities/channels/voice/voice-channels.service.ts
Normal file
14
core/src/entities/channels/voice/voice-channels.service.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
export interface VoiceChannelsServiceInterface<
|
||||
C extends { name: string; id: string },
|
||||
> {
|
||||
getVoiceChannelById(channelId: string): Promise<C | null>;
|
||||
|
||||
createVoiceChannel(name: string): Promise<C>;
|
||||
|
||||
cloneVoiceChannel(
|
||||
channel: C,
|
||||
options?: { name?: string; position?: number },
|
||||
): Promise<C>;
|
||||
|
||||
deleteVoiceChannel(channelId: string): Promise<void>;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import { VoiceChannels } from "entities/channels/channels.schema";
|
||||
import z from "zod";
|
||||
|
||||
export const DynamicVoiceChannelKeyOptions = [
|
||||
VoiceChannels.enum["for-two"],
|
||||
VoiceChannels.enum["for-three"],
|
||||
VoiceChannels.enum["for-four"],
|
||||
VoiceChannels.enum["for-group"],
|
||||
] as const;
|
||||
|
||||
export const DynamicVoiceChannelKeys = z.enum(DynamicVoiceChannelKeyOptions);
|
||||
|
||||
export const DynamicVoiceChannel = z.object({
|
||||
channelId: z.string(),
|
||||
size: z.number(),
|
||||
key: DynamicVoiceChannelKeys,
|
||||
});
|
||||
|
||||
export const DynamicVoiceChannels = z.array(DynamicVoiceChannel);
|
||||
export const DynamicVoiceChannelsMap = z.map(z.string(), DynamicVoiceChannel);
|
||||
@@ -0,0 +1,62 @@
|
||||
import type { VoiceChannelsServiceInterface } from "entities/channels/voice/voice-channels.service";
|
||||
import type z from "zod";
|
||||
import type {
|
||||
DynamicVoiceChannels,
|
||||
DynamicVoiceChannelsMap,
|
||||
} from "./dynamic-voice-channels.schema";
|
||||
|
||||
export class DynamicVoiceChannelsService<
|
||||
C extends { name: string; id: string },
|
||||
> {
|
||||
private voiceChannelsService: VoiceChannelsServiceInterface<C>;
|
||||
dynamicVoiceChannels: z.output<typeof DynamicVoiceChannels>;
|
||||
validChannelIds = new Set<string>();
|
||||
dynamicVoiceChannelsMap: z.output<typeof DynamicVoiceChannelsMap>;
|
||||
createdChannelIdsSet = new Set<string>();
|
||||
|
||||
constructor(
|
||||
voiceChannelsService: VoiceChannelsServiceInterface<C>,
|
||||
dynamicVoiceChannels: z.output<typeof DynamicVoiceChannels>,
|
||||
) {
|
||||
this.voiceChannelsService = voiceChannelsService;
|
||||
this.dynamicVoiceChannels = dynamicVoiceChannels;
|
||||
|
||||
this.dynamicVoiceChannelsMap = new Map(
|
||||
dynamicVoiceChannels.map((channel) => [channel.channelId, channel]),
|
||||
);
|
||||
|
||||
this.validChannelIds = new Set(
|
||||
dynamicVoiceChannels.map((channel) => channel.channelId),
|
||||
);
|
||||
}
|
||||
|
||||
async createDynamicVoiceChannel(id: string): Promise<C | void> {
|
||||
if (this.validChannelIds.has(id)) {
|
||||
// Channel is one of the dynamic voice channels, create a new one based on it
|
||||
|
||||
const channel = await this.voiceChannelsService.getVoiceChannelById(id);
|
||||
|
||||
if (!channel) {
|
||||
throw new Error(`Channel with id ${id} not found`);
|
||||
}
|
||||
|
||||
const newChannel = await this.voiceChannelsService.cloneVoiceChannel(
|
||||
channel,
|
||||
{
|
||||
name: channel.name,
|
||||
},
|
||||
);
|
||||
|
||||
this.createdChannelIdsSet.add(newChannel.id);
|
||||
|
||||
return newChannel;
|
||||
} else {
|
||||
// Channel is not a dynamic voice channel, do nothing
|
||||
}
|
||||
}
|
||||
|
||||
deleteDynamicVoiceChannel(channelId: string): Promise<void> {
|
||||
this.createdChannelIdsSet.delete(channelId);
|
||||
return this.voiceChannelsService.deleteVoiceChannel(channelId);
|
||||
}
|
||||
}
|
||||
@@ -5,5 +5,5 @@ export type TextBasedFeatureHandleMessageSend = (
|
||||
|
||||
export type TextBasedFeatureInput = {
|
||||
channelId: string;
|
||||
handleMessageSend: TextBasedFeatureHandleMessageSend;
|
||||
messagesService: TextBasedFeatureHandleMessageSend;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user