Compare commits
	
		
			19 commits
		
	
	
		
			23d87fb9a4
			...
			ab69eab8a4
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| ab69eab8a4 | |||
| 717dc185e5 | |||
| 7932f8af85 | |||
| 0776cc6ccd | |||
| e7b4dfea9c | |||
| ea08e16963 | |||
| 1efd301e1d | |||
| dc7b444086 | |||
| 255e166e8c | |||
| d4f4664c25 | |||
| 3de762d428 | |||
| cffd3c9f2e | |||
| 5b7433de32 | |||
| 7916f82b55 | |||
| 7905802825 | |||
| 3891506163 | |||
| d8e6de62e5 | |||
| 5a152b87b8 | |||
| a968bacffd | 
					 18 changed files with 181 additions and 60 deletions
				
			
		
							
								
								
									
										66
									
								
								package-lock.json
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										66
									
								
								package-lock.json
									
										
									
										generated
									
									
									
								
							| 
						 | 
				
			
			@ -10,7 +10,7 @@
 | 
			
		|||
      "license": "AGPL-3.0-or-later",
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "@chriscdn/promise-semaphore": "^3.0.1",
 | 
			
		||||
        "@cloudrac3r/discord-markdown": "^2.6.5",
 | 
			
		||||
        "@cloudrac3r/discord-markdown": "^2.6.7",
 | 
			
		||||
        "@cloudrac3r/giframe": "^0.4.3",
 | 
			
		||||
        "@cloudrac3r/html-template-tag": "^5.0.1",
 | 
			
		||||
        "@cloudrac3r/in-your-element": "^1.1.1",
 | 
			
		||||
| 
						 | 
				
			
			@ -119,9 +119,9 @@
 | 
			
		|||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/@chriscdn/promise-semaphore": {
 | 
			
		||||
      "version": "3.0.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@chriscdn/promise-semaphore/-/promise-semaphore-3.0.1.tgz",
 | 
			
		||||
      "integrity": "sha512-fVlCnoYE4hDzpcYRPtmN7dmcpmd2zxyPWjyfjIKI9Y+gsI7rwZSkjtuwMi8HFtlkSmNh8L7Zr37hdqeL13sYrw==",
 | 
			
		||||
      "version": "3.1.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@chriscdn/promise-semaphore/-/promise-semaphore-3.1.1.tgz",
 | 
			
		||||
      "integrity": "sha512-ALLLLYlPfd/QZLptcVi6HQRK1zaCDWZoqYYw+axLmCatFs4gVTSZ5nqlyxwFe4qwR/K84HvOMa9hxda881FqMA==",
 | 
			
		||||
      "license": "MIT"
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/@cloudcmd/stub": {
 | 
			
		||||
| 
						 | 
				
			
			@ -225,9 +225,9 @@
 | 
			
		|||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/@cloudrac3r/discord-markdown": {
 | 
			
		||||
      "version": "2.6.5",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@cloudrac3r/discord-markdown/-/discord-markdown-2.6.5.tgz",
 | 
			
		||||
      "integrity": "sha512-B4uQNsyva5JNW0CVYkcunMQwWfrok1Hd5FYww/cWcvb98zp/pJdJfE3hoRl9EbnxNK2l62IJQ9j8HmssMFHJ9Q==",
 | 
			
		||||
      "version": "2.6.7",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@cloudrac3r/discord-markdown/-/discord-markdown-2.6.7.tgz",
 | 
			
		||||
      "integrity": "sha512-bWLmBYWaNEDcQfZHDz4jaAxLKA9161ruEnHo3ms6kfRw8uYku/Uz7U1xTmQ2dQF/q1PiuBvM9I37pLiotlQj8A==",
 | 
			
		||||
      "license": "MIT",
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "simple-markdown": "^0.7.3"
 | 
			
		||||
| 
						 | 
				
			
			@ -949,9 +949,9 @@
 | 
			
		|||
      "license": "MIT"
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/@stackoverflow/stacks": {
 | 
			
		||||
      "version": "2.8.3",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@stackoverflow/stacks/-/stacks-2.8.3.tgz",
 | 
			
		||||
      "integrity": "sha512-ZGBeuXJC7moK/f+lgl2dCAW85etD/RO0DNubocdH2qzpJMuuGXX0GMeEAfrTOe+B00I8E1OqTnS1cpkqGdHBdQ==",
 | 
			
		||||
      "version": "2.8.4",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@stackoverflow/stacks/-/stacks-2.8.4.tgz",
 | 
			
		||||
      "integrity": "sha512-FfA7Bw7a0AQrMw3/bG6G4BUrZ698F7Cdk6HkR9T7jdaufORkiX5d16wI4j4b5Sqm1FwkaZAF+ZSKLL1w0tAsew==",
 | 
			
		||||
      "license": "MIT",
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "@hotwired/stimulus": "^3.2.2",
 | 
			
		||||
| 
						 | 
				
			
			@ -1107,9 +1107,9 @@
 | 
			
		|||
      "dev": true
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/@types/node": {
 | 
			
		||||
      "version": "22.17.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@types/node/-/node-22.17.1.tgz",
 | 
			
		||||
      "integrity": "sha512-y3tBaz+rjspDTylNjAX37jEC3TETEFGNJL6uQDxwF9/8GLLIjW1rvVHlynyuUKMnMr1Roq8jOv3vkopBjC4/VA==",
 | 
			
		||||
      "version": "22.18.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@types/node/-/node-22.18.0.tgz",
 | 
			
		||||
      "integrity": "sha512-m5ObIqwsUp6BZzyiy4RdZpzWGub9bqLJMvZDD0QMXhxjqMHMENlj+SqF5QxoUwaQNFe+8kz8XM8ZQhqkQPTgMQ==",
 | 
			
		||||
      "dev": true,
 | 
			
		||||
      "license": "MIT",
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
| 
						 | 
				
			
			@ -1452,18 +1452,30 @@
 | 
			
		|||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/cloudstorm": {
 | 
			
		||||
      "version": "0.14.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/cloudstorm/-/cloudstorm-0.14.0.tgz",
 | 
			
		||||
      "integrity": "sha512-EgjMGxb2Z+L6Acti6DzL/bEbR495AIqPThyW4DaG6Jpvd0ZuM5eC13EiyxV8wlqAME612QO2LjqbhkdXn/327Q==",
 | 
			
		||||
      "version": "0.14.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/cloudstorm/-/cloudstorm-0.14.1.tgz",
 | 
			
		||||
      "integrity": "sha512-x95WCKg818E1rE1Ru45NPD3RoIq0pg3WxwvF0GE7Eq07pAeLcjSRqM1lUmbmfjdOqZrWdSRYA1NETVZ8QhVrIA==",
 | 
			
		||||
      "license": "MIT",
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "discord-api-types": "^0.38.12",
 | 
			
		||||
        "snowtransfer": "^0.14.2"
 | 
			
		||||
        "discord-api-types": "^0.38.21",
 | 
			
		||||
        "snowtransfer": "^0.15.0"
 | 
			
		||||
      },
 | 
			
		||||
      "engines": {
 | 
			
		||||
        "node": ">=22.0.0"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/cloudstorm/node_modules/snowtransfer": {
 | 
			
		||||
      "version": "0.15.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/snowtransfer/-/snowtransfer-0.15.0.tgz",
 | 
			
		||||
      "integrity": "sha512-kEDGKtFiH5nSkHsDZonEUuDx99lUasJoZ7AGrgvE8HzVG59vjvqc//C+pjWj4DuJqTj4Q+Z1L/M/MYNim8F2VA==",
 | 
			
		||||
      "license": "MIT",
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "discord-api-types": "^0.38.21"
 | 
			
		||||
      },
 | 
			
		||||
      "engines": {
 | 
			
		||||
        "node": ">=16.15.0"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/color": {
 | 
			
		||||
      "version": "4.2.3",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz",
 | 
			
		||||
| 
						 | 
				
			
			@ -1616,9 +1628,9 @@
 | 
			
		|||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/discord-api-types": {
 | 
			
		||||
      "version": "0.38.19",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.38.19.tgz",
 | 
			
		||||
      "integrity": "sha512-NUNMTgjYrgxt7wrTNEqnEez4hIAYbfyBpsjxT5gW7+82GjQCPDZvN+em6t+4/P5kGWnnwDa4ci070BV7eI6GbA==",
 | 
			
		||||
      "version": "0.38.22",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.38.22.tgz",
 | 
			
		||||
      "integrity": "sha512-2gnYrgXN3yTlv2cKBISI/A8btZwsSZLwKpIQXeI1cS8a7W7wP3sFVQOm3mPuuinTD8jJCKGPGNH399zE7Un1kA==",
 | 
			
		||||
      "license": "MIT",
 | 
			
		||||
      "workspaces": [
 | 
			
		||||
        "scripts/actions/documentation"
 | 
			
		||||
| 
						 | 
				
			
			@ -3076,9 +3088,9 @@
 | 
			
		|||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/tar-fs": {
 | 
			
		||||
      "version": "2.1.3",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.3.tgz",
 | 
			
		||||
      "integrity": "sha512-090nwYJDmlhwFwEW3QQl+vaNnxsO2yVsd45eTKRBzSzu+hlb1w2K9inVq5b0ngXuLVqQ4ApvsUHHnu/zQNkWAg==",
 | 
			
		||||
      "version": "2.1.4",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz",
 | 
			
		||||
      "integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==",
 | 
			
		||||
      "license": "MIT",
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "chownr": "^1.1.1",
 | 
			
		||||
| 
						 | 
				
			
			@ -3447,9 +3459,9 @@
 | 
			
		|||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/zod": {
 | 
			
		||||
      "version": "4.0.17",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/zod/-/zod-4.0.17.tgz",
 | 
			
		||||
      "integrity": "sha512-1PHjlYRevNxxdy2JZ8JcNAw7rX8V9P1AKkP+x/xZfxB0K5FYfuV+Ug6P/6NVSR2jHQ+FzDDoDHS04nYUsOIyLQ==",
 | 
			
		||||
      "version": "4.1.5",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.5.tgz",
 | 
			
		||||
      "integrity": "sha512-rcUUZqlLJgBC33IT3PNMgsCq6TzLQEG/Ei/KTCU0PedSWRMAXoOUN+4t/0H+Q8bdnLPdqUYnvboJT0bn/229qg==",
 | 
			
		||||
      "license": "MIT",
 | 
			
		||||
      "funding": {
 | 
			
		||||
        "url": "https://github.com/sponsors/colinhacks"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -19,7 +19,7 @@
 | 
			
		|||
  },
 | 
			
		||||
  "dependencies": {
 | 
			
		||||
    "@chriscdn/promise-semaphore": "^3.0.1",
 | 
			
		||||
    "@cloudrac3r/discord-markdown": "^2.6.5",
 | 
			
		||||
    "@cloudrac3r/discord-markdown": "^2.6.7",
 | 
			
		||||
    "@cloudrac3r/giframe": "^0.4.3",
 | 
			
		||||
    "@cloudrac3r/html-template-tag": "^5.0.1",
 | 
			
		||||
    "@cloudrac3r/in-your-element": "^1.1.1",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -120,16 +120,28 @@ function defineEchoHandler() {
 | 
			
		|||
		/** @type {string} */ // @ts-ignore
 | 
			
		||||
		const serverOrigin = await serverOriginPrompt.run()
 | 
			
		||||
 | 
			
		||||
		console.log("OOYE has its own web server. It needs to be accessible on the public internet.")
 | 
			
		||||
		console.log("What port would you like OOYE to use? You can connect your reverse proxy to this port later.")
 | 
			
		||||
		/** @type {{socket: string | number}} */
 | 
			
		||||
		const portResponse = await prompt({
 | 
			
		||||
			type: "input",
 | 
			
		||||
			name: "socket",
 | 
			
		||||
			message: "Web server port",
 | 
			
		||||
			initial: "6693"
 | 
			
		||||
		})
 | 
			
		||||
		portResponse.socket = +portResponse.socket || portResponse.socket // convert to number if numeric
 | 
			
		||||
		
 | 
			
		||||
		const app = createApp()
 | 
			
		||||
		app.use(defineEchoHandler())
 | 
			
		||||
		const server = createServer(toNodeListener(app))
 | 
			
		||||
		await server.listen(6693)
 | 
			
		||||
		await server.listen(portResponse.socket)
 | 
			
		||||
 | 
			
		||||
		console.log("OOYE has its own web server. It needs to be accessible on the public internet.")
 | 
			
		||||
		console.log("You need to enter a public URL where you will be able to host this web server.")
 | 
			
		||||
		console.log("OOYE listens on localhost:6693, so you will probably have to set up a reverse proxy.")
 | 
			
		||||
		console.log("Now you need to enter a public URL that OOYE's web server will live on.")
 | 
			
		||||
		console.log("Set up your reverse proxy so that this URL accesses OOYE.")
 | 
			
		||||
		console.log("Examples: https://gitdab.com/cadence/out-of-your-element/src/branch/main/docs/get-started.md#appendix")
 | 
			
		||||
		console.log("Now listening on port 6693. Feel free to send some test requests.")
 | 
			
		||||
		if (typeof portResponse.socket === "number") {
 | 
			
		||||
			console.log(`Now listening on http://localhost:${portResponse.socket}. Feel free to send some test requests.`)
 | 
			
		||||
		}
 | 
			
		||||
		/** @type {{bridge_origin: string}} */
 | 
			
		||||
		const bridgeOriginResponse = await prompt({
 | 
			
		||||
			type: "input",
 | 
			
		||||
| 
						 | 
				
			
			@ -255,6 +267,7 @@ function defineEchoHandler() {
 | 
			
		|||
		reg = {
 | 
			
		||||
			...template,
 | 
			
		||||
			url: bridgeOriginResponse.bridge_origin,
 | 
			
		||||
			...portResponse,
 | 
			
		||||
			ooye: {
 | 
			
		||||
				...template.ooye,
 | 
			
		||||
				...bridgeOriginResponse,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -146,7 +146,7 @@ async function syncUser(messageID, author, roomID, shouldActuallySync) {
 | 
			
		|||
	try {
 | 
			
		||||
		// API lookup
 | 
			
		||||
		var pkMessage = await fetchMessage(messageID)
 | 
			
		||||
		db.prepare("INSERT OR IGNORE INTO sim_proxy (user_id, proxy_owner_id, displayname) VALUES (?, ?, ?)").run(pkMessage.member.uuid, pkMessage.sender, author.username)
 | 
			
		||||
		db.prepare("REPLACE INTO sim_proxy (user_id, proxy_owner_id, displayname) VALUES (?, ?, ?)").run(pkMessage.member.uuid, pkMessage.sender, author.username)
 | 
			
		||||
	} catch (e) {
 | 
			
		||||
		// Fall back to offline cache
 | 
			
		||||
		const senderMxid = from("sim_proxy").join("sim", "user_id").join("sim_member", "mxid").where({displayname: author.username, room_id: roomID}).pluck("mxid").get()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -33,9 +33,10 @@ function getDiscordParseCallbacks(message, guild, useHTML) {
 | 
			
		|||
		user: node => {
 | 
			
		||||
			const mxid = select("sim", "mxid", {user_id: node.id}).pluck().get()
 | 
			
		||||
			const interaction = message.interaction_metadata || message.interaction
 | 
			
		||||
			const username = message.mentions.find(ment => ment.id === node.id)?.username
 | 
			
		||||
				|| message.referenced_message?.mentions.find(ment => ment.id === node.id)?.username
 | 
			
		||||
			const username = message.mentions?.find(ment => ment.id === node.id)?.username
 | 
			
		||||
				|| message.referenced_message?.mentions?.find(ment => ment.id === node.id)?.username
 | 
			
		||||
				|| (interaction?.user.id === node.id ? interaction.user.username : null)
 | 
			
		||||
				|| (message.author.id === node.id ? message.author.username : null)
 | 
			
		||||
				|| node.id
 | 
			
		||||
			if (mxid && useHTML) {
 | 
			
		||||
				return `<a href="https://matrix.to/#/${mxid}">@${username}</a>`
 | 
			
		||||
| 
						 | 
				
			
			@ -407,13 +408,13 @@ async function messageToEvent(message, guild, options = {}, di) {
 | 
			
		|||
 | 
			
		||||
		async function transformParsedVia(parsed) {
 | 
			
		||||
			for (const node of parsed) {
 | 
			
		||||
				if (node.type === "discordChannel") {
 | 
			
		||||
				if (node.type === "discordChannel" || node.type === "discordChannelLink") {
 | 
			
		||||
					node.row = select("channel_room", ["room_id", "name", "nick"], {channel_id: node.id}).get()
 | 
			
		||||
					if (node.row?.room_id) {
 | 
			
		||||
						node.via = await getViaServersMemo(node.row.room_id)
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
				;for (const maybeChildNodesArray of [node, node.content, node.items]) {
 | 
			
		||||
				for (const maybeChildNodesArray of [node, node.content, node.items]) {
 | 
			
		||||
					if (Array.isArray(maybeChildNodesArray)) {
 | 
			
		||||
						await transformParsedVia(maybeChildNodesArray)
 | 
			
		||||
					}
 | 
			
		||||
| 
						 | 
				
			
			@ -610,7 +611,7 @@ async function messageToEvent(message, guild, options = {}, di) {
 | 
			
		|||
			const event = invite.guild_scheduled_event
 | 
			
		||||
			if (!event) continue // the event ID provided was not valid
 | 
			
		||||
 | 
			
		||||
			const formatter = new Intl.DateTimeFormat("en-NZ", {month: "long", day: "numeric", hour: "numeric", minute: "2-digit", timeZoneName: "shortGeneric"}) // 9 June at 3:00 pm NZT
 | 
			
		||||
			const formatter = new Intl.DateTimeFormat("en-NZ", {month: "long", day: "numeric", hour: "numeric", minute: "2-digit", timeZoneName: "shortGeneric", timeZone: reg.ooye.time_zone}) // 9 June at 3:00 pm NZT
 | 
			
		||||
			const rep = new mxUtils.MatrixStringBuilder()
 | 
			
		||||
 | 
			
		||||
			// Add time
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -100,6 +100,44 @@ test("message2event: simple room mention", async t => {
 | 
			
		|||
	t.equal(called, 2, "should call getStateEvent and getJoinedMembers once each")
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
test("message2event: simple room link", async t => {
 | 
			
		||||
	let called = 0
 | 
			
		||||
	const events = await messageToEvent(data.message.simple_room_link, data.guild.general, {}, {
 | 
			
		||||
		api: {
 | 
			
		||||
			async getStateEvent(roomID, type, key) {
 | 
			
		||||
				called++
 | 
			
		||||
				t.equal(roomID, "!BnKuBPCvyfOkhcUjEu:cadence.moe")
 | 
			
		||||
				t.equal(type, "m.room.power_levels")
 | 
			
		||||
				t.equal(key, "")
 | 
			
		||||
				return {
 | 
			
		||||
					users: {
 | 
			
		||||
						"@_ooye_bot:cadence.moe": 100
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			},
 | 
			
		||||
			async getJoinedMembers(roomID) {
 | 
			
		||||
				called++
 | 
			
		||||
				t.equal(roomID, "!BnKuBPCvyfOkhcUjEu:cadence.moe")
 | 
			
		||||
				return {
 | 
			
		||||
					joined: {
 | 
			
		||||
						"@_ooye_bot:cadence.moe": {display_name: null, avatar_url: null},
 | 
			
		||||
						"@user:matrix.org": {display_name: null, avatar_url: null}
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	})
 | 
			
		||||
	t.deepEqual(events, [{
 | 
			
		||||
		$type: "m.room.message",
 | 
			
		||||
		"m.mentions": {},
 | 
			
		||||
		msgtype: "m.text",
 | 
			
		||||
		body: "#worm-farm",
 | 
			
		||||
		format: "org.matrix.custom.html",
 | 
			
		||||
		formatted_body: '<a href="https://matrix.to/#/!BnKuBPCvyfOkhcUjEu:cadence.moe?via=cadence.moe&via=matrix.org">#worm-farm</a>'
 | 
			
		||||
	}])
 | 
			
		||||
	t.equal(called, 2, "should call getStateEvent and getJoinedMembers once each")
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
test("message2event: nicked room mention", async t => {
 | 
			
		||||
	let called = 0
 | 
			
		||||
	const events = await messageToEvent(data.message.nicked_room_mention, data.guild.general, {}, {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -32,13 +32,10 @@ async function threadToAnnouncement(parentRoomID, threadRoomID, creatorMxid, thr
 | 
			
		|||
	const template = creatorMxid ? "started a thread:" : "Thread started:"
 | 
			
		||||
	const via = await mxUtils.getViaServersQuery(threadRoomID, di.api)
 | 
			
		||||
	let body = `${template} ${thread.name} https://matrix.to/#/${threadRoomID}?${via.toString()}`
 | 
			
		||||
	let html = `${template} <a href="https://matrix.to/#/${threadRoomID}?${via.toString()}">${thread.name}</a>`
 | 
			
		||||
 | 
			
		||||
	return {
 | 
			
		||||
		msgtype,
 | 
			
		||||
		body,
 | 
			
		||||
		format: "org.matrix.custom.html",
 | 
			
		||||
		formatted_body: html,
 | 
			
		||||
		"m.mentions": {},
 | 
			
		||||
		...context
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -55,8 +55,6 @@ test("thread2announcement: no known creator, no branched from event", async t =>
 | 
			
		|||
	t.deepEqual(content, {
 | 
			
		||||
		msgtype: "m.text",
 | 
			
		||||
		body: "Thread started: test thread https://matrix.to/#/!thread?via=cadence.moe&via=matrix.org",
 | 
			
		||||
		format: "org.matrix.custom.html",
 | 
			
		||||
		formatted_body: `Thread started: <a href="https://matrix.to/#/!thread?via=cadence.moe&via=matrix.org">test thread</a>`,
 | 
			
		||||
		"m.mentions": {}
 | 
			
		||||
	})
 | 
			
		||||
})
 | 
			
		||||
| 
						 | 
				
			
			@ -69,8 +67,6 @@ test("thread2announcement: known creator, no branched from event", async t => {
 | 
			
		|||
	t.deepEqual(content, {
 | 
			
		||||
		msgtype: "m.emote",
 | 
			
		||||
		body: "started a thread: test thread https://matrix.to/#/!thread?via=cadence.moe&via=matrix.org",
 | 
			
		||||
		format: "org.matrix.custom.html",
 | 
			
		||||
		formatted_body: `started a thread: <a href="https://matrix.to/#/!thread?via=cadence.moe&via=matrix.org">test thread</a>`,
 | 
			
		||||
		"m.mentions": {}
 | 
			
		||||
	})
 | 
			
		||||
})
 | 
			
		||||
| 
						 | 
				
			
			@ -95,8 +91,6 @@ test("thread2announcement: no known creator, branched from discord event", async
 | 
			
		|||
	t.deepEqual(content, {
 | 
			
		||||
		msgtype: "m.text",
 | 
			
		||||
		body: "Thread started: test thread https://matrix.to/#/!thread?via=cadence.moe&via=matrix.org",
 | 
			
		||||
		format: "org.matrix.custom.html",
 | 
			
		||||
		formatted_body: `Thread started: <a href="https://matrix.to/#/!thread?via=cadence.moe&via=matrix.org">test thread</a>`,
 | 
			
		||||
		"m.mentions": {},
 | 
			
		||||
		"m.relates_to": {
 | 
			
		||||
			"m.in_reply_to": {
 | 
			
		||||
| 
						 | 
				
			
			@ -126,8 +120,6 @@ test("thread2announcement: known creator, branched from discord event", async t
 | 
			
		|||
	t.deepEqual(content, {
 | 
			
		||||
		msgtype: "m.emote",
 | 
			
		||||
		body: "started a thread: test thread https://matrix.to/#/!thread?via=cadence.moe&via=matrix.org",
 | 
			
		||||
		format: "org.matrix.custom.html",
 | 
			
		||||
		formatted_body: `started a thread: <a href="https://matrix.to/#/!thread?via=cadence.moe&via=matrix.org">test thread</a>`,
 | 
			
		||||
		"m.mentions": {},
 | 
			
		||||
		"m.relates_to": {
 | 
			
		||||
			"m.in_reply_to": {
 | 
			
		||||
| 
						 | 
				
			
			@ -157,8 +149,6 @@ test("thread2announcement: no known creator, branched from matrix event", async
 | 
			
		|||
	t.deepEqual(content, {
 | 
			
		||||
		msgtype: "m.text",
 | 
			
		||||
		body: "Thread started: test thread https://matrix.to/#/!thread?via=cadence.moe&via=matrix.org",
 | 
			
		||||
		format: "org.matrix.custom.html",
 | 
			
		||||
		formatted_body: `Thread started: <a href="https://matrix.to/#/!thread?via=cadence.moe&via=matrix.org">test thread</a>`,
 | 
			
		||||
		"m.mentions": {
 | 
			
		||||
			user_ids: ["@cadence:cadence.moe"]
 | 
			
		||||
		},
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,6 +7,8 @@ const {sync} = require("../../passthrough")
 | 
			
		|||
const emojiSheetConverter = sync.require("../converters/emoji-sheet")
 | 
			
		||||
/** @type {import("../../matrix/api")} */
 | 
			
		||||
const api = sync.require("../../matrix/api")
 | 
			
		||||
/** @type {import("../../matrix/mreq")} */
 | 
			
		||||
const mreq = sync.require("../../matrix/mreq")
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Downloads the emoji from the web and converts to uncompressed PNG data.
 | 
			
		||||
| 
						 | 
				
			
			@ -19,6 +21,10 @@ async function getAndConvertEmoji(mxc) {
 | 
			
		|||
	// If we were using connection pooling, we would be forced to download the entire GIF.
 | 
			
		||||
	// So we set no agent to ensure we are not connection pooling.
 | 
			
		||||
	const res = await api.getMedia(mxc, {signal: abortController.signal})
 | 
			
		||||
	if (res.status !== 200) {
 | 
			
		||||
		const root = await res.json()
 | 
			
		||||
		throw new mreq.MatrixServerError(root, {mxc})
 | 
			
		||||
	}
 | 
			
		||||
	const readable = stream.Readable.fromWeb(res.body)
 | 
			
		||||
	return emojiSheetConverter.convertImageStream(readable, () => {
 | 
			
		||||
		abortController.abort()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -13,10 +13,12 @@ const utils = sync.require("../converters/utils")
 | 
			
		|||
 */
 | 
			
		||||
async function deleteMessage(event) {
 | 
			
		||||
	const rows = from("event_message").join("message_channel", "message_id").select("channel_id", "message_id").where({event_id: event.redacts}).all()
 | 
			
		||||
	if (!rows.length) return
 | 
			
		||||
	for (const row of rows) {
 | 
			
		||||
		db.prepare("DELETE FROM message_channel WHERE message_id = ?").run(row.message_id)
 | 
			
		||||
		await discord.snow.channel.deleteMessage(row.channel_id, row.message_id, event.content.reason)
 | 
			
		||||
		db.prepare("DELETE FROM event_message WHERE message_id = ?").run(row.message_id)
 | 
			
		||||
	}
 | 
			
		||||
	db.prepare("DELETE FROM message_channel WHERE message_id = ?").run(rows[0].message_id)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,9 +5,8 @@ const {join} = require("path")
 | 
			
		|||
 | 
			
		||||
const passthrough = require("../../passthrough")
 | 
			
		||||
 | 
			
		||||
const {id} = require("../../../addbot")
 | 
			
		||||
 | 
			
		||||
async function setupEmojis() {
 | 
			
		||||
	const {id} = require("../../../addbot")
 | 
			
		||||
	const {discord, db} = passthrough
 | 
			
		||||
	const emojis = await discord.snow.assets.getAppEmojis(id)
 | 
			
		||||
	for (const name of ["L1", "L2"]) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -11,6 +11,7 @@ const entities = require("entities")
 | 
			
		|||
 | 
			
		||||
const passthrough = require("../../passthrough")
 | 
			
		||||
const {sync, db, discord, select, from} = passthrough
 | 
			
		||||
const {reg} = require("../../matrix/read-registration")
 | 
			
		||||
/** @type {import("../converters/utils")} */
 | 
			
		||||
const mxUtils = sync.require("../converters/utils")
 | 
			
		||||
/** @type {import("../../discord/utils")} */
 | 
			
		||||
| 
						 | 
				
			
			@ -238,7 +239,8 @@ function convertEmoji(mxcUrl, nameForGuess, allowSpriteSheetIndicator, allowLink
 | 
			
		|||
		if (!found) row = null
 | 
			
		||||
	}
 | 
			
		||||
	// Or, if we don't have an emoji right now, we search for the name instead.
 | 
			
		||||
	if (!row && nameForGuess) {
 | 
			
		||||
	const isLocalMxc = mxcUrl?.match(/^mxc:\/\/([^/]+)/)?.[1] === reg.ooye.server_name
 | 
			
		||||
	if (!row && nameForGuess && isLocalMxc) {
 | 
			
		||||
		const nameForGuessLower = nameForGuess.toLowerCase()
 | 
			
		||||
		for (const guild of discord.guilds.values()) {
 | 
			
		||||
			/** @type {{name: string, id: string, animated: number}[]} */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -391,7 +391,9 @@ async function getMedia(mxc, init = {}) {
 | 
			
		|||
		},
 | 
			
		||||
		...init
 | 
			
		||||
	})
 | 
			
		||||
	assert(res.body)
 | 
			
		||||
	if (init.method !== "HEAD") {
 | 
			
		||||
		assert(res.body)
 | 
			
		||||
	}
 | 
			
		||||
	// @ts-ignore
 | 
			
		||||
	return res
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										1
									
								
								src/types.d.ts
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								src/types.d.ts
									
										
									
									
										vendored
									
									
								
							| 
						 | 
				
			
			@ -31,6 +31,7 @@ export type AppServiceRegistrationConfig = {
 | 
			
		|||
		discord_origin?: string
 | 
			
		||||
		discord_cdn_origin?: string,
 | 
			
		||||
		web_password: string
 | 
			
		||||
		time_zone?: string
 | 
			
		||||
	}
 | 
			
		||||
	old_bridge?: {
 | 
			
		||||
		as_token: string
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -76,13 +76,13 @@ as.router.post("/api/link-space", defineEventHandler(async event => {
 | 
			
		|||
	if (existing) throw createError({status: 400, message: "Bad Request", data: `Guild ID ${guildID} or space ID ${spaceID} are already bridged and cannot be reused`})
 | 
			
		||||
 | 
			
		||||
	const inviteSender = select("invite", "mxid", {mxid: session.data.mxid, room_id: spaceID}).pluck().get()
 | 
			
		||||
    const via = [ inviteSender?.match(/:(.*)/)?.[1] ?? "" ]
 | 
			
		||||
	const via = [ inviteSender?.match(/:(.*)/)?.[1] ?? "" ]
 | 
			
		||||
 | 
			
		||||
	// Check space exists and bridge is joined
 | 
			
		||||
	try {
 | 
			
		||||
		await api.joinRoom(parsedBody.space_id, null, via)
 | 
			
		||||
	} catch (e) {
 | 
			
		||||
        throw createError({status: 400, message: "Unable To Join", data: `Unable to join the requested Matrix space. Please invite the bridge to the space and try again. (Server said: ${e.errcode} - ${e.message})`})
 | 
			
		||||
		throw createError({status: 400, message: "Unable To Join", data: `Unable to join the requested Matrix space. Please invite the bridge to the space and try again. (Server said: ${e.errcode} - ${e.message})`})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Check bridge has PL 100
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -77,7 +77,7 @@ test("web link space: check that OOYE is joined", async t => {
 | 
			
		|||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}))
 | 
			
		||||
	t.equal(error.data, "M_FORBIDDEN - not allowed to join I guess")
 | 
			
		||||
	t.equal(error.data, "Unable to join the requested Matrix space. Please invite the bridge to the space and try again. (Server said: M_FORBIDDEN - not allowed to join I guess)")
 | 
			
		||||
	t.equal(called, 1)
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										57
									
								
								test/data.js
									
										
									
									
									
								
							
							
						
						
									
										57
									
								
								test/data.js
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -1398,6 +1398,63 @@ module.exports = {
 | 
			
		|||
			attachments: [],
 | 
			
		||||
			guild_id: "112760669178241024"
 | 
			
		||||
		},
 | 
			
		||||
		simple_room_link: {
 | 
			
		||||
			type: 0,
 | 
			
		||||
			tts: false,
 | 
			
		||||
			timestamp: "2023-07-10T20:04:25.939000+00:00",
 | 
			
		||||
			referenced_message: null,
 | 
			
		||||
			pinned: false,
 | 
			
		||||
			nonce: "1128054139385806848",
 | 
			
		||||
			mentions: [],
 | 
			
		||||
			mention_roles: [],
 | 
			
		||||
			mention_everyone: false,
 | 
			
		||||
			member: {
 | 
			
		||||
				roles: [
 | 
			
		||||
					"112767366235959296",  "118924814567211009",
 | 
			
		||||
					"204427286542417920",  "199995902742626304",
 | 
			
		||||
					"222168467627835392",  "238028326281805825",
 | 
			
		||||
					"259806643414499328",  "265239342648131584",
 | 
			
		||||
					"271173313575780353",  "287733611912757249",
 | 
			
		||||
					"225744901915148298",  "305775031223320577",
 | 
			
		||||
					"318243902521868288",  "348651574924541953",
 | 
			
		||||
					"349185088157777920",  "378402925128712193",
 | 
			
		||||
					"392141548932038658",  "393912152173576203",
 | 
			
		||||
					"482860581670486028",  "495384759074160642",
 | 
			
		||||
					"638988388740890635",  "373336013109461013",
 | 
			
		||||
					"530220455085473813",  "454567553738473472",
 | 
			
		||||
					"790724320824655873",  "1123518980456452097",
 | 
			
		||||
					"1040735082610167858", "695946570482450442",
 | 
			
		||||
					"1123460940935991296", "849737964090556488"
 | 
			
		||||
				],
 | 
			
		||||
				premium_since: null,
 | 
			
		||||
				pending: false,
 | 
			
		||||
				nick: null,
 | 
			
		||||
				mute: false,
 | 
			
		||||
				joined_at: "2015-11-11T09:55:40.321000+00:00",
 | 
			
		||||
				flags: 0,
 | 
			
		||||
				deaf: false,
 | 
			
		||||
				communication_disabled_until: null,
 | 
			
		||||
				avatar: null
 | 
			
		||||
			},
 | 
			
		||||
			id: "1128054143064494233",
 | 
			
		||||
			flags: 0,
 | 
			
		||||
			embeds: [],
 | 
			
		||||
			edited_timestamp: null,
 | 
			
		||||
			content: "https://discord.com/channels/112760669178241024/1100319550446252084",
 | 
			
		||||
			components: [],
 | 
			
		||||
			channel_id: "266767590641238027",
 | 
			
		||||
			author: {
 | 
			
		||||
				username: "kumaccino",
 | 
			
		||||
				public_flags: 128,
 | 
			
		||||
				id: "113340068197859328",
 | 
			
		||||
				global_name: "kumaccino",
 | 
			
		||||
				discriminator: "0",
 | 
			
		||||
				avatar_decoration: null,
 | 
			
		||||
				avatar: "b48302623a12bc7c59a71328f72ccb39"
 | 
			
		||||
			},
 | 
			
		||||
			attachments: [],
 | 
			
		||||
			guild_id: "112760669178241024"
 | 
			
		||||
		},
 | 
			
		||||
		nicked_room_mention: {
 | 
			
		||||
			type: 0,
 | 
			
		||||
			tts: false,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -27,6 +27,7 @@ reg.namespaces = {
 | 
			
		|||
	aliases: [{regex: "#_ooye_.*:cadence.moe", exclusive: true}]
 | 
			
		||||
}
 | 
			
		||||
reg.ooye.bridge_origin = "https://bridge.example.org"
 | 
			
		||||
reg.ooye.time_zone = "Pacific/Auckland"
 | 
			
		||||
 | 
			
		||||
const sync = new HeatSync({watchFS: false})
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue