From 0941df15831c64d5321bbbf381e09e4340b44268 Mon Sep 17 00:00:00 2001 From: Madeline <46743919+MaddyUnderStars@users.noreply.github.com> Date: Wed, 16 Aug 2023 10:13:22 +0000 Subject: [PATCH] a ton of broken shit and approx 1 nice function --- .vscode/launch.json | 3 + assets/schemas.json | Bin 18458145 -> 12483286 bytes package-lock.json | Bin 293730 -> 294022 bytes package.json | 1 + src/activitypub/Server.ts | 2 + src/activitypub/listener/index.ts | 136 ++++++++++++ .../routes/channel/#channel_id/inbox.ts | 6 +- src/activitypub/routes/user/#user_id/inbox.ts | 33 +++ .../routes/user/{ => #user_id}/index.ts | 4 +- src/activitypub/routes/user/inbox.ts | 12 -- src/activitypub/util/fetch.ts | 8 + src/activitypub/util/index.ts | 1 + src/activitypub/util/transforms/Message.ts | 141 ------------- src/activitypub/util/transforms/index.ts | 197 +++++++++++++++++- src/bundle/index.ts | 5 +- src/util/entities/Channel.ts | 14 ++ src/util/entities/Message.ts | 19 +- src/util/entities/User.ts | 3 + src/util/schemas/MessageAcknowledgeSchema.ts | 3 + src/util/util/Event.ts | 4 +- 20 files changed, 430 insertions(+), 162 deletions(-) create mode 100644 src/activitypub/listener/index.ts create mode 100644 src/activitypub/routes/user/#user_id/inbox.ts rename src/activitypub/routes/user/{ => #user_id}/index.ts (73%) delete mode 100644 src/activitypub/routes/user/inbox.ts create mode 100644 src/activitypub/util/fetch.ts delete mode 100644 src/activitypub/util/transforms/Message.ts diff --git a/.vscode/launch.json b/.vscode/launch.json index 611fefa04..4aa8ca34b 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -15,6 +15,9 @@ "skipFiles": ["/**"], "program": "${workspaceFolder}/src/bundle/start.ts", "outFiles": ["${workspaceFolder}/dist/**/*.js"], + "env": { + "CONFIG_PATH": "./config.json" + }, "preLaunchTask": "tsc: build - tsconfig.json" } ] diff --git a/assets/schemas.json b/assets/schemas.json index ec0b51e9dec3dd70351ce5b75330f0326578e6ef..647e693b25f861737abb08f63c42aab14418b562 100644 GIT binary patch delta 31868 zcmeI5eN2;A6vp%N(ehSki+~`YR?sjZU;|7Mr?N;y!3hi*bIwqd;#U-zonJUt6-O3= z5}DjB(|~d6Br43!-r>hy-7>A;Iuy5Te#}i>bWx^5Woq2*TVq^e*3 z?m6e4d*6-6uLX2;b^EubEaG`fzS(1D_O3t;JCo)c!OSJ*M3&)7*`;`>Tj z#nE&VYf3Fx&Q0rV2c>{#fB1E{UnI*g8iRP#qTDQruf|=>v-u)b>1CGd?=leV?0Iui% zAODAI$oqZ+CIS};*HM{Y+O8>sx@czXwm?@TU7eEoN?ngm=!&K*f$Q^q#QE@fcqWyVQL_}O6 z9O>DSs=`!cDOr)(P#INa4qLQi5(GvUSP!1LweR3JfXp*8en1~9dJ^|LO!^t%MdRgL z3{sv+x$}uJSp#Rmh}1~WQx?YMquE8p#ZJy`*084b1Pu?_Jjf48m{;Jq0QV=dFfucP zlyt4$9B5!`qGE?J z7Nq23!5}UeOi+V6Rivs~^}orH>L-Jj76q_* zSAD`=zwz=}$-lo1SrTQ*JDO6_7a{I(iZ)W&i6OVJv``Jq&;OIwAGpZ4D&vNS0T%%m zH~Neb)qUv15tmVfk$JJfU%VQ~tExuDL1biTTtMpyUPNBUW$wH%x)!BAkyjD6JPD-S zEw>wKW7C0)jB9Iob`QiYGOm}`j|VP#A@rSd!rs?WW>d>-sVgrJW+a>OZ2y9EdJaNV-31E zWL%R{XSXAE(Qt_!p;pVB8r0bYTs)(-;IImk`7VyhH*fa1~c9*$G@UTw;Xj$kAo#z(vOO!F_uM zTG3=&;|8uAi6$W#m#%OX$I?i~mASk+2C<8ZOFfc$YsZJHVMwo#`CY8}3a0c5S%z+P zEUF`~klh?rw+*q(D`e4g)?%BRJfowAf6FHy0hxEmKKs65J&fs%G1I#+OimT)C3jG0?D4rO&MZCPx>J-H9NJ-1gByKKwt zRc{C`s-Js4yz#cU%SX#v$N9)S@lo0_+~Y%q%mw8A&?Fg`xuvZEWfTDyzkJ_0-iq4E zbM53v`O!7$IpkP4d`J+MimTo(>v-YR69ypj3fZ07Q(7P+Aak7&6t28^_+C#;OA&dA zlU!9@5A0YTiM-lnzBcAzS_u|O&x_>KGrRdy9{&J867?ng*j)1jU4P1Ux%>QxlGG+i zdIT_vz{G6-F=d;CTq*oIa+4f zrU`upw;lSV>Qk})ZIu2({G;+xUoxRwGKEOErG}F6s?!Hb^lM>~10th}OetI(E=@oX MqwzW@`{>^L7uSL}Q2+n{ delta 431614 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 { + const digest = crypto + .createHash("sha256") + .update(JSON.stringify(message)) + .digest("base64"); + const signer = crypto.createSign("sha256"); + const now = new Date(); + + const url = new URL(inbox); + const inboxFrag = url.pathname; + const toSign = + `(request-target): post ${inboxFrag}\n` + + `host: ${url.hostname}\n` + + `date: ${now.toUTCString()}\n` + + `digest: SHA-256=${digest}`; + + signer.update(toSign); + signer.end(); + + const signature = signer.sign(privateKey); + const sig_b64 = signature.toString("base64"); + + const { webDomain } = Config.get().federation; + const header = + `keyId="https://${webDomain}/fed/${sender}",` + + `headers="(request-target) host date digest",` + + `signature=${sig_b64}`; + + return await fetch( + inbox, + OrmUtils.mergeDeep(fetchOpts, { + method: "POST", + body: message, + headers: { + Host: url.hostname, + Date: now.toUTCString(), + Digest: `SHA-256=${digest}`, + Signature: header, + }, + }), + ); +}; + +const onMessage = async (event: MessageCreateEvent) => { + const channel_id = event.channel_id; + const channel = await Channel.findOneOrFail({ + where: { id: channel_id }, + relations: { + recipients: { + user: true, + }, + }, + }); + if (channel.isDm()) { + const message = await Message.findOneOrFail({ + where: { id: event.data.id }, + }); + const apMessage = message.toCreateAP(); + + for (const recipient of channel.recipients || []) { + if (recipient.user.federatedId) { + const user = await resolveWebfinger(recipient.user.federatedId); + if (!APObjectIsPerson(user)) + throw new APError("Cannot deliver message"); + + if (!user.id) throw new APError("Receiver ID is null?"); + + apMessage.to = [user.id]; + + const sender = await User.findOneOrFail({ + where: { id: event.data.author_id }, + select: ["privateKey"], + }); + + if (typeof user.inbox != "string") + throw new APError("inbox must be URL"); + + console.log( + await sendSignedMessage( + user.inbox, + `user/${event.data.author_id}`, + message, + sender.privateKey, + ).then((x) => x.text()), + ); + } + } + } +}; + +type ListenerFunc = (event: Event) => Promise; + +const listeners = { + MESSAGE_CREATE: onMessage, +} as Record; + +export const setupListener = () => { + if (RabbitMQ.connection) + throw new APError("Activitypub module has not implemented RabbitMQ"); + + // for (const event in listeners) { + // // process.setMaxListeners(process.getMaxListeners() + 1); + // // process.addListener("message", (msg) => + // // listener(msg as ProcessEvent, event, listeners[event as EVENTEnum]), + // // ); + + events.setMaxListeners(events.getMaxListeners() + 1); + events.onAny((event, msg) => listeners[msg.event as EVENTEnum]?.(msg)); + // } +}; diff --git a/src/activitypub/routes/channel/#channel_id/inbox.ts b/src/activitypub/routes/channel/#channel_id/inbox.ts index 2dd361430..89a990dc9 100644 --- a/src/activitypub/routes/channel/#channel_id/inbox.ts +++ b/src/activitypub/routes/channel/#channel_id/inbox.ts @@ -14,7 +14,11 @@ router.post("/", route({}), async (req, res) => { const message = await messageFromAP(body.object); - if ((await Message.count({ where: { id: message.id } })) != 0) + if ( + (await Message.count({ + where: { federatedId: message.federatedId }, + })) != 0 + ) return res.status(200); await message.save(); diff --git a/src/activitypub/routes/user/#user_id/inbox.ts b/src/activitypub/routes/user/#user_id/inbox.ts new file mode 100644 index 000000000..89a990dc9 --- /dev/null +++ b/src/activitypub/routes/user/#user_id/inbox.ts @@ -0,0 +1,33 @@ +import { messageFromAP } from "@spacebar/ap"; +import { route } from "@spacebar/api"; +import { Message, emitEvent } from "@spacebar/util"; +import { Router } from "express"; +import { HTTPError } from "lambert-server"; + +const router = Router(); +export default router; + +router.post("/", route({}), async (req, res) => { + const body = req.body; + + if (body.type != "Create") throw new HTTPError("not implemented"); + + const message = await messageFromAP(body.object); + + if ( + (await Message.count({ + where: { federatedId: message.federatedId }, + })) != 0 + ) + return res.status(200); + + await message.save(); + + await emitEvent({ + event: "MESSAGE_CREATE", + channel_id: message.channel_id, + data: message.toJSON(), + }); + + return res.status(200); +}); diff --git a/src/activitypub/routes/user/index.ts b/src/activitypub/routes/user/#user_id/index.ts similarity index 73% rename from src/activitypub/routes/user/index.ts rename to src/activitypub/routes/user/#user_id/index.ts index 67664dea7..d92663ff1 100644 --- a/src/activitypub/routes/user/index.ts +++ b/src/activitypub/routes/user/#user_id/index.ts @@ -5,8 +5,8 @@ import { Request, Response, Router } from "express"; const router = Router(); export default router; -router.get("/:id", route({}), async (req: Request, res: Response) => { - const id = req.params.id; +router.get("/", route({}), async (req: Request, res: Response) => { + const id = req.params.user_id; const user = await User.findOneOrFail({ where: { id } }); diff --git a/src/activitypub/routes/user/inbox.ts b/src/activitypub/routes/user/inbox.ts deleted file mode 100644 index 2065d7f21..000000000 --- a/src/activitypub/routes/user/inbox.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { route } from "@spacebar/api"; -import { Router } from "express"; -import { HTTPError } from "lambert-server"; - -const router = Router(); -export default router; - -router.post("/", route({}), async (req, res) => { - const body = req.body; - - if (body.type != "Create") throw new HTTPError("not implemented"); -}); diff --git a/src/activitypub/util/fetch.ts b/src/activitypub/util/fetch.ts new file mode 100644 index 000000000..07acdc453 --- /dev/null +++ b/src/activitypub/util/fetch.ts @@ -0,0 +1,8 @@ +import { DEFAULT_FETCH_OPTIONS } from "@spacebar/api"; +import { OrmUtils } from "@spacebar/util"; + +export const fetchOpts = OrmUtils.mergeDeep(DEFAULT_FETCH_OPTIONS, { + headers: { + Accept: "application/activity+json", + }, +}); diff --git a/src/activitypub/util/index.ts b/src/activitypub/util/index.ts index 767e75d95..fe142a647 100644 --- a/src/activitypub/util/index.ts +++ b/src/activitypub/util/index.ts @@ -1,3 +1,4 @@ export * from "./APError"; export * from "./OrderedCollection"; +export * from "./fetch"; export * from "./transforms/index"; diff --git a/src/activitypub/util/transforms/Message.ts b/src/activitypub/util/transforms/Message.ts deleted file mode 100644 index 3669121ca..000000000 --- a/src/activitypub/util/transforms/Message.ts +++ /dev/null @@ -1,141 +0,0 @@ -import { APError } from "@spacebar/ap"; -import { DEFAULT_FETCH_OPTIONS } from "@spacebar/api"; -import { - Channel, - Config, - Member, - Message, - OrmUtils, - Snowflake, - User, - UserSettings, -} from "@spacebar/util"; -import { APNote, APPerson, AnyAPObject } from "activitypub-types"; -import fetch from "node-fetch"; -import { ProxyAgent } from "proxy-agent"; -import TurndownService from "turndown"; - -const fetchOpts = OrmUtils.mergeDeep(DEFAULT_FETCH_OPTIONS, { - headers: { - Accept: "application/activity+json", - }, -}); - -const hasAPContext = (data: object) => { - if (!("@context" in data)) return false; - const context = data["@context"]; - const activitystreams = "https://www.w3.org/ns/activitystreams"; - if (Array.isArray(context)) - return context.find((x) => x == activitystreams); - return context == activitystreams; -}; - -export const resolveAPObject = async (data: string | T): Promise => { - // we were already given an AP object - if (typeof data != "string") return data; - - const agent = new ProxyAgent(); - const ret = await fetch(data, { - ...fetchOpts, - agent, - }); - - const json = await ret.json(); - - if (!hasAPContext(json)) throw new APError("Object is not APObject"); - - return json; -}; - -export const messageFromAP = async (data: APNote): Promise => { - if (!data.id) throw new APError("Message must have ID"); - if (data.type != "Note") throw new APError("Message must be Note"); - - const to = Array.isArray(data.to) - ? data.to.filter((x) => - typeof x == "string" ? x.includes("channel") : false, - )[0] - : data.to; - if (!to || typeof to != "string") - throw new APError("Message not deliverable"); - - // TODO: use a regex - const channel_id = to.split("/").reverse()[0]; - const channel = await Channel.findOneOrFail({ - where: { id: channel_id }, - relations: { guild: true }, - }); - - if (!data.attributedTo) - throw new APError("Message must have author (attributedTo)"); - const attrib = await resolveAPObject( - Array.isArray(data.attributedTo) - ? data.attributedTo[0] // hmm - : data.attributedTo, - ); - - if (!APObjectIsPerson(attrib)) - throw new APError("Message attributedTo must be Person"); - - const user = await userFromAP(attrib); - const member = channel.guild - ? await Member.findOneOrFail({ - where: { id: user.id, guild_id: channel.guild.id }, - }) - : undefined; - - return Message.create({ - id: data.id, - content: new TurndownService().turndown(data.content), - timestamp: data.published, - author: user, - guild: channel.guild, - member, - channel, - - type: 0, - sticker_items: [], - attachments: [], - embeds: [], - reactions: [], - mentions: [], - mention_roles: [], - mention_channels: [], - }); -}; - -export const APObjectIsPerson = (object: AnyAPObject): object is APPerson => { - return object.type == "Person"; -}; - -export const userFromAP = async (data: APPerson): Promise => { - if (!data.id) throw new APError("User must have ID"); - - const url = new URL(data.id); - const email = `${url.pathname.split("/").reverse()[0]}@${url.hostname}`; - - return User.create({ - id: Snowflake.generate(), - username: data.preferredUsername, - discriminator: url.hostname, - bio: new TurndownService().turndown(data.summary), - email, - data: { - hash: "#", - valid_tokens_since: new Date(), - }, - extended_settings: "{}", - settings: UserSettings.create(), - publicKey: "", - privateKey: "", - premium: false, - - premium_since: Config.get().defaults.user.premium - ? new Date() - : undefined, - rights: Config.get().register.defaultRights, - premium_type: Config.get().defaults.user.premiumType ?? 0, - verified: Config.get().defaults.user.verified ?? true, - created_at: new Date(), - }); -}; diff --git a/src/activitypub/util/transforms/index.ts b/src/activitypub/util/transforms/index.ts index 6596816a1..e8107ca0c 100644 --- a/src/activitypub/util/transforms/index.ts +++ b/src/activitypub/util/transforms/index.ts @@ -1 +1,196 @@ -export * from "./Message"; +import { APError, fetchOpts } from "@spacebar/ap"; +import { + Channel, + Config, + DmChannelDTO, + Member, + Message, + Snowflake, + User, + UserSettings, + WebfingerResponse, +} from "@spacebar/util"; +import { APNote, APPerson, AnyAPObject } from "activitypub-types"; +import fetch from "node-fetch"; +import { ProxyAgent } from "proxy-agent"; +import TurndownService from "turndown"; + +const hasAPContext = (data: object) => { + if (!("@context" in data)) return false; + const context = data["@context"]; + const activitystreams = "https://www.w3.org/ns/activitystreams"; + if (Array.isArray(context)) + return context.find((x) => x == activitystreams); + return context == activitystreams; +}; + +export const resolveAPObject = async (data: string | T): Promise => { + // we were already given an AP object + if (typeof data != "string") return data; + + const agent = new ProxyAgent(); + const ret = await fetch(data, { + ...fetchOpts, + agent, + }); + + const json = await ret.json(); + + if (!hasAPContext(json)) throw new APError("Object is not APObject"); + + return json; +}; + +export const resolveWebfinger = async ( + lookup: string, +): Promise => { + let domain: string, user: string; + if (lookup.includes("@")) { + // lookup a @handle + + if (lookup[0] == "@") lookup = lookup.slice(1); + [domain, user] = lookup.split("@"); + } else { + // lookup was a URL ( hopefully ) + const url = new URL(lookup); + domain = url.hostname; + user = url.pathname.split("/").reverse()[0]; + } + + const agent = new ProxyAgent(); + const wellknown = (await fetch( + `https://${domain}/.well-known/webfinger?resource=${lookup}`, + { + agent, + ...fetchOpts, + }, + ).then((x) => x.json())) as WebfingerResponse; + + const link = wellknown.links.find((x) => x.rel == "self"); + if (!link) throw new APError(".well-known did not contain rel=self link"); + + return await resolveAPObject(link.href); +}; + +export const messageFromAP = async (data: APNote): Promise => { + if (!data.id) throw new APError("Message must have ID"); + if (data.type != "Note") throw new APError("Message must be Note"); + + if (!data.attributedTo) + throw new APError("Message must have author (attributedTo)"); + const attrib = await resolveAPObject( + Array.isArray(data.attributedTo) + ? data.attributedTo[0] // hmm + : data.attributedTo, + ); + + if (!APObjectIsPerson(attrib)) + throw new APError("Message attributedTo must be Person"); + + const user = await userFromAP(attrib); + + const to = Array.isArray(data.to) + ? data.to.filter((x) => + typeof x == "string" + ? x.includes("channel") || x.includes("user") + : false, + )[0] + : data.to; + if (!to || typeof to != "string") + throw new APError("Message not deliverable"); + + // TODO: use a regex + + let channel: Channel | DmChannelDTO; + const to_id = to.split("/").reverse()[0]; + if (to.includes("user")) { + // this is a DM channel + const toUser = await User.findOneOrFail({ where: { id: to_id } }); + + // Channel.createDMCHannel does a .save() so the author must be present + await user.save(); + + // const cache = await Channel.findOne({ where: { recipients: []}}) + + channel = await Channel.createDMChannel( + [toUser.id, user.id], + toUser.id, + ); + } else { + channel = await Channel.findOneOrFail({ + where: { id: to_id }, + relations: { guild: true }, + }); + } + + const member = + channel instanceof Channel + ? await Member.findOneOrFail({ + where: { id: user.id, guild_id: channel.guild.id }, + }) + : undefined; + + return Message.create({ + id: Snowflake.generate(), + federatedId: data.id, + content: new TurndownService().turndown(data.content), + timestamp: data.published, + author: user, + guild: channel instanceof Channel ? channel.guild : undefined, + member, + channel_id: channel.id, + + type: 0, + sticker_items: [], + attachments: [], + embeds: [], + reactions: [], + mentions: [], + mention_roles: [], + mention_channels: [], + }); +}; + +export const APObjectIsPerson = (object: AnyAPObject): object is APPerson => { + return object.type == "Person"; +}; + +export const userFromAP = async (data: APPerson): Promise => { + if (!data.id) throw new APError("User must have ID"); + + const url = new URL(data.id); + const email = `${url.pathname.split("/").reverse()[0]}@${url.hostname}`; + + // don't like this + // the caching should probably be done elsewhere + // this function should only be for converting AP to SB (ideally) + const cache = await User.findOne({ + where: { federatedId: url.toString() }, + }); + if (cache) return cache; + + return User.create({ + federatedId: url.toString(), + username: data.preferredUsername, + discriminator: url.hostname, + bio: new TurndownService().turndown(data.summary), + email, + data: { + hash: "#", + valid_tokens_since: new Date(), + }, + extended_settings: "{}", + settings: UserSettings.create(), + publicKey: "", + privateKey: "", + premium: false, + + premium_since: Config.get().defaults.user.premium + ? new Date() + : undefined, + rights: Config.get().register.defaultRights, + premium_type: Config.get().defaults.user.premiumType ?? 0, + verified: Config.get().defaults.user.verified ?? true, + created_at: new Date(), + }); +}; diff --git a/src/bundle/index.ts b/src/bundle/index.ts index c6af4f005..8b1c9429d 100644 --- a/src/bundle/index.ts +++ b/src/bundle/index.ts @@ -16,7 +16,8 @@ along with this program. If not, see . */ +export * from "@spacebar/ap"; export * from "@spacebar/api"; -export * from "@spacebar/util"; -export * from "@spacebar/gateway"; export * from "@spacebar/cdn"; +export * from "@spacebar/gateway"; +export * from "@spacebar/util"; diff --git a/src/util/entities/Channel.ts b/src/util/entities/Channel.ts index a5191dc31..8b692ac7e 100644 --- a/src/util/entities/Channel.ts +++ b/src/util/entities/Channel.ts @@ -387,6 +387,18 @@ export class Channel extends BaseClass { if (channel == null) { name = trimSpecial(name); + const { publicKey, privateKey } = await generateKeyPair("rsa", { + modulusLength: 4096, + publicKeyEncoding: { + type: "spki", + format: "pem", + }, + privateKeyEncoding: { + type: "pkcs8", + format: "pem", + }, + }); + channel = await Channel.create({ name, type, @@ -403,6 +415,8 @@ export class Channel extends BaseClass { }), ), nsfw: false, + publicKey, + privateKey, }).save(); } diff --git a/src/util/entities/Message.ts b/src/util/entities/Message.ts index bbbb2ac18..3bf3b9d00 100644 --- a/src/util/entities/Message.ts +++ b/src/util/entities/Message.ts @@ -16,7 +16,7 @@ along with this program. If not, see . */ -import type { APAnnounce, APNote } from "activitypub-types"; +import type { APAnnounce, APCreate, APNote } from "activitypub-types"; import { Column, CreateDateColumn, @@ -220,6 +220,9 @@ export class Message extends BaseClass { @Column({ type: "simple-json", nullable: true }) components?: MessageComponent[]; + @Column({ nullable: true }) + federatedId: string; + toJSON(): Message { return { ...this, @@ -227,6 +230,7 @@ export class Message extends BaseClass { member_id: undefined, webhook_id: undefined, application_id: undefined, + federatedId: undefined, nonce: this.nonce ?? undefined, tts: this.tts ?? false, @@ -256,6 +260,19 @@ export class Message extends BaseClass { }; } + toCreateAP(): APCreate { + const { webDomain } = Config.get().federation; + + return { + "@context": "https://www.w3.org/ns/activitystreams", + type: "Create", + id: `https://${webDomain}/fed/channel/${this.channel_id}/messages/${this.id}`, + to: [], + actor: `https://${webDomain}/fed/user/${this.author_id}`, + object: this.toAP(), + }; + } + // TODO: move to AP module toAP(): APNote { const { webDomain } = Config.get().federation; diff --git a/src/util/entities/User.ts b/src/util/entities/User.ts index f92136931..1594093f6 100644 --- a/src/util/entities/User.ts +++ b/src/util/entities/User.ts @@ -252,6 +252,9 @@ export class User extends BaseClass { @Column({ select: false }) privateKey: string; + @Column({ nullable: true }) + federatedId: string; + // TODO: I don't like this method? validate() { if (this.discriminator) { diff --git a/src/util/schemas/MessageAcknowledgeSchema.ts b/src/util/schemas/MessageAcknowledgeSchema.ts index 28cd9c79f..726dc21b5 100644 --- a/src/util/schemas/MessageAcknowledgeSchema.ts +++ b/src/util/schemas/MessageAcknowledgeSchema.ts @@ -19,4 +19,7 @@ export interface MessageAcknowledgeSchema { manual?: boolean; mention_count?: number; + flags?: number; + last_viewed?: number; + token?: unknown; // was null } diff --git a/src/util/util/Event.ts b/src/util/util/Event.ts index 01f4911a6..76a529ed9 100644 --- a/src/util/util/Event.ts +++ b/src/util/util/Event.ts @@ -17,9 +17,9 @@ */ import { Channel } from "amqplib"; -import { RabbitMQ } from "./RabbitMQ"; -import EventEmitter from "events"; +import EventEmitter from "eventemitter2"; import { EVENT, Event } from "../interfaces"; +import { RabbitMQ } from "./RabbitMQ"; export const events = new EventEmitter(); export async function emitEvent(payload: Omit) {