From 6717e96dfe7ff6ba476f56f9f3122980ecf34075 Mon Sep 17 00:00:00 2001 From: Cynthia Foxwell Date: Tue, 13 Aug 2024 23:14:21 -0600 Subject: [PATCH] add userinfo --- src/modules/utility/userinfo.js | 323 ++++++++++++++++++++++++++++++++ 1 file changed, 323 insertions(+) create mode 100644 src/modules/utility/userinfo.js diff --git a/src/modules/utility/userinfo.js b/src/modules/utility/userinfo.js new file mode 100644 index 0000000..0c9a308 --- /dev/null +++ b/src/modules/utility/userinfo.js @@ -0,0 +1,323 @@ +const Command = require("@lib/command.js"); + +const { + ActivityTypeNames, + APIEndpoints, + ApplicationFlags, + BadgeURLs, + CDNEndpoints, + UserFlags, +} = require("@util/dconstants.js"); +const { + RegExp: {Snowflake: SNOWFLAKE_REGEX}, + Icons, +} = require("@util/constants.js"); + +const {formatUsername, getDefaultAvatar} = require("@util/misc.js"); +const {snowflakeToTimestamp} = require("@util/time.js"); + +const ONE_MONTH = 2628000; + +const userinfo = new Command("userinfo"); +userinfo.category = "utility"; +userinfo.helpText = "Get information on a user"; +userinfo.usage = ""; +userinfo.addAlias("user"); +userinfo.addAlias("uinfo"); +userinfo.addAlias("ui"); +userinfo.addAlias("profile"); +userinfo.addAlias("whois"); +userinfo.callback = async function (msg, line) { + let id; + if (!line || line == "") { + id = msg.author.id; + } else { + if (!SNOWFLAKE_REGEX.test(line)) return "Not a snowflake."; + id = line.match(SNOWFLAKE_REGEX)[1]; + } + + if (!id) return "Failed to get ID somehow."; + + let user; + try { + user = await hf.bot.requestHandler.request("GET", APIEndpoints.USER(id), false); + } catch (err) { + if (err.code == 10013) { + return "User not found."; + } else { + return `${Icons.silk.error} Failed to get user:\n\`\`\`\n${err}\`\`\``; + } + } + + if (!user) return `${Icons.silk.error} Failed to get user without erroring.`; + + let guild, member; + if (msg.guildID) { + guild = msg.channel.guild || hf.bot.guilds.get(msg.guildID); + member = guild.members.get(id); + } + + // FIXME: horrible, probably needs to be moved out of this function for later + const badges = []; + + if ((user.flags & UserFlags.STAFF) !== 0) { + badges.push(`[${Icons.badges.staff}](${BadgeURLs.staff})`); + } + if ((user.flags & UserFlags.PARTNER) !== 0) { + badges.push(`[${Icons.badges.partner}](${BadgeURLs.partner})`); + } + if ((user.flags & UserFlags.CERTIFIED_MODERATOR) !== 0) { + badges.push(`[${Icons.badges.certified_moderator}](${BadgeURLs.certified_moderator})`); + } + if ((user.flags & UserFlags.HYPESQUAD) !== 0) { + badges.push(`[${Icons.badges.hypesquad}](${BadgeURLs.hypesquad})`); + } + if ((user.flags & UserFlags.HYPESQUAD_ONLINE_HOUSE_1) !== 0) { + badges.push(`[${Icons.badges.hypesquad_house_1}](${BadgeURLs.hypesquad_house_1})`); + } + if ((user.flags & UserFlags.HYPESQUAD_ONLINE_HOUSE_2) !== 0) { + badges.push(`[${Icons.badges.hypesquad_house_2}](${BadgeURLs.hypesquad_house_2})`); + } + if ((user.flags & UserFlags.HYPESQUAD_ONLINE_HOUSE_3) !== 0) { + badges.push(`[${Icons.badges.hypesquad_house_3}](${BadgeURLs.hypesquad_house_3})`); + } + if ((user.flags & UserFlags.BUG_HUNTER_LEVEL_1) !== 0) { + badges.push(`[${Icons.badges.bug_hunter_level_1}](${BadgeURLs.bug_hunter_level_1})`); + } + if ((user.flags & UserFlags.BUG_HUNTER_LEVEL_2) !== 0) { + badges.push(`[${Icons.badges.bug_hunter_level_2}](${BadgeURLs.bug_hunter_level_2})`); + } + if ((user.flags & UserFlags.ACTIVE_DEVELOPER) !== 0) { + badges.push(`[${Icons.badges.active_developer}](${BadgeURLs.active_developer})`); + } + if ((user.flags & UserFlags.VERIFIED_DEVELOPER) !== 0) { + badges.push(Icons.badges.verified_developer); + } + if ((user.flags & UserFlags.PREMIUM_EARLY_SUPPORTER) !== 0) { + badges.push(`[${Icons.badges.early_supporter}](${BadgeURLs.early_supporter})`); + } + if (user.banner || user.avatar.startsWith("a_")) { + badges.push(`[${Icons.badges.premium}](${BadgeURLs.premium})`); + } + + let boosting = member?.premiumSince; + let anyMember = member; + if (!boosting) { + for (const g of hf.bot.guilds.values()) { + const m = g.members.get(id); + if (!boosting && m?.premiumSince) { + boosting = m.premiumSince; + anyMember = m; + break; + } + } + } + if (!anyMember) { + for (const g of hf.bot.guilds.values()) { + const m = g.members.get(id); + if (m != null) { + anyMember = m; + break; + } + } + } + if (boosting) { + let icon = Icons.badges.guild_booster_lvl1; + const delta = Date.now() - Math.floor(boosting / 1000); + if (delta >= ONE_MONTH * 2) { + icon = Icons.badges.guild_booster_lvl2; + } + if (delta >= ONE_MONTH * 3) { + icon = Icons.badges.guild_booster_lvl3; + } + if (delta >= ONE_MONTH * 6) { + icon = Icons.badges.guild_booster_lvl4; + } + if (delta >= ONE_MONTH * 9) { + icon = Icons.badges.guild_booster_lvl5; + } + if (delta >= ONE_MONTH * 12) { + icon = Icons.badges.guild_booster_lvl6; + } + if (delta >= ONE_MONTH * 15) { + icon = Icons.badges.guild_booster_lvl7; + } + if (delta >= ONE_MONTH * 18) { + icon = Icons.badges.guild_booster_lvl8; + } + if (delta >= ONE_MONTH * 24) { + icon = Icons.badges.guild_booster_lvl9; + } + + badges.push(`[${icon}](${BadgeURLs.premium})`); + } + + let botDeleted = false; + if (user.bot) { + try { + const app = await hf.bot.requestHandler.request("GET", APIEndpoints.APPLICATION_RPC(id), false); + + if ((app.flags & ApplicationFlags.APPLICATION_COMMAND_BADGE) !== 0) { + badges.push(`[${Icons.badges.bot_commands}](${BadgeURLs.bot_commands})`); + } + if ((app.flags & ApplicationFlags.AUTO_MODERATION_RULE_CREATE_BADGE) !== 0) { + badges.push(Icons.badges.automod); + } + } catch (err) { + if (err.code == 10002) { + botDeleted = true; + } + } + } + + if (user.avatar_decoration_data?.expires_at || user.avatar_decoration_data?.sku_id == "1226939756617793606") { + badges.push(`[${Icons.badges.quest_completed}](${BadgeURLs.quest_completed})`); + } + + const defaultAvatar = getDefaultAvatar(id, user.discriminator ?? 0); + const avatar = user.avatar ? CDNEndpoints.USER_AVATAR(id, user.avatar) : defaultAvatar; + + const banner = user.banner && CDNEndpoints.BANNER(id, user.banner); + + const images = []; + + const avatarRes = await fetch(avatar, {method: "HEAD"}); + const avatarMod = avatarRes.headers.get("last-modified"); + let avatarUrl = `[Avatar](${avatar})`; + if (avatarMod && avatar != defaultAvatar) { + const modDate = Math.floor(new Date(avatarMod).getTime() / 1000); + avatarUrl += ` \u2022 Updated `; + } + images.push(avatarUrl); + + if (banner) { + const bannerRes = await fetch(banner, {method: "HEAD"}); + const bannerMod = bannerRes.headers.get("last-modified"); + let bannerUrl = `[Banner](${banner})`; + if (bannerMod) { + const modDate = Math.floor(new Date(bannerMod).getTime() / 1000); + bannerUrl += ` \u2022 Updated `; + } + images.push(bannerUrl); + } + + const guildImages = []; + if (member) { + const guildAvatar = member.avatar && CDNEndpoints.GUILD_MEMBER_AVATAR(msg.guildID, id, member.avatar); + if (guildAvatar) { + const avatarRes = await fetch(guildAvatar, {method: "HEAD"}); + const avatarMod = avatarRes.headers.get("last-modified"); + let avatarUrl = `[Guild Avatar](${guildAvatar})`; + if (avatarMod) { + const modDate = Math.floor(new Date(avatarMod).getTime() / 1000); + avatarUrl += ` \u2022 Updated `; + } + guildImages.push(avatarUrl); + } + + const memberObj = await hf.bot.requestHandler.request("GET", APIEndpoints.GUILD_MEMBER(msg.guildID, id), true); + const guildBanner = memberObj.banner && CDNEndpoints.GUILD_MEMBER_BANNER(msg.guildID, id, memberObj.banner); + if (guildBanner) { + const bannerRes = await fetch(guildBanner, {method: "HEAD"}); + const bannerMod = bannerRes.headers.get("last-modified"); + let bannerUrl = `[Guild Banner](${guildBanner})`; + if (bannerMod) { + const modDate = Math.floor(new Date(bannerMod).getTime() / 1000); + bannerUrl += ` \u2022 Updated `; + } + guildImages.push(bannerUrl); + } + } + + const shared = Array.from(hf.bot.guilds.values()).filter((g) => g.members.get(id) != null); + + const descLines = []; + if (badges.length > 0) { + descLines.push(badges.join("")); + } + if (botDeleted) { + descLines.push("*This bot's application has been deleted*"); + } + if (user.clan) { + // TODO: badge emojis via color aproximation of the default presets + descLines.push(`**${user.clan.tag}** (\`${user.clan.identity_guild_id}\`)`); + } + + if (anyMember) { + const icons = []; + for (const platform of Object.keys(anyMember.clientStatus)) { + const status = anyMember.clientStatus[platform]; + if (status == "offline") continue; + + icons.push(Icons.presence[platform][status]); + } + + descLines.push(""); + if (icons.length > 0) { + descLines.push(icons.join("")); + } + + for (const activity of anyMember.activities) { + if (activity.type == 4 || activity.type == 6) { + descLines.push(ActivityTypeNames[activity.type]); + } else { + descLines.push(`${ActivityTypeNames[activity.type]} **${activity.name}**`); + } + } + } + + const embed = { + color: user.accent_color, + thumbnail: { + url: avatar, + }, + image: { + url: banner, + }, + title: `${formatUsername(user)} ${user.bot ? Icons.boat : ""}`, + description: descLines.join("\n"), + fields: [ + { + name: "Created", + value: ``, + inline: true, + }, + member && { + name: "Joined", + value: ``, + inline: true, + }, + member?.nick && { + name: "Nickname", + value: member.nick, + inline: true, + }, + member?.roles?.length > 0 && { + name: "Roles", + value: member.roles + .sort((a, b) => guild.roles.get(b).position - guild.roles.get(a).position) + .map((role) => `<@&${role}>`) + .join(", "), + inline: true, + }, + shared.length > 0 && { + name: "Bot Mutual Servers", + value: `${shared.length} servers`, + inline: false, + }, + images.length > 0 && { + name: "\u200b", + value: images.join("\n"), + inline: true, + }, + guildImages.length > 0 && { + name: "\u200b", + value: guildImages.join("\n"), + inline: true, + }, + ].filter((x) => !!x), + }; + + return {embeds: [embed]}; +}; +hf.registerCommand(userinfo);