2023-04-30 12:57:30 +00:00
// @ts-check
2023-05-07 20:27:42 +00:00
const assert = require ( "assert" )
2023-05-08 12:58:46 +00:00
const reg = require ( "../../matrix/read-registration" )
2023-04-30 12:57:30 +00:00
2023-05-07 20:27:42 +00:00
const passthrough = require ( "../../passthrough" )
const { discord , sync , db } = passthrough
2023-05-08 05:22:20 +00:00
/** @type {import("../../matrix/api")} */
const api = sync . require ( "../../matrix/api" )
2023-05-07 20:27:42 +00:00
/** @type {import("../../matrix/file")} */
const file = sync . require ( "../../matrix/file" )
2023-05-08 11:37:51 +00:00
/** @type {import("../converters/user-to-mxid")} */
const userToMxid = sync . require ( "../converters/user-to-mxid" )
2023-05-07 20:27:42 +00:00
/ * *
* A sim is an account that is being simulated by the bridge to copy events from the other side .
* @ param { import ( "discord-api-types/v10" ) . APIUser } user
2023-05-08 12:58:46 +00:00
* @ returns mxid
2023-05-07 20:27:42 +00:00
* /
async function createSim ( user ) {
2023-05-08 12:58:46 +00:00
// Choose sim name
2023-05-08 11:37:51 +00:00
const simName = userToMxid . userToSimName ( user )
2023-05-08 12:58:46 +00:00
const localpart = reg . namespace _prefix + simName
const mxid = "@" + localpart + ":cadence.moe"
// Save chosen name in the database forever
2023-05-09 03:29:46 +00:00
// Making this database change right away so that in a concurrent registration, the 2nd registration will already have generated a different localpart because it can see this row when it generates
2023-05-08 12:58:46 +00:00
db . prepare ( "INSERT INTO sim (discord_id, sim_name, localpart, mxid) VALUES (?, ?, ?, ?)" ) . run ( user . id , simName , localpart , mxid )
// Register matrix user with that name
2023-05-09 03:29:46 +00:00
try {
await api . register ( localpart )
} catch ( e ) {
// If user creation fails, manually undo the database change. Still isn't perfect, but should help.
// (A transaction would be preferable, but I don't think it's safe to leave transaction open across event loop ticks.)
db . prepare ( "DELETE FROM sim WHERE discord_id = ?" ) . run ( user . id )
throw e
}
2023-05-08 12:58:46 +00:00
return mxid
2023-05-08 05:22:20 +00:00
}
2023-05-08 12:58:46 +00:00
/ * *
* Ensure a sim is registered for the user .
* If there is already a sim , use that one . If there isn ' t one yet , register a new sim .
2023-05-09 05:13:59 +00:00
* @ param { import ( "discord-api-types/v10" ) . APIUser } user
2023-05-08 12:58:46 +00:00
* @ returns mxid
* /
async function ensureSim ( user ) {
let mxid = null
const existing = db . prepare ( "SELECT mxid FROM sim WHERE discord_id = ?" ) . pluck ( ) . get ( user . id )
if ( existing ) {
mxid = existing
} else {
mxid = await createSim ( user )
}
return mxid
}
/ * *
* Ensure a sim is registered for the user and is joined to the room .
2023-05-09 05:13:59 +00:00
* @ param { import ( "discord-api-types/v10" ) . APIUser } user
2023-05-08 12:58:46 +00:00
* @ returns mxid
* /
async function ensureSimJoined ( user , roomID ) {
// Ensure room ID is really an ID, not an alias
assert . ok ( roomID [ 0 ] === "!" )
// Ensure user
const mxid = await ensureSim ( user )
// Ensure joined
const existing = db . prepare ( "SELECT * FROM sim_member WHERE room_id = ? and mxid = ?" ) . get ( roomID , mxid )
if ( ! existing ) {
await api . inviteToRoom ( roomID , mxid )
await api . joinRoom ( roomID , mxid )
db . prepare ( "INSERT INTO sim_member (room_id, mxid) VALUES (?, ?)" ) . run ( roomID , mxid )
}
return mxid
}
module . exports . ensureSim = ensureSim
module . exports . ensureSimJoined = ensureSimJoined