NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name girls battle strength scouter // @namespace https://openuserjs.org/users/fujimasa // @version 0.6.6 // @description show girls' popularity // @author fujimasa // @match https://www.cityheaven.net/* // @run-at document-end // @grant unsafeWindow // @grant GM_registerMenuCommand // @grant GM_setValue // @grant GM_getValue // @grant GM_listValues // @grant GM_openInTab // @grant GM_deleteValue // @grant GM_addStyle // @grant GM_getResourceText // @grant GM_getResourceURL // require https://cdnjs.cloudflare.com/ajax/libs/cash/8.1.0/cash.min.js // @require https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js // @require https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js // @resource jqueryui https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/themes/smoothness/jquery-ui.css // @resource spinner4 https://raw.githubusercontent.com/fujima3/4650a76e-fa4d-449a-9b9a-cc3b1eb0aac8/develop/style/spinner4.css?ver=9 // @copyright 2021, fujimasa (https://openuserjs.org/users/fujimasa) // @license MIT // @updateURL https://openuserjs.org/meta/fujimasa/girls_battle_strength_scouter.meta.js // @downloadURL https://openuserjs.org/install/fujimasa/girls_battle_strength_scouter.user.js // ==/UserScript== const $ = window.jQuery; $(function () { 'use strict' // Your code here... function addHeaderResource(resource) { $("<link>", { href: GM_getResourceURL(resource), rel: 'stylesheet', type: 'text/css' }) .appendTo(document.head) } function initializeDialog() { $("<div/>", { id: "dialog", style: "text-align:left", title: "May I open girls tab ?" }) .appendTo("body") } // enable jquery-ui addHeaderResource("jqueryui") addHeaderResource("orbitron") addHeaderResource("spinner4") initializeDialog() const PageType = { LIST: 1, GIRL: 2, NULL: -99 } const UNDEFINED = -1 const NULL = -2 // commonData = {baseUrl, pageType, shopId} const commonData = extractCommonData() function cleanByShopId() { GM_listValues().forEach(k => { if (k.startsWith(commonData.shopId)) GM_deleteValue(k) }) GM_deleteValue("undefined") console.debug("cleaned.") } /** * * @return object {baseUrl:String, pageType:PageType, shopId:String} */ function extractCommonData() { const data = { baseUrl: undefined, pageType: PageType.NULL, shopId: undefined } const [all, domain, region, group, detail, shop, page] = window.location.href.match(/(https:\/\/[^\/]+)\/([^\/]+)\/([^\/]+)\/([^\/]+)\/([^\/]+)\/([^\/]*)/); console.debug([all, domain, region, group, detail, shop, page]) data.baseUrl = domain + "/" + region + "/" + group + "/" + detail + "/" + shop switch (true) { case /girllist/.test(page): data.pageType = PageType.LIST break case /girlid-.+/.test(page): data.pageType = PageType.GIRL break default: // nop console.dir({ "page": page }) } data.shopId = unsafeWindow.dataLayer.filter(el => el.shop_id !== undefined)[0].shop_id return data } switch (commonData.pageType) { case PageType.LIST: commonCommand() listCommand() break case PageType.GIRL: commonCommand() // girlCommand() break case PageType.NULL: commonCommand() break } /** * save , restore girls' fav count * @constructor */ function FavRepository() { this.prototype = { /** * save fav count by constructor parameters. * @param shopId * @param girlId * @param name * @param fav */ save: function (shopId, girlId, name, fav) { const key = [shopId, girlId, name].join(".") console.dir(key) console.debug("key: " + key + " fav: " + fav) GM_setValue(key, fav) }, /** * returns fav count by constructor parameters. * @returns fav count */ findFav: function (shopId, girlId, name) { const key = [shopId, girlId, name].join(".") let v = GM_getValue(key) if (v === undefined) return UNDEFINED return v }, /** * list by shopId .<br/> * name = shopId.girlId.girlName * @returns [{name, fav}] */ listByShopId: function (shopId) { const names = GM_listValues() const dict = [] names.forEach(name => { if (name.startsWith(shopId)) { dict.push({ "name": name, "fav": Number(GM_getValue(name)) }) } }) return dict }, /** * list by shopId those sort fav ascending * @returns [{name, fav}] */ listByShopIdAsc: function (shopId) { const d = this.listByShopId(shopId) d.sort(function (a, b) { if (a.fav < b.fav) return 1 if (a.fav > b.fav) return -1 return 0 }) return d }, /** * list by shopId those sort fav descending * @returns [{name, fav}] */ listByShopIdDesc: function (shopId) { const d = this.listByShopId(shopId) d.sort((a, b) => { if (a.fav < b.fav) return -1 if (a.fav > b.fav) return 1 return 0 }) return d } } } function commonCommand() { GM_registerMenuCommand("parse girls", () => { window.location.href = commonData.baseUrl + "/girllist/" console.debug("moved.") }, "") } function listCommand() { registerMenu() const refs = extractDomElements() getGirlsFav(refs) function registerMenu() { GM_registerMenuCommand("clean shop data", cleanByShopId, "") // open strongest members GM_registerMenuCommand("open strongest", () => { $('#dialog p').remove() const range = [1, 2, 3, 4, 5] const dict = new FavRepository().prototype.listByShopIdAsc(commonData.shopId) const tops = [] Array.from(range, (v, k) => { const id = dict[k].name.split(".")[1] const name = dict[k].name.split(".")[2] tops.unshift(id) $("#dialog").append($("<p/>").text(id + ":" + name + " = " + dict[k].fav)) }) $("#dialog").dialog({ height: "auto", modal: false, position: { my: "center", at: "center", of: document }, buttons: { "OK": () => { tops.forEach(_gid => GM_openInTab(commonData.baseUrl + "/girlid-" + _gid + "/", "insert")) $(this).dialog("close"); } } }); }, "") // open weakest members GM_registerMenuCommand("open weakest", () => { $('#dialog p').remove() const range = [1, 2, 3, 4, 5] const dict = new FavRepository().prototype.listByShopIdDesc(commonData.shopId) const bottoms = [] Array.from(range, (v, k) => { const id = dict[k].name.split(".")[1] const name = dict[k].name.split(".")[2] bottoms.unshift(id) $("#dialog").append($("<p/>").text(id + ":" + name + " = " + dict[k].fav)) }) $("#dialog").dialog({ height: "auto", modal: false, position: { my: "center", at: "center", of: document }, buttons: { "OK": () => { bottoms.forEach(_gid => GM_openInTab(commonData.baseUrl + "/girlid-" + _gid + "/", "insert")) $(this).dialog("close") } } }) }, "") } /** * * @returns {{outs: [], hrefs: []}} */ function extractDomElements() { const parseTarget = [] parseTarget.push({ "link": "li > div.girllistimg a:nth-child(1)", "output": "li > div.girllistimg" }) parseTarget.push({ "link": "li > div.girl_img > a:nth-child(1)", "output": "li > div.girl_img" }) const refs = { "hrefs": [], "outs": [] } parseTarget.forEach(elm => { if (refs.hrefs.length === 0) { refs.hrefs = $(elm.link) refs.outs = $(elm.output) } }) return refs } function initGirlsFav(outputElement) { outputElement.prepend($('<div/>', { class: "loader4", text: "" })) } function putGirlsFav(outputElement, cnt) { $(".loader4").remove() outputElement.prepend(cnt) } function getGirlsFav(ref) { ref.hrefs.each((idx, linkUrl) => { const girlData = linkUrl.href.match(/girlid-([0-9]+)/) if (girlData === undefined) { console.warn("[" + idx + "] undefined girls' data: " + linkUrl) return } const shopId = commonData.shopId const girlId = girlData[1] const girlName = linkUrl.children[0].alt // TODO: It's not always exist img under anchor. initGirlsFav($(refs.outs[idx])) // sync let cnt = new FavRepository().prototype.findFav(shopId, girlId, girlName) if (cnt === UNDEFINED) { $.ajax({ type: "GET", url: "https://www.cityheaven.net/api/myheaven/v1/getgirlfavcnt/", cache: false, async: true, data: { "commu_id": shopId, "girl_id": girlId } }).then( response => { // async const c = Number(response.cnt) if (c !== undefined && c > 0) new FavRepository().prototype.save(shopId, girlId, girlName, c) putGirlsFav($(refs.outs[idx]), c) }, error => { console.error(error) } ) } else { putGirlsFav($(refs.outs[idx]), cnt) } }) } } // --debug //console.dir(dict) })