Adds Event system and automatic messages
This commit is contained in:
parent
0e10ea3cab
commit
2f826fbf36
20 changed files with 428 additions and 18 deletions
27
source/Events/DefaultEvents.ts
Normal file
27
source/Events/DefaultEvents.ts
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
import {EventHandler, TimedEvent} from "./EventHandler";
|
||||
import {Container} from "../Container/Container";
|
||||
import {ReminderEvent} from "./ReminderEvent";
|
||||
import {ElementCreatedEvent} from "./ElementCreatedEvent";
|
||||
import {ClassNamed} from "../types/Class";
|
||||
import {sendCreatedNotificationEventHandler} from "./Handlers/SendCreatedNotification";
|
||||
import {PlaydateModel} from "../Models/PlaydateModel";
|
||||
|
||||
export class DefaultEvents {
|
||||
public static setupTimed() {
|
||||
const events: TimedEvent[] = [
|
||||
new ReminderEvent()
|
||||
]
|
||||
|
||||
const eventHandler = Container.get<EventHandler>(EventHandler.name);
|
||||
|
||||
events.forEach((event) => {
|
||||
eventHandler.addTimed(event);
|
||||
})
|
||||
}
|
||||
|
||||
public static setupHandlers() {
|
||||
const eventHandler = Container.get<EventHandler>(EventHandler.name);
|
||||
|
||||
eventHandler.addHandler<ElementCreatedEvent<PlaydateModel>>(ElementCreatedEvent.name, sendCreatedNotificationEventHandler);
|
||||
}
|
||||
}
|
||||
10
source/Events/ElementCreatedEvent.ts
Normal file
10
source/Events/ElementCreatedEvent.ts
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
import {Model} from "../Models/Model";
|
||||
|
||||
export class ElementCreatedEvent<T extends Model = Model> {
|
||||
constructor(
|
||||
public readonly tableName: string,
|
||||
public readonly instanceValues: Partial<T>,
|
||||
public readonly instanceId: number
|
||||
) {
|
||||
}
|
||||
}
|
||||
48
source/Events/EventHandler.ts
Normal file
48
source/Events/EventHandler.ts
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
import cron from "node-cron";
|
||||
import {Nullable} from "../types/Nullable";
|
||||
import {Class, ClassNamed} from "../types/Class";
|
||||
|
||||
export type EventConfiguration = {
|
||||
name: string,
|
||||
maxExecutions?: number,
|
||||
}
|
||||
|
||||
export interface TimedEvent {
|
||||
configuration: EventConfiguration,
|
||||
cronExpression: string,
|
||||
execute: () => void
|
||||
}
|
||||
|
||||
export class EventHandler {
|
||||
private eventHandlers: Map<string, CallableFunction[]> = new Map();
|
||||
|
||||
constructor() {
|
||||
}
|
||||
|
||||
public addHandler<T extends Class>(eventName: string, handler: (event: T) => void) {
|
||||
if (!this.eventHandlers.has(eventName)) {
|
||||
this.eventHandlers.set(eventName, []);
|
||||
}
|
||||
|
||||
this.eventHandlers.get(eventName)?.push(handler);
|
||||
}
|
||||
|
||||
public dispatch<T extends Class>(event: T) {
|
||||
const eventName = event.constructor.name;
|
||||
if (!this.eventHandlers.has(eventName)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.eventHandlers.get(eventName)?.forEach((handler) => {
|
||||
handler(event);
|
||||
})
|
||||
}
|
||||
|
||||
public addTimed(event: TimedEvent) {
|
||||
if (!cron.validate(event.cronExpression)) {
|
||||
throw new Error(`Can't create event with name '${event.configuration.name}': Invalid cron expression.`)
|
||||
}
|
||||
|
||||
cron.schedule(event.cronExpression, event.execute.bind(event), event.configuration);
|
||||
}
|
||||
}
|
||||
75
source/Events/Handlers/SendCreatedNotification.ts
Normal file
75
source/Events/Handlers/SendCreatedNotification.ts
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
import {ElementCreatedEvent} from "../ElementCreatedEvent";
|
||||
import {DefaultHandler} from "../DefaultEvents";
|
||||
import {PlaydateModel} from "../../Models/PlaydateModel";
|
||||
import PlaydateTableConfiguration from "../../Database/tables/Playdate";
|
||||
import {EmbedBuilder, roleMention, time} from "discord.js";
|
||||
import {ArrayUtils} from "../../Utilities/ArrayUtils";
|
||||
import {GroupConfigurationHandler} from "../../Groups/GroupConfigurationHandler";
|
||||
import {Container} from "../../Container/Container";
|
||||
import {GroupConfigurationRenderer} from "../../Groups/GroupConfigurationRenderer";
|
||||
import {GroupConfigurationRepository} from "../../Repositories/GroupConfigurationRepository";
|
||||
import {DiscordClient} from "../../Discord/DiscordClient";
|
||||
|
||||
const NEW_PLAYDATE_MESSAGES = [
|
||||
'A new playdate was added. Lets hope, your GM has not planned to kill you. >:]',
|
||||
'Oh look. A new playdate... neat.',
|
||||
'A new playdate. Lets polish the dice.'
|
||||
];
|
||||
|
||||
export async function sendCreatedNotificationEventHandler(event: ElementCreatedEvent<PlaydateModel>) {
|
||||
if (event.tableName !== PlaydateTableConfiguration.name) {
|
||||
return;
|
||||
}
|
||||
|
||||
const playdate = event.instanceValues;
|
||||
if (!playdate.group || !playdate.from_time || !playdate.to_time) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
const configurationHandler = new GroupConfigurationHandler(
|
||||
Container.get<GroupConfigurationRepository>(GroupConfigurationRepository.name),
|
||||
playdate.group
|
||||
);
|
||||
|
||||
const targetChannel = configurationHandler.getConfigurationByPath('channels.newPlaydates');
|
||||
if (!targetChannel) {
|
||||
return;
|
||||
}
|
||||
|
||||
const channel = await Container.get<DiscordClient>(DiscordClient.name).Client.channels.fetch(targetChannel)
|
||||
if (!channel) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!channel.isTextBased()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!channel.isSendable()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const embed = new EmbedBuilder()
|
||||
.setTitle("New Playdate added")
|
||||
.setDescription(
|
||||
ArrayUtils.chooseRandom(NEW_PLAYDATE_MESSAGES)
|
||||
)
|
||||
.addFields({
|
||||
name: "Playdate:",
|
||||
value: `${time(playdate.from_time, "F")} - ${time(playdate.to_time, 'F')}`,
|
||||
})
|
||||
.setFooter({
|
||||
text: `Group: ${playdate.group.name}`
|
||||
});
|
||||
|
||||
channel.send({
|
||||
content: roleMention(playdate.group.role.roleid),
|
||||
embeds: [
|
||||
embed
|
||||
],
|
||||
allowedMentions: {
|
||||
roles: [ playdate.group.role.roleid ]
|
||||
}
|
||||
})
|
||||
}
|
||||
122
source/Events/ReminderEvent.ts
Normal file
122
source/Events/ReminderEvent.ts
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
import {CronExpression, Event, EventConfiguration, TimedEvent} from "./EventHandler";
|
||||
import {Container} from "../Container/Container";
|
||||
import Playdate from "../Database/tables/Playdate";
|
||||
import {PlaydateRepository} from "../Repositories/PlaydateRepository";
|
||||
import {GroupConfigurationHandler} from "../Groups/GroupConfigurationHandler";
|
||||
import {GroupConfigurationRepository} from "../Repositories/GroupConfigurationRepository";
|
||||
import {PlaydateModel} from "../Models/PlaydateModel";
|
||||
import {ChannelId} from "../types/DiscordTypes";
|
||||
import {DiscordClient} from "../Discord/DiscordClient";
|
||||
import {EmbedBuilder, MessageFlags, roleMention, time} from "discord.js";
|
||||
import {ArrayUtils} from "../Utilities/ArrayUtils";
|
||||
|
||||
export class ReminderEvent implements TimedEvent {
|
||||
private static REMINDER_INTERVALS = [
|
||||
1,
|
||||
7
|
||||
];
|
||||
|
||||
private static REMINDER_NOTIFICATIONS = [
|
||||
'The darkness approaches. Get ready!',
|
||||
'Your aid is requested once again.',
|
||||
'Grab your dice and show them evil-doers how its done!',
|
||||
]
|
||||
|
||||
|
||||
configuration: EventConfiguration = {
|
||||
name: "Reminders",
|
||||
}
|
||||
|
||||
cronExpression: CronExpression = "0 9 * * *"
|
||||
|
||||
private groupConfigurationRepository: GroupConfigurationRepository
|
||||
private playdateRepository: PlaydateRepository
|
||||
private discordClient: DiscordClient
|
||||
|
||||
constructor() {
|
||||
this.playdateRepository = Container.get<PlaydateRepository>(PlaydateRepository.name);
|
||||
this.groupConfigurationRepository = Container.get<GroupConfigurationRepository>(GroupConfigurationRepository.name);
|
||||
this.discordClient = Container.get<DiscordClient>(DiscordClient.name);
|
||||
}
|
||||
|
||||
async execute() {
|
||||
const today = new Date();
|
||||
today.setHours(0,0,0,0);
|
||||
|
||||
const playdates = ReminderEvent.REMINDER_INTERVALS.flatMap((interval) => {
|
||||
const fromDate = new Date(today.valueOf())
|
||||
fromDate.setDate(fromDate.getDate() + interval);
|
||||
|
||||
const toDate = new Date(today.valueOf())
|
||||
toDate.setDate(toDate.getDate() + interval);
|
||||
toDate.setHours(23,59,59,999);
|
||||
|
||||
return this.playdateRepository.findPlaydatesInRange(fromDate, toDate);
|
||||
}, this)
|
||||
|
||||
const promises = playdates
|
||||
.map((playdate) => {
|
||||
if (!playdate.group) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
const configurationHandler = new GroupConfigurationHandler(
|
||||
this.groupConfigurationRepository,
|
||||
playdate.group
|
||||
);
|
||||
|
||||
const config = configurationHandler.getConfiguration();
|
||||
const targetChannel = config.channels?.playdateReminders;
|
||||
|
||||
if (!targetChannel) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return this.sendReminder(playdate, targetChannel, config.locale);
|
||||
}, this)
|
||||
|
||||
await Promise.all(promises);
|
||||
}
|
||||
|
||||
private async sendReminder(playdate: PlaydateModel, targetChannel: ChannelId, locale: Intl.Locale) {
|
||||
if (!playdate.group) {
|
||||
return;
|
||||
}
|
||||
|
||||
const channel = await this.discordClient.Client.channels.fetch(targetChannel)
|
||||
if (!channel) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!channel.isTextBased()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!channel.isSendable()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const embed = new EmbedBuilder()
|
||||
.setTitle("Playdate reminder")
|
||||
.setDescription(
|
||||
ArrayUtils.chooseRandom(ReminderEvent.REMINDER_NOTIFICATIONS)
|
||||
)
|
||||
.addFields({
|
||||
name: "Next Playdate:",
|
||||
value: `${time(playdate.from_time, "F")} - ${time(playdate.from_time, 'R')}`,
|
||||
})
|
||||
.setFooter({
|
||||
text: `Group: ${playdate.group.name}`
|
||||
});
|
||||
|
||||
channel.send({
|
||||
content: roleMention(playdate.group.role.roleid),
|
||||
embeds: [
|
||||
embed
|
||||
],
|
||||
allowedMentions: {
|
||||
roles: [ playdate.group.role.roleid ]
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue