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;
};