got edit2changes passing tests
This commit is contained in:
parent
9bf5e8013d
commit
a41e3c45e7
6 changed files with 193 additions and 72 deletions
|
@ -27,7 +27,7 @@ async function sendMessage(message, guild) {
|
|||
await registerUser.syncUser(message.author, message.member, message.guild_id, roomID)
|
||||
}
|
||||
|
||||
const events = await messageToEvent.messageToEvent(message, guild, api)
|
||||
const events = await messageToEvent.messageToEvent(message, guild, {}, {api})
|
||||
const eventIDs = []
|
||||
let eventPart = 0 // 0 is primary, 1 is supporting
|
||||
for (const event of events) {
|
||||
|
|
|
@ -29,7 +29,9 @@ async function editToChanges(message, guild) {
|
|||
|
||||
// Figure out what we will be replacing them with
|
||||
|
||||
const newEvents = await messageToEvent.messageToEvent(message, guild, api)
|
||||
const newFallbackContent = await messageToEvent.messageToEvent(message, guild, {includeEditFallbackStar: true}, {api})
|
||||
const newInnerContent = await messageToEvent.messageToEvent(message, guild, {includeReplyFallback: false}, {api})
|
||||
assert.ok(newFallbackContent.length === newInnerContent.length)
|
||||
|
||||
// Match the new events to the old events
|
||||
|
||||
|
@ -47,21 +49,27 @@ async function editToChanges(message, guild) {
|
|||
let eventsToSend = []
|
||||
// 4. Events that are matched and have definitely not changed, so they don't need to be edited or replaced at all. This is represented as nothing.
|
||||
|
||||
function shift() {
|
||||
newFallbackContent.shift()
|
||||
newInnerContent.shift()
|
||||
}
|
||||
|
||||
// For each old event...
|
||||
outer: while (newEvents.length) {
|
||||
const newe = newEvents[0]
|
||||
outer: while (newFallbackContent.length) {
|
||||
const newe = newFallbackContent[0]
|
||||
// Find a new event to pair it with...
|
||||
for (let i = 0; i < oldEventRows.length; i++) {
|
||||
const olde = oldEventRows[i]
|
||||
if (olde.event_type === newe.$type && olde.event_subtype === (newe.msgtype || null)) {
|
||||
if (olde.event_type === newe.$type && olde.event_subtype === (newe.msgtype ?? null)) { // The spec does allow subtypes to change, so I can change this condition later if I want to
|
||||
// Found one!
|
||||
// Set up the pairing
|
||||
eventsToReplace.push({
|
||||
old: olde,
|
||||
new: newe
|
||||
newFallbackContent: newFallbackContent[0],
|
||||
newInnerContent: newInnerContent[0]
|
||||
})
|
||||
// These events have been handled now, so remove them from the source arrays
|
||||
newEvents.shift()
|
||||
shift()
|
||||
oldEventRows.splice(i, 1)
|
||||
// Go all the way back to the start of the next iteration of the outer loop
|
||||
continue outer
|
||||
|
@ -69,7 +77,7 @@ async function editToChanges(message, guild) {
|
|||
}
|
||||
// If we got this far, we could not pair it to an existing event, so it'll have to be a new one
|
||||
eventsToSend.push(newe)
|
||||
newEvents.shift()
|
||||
shift()
|
||||
}
|
||||
// Anything remaining in oldEventRows is present in the old version only and should be redacted.
|
||||
eventsToRedact = oldEventRows
|
||||
|
@ -92,7 +100,7 @@ async function editToChanges(message, guild) {
|
|||
|
||||
// Removing unnecessary properties before returning
|
||||
eventsToRedact = eventsToRedact.map(e => e.event_id)
|
||||
eventsToReplace = eventsToReplace.map(e => ({oldID: e.old.event_id, new: eventToReplacementEvent(e.old.event_id, e.new)}))
|
||||
eventsToReplace = eventsToReplace.map(e => ({oldID: e.old.event_id, new: eventToReplacementEvent(e.old.event_id, e.newFallbackContent, e.newInnerContent)}))
|
||||
|
||||
return {eventsToReplace, eventsToRedact, eventsToSend}
|
||||
}
|
||||
|
@ -100,31 +108,26 @@ async function editToChanges(message, guild) {
|
|||
/**
|
||||
* @template T
|
||||
* @param {string} oldID
|
||||
* @param {T} content
|
||||
* @param {T} newFallbackContent
|
||||
* @param {T} newInnerContent
|
||||
* @returns {import("../../types").Event.ReplacementContent<T>} content
|
||||
*/
|
||||
function eventToReplacementEvent(oldID, content) {
|
||||
const newContent = {
|
||||
...content,
|
||||
function eventToReplacementEvent(oldID, newFallbackContent, newInnerContent) {
|
||||
const content = {
|
||||
...newFallbackContent,
|
||||
"m.mentions": {},
|
||||
"m.new_content": {
|
||||
...content
|
||||
...newInnerContent
|
||||
},
|
||||
"m.relates_to": {
|
||||
rel_type: "m.replace",
|
||||
event_id: oldID
|
||||
}
|
||||
}
|
||||
if (typeof newContent.body === "string") {
|
||||
newContent.body = "* " + newContent.body
|
||||
}
|
||||
if (typeof newContent.formatted_body === "string") {
|
||||
newContent.formatted_body = "* " + newContent.formatted_body
|
||||
}
|
||||
delete newContent["m.new_content"]["$type"]
|
||||
delete content["m.new_content"]["$type"]
|
||||
// Client-Server API spec 11.37.3: Any m.relates_to property within m.new_content is ignored.
|
||||
delete newContent["m.new_content"]["m.relates_to"]
|
||||
return newContent
|
||||
delete content["m.new_content"]["m.relates_to"]
|
||||
return content
|
||||
}
|
||||
|
||||
module.exports.editToChanges = editToChanges
|
||||
|
|
|
@ -47,9 +47,13 @@ test("edit2changes: edit of reply to skull webp attachment with content", async
|
|||
oldID: "$vgTKOR5ZTYNMKaS7XvgEIDaOWZtVCEyzLLi5Pc5Gz4M",
|
||||
new: {
|
||||
$type: "m.room.message",
|
||||
// TODO: read "edits of replies" in the spec!!!
|
||||
msgtype: "m.text",
|
||||
body: "* Edit",
|
||||
body: "> Extremity: Image\n\n* Edit",
|
||||
format: "org.matrix.custom.html",
|
||||
formatted_body:
|
||||
'<mx-reply><blockquote><a href="https://matrix.to/#/!kLRqKKUQXcibIMtOpl:cadence.moe/$oLyUTyZ_7e_SUzGNWZKz880ll9amLZvXGbArJCKai2Q">In reply to</a> Extremity'
|
||||
+ '<br>Image</blockquote></mx-reply>'
|
||||
+ '* Edit',
|
||||
"m.mentions": {},
|
||||
"m.new_content": {
|
||||
msgtype: "m.text",
|
||||
|
@ -60,7 +64,6 @@ test("edit2changes: edit of reply to skull webp attachment with content", async
|
|||
rel_type: "m.replace",
|
||||
event_id: "$vgTKOR5ZTYNMKaS7XvgEIDaOWZtVCEyzLLi5Pc5Gz4M"
|
||||
}
|
||||
// TODO: read "edits of replies" in the spec!!!
|
||||
}
|
||||
}])
|
||||
})
|
||||
|
|
|
@ -55,9 +55,12 @@ function getDiscordParseCallbacks(message, useHTML) {
|
|||
/**
|
||||
* @param {import("discord-api-types/v10").APIMessage} message
|
||||
* @param {import("discord-api-types/v10").APIGuild} guild
|
||||
* @param {import("../../matrix/api")} api simple-as-nails dependency injection for the matrix API
|
||||
* @param {{includeReplyFallback?: boolean, includeEditFallbackStar?: boolean}} options default values:
|
||||
* - includeReplyFallback: true
|
||||
* - includeEditFallbackStar: false
|
||||
* @param {{api: import("../../matrix/api")}} di simple-as-nails dependency injection for the matrix API
|
||||
*/
|
||||
async function messageToEvent(message, guild, api) {
|
||||
async function messageToEvent(message, guild, options = {}, di) {
|
||||
const events = []
|
||||
|
||||
/**
|
||||
|
@ -99,7 +102,7 @@ async function messageToEvent(message, guild, api) {
|
|||
}
|
||||
if (repliedToEventOriginallyFromMatrix) {
|
||||
// Need to figure out who sent that event...
|
||||
const event = await api.getEvent(repliedToEventRoomId, repliedToEventId)
|
||||
const event = await di.api.getEvent(repliedToEventRoomId, repliedToEventId)
|
||||
repliedToEventSenderMxid = event.sender
|
||||
// Need to add the sender to m.mentions
|
||||
addMention(repliedToEventSenderMxid)
|
||||
|
@ -133,7 +136,7 @@ async function messageToEvent(message, guild, api) {
|
|||
if (matches.length && matches.some(m => m[1].match(/[a-z]/i))) {
|
||||
const writtenMentionsText = matches.map(m => m[1].toLowerCase())
|
||||
const roomID = db.prepare("SELECT room_id FROM channel_room WHERE channel_id = ?").pluck().get(message.channel_id)
|
||||
const {joined} = await api.getJoinedMembers(roomID)
|
||||
const {joined} = await di.api.getJoinedMembers(roomID)
|
||||
for (const [mxid, member] of Object.entries(joined)) {
|
||||
if (!userRegex.some(rx => mxid.match(rx))) {
|
||||
const localpart = mxid.match(/@([^:]*)/)
|
||||
|
@ -143,8 +146,15 @@ async function messageToEvent(message, guild, api) {
|
|||
}
|
||||
}
|
||||
|
||||
// Star * prefix for fallback edits
|
||||
if (options.includeEditFallbackStar) {
|
||||
body = "* " + body
|
||||
html = "* " + html
|
||||
}
|
||||
|
||||
// Fallback body/formatted_body for replies
|
||||
if (repliedToEventId) {
|
||||
// This branch is optional - do NOT change anything apart from the reply fallback, since it may not be run
|
||||
if (repliedToEventId && options.includeReplyFallback !== false) {
|
||||
let repliedToDisplayName
|
||||
let repliedToUserHtml
|
||||
if (repliedToEventOriginallyFromMatrix && repliedToEventSenderMxid) {
|
||||
|
|
|
@ -30,7 +30,7 @@ function mockGetEvent(t, roomID_in, eventID_in, outer) {
|
|||
}
|
||||
|
||||
test("message2event: simple plaintext", async t => {
|
||||
const events = await messageToEvent(data.message.simple_plaintext, data.guild.general)
|
||||
const events = await messageToEvent(data.message.simple_plaintext, data.guild.general, {})
|
||||
t.deepEqual(events, [{
|
||||
$type: "m.room.message",
|
||||
"m.mentions": {},
|
||||
|
@ -40,7 +40,7 @@ test("message2event: simple plaintext", async t => {
|
|||
})
|
||||
|
||||
test("message2event: simple user mention", async t => {
|
||||
const events = await messageToEvent(data.message.simple_user_mention, data.guild.general)
|
||||
const events = await messageToEvent(data.message.simple_user_mention, data.guild.general, {})
|
||||
t.deepEqual(events, [{
|
||||
$type: "m.room.message",
|
||||
"m.mentions": {},
|
||||
|
@ -52,7 +52,7 @@ test("message2event: simple user mention", async t => {
|
|||
})
|
||||
|
||||
test("message2event: simple room mention", async t => {
|
||||
const events = await messageToEvent(data.message.simple_room_mention, data.guild.general)
|
||||
const events = await messageToEvent(data.message.simple_room_mention, data.guild.general, {})
|
||||
t.deepEqual(events, [{
|
||||
$type: "m.room.message",
|
||||
"m.mentions": {},
|
||||
|
@ -64,7 +64,7 @@ test("message2event: simple room mention", async t => {
|
|||
})
|
||||
|
||||
test("message2event: simple message link", async t => {
|
||||
const events = await messageToEvent(data.message.simple_message_link, data.guild.general)
|
||||
const events = await messageToEvent(data.message.simple_message_link, data.guild.general, {})
|
||||
t.deepEqual(events, [{
|
||||
$type: "m.room.message",
|
||||
"m.mentions": {},
|
||||
|
@ -76,7 +76,7 @@ test("message2event: simple message link", async t => {
|
|||
})
|
||||
|
||||
test("message2event: attachment with no content", async t => {
|
||||
const events = await messageToEvent(data.message.attachment_no_content, data.guild.general)
|
||||
const events = await messageToEvent(data.message.attachment_no_content, data.guild.general, {})
|
||||
t.deepEqual(events, [{
|
||||
$type: "m.room.message",
|
||||
"m.mentions": {},
|
||||
|
@ -94,7 +94,7 @@ test("message2event: attachment with no content", async t => {
|
|||
})
|
||||
|
||||
test("message2event: stickers", async t => {
|
||||
const events = await messageToEvent(data.message.sticker, data.guild.general)
|
||||
const events = await messageToEvent(data.message.sticker, data.guild.general, {})
|
||||
t.deepEqual(events, [{
|
||||
$type: "m.room.message",
|
||||
"m.mentions": {},
|
||||
|
@ -127,7 +127,7 @@ test("message2event: stickers", async t => {
|
|||
})
|
||||
|
||||
test("message2event: skull webp attachment with content", async t => {
|
||||
const events = await messageToEvent(data.message.skull_webp_attachment_with_content, data.guild.general)
|
||||
const events = await messageToEvent(data.message.skull_webp_attachment_with_content, data.guild.general, {})
|
||||
t.deepEqual(events, [{
|
||||
$type: "m.room.message",
|
||||
"m.mentions": {},
|
||||
|
@ -150,7 +150,7 @@ test("message2event: skull webp attachment with content", async t => {
|
|||
})
|
||||
|
||||
test("message2event: reply to skull webp attachment with content", async t => {
|
||||
const events = await messageToEvent(data.message.reply_to_skull_webp_attachment_with_content, data.guild.general)
|
||||
const events = await messageToEvent(data.message.reply_to_skull_webp_attachment_with_content, data.guild.general, {})
|
||||
t.deepEqual(events, [{
|
||||
$type: "m.room.message",
|
||||
"m.relates_to": {
|
||||
|
@ -183,15 +183,17 @@ test("message2event: reply to skull webp attachment with content", async t => {
|
|||
})
|
||||
|
||||
test("message2event: simple reply to matrix user", async t => {
|
||||
const events = await messageToEvent(data.message.simple_reply_to_matrix_user, data.guild.general, {
|
||||
getEvent: mockGetEvent(t, "!kLRqKKUQXcibIMtOpl:cadence.moe", "$Ij3qo7NxMA4VPexlAiIx2CB9JbsiGhJeyt-2OvkAUe4", {
|
||||
type: "m.room.message",
|
||||
content: {
|
||||
msgtype: "m.text",
|
||||
body: "so can you reply to my webhook uwu"
|
||||
},
|
||||
sender: "@cadence:cadence.moe"
|
||||
})
|
||||
const events = await messageToEvent(data.message.simple_reply_to_matrix_user, data.guild.general, {}, {
|
||||
api: {
|
||||
getEvent: mockGetEvent(t, "!kLRqKKUQXcibIMtOpl:cadence.moe", "$Ij3qo7NxMA4VPexlAiIx2CB9JbsiGhJeyt-2OvkAUe4", {
|
||||
type: "m.room.message",
|
||||
content: {
|
||||
msgtype: "m.text",
|
||||
body: "so can you reply to my webhook uwu"
|
||||
},
|
||||
sender: "@cadence:cadence.moe"
|
||||
})
|
||||
}
|
||||
})
|
||||
t.deepEqual(events, [{
|
||||
$type: "m.room.message",
|
||||
|
@ -215,34 +217,66 @@ test("message2event: simple reply to matrix user", async t => {
|
|||
}])
|
||||
})
|
||||
|
||||
test("message2event: simple reply to matrix user, reply fallbacks disabled", async t => {
|
||||
const events = await messageToEvent(data.message.simple_reply_to_matrix_user, data.guild.general, {includeReplyFallback: false}, {
|
||||
api: {
|
||||
getEvent: mockGetEvent(t, "!kLRqKKUQXcibIMtOpl:cadence.moe", "$Ij3qo7NxMA4VPexlAiIx2CB9JbsiGhJeyt-2OvkAUe4", {
|
||||
type: "m.room.message",
|
||||
content: {
|
||||
msgtype: "m.text",
|
||||
body: "so can you reply to my webhook uwu"
|
||||
},
|
||||
sender: "@cadence:cadence.moe"
|
||||
})
|
||||
}
|
||||
})
|
||||
t.deepEqual(events, [{
|
||||
$type: "m.room.message",
|
||||
"m.relates_to": {
|
||||
"m.in_reply_to": {
|
||||
event_id: "$Ij3qo7NxMA4VPexlAiIx2CB9JbsiGhJeyt-2OvkAUe4"
|
||||
}
|
||||
},
|
||||
"m.mentions": {
|
||||
user_ids: [
|
||||
"@cadence:cadence.moe"
|
||||
]
|
||||
},
|
||||
msgtype: "m.text",
|
||||
body: "Reply"
|
||||
}])
|
||||
})
|
||||
|
||||
test("message2event: simple written @mention for matrix user", async t => {
|
||||
const events = await messageToEvent(data.message.simple_written_at_mention_for_matrix, data.guild.general, {
|
||||
async getJoinedMembers(roomID) {
|
||||
t.equal(roomID, "!kLRqKKUQXcibIMtOpl:cadence.moe")
|
||||
return new Promise(resolve => {
|
||||
setTimeout(() => {
|
||||
resolve({
|
||||
joined: {
|
||||
"@cadence:cadence.moe": {
|
||||
display_name: "cadence [they]",
|
||||
avatar_url: "whatever"
|
||||
},
|
||||
"@huckleton:cadence.moe": {
|
||||
display_name: "huck",
|
||||
avatar_url: "whatever"
|
||||
},
|
||||
"@_ooye_botrac4r:cadence.moe": {
|
||||
display_name: "botrac4r",
|
||||
avatar_url: "whatever"
|
||||
},
|
||||
"@_ooye_bot:cadence.moe": {
|
||||
display_name: "Out Of Your Element",
|
||||
avatar_url: "whatever"
|
||||
const events = await messageToEvent(data.message.simple_written_at_mention_for_matrix, data.guild.general, {}, {
|
||||
api: {
|
||||
async getJoinedMembers(roomID) {
|
||||
t.equal(roomID, "!kLRqKKUQXcibIMtOpl:cadence.moe")
|
||||
return new Promise(resolve => {
|
||||
setTimeout(() => {
|
||||
resolve({
|
||||
joined: {
|
||||
"@cadence:cadence.moe": {
|
||||
display_name: "cadence [they]",
|
||||
avatar_url: "whatever"
|
||||
},
|
||||
"@huckleton:cadence.moe": {
|
||||
display_name: "huck",
|
||||
avatar_url: "whatever"
|
||||
},
|
||||
"@_ooye_botrac4r:cadence.moe": {
|
||||
display_name: "botrac4r",
|
||||
avatar_url: "whatever"
|
||||
},
|
||||
"@_ooye_bot:cadence.moe": {
|
||||
display_name: "Out Of Your Element",
|
||||
avatar_url: "whatever"
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
t.deepEqual(events, [{
|
||||
|
|
71
gorge.js
Normal file
71
gorge.js
Normal file
|
@ -0,0 +1,71 @@
|
|||
// @ts-check
|
||||
|
||||
const assert = require("assert").strict
|
||||
const {test} = require("supertape")
|
||||
|
||||
|
||||
/**
|
||||
* @param {{id: string, name: string, description?: string?}} route
|
||||
* @param {{id: string}} vehicle
|
||||
* @param {string?} customName
|
||||
*
|
||||
function convertNameAndTopic(route, vehicle, customName) {
|
||||
const convertedName = customName || route.name
|
||||
const convertedTopic = (() => {
|
||||
// custom name + description
|
||||
if (customName && route.description)
|
||||
return `${route.name} | ${route.description}\n\nRoute ID: ${route.id}\nVehicle ID: ${vehicle.id}`
|
||||
// custom name + no description
|
||||
if (customName)
|
||||
return `${route.name}\n\nRoute ID: ${route.id}\nVehicle ID: ${vehicle.id}`
|
||||
// no custom name + description
|
||||
if (route.description)
|
||||
return `${route.description}\n\nRoute ID: ${route.id}\nVehicle ID: ${vehicle.id}`
|
||||
// neither
|
||||
return `Route ID: ${route.id}\nVehicle ID: ${vehicle.id}`
|
||||
})()
|
||||
|
||||
return [convertedName, convertedTopic]
|
||||
}*/
|
||||
|
||||
function convertNameAndTopic(route, vehicle, customName) {
|
||||
const convertedName = customName || route.name;
|
||||
const maybeDescriptionWithPipe = route.description ? ` | ${route.description}` : '';
|
||||
const maybeDescriptionWithNewlines = route.description ? `${route.description}\n\n` : '';
|
||||
const routeIdPart = `Route ID: ${route.id}`;
|
||||
const vehicleIdPart = `Vehicle ID: ${vehicle.id}`;
|
||||
|
||||
const convertedTopic = customName
|
||||
? `${route.name}${maybeDescriptionWithPipe}\n\n${routeIdPart}\n${vehicleIdPart}`
|
||||
: `${maybeDescriptionWithNewlines}${routeIdPart}\n${vehicleIdPart}`;
|
||||
|
||||
return [convertedName, convertedTopic];
|
||||
}
|
||||
|
||||
test("convertNameAndTopic: description and custom name", t => {
|
||||
t.deepEqual(
|
||||
convertNameAndTopic({id: "111", name: "I-386", description: "Through the gorge southeast of Lonely Peak"}, {id: "111"}, "gorge"),
|
||||
["gorge", "I-386 | Through the gorge southeast of Lonely Peak\n\nRoute ID: 111\nVehicle ID: 111"]
|
||||
)
|
||||
})
|
||||
|
||||
test("convertNameAndTopic: custom name only", t => {
|
||||
t.deepEqual(
|
||||
convertNameAndTopic({id: "222", name: "I-386"}, {id: "222"}, "gorge"),
|
||||
["gorge", "I-386\n\nRoute ID: 222\nVehicle ID: 222"]
|
||||
)
|
||||
})
|
||||
|
||||
test("convertNameAndTopic: description only", t => {
|
||||
t.deepEqual(
|
||||
convertNameAndTopic({id: "333", name: "I-386", description: "Through the gorge southeast of Lonely Peak"}, {id: "333"}, null),
|
||||
["I-386", "Through the gorge southeast of Lonely Peak\n\nRoute ID: 333\nVehicle ID: 333"]
|
||||
)
|
||||
})
|
||||
|
||||
test("convertNameAndTopic: neither", t => {
|
||||
t.deepEqual(
|
||||
convertNameAndTopic({id: "444", name: "I-386"}, {id: "444"}, null),
|
||||
["I-386", "Route ID: 444\nVehicle ID: 444"]
|
||||
)
|
||||
})
|
Loading…
Add table
Add a link
Reference in a new issue