out-of-your-element/d2m/actions/create-space.js

98 lines
3.2 KiB
JavaScript
Raw Normal View History

2023-05-04 20:25:00 +00:00
// @ts-check
2023-08-19 06:39:23 +00:00
const assert = require("assert")
const DiscordTypes = require("discord-api-types/v10")
2023-05-04 20:25:00 +00:00
const passthrough = require("../../passthrough")
2023-08-23 00:31:31 +00:00
const { discord, sync, db } = passthrough
/** @type {import("../../matrix/api")} */
const api = sync.require("../../matrix/api")
/** @type {import("../../matrix/file")} */
const file = sync.require("../../matrix/file")
/** @type {import("./create-room")} */
const createRoom = sync.require("./create-room")
2023-08-23 00:31:31 +00:00
/** @type {import("../../matrix/kstate")} */
const ks = sync.require("../../matrix/kstate")
2023-05-04 20:25:00 +00:00
/**
* @param {import("discord-api-types/v10").RESTGetAPIGuildResult} guild
* @param {any} kstate
2023-05-04 20:25:00 +00:00
*/
async function createSpace(guild, kstate) {
const name = kstate["m.room.name/"].name
const topic = kstate["m.room.topic/"]?.topic || undefined
assert(name)
const roomID = await createRoom.postApplyPowerLevels(kstate, async kstate => {
return api.createRoom({
name,
preset: "private_chat", // cannot join space unless invited
visibility: "private",
power_level_content_override: {
events_default: 100, // space can only be managed by bridge
invite: 0 // any existing member can invite others
},
invite: ["@cadence:cadence.moe"], // TODO
topic,
creation_content: {
type: "m.space"
},
initial_state: ks.kstateToState(kstate)
})
2023-05-04 20:25:00 +00:00
})
db.prepare("INSERT INTO guild_space (guild_id, space_id) VALUES (?, ?)").run(guild.id, roomID)
return roomID
2023-05-04 20:25:00 +00:00
}
/**
* @param {DiscordTypes.APIGuild} guild]
*/
async function guildToKState(guild) {
const avatarEventContent = {}
if (guild.icon) {
avatarEventContent.discord_path = file.guildIcon(guild)
avatarEventContent.url = await file.uploadDiscordFileToMxc(avatarEventContent.discord_path) // TODO: somehow represent future values in kstate (callbacks?), while still allowing for diffing, so test cases don't need to touch the media API
}
let history_visibility = "invited"
if (guild["thread_metadata"]) history_visibility = "world_readable"
const guildKState = {
"m.room.name/": {name: guild.name},
"m.room.avatar/": avatarEventContent,
"m.room.guest_access/": {guest_access: "can_join"}, // guests can join space if other conditions are met
2023-08-23 00:31:31 +00:00
"m.room.history_visibility/": {history_visibility: "invited"} // any events sent after user was invited are visible
}
return guildKState
}
async function syncSpace(guildID) {
/** @ts-ignore @type {DiscordTypes.APIGuild} */
const guild = discord.guilds.get(guildID)
assert.ok(guild)
2023-08-23 00:31:31 +00:00
/** @type {string?} */
const spaceID = db.prepare("SELECT space_id from guild_space WHERE guild_id = ?").pluck().get(guildID)
const guildKState = await guildToKState(guild)
2023-08-23 00:31:31 +00:00
if (!spaceID) {
const spaceID = await createSpace(guild, guildKState)
2023-08-23 00:31:31 +00:00
return spaceID // Naturally, the newly created space is already up to date, so we can always skip syncing here.
}
2023-08-23 00:31:31 +00:00
console.log(`[space sync] to matrix: ${guild.name}`)
2023-08-23 00:31:31 +00:00
// sync channel state to room
const spaceKState = await createRoom.roomToKState(spaceID)
const spaceDiff = ks.diffKState(spaceKState, guildKState)
await createRoom.applyKStateDiffToRoom(spaceID, spaceDiff)
2023-08-23 00:31:31 +00:00
return spaceID
}
2023-05-04 20:25:00 +00:00
module.exports.createSpace = createSpace
2023-08-23 00:31:31 +00:00
module.exports.syncSpace = syncSpace
module.exports.guildToKState = guildToKState