15 Commits

Author SHA1 Message Date
c1a449bafe 1.1.2
All checks were successful
Build a docker image for node-jellyfin-role-bot / build-docker-image (push) Successful in 1m50s
2023-06-23 19:46:20 +02:00
d5d82043f0 temporarily remove second tag on docker build 2023-06-23 19:46:06 +02:00
51ebf2e939 1.1.1
All checks were successful
Build a docker image for node-jellyfin-role-bot / build-docker-image (push) Successful in 12s
2023-06-23 19:44:58 +02:00
f314b2f355 maybe fix a docker build typo 2023-06-23 19:44:44 +02:00
a4d7c57d10 1.1.0
All checks were successful
Build a docker image for node-jellyfin-role-bot / build-docker-image (push) Successful in 14s
2023-06-23 19:33:31 +02:00
2802afa7d5 Merge pull request 'feat/#42_announce_manual_watchparty' (#50) from feat/#42_announce_manual_watchparty into master
Reviewed-on: #50
2023-06-23 19:31:57 +02:00
3a5ea5d4ff improve message clarity when no start date in event found
All checks were successful
Compile the repository / compile (pull_request) Successful in 4m8s
2023-06-23 19:30:17 +02:00
45d87275bf prevent announcement when description contains !private
All checks were successful
Compile the repository / compile (pull_request) Successful in 1m43s
2023-06-23 15:56:36 +02:00
31e440434e properly clean up wp announcements without event only 2023-06-23 15:51:48 +02:00
3d70b56eb7 Merge pull request 'feat/#43_append_event_invite' (#49) from feat/#43_append_event_invite into master
Reviewed-on: #49
2023-06-23 15:49:59 +02:00
3298c7a244 Merge pull request 'Create option "none of that" in voting' (#48) from feat/#39_none_of_that into master
Reviewed-on: #48
2023-06-23 15:49:43 +02:00
5b98c9bf2f Rename event files to specific case
We can add multiple eventhandlers per eventname. To avoid confusion and large files and to improve concise file names the event files were renamed
2023-06-23 14:37:41 +02:00
9da8f47784 add event box to vote closed message
All checks were successful
Compile the repository / compile (pull_request) Successful in 1m18s
2023-06-22 19:38:59 +02:00
e8c58d5ff8 add event box to create message 2023-06-22 19:35:47 +02:00
8569a3e1e6 Create option "none of that" in voting
All checks were successful
Compile the repository / compile (pull_request) Successful in 1m19s
2023-06-22 18:53:29 +02:00
11 changed files with 132 additions and 22 deletions

View File

@ -22,6 +22,6 @@ jobs:
- name: Log in to the Container registry - name: Log in to the Container registry
run: docker login -u ${{ env.USER }} -p ${{ secrets.TOKEN }} ${{ env.REGISTRY }} run: docker login -u ${{ env.USER }} -p ${{ secrets.TOKEN }} ${{ env.REGISTRY }}
- name: Build Container - name: Build Container
run: docker build -t "${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest" -t "${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ node -p "require('./package.json').version" }}". run: docker build -t "${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest" .
- name: Push Container - name: Push Container
run: docker push --all-tags "${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}" run: docker push --all-tags "${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}"

4
package-lock.json generated
View File

@ -1,12 +1,12 @@
{ {
"name": "node-jellyfin-discord-bot", "name": "node-jellyfin-discord-bot",
"version": "1.0.4", "version": "1.1.2",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "node-jellyfin-discord-bot", "name": "node-jellyfin-discord-bot",
"version": "1.0.4", "version": "1.1.2",
"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.0.4", "version": "1.1.2",
"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

@ -3,7 +3,7 @@ import { Guild, GuildScheduledEvent, GuildScheduledEventEditOptions, GuildSchedu
import { v4 as uuid } from 'uuid' import { v4 as uuid } from 'uuid'
import { client } from '../..' import { client } from '../..'
import { config } from '../configuration' import { config } from '../configuration'
import { Emotes } from '../events/guildScheduledEventCreate' import { Emotes } from '../events/autoCreateVoteByWPEvent'
import { Maybe } from '../interfaces' import { Maybe } from '../interfaces'
import { logger } from '../logger' import { logger } from '../logger'
import { Command } from '../structures/command' import { Command } from '../structures/command'
@ -75,7 +75,7 @@ export async function closePoll(guild: Guild, requestId: string) {
async function sendVoteClosedMessage(event: GuildScheduledEvent, movie: string, guildId: string, requestId: string) { 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 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 time = event.scheduledStartAt ? format(event.scheduledStartAt, "HH:mm") : "Fehler, event hatte kein Datum"
const body = `[Abstimmung beendet] <@&${config.bot.announcement_role}> Wir gucken ${movie} am ${date} um ${time}` 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 = { const options: MessageCreateOptions = {
content: body, content: body,
allowedMentions: { parse: ["roles"] } allowedMentions: { parse: ["roles"] }

View File

@ -0,0 +1,48 @@
import { GuildScheduledEvent, TextChannel } from "discord.js";
import { v4 as uuid } from "uuid";
import { client } from "../..";
import { config } from "../configuration";
import { createDateStringFromEvent } from "../helper/dateHelper";
import { Maybe } from "../interfaces";
import { logger } from "../logger";
export const name = 'guildScheduledEventCreate'
export async function execute(event: GuildScheduledEvent) {
const guildId = event.guildId
const requestId = uuid()
try {
if (!event.description) {
logger.debug("Got GuildScheduledEventCreate event. But has no description. Aborting.")
return
}
if (event.description.includes("!wp")) {
logger.info("Got manual create event of watchparty event!", { guildId, requestId })
if(event.description.includes("!private")) {
logger.info("Event description contains \"!private\". Won't announce.", { guildId, requestId })
return
}
const channel: Maybe<TextChannel> = client.getAnnouncementChannelForGuild(guildId)
if (!channel) {
logger.error("Could not obtain announcement channel. Aborting announcement.", { guildId, requestId })
return
}
const message = `[Watchparty] https://discord.com/events/${event.guildId}/${event.id} \nHey <@&${config.bot.announcement_role}>, wir gucken ${event.name} ${createDateStringFromEvent(event, guildId, requestId)}`
channel.send(message)
} else {
logger.debug("Got GuildScheduledEventCreate event but no !wp in description. Not creating manual wp announcement.", { guildId, requestId })
}
} catch (error) {
// sendFailureDM(error)
logger.error(<string>error, { guildId, requestId })
}
}

View File

@ -1,9 +1,9 @@
import { format } from "date-fns";
import { GuildScheduledEvent, Message, MessageCreateOptions, TextChannel } from "discord.js"; import { GuildScheduledEvent, Message, MessageCreateOptions, TextChannel } from "discord.js";
import { ScheduledTask } from "node-cron"; import { ScheduledTask } from "node-cron";
import { v4 as uuid } from "uuid"; import { v4 as uuid } from "uuid";
import { client, yavinJellyfinHandler } from "../.."; import { client, yavinJellyfinHandler } from "../..";
import { config } from "../configuration"; import { config } from "../configuration";
import { createDateStringFromEvent } from "../helper/dateHelper";
import { Maybe } from "../interfaces"; import { Maybe } from "../interfaces";
import { logger } from "../logger"; import { logger } from "../logger";
@ -11,12 +11,12 @@ import { logger } from "../logger";
export const name = 'guildScheduledEventCreate' export const name = 'guildScheduledEventCreate'
export enum Emotes { "1⃣", "2⃣", "3⃣", "4⃣", "5⃣", "6⃣", "7⃣", "8⃣", "9⃣", "🔟" } export enum Emotes { "1⃣", "2⃣", "3⃣", "4⃣", "5⃣", "6⃣", "7⃣", "8⃣", "9⃣", "🔟" }
export const NONE_OF_THAT = "❌"
export let task: ScheduledTask | undefined export let task: ScheduledTask | undefined
export async function execute(event: GuildScheduledEvent) { export async function execute(event: GuildScheduledEvent) {
const requestId = uuid() const requestId = uuid()
logger.debug(`New event created: ${JSON.stringify(event, null, 2)}`, { guildId: event.guildId, requestId })
if (event.name.toLowerCase().includes("!nextwp")) { if (event.name.toLowerCase().includes("!nextwp")) {
logger.info("Event was a placeholder event to start a new watchparty and voting. Creating vote!", { guildId: event.guildId, requestId }) logger.info("Event was a placeholder event to start a new watchparty and voting. Creating vote!", { guildId: event.guildId, requestId })
@ -38,17 +38,16 @@ export async function execute(event: GuildScheduledEvent) {
logger.info("EVENT DOES NOT HAVE STARTDATE; CANCELLING", {guildId: event.guildId, requestId}) logger.info("EVENT DOES NOT HAVE STARTDATE; CANCELLING", {guildId: event.guildId, requestId})
return return
} }
const date = format(event.scheduledStartAt, "dd.MM") 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 time = format(event.scheduledStartAt, "HH:mm")
let message = `[Abstimmung]\n<@&${config.bot.announcement_role}> Es gibt eine neue Abstimmung für die nächste Watchparty am ${date} um ${time}}! 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")
} }
message = message.concat(NONE_OF_THAT).concat(": Wenn dir nichts davon gefällt.")
const options: MessageCreateOptions = { const options: MessageCreateOptions = {
allowedMentions: { parse: ["roles"]}, allowedMentions: { parse: ["roles"]},
content: message content: message,
} }
const sentMessage: Message<true> = await (await announcementChannel.fetch()).send(options) const sentMessage: Message<true> = await (await announcementChannel.fetch()).send(options)
@ -56,6 +55,7 @@ export async function execute(event: GuildScheduledEvent) {
for (let i = 0; i < movies.length; i++) { for (let i = 0; i < movies.length; i++) {
sentMessage.react(Emotes[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
} }

View File

@ -0,0 +1,52 @@
import { Collection, GuildScheduledEvent, GuildScheduledEventStatus, Message } from "discord.js";
import { v4 as uuid } from "uuid";
import { client } from "../..";
import { logger } from "../logger";
export const name = 'guildScheduledEventUpdate'
export async function execute(oldEvent: GuildScheduledEvent, newEvent: GuildScheduledEvent) {
const requestId = uuid()
try {
if (!newEvent.guild) {
logger.error("Event has no guild, aborting.", { guildId: newEvent.guildId, requestId })
return
}
const guildId = newEvent.guildId
if (newEvent.description?.toLowerCase().includes("!wp") && newEvent.status === GuildScheduledEventStatus.Completed) {
logger.info("A watchparty ended. Cleaning up announcements!", { guildId, requestId })
const announcementChannel = client.getAnnouncementChannelForGuild(newEvent.guild.id)
if (!announcementChannel) {
logger.error("Could not find announcement channel. Aborting", { guildId: newEvent.guild.id, requestId })
return
}
const events = await newEvent.guild.scheduledEvents.fetch()
const wpAnnouncements = (await announcementChannel.messages.fetch()).filter(message => !message.cleanContent.includes("[initial]"))
const announcementsWithoutEvent = filterAnnouncementsByPendingWPs(wpAnnouncements, events)
logger.info(`Deleting ${announcementsWithoutEvent.length} announcements.`, {guildId, requestId})
announcementsWithoutEvent.forEach(message => message.delete())
}
} catch (error) {
logger.error(<string>error, { guildId: newEvent.guildId, requestId })
}
}
function filterAnnouncementsByPendingWPs(messages: Collection<string, Message<true>>, events: Collection<string, GuildScheduledEvent<GuildScheduledEventStatus>>): Message<true>[] {
const filteredMessages: Message<true>[] = []
for (const message of messages.values()) {
let foundEventForMessage = false
for (const event of events.values()) {
if (message.cleanContent.includes(event.id)) { //announcement always has eventid because of eventbox
foundEventForMessage = true
}
}
if(!foundEventForMessage){
filteredMessages.push(message)
}
}
return filteredMessages
}

View File

@ -1,6 +1,6 @@
import { GuildMember, GuildScheduledEvent, GuildScheduledEventStatus } from "discord.js"; import { GuildMember, GuildScheduledEvent, GuildScheduledEventStatus } from "discord.js";
import { v4 as uuid } from "uuid"; import { v4 as uuid } from "uuid";
import { client, jellyfinHandler } from "../.."; import { jellyfinHandler } from "../..";
import { getGuildSpecificTriggerRoleId } from "../helper/roleFilter"; import { getGuildSpecificTriggerRoleId } from "../helper/roleFilter";
import { logger } from "../logger"; import { logger } from "../logger";
@ -10,7 +10,7 @@ export const name = 'guildScheduledEventUpdate'
export async function execute(oldEvent: GuildScheduledEvent, newEvent: GuildScheduledEvent) { export async function execute(oldEvent: GuildScheduledEvent, newEvent: GuildScheduledEvent) {
try { try {
const requestId = uuid() const requestId = uuid()
logger.debug(`Got scheduledEvent update. New Event: ${JSON.stringify(newEvent, null, 2)}`, { guildId: newEvent.guildId, requestId }) // logger.debug(`Got scheduledEvent update. New Event: ${JSON.stringify(newEvent, null, 2)}`, { guildId: newEvent.guildId, requestId })
if (!newEvent.guild) { if (!newEvent.guild) {
logger.error("Event has no guild, aborting.", { guildId: newEvent.guildId, requestId }) logger.error("Event has no guild, aborting.", { guildId: newEvent.guildId, requestId })
return return
@ -32,13 +32,7 @@ export async function execute(oldEvent: GuildScheduledEvent, newEvent: GuildSche
if (newEvent.status === GuildScheduledEventStatus.Active) if (newEvent.status === GuildScheduledEventStatus.Active)
createJFUsers(members, newEvent.name, requestId) createJFUsers(members, newEvent.name, requestId)
else { else {
const announcementChannel = await client.getAnnouncementChannelForGuild(newEvent.guild.id)
if(!announcementChannel) {
logger.error("Could not find announcement channel. Aborting", { guildId: newEvent.guild.id, requestId })
return
}
const announcements = (await announcementChannel.messages.fetch()).filter(message => !message.pinned)
announcements.forEach(message => message.delete())
members.forEach(member => { members.forEach(member => {
member.createDM().then(channel => channel.send(`Die Watchparty ist vorbei, dein Account wurde wieder gelöscht. Wenn du einen permanenten Account haben möchtest, melde dich bei Samantha oder Marukus.`)) member.createDM().then(channel => channel.send(`Die Watchparty ist vorbei, dein Account wurde wieder gelöscht. Wenn du einen permanenten Account haben möchtest, melde dich bei Samantha oder Marukus.`))
}) })

View File

@ -0,0 +1,16 @@
import { format } from "date-fns";
import { GuildScheduledEvent } from "discord.js";
import { logger } from "../logger";
export function createDateStringFromEvent(event: GuildScheduledEvent, requestId: string, guildId?: string): string {
if(!event.scheduledStartAt) {
logger.error("Event has no start. Cannot create dateString.", {guildId, requestId})
return `"habe keinen Startzeitpunkt ermitteln können"`
}
const date = format(event.scheduledStartAt, "dd.MM")
const time = format(event.scheduledStartAt, "HH:mm")
return `am ${date} um ${time}`
}