/[sudobot]/branches/6.x/src/commands/information/ProfileCommand.ts
ViewVC logotype

Contents of /branches/6.x/src/commands/information/ProfileCommand.ts

Parent Directory Parent Directory | Revision Log Revision Log


Revision 577 - (show annotations)
Mon Jul 29 18:52:37 2024 UTC (8 months ago) by rakinar2
File MIME type: application/typescript
File size: 12040 byte(s)
chore: add old version archive branches (2.x to 9.x-dev)
1 /**
2 * This file is part of SudoBot.
3 *
4 * Copyright (C) 2021-2023 OSN Developers.
5 *
6 * SudoBot is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU Affero General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * SudoBot is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Affero General Public License for more details.
15 *
16 * You should have received a copy of the GNU Affero General Public License
17 * along with SudoBot. If not, see <https://www.gnu.org/licenses/>.
18 */
19
20 import { formatDistanceToNowStrict } from "date-fns";
21 import {
22 APIEmbedField,
23 ActivityType,
24 EmbedBuilder,
25 Emoji,
26 Guild,
27 GuildMember,
28 PermissionFlagsBits,
29 Role,
30 SlashCommandBuilder,
31 User,
32 roleMention
33 } from "discord.js";
34 import Client from "../../core/Client";
35 import Command, { AnyCommandContext, ArgumentType, CommandMessage, CommandReturn, ValidationRule } from "../../core/Command";
36 import { log, logError } from "../../utils/logger";
37 import { getUserBadges } from "../../utils/user";
38 import { getEmoji } from "../../utils/utils";
39
40 const status = (s: "idle" | "online" | "dnd" | "invisible" | null | undefined): string => {
41 if (s === "idle") return "Idle";
42 else if (s === "dnd") return "Do not disturb";
43 else if (s === "online") return "Online";
44 else if (s === undefined || s === null || s === "invisible") return "Offline/Invisible";
45
46 return s;
47 };
48
49 const statusEmoji = (client: Client, s: "idle" | "online" | "dnd" | "invisible" | null | undefined): string => {
50 if (s === "idle") return getEmoji(client, "idle");
51 else if (s === "dnd") return getEmoji(client, "dnd");
52 else if (s === "online") return getEmoji(client, "online");
53 else if (s === undefined || s === null || s === "invisible") return getEmoji(client, "invisible");
54
55 return s;
56 };
57
58 const getStatusText = (client: Client, member: GuildMember) =>
59 "" +
60 ((member?.presence?.clientStatus?.desktop
61 ? statusEmoji(client, member?.presence?.clientStatus?.desktop) +
62 " Desktop (" +
63 status(member?.presence?.clientStatus?.desktop) +
64 ")\n"
65 : "") +
66 (member?.presence?.clientStatus?.web
67 ? statusEmoji(client, member?.presence?.clientStatus?.web) +
68 " Web (" +
69 status(member?.presence?.clientStatus?.web) +
70 ")\n"
71 : "") +
72 (member?.presence?.clientStatus?.mobile
73 ? statusEmoji(client, member?.presence?.clientStatus?.mobile) +
74 " Mobile (" +
75 status(member?.presence?.clientStatus?.mobile) +
76 ")"
77 : ""));
78
79 export function getPermissionLevel(
80 { permissions, guild, id }: { id: string; permissions: GuildMember["permissions"]; guild: Guild },
81 string: boolean = false
82 ) {
83 if (guild.ownerId === id) {
84 return string ? "100" : 100;
85 }
86
87 const allBits = Object.keys(PermissionFlagsBits).length;
88 const array = permissions.toArray();
89
90 if (array.includes("Administrator")) {
91 return string ? "100" : 100;
92 }
93
94 const percentage = Math.round((array.length / allBits) * 100);
95 return string ? percentage.toString() : percentage;
96 }
97
98 export default class ProfileCommand extends Command {
99 public readonly name = "profile";
100 public readonly validationRules: ValidationRule[] = [
101 {
102 types: [ArgumentType.Member, ArgumentType.User],
103 name: "member",
104 optional: true,
105 errors: {
106 "type:invalid": "Invalid user given!",
107 "entity:null": "That user could not be found!",
108 },
109 entity: {
110 notNull: true
111 },
112 }
113 ];
114 public readonly aliases = ["userprofile", "userinfo"];
115 public readonly permissions = [];
116
117 public readonly description = "Shows your or someone else's profile.";
118 public readonly slashCommandBuilder = new SlashCommandBuilder().addUserOption(option =>
119 option.setName("member").setDescription("The target member or user")
120 );
121
122 private isAvailableEmoji({ id, identifier }: Emoji) {
123 for (const [, guild] of this.client.guilds.cache) {
124 const emoji = guild.emojis.cache.find(e => e.id === id || e.identifier === identifier);
125
126 if (emoji) {
127 return true;
128 }
129 }
130
131 return false;
132 }
133
134 private isGuildMember(member: GuildMember | User | null): member is GuildMember {
135 return member instanceof GuildMember;
136 }
137
138 async execute(message: CommandMessage, context: AnyCommandContext): Promise<CommandReturn> {
139 await this.deferIfInteraction(message);
140
141 const member: GuildMember | User | null =
142 (context.isLegacy
143 ? context.parsedNamedArgs.member
144 : context.options.getMember("member") ?? context.options.getUser("member", true)) ?? message.member;
145
146 if (!member) {
147 await this.error(message, "Could not resolve that member!");
148 return;
149 }
150
151 const isMember = this.isGuildMember(member);
152 const user = isMember ? (member.user! as User) : member;
153 const activities: string[] = [];
154
155 if (isMember && member?.presence) {
156 for (const a of member?.presence?.activities.values()!) {
157 log(a);
158
159 if (a.type === ActivityType.Custom) {
160 activities.push(
161 `${a.emoji && this.isAvailableEmoji(a.emoji) ? `${a.emoji.toString()}` : ":small_blue_diamond:"} ${
162 a.state
163 }`
164 );
165 } else if (a.type === ActivityType.Listening) {
166 if (a.name === "Spotify") {
167 const url = a.url ? `${a.url}` : null;
168 activities.push(
169 `${this.emoji("spotify")} Listening to **Spotify**: ${url ? "[" : "__"}${a.state?.split(/\;/)[0]} - ${
170 a.details
171 }${url ? "](" + url + ")" : "__"}`
172 );
173 continue;
174 }
175
176 activities.push(`:musical_note: Listening to **${a.name}**`);
177 } else if (a.type === ActivityType.Competing) {
178 activities.push(`:fire: Competing **${a.name}**`);
179 } else if (a.type === ActivityType.Playing) {
180 activities.push(`:video_game: Playing **${a.name}**`);
181 } else if (a.type === ActivityType.Streaming) {
182 activities.push(`:video_camera: Streaming **${a.name}**`);
183 } else if (a.type === ActivityType.Watching) {
184 activities.push(`:tv: Watching **${a.name}**`);
185 }
186 }
187 }
188
189 const allRoles = isMember
190 ? [...member!.roles.cache.values()]
191 .filter(role => role.id !== message.guildId!)
192 .sort((role1, role2) => {
193 return role2.position - role1.position;
194 })
195 : null;
196 const limit = 10;
197 const roles = (isMember ? (allRoles!.length > limit ? allRoles!.slice(0, limit) : allRoles) : ([] as Role[]))!
198 .reduce((acc, value) => `${acc} ${roleMention(value.id)}`, "")!
199 .trim()!;
200 const statusText = isMember ? getStatusText(this.client, member!) : null;
201
202 const fields: APIEmbedField[] = [
203 ...(isMember
204 ? [
205 {
206 name: "Nickname",
207 value: `${member!.nickname?.replace(/\*\<\>\@\_\~\|/g, "") ?? "*Nickname is not set*"}`
208 }
209 ]
210 : []),
211 {
212 name: "Display Name",
213 value: `${user!.displayName?.replace(/\*\<\>\@\_\~\|/g, "") ?? "*Display name is not set*"}`
214 },
215 {
216 name: "Account Created",
217 value: `${user.createdAt.toLocaleDateString("en-US")} (${formatDistanceToNowStrict(user.createdTimestamp, {
218 addSuffix: true
219 })})`,
220 inline: true
221 },
222 ...(isMember
223 ? [
224 {
225 name: "Joined at",
226 value: `${member!.joinedAt!.toLocaleDateString("en-US")} (${formatDistanceToNowStrict(
227 member!.joinedTimestamp!,
228 {
229 addSuffix: true
230 }
231 )})`,
232 inline: true
233 },
234 {
235 name: "Active Devices",
236 value: `${statusText === "" ? `${this.emoji("invisible")} Offline/Invisible` : statusText}`
237 },
238 {
239 name: "Status",
240 value: `${activities.length === 0 ? "*No status set*" : activities.join("\n")}`
241 },
242 {
243 name: "Roles",
244 value:
245 roles === ""
246 ? "*No roles assigned*"
247 : `${roles} ${allRoles!.length > limit ? `**+ ${allRoles!.length - limit} More**` : ""}`
248 }
249 ]
250 : [])
251 ];
252
253 const badges = getUserBadges(this.client, user);
254
255 if (badges.length > 0) {
256 fields.push({
257 name: "Badges",
258 value: badges.join("\n")
259 });
260 }
261
262 let banner: string | undefined;
263
264 try {
265 await user.fetch(true);
266 banner = user!.bannerURL({ size: 4096, forceStatic: false }) ?? undefined;
267 } catch (e) {
268 logError(e);
269 }
270
271 log("Banner", banner, user!.banner);
272
273 let permissionPercentage = 0;
274
275 if (isMember) {
276 if (this.client.permissionManager.usesLevelBasedMode(member.guild.id)) {
277 permissionPercentage = (await this.client.permissionManager.getManager(member.guild.id)).getPermissionLevel(
278 member
279 );
280 } else {
281 permissionPercentage =
282 message.guild!.ownerId === user.id || (isMember && member.permissions.has("Administrator"))
283 ? 100
284 : (getPermissionLevel(member, false) as number);
285 }
286 }
287
288 await this.deferredReply(message, {
289 embeds: [
290 new EmbedBuilder({
291 image: banner
292 ? {
293 url: banner
294 }
295 : undefined
296 })
297 .setColor(user!.hexAccentColor ? user!.hexAccentColor! : "#007bff")
298 .setAuthor({
299 name: user.tag!,
300 iconURL: user.displayAvatarURL()
301 })
302 .setThumbnail(
303 (isMember ? member : user).displayAvatarURL({
304 size: 4096,
305 forceStatic: false
306 })
307 )
308 .setFields(fields)
309 .setFooter({
310 text:
311 `${user.bot ? "Bot" : "User"} • ${member!.id}` +
312 (isMember ? ` • Has ${permissionPercentage}% permissions` : "")
313 })
314 ]
315 });
316 }
317 }

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26