Compare commits
2 Commits
976175242b
...
7d794a8001
Author | SHA1 | Date | |
---|---|---|---|
7d794a8001 | |||
8df180898e |
@ -1,10 +1,10 @@
|
|||||||
import { format, isToday } from "date-fns";
|
import { format, isToday } from "date-fns";
|
||||||
import { utcToZonedTime } from "date-fns-tz"
|
import { utcToZonedTime } from "date-fns-tz"
|
||||||
import { GuildScheduledEvent } from "discord.js";
|
|
||||||
import { logger } from "../logger";
|
import { logger } from "../logger";
|
||||||
import de from "date-fns/locale/de";
|
import de from "date-fns/locale/de";
|
||||||
|
import { Maybe } from "../interfaces";
|
||||||
|
|
||||||
export function createDateStringFromEvent(eventStartDate:Date, requestId: string, guildId?: string): string {
|
export function createDateStringFromEvent(eventStartDate: Maybe<Date>, requestId: string, guildId?: string): string {
|
||||||
if (!eventStartDate) {
|
if (!eventStartDate) {
|
||||||
logger.error("Event has no start. Cannot create dateString.", { guildId, requestId })
|
logger.error("Event has no start. Cannot create dateString.", { guildId, requestId })
|
||||||
return `"habe keinen Startzeitpunkt ermitteln können"`
|
return `"habe keinen Startzeitpunkt ermitteln können"`
|
||||||
|
@ -21,8 +21,7 @@ export type Vote = {
|
|||||||
}
|
}
|
||||||
export type VoteMessageInfo = {
|
export type VoteMessageInfo = {
|
||||||
votes: Vote[],
|
votes: Vote[],
|
||||||
eventId: string,
|
event: GuildScheduledEvent,
|
||||||
eventDate: Date
|
|
||||||
}
|
}
|
||||||
export default class VoteController {
|
export default class VoteController {
|
||||||
private client: ExtendedClient
|
private client: ExtendedClient
|
||||||
@ -74,11 +73,8 @@ export default class VoteController {
|
|||||||
logger.debug(`${vote.movie} : ${vote.count} -> above: ${overOneVote}`)
|
logger.debug(`${vote.movie} : ${vote.count} -> above: ${overOneVote}`)
|
||||||
return overOneVote
|
return overOneVote
|
||||||
}
|
}
|
||||||
public async handleReroll(voteMessage: VoteMessage, guildId: string, requestId: string) {
|
|
||||||
// get the movies currently being voted on, their votes, the eventId and its date
|
|
||||||
const voteInfo: VoteMessageInfo = await this.parseVoteInfoFromVoteMessage(voteMessage, requestId)
|
|
||||||
|
|
||||||
let movies: string[] = Array()
|
public async generateRerollMovieList(voteInfo: VoteMessageInfo, guildId: string, requestId: string) {
|
||||||
if (config.bot.reroll_retains_top_picks) {
|
if (config.bot.reroll_retains_top_picks) {
|
||||||
const votedOnMovies = voteInfo.votes.filter(this.hasAtLeastOneVote).filter(x => x.emote !== NONE_OF_THAT)
|
const votedOnMovies = voteInfo.votes.filter(this.hasAtLeastOneVote).filter(x => x.emote !== NONE_OF_THAT)
|
||||||
logger.info(`Found ${votedOnMovies.length} with votes`, { requestId, guildId })
|
logger.info(`Found ${votedOnMovies.length} with votes`, { requestId, guildId })
|
||||||
@ -86,40 +82,53 @@ export default class VoteController {
|
|||||||
logger.info(`Fetching ${newMovieCount} from jellyfin`)
|
logger.info(`Fetching ${newMovieCount} from jellyfin`)
|
||||||
const newMovies: string[] = await this.yavinJellyfinHandler.getRandomMovieNames(newMovieCount, guildId, requestId)
|
const newMovies: string[] = await this.yavinJellyfinHandler.getRandomMovieNames(newMovieCount, guildId, requestId)
|
||||||
// merge
|
// merge
|
||||||
movies = newMovies.concat(votedOnMovies.map(x => x.movie))
|
return newMovies.concat(votedOnMovies.map(x => x.movie))
|
||||||
} else {
|
} else {
|
||||||
// get movies from jellyfin to fill the remaining slots
|
// get movies from jellyfin to fill the remaining slots
|
||||||
const newMovieCount: number = config.bot.random_movie_count
|
const newMovieCount: number = config.bot.random_movie_count
|
||||||
logger.info(`Fetching ${newMovieCount} from jellyfin`)
|
logger.info(`Fetching ${newMovieCount} from jellyfin`)
|
||||||
movies = await this.yavinJellyfinHandler.getRandomMovieNames(newMovieCount, guildId, requestId)
|
return await this.yavinJellyfinHandler.getRandomMovieNames(newMovieCount, guildId, requestId)
|
||||||
}
|
}
|
||||||
// create new message
|
}
|
||||||
|
|
||||||
|
public async handleReroll(voteMessage: VoteMessage, guildId: string, requestId: string) {
|
||||||
|
// get the movies currently being voted on, their votes, the eventId and its date
|
||||||
|
const voteInfo: VoteMessageInfo = await this.parseVoteInfoFromVoteMessage(voteMessage, requestId)
|
||||||
|
if (!voteInfo.event.scheduledStartAt) {
|
||||||
|
logger.info("Event does not have a start date, cancelling", { guildId: voteInfo.event.guildId, requestId })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let movies: string[] = await this.generateRerollMovieList(voteInfo, guildId, requestId)
|
||||||
|
|
||||||
logger.info(`Creating new poll message with new movies: ${movies}`, { requestId, guildId })
|
|
||||||
const messageText = this.createVoteMessageText(voteInfo.eventId, voteInfo.eventDate, movies, guildId, requestId)
|
|
||||||
const announcementChannel = this.client.getAnnouncementChannelForGuild(guildId)
|
const announcementChannel = this.client.getAnnouncementChannelForGuild(guildId)
|
||||||
if (!announcementChannel) {
|
if (!announcementChannel) {
|
||||||
logger.error(`No announcementChannel found for ${guildId}, can't post poll`)
|
logger.error(`No announcementChannel found for ${guildId}, can't post poll`)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
logger.info(`Trying to remove old vote Message`, { requestId, guildId })
|
logger.info(`Trying to remove old vote Message`, { requestId, guildId })
|
||||||
this.removeMessage(voteMessage)
|
this.removeMessage(voteMessage)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
// TODO: integrate failure DM to media Admin to inform about inability to delete old message
|
||||||
logger.error(`Error during removeMessage: ${err}`)
|
logger.error(`Error during removeMessage: ${err}`)
|
||||||
}
|
}
|
||||||
|
const sentMessage = this.prepareAndSendVoteMessage({
|
||||||
const sentMessage = await this.sendVoteMessage(messageText, movies.length, announcementChannel)
|
event: voteInfo.event,
|
||||||
sentMessage.pin()
|
movies,
|
||||||
logger.info(`Sent and pinned new poll message`, { requestId, guildId })
|
announcementChannel,
|
||||||
|
startDate: voteInfo.event.scheduledStartAt,
|
||||||
|
pinAfterSending: true
|
||||||
|
},
|
||||||
|
guildId,
|
||||||
|
requestId)
|
||||||
|
logger.debug(`Sent reroll message: ${JSON.stringify(sentMessage)}`, { requestId, guildId })
|
||||||
}
|
}
|
||||||
|
|
||||||
private async fetchEventStartDateByEventId(guild: Guild, eventId: string, requestId: string): Promise<Maybe<Date>> {
|
private async fetchEventByEventId(guild: Guild, eventId: string, requestId: string): Promise<Maybe<GuildScheduledEvent>> {
|
||||||
const guildEvent: GuildScheduledEvent = await guild.scheduledEvents.fetch(eventId)
|
const guildEvent: GuildScheduledEvent = await guild.scheduledEvents.fetch(eventId)
|
||||||
if (!guildEvent) logger.error(`GuildScheduledEvent with id${eventId} could not be found`, { requestId, guildId: guild.id })
|
if (!guildEvent) logger.error(`GuildScheduledEvent with id${eventId} could not be found`, { requestId, guildId: guild.id })
|
||||||
if (guildEvent.scheduledStartAt)
|
return guildEvent
|
||||||
return guildEvent.scheduledStartAt
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async parseVoteInfoFromVoteMessage(message: VoteMessage, requestId: string): Promise<VoteMessageInfo> {
|
public async parseVoteInfoFromVoteMessage(message: VoteMessage, requestId: string): Promise<VoteMessageInfo> {
|
||||||
@ -129,8 +138,7 @@ export default class VoteController {
|
|||||||
if (!message.guild)
|
if (!message.guild)
|
||||||
throw new Error(`Message ${message.id} not a guild message`)
|
throw new Error(`Message ${message.id} not a guild message`)
|
||||||
|
|
||||||
let eventStartDate: Maybe<Date> = await this.fetchEventStartDateByEventId(message.guild, parsedIds.eventId, requestId)
|
const event: Maybe<GuildScheduledEvent> = await this.fetchEventByEventId(message.guild, parsedIds.eventId, requestId)
|
||||||
if (!eventStartDate) eventStartDate = this.parseEventDateFromMessage(message.cleanContent, message.guild.id, requestId)
|
|
||||||
|
|
||||||
let votes: Vote[] = []
|
let votes: Vote[] = []
|
||||||
for (const line of lines) {
|
for (const line of lines) {
|
||||||
@ -149,7 +157,7 @@ export default class VoteController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return <VoteMessageInfo>{ eventId: parsedIds.eventId, eventDate: eventStartDate, votes }
|
return <VoteMessageInfo>{ event, votes }
|
||||||
}
|
}
|
||||||
public parseEventDateFromMessage(message: string, guildId: string, requestId: string): Date {
|
public parseEventDateFromMessage(message: string, guildId: string, requestId: string): Date {
|
||||||
logger.warn(`Falling back to RegEx parsing to get Event Date`, { guildId, requestId })
|
logger.warn(`Falling back to RegEx parsing to get Event Date`, { guildId, requestId })
|
||||||
@ -168,15 +176,15 @@ export default class VoteController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async prepareAndSendVoteMessage(inputInfo: prepareVoteMessageInput, guildId: string, requestId: string) {
|
public async prepareAndSendVoteMessage(inputInfo: prepareVoteMessageInput, guildId: string, requestId: string) {
|
||||||
const messageText = this.createVoteMessageText(inputInfo.event.id, inputInfo.startDate, inputInfo.movies, guildId, requestId)
|
const messageText = this.createVoteMessageText(inputInfo.event, inputInfo.movies, guildId, requestId)
|
||||||
const sentMessage = await this.sendVoteMessage(messageText, inputInfo.movies.length, inputInfo.announcementChannel)
|
const sentMessage = await this.sendVoteMessage(messageText, inputInfo.movies.length, inputInfo.announcementChannel)
|
||||||
if (inputInfo.pinAfterSending)
|
if (inputInfo.pinAfterSending)
|
||||||
sentMessage.pin()
|
sentMessage.pin()
|
||||||
return sentMessage
|
return sentMessage
|
||||||
}
|
}
|
||||||
|
|
||||||
public createVoteMessageText(eventId: string, eventStartDate: Date, movies: string[], guildId: string, requestId: string): string {
|
public createVoteMessageText(event: GuildScheduledEvent, movies: string[], guildId: string, requestId: string): string {
|
||||||
let message = `[Abstimmung] für https://discord.com/events/${guildId}/${eventId} \n<@&${config.bot.announcement_role}> Es gibt eine neue Abstimmung für die nächste Watchparty ${createDateStringFromEvent(eventStartDate, guildId, requestId)}! Stimme hierunter für den nächsten Film ab!\n`
|
let message = `[Abstimmung] für https://discord.com/events/${guildId}/${event.id} \n<@&${config.bot.announcement_role}> Es gibt eine neue Abstimmung für die nächste Watchparty ${createDateStringFromEvent(event.scheduledStartAt, guildId, requestId)}! Stimme hierunter für den nächsten Film ab!\n`
|
||||||
|
|
||||||
for (let i = 0; i < movies.length; i++) {
|
for (let i = 0; i < movies.length; i++) {
|
||||||
message = message.concat(Emotes[i]).concat(": ").concat(movies[i]).concat("\n")
|
message = message.concat(Emotes[i]).concat(": ").concat(movies[i]).concat("\n")
|
||||||
@ -186,6 +194,7 @@ export default class VoteController {
|
|||||||
return message
|
return message
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Refactor into separate message controller
|
||||||
public async sendVoteMessage(messageText: string, movieCount: number, announcementChannel: TextChannel) {
|
public async sendVoteMessage(messageText: string, movieCount: number, announcementChannel: TextChannel) {
|
||||||
|
|
||||||
const options: MessageCreateOptions = {
|
const options: MessageCreateOptions = {
|
||||||
|
@ -6,7 +6,7 @@ export const noGuildId = 'NoGuildId'
|
|||||||
|
|
||||||
|
|
||||||
const printFn = format.printf(({ guildId, level, message, errorCode, requestId, timestamp: logTimestamp }: { [k: string]: string }) => {
|
const printFn = format.printf(({ guildId, level, message, errorCode, requestId, timestamp: logTimestamp }: { [k: string]: string }) => {
|
||||||
return `[${guildId ?? ''}][${level}][${logTimestamp}][${errorCode ?? ''}][${requestId ?? ''}]:${message}`
|
return `[${guildId ?? ''}][${level.padStart(5, " ")}][${logTimestamp}][${errorCode ?? ''}][${requestId ?? ''}]:${message}`
|
||||||
})
|
})
|
||||||
|
|
||||||
const logFormat = format.combine(
|
const logFormat = format.combine(
|
||||||
|
@ -29,11 +29,16 @@ describe('vote controller - none_of_that functions', () => {
|
|||||||
id: 'mockId'
|
id: 'mockId'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const mockEvent: GuildScheduledEvent = <GuildScheduledEvent><unknown>{
|
||||||
|
scheduledStartAt: testEventDate,
|
||||||
|
id: testEventId,
|
||||||
|
guild: testGuildId
|
||||||
|
}
|
||||||
const mockJellyfinHandler: JellyfinHandler = <JellyfinHandler><unknown>{
|
const mockJellyfinHandler: JellyfinHandler = <JellyfinHandler><unknown>{
|
||||||
getRandomMovieNames: jest.fn().mockReturnValue(["movie1"])
|
getRandomMovieNames: jest.fn().mockReturnValue(["movie1"])
|
||||||
}
|
}
|
||||||
const votes = new VoteController(mockClient, mockJellyfinHandler)
|
const votes = new VoteController(mockClient, mockJellyfinHandler)
|
||||||
const mockMessageContent = votes.createVoteMessageText(testEventId, testEventDate, testMovies, testGuildId, "requestId")
|
const mockMessageContent = votes.createVoteMessageText(mockEvent, testMovies, testGuildId, "requestId")
|
||||||
|
|
||||||
test('sendVoteClosedMessage', async () => {
|
test('sendVoteClosedMessage', async () => {
|
||||||
mockClient.getAnnouncementChannelForGuild = jest.fn().mockReturnValue({
|
mockClient.getAnnouncementChannelForGuild = jest.fn().mockReturnValue({
|
||||||
@ -57,29 +62,6 @@ describe('vote controller - none_of_that functions', () => {
|
|||||||
content: `[Abstimmung beendet] für https://discord.com/events/${testGuildId}/${testEventId}\n<@&WATCHPARTY_ANNOUNCEMENT_ROLE> Wir gucken MovieNew am 01.01. um 01:00`
|
content: `[Abstimmung beendet] für https://discord.com/events/${testGuildId}/${testEventId}\n<@&WATCHPARTY_ANNOUNCEMENT_ROLE> Wir gucken MovieNew am 01.01. um 01:00`
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
// test('checkForPollsToClose', async () => {
|
|
||||||
//
|
|
||||||
// const testGuild: Guild = <Guild><unknown>{
|
|
||||||
// scheduledEvents: {
|
|
||||||
// fetch: jest.fn().mockImplementation(() => {
|
|
||||||
// return new Promise(resolve => {
|
|
||||||
// resolve([
|
|
||||||
// { name: "Event Name" },
|
|
||||||
// { name: "Event: VOTING OFFEN", scheduledStartTimestamp: "" },
|
|
||||||
// { name: "another voting" },
|
|
||||||
// ]
|
|
||||||
// )
|
|
||||||
// })
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// const result = await votes.checkForPollsToClose(testGuild)
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// })
|
|
||||||
|
|
||||||
test('getVotesByEmote', async () => {
|
test('getVotesByEmote', async () => {
|
||||||
const mockMessage: Message = <Message><unknown>{
|
const mockMessage: Message = <Message><unknown>{
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import { Emoji, NONE_OF_THAT } from "../../server/constants"
|
import { Emoji, NONE_OF_THAT } from "../../server/constants"
|
||||||
import VoteController, { Vote, VoteMessageInfo } from "../../server/helper/vote.controller"
|
import VoteController, { VoteMessageInfo } from "../../server/helper/vote.controller"
|
||||||
import { JellyfinHandler } from "../../server/jellyfin/handler"
|
import { JellyfinHandler } from "../../server/jellyfin/handler"
|
||||||
import { ExtendedClient } from "../../server/structures/client"
|
import { ExtendedClient } from "../../server/structures/client"
|
||||||
import { VoteMessage } from "../../server/helper/messageIdentifiers"
|
import { VoteMessage } from "../../server/helper/messageIdentifiers"
|
||||||
import { Message, MessageReaction } from "discord.js"
|
import { GuildScheduledEvent, MessageReaction } from "discord.js"
|
||||||
test('parse votes from vote message', async () => {
|
test('parse votes from vote message', async () => {
|
||||||
const testMovies = [
|
const testMovies = [
|
||||||
'Movie1',
|
'Movie1',
|
||||||
@ -16,12 +16,16 @@ test('parse votes from vote message', async () => {
|
|||||||
const testEventDate = new Date('2023-01-01')
|
const testEventDate = new Date('2023-01-01')
|
||||||
const testGuildId = "888999888"
|
const testGuildId = "888999888"
|
||||||
const voteController: VoteController = new VoteController(<ExtendedClient>{}, <JellyfinHandler>{})
|
const voteController: VoteController = new VoteController(<ExtendedClient>{}, <JellyfinHandler>{})
|
||||||
const testMessage = voteController.createVoteMessageText(testEventId, testEventDate, testMovies, testGuildId, "requestId")
|
const mockEvent: GuildScheduledEvent = <GuildScheduledEvent><unknown>{
|
||||||
|
scheduledStartAt: testEventDate,
|
||||||
|
id: testEventId,
|
||||||
|
guild: testGuildId
|
||||||
|
}
|
||||||
|
const testMessage = voteController.createVoteMessageText(mockEvent, testMovies, testGuildId, "requestId")
|
||||||
|
|
||||||
|
|
||||||
const expectedResult: VoteMessageInfo = {
|
const expectedResult: VoteMessageInfo = {
|
||||||
eventId: testEventId,
|
event: mockEvent,
|
||||||
eventDate: testEventDate,
|
|
||||||
votes: [
|
votes: [
|
||||||
{ emote: Emoji.one, count: 1, movie: testMovies[0] },
|
{ emote: Emoji.one, count: 1, movie: testMovies[0] },
|
||||||
{ emote: Emoji.two, count: 2, movie: testMovies[1] },
|
{ emote: Emoji.two, count: 2, movie: testMovies[1] },
|
||||||
@ -40,6 +44,8 @@ test('parse votes from vote message', async () => {
|
|||||||
fetch: jest.fn().mockImplementation((input: any) => {
|
fetch: jest.fn().mockImplementation((input: any) => {
|
||||||
if (input === testEventId)
|
if (input === testEventId)
|
||||||
return {
|
return {
|
||||||
|
id: testEventId,
|
||||||
|
guild: testGuildId,
|
||||||
scheduledStartAt: testEventDate
|
scheduledStartAt: testEventDate
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -61,8 +67,8 @@ test('parse votes from vote message', async () => {
|
|||||||
const result = await voteController.parseVoteInfoFromVoteMessage(message, 'requestId')
|
const result = await voteController.parseVoteInfoFromVoteMessage(message, 'requestId')
|
||||||
console.log(JSON.stringify(result))
|
console.log(JSON.stringify(result))
|
||||||
expect(Array.isArray(result)).toBe(false)
|
expect(Array.isArray(result)).toBe(false)
|
||||||
expect(result.eventId).toEqual(testEventId)
|
expect(result.event.id).toEqual(testEventId)
|
||||||
expect(result.eventDate).toEqual(testEventDate)
|
expect(result.event.scheduledStartAt).toEqual(testEventDate)
|
||||||
expect(result.votes.length).toEqual(expectedResult.votes.length)
|
expect(result.votes.length).toEqual(expectedResult.votes.length)
|
||||||
expect(result).toEqual(expectedResult)
|
expect(result).toEqual(expectedResult)
|
||||||
})
|
})
|
||||||
@ -79,7 +85,12 @@ test('parse votes from vote message', () => {
|
|||||||
const testEventDate = new Date('2023-01-01')
|
const testEventDate = new Date('2023-01-01')
|
||||||
const testGuildId = "888999888"
|
const testGuildId = "888999888"
|
||||||
const voteController: VoteController = new VoteController(<ExtendedClient>{}, <JellyfinHandler>{})
|
const voteController: VoteController = new VoteController(<ExtendedClient>{}, <JellyfinHandler>{})
|
||||||
const testMessage = voteController.createVoteMessageText(testEventId, testEventDate, testMovies, testGuildId, "requestId")
|
const mockEvent: GuildScheduledEvent = <GuildScheduledEvent><unknown>{
|
||||||
|
scheduledStartAt: testEventDate,
|
||||||
|
id: testEventId,
|
||||||
|
guild: testGuildId
|
||||||
|
}
|
||||||
|
const testMessage = voteController.createVoteMessageText(mockEvent, testMovies, testGuildId, "requestId")
|
||||||
|
|
||||||
const result = voteController.parseGuildIdAndEventIdFromWholeMessage(testMessage)
|
const result = voteController.parseGuildIdAndEventIdFromWholeMessage(testMessage)
|
||||||
expect(result).toEqual({ guildId: testGuildId, eventId: testEventId })
|
expect(result).toEqual({ guildId: testGuildId, eventId: testEventId })
|
||||||
@ -108,7 +119,12 @@ test.skip('handles complete none_of_that vote', () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
const voteController = new VoteController(mockClient, mockJellyfinHandler)
|
const voteController = new VoteController(mockClient, mockJellyfinHandler)
|
||||||
const mockMessageContent = voteController.createVoteMessageText(testEventId, testEventDate, testMovies, testGuildId, "requestId")
|
const mockEvent: GuildScheduledEvent = <GuildScheduledEvent><unknown>{
|
||||||
|
scheduledStartAt: testEventDate,
|
||||||
|
id: testEventId,
|
||||||
|
guild: testGuildId
|
||||||
|
}
|
||||||
|
const mockMessageContent = voteController.createVoteMessageText(mockEvent, testMovies, testGuildId, "requestId")
|
||||||
const reactedUponMessage: VoteMessage = <VoteMessage><unknown>{
|
const reactedUponMessage: VoteMessage = <VoteMessage><unknown>{
|
||||||
cleanContent: mockMessageContent,
|
cleanContent: mockMessageContent,
|
||||||
guild: {
|
guild: {
|
||||||
|
Loading…
Reference in New Issue
Block a user