'use strict'; const { time } = require("discord.js"); const path = require('path'); const fs = require('fs'); const logger = require('../../utils/logger'); const env = process.env; async function fetch_avatar(id) { const res = await fetch(`https://thumbnails.roproxy.com/v1/users/avatar?userIds=${id}&size=420x420&format=Png&isCircular=false`, { method: 'GET', headers: { 'Content-Type': 'application/json' } }); const data = await res.json(); logger.debug(`[fetch_avatar] ${JSON.stringify(data)}`); if (data.data && data.data[0] && data.data[0].imageUrl) { logger.debug(`[fetch_avatar] fetched avatar for user id ${id}: ${data.data[0].imageUrl}`); return data.data[0].imageUrl; } else { return null; } } async function fetch_item_thumb(id) { const res = await fetch(`https://thumbnails.roproxy.com/v1/assets?assetIds=${id}&returnPolicy=PlaceHolder&size=420x420&format=Png&isCircular=false`, { method: 'GET', headers: { 'Content-Type': 'application/json' } }); const data = await res.json(); logger.debug(`[fetch_item_thumb] ${JSON.stringify(data)}`); if (data.data && data.data[0] && data.data[0].imageUrl) { logger.debug(`[fetch_item_thumb] fetched item thumbnail for item id ${id}: ${data.data[0].imageUrl}`); return data.data[0].imageUrl; } else { return null; } } function to_unix_timestamp(dateString) { const unixSeconds = Math.floor(new Date(dateString).getTime() / 1000); logger.debug(unixSeconds); return unixSeconds; } module.exports = { name: 'rbx', description: `various commands related to roblox`, aliases: ['roblox'], usage: 'rbx user \n.rbx item \n.rbx group \n.rbx game ', async execute(client, message) { var embed = {}; const args = message.content.split(' ').slice(1); if (args.length < 2) { return message.reply({ content: null, embeds: [{ color: 0x8aa2b3, author: { name: message.author.username, icon_url: message.author.displayAvatarURL(), }, description: '<:no:1481811955592466492> please provide a subcommand and an argument!', footer: { text: `twitter @ ${message.guild.name}`, icon_url: client.user.displayAvatarURL(), }, timestamp: new Date(), }] }); } const subcommand = args[0].toLowerCase(); const argument = args.slice(1).join(' '); let sent = await message.reply({ content: null, embeds: [{ color: 0x8aa2b3, title: '', author: { name: message.author.username, icon_url: message.author.displayAvatarURL(), }, description: ' fetching...', footer: { text: `twitter @ ${message.guild.name}`, icon_url: client.user.displayAvatarURL(), }, timestamp: new Date(), }] }); const res = await fetch(`https://users.roblox.com/v1/usernames/users`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${process.env.ROBLOSECURITY}` }, body: JSON.stringify({ usernames: [argument], excludeBannedUsers: false }) }); const data = await res.json(); logger.debug(`[rbx] fetched user data (roblox, standard) for "${argument}": ${JSON.stringify(data)}`); if (!data.data || !data.data[0]) { throw new Error(`user ${argument} not found...`); } const extra_res = await fetch(`https://users.roblox.com/v1/users/${data.data[0].id}`, { method: 'GET', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${process.env.ROBLOSECURITY}` } }); const extra_data = await extra_res.json(); logger.debug(`[rbx] fetched user data (roblox, extra) for "${argument}": ${JSON.stringify(extra_data)}`); const r_res = await fetch(`https://api.rolimons.com/players/v1/playerinfo/${data.data[0].id}`, { method: 'GET', headers: { 'Content-Type': 'application/json' } }); let r_data = {}; if (r_res.ok) { r_data = await r_res.json(); } else { logger.warn(`[rbx] Failed to fetch rolimons playerinfo for id ${data.data[0].id}: HTTP ${r_res.status}`); r_data = {}; } logger.debug(`[rbx] fetched user data (rolimons) for "${argument}": ${JSON.stringify(r_data)}`); const ra_res = await fetch(`https://api.rolimons.com/players/v1/playerassets/${data.data[0].id}`, { method: 'GET', headers: { 'Content-Type': 'application/json' } }); const ra_data = await ra_res.json(); logger.debug(`[rbx] fetched user data (rolimons, assets) for "${argument}": ${JSON.stringify(ra_data)}`); if (subcommand === 'user' || subcommand === 'u') { try { if (!data.data || !data.data[0]) { embed = { color: 0x8aa2b3, author: { name: message.author.username, icon_url: message.author.displayAvatarURL(), }, description: '<:no:1481811955592466492> could not find a roblox user with that username!', footer: { text: `twitter @ ${message.guild.name}`, icon_url: client.user.displayAvatarURL(), }, timestamp: new Date(), } } else { const user = data.data[0]; const avatarUrl = await fetch_avatar(user.id); const hasCheckmark = user.hasVerifiedBadge; const hasPremium = r_data.premium; const isOnline = ra_data.isOnline; const viewInvRes = await fetch(`https://inventory.roproxy.com/v1/users/${user.id}/can-view-inventory`); const isInvPublic = (await viewInvRes.json()).canView; const formatted_desc = (extra_data.description || "no description") .split('\n') .map(line => line.trim() ? `-# *${line}*` : '') .join('\n'); embed = { color: 0x8aa2b3, author: { name: message.author.username, icon_url: message.author.displayAvatarURL(), }, description: `**[${user.displayName}](https://www.roblox.com/users/${user.id}/profile)** [(${user.name})](https://www.rolimons.com/player/${user.id}) ${isOnline ? "<:online:1482657396362055826>" : "<:offline:1482657564650115073>"} ${hasPremium ? "<:premium:1482660040358760602>" : ""} ${hasCheckmark ? "<:Verified:1376786225486364823>" : ""}\n${formatted_desc}`, fields: [ { name: 'id', value: String(extra_data.id), inline: true }, { name: 'rap', value: String(r_data.rap), inline: true }, { name: 'value', value: String(r_data.value), inline: true }, { name: 'inventory', value: `${isInvPublic ? 'public' : 'private'}`, inline: true }, { name: 'terminated', value: String(r_data.terminated), inline: true }, { name: 'date created', value: ``, inline: true }, { name: 'last online', value: `${isOnline ? "now" : ""}`, inline: true }, ], thumbnail: { url: avatarUrl, height: 420, width: 420, }, footer: { text: `twitter @ ${message.guild.name}`, icon_url: client.user.displayAvatarURL(), }, timestamp: new Date(), }; } } catch (err) { logger.error(`[rbx] failed to fetch user data for "${argument}":`, err); embed = { color: 0x8aa2b3, author: { name: message.author.username, icon_url: message.author.displayAvatarURL(), }, description: `<:no:1481811955592466492> could not fetch data for user "${argument}"!`, footer: { text: `twitter @ ${message.guild.name}`, icon_url: client.user.displayAvatarURL(), }, timestamp: new Date(), } } } else if (subcommand === 'item') { try { const res = await fetch(`https://economy.roproxy.com/v2/assets/${argument}/details`, { method: 'GET', headers: { 'Content-Type': 'application/json', 'Cookie': `.ROBLOSECURITY=${process.env.ROBLOSECURITY}` } }); const p_res = await fetch(`https://thumbnails.roproxy.com/v1/assets?assetIds=${argument}&returnPolicy=PlaceHolder&size=420x420&format=Png&isCircular=false`, { method: 'GET', headers: { 'Content-Type': 'application/json', 'Cookie': `.ROBLOSECURITY=${process.env.ROBLOSECURITY}` } }); const resale_res = await fetch(`https://economy.roproxy.com/v1/assets/${argument}/resale-data`, { method: 'GET', headers: { 'Content-Type': 'application/json', 'Cookie': `.ROBLOSECURITY=${process.env.ROBLOSECURITY}` } }); const item = await res.json(); logger.debug(`[rbx] fetched item data (roblox) for "${argument}": ${JSON.stringify(item)}`); const resale_data = await resale_res.json(); logger.debug(`[rbx] fetched resale data for "${argument}": ${JSON.stringify(resale_data)}`); let itemsJson; try { const itemsPath = path.resolve(process.cwd(), 'items.json'); const itemsRaw = fs.readFileSync(itemsPath, 'utf8'); itemsJson = JSON.parse(itemsRaw); if (itemsJson.items && itemsJson.items[argument]) { logger.debug(`[rbx] found item ${argument} in items.json: ${JSON.stringify(itemsJson.items[argument])}`); } } catch (e) { logger.warn(`[rbx] could not read items.json: ${e.message}`); } let item_e = itemsJson.items[argument]; var hasAcronym = false; if (item_e[1] = "") { hasAcronym = false; } else { hasAcronym = true; } const item_thumb = await fetch_item_thumb(item.AssetId); const is_offsale = item.IsForSale; const is_limited = item.IsLimited; const is_limited_unique = item.IsLimitedUnique; const is_limited_at_all = is_limited || is_limited_unique; const limited_string = is_limited ? (is_limited_unique ? "true (unique)" : "true") : "false"; embed = { color: 0x8aa2b3, author: { name: message.author.username, icon_url: message.author.displayAvatarURL(), }, description: `[${item.Name}](https://roblox.com/catalog/${argument}) ${hasAcronym ? item_e[1] : ""}\n-# *${item.Description}*`, fields: [ { name: "id", value: `${item.AssetId}`, inline: true }, { name: "price", value: `${is_offsale ? "offsale" : item.PriceInRobux}`, inline: true }, { name: "limited", value: `${limited_string}`, inline: true }, { name: "rap", value: `${is_limited_at_all ? resale_data.recentAveragePrice : "0"}`, inline: true }, { name: "creator", value: `${item.Creator.Name}`, inline: true }, ], thumbnail: { url: item_thumb, height: 420, width: 420 }, footer: { text: `twitter @ ${message.guild.name}`, icon_url: client.user.displayAvatarURL(), }, timestamp: new Date(), }; } catch (err) { logger.error(`[rbx] failed to fetch item data for "${argument}":`, err); embed = { color: 0x8aa2b3, author: { name: message.author.username, icon_url: message.author.displayAvatarURL(), }, description: `<:no:1481811955592466492> could not fetch data for item "${argument}"!`, footer: { text: `twitter @ ${message.guild.name}`, icon_url: client.user.displayAvatarURL(), }, timestamp: new Date(), } } } else if (subcommand === 'group') { try { const res = await fetch(`https://groups.roproxy.com/v1/groups/${argument}`); const p_res = await fetch(`https://thumbnails.roproxy.com/v1/groups/icons?groupIds=${argument}&size=420x420&format=Png&isCircular=false`); if (!res.ok) throw new Error(); if (!p_res.ok) throw new Error(); const group = await res.json(); const p_json = await p_res.json(); logger.debug(`${JSON.stringify(p_json)}`); const thumb = (p_json && p_json.data && p_json.data[0]) ? p_json.data[0].imageUrl : null; embed = { color: 0x8aa2b3, author: { name: message.author.username, icon_url: message.author.displayAvatarURL(), }, description: `**[${group.name}](https://roblox.com/communities/${argument})**\n-# *${group.description || "no description"}*`, thumbnail: { url: thumb, height: 420, width: 420 }, fields: [ { name: "members", value: `${group.memberCount}`, inline: true }, { name: "owner", value: `${group.owner.username}`, inline: true }, { name: "shout", value: `${group.shout}`, inline: true }, { name: "open", value: `${group.publicEntryAllowed}`, inline: true }, { name: "verified", value: `${group.hasVerifiedBadge}`, inline: true }, ], footer: { text: `twitter @ ${message.guild.name}`, icon_url: client.user.displayAvatarURL(), }, timestamp: new Date(), }; } catch (err) { logger.error(`[rbx] failed to fetch group data for "${argument}":`, err); embed.title = 'Error'; embed.description = 'Failed to fetch group data.'; } } else if (subcommand === 'game') { try { const res = await fetch(`https://games.roproxy.com/v1/games?universeIds=${argument}`); if (!res.ok) throw new Error(); const data = await res.json(); if (!data.data || !data.data[0]) { embed.title = 'Game Not Found'; embed.description = `Could not find a Roblox game with the universe ID **${argument}**.`; } else { const game = data.data[0]; embed.title = `Roblox Game: ${game.name}`; embed.description = game.description || 'No description.'; embed.thumbnail.url = `https://www.roproxy.com/asset-thumbnail/image?assetId=${game.rootPlaceId}&width=420&height=420&format=png`; embed.fields = [ { name: 'Creator', value: game.creator.name, inline: true }, { name: 'Playing', value: game.playing.toLocaleString(), inline: true }, { name: 'Visits', value: game.visits.toLocaleString(), inline: true } ]; } } catch (err) { embed.title = 'Error'; embed.description = 'Failed to fetch game data.'; } }/* else { embed.title = 'invalid subcommand'; embed.description = 'Valid subcommands: `user`, `item`, `group`, `game`'; }*/ await sent.edit({ content: null, embeds: [embed] }); }, };