begin rewriting first services to be domain agnostic
more rewrites
This commit is contained in:
@@ -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
|
||||
);
|
||||
Reference in New Issue
Block a user