begin rewriting first services to be domain agnostic
more rewrites
This commit is contained in:
2
adapters/discord/.gitignore
vendored
2
adapters/discord/.gitignore
vendored
@@ -21,6 +21,8 @@ report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
.env.local
|
||||
.env.dev
|
||||
.env.prod
|
||||
|
||||
# caches
|
||||
.eslintcache
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
console.log("Hello via Bun!");
|
||||
@@ -1,16 +1,30 @@
|
||||
{
|
||||
"name": "@avocadi/bot-adapter-discord",
|
||||
"module": "index.ts",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "tsdown",
|
||||
"dev:prod": "NODE_ENV=production tsdown --watch & node --watch ./dist/index.js",
|
||||
"dev": "NODE_ENV=development tsdown --watch & node --watch ./dist/index.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bun": "latest"
|
||||
"@types/bun": "latest",
|
||||
"tsdown": "catalog:"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": "^5"
|
||||
},
|
||||
"dependencies": {
|
||||
"zod": "catalog:",
|
||||
"@avocadi/bot-core": "workspace:*"
|
||||
"@avocadi/bot-core": "workspace:*",
|
||||
"@discordjs/rest": "^2.6.0",
|
||||
"cron": "^4.4.0",
|
||||
"discord.js": "^14.25.1",
|
||||
"dotenv": "^17.3.1",
|
||||
"dotenv-expand": "^12.0.3",
|
||||
"zod": "catalog:"
|
||||
},
|
||||
"exports": {
|
||||
".": "./dist/index.js",
|
||||
"./package.json": "./package.json"
|
||||
}
|
||||
}
|
||||
|
||||
18
adapters/discord/src/actions/publish-commands.ts
Normal file
18
adapters/discord/src/actions/publish-commands.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { config } from "config";
|
||||
import { Routes } from "discord.js";
|
||||
import getCommands from "entitites/commands";
|
||||
import { discordRestClient } from "lib/rest-client";
|
||||
|
||||
export const publishCommands = async () => {
|
||||
try {
|
||||
await discordRestClient.put(
|
||||
Routes.applicationCommands(config.discord.applicationId),
|
||||
{
|
||||
body: getCommands(),
|
||||
},
|
||||
);
|
||||
console.log("Successfully added commands");
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
};
|
||||
@@ -11,6 +11,9 @@ export const ConfigSchema = z.object({
|
||||
voice: z.record(VoiceChannels, z.string()),
|
||||
}),
|
||||
roleMapping: z.record(Roles, z.string()),
|
||||
reactionRoles: z.object({
|
||||
allowedMessageIds: z.array(z.string()),
|
||||
}),
|
||||
serverId: z.string(),
|
||||
version: z.number(),
|
||||
discord: z.object({
|
||||
|
||||
6
adapters/discord/src/config/env/env.schema.ts
vendored
Normal file
6
adapters/discord/src/config/env/env.schema.ts
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
import z from "zod";
|
||||
|
||||
export const EnvSchema = z.object({
|
||||
DISCORD_APPLICATION_ID: z.string(),
|
||||
DISCORD_TOKEN: z.string(),
|
||||
});
|
||||
29
adapters/discord/src/config/env/index.ts
vendored
Normal file
29
adapters/discord/src/config/env/index.ts
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
import { readFileSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
import dotenv from "dotenv";
|
||||
import dotenvExpand from "dotenv-expand";
|
||||
import { EnvSchema } from "./env.schema";
|
||||
|
||||
const envFile =
|
||||
process.env.NODE_ENV === "production" ? ".env.prod" : ".env.dev";
|
||||
|
||||
const envPath = join(process.cwd(), envFile);
|
||||
|
||||
const rawEnv = Buffer.from(
|
||||
readFileSync(envPath, {
|
||||
encoding: "utf8",
|
||||
}),
|
||||
);
|
||||
|
||||
const envJson = { processEnv: dotenv.parse(rawEnv) };
|
||||
|
||||
const targetObj = {};
|
||||
|
||||
const expandedEnv = dotenvExpand.expand({
|
||||
processEnv: targetObj,
|
||||
parsed: envJson.processEnv,
|
||||
}) as { processEnv: Record<string, string> };
|
||||
|
||||
const env = EnvSchema.parse(expandedEnv.processEnv);
|
||||
|
||||
export default env;
|
||||
@@ -1,5 +1,6 @@
|
||||
import type z from "zod";
|
||||
import type { ConfigSchema } from "./config.schema";
|
||||
import env from "./env";
|
||||
|
||||
export const config: z.output<typeof ConfigSchema> = {
|
||||
channelMapping: {
|
||||
@@ -34,10 +35,15 @@ export const config: z.output<typeof ConfigSchema> = {
|
||||
people: "1321470720424939662",
|
||||
bot: "1321491461111283722",
|
||||
},
|
||||
reactionRoles: {
|
||||
allowedMessageIds: [
|
||||
"1321491461111283722", // Example message ID for reaction roles
|
||||
],
|
||||
},
|
||||
serverId: "1316153371899592774",
|
||||
version: 1,
|
||||
discord: {
|
||||
token: process.env.DISCORD_TOKEN || "",
|
||||
applicationId: process.env.DISCORD_APPLICATION_ID || "",
|
||||
token: env.DISCORD_TOKEN,
|
||||
applicationId: env.DISCORD_APPLICATION_ID,
|
||||
},
|
||||
};
|
||||
@@ -1,74 +1,16 @@
|
||||
import { Commands, type CommandsType } from "commands";
|
||||
import {
|
||||
ChannelType,
|
||||
Client,
|
||||
Events,
|
||||
IntentsBitField,
|
||||
type VoiceState,
|
||||
type ButtonInteraction,
|
||||
type CacheType,
|
||||
type ChatInputCommandInteraction,
|
||||
type Interaction,
|
||||
type ModalSubmitInteraction,
|
||||
ActivityType,
|
||||
type Channel,
|
||||
} from "discord.js";
|
||||
import client from "lib/client";
|
||||
import EventEmitter from "node:events";
|
||||
import DiscordService from "discord.service";
|
||||
import { WaterMeService } from "actions/waterMe/waterMe.service";
|
||||
import { MedicationService } from "actions/medication/medication.service";
|
||||
import { HelpService } from "actions/help/help.service";
|
||||
import { SupportService } from "actions/support/support.service";
|
||||
import { GreetingService } from "actions/greeting/greeting.service";
|
||||
import { ActivityService } from "actions/activity/activity.service";
|
||||
import { DmService } from "actions/dm/dm.service";
|
||||
import { CustomMessageService } from "actions/customMessage/customMessage.service";
|
||||
import { DynamicVChannelService } from "actions/dynamicVChannel/dynamicVChannel.service";
|
||||
import { DebugService } from "actions/debug/debug.service";
|
||||
import { ReactRolesService } from "actions/reactRole/reactRoles.service";
|
||||
import config from "config";
|
||||
import {
|
||||
type WaterMeController,
|
||||
waterMeController,
|
||||
} from "features/water-me/water-me.controller";
|
||||
import client from "lib/client";
|
||||
|
||||
export default class DiscordController extends EventEmitter {
|
||||
private discordService: DiscordService;
|
||||
private waterMeService: WaterMeService;
|
||||
private greetingService: GreetingService;
|
||||
private medicationService: MedicationService;
|
||||
private helpService: HelpService;
|
||||
private supportService: SupportService;
|
||||
private activityService: ActivityService;
|
||||
private dmService: DmService;
|
||||
private customMessageService: CustomMessageService;
|
||||
private dynamicVChannelService: DynamicVChannelService;
|
||||
private debugService: DebugService;
|
||||
|
||||
private channelListeners = new Map();
|
||||
private reactRolesService: ReactRolesService;
|
||||
|
||||
private waterMeController: WaterMeController = waterMeController;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
let channelListeners = new Map();
|
||||
this.discordService = new DiscordService();
|
||||
this.waterMeService = new WaterMeService();
|
||||
this.greetingService = new GreetingService();
|
||||
this.medicationService = new MedicationService();
|
||||
this.helpService = new HelpService();
|
||||
this.supportService = new SupportService();
|
||||
this.activityService = new ActivityService();
|
||||
this.dmService = new DmService();
|
||||
this.customMessageService = new CustomMessageService();
|
||||
this.dynamicVChannelService = new DynamicVChannelService();
|
||||
this.debugService = new DebugService();
|
||||
this.reactRolesService = new ReactRolesService();
|
||||
|
||||
client.on("messageReactionAdd", async (reaction, user) => {
|
||||
await this.reactRolesService.roleMention(reaction, user, true);
|
||||
});
|
||||
|
||||
client.on("messageReactionRemove", async (reaction, user) => {
|
||||
await this.reactRolesService.roleMention(reaction, user, false);
|
||||
});
|
||||
|
||||
// log when running
|
||||
client.once("ready", async () => {
|
||||
@@ -146,7 +88,8 @@ export default class DiscordController extends EventEmitter {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const newChannel = await this.dynamicVChannelService.createVChannel(
|
||||
const newChannel =
|
||||
await this.dynamicVChannelService.createVChannel(
|
||||
newState,
|
||||
channel,
|
||||
);
|
||||
@@ -177,7 +120,9 @@ export default class DiscordController extends EventEmitter {
|
||||
}
|
||||
},
|
||||
);
|
||||
console.log(`----------------\nversion ${config.discord.version}\n----------------`);
|
||||
console.log(
|
||||
`----------------\nversion ${config.discord.version}\n----------------`,
|
||||
);
|
||||
}
|
||||
|
||||
async setActivity(state: number) {
|
||||
@@ -213,7 +158,6 @@ export default class DiscordController extends EventEmitter {
|
||||
console.log("bot is offline.");
|
||||
}
|
||||
|
||||
|
||||
async init() {
|
||||
await this.discordService.init();
|
||||
}
|
||||
32
adapters/discord/src/entitites/commands/index.ts
Normal file
32
adapters/discord/src/entitites/commands/index.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import { CommandsCollection } from "@avocadi/bot-core/entities/commands/commands.entity";
|
||||
import type { Command } from "@avocadi/bot-core/entities/commands/commands.schema";
|
||||
import { SlashCommandBuilder } from "discord.js";
|
||||
import type { z } from "zod";
|
||||
|
||||
const convertCommandToDiscordFormat = (
|
||||
command: z.output<typeof Command>,
|
||||
key: string,
|
||||
) => {
|
||||
const slashCommand = new SlashCommandBuilder()
|
||||
.setName(command.name || key)
|
||||
.setDescription(command.description);
|
||||
|
||||
if (command.options) {
|
||||
command.options.forEach((option) => {
|
||||
slashCommand.addStringOption((opt) =>
|
||||
opt
|
||||
.setName(option.name)
|
||||
.setDescription(option.description)
|
||||
.setRequired(option.required),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
return slashCommand;
|
||||
};
|
||||
|
||||
export default function getCommands() {
|
||||
return Object.entries(CommandsCollection).map(([key, command]) =>
|
||||
convertCommandToDiscordFormat(command, key),
|
||||
);
|
||||
}
|
||||
20
adapters/discord/src/entitites/messages/messages.service.ts
Normal file
20
adapters/discord/src/entitites/messages/messages.service.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import type { MessagesServiceInterface } from "@avocadi/bot-core/entities/messages/messages.service";
|
||||
import { createLogger } from "@avocadi/bot-core/lib/logger";
|
||||
import type { User } from "discord.js";
|
||||
import client from "lib/client";
|
||||
|
||||
export class MessagesService implements MessagesServiceInterface<User> {
|
||||
private logger = createLogger("MessagesService");
|
||||
|
||||
async sendToUser(userInput: User, message: string): Promise<void> {
|
||||
const user = await client.users.fetch(userInput.id);
|
||||
|
||||
if (user) {
|
||||
await user.send(message);
|
||||
} else {
|
||||
this.logger.error(`User with ID ${userInput.id} not found.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const messagesService = new MessagesService();
|
||||
33
adapters/discord/src/entitites/roles/roles.service.ts
Normal file
33
adapters/discord/src/entitites/roles/roles.service.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import type { RolesServiceInterface } from "@avocadi/bot-core/entities/roles/roles.service";
|
||||
import { createLogger } from "@avocadi/bot-core/lib/logger";
|
||||
import type { GuildMember } from "discord.js";
|
||||
|
||||
export class RolesService implements RolesServiceInterface<GuildMember> {
|
||||
private logger = createLogger("RolesService");
|
||||
|
||||
async assignRole(user: GuildMember, role: string) {
|
||||
const roleToAssign = user.guild.roles.cache.find((r) => r.name === role);
|
||||
if (!roleToAssign) {
|
||||
this.logger.error(`Role ${role} not found in guild ${user.guild.name}.`);
|
||||
return;
|
||||
}
|
||||
await user.roles.add(roleToAssign);
|
||||
}
|
||||
|
||||
async removeRole(user: GuildMember, role: string) {
|
||||
const roleToRemove = user.guild.roles.cache.find((r) => r.name === role);
|
||||
if (!roleToRemove) {
|
||||
this.logger.error(`Role ${role} not found in guild ${user.guild.name}.`);
|
||||
return;
|
||||
}
|
||||
await user.roles.remove(roleToRemove);
|
||||
}
|
||||
|
||||
async getRoles(user: GuildMember) {
|
||||
return user.roles.cache.map((role) => role.name);
|
||||
}
|
||||
|
||||
async hasRole(user: GuildMember, role: string) {
|
||||
return user.roles.cache.some((r) => r.name === role);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
import { GreetingService } from "@avocadi/bot-core/features/greeting/greeting.service";
|
||||
import { messagesService } from "entitites/messages/messages.service";
|
||||
|
||||
export const greetingsService = new GreetingService(messagesService);
|
||||
@@ -0,0 +1,128 @@
|
||||
import type { Roles } from "@avocadi/bot-core/entities/roles/roles.schema";
|
||||
import { createLogger } from "@avocadi/bot-core/lib/logger";
|
||||
import { config } from "config";
|
||||
import type {
|
||||
GuildMember,
|
||||
MessageReaction,
|
||||
PartialMessageReaction,
|
||||
PartialUser,
|
||||
User,
|
||||
} from "discord.js";
|
||||
import type z from "zod";
|
||||
|
||||
export class ReactionRolesService {
|
||||
private logger = createLogger("ReactionRolesService");
|
||||
|
||||
/**
|
||||
* This method validates if the reaction is on an allowed message for reaction roles.
|
||||
* @param reaction
|
||||
* @returns
|
||||
*/
|
||||
async validateReaction(reaction: MessageReaction | PartialMessageReaction) {
|
||||
this.logger.info(
|
||||
`Validating reaction ${reaction.emoji.name} on message ${reaction.message.id}`,
|
||||
);
|
||||
|
||||
const message = await reaction.message.fetch();
|
||||
|
||||
if (!message) {
|
||||
this.logger.error(`Message with ID ${reaction.message.id} not found.`);
|
||||
throw new Error("Message not found");
|
||||
}
|
||||
|
||||
if (!config.reactionRoles.allowedMessageIds.includes(message.id)) {
|
||||
this.logger.error(
|
||||
`Message with ID ${message.id} is not allowed for reaction roles.`,
|
||||
);
|
||||
throw new Error("Message not allowed for reaction roles");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a reaction, validates it, and assigns or removes the given role.
|
||||
* @param reaction
|
||||
* @param guild
|
||||
* @param user
|
||||
* @param role
|
||||
*/
|
||||
async handleReaction(
|
||||
reaction: MessageReaction | PartialMessageReaction,
|
||||
user: User | PartialUser,
|
||||
targetRole: z.output<typeof Roles>,
|
||||
action: "add" | "remove",
|
||||
) {
|
||||
const guild = reaction.message.guild;
|
||||
|
||||
if (!guild) {
|
||||
this.logger.error(`Guild not found for message ${reaction.message.id}.`);
|
||||
throw new Error("Guild not found");
|
||||
}
|
||||
|
||||
await this.validateReaction(reaction);
|
||||
|
||||
const role = guild.roles.cache.get(config.roleMapping[targetRole]);
|
||||
|
||||
if (!role) {
|
||||
this.logger.error(`Role ${targetRole} not found in guild ${guild.name}.`);
|
||||
throw new Error("Role not found");
|
||||
}
|
||||
|
||||
const member = await guild.members.fetch(user.id);
|
||||
|
||||
if (!member) {
|
||||
this.logger.error(
|
||||
`User with ID ${user.id} not found in guild ${guild.name}.`,
|
||||
);
|
||||
throw new Error("User not found");
|
||||
}
|
||||
|
||||
if (member.roles.cache.has(role.id)) {
|
||||
if (action === "remove") {
|
||||
this.removeRoleByReaction(reaction, member, targetRole);
|
||||
} else {
|
||||
this.logger.info(
|
||||
`User ${member.user.tag} already has role ${targetRole}. No action taken.`,
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (action === "remove") {
|
||||
this.logger.info(
|
||||
`User ${member.user.tag} does not have role ${targetRole}. No action taken.`,
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.assignRoleByReaction(reaction, member, targetRole);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
async assignRoleByReaction(
|
||||
reaction: MessageReaction | PartialMessageReaction,
|
||||
member: GuildMember,
|
||||
role: z.output<typeof Roles>,
|
||||
) {
|
||||
this.logger.info(
|
||||
`Assigning role ${role} based on reaction ${reaction.emoji.name} by user ${member.user.tag}`,
|
||||
);
|
||||
|
||||
await member.roles.add(config.roleMapping[role]);
|
||||
}
|
||||
|
||||
async removeRoleByReaction(
|
||||
reaction: MessageReaction | PartialMessageReaction,
|
||||
member: GuildMember,
|
||||
role: z.output<typeof Roles>,
|
||||
) {
|
||||
this.logger.info(
|
||||
`Removing role ${role} based on reaction ${reaction.emoji.name} by user ${member.user.tag}`,
|
||||
);
|
||||
|
||||
await member.roles.remove(config.roleMapping[role]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import type { WaterMeService } from "@avocadi/bot-core/features/water-me/water-me.service";
|
||||
import {
|
||||
ActionRowBuilder,
|
||||
ButtonBuilder,
|
||||
ButtonStyle,
|
||||
type CacheType,
|
||||
type Interaction,
|
||||
} from "discord.js";
|
||||
import { waterMeService } from "./water-me.service";
|
||||
|
||||
class WaterMeController {
|
||||
waterMeService: WaterMeService;
|
||||
|
||||
constructor() {
|
||||
this.waterMeService = waterMeService;
|
||||
}
|
||||
|
||||
async handleInteraction(interaction: Interaction<CacheType>) {
|
||||
const result = this.waterMeService.waterMe();
|
||||
|
||||
const moreButton = new ButtonBuilder()
|
||||
.setCustomId("moreWater")
|
||||
.setLabel("mehr")
|
||||
.setStyle(ButtonStyle.Secondary);
|
||||
|
||||
const row = new ActionRowBuilder<ButtonBuilder>().addComponents(moreButton);
|
||||
|
||||
if (interaction.isChatInputCommand()) {
|
||||
await interaction.reply({
|
||||
content: result.reply,
|
||||
components: [row],
|
||||
});
|
||||
} else if (interaction.isButton()) {
|
||||
await interaction.reply({
|
||||
content: result.reply,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const waterMeController = new WaterMeController();
|
||||
@@ -0,0 +1,8 @@
|
||||
import { WaterMeService } from "@avocadi/bot-core/features/water-me/water-me.service";
|
||||
|
||||
export const waterMeService = new WaterMeService({
|
||||
channelId: "123",
|
||||
handleMessageSend: async (message, channelId) => {
|
||||
console.log(`Sending message to channel ${channelId}: ${message}`);
|
||||
},
|
||||
});
|
||||
12
adapters/discord/src/features/water-me/water-me.tasks.ts
Normal file
12
adapters/discord/src/features/water-me/water-me.tasks.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { CronJob } from "cron";
|
||||
import { waterMeService } from "./water-me.service";
|
||||
|
||||
new CronJob(
|
||||
"0 0 20 * * *", // cronTime
|
||||
async () => {
|
||||
await waterMeService.notifyIfThirsty();
|
||||
}, // onTick
|
||||
null, // onComplete
|
||||
true, // start
|
||||
"Europe/Berlin", // timeZone
|
||||
);
|
||||
4
adapters/discord/src/index.ts
Normal file
4
adapters/discord/src/index.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
import { publishCommands } from "actions/publish-commands";
|
||||
|
||||
// Publish commands when the application starts
|
||||
await publishCommands();
|
||||
24
adapters/discord/src/lib/client.ts
Normal file
24
adapters/discord/src/lib/client.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { config } from "config";
|
||||
import { Client, IntentsBitField, Partials } from "discord.js";
|
||||
|
||||
const client = new Client({
|
||||
intents: [
|
||||
IntentsBitField.Flags.Guilds,
|
||||
IntentsBitField.Flags.GuildMembers,
|
||||
IntentsBitField.Flags.GuildModeration,
|
||||
IntentsBitField.Flags.GuildMessages,
|
||||
IntentsBitField.Flags.GuildMessageReactions,
|
||||
IntentsBitField.Flags.GuildMessagePolls,
|
||||
IntentsBitField.Flags.GuildVoiceStates,
|
||||
IntentsBitField.Flags.MessageContent,
|
||||
IntentsBitField.Flags.DirectMessages,
|
||||
IntentsBitField.Flags.DirectMessageReactions,
|
||||
IntentsBitField.Flags.DirectMessageTyping,
|
||||
IntentsBitField.Flags.DirectMessagePolls,
|
||||
],
|
||||
partials: [Partials.Channel, Partials.Message, Partials.Reaction],
|
||||
});
|
||||
|
||||
await client.login(config.discord.token);
|
||||
|
||||
export default client;
|
||||
6
adapters/discord/src/lib/rest-client.ts
Normal file
6
adapters/discord/src/lib/rest-client.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { REST } from "@discordjs/rest";
|
||||
import { config } from "config";
|
||||
|
||||
export const discordRestClient = new REST({ version: "10" }).setToken(
|
||||
config.discord.token,
|
||||
);
|
||||
@@ -0,0 +1,12 @@
|
||||
import { greetingsService } from "features/greeting/greetings.service";
|
||||
import client from "lib/client";
|
||||
|
||||
client.on("guildMemberAdd", async (member) => {
|
||||
if (member.user.bot) {
|
||||
// Don't send a welcome message for bots (sorry tom)
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
greetingsService.sendGreeting(member.user, member.user.username);
|
||||
});
|
||||
@@ -0,0 +1,20 @@
|
||||
import { Roles } from "@avocadi/bot-core/entities/roles/roles.schema";
|
||||
import { ReactionRolesService } from "features/reaction-roles/reaction-roles.service";
|
||||
import client from "lib/client";
|
||||
|
||||
const reactionRolesService = new ReactionRolesService();
|
||||
|
||||
// Currently only used for the "people" role, but can be extended to handle multiple roles based on the reaction emoji
|
||||
|
||||
client.on("messageReactionAdd", async (reaction, user) => {
|
||||
reactionRolesService.handleReaction(reaction, user, Roles.enum.people, "add");
|
||||
});
|
||||
|
||||
client.on("messageReactionRemove", async (reaction, user) => {
|
||||
reactionRolesService.handleReaction(
|
||||
reaction,
|
||||
user,
|
||||
Roles.enum.people,
|
||||
"remove",
|
||||
);
|
||||
});
|
||||
@@ -0,0 +1,6 @@
|
||||
import { Events } from "discord.js";
|
||||
import client from "lib/client";
|
||||
|
||||
client.on(Events.VoiceStateUpdate, async (oldState, newState) => {
|
||||
// TODO: handle updates
|
||||
});
|
||||
@@ -21,6 +21,8 @@
|
||||
"noUncheckedIndexedAccess": true,
|
||||
"noImplicitOverride": true,
|
||||
|
||||
"baseUrl": "src",
|
||||
|
||||
// Some stricter flags (disabled by default)
|
||||
"noUnusedLocals": false,
|
||||
"noUnusedParameters": false,
|
||||
|
||||
9
adapters/discord/tsdown.config.ts
Normal file
9
adapters/discord/tsdown.config.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { defineConfig } from "tsdown";
|
||||
|
||||
export default defineConfig({
|
||||
entry: ["./src/index.ts"],
|
||||
format: "esm",
|
||||
dts: true,
|
||||
exports: true,
|
||||
fixedExtension: false,
|
||||
});
|
||||
29
bun.lock
29
bun.lock
@@ -11,10 +11,16 @@
|
||||
"name": "@avocadi/bot-adapter-discord",
|
||||
"dependencies": {
|
||||
"@avocadi/bot-core": "workspace:*",
|
||||
"@discordjs/rest": "^2.6.0",
|
||||
"cron": "^4.4.0",
|
||||
"discord.js": "^14.25.1",
|
||||
"dotenv": "^17.3.1",
|
||||
"dotenv-expand": "^12.0.3",
|
||||
"zod": "catalog:",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bun": "latest",
|
||||
"tsdown": "catalog:",
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": "^5",
|
||||
@@ -37,6 +43,7 @@
|
||||
"discord.js": "^14.16.3",
|
||||
"dotenv": "^16.4.7",
|
||||
"drizzle-orm": "^0.38.3",
|
||||
"tslog": "^4.10.2",
|
||||
"zod": "catalog:",
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -221,7 +228,7 @@
|
||||
|
||||
"@types/jsesc": ["@types/jsesc@2.5.1", "", {}, "sha512-9VN+6yxLOPLOav+7PwjZbxiID2bVaeq0ED4qSQmdQTdjnXJSaCVKTR58t15oqH1H5t8Ng2ZX1SabJVoN9Q34bw=="],
|
||||
|
||||
"@types/luxon": ["@types/luxon@3.4.2", "", {}, "sha512-TifLZlFudklWlMBfhubvgqTXRzLDI5pCbGa4P8a3wPyUQSW+1xQ5eDsreP9DWHX3tjq1ke96uYG/nwundroWcA=="],
|
||||
"@types/luxon": ["@types/luxon@3.7.1", "", {}, "sha512-H3iskjFIAn5SlJU7OuxUmTEpebK6TKB8rxZShDslBMZJ5u9S//KM1sbdAisiSrqwLQncVjnpi2OK2J51h+4lsg=="],
|
||||
|
||||
"@types/node": ["@types/node@25.2.3", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-m0jEgYlYz+mDJZ2+F4v8D1AyQb+QzsNqRuI7xg1VQX/KlKS0qT9r1Mo16yo5F/MtifXFgaofIFsdFMox2SxIbQ=="],
|
||||
|
||||
@@ -253,7 +260,7 @@
|
||||
|
||||
"chownr": ["chownr@1.1.4", "", {}, "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="],
|
||||
|
||||
"cron": ["cron@3.5.0", "", { "dependencies": { "@types/luxon": "~3.4.0", "luxon": "~3.5.0" } }, "sha512-0eYZqCnapmxYcV06uktql93wNWdlTmmBFP2iYz+JPVcQqlyFYcn1lFuIk4R54pkOmE7mcldTAPZv6X5XA4Q46A=="],
|
||||
"cron": ["cron@4.4.0", "", { "dependencies": { "@types/luxon": "~3.7.0", "luxon": "~3.7.0" } }, "sha512-fkdfq+b+AHI4cKdhZlppHveI/mgz2qpiYxcm+t5E5TsxX7QrLS1VE0+7GENEk9z0EeGPcpSciGv6ez24duWhwQ=="],
|
||||
|
||||
"debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
|
||||
|
||||
@@ -269,7 +276,9 @@
|
||||
|
||||
"discord.js": ["discord.js@14.25.1", "", { "dependencies": { "@discordjs/builders": "^1.13.0", "@discordjs/collection": "1.5.3", "@discordjs/formatters": "^0.6.2", "@discordjs/rest": "^2.6.0", "@discordjs/util": "^1.2.0", "@discordjs/ws": "^1.2.3", "@sapphire/snowflake": "3.5.3", "discord-api-types": "^0.38.33", "fast-deep-equal": "3.1.3", "lodash.snakecase": "4.1.1", "magic-bytes.js": "^1.10.0", "tslib": "^2.6.3", "undici": "6.21.3" } }, "sha512-2l0gsPOLPs5t6GFZfQZKnL1OJNYFcuC/ETWsW4VtKVD/tg4ICa9x+jb9bkPffkMdRpRpuUaO/fKkHCBeiCKh8g=="],
|
||||
|
||||
"dotenv": ["dotenv@16.6.1", "", {}, "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow=="],
|
||||
"dotenv": ["dotenv@17.3.1", "", {}, "sha512-IO8C/dzEb6O3F9/twg6ZLXz164a2fhTnEWb95H23Dm4OuN+92NmEAlTrupP9VW6Jm3sO26tQlqyvyi4CsnY9GA=="],
|
||||
|
||||
"dotenv-expand": ["dotenv-expand@12.0.3", "", { "dependencies": { "dotenv": "^16.4.5" } }, "sha512-uc47g4b+4k/M/SeaW1y4OApx+mtLWl92l5LMPP0GNXctZqELk+YGgOPIIC5elYmUH4OuoK3JLhuRUYegeySiFA=="],
|
||||
|
||||
"drizzle-kit": ["drizzle-kit@0.30.6", "", { "dependencies": { "@drizzle-team/brocli": "^0.10.2", "@esbuild-kit/esm-loader": "^2.5.5", "esbuild": "^0.19.7", "esbuild-register": "^3.5.0", "gel": "^2.0.0" }, "bin": { "drizzle-kit": "bin.cjs" } }, "sha512-U4wWit0fyZuGuP7iNmRleQyK2V8wCuv57vf5l3MnG4z4fzNTjY/U13M8owyQ5RavqvqxBifWORaR3wIUzlN64g=="],
|
||||
|
||||
@@ -323,7 +332,7 @@
|
||||
|
||||
"lodash.snakecase": ["lodash.snakecase@4.1.1", "", {}, "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw=="],
|
||||
|
||||
"luxon": ["luxon@3.5.0", "", {}, "sha512-rh+Zjr6DNfUYR3bPwJEnuwDdqMbxZW7LOQfUN4B54+Cl+0o5zaU9RJ6bcidfDtC1cWCZXQ+nvX8bf6bAji37QQ=="],
|
||||
"luxon": ["luxon@3.7.2", "", {}, "sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew=="],
|
||||
|
||||
"magic-bytes.js": ["magic-bytes.js@1.13.0", "", {}, "sha512-afO2mnxW7GDTXMm5/AoN1WuOcdoKhtgXjIvHmobqTD1grNplhGdv3PFOyjCVmrnOZBIT/gD/koDKpYG+0mvHcg=="],
|
||||
|
||||
@@ -397,6 +406,8 @@
|
||||
|
||||
"tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
||||
|
||||
"tslog": ["tslog@4.10.2", "", {}, "sha512-XuELoRpMR+sq8fuWwX7P0bcj+PRNiicOKDEb3fGNURhxWVyykCi9BNq7c4uVz7h7P0sj8qgBsr5SWS6yBClq3g=="],
|
||||
|
||||
"tunnel-agent": ["tunnel-agent@0.6.0", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w=="],
|
||||
|
||||
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
|
||||
@@ -419,12 +430,22 @@
|
||||
|
||||
"zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="],
|
||||
|
||||
"@avocadi/bot-core/cron": ["cron@3.5.0", "", { "dependencies": { "@types/luxon": "~3.4.0", "luxon": "~3.5.0" } }, "sha512-0eYZqCnapmxYcV06uktql93wNWdlTmmBFP2iYz+JPVcQqlyFYcn1lFuIk4R54pkOmE7mcldTAPZv6X5XA4Q46A=="],
|
||||
|
||||
"@avocadi/bot-core/dotenv": ["dotenv@16.6.1", "", {}, "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow=="],
|
||||
|
||||
"@esbuild-kit/core-utils/esbuild": ["esbuild@0.18.20", "", { "optionalDependencies": { "@esbuild/android-arm": "0.18.20", "@esbuild/android-arm64": "0.18.20", "@esbuild/android-x64": "0.18.20", "@esbuild/darwin-arm64": "0.18.20", "@esbuild/darwin-x64": "0.18.20", "@esbuild/freebsd-arm64": "0.18.20", "@esbuild/freebsd-x64": "0.18.20", "@esbuild/linux-arm": "0.18.20", "@esbuild/linux-arm64": "0.18.20", "@esbuild/linux-ia32": "0.18.20", "@esbuild/linux-loong64": "0.18.20", "@esbuild/linux-mips64el": "0.18.20", "@esbuild/linux-ppc64": "0.18.20", "@esbuild/linux-riscv64": "0.18.20", "@esbuild/linux-s390x": "0.18.20", "@esbuild/linux-x64": "0.18.20", "@esbuild/netbsd-x64": "0.18.20", "@esbuild/openbsd-x64": "0.18.20", "@esbuild/sunos-x64": "0.18.20", "@esbuild/win32-arm64": "0.18.20", "@esbuild/win32-ia32": "0.18.20", "@esbuild/win32-x64": "0.18.20" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA=="],
|
||||
|
||||
"discord.js/@discordjs/collection": ["@discordjs/collection@1.5.3", "", {}, "sha512-SVb428OMd3WO1paV3rm6tSjM4wC+Kecaa1EUGX7vc6/fddvw/6lg90z4QtCqm21zvVe92vMMDt9+DkIvjXImQQ=="],
|
||||
|
||||
"discord.js/@sapphire/snowflake": ["@sapphire/snowflake@3.5.3", "", {}, "sha512-jjmJywLAFoWeBi1W7994zZyiNWPIiqRRNAmSERxyg93xRGzNYvGjlZ0gR6x0F4gPRi2+0O6S71kOZYyr3cxaIQ=="],
|
||||
|
||||
"dotenv-expand/dotenv": ["dotenv@16.6.1", "", {}, "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow=="],
|
||||
|
||||
"@avocadi/bot-core/cron/@types/luxon": ["@types/luxon@3.4.2", "", {}, "sha512-TifLZlFudklWlMBfhubvgqTXRzLDI5pCbGa4P8a3wPyUQSW+1xQ5eDsreP9DWHX3tjq1ke96uYG/nwundroWcA=="],
|
||||
|
||||
"@avocadi/bot-core/cron/luxon": ["luxon@3.5.0", "", {}, "sha512-rh+Zjr6DNfUYR3bPwJEnuwDdqMbxZW7LOQfUN4B54+Cl+0o5zaU9RJ6bcidfDtC1cWCZXQ+nvX8bf6bAji37QQ=="],
|
||||
|
||||
"@esbuild-kit/core-utils/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.18.20", "", { "os": "android", "cpu": "arm" }, "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw=="],
|
||||
|
||||
"@esbuild-kit/core-utils/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.18.20", "", { "os": "android", "cpu": "arm64" }, "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ=="],
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
"discord.js": "^14.16.3",
|
||||
"dotenv": "^16.4.7",
|
||||
"drizzle-orm": "^0.38.3",
|
||||
"tslog": "^4.10.2",
|
||||
"zod": "catalog:"
|
||||
},
|
||||
"trustedDependencies": [
|
||||
@@ -32,8 +33,17 @@
|
||||
"./db": "./dist/db/index.js",
|
||||
"./db/schema": "./dist/db/schema.js",
|
||||
"./entities/channels/channels.schema": "./dist/entities/channels/channels.schema.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",
|
||||
"./lib/client": "./dist/lib/client.js",
|
||||
"./entities/roles/roles.service": "./dist/entities/roles/roles.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",
|
||||
"./features/water-me/water-me.service": "./dist/features/water-me/water-me.service.js",
|
||||
"./lib/logger": "./dist/lib/logger.js",
|
||||
"./lib/utils": "./dist/lib/utils.js",
|
||||
"./lib/utils.test": "./dist/lib/utils.test.js",
|
||||
"./package.json": "./package.json"
|
||||
|
||||
@@ -1,19 +1,18 @@
|
||||
import config from "config";
|
||||
import client from "lib/client";
|
||||
import { getRandomInt } from "lib/utils";
|
||||
import { dmWelcomeContent, dmAcceptedContent } from "./dm.components.ts";
|
||||
import {
|
||||
type CacheType,
|
||||
Client,
|
||||
EmbedBuilder,
|
||||
type Message,
|
||||
type CacheType,
|
||||
type GuildMember,
|
||||
type Interaction,
|
||||
type Message,
|
||||
type OmitPartialGroupDMChannel,
|
||||
} from "discord.js";
|
||||
import client from "lib/client";
|
||||
import { getRandomInt } from "lib/utils";
|
||||
import { dmAcceptedContent, dmWelcomeContent } from "./dm.components.ts";
|
||||
|
||||
export class DmService {
|
||||
|
||||
async handleInteraction(interaction: Interaction<CacheType>) {
|
||||
// todo
|
||||
}
|
||||
@@ -21,13 +20,15 @@ export class DmService {
|
||||
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`
|
||||
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.channelIdLog);
|
||||
if (channel?.isTextBased() && channel?.isSendable()) {
|
||||
await channel.send(`konnte keine private nachricht an <@${member.user.id}> senden`);
|
||||
await channel.send(
|
||||
`konnte keine private nachricht an <@${member.user.id}> senden`,
|
||||
);
|
||||
}
|
||||
console.error("error while sending a welcome msg:", error);
|
||||
}
|
||||
@@ -41,7 +42,9 @@ export class DmService {
|
||||
const channels = client.channels;
|
||||
const channel = channels.cache.get(config.discord.channelIdLog);
|
||||
if (channel?.isTextBased() && channel?.isSendable()) {
|
||||
await channel.send(`konnte keine private nachricht an <@${member.user.id}> senden`);
|
||||
await channel.send(
|
||||
`konnte keine private nachricht an <@${member.user.id}> senden`,
|
||||
);
|
||||
}
|
||||
console.error("error while sending a welcome msg:", error);
|
||||
}
|
||||
@@ -56,8 +59,7 @@ export class DmService {
|
||||
|
||||
if (message.author.bot) {
|
||||
context = `<@${author}> hat an <@${recipient}> geschrieben:\n"${message.content}"`;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
context = `<@${author}> hat geschrieben:\n"${message.content}"`;
|
||||
}
|
||||
|
||||
@@ -81,7 +83,9 @@ export class DmService {
|
||||
const channels = client.channels;
|
||||
const channel = channels.cache.get(config.discord.channelIdLog);
|
||||
if (channel?.isTextBased() && channel?.isSendable()) {
|
||||
await channel.send(`konnte keine private nachricht an <@${member.user.id}> senden`);
|
||||
await channel.send(
|
||||
`konnte keine private nachricht an <@${member.user.id}> senden`,
|
||||
);
|
||||
}
|
||||
console.error("error while sending a accept msg:", error);
|
||||
}
|
||||
@@ -90,13 +94,15 @@ export class DmService {
|
||||
async roleMentionDm(member: GuildMember, add: boolean) {
|
||||
console.log("rolementionadd dm");
|
||||
try {
|
||||
const contentRoleMentionDm = `du hast die rolle **streber:in** erfolgreich ** *${(add ? "zugeteilt" : "entfernt")}* ** bekommen :3 <#${config.discord.channelIdOffTopic}> <:avocadi_cute:1321893797138923602>`;
|
||||
const contentRoleMentionDm = `du hast die rolle **streber:in** erfolgreich ** *${add ? "zugeteilt" : "entfernt"}* ** bekommen :3 <#${config.discord.channelIdOffTopic}> <:avocadi_cute:1321893797138923602>`;
|
||||
client.users.send(member, contentRoleMentionDm);
|
||||
} catch (error) {
|
||||
const channels = client.channels;
|
||||
const channel = channels.cache.get(config.discord.channelIdLog);
|
||||
if (channel?.isTextBased() && channel?.isSendable()) {
|
||||
await channel.send(`konnte keine private nachricht an <@${member.user.id}> senden`);
|
||||
await channel.send(
|
||||
`konnte keine private nachricht an <@${member.user.id}> senden`,
|
||||
);
|
||||
}
|
||||
console.error("error while sending a accept msg:", error);
|
||||
}
|
||||
|
||||
@@ -1,24 +1,21 @@
|
||||
import { DmService } from "actions/dm/dm.service.ts";
|
||||
import { Commands, type CommandsType } from "commands/index.ts";
|
||||
import config from "config";
|
||||
import {
|
||||
type CacheType,
|
||||
type ChatInputCommandInteraction,
|
||||
type GuildMember,
|
||||
GuildMemberRoleManager,
|
||||
type Interaction,
|
||||
} from "discord.js";
|
||||
import client from "lib/client";
|
||||
import { getRandomInt } from "lib/utils";
|
||||
import { checkPermission } from "permissions/index.ts";
|
||||
import {
|
||||
getWelcomeContent,
|
||||
greetContent,
|
||||
sleepContent,
|
||||
} from "./greeting.components.ts";
|
||||
import {
|
||||
type ChatInputCommandInteraction,
|
||||
Client,
|
||||
EmbedBuilder,
|
||||
type CacheType,
|
||||
type GuildMember,
|
||||
type Interaction,
|
||||
GuildMemberRoleManager,
|
||||
type APIInteractionGuildMember,
|
||||
} from "discord.js";
|
||||
import { DmService } from "actions/dm/dm.service.ts";
|
||||
import { Commands, type CommandsType } from "commands/index.ts";
|
||||
import { checkPermission } from "permissions/index.ts";
|
||||
|
||||
export class GreetingService {
|
||||
dmService: DmService;
|
||||
@@ -27,17 +24,15 @@ export class GreetingService {
|
||||
this.dmService = new DmService();
|
||||
}
|
||||
|
||||
async handleInteraction(
|
||||
interaction: Interaction<CacheType>
|
||||
) {
|
||||
|
||||
async handleInteraction(interaction: Interaction<CacheType>) {
|
||||
if (interaction.isChatInputCommand()) {
|
||||
await this.handleChatInputCommand(interaction);
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
async handleChatInputCommand(interaction: ChatInputCommandInteraction<CacheType>) {
|
||||
async handleChatInputCommand(
|
||||
interaction: ChatInputCommandInteraction<CacheType>,
|
||||
) {
|
||||
const commandName = interaction.commandName as CommandsType;
|
||||
switch (commandName) {
|
||||
case Commands.Enum.accept:
|
||||
@@ -72,9 +67,7 @@ export class GreetingService {
|
||||
}
|
||||
}
|
||||
|
||||
async acceptUser(
|
||||
interaction: ChatInputCommandInteraction<CacheType>
|
||||
) {
|
||||
async acceptUser(interaction: ChatInputCommandInteraction<CacheType>) {
|
||||
console.log("accept user");
|
||||
|
||||
// get the string option
|
||||
@@ -83,7 +76,7 @@ export class GreetingService {
|
||||
//console.log(input);
|
||||
|
||||
// permission check
|
||||
if (await checkPermission(interaction.member) !== true) {
|
||||
if ((await checkPermission(interaction.member)) !== true) {
|
||||
await interaction.reply({
|
||||
content: "du hast keine rechte fuer diesen befehl",
|
||||
ephemeral: true,
|
||||
@@ -108,7 +101,7 @@ export class GreetingService {
|
||||
const username = member.user.username;
|
||||
console.log(username);
|
||||
|
||||
if (await this.checkRole(member) === true) {
|
||||
if ((await this.checkRole(member)) === true) {
|
||||
await interaction.reply({
|
||||
content: `${member.user.username} hat die rolle *lernende:r* schon!`,
|
||||
ephemeral: true,
|
||||
@@ -192,8 +185,7 @@ export class GreetingService {
|
||||
return greetContent[getRandomInt(0, greetContent.length - 1)];
|
||||
}
|
||||
|
||||
async reminderCommand(
|
||||
interaction: ChatInputCommandInteraction<CacheType>) {
|
||||
async reminderCommand(interaction: ChatInputCommandInteraction<CacheType>) {
|
||||
console.log("remind user");
|
||||
|
||||
// get the string option
|
||||
@@ -203,7 +195,7 @@ export class GreetingService {
|
||||
|
||||
try {
|
||||
// permission check
|
||||
if (await checkPermission(interaction.member) !== true) {
|
||||
if ((await checkPermission(interaction.member)) !== true) {
|
||||
await interaction.reply({
|
||||
content: "du hast keine rechte fuer diesen befehl",
|
||||
ephemeral: true,
|
||||
@@ -227,7 +219,7 @@ export class GreetingService {
|
||||
const username = member.user.username;
|
||||
console.log(username);
|
||||
|
||||
if (await this.checkRole(member) === true) {
|
||||
if ((await this.checkRole(member)) === true) {
|
||||
await interaction.reply({
|
||||
content: `${member.user.username} hat die rolle *lernende:r* schon!`,
|
||||
ephemeral: true,
|
||||
@@ -252,9 +244,7 @@ export class GreetingService {
|
||||
}
|
||||
}
|
||||
|
||||
async welcomeCommand(
|
||||
interaction: ChatInputCommandInteraction<CacheType>
|
||||
) {
|
||||
async welcomeCommand(interaction: ChatInputCommandInteraction<CacheType>) {
|
||||
console.log("welcome user");
|
||||
|
||||
// get the string option
|
||||
@@ -275,7 +265,7 @@ export class GreetingService {
|
||||
return;
|
||||
}
|
||||
|
||||
if (await checkPermission(interaction.member) !== true) {
|
||||
if ((await checkPermission(interaction.member)) !== true) {
|
||||
await interaction.reply({
|
||||
content: "du hast keine rechte fuer diesen befehl",
|
||||
ephemeral: true,
|
||||
@@ -290,7 +280,7 @@ export class GreetingService {
|
||||
|
||||
const welcomeContent = getWelcomeContent(member);
|
||||
|
||||
if (await this.checkRole(member) === true) {
|
||||
if ((await this.checkRole(member)) === true) {
|
||||
await interaction.reply({
|
||||
content: `${member.user.username} wurde schon begruesst!`,
|
||||
ephemeral: true,
|
||||
@@ -306,7 +296,6 @@ export class GreetingService {
|
||||
}
|
||||
|
||||
await this.dmService.welcomePrivate(member);
|
||||
|
||||
} catch (error) {
|
||||
console.error("error while sending a welcome command msg:", error);
|
||||
}
|
||||
@@ -315,12 +304,10 @@ export class GreetingService {
|
||||
content: `erfolgreich welcome command: ${member.user.username}`,
|
||||
ephemeral: true,
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error("fehler bei welcome command", error);
|
||||
await interaction.reply({
|
||||
content:
|
||||
"fehler bei welcome command",
|
||||
content: "fehler bei welcome command",
|
||||
ephemeral: true,
|
||||
});
|
||||
}
|
||||
@@ -329,7 +316,10 @@ export class GreetingService {
|
||||
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)) {
|
||||
if (
|
||||
member.roles.cache.has(config.discord.roleAdmin) ||
|
||||
member.roles.cache.has(config.discord.roleMod)
|
||||
) {
|
||||
console.log("user has permission");
|
||||
hasRole = true;
|
||||
}
|
||||
|
||||
@@ -1,99 +0,0 @@
|
||||
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??? ich brauche noch mehr wasser :(",
|
||||
"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.channelIdBot);
|
||||
|
||||
if (
|
||||
channel?.isTextBased &&
|
||||
channel?.isSendable() &&
|
||||
this.waterLevel <= this.thirsty
|
||||
) {
|
||||
await channel.send({ content: "ich brauche wasser :(" });
|
||||
}
|
||||
}
|
||||
|
||||
waterMe() {
|
||||
const reply = this.getReply();
|
||||
|
||||
this.waterLevel++;
|
||||
console.log(this.waterLevel);
|
||||
|
||||
return {
|
||||
reply,
|
||||
};
|
||||
}
|
||||
|
||||
async handleInteraction(interaction: Interaction<CacheType>) {
|
||||
const result = this.waterMe();
|
||||
|
||||
const moreButton = new ButtonBuilder()
|
||||
.setCustomId("moreWater")
|
||||
.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,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
import { CronJob } from "cron";
|
||||
import { WaterMeService } from "actions/waterMe/waterMe.service";
|
||||
|
||||
const waterMeService = new WaterMeService();
|
||||
/*
|
||||
new CronJob(
|
||||
"0 0 20 * * *", // 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.
|
||||
@@ -1,123 +0,0 @@
|
||||
import { SlashCommandBuilder, userMention } from "discord.js";
|
||||
import { z } from "zod";
|
||||
|
||||
export const Commands = z.enum(["giessen", "medikamente", "hilfe", "support", "kofi", "disboard", "discadia", "accept", "welcome", "embed", "message", "reminder", "version"]);
|
||||
|
||||
export const CommandsMeta: Record<z.output<typeof Commands>, { description: string }> = {
|
||||
giessen: {
|
||||
description: "giess mich mit etwas wasser :3"
|
||||
},
|
||||
medikamente: {
|
||||
description: "ich erinnere dich gerne daran, deine medikamente zu nehmen! :)"
|
||||
},
|
||||
hilfe: {
|
||||
description: "ich schreibe dir auf, was du alles mit mir machen kannst :)"
|
||||
},
|
||||
support: {
|
||||
description: "unterstuetze uns! link zu unserem ko-fi, disboard und discardia c:"
|
||||
},
|
||||
kofi: {
|
||||
description: "link zu unserem ko-fi (spendenportal):"
|
||||
},
|
||||
disboard: {
|
||||
description: "link zu disboard, hier kannst du uns bewerten!"
|
||||
},
|
||||
discadia: {
|
||||
description: "link zu discadia, hier kannst du fuer uns voten!"
|
||||
},
|
||||
accept: {
|
||||
description: "admin use only"
|
||||
},
|
||||
welcome: {
|
||||
description: "admin use only"
|
||||
},
|
||||
embed: {
|
||||
description: "admin use only"
|
||||
},
|
||||
message: {
|
||||
description: "admin use only"
|
||||
},
|
||||
reminder: {
|
||||
description: "admin use only"
|
||||
},
|
||||
version: {
|
||||
description: "admin use only"
|
||||
}
|
||||
}
|
||||
|
||||
export type CommandsType = z.output<typeof Commands>;
|
||||
|
||||
export default function getCommands() {
|
||||
const commands = [
|
||||
new SlashCommandBuilder()
|
||||
.setName(Commands.Enum.giessen)
|
||||
.setDescription(CommandsMeta.giessen.description),
|
||||
new SlashCommandBuilder()
|
||||
.setName(Commands.Enum.medikamente)
|
||||
.setDescription(CommandsMeta.medikamente.description),
|
||||
new SlashCommandBuilder()
|
||||
.setName(Commands.Enum.hilfe)
|
||||
.setDescription(CommandsMeta.hilfe.description),
|
||||
new SlashCommandBuilder()
|
||||
.setName(Commands.Enum.support)
|
||||
.setDescription(CommandsMeta.support.description),
|
||||
new SlashCommandBuilder()
|
||||
.setName(Commands.Enum.kofi)
|
||||
.setDescription(CommandsMeta.kofi.description),
|
||||
new SlashCommandBuilder()
|
||||
.setName(Commands.Enum.disboard)
|
||||
.setDescription(CommandsMeta.disboard.description),
|
||||
new SlashCommandBuilder()
|
||||
.setName(Commands.Enum.discadia)
|
||||
.setDescription(CommandsMeta.discadia.description),
|
||||
new SlashCommandBuilder()
|
||||
.setName(Commands.Enum.accept)
|
||||
.setDescription(CommandsMeta.accept.description)
|
||||
.addStringOption(option =>
|
||||
option.setName('input')
|
||||
.setDescription('input for bot')
|
||||
.setRequired(true)),
|
||||
new SlashCommandBuilder()
|
||||
.setName(Commands.Enum.welcome)
|
||||
.setDescription(CommandsMeta.welcome.description)
|
||||
.addStringOption(option =>
|
||||
option.setName('input')
|
||||
.setDescription('input for bot')
|
||||
.setRequired(true)),
|
||||
new SlashCommandBuilder()
|
||||
.setName(Commands.Enum.embed)
|
||||
.setDescription(CommandsMeta.embed.description)
|
||||
.addStringOption(option =>
|
||||
option.setName('title')
|
||||
.setDescription('title')
|
||||
.setRequired(true))
|
||||
.addStringOption(option =>
|
||||
option.setName('description')
|
||||
.setDescription('description')
|
||||
.setRequired(true))
|
||||
.addBooleanOption(option =>
|
||||
option.setName('timestamp')
|
||||
.setDescription('timestamp bool')
|
||||
.setRequired(false)),
|
||||
new SlashCommandBuilder()
|
||||
.setName(Commands.Enum.message)
|
||||
.setDescription(CommandsMeta.message.description)
|
||||
.addStringOption(option =>
|
||||
option.setName('input')
|
||||
.setDescription('input for bot')
|
||||
.setRequired(true)),
|
||||
new SlashCommandBuilder()
|
||||
.setName(Commands.Enum.reminder)
|
||||
.setDescription(CommandsMeta.reminder.description)
|
||||
.addStringOption(option =>
|
||||
option.setName('input')
|
||||
.setDescription('input for bot')
|
||||
.setRequired(true)),
|
||||
new SlashCommandBuilder()
|
||||
.setName(Commands.Enum.version)
|
||||
.setDescription(CommandsMeta.version.description),
|
||||
|
||||
].map((command) => command.toJSON());
|
||||
|
||||
return commands;
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
import { Routes } from "discord.js";
|
||||
import { REST } from "@discordjs/rest";
|
||||
import config from "config";
|
||||
import getCommands from "commands";
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
84
core/src/entities/commands/commands.entity.ts
Normal file
84
core/src/entities/commands/commands.entity.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
import type z from "zod";
|
||||
import type { Commands } from "./commands.schema";
|
||||
|
||||
// TODO: add missing options
|
||||
export const CommandsCollection: z.output<typeof Commands> = {
|
||||
giessen: {
|
||||
description: "giess mich mit etwas wasser :3",
|
||||
},
|
||||
medikamente: {
|
||||
description:
|
||||
"ich erinnere dich gerne daran, deine medikamente zu nehmen! :)",
|
||||
},
|
||||
hilfe: {
|
||||
description: "ich schreibe dir auf, was du alles mit mir machen kannst :)",
|
||||
},
|
||||
support: {
|
||||
description:
|
||||
"unterstuetze uns! link zu unserem ko-fi, disboard und discardia c:",
|
||||
},
|
||||
kofi: {
|
||||
description: "link zu unserem ko-fi (spendenportal):",
|
||||
},
|
||||
disboard: {
|
||||
description: "link zu disboard, hier kannst du uns bewerten!",
|
||||
},
|
||||
discadia: {
|
||||
description: "link zu discadia, hier kannst du fuer uns voten!",
|
||||
},
|
||||
accept: {
|
||||
description: "admin use only",
|
||||
options: [
|
||||
{
|
||||
name: "input",
|
||||
description: "input for bot",
|
||||
required: true,
|
||||
type: "string",
|
||||
},
|
||||
],
|
||||
},
|
||||
welcome: {
|
||||
description: "admin use only",
|
||||
options: [
|
||||
{
|
||||
name: "input",
|
||||
description: "input for bot",
|
||||
required: true,
|
||||
type: "string",
|
||||
},
|
||||
],
|
||||
},
|
||||
embed: {
|
||||
description: "admin use only",
|
||||
options: [
|
||||
{
|
||||
name: "title",
|
||||
description: "title",
|
||||
required: true,
|
||||
type: "string",
|
||||
},
|
||||
{
|
||||
name: "description",
|
||||
description: "description",
|
||||
required: true,
|
||||
type: "string",
|
||||
},
|
||||
{
|
||||
name: "timestamp",
|
||||
description: "timestamp bool",
|
||||
required: false,
|
||||
type: "boolean",
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
message: {
|
||||
description: "admin use only",
|
||||
},
|
||||
reminder: {
|
||||
description: "admin use only",
|
||||
},
|
||||
version: {
|
||||
description: "admin use only",
|
||||
},
|
||||
};
|
||||
59
core/src/entities/commands/commands.schema.ts
Normal file
59
core/src/entities/commands/commands.schema.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import z from "zod";
|
||||
|
||||
export const CommandKeyOptions = [
|
||||
"giessen",
|
||||
"medikamente",
|
||||
"hilfe",
|
||||
"support",
|
||||
"kofi",
|
||||
"disboard",
|
||||
"discadia",
|
||||
"accept",
|
||||
"welcome",
|
||||
"embed",
|
||||
"message",
|
||||
"reminder",
|
||||
"version",
|
||||
] as const;
|
||||
|
||||
export const CommandOptionTypeOptions = [
|
||||
"string",
|
||||
"integer",
|
||||
"boolean",
|
||||
"mentionable",
|
||||
"channel",
|
||||
"role",
|
||||
"user",
|
||||
] as const;
|
||||
|
||||
export const CommandOptionTypes = z.enum(CommandOptionTypeOptions);
|
||||
|
||||
export const CommandOptionCommon = z.object({
|
||||
name: z.string(),
|
||||
description: z.string(),
|
||||
required: z.boolean(),
|
||||
});
|
||||
|
||||
export const CommandOptionString = CommandOptionCommon.extend({
|
||||
type: z.literal(CommandOptionTypes.enum.string),
|
||||
});
|
||||
|
||||
export const CommandOptionBoolean = CommandOptionCommon.extend({
|
||||
type: z.literal(CommandOptionTypes.enum.boolean),
|
||||
});
|
||||
|
||||
// TODO: add other option types
|
||||
export const CommandOption = z.discriminatedUnion("type", [
|
||||
CommandOptionString,
|
||||
CommandOptionBoolean,
|
||||
]);
|
||||
|
||||
export const CommandKeys = z.enum(CommandKeyOptions);
|
||||
|
||||
export const Command = z.object({
|
||||
name: z.string().optional(),
|
||||
description: z.string(),
|
||||
options: z.array(CommandOption).optional(),
|
||||
});
|
||||
|
||||
export const Commands = z.record(CommandKeys, Command);
|
||||
5
core/src/entities/interactions/interactions.schema.ts
Normal file
5
core/src/entities/interactions/interactions.schema.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import z from "zod";
|
||||
|
||||
export const InteractionOptions = ["command", "button"] as const;
|
||||
|
||||
export const Interactions = z.enum(InteractionOptions);
|
||||
3
core/src/entities/messages/messages.service.ts
Normal file
3
core/src/entities/messages/messages.service.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export interface MessagesServiceInterface<U = unknown> {
|
||||
sendToUser(user: U, message: string): Promise<void>;
|
||||
}
|
||||
6
core/src/entities/roles/roles.service.ts
Normal file
6
core/src/entities/roles/roles.service.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
export interface RolesServiceInterface<U = unknown> {
|
||||
assignRole(user: U, role: string): void | Promise<void>;
|
||||
removeRole(user: U, role: string): void | Promise<void>;
|
||||
getRoles(user: U): string[] | Promise<string[]>;
|
||||
hasRole(user: U, role: string): boolean | Promise<boolean>;
|
||||
}
|
||||
14
core/src/features/greeting/greeting.service.ts
Normal file
14
core/src/features/greeting/greeting.service.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import type { MessagesServiceInterface } from "entities/messages/messages.service";
|
||||
|
||||
export class GreetingService<U = unknown> {
|
||||
messagesService: MessagesServiceInterface<U>;
|
||||
|
||||
constructor(messagesService: MessagesServiceInterface<U>) {
|
||||
this.messagesService = messagesService;
|
||||
}
|
||||
|
||||
async sendGreeting(user: U, userName: string) {
|
||||
const greetingMessage = `Hello, ${userName}! Welcome to the server!`;
|
||||
await this.messagesService.sendToUser(user, greetingMessage);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
export type TextBasedFeatureHandleMessageSend = (
|
||||
message: string,
|
||||
channelId: string,
|
||||
) => void | Promise<void>;
|
||||
|
||||
export type TextBasedFeatureInput = {
|
||||
channelId: string;
|
||||
handleMessageSend: TextBasedFeatureHandleMessageSend;
|
||||
};
|
||||
18
core/src/features/text-based-feature/text-based-feature.ts
Normal file
18
core/src/features/text-based-feature/text-based-feature.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import type {
|
||||
TextBasedFeatureHandleMessageSend,
|
||||
TextBasedFeatureInput,
|
||||
} from "./text-based-feature.schema";
|
||||
|
||||
export class TextBasedFeature {
|
||||
channelId: string;
|
||||
handleMessageSend: TextBasedFeatureHandleMessageSend;
|
||||
|
||||
constructor(input: TextBasedFeatureInput) {
|
||||
this.channelId = input.channelId;
|
||||
this.handleMessageSend = input.handleMessageSend;
|
||||
}
|
||||
|
||||
async sendMessage(input: { content: string }) {
|
||||
this.handleMessageSend(input.content, this.channelId);
|
||||
}
|
||||
}
|
||||
64
core/src/features/water-me/water-me.service.ts
Normal file
64
core/src/features/water-me/water-me.service.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
import { TextBasedFeature } from "features/text-based-feature/text-based-feature";
|
||||
import type { TextBasedFeatureInput } from "features/text-based-feature/text-based-feature.schema";
|
||||
import { createLogger } from "lib/logger";
|
||||
import { getRandomInt } from "lib/utils";
|
||||
|
||||
export class WaterMeService extends TextBasedFeature {
|
||||
waterLevel: number;
|
||||
|
||||
private logger = createLogger("WaterMeService");
|
||||
|
||||
private thirsty = 3 as const;
|
||||
private enough = 10 as const;
|
||||
|
||||
constructor(input: TextBasedFeatureInput) {
|
||||
super(input);
|
||||
this.waterLevel = 0;
|
||||
}
|
||||
|
||||
getReply() {
|
||||
const thirstyReplies = [
|
||||
"... wow das wars schon??? ich brauche noch mehr wasser :(",
|
||||
"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 notifyIfThirsty() {
|
||||
if (this.waterLevel <= this.thirsty) {
|
||||
await this.sendMessage({ content: "ich brauche wasser :(" });
|
||||
}
|
||||
}
|
||||
|
||||
waterMe() {
|
||||
const reply = this.getReply();
|
||||
|
||||
this.waterLevel++;
|
||||
this.logger.info(`Water level increased to ${this.waterLevel}`);
|
||||
|
||||
return {
|
||||
reply,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
import config from "config";
|
||||
import { Client, GatewayIntentBits, Partials, ChannelType, Events, IntentsBitField } from "discord.js";
|
||||
|
||||
const client = new Client({
|
||||
intents: [IntentsBitField.Flags.Guilds,
|
||||
IntentsBitField.Flags.GuildMembers,
|
||||
IntentsBitField.Flags.GuildModeration,
|
||||
IntentsBitField.Flags.GuildMessages,
|
||||
IntentsBitField.Flags.GuildMessageReactions,
|
||||
IntentsBitField.Flags.GuildMessagePolls,
|
||||
IntentsBitField.Flags.GuildVoiceStates,
|
||||
IntentsBitField.Flags.MessageContent,
|
||||
IntentsBitField.Flags.DirectMessages,
|
||||
IntentsBitField.Flags.DirectMessageReactions,
|
||||
IntentsBitField.Flags.DirectMessageTyping,
|
||||
IntentsBitField.Flags.DirectMessagePolls,],
|
||||
partials: [Partials.Channel, Partials.Message, Partials.Reaction]
|
||||
});
|
||||
|
||||
await client.login(config.discord.token);
|
||||
|
||||
export default client;
|
||||
8
core/src/lib/logger.ts
Normal file
8
core/src/lib/logger.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { Logger } from "tslog";
|
||||
|
||||
export const createLogger = (name: string) =>
|
||||
new Logger({
|
||||
name,
|
||||
prettyLogTemplate:
|
||||
"{{yyyy}}.{{mm}}.{{dd}} {{hh}}:{{MM}}:{{ss}}:{{ms}}\t{{logLevelName}}\t{{name}}\t",
|
||||
});
|
||||
@@ -4,6 +4,7 @@ export default defineConfig({
|
||||
entry: [
|
||||
"./src/index.ts",
|
||||
"./src/entities/**/*.ts",
|
||||
"./src/features/**/*.ts",
|
||||
"./src/lib/**/*.ts",
|
||||
"./src/api/**/*.ts",
|
||||
"./src/db/**/*.ts",
|
||||
|
||||
Reference in New Issue
Block a user