|
1 | | -import type { GuildMember } from "discord.js"; |
| 1 | +import { |
| 2 | + Routes, |
| 3 | + type APIGuild, |
| 4 | + PermissionFlagsBits, |
| 5 | +} from "discord-api-types/v10"; |
| 6 | + |
| 7 | +import type { REST } from "@discordjs/rest"; |
| 8 | +import { type GuildMember } from "discord.js"; |
| 9 | + |
| 10 | +import { complement, intersection } from "#~/helpers/sets.js"; |
| 11 | + |
2 | 12 | import type { AccessToken } from "simple-oauth2"; |
3 | 13 | import { fetchSettings, SETTINGS } from "#~/models/guilds.server"; |
4 | 14 |
|
@@ -88,3 +98,101 @@ export const timeout = async (member: GuildMember | null) => { |
88 | 98 | } |
89 | 99 | return member.timeout(OVERNIGHT); |
90 | 100 | }; |
| 101 | + |
| 102 | +const authzRoles = { |
| 103 | + mod: "MOD", |
| 104 | + admin: "ADMIN", |
| 105 | + manager: "MANAGER", |
| 106 | + manageChannels: "MANAGE_CHANNELS", |
| 107 | + manageGuild: "MANAGE_GUILD", |
| 108 | + manageRoles: "MANAGE_ROLES", |
| 109 | +} as const; |
| 110 | + |
| 111 | +const isUndefined = (x: unknown): x is undefined => typeof x === "undefined"; |
| 112 | + |
| 113 | +const processGuild = (g: APIGuild) => { |
| 114 | + const perms = BigInt(g.permissions || 0); |
| 115 | + const authz = new Set<(typeof authzRoles)[keyof typeof authzRoles]>(); |
| 116 | + |
| 117 | + if (perms & PermissionFlagsBits.Administrator) { |
| 118 | + authz.add(authzRoles.admin); |
| 119 | + } |
| 120 | + if (perms & PermissionFlagsBits.ModerateMembers) { |
| 121 | + authz.add(authzRoles.mod); |
| 122 | + } |
| 123 | + if (perms & PermissionFlagsBits.ManageChannels) { |
| 124 | + authz.add(authzRoles.manageChannels); |
| 125 | + authz.add(authzRoles.manager); |
| 126 | + } |
| 127 | + if (perms & PermissionFlagsBits.ManageGuild) { |
| 128 | + authz.add(authzRoles.manageGuild); |
| 129 | + authz.add(authzRoles.manager); |
| 130 | + } |
| 131 | + if (perms & PermissionFlagsBits.ManageRoles) { |
| 132 | + authz.add(authzRoles.manageRoles); |
| 133 | + authz.add(authzRoles.manager); |
| 134 | + } |
| 135 | + |
| 136 | + return { |
| 137 | + id: g.id as string, |
| 138 | + icon: g.icon, |
| 139 | + name: g.name as string, |
| 140 | + authz: [...authz.values()], |
| 141 | + }; |
| 142 | +}; |
| 143 | + |
| 144 | +export interface Guild extends ReturnType<typeof processGuild> { |
| 145 | + hasBot: boolean; |
| 146 | +} |
| 147 | + |
| 148 | +export const fetchGuilds = async ( |
| 149 | + userRest: REST, |
| 150 | + botRest: REST, |
| 151 | +): Promise<Guild[]> => { |
| 152 | + const [rawUserGuilds, rawBotGuilds] = (await Promise.all([ |
| 153 | + userRest.get(Routes.userGuilds()), |
| 154 | + botRest.get(Routes.userGuilds()), |
| 155 | + ])) as [APIGuild[], APIGuild[]]; |
| 156 | + |
| 157 | + const botGuilds = new Map( |
| 158 | + rawBotGuilds.reduce( |
| 159 | + (accum, val) => { |
| 160 | + const guild = processGuild(val); |
| 161 | + if (guild.authz.length > 0) { |
| 162 | + accum.push([val.id, guild]); |
| 163 | + } |
| 164 | + return accum; |
| 165 | + }, |
| 166 | + [] as [string, Omit<Guild, "hasBot">][], |
| 167 | + ), |
| 168 | + ); |
| 169 | + const userGuilds = new Map( |
| 170 | + rawUserGuilds.reduce( |
| 171 | + (accum, val) => { |
| 172 | + const guild = processGuild(val); |
| 173 | + if (guild.authz.includes("MANAGER")) { |
| 174 | + accum.push([val.id, guild]); |
| 175 | + } |
| 176 | + return accum; |
| 177 | + }, |
| 178 | + [] as [string, Omit<Guild, "hasBot">][], |
| 179 | + ), |
| 180 | + ); |
| 181 | + |
| 182 | + const botGuildIds = new Set(botGuilds.keys()); |
| 183 | + const userGuildIds = new Set(userGuilds.keys()); |
| 184 | + |
| 185 | + const manageableGuilds = intersection(botGuildIds, userGuildIds); |
| 186 | + const invitableGuilds = complement(userGuildIds, botGuildIds); |
| 187 | + |
| 188 | + return [ |
| 189 | + ...[...manageableGuilds].map((gId) => { |
| 190 | + const guild = botGuilds.get(gId); |
| 191 | + return guild ? { ...guild, hasBot: true } : undefined; |
| 192 | + }), |
| 193 | + ...[...invitableGuilds].map((gId) => { |
| 194 | + const guild = botGuilds.get(gId); |
| 195 | + return guild ? { ...guild, hasBot: false } : undefined; |
| 196 | + }), |
| 197 | + ].filter((g) => !isUndefined(g)); |
| 198 | +}; |
0 commit comments