Camamba User

User represents a camamba User, UserSearch represents a search-request for one or many users

目前为 2020-06-19 提交的版本,查看 最新版本

此脚本不应直接安装,它是供其他脚本使用的外部库。如果你需要使用该库,请在脚本元属性加入:// @require https://update.gf.qytechs.cn/scripts/405699/818213/Camamba%20User.js

// ==UserScript==
// @name         Camamba User
// @namespace    dannysaurus.camamba
//
// @include      http://www.camamba.com/*
// @include      https://www.camamba.com/*
// @include      http://www.de.camamba.com/*
// @include      https://www.de.camamba.com/*
//
// @connect      camamba.com
// @grant        GM_xmlhttpRequest
//
// @require      https://gf.qytechs.cn/scripts/405143-simplecache/code/SimpleCache.js
// @require      https://gf.qytechs.cn/scripts/405144-httprequest/code/HttpRequest.js
// @require      https://gf.qytechs.cn/scripts/391854-enum/code/Enum.js
//
// @require      https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js
//
// @version      0.1
// @license      MIT License
// @description  User represents a camamba User, UserSearch represents a search-request for one or many users
// @author       Gerrit Höhle
// ==/UserScript==

/* jslint esnext: true */
/* globals SimpleCache, HttpRequest, HttpRequestHtml, Enum, $, jQuery */
const User = (() => {
    'use strict';
    const GENDERS = class GENDER extends Enum {}.init({ ANY: 'any' , FEMALE: 'female' , MALE: 'male', COUPLE: 'couple' });

    return class User {
        constructor({ uid, nick, age, gender, location, distanceKm, latitudeDec, longitudeDec, isOnline, lastSeen, room, roomId, isReal, isPremium, isSuper, userLevel, timeStamp = new Date() } = {}) {
            Object.assign(this, { uid, nick, age, _gender: gender, location, distanceKm, latitudeDec, longitudeDec, isOnline, lastSeen, room, roomId, isReal, isPremium, isSuper, userLevel, timeStamp });
        }

        get gender() {
            return this._gender instanceof GENDERS ? this._gender : GENDERS.ANY;
        }

        set gender(value) {
            this._gender = value;
        }

        static get GENDERS() {
            return GENDERS;
        }
    };
})();

const UserSearch = (() => {
    const UserController = (() => {
        const GENDERS = User.GENDERS;
    
        const getTextNodeValues = node => {
            if (node.nodeType === Node.TEXT_NODE) {
                const nodeValue = node.nodeValue.replace(/"/,'');
                return (/\S/.test(nodeValue)) ? [ nodeValue ] : [];
            }
            return Array.from(node.childNodes).flatMap(node => getTextNodeValues(node));
        };
    
        const fetchJsLinks = (html, user) => {
            const jsLinks = Object.assign( {}, ...[ 'openProfile', 'sendMail', 'openChat', 'openMap' ].map(fncName => {
                const linkElement = $(html).find(`a[href*="javascript:${fncName}("]`);
                if (linkElement.length > 0) {
                    const href = linkElement.attr('href') || '';
                    const fncParam = ((href.match(/\((.+)\)/) || [])[1] || '').replace(/['"]/g,'');
                    const innerHtml = (linkElement.html() || '').trim();
    
                    return { [fncName]: { href, fncParam, html: innerHtml } };
                }
            }));
    
            if (typeof jsLinks.sendMail !== 'undefined') {
                user.uid = Number.parseInt(jsLinks.sendMail.fncParam);
            }
    
            if (typeof jsLinks.openProfile !== 'undefined') {
                user.nick = jsLinks.openProfile.fncParam;
            }
    
            if (typeof jsLinks.openChat !== 'undefined') {
                user.roomId = jsLinks.openChat.fncParam;
                user.room = jsLinks.openChat.html;
            }
    
            if (typeof jsLinks.openMap !== 'undefined') {
                const matchCoords = jsLinks.openMap.fncParam.match(/(-?\d{1,3}\.\d{8}),(-?\d{1,2}\.\d{8})/);
                if (matchCoords !== null) {
                    user.longitudeDec = Number.parseFloat(matchCoords[1]);
                    user.latitudeDec = Number.parseFloat(matchCoords[2]);
                }
                user.location = jsLinks.openMap.html;
            }
        };
    
        const fetchTextNodes = (html, user) => {
            const textNodes = getTextNodeValues(html);
    
            for (let gender of [ GENDERS.FEMALE, GENDERS.MALE, GENDERS.COUPLE ].map(enm => enm.text)) {
                const match = textNodes[1].match(new RegExp(`^\\W*\(${gender}\),\\s\(\\d{1,3}\)`));
                if (match !== null) {
    
                    user.age = Number.parseInt(match[2]);
                    user.gender = match[1];
                    break;
                }
            }
    
            const matches = textNodes[2].match(/Online\s((\d{1,4})\s(minutes|hours|days)\s)?(ago|now|never)/);
            if (matches !== null) {
                const term = matches[4];
                if (term === 'never') {
                    user.lastSeen = new Date(0);
                    user.isOnline = false;
                } else {
                    const timeStampShort = new Date(user.timeStamp.getFullYear(), user.timeStamp.getMonth(), user.timeStamp.getDate(), user.timeStamp.getHours(), user.timeStamp.getMinutes());
    
                    if (term === 'ago') {
                        user.lastSeen = timeStampShort;
                        user.isOnline = false;
    
                        const value = Number.parseInt(matches[2]);
                        const unit = matches[3];
                        switch (unit) {
                            case 'minutes':
                                user.lastSeen.setMinutes(user.lastSeen.getMinutes() - value);
                                break;
                            case 'hours':
                                user.lastSeen.setMinutes(0);
                                user.lastSeen.setHours(user.lastSeen.getHours() - value);
                                break;
                            case 'days':
                                user.lastSeen.setMinutes(0);
                                user.lastSeen.setHours(0);
                                user.lastSeen.setDate(user.lastSeen.getDate() - value);
                                break;
                        }
                    } else if (term === 'now') {
                        user.lastSeen = timeStampShort;
                        user.isOnline = true;
                    }
                }
            }
    
            for (let txt of textNodes.slice(2)) {
                const distanceMatch = txt.match(/(\d{0,2},?\d{1,3})\skm\sfrom\syou/);
                if (distanceMatch !== null) {
    
                    const distanceKm = Number.parseInt(distanceMatch[1].replace(/\D/g, ''));
                    if (Number.isInteger(distanceKm)) {
    
                        user.distanceKm = distanceKm;
                    }
                }
            }
        };
    
        const fetchImages = (html, user) => {
            const userData = {};
            let foundImage = false;
    
            for (let [property, imageName] of Object.entries({ isPremium: 'premium.png', isSuper: 'super_premium.png', isReal: 'real.png' })) {
                userData[property] = $(html).find(`img[src$='/gfx/${imageName}']`).length > 0;
                foundImage |= userData[property];
            }
    
            if (foundImage) {
                Object.assign(user, userData);
            }
        };
    
        const fetchCustom = (html, user) => {
            const embedRankSrc = $(html).find("embed[src^='/levelRank.swf?displayRank']").attr('src') || '';
            const match = embedRankSrc.match(/displayRank=(\d{1,3})/);
            if (match !== null) {
                user.userLevel = Number.parseInt(match[1]);
            }
        };
    
        return class UserController {
            constructor(user = new User()) {
                this.user = user;
            }
    
            parseHtml(html = '') {
                fetchJsLinks(html, this.user);
                fetchTextNodes(html, this.user);
                fetchImages(html, this.user);
                fetchCustom(html, this.user);
                return this;
            }
    
            static parseHtml(user = new User(), html = '') {
                return new UserController(user).parseHtml(html);
            }
        };
    })();

    const createUrlParamsFromUserSearch = userSearch => Object.fromEntries(
        Object.entries({
            nick: 'nick',
            gender: 'gender',
            page: 'page',
            isOnline: 'online',
            hasPicture: 'picture',
            isPremium: 'isprem',
            isReal: 'isreal',
            isSuper: 'issuper'
        })
        .filter(([ propName, paramName ]) => userSearch[propName])
        .map(([ propName, paramName ]) => {
            let paramValue = userSearch[propName];
            paramValue = (typeof paramValue === 'boolean') ? (paramValue ? 1 : 0) : String(paramValue).replace(/\s/g, '+');
            return [ paramName, paramValue ];
        })
    );

    return class UserSearch {
        constructor({ nick = '', gender = User.GENDERS.ANY, isOnline = false, hasPicture = false, isPremium = false, isReal = false, isSuper = false, page = 0 } = {}) {
            Object.assign(this, { nick, gender, isOnline, hasPicture, isPremium, isReal, isSuper, page });
        }

        execute() {
            return HttpRequestHtml.send({
                url: 'https://www.camamba.com/search.php', params: createUrlParamsFromUserSearch(this)
            }).then((response) => {
                const userNodes = $(response.html).find('.searchNormal, .searchSuper').get();
                return userNodes.map(node => new UserController().parseHtml(node).user);
            }).then(users => Promise.all(
                users.map(user => HttpRequestHtml.send({
                    url: 'https://www.camamba.com/user_level.php', params: { uid: user.uid }
                }).then((response) => new UserController(user).parseHtml(response.html).user))
            ));
        }

        static execute(...args) {
            return new UserSearch(...args).execute();
        }

        static get GENDERS() {
            return User.GENDERS;
        }
    };
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址