-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit d3ac55f
Showing
6 changed files
with
362 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
.env | ||
package-lock.json | ||
node_modules/ | ||
public/logs.txt |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
export default { | ||
users: { | ||
3887932258: { | ||
name: "luucc_ss", | ||
displayName: "lulu", | ||
preDisplay: "a", | ||
} | ||
/* | ||
99060139: { | ||
name: "JuliaMinegirl", | ||
displayName: "julia", | ||
preDisplay: "a" | ||
}, | ||
99451656: { | ||
name: "Crisminegirl", | ||
displayName: "cris", | ||
preDisplay: "a" | ||
}, | ||
104607645: { | ||
name: "TexWillerHS", | ||
displayName: "tex", | ||
preDisplay: "o" | ||
},*/ | ||
}, | ||
discord: { | ||
status: "JuliaMinegirl 🎀", | ||
name: "rbxuserspy", | ||
displayName: "rbx user spy :3", | ||
description: "stalkeando a julia minegirl", | ||
vcStatusId: "1296162311308054528", | ||
updatesTcId: "1296158553702400040" | ||
}, | ||
port: 80 | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
import config from "./config.js"; | ||
import fs from "node:fs"; | ||
import axios from "axios"; | ||
import dotenv from "dotenv"; | ||
import express from "express"; | ||
import { Client, GatewayIntentBits, ActivityType, ButtonBuilder, ButtonStyle, ActionRowBuilder } from "discord.js"; | ||
dotenv.config(); | ||
const app = express(); | ||
app.use(express.static("public")); | ||
let sessionInfo = { checks: 0, users: {}, erd: 0, efd: 0, esm: 0, startTime: new Date().toISOString(), nextCheck: "" }; | ||
let tc; | ||
Object.keys(config.users).forEach((user) => { | ||
sessionInfo.users[user] = { | ||
lastStatus: -1, | ||
lastStatusBegin: "", | ||
lastLocation: "", | ||
placeId: null, | ||
gameId: null, | ||
status: 0 | ||
}; | ||
}); | ||
async function log(data) { | ||
return fs.appendFileSync("public/logs.txt", `[${new Date().toISOString()}] ${data}\n`); | ||
}; | ||
const send = async (c) => await tc.send(c).catch((err) => { | ||
sessionInfo.esm += 1; | ||
log(`❌ Line 19: Error sending message: ${error}`); | ||
}); | ||
function timeSince(isostr) { | ||
const timestamp = new Date(isostr).getTime(); | ||
const now = new Date().getTime(); | ||
const diff = now - timestamp; | ||
const hours = Math.floor(diff / (1000 * 60 * 60)); | ||
const minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60)); | ||
const seconds = Math.floor((diff % (1000 * 60)) / 1000); | ||
let parts = []; | ||
if (hours && hours > 0) parts.push(`${hours} hora${hours != 1 ? "s" : ""}`); | ||
if (minutes && minutes > 0) parts.push(`${minutes} minuto${minutes != 1 ? "s" : ""}`); | ||
if (seconds && seconds > 0) parts.push(`${seconds} segundo${seconds != 1 ? "s" : ""}`); | ||
return parts.length > 0 ? parts.join(", ") : "agora"; | ||
}; | ||
app.get("/info", (_, res) => { | ||
res.json(sessionInfo); | ||
}); | ||
app.get("/config", (_, res) => { | ||
res.json(config); | ||
}); | ||
app.get("/check", async function (_, res) { | ||
await check(true); | ||
res.json(sessionInfo); | ||
}); | ||
app.get("/user", (req, res) => { | ||
const response = config.users[req.query.id]; | ||
response ? res.json(response) : res.sendStatus(404); | ||
}); | ||
const statusEmoji = ['⚫', '🔵', '🟢', '🟠', '❔']; | ||
const statusText = ['offline', 'online', 'jogando', 'no studio', 'invisível']; | ||
async function check(individual) { | ||
await axios.post("https://presence.roblox.com/v1/presence/users", { "userIds": Object.keys(sessionInfo.users) }, { | ||
headers: { | ||
"accept": "application/json", | ||
"Content-Type": "application/json", | ||
"Cookie": process.env.cookie | ||
}, withCredentials: true | ||
}) | ||
.then(function (response) { | ||
if (response.data["userPresences"] && response.data.userPresences.length > 0) { | ||
response.data.userPresences.forEach((presence) => { | ||
console.log(presence); | ||
const { userPresenceType, lastLocation, placeId, gameId, userId } = presence; | ||
if (userPresenceType != sessionInfo.users[presence.userId].status || presence.gameId != sessionInfo.users[presence.userId].gameId) { | ||
sessionInfo.users[userId].lastStatus = sessionInfo.users[userId].status; | ||
sessionInfo.users[userId].status = userPresenceType; | ||
sessionInfo.users[userId].placeId = placeId; | ||
sessionInfo.users[userId].gameId = gameId; | ||
if (presence.userPresenceType === 2 && placeId && gameId) { | ||
const button = new ButtonBuilder() | ||
.setLabel('entrar') | ||
.setURL(`https://deepblox.onrender.com/experiences/start?placeId=${placeId}&gameInstanceId=${gameId}`) | ||
.setStyle(ButtonStyle.Link); | ||
const row = new ActionRowBuilder() | ||
.addComponents(button); | ||
send({ | ||
content: `\`🟢\` **[${config.users[userId].preDisplay} ${config.users[userId].displayName}](<https://www.roblox.com/users/${userId}/profile>)** está jogando [${lastLocation}](https://www.roblox.com/games/${placeId})${sessionInfo.users[userId].lastStatus > 0 ? `\n-# ficou ${statusText[sessionInfo.users[userId].lastStatus]}${sessionInfo.users[userId].lastStatus === 2 ? " " + sessionInfo.users[userId].lastLocation : ""} por ${timeSince(sessionInfo.users[userId].lastStatusBegin)}` : ""}`, | ||
components: [row] | ||
}); | ||
} else { | ||
send(`\`${statusEmoji[userPresenceType]}\` **[${config.users[userId].preDisplay} ${config.users[userId].displayName}](<https://www.roblox.com/users/${userId}/profile>)** está ${statusText[userPresenceType]}${sessionInfo.users[userId].lastStatus > 0 ? `\n-# ficou ${statusText[sessionInfo.users[userId].lastStatus]}${sessionInfo.users[userId].lastStatus === 2 ? " " + sessionInfo.users[userId].lastLocation : ""} por ${timeSince(sessionInfo.users[userId].lastStatusBegin)}` : ""}`); | ||
}; | ||
sessionInfo.users[userId].lastLocation = lastLocation; | ||
sessionInfo.users[presence.userId].lastStatusBegin = new Date().toISOString(); | ||
}; | ||
}); | ||
} else { | ||
log(`❌ Line 214: Error reading data: ${response.data}`); | ||
}; | ||
}) | ||
.catch(function (error) { | ||
sessionInfo.efd += 1; | ||
log(`❌ Line 219: Error fetching data: ${error}`); | ||
}); | ||
sessionInfo.checks += 1; | ||
if (!individual) sessionInfo.nextCheck = new Date(new Date().getTime() + 30000).toISOString(); | ||
}; | ||
const client = new Client({ intents: [GatewayIntentBits.Guilds] }); | ||
|
||
const changeName = (n, c) => { if (c.name != n) return c.setName(n); }; | ||
client.on('ready', async function () { | ||
tc = await client.channels.fetch(config.discord.updatesTcId); | ||
const vc = await client.channels.fetch(config.discord.vcStatusId); | ||
await changeName("bot: online 🟢", vc); | ||
client.user.setPresence({ | ||
activities: [{ | ||
name: config.discord.status, | ||
type: ActivityType.Watching | ||
}], | ||
status: 'online' | ||
}); | ||
check(); | ||
setInterval(check, 30000); | ||
app.listen(config.port, function () { | ||
console.log("✅ http://localhost:" + config.port); | ||
}); | ||
log("🟢 Online"); | ||
for (let evt of ['SIGTERM', 'SIGINT', 'SIGHUP']) { | ||
process.on(evt, async function () { | ||
process.stdin.resume(); | ||
await changeName("bot: offline 🔴", vc); | ||
await log("🔴 Offline"); | ||
process.exit(); | ||
}); | ||
}; | ||
}); | ||
client.login(process.env.token); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
{ | ||
"name": "rbxuserspy", | ||
"version": "1.0.0", | ||
"main": "index.js", | ||
"type": "module", | ||
"scripts": { | ||
"start": "node ." | ||
}, | ||
"dependencies": { | ||
"axios": "^1.7.7", | ||
"discord.js": "^14.16.3", | ||
"dotenv": "^16.4.5", | ||
"express": "^4.21.1" | ||
} | ||
} |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,175 @@ | ||
<!DOCTYPE html> | ||
<html> | ||
<head> | ||
<meta charset="UTF-8"> | ||
<meta name="viewport" content="width=device-width, initial-scale=0.65"> | ||
<title>rbxuserspy by luluwaffless</title> | ||
<script src="https://unpkg.com/twemoji@latest/dist/twemoji.min.js" crossorigin="anonymous"></script> | ||
<script src="https://code.jquery.com/jquery-3.7.1.js" integrity="sha256-eKhayi8LEQwp4NKxN+CfCh+3qOVUtJn3QNZ0TciWLP4=" crossorigin="anonymous"></script> | ||
<style type="text/css"> | ||
body { | ||
font-size: large; | ||
text-align: center; | ||
color: rgb(237, 237, 249); | ||
background-color: hsl(333, 77%, 5%); | ||
font-family: monospace; | ||
} | ||
.emoji { | ||
width: 1em; | ||
height: 1em; | ||
vertical-align: middle; | ||
} | ||
.other { | ||
color: #808080; | ||
} | ||
a { | ||
color: #ffffff; | ||
} | ||
#container { | ||
left: 50%; | ||
top: 50%; | ||
transform: translate(-50%, -50%); | ||
position: absolute; | ||
border-radius: 10px; | ||
padding: 10px; | ||
border: 1px solid rgba(255, 255, 255, 0.2); | ||
background-color: hsl(333, 77%, 10%); | ||
box-shadow: 0px 0px 10px 5px rgba(10, 10, 20, 0.2); | ||
} | ||
.blue { | ||
color: rgb(128, 128, 255); | ||
} | ||
.green { | ||
color: rgb(128, 255, 128); | ||
} | ||
.red { | ||
color: rgb(255, 128, 128); | ||
} | ||
.yellow { | ||
color: rgb(255, 255, 128); | ||
} | ||
.small { | ||
font-size: small; | ||
color: rgb(109, 109, 121); | ||
padding: 0; | ||
margin: 0; | ||
} | ||
#grid-container { | ||
display: flex; /* Switch to flexbox */ | ||
gap: 10px; /* Add gap between items */ | ||
} | ||
.item { | ||
flex: 1 1 0; /* Ensure all items have equal width */ | ||
padding: 10px; | ||
border: 1px solid rgba(255, 255, 255, 0.2); | ||
border-radius: 10px; | ||
background-color: hsl(333, 77%, 15%); | ||
display: flex; | ||
align-items: center; | ||
justify-content: center; | ||
white-space: nowrap; | ||
} | ||
.list { | ||
text-align: left; | ||
} | ||
.center-item { | ||
grid-column: span 2; | ||
justify-self: center; | ||
} | ||
</style> | ||
<link rel="icon" type="image/x-icon" href="icon.png"> | ||
</head> | ||
<body> | ||
<div id="container" class="containerOffline"> | ||
<div style="padding: 10px;"> | ||
<a href="#">home</a> <a class="other" href="/logs.txt">logs</a> <a class="other" href="/config">config</a><br><br> | ||
</div> | ||
<div class="title"> | ||
<span style="font-weight: bolder; font-size: xx-large; color: #ffffff;">rbxuserspy</span><br><span style="font-size: small; color: #808080;">by luluwaffless</span> | ||
</div><br> | ||
<div id="grid-container"> | ||
<div class="item"> | ||
<span>👥 <a href="#" onclick="$.ajax({url:'/check'})">Users</a>:<div class="list"><br>🔎 Checks: <span class="blue" id="checks"></span><br>👥 Users:<br><span id="users"></span><br>⌛ Next Check: <span id="nextCheck" class="yellow"></span></div></span> | ||
</div> | ||
<div class="item center-item"> | ||
<span>❌ Errors:<div class="list"><br>📖 Read: <span class="red" id="erd"></span><br>🌐 Fetch: <span class="red" id="efd"></span><br>💬 Message: <span class="red" id="esm"></span></div></span> | ||
</div> | ||
</div> | ||
<div style="padding: 10px;"> | ||
<span class="small" >⏱️ Runtime: <span id="runtime"></span></span> | ||
</div> | ||
</div> | ||
</body> | ||
<script type="text/javascript"> | ||
twemoji.parse( | ||
document.body, | ||
{ base: 'https://cdn.jsdelivr.net/gh/twitter/[email protected]/assets/' } | ||
); | ||
</script> | ||
<script type="text/javascript"> | ||
const info = {checks: document.getElementById("checks"), users: document.getElementById("users"), erd: document.getElementById("erd"), efd: document.getElementById("efd"), esm: document.getElementById("esm"), runtime: document.getElementById("runtime"), nextCheck: document.getElementById("nextCheck")}; | ||
function timeSince(timestamp) { | ||
const now = new Date().getTime(); | ||
const diff = now - timestamp; | ||
const hours = Math.floor(diff / (1000 * 60 * 60)); | ||
const minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60)); | ||
const seconds = Math.floor((diff % (1000 * 60)) / 1000); | ||
let parts = []; | ||
if (hours) parts.push(hours); | ||
if (minutes || hours) parts.push(minutes.toString().padStart(hours ? 2 : 1, '0')); | ||
parts.push(seconds.toString().padStart(minutes || hours ? 2 : 1, '0')); | ||
return parts.join(':'); | ||
}; | ||
function timeUntil(timestamp) { | ||
const now = new Date().getTime(); | ||
const diff = timestamp - now; | ||
if (diff < 1000) return 'checking...'; | ||
const hours = Math.floor(diff / (1000 * 60 * 60)); | ||
const minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60)); | ||
const seconds = Math.floor((diff % (1000 * 60)) / 1000); | ||
let parts = []; | ||
if (hours) parts.push(hours); | ||
if (minutes || hours) parts.push(minutes.toString().padStart(hours ? 2 : 1, '0')); | ||
parts.push(seconds.toString().padStart(minutes || hours ? 2 : 1, '0')); | ||
return parts.join(':'); | ||
}; | ||
|
||
$.ajax("/config", { | ||
success: function(data) { | ||
let innerHTML = []; | ||
Object.keys(data.users).forEach(function(id) { | ||
const user = data.users[id]; | ||
innerHTML.push(`- <a href="https://www.roblox.com/users/${id}/profile">${user.name}</a>: <a id="${id}" class="green"></a>`); | ||
}); | ||
info.users.innerHTML = innerHTML.join("<br>"); | ||
} | ||
}); | ||
function main() { | ||
const statusText = ['Offline', 'Online', 'Playing', 'In Studio', 'Invisible']; | ||
$.ajax("/info", { | ||
success: function(data) { | ||
const nextCheck = new Date(data.nextCheck).getTime(); | ||
const runtimeDate = new Date(data.startTime).getTime(); | ||
info.checks.innerHTML = data.checks; | ||
info.erd.innerHTML = data.erd; | ||
info.efd.innerHTML = data.efd; | ||
info.esm.innerHTML = data.esm; | ||
Object.keys(data.users).forEach(function(id) { | ||
const user = data.users[id]; | ||
const element = document.getElementById(id); | ||
if (user.status == 2 && user.lastLocation && user.placeId && user.gameId) { | ||
element.innerHTML = `Playing ${user.lastLocation}`; | ||
element.href = `roblox://experiences/start?placeId=${user.placeId}&gameInstanceId=${user.gameId}`; | ||
} else { | ||
element.innerHTML = statusText[user.status]; | ||
}; | ||
}); | ||
info.runtime.innerHTML = timeSince(runtimeDate); | ||
info.nextCheck.innerHTML = timeUntil(nextCheck); | ||
} | ||
}); | ||
setTimeout(main, 1000); | ||
}; | ||
main(); | ||
</script> | ||
</html> |