diff --git a/src/d2m/discord-client.js b/src/d2m/discord-client.js index 9745ec2..f65acf8 100644 --- a/src/d2m/discord-client.js +++ b/src/d2m/discord-client.js @@ -1,5 +1,6 @@ // @ts-check +const DiscordTypes = require("discord-api-types/v10") const {Endpoints, SnowTransfer} = require("snowtransfer") const {reg} = require("../matrix/read-registration") const {Client: CloudStorm} = require("cloudstorm") @@ -36,15 +37,15 @@ class DiscordClient { } }) this.ready = false - /** @type {import("discord-api-types/v10").APIUser} */ + /** @type {DiscordTypes.APIUser} */ // @ts-ignore avoid setting as or null because we know we need to wait for ready anyways this.user = null - /** @type {Pick} */ + /** @type {Pick} */ // @ts-ignore this.application = null - /** @type {Map} */ + /** @type {Map} */ this.channels = new Map() - /** @type {Map} */ + /** @type {Map} */ // we get members from the GUILD_CREATE and we do maintain it this.guilds = new Map() /** @type {Map>} */ this.guildChannelMap = new Map() diff --git a/src/d2m/discord-packets.js b/src/d2m/discord-packets.js index a0f2be9..c9424a4 100644 --- a/src/d2m/discord-packets.js +++ b/src/d2m/discord-packets.js @@ -32,6 +32,7 @@ const utils = { console.log(`Discord logged in as ${client.user.username}#${client.user.discriminator} (${client.user.id})`) } else if (message.t === "GUILD_CREATE") { + message.d.members = message.d.members.filter(m => m.user.id === client.user.id) // only keep the bot's own member - it's needed to determine private channels on web client.guilds.set(message.d.id, message.d) const arr = [] client.guildChannelMap.set(message.d.id, arr) @@ -101,6 +102,13 @@ const utils = { } } + } else if (message.t === "GUILD_MEMBER_UPDATE") { + const guild = client.guilds.get(message.d.guild_id) + const member = guild?.members.find(m => m.user.id === message.d.user.id) + if (member) { // only update existing members (i.e. the bot's own member) - don't want to inflate the cache with new irrelevant ones + Object.assign(member, message.d) + } + } else if (message.t === "THREAD_CREATE") { client.channels.set(message.d.id, message.d) if (message.d["guild_id"]) { diff --git a/src/web/pug/guild.pug b/src/web/pug/guild.pug index 5899b98..912abcf 100644 --- a/src/web/pug/guild.pug +++ b/src/web/pug/guild.pug @@ -192,12 +192,17 @@ block body .s-card.p0 ul.my8.ml24 each row in removedUncachedChannels - li: a(href=`https://discord.com/channels/${guild_id}/${row.channel_id}`)= row.nick || row.name + li: a(href=`https://discord.com/channels/${guild_id}/${row.id}`)= row.nick || row.name h3.mt24 Unavailable channels: Wrong type .s-card.p0 ul.my8.ml24 each row in removedWrongTypeChannels - li: a(href=`https://discord.com/channels/${guild_id}/${row.channel_id}`) (#{row.type}) #{row.name} + li: a(href=`https://discord.com/channels/${guild_id}/${row.id}`) (#{row.type}) #{row.name} + h3.mt24 Unavailable channels: Bridge can't access + .s-card.p0 + ul.my8.ml24 + each row in removedPrivateChannels + li: a(href=`https://discord.com/channels/${guild_id}/${row.id}`)= row.name div- // Rooms h3.mt24 Unavailable rooms: Already linked .s-card.p0 diff --git a/src/web/routes/guild.js b/src/web/routes/guild.js index b9c42e7..8c2d99d 100644 --- a/src/web/routes/guild.js +++ b/src/web/routes/guild.js @@ -1,5 +1,6 @@ // @ts-check +const DiscordTypes = require("discord-api-types/v10") const assert = require("assert/strict") const {z} = require("zod") const {H3Event, defineEventHandler, sendRedirect, createError, getValidatedQuery, readValidatedBody, setResponseHeader} = require("h3") @@ -8,6 +9,7 @@ const {LRUCache} = require("lru-cache") const Ty = require("../../types") const uqr = require("uqr") +const {id: botID} = require("../../../addbot") const {discord, as, sync, select, from, db} = require("../../passthrough") /** @type {import("../pug-sync")} */ const pugSync = sync.require("../pug-sync") @@ -15,6 +17,8 @@ const pugSync = sync.require("../pug-sync") const createSpace = sync.require("../../d2m/actions/create-space") /** @type {import("../auth")} */ const auth = require("../auth") +/** @type {import("../../discord/utils")} */ +const utils = sync.require("../../discord/utils") const {reg} = require("../../matrix/read-registration") const schema = { @@ -68,10 +72,11 @@ function filterTo(xs, fn) { } /** - * @param {string} guildID + * @param {DiscordTypes.APIGuild} guild * @param {Ty.R.Hierarchy[]} rooms + * @param {string[]} roles */ -function getChannelRoomsLinks(guildID, rooms) { +function getChannelRoomsLinks(guild, rooms, roles) { function getPosition(channel) { let position = 0 let looking = channel @@ -83,7 +88,7 @@ function getChannelRoomsLinks(guildID, rooms) { return position } - let channelIDs = discord.guildChannelMap.get(guildID) + let channelIDs = discord.guildChannelMap.get(guild.id) assert(channelIDs) let linkedChannels = select("channel_room", ["channel_id", "room_id", "name", "nick"], {channel_id: channelIDs}).all() @@ -93,8 +98,13 @@ function getChannelRoomsLinks(guildID, rooms) { linkedChannelsWithDetails.sort((a, b) => getPosition(a.channel) - getPosition(b.channel)) let unlinkedChannelIDs = channelIDs.filter(c => !linkedChannelIDs.includes(c)) + /** @type {DiscordTypes.APIGuildChannel[]} */ // @ts-ignore let unlinkedChannels = unlinkedChannelIDs.map(c => discord.channels.get(c)) let removedWrongTypeChannels = filterTo(unlinkedChannels, c => c && [0, 5].includes(c.type)) + let removedPrivateChannels = filterTo(unlinkedChannels, c => { + const permissions = utils.getPermissions(roles, guild.roles, botID, c["permission_overwrites"]) + return utils.hasPermission(permissions, DiscordTypes.PermissionFlagsBits.ViewChannel) + }) unlinkedChannels.sort((a, b) => getPosition(a) - getPosition(b)) let linkedRoomIDs = linkedChannels.map(c => c.room_id) @@ -107,7 +117,7 @@ function getChannelRoomsLinks(guildID, rooms) { return { linkedChannelsWithDetails, unlinkedChannels, unlinkedRooms, - removedUncachedChannels, removedWrongTypeChannels, removedLinkedRooms, removedWrongTypeRooms, removedArchivedThreadRooms + removedUncachedChannels, removedWrongTypeChannels, removedPrivateChannels, removedLinkedRooms, removedWrongTypeRooms, removedArchivedThreadRooms } } @@ -130,16 +140,18 @@ as.router.get("/guild", defineEventHandler(async event => { return pugSync.render(event, "guild_not_linked.pug", {guild, guild_id, spaces}) } + const roles = guild.members?.find(m => m.user.id === botID)?.roles || [] + // Easy mode guild that hasn't been linked yet - need to remove elements that would require an existing space if (!row.space_id) { - const links = getChannelRoomsLinks(guild_id, []) + const links = getChannelRoomsLinks(guild, [], roles) return pugSync.render(event, "guild.pug", {guild, guild_id, ...links, ...row}) } // Linked guild const api = getAPI(event) const rooms = await api.getFullHierarchy(row.space_id) - const links = getChannelRoomsLinks(guild_id, rooms) + const links = getChannelRoomsLinks(guild, rooms, roles) return pugSync.render(event, "guild.pug", {guild, guild_id, ...links, ...row}) }))