From eaec14f4fbc4a9dcb0355aa6b864b0f4d3342def Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Wed, 21 Aug 2024 15:00:40 +0000 Subject: [PATCH] initial commit --- .env.example | 7 + .gitignore | 3 + commands/catgpt.js | 41 +++ commands/eval.js | 22 ++ commands/help.js | 55 +++ commands/info.js | 16 + commands/restart.js | 13 + index.js | 126 +++++++ lib/ext.js | 107 ++++++ lib/fedimbed.js | 597 +++++++++++++++++++++++++++++++ modules/fedimbed.js | 92 +++++ modules/meow.js | 45 +++ package.json | 20 ++ pnpm-lock.yaml | 843 ++++++++++++++++++++++++++++++++++++++++++++ 14 files changed, 1987 insertions(+) create mode 100644 .env.example create mode 100644 .gitignore create mode 100644 commands/catgpt.js create mode 100644 commands/eval.js create mode 100644 commands/help.js create mode 100644 commands/info.js create mode 100644 commands/restart.js create mode 100644 index.js create mode 100644 lib/ext.js create mode 100644 lib/fedimbed.js create mode 100644 modules/fedimbed.js create mode 100644 modules/meow.js create mode 100644 package.json create mode 100644 pnpm-lock.yaml diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..a43985b --- /dev/null +++ b/.env.example @@ -0,0 +1,7 @@ +PREFIX=! +BASE_URL=https://example.com +OWNER_ID=@root:example.com +USER_ID=@bot:example.com +ACCESS_TOKEN=1234567890 +DEVICE_ID=Bot-Device +LOG_CHANNEL=!1234567890:example.com diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7720d63 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.env +node_modules +data/* diff --git a/commands/catgpt.js b/commands/catgpt.js new file mode 100644 index 0000000..b8f27e1 --- /dev/null +++ b/commands/catgpt.js @@ -0,0 +1,41 @@ +var meows = [ + "mreow", + "miau", + "mewo", + "maow", + "mrow", + "mrao", + "meow", + "mew", + "nya", +]; + +var emoticons = [ + ":3", + "^w^", + "=^w^=", + "-w-", + ":333" +]; + +async function execute(client, event) { + var reply = ""; + for(let i = 0;i < Math.random() * 15; i++) + reply += " " + meows.random(); + + reply += "!".repeat(Math.random() * 5); + + if(Math.random() > 0.5) { + reply += " " + emoticons.random(); + } + + await client.reply(event, reply); +} + +export default { + command: "catgpt", + name: "catgpt", + usage: "[prompt]", + desc: "advanced NAI-based language meowdel", + execute +} diff --git a/commands/eval.js b/commands/eval.js new file mode 100644 index 0000000..9833c60 --- /dev/null +++ b/commands/eval.js @@ -0,0 +1,22 @@ +import { encode } from "html-entities"; +import util from "util"; + +function execute(client, event, args) { + var c = ""; + try { + var result = eval(args); + c = util.format(result).replaceAll(process.env.ACCESS_TOKEN, "*".repeat(process.env.ACCESS_TOKEN.length)); + } catch(err) { + c = util.format(err); + } + client.reply(event, c, `
${encode(c)}
`); +} + +export default { + command: "eval", + name: "eval", + usage: "", + owner: true, + desc: "Run JS code and reply with the result", + execute +} diff --git a/commands/help.js b/commands/help.js new file mode 100644 index 0000000..56df75b --- /dev/null +++ b/commands/help.js @@ -0,0 +1,55 @@ +import { encode } from "html-entities"; + +function execute(client, event, args) { + var name = args.toLowerCase(); + + var command = client.commands.filter(c=>(c.name.toLowerCase()==name || c.command.toLowerCase()==name))[0]; + var module = client.modules.filter(m=>m.name.toLowerCase()==name)[0]; + var specific = command ?? module ?? false; + + if(args != "") { + if(!command && !module) { + client.reply(event, `Section "${args}" not found.\nRun "${process.env.PREFIX}help" for a list of commands and modules.`); + return; + } + + if(module) { + var reply = module.name + "\n"; + reply += module.desc; + + var replyHTML = `${encode(module.name)}
` + + `${encode(module.desc)}`; + + client.reply(event, reply, replyHTML); + return; + } + + var reply = command.name + "\n" + + command.desc + "\n\n" + + command.usage; + + var replyHTML = `${encode(command.name)}
` + + `${encode(command.desc)}` + + `
${encode(command.usage)}`; + + client.reply(event, reply, replyHTML); + return; + } + + var reply = `commands: ${client.commands.map(m => m.name).join(", ")}\n` + + `modules: ${client.modules.map(m => m.name).join(", ")}\n` + + `run ${process.env.PREFIX}help for more information.`; + + var replyHTML = `commands: ${client.commands.map(m => encode(m.name)).join(", ")}
` + + `modules: ${client.modules.map(m => encode(m.name)).join(", ")}
` + + encode(`run ${process.env.PREFIX}help for more information.`); + + client.reply(event, reply, replyHTML); +} + +export default { + command: "help", + name: "help", + desc: "show list of commands and modules", + execute +} diff --git a/commands/info.js b/commands/info.js new file mode 100644 index 0000000..25f1adc --- /dev/null +++ b/commands/info.js @@ -0,0 +1,16 @@ +function execute(client, event, args) { + var info = `${client.name}\n\n`; + info += `Commands: ${client.commands.length}\n`; + info += `Modules: ${client.commands.length}\n`; + info += `Prefix: ${process.env.PREFIX} (you can also mention the bot!)\n`; + info += `Owner: ${process.env.OWNER_ID}`; + + client.reply(event, info); +} + +export default { + command: "info", + name: "info", + desc: "show general info about the bot", + execute +} diff --git a/commands/restart.js b/commands/restart.js new file mode 100644 index 0000000..9bc0166 --- /dev/null +++ b/commands/restart.js @@ -0,0 +1,13 @@ +import { exec } from "node:child_process"; + +function execute(client, event, args) { + exec("systemctl --user restart possumbot"); +} + +export default { + command: "restart", + name: "restart", + owner: true, + desc: "restarts the bot", + execute +} diff --git a/index.js b/index.js new file mode 100644 index 0000000..3391534 --- /dev/null +++ b/index.js @@ -0,0 +1,126 @@ +import { encode } from "html-entities"; +import * as sdk from "matrix-js-sdk"; +import sdkExt from "./lib/ext.js"; +import { resolve } from "node:path"; +import fs from "node:fs"; +import util from "util"; + +import fetch from "node-fetch"; +global.fetch = fetch; + +import Olm from "@matrix-org/olm"; +global.Olm = Olm; + +import env from "dotenv"; +env.config(); + +const client = sdk.createClient({ + baseUrl: process.env.BASE_URL, + accessToken: process.env.ACCESS_TOKEN, + deviceId: process.env.DEVICE_ID, + userId: process.env.USER_ID +}); + +sdkExt(client); + +var prefixes = [process.env.PREFIX]; +client.initialized = false; + +client.modules = []; +client.commands = []; + +for (const file of fs.readdirSync(resolve("modules"))) { + try { + if(file.startsWith(".")) continue; + var module = (await import(resolve("modules", file))).default; + client.modules.push(module); + client.log("[load:modules]", `loaded ${module.name}`); + } catch (err) { + client.error("[load:modules]", `failed to load "${file}":\n`, err); + } +} + +for (const file of fs.readdirSync(resolve("commands"))) { + try { + if(file.startsWith(".")) continue; + var command = (await import(resolve("commands", file))).default; + command.usage = process.env.PREFIX + command.command + (command.usage ? " " + command.usage : ''); + client.commands.push(command); + client.log("[load:commands]", `loaded ${command.name}`); + } catch (err) { + client.error("[load:commands]", `failed to load "${file}":`, err); + } +} + +function doModule(client, event) { + client.modules.forEach(m => { + if(!m) return; + m.hooks?.message(client, event); + }); +} + +function doCommand(client, event, cmd, args) { + var command; + command = client.commands.filter(c=>c.command==cmd)[0]; + if(!command) + return false; + + if(command.owner && event.sender.userId != process.env.OWNER_ID) { + client.reply(event, "nuh uh", 'nuh uh'); + return true; + } + + command.execute(client, event, args); + return true; +} + +client.once("sync", async function (state, prevState, data) { + switch (state) { + case "PREPARED": + client.name = client._store.users[client.credentials.userId].displayName; + client.log("[sdk:sync]", "connected:", client.name); + prefixes.push(client.name + ": "); + prefixes.push(client.name + " "); + client.initialized = true; + break; + } +}); + +client.on(sdk.RoomEvent.Timeline, async function (event, room, toStartOfTimeline) { + if (!client.initialized || toStartOfTimeline || event.getType() !== "m.room.message" || event.sender.userId == client.credentials.userId) { + return; + } + if(event.event.content["m.relates_to"] != null) + event.event.content.body = event.event.content.body.substring(event.event.content.body.indexOf("\n\n") + 2); + + var content = event.event.content.body; + var isCommand = false; + for(const prefix of prefixes) { + if(content.startsWith(prefix)) { + isCommand = true; + content = content.substring(prefix.length); + break; + } + } + if(!isCommand) { + doModule(client, event); + } else { + var args = content.split(/\s/g); + var cmd = args.shift(); + args = content.substring(cmd.length + 1); + if(!doCommand(client, event, cmd, args)) { + client.reply(event, `Command "${cmd}" not found.\nRun "${process.env.PREFIX}help" for a list of commands.`); + } + } +}); + +client.on("Room.myMembership", function (room, membership, prevMembership) { + if (membership === "invite") { + client.joinRoom(room.roomId).then(function () { + client.log("[client:autojoin] joined %s", room.roomId); + }); + } +}); + +//await client.initCrypto(); +await client.startClient(); diff --git a/lib/ext.js b/lib/ext.js new file mode 100644 index 0000000..4c575a9 --- /dev/null +++ b/lib/ext.js @@ -0,0 +1,107 @@ +import { encode } from "html-entities"; +import forceSync from 'sync-rpc'; +import { JSDOM } from "jsdom"; +import util from "util"; +import fs from "fs"; + +Array.prototype.random = function(){return this[Math.floor(Math.random() * this.length)]} + +var cache = {}; +var log = null; + +export default function(client) { + client.logEvent = null; + + client.cache = { + get: (key) => { + return cache[key] ?? null; + }, + set: (key, value) => { + cache[key] = value; + fs.writeFileSync("data/cache.json", JSON.stringify(cache)); + } + } + + client.reply = function(event, text, html) { + var mcontent = { + body: "> <" + event.sender.userId + "> " + event.event.content.body.split("\n")[0] + "\n\n" + text.trim(), + msgtype: "m.notice", + "m.relates_to": { + "m.in_reply_to": { + "event_id": event.event.event_id + } + } + }; + if(html) { + mcontent = { + format: "org.matrix.custom.html", + formatted_body: "
In reply to " + event.sender.userId + "
" + encode(event.event.content.body.split("\n")[0]) + "
" + html.trim(), + ...mcontent + } + } + client.sendEvent(event.event.room_id, "m.room.message", mcontent, "", (err, res) => { + console.log(err); + }); + } + + client.logData = []; + client.startup = new Date(); + + client.updateLog = async function() { + if(log && (log.body == client.logData.join("\n") || log["m.new_content"]?.body == client.logData.join("\n"))) { + setTimeout(client.updateLog, 1000); + return; + } + + var mcontent = { + format: "org.matrix.custom.html", + formatted_body: `
[client:logger] started: ${client.startup}\n${encode(client.logData.join("\n"))}
`, + body: client.logData.join("\n"), + msgtype: "m.notice" + } + + if(!client.logEvent) { + log = mcontent; + } else { + log["m.new_content"] = mcontent; + log["m.relates_to"] = { + rel_type: "m.replace", + event_id: client.logEvent + } + } + + var x = await client.sendEvent(process.env.LOG_CHANNEL, "m.room.message", log, "", (err, res) => { + console.log("[client:log]", res); + console.error(err); + }); + + client.logEvent = client.logEvent ?? x.event_id; + + setTimeout(client.updateLog, 1000); + } + + client.log = function() { + var data = util.format.apply(null, arguments); + client.logData.push(data); + console.log(data); + } + + client.error = client.log; + + if(fs.existsSync("data/cache.json")) { + var data = fs.readFileSync("data/cache.json"); + + try { + cache = JSON.parse(data); + client.log("[sdkext:cache]", 'loaded', Object.keys(cache).length, 'cache entries!'); + } catch(err) { + client.error("[sdkext:cache]", err); + } + } + + process.on('uncaughtException', function (err) { + console.error('[core:err]', err); + }); + + client.updateLog(); +} diff --git a/lib/fedimbed.js b/lib/fedimbed.js new file mode 100644 index 0000000..5fa8f45 --- /dev/null +++ b/lib/fedimbed.js @@ -0,0 +1,597 @@ +import httpSignature from "@peertube/http-signature"; +import path from "node:path"; +import fs from "node:fs"; + +const FRIENDLY_USERAGENT = "PossumBot/1.0 (+https://bot.possum.city/)"; + +const URLS_REGEX = /(?:\s|^|\]\()(\|\|\s*)?(https?:\/\/[^\s<]+[^<.,:;"'\]|)\s])(\s*\)?\|\||\s*[\S]*?\))?/g; + +const PATH_REGEX = { + mastodon: /^\/@(.+?)\/(\d+)\/?/, + mastodon2: /^\/(.+?)\/statuses\/\d+\/?/, + pleroma: /^\/objects\/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\/?/, + pleroma2: /^\/notice\/[A-Za-z0-9]+\/?/, + misskey: /^\/notes\/[a-z0-9]+\/?/, + gotosocial: /^\/@(.+?)\/statuses\/[0-9A-Z]+\/?/, + lemmy: /^\/post\/\d+\/?/, + honk: /^\/u\/(.+?)\/h\/(.+?)\/?/, + cohost: /^\/[A-Za-z0-9]+\/post\/\d+-[A-Za-z0-9-]+\/?/, +}; + +const PLATFORM_COLORS = { + mastodon: 0x2791da, + pleroma: 0xfba457, + akkoma: 0x593196, + misskey: 0x99c203, + calckey: 0x31748f, + firefish: 0xf07a5b, // YCbCr interpolated from the two logo colors + gotosocial: 0xff853e, + lemmy: 0x14854f, + birdsitelive: 0x1da1f2, + iceshrimp: 0x8e82f9, // YCbCr interpolated as the accent color is a gradient + cohost: 0x83254f, +}; + +const domainCache = new Map(); +domainCache.set("cohost.org", "cohost"); // no nodeinfo + +async function resolvePlatform(url) { + const urlObj = new URL(url); + if(domainCache.has(urlObj.hostname)) return domainCache.get(urlObj.hostname); + + const res = await fetch(urlObj.origin + "/.well-known/nodeinfo", { + headers: {"User-Agent": FRIENDLY_USERAGENT}, + }).then((res) => res.text()); + + if(!res.startsWith("{")) { + console.error("[fedimbed]", `No nodeinfo for "${urlObj.hostname}"???`); + domainCache.set(urlObj.hostname, null); + return null; + } + + const probe = JSON.parse(res); + + if(!probe?.links) { + console.error("[fedimbed]", `No nodeinfo for "${urlObj.hostname}"???`); + domainCache.set(urlObj.hostname, null); + return null; + } + + const nodeinfo = await fetch(probe.links[probe.links.length - 1].href, { + headers: {"User-Agent": FRIENDLY_USERAGENT}, + }).then((res) => res.json()); + + if(!nodeinfo?.software?.name) { + console.error("[fedimbed]", `Got nodeinfo for "${urlObj.hostname}", but missing software name.`); + domainCache.set(urlObj.hostname, null); + return null; + } + + domainCache.set(urlObj.hostname, nodeinfo.software.name); + return nodeinfo.software.name; +} + +const keyId = "https://bot.possum.city/actor#main-key"; +const privKey = fs.readFileSync("data/private.pem"); +async function signedFetch(url, options) { + const urlObj = new URL(url); + + const headers = { + host: urlObj.host, + date: new Date().toUTCString(), + }; + + const headerNames = ["(request-target)", "host", "date"]; + + httpSignature.sign( + { + getHeader: (name) => headers[name.toLowerCase()], + setHeader: (name, value) => (headers[name] = value), + method: options.method ?? "GET", + path: urlObj.pathname, + }, + { + keyId, + key: privKey, + headers: headerNames, + authorizationHeaderName: "signature", + } + ); + + options.headers = Object.assign(headers, options.headers ?? {}); + + return await fetch(url, options); +} + +async function processUrl(url) { + let spoiler = false; + let invalidUrl = false; + let urlObj; + try { + urlObj = new URL(url); + } catch(err) { + console.error("[fedimbed]", err); + invalidUrl = true; + } + + if(invalidUrl) return {}; + + // some lemmy instances have old reddit frontend subdomains + // but these frontends are just frontends and dont actually expose the API + if(urlObj.hostname.startsWith("old.")) { + urlObj.hostname = urlObj.hostname.replace("old.", ""); + url = urlObj.href; + } + + let platform = (await resolvePlatform(url)) ?? ""; + let color = PLATFORM_COLORS[platform]; + let platformName = platform + .replace("gotosocial", "GoToSocial") + .replace("birdsitelive", '"Twitter" (BirdsiteLive)') + .replace(/^(.)/, (_, c) => c.toUpperCase()) + .replace("Cohost", "cohost"); + + const files = []; + let content, + cw, + author, + timestamp, + title, + poll, + emotes = [], + sensitive = false; + + // Fetch post + let rawPostData; + try { + rawPostData = await signedFetch(url, { + headers: { + "User-Agent": FRIENDLY_USERAGENT, + Accept: 'application/activity+json, application/ld+json; profile="https://www.w3.org/ns/activitystreams"', + }, + }).then((res) => res.text()); + } catch (err) { + console.error("[fedimbed]", `Failed to signed fetch "${url}", retrying unsigned: ${err}`); + } + if(!rawPostData) { + try { + rawPostData = await fetch(url, { + headers: { + "User-Agent": FRIENDLY_USERAGENT, + Accept: 'application/activity+json, application/ld+json; profile="https://www.w3.org/ns/activitystreams"', + }, + }).then((res) => res.text()); + } catch (err) { + console.error("[fedimbed]", `Failed to fetch "${url}": ${err}`); + } + } + + let postData; + if(rawPostData?.startsWith("{")) { + try { + postData = JSON.parse(rawPostData); + } catch (err) { + console.error("[fedimbed]", `Failed to decode JSON for "${url}": ${err}\n "${rawPostData}"`); + } + } else { + console.warn("[fedimbed]", `Got non-JSON for "${url}": ${rawPostData}`); + } + + if(postData?.error) { + console.error("[fedimbed]", `Received error for "${url}": ${postData.error}`); + console.error("[fedimbed]", postData); + return postData; + } + + if(!postData) { + // We failed to get post. + // Assume it was due to AFM or forced HTTP signatures and use MastoAPI + + // Follow redirect from /object since we need the ID from /notice + if(PATH_REGEX.pleroma.test(urlObj.pathname)) { + url = await signedFetch(url, { + method: "HEAD", + headers: { + "User-Agent": FRIENDLY_USERAGENT, + }, + redirect: "manual", + }).then((res) => res.headers.get("location")); + if(url.startsWith("/")) { + url = urlObj.origin + url; + } + urlObj = new URL(url); + } + + let redirUrl; + const options = {}; + const headers = {}; + if(PATH_REGEX.pleroma2.test(urlObj.pathname)) { + redirUrl = url.replace("notice", "api/v1/statuses"); + } else if(PATH_REGEX.mastodon.test(urlObj.pathname)) { + const postId = urlObj.pathname.match(PATH_REGEX.mastodon)?.[2]; + redirUrl = urlObj.origin + "/api/v1/statuses/" + postId; + } else if(PATH_REGEX.mastodon2.test(urlObj.pathname)) { + redirUrl = url.replace(/^\/(.+?)\/statuses/, "/api/v1/statuses"); + } else if(PATH_REGEX.misskey.test(urlObj.pathname)) { + let noteId = url.split("/notes/")[1]; + if(noteId.indexOf("/") > -1) { + noteId = noteId.split("/")[0]; + } else if(noteId.indexOf("?") > -1) { + noteId = noteId.split("?")[0]; + } else if(noteId.indexOf("#") > -1) { + noteId = noteId.split("#")[0]; + } + console.log("[fedimbed]", "Misskey post ID: " + noteId); + redirUrl = urlObj.origin + "/api/notes/show/"; + options.method = "POST"; + options.body = JSON.stringify({noteId}); + headers["Content-Type"] = "application/json"; + } else { + console.error("[fedimbed]", `Missing MastoAPI replacement for "${platform}"`); + } + + if(redirUrl) { + console.log( + "[fedimbed]", + `Redirecting "${url}" to "${redirUrl}": ${JSON.stringify(options)}, ${JSON.stringify(headers)}` + ); + let rawPostData2; + try { + rawPostData2 = await signedFetch( + redirUrl, + Object.assign(options, { + headers: Object.assign(headers, { + "User-Agent": FRIENDLY_USERAGENT, + }), + }) + ).then((res) => res.text()); + } catch (err) { + console.error("[fedimbed]", `Failed to signed fetch "${url}" via MastoAPI, retrying unsigned: ${err}`); + } + if(!rawPostData2) { + try { + rawPostData2 = await signedFetch( + redirUrl, + Object.assign(options, { + headers: Object.assign(headers, { + "User-Agent": FRIENDLY_USERAGENT, + }), + }) + ).then((res) => res.text()); + } catch (err) { + console.error("[fedimbed]", `Failed to fetch "${url}" via MastoAPI: ${err}`); + } + } + + let postData2; + if(rawPostData2?.startsWith("{")) { + postData2 = JSON.parse(rawPostData2); + } else { + console.warn("[fedimbed]", `Got non-JSON for "${url}" via MastoAPI: ${rawPostData2}`); + } + + if(!postData2) { + console.warn("[fedimbed]", `Bailing trying to re-embed "${url}": Failed to get post from normal and MastoAPI.`); + } else if(postData2.error) { + console.error( + "[fedimbed]", + `Bailing trying to re-embed "${url}", MastoAPI gave us error: ${JSON.stringify(postData2.error)}` + ); + } else { + cw = postData2.spoiler_warning ?? postData2.spoiler_text ?? postData2.cw; + content = + postData2.akkoma?.source?.content ?? + postData2.pleroma?.content?.["text/plain"] ?? + postData2.text ?? + postData2.content; + author = { + name: + postData2.account?.display_name ?? + postData2.account?.username ?? + postData2.user?.name ?? + postData2.user?.username, + handle: + postData2.account?.fqn ?? `${postData2.account?.username ?? postData2.user?.username}@${urlObj.hostname}`, + url: postData2.account?.url ?? `${urlObj.origin}/@${postData2.account?.username ?? postData2.user?.username}`, + avatar: postData2.account?.avatar ?? postData2.user?.avatarUrl, + }; + timestamp = postData2.created_at ?? postData2.createdAt; + emotes = postData2.emojis.filter((x) => !x.name.endsWith("@.")).map((x) => ({name: `:${x.name}:`, url: x.url})); + sensitive = postData2.sensitive; + + const attachments = postData2.media_attachments ?? postData2.files; + if(attachments) { + for(const attachment of attachments) { + const contentType = await fetch(attachment.url, { + method: "HEAD", + }).then((res) => res.headers.get("Content-Type")); + + if(contentType) { + if(contentType.startsWith("image/") || contentType.startsWith("video/") || contentType.startsWith("audio/")) { + files.push({ + url: attachment.url, + desc: attachment.description ?? attachment.comment, + type: contentType, + }); + } + } else { + const type = attachment.type?.toLowerCase(); + + const fileType = + attachment.pleroma?.mime_type ?? type.indexOf("/") > -1 + ? type + : type + + "/" + + (url.match(/\.([a-z0-9]{3,4})$/)?.[0] ?? type == "image" + ? "png" + : type == "video" + ? "mp4" + : "mpeg"); + if(type.startsWith("image") || type.startsWith("video") || type.startsWith("audio")) { + files.push({ + url: attachment.url, + desc: attachment.description ?? attachment.comment, + type: fileType, + }); + } + } + } + } + + if(postData2.sensitive && attachments.length > 0) { + spoiler = true; + } + + if(postData2.poll) { + poll = { + end: new Date(postData2.poll.expires_at), + total: postData2.poll.votes_count, + options: postData2.poll.options.map((o) => ({ + name: o.title, + count: o.votes_count, + })), + }; + } + } + } + } else { + if(postData.id) { + const realUrlObj = new URL(postData.id); + if(realUrlObj.origin != urlObj.origin) { + platform = await resolvePlatform(postData.id); + color = PLATFORM_COLORS[platform]; + platformName = platform.replace("gotosocial", "GoToSocial").replace(/^(.)/, (_, c) => c.toUpperCase()); + url = postData.id; + } + } + + content = postData._misskey_content ?? postData.source?.content ?? postData.content; + cw = postData.summary; + timestamp = postData.published; + sensitive = postData.sensitive; + + if(postData.tag) { + let tag = postData.tag; + // gts moment + if(!Array.isArray(tag)) tag = [tag]; + emotes = tag.filter((x) => !!x.icon).map((x) => ({name: x.name, url: x.icon.url})); + } + + // NB: gts doesnt send singular attachments as array + const attachments = Array.isArray(postData.attachment) ? postData.attachment : [postData.attachment]; + for(const attachment of attachments) { + if(!attachment) continue; + if(attachment.mediaType) { + if(attachment.mediaType.startsWith("video/") || attachment.mediaType.startsWith("image/") || attachment.mediaType.startsWith("audio/")) { + files.push({ + url: attachment.url, + desc: attachment.name ?? attachment.description ?? attachment.comment, + type: attachment.mediaType, + }); + } + } else if(attachment.url) { + const contentType = await fetch(attachment.url, { + method: "HEAD", + }).then((res) => res.headers.get("Content-Type")); + + if(contentType) { + if(contentType.startsWith("image/") || contentType.startsWith("video/") || contentType.startsWith("audio/")) { + files.push({ + url: attachment.url, + desc: attachment.name ?? attachment.description ?? attachment.comment, + type: contentType, + }); + } + } else { + const type = attachment.type?.toLowerCase(); + + const fileType = + type.indexOf("/") > -1 + ? type + : type + + "/" + + (url.match(/\.([a-z0-9]{3,4})$/)?.[0] ?? type == "image" ? "png" : type == "video" ? "mp4" : "mpeg"); + if(type.startsWith("image") || type.startsWith("video") || type.startsWith("audio")) { + files.push({ + url: attachment.url, + desc: attachment.name ?? attachment.description ?? attachment.comment, + type: fileType, + }); + } + } + } else { + console.warn("[fedimbed]", `Unhandled attachment structure! ${JSON.stringify(attachment)}`); + } + } + + if(postData.sensitive && attachments.length > 0) { + spoiler = true; + } + + if(postData.image?.url) { + const imageUrl = new URL(postData.image.url); + const contentType = await fetch(postData.image.url, { + method: "HEAD", + }).then((res) => res.headers.get("Content-Type")); + files.push({ + url: postData.image.url, + desc: "", + type: contentType ?? "image/" + imageUrl.pathname.substring(imageUrl.pathname.lastIndexOf(".") + 1), + }); + } + + if(postData.name) title = postData.name; + + // Author data is not sent with the post with AS2 + const authorData = await signedFetch(postData.actor ?? postData.attributedTo, { + headers: { + "User-Agent": FRIENDLY_USERAGENT, + Accept: 'application/activity+json, application/ld+json; profile="https://www.w3.org/ns/activitystreams"', + }, + }) + .then((res) => res.json()) + .catch((err) => { + // only posts can be activity+json right now, reduce log spam + if(platform !== "cohost") console.error("[fedimbed]", `Failed to get author for "${url}": ${err}`); + }); + + if(authorData) { + const authorUrlObj = new URL(authorData.url ?? authorData.id); + author = { + name: authorData.name, + handle: `${authorData.preferredUsername}@${authorUrlObj.hostname}`, + url: authorData.url, + avatar: authorData.icon?.url, + }; + } else { + // bootleg author, mainly for cohost + const authorUrl = postData.actor ?? postData.attributedTo; + const authorUrlObj = new URL(authorUrl); + const name = authorUrlObj.pathname.substring(authorUrlObj.pathname.lastIndexOf("/") + 1); + author = { + name, + handle: `${name}@${authorUrlObj.hostname}`, + url: authorUrl, + }; + } + + if(postData.endTime && postData.oneOf && postData.votersCount) { + poll = { + end: new Date(postData.endTime), + total: postData.votersCount, + options: postData.oneOf.map((o) => ({ + name: o.name, + count: o.replies.totalItems, + })), + }; + } + } + + // We could just continue without author but it'd look ugly and be confusing. + if(!author) { + console.warn("[fedimbed]", `Bailing trying to re-embed "${url}": Failed to get author.`); + return {}; + } + + // Start constructing embed + content = content ?? ""; + cw = cw ?? ""; + + //content = htmlToMarkdown(content); + for(const emote of emotes) { + //content = content.replaceAll(emote.name, `[${emote.name}](${emote.url})`); + content = content.replaceAll(emote.name, `${emote.name}`); + } + + //cw = htmlToMarkdown(cw); + + let desc = cw; + if((cw != "" || sensitive) && files.length) { + desc += "" + content + ""; + } else { + desc = content; + } + + const user = author.name ? `${author.name} (${author.handle})` : author.handle; + + const baseEmbed = { + color, + url, + timestamp, + description: desc, + title: title ?? user, + author: title + ? { + name: user, + url: author.url, + } + : null, + footer: { + text: platformName, + }, + thumbnail: { + url: author.avatar, + }, + fields: [], + }; + + if(poll) { + baseEmbed.fields.push({ + name: "Poll", + value: + poll.options + .map((o) => { + const percent = o.count / poll.total; + const bar = Math.round(percent * 30); + + return `**${o.name}** (${o.count}, ${Math.round(percent * 100)}%)\n\`[${"=".repeat(bar)}${" ".repeat( + 30 - bar + )}]\``; + }) + .join("\n\n") + `\n\n${poll.total} votes \u2022 Ends `, + }); + } + + const embeds = [baseEmbed]; + + return { + content: + cw != "" && (files.length > 0) + ? `:warning: ${cw} ${url}` + : spoiler + ? `${url}` + : "", + embeds, + files + }; +} + +async function fedimbed(msg) { + if(URLS_REGEX.test(msg)) { + const urls = msg.match(URLS_REGEX); + for(let url of urls) { + let urlObj; + try { + urlObj = new URL(url); + } catch { + console.error("[fedimbed]", `Invalid URL "${url}"`); + // noop + } + for(const service of Object.keys(PATH_REGEX)) { + const regex = PATH_REGEX[service]; + if(urlObj && regex.test(urlObj.pathname)) { + console.log("[fedimbed]", `Hit "${service}" for "${url}", processing now.`); + try { + const response = await processUrl(url); + return response; + } catch (err) { + console.error("[fedimbed]", `Error processing "${url}":\n` + err.stack); + } + break; + } + } + } + } +} + +export default fedimbed; diff --git a/modules/fedimbed.js b/modules/fedimbed.js new file mode 100644 index 0000000..8324eab --- /dev/null +++ b/modules/fedimbed.js @@ -0,0 +1,92 @@ +import fedimbed from "../lib/fedimbed.js"; +import { encode } from "html-entities"; +import { JSDOM } from "jsdom"; +import util from "util"; + +async function onMessage(client, event) { + const embed = await fedimbed(event.event.content.body); + if(!embed) return; + const dom = new JSDOM(""); + const document = dom.window.document; + var quote = document.createElement("blockquote"); + if(!embed.embeds) { + var c = util.format(embed); + client.reply(event, c, `
${encode(c)}
`); + return; + } + for(const emb of embed.embeds) { + var link = document.createElement("a"); + link.href = emb.url; + + if(emb.thumbnail && emb.thumbnail.url) { + var avatar = document.createElement("img"); + var matrixUrl = client.cache.get("fedimbed_" + emb.thumbnail.url); + if(!matrixUrl) { + const buffer = await fetch(emb.thumbnail.url, { + headers: { + "User-Agent": "PossumBot/1.0 (+https://bot.possum.city/)", + }, + }).then((res) => res.arrayBuffer()).then((buf) => Buffer.from(buf)); + const uploadResponse = await client.uploadContent(buffer, { rawResponse: false }); + matrixUrl = uploadResponse.content_uri; + client.cache.set("fedimbed_" + emb.thumbnail.url, matrixUrl); + } + avatar.src = matrixUrl; + avatar.height = "16"; + link.appendChild(avatar); + } + + var linkText = document.createElement("span"); + linkText.innerHTML = ((emb.thumbnail?.url) ? " " : "") + emb.title; + link.appendChild(linkText); + + quote.appendChild(link); + + var text = document.createElement("p"); + text.innerHTML = emb.description; + quote.appendChild(text); + } + for(const file of embed.files) { + var matrixUrl = client.cache.get("fedimbed_" + file.url); + if(!matrixUrl) { + const buffer = await fetch(file.url, { + headers: { + "User-Agent": "PossumBot/1.0 (+https://bot.possum.city/)", + }, + }).then((res) => res.arrayBuffer()).then((buf) => Buffer.from(buf)); + const uploadResponse = await client.uploadContent(buffer, { rawResponse: false }); + matrixUrl = uploadResponse.content_uri; + client.cache.set("fedimbed_" + file.url, matrixUrl); + } + var media; + switch(file.type.split("/")[0]) { + case "audio": + media = document.createElement("audio"); + break; + case "video": + media = document.createElement("video"); + break; + case "image": + media = document.createElement("img"); + break; + } + media.src = matrixUrl; + media.alt = file.desc; + quote.appendChild(media); + } + document.body.appendChild(quote); + + let x = document.createElement("small"); + x.innerHTML = "Powered by HF Fedimbed"; + document.body.appendChild(x); + + client.reply(event, "This message uses HTML, which your client does not support.", document.body.outerHTML); +} + +export default { + name: "fedimbed", + desc: "embed fediverse post contents", + hooks: { + message: onMessage + } +} diff --git a/modules/meow.js b/modules/meow.js new file mode 100644 index 0000000..f33c40f --- /dev/null +++ b/modules/meow.js @@ -0,0 +1,45 @@ +var meows = [ + "mreow", + "miau", + "mewo", + "maow", + "mrow", + "mrao", + "meow", + "mew", + "nya", +]; + +var emoticons = [ + ":3", + "^w^", + "=^w^=", + "-w-", + ":333" +]; + +async function onMessage(client, event) { + if(event.event.content["m.new_content"] != null) return; + + for(const meow of meows) { + if(event.event.content.body.toLowerCase().includes(meow)) { + var reply = meows.random(); + reply += "!".repeat(Math.random()*5) + + if(Math.random() > 0.5) { + reply += " " + emoticons.random(); + } + + client.reply(event, reply); + break; + } + } +} + +export default { + name: "meow", + desc: ":33", + hooks: { + message: onMessage + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..cac5e9a --- /dev/null +++ b/package.json @@ -0,0 +1,20 @@ +{ + "name": "possumbot", + "type": "module", + "version": "0.0.0", + "description": "General purpose Matrix bot", + "main": "index.js", + "author": "Ashley Graves", + "license": "GPL-v3", + "dependencies": { + "@matrix-org/olm": "^3.2.15", + "@peertube/http-signature": "^1.7.0", + "cli-color": "^2.0.4", + "dotenv": "^16.4.5", + "html-entities": "^2.5.2", + "jsdom": "^24.1.1", + "matrix-js-sdk": "^32.0.0", + "node-fetch": "^3.3.2", + "sync-rpc": "^1.3.6" + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..884a665 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,843 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@matrix-org/olm': + specifier: ^3.2.15 + version: 3.2.15 + '@peertube/http-signature': + specifier: ^1.7.0 + version: 1.7.0 + cli-color: + specifier: ^2.0.4 + version: 2.0.4 + dotenv: + specifier: ^16.4.5 + version: 16.4.5 + html-entities: + specifier: ^2.5.2 + version: 2.5.2 + jsdom: + specifier: ^24.1.1 + version: 24.1.1 + matrix-js-sdk: + specifier: ^32.0.0 + version: 32.4.0 + node-fetch: + specifier: ^3.3.2 + version: 3.3.2 + sync-rpc: + specifier: ^1.3.6 + version: 1.3.6 + +packages: + + '@babel/runtime@7.25.0': + resolution: {integrity: sha512-7dRy4DwXwtzBrPbZflqxnvfxLF8kdZXPkhymtDeFoFqE6ldzjQFgYTtYIFARcLEYDrqfBfYcZt1WqFxRoyC9Rw==} + engines: {node: '>=6.9.0'} + + '@matrix-org/matrix-sdk-crypto-wasm@4.10.0': + resolution: {integrity: sha512-zOqKVAYPfzs6Hav/Km9F5xWwoQ0bxDuoUU0/121m03Fg2VnfcHk43TjKImZolFc7IlgXwVGoda9Pp9Z/eTVKJA==} + engines: {node: '>= 10'} + + '@matrix-org/olm@3.2.15': + resolution: {integrity: sha512-S7lOrndAK9/8qOtaTq/WhttJC/o4GAzdfK0MUPpo8ApzsJEC0QjtwrkC3KBXdFP1cD1MXi/mlKR7aaoVMKgs6Q==} + + '@peertube/http-signature@1.7.0': + resolution: {integrity: sha512-aGQIwo6/sWtyyqhVK4e1MtxYz4N1X8CNt6SOtCc+Wnczs5S5ONaLHDDR8LYaGn0MgOwvGgXyuZ5sJIfd7iyoUw==} + engines: {node: '>=0.10'} + + '@types/events@3.0.3': + resolution: {integrity: sha512-trOc4AAUThEz9hapPtSd7wf5tiQKvTtu5b371UxXdTuqzIh0ArcRspRP0i0Viu+LXstIQ1z96t1nsPxT9ol01g==} + + '@types/retry@0.12.0': + resolution: {integrity: sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==} + + agent-base@7.1.1: + resolution: {integrity: sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==} + engines: {node: '>= 14'} + + another-json@0.2.0: + resolution: {integrity: sha512-/Ndrl68UQLhnCdsAzEXLMFuOR546o2qbYRqCglaNHbjXrwG1ayTcdwr3zkSGOGtGXDyR5X9nCFfnyG2AFJIsqg==} + + asn1@0.2.6: + resolution: {integrity: sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==} + + assert-plus@1.0.0: + resolution: {integrity: sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==} + engines: {node: '>=0.8'} + + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + + base-x@4.0.0: + resolution: {integrity: sha512-FuwxlW4H5kh37X/oW59pwTzzTKRzfrrQwhmyspRM7swOEZcHtDZSCt45U6oKgtuFE+WYPblePMVIPR4RZrh/hw==} + + bcrypt-pbkdf@1.0.2: + resolution: {integrity: sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==} + + bs58@5.0.0: + resolution: {integrity: sha512-r+ihvQJvahgYT50JD05dyJNKlmmSlMoOGwn1lCcEzanPglg7TxYjioQUYehQ9mAR/+hOSd2jRc/Z2y5UxBymvQ==} + + cli-color@2.0.4: + resolution: {integrity: sha512-zlnpg0jNcibNrO7GG9IeHH7maWFeCz+Ja1wx/7tZNU5ASSSSZ+/qZciM0/LHCYxSdqv5h2sdbQ/PXYdOuetXvA==} + engines: {node: '>=0.10'} + + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + + content-type@1.0.5: + resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} + engines: {node: '>= 0.6'} + + core-util-is@1.0.2: + resolution: {integrity: sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==} + + cssstyle@4.0.1: + resolution: {integrity: sha512-8ZYiJ3A/3OkDd093CBT/0UKDWry7ak4BdPTFP2+QEP7cmhouyq/Up709ASSj2cK02BbZiMgk7kYjZNS4QP5qrQ==} + engines: {node: '>=18'} + + d@1.0.2: + resolution: {integrity: sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==} + engines: {node: '>=0.12'} + + dashdash@1.14.1: + resolution: {integrity: sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==} + engines: {node: '>=0.10'} + + data-uri-to-buffer@4.0.1: + resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==} + engines: {node: '>= 12'} + + data-urls@5.0.0: + resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==} + engines: {node: '>=18'} + + debug@4.3.6: + resolution: {integrity: sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + decimal.js@10.4.3: + resolution: {integrity: sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==} + + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + + dotenv@16.4.5: + resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==} + engines: {node: '>=12'} + + ecc-jsbn@0.1.2: + resolution: {integrity: sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==} + + entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + + es5-ext@0.10.64: + resolution: {integrity: sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==} + engines: {node: '>=0.10'} + + es6-iterator@2.0.3: + resolution: {integrity: sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==} + + es6-symbol@3.1.4: + resolution: {integrity: sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==} + engines: {node: '>=0.12'} + + es6-weak-map@2.0.3: + resolution: {integrity: sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==} + + esniff@2.0.1: + resolution: {integrity: sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==} + engines: {node: '>=0.10'} + + event-emitter@0.3.5: + resolution: {integrity: sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==} + + events@3.3.0: + resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} + engines: {node: '>=0.8.x'} + + ext@1.7.0: + resolution: {integrity: sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==} + + extsprintf@1.3.0: + resolution: {integrity: sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==} + engines: {'0': node >=0.6.0} + + fetch-blob@3.2.0: + resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} + engines: {node: ^12.20 || >= 14.13} + + form-data@4.0.0: + resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==} + engines: {node: '>= 6'} + + formdata-polyfill@4.0.10: + resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==} + engines: {node: '>=12.20.0'} + + get-port@3.2.0: + resolution: {integrity: sha512-x5UJKlgeUiNT8nyo/AcnwLnZuZNcSjSw0kogRB+Whd1fjjFq4B1hySFxSFWWSn4mIBzg3sRNUDFYc4g5gjPoLg==} + engines: {node: '>=4'} + + getpass@0.1.7: + resolution: {integrity: sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==} + + html-encoding-sniffer@4.0.0: + resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==} + engines: {node: '>=18'} + + html-entities@2.5.2: + resolution: {integrity: sha512-K//PSRMQk4FZ78Kyau+mZurHn3FH0Vwr+H36eE0rPbeYkRRi9YxceYPhuN60UwWorxyKHhqoAJl2OFKa4BVtaA==} + + http-proxy-agent@7.0.2: + resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} + engines: {node: '>= 14'} + + https-proxy-agent@7.0.5: + resolution: {integrity: sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==} + engines: {node: '>= 14'} + + iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + + is-potential-custom-element-name@1.0.1: + resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} + + is-promise@2.2.2: + resolution: {integrity: sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==} + + jsbn@0.1.1: + resolution: {integrity: sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==} + + jsdom@24.1.1: + resolution: {integrity: sha512-5O1wWV99Jhq4DV7rCLIoZ/UIhyQeDR7wHVyZAHAshbrvZsLs+Xzz7gtwnlJTJDjleiTKh54F4dXrX70vJQTyJQ==} + engines: {node: '>=18'} + peerDependencies: + canvas: ^2.11.2 + peerDependenciesMeta: + canvas: + optional: true + + json-schema@0.4.0: + resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==} + + jsprim@1.4.2: + resolution: {integrity: sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==} + engines: {node: '>=0.6.0'} + + jwt-decode@4.0.0: + resolution: {integrity: sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==} + engines: {node: '>=18'} + + loglevel@1.9.1: + resolution: {integrity: sha512-hP3I3kCrDIMuRwAwHltphhDM1r8i55H33GgqjXbrisuJhF4kRhW1dNuxsRklp4bXl8DSdLaNLuiL4A/LWRfxvg==} + engines: {node: '>= 0.6.0'} + + lru-queue@0.1.0: + resolution: {integrity: sha512-BpdYkt9EvGl8OfWHDQPISVpcl5xZthb+XPsbELj5AQXxIC8IriDZIQYjBJPEm5rS420sjZ0TLEzRcq5KdBhYrQ==} + + matrix-events-sdk@0.0.1: + resolution: {integrity: sha512-1QEOsXO+bhyCroIe2/A5OwaxHvBm7EsSQ46DEDn8RBIfQwN5HWBpFvyWWR4QY0KHPPnnJdI99wgRiAl7Ad5qaA==} + + matrix-js-sdk@32.4.0: + resolution: {integrity: sha512-mzWfF4rJaTFLDfkedqP2jh/i1v5pv6xRHPkAQLn1ytXi72TFKLlKQmjaNUXfQYkmriIYnGYYQwBXQeJgwaT8SQ==} + engines: {node: '>=18.0.0'} + + matrix-widget-api@1.8.2: + resolution: {integrity: sha512-kdmks3CvFNPIYN669Y4rO13KrazDvX8KHC7i6jOzJs8uZ8s54FNkuRVVyiQHeVCSZG5ixUqW9UuCj9lf03qxTQ==} + + memoizee@0.4.17: + resolution: {integrity: sha512-DGqD7Hjpi/1or4F/aYAspXKNm5Yili0QDAFAY4QYvpqpgiY6+1jOfqpmByzjxbWd/T9mChbCArXAbDAsTm5oXA==} + engines: {node: '>=0.12'} + + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + + ms@2.1.2: + resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + + next-tick@1.1.0: + resolution: {integrity: sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==} + + node-domexception@1.0.0: + resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} + engines: {node: '>=10.5.0'} + + node-fetch@3.3.2: + resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + nwsapi@2.2.12: + resolution: {integrity: sha512-qXDmcVlZV4XRtKFzddidpfVP4oMSGhga+xdMc25mv8kaLUHtgzCDhUxkrN8exkGdTlLNaXj7CV3GtON7zuGZ+w==} + + oidc-client-ts@3.0.1: + resolution: {integrity: sha512-xX8unZNtmtw3sOz4FPSqDhkLFnxCDsdo2qhFEH2opgWnF/iXMFoYdBQzkwCxAZVgt3FT3DnuBY3k80EZHT0RYg==} + engines: {node: '>=18'} + + p-retry@4.6.2: + resolution: {integrity: sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==} + engines: {node: '>=8'} + + parse5@7.1.2: + resolution: {integrity: sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==} + + psl@1.9.0: + resolution: {integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + querystringify@2.2.0: + resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==} + + regenerator-runtime@0.14.1: + resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} + + requires-port@1.0.0: + resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} + + retry@0.13.1: + resolution: {integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==} + engines: {node: '>= 4'} + + rrweb-cssom@0.6.0: + resolution: {integrity: sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw==} + + rrweb-cssom@0.7.1: + resolution: {integrity: sha512-TrEMa7JGdVm0UThDJSx7ddw5nVm3UJS9o9CCIZ72B1vSyEZoziDqBYP3XIoi/12lKrJR8rE3jeFHMok2F/Mnsg==} + + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + saxes@6.0.0: + resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} + engines: {node: '>=v12.22.7'} + + sdp-transform@2.14.2: + resolution: {integrity: sha512-icY6jVao7MfKCieyo1AyxFYm1baiM+fA00qW/KrNNVlkxHAd34riEKuEkUe4bBb3gJwLJZM+xT60Yj1QL8rHiA==} + hasBin: true + + sshpk@1.18.0: + resolution: {integrity: sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==} + engines: {node: '>=0.10.0'} + hasBin: true + + symbol-tree@3.2.4: + resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} + + sync-rpc@1.3.6: + resolution: {integrity: sha512-J8jTXuZzRlvU7HemDgHi3pGnh/rkoqR/OZSjhTyyZrEkkYQbk7Z33AXp37mkPfPpfdOuj7Ex3H/TJM1z48uPQw==} + + timers-ext@0.1.8: + resolution: {integrity: sha512-wFH7+SEAcKfJpfLPkrgMPvvwnEtj8W4IurvEyrKsDleXnKLCDw71w8jltvfLa8Rm4qQxxT4jmDBYbJG/z7qoww==} + engines: {node: '>=0.12'} + + tough-cookie@4.1.4: + resolution: {integrity: sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==} + engines: {node: '>=6'} + + tr46@5.0.0: + resolution: {integrity: sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==} + engines: {node: '>=18'} + + tweetnacl@0.14.5: + resolution: {integrity: sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==} + + type@2.7.3: + resolution: {integrity: sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==} + + unhomoglyph@1.0.6: + resolution: {integrity: sha512-7uvcWI3hWshSADBu4JpnyYbTVc7YlhF5GDW/oPD5AxIxl34k4wXR3WDkPnzLxkN32LiTCTKMQLtKVZiwki3zGg==} + + universalify@0.2.0: + resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==} + engines: {node: '>= 4.0.0'} + + url-parse@1.5.10: + resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==} + + uuid@9.0.1: + resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} + hasBin: true + + verror@1.10.0: + resolution: {integrity: sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==} + engines: {'0': node >=0.6.0} + + w3c-xmlserializer@5.0.0: + resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} + engines: {node: '>=18'} + + web-streams-polyfill@3.3.3: + resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} + engines: {node: '>= 8'} + + webidl-conversions@7.0.0: + resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} + engines: {node: '>=12'} + + whatwg-encoding@3.1.1: + resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} + engines: {node: '>=18'} + + whatwg-mimetype@4.0.0: + resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==} + engines: {node: '>=18'} + + whatwg-url@14.0.0: + resolution: {integrity: sha512-1lfMEm2IEr7RIV+f4lUNPOqfFL+pO+Xw3fJSqmjX9AbXcXcYOkCe1P6+9VBZB6n94af16NfZf+sSk0JCBZC9aw==} + engines: {node: '>=18'} + + ws@8.18.0: + resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + xml-name-validator@5.0.0: + resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==} + engines: {node: '>=18'} + + xmlchars@2.2.0: + resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} + +snapshots: + + '@babel/runtime@7.25.0': + dependencies: + regenerator-runtime: 0.14.1 + + '@matrix-org/matrix-sdk-crypto-wasm@4.10.0': {} + + '@matrix-org/olm@3.2.15': {} + + '@peertube/http-signature@1.7.0': + dependencies: + assert-plus: 1.0.0 + jsprim: 1.4.2 + sshpk: 1.18.0 + + '@types/events@3.0.3': {} + + '@types/retry@0.12.0': {} + + agent-base@7.1.1: + dependencies: + debug: 4.3.6 + transitivePeerDependencies: + - supports-color + + another-json@0.2.0: {} + + asn1@0.2.6: + dependencies: + safer-buffer: 2.1.2 + + assert-plus@1.0.0: {} + + asynckit@0.4.0: {} + + base-x@4.0.0: {} + + bcrypt-pbkdf@1.0.2: + dependencies: + tweetnacl: 0.14.5 + + bs58@5.0.0: + dependencies: + base-x: 4.0.0 + + cli-color@2.0.4: + dependencies: + d: 1.0.2 + es5-ext: 0.10.64 + es6-iterator: 2.0.3 + memoizee: 0.4.17 + timers-ext: 0.1.8 + + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + + content-type@1.0.5: {} + + core-util-is@1.0.2: {} + + cssstyle@4.0.1: + dependencies: + rrweb-cssom: 0.6.0 + + d@1.0.2: + dependencies: + es5-ext: 0.10.64 + type: 2.7.3 + + dashdash@1.14.1: + dependencies: + assert-plus: 1.0.0 + + data-uri-to-buffer@4.0.1: {} + + data-urls@5.0.0: + dependencies: + whatwg-mimetype: 4.0.0 + whatwg-url: 14.0.0 + + debug@4.3.6: + dependencies: + ms: 2.1.2 + + decimal.js@10.4.3: {} + + delayed-stream@1.0.0: {} + + dotenv@16.4.5: {} + + ecc-jsbn@0.1.2: + dependencies: + jsbn: 0.1.1 + safer-buffer: 2.1.2 + + entities@4.5.0: {} + + es5-ext@0.10.64: + dependencies: + es6-iterator: 2.0.3 + es6-symbol: 3.1.4 + esniff: 2.0.1 + next-tick: 1.1.0 + + es6-iterator@2.0.3: + dependencies: + d: 1.0.2 + es5-ext: 0.10.64 + es6-symbol: 3.1.4 + + es6-symbol@3.1.4: + dependencies: + d: 1.0.2 + ext: 1.7.0 + + es6-weak-map@2.0.3: + dependencies: + d: 1.0.2 + es5-ext: 0.10.64 + es6-iterator: 2.0.3 + es6-symbol: 3.1.4 + + esniff@2.0.1: + dependencies: + d: 1.0.2 + es5-ext: 0.10.64 + event-emitter: 0.3.5 + type: 2.7.3 + + event-emitter@0.3.5: + dependencies: + d: 1.0.2 + es5-ext: 0.10.64 + + events@3.3.0: {} + + ext@1.7.0: + dependencies: + type: 2.7.3 + + extsprintf@1.3.0: {} + + fetch-blob@3.2.0: + dependencies: + node-domexception: 1.0.0 + web-streams-polyfill: 3.3.3 + + form-data@4.0.0: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + mime-types: 2.1.35 + + formdata-polyfill@4.0.10: + dependencies: + fetch-blob: 3.2.0 + + get-port@3.2.0: {} + + getpass@0.1.7: + dependencies: + assert-plus: 1.0.0 + + html-encoding-sniffer@4.0.0: + dependencies: + whatwg-encoding: 3.1.1 + + html-entities@2.5.2: {} + + http-proxy-agent@7.0.2: + dependencies: + agent-base: 7.1.1 + debug: 4.3.6 + transitivePeerDependencies: + - supports-color + + https-proxy-agent@7.0.5: + dependencies: + agent-base: 7.1.1 + debug: 4.3.6 + transitivePeerDependencies: + - supports-color + + iconv-lite@0.6.3: + dependencies: + safer-buffer: 2.1.2 + + is-potential-custom-element-name@1.0.1: {} + + is-promise@2.2.2: {} + + jsbn@0.1.1: {} + + jsdom@24.1.1: + dependencies: + cssstyle: 4.0.1 + data-urls: 5.0.0 + decimal.js: 10.4.3 + form-data: 4.0.0 + html-encoding-sniffer: 4.0.0 + http-proxy-agent: 7.0.2 + https-proxy-agent: 7.0.5 + is-potential-custom-element-name: 1.0.1 + nwsapi: 2.2.12 + parse5: 7.1.2 + rrweb-cssom: 0.7.1 + saxes: 6.0.0 + symbol-tree: 3.2.4 + tough-cookie: 4.1.4 + w3c-xmlserializer: 5.0.0 + webidl-conversions: 7.0.0 + whatwg-encoding: 3.1.1 + whatwg-mimetype: 4.0.0 + whatwg-url: 14.0.0 + ws: 8.18.0 + xml-name-validator: 5.0.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + json-schema@0.4.0: {} + + jsprim@1.4.2: + dependencies: + assert-plus: 1.0.0 + extsprintf: 1.3.0 + json-schema: 0.4.0 + verror: 1.10.0 + + jwt-decode@4.0.0: {} + + loglevel@1.9.1: {} + + lru-queue@0.1.0: + dependencies: + es5-ext: 0.10.64 + + matrix-events-sdk@0.0.1: {} + + matrix-js-sdk@32.4.0: + dependencies: + '@babel/runtime': 7.25.0 + '@matrix-org/matrix-sdk-crypto-wasm': 4.10.0 + another-json: 0.2.0 + bs58: 5.0.0 + content-type: 1.0.5 + jwt-decode: 4.0.0 + loglevel: 1.9.1 + matrix-events-sdk: 0.0.1 + matrix-widget-api: 1.8.2 + oidc-client-ts: 3.0.1 + p-retry: 4.6.2 + sdp-transform: 2.14.2 + unhomoglyph: 1.0.6 + uuid: 9.0.1 + + matrix-widget-api@1.8.2: + dependencies: + '@types/events': 3.0.3 + events: 3.3.0 + + memoizee@0.4.17: + dependencies: + d: 1.0.2 + es5-ext: 0.10.64 + es6-weak-map: 2.0.3 + event-emitter: 0.3.5 + is-promise: 2.2.2 + lru-queue: 0.1.0 + next-tick: 1.1.0 + timers-ext: 0.1.8 + + mime-db@1.52.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + + ms@2.1.2: {} + + next-tick@1.1.0: {} + + node-domexception@1.0.0: {} + + node-fetch@3.3.2: + dependencies: + data-uri-to-buffer: 4.0.1 + fetch-blob: 3.2.0 + formdata-polyfill: 4.0.10 + + nwsapi@2.2.12: {} + + oidc-client-ts@3.0.1: + dependencies: + jwt-decode: 4.0.0 + + p-retry@4.6.2: + dependencies: + '@types/retry': 0.12.0 + retry: 0.13.1 + + parse5@7.1.2: + dependencies: + entities: 4.5.0 + + psl@1.9.0: {} + + punycode@2.3.1: {} + + querystringify@2.2.0: {} + + regenerator-runtime@0.14.1: {} + + requires-port@1.0.0: {} + + retry@0.13.1: {} + + rrweb-cssom@0.6.0: {} + + rrweb-cssom@0.7.1: {} + + safer-buffer@2.1.2: {} + + saxes@6.0.0: + dependencies: + xmlchars: 2.2.0 + + sdp-transform@2.14.2: {} + + sshpk@1.18.0: + dependencies: + asn1: 0.2.6 + assert-plus: 1.0.0 + bcrypt-pbkdf: 1.0.2 + dashdash: 1.14.1 + ecc-jsbn: 0.1.2 + getpass: 0.1.7 + jsbn: 0.1.1 + safer-buffer: 2.1.2 + tweetnacl: 0.14.5 + + symbol-tree@3.2.4: {} + + sync-rpc@1.3.6: + dependencies: + get-port: 3.2.0 + + timers-ext@0.1.8: + dependencies: + es5-ext: 0.10.64 + next-tick: 1.1.0 + + tough-cookie@4.1.4: + dependencies: + psl: 1.9.0 + punycode: 2.3.1 + universalify: 0.2.0 + url-parse: 1.5.10 + + tr46@5.0.0: + dependencies: + punycode: 2.3.1 + + tweetnacl@0.14.5: {} + + type@2.7.3: {} + + unhomoglyph@1.0.6: {} + + universalify@0.2.0: {} + + url-parse@1.5.10: + dependencies: + querystringify: 2.2.0 + requires-port: 1.0.0 + + uuid@9.0.1: {} + + verror@1.10.0: + dependencies: + assert-plus: 1.0.0 + core-util-is: 1.0.2 + extsprintf: 1.3.0 + + w3c-xmlserializer@5.0.0: + dependencies: + xml-name-validator: 5.0.0 + + web-streams-polyfill@3.3.3: {} + + webidl-conversions@7.0.0: {} + + whatwg-encoding@3.1.1: + dependencies: + iconv-lite: 0.6.3 + + whatwg-mimetype@4.0.0: {} + + whatwg-url@14.0.0: + dependencies: + tr46: 5.0.0 + webidl-conversions: 7.0.0 + + ws@8.18.0: {} + + xml-name-validator@5.0.0: {} + + xmlchars@2.2.0: {}