UNPKG

n8n-nodes-discord-dnd

Version:
870 lines (869 loc) 62.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ActionEventHandler = void 0; const discord_js_1 = require("discord.js"); const types_1 = require("../../Interfaces/types"); class ActionEventHandler { constructor(client, actionInstance) { this.client = client; this.actionInstance = actionInstance; } /** * Fetch all users who have RSVP'd (interested) in a guild scheduled event */ async fetchEventInterestedUsers(guildId, eventId) { var _a, _b; const botToken = this.client.token; if (!botToken) { throw new Error("Bot token is not available"); } const limit = 100; let before = undefined; const allUserIds = []; try { while (true) { const params = new URLSearchParams({ limit: String(limit), with_member: "false", // We only need user IDs }); if (before) { params.set("before", before); } const response = await fetch(`https://discord.com/api/v10/guilds/${guildId}/scheduled-events/${eventId}/users?${params.toString()}`, { headers: { Authorization: `Bot ${botToken}`, "Content-Type": "application/json", }, }); if (!response.ok) { const errorText = await response.text(); throw new Error(`Discord API error ${response.status}: ${errorText}`); } const chunk = await response.json(); // Extract user IDs from the response if (Array.isArray(chunk)) { const userIds = chunk .map((item) => { var _a; return (_a = item.user) === null || _a === void 0 ? void 0 : _a.id; }) .filter((id) => id !== undefined); allUserIds.push(...userIds); // If we got fewer results than the limit, we've reached the end if (chunk.length < limit) { break; } // Use the last user ID for pagination const lastUserId = (_b = (_a = chunk[chunk.length - 1]) === null || _a === void 0 ? void 0 : _a.user) === null || _b === void 0 ? void 0 : _b.id; if (!lastUserId) { break; } before = lastUserId; } else { break; } } } catch (error) { // Log error but don't fail the entire operation console.error(`Failed to fetch interested users: ${error.message}`); return []; } return allUserIds; } async setupEventHandler(action) { var _a; const data = {}; switch (action) { case types_1.ActionEventType.SEND_TYPING: const channelId = this.actionInstance.getNodeParameter("channelId", 0); const channel = (await this.client.channels.fetch(channelId)); if (channel === null || channel === void 0 ? void 0 : channel.isTextBased()) { await channel.sendTyping(); data.success = true; data.message = "Typing indicator sent successfully."; } else { throw new Error("The provided channel is not a text channel!"); } break; case types_1.ActionEventType.SEND_MESSAGE: // Get message content let messageContent = this.actionInstance.getNodeParameter("messageContent", 0, ""); let embeds = []; let files = []; // Process embeds const embedsCollection = this.actionInstance.getNodeParameter("embeds", 0, { embed: [] }); if (embedsCollection && embedsCollection.embed && embedsCollection.embed.length > 0) { embeds = embedsCollection.embed.map((embed) => { const inputMethod = embed.inputMethod || "fields"; // Handle JSON embeds if (inputMethod === "json") { try { return JSON.parse(embed.jsonPayload || "{}"); } catch (error) { throw new Error(`Invalid JSON in embed: ${error.message}`); } } // Handle field-based embeds const processedEmbed = {}; if (embed.description) processedEmbed.description = embed.description; if (embed.title) processedEmbed.title = embed.title; if (embed.url) processedEmbed.url = embed.url; if (embed.color) processedEmbed.color = embed.color; if (embed.timestamp) { processedEmbed.timestamp = new Date(embed.timestamp).toISOString(); } // Process thumbnail and image if (embed.thumbnailUrl) { processedEmbed.thumbnail = { url: embed.thumbnailUrl }; } if (embed.imageUrl) { processedEmbed.image = { url: embed.imageUrl }; } // Process author if (embed.authorName) { processedEmbed.author = { name: embed.authorName, }; } // Process video if (embed.videoUrl) { processedEmbed.video = { url: embed.videoUrl }; } return processedEmbed; }); // Filter out empty embeds embeds = embeds.filter(e => Object.keys(e).length > 0); } // Process file uploads if any const filesCollection = this.actionInstance.getNodeParameter("files", 0, { file: [] }); if (filesCollection.file && filesCollection.file.length > 0) { for (const fileData of filesCollection.file) { if (fileData.binaryProperty) { const binaryData = this.actionInstance.helpers.getBinaryDataBuffer(0, fileData.binaryProperty); files.push({ attachment: binaryData, name: fileData.fileName || "file", }); } } } // Check if we have anything to send if (!messageContent && embeds.length === 0 && files.length === 0) { throw new Error("Cannot send an empty message! Please provide at least one of: Message Content, Embeds, or Files."); } // Get options const options = this.actionInstance.getNodeParameter("options", 0, {}); // Get send to destination const sendTo = this.actionInstance.getNodeParameter("sendTo", 0, "channel"); let messageChannel; if (sendTo === "user") { const userId = this.actionInstance.getNodeParameter("userId", 0); try { const user = await this.client.users.fetch(userId); messageChannel = await user.createDM(); } catch (error) { throw new Error(`Failed to create DM channel: ${error.message}`); } } else { const channelId = this.actionInstance.getNodeParameter("channelId", 0); messageChannel = (await this.client.channels.fetch(channelId)); if (!(messageChannel === null || messageChannel === void 0 ? void 0 : messageChannel.isTextBased())) { throw new Error("The provided channel is not a text channel!"); } } const messageOptions = { content: messageContent || undefined, embeds: embeds.length > 0 ? embeds : undefined, files: files.length > 0 ? files : undefined, }; // Add flags if present if (options.flags && Array.isArray(options.flags)) { let flags = 0; const flagsArray = options.flags; if (flagsArray.includes("suppressEmbeds")) { flags |= 1 << 2; // SUPPRESS_EMBEDS = 1 << 2 } if (flagsArray.includes("suppressNotifications")) { flags |= 1 << 12; // SUPPRESS_NOTIFICATIONS = 1 << 12 } if (flags > 0) { messageOptions.flags = flags; } } // Add reply if present if (options.messageId) { messageOptions.reply = { messageReference: options.messageId }; } try { const message = await messageChannel.send(messageOptions); data.success = true; data.message = "Message sent successfully."; data.messageId = message.id; } catch (error) { console.error("Discord API Send Error:", error); throw new Error(`Failed to send message: ${error.message}`); } break; case types_1.ActionEventType.DELETE_MESSAGE: const deleteChannelId = this.actionInstance.getNodeParameter("channelId", 0); const messageId = this.actionInstance.getNodeParameter("messageId", 0); const deleteChannel = (await this.client.channels.fetch(deleteChannelId)); if (!(deleteChannel === null || deleteChannel === void 0 ? void 0 : deleteChannel.isTextBased())) { throw new Error("The provided channel is not a text channel!"); } try { const messageToDelete = await deleteChannel.messages.fetch(messageId); await messageToDelete.delete(); data.success = true; data.message = "Message deleted successfully."; } catch (error) { throw new Error(`Failed to delete message: ${error.message}`); } break; case types_1.ActionEventType.EDIT_MESSAGE: const editChannelId = this.actionInstance.getNodeParameter("channelId", 0); const editMessageId = this.actionInstance.getNodeParameter("messageId", 0); const newContent = this.actionInstance.getNodeParameter("newContent", 0); const editChannel = (await this.client.channels.fetch(editChannelId)); if (!(editChannel === null || editChannel === void 0 ? void 0 : editChannel.isTextBased())) { throw new Error("The provided channel is not a text channel!"); } try { const messageToEdit = await editChannel.messages.fetch(editMessageId); const editedMessage = await messageToEdit.edit(newContent); data.success = true; data.message = "Message edited successfully."; data.editedMessageId = editedMessage.id; } catch (error) { throw new Error(`Failed to edit message: ${error.message}`); } break; case types_1.ActionEventType.REACT_TO_MESSAGE: const reactChannelId = this.actionInstance.getNodeParameter("channelId", 0); const reactMessageId = this.actionInstance.getNodeParameter("messageId", 0); const emoji = this.actionInstance.getNodeParameter("emoji", 0); const reactChannel = (await this.client.channels.fetch(reactChannelId)); if (!(reactChannel === null || reactChannel === void 0 ? void 0 : reactChannel.isTextBased())) { throw new Error("The provided channel is not a text channel!"); } try { const messageToReact = await reactChannel.messages.fetch(reactMessageId); await messageToReact.react(emoji); data.success = true; data.message = "Reaction added successfully."; } catch (error) { throw new Error(`Failed to add reaction: ${error.message}`); } break; case types_1.ActionEventType.PIN_MESSAGE: const pinChannelId = this.actionInstance.getNodeParameter("channelId", 0); const pinMessageId = this.actionInstance.getNodeParameter("messageId", 0); const pinChannel = (await this.client.channels.fetch(pinChannelId)); if (!(pinChannel === null || pinChannel === void 0 ? void 0 : pinChannel.isTextBased())) { throw new Error("The provided channel is not a text channel!"); } try { const messageToPin = await pinChannel.messages.fetch(pinMessageId); await messageToPin.pin(); data.success = true; data.message = "Message pinned successfully."; } catch (error) { throw new Error(`Failed to pin message: ${error.message}`); } break; case types_1.ActionEventType.UNPIN_MESSAGE: const unpinChannelId = this.actionInstance.getNodeParameter("channelId", 0); const unpinMessageId = this.actionInstance.getNodeParameter("messageId", 0); const unpinChannel = (await this.client.channels.fetch(unpinChannelId)); if (!(unpinChannel === null || unpinChannel === void 0 ? void 0 : unpinChannel.isTextBased())) { throw new Error("The provided channel is not a text channel!"); } try { const messageToUnpin = await unpinChannel.messages.fetch(unpinMessageId); await messageToUnpin.unpin(); data.success = true; data.message = "Message unpinned successfully."; } catch (error) { throw new Error(`Failed to unpin message: ${error.message}`); } break; case types_1.ActionEventType.REMOVE_REACTION: const removeReactChannelId = this.actionInstance.getNodeParameter("channelId", 0); const removeReactMessageId = this.actionInstance.getNodeParameter("messageId", 0); const userId = this.actionInstance.getNodeParameter("userId", 0); const removeEmoji = this.actionInstance.getNodeParameter("emoji", 0); const all = this.actionInstance.getNodeParameter("all", 0); const removeReactChannel = (await this.client.channels.fetch(removeReactChannelId)); if (!(removeReactChannel === null || removeReactChannel === void 0 ? void 0 : removeReactChannel.isTextBased())) { throw new Error("The provided channel is not a text channel!"); } try { const messageToRemoveReact = await removeReactChannel.messages.fetch(removeReactMessageId); // If 'all' is true, remove all reactions from the message if (all) { // Remove all reactions from user if userId is provided and removeEmoji is not specified if (userId) { try { const reactions = messageToRemoveReact.reactions.cache.filter((reaction) => reaction.users.cache.has(userId)); for (const reaction of reactions.values()) { await reaction.users.remove(userId); } data.success = true; data.message = "All reactions from user removed successfully."; return data; } catch (error) { throw new Error(`Failed to remove reactions from user: ${error.message}`); } } // If removeEmoji is specified, remove all reactions of that emoji if (removeEmoji) { try { await ((_a = messageToRemoveReact.reactions.cache .get(removeEmoji)) === null || _a === void 0 ? void 0 : _a.remove()); data.success = true; data.message = `All reactions for emoji ${removeEmoji} removed successfully.`; return data; } catch (error) { throw new Error(`Failed to remove reactions for emoji ${removeEmoji}: ${error.message}`); } } // If no emoji is specified, remove all reactions // Remove all reactions from the message await messageToRemoveReact.reactions .removeAll() .catch((error) => { throw new Error(`Failed to remove all reactions: ${error.message}`); }); data.success = true; data.message = "All reactions removed successfully."; return data; } // Remove a specific reaction const reaction = messageToRemoveReact.reactions.cache.find((r) => r.emoji.name === removeEmoji || r.emoji.toString() === removeEmoji); if (reaction) { // If a user ID is provided, remove the reaction for that user if (userId) { await reaction.users.remove(userId).catch((error) => { throw new Error(`Failed to remove reaction from user: ${error.message}`); }); } else { // If no user ID is provided, remove the bot's reaction if (!this.client.user) throw new Error("Client user is not initialized"); await reaction.users .remove(this.client.user.id) .catch((error) => { throw new Error(`Failed to remove reaction from the bot: ${error.message}`); }); } data.success = true; data.message = "Reaction removed successfully."; } else { throw new Error("Reaction not found on the message."); } } catch (error) { throw new Error(`Failed to remove reaction: ${error.message}`); } break; case types_1.ActionEventType.GET_GUILD_SCHEDULED_EVENT: const getGuildScheduledEventId = this.actionInstance.getNodeParameter("guildScheduledEventId", 0); const getGuildId = this.actionInstance.getNodeParameter("guildId", 0); try { const guild = await this.client.guilds.fetch(getGuildId); const guildScheduledEvent = await guild.scheduledEvents.fetch(getGuildScheduledEventId); if (!guildScheduledEvent) { throw new Error("Guild scheduled event not found."); } // Get all information about the event data.id = guildScheduledEvent.id; data.guildId = guildScheduledEvent.guildId; data.channelId = guildScheduledEvent.channelId; data.creatorId = guildScheduledEvent.creatorId; data.name = guildScheduledEvent.name; data.description = guildScheduledEvent.description; data.scheduledStartTimestamp = guildScheduledEvent.scheduledStartTimestamp; data.scheduledEndTimestamp = guildScheduledEvent.scheduledEndTimestamp; data.privacyLevel = guildScheduledEvent.privacyLevel; data.status = guildScheduledEvent.status; data.entityType = guildScheduledEvent.entityType; data.entityId = guildScheduledEvent.entityId; data.entityMetadata = guildScheduledEvent.entityMetadata; data.userCount = guildScheduledEvent.userCount; data.image = guildScheduledEvent.image; data.createdTimestamp = guildScheduledEvent.createdTimestamp; data.url = guildScheduledEvent.url; // Add creator information if available if (guildScheduledEvent.creator) { data.creator = { id: guildScheduledEvent.creator.id, username: guildScheduledEvent.creator.username, discriminator: guildScheduledEvent.creator.discriminator, avatar: guildScheduledEvent.creator.avatar, bot: guildScheduledEvent.creator.bot, }; } // Fetch interested users (RSVPed users) const interestedUserIds = await this.fetchEventInterestedUsers(getGuildId, getGuildScheduledEventId); data.interestedUsers = interestedUserIds; data.interestedUsersCount = interestedUserIds.length; data.success = true; } catch (error) { throw new Error(`Failed to get guild scheduled event: ${error.message}`); } break; case types_1.ActionEventType.GUILD_SCHEDULED_EVENT_UPDATE: const guildScheduledEventId = this.actionInstance.getNodeParameter("guildScheduledEventId", 0); const guildId = this.actionInstance.getNodeParameter("guildId", 0); const updateFields = this.actionInstance.getNodeParameter("updateFields", 0, {}); const name = updateFields.name; const scheduledStartTime = updateFields.scheduledStartTime; const scheduledEndTime = updateFields.scheduledEndTime; const description = updateFields.description; try { const guild = await this.client.guilds.fetch(guildId); const guildScheduledEvent = await guild.scheduledEvents.fetch(guildScheduledEventId); if (!guildScheduledEvent) { throw new Error("Guild scheduled event not found."); } const updateData = Object.fromEntries(Object.entries({ name, scheduledStartTime, scheduledEndTime, description, }).filter(([_, v]) => v !== undefined)); await guildScheduledEvent.edit(updateData); data.success = true; data.message = "Guild scheduled event updated successfully."; } catch (error) { throw new Error(`Failed to update guild scheduled event: ${error.message}`); } break; case types_1.ActionEventType.CREATE_GUILD_SCHEDULED_EVENT: const createEventGuildId = this.actionInstance.getNodeParameter("guildId", 0); const eventName = this.actionInstance.getNodeParameter("eventName", 0); const eventStartTime = this.actionInstance.getNodeParameter("eventStartTime", 0); const eventEntityType = this.actionInstance.getNodeParameter("eventEntityType", 0); const eventPrivacyLevel = this.actionInstance.getNodeParameter("eventPrivacyLevel", 0); const eventChannelId = this.actionInstance.getNodeParameter("eventChannelId", 0, ""); const eventOptions = this.actionInstance.getNodeParameter("eventOptions", 0, {}); try { const createEventGuild = await this.client.guilds.fetch(createEventGuildId); const createEventData = { name: eventName, scheduledStartTime: new Date(eventStartTime), privacyLevel: eventPrivacyLevel, entityType: eventEntityType, }; // Add channel for Voice or Stage Instance if ((eventEntityType === 1 || eventEntityType === 2) && eventChannelId) { createEventData.channel = eventChannelId; } // Add optional fields if (eventOptions.description) { createEventData.description = eventOptions.description; } if (eventOptions.scheduledEndTime) { createEventData.scheduledEndTime = new Date(eventOptions.scheduledEndTime); } if (eventOptions.entityMetadataLocation) { createEventData.entityMetadata = { location: eventOptions.entityMetadataLocation, }; } if (eventOptions.image) { createEventData.image = eventOptions.image; } const createdEvent = await createEventGuild.scheduledEvents.create(createEventData); data.success = true; data.message = "Guild scheduled event created successfully."; data.eventId = createdEvent.id; data.eventUrl = createdEvent.url; data.event = { id: createdEvent.id, name: createdEvent.name, description: createdEvent.description, scheduledStartTimestamp: createdEvent.scheduledStartTimestamp, scheduledEndTimestamp: createdEvent.scheduledEndTimestamp, status: createdEvent.status, entityType: createdEvent.entityType, url: createdEvent.url, }; } catch (error) { throw new Error(`Failed to create guild scheduled event: ${error.message}`); } break; case types_1.ActionEventType.GET_MANY_GUILD_SCHEDULED_EVENTS: const getManyEventsGuildId = this.actionInstance.getNodeParameter("guildId", 0); try { const getManyEventsGuild = await this.client.guilds.fetch(getManyEventsGuildId); const events = await getManyEventsGuild.scheduledEvents.fetch(); // Fetch interested users for each event const eventsArray = await Promise.all(events.map(async (event) => { const interestedUserIds = await this.fetchEventInterestedUsers(getManyEventsGuildId, event.id); return { id: event.id, guildId: event.guildId, channelId: event.channelId, creatorId: event.creatorId, name: event.name, description: event.description, scheduledStartTimestamp: event.scheduledStartTimestamp, scheduledEndTimestamp: event.scheduledEndTimestamp, privacyLevel: event.privacyLevel, status: event.status, entityType: event.entityType, entityId: event.entityId, entityMetadata: event.entityMetadata, userCount: event.userCount, image: event.image, createdTimestamp: event.createdTimestamp, url: event.url, creator: event.creator ? { id: event.creator.id, username: event.creator.username, discriminator: event.creator.discriminator, avatar: event.creator.avatar, bot: event.creator.bot, } : null, interestedUsers: interestedUserIds, interestedUsersCount: interestedUserIds.length, }; })); data.success = true; data.count = eventsArray.length; data.events = eventsArray; } catch (error) { throw new Error(`Failed to get guild scheduled events: ${error.message}`); } break; case types_1.ActionEventType.UPDATE_GUILD_SCHEDULED_EVENT: const updateEventGuildId = this.actionInstance.getNodeParameter("guildId", 0); const updateEventId = this.actionInstance.getNodeParameter("guildScheduledEventId", 0); const eventUpdateFields = this.actionInstance.getNodeParameter("eventUpdateFields", 0, {}); try { const updateEventGuild = await this.client.guilds.fetch(updateEventGuildId); const eventToUpdate = await updateEventGuild.scheduledEvents.fetch(updateEventId); if (!eventToUpdate) { throw new Error("Guild scheduled event not found."); } const updateEventData = {}; if (eventUpdateFields.name) { updateEventData.name = eventUpdateFields.name; } if (eventUpdateFields.description !== undefined) { updateEventData.description = eventUpdateFields.description; } if (eventUpdateFields.scheduledStartTime) { updateEventData.scheduledStartTime = new Date(eventUpdateFields.scheduledStartTime); } if (eventUpdateFields.scheduledEndTime) { updateEventData.scheduledEndTime = new Date(eventUpdateFields.scheduledEndTime); } if (eventUpdateFields.channelId) { updateEventData.channel = eventUpdateFields.channelId; } if (eventUpdateFields.entityType !== undefined) { updateEventData.entityType = eventUpdateFields.entityType; } if (eventUpdateFields.status !== undefined) { updateEventData.status = eventUpdateFields.status; } if (eventUpdateFields.entityMetadataLocation) { updateEventData.entityMetadata = { location: eventUpdateFields.entityMetadataLocation, }; } if (eventUpdateFields.image) { updateEventData.image = eventUpdateFields.image; } await eventToUpdate.edit(updateEventData); data.success = true; data.message = "Guild scheduled event updated successfully."; data.eventId = eventToUpdate.id; } catch (error) { throw new Error(`Failed to update guild scheduled event: ${error.message}`); } break; case types_1.ActionEventType.DELETE_GUILD_SCHEDULED_EVENT: const deleteEventGuildId = this.actionInstance.getNodeParameter("guildId", 0); const deleteEventId = this.actionInstance.getNodeParameter("guildScheduledEventId", 0); try { const deleteEventGuild = await this.client.guilds.fetch(deleteEventGuildId); const eventToDelete = await deleteEventGuild.scheduledEvents.fetch(deleteEventId); if (!eventToDelete) { throw new Error("Guild scheduled event not found."); } await eventToDelete.delete(); data.success = true; data.message = "Guild scheduled event deleted successfully."; data.deletedEventId = deleteEventId; } catch (error) { throw new Error(`Failed to delete guild scheduled event: ${error.message}`); } break; // Channel Actions case types_1.ActionEventType.CREATE_CHANNEL: const createGuildId = this.actionInstance.getNodeParameter("channelGuildId", 0); const channelName = this.actionInstance.getNodeParameter("channelName", 0); const channelType = this.actionInstance.getNodeParameter("channelType", 0); const createChannelOptions = this.actionInstance.getNodeParameter("channelOptions", 0, {}); const createPermissionOverwrites = this.actionInstance.getNodeParameter("permissionOverwrites", 0, { permission: [] }); const createPermissionAdd = this.actionInstance.getNodeParameter("permissionAdd", 0, { permission: [] }); try { const createGuild = await this.client.guilds.fetch(createGuildId); const channelCreateOptions = { name: channelName, type: channelType, }; // Add optional fields if provided if (createChannelOptions.topic) { channelCreateOptions.topic = createChannelOptions.topic; } if (createChannelOptions.position !== undefined) { channelCreateOptions.position = createChannelOptions.position; } if (createChannelOptions.nsfw !== undefined) { channelCreateOptions.nsfw = createChannelOptions.nsfw; } if (createChannelOptions.bitrate) { channelCreateOptions.bitrate = createChannelOptions.bitrate; } if (createChannelOptions.userLimit !== undefined) { channelCreateOptions.userLimit = createChannelOptions.userLimit; } if (createChannelOptions.rateLimitPerUser !== undefined) { channelCreateOptions.rateLimitPerUser = createChannelOptions.rateLimitPerUser; } if (createChannelOptions.parent) { channelCreateOptions.parent = createChannelOptions.parent; } // Prepare permission overwrites array let finalPermissionOverwrites = []; // Handle inherit parent permissions if (createChannelOptions.inheritParentPermissions === true && createChannelOptions.parent) { const parentChannel = await this.client.channels.fetch(createChannelOptions.parent); if (parentChannel && !parentChannel.isDMBased()) { const parentGuildChannel = parentChannel; if (parentGuildChannel.permissionOverwrites) { finalPermissionOverwrites = Array.from(parentGuildChannel.permissionOverwrites.cache.values()).map((overwrite) => ({ id: overwrite.id, type: overwrite.type, allow: overwrite.allow.bitfield, deny: overwrite.deny.bitfield, })); } } } // Process permission overwrites (will replace inherited permissions if both are set) if (createPermissionOverwrites.permission && createPermissionOverwrites.permission.length > 0) { finalPermissionOverwrites = createPermissionOverwrites.permission.map((perm) => { const overwrite = { id: perm.id, type: perm.type === "role" ? 0 : 1, }; // Convert permission names to PermissionsBitField flags if (perm.allow && perm.allow.length > 0) { overwrite.allow = perm.allow.map((p) => discord_js_1.PermissionsBitField.Flags[p]); } if (perm.deny && perm.deny.length > 0) { overwrite.deny = perm.deny.map((p) => discord_js_1.PermissionsBitField.Flags[p]); } return overwrite; }); } // Handle permission add - add to existing overwrites if (createPermissionAdd.permission && createPermissionAdd.permission.length > 0) { for (const perm of createPermissionAdd.permission) { // Find if this target already has an overwrite const existingIndex = finalPermissionOverwrites.findIndex(ow => ow.id === perm.id); if (existingIndex >= 0) { // Target already exists, merge permissions const existing = finalPermissionOverwrites[existingIndex]; // Get current bitfields let currentAllow = typeof existing.allow === 'bigint' ? existing.allow : BigInt(0); let currentDeny = typeof existing.deny === 'bigint' ? existing.deny : BigInt(0); // If allow/deny is array, convert to bitfield if (Array.isArray(existing.allow)) { currentAllow = existing.allow.reduce((acc, flag) => acc | flag, BigInt(0)); } if (Array.isArray(existing.deny)) { currentDeny = existing.deny.reduce((acc, flag) => acc | flag, BigInt(0)); } // Add new permissions if (perm.allow && perm.allow.length > 0) { for (const p of perm.allow) { const flag = discord_js_1.PermissionsBitField.Flags[p]; currentAllow |= flag; } } if (perm.deny && perm.deny.length > 0) { for (const p of perm.deny) { const flag = discord_js_1.PermissionsBitField.Flags[p]; currentDeny |= flag; } } finalPermissionOverwrites[existingIndex] = { id: perm.id, type: perm.type === "role" ? 0 : 1, allow: currentAllow, deny: currentDeny, }; } else { // New target, create new overwrite let allowBitfield = BigInt(0); let denyBitfield = BigInt(0); if (perm.allow && perm.allow.length > 0) { for (const p of perm.allow) { const flag = discord_js_1.PermissionsBitField.Flags[p]; allowBitfield |= flag; } } if (perm.deny && perm.deny.length > 0) { for (const p of perm.deny) { const flag = discord_js_1.PermissionsBitField.Flags[p]; denyBitfield |= flag; } } finalPermissionOverwrites.push({ id: perm.id, type: perm.type === "role" ? 0 : 1, allow: allowBitfield, deny: denyBitfield, }); } } } // Set final permission overwrites if (finalPermissionOverwrites.length > 0) { channelCreateOptions.permissionOverwrites = finalPermissionOverwrites; } const createdChannel = await createGuild.channels.create(channelCreateOptions); data.success = true; data.message = "Channel created successfully."; data.channelId = createdChannel.id; data.channelName = createdChannel.name; data.channelType = createdChannel.type; } catch (error) { throw new Error(`Failed to create channel: ${error.message}`); } break; case types_1.ActionEventType.DELETE_CHANNEL: const deleteChannelTargetId = this.actionInstance.getNodeParameter("targetChannelId", 0); try { const channelToDelete = await this.client.channels.fetch(deleteChannelTargetId); if (!channelToDelete) { throw new Error("Channel not found."); } if (!channelToDelete.isDMBased()) { await channelToDelete.delete(); data.success = true; data.message = "Channel deleted successfully."; data.channelId = deleteChannelTargetId; } else { throw new Error("Cannot delete DM channels."); } } catch (error) { throw new Error(`Failed to delete channel: ${error.message}`); } break; case types_1.ActionEventType.UPDATE_CHANNEL: const updateChannelTargetId = this.actionInstance.getNodeParameter("targetChannelId", 0); const updateChannelOptions = this.actionInstance.getNodeParameter("channelOptions", 0, {}); const updatePermissionOverwrites = this.actionInstance.getNodeParameter("permissionOverwrites", 0, { permission: [] }); const updatePermissionAdd = this.actionInstance.getNodeParameter("permissionAdd", 0, { permission: [] }); const updatePermissionRemove = this.actionInstance.getNodeParameter("permissionRemove", 0, { permission: [] }); try { const channelToUpdate = await this.client.channels.fetch(updateChannelTargetId); if (!channelToUpdate) { throw new Error("Channel not found."); } if (channelToUpdate.isDMBased()) { throw new Error("Cannot update DM channels."); } const guildChannel = channelToUpdate; const editOptions = {}; // Add fields to update if provided if (updateChannelOptions.topic !== undefined) { editOptions.topic = updateChannelOptions.topic; } if (updateChannelOptions.position !== undefined) { editOptions.position = updateChannelOptions.position; } if (updateChannelOptions.nsfw !== undefined) { editOptions.nsfw = updateChannelOptions.nsfw; } if (updateChannelOptions.bitrate !== undefined) { editOptions.bitrate = updateChannelOptions.bitrate; } if (updateChannelOptions.userLimit !== undefined) { editOptions.userLimit = updateChannelOptions.userLimit; } if (updateChannelOptions.rateLimitPerUser !== undefined) { editOptions.rateLimitPerUser = updateChannelOptions.rateLimitPerUser; } if (updateChannelOptions.parent !== undefined) { editOptions.parent = updateChannelOptions.parent; } // Prepare permission overwrites let finalPermissionOverwrites; let shouldUpdatePermissions = false; // Handle inherit parent permissions for UPDATE if (updateChannelOptions.inheritParentPermissions === true && updateChannelOptions.parent) { const parentChannel = await this.client.channels.fetch(updateChannelOptions.parent); if (parentChannel && !parentChannel.isDMBased()) { const parentGuildChannel = parentChannel; if (parentGuildChannel.permissionOverwrites) { finalPermissionOverwrites = Array.from(parentGuildChannel.permissionOverwrites.cache.values()).map((overwrite) => ({ id: overwrite.id, type: overwrite.type, allow: overwrite.allow.bitfield, deny: overwrite.deny.bitfield, })); shouldUpdatePermissions = true; } } } // Process permission overwrites (complete replacement) if (updatePermissionOverwrites.permission && updatePermissionOverwrites.permission.length > 0) { finalPermissionOverwrites = updatePermissionOverwrites.permission.map((perm) => { const overwrite = { id: perm.id, type: perm.type === "role" ? 0 : 1, }; // Convert permission names to PermissionsBitField flags if (perm.allow && perm.allow.length > 0) { overwrite.allow = perm.allow.map((p) => discord_js_1.PermissionsBitField.Flags[p]); } if (perm.deny && perm.deny.length > 0) { overwrite.deny = perm.deny.map((p) => discord_js_1.PermissionsBitField.Flags[p]); }