diff --git a/d2m/converters/message-to-event.js b/d2m/converters/message-to-event.js
index 69d1619..15eaf34 100644
--- a/d2m/converters/message-to-event.js
+++ b/d2m/converters/message-to-event.js
@@ -158,7 +158,7 @@ async function messageToEvent(message, guild, options = {}, di) {
// Handling emojis that we don't know about. The emoji has to be present in the DB for it to be picked up in the emoji markdown converter.
// So we scan the message ahead of time for all its emojis and ensure they are in the DB.
- const emojiMatches = [...content.matchAll(/<(a?):([^:>]{2,64}):([0-9]+)>/g)]
+ const emojiMatches = [...content.matchAll(/<(a?):([^:>]{2,20}):([0-9]+)>/g)]
const emojiDownloads = []
for (const match of emojiMatches) {
const id = match[3]
diff --git a/d2m/converters/message-to-event.test.js b/d2m/converters/message-to-event.test.js
index 34f376f..c09708d 100644
--- a/d2m/converters/message-to-event.test.js
+++ b/d2m/converters/message-to-event.test.js
@@ -403,18 +403,3 @@ test("message2event: mid-message small bridged emoji", async t => {
formatted_body: 'h is for !'
}])
})
-
-test("message2event: emoji triple long name", async t => {
- const events = await messageToEvent(data.message.emoji_triple_long_name, data.guild.general, {})
- t.deepEqual(events, [{
- $type: "m.room.message",
- "m.mentions": {},
- msgtype: "m.text",
- body: ":brillillillilliant_move::brillillillilliant_move::brillillillilliant_move:",
- format: "org.matrix.custom.html",
- formatted_body:
- '
'
- + '
'
- + '
'
- }])
-})
diff --git a/db/ooye-test-data.sql b/db/ooye-test-data.sql
index 2a56381..68c6d00 100644
--- a/db/ooye-test-data.sql
+++ b/db/ooye-test-data.sql
@@ -74,14 +74,12 @@ INSERT INTO emoji (id, name, animated, mxc_url) VALUES
('230201364309868544', 'hippo', 0, 'mxc://cadence.moe/qWmbXeRspZRLPcjseyLmeyXC'),
('393635038903926784', 'hipposcope', 1, 'mxc://cadence.moe/WbYqNlACRuicynBfdnPYtmvc'),
('362741439211503616', 'bn_re', 0, 'mxc://cadence.moe/OIpqpfxTnHKokcsYqDusxkBT'),
-('551636841284108289', 'ae_botrac4r', 0, 'mxc://cadence.moe/skqfuItqxNmBYekzmVKyoLzs'),
-('975572106295259148', 'brillillillilliant_move', 0, 'mxc://cadence.moe/scfRIDOGKWFDEBjVXocWYQHik');
+('551636841284108289', 'ae_botrac4r', 0, 'mxc://cadence.moe/skqfuItqxNmBYekzmVKyoLzs');
INSERT INTO member_cache (room_id, mxid, displayname, avatar_url) VALUES
('!kLRqKKUQXcibIMtOpl:cadence.moe', '@cadence:cadence.moe', 'cadence [they]', NULL),
('!BpMdOUkWWhFxmTrENV:cadence.moe', '@cadence:cadence.moe', 'cadence [they]', 'malformed mxc'),
('!fGgIymcYWOqjbSRUdV:cadence.moe', '@cadence:cadence.moe', 'cadence [they]', 'mxc://cadence.moe/azCAhThKTojXSZJRoWwZmhvU'),
-('!PnyBKvUBOhjuCucEfk:cadence.moe', '@cadence:cadence.moe', 'cadence [they]', 'mxc://cadence.moe/azCAhThKTojXSZJRoWwZmhvU'),
-('!maggESguZBqGBZtSnr:cadence.moe', '@cadence:cadence.moe', 'cadence [they]', 'mxc://cadence.moe/azCAhThKTojXSZJRoWwZmhvU');
+('!PnyBKvUBOhjuCucEfk:cadence.moe', '@cadence:cadence.moe', 'cadence [they]', 'mxc://cadence.moe/azCAhThKTojXSZJRoWwZmhvU');
COMMIT;
diff --git a/m2d/actions/send-event.js b/m2d/actions/send-event.js
index d5797b9..ac4ef60 100644
--- a/m2d/actions/send-event.js
+++ b/m2d/actions/send-event.js
@@ -58,8 +58,9 @@ 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 = select("channel_room", ["channel_id", "thread_parent"], "WHERE room_id = ?").get(event.room_id)
- if (!row) return // allow the bot to exist in unbridged rooms, just don't do anything with it
+ assert(row)
let channelID = row.channel_id
let threadID = undefined
if (row.thread_parent) {
diff --git a/m2d/converters/emoji-sheet.js b/m2d/converters/emoji-sheet.js
index c271567..7e698c5 100644
--- a/m2d/converters/emoji-sheet.js
+++ b/m2d/converters/emoji-sheet.js
@@ -37,7 +37,7 @@ async function compositeMatrixEmojis(mxcs) {
/** @type {{info: sharp.OutputInfo, buffer: Buffer}} */
const result = await new Promise((resolve, reject) => {
const transformer = sharp()
- .resize(SIZE, SIZE, {fit: "contain", background: {r: 0, g: 0, b: 0, alpha: 0}})
+ .resize(SIZE, SIZE, {fit: "contain"})
.png({compressionLevel: 0})
.toBuffer((err, buffer, info) => {
if (err) return reject(err)
diff --git a/m2d/converters/event-to-message.js b/m2d/converters/event-to-message.js
index b028dc5..4c6e9e2 100644
--- a/m2d/converters/event-to-message.js
+++ b/m2d/converters/event-to-message.js
@@ -120,31 +120,32 @@ turndownService.addRule("emoji", {
replacement: function (content, node) {
const mxcUrl = node.getAttribute("src")
- // Get the known emoji from the database. (We may not be able to actually use this if it was from another server.)
- const row = select("emoji", ["id", "name", "animated"], "WHERE mxc_url = ?").get(mxcUrl)
- // Also guess a suitable emoji based on the ID (if available) or name
- let guess = null
- const guessedName = node.getAttribute("title").replace(/^:|:$/g, "")
- for (const guild of discord?.guilds.values() || []) {
- /** @type {{name: string, id: string, animated: number}[]} */
- // @ts-ignore
- const emojis = guild.emojis
- const match = emojis.find(e => e.id === row?.id) || emojis.find(e => e.name === guessedName) || emojis.find(e => e.name?.toLowerCase() === guessedName.toLowerCase())
- if (match) {
- guess = match
- break
+ let row = select("emoji", ["id", "name", "animated"], "WHERE mxc_url = ?").get(mxcUrl)
+ if (!row) {
+ // We don't know what this is... but maybe we can guess based on the name?
+ const guessedName = node.getAttribute("title").replace(/^:|:$/g, "")
+ for (const guild of discord?.guilds.values() || []) {
+ /** @type {{name: string, id: string, animated: number}[]} */
+ // @ts-ignore
+ const emojis = guild.emojis
+ const match = emojis.find(e => e.name === guessedName) || emojis.find(e => e.name?.toLowerCase() === guessedName.toLowerCase())
+ if (match) {
+ row = match
+ break
+ }
}
}
- if (guess) {
- // We know an emoji, and we can use it
- const animatedChar = guess.animated ? "a" : ""
- return `<${animatedChar}:${guess.name}:${guess.id}>`
- } else if (endOfMessageEmojis.includes(mxcUrl)) {
- // We can't locate or use a suitable emoji. After control returns, it will rewind over this, delete this section, and upload the emojis as a sprite sheet.
- return `<::>`
+ if (row) {
+ const animatedChar = row.animated ? "a" : ""
+ return `<${animatedChar}:${row.name}:${row.id}>`
} else {
- // We prefer not to upload this as a sprite sheet because the emoji is not at the end of the message, it is in the middle.
- return `[${node.getAttribute("title")}](${utils.getPublicUrlForMxc(mxcUrl)})`
+ if (endOfMessageEmojis.includes(mxcUrl)) {
+ // After control returns to the main converter, it will rewind over this, delete this section, and upload the emojis as a sprite sheet.
+ return `<::>`
+ } else {
+ // This emoji is not at the end of the message, it is in the middle. We don't upload middle emojis as a sprite sheet.
+ return `[${node.getAttribute("title")}](${utils.getPublicUrlForMxc(mxcUrl)})`
+ }
}
}
})
diff --git a/m2d/converters/event-to-message.test.js b/m2d/converters/event-to-message.test.js
index a65f747..438334b 100644
--- a/m2d/converters/event-to-message.test.js
+++ b/m2d/converters/event-to-message.test.js
@@ -1805,6 +1805,7 @@ slow()("event2message: unknown emoji in the end is reuploaded as a sprite sheet"
})
slow()("event2message: known and unknown emojis in the end are reuploaded as a sprite sheet", async t => {
+ t.comment("SKIPPED")
const messages = await eventToMessage({
type: "m.room.message",
sender: "@cadence:cadence.moe",
@@ -1828,28 +1829,3 @@ slow()("event2message: known and unknown emojis in the end are reuploaded as a s
fileContentStart: "iVBORw0KGgoAAAANSUhEUgAAAGAAAAAwCAYAAADuFn/PAAAACXBIWXMAAAPoAAAD6AG1e1JrAAAT5UlEQVR4nOVbCXSVRZauR9gMsoYlvKwvARKSkPUlJOyL"
})
})
-
-slow()("event2message: all unknown chess emojis are reuploaded as a sprite sheet", async t => {
- const messages = await eventToMessage({
- type: "m.room.message",
- sender: "@cadence:cadence.moe",
- content: {
- msgtype: "m.text",
- body: "testing :chess_good_move::chess_incorrect::chess_blund::chess_brilliant_move::chess_blundest::chess_draw_black:",
- format: "org.matrix.custom.html",
- formatted_body: "testing
"
- },
- event_id: "$Me6iE8C8CZyrDEOYYrXKSYRuuh_25Jj9kZaNrf7LKr4",
- room_id: "!maggESguZBqGBZtSnr:cadence.moe"
- })
- const testResult = {
- content: messages.messagesToSend[0].content,
- fileName: messages.messagesToSend[0].pendingFiles[0].name,
- fileContentStart: messages.messagesToSend[0].pendingFiles[0].buffer.subarray(0, 90).toString("base64")
- }
- t.deepEqual(testResult, {
- content: "testing",
- fileName: "emojis.png",
- fileContentStart: "iVBORw0KGgoAAAANSUhEUgAAASAAAAAwCAYAAACxIqevAAAACXBIWXMAAAPoAAAD6AG1e1JrAAAgAElEQVR4nOV9B1xUV9r3JMbEGBQLbRodhukDg2jWZP02"
- })
-})
diff --git a/matrix/matrix-command-handler.js b/matrix/matrix-command-handler.js
index 0f90bc6..e2d7f87 100644
--- a/matrix/matrix-command-handler.js
+++ b/matrix/matrix-command-handler.js
@@ -12,9 +12,6 @@ const api = sync.require("./api")
const mxUtils = sync.require("../m2d/converters/utils")
/** @type {import("../discord/utils")} */
const dUtils = sync.require("../discord/utils")
-/** @type {import("./kstate")} */
-const ks = sync.require("./kstate")
-const reg = require("./read-registration")
const PREFIXES = ["//", "/"]
@@ -72,7 +69,6 @@ function onReactionAdd(event) {
/**
* @callback CommandExecute
* @param {Ty.Event.Outer_M_Room_Message} event
- * @param {string} realBody
* @param {any} [ctx]
*/
@@ -85,13 +81,13 @@ function onReactionAdd(event) {
/** @param {CommandExecute} execute */
function replyctx(execute) {
/** @type {CommandExecute} */
- return function(event, realBody, ctx = {}) {
+ return function(event, ctx = {}) {
ctx["m.relates_to"] = {
"m.in_reply_to": {
event_id: event.event_id
}
}
- return execute(event, realBody, ctx)
+ return execute(event, ctx)
}
}
@@ -110,7 +106,7 @@ class MatrixStringBuilder {
*/
add(body, formattedBody, condition = true) {
if (condition) {
- if (formattedBody == undefined) formattedBody = body
+ if (!formattedBody) formattedBody = body
this.body += body
this.formattedBody += formattedBody
}
@@ -124,7 +120,7 @@ class MatrixStringBuilder {
*/
addLine(body, formattedBody, condition = true) {
if (condition) {
- if (formattedBody == undefined) formattedBody = body
+ if (!formattedBody) formattedBody = body
if (this.body.length && this.body.slice(-1) !== "\n") this.body += "\n"
this.body += body
const match = this.formattedBody.match(/<\/?([a-zA-Z]+[a-zA-Z0-9]*)[^>]*>\s*$/)
@@ -148,14 +144,13 @@ class MatrixStringBuilder {
const commands = [{
aliases: ["emoji"],
execute: replyctx(
- async (event, realBody, ctx) => {
+ async (event, ctx) => {
// Guard
/** @type {string} */ // @ts-ignore
const channelID = select("channel_room", "channel_id", "WHERE room_id = ?").pluck().get(event.room_id)
const guildID = discord.channels.get(channelID)?.["guild_id"]
let matrixOnlyReason = null
const matrixOnlyConclusion = "So the emoji will be uploaded on Matrix-side only. It will still be usable over the bridge, but may have degraded functionality."
- // Check if we can/should upload to Discord, for various causes
if (!guildID) {
matrixOnlyReason = "NOT_BRIDGED"
} else {
@@ -169,68 +164,47 @@ const commands = [{
matrixOnlyReason = "USER_PERMISSIONS"
}
}
- if (matrixOnlyReason) {
- // If uploading to Matrix, check if we have permission
- const state = await api.getAllState(event.room_id)
- const kstate = ks.stateToKState(state)
- const powerLevels = kstate["m.room.power_levels/"]
- const required = powerLevels.events["im.ponies.room_emotes"] ?? powerLevels.state_default ?? 50
- const have = powerLevels.users[`@${reg.sender_localpart}:${reg.ooye.server_name}`] ?? powerLevels.users_default ?? 0
- if (have < required) {
- return api.sendEvent(event.room_id, "m.room.message", {
- ...ctx,
- msgtype: "m.text",
- body: "I don't have sufficient permissions in this Matrix room to edit emojis."
- })
- }
- }
- /** @type {{url: string, name: string}[]} */
- const toUpload = []
- const nameMatch = realBody.match(/:([a-zA-Z0-9_]{2,}):/)
- const mxcMatch = realBody.match(/(mxc:\/\/.*?)\b/)
- if (event.content["m.relates_to"]?.["m.in_reply_to"]?.event_id) {
- const repliedToEventID = event.content["m.relates_to"]["m.in_reply_to"].event_id
- const repliedToEvent = await api.getEvent(event.room_id, repliedToEventID)
- if (nameMatch && repliedToEvent.type === "m.room.message" && repliedToEvent.content.msgtype === "m.image" && repliedToEvent.content.url) {
- toUpload.push({url: repliedToEvent.content.url, name: nameMatch[1]})
- } else if (repliedToEvent.type === "m.room.message" && repliedToEvent.content.msgtype === "m.text" && "formatted_body" in repliedToEvent.content) {
- const namePrefixMatch = realBody.match(/:([a-zA-Z0-9_]{2,})(?:\b|:)/)
- const imgMatches = [...repliedToEvent.content.formatted_body.matchAll(/
]*>/g)]
- for (const match of imgMatches) {
- const e = match[0]
- const url = e.match(/src="([^"]*)"/)?.[1]
- let name = e.match(/title=":?([^":]*):?"/)?.[1]
- if (!url || !name) continue
- if (namePrefixMatch) name = namePrefixMatch[1] + name
- toUpload.push({url, name})
- }
- }
- }
- if (!toUpload.length && mxcMatch && nameMatch) {
- toUpload.push({url: mxcMatch[1], name: nameMatch[1]})
- }
- if (!toUpload.length) {
+ const nameMatch = event.content.body.match(/:([a-zA-Z0-9_]{2,}):/)
+ if (!nameMatch) {
return api.sendEvent(event.room_id, "m.room.message", {
...ctx,
msgtype: "m.text",
- body: "Not sure what image you wanted to add. Try replying to an uploaded image when you use the command, or write an mxc:// URL in your message. You should specify the new name :like_this:."
+ body: "Not sure what you want to call this emoji. Try writing a new :name: in colons. The name can have letters, numbers, and underscores."
+ })
+ }
+ const name = nameMatch[1]
+
+ let mxc
+ const mxcMatch = event.content.body.match(/(mxc:\/\/.*?)\b/)
+ if (mxcMatch) {
+ mxc = mxcMatch[1]
+ }
+ if (!mxc && event.content["m.relates_to"]?.["m.in_reply_to"]?.event_id) {
+ const repliedToEventID = event.content["m.relates_to"]["m.in_reply_to"].event_id
+ const repliedToEvent = await api.getEvent(event.room_id, repliedToEventID)
+ if (repliedToEvent.type === "m.room.message" && repliedToEvent.content.msgtype === "m.image" && repliedToEvent.content.url) {
+ mxc = repliedToEvent.content.url
+ }
+ }
+ if (!mxc) {
+ return api.sendEvent(event.room_id, "m.room.message", {
+ ...ctx,
+ msgtype: "m.text",
+ body: "Not sure what image you wanted to add. Try replying to an uploaded image when you use the command, or write an mxc:// URL in your message."
})
}
- const b = new MatrixStringBuilder()
- .addLine("## Emoji preview", "