Camamba Chat Helpers

decorates "knownUsers" and "rooms" objects with functions useful for console and other scripts

As of 26.03.2021. See ბოლო ვერსია.

ეს სკრიპტი არ უნდა იყოს პირდაპირ დაინსტალირებული. ეს ბიბლიოთეკაა, სხვა სკრიპტებისთვის უნდა ჩართეთ მეტა-დირექტივაში // @require https://update.greatest.deepsurf.us/scripts/423722/915068/Camamba%20Chat%20Helpers.js.

// ==UserScript==
// @name         Camamba Chat Helpers
// @namespace    dannysaurus.camamba
// @version      0.1.3
// @description  decorates "knownUsers" and "rooms" objects with functions useful for console and other scripts
// @license      MIT License
// @include      https://www.camamba.com/chat/
// @include      https://www.de.camamba.com/chat/
// @include      https://www.camamba.com/chat/
// @include      https://www.de.camamba.com/chat/
// @grant        none
// ==/UserScript==

/* jslint esversion: 9 */
/* global me, camData, rooms, blockList, friendList, friendRequests, adminMessages, jsLang, byId, myRooms, knownUsers, activeRoom, selectedUser, settings, onMessageHandlers, postMessageHandlers */

(function() {
    function decorateUsers(users = {}) {
        
        const isUser = (user) => user.id;

        const toArray = () => {
            if (Array.isArray(users)) {
                return [...users];
            }

            if (users.id && users.name) {
                return [ users ];
            }

            return Object.values(users);
        };

        const toString = () => {
            return toArray().map(u => {

                return Object.entries(u)
                    .map(([prop, val]) => prop + ':' + val)
                    .join('\t');

            }).join('\n');
        };

        const by = (userPredicateFnc) => {
            const result = [], excluded = [];

            Object.values(users).filter(u => isUser(u)).forEach(u => {
                if(userPredicateFnc(u)) {
                    result.push(u);
                } else {
                    excluded.push(u);
                }
            });

            if (excluded.length) {
                result.excluded = decorateUsers(excluded);
                result.excludedAll = decorateUsers([ ...excluded, ...users.excludedAll ]);
            }

            return decorateUsers(result);
        };

        const byId = (id) => {
            return by(user => user.id == id);
        };

        const byName = (name) => {
            const nameLower = String(name).toLowerCase();
            return by(u => u.name.toLowerCase().includes(nameLower));
        };

        const byGender = (gender) => {
            const genderLower = String(gender).toLowerCase();
            return by(u => u.gender.toLowerCase().startsWIth(genderLower));
        };

        const byIsCammed = () => {
            if (!camData) return false;

            const camDataUserIds = new Set(
                Object.values(camData)
                    .filter(cd => cd.user)
                    .map(cd => String(cd.user))
            );

            return by(u => camDataUserIds.has(String(u.id)));
        };

        const byViewing = () => {
            return users.by(user => user.viewing);
        };
        
        const byPos = (pos) => {
            return toArray()[pos];
        };

        const stopViewing = () => byViewing.forEach(user => {
            if (user.viewing) {
                janusSend('remove', user.id);
            }
        });

        const save = () => toArray().forEach(user => {
            user.original = {...user};
        });

        const restore = () => by(user => user.original).forEach(user => {
            Object.assign(user, user.original);
            delete user.original;
        });

        return Object.defineProperties(users, Object.fromEntries(Object.entries({
            excluded: users.excluded || [],
            excludedAll: users.excludedAll || [],
            toArray,
            toString,
            by,
            byId,
            byName,
            byGender,
            byPos,
            byIsCammed,
            byIsNotCammed: () => byIsCammed().excluded,
            byViewing,
            
            stopViewing,
            
            save,
            restore
        }).map(([propName, value]) => {
            return [propName, { value, configurable: true }];
        })));
    }

    function decorateRooms(rooms = {}) {
        const roomsByName = (name) => {
            const nameLower = String(name).toLowerCase();

            const result = {};

            Object.entries(rooms).forEach(([roomId, roomName]) => {

                if (roomName.toLowerCase().includes(nameLower)) {
                    result[roomId] = roomName;
                }
            });

            return result;
        };

        return Object.defineProperties(rooms, {
            byName: { value: roomsByName, configurable: true },
        });
    }

    const patchObject = function(getExpectedObjFnc, patchFnc, timeOutRetryMillis = 200, maxPeriodTryMillis = 5000) {
        const expectedObj = getExpectedObjFnc();

        if (!expectedObj && timeOutRetryMillis <= maxPeriodTryMillis) {
            setTimeout(() => patchObject(getExpectedObjFnc, patchFnc, timeOutRetryMillis), maxPeriodTryMillis - timeOutRetryMillis);
            return;
        }
        patchFnc(expectedObj);
    };

    patchObject(() => knownUsers, users => decorateUsers(users));
    patchObject(() => rooms, rooms => decorateRooms(rooms));
})();