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

Contents of /branches/5.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: 10511 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 SlashCommandBuilder,
30 roleMention
31 } from "discord.js";
32 import Client from "../../core/Client";
33 import Command, { AnyCommandContext, ArgumentType, CommandMessage, CommandReturn, ValidationRule } from "../../core/Command";
34 import { log, logError } from "../../utils/logger";
35 import { getUserBadges } from "../../utils/user";
36 import { getEmoji } from "../../utils/utils";
37
38 const status = (s: "idle" | "online" | "dnd" | "invisible" | null | undefined): string => {
39 if (s === "idle") return "Idle";
40 else if (s === "dnd") return "Do not disturb";
41 else if (s === "online") return "Online";
42 else if (s === undefined || s === null || s === "invisible") return "Offline/Invisible";
43
44 return s;
45 };
46
47 const statusEmoji = (client: Client, s: "idle" | "online" | "dnd" | "invisible" | null | undefined): string => {
48 if (s === "idle") return getEmoji(client, "idle");
49 else if (s === "dnd") return getEmoji(client, "dnd");
50 else if (s === "online") return getEmoji(client, "online");
51 else if (s === undefined || s === null || s === "invisible") return getEmoji(client, "invisible");
52
53 return s;
54 };
55
56 const getStatusText = (client: Client, member: GuildMember) =>
57 "" +
58 ((member?.presence?.clientStatus?.desktop
59 ? statusEmoji(client, member?.presence?.clientStatus?.desktop) +
60 " Desktop (" +
61 status(member?.presence?.clientStatus?.desktop) +
62 ")\n"
63 : "") +
64 (member?.presence?.clientStatus?.web
65 ? statusEmoji(client, member?.presence?.clientStatus?.web) +
66 " Web (" +
67 status(member?.presence?.clientStatus?.web) +
68 ")\n"
69 : "") +
70 (member?.presence?.clientStatus?.mobile
71 ? statusEmoji(client, member?.presence?.clientStatus?.mobile) +
72 " Mobile (" +
73 status(member?.presence?.clientStatus?.mobile) +
74 ")"
75 : ""));
76
77 export function getPermissionLevel(
78 { permissions, guild, id }: { id: string; permissions: GuildMember["permissions"]; guild: Guild },
79 string: boolean = false
80 ) {
81 if (guild.ownerId === id) {
82 return string ? "100" : 100;
83 }
84
85 const allBits = Object.keys(PermissionFlagsBits).length;
86 const array = permissions.toArray();
87
88 if (array.includes("Administrator")) {
89 return string ? "100" : 100;
90 }
91
92 const percentage = Math.round((array.length / allBits) * 100);
93 return string ? percentage.toString() : percentage;
94 }
95
96 export default class ProfileCommand extends Command {
97 public readonly name = "profile";
98 public readonly validationRules: ValidationRule[] = [
99 {
100 types: [ArgumentType.GuildMember],
101 name: "member",
102 optional: true,
103 typeErrorMessage: "Invalid member given",
104 entityNotNull: true,
105 entityNotNullErrorMessage: "That member could not be found!"
106 }
107 ];
108 public readonly aliases = ["userprofile"];
109 public readonly permissions = [];
110
111 public readonly description = "Shows your or someone else's profile.";
112 public readonly slashCommandBuilder = new SlashCommandBuilder().addUserOption(option =>
113 option.setName("member").setDescription("The target member")
114 );
115
116 private isAvailableEmoji({ id, identifier }: Emoji) {
117 for (const [, guild] of this.client.guilds.cache) {
118 const emoji = guild.emojis.cache.find(e => e.id === id || e.identifier === identifier);
119
120 if (emoji) {
121 return true;
122 }
123 }
124
125 return false;
126 }
127
128 async execute(message: CommandMessage, context: AnyCommandContext): Promise<CommandReturn> {
129 await this.deferIfInteraction(message);
130
131 const member: GuildMember | null =
132 (context.isLegacy ? context.parsedNamedArgs.member : context.options.getMember("member")) ?? message.member;
133
134 if (!member) {
135 await this.error(message, "Could not resolve that member!");
136 return;
137 }
138
139 const activities: string[] = [];
140
141 log(member.presence);
142
143 if (member?.presence) {
144 for (const a of member?.presence?.activities.values()!) {
145 log(a);
146
147 if (a.type === ActivityType.Custom) {
148 activities.push(
149 `${a.emoji && this.isAvailableEmoji(a.emoji) ? `${a.emoji.toString()}` : ":small_blue_diamond:"} ${
150 a.state
151 }`
152 );
153 } else if (a.type === ActivityType.Listening) {
154 if (a.name === "Spotify") {
155 const url = a.url ? `${a.url}` : null;
156 activities.push(
157 `${this.emoji("spotify")} Listening to **Spotify**: ${url ? "[" : "__"}${a.state?.split(/\;/)[0]} - ${
158 a.details
159 }${url ? "](" + url + ")" : "__"}`
160 );
161 continue;
162 }
163
164 activities.push(`:musical_note: Listening to **${a.name}**`);
165 } else if (a.type === ActivityType.Competing) {
166 activities.push(`:fire: Competing **${a.name}**`);
167 } else if (a.type === ActivityType.Playing) {
168 activities.push(`:video_game: Playing **${a.name}**`);
169 } else if (a.type === ActivityType.Streaming) {
170 activities.push(`:video_camera: Streaming **${a.name}**`);
171 } else if (a.type === ActivityType.Watching) {
172 activities.push(`:tv: Watching **${a.name}**`);
173 }
174 }
175 }
176
177 const allRoles = [...member!.roles.cache.values()]
178 .filter(role => role.id !== message.guildId!)
179 .sort((role1, role2) => {
180 return role2.position - role1.position;
181 });
182 const limit = 10;
183 const roles = (allRoles.length > limit ? allRoles.slice(0, limit) : allRoles)
184 .reduce((acc, value) => `${acc} ${roleMention(value.id)}`, "")!
185 .trim()!;
186 const statusText = getStatusText(this.client, member!);
187
188 const fields: APIEmbedField[] = [
189 {
190 name: "Nickname",
191 value: `${member!.nickname?.replace(/\*\<\>\@\_\~\|/g, "") ?? "*Nickname not set*"}`
192 },
193 {
194 name: "Account Created",
195 value: `${member!.user.createdAt.toLocaleDateString("en-US")} (${formatDistanceToNowStrict(
196 member!.user.createdTimestamp,
197 { addSuffix: true }
198 )})`,
199 inline: true
200 },
201 {
202 name: "Joined at",
203 value: `${member!.joinedAt!.toLocaleDateString("en-US")} (${formatDistanceToNowStrict(member!.joinedTimestamp!, {
204 addSuffix: true
205 })})`,
206 inline: true
207 },
208 {
209 name: "Active Devices",
210 value: `${statusText === "" ? `${this.emoji("invisible")} Offline/Invisible` : statusText}`
211 },
212 {
213 name: "Status",
214 value: `${activities.length === 0 ? "*No status set*" : activities.join("\n")}`
215 },
216 {
217 name: "Roles",
218 value:
219 roles === ""
220 ? "*No roles assigned*"
221 : `${roles} ${allRoles.length > limit ? `**+ ${allRoles.length - limit} More**` : ""}`
222 }
223 ];
224
225 const badges = getUserBadges(this.client, member!.user);
226
227 if (badges.length > 0) {
228 fields.push({
229 name: "Badges",
230 value: badges.join("\n")
231 });
232 }
233
234 let banner: string | undefined;
235
236 try {
237 await member?.user.fetch(true);
238 banner = member!.user!.bannerURL({ size: 4096, forceStatic: false }) ?? undefined;
239 } catch (e) {
240 logError(e);
241 }
242
243 log("Banner", banner, member!.user!.banner);
244
245 let permissionPercentage = 0;
246
247 if (this.client.configManager.config[message.guildId!]?.permissions.mode === "levels") {
248 permissionPercentage = this.client.permissionManager.getMemberPermissionLevel(member);
249 } else {
250 permissionPercentage = getPermissionLevel(member, false) as number;
251 }
252
253 await this.deferredReply(message, {
254 embeds: [
255 new EmbedBuilder({
256 image: banner
257 ? {
258 url: banner
259 }
260 : undefined
261 })
262 .setColor(member!.user!.hexAccentColor ? member!.user!.hexAccentColor! : "#007bff")
263 .setAuthor({
264 name: member?.user.tag!,
265 iconURL: member!.user.displayAvatarURL()
266 })
267 .setThumbnail(
268 member!.displayAvatarURL({
269 size: 4096,
270 forceStatic: false
271 })
272 )
273 .setFields(fields)
274 .setFooter({
275 text: `${member?.user.bot ? "Bot" : "User"} • ${member!.id} • Has ${permissionPercentage}% permissions`
276 })
277 ]
278 });
279 }
280 }

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26