13 Commits

Author SHA1 Message Date
fec0bc31f1 1.1.4
All checks were successful
Build a docker image for node-jellyfin-role-bot / build-docker-image (push) Successful in 56s
2023-11-19 20:25:32 +01:00
1bfcaa95f9 Merge pull request 'feat/40-reroll-on-disinterest' (#54) from feat/40-reroll-on-disinterest into master
Reviewed-on: #54
2023-11-19 20:24:35 +01:00
fb4ab59dc6 rename emotes to validvoteemotes
All checks were successful
Run unit tests / test (pull_request) Successful in 14s
Compile the repository / compile (pull_request) Successful in 16s
2023-11-19 20:22:14 +01:00
6d40930dc1 fix incorrect log regarding update cancellation, fixes return type of function to use Maybe
All checks were successful
Compile the repository / compile (pull_request) Successful in 17s
Run unit tests / test (pull_request) Successful in 14s
2023-11-19 20:17:51 +01:00
4e9fe587b0 rename to getOpenPollEvent
All checks were successful
Compile the repository / compile (pull_request) Successful in 17s
Run unit tests / test (pull_request) Successful in 13s
2023-11-19 20:13:49 +01:00
03b6a30ffa remove unnecessary if
All checks were successful
Compile the repository / compile (pull_request) Successful in 16s
Run unit tests / test (pull_request) Successful in 18s
2023-11-19 20:11:03 +01:00
7d794a8001 refactor voteInfo to include event instead of eventid and startDate
All checks were successful
Run unit tests / test (pull_request) Successful in 16s
Compile the repository / compile (pull_request) Successful in 18s
2023-11-19 20:04:30 +01:00
8df180898e pad logging level to always be 5 characters 2023-11-19 20:04:06 +01:00
976175242b reorder close poll and use message identifier
All checks were successful
Compile the repository / compile (pull_request) Successful in 28s
Run unit tests / test (pull_request) Successful in 16s
2023-11-19 18:56:39 +01:00
68546b0b50 adjust message identifier in test 2023-11-19 18:56:08 +01:00
1348abbd48 make message identifiers actually work properly with LSP 2023-11-19 18:55:51 +01:00
fce9091114 rename message type union to better reflect its intention 2023-11-19 18:24:33 +01:00
081f3c6201 fix incorrect branded type 2023-11-19 18:24:13 +01:00
10 changed files with 114 additions and 104 deletions

4
package-lock.json generated
View File

@ -1,12 +1,12 @@
{ {
"name": "node-jellyfin-discord-bot", "name": "node-jellyfin-discord-bot",
"version": "1.1.3", "version": "1.1.4",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "node-jellyfin-discord-bot", "name": "node-jellyfin-discord-bot",
"version": "1.1.3", "version": "1.1.4",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@discordjs/rest": "^1.7.0", "@discordjs/rest": "^1.7.0",

View File

@ -1,6 +1,6 @@
{ {
"name": "node-jellyfin-discord-bot", "name": "node-jellyfin-discord-bot",
"version": "1.1.3", "version": "1.1.4",
"description": "A discord bot to sync jellyfin accounts with discord roles", "description": "A discord bot to sync jellyfin accounts with discord roles",
"main": "index.js", "main": "index.js",
"license": "MIT", "license": "MIT",

View File

@ -1,6 +1,7 @@
export enum Emotes { "1⃣", "2⃣", "3⃣", "4⃣", "5⃣", "6⃣", "7⃣", "8⃣", "9⃣", "🔟" } export enum ValidVoteEmotes { "1⃣", "2⃣", "3⃣", "4⃣", "5⃣", "6⃣", "7⃣", "8⃣", "9⃣", "🔟" }
export const NONE_OF_THAT = "❌" export const NONE_OF_THAT = "❌"
// WIP
export const Emoji = { export const Emoji = {
"one": "\u0031\uFE0F\u20E3", "one": "\u0031\uFE0F\u20E3",
"two": "\u0032\uFE0F\u20E3", "two": "\u0032\uFE0F\u20E3",

View File

@ -1,7 +1,7 @@
import { Message, MessageReaction, User } from "discord.js"; import { Message, MessageReaction, User } from "discord.js";
import { logger, newRequestId, noGuildId } from "../logger"; import { logger, newRequestId, noGuildId } from "../logger";
import { Emoji, Emotes, NONE_OF_THAT } from "../constants"; import { Emoji, ValidVoteEmotes, NONE_OF_THAT } from "../constants";
import { client } from "../.."; import { client } from "../..";
import { isInitialAnnouncement, isVoteMessage } from "../helper/messageIdentifiers"; import { isInitialAnnouncement, isVoteMessage } from "../helper/messageIdentifiers";
@ -26,7 +26,7 @@ export async function execute(messageReaction: MessageReaction, user: User) {
logger.info(`emoji: ${messageReaction.emoji.toString()}`) logger.info(`emoji: ${messageReaction.emoji.toString()}`)
if (!Object.values(Emotes).includes(messageReaction.emoji.toString()) && messageReaction.emoji.toString() !== NONE_OF_THAT) { if (!Object.values(ValidVoteEmotes).includes(messageReaction.emoji.toString()) && messageReaction.emoji.toString() !== NONE_OF_THAT) {
logger.info(`${messageReaction.emoji.toString()} currently not handled`) logger.info(`${messageReaction.emoji.toString()} currently not handled`)
return return
} }
@ -36,9 +36,6 @@ export async function execute(messageReaction: MessageReaction, user: User) {
logger.info(`Reaction is NONE_OF_THAT on a vote message. Handling`, { requestId, guildId }) logger.info(`Reaction is NONE_OF_THAT on a vote message. Handling`, { requestId, guildId })
return client.voteController.handleNoneOfThatVote(messageReaction, reactedUponMessage, requestId, guildId) return client.voteController.handleNoneOfThatVote(messageReaction, reactedUponMessage, requestId, guildId)
} }
if (messageReaction.emoji.toString() === Emoji.one) {
// do something
}
} }
else if (isInitialAnnouncement(reactedUponMessage)) { else if (isInitialAnnouncement(reactedUponMessage)) {
if (messageReaction.emoji.toString() === Emoji.ticket) { if (messageReaction.emoji.toString() === Emoji.ticket) {

View File

@ -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"`

View File

@ -2,11 +2,11 @@ import { Message } from "discord.js";
// branded types to differentiate objects of identical Type but different contents // branded types to differentiate objects of identical Type but different contents
export type VoteEndMessage = Message & { readonly __brand: 'vote' } export type VoteEndMessage = Message<true> & { readonly __brand: 'voteend' }
export type AnnouncementMessage = Message & { readonly __brand: 'announcement' } export type AnnouncementMessage = Message<true> & { readonly __brand: 'announcement' }
export type VoteMessage = Message & { readonly __brand: 'voteend' } export type VoteMessage = Message<true> & { readonly __brand: 'vote' }
export type DiscordMessage = VoteMessage | VoteEndMessage | AnnouncementMessage export type KnownDiscordMessage = VoteMessage | VoteEndMessage | AnnouncementMessage
export function isVoteMessage(message: Message): message is VoteMessage { export function isVoteMessage(message: Message): message is VoteMessage {
return message.cleanContent.includes('[Abstimmung]') return message.cleanContent.includes('[Abstimmung]')

View File

@ -1,5 +1,5 @@
import { Guild, GuildScheduledEvent, GuildScheduledEventEditOptions, GuildScheduledEventSetStatusArg, GuildScheduledEventStatus, Message, MessageCreateOptions, MessageReaction, TextChannel } from "discord.js" import { Guild, GuildScheduledEvent, GuildScheduledEventEditOptions, GuildScheduledEventSetStatusArg, GuildScheduledEventStatus, Message, MessageCreateOptions, MessageReaction, TextChannel } from "discord.js"
import { Emotes, NONE_OF_THAT } from "../constants" import { ValidVoteEmotes, NONE_OF_THAT } from "../constants"
import { logger, newRequestId } from "../logger" import { logger, newRequestId } from "../logger"
import { getMembersWithRoleFromGuild } from "./roleFilter" import { getMembersWithRoleFromGuild } from "./roleFilter"
import { config } from "../configuration" import { config } from "../configuration"
@ -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
logger.info(`Creating new poll message with new movies: ${movies}`, { requestId, guildId }) public async handleReroll(voteMessage: VoteMessage, guildId: string, requestId: string) {
const messageText = this.createVoteMessageText(voteInfo.eventId, voteInfo.eventDate, movies, guildId, requestId) // 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)
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,24 +176,25 @@ 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(ValidVoteEmotes[i]).concat(": ").concat(movies[i]).concat("\n")
} }
message = message.concat(NONE_OF_THAT).concat(": Wenn dir nichts davon gefällt.") message = message.concat(NONE_OF_THAT).concat(": Wenn dir nichts davon gefällt.")
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 = {
@ -196,7 +205,7 @@ export default class VoteController {
const sentMessage: Message<true> = await (await announcementChannel.fetch()).send(options) const sentMessage: Message<true> = await (await announcementChannel.fetch()).send(options)
for (let i = 0; i < movieCount; i++) { for (let i = 0; i < movieCount; i++) {
sentMessage.react(Emotes[i]) sentMessage.react(ValidVoteEmotes[i])
} }
sentMessage.react(NONE_OF_THAT) sentMessage.react(NONE_OF_THAT)
@ -225,33 +234,35 @@ export default class VoteController {
const lastMessage: Message<true> = messages[0] const lastMessage: Message<true> = messages[0]
if (!isVoteMessage(lastMessage)) {
logger.error(`Found message that is not a vote message, can't proceed`, { guildId, requestId })
logger.debug(`Found messages: ${JSON.stringify(messages, null, 2)}`, { guildId, requestId }) logger.debug(`Found messages: ${JSON.stringify(messages, null, 2)}`, { guildId, requestId })
logger.debug(`Last message: ${JSON.stringify(lastMessage, null, 2)}`, { guildId, requestId }) logger.debug(`Last message: ${JSON.stringify(lastMessage, null, 2)}`, { guildId, requestId })
}
else {
const votes = (await this.getVotesByEmote(lastMessage, guildId, requestId)) const votes = (await this.getVotesByEmote(lastMessage, guildId, requestId))
.sort((a, b) => b.count - a.count) .sort((a, b) => b.count - a.count)
logger.debug(`votes: ${JSON.stringify(votes, null, 2)}`, { guildId, requestId }) logger.debug(`votes: ${JSON.stringify(votes, null, 2)}`, { guildId, requestId })
logger.info("Deleting vote message") logger.info("Deleting vote message")
lastMessage.unpin()
await lastMessage.delete() await lastMessage.delete()
const event = await this.getOpenEvent(guild, guild.id, requestId) const event = await this.getOpenPollEvent(guild, guild.id, requestId)
if (event && votes?.length > 0) { if (event && votes?.length > 0) {
this.updateOpenPollEventWithVoteResults(event, votes, guild, guildId, requestId) this.updateOpenPollEventWithVoteResults(event, votes, guild, guildId, requestId)
this.sendVoteClosedMessage(event, votes[0].movie, guildId, requestId) this.sendVoteClosedMessage(event, votes[0].movie, guildId, requestId)
} }
lastMessage.unpin() }
} }
/** /**
* gets votes for the movies without the NONE_OF_THAT votes * gets votes for the movies without the NONE_OF_THAT votes
*/ */
public async getVotesByEmote(message: Message, guildId: string, requestId: string): Promise<Vote[]> { public async getVotesByEmote(message: VoteMessage, guildId: string, requestId: string): Promise<Vote[]> {
const votes: Vote[] = [] const votes: Vote[] = []
logger.debug(`Number of items in emotes: ${Object.values(Emotes).length}`, { guildId, requestId }) logger.debug(`Number of items in emotes: ${Object.values(ValidVoteEmotes).length}`, { guildId, requestId })
for (let i = 0; i < Object.keys(Emotes).length / 2; i++) { for (let i = 0; i < Object.keys(ValidVoteEmotes).length / 2; i++) {
const emote = Emotes[i] const emote = ValidVoteEmotes[i]
logger.debug(`Getting reaction for emote ${emote}`, { guildId, requestId }) logger.debug(`Getting reaction for emote ${emote}`, { guildId, requestId })
const reaction = message.reactions.resolve(emote) const reaction = message.reactions.resolve(emote)
logger.debug(`Reaction for emote ${emote}: ${JSON.stringify(reaction, null, 2)}`, { guildId, requestId }) logger.debug(`Reaction for emote ${emote}: ${JSON.stringify(reaction, null, 2)}`, { guildId, requestId })
@ -262,15 +273,15 @@ export default class VoteController {
} }
return votes return votes
} }
public async getOpenEvent(guild: Guild, guildId: string, requestId: string): Promise<GuildScheduledEvent | null> { public async getOpenPollEvent(guild: Guild, guildId: string, requestId: string): Promise<Maybe<GuildScheduledEvent>> {
const voteEvents = (await guild.scheduledEvents.fetch()) const voteEvents = (await guild.scheduledEvents.fetch())
.map((value) => value) .map((value) => value)
.filter(event => event.name.toLowerCase().includes("voting offen")) .filter(event => event.name.toLowerCase().includes("voting offen"))
logger.debug(`Found events: ${JSON.stringify(voteEvents, null, 2)}`, { guildId, requestId }) logger.debug(`Found events: ${JSON.stringify(voteEvents, null, 2)}`, { guildId, requestId })
if (!voteEvents || voteEvents.length <= 0) { if (!voteEvents || voteEvents.length <= 0) {
logger.error("Could not find vote event. Cancelling update!", { guildId, requestId }) logger.error("Could not find an open vote event.", { guildId, requestId })
return null return
} }
return voteEvents[0] return voteEvents[0]
} }

View File

@ -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(

View File

@ -3,6 +3,7 @@ import VoteController 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 { Emoji, NONE_OF_THAT } from "../../server/constants" import { Emoji, NONE_OF_THAT } from "../../server/constants"
import { isVoteMessage } from "../../server/helper/messageIdentifiers"
describe('vote controller - none_of_that functions', () => { describe('vote controller - none_of_that functions', () => {
const testEventId = '1234321' const testEventId = '1234321'
@ -28,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({
@ -56,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>{
@ -89,8 +72,10 @@ describe('vote controller - none_of_that functions', () => {
}) })
} }
} }
if (isVoteMessage(mockMessage)) {
const result = await votes.getVotesByEmote(mockMessage, 'guildId', 'requestId') const result = await votes.getVotesByEmote(mockMessage, 'guildId', 'requestId')
expect(result.length).toEqual(5) expect(result.length).toEqual(5)
expect(result).toEqual(votesList.filter(x => x.movie != NONE_OF_THAT)) expect(result).toEqual(votesList.filter(x => x.movie != NONE_OF_THAT))
}
}) })
}) })

View File

@ -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: {