From cb0443821ef8da4b44ee2f4fb677854896287705 Mon Sep 17 00:00:00 2001 From: Rory& Date: Mon, 7 Jul 2025 21:59:25 +0200 Subject: [PATCH] Add emoji source endpoint --- assets/schemas.json | Bin 27237704 -> 27775639 bytes src/api/routes/emojis/#emoji_id/source.ts | 93 ++++++++++++++++++ .../schemas/responses/EmojiSourceResponse.ts | 44 +++++++++ src/util/schemas/responses/index.ts | 1 + 4 files changed, 138 insertions(+) create mode 100644 src/api/routes/emojis/#emoji_id/source.ts create mode 100644 src/util/schemas/responses/EmojiSourceResponse.ts diff --git a/assets/schemas.json b/assets/schemas.json index f098d0157220949dac03f4804686bee95c44f83b..d7a7e0ca561df509443ce7b4e76bb2182da62180 100755 GIT binary patch delta 27154 zcmd6v`(Mp@9>*W=qtmIJq}z6(i;|H`B}Oh2MTSi2OeJ)$p+;(?gh;W{heh8^_)w!$ zgBgTvEc>2v`P4W&OA}kmS{^PZYmmE?ODy(#svl+_yMMs@hx2%JdYm7gkJtP1d4FD? z&-Z(_?!B7t)0(e22GNgr$xUA+U#lIO%oF?M*K*hSUY=aD=}-f&f9U!(Pn0Zw;Tj(| z6>m;%ux)#6aN8c+ZG;ig&koVg{`Fm=<)W)* zlN%jya--vmcCv_EMl|L(j`y)IW^@W1BIK%9IA^=&m3gT4%H69L$x2-(T%oYUNdI%LOpR_=&*TILoUy2fvv+a zuyw>A2qQ*kdtr38w-?oDpQ+}Kl_dk1uWA(fs(iN-mNKerG)9$;nM+qn=I=EY{k{CA z6IRi>VH{dFjHf*CUXGZEmm@x@yiatJ zu`iq2G83n^d=^Ewj`)d=T1z%QH5A9E&hjH1@(*2M_@PTP_TOYf&U97bsH=)drrX%H z_2Jt=$?C?=#=5al#|V!a?;7XeyT-YGbhTt>5~A@;LW~1l6}i4BLB+B7pCr=mmrmpOzJ(y4tR zI<=?S(yeXQP?(Mlg&7+Ot9aFJFNAS)b>0tx5jj>~S8~x-`g6*ed=13rIp;KG(b6$7i*1% zcKVv0Qy*HC;q5Blcs*wSUCp z+8RCKgmm{kitfID($H-`OmX2DDlQy%Aw0&{Bq7!${g_8M(DKR{Cxr8Tug7ci0KWN zj!-m$4bPCtUiE=_m+`>7e=Z>$=mQV0;DLu%qX?HvasD+d&cFUEVHLshCW58@6kQdy zH8CnM;x#MVjQFf|vF1E~@3hrS_BT+!_f{9(`&ZhygO*^*GDB};X6Ubj3Fm;j{tmk9 z@4k6T)?ew#0Vd}F)~vjrL|Ds8^+T*wKguAC_)W!Q{HEebI$cC<{iM-g#L9i1VYyFx z5Z#S;&x5zQP0um6sbdUXL3=o-$G^bo@h`_x=d^qF3hkb~_M*$lF0Flom)5@hiZEh} z^*d~_em|cuqHf=Z6S{qnLKji0^;3YT_3J|!lFN&Zk#zK&5s0Jbl(3kvoQ7FW)Wn#; zw8vzJ4XLox6hvXC8ReKuxkYLaxkcuXOgP+W_7))0?E3@ZE+iS|bP;#Z3a-$#*nYDK zl9~c+Kr{u|g5?XkHI#nqcOcSl*+C-V^o+@N01=bzNV?E9%7Gv>N+W3GFbF1j+|Al=(2y{O?<2_V9)62Xe@bEv(jul6T_c(p&7a+B{xT{Lk%h(!|@Kt5q7q-Up@ z*PH?(uQ?UMI_Vmp(U}Y7r86@P9@DLT>9Qgn#AQVW%%NNL(#f)~K%6XF4ByhtOzE-r z5)hBQv!IpgOL}EXA@+PWh}iQ>;SJTClwa4XI_)q_>Yum_ME}I)uz~I*qBWQwCX#o@ z3J~5Mxp0@NsAsBsQ4g(O1>(^9JQz<{)o!;&YjA6fwVm=NX}-33^>9#2?e_T~+U*OV znXb-~0bXdT*Md1=6ST(<>j+r~q9de`vUMj3{dy2WUj!Pu87oy}Z2(b`wGn&?XJ&%Y zCJ+fmn_(+qB@6f#5Cyy#CK84W9#$gTpTo97F5wMm_u9n!RZg?*c@F%D*2c;3)VDUE zXcdg-K9BtSbMKj+cD!}UeRp0ktDmz}BEAMuB1)l(u=s{IMT+|H9biuwGW8Azk$Pt* l+#}o&ZY(bYabtNo+#n1YY_?mjHM0sJ`&Pb_)e5uK{{e01kqrO< delta 3444 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 diff --git a/src/api/routes/emojis/#emoji_id/source.ts b/src/api/routes/emojis/#emoji_id/source.ts new file mode 100644 index 00000000..7ce8b42e --- /dev/null +++ b/src/api/routes/emojis/#emoji_id/source.ts @@ -0,0 +1,93 @@ +/* + Spacebar: A FOSS re-implementation and extension of the Discord.com backend. + Copyright (C) 2023 Spacebar and Spacebar Contributors + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ + +import { route } from "@spacebar/api"; +import { + Emoji, + APIErrorResponse, + DiscordApiErrors, + EmojiSourceResponse, + Guild, + Member, + EmojiGuild, +} from "@spacebar/util"; +import { Request, Response, Router } from "express"; + +const router = Router(); + +router.get( + "/", + route({ + responses: { + 200: { + body: "EmojiSourceResponse", + }, + 404: { + body: "APIErrorResponse", + }, + }, + }), + async (req: Request, res: Response) => { + const { emoji_id } = req.params; + + const emoji = await Emoji.findOne({ where: { id: emoji_id } }); + if (!emoji) { + res.status(404).json({ + code: DiscordApiErrors.UNKNOWN_EMOJI.code, + message: `No emoji with ID ${emoji_id} appear to exist. Are you sure you didn't mistype it?`, + errors: {}, + } as APIErrorResponse); + return; + } + + // TODO: emojis can be owned by applications these days, account for this when we get there? + res.json({ + type: "GUILD", + guild: { + ...(await Guild.findOne({ + where: { + id: emoji.guild_id, + }, + select: { + id: true, + name: true, + icon: true, + description: true, + features: true, + emojis: true, + premium_tier: true, + premium_subscription_count: true, + }, + })), + approximate_member_count: await Member.countBy({ + guild_id: emoji.guild_id, + }), + approximate_presence_count: await Member.countBy({ + guild_id: emoji.guild_id, + user: { + sessions: { + status: "online", + }, + }, + }), + } as EmojiGuild, + } as EmojiSourceResponse); + }, +); + +export default router; diff --git a/src/util/schemas/responses/EmojiSourceResponse.ts b/src/util/schemas/responses/EmojiSourceResponse.ts new file mode 100644 index 00000000..6eeebb32 --- /dev/null +++ b/src/util/schemas/responses/EmojiSourceResponse.ts @@ -0,0 +1,44 @@ +/* + Spacebar: A FOSS re-implementation and extension of the Discord.com backend. + Copyright (C) 2025 Spacebar and Spacebar Contributors + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ + +import { Emoji } from "@spacebar/util"; + +export interface EmojiSourceResponse { + type: "GUILD" | "APPLICATION"; + guild?: EmojiGuild | null; + application?: EmojiApplication | null; +} + +// keep in sync with +export interface EmojiGuild { + id: string; + name: string; + icon?: string | null; + description?: string | null; + features: string[]; + emojis: Emoji[]; + premium_tier: number; + premium_subscription_count?: number; + approximate_member_count?: number; + approximate_presence_count?: number; +} + +export interface EmojiApplication { + id: string; + name: string; +} diff --git a/src/util/schemas/responses/index.ts b/src/util/schemas/responses/index.ts index 67daaddc..29537cf7 100644 --- a/src/util/schemas/responses/index.ts +++ b/src/util/schemas/responses/index.ts @@ -24,6 +24,7 @@ export * from "./CaptchaRequiredResponse"; export * from "./DiscoverableGuildsResponse"; export * from "./EmailDomainLookupResponse"; export * from "./EmailDomainLookupVerifyCodeResponse"; +export * from "./EmojiSourceResponse" export * from "./GatewayBotResponse"; export * from "./GatewayResponse"; export * from "./GenerateRegistrationTokensResponse";