feat/40-reroll-on-disinterest #54
@ -1,14 +1,8 @@
|
||||
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 { Maybe } from '../interfaces'
|
||||
import { logger } from '../logger'
|
||||
import { Command } from '../structures/command'
|
||||
import { RunOptions } from '../types/commandTypes'
|
||||
import { messageIsVoteEndedMessage, messageIsVoteMessage } from '../helper/messageIdentifiers'
|
||||
import { Emotes } from '../constants'
|
||||
|
||||
export default new Command({
|
||||
name: 'closepoll',
|
||||
@ -26,160 +20,6 @@ export default new Command({
|
||||
logger.info("Got command for closing poll!", { guildId, requestId })
|
||||
|
||||
command.followUp("Alles klar, beende die Umfrage :)")
|
||||
closePoll(command.guild, requestId)
|
||||
client.voteController.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 => !messageIsVoteEndedMessage(message) && messageIsVoteMessage(message))
|
||||
.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 })
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,8 @@
|
||||
import { GuildScheduledEvent, Message, MessageCreateOptions, TextChannel } from "discord.js";
|
||||
import { GuildScheduledEvent, TextChannel } from "discord.js";
|
||||
import { v4 as uuid } from "uuid";
|
||||
import { client, yavinJellyfinHandler } from "../..";
|
||||
import { config } from "../configuration";
|
||||
import { createDateStringFromEvent } from "../helper/dateHelper";
|
||||
import { Maybe } from "../interfaces";
|
||||
import { logger } from "../logger";
|
||||
import { Emotes, NONE_OF_THAT } from "../constants";
|
||||
|
||||
|
||||
export const name = 'guildScheduledEventCreate'
|
||||
@ -33,26 +30,9 @@ export async function execute(event: GuildScheduledEvent) {
|
||||
logger.info("EVENT DOES NOT HAVE STARTDATE; CANCELLING", { guildId: event.guildId, requestId })
|
||||
return
|
||||
}
|
||||
let message = `[Abstimmung] für https://discord.com/events/${event.guildId}/${event.id}\n<@&${config.bot.announcement_role}> Es gibt eine neue Abstimmung für die nächste Watchparty ${createDateStringFromEvent(event, event.guildId, requestId)}! Stimme hierunter für den nächsten Film ab!\n`
|
||||
const sentMessage = await client.voteController.createVoteMessage(event, announcementChannel, movies, event.guild?.id ?? "", requestId)
|
||||
magnetotail marked this conversation as resolved
Outdated
|
||||
|
||||
for (let i = 0; i < movies.length; i++) {
|
||||
message = message.concat(Emotes[i]).concat(": ").concat(movies[i]).concat("\n")
|
||||
}
|
||||
message = message.concat(NONE_OF_THAT).concat(": Wenn dir nichts davon gefällt.")
|
||||
|
||||
const options: MessageCreateOptions = {
|
||||
allowedMentions: { parse: ["roles"] },
|
||||
content: message,
|
||||
}
|
||||
|
||||
const sentMessage: Message<true> = await (await announcementChannel.fetch()).send(options)
|
||||
|
||||
for (let i = 0; i < movies.length; i++) {
|
||||
sentMessage.react(Emotes[i])
|
||||
}
|
||||
sentMessage.react(NONE_OF_THAT)
|
||||
|
||||
// sentMessage.pin() //todo: uncomment when bot has permission to pin messages. Also update closepoll.ts to only fetch pinned messages
|
||||
sentMessage.pin() //todo: uncomment when bot has permission to pin messages. Also update closepoll.ts to only fetch pinned messages
|
||||
}
|
||||
magnetotail marked this conversation as resolved
Outdated
magnetotail
commented
Das pinnen sollte glaube ich auch nicht das event übernehmen sondern der Controller Das pinnen sollte glaube ich auch nicht das event übernehmen sondern der Controller
magnetotail
commented
Evtl halt über nen Parameter bestimmen. Auf lange Sicht gehört das eigentliche verschicken der Message eh in einen eigenen controller aber der voteController kann die Daten für eine voteMessage entgegennehmen die aufbereiten und dann über den messageController die eigentliche message schicken lassen Evtl halt über nen Parameter bestimmen. Auf lange Sicht gehört das eigentliche verschicken der Message eh in einen eigenen controller aber der voteController kann die Daten für eine voteMessage entgegennehmen die aufbereiten und dann über den messageController die eigentliche message schicken lassen
kenobi
commented
4600820889d046972bb264912f0ad929c8950dac
kenobi
commented
gelöst durch das Handling in #54 (comment) gelöst durch das Handling in https://gitea.brudi.xyz/kenobi/jellyfin-discord-bot/pulls/54#issuecomment-526
Es gibt noch ein Auftreten vom 'separaten' Pinnen (vote.controller.ts:114). Dort ist ein bisschen mehr refactoring nötig um die Input Paramter zu füllen.
Dieser Kommentar sollte allerdings fertig behandelt sein.
|
||||
}
|
||||
|
||||
|
@ -1,9 +1,9 @@
|
||||
|
||||
kenobi marked this conversation as resolved
magnetotail
commented
Rename file to something like "handleVoteReaction" to make it separate from future reactionadd handlers Rename file to something like "handleVoteReaction" to make it separate from future reactionadd handlers
kenobi
commented
Agreement to use handleMessageReactionAdd as a 'reaction pre processor' to hand off reaction emoji to other handling methods and keep the eventHandler itself generic. Agreement to use handleMessageReactionAdd as a 'reaction pre processor' to hand off reaction emoji to other handling methods and keep the eventHandler itself generic.
|
||||
import { Message, MessageReaction, User } from "discord.js";
|
||||
import { logger, newRequestId, noGuildId } from "../logger";
|
||||
import { NONE_OF_THAT } from "../constants";
|
||||
import { Emoji, Emotes, NONE_OF_THAT } from "../constants";
|
||||
import { client } from "../..";
|
||||
import { messageIsVoteMessage } from "../helper/messageIdentifiers";
|
||||
import { isInitialAnnouncement, isVoteMessage } from "../helper/messageIdentifiers";
|
||||
|
||||
|
||||
export const name = 'messageReactionAdd'
|
||||
@ -25,13 +25,25 @@ export async function execute(messageReaction: MessageReaction, user: User) {
|
||||
//logger.debug(`reactedUponMessage payload: ${JSON.stringify(reactedUponMessage)}`)
|
||||
|
||||
logger.info(`emoji: ${messageReaction.emoji.toString()}`)
|
||||
if (messageReaction.emoji.toString() === NONE_OF_THAT) {
|
||||
if (messageIsVoteMessage(reactedUponMessage)) {
|
||||
logger.info(`Reaction is NONE_OF_THAT on a vote message. Handling`, { requestId, guildId })
|
||||
return client.VoteController.handleNoneOfThatVote(messageReaction, user, reactedUponMessage, requestId, guildId)
|
||||
|
||||
if (!Object.values(Emotes).includes(messageReaction.emoji.toString())) {
|
||||
logger.info(`${messageReaction.emoji.toString()} currently not handled`)
|
||||
return
|
||||
}
|
||||
logger.info(`Found a match for ${messageReaction.emoji.toString()}`)
|
||||
if (isVoteMessage(reactedUponMessage)) {
|
||||
if (messageReaction.emoji.toString() === NONE_OF_THAT) {
|
||||
logger.info(`Reaction is NONE_OF_THAT on a vote message. Handling`, { requestId, guildId })
|
||||
return client.voteController.handleNoneOfThatVote(messageReaction, user, reactedUponMessage, requestId, guildId)
|
||||
}
|
||||
if (messageReaction.emoji.toString() === Emoji.one) {
|
||||
// do something
|
||||
magnetotail marked this conversation as resolved
magnetotail
commented
? ?
magnetotail
commented
Replace this do something with a meaningful todo Replace this do something with a meaningful todo
kenobi
commented
removed removed
03b6a30ffa67afca9a4bc0563f7bf092bbcdd60b
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
else if (isInitialAnnouncement(reactedUponMessage)) {
|
||||
if (messageReaction.emoji.toString() === Emoji.ticket) {
|
||||
logger.error(`Got a role emoji. Not implemented yet. ${reactedUponMessage.id}`)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@ -1,19 +1,35 @@
|
||||
import { Message, MessageReaction, User } from "discord.js"
|
||||
import { Guild, GuildScheduledEvent, GuildScheduledEventEditOptions, GuildScheduledEventSetStatusArg, GuildScheduledEventStatus, Message, MessageCreateOptions, MessageReaction, TextChannel, User } from "discord.js"
|
||||
import { client } from "../.."
|
||||
import { NONE_OF_THAT } from "../constants"
|
||||
import { logger } from "../logger"
|
||||
import { Emotes, NONE_OF_THAT } from "../constants"
|
||||
import { logger, newRequestId } from "../logger"
|
||||
import { getMembersWithRoleFromGuild } from "./roleFilter"
|
||||
import { config } from "../configuration"
|
||||
import { VoteMessage, isVoteEndedMessage, isVoteMessage } from "./messageIdentifiers"
|
||||
import { createDateStringFromEvent } from "./dateHelper"
|
||||
import { Maybe } from "../interfaces"
|
||||
import format from "date-fns/format"
|
||||
import toDate from "date-fns/toDate"
|
||||
import differenceInDays from "date-fns/differenceInDays"
|
||||
import addDays from "date-fns/addDays"
|
||||
kenobi marked this conversation as resolved
Outdated
magnetotail
commented
Move check to handleMessageReactionAdd Move check to handleMessageReactionAdd
kenobi
commented
moved moved
|
||||
import isAfter from "date-fns/isAfter"
|
||||
|
||||
kenobi marked this conversation as resolved
Outdated
magnetotail
commented
duplicate check. already checked before call in handleMessageReactionAdd duplicate check. already checked before call in handleMessageReactionAdd
kenobi
commented
removed removed
|
||||
export type Vote = {
|
||||
emote: string, //todo habs nicht hinbekommen hier Emotes zu nutzen
|
||||
count: number,
|
||||
movie: string
|
||||
}
|
||||
export default class VoteController {
|
||||
|
||||
public async handleNoneOfThatVote(messageReaction: MessageReaction, user: User, reactedUponMessage: Message, requestId: string, guildId: string) {
|
||||
public async handleNoneOfThatVote(messageReaction: MessageReaction, user: User, reactedUponMessage: VoteMessage, requestId: string, guildId: string) {
|
||||
if (!messageReaction.message.guild) return 'No guild'
|
||||
logger.debug(`${reactedUponMessage.id} is vote message`, { requestId, guildId })
|
||||
|
||||
const watcherRoleMember = await getMembersWithRoleFromGuild(config.bot.announcement_role, messageReaction.message.guild)
|
||||
logger.info("ROLE MEMBERS " + JSON.stringify(watcherRoleMember), { requestId, guildId })
|
||||
|
||||
const watcherRoleMemberCount = watcherRoleMember.size
|
||||
logger.info(`MEMBER COUNT: ${watcherRoleMemberCount}`, { requestId, guildId })
|
||||
|
||||
const noneOfThatReactions = messageReaction.message.reactions.cache.get(NONE_OF_THAT)?.users.cache.filter(x => x.id !== client.user?.id).size ?? 0
|
||||
|
||||
const memberThreshold = (watcherRoleMemberCount / 2)
|
||||
@ -21,7 +37,176 @@ export default class VoteController {
|
||||
if (noneOfThatReactions > memberThreshold) {
|
||||
logger.info('Starting poll reroll', { requestId, guildId })
|
||||
messageReaction.message.edit((messageReaction.message.content ?? "").concat('\nDiese Abstimmung muss wiederholt werden.'))
|
||||
|
||||
}
|
||||
logger.info(`No reroll`, { requestId, guildId })
|
||||
}
|
||||
|
||||
|
||||
public async createVoteMessage(event: GuildScheduledEvent, announcementChannel: TextChannel, movies: string[], guildId: string, requestId: string): Promise<Message<boolean>> {
|
||||
|
||||
let message = `[Abstimmung] für https://discord.com/events/${event.guildId}/${event.id}\n<@&${config.bot.announcement_role}> Es gibt eine neue Abstimmung für die nächste Watchparty ${createDateStringFromEvent(event, event.guildId, requestId)}! Stimme hierunter für den nächsten Film ab!\n`
|
||||
|
||||
for (let i = 0; i < movies.length; i++) {
|
||||
message = message.concat(Emotes[i]).concat(": ").concat(movies[i]).concat("\n")
|
||||
}
|
||||
message = message.concat(NONE_OF_THAT).concat(": Wenn dir nichts davon gefällt.")
|
||||
|
||||
const options: MessageCreateOptions = {
|
||||
allowedMentions: { parse: ["roles"] },
|
||||
content: message,
|
||||
}
|
||||
|
||||
const sentMessage: Message<true> = await (await announcementChannel.fetch()).send(options)
|
||||
magnetotail marked this conversation as resolved
Outdated
magnetotail
commented
Parameter die eine Message sind heißen oft "message", manchmal aber auch "msg". sollte vereinheitlicht sein Parameter die eine Message sind heißen oft "message", manchmal aber auch "msg". sollte vereinheitlicht sein
kenobi
commented
66507cb08fa50ba3a7be28388c55b21227fb2261
kenobi
commented
Jetzt sollten alle Occurences beseitigt sein. fc64728a780f99b56aebff7f0a7c5d24a901d90d
Jetzt sollten alle Occurences beseitigt sein.
|
||||
|
||||
for (let i = 0; i < movies.length; i++) {
|
||||
sentMessage.react(Emotes[i])
|
||||
}
|
||||
sentMessage.react(NONE_OF_THAT)
|
||||
|
||||
magnetotail marked this conversation as resolved
Outdated
magnetotail
commented
above WHAT threshold? What does it do?? above WHAT threshold? What does it do??
kenobi
commented
20da25f2bf9a473704f8b4660e5f05183679ba39
kenobi
commented
Mit einem Kommentar versehen und entsprechend deines vorschlags umbenannt Mit einem Kommentar versehen und entsprechend deines vorschlags umbenannt
|
||||
return sentMessage
|
||||
magnetotail marked this conversation as resolved
Outdated
magnetotail
commented
threshold seems to be a magic number threshold seems to be a magic number
magnetotail
commented
Or rename method to "hasAtLeastOneVote" Or rename method to "hasAtLeastOneVote"
kenobi
commented
296a490e935cbdb79b70d73d2df9bc12a5774c53
kenobi
commented
done done
|
||||
}
|
||||
|
||||
public async closePoll(guild: Guild, requestId: string) {
|
||||
const guildId = guild.id
|
||||
logger.info("stopping poll", { guildId, requestId })
|
||||
magnetotail marked this conversation as resolved
Outdated
magnetotail
commented
comment is misleading, voteinfo is also used to get the eventid and eventdate in line 93 comment is misleading, voteinfo is also used to get the eventid and eventdate in line 93
kenobi
commented
119343c916b023a926e534575ae803cdec5b9594
|
||||
|
||||
const announcementChannel: Maybe<TextChannel> = client.getAnnouncementChannelForGuild(guildId)
|
||||
if (!announcementChannel) {
|
||||
logger.error("Could not find the textchannel. Unable to close poll.", { guildId, requestId })
|
||||
magnetotail marked this conversation as resolved
Outdated
magnetotail
commented
maybe extract this if-else to a method to keep code more compact maybe extract this if-else to a method to keep code more compact
kenobi
commented
7d794a8001a66d068f949c893d689a068c3caeed
done
|
||||
return
|
||||
}
|
||||
|
||||
const messages: Message<true>[] = (await announcementChannel.messages.fetch()) //todo: fetch only pinned messages
|
||||
.map((value) => value)
|
||||
.filter(message => !isVoteEndedMessage(message) && isVoteMessage(message))
|
||||
.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 })
|
||||
|
||||
magnetotail marked this conversation as resolved
Outdated
magnetotail
commented
is not a message, only messagetext is not a message, only messagetext
kenobi
commented
a455fd8ff7e6b8ffb032fb4aed9389da68ee513b
kenobi
commented
done done
|
||||
logger.debug(`Last message: ${JSON.stringify(lastMessage, null, 2)}`, { guildId, requestId })
|
||||
|
||||
|
||||
const votes = (await this.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 this.getEvent(guild, guild.id, requestId)
|
||||
if (event) {
|
||||
this.updateEvent(event, votes, guild, guildId, requestId)
|
||||
this.sendVoteClosedMessage(event, votes[0].movie, guildId, requestId)
|
||||
}
|
||||
magnetotail marked this conversation as resolved
Outdated
magnetotail
commented
why not pin message in method above? why not pin message in method above?
kenobi
commented
7d794a8001a66d068f949c893d689a068c3caeed
should have been moved to prepareAndSendVoteMessage()
|
||||
|
||||
lastMessage.unpin() //todo: uncomment when bot has permission to pin/unpin
|
||||
|
||||
}
|
||||
public async 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 = 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: this.extractMovieFromMessageByEmote(message, emote) }
|
||||
votes.push(vote)
|
||||
}
|
||||
}
|
||||
return votes
|
||||
}
|
||||
public async 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]
|
||||
}
|
||||
public async 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)
|
||||
}
|
||||
public async sendVoteClosedMessage(event: GuildScheduledEvent, movie: string, guildId: string, requestId: string) {
|
||||
kenobi marked this conversation as resolved
Outdated
magnetotail
commented
wtf :D wtf :D
kenobi
commented
it's magic ✨ it's magic ✨
|
||||
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)
|
||||
}
|
||||
private 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
|
||||
|
||||
magnetotail marked this conversation as resolved
Outdated
magnetotail
commented
rename message to messageText rename message to messageText
kenobi
commented
done done
ca99987a20baeceda27cb5e206bff42a54f31b04
|
||||
return movie
|
||||
}
|
||||
public async checkForPollsToClose(guild: Guild): Promise<void> {
|
||||
const requestId = newRequestId()
|
||||
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 })
|
||||
this.closePoll(guild, requestId)
|
||||
} else {
|
||||
logger.info(`ScheduledStart: ${closePollDate}. Now: ${toDate(Date.now())}`, { guildId: guild.id, requestId })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,6 @@ import { Maybe } from "../interfaces";
|
||||
import { JellyfinHandler } from "../jellyfin/handler";
|
||||
import { logger } from "../logger";
|
||||
import { CommandType } from "../types/commandTypes";
|
||||
import { checkForPollsToClose } from "../commands/closepoll";
|
||||
import { isInitialAnnouncement } from "../helper/messageIdentifiers";
|
||||
import VoteController from "../helper/vote.controller";
|
||||
|
||||
@ -18,7 +17,7 @@ export class ExtendedClient extends Client {
|
||||
private eventFilePath = `${__dirname}/../events`
|
||||
private commandFilePath = `${__dirname}/../commands`
|
||||
private jellyfin: JellyfinHandler
|
||||
public VoteController: VoteController = new VoteController()
|
||||
public voteController: VoteController = new VoteController()
|
||||
public commands: Collection<string, CommandType> = new Collection()
|
||||
private announcementChannels: Collection<string, TextChannel> = new Collection() //guildId to TextChannel
|
||||
private announcementRoleHandlerTask: Collection<string, ScheduledTask> = new Collection() //one task per guild
|
||||
@ -194,7 +193,7 @@ export class ExtendedClient extends Client {
|
||||
|
||||
private async startPollCloseBackgroundTasks() {
|
||||
for (const guild of this.guilds.cache) {
|
||||
this.pollCloseBackgroundTasks.set(guild[1].id, schedule("0 * * * * *", () => checkForPollsToClose(guild[1])))
|
||||
this.pollCloseBackgroundTasks.set(guild[1].id, schedule("0 * * * * *", () => this.voteController.checkForPollsToClose(guild[1])))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Warum erst eine message erstellen lassen die dann vom gleichen controller in der nächsten Zeile verschickt wird? Fände besser dem Controller zu sagen "Schick ne Vote message, hier sind die Infos die du brauchst"
True. Habe das handling in den vote controller verlegt.
Die Input Parameter sind dadurch allerdings lang genug geworden, dass ich ein separates Interface dafür erstellt habe.
4600820889