diff --git a/matrix/matrix-command-handler.js b/matrix/matrix-command-handler.js
index e2d7f87..0f90bc6 100644
--- a/matrix/matrix-command-handler.js
+++ b/matrix/matrix-command-handler.js
@@ -12,6 +12,9 @@ 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 = ["//", "/"]
@@ -69,6 +72,7 @@ function onReactionAdd(event) {
/**
* @callback CommandExecute
* @param {Ty.Event.Outer_M_Room_Message} event
+ * @param {string} realBody
* @param {any} [ctx]
*/
@@ -81,13 +85,13 @@ function onReactionAdd(event) {
/** @param {CommandExecute} execute */
function replyctx(execute) {
/** @type {CommandExecute} */
- return function(event, ctx = {}) {
+ return function(event, realBody, ctx = {}) {
ctx["m.relates_to"] = {
"m.in_reply_to": {
event_id: event.event_id
}
}
- return execute(event, ctx)
+ return execute(event, realBody, ctx)
}
}
@@ -106,7 +110,7 @@ class MatrixStringBuilder {
*/
add(body, formattedBody, condition = true) {
if (condition) {
- if (!formattedBody) formattedBody = body
+ if (formattedBody == undefined) formattedBody = body
this.body += body
this.formattedBody += formattedBody
}
@@ -120,7 +124,7 @@ class MatrixStringBuilder {
*/
addLine(body, formattedBody, condition = true) {
if (condition) {
- if (!formattedBody) formattedBody = body
+ if (formattedBody == undefined) 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*$/)
@@ -144,13 +148,14 @@ class MatrixStringBuilder {
const commands = [{
aliases: ["emoji"],
execute: replyctx(
- async (event, ctx) => {
+ async (event, realBody, 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 {
@@ -164,47 +169,68 @@ const commands = [{
matrixOnlyReason = "USER_PERMISSIONS"
}
}
-
- 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 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 (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."
+ })
}
}
- if (!mxc) {
+
+ /** @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) {
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."
+ 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:."
})
}
+ const b = new MatrixStringBuilder()
+ .addLine("## Emoji preview", "