This commit is contained in:
2024-12-27 01:23:35 +01:00
parent 7d349a37d4
commit c06ac10d09
18 changed files with 543 additions and 0 deletions

View File

@@ -0,0 +1,15 @@
import { SlashCommandBuilder } from "discord.js";
import { z } from "zod";
export const Commands = z.enum(["water-me"]);
export type CommandsType = z.output<typeof Commands>;
export default function getCommands() {
const commands = [
new SlashCommandBuilder()
.setName(Commands.Enum["water-me"])
.setDescription("giess mich mit etwas wasser :3"),
].map((command) => command.toJSON());
return commands;
}

12
src/config.ts Normal file
View File

@@ -0,0 +1,12 @@
const test = true;
export default {
discord: {
channelId:
(test
? process.env.DISCORD_TEST_CHANNEL_ID
: process.env.DISCORD_CHANNEL_ID) || "",
applicationId: process.env.DISCORD_APPLICATION_ID || "",
token: process.env.DISCORD_TOKEN || "",
},
};

View File

@@ -0,0 +1,83 @@
import { Commands, type CommandsType } from "components/commands.component";
import type {
ButtonInteraction,
CacheType,
ChatInputCommandInteraction,
Interaction,
ModalSubmitInteraction,
} from "discord.js";
import client from "lib/client";
import EventEmitter from "node:events";
import DiscordService from "services/discord.service";
import { WaterMeService } from "services/water-me.service";
export default class DiscordController extends EventEmitter {
private discordService!: DiscordService;
waterMeService: WaterMeService;
constructor() {
super();
this.discordService = new DiscordService();
this.waterMeService = new WaterMeService();
// log when running
client.once("ready", () => {
console.log("Listening...");
});
// listen for interactions
client.on("interactionCreate", this.handleInteraction.bind(this));
}
async init() {
await this.discordService.init();
}
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 handleButton(interaction: ButtonInteraction<CacheType>) {
const { customId } = interaction;
console.log(interaction.customId);
if (customId === "moreWater") {
await this.waterMeService.handleInteraction(interaction);
}
}
async handleChatInputCommand(
interaction: ChatInputCommandInteraction<CacheType>,
) {
const commandName = interaction.commandName as CommandsType;
// add commands
switch (commandName) {
case Commands.Enum["water-me"]:
await this.waterMeService.handleInteraction(interaction);
return;
default:
break;
}
}
// wenn neues fenster durch buttonclick or so
async handleModalSubmit(interaction: ModalSubmitInteraction<CacheType>) {
const { customId } = interaction;
switch (customId) {
default:
break;
}
}
}

11
src/index.ts Normal file
View File

@@ -0,0 +1,11 @@
import "tasks/water-me.task";
import "tasks/greeting.task";
import "tasks/drink.task";
import DiscordController from "controllers/discord.controller";
// = main file
// bootstrap application
const discordController = new DiscordController();
discordController.init();

8
src/lib/client.ts Normal file
View File

@@ -0,0 +1,8 @@
import config from "config";
import { Client, IntentsBitField } from "discord.js";
const client = new Client({ intents: [IntentsBitField.Flags.Guilds] });
await client.login(config.discord.token);
export default client;

5
src/lib/utils.ts Normal file
View File

@@ -0,0 +1,5 @@
export function getRandomInt(min: number, max: number) {
const nextMin = Math.ceil(min);
const nextMax = Math.floor(max);
return Math.floor(Math.random() * (nextMax - nextMin + 1)) + nextMin;
}

View File

@@ -0,0 +1,26 @@
import { Routes } from "discord.js";
import { REST } from "@discordjs/rest";
import config from "config";
import getCommands from "components/commands.component";
export default class DiscordService {
rest: REST;
constructor() {
this.rest = new REST({ version: "10" }).setToken(config.discord.token);
}
async init() {
try {
await this.rest.put(
Routes.applicationCommands(config.discord.applicationId),
{
body: getCommands(),
},
);
console.log("Successfully added commands");
} catch (e) {
console.error(e);
}
}
}

View File

@@ -0,0 +1,14 @@
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.channelId);
if (channel?.isTextBased && channel?.isSendable()) {
await channel.send({ content: "HALLOOOO" });
}
}
}

View File

@@ -0,0 +1,14 @@
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.channelId);
if (channel?.isTextBased && channel?.isSendable()) {
await channel.send({ content: "HALLOOOO" });
}
}
}

View File

@@ -0,0 +1,98 @@
import { CronJob } from "cron";
import { getRandomInt } from "lib/utils";
import config from "config";
import client from "lib/client";
import {
ActionRowBuilder,
ButtonBuilder,
ButtonStyle,
type CacheType,
type Interaction,
} from "discord.js";
export class WaterMeService {
waterLevel: number;
private thirsty = 3 as const;
private enough = 10 as const;
constructor() {
this.waterLevel = 0;
}
getReply() {
const thirstyReplies = [
"... wow das wars schon???",
"dankeeeee!!!! ich waer fast verdurstet :(((",
"*roelpssssss*",
];
const fullReplies = [
"langsam reicht es :o",
"poah, das hat gut getan",
"das ist krass :3",
];
const tooMuchReplies = [
"ES REICHT!!!!",
"bitte hoer auf, ich platze gleich :(",
];
if (this.waterLevel <= this.thirsty) {
return thirstyReplies[getRandomInt(0, thirstyReplies.length - 1)];
}
if (this.waterLevel > this.thirsty && this.waterLevel <= this.enough) {
return fullReplies[getRandomInt(0, fullReplies.length - 1)];
}
if (this.waterLevel > this.enough) {
return tooMuchReplies[getRandomInt(0, tooMuchReplies.length - 1)];
}
}
async isThirsty() {
const channels = client.channels;
const channel = channels.cache.get(config.discord.channelId);
if (
channel?.isTextBased &&
channel?.isSendable() &&
this.waterLevel <= this.thirsty
) {
await channel.send({ content: "ich brauche wasser :(" });
}
}
waterMe() {
const reply = this.getReply();
this.waterLevel++;
return {
reply,
};
}
async handleInteraction(interaction: Interaction<CacheType>) {
const result = this.waterMe();
const moreButton = new ButtonBuilder()
.setCustomId("more")
.setLabel("mehr")
.setStyle(ButtonStyle.Secondary);
const row = new ActionRowBuilder().addComponents(moreButton);
if (interaction.isChatInputCommand()) {
await interaction.reply({
content: result.reply,
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
components: [row as any],
});
} else if (interaction.isButton()) {
await interaction.reply({
content: result.reply,
});
}
}
}

0
src/tasks/drink.task.ts Normal file
View File

View File

@@ -0,0 +1,16 @@
import { CronJob } from "cron";
import { GreetingService } from "services/greeting.service";
const greetingService = new GreetingService();
new CronJob(
"0 */1 * * * *", // cronTime
async () => {
console.log("called greeting");
await greetingService.greet();
}, // onTick
null, // onComplete
true, // start
"Europe/Berlin", // timeZone
);
// job.start() is optional here because of the fourth parameter set to true.

View File

@@ -0,0 +1,16 @@
import { CronJob } from "cron";
import { WaterMeService } from "services/water-me.service";
const waterMeService = new WaterMeService();
new CronJob(
"0 0 */2 * * *", // cronTime
async () => {
console.log("isThirsty()");
await waterMeService.isThirsty();
}, // onTick
null, // onComplete
true, // start
"Europe/Berlin", // timeZone
);
// job.start() is optional here because of the fourth parameter set to true.