- // ==UserScript==
- // @name Camamba Users Search Library
- // @namespace hoehleg.userscripts.private
- // @version 0.0.9
- // @description fetches Users
- // @author Gerrit Höhle
- // @license MIT
- //
- // @require https://greasyfork.org/scripts/405144-httprequest/code/HttpRequest.js?version=1063408
- //
- // @grant GM_xmlhttpRequest
- // @grant GM.getValue
- // @grant GM.setValue
- // @grant GM.listValues
- // ==/UserScript==
-
- // https://greasyfork.org/scripts/446634-camamba-users-search-library/
-
- /* jslint esversion: 11 */
-
- const cus_cache = (() => {
-
- /**
- * @param {{ uid: number, name: string }} keyAsObject
- * @returns {string?}
- */
- const objectToKey = (keyAsObject) => {
- if (!keyAsObject?.uid && !keyAsObject?.name) {
- return null;
- }
- return `cus${keyAsObject.uid || ""}'${keyAsObject.name || ""}`;
- };
-
- /**
- * @param {string} key
- * @returns {{ uid: number, name: string }}
- */
- const keyToObject = (keyAsString) => {
- const keyAsArray = keyAsString.slice(3).split("'");
- return {
- uid: Number.parseInt(keyAsArray[0]) || 0,
- name: keyAsArray[1] || "",
- };
- };
-
- /** @type {object.<{ uid: number, name: string }>} */
- const cache = {};
- /** @type {object.<{ uid: number, name: string }>} */
- const keysByName = {};
- /** @type {object.<{ uid: number, name: string }>} */
- const keysByUid = {};
-
- const addToCache = ({ uid, name, content }) => {
- const key = { uid, name };
-
- if (uid) {
- const storedByUid = keysByUid[uid];
-
- if (name) {
- if (storedByUid && storedByUid.name !== name) {
-
- if (keysByName.hasOwnProperty(storedByUid.name)) {
- delete keysByName[storedByUid.name];
- }
-
- GM.deleteKey(objectToKey(storedByUid));
- }
- keysByName[name] = key;
- }
-
- keysByUid[uid] = key;
- }
- if (name) {
- keysByName[name] = key;
- }
- cache[objectToKey(key)] = content || null;
- };
-
- (async () => {
- for (const keyAsString of (await GM.listValues()).filter(key => key.startsWith("cus"))) {
- addToCache(keyToObject(keyAsString));
- }
- })();
-
- return {
-
- /**
- * @param {{ uid: number?, name?: string, guessLog: GuessLog?, userLevel: UserLevel?}} param0
- */
- store: async ({ uid, name, guessLog, userLevel }) => {
- if (!name && !uid || !guessLog && !userLevel) {
- return;
- }
-
- const content = {};
- if (userLevel?.level) {
- content.lvl = userLevel.level;
- }
- if (userLevel?.timeStamp) {
- content.lvlTS = userLevel.timeStamp;
- }
- if (guessLog?.ipList?.length) {
- content.ips = guessLog.ipList;
- }
- if (guessLog?.prints?.length) {
- content.prints = guessLog.prints;
- }
- if (guessLog?.scorePassword) {
- content.scrPW = guessLog.scorePassword;
- }
- if (guessLog?.scoreFinal) {
- content.scrFinal = guessLog.scoreFinal;
- }
- if (guessLog?.timeStamp) {
- content.scrTS = guessLog.timeStamp;
- }
-
- const key = objectToKey({ name, uid });
- await GM.setValue(key, JSON.stringify(content));
- addToCache({ name, uid, content });
- },
-
- /**
- * @param {{ uid: number, name: string }} param0
- * @return {Promise<{ guessLog : GuessLog, userLevel: UserLevel }}>
- */
- read: async ({ uid = 0, name = "" }) => {
- let keyAsObject = null;
- if (uid && keysByUid[uid]) {
- keyAsObject = keysByUid[uid];
- } else if (name && keysByName[name]) {
- keyAsObject = keysByName[name];
- }
- const key = objectToKey(keyAsObject);
- if (!key) {
- return {};
- }
-
- let data = {};
- if (!cache[key]) {
- cache[key] = JSON.parse(await GM.getValue(key, "{}"));
- }
- data = cache[key];
-
- const guessLog = new GuessLog({
- userName: keyAsObject?.name || name,
- ipList: data.ips || [],
- prints: data.prints || [],
- scorePassword: data.scrPW || 0,
- scoreFinal: data.scrFinal || 0,
- timeStamp: data.scrTS || 0,
- });
-
- const userLevel = new UserLevel({
- name: keyAsObject?.name || name,
- uid: keyAsObject?.uid || uid,
- level: data.lvl || 0,
- timeStamp: data.lvlTS || 0,
- });
-
- return { guessLog, userLevel };
- },
- };
- })();
-
- class GuessLog {
- /** @param {{ userName: string, ipList: string[], prints: string[], scorePassword: number, scoreFinal: number, timeStamp: number }} param0 */
- constructor({ userName, ipList, prints, scorePassword, scoreFinal, timeStamp }) {
- /** @type {string} */
- this.userName = userName;
- /** @type {string[]} */
- this.ipList = ipList;
- /** @type {string[]} */
- this.prints = prints;
- /** @type {number} */
- this.scorePassword = scorePassword;
- /** @type {number} */
- this.scoreFinal = scoreFinal;
- /** @type {number} */
- this.timeStamp = timeStamp;
- }
- }
-
- /**
- * @typedef {object} UserParams
- * @property {string} name
- * @property {uid} [number]
- * @property {'male'|'female'|'couple'?} [gender]
- * @property {number} [age]
- * @property {number} [level]
- * @property {number} [longitude]
- * @property {number} [latitude]
- * @property {string} [location]
- * @property {number} [distanceKM]
- * @property {boolean} [isReal]
- * @property {boolean} [hasPremium]
- * @property {boolean} [hasSuper]
- * @property {boolean} [isPerma]
- * @property {boolean} [isOnline]
- * @property {string} [room]
- * @property {Date} [lastSeen]
- * @property {Date} [regDate]
- * @property {string[]} [ipList]
- * @property {Date} [scorePassword]
- * @property {Date} [scoreFinal]
- * @property {(date: Date) => string} [dateToHumanReadable]
- */
-
- class GuessLogSearch extends HttpRequestHtml {
-
- constructor(name) {
- /**
- * @param {string} labelText
- * @param {string} textContent
- * @returns {number}
- */
- const matchScore = (labelText, textContent) => {
- const regexLookBehind = new RegExp("(?<=" + labelText + ":\\s)");
- const regexFloat = /\d{1,2}\.?\d{0,20}/;
- const regexLookAhead = /(?=\spoints)/;
-
- for (const regexesToJoin of [
- [regexLookBehind, regexFloat, regexLookAhead],
- [regexLookBehind, regexFloat]
- ]) {
- const regexAsString = regexesToJoin.map(re => re.source).join("");
- const matcher = new RegExp(regexAsString, "i").exec(textContent);
- if (matcher != null) {
- return Number.parseFloat(matcher[0]);
- }
- }
- };
-
- /**
- * @param {RegExp} regex
- * @param {string} textContent
- * @returns {Array<String>}
- */
- const matchList = (regex, textContent) => {
- const results = [...textContent.matchAll(regex)].reduce((a, b) => [...a, ...b], []);
- if (results.length) {
- const resultsDistinct = [...new Set(results)];
- return resultsDistinct;
- }
- };
-
- super({
- url: 'https://www.camamba.com/guesslog.php',
- params: { name },
-
- /**
- * @param {{ html: Document}} resp
- * @returns {GuessLog}
- */
- resultTransformer: (resp) => {
- const textContent = resp.html.body.textContent;
-
- const ipList = matchList(/(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])/g, textContent);
- const prints = matchList(/(?<=Print\d{0,2}\schecked\sis\s)[0-9a-f]+/g, textContent);
-
- const scorePassword = matchScore("password check", textContent);
- const scoreFinal = matchScore("final score", textContent);
-
- return { userName: name, ipList, prints, scorePassword, scoreFinal, timeStamp: new Date().getTime() };
- }
- });
- }
-
- /** @returns {Promise<GuessLog>} */
- async send() {
- const maxHoursInCache = 6;
-
- const name = this.params.name;
- const cache = await cus_cache.read({ name });
-
- let result = cache?.guessLog;
- const timeStamp = result?.timeStamp;
-
- if (!timeStamp || new Date().getTime() - timeStamp >= maxHoursInCache * 60 * 60 * 1000) {
- result = await super.send();
- cus_cache.store({ ...(cache || {}), name, guessLog: result });
- }
-
- return result;
- }
-
- /**
- * @param {string} name
- * @returns {Promise<GuessLog>}
- */
- static async send(name) {
- return await new GuessLogSearch(name).send();
- }
- }
-
- /**
- * @typedef {Object} BanLog
- * @property {string} moderator - user or moderator who triggered the log
- * @property {string} user - user who is subject
- * @property {Date} date - date of this log
- * @property {string} reason - content
- */
-
- class BannLogSearch extends HttpRequestHtml {
- /**
- * @param {number} uid
- */
- constructor(uid = null) {
- super({
- url: 'https://www.camamba.com/banlog.php',
- params: uid ? { admin: uid } : {},
- resultTransformer: (response, _request) => {
- const results = [];
- const xPathExpr = "//tr" + ['User', 'Moderator', 'Date', 'Reason'].map(hdrText => `[td[span[text()='${hdrText}']]]`).join("");
- let tr = (response.html.evaluate(xPathExpr, response.html.body, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue || {}).nextElementSibling;
-
- while (tr) {
- const tds = tr.querySelectorAll('td');
- const user = tds[0].querySelector("a") || tds[0].textContent;
- const moderator = tds[1].textContent;
-
- let date;
- const dateMatch = /(\d{2}).(\d{2}).(\d{4})<br>(\d{1,2}):(\d{2}):(\d{2})/.exec(tds[2].innerHTML);
- if (dateMatch) {
- const day = dateMatch[1];
- const month = dateMatch[2];
- const year = dateMatch[3];
- const hour = dateMatch[4];
- const minute = dateMatch[5];
- const second = dateMatch[6];
- date = new Date(year, month - 1, day, hour, minute, second);
- }
-
- const reason = tds[3].textContent;
- results.push({ user, moderator, date, reason });
-
- tr = tr.nextElementSibling;
- }
-
- return results;
- }
- });
- }
-
- /**
- * @param {number} uid
- * @returns {Promise<BanLog[]>}
- */
- static async send(uid) {
- return await new BannLogSearch(uid).send();
- }
- }
-
- class GalleryImage {
- constructor({ dataURI, href }) {
- /** @type {string} */
- this.dataURI = dataURI;
- /** @type {string} */
- this.href = href;
- }
- }
-
-
- class UserLevel {
- /** @param {{ level: number, uid: number, name: string, timeStamp: number }} param0 */
- constructor({ level, uid = null, name = null, timeStamp = null }) {
- /** @type {number} */
- this.level = level !== null ? Number.parseInt(level) : null;
- /** @type {number} */
- this.uid = uid !== null ? Number.parseInt(uid) : null;
- /** @type {string} */
- this.name = name;
- /** @type {number} */
- this.timeStamp = timeStamp !== null ? Number.parseInt(timeStamp) : null;
- }
- }
-
- class UserLevelSearch extends HttpRequestHtml {
- constructor(uid) {
- super({
- url: 'https://www.camamba.com/user_level.php',
- params: { uid },
-
- /**
- * @param {{ html: Document }} response
- * @param {{ params: { uid: number }}} request
- * @returns {UserLevel}
- */
- resultTransformer: (response, request) => {
- const html = response.html;
-
- let name = null, level = null;
-
- const nameElement = html.querySelector('b');
- if (nameElement) {
- name = nameElement.textContent;
- }
-
- const levelElement = html.querySelector('font.xxltext');
- if (levelElement) {
- const levelMatch = /\d{1,3}/.exec(levelElement.textContent);
- if (levelMatch) {
- level = Number.parseInt(levelMatch);
- }
- }
-
- return new UserLevel({ uid: request.params.uid, name, level, timeStamp: new Date().getTime() });
- }
- });
- }
-
- /**
- * @returns {Promise<UserLevel>}
- */
- async send() {
- const maxHoursInCache = 24;
-
- const uid = this.params.uid;
- const cache = await cus_cache.read({ uid });
-
- let result = cache?.userLevel;
- const timeStamp = result?.timeStamp;
-
- if (!timeStamp || new Date().getTime() - timeStamp >= maxHoursInCache * 60 * 60 * 1000) {
- result = await super.send();
- cus_cache.store({ ...(cache || {}), uid, userLevel: result });
- }
-
- return result;
- }
-
- /**
- * @param {number} uid
- * @returns {Promise<UserLevel>}
- */
- static async send(uid) {
- return await new UserLevelSearch(uid).send();
- }
- }
-
- class User {
- /** @param {UserParams} param0 */
- constructor({
- name, uid = 0, gender = null, age = null,
- longitude = null, latitude = null, location = null, distanceKM = null,
- isReal = null, hasPremium = null, hasSuper = null, isPerma = null,
- isOnline = null, room = null, lastSeen = null, regDate = null,
- dateToHumanReadable = (date) => date ?
- date.toLocaleString('de-DE', { timeStyle: "medium", dateStyle: "short", timeZone: 'CET' }) : '',
- }) {
- /** @type {string} */
- this.name = String(name);
- /** @type {number?} */
- this.uid = uid;
- /** @type {'male'|'female'|'couple'?} */
- this.gender = gender;
- /** @type {number?} */
- this.age = age;
-
- /** @type {number?} */
- this.longitude = longitude;
- /** @type {number?} */
- this.latitude = latitude;
- /** @type {string?} */
- this.location = location;
- /** @type {number?} */
- this.distanceKM = distanceKM;
-
- /** @type {boolean?} */
- this.isReal = isReal;
- /** @type {boolean?} */
- this.hasPremium = hasPremium;
- /** @type {boolean?} */
- this.hasSuper = hasSuper;
- /** @type {boolean?} */
- this.isPerma = isPerma;
-
- /** @type {boolean?} */
- this.isOnline = isOnline;
- /** @type {string?} */
- this.room = room;
- /** @type {Date?} */
- this.lastSeen = lastSeen;
- /** @type {Date?} */
- this.regDate = regDate;
-
- /** @type {string[]} */
- this.prints = [];
- /** @type {string[]} */
- this.ipList = [];
- /** @type {number?} */
- this.scorePassword = null;
- /** @type {number?} */
- this.scoreFinal = null;
- /** @type {number} */
- this.guessLogTS = null;
-
- /** @type {(date: Date) => string} */
- this.dateToHumanReadable = dateToHumanReadable;
-
- /** @type {number?} */
- this.level = null;
- /** @type {number} */
- this.levelTS = null;
-
- /** @type {string[]} */
- this.galleryData = [];
- /** @type {number} */
- this.galleryDataTS = null;
- }
-
- /** @type {string} @readonly */
- get lastSeenHumanReadable() {
- return this.dateToHumanReadable(this.lastSeen);
- }
-
- /** @type {string} @readonly */
- get regDateHumanReadable() {
- return this.dateToHumanReadable(this.regDate);
- }
-
- get galleryAsImgElements() {
- if (!this.galleryData) {
- return [];
- }
-
- return this.galleryData.map(data => Object.assign(document.createElement('img'), {
- src: data.dataURI
- }));
- }
-
- async updateGalleryHref() {
- const pictureLinks = (await HttpRequestHtml.send({
- url: "https://www.camamba.com/profile_view.php",
- params: Object.assign(
- { m: 'gallery' },
- this.uid ? { uid: this.uid } : { user: this.name }
- ),
-
- pageNr: 0,
- pagesMaxCount: 500,
-
- resultTransformer: (response) => {
- const hrefList = [...response.html.querySelectorAll("img.picborder")].map(img => img.src);
- return hrefList.map(href => href.slice(0, 0 - ".s.jpg".length) + ".l.jpg");
- },
- hasNextPage: (_resp, _httpRequestHtml, lastResult) => {
- return lastResult.length >= 15;
- },
- paramsConfiguratorForPageNr: (params, pageNr) => ({ ...params, page: pageNr }),
- })).flat();
-
- this.galleryData = pictureLinks.map(href => ({ href }));
- this.galleryDataTS = new Date().getTime();
- }
-
- async updateGalleryData(includeUpdateOfHref = true) {
- if (includeUpdateOfHref) {
- await this.updateGalleryHref();
- }
-
- const readGalleryData = this.galleryData.map(({ href }) => (async () => {
- const dataURI = await HttpRequestBlob.send({ url: href });
- return new GalleryImage({ dataURI, href });
- })());
-
- this.galleryData = await Promise.all(readGalleryData);
- this.galleryDataTS = new Date().getTime();
- }
-
- async updateLevel() {
- const { level, timeStamp, name } = await UserLevelSearch.send(this.uid);
- this.level = level;
- this.levelTS = timeStamp;
- this.name = name;
- }
-
- async updateGuessLog() {
- /** @type {GuessLog} */
- const guessLog = await GuessLogSearch.send(this.name);
- this.guessLogTS = new Date().getTime();
-
- this.ipList = guessLog.ipList;
- this.prints = guessLog.prints;
- this.scorePassword = guessLog.scorePassword;
- this.scoreFinal = guessLog.scoreFinal;
- }
-
- async addNote(text) {
- if (!this.uid) {
- return await Promise.reject({
- status: 500,
- statusText: "missing uid"
- });
- }
-
- return await new Promise((res, rej) => GM_xmlhttpRequest({
- url: 'https://www.camamba.com/profile_view.php',
- method: 'POST',
- data: `uid=${this.uid}&modnote=${encodeURIComponent(text)}&m=admin&nomen=1`,
- headers: {
- "Content-Type": "application/x-www-form-urlencoded"
- },
- onload: (xhr) => {
- res(xhr.responseText);
- },
- onerror: (xhr) => rej({
- status: xhr.status,
- statusText: xhr.statusText
- }),
- }));
- }
- }
-
- class UserSearch extends HttpRequestHtml {
- /** @param {{
- * name: string?,
- * uid: number?,
- * gender: ('any' | 'male' | 'female' |'couple')?,
- * isOnline: boolean?, hasReal: boolean?, hasPremium: boolean?, hasSuper: boolean?, hasPicture: boolean?,
- * isSortByRegDate: boolean?,
- * isSortByDistance: boolean?,
- * isShowAll: boolean?,
- * pageNr: number?,
- * pagesMaxCount: number?,
- * keepInCacheTimoutMs: number?
- * }} param0 */
- constructor({
- name = null,
- uid = 0,
- gender = 'any',
- isOnline = null,
- hasReal = null,
- hasPremium = null,
- hasSuper = null,
- hasPicture = null,
- isSortByRegDate = null,
- isSortByDistance = null,
- isShowAll = null,
- pageNr = 1,
- pagesMaxCount = 1,
- keepInCacheTimoutMs
- } = {}) {
- let params = Object.assign(
- (name ? {
- nick: name
- } : {}),
- {
- gender: gender.toLowerCase(),
- },
- Object.fromEntries(Object.entries({
- online: isOnline,
- isreal: hasReal,
- isprem: hasPremium,
- issuper: hasSuper,
- picture: hasPicture,
- sortreg: isSortByRegDate,
- byDistance: isSortByDistance,
- showall: isShowAll,
- })
- .filter(([_k, v]) => typeof v !== 'undefined' && v !== null)
- .map(([k, v]) => ([[k], v ? 1 : 0])))
- );
-
- params = Object.entries(params).map(([key, value]) => key + '=' + value).join('&');
-
- if (params.length) {
- params += "&";
- }
- params += `page=${Math.max(pageNr - 1, 0)}`;
-
- super({
- url: 'https://www.camamba.com/search.php',
- params,
- pageNr: Math.max(pageNr, 1),
- pagesMaxCount: Math.max(pagesMaxCount, 1),
- keepInCacheTimoutMs,
-
- resultTransformer: (response) => {
- /** @type {Array<User>} */
- const users = [];
-
- for (const trNode of response.html.querySelectorAll('.searchSuper tr, .searchNormal tr')) {
- const innerHTML = trNode.innerHTML;
-
- const nameMatch = /<a\s+?href=["']javascript:openProfile\(["'](.+?)["']\)/.exec(innerHTML);
-
- if (!nameMatch) {
- break;
- }
-
- const user = new User({
- name: nameMatch[1],
- isReal: /<img src="\/gfx\/real.png"/.test(innerHTML),
- hasPremium: /<a href="\/premium.php">/.test(innerHTML),
- hasSuper: /<img src="\/gfx\/super_premium.png"/.test(innerHTML),
- isOnline: /Online\snow(\s\in|,\snot in chat)/.test(innerHTML),
- });
-
- const uidMatch = /<a\s+?href=["']javascript:sendMail\(["'](\d{1,8})["']\)/.exec(innerHTML) || /<img\ssrc="\/userpics\/(\d{1,8})/.exec(innerHTML);
- if (uidMatch) {
- user.uid = Number.parseInt(uidMatch[1]);
- }
-
- // Längengrad, Breitengrad, Ortsname
- const locationMatch = /<a\s+?href="javascript:openMap\((-?\d{1,3}\.\d{8}),(-?\d{1,3}\.\d{8})\);">(.+?)<\/a>/.exec(innerHTML);
- if (locationMatch) {
- user.longitude = Number.parseFloat(locationMatch[1]);
- user.latitude = Number.parseFloat(locationMatch[2]);
- user.location = locationMatch[3];
- }
-
- // Entfernung in km
- const distanceMatch = /(\d{1,5})\skm\sfrom\syou/.exec(innerHTML);
- if (distanceMatch) {
- user.distanceKM = parseInt(distanceMatch[1]);
- }
-
- // Geschlecht und Alter
- const genderAgeMatch = /(male|female|couple),\s(\d{1,4})(?:<br>){2}Online/.exec(innerHTML);
- if (genderAgeMatch) {
- user.gender = genderAgeMatch[1];
- user.age = genderAgeMatch[2];
- }
-
- // zuletzt Online
- if (user.isOnline) {
- user.lastSeen = new Date();
- } else {
- const lastSeenMatch = /(\d{1,4})\s(minutes|hours|days)\sago/.exec(innerHTML);
- if (lastSeenMatch) {
- const value = parseInt(lastSeenMatch[1]);
-
- const factorToMillis = {
- 'minutes': 1000 * 60,
- 'hours': 1000 * 60 * 60,
- 'days': 1000 * 60 * 60 * 24,
- }[lastSeenMatch[2]];
-
- user.lastSeen = new Date(Date.now() - value * factorToMillis);
- }
- }
-
- // Raumname
- const roomMatch = /(?:ago|now)\sin\s([\w\s]+?|p\d{1,8})<br>/.exec(innerHTML);
- if (roomMatch) {
- user.room = roomMatch[1];
- }
-
- // regDate
- const regDateMatch = /(\d{2}).(\d{2}).(\d{4})\s(\d{1,2}):(\d{2}):(\d{2})/.exec(innerHTML);
- if (regDateMatch) {
- const regDateDay = regDateMatch[1];
- const regDateMonth = regDateMatch[2];
- const regDateYear = regDateMatch[3];
- const regDateHour = regDateMatch[4];
- const regDateMinute = regDateMatch[5];
- const regDateSecond = regDateMatch[6];
- user.regDate = new Date(regDateYear, regDateMonth - 1, regDateDay, regDateHour, regDateMinute, regDateSecond);
- }
-
- users.push(user);
- }
-
- return users;
- },
-
- hasNextPage: (_resp, _httpRequestHtml, lastResult) => {
- return lastResult.length >= 50;
- },
-
- paramsConfiguratorForPageNr: (params, pageNr) => {
- return params.replace(/page=\d+(?:$)/, `page=${pageNr - 1}`);
- },
- });
- this.uid = uid || null;
- }
-
- /** @returns {Promise<User[]>} */
- async send() {
- if (this.uid) {
- const user = new User({ uid: this.uid });
- await user.updateLevel();
-
- if (!user.name || user.level) {
- return [];
- }
- if (this.params.nick) {
- const unameURIencoded = encodeURIComponent(user.name.toLowerCase());
- const unameFromSearchParam = encodeURIComponent(this.params.nick.toLowerCase()).trim();
- if (unameURIencoded.includes(unameFromSearchParam)) {
- return [];
- }
- }
-
- this.params.nick = user.name;
- const result = (await super.send()).flat().find(u => u.uid == this.uid);
- if (!result) {
- return [];
- }
-
- return [Object.assign(user, result)];
- }
-
- return (await super.send()).flat();
- }
-
- /**
- * @param {{
- * name: string,
- * uid: number?,
- * gender: 'any' | 'male' | 'female' |'couple',
- * isOnline: boolean,hasReal: boolean, hasPremium: boolean, hasSuper: boolean, hasPicture: boolean,
- * isSortByRegDate: boolean,
- * isSortByDistance: boolean,
- * isShowAll: boolean,
- * pageNr: number,
- * pagesMaxCount: number,
- * keepInCacheTimoutMs: number
- * }} param0
- * @returns {Promise<User[]>}
- */
- static async send(param0) {
- return await new UserSearch(param0).send();
- }
- }