Sammy
5b98c9bf2f
We can add multiple eventhandlers per eventname. To avoid confusion and large files and to improve concise file names the event files were renamed
184 lines
8.3 KiB
TypeScript
184 lines
8.3 KiB
TypeScript
import { addDays, differenceInDays, format, isAfter, toDate } from 'date-fns'
|
|
import { Guild, GuildScheduledEvent, GuildScheduledEventEditOptions, GuildScheduledEventSetStatusArg, GuildScheduledEventStatus, Message, MessageCreateOptions, TextChannel } from 'discord.js'
|
|
import { v4 as uuid } from 'uuid'
|
|
import { client } from '../..'
|
|
import { config } from '../configuration'
|
|
import { Emotes } from '../events/autoCreateVoteByWPEvent'
|
|
import { Maybe } from '../interfaces'
|
|
import { logger } from '../logger'
|
|
import { Command } from '../structures/command'
|
|
import { RunOptions } from '../types/commandTypes'
|
|
|
|
export default new Command({
|
|
name: 'closepoll',
|
|
description: 'Aktuelle Umfrage für nächste Watchparty beenden und Gewinner in Event eintragen.',
|
|
options: [],
|
|
run: async (interaction: RunOptions) => {
|
|
const command = interaction.interaction
|
|
const requestId = uuid()
|
|
if (!command.guild) {
|
|
logger.error("No guild found in interaction. Cancelling closing request", { requestId })
|
|
command.followUp("Es gab leider ein Problem. Ich konnte deine Anfrage nicht bearbeiten :(")
|
|
return
|
|
}
|
|
const guildId = command.guildId
|
|
logger.info("Got command for closing poll!", { guildId, requestId })
|
|
|
|
command.followUp("Alles klar, beende die Umfrage :)")
|
|
closePoll(command.guild, requestId)
|
|
}
|
|
})
|
|
|
|
export async function closePoll(guild: Guild, requestId: string) {
|
|
const guildId = guild.id
|
|
logger.info("stopping poll", { guildId, requestId })
|
|
|
|
const announcementChannel: Maybe<TextChannel> = client.getAnnouncementChannelForGuild(guildId)
|
|
if (!announcementChannel) {
|
|
logger.error("Could not find the textchannel. Unable to close poll.", { guildId, requestId })
|
|
return
|
|
}
|
|
|
|
const messages: Message<true>[] = (await announcementChannel.messages.fetch()) //todo: fetch only pinned messages
|
|
.map((value) => value)
|
|
.filter(message => !message.cleanContent.includes("[Abstimmung beendet]") && message.cleanContent.includes("[Abstimmung]"))
|
|
.sort((a, b) => b.createdTimestamp - a.createdTimestamp)
|
|
|
|
if (!messages || messages.length <= 0) {
|
|
logger.info("Could not find any vote messages. Cancelling pollClose", { guildId, requestId })
|
|
return
|
|
}
|
|
|
|
const lastMessage: Message<true> = messages[0]
|
|
|
|
logger.debug(`Found messages: ${JSON.stringify(messages, null, 2)}`, { guildId, requestId })
|
|
|
|
logger.debug(`Last message: ${JSON.stringify(lastMessage, null, 2)}`, { guildId, requestId })
|
|
|
|
|
|
const votes = await (await getVotesByEmote(lastMessage, guildId, requestId))
|
|
.sort((a, b) => b.count - a.count)
|
|
|
|
logger.debug(`votes: ${JSON.stringify(votes, null, 2)}`, { guildId, requestId })
|
|
|
|
logger.info("Deleting vote message")
|
|
await lastMessage.delete()
|
|
const event = await getEvent(guild, guild.id, requestId)
|
|
if (event) {
|
|
updateEvent(event, votes, guild, guildId, requestId)
|
|
sendVoteClosedMessage(event, votes[0].movie, guildId, requestId)
|
|
}
|
|
|
|
//lastMessage.unpin() //todo: uncomment when bot has permission to pin/unpin
|
|
}
|
|
|
|
async function sendVoteClosedMessage(event: GuildScheduledEvent, movie: string, guildId: string, requestId: string) {
|
|
const date = event.scheduledStartAt ? format(event.scheduledStartAt, "dd.MM") : "Fehler, event hatte kein Datum"
|
|
const time = event.scheduledStartAt ? format(event.scheduledStartAt, "HH:mm") : "Fehler, event hatte kein Datum"
|
|
const body = `[Abstimmung beendet] für https://discord.com/events/${event.guildId}/${event.id}\n<@&${config.bot.announcement_role}> Wir gucken ${movie} am ${date} um ${time}`
|
|
const options: MessageCreateOptions = {
|
|
content: body,
|
|
allowedMentions: { parse: ["roles"] }
|
|
}
|
|
const announcementChannel = client.getAnnouncementChannelForGuild(guildId)
|
|
logger.info("Sending vote closed message.", { guildId, requestId })
|
|
if (!announcementChannel) {
|
|
logger.error("Could not find announcement channel. Please fix!", { guildId, requestId })
|
|
return
|
|
}
|
|
announcementChannel.send(options)
|
|
}
|
|
|
|
async function updateEvent(voteEvent: GuildScheduledEvent, votes: Vote[], guild: Guild, guildId: string, requestId: string) {
|
|
logger.info(`Updating event with movie ${votes[0].movie}.`, { guildId, requestId })
|
|
const options: GuildScheduledEventEditOptions<GuildScheduledEventStatus.Scheduled, GuildScheduledEventSetStatusArg<GuildScheduledEventStatus.Scheduled>> = {
|
|
name: votes[0].movie,
|
|
description: `!wp\nNummer 2: ${votes[1].movie} mit ${votes[1].count - 1} Stimmen\nNummer 3: ${votes[2].movie} mit ${votes[2].count - 1} Stimmen`
|
|
}
|
|
logger.debug(`Updating event: ${JSON.stringify(voteEvent, null, 2)}`, { guildId, requestId })
|
|
logger.info("Updating event.", { guildId, requestId })
|
|
voteEvent.edit(options)
|
|
}
|
|
|
|
async function getEvent(guild: Guild, guildId: string, requestId: string): Promise<GuildScheduledEvent | null> {
|
|
const voteEvents = (await guild.scheduledEvents.fetch())
|
|
.map((value) => value)
|
|
.filter(event => event.name.toLowerCase().includes("voting offen"))
|
|
logger.debug(`Found events: ${JSON.stringify(voteEvents, null, 2)}`, { guildId, requestId })
|
|
|
|
if (!voteEvents || voteEvents.length <= 0) {
|
|
logger.error("Could not find vote event. Cancelling update!", { guildId, requestId })
|
|
return null
|
|
}
|
|
return voteEvents[0]
|
|
}
|
|
|
|
type Vote = {
|
|
emote: string, //todo habs nicht hinbekommen hier Emotes zu nutzen
|
|
count: number,
|
|
movie: string
|
|
}
|
|
|
|
async function getVotesByEmote(message: Message, guildId: string, requestId: string): Promise<Vote[]> {
|
|
const votes: Vote[] = []
|
|
logger.debug(`Number of items in emotes: ${Object.values(Emotes).length}`, { guildId, requestId })
|
|
for (let i = 0; i < Object.keys(Emotes).length / 2; i++) {
|
|
const emote = Emotes[i]
|
|
logger.debug(`Getting reaction for emote ${emote}`, { guildId, requestId })
|
|
const reaction = await message.reactions.resolve(emote)
|
|
logger.debug(`Reaction for emote ${emote}: ${JSON.stringify(reaction, null, 2)}`, { guildId, requestId })
|
|
if (reaction) {
|
|
const vote: Vote = { emote: emote, count: reaction.count, movie: extractMovieFromMessageByEmote(message, emote) }
|
|
votes.push(vote)
|
|
}
|
|
}
|
|
return votes
|
|
}
|
|
|
|
function extractMovieFromMessageByEmote(message: Message, emote: string): string {
|
|
const lines = message.cleanContent.split("\n")
|
|
const emoteLines = lines.filter(line => line.includes(emote))
|
|
|
|
if (!emoteLines) {
|
|
return ""
|
|
}
|
|
const movie = emoteLines[0].substring(emoteLines[0].indexOf(emote) + emote.length + 2) // plus colon and space
|
|
|
|
return movie
|
|
}
|
|
|
|
export async function checkForPollsToClose(guild: Guild): Promise<void> {
|
|
const requestId = uuid()
|
|
logger.info(`Automatic check for poll closing.`, { guildId: guild.id, requestId })
|
|
const events = (await guild.scheduledEvents.fetch()).filter(event => event.name.toLocaleLowerCase().includes("voting offen")).map(event => event)
|
|
if (events.length > 1) {
|
|
logger.error("Handling more than one Event is not implemented yet. Found more than one poll to close")
|
|
return
|
|
} else if (events.length == 0) {
|
|
logger.info("Could not find any events. Cancelling", { guildId: guild.id, requestId })
|
|
}
|
|
|
|
const updatedEvent = events[0] //add two hours because of different timezones in discord api and Date.now()
|
|
if (!updatedEvent.scheduledStartTimestamp) {
|
|
logger.error("Event does not have a scheduled start time. Cancelling", { guildId: guild.id, requestId })
|
|
return
|
|
}
|
|
|
|
const createDate: Date = toDate(updatedEvent.createdTimestamp)
|
|
const eventDate: Date = toDate(updatedEvent.scheduledStartTimestamp)
|
|
const difference: number = differenceInDays(createDate, eventDate)
|
|
|
|
if (difference <= 2) {
|
|
logger.info("Less than two days between event create and event start. Not closing poll.", { guildId: guild.id, requestId })
|
|
return
|
|
}
|
|
|
|
const closePollDate: Date = addDays(eventDate, -2)
|
|
|
|
if (isAfter(Date.now(), closePollDate)) {
|
|
logger.info("Less than two days until event. Closing poll", { guildId: guild.id, requestId })
|
|
closePoll(guild, requestId)
|
|
} else {
|
|
logger.info(`ScheduledStart: ${closePollDate}. Now: ${toDate(Date.now())}`, { guildId: guild.id, requestId })
|
|
}
|
|
} |