Compare commits

..

No commits in common. "cd2e012b8eb280b02464ddc3da22cf2837225b15" and "cd5a501307e7e259892d66a4636aec8945c13da7" have entirely different histories.

12 changed files with 19 additions and 61 deletions

View file

@ -107,7 +107,7 @@ async function attachmentToEvent(mentions, attachment) {
msgtype: "m.text", msgtype: "m.text",
body: `${emoji} Uploaded SPOILER file: ${attachment.url} (${pb(attachment.size)})`, body: `${emoji} Uploaded SPOILER file: ${attachment.url} (${pb(attachment.size)})`,
format: "org.matrix.custom.html", format: "org.matrix.custom.html",
formatted_body: `<blockquote>${emoji} Uploaded SPOILER file: <a href="${attachment.url}">${attachment.url}</a> (${pb(attachment.size)})</blockquote>` formatted_body: `<blockquote>${emoji} Uploaded SPOILER file: <a href="${attachment.url}"><span data-mx-spoiler>${attachment.url}</span></a> (${pb(attachment.size)})</blockquote>`
} }
} }
// for large files, always link them instead of uploading so I don't use up all the space in the content repo // for large files, always link them instead of uploading so I don't use up all the space in the content repo

View file

@ -176,7 +176,7 @@ test("message2event: spoiler attachment", async t => {
msgtype: "m.text", msgtype: "m.text",
body: "📄 Uploaded SPOILER file: https://cdn.discordapp.com/attachments/1100319550446252084/1147465564307079258/SPOILER_69-GNDP-CADENCE.nfs.gci (74 KB)", body: "📄 Uploaded SPOILER file: https://cdn.discordapp.com/attachments/1100319550446252084/1147465564307079258/SPOILER_69-GNDP-CADENCE.nfs.gci (74 KB)",
format: "org.matrix.custom.html", format: "org.matrix.custom.html",
formatted_body: "<blockquote>📄 Uploaded SPOILER file: <a href=\"https://cdn.discordapp.com/attachments/1100319550446252084/1147465564307079258/SPOILER_69-GNDP-CADENCE.nfs.gci\">https://cdn.discordapp.com/attachments/1100319550446252084/1147465564307079258/SPOILER_69-GNDP-CADENCE.nfs.gci</a> (74 KB)</blockquote>" formatted_body: "<blockquote>📄 Uploaded SPOILER file: <span data-mx-spoiler><a href=\"https://cdn.discordapp.com/attachments/1100319550446252084/1147465564307079258/SPOILER_69-GNDP-CADENCE.nfs.gci\">View</a></span> (74 KB)</blockquote>"
}]) }])
}) })

View file

@ -5,10 +5,6 @@ const assert = require("assert")
const passthrough = require("../../passthrough") const passthrough = require("../../passthrough")
const {select} = passthrough const {select} = passthrough
const SPECIAL_USER_MAPPINGS = new Map([
["1081004946872352958", ["clyde_ai", "clyde"]]
])
/** /**
* Downcased and stripped username. Can only include a basic set of characters. * Downcased and stripped username. Can only include a basic set of characters.
* https://spec.matrix.org/v1.6/appendices/#user-identifiers * https://spec.matrix.org/v1.6/appendices/#user-identifiers
@ -34,7 +30,7 @@ function downcaseUsername(user) {
/** @param {string[]} preferences */ /** @param {string[]} preferences */
function* generateLocalpartAlternatives(preferences) { function* generateLocalpartAlternatives(preferences) {
const best = preferences[0] const best = preferences[0]
assert(best) assert.ok(best)
// First, suggest the preferences... // First, suggest the preferences...
for (const localpart of preferences) { for (const localpart of preferences) {
yield localpart yield localpart
@ -54,18 +50,15 @@ function* generateLocalpartAlternatives(preferences) {
* @returns {string} * @returns {string}
*/ */
function userToSimName(user) { function userToSimName(user) {
if (!SPECIAL_USER_MAPPINGS.has(user.id)) { // skip this check for known special users assert.notEqual(user.discriminator, "0000", "cannot create user for a webhook")
assert.notEqual(user.discriminator, "0000", `cannot create user for a webhook: ${JSON.stringify(user)}`)
}
// 1. Is sim user already registered? // 1. Is sim user already registered?
const existing = select("sim", "sim_name", {user_id: user.id}).pluck().get() const existing = select("sim", "sim_name", {user_id: user.id}).pluck().get()
if (existing) return existing if (existing) return existing
// 2. Register based on username (could be new or old format) // 2. Register based on username (could be new or old format)
// (Unless it's a special user, in which case copy their provided mappings.)
const downcased = downcaseUsername(user) const downcased = downcaseUsername(user)
const preferences = SPECIAL_USER_MAPPINGS.get(user.id) || [downcased] const preferences = [downcased]
if (user.discriminator.length === 4) { // Old style tag? If user.username is unavailable, try the full tag next if (user.discriminator.length === 4) { // Old style tag? If user.username is unavailable, try the full tag next
preferences.push(downcased + user.discriminator) preferences.push(downcased + user.discriminator)
} }

View file

@ -1,7 +1,6 @@
const {test} = require("supertape") const {test} = require("supertape")
const tryToCatch = require("try-to-catch") const tryToCatch = require("try-to-catch")
const assert = require("assert") const assert = require("assert")
const data = require("../../test/data")
const {userToSimName} = require("./user-to-mxid") const {userToSimName} = require("./user-to-mxid")
test("user2name: cannot create user for a webhook", async t => { test("user2name: cannot create user for a webhook", async t => {
@ -40,7 +39,3 @@ test("user2name: uses ID if name becomes too short", t => {
test("user2name: uses ID when name has only disallowed characters", t => { test("user2name: uses ID when name has only disallowed characters", t => {
t.equal(userToSimName({username: "!@#$%^&*", discriminator: "0001", id: "9"}), "9") t.equal(userToSimName({username: "!@#$%^&*", discriminator: "0001", id: "9"}), "9")
}) })
test("user2name: works on special user", t => {
t.equal(userToSimName(data.user.clyde_ai), "clyde_ai")
})

View file

@ -15,8 +15,6 @@ const channelWebhook = sync.require("./channel-webhook")
const eventToMessage = sync.require("../converters/event-to-message") const eventToMessage = sync.require("../converters/event-to-message")
/** @type {import("../../matrix/api")}) */ /** @type {import("../../matrix/api")}) */
const api = sync.require("../../matrix/api") const api = sync.require("../../matrix/api")
/** @type {import("../../d2m/actions/register-user")} */
const registerUser = sync.require("../../d2m/actions/register-user")
/** /**
* @param {DiscordTypes.RESTPostAPIWebhookWithTokenJSONBody & {files?: {name: string, file: Buffer | Readable}[], pendingFiles?: ({name: string, url: string} | {name: string, url: string, key: string, iv: string} | {name: string, buffer: Buffer | Readable})[]}} message * @param {DiscordTypes.RESTPostAPIWebhookWithTokenJSONBody & {files?: {name: string, file: Buffer | Readable}[], pendingFiles?: ({name: string, url: string} | {name: string, url: string, key: string, iv: string} | {name: string, buffer: Buffer | Readable})[]}} message
@ -75,7 +73,7 @@ async function sendEvent(event) {
// no need to sync the matrix member to the other side. but if I did need to, this is where I'd do it // no need to sync the matrix member to the other side. but if I did need to, this is where I'd do it
let {messagesToEdit, messagesToSend, messagesToDelete, ensureJoined} = await eventToMessage.eventToMessage(event, guild, {api, snow: discord.snow}) let {messagesToEdit, messagesToSend, messagesToDelete} = await eventToMessage.eventToMessage(event, guild, {api, snow: discord.snow})
messagesToEdit = await Promise.all(messagesToEdit.map(async e => { messagesToEdit = await Promise.all(messagesToEdit.map(async e => {
e.message = await resolvePendingFiles(e.message) e.message = await resolvePendingFiles(e.message)
@ -109,10 +107,6 @@ async function sendEvent(event) {
messageResponses.push(messageResponse) messageResponses.push(messageResponse)
} }
for (const user of ensureJoined) {
registerUser.ensureSimJoined(user, event.room_id)
}
return messageResponses return messageResponses
} }

View file

@ -505,13 +505,12 @@ async function eventToMessage(event, guild, di) {
content = displayNameRunoff + replyLine + content content = displayNameRunoff + replyLine + content
// Handling written @mentions: we need to look for candidate Discord members to join to the room // Handling written @mentions: we need to look for candidate Discord members to join to the room
let writtenMentionMatch = content.match(/(?:^|[^"<>/A-Za-z0-9])@([A-Za-z][A-Za-z0-9._\[\]\(\)-]+):?/d) // /d flag for indices requires node.js 16+ let writtenMentionMatch = content.match(/(?:^|[^"<>/A-Za-z0-9])@([A-Za-z][A-Za-z0-9._\[\]\(\)-]+):?/)
if (writtenMentionMatch) { if (writtenMentionMatch) {
const results = await di.snow.guild.searchGuildMembers(guild.id, {query: writtenMentionMatch[1]}) const results = await di.snow.guild.searchGuildMembers(guild.id, {query: writtenMentionMatch[1]})
if (results[0]) { if (results[0]) {
assert(results[0].user) assert(results[0].user)
// @ts-ignore - typescript doesn't know about indices yet content = content.slice(0, writtenMentionMatch.index) + `<@${results[0].user.id}>` + content.slice(writtenMentionMatch.index + writtenMentionMatch[0].length)
content = content.slice(0, writtenMentionMatch.indices[1][0]-1) + `<@${results[0].user.id}>` + content.slice(writtenMentionMatch.indices[1][1])
ensureJoined.push(results[0].user) ensureJoined.push(results[0].user)
} }
} }

View file

@ -2179,7 +2179,7 @@ test("event2message: guessed @mentions may join members to mention", async t =>
sender: "@cadence:cadence.moe", sender: "@cadence:cadence.moe",
content: { content: {
msgtype: "m.text", msgtype: "m.text",
body: "hey @subtext, what food would you like to order?" body: "@subtext: what food would you like to order?"
}, },
event_id: "$u5gSwSzv_ZQS3eM00mnTBCor8nx_A_AwuQz7e59PZk8", event_id: "$u5gSwSzv_ZQS3eM00mnTBCor8nx_A_AwuQz7e59PZk8",
room_id: "!kLRqKKUQXcibIMtOpl:cadence.moe" room_id: "!kLRqKKUQXcibIMtOpl:cadence.moe"
@ -2202,7 +2202,7 @@ test("event2message: guessed @mentions may join members to mention", async t =>
messagesToEdit: [], messagesToEdit: [],
messagesToSend: [{ messagesToSend: [{
username: "cadence [they]", username: "cadence [they]",
content: "hey <@321876634777218072>, what food would you like to order?", content: "<@321876634777218072> what food would you like to order?",
avatar_url: undefined avatar_url: undefined
}], }],
ensureJoined: [subtext.user] ensureJoined: [subtext.user]

View file

@ -19,17 +19,15 @@ const makeTxnId = sync.require("./txnid")
* @returns {string} the new endpoint * @returns {string} the new endpoint
*/ */
function path(p, mxid, otherParams = {}) { function path(p, mxid, otherParams = {}) {
if (!mxid) return p
const u = new URL(p, "http://localhost") const u = new URL(p, "http://localhost")
if (mxid) u.searchParams.set("user_id", mxid) u.searchParams.set("user_id", mxid)
for (const entry of Object.entries(otherParams)) { for (const entry of Object.entries(otherParams)) {
if (entry[1] != undefined) { if (entry[1] != undefined) {
u.searchParams.set(entry[0], entry[1]) u.searchParams.set(entry[0], entry[1])
} }
} }
let result = u.pathname return u.pathname + "?" + u.searchParams.toString()
const str = u.searchParams.toString()
if (str) result += "?" + str
return result
} }
/** /**
@ -90,7 +88,7 @@ async function getEvent(roomID, eventID) {
*/ */
async function getEventForTimestamp(roomID, ts) { async function getEventForTimestamp(roomID, ts) {
/** @type {{event_id: string, origin_server_ts: number}} */ /** @type {{event_id: string, origin_server_ts: number}} */
const root = await mreq.mreq("GET", path(`/client/v1/rooms/${roomID}/timestamp_to_event`, null, {ts})) const root = await mreq.mreq("GET", path(`/client/v3/rooms/${roomID}/timestamp_to_event`, null, {ts}))
return root return root
} }

View file

@ -20,7 +20,3 @@ test("api path: existing query parameters with mxid", t => {
test("api path: real world mxid", t => { test("api path: real world mxid", t => {
t.equal(path("/hello/world", "@cookie_monster:cadence.moe"), "/hello/world?user_id=%40cookie_monster%3Acadence.moe") t.equal(path("/hello/world", "@cookie_monster:cadence.moe"), "/hello/world?user_id=%40cookie_monster%3Acadence.moe")
}) })
test("api path: extras number works", t => {
t.equal(path(`/client/v3/rooms/!example/timestamp_to_event`, null, {ts: 1687324651120}), "/client/v3/rooms/!example/timestamp_to_event?ts=1687324651120")
})

8
package-lock.json generated
View file

@ -12,7 +12,7 @@
"@chriscdn/promise-semaphore": "^2.0.1", "@chriscdn/promise-semaphore": "^2.0.1",
"better-sqlite3": "^9.0.0", "better-sqlite3": "^9.0.0",
"chunk-text": "^2.0.1", "chunk-text": "^2.0.1",
"cloudstorm": "^0.9.5", "cloudstorm": ">=0.9.0",
"discord-markdown": "git+https://git.sr.ht/~cadence/nodejs-discord-markdown#de519353668c87ecc8c543e9749093481bc72ff8", "discord-markdown": "git+https://git.sr.ht/~cadence/nodejs-discord-markdown#de519353668c87ecc8c543e9749093481bc72ff8",
"entities": "^4.5.0", "entities": "^4.5.0",
"giframe": "github:cloudrac3r/giframe#v0.4.1", "giframe": "github:cloudrac3r/giframe#v0.4.1",
@ -783,9 +783,9 @@
} }
}, },
"node_modules/cloudstorm": { "node_modules/cloudstorm": {
"version": "0.9.5", "version": "0.9.2",
"resolved": "https://registry.npmjs.org/cloudstorm/-/cloudstorm-0.9.5.tgz", "resolved": "https://registry.npmjs.org/cloudstorm/-/cloudstorm-0.9.2.tgz",
"integrity": "sha512-WKaCsTDobR5c3YOmAchIa4QhPyWkUtYP3wNC/h6iE4bXE1DdN432FD3u3cuD3fX1Km9fPgpGBi4m6KYf5GFkJg==", "integrity": "sha512-dXyK8/SseyhAvblPDbDILCb6ghpoJnBAiBx1ig5/yQ54TvOXlZJ4MC+So7EJDdaHkTgnf38F8qNyBNN29sMMcQ==",
"dependencies": { "dependencies": {
"snowtransfer": "^0.9.0" "snowtransfer": "^0.9.0"
}, },

View file

@ -18,7 +18,7 @@
"@chriscdn/promise-semaphore": "^2.0.1", "@chriscdn/promise-semaphore": "^2.0.1",
"better-sqlite3": "^9.0.0", "better-sqlite3": "^9.0.0",
"chunk-text": "^2.0.1", "chunk-text": "^2.0.1",
"cloudstorm": "^0.9.5", "cloudstorm": ">=0.9.0",
"discord-markdown": "git+https://git.sr.ht/~cadence/nodejs-discord-markdown#de519353668c87ecc8c543e9749093481bc72ff8", "discord-markdown": "git+https://git.sr.ht/~cadence/nodejs-discord-markdown#de519353668c87ecc8c543e9749093481bc72ff8",
"entities": "^4.5.0", "entities": "^4.5.0",
"giframe": "github:cloudrac3r/giframe#v0.4.1", "giframe": "github:cloudrac3r/giframe#v0.4.1",

View file

@ -183,23 +183,6 @@ module.exports = {
safety_alerts_channel_id: null safety_alerts_channel_id: null
} }
}, },
user: {
clyde_ai: {
id: "1081004946872352958",
username: "clyde",
avatar: "a_6170487d32fdfe9f988720ad80e6ab8c",
discriminator: "0000",
public_flags: 0,
premium_type: 2,
flags: 0,
bot: true,
banner: null,
accent_color: null,
global_name: "Clyde",
avatar_decoration_data: null,
banner_color: null
}
},
member: { member: {
kumaccino: { kumaccino: {
avatar: null, avatar: null,