Compare commits
No commits in common. "39f41e7d53055be26dfa8309884bd651fc55b32c" and "4151979e108d5803c9ad3aaec8f27a17178744f0" have entirely different histories.
39f41e7d53
...
4151979e10
9 changed files with 83 additions and 184 deletions
|
@ -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.
|
// 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.
|
// 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 = []
|
const emojiDownloads = []
|
||||||
for (const match of emojiMatches) {
|
for (const match of emojiMatches) {
|
||||||
const id = match[3]
|
const id = match[3]
|
||||||
|
|
|
@ -403,18 +403,3 @@ test("message2event: mid-message small bridged emoji", async t => {
|
||||||
formatted_body: 'h is for <img data-mx-emoticon height="32" src="mxc://cadence.moe/qWmbXeRspZRLPcjseyLmeyXC" title=":hippo:" alt=":hippo:">!'
|
formatted_body: 'h is for <img data-mx-emoticon height="32" src="mxc://cadence.moe/qWmbXeRspZRLPcjseyLmeyXC" title=":hippo:" alt=":hippo:">!'
|
||||||
}])
|
}])
|
||||||
})
|
})
|
||||||
|
|
||||||
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:
|
|
||||||
'<img data-mx-emoticon height="32" src="mxc://cadence.moe/scfRIDOGKWFDEBjVXocWYQHik" title=":brillillillilliant_move:" alt=":brillillillilliant_move:">'
|
|
||||||
+ '<img data-mx-emoticon height="32" src="mxc://cadence.moe/scfRIDOGKWFDEBjVXocWYQHik" title=":brillillillilliant_move:" alt=":brillillillilliant_move:">'
|
|
||||||
+ '<img data-mx-emoticon height="32" src="mxc://cadence.moe/scfRIDOGKWFDEBjVXocWYQHik" title=":brillillillilliant_move:" alt=":brillillillilliant_move:">'
|
|
||||||
}])
|
|
||||||
})
|
|
||||||
|
|
|
@ -74,14 +74,12 @@ INSERT INTO emoji (id, name, animated, mxc_url) VALUES
|
||||||
('230201364309868544', 'hippo', 0, 'mxc://cadence.moe/qWmbXeRspZRLPcjseyLmeyXC'),
|
('230201364309868544', 'hippo', 0, 'mxc://cadence.moe/qWmbXeRspZRLPcjseyLmeyXC'),
|
||||||
('393635038903926784', 'hipposcope', 1, 'mxc://cadence.moe/WbYqNlACRuicynBfdnPYtmvc'),
|
('393635038903926784', 'hipposcope', 1, 'mxc://cadence.moe/WbYqNlACRuicynBfdnPYtmvc'),
|
||||||
('362741439211503616', 'bn_re', 0, 'mxc://cadence.moe/OIpqpfxTnHKokcsYqDusxkBT'),
|
('362741439211503616', 'bn_re', 0, 'mxc://cadence.moe/OIpqpfxTnHKokcsYqDusxkBT'),
|
||||||
('551636841284108289', 'ae_botrac4r', 0, 'mxc://cadence.moe/skqfuItqxNmBYekzmVKyoLzs'),
|
('551636841284108289', 'ae_botrac4r', 0, 'mxc://cadence.moe/skqfuItqxNmBYekzmVKyoLzs');
|
||||||
('975572106295259148', 'brillillillilliant_move', 0, 'mxc://cadence.moe/scfRIDOGKWFDEBjVXocWYQHik');
|
|
||||||
|
|
||||||
INSERT INTO member_cache (room_id, mxid, displayname, avatar_url) VALUES
|
INSERT INTO member_cache (room_id, mxid, displayname, avatar_url) VALUES
|
||||||
('!kLRqKKUQXcibIMtOpl:cadence.moe', '@cadence:cadence.moe', 'cadence [they]', NULL),
|
('!kLRqKKUQXcibIMtOpl:cadence.moe', '@cadence:cadence.moe', 'cadence [they]', NULL),
|
||||||
('!BpMdOUkWWhFxmTrENV:cadence.moe', '@cadence:cadence.moe', 'cadence [they]', 'malformed mxc'),
|
('!BpMdOUkWWhFxmTrENV:cadence.moe', '@cadence:cadence.moe', 'cadence [they]', 'malformed mxc'),
|
||||||
('!fGgIymcYWOqjbSRUdV:cadence.moe', '@cadence:cadence.moe', 'cadence [they]', 'mxc://cadence.moe/azCAhThKTojXSZJRoWwZmhvU'),
|
('!fGgIymcYWOqjbSRUdV:cadence.moe', '@cadence:cadence.moe', 'cadence [they]', 'mxc://cadence.moe/azCAhThKTojXSZJRoWwZmhvU'),
|
||||||
('!PnyBKvUBOhjuCucEfk: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');
|
|
||||||
|
|
||||||
COMMIT;
|
COMMIT;
|
||||||
|
|
|
@ -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 */
|
/** @param {Ty.Event.Outer_M_Room_Message | Ty.Event.Outer_M_Room_Message_File | Ty.Event.Outer_M_Sticker} event */
|
||||||
async function sendEvent(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)
|
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 channelID = row.channel_id
|
||||||
let threadID = undefined
|
let threadID = undefined
|
||||||
if (row.thread_parent) {
|
if (row.thread_parent) {
|
||||||
|
|
|
@ -37,7 +37,7 @@ async function compositeMatrixEmojis(mxcs) {
|
||||||
/** @type {{info: sharp.OutputInfo, buffer: Buffer}} */
|
/** @type {{info: sharp.OutputInfo, buffer: Buffer}} */
|
||||||
const result = await new Promise((resolve, reject) => {
|
const result = await new Promise((resolve, reject) => {
|
||||||
const transformer = sharp()
|
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})
|
.png({compressionLevel: 0})
|
||||||
.toBuffer((err, buffer, info) => {
|
.toBuffer((err, buffer, info) => {
|
||||||
if (err) return reject(err)
|
if (err) return reject(err)
|
||||||
|
|
|
@ -120,33 +120,34 @@ turndownService.addRule("emoji", {
|
||||||
|
|
||||||
replacement: function (content, node) {
|
replacement: function (content, node) {
|
||||||
const mxcUrl = node.getAttribute("src")
|
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.)
|
let row = select("emoji", ["id", "name", "animated"], "WHERE mxc_url = ?").get(mxcUrl)
|
||||||
const row = select("emoji", ["id", "name", "animated"], "WHERE mxc_url = ?").get(mxcUrl)
|
if (!row) {
|
||||||
// Also guess a suitable emoji based on the ID (if available) or name
|
// We don't know what this is... but maybe we can guess based on the name?
|
||||||
let guess = null
|
|
||||||
const guessedName = node.getAttribute("title").replace(/^:|:$/g, "")
|
const guessedName = node.getAttribute("title").replace(/^:|:$/g, "")
|
||||||
for (const guild of discord?.guilds.values() || []) {
|
for (const guild of discord?.guilds.values() || []) {
|
||||||
/** @type {{name: string, id: string, animated: number}[]} */
|
/** @type {{name: string, id: string, animated: number}[]} */
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const emojis = guild.emojis
|
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())
|
const match = emojis.find(e => e.name === guessedName) || emojis.find(e => e.name?.toLowerCase() === guessedName.toLowerCase())
|
||||||
if (match) {
|
if (match) {
|
||||||
guess = match
|
row = match
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (guess) {
|
}
|
||||||
// We know an emoji, and we can use it
|
if (row) {
|
||||||
const animatedChar = guess.animated ? "a" : ""
|
const animatedChar = row.animated ? "a" : ""
|
||||||
return `<${animatedChar}:${guess.name}:${guess.id}>`
|
return `<${animatedChar}:${row.name}:${row.id}>`
|
||||||
} else if (endOfMessageEmojis.includes(mxcUrl)) {
|
} else {
|
||||||
// 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.
|
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 `<::>`
|
return `<::>`
|
||||||
} else {
|
} 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.
|
// 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)})`
|
return `[${node.getAttribute("title")}](${utils.getPublicUrlForMxc(mxcUrl)})`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
turndownService.addRule("fencedCodeBlock", {
|
turndownService.addRule("fencedCodeBlock", {
|
||||||
|
|
|
@ -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 => {
|
slow()("event2message: known and unknown emojis in the end are reuploaded as a sprite sheet", async t => {
|
||||||
|
t.comment("SKIPPED")
|
||||||
const messages = await eventToMessage({
|
const messages = await eventToMessage({
|
||||||
type: "m.room.message",
|
type: "m.room.message",
|
||||||
sender: "@cadence:cadence.moe",
|
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"
|
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 <img data-mx-emoticon height=\"32\" src=\"mxc://cadence.moe/lHfmJpzgoNyNtYHdAmBHxXix\" title=\":chess_good_move:\" alt=\":chess_good_move:\"><img data-mx-emoticon height=\"32\" src=\"mxc://cadence.moe/MtRdXixoKjKKOyHJGWLsWLNU\" title=\":chess_incorrect:\" alt=\":chess_incorrect:\"><img data-mx-emoticon height=\"32\" src=\"mxc://cadence.moe/HXfFuougamkURPPMflTJRxGc\" title=\":chess_blund:\" alt=\":chess_blund:\"><img data-mx-emoticon height=\"32\" src=\"mxc://cadence.moe/ikYKbkhGhMERAuPPbsnQzZiX\" title=\":chess_brilliant_move:\" alt=\":chess_brilliant_move:\"><img data-mx-emoticon height=\"32\" src=\"mxc://cadence.moe/AYPpqXzVJvZdzMQJGjioIQBZ\" title=\":chess_blundest:\" alt=\":chess_blundest:\"><img data-mx-emoticon height=\"32\" src=\"mxc://cadence.moe/UVuzvpVUhqjiueMxYXJiFEAj\" title=\":chess_draw_black:\" alt=\":chess_draw_black:\">"
|
|
||||||
},
|
|
||||||
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"
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
|
@ -12,9 +12,6 @@ const api = sync.require("./api")
|
||||||
const mxUtils = sync.require("../m2d/converters/utils")
|
const mxUtils = sync.require("../m2d/converters/utils")
|
||||||
/** @type {import("../discord/utils")} */
|
/** @type {import("../discord/utils")} */
|
||||||
const dUtils = sync.require("../discord/utils")
|
const dUtils = sync.require("../discord/utils")
|
||||||
/** @type {import("./kstate")} */
|
|
||||||
const ks = sync.require("./kstate")
|
|
||||||
const reg = require("./read-registration")
|
|
||||||
|
|
||||||
const PREFIXES = ["//", "/"]
|
const PREFIXES = ["//", "/"]
|
||||||
|
|
||||||
|
@ -72,7 +69,6 @@ function onReactionAdd(event) {
|
||||||
/**
|
/**
|
||||||
* @callback CommandExecute
|
* @callback CommandExecute
|
||||||
* @param {Ty.Event.Outer_M_Room_Message} event
|
* @param {Ty.Event.Outer_M_Room_Message} event
|
||||||
* @param {string} realBody
|
|
||||||
* @param {any} [ctx]
|
* @param {any} [ctx]
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -85,13 +81,13 @@ function onReactionAdd(event) {
|
||||||
/** @param {CommandExecute} execute */
|
/** @param {CommandExecute} execute */
|
||||||
function replyctx(execute) {
|
function replyctx(execute) {
|
||||||
/** @type {CommandExecute} */
|
/** @type {CommandExecute} */
|
||||||
return function(event, realBody, ctx = {}) {
|
return function(event, ctx = {}) {
|
||||||
ctx["m.relates_to"] = {
|
ctx["m.relates_to"] = {
|
||||||
"m.in_reply_to": {
|
"m.in_reply_to": {
|
||||||
event_id: event.event_id
|
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) {
|
add(body, formattedBody, condition = true) {
|
||||||
if (condition) {
|
if (condition) {
|
||||||
if (formattedBody == undefined) formattedBody = body
|
if (!formattedBody) formattedBody = body
|
||||||
this.body += body
|
this.body += body
|
||||||
this.formattedBody += formattedBody
|
this.formattedBody += formattedBody
|
||||||
}
|
}
|
||||||
|
@ -124,7 +120,7 @@ class MatrixStringBuilder {
|
||||||
*/
|
*/
|
||||||
addLine(body, formattedBody, condition = true) {
|
addLine(body, formattedBody, condition = true) {
|
||||||
if (condition) {
|
if (condition) {
|
||||||
if (formattedBody == undefined) formattedBody = body
|
if (!formattedBody) formattedBody = body
|
||||||
if (this.body.length && this.body.slice(-1) !== "\n") this.body += "\n"
|
if (this.body.length && this.body.slice(-1) !== "\n") this.body += "\n"
|
||||||
this.body += body
|
this.body += body
|
||||||
const match = this.formattedBody.match(/<\/?([a-zA-Z]+[a-zA-Z0-9]*)[^>]*>\s*$/)
|
const match = this.formattedBody.match(/<\/?([a-zA-Z]+[a-zA-Z0-9]*)[^>]*>\s*$/)
|
||||||
|
@ -148,14 +144,13 @@ class MatrixStringBuilder {
|
||||||
const commands = [{
|
const commands = [{
|
||||||
aliases: ["emoji"],
|
aliases: ["emoji"],
|
||||||
execute: replyctx(
|
execute: replyctx(
|
||||||
async (event, realBody, ctx) => {
|
async (event, ctx) => {
|
||||||
// Guard
|
// Guard
|
||||||
/** @type {string} */ // @ts-ignore
|
/** @type {string} */ // @ts-ignore
|
||||||
const channelID = select("channel_room", "channel_id", "WHERE room_id = ?").pluck().get(event.room_id)
|
const channelID = select("channel_room", "channel_id", "WHERE room_id = ?").pluck().get(event.room_id)
|
||||||
const guildID = discord.channels.get(channelID)?.["guild_id"]
|
const guildID = discord.channels.get(channelID)?.["guild_id"]
|
||||||
let matrixOnlyReason = null
|
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."
|
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) {
|
if (!guildID) {
|
||||||
matrixOnlyReason = "NOT_BRIDGED"
|
matrixOnlyReason = "NOT_BRIDGED"
|
||||||
} else {
|
} else {
|
||||||
|
@ -169,68 +164,47 @@ const commands = [{
|
||||||
matrixOnlyReason = "USER_PERMISSIONS"
|
matrixOnlyReason = "USER_PERMISSIONS"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (matrixOnlyReason) {
|
|
||||||
// If uploading to Matrix, check if we have permission
|
const nameMatch = event.content.body.match(/:([a-zA-Z0-9_]{2,}):/)
|
||||||
const state = await api.getAllState(event.room_id)
|
if (!nameMatch) {
|
||||||
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", {
|
return api.sendEvent(event.room_id, "m.room.message", {
|
||||||
...ctx,
|
...ctx,
|
||||||
msgtype: "m.text",
|
msgtype: "m.text",
|
||||||
body: "I don't have sufficient permissions in this Matrix room to edit emojis."
|
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]
|
||||||
|
|
||||||
/** @type {{url: string, name: string}[]} */
|
let mxc
|
||||||
const toUpload = []
|
const mxcMatch = event.content.body.match(/(mxc:\/\/.*?)\b/)
|
||||||
const nameMatch = realBody.match(/:([a-zA-Z0-9_]{2,}):/)
|
if (mxcMatch) {
|
||||||
const mxcMatch = realBody.match(/(mxc:\/\/.*?)\b/)
|
mxc = mxcMatch[1]
|
||||||
if (event.content["m.relates_to"]?.["m.in_reply_to"]?.event_id) {
|
}
|
||||||
|
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 repliedToEventID = event.content["m.relates_to"]["m.in_reply_to"].event_id
|
||||||
const repliedToEvent = await api.getEvent(event.room_id, repliedToEventID)
|
const repliedToEvent = await api.getEvent(event.room_id, repliedToEventID)
|
||||||
if (nameMatch && repliedToEvent.type === "m.room.message" && repliedToEvent.content.msgtype === "m.image" && repliedToEvent.content.url) {
|
if (repliedToEvent.type === "m.room.message" && repliedToEvent.content.msgtype === "m.image" && repliedToEvent.content.url) {
|
||||||
toUpload.push({url: repliedToEvent.content.url, name: nameMatch[1]})
|
mxc = repliedToEvent.content.url
|
||||||
} 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(/<img [^>]*>/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 (!mxc) {
|
||||||
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", {
|
return api.sendEvent(event.room_id, "m.room.message", {
|
||||||
...ctx,
|
...ctx,
|
||||||
msgtype: "m.text",
|
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 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()
|
const sent = await api.sendEvent(event.room_id, "m.room.message", {
|
||||||
|
...ctx,
|
||||||
|
...new MatrixStringBuilder()
|
||||||
.addLine("## Emoji preview", "<h2>Emoji preview</h2>")
|
.addLine("## Emoji preview", "<h2>Emoji preview</h2>")
|
||||||
.addLine(`Ⓜ️ This room isn't bridged to Discord. ${matrixOnlyConclusion}`, `Ⓜ️ <em>This room isn't bridged to Discord. ${matrixOnlyConclusion}</em>`, matrixOnlyReason === "NOT_BRIDGED")
|
.addLine(`Ⓜ️ This room isn't bridged to Discord. ${matrixOnlyConclusion}`, `Ⓜ️ <em>This room isn't bridged to Discord. ${matrixOnlyConclusion}</em>`, matrixOnlyReason === "NOT_BRIDGED")
|
||||||
.addLine(`Ⓜ️ *Discord ran out of space for emojis. ${matrixOnlyConclusion}`, `Ⓜ️ <em>Discord ran out of space for emojis. ${matrixOnlyConclusion}</em>`, matrixOnlyReason === "CAPACITY")
|
.addLine(`Ⓜ️ *Discord ran out of space for emojis. ${matrixOnlyConclusion}`, `Ⓜ️ <em>Discord ran out of space for emojis. ${matrixOnlyConclusion}</em>`, matrixOnlyReason === "CAPACITY")
|
||||||
.addLine(`Ⓜ️ *If you were a Discord user, you wouldn't have permission to create emojis. ${matrixOnlyConclusion}`, `Ⓜ️ <em>If you were a Discord user, you wouldn't have permission to create emojis. ${matrixOnlyConclusion}</em>`, matrixOnlyReason === "CAPACITY")
|
.addLine(`Ⓜ️ *If you were a Discord user, you wouldn't have permission to create emojis. ${matrixOnlyConclusion}`, `Ⓜ️ <em>If you were a Discord user, you wouldn't have permission to create emojis. ${matrixOnlyConclusion}</em>`, matrixOnlyReason === "CAPACITY")
|
||||||
.addLine("[Preview not available in plain text.]", "Preview:")
|
.addLine("[Preview not available in plain text.]", `Preview: <img data-mx-emoticon height="48" src="${mxc} title=":${name}:" alt=":${name}:">`)
|
||||||
for (const e of toUpload) {
|
.addLine("Hit ✅ to add it.")
|
||||||
b.add("", `<img data-mx-emoticon height="48" src="${e.url}" title=":${e.name}:" alt=":${e.name}:">`)
|
.get()
|
||||||
}
|
|
||||||
b.addLine("Hit ✅ to add it.")
|
|
||||||
const sent = await api.sendEvent(event.room_id, "m.room.message", {
|
|
||||||
...ctx,
|
|
||||||
...b.get()
|
|
||||||
})
|
})
|
||||||
addButton(event.room_id, sent, "✅", event.sender).then(async () => {
|
addButton(event.room_id, sent, "✅", event.sender).then(async () => {
|
||||||
if (matrixOnlyReason) {
|
if (matrixOnlyReason) {
|
||||||
|
@ -249,36 +223,30 @@ const commands = [{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!("images" in pack)) pack.images = {}
|
if (!("images" in pack)) pack.images = {}
|
||||||
const b = new MatrixStringBuilder()
|
pack.images[name] = {
|
||||||
.addLine(`Created ${toUpload.length} emojis`, "")
|
url: mxc // Directly use the same file that the Matrix user uploaded. Don't need to worry about dimensions/filesize because clients already request their preferred resized version from the homeserver.
|
||||||
for (const e of toUpload) {
|
|
||||||
pack.images[e.name] = {
|
|
||||||
url: e.url // Directly use the same file that the Matrix user uploaded. Don't need to worry about dimensions/filesize because clients already request their preferred resized version from the homeserver.
|
|
||||||
}
|
}
|
||||||
b.add("", `<img data-mx-emoticon height="48" src="${e.url}" title=":${e.name}:" alt=":${e.name}:">`)
|
|
||||||
}
|
|
||||||
await api.sendState(event.room_id, type, key, pack)
|
|
||||||
api.sendEvent(event.room_id, "m.room.message", {
|
api.sendEvent(event.room_id, "m.room.message", {
|
||||||
...ctx,
|
...ctx,
|
||||||
...b.get()
|
...new MatrixStringBuilder()
|
||||||
|
.addLine(`Created :${name}:`, `<img data-mx-emoticon height="48" src="${mxc}" title=":${name}:" alt=":${name}:">`)
|
||||||
|
.get()
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
// Upload it to Discord and have the bridge sync it back to Matrix again
|
// Upload it to Discord and have the bridge sync it back to Matrix again
|
||||||
for (const e of toUpload) {
|
const publicUrl = mxUtils.getPublicUrlForMxc(mxc)
|
||||||
const publicUrl = mxUtils.getPublicUrlForMxc(e.url)
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const resizeInput = await fetch(publicUrl, {agent: false}).then(res => res.arrayBuffer())
|
const resizeInput = await fetch(publicUrl, {agent: false}).then(res => res.arrayBuffer())
|
||||||
const resizeOutput = await sharp(resizeInput)
|
const resizeOutput = await sharp(resizeInput)
|
||||||
.resize(EMOJI_SIZE, EMOJI_SIZE, {fit: "inside", withoutEnlargement: true, background: {r: 0, g: 0, b: 0, alpha: 0}})
|
.resize(EMOJI_SIZE, EMOJI_SIZE, {fit: "inside", withoutEnlargement: true, background: {r: 0, g: 0, b: 0, alpha: 0}})
|
||||||
.png()
|
.png()
|
||||||
.toBuffer({resolveWithObject: true})
|
.toBuffer({resolveWithObject: true})
|
||||||
console.log(`uploading emoji ${resizeOutput.data.length} bytes to :${e.name}:`)
|
console.log(`uploading emoji ${resizeOutput.data.length} bytes to :${name}:`)
|
||||||
const emoji = await discord.snow.guildAssets.createEmoji(guildID, {name: e.name, image: "data:image/png;base64," + resizeOutput.data.toString("base64")})
|
const emoji = await discord.snow.guildAssets.createEmoji(guildID, {name, image: "data:image/png;base64," + resizeOutput.data.toString("base64")})
|
||||||
}
|
|
||||||
api.sendEvent(event.room_id, "m.room.message", {
|
api.sendEvent(event.room_id, "m.room.message", {
|
||||||
...ctx,
|
...ctx,
|
||||||
msgtype: "m.text",
|
msgtype: "m.text",
|
||||||
body: `Created ${toUpload.length} emojis`
|
body: `Created :${name}:`
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -308,7 +276,7 @@ async function execute(event) {
|
||||||
const command = commands.find(c => c.aliases.includes(commandName))
|
const command = commands.find(c => c.aliases.includes(commandName))
|
||||||
if (!command) return
|
if (!command) return
|
||||||
|
|
||||||
await command.execute(event, realBody)
|
await command.execute(event)
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports.execute = execute
|
module.exports.execute = execute
|
||||||
|
|
30
test/data.js
30
test/data.js
|
@ -1038,36 +1038,6 @@ module.exports = {
|
||||||
flags: 0,
|
flags: 0,
|
||||||
components: []
|
components: []
|
||||||
},
|
},
|
||||||
emoji_triple_long_name: {
|
|
||||||
id: "1156394116540805170",
|
|
||||||
type: 0,
|
|
||||||
content: "<:brillillillilliant_move:975572106295259148><:brillillillilliant_move:975572106295259148><:brillillillilliant_move:975572106295259148>",
|
|
||||||
channel_id: "112760669178241024",
|
|
||||||
author: {
|
|
||||||
id: "111604486476181504",
|
|
||||||
username: "kyuugryphon",
|
|
||||||
avatar: "e4ce31267ca524d19be80e684d4cafa1",
|
|
||||||
discriminator: "0",
|
|
||||||
public_flags: 0,
|
|
||||||
flags: 0,
|
|
||||||
banner: null,
|
|
||||||
accent_color: null,
|
|
||||||
global_name: "KyuuGryphon",
|
|
||||||
avatar_decoration_data: null,
|
|
||||||
banner_color: null
|
|
||||||
},
|
|
||||||
attachments: [],
|
|
||||||
embeds: [],
|
|
||||||
mentions: [],
|
|
||||||
mention_roles: [],
|
|
||||||
pinned: false,
|
|
||||||
mention_everyone: false,
|
|
||||||
tts: false,
|
|
||||||
timestamp: "2023-09-27T00:57:22.147000+00:00",
|
|
||||||
edited_timestamp: null,
|
|
||||||
flags: 0,
|
|
||||||
components: []
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
message_with_embeds: {
|
message_with_embeds: {
|
||||||
nothing_but_a_field: {
|
nothing_but_a_field: {
|
||||||
|
|
Loading…
Reference in a new issue