NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name Muffin for Ukrgo // @namespace nincognito // @version 1.0.0 // @description muffin.net profile reference // @author you // @copyright 2020, nincognito (https://openuserjs.org/users/nincognito) // @license NASA-1.3 // @require https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.18.2/babel.js // @require https://cdnjs.cloudflare.com/ajax/libs/babel-polyfill/6.16.0/polyfill.js // @require https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js // @match kiev.ukrgo.com/* // @grant GM_xmlhttpRequest // @connect keksik.net // ==/UserScript== /* globals $ */ ////// CONFIG ///// let requestIntervalMs = 900; // for less then 800ms server blocks with 503 let muffinCorsFetch = false; let DEBUG = false; let limitProfiles = 0*DEBUG; // total 56 on single page, 0 - no limit if (DEBUG === undefined || !DEBUG) console.log = function(){}; ////// MAIN ////// if (/\/post_\d+/.test(window.location.pathname)) { // Profile URL let click_wait = 1000; // less than 750ms may cause fallstart parsing console.log("click \"Показать\" and wait %s sec", click_wait/1000); $("#post-phones-show-div > input").click(); // click then wait for chnages to take effect // ugly workaround with delayed processing, todo MutationObserver https://stackoverflow.com/a/16726669/1294414 setTimeout(function(){ let phoneNumberEl = $("#post-phones-show-div > .post-contacts > span") let phoneNumber = phoneNumberEl.text().trim(); phoneNumberEl.after(" ", createMuffinAnchorTag(phoneNumber)); console.log("Phone number: %s", phoneNumber); }, click_wait); } else if (window.location.pathname === "/view_subsection.php" && window.location.search.includes("id_subsection=146")) { // Boardlist URL subsection_id=146 console.log("Subsection page: %s", window.location.search); let headers = $("div.post, .main-content h3"); // banners 3+3, main content 50 let numberOfHeaders = headers.length; // ajax requests in intervals let iid = setInterval(() => { if (headers.length - limitProfiles) { let header = $(headers.splice(0, 1)[0]); let profileUrl = header.find(".link_post").get(0).href; let count = numberOfHeaders - headers.length; console.log("Fetch profile #%s: %s", count, profileUrl); // fetch profile page $.ajax({ url: profileUrl, method: "GET", timeout: 5000, dataType: "html", _header: header, _count: count, error: ajaxErrorHandler(), success: function(htmlData, status, xhr) { let postId, postHash; let onclickHandler = $(htmlData).find(".post-contacts input").prop("onclick"); if (!onclickHandler) { console.error("#%s no click handler parsed", this._count); this._header.append("📵"); return true; } let parsedHandlerArgs = String(onclickHandler).match(/showPhonesWithDigits\(['"](\d+)['"].*['"](\w+)['"]/i); [postId, postHash] = parsedHandlerArgs.slice(1, 3); console.log("#%s parsed id %s, hash %s", this._count, postId, postHash); // fetch hidden phone number $.ajax({ method: "POST", url: "/moduls/showphonesnumbers.php", data: `i=${postId}&s=${postHash}`, dataType: "html", _header: this._header, _count: this._count, error: ajaxErrorHandler(), success: function (htmlData, textStatus, jqXHR) { if (htmlData) { let phoneNumber = validatePhoneNumber($(htmlData).find("span").text()); console.log("#%s '%s' fetched number: %s", this._count, this.url, phoneNumber); this._header.append(createMuffinAnchorTag(phoneNumber)); if (muffinCorsFetch) { muffinCors(phoneNumber, {header: this._header}); } } else { console.error("#%s: no data returend from POST %s, data=%s", this._count, this.url, this.data); } } }); } }); } else { // empty anchors array clearInterval(iid); } }, requestIntervalMs); } else { console.log("Not 'Uslugi' URL: %s", window.location.href); } //////////////// UTILITIES ////////////////////// function getMuffinUrl(phone) { return "https://keksik.net/phone/" + phone; } function createMuffinAnchorTag(phone) { return $("<a>").attr({ href: getMuffinUrl(phone), target: "_blank" }).text("🧁"); } function createStatusLabelTag(msg) { return $("<sub>").css({ "font-weight": 100 }).text(msg); } function ajaxErrorHandler() { return function(xhr, status, exception) { console.error("Request failed with '%s' (%s): %s %s %s", status, exception, this.method, this.url, this.data ? `, data ${this.data}` : ""); let errorIcon = null; switch (status) { case "error": errorIcon = "🛑"; break; case "timeout": errorIcon = "⏳"; break; default: errorIcon = "❌"; }; this._header.append(errorIcon); if (DEBUG) this._header.append(" ", createStatusLabelTag(exception)); }; } function muffinCors(phone, context) { GM_xmlhttpRequest({ // ajax CORS method: "GET", url: getMuffinUrl(phone), context: context, onload: function(response) { console.log("Muffin response: %s %s", response.status, response.statusText); response.context.header.append(response.status == 200 ? "✓": "✗"); if (DEBUG) { response.context.header.append(createStatusLabelTag(response.status)); } }, onerror: function(response) { console.error("Muffin CORS error %s %s: %s", response.status, response.statusText, this.url); } }); }; function isPhoneNumberValid(numberStr) { return /^0\d{9}$/.test(numberStr); } // 0xx44442211 function validatePhoneNumber(rawNumber){ if (isPhoneNumberValid(rawNumber)) { return rawNumber; } let phoneNumber = null; // missed zero, e.g. 671234455 if (rawNumber.length === 9) { phoneNumber = "0" + rawNumber; } // leading zeros, e.g. 000991234455 else if (rawNumber.length > 10 && rawNumber.slice(0, rawNumber.length-10).split("").every(e => e === "0")) { phoneNumber = rawNumber.slice(rawNumber.length - 10); } // leading 38, e.g. 380991234455 else if (rawNumber.length === 12 && rawNumber.slice(0, 2) === "38") { phoneNumber = rawNumber.slice(2); } // multiple numbers, e.g. 0981112233; 0674455666 else if (rawNumber.includes(";")) { phoneNumber = rawNumber.split(";")[0]; } else { console.error("invalid number %s, returning as-is", rawNumber); return rawNumber; } console.log("--- validator corrected from %s to %s", rawNumber, phoneNumber); return phoneNumber; };