forked from cadence/out-of-your-element
		
	Finish moving from SQL to New Funny ORM
This commit is contained in:
		
							parent
							
								
									4e1e590c3a
								
							
						
					
					
						commit
						79bd0254f0
					
				
					 19 changed files with 87 additions and 87 deletions
				
			
		| 
						 | 
				
			
			@ -3,7 +3,7 @@
 | 
			
		|||
const assert = require("assert").strict
 | 
			
		||||
 | 
			
		||||
const passthrough = require("../../passthrough")
 | 
			
		||||
const { discord, sync, db } = passthrough
 | 
			
		||||
const {discord, sync, db, select} = passthrough
 | 
			
		||||
/** @type {import("../../matrix/api")} */
 | 
			
		||||
const api = sync.require("../../matrix/api")
 | 
			
		||||
/** @type {import("./register-user")} */
 | 
			
		||||
| 
						 | 
				
			
			@ -17,7 +17,7 @@ const createRoom = sync.require("../actions/create-room")
 | 
			
		|||
async function addReaction(data) {
 | 
			
		||||
	const user = data.member?.user
 | 
			
		||||
	assert.ok(user && user.username)
 | 
			
		||||
	const parentID = db.prepare("SELECT event_id FROM event_message WHERE message_id = ? AND part = 0").pluck().get(data.message_id) // 0 = primary
 | 
			
		||||
	const parentID = select("event_message", "event_id", "WHERE message = ? AND part = 0").pluck().get(data.message_id) // 0 = primary
 | 
			
		||||
	if (!parentID) return // Nothing can be done if the parent message was never bridged.
 | 
			
		||||
	assert.equal(typeof parentID, "string")
 | 
			
		||||
	const roomID = await createRoom.ensureRoom(data.channel_id)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,7 +3,7 @@
 | 
			
		|||
const assert = require("assert")
 | 
			
		||||
 | 
			
		||||
const passthrough = require("../../passthrough")
 | 
			
		||||
const { discord, sync, db } = passthrough
 | 
			
		||||
const {discord, sync, db, select} = passthrough
 | 
			
		||||
/** @type {import("../converters/thread-to-announcement")} */
 | 
			
		||||
const threadToAnnouncement = sync.require("../converters/thread-to-announcement")
 | 
			
		||||
/** @type {import("../../matrix/api")} */
 | 
			
		||||
| 
						 | 
				
			
			@ -15,8 +15,7 @@ const api = sync.require("../../matrix/api")
 | 
			
		|||
 * @param {import("discord-api-types/v10").APIThreadChannel} thread
 | 
			
		||||
 */
 | 
			
		||||
async function announceThread(parentRoomID, threadRoomID, thread) {
 | 
			
		||||
   /** @type {string?} */
 | 
			
		||||
   const creatorMxid = db.prepare("SELECT mxid FROM sim WHERE discord_id = ?").pluck().get(thread.owner_id)
 | 
			
		||||
	const creatorMxid = select("sim", "mxid", "WHERE discord_id = ?").pluck().get(thread.owner_id)
 | 
			
		||||
 | 
			
		||||
   const content = await threadToAnnouncement.threadToAnnouncement(parentRoomID, threadRoomID, creatorMxid, thread, {api})
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,7 +5,7 @@ const DiscordTypes = require("discord-api-types/v10")
 | 
			
		|||
const reg = require("../../matrix/read-registration")
 | 
			
		||||
 | 
			
		||||
const passthrough = require("../../passthrough")
 | 
			
		||||
const { discord, sync, db } = passthrough
 | 
			
		||||
const {discord, sync, db, select} = passthrough
 | 
			
		||||
/** @type {import("../../matrix/file")} */
 | 
			
		||||
const file = sync.require("../../matrix/file")
 | 
			
		||||
/** @type {import("../../matrix/api")} */
 | 
			
		||||
| 
						 | 
				
			
			@ -41,7 +41,7 @@ function applyKStateDiffToRoom(roomID, kstate) {
 | 
			
		|||
/**
 | 
			
		||||
 * @param {{id: string, name: string, topic?: string?, type: number}} channel
 | 
			
		||||
 * @param {{id: string}} guild
 | 
			
		||||
 * @param {string?} customName
 | 
			
		||||
 * @param {string | null | undefined} customName
 | 
			
		||||
 */
 | 
			
		||||
function convertNameAndTopic(channel, guild, customName) {
 | 
			
		||||
	let channelPrefix =
 | 
			
		||||
| 
						 | 
				
			
			@ -71,7 +71,7 @@ async function channelToKState(channel, guild) {
 | 
			
		|||
	const spaceID = await createSpace.ensureSpace(guild.id)
 | 
			
		||||
	assert.ok(typeof spaceID === "string")
 | 
			
		||||
 | 
			
		||||
	const row = db.prepare("SELECT nick, custom_avatar FROM channel_room WHERE channel_id = ?").get(channel.id)
 | 
			
		||||
	const row = select("channel_room", ["nick", "custom_avatar"], "WHERE channel_id = ?").get(channel.id)
 | 
			
		||||
	const customName = row?.nick
 | 
			
		||||
	const customAvatar = row?.custom_avatar
 | 
			
		||||
	const [convertedName, convertedTopic] = convertNameAndTopic(channel, guild, customName)
 | 
			
		||||
| 
						 | 
				
			
			@ -248,8 +248,7 @@ async function _syncRoom(channelID, shouldActuallySync) {
 | 
			
		|||
		await inflightRoomCreate.get(channelID) // just waiting, and then doing a new db query afterwards, is the simplest way of doing it
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/** @type {{room_id: string, thread_parent: string?}} */
 | 
			
		||||
	const existing = db.prepare("SELECT room_id, thread_parent from channel_room WHERE channel_id = ?").get(channelID)
 | 
			
		||||
	const existing = select("channel_room", ["room_id", "thread_parent"], "WHERE channel_id = ?").get(channelID)
 | 
			
		||||
 | 
			
		||||
	if (!existing) {
 | 
			
		||||
		const creation = (async () => {
 | 
			
		||||
| 
						 | 
				
			
			@ -309,9 +308,9 @@ async function _unbridgeRoom(channelID) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
async function unbridgeDeletedChannel(channelID, guildID) {
 | 
			
		||||
	const roomID = db.prepare("SELECT room_id from channel_room WHERE channel_id = ?").pluck().get(channelID)
 | 
			
		||||
	const roomID = select("channel_room", "room_id", "WHERE channel_id = ?").pluck().get(channelID)
 | 
			
		||||
	assert.ok(roomID)
 | 
			
		||||
	const spaceID = db.prepare("SELECT space_id FROM guild_space WHERE guild_id = ?").pluck().get(guildID)
 | 
			
		||||
	const spaceID = select("guild_space", "space_id", "WHERE guild_id = ?").pluck().get(guildID)
 | 
			
		||||
	assert.ok(spaceID)
 | 
			
		||||
 | 
			
		||||
	// remove room from being a space member
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,7 +5,7 @@ const DiscordTypes = require("discord-api-types/v10")
 | 
			
		|||
const reg = require("../../matrix/read-registration")
 | 
			
		||||
 | 
			
		||||
const passthrough = require("../../passthrough")
 | 
			
		||||
const { discord, sync, db } = passthrough
 | 
			
		||||
const {discord, sync, db, select} = passthrough
 | 
			
		||||
/** @type {import("../../matrix/api")} */
 | 
			
		||||
const api = sync.require("../../matrix/api")
 | 
			
		||||
/** @type {import("../../matrix/file")} */
 | 
			
		||||
| 
						 | 
				
			
			@ -86,8 +86,7 @@ async function _syncSpace(guildID, shouldActuallySync) {
 | 
			
		|||
		await inflightSpaceCreate.get(guildID) // just waiting, and then doing a new db query afterwards, is the simplest way of doing it
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/** @type {string?} */
 | 
			
		||||
	const spaceID = db.prepare("SELECT space_id from guild_space WHERE guild_id = ?").pluck().get(guildID)
 | 
			
		||||
	const spaceID = select("guild_space", "space_id", "WHERE guild_id = ?").pluck().get(guildID)
 | 
			
		||||
 | 
			
		||||
	if (!spaceID) {
 | 
			
		||||
		const creation = (async () => {
 | 
			
		||||
| 
						 | 
				
			
			@ -118,7 +117,7 @@ async function _syncSpace(guildID, shouldActuallySync) {
 | 
			
		|||
	const newAvatarState = spaceDiff["m.room.avatar/"]
 | 
			
		||||
	if (guild.icon && newAvatarState?.url) {
 | 
			
		||||
		// don't try to update rooms with custom avatars though
 | 
			
		||||
		const roomsWithCustomAvatars = db.prepare("SELECT room_id FROM channel_room WHERE custom_avatar IS NOT NULL").pluck().all()
 | 
			
		||||
		const roomsWithCustomAvatars = select("channel_room", "room_id", "WHERE custom_avatar IS NOT NULL").pluck().all()
 | 
			
		||||
 | 
			
		||||
		const childRooms = ks.kstateToState(spaceKState).filter(({type, state_key, content}) => {
 | 
			
		||||
			return type === "m.space.child" && "via" in content && !roomsWithCustomAvatars.includes(state_key)
 | 
			
		||||
| 
						 | 
				
			
			@ -154,8 +153,7 @@ async function syncSpaceFully(guildID) {
 | 
			
		|||
	const guild = discord.guilds.get(guildID)
 | 
			
		||||
	assert.ok(guild)
 | 
			
		||||
 | 
			
		||||
	/** @type {string?} */
 | 
			
		||||
	const spaceID = db.prepare("SELECT space_id from guild_space WHERE guild_id = ?").pluck().get(guildID)
 | 
			
		||||
	const spaceID = select("guild_space", "space_id", "WHERE guild_id = ?").pluck().get(guildID)
 | 
			
		||||
 | 
			
		||||
	const guildKState = await guildToKState(guild)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -176,7 +174,7 @@ async function syncSpaceFully(guildID) {
 | 
			
		|||
	}).map(({state_key}) => state_key)
 | 
			
		||||
 | 
			
		||||
	for (const roomID of childRooms) {
 | 
			
		||||
		const channelID = db.prepare("SELECT channel_id FROM channel_room WHERE room_id = ?").pluck().get(roomID)
 | 
			
		||||
		const channelID = select("channel_room", "channel_id", "WHERE room_id = ?").pluck().get(roomID)
 | 
			
		||||
		if (!channelID) continue
 | 
			
		||||
		if (discord.channels.has(channelID)) {
 | 
			
		||||
			await createRoom.syncRoom(channelID)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,7 +1,7 @@
 | 
			
		|||
// @ts-check
 | 
			
		||||
 | 
			
		||||
const passthrough = require("../../passthrough")
 | 
			
		||||
const { sync, db } = passthrough
 | 
			
		||||
const {sync, db, select} = passthrough
 | 
			
		||||
/** @type {import("../../matrix/api")} */
 | 
			
		||||
const api = sync.require("../../matrix/api")
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -9,13 +9,10 @@ const api = sync.require("../../matrix/api")
 | 
			
		|||
 * @param {import("discord-api-types/v10").GatewayMessageDeleteDispatchData} data
 | 
			
		||||
 */
 | 
			
		||||
async function deleteMessage(data) {
 | 
			
		||||
	/** @type {string?} */
 | 
			
		||||
	const roomID = db.prepare("SELECT room_id FROM channel_room WHERE channel_id = ?").pluck().get(data.channel_id)
 | 
			
		||||
	const roomID = select("channel_room", "room_id", "WHERE channel_id = ?").pluck().get(data.channel_id)
 | 
			
		||||
	if (!roomID) return
 | 
			
		||||
 | 
			
		||||
	/** @type {string[]} */
 | 
			
		||||
	const eventsToRedact = db.prepare("SELECT event_id FROM event_message WHERE message_id = ?").pluck().all(data.id)
 | 
			
		||||
 | 
			
		||||
	const eventsToRedact = select("event_message", "event_id", "WHERE message_id = ?").pluck().all(data.id)
 | 
			
		||||
	for (const eventID of eventsToRedact) {
 | 
			
		||||
		// Unfortunately, we can't specify a sender to do the redaction as, unless we find out that info via the audit logs
 | 
			
		||||
		await api.redactEvent(roomID, eventID)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,7 +4,7 @@ const assert = require("assert")
 | 
			
		|||
const reg = require("../../matrix/read-registration")
 | 
			
		||||
 | 
			
		||||
const passthrough = require("../../passthrough")
 | 
			
		||||
const { discord, sync, db } = passthrough
 | 
			
		||||
const {discord, sync, db, select} = passthrough
 | 
			
		||||
/** @type {import("../../matrix/api")} */
 | 
			
		||||
const api = sync.require("../../matrix/api")
 | 
			
		||||
/** @type {import("../../matrix/file")} */
 | 
			
		||||
| 
						 | 
				
			
			@ -47,7 +47,7 @@ async function createSim(user) {
 | 
			
		|||
 */
 | 
			
		||||
async function ensureSim(user) {
 | 
			
		||||
	let mxid = null
 | 
			
		||||
	const existing = db.prepare("SELECT mxid FROM sim WHERE discord_id = ?").pluck().get(user.id)
 | 
			
		||||
	const existing = select("sim", "mxid", "WHERE discord_id = ?").pluck().get(user.id)
 | 
			
		||||
	if (existing) {
 | 
			
		||||
		mxid = existing
 | 
			
		||||
	} else {
 | 
			
		||||
| 
						 | 
				
			
			@ -70,7 +70,7 @@ async function ensureSimJoined(user, roomID) {
 | 
			
		|||
	const mxid = await ensureSim(user)
 | 
			
		||||
 | 
			
		||||
	// Ensure joined
 | 
			
		||||
	const existing = db.prepare("SELECT * FROM sim_member WHERE room_id = ? and mxid = ?").get(roomID, mxid)
 | 
			
		||||
	const existing = select("sim_member", "mxid", "WHERE room_id = ? AND mxid = ?").pluck().get(roomID, mxid)
 | 
			
		||||
	if (!existing) {
 | 
			
		||||
		try {
 | 
			
		||||
			await api.inviteToRoom(roomID, mxid)
 | 
			
		||||
| 
						 | 
				
			
			@ -137,7 +137,7 @@ async function syncUser(user, member, guildID, roomID) {
 | 
			
		|||
	const mxid = await ensureSimJoined(user, roomID)
 | 
			
		||||
	const content = await memberToStateContent(user, member, guildID)
 | 
			
		||||
	const profileEventContentHash = calculateProfileEventContentHash(content)
 | 
			
		||||
	const existingHash = db.prepare("SELECT profile_event_content_hash FROM sim_member WHERE room_id = ? AND mxid = ?").pluck().get(roomID, mxid)
 | 
			
		||||
	const existingHash = select("sim_member", "profile_event_content_hash", "WHERE room_id = ? AND mxid = ?").pluck().get(roomID, mxid)
 | 
			
		||||
	// only do the actual sync if the hash has changed since we last looked
 | 
			
		||||
	if (existingHash !== profileEventContentHash) {
 | 
			
		||||
		await api.sendState(roomID, "m.room.member", mxid, content, mxid)
 | 
			
		||||
| 
						 | 
				
			
			@ -147,17 +147,18 @@ async function syncUser(user, member, guildID, roomID) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
async function syncAllUsersInRoom(roomID) {
 | 
			
		||||
	const mxids = db.prepare("SELECT mxid FROM sim_member WHERE room_id = ?").pluck().all(roomID)
 | 
			
		||||
	const mxids = select("sim_member", "mxid", "WHERE room_id = ?").pluck().all(roomID)
 | 
			
		||||
 | 
			
		||||
	const channelID = db.prepare("SELECT channel_id FROM channel_room WHERE room_id = ?").pluck().get(roomID)
 | 
			
		||||
	const channelID = select("channel_room", "channel_id", "WHERE room_id = ?").pluck().get(roomID)
 | 
			
		||||
	assert.ok(typeof channelID === "string")
 | 
			
		||||
 | 
			
		||||
	/** @ts-ignore @type {import("discord-api-types/v10").APIGuildChannel} */
 | 
			
		||||
	const channel = discord.channels.get(channelID)
 | 
			
		||||
	const guildID = channel.guild_id
 | 
			
		||||
	assert.ok(typeof guildID === "string")
 | 
			
		||||
 | 
			
		||||
	for (const mxid of mxids) {
 | 
			
		||||
		const userID = db.prepare("SELECT discord_id FROM sim WHERE mxid = ?").pluck().get(mxid)
 | 
			
		||||
		const userID = select("sim", "discord_id", "WHERE mxid = ?").pluck().get(mxid)
 | 
			
		||||
		assert.ok(typeof userID === "string")
 | 
			
		||||
 | 
			
		||||
		/** @ts-ignore @type {Required<import("discord-api-types/v10").APIGuildMember>} */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,7 +3,7 @@
 | 
			
		|||
const assert = require("assert")
 | 
			
		||||
 | 
			
		||||
const passthrough = require("../../passthrough")
 | 
			
		||||
const { discord, sync, db } = passthrough
 | 
			
		||||
const {discord, sync, db, select} = passthrough
 | 
			
		||||
/** @type {import("./message-to-event")} */
 | 
			
		||||
const messageToEvent = sync.require("../converters/message-to-event")
 | 
			
		||||
/** @type {import("../actions/register-user")} */
 | 
			
		||||
| 
						 | 
				
			
			@ -21,17 +21,15 @@ const createRoom = sync.require("../actions/create-room")
 | 
			
		|||
async function editToChanges(message, guild, api) {
 | 
			
		||||
	// Figure out what events we will be replacing
 | 
			
		||||
 | 
			
		||||
	const roomID = db.prepare("SELECT room_id FROM channel_room WHERE channel_id = ?").pluck().get(message.channel_id)
 | 
			
		||||
	/** @type {string?} */
 | 
			
		||||
	let senderMxid = db.prepare("SELECT mxid FROM sim WHERE discord_id = ?").pluck().get(message.author.id) || null
 | 
			
		||||
	const roomID = select("channel_room", "room_id", "WHERE channel_id = ?").pluck().get(message.channel_id)
 | 
			
		||||
	let senderMxid = select("sim", "mxid", "WHERE discord_id = ?").pluck().get(message.author.id) || null
 | 
			
		||||
	if (senderMxid) {
 | 
			
		||||
		const senderIsInRoom = db.prepare("SELECT * FROM sim_member WHERE room_id = ? and mxid = ?").get(roomID, senderMxid)
 | 
			
		||||
		const senderIsInRoom = select("sim_member", "mxid", "WHERE room_id = ? AND mxid = ?").get(roomID, senderMxid)
 | 
			
		||||
		if (!senderIsInRoom) {
 | 
			
		||||
			senderMxid = null // just send as ooye bot
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	/** @type {{event_id: string, event_type: string, event_subtype: string?, part: number}[]} */
 | 
			
		||||
   const oldEventRows = db.prepare("SELECT event_id, event_type, event_subtype, part FROM event_message WHERE message_id = ?").all(message.id)
 | 
			
		||||
	const oldEventRows = select("event_message", ["event_id", "event_type", "event_subtype", "part"], "WHERE message_id = ?").all(message.id)
 | 
			
		||||
 | 
			
		||||
	// Figure out what we will be replacing them with
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -6,7 +6,7 @@ const assert = require("assert").strict
 | 
			
		|||
const {PNG} = require("pngjs")
 | 
			
		||||
 | 
			
		||||
const passthrough = require("../../passthrough")
 | 
			
		||||
const { sync, db, discord } = passthrough
 | 
			
		||||
const {sync, db, discord, select} = passthrough
 | 
			
		||||
/** @type {import("../../matrix/file")} */
 | 
			
		||||
const file = sync.require("../../matrix/file")
 | 
			
		||||
//** @type {import("../../matrix/mreq")} */
 | 
			
		||||
| 
						 | 
				
			
			@ -38,7 +38,7 @@ const Rlottie = (async () => {
 | 
			
		|||
 * @returns {Promise<{mxc: string, info: typeof INFO}>}
 | 
			
		||||
 */
 | 
			
		||||
async function convert(stickerItem) {
 | 
			
		||||
	const existingMxc = db.prepare("SELECT mxc FROM lottie WHERE id = ?").pluck().get(stickerItem.id)
 | 
			
		||||
	const existingMxc = select("lottie", "mxc", "WHERE id = ?").pluck().get(stickerItem.id)
 | 
			
		||||
	if (existingMxc) return {mxc: existingMxc, info: INFO}
 | 
			
		||||
	const r = await Rlottie
 | 
			
		||||
	const res = await fetch(file.DISCORD_IMAGES_BASE + file.sticker(stickerItem))
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,9 +1,9 @@
 | 
			
		|||
// @ts-check
 | 
			
		||||
 | 
			
		||||
const assert = require("assert")
 | 
			
		||||
const assert = require("assert").strict
 | 
			
		||||
 | 
			
		||||
const passthrough = require("../../passthrough")
 | 
			
		||||
const { discord, sync, db } = passthrough
 | 
			
		||||
const {discord, sync, db, select} = passthrough
 | 
			
		||||
/** @type {import("../../matrix/read-registration")} */
 | 
			
		||||
const reg = sync.require("../../matrix/read-registration.js")
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -17,8 +17,7 @@ const userRegex = reg.namespaces.users.map(u => new RegExp(u.regex))
 | 
			
		|||
 * @param {{api: import("../../matrix/api")}} di simple-as-nails dependency injection for the matrix API
 | 
			
		||||
 */
 | 
			
		||||
async function threadToAnnouncement(parentRoomID, threadRoomID, creatorMxid, thread, di) {
 | 
			
		||||
	/** @type {string?} */
 | 
			
		||||
	const branchedFromEventID = db.prepare("SELECT event_id FROM event_message WHERE message_id = ?").pluck().get(thread.id)
 | 
			
		||||
	const branchedFromEventID = select("event_message", "event_id", "WHERE message_id = ?").pluck().get(thread.id)
 | 
			
		||||
	/** @type {{"m.mentions"?: any, "m.in_reply_to"?: any}} */
 | 
			
		||||
	const context = {}
 | 
			
		||||
	if (branchedFromEventID) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,7 +3,7 @@
 | 
			
		|||
const assert = require("assert")
 | 
			
		||||
 | 
			
		||||
const passthrough = require("../../passthrough")
 | 
			
		||||
const { sync, db } = passthrough
 | 
			
		||||
const {select} = passthrough
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Downcased and stripped username. Can only include a basic set of characters.
 | 
			
		||||
| 
						 | 
				
			
			@ -53,7 +53,7 @@ function userToSimName(user) {
 | 
			
		|||
	assert.notEqual(user.discriminator, "0000", "cannot create user for a webhook")
 | 
			
		||||
 | 
			
		||||
	// 1. Is sim user already registered?
 | 
			
		||||
	const existing = db.prepare("SELECT sim_name FROM sim WHERE discord_id = ?").pluck().get(user.id)
 | 
			
		||||
	const existing = select("sim", "sim_name", "WHERE discord_id = ?").pluck().get(user.id)
 | 
			
		||||
	if (existing) return existing
 | 
			
		||||
 | 
			
		||||
	// 2. Register based on username (could be new or old format)
 | 
			
		||||
| 
						 | 
				
			
			@ -64,8 +64,7 @@ function userToSimName(user) {
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	// Check for conflicts with already registered sims
 | 
			
		||||
	/** @type {string[]} */
 | 
			
		||||
	const matches = db.prepare("SELECT sim_name FROM sim WHERE sim_name LIKE ? ESCAPE '@'").pluck().all(downcased + "%")
 | 
			
		||||
	const matches = select("sim", "sim_name", "WHERE sim_name LIKE ? ESCAPE '@'").pluck().all(downcased + "%")
 | 
			
		||||
	// Keep generating until we get a suggestion that doesn't conflict
 | 
			
		||||
	for (const suggestion of generateLocalpartAlternatives(preferences)) {
 | 
			
		||||
		if (!matches.includes(suggestion)) return suggestion
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,6 @@
 | 
			
		|||
const assert = require("assert").strict
 | 
			
		||||
const util = require("util")
 | 
			
		||||
const {sync, db} = require("../passthrough")
 | 
			
		||||
const {sync, db, select, from} = require("../passthrough")
 | 
			
		||||
 | 
			
		||||
/** @type {import("./actions/send-message")}) */
 | 
			
		||||
const sendMessage = sync.require("./actions/send-message")
 | 
			
		||||
| 
						 | 
				
			
			@ -42,7 +42,7 @@ module.exports = {
 | 
			
		|||
 | 
			
		||||
		const channelID = gatewayMessage.d.channel_id
 | 
			
		||||
		if (!channelID) return
 | 
			
		||||
		const roomID = db.prepare("SELECT room_id FROM channel_room WHERE channel_id = ?").pluck().get(channelID)
 | 
			
		||||
		const roomID = select("channel_room", "room_id", "WHERE channel_id = ?").pluck().get(channelID)
 | 
			
		||||
		if (!roomID) return
 | 
			
		||||
 | 
			
		||||
		let stackLines = e.stack.split("\n")
 | 
			
		||||
| 
						 | 
				
			
			@ -80,8 +80,8 @@ module.exports = {
 | 
			
		|||
	 */
 | 
			
		||||
	async checkMissedMessages(client, guild) {
 | 
			
		||||
		if (guild.unavailable) return
 | 
			
		||||
		const bridgedChannels = db.prepare("SELECT channel_id FROM channel_room").pluck().all()
 | 
			
		||||
		const prepared = db.prepare("SELECT 1 FROM event_message WHERE message_id = ?").pluck()
 | 
			
		||||
		const bridgedChannels = select("channel_room", "channel_id").pluck().all()
 | 
			
		||||
		const prepared = select("event_message", "1", "WHERE message_id = ?").pluck()
 | 
			
		||||
		for (const channel of guild.channels.concat(guild.threads)) {
 | 
			
		||||
			if (!bridgedChannels.includes(channel.id)) continue
 | 
			
		||||
			if (!channel.last_message_id) continue
 | 
			
		||||
| 
						 | 
				
			
			@ -125,7 +125,7 @@ module.exports = {
 | 
			
		|||
	 * @param {import("discord-api-types/v10").APIThreadChannel} thread
 | 
			
		||||
	 */
 | 
			
		||||
	async onThreadCreate(client, thread) {
 | 
			
		||||
		const parentRoomID = db.prepare("SELECT room_id FROM channel_room WHERE channel_id = ?").pluck().get(thread.parent_id)
 | 
			
		||||
		const parentRoomID = select("channel_room", "room_id", "WHERE channel_room = ?").pluck().get(thread.parent_id)
 | 
			
		||||
		if (!parentRoomID) return // Not interested in a thread if we aren't interested in its wider channel
 | 
			
		||||
		const threadRoomID = await createRoom.syncRoom(thread.id) // Create room (will share the same inflight as the initial message to the thread)
 | 
			
		||||
		await announceThread.announceThread(parentRoomID, threadRoomID, thread)
 | 
			
		||||
| 
						 | 
				
			
			@ -136,7 +136,7 @@ module.exports = {
 | 
			
		|||
	 * @param {import("discord-api-types/v10").GatewayGuildUpdateDispatchData} guild
 | 
			
		||||
	 */
 | 
			
		||||
	async onGuildUpdate(client, guild) {
 | 
			
		||||
		const spaceID = db.prepare("SELECT space_id FROM guild_space WHERE guild_id = ?").pluck().get(guild.id)
 | 
			
		||||
		const spaceID = select("guild_space", "space_id", "WHERE guild_id = ?").pluck().get(guild.id)
 | 
			
		||||
		if (!spaceID) return
 | 
			
		||||
		await createSpace.syncSpace(guild.id)
 | 
			
		||||
	},
 | 
			
		||||
| 
						 | 
				
			
			@ -147,7 +147,7 @@ module.exports = {
 | 
			
		|||
	 * @param {boolean} isThread
 | 
			
		||||
	 */
 | 
			
		||||
	async onChannelOrThreadUpdate(client, channelOrThread, isThread) {
 | 
			
		||||
		const roomID = db.prepare("SELECT room_id FROM channel_room WHERE channel_id = ?").get(channelOrThread.id)
 | 
			
		||||
		const roomID = select("channel_room", "room_id", "WHERE channel_id = ?").pluck().get(channelOrThread.id)
 | 
			
		||||
		if (!roomID) return // No target room to update the data on
 | 
			
		||||
		await createRoom.syncRoom(channelOrThread.id)
 | 
			
		||||
	},
 | 
			
		||||
| 
						 | 
				
			
			@ -158,7 +158,7 @@ module.exports = {
 | 
			
		|||
	 */
 | 
			
		||||
	async onMessageCreate(client, message) {
 | 
			
		||||
		if (message.webhook_id) {
 | 
			
		||||
			const row = db.prepare("SELECT webhook_id FROM webhook WHERE webhook_id = ?").pluck().get(message.webhook_id)
 | 
			
		||||
			const row = select("webhook", "1", "WHERE webhook_id = ?").pluck().get(message.webhook_id)
 | 
			
		||||
			if (row) {
 | 
			
		||||
				// The message was sent by the bridge's own webhook on discord. We don't want to reflect this back, so just drop it.
 | 
			
		||||
				return
 | 
			
		||||
| 
						 | 
				
			
			@ -179,7 +179,7 @@ module.exports = {
 | 
			
		|||
	 */
 | 
			
		||||
	async onMessageUpdate(client, data) {
 | 
			
		||||
		if (data.webhook_id) {
 | 
			
		||||
			const row = db.prepare("SELECT webhook_id FROM webhook WHERE webhook_id = ?").pluck().get(data.webhook_id)
 | 
			
		||||
			const row = select("webhook", "1", "WHERE webhook_id = ?").pluck().get(message.webhook_id)
 | 
			
		||||
			if (row) {
 | 
			
		||||
				// The update was sent by the bridge's own webhook on discord. We don't want to reflect this back, so just drop it.
 | 
			
		||||
				return
 | 
			
		||||
| 
						 | 
				
			
			@ -222,9 +222,9 @@ module.exports = {
 | 
			
		|||
	 * @param {import("discord-api-types/v10").GatewayTypingStartDispatchData} data
 | 
			
		||||
	 */
 | 
			
		||||
	async onTypingStart(client, data) {
 | 
			
		||||
		const roomID = db.prepare("SELECT room_id FROM channel_room WHERE channel_id = ?").pluck().get(data.channel_id)
 | 
			
		||||
		const roomID = select("channel_room", "room_id", "WHERE channel_id = ?").pluck().get(data.channel_id)
 | 
			
		||||
		if (!roomID) return
 | 
			
		||||
		const mxid = db.prepare("SELECT mxid FROM sim INNER JOIN sim_member USING (mxid) WHERE discord_id = ? AND room_id = ?").pluck().get(data.user_id, roomID)
 | 
			
		||||
		const mxid = from("sim").join("sim_member", "mxid").and("WHERE discord_id = ? AND room_id = ?").pluck("mxid").get(data.user_id, roomID)
 | 
			
		||||
		if (!mxid) return
 | 
			
		||||
		// Each Discord user triggers the notification every 8 seconds as long as they remain typing.
 | 
			
		||||
		// Discord does not send typing stopped events, so typing only stops if the timeout is reached or if the user sends their message.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										7
									
								
								db/orm-utils.d.ts
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										7
									
								
								db/orm-utils.d.ts
									
										
									
									
										vendored
									
									
								
							| 
						 | 
				
			
			@ -27,6 +27,11 @@ export type Models = {
 | 
			
		|||
		space_id: string
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	lottie: {
 | 
			
		||||
		id: string
 | 
			
		||||
		mxc: string
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	member_cache: {
 | 
			
		||||
		room_id: string
 | 
			
		||||
		mxid: string
 | 
			
		||||
| 
						 | 
				
			
			@ -49,7 +54,7 @@ export type Models = {
 | 
			
		|||
	sim_member: {
 | 
			
		||||
		mxid: string
 | 
			
		||||
		room_id: string
 | 
			
		||||
		profile_event_content_hash: string
 | 
			
		||||
		profile_event_content_hash: any
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	webhook: {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,7 +5,7 @@ const util = require("util")
 | 
			
		|||
const DiscordTypes = require("discord-api-types/v10")
 | 
			
		||||
const reg = require("../matrix/read-registration")
 | 
			
		||||
 | 
			
		||||
const {discord, sync, db} = require("../passthrough")
 | 
			
		||||
const {discord, sync, db, select} = require("../passthrough")
 | 
			
		||||
/** @type {import("../matrix/api")}) */
 | 
			
		||||
const api = sync.require("../matrix/api")
 | 
			
		||||
/** @type {import("../matrix/file")} */
 | 
			
		||||
| 
						 | 
				
			
			@ -80,7 +80,7 @@ const commands = [{
 | 
			
		|||
	execute: replyctx(
 | 
			
		||||
		async (message, channel, guild, ctx) => {
 | 
			
		||||
			// Guard
 | 
			
		||||
			const roomID = db.prepare("SELECT room_id FROM channel_room WHERE channel_id = ?").pluck().get(channel.id)
 | 
			
		||||
			const roomID = select("channel_room", "room_id", "WHERE channel_id = ?").pluck().get(channel.id)
 | 
			
		||||
			if (!roomID) return discord.snow.channel.createMessage(channel.id, {
 | 
			
		||||
				...ctx,
 | 
			
		||||
				content: "This channel isn't bridged to the other side."
 | 
			
		||||
| 
						 | 
				
			
			@ -124,8 +124,8 @@ const commands = [{
 | 
			
		|||
	execute: replyctx(
 | 
			
		||||
		async (message, channel, guild, ctx) => {
 | 
			
		||||
			// Check guild is bridged
 | 
			
		||||
			const spaceID = db.prepare("SELECT space_id FROM guild_space WHERE guild_id = ?").pluck().get(guild.id)
 | 
			
		||||
			const roomID = db.prepare("SELECT room_id FROM channel_room WHERE channel_id = ?").pluck().get(channel.id)
 | 
			
		||||
			const spaceID = select("guild_space", "space_id", "WHERE guild_id = ?").pluck().get(guild.id)
 | 
			
		||||
			const roomID = select("channel_room", "room_id", "WHERE channel_id = ?").pluck().get(channel.id)
 | 
			
		||||
			if (!spaceID || !roomID) return discord.snow.channel.createMessage(channel.id, {
 | 
			
		||||
				...ctx,
 | 
			
		||||
				content: "This server isn't bridged to Matrix, so you can't invite Matrix users."
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,15 +4,15 @@ const assert = require("assert").strict
 | 
			
		|||
const Ty = require("../../types")
 | 
			
		||||
 | 
			
		||||
const passthrough = require("../../passthrough")
 | 
			
		||||
const { discord, sync, db } = passthrough
 | 
			
		||||
const {discord, sync, db, select} = passthrough
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @param {Ty.Event.Outer<Ty.Event.M_Reaction>} event
 | 
			
		||||
 */
 | 
			
		||||
async function addReaction(event) {
 | 
			
		||||
	const channelID = db.prepare("SELECT channel_id FROM channel_room WHERE room_id = ?").pluck().get(event.room_id)
 | 
			
		||||
	const channelID = select("channel_room", "channel_id", "WHERE room_id = ?").pluck().get(event.room_id)
 | 
			
		||||
	if (!channelID) return // We just assume the bridge has already been created
 | 
			
		||||
	const messageID = db.prepare("SELECT message_id FROM event_message WHERE event_id = ? AND part = 0").pluck().get(event.content["m.relates_to"].event_id) // 0 = primary
 | 
			
		||||
	const messageID = select("event_message", "message_id", "WHERE event_id = ? AND part = 0").pluck().get(event.content["m.relates_to"].event_id) // 0 = primary
 | 
			
		||||
	if (!messageID) return // Nothing can be done if the parent message was never bridged.
 | 
			
		||||
 | 
			
		||||
	// no need to sync the matrix member to the other side. but if I did need to, this is where I'd do it
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,7 +3,7 @@
 | 
			
		|||
const assert = require("assert").strict
 | 
			
		||||
const DiscordTypes = require("discord-api-types/v10")
 | 
			
		||||
const passthrough = require("../../passthrough")
 | 
			
		||||
const {discord, db} = passthrough
 | 
			
		||||
const {discord, db, select} = passthrough
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Look in the database to find webhook credentials for a channel.
 | 
			
		||||
| 
						 | 
				
			
			@ -14,10 +14,13 @@ const {discord, db} = passthrough
 | 
			
		|||
 */
 | 
			
		||||
async function ensureWebhook(channelID, forceCreate = false) {
 | 
			
		||||
	if (!forceCreate) {
 | 
			
		||||
		/** @type {{id: string, token: string} | null} */
 | 
			
		||||
		const row = db.prepare("SELECT webhook_id as id, webhook_token as token FROM webhook WHERE channel_id = ?").get(channelID)
 | 
			
		||||
		const row = select("webhook", ["webhook_id", "webhook_token"], "WHERE channel_id = ?").get(channelID)
 | 
			
		||||
		if (row) {
 | 
			
		||||
			return {created: false, ...row}
 | 
			
		||||
			return {
 | 
			
		||||
				id: row.webhook_id,
 | 
			
		||||
				token: row.webhook_token,
 | 
			
		||||
				created: false
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,7 +7,7 @@ const {promisify} = require("util")
 | 
			
		|||
const Ty = require("../../types")
 | 
			
		||||
const DiscordTypes = require("discord-api-types/v10")
 | 
			
		||||
const passthrough = require("../../passthrough")
 | 
			
		||||
const {sync, discord, db} = passthrough
 | 
			
		||||
const {sync, discord, db, select} = passthrough
 | 
			
		||||
 | 
			
		||||
/** @type {import("./channel-webhook")} */
 | 
			
		||||
const channelWebhook = sync.require("./channel-webhook")
 | 
			
		||||
| 
						 | 
				
			
			@ -53,7 +53,8 @@ async function resolvePendingFiles(message) {
 | 
			
		|||
/** @param {Ty.Event.Outer_M_Room_Message | Ty.Event.Outer_M_Room_Message_File | Ty.Event.Outer_M_Sticker} event */
 | 
			
		||||
async function sendEvent(event) {
 | 
			
		||||
	// TODO: we just assume the bridge has already been created, is that really ok?
 | 
			
		||||
	const row = db.prepare("SELECT channel_id, thread_parent FROM channel_room WHERE room_id = ?").get(event.room_id)
 | 
			
		||||
	const row = select("channel_room", ["channel_id", "thread_parent"], "WHERE room_id = ?").get(event.room_id)
 | 
			
		||||
	assert(row)
 | 
			
		||||
	let channelID = row.channel_id
 | 
			
		||||
	let threadID = undefined
 | 
			
		||||
	if (row.thread_parent) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,7 +7,7 @@ const TurndownService = require("turndown")
 | 
			
		|||
const assert = require("assert").strict
 | 
			
		||||
 | 
			
		||||
const passthrough = require("../../passthrough")
 | 
			
		||||
const { sync, db, discord } = passthrough
 | 
			
		||||
const {sync, db, discord, select, from} = passthrough
 | 
			
		||||
/** @type {import("../../matrix/file")} */
 | 
			
		||||
const file = sync.require("../../matrix/file")
 | 
			
		||||
/** @type {import("../converters/utils")} */
 | 
			
		||||
| 
						 | 
				
			
			@ -148,7 +148,7 @@ turndownService.addRule("fencedCodeBlock", {
 | 
			
		|||
 * @returns {Promise<{displayname?: string?, avatar_url?: string?}>}
 | 
			
		||||
 */
 | 
			
		||||
async function getMemberFromCacheOrHomeserver(roomID, mxid, api) {
 | 
			
		||||
	const row = db.prepare("SELECT displayname, avatar_url FROM member_cache WHERE room_id = ? AND mxid = ?").get(roomID, mxid)
 | 
			
		||||
	const row = select("member_cache", ["displayname", "avatar_url"], "WHERE room_id = ? AND mxid = ?").get(roomID, mxid)
 | 
			
		||||
	if (row) return row
 | 
			
		||||
	return api.getStateEvent(roomID, "m.room.member", mxid).then(event => {
 | 
			
		||||
		db.prepare("REPLACE INTO member_cache (room_id, mxid, displayname, avatar_url) VALUES (?, ?, ?, ?)").run(roomID, mxid, event?.displayname || null, event?.avatar_url || null)
 | 
			
		||||
| 
						 | 
				
			
			@ -230,7 +230,7 @@ async function eventToMessage(event, guild, di) {
 | 
			
		|||
			if (relType !== "m.replace") return
 | 
			
		||||
			const originalEventId = relatesTo.event_id
 | 
			
		||||
			if (!originalEventId) return
 | 
			
		||||
			messageIDsToEdit = db.prepare("SELECT message_id FROM event_message WHERE event_id = ? ORDER BY part").pluck().all(originalEventId)
 | 
			
		||||
			messageIDsToEdit = select("event_message", "message_id", "WHERE event_id = ? ORDER BY part").pluck().all(originalEventId)
 | 
			
		||||
			if (!messageIDsToEdit.length) return
 | 
			
		||||
 | 
			
		||||
			// Ok, it's an edit.
 | 
			
		||||
| 
						 | 
				
			
			@ -261,7 +261,7 @@ async function eventToMessage(event, guild, di) {
 | 
			
		|||
			if (!repliedToEventId) return
 | 
			
		||||
			let repliedToEvent = await di.api.getEvent(event.room_id, repliedToEventId)
 | 
			
		||||
			if (!repliedToEvent) return
 | 
			
		||||
			const row = db.prepare("SELECT channel_id, message_id FROM event_message INNER JOIN message_channel USING (message_id) WHERE event_id = ? ORDER BY part").get(repliedToEventId)
 | 
			
		||||
			const row = from("event_message").join("message_channel", "message_id").select("channel_id", "message_id").and("WHERE event_id = ? ORDER BY part").get(repliedToEventId)
 | 
			
		||||
			if (row) {
 | 
			
		||||
				replyLine = `<:L1:1144820033948762203><:L2:1144820084079087647>https://discord.com/channels/${guild.id}/${row.channel_id}/${row.message_id} `
 | 
			
		||||
			} else {
 | 
			
		||||
| 
						 | 
				
			
			@ -269,7 +269,7 @@ async function eventToMessage(event, guild, di) {
 | 
			
		|||
			}
 | 
			
		||||
			const sender = repliedToEvent.sender
 | 
			
		||||
			const senderName = sender.match(/@([^:]*)/)?.[1] || sender
 | 
			
		||||
			const authorID = db.prepare("SELECT discord_id FROM sim WHERE mxid = ?").pluck().get(repliedToEvent.sender)
 | 
			
		||||
			const authorID = select("sim", "discord_id", "WHERE mxid = ?").pluck().get(repliedToEvent.sender)
 | 
			
		||||
			if (authorID) {
 | 
			
		||||
				replyLine += `<@${authorID}>`
 | 
			
		||||
			} else {
 | 
			
		||||
| 
						 | 
				
			
			@ -312,14 +312,14 @@ async function eventToMessage(event, guild, di) {
 | 
			
		|||
			// Handling mentions of Discord users
 | 
			
		||||
			input = input.replace(/("https:\/\/matrix.to\/#\/(@[^"]+)")>/g, (whole, attributeValue, mxid) => {
 | 
			
		||||
				if (!utils.eventSenderIsFromDiscord(mxid)) return whole
 | 
			
		||||
				const userID = db.prepare("SELECT discord_id FROM sim WHERE mxid = ?").pluck().get(mxid)
 | 
			
		||||
				const userID = select("sim", "discord_id", "WHERE mxid = ?").pluck().get(mxid)
 | 
			
		||||
				if (!userID) return whole
 | 
			
		||||
				return `${attributeValue} data-user-id="${userID}">`
 | 
			
		||||
			})
 | 
			
		||||
 | 
			
		||||
			// Handling mentions of Discord rooms
 | 
			
		||||
			input = input.replace(/("https:\/\/matrix.to\/#\/(![^"]+)")>/g, (whole, attributeValue, roomID) => {
 | 
			
		||||
				const channelID = db.prepare("SELECT channel_id FROM channel_room WHERE room_id = ?").pluck().get(roomID)
 | 
			
		||||
				const channelID = select("channel_room", "channel_id", "WHERE room_id = ?").pluck().get(roomID)
 | 
			
		||||
				if (!channelID) return whole
 | 
			
		||||
				return `${attributeValue} data-channel-id="${channelID}">`
 | 
			
		||||
			})
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,7 +2,7 @@ const {test} = require("supertape")
 | 
			
		|||
const {eventToMessage} = require("./event-to-message")
 | 
			
		||||
const data = require("../../test/data")
 | 
			
		||||
const {MatrixServerError} = require("../../matrix/mreq")
 | 
			
		||||
const {db} = require("../../passthrough")
 | 
			
		||||
const {db, select} = require("../../passthrough")
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @param {string} roomID
 | 
			
		||||
| 
						 | 
				
			
			@ -1414,7 +1414,8 @@ test("event2message: caches the member if the member is not known", async t => {
 | 
			
		|||
			}]
 | 
			
		||||
		}
 | 
			
		||||
	)
 | 
			
		||||
	t.deepEqual(db.prepare("SELECT avatar_url, displayname, mxid FROM member_cache WHERE room_id = '!should_be_newly_cached:cadence.moe'").all(), [
 | 
			
		||||
 | 
			
		||||
	t.deepEqual(select("member_cache", ["avatar_url", "displayname", "mxid"], "WHERE room_id = '!should_be_newly_cached:cadence.moe'").all(), [
 | 
			
		||||
		{avatar_url: "mxc://cadence.moe/this_is_the_avatar", displayname: null, mxid: "@should_be_newly_cached:cadence.moe"}
 | 
			
		||||
	])
 | 
			
		||||
	t.equal(called, 1, "getStateEvent should be called once")
 | 
			
		||||
| 
						 | 
				
			
			@ -1457,7 +1458,7 @@ test("event2message: skips caching the member if the member does not exist, some
 | 
			
		|||
			}]
 | 
			
		||||
		}
 | 
			
		||||
	)
 | 
			
		||||
	t.deepEqual(db.prepare("SELECT avatar_url, displayname, mxid FROM member_cache WHERE room_id = '!not_real:cadence.moe'").all(), [])
 | 
			
		||||
	t.deepEqual(select("member_cache", ["avatar_url", "displayname", "mxid"], "WHERE room_id = '!not_real:cadence.moe'").all(), [])
 | 
			
		||||
	t.equal(called, 1, "getStateEvent should be called once")
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1500,7 +1501,7 @@ test("event2message: overly long usernames are shifted into the message content"
 | 
			
		|||
			}]
 | 
			
		||||
		}
 | 
			
		||||
	)
 | 
			
		||||
	t.deepEqual(db.prepare("SELECT avatar_url, displayname, mxid FROM member_cache WHERE room_id = '!should_be_newly_cached_2:cadence.moe'").all(), [
 | 
			
		||||
	t.deepEqual(select("member_cache", ["avatar_url", "displayname", "mxid"], "WHERE room_id = '!should_be_newly_cached_2:cadence.moe'").all(), [
 | 
			
		||||
		{avatar_url: null, displayname: "I am BLACK I am WHITE I am SHORT I am LONG I am EVERYTHING YOU THINK IS IMPORTANT and I DON'T MATTER", mxid: "@should_be_newly_cached_2:cadence.moe"}
 | 
			
		||||
	])
 | 
			
		||||
	t.equal(called, 1, "getStateEvent should be called once")
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,7 +3,7 @@
 | 
			
		|||
const fetch = require("node-fetch").default
 | 
			
		||||
 | 
			
		||||
const passthrough = require("../passthrough")
 | 
			
		||||
const { sync, db } = passthrough
 | 
			
		||||
const {sync, db, select} = passthrough
 | 
			
		||||
/** @type {import("./mreq")} */
 | 
			
		||||
const mreq = sync.require("./mreq")
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -33,7 +33,7 @@ async function uploadDiscordFileToMxc(path) {
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	// Has this file already been uploaded in the past? Grab the existing copy from the database.
 | 
			
		||||
	const existingFromDb = db.prepare("SELECT mxc_url FROM file WHERE discord_url = ?").pluck().get(url)
 | 
			
		||||
	const existingFromDb = select("file", "mxc_url", "WHERE discord_url = ?").pluck().get(url)
 | 
			
		||||
	if (typeof existingFromDb === "string") {
 | 
			
		||||
		return existingFromDb
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue