Refactor reaction removals and add tests
This commit is contained in:
		
							parent
							
								
									dc92dc0b69
								
							
						
					
					
						commit
						bf0691f9bb
					
				
					 6 changed files with 318 additions and 102 deletions
				
			
		
							
								
								
									
										88
									
								
								d2m/converters/remove-reaction.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								d2m/converters/remove-reaction.js
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,88 @@
 | 
			
		|||
// @ts-check
 | 
			
		||||
 | 
			
		||||
const Ty = require("../../types")
 | 
			
		||||
const DiscordTypes = require("discord-api-types/v10")
 | 
			
		||||
 | 
			
		||||
const passthrough = require("../../passthrough")
 | 
			
		||||
const {discord, sync, select} = passthrough
 | 
			
		||||
/** @type {import("../../m2d/converters/utils")} */
 | 
			
		||||
const utils = sync.require("../../m2d/converters/utils")
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @typedef ReactionRemoveRequest
 | 
			
		||||
 * @prop {string} eventID
 | 
			
		||||
 * @prop {string | null} mxid
 | 
			
		||||
 * @prop {BigInt} [hash]
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @param {DiscordTypes.GatewayMessageReactionRemoveDispatchData} data
 | 
			
		||||
 * @param {Ty.Pagination<Ty.Event.Outer<Ty.Event.M_Reaction>>} relations
 | 
			
		||||
 * @param {string} key
 | 
			
		||||
 */
 | 
			
		||||
function removeReaction(data, relations, key) {
 | 
			
		||||
	/** @type {ReactionRemoveRequest[]} */
 | 
			
		||||
	const removals = []
 | 
			
		||||
 | 
			
		||||
	const wantToRemoveMatrixReaction = data.user_id === discord.application.id
 | 
			
		||||
	for (const event of relations.chunk) {
 | 
			
		||||
		const eventID = event.event_id
 | 
			
		||||
		if (event.content["m.relates_to"].key === key) {
 | 
			
		||||
			const lookingAtMatrixReaction = !utils.eventSenderIsFromDiscord(event.sender)
 | 
			
		||||
			if (lookingAtMatrixReaction && wantToRemoveMatrixReaction) {
 | 
			
		||||
				// We are removing a Matrix user's reaction, so we need to redact from the correct user ID (not @_ooye_matrix_bridge).
 | 
			
		||||
				// Even though the bridge bot only reacted once on Discord-side, multiple Matrix users may have
 | 
			
		||||
				// reacted on Matrix-side. Semantically, we want to remove the reaction from EVERY Matrix user.
 | 
			
		||||
				// Also need to clean up the database.
 | 
			
		||||
				const hash = utils.getEventIDHash(event.event_id)
 | 
			
		||||
				removals.push({eventID, mxid: null, hash})
 | 
			
		||||
			}
 | 
			
		||||
			if (!lookingAtMatrixReaction && !wantToRemoveMatrixReaction) {
 | 
			
		||||
				// We are removing a Discord user's reaction, so we just make the sim user remove it.
 | 
			
		||||
				const mxid = select("sim", "mxid", {user_id: data.user_id}).pluck().get()
 | 
			
		||||
				if (mxid === event.sender) {
 | 
			
		||||
					removals.push({eventID, mxid})
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return removals
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @param {DiscordTypes.GatewayMessageReactionRemoveEmojiDispatchData} data
 | 
			
		||||
 * @param {Ty.Pagination<Ty.Event.Outer<Ty.Event.M_Reaction>>} relations
 | 
			
		||||
 * @param {string} key
 | 
			
		||||
 */
 | 
			
		||||
function removeEmojiReaction(data, relations, key) {
 | 
			
		||||
	/** @type {ReactionRemoveRequest[]} */
 | 
			
		||||
	const removals = []
 | 
			
		||||
 | 
			
		||||
	for (const event of relations.chunk) {
 | 
			
		||||
		const eventID = event.event_id
 | 
			
		||||
		if (event.content["m.relates_to"].key === key) {
 | 
			
		||||
			const mxid = utils.eventSenderIsFromDiscord(event.sender) ? event.sender : null
 | 
			
		||||
			removals.push({eventID, mxid})
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return removals
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @param {DiscordTypes.GatewayMessageReactionRemoveAllDispatchData} data
 | 
			
		||||
 * @param {Ty.Pagination<Ty.Event.Outer<Ty.Event.M_Reaction>>} relations
 | 
			
		||||
 * @returns {ReactionRemoveRequest[]}
 | 
			
		||||
 */
 | 
			
		||||
function removeAllReactions(data, relations) {
 | 
			
		||||
	return relations.chunk.map(event => {
 | 
			
		||||
		const eventID = event.event_id
 | 
			
		||||
		const mxid = utils.eventSenderIsFromDiscord(event.sender) ? event.sender : null
 | 
			
		||||
		return {eventID, mxid}
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
module.exports.removeReaction = removeReaction
 | 
			
		||||
module.exports.removeEmojiReaction = removeEmojiReaction
 | 
			
		||||
module.exports.removeAllReactions = removeAllReactions
 | 
			
		||||
							
								
								
									
										172
									
								
								d2m/converters/remove-reaction.test.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										172
									
								
								d2m/converters/remove-reaction.test.js
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,172 @@
 | 
			
		|||
// @ts-check
 | 
			
		||||
 | 
			
		||||
const {test} = require("supertape")
 | 
			
		||||
const removeReaction = require("./remove-reaction")
 | 
			
		||||
 | 
			
		||||
const BRIDGE_ID = "684280192553844747"
 | 
			
		||||
 | 
			
		||||
function fakeSpecificReactionRemoval(userID, emoji, emojiID) {
 | 
			
		||||
	return {
 | 
			
		||||
		channel_id: "THE_CHANNEL",
 | 
			
		||||
		message_id: "THE_MESSAGE",
 | 
			
		||||
		user_id: userID,
 | 
			
		||||
		emoji: {id: emojiID, name: emoji}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function fakeEmojiReactionRemoval(emoji, emojiID) {
 | 
			
		||||
	return {
 | 
			
		||||
		channel_id: "THE_CHANNEL",
 | 
			
		||||
		message_id: "THE_MESSAGE",
 | 
			
		||||
		emoji: {id: emojiID, name: emoji}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function fakeAllReactionRemoval() {
 | 
			
		||||
	return {
 | 
			
		||||
		channel_id: "THE_CHANNEL",
 | 
			
		||||
		message_id: "THE_MESSAGE"
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function fakeChunk(chunk) {
 | 
			
		||||
	return {
 | 
			
		||||
		chunk: chunk.map(({sender, key}, i) => ({
 | 
			
		||||
			content: {
 | 
			
		||||
				"m.relates_to": {
 | 
			
		||||
					rel_type: "m.annotation",
 | 
			
		||||
					event_id: "$message",
 | 
			
		||||
					key
 | 
			
		||||
				}
 | 
			
		||||
			},
 | 
			
		||||
			event_id: `$reaction_${i}`,
 | 
			
		||||
			sender,
 | 
			
		||||
			type: "m.reaction",
 | 
			
		||||
			origin_server_ts: 0,
 | 
			
		||||
			room_id: "!THE_ROOM",
 | 
			
		||||
			unsigned: null
 | 
			
		||||
		}))
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
test("remove reaction: a specific discord user's reaction is removed", t => {
 | 
			
		||||
	const removals = removeReaction.removeReaction(
 | 
			
		||||
		fakeSpecificReactionRemoval("820865262526005258", "🐈", null),
 | 
			
		||||
		fakeChunk([{key: "🐈", sender: "@_ooye_crunch_god:cadence.moe"}]),
 | 
			
		||||
		"🐈"
 | 
			
		||||
	)
 | 
			
		||||
	t.deepEqual(removals, [{
 | 
			
		||||
		eventID: "$reaction_0",
 | 
			
		||||
		mxid: "@_ooye_crunch_god:cadence.moe"
 | 
			
		||||
	}])
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
test("remove reaction: a specific matrix user's reaction is removed", t => {
 | 
			
		||||
	const removals = removeReaction.removeReaction(
 | 
			
		||||
		fakeSpecificReactionRemoval(BRIDGE_ID, "🐈", null),
 | 
			
		||||
		fakeChunk([{key: "🐈", sender: "@cadence:cadence.moe"}]),
 | 
			
		||||
		"🐈"
 | 
			
		||||
	)
 | 
			
		||||
	t.deepEqual(removals, [{
 | 
			
		||||
		eventID: "$reaction_0",
 | 
			
		||||
		mxid: null,
 | 
			
		||||
		hash: 2842343637291700751n
 | 
			
		||||
	}])
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
test("remove reaction: a specific discord user's reaction is removed when there are multiple reactions", t => {
 | 
			
		||||
	const removals = removeReaction.removeReaction(
 | 
			
		||||
		fakeSpecificReactionRemoval("820865262526005258", "🐈", null),
 | 
			
		||||
		fakeChunk([
 | 
			
		||||
			{key: "🐈⬛", sender: "@_ooye_crunch_god:cadence.moe"},
 | 
			
		||||
			{key: "🐈", sender: "@_ooye_crunch_god:cadence.moe"},
 | 
			
		||||
			{key: "🐈", sender: "@_ooye_extremity:cadence.moe"},
 | 
			
		||||
			{key: "🐈", sender: "@cadence:cadence.moe"},
 | 
			
		||||
			{key: "🐈", sender: "@zoe:cadence.moe"}
 | 
			
		||||
		]),
 | 
			
		||||
		"🐈"
 | 
			
		||||
	)
 | 
			
		||||
	t.deepEqual(removals, [{
 | 
			
		||||
		eventID: "$reaction_1",
 | 
			
		||||
		mxid: "@_ooye_crunch_god:cadence.moe"
 | 
			
		||||
	}])
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
test("remove reaction: a specific reaction leads to all matrix users' reaction of the emoji being removed", t => {
 | 
			
		||||
	const removals = removeReaction.removeReaction(
 | 
			
		||||
		fakeSpecificReactionRemoval(BRIDGE_ID, "🐈", null),
 | 
			
		||||
		fakeChunk([
 | 
			
		||||
			{key: "🐈", sender: "@_ooye_crunch_god:cadence.moe"},
 | 
			
		||||
			{key: "🐈", sender: "@cadence:cadence.moe"},
 | 
			
		||||
			{key: "🐈⬛", sender: "@zoe:cadence.moe"},
 | 
			
		||||
			{key: "🐈", sender: "@zoe:cadence.moe"},
 | 
			
		||||
			{key: "🐈", sender: "@_ooye_extremity:cadence.moe"}
 | 
			
		||||
		]),
 | 
			
		||||
		"🐈"
 | 
			
		||||
	)
 | 
			
		||||
	t.deepEqual(removals, [{
 | 
			
		||||
		eventID: "$reaction_1",
 | 
			
		||||
		mxid: null,
 | 
			
		||||
		hash: -8635141960139030904n
 | 
			
		||||
	}, {
 | 
			
		||||
		eventID: "$reaction_3",
 | 
			
		||||
		mxid: null,
 | 
			
		||||
		hash: 326222869084879263n
 | 
			
		||||
	}])
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
test("remove reaction: an emoji removes all instances of the emoij from both sides", t => {
 | 
			
		||||
	const removals = removeReaction.removeEmojiReaction(
 | 
			
		||||
		fakeEmojiReactionRemoval("🐈", null),
 | 
			
		||||
		fakeChunk([
 | 
			
		||||
			{key: "🐈", sender: "@_ooye_crunch_god:cadence.moe"},
 | 
			
		||||
			{key: "🐈", sender: "@cadence:cadence.moe"},
 | 
			
		||||
			{key: "🐈⬛", sender: "@zoe:cadence.moe"},
 | 
			
		||||
			{key: "🐈", sender: "@zoe:cadence.moe"},
 | 
			
		||||
			{key: "🐈", sender: "@_ooye_extremity:cadence.moe"}
 | 
			
		||||
		]),
 | 
			
		||||
		"🐈"
 | 
			
		||||
	)
 | 
			
		||||
	t.deepEqual(removals, [{
 | 
			
		||||
		eventID: "$reaction_0",
 | 
			
		||||
		mxid: "@_ooye_crunch_god:cadence.moe"
 | 
			
		||||
	}, {
 | 
			
		||||
		eventID: "$reaction_1",
 | 
			
		||||
		mxid: null
 | 
			
		||||
	}, {
 | 
			
		||||
		eventID: "$reaction_3",
 | 
			
		||||
		mxid: null
 | 
			
		||||
	}, {
 | 
			
		||||
		eventID: "$reaction_4",
 | 
			
		||||
		mxid: "@_ooye_extremity:cadence.moe"
 | 
			
		||||
	}])
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
test("remove reaction: remove all removes all from both sides", t => {
 | 
			
		||||
	const removals = removeReaction.removeAllReactions(
 | 
			
		||||
		fakeAllReactionRemoval(),
 | 
			
		||||
		fakeChunk([
 | 
			
		||||
			{key: "🐈", sender: "@_ooye_crunch_god:cadence.moe"},
 | 
			
		||||
			{key: "🐈", sender: "@cadence:cadence.moe"},
 | 
			
		||||
			{key: "🐈⬛", sender: "@zoe:cadence.moe"},
 | 
			
		||||
			{key: "🐈", sender: "@zoe:cadence.moe"},
 | 
			
		||||
			{key: "🐈", sender: "@_ooye_extremity:cadence.moe"}
 | 
			
		||||
		])
 | 
			
		||||
	)
 | 
			
		||||
	t.deepEqual(removals, [{
 | 
			
		||||
		eventID: "$reaction_0",
 | 
			
		||||
		mxid: "@_ooye_crunch_god:cadence.moe"
 | 
			
		||||
	}, {
 | 
			
		||||
		eventID: "$reaction_1",
 | 
			
		||||
		mxid: null
 | 
			
		||||
	}, {
 | 
			
		||||
		eventID: "$reaction_2",
 | 
			
		||||
		mxid: null
 | 
			
		||||
	}, {
 | 
			
		||||
		eventID: "$reaction_3",
 | 
			
		||||
		mxid: null
 | 
			
		||||
	}, {
 | 
			
		||||
		eventID: "$reaction_4",
 | 
			
		||||
		mxid: "@_ooye_extremity:cadence.moe"
 | 
			
		||||
	}])
 | 
			
		||||
})
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue