i dont even know what this PR is supposed to be about anymore, everyone lost the plot somewhere in the middle of act 2 #74
6 changed files with 73 additions and 17 deletions
New system of linking
commit
dc03dbd5f4
|
|
@ -35,6 +35,7 @@ const PRIVACY_ENUMS = {
|
|||
ROOM_HISTORY_VISIBILITY: ["shared", "shared", "world_readable"], // any events sent after <value> are visible, but for world_readable anybody can read without even joining
|
||||
GUEST_ACCESS: ["can_join", "forbidden", "forbidden"], // whether guests can join space if other conditions are met
|
||||
SPACE_JOIN_RULES: ["invite", "public", "public"],
|
||||
/** @type {import("../../types").JoinRule[]} */
|
||||
ROOM_JOIN_RULES: ["restricted", "public", "public"]
|
||||
}
|
||||
|
||||
|
|
@ -88,7 +89,7 @@ async function channelToKState(channel, guild, di) {
|
|||
const guildSpaceID = await createSpace.ensureSpace(guild)
|
||||
/** Used as the literal parent on Matrix, for categorisation. Will be the same as `guildSpaceID` unless it's a forum channel's thread, in which case a different space is used to group those threads. */
|
||||
let parentSpaceID = guildSpaceID
|
||||
if (parentChannel?.type === DiscordTypes.ChannelType.GuildForum) {
|
||||
if (parentChannel?.type === DiscordTypes.ChannelType.GuildForum || parentChannel?.type === DiscordTypes.ChannelType.GuildMedia) { //TODO: Once Ellie's and Guzio's MSC for room-in-room embedding starts being implemented, make this check for whether THIS channel (not its parent) is a thread of ANY type (not just threads in forum/media channels) - thus making it so that threads always appear embedded under their parent.
|
||||
parentSpaceID = await ensureRoom(channel.parent_id)
|
||||
assert(typeof parentSpaceID === "string")
|
||||
}
|
||||
|
|
@ -111,7 +112,7 @@ async function channelToKState(channel, guild, di) {
|
|||
let history_visibility = PRIVACY_ENUMS.ROOM_HISTORY_VISIBILITY[privacyLevel]
|
||||
if (channel["thread_metadata"]) history_visibility = "world_readable"
|
||||
|
||||
/** @type {{join_rule: string, allow?: any}} */
|
||||
/** @type {{join_rule: import("../../types").JoinRule, allow?: {type: "m.room_membership", room_id: string}[]}} */
|
||||
let join_rules = {
|
||||
join_rule: "restricted",
|
||||
allow: [{
|
||||
|
|
@ -119,6 +120,13 @@ async function channelToKState(channel, guild, di) {
|
|||
room_id: guildSpaceID
|
||||
}]
|
||||
}
|
||||
if (guildSpaceID !== parentSpaceID) {
|
||||
//@ts-ignore - join_rules.allow most certainly IS defined because we literally define it ~5 lines earlier
|
||||
join_rules.allow[1] = {
|
||||
type: "m.room_membership",
|
||||
room_id: parentSpaceID
|
||||
}
|
||||
}
|
||||
if (PRIVACY_ENUMS.ROOM_JOIN_RULES[privacyLevel] !== "restricted") {
|
||||
join_rules = {join_rule: PRIVACY_ENUMS.ROOM_JOIN_RULES[privacyLevel]}
|
||||
}
|
||||
|
|
|
|||
2
src/types.d.ts
vendored
2
src/types.d.ts
vendored
|
|
@ -505,6 +505,8 @@ export namespace R {
|
|||
|
||||
export type Membership = "invite" | "knock" | "join" | "leave" | "ban"
|
||||
|
||||
export type JoinRule = "public" | "knock" | "invite" | "private" | "restricted" | "knock_restricted"
|
||||
|
||||
export type Pagination<T> = {
|
||||
chunk: T[]
|
||||
next_batch?: string
|
||||
|
|
|
|||
5
src/web/pug/explain.pug
Normal file
5
src/web/pug/explain.pug
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
extends includes/template.pug
|
||||
|
||||
block body
|
||||
.ta-center.wmx5.p48.mx-auto#ok
|
||||
p.mt24.fs-body2= msg
|
||||
|
|
@ -238,6 +238,8 @@ block body
|
|||
ul.my8.ml24
|
||||
each row in removedWrongTypeChannels
|
||||
li: a(href=`https://discord.com/channels/${guild_id}/${row.id}`) (#{row.type}) #{row.name}
|
||||
"|"
|
||||
a(href=`/explain?type=${row.type}`) Why?
|
||||
h3.mt24 Unavailable channels: Discord bot can't access
|
||||
.s-card.p0
|
||||
ul.my8.ml24
|
||||
|
|
@ -254,13 +256,13 @@ block body
|
|||
ul.my8.ml24
|
||||
each row in removedEncryptedRooms
|
||||
li: a(href=`https://matrix.to/#/${row.room_id}`)= row.name
|
||||
h3.mt24 Unavailable rooms: Wrong type
|
||||
.s-card.p0
|
||||
ul.my8.ml24
|
||||
each row in removedWrongTypeRooms
|
||||
li: a(href=`https://matrix.to/#/${row.room_id}`) (#{row.room_type}) #{row.name}
|
||||
h3.mt24 Unavailable rooms: Archived thread
|
||||
.s-card.p0
|
||||
ul.my8.ml24
|
||||
each row in removedArchivedThreadRooms
|
||||
li: a(href=`https://matrix.to/#/${row.room_id}`)= row.name
|
||||
// h3.mt24 Unavailable rooms: Wrong type
|
||||
// .s-card.p0
|
||||
// ul.my8.ml24
|
||||
// each row in removedWrongTypeRooms
|
||||
// li: a(href=`https://matrix.to/#/${row.room_id}`) (#{row.room_type}) #{row.name}
|
||||
// h3.mt24 Unavailable rooms: Archived thread
|
||||
// .s-card.p0
|
||||
// ul.my8.ml24
|
||||
// each row in removedArchivedThreadRooms
|
||||
// li: a(href=`https://matrix.to/#/${row.room_id}`)= row.name
|
||||
|
|
|
|||
|
|
@ -38,6 +38,9 @@ const schema = {
|
|||
}),
|
||||
inviteNonce: z.object({
|
||||
nonce: z.string()
|
||||
}),
|
||||
explain: z.object({
|
||||
type: z.number()
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -53,6 +56,27 @@ function getAPI(event) {
|
|||
/** @type {LRUCache<string, string>} nonce to guild id */
|
||||
const validNonce = new LRUCache({max: 200})
|
||||
|
||||
/**
|
||||
* TYPING = Channels on which Discord messages can be sent. They should be bridgeable to anything other than an m.space (because if it did end up as a space, no one would be able to actually see the text messages sent there).
|
||||
* SPACE = Channels on which Discord messages cannot be received. They should be bridgeable to m.space only (because not only does m.space make sending messages impossible on any sane client (thus preventing Discord-caused errors), but it also just-so-happens that both currently-existing message-unsupporting channel types (Categories and School hubs) are sort of "indexes", which fits nicely to m.space).
|
||||
* MIXED = Forum-like channels. They can be bridged to both m.space and anything other than an m.space - hence the name.
|
||||
* @type {Map<DiscordTypes.ChannelType, {type: "TYPING"|"MIXED"|"SPACE", humanName:string, unsupported?: string}>}*/
|
||||
const linkRules = new Map([
|
||||
[0, {type: "TYPING", humanName:"Normal text channels"}],
|
||||
[1, {type: "TYPING", humanName:"Normal DMs", unsupported: "OOYE won't support DMs until a good way of doing it can be figured out. Please see https://gitdab.com/cadence/out-of-your-element#caveats for more."}],
|
||||
[2, {type: "TYPING", humanName:"Normal VCs"}],
|
||||
[3, {type: "TYPING", humanName:"Group DMs", unsupported: "OOYE won't support DMs until a good way of doing it can be figured out. Please see https://gitdab.com/cadence/out-of-your-element#caveats for more."}],
|
||||
[4, {type: "SPACE", humanName:"Categories", unsupported: "There is no concept of categories on Matrix."}], //...at least officially. In practice, some clients will render sub-spaces as categories. TODO: Bridge categories to sub-spaces.
|
||||
[5, {type: "TYPING", humanName:"Announcement text channels"}],
|
||||
[10, {type: "TYPING", humanName:"Announcement threads"}], //Currently testing: Letting users bridge threads however they like. In case this doesn't work: , unsupported: "Threads must be bridged automatically, to ensure proper lifecycle management (so that archiving threads won't break them). Please send a message in this thread to bridge it automatically."
|
||||
[11, {type: "TYPING", humanName:"Normal threads"}],
|
||||
[12, {type: "TYPING", humanName:"Private threads"}],
|
||||
[13, {type: "TYPING", humanName:"Stage VCs"}],
|
||||
[14, {type: "SPACE", humanName:"School hubs", unsupported: "Bots cannot be members of school hubs. How in the sweet hell did you manage to put OOYE on one, anyway??? ~~Emma, please stop breaking Discord API in cursed ways again.~~"}],
|
||||
[15, {type: "MIXED", humanName:"Normal forums"}],
|
||||
[16, {type: "MIXED", humanName:"Media forums"}],
|
||||
])
|
||||
|
||||
/**
|
||||
* @param {{type: number, parent_id?: string | null, position?: number}} channel
|
||||
* @param {Map<string, {type: number, parent_id?: string | null, position?: number}>} channels
|
||||
|
|
@ -112,7 +136,10 @@ function getChannelRoomsLinks(guild, rooms, roles) {
|
|||
let unlinkedChannelIDs = channelIDs.filter(c => !linkedChannelIDs.includes(c))
|
||||
/** @type {DiscordTypes.APIGuildChannel[]} */ // @ts-ignore
|
||||
let unlinkedChannels = unlinkedChannelIDs.map(c => discord.channels.get(c))
|
||||
let removedWrongTypeChannels = dUtils.filterTo(unlinkedChannels, c => c && [0, 2, 5, 13, 15, 16].includes(c.type))
|
||||
let removedWrongTypeChannels = dUtils.filterTo(unlinkedChannels, c => {
|
||||
const rule = linkRules.get(c?.type)
|
||||
return rule && !rule.unsupported
|
||||
})
|
||||
let removedPrivateChannels = dUtils.filterTo(unlinkedChannels, c => {
|
||||
const permissions = dUtils.getPermissions(guild.id, roles, guild.roles, botID, c["permission_overwrites"])
|
||||
return dUtils.hasSomePermissions(permissions, ["Administrator", "ViewChannel"])
|
||||
|
|
@ -122,11 +149,11 @@ function getChannelRoomsLinks(guild, rooms, roles) {
|
|||
let linkedRoomIDs = linkedChannels.map(c => c.room_id)
|
||||
let unlinkedRooms = [...rooms]
|
||||
let removedLinkedRooms = dUtils.filterTo(unlinkedRooms, r => !linkedRoomIDs.includes(r.room_id))
|
||||
let removedWrongTypeRooms = dUtils.filterTo(unlinkedRooms, r => !(r.room_type && r.room_type === "m.space"))
|
||||
let removedWrongTypeRooms = []
|
||||
let removedEncryptedRooms = dUtils.filterTo(unlinkedRooms, r => !r.encryption && !r["im.nheko.summary.encryption"])
|
||||
// https://discord.com/developers/docs/topics/threads#active-archived-threads
|
||||
// need to filter out linked archived threads from unlinkedRooms, will just do that by comparing against the name
|
||||
let removedArchivedThreadRooms = dUtils.filterTo(unlinkedRooms, r => r.name && !r.name.match(/^\[(🔒)?⛓️\]/))
|
||||
let removedArchivedThreadRooms = [] //dUtils.filterTo(unlinkedRooms, r => r.name && !r.name.match(/^\[(🔒)?⛓️\]/)) COMMENTED OUT - Currently testing: Letting users bridge threads however they like.
|
||||
|
||||
return {
|
||||
linkedChannelsWithDetails, unlinkedChannels, unlinkedRooms,
|
||||
|
|
@ -182,6 +209,12 @@ as.router.get("/guild", defineEventHandler(async event => {
|
|||
return pugSync.render(event, "guild.pug", {guild, guild_id, ...links, ...row})
|
||||
}))
|
||||
|
||||
as.router.get("/explain", defineEventHandler(async event => {
|
||||
const {type} = await getValidatedQuery(event, schema.explain.parse)
|
||||
const msg = type+" is unsupported" //TODO: actually explain (I'm sure I messed something up anyway, I'll leave it for now)
|
||||
return pugSync.render(event, "explain.pug", {msg})
|
||||
}))
|
||||
|
||||
as.router.get("/qr", defineEventHandler(async event => {
|
||||
const {guild_id} = await getValidatedQuery(event, schema.qr.parse)
|
||||
const managed = await auth.getManagedGuilds(event)
|
||||
|
|
@ -267,3 +300,4 @@ as.router.post("/api/invite", defineEventHandler(async event => {
|
|||
|
||||
module.exports._getPosition = getPosition
|
||||
module.exports.getInviteTargetSpaces = getInviteTargetSpaces
|
||||
module.exports.linkRules = linkRules
|
||||
|
|
|
|||
|
|
@ -173,7 +173,7 @@ as.router.post("/api/link", defineEventHandler(async event => {
|
|||
const row = from("channel_room").select("channel_id", "room_id").and("WHERE channel_id = ? OR room_id = ?").get(channel.id, parsedBody.matrix)
|
||||
if (row) throw createError({status: 400, message: "Bad Request", data: `Channel ID ${row.channel_id} or room ID ${parsedBody.matrix} are already bridged and cannot be reused`})
|
||||
|
||||
// Check room is an actual room (not space) and is part of the guild's space
|
||||
// Check whether the room is an actual room or a space, and if it's a part of the guild's space
|
||||
let foundRoom = false
|
||||
let foundSpace = false
|
||||
/** @type {string[]?} */
|
||||
|
|
@ -198,7 +198,12 @@ as.router.post("/api/link", defineEventHandler(async event => {
|
|||
}
|
||||
}
|
||||
if (!foundRoom) throw createError({status: 400, message: "Bad Request", data: "Matrix room needs to be part of the bridged space"})
|
||||
else if (foundSpace) throw createError({status: 400, message: "Bad Request", data: "Matrix room cannot be of type m.space"})
|
||||
|
||||
//Ensure link rules are upheld
|
||||
const rule = guildRoute.linkRules.get(channel.type)
|
||||
if (!rule || rule.unsupported) throw createError({status:400, message: "Bad Request", data: "You cannot bridge " + (rule ? (rule.humanName+"(type-" + channel.type+" channels)") : ("unknown-type ("+channel.type+") channels")) + " because: " + (rule ? rule.unsupported : "OOYE doesn't even know what they are yet.")})
|
||||
else if (foundSpace && rule.type === "TYPING") throw createError({status: 400, message: "Bad Request", data: "Matrix room cannot be of type m.space when bridging to "+rule.humanName})
|
||||
else if (!foundSpace && rule.type === "SPACE") throw createError({status: 400, message: "Bad Request", data: "Matrix room must be of type m.space when bridging to "+rule.humanName})
|
||||
|
||||
// Check room exists and bridge is joined
|
||||
try {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue