SA:MP JS Удобный, легкий и полный пробив игроков по серверам Arizona Role Play

normalped

Участник
Автор темы
55
23
Версия SA-MP
  1. Любая
  2. Другая
Скрипт, позволяющий без лишних усилий получить всю доступную информацию про пользователя с любых серверов Arizona Role Play. Все, что нужно - вставить код в консоль разработчика в браузере и пользоваться его функционалом.

Настройка:
1. Зайдите на сайт и войдите в свой аккаунт.
2. Откройте консоль браузера из DevTools.
3. Вставьте главный код скрипта из спойлера ниже в поле для ввода и нажмите Enter.

Использование:
Теперь, чтобы получить информацию, вам нужно так же выполнить код из примера, указав ник игрока и диапазон номеров от серверов для поиска.


JavaScript:
Finder.find('Nick_Name', '1, 2, 5 - 6');

Здесь мы получаем информацию про игрока на Phoenix, Tucson, Brainburg, Saint-Rose.

Обратите внимание, что в диапазоне не обязательно добавлять пробелы, и номера мобильных серверов начинаются с сотни, например у Mobile 1 номер будет 101. Также после обновлений сайта работоспособность скрипта может нарушиться. Попробуйте изменить переменную sitekey в основном скрипте на более актуальную: откройте вкладку Elements в DevTools и с помощью поиска (Ctrl + F) найдите подобный, но актуальный ключ по запросу sitekey.


1744796917659.png

1744796943841.png
JavaScript:
class Finder {
    static accessToken;
    static captchaToken;
    static servers = [];
    static sitekey = '0x4AAAAAAA4cVvk7rjr3XWxi'

    static server = (id) =>
        this.servers[id] ? `${this.servers[id].title} (${id})` : `Server (${id})`

    static cookie = (name) => {
        const value = `; ${document.cookie}`;
        const parts = value.split(`; ${name}=`);
        if (parts.length === 2) return parts.pop().split(';').shift();
    };

    static captcha = () => new Promise(resolve => {
        window.turnstile.render(document.querySelector('#cf-turnstile'), {
            sitekey: this.sitekey,
            callback: resolve
        });
    });

    static parseRange = (str) => {
        const res = str
            .split(',')
            .flatMap(part => {
                if (part.includes('-')) {
                    const [start, end] = part.split('-').map(Number);
                    return Array.from({ length: end - start + 1 }, (_, i) => start + i);
                }
                return [Number(part)];
            });

        if (res.length < 1) throw new Error('Не удалось запарсить диапазон!');
        return [...new Set(res)];
    };

    static fetchPlayer = async (name, server) => {
        this.captchaToken = await this.captcha();

        const body = {
            gReCaptchaToken: this.captchaToken,
            login: name,
            server: server
        };

        const config = {
            method: 'POST',
            headers: {
                'Authorization': `Bearer ${this.accessToken}`,
                'Content-Type': 'application/json',
                'Referer': 'https://arizona-rp.com/',
            },
            body: JSON.stringify(body)
        };

        const res = await fetch('https://n-api.arizona-rp.com/api/account/find', config);
        let data;

        try {
            data = await res.json();
        } catch {
            data = null;
        }

        return res.ok ? data : { code: res.status, message: data?.message || res.statusText };
    };

    static fetchEstate = async (name, server) => {
        const res = await fetch(`https://n-api.arizona-rp.com/api/map/${server}`);
        if (res.ok) {
            const data = await res.json();
            const businesses = {};
            const houses = {};

            data.businesses.noAuction = Object.values(data.businesses.noAuction).flat()

            const filter = (target, source, keys) => {
                keys.forEach(key => {
                    target[key] = source[key].filter(obj => obj.owner === name);
                });
            };

            filter(businesses, data.businesses, ['noAuction', 'onAuction', 'onMarketplace']);
            filter(houses, data.houses, ['hasOwner', 'onAuction', 'onMarketplace']);

            return { businesses, houses };
        }
        return { code: res.status, message: res.statusText }
    };

    static fetchServers = async () => {
        const res = await fetch('https://n-api.arizona-rp.com/api/servers/arizona');
        if (res.ok) return await res.json();
        return { code: res.status, message: res.statusText }
    };

    static fetch = async (...args) => {
        const res = { errors: [] };
        const player = await this.fetchPlayer(...args);
        const estate = await this.fetchEstate(...args);

        for (const data of [ player, estate ]) {
            if (data.code) {
                res.errors.push(data);
                continue;
            }
            Object.assign(res, data);
        }

        return res;
    };

    static format = (data, name, server) => {
        if (data.errors.length > 0) {
            console.groupCollapsed(`%c❌ ${this.server(server)} - Ошибка: ${data.errors[0].code} | ${data.errors[0].message}`, 'color: #f92f60; font-size: 1.8rem;');
            console.groupEnd();
            return;
        }

        console.groupCollapsed(`%c✅ ${this.server(server)} - Игрок найден:`, 'color: #00d26a; font-size: 1.8rem;');

        const number = (amount, suffix = '$') => {
            if (!amount && typeof amount !== 'number') return;
            if (typeof amount === 'string') {
                amount = Number(amount.replace(/\s/g, ''));
                if (isNaN(amount)) return;
            }
            if (typeof amount !== 'number' || isNaN(amount)) return;
            return new Intl.NumberFormat(navigator.language || 'en-US', {
                style: 'decimal',
                minimumFractionDigits: 0
            }).format(amount) + ' ' + suffix;
        };

        const bank = (amount) => amount !== -1 ? number(amount) : 'Не открыт';

        const estate = (arr) => {
            if (!Array.isArray(arr) || arr.length === 0) return 'Пусто';
            return '\n' + arr
                .map((item, i) => ` ${i + 1}. #${item?.id} - ${item?.name};`)
                .join('\n');
        };

        const lines = [
            ['🏷️ Имя', data?.name],
            ['📈 Уровень', data?.level],
            ['✨ Очки опыта', `${data?.xp} / ${data?.maxXp}`],
            ['❓ Пол', data?.gender],
            ['⭐ VIP', `${data?.vip ?? 'нет'}${data?.vip ? ` (${data?.vipExpirationDate ?? 'Вечный'})` : ''}`],
            ['⌛ Наиграно', number(data?.PlayHours, 'ч.')],
            ['🪙 AZ-Coins', number(data?.coins, 'AZ')],
            ['💰 Накопления', number(data?.money)],
            ['💵 Наличные деньги', number(data?.cash)],
            ['💳 Деньги в банке', number(data?.bank)],
            ['🏦 Деньги на депозите', number(data?.deposit)],
            ['📑 Личный счет #1', bank(data?.bankAccount1)],
            ['📑 Личный счет #2', bank(data?.bankAccount2)],
            ['📑 Личный счет #3', bank(data?.bankAccount3)],
            ['📑 Личный счет #4', bank(data?.bankAccount4)],
            ['📑 Личный счет #5', bank(data?.bankAccount5)],
            ['📑 Личный счет #6', bank(data?.bankAccount6)],
            ['💳 Банковская карта', data?.bankCard ? 'есть' : 'нет'],
            ['📱 Номер телефона', data?.telephone ?? 'нет'],
            ['📱 Баланс телефона', number(data?.telephoneBalance) ?? 'нет'],
            ['🔗 Зависимость', data?.drugAddict],
            ['💼 Работа', data?.hasJob],
            ['💍 Супруг(а)', data?.married || 'нет'],
            ['🕵️ Уровень розыска', data?.wantedLevel],
            ['❤️ Здоровье', data?.hp],
            ['🍖 Голод', `${data?.hunger} / ${data?.maxHunger}`],
            ['⚠️ Предупреждения', data?.warns],
            ['🙏 Благотворительность', number(data?.blago) ?? 'нет'],
            ['🏠 Дома', estate(data?.houses?.hasOwner)],
            ['🏠 Дома на аукционе', estate(data?.houses?.onAuction)],
            ['🏠 Дома на маркетплейсе', estate(data?.houses?.onMarketplace)],
            ['🏢 Бизнесы', estate(data?.businesses?.noAuction)],
            ['🏢 Бизнесы на аукционе', estate(data?.businesses?.onAuction)],
            ['🏢 Бизнесы на маркетплейсе', estate(data?.businesses?.onMarketplace)],
        ];

        const output = lines.map(([key, value]) =>
            [`%c${key}%c — %c${value ?? '-'}`,
            'font-size: 1.1rem;',
            'font-size: 1.5rem; color: gray;',
            'font-size: 1.1rem; font-weight: bold']
        );

        const format = output.map(o => o[0]).join('\n');
        const styles = output.flatMap(o => o.slice(1));

        console.log(format, ...styles);
        console.groupEnd();
    };

    static find = async (name, range) => {
        this.accessToken = this.cookie('accessToken');
        const servers = this.parseRange(range);
        const res = {};

        const data = await this.fetchServers();
        if (!data.code) {
            this.servers = data.reduce((acc, item) => {
                acc[item.id] = item;
                return acc;
            }, {});
        }

        for (const server of servers) {
            const data = await this.fetch(name, server);
            this.format(data, name, server);
            res[server] = data;
        }

        const keys = Object.keys(res).length;
        if (keys > 1) {
            console.log('\n');
            const failed = Object.entries(res).filter(([key, value]) => value?.errors?.length > 0);
            console.groupCollapsed(`%cℹ️ Итого (${keys - failed.length} / ${keys}):`, 'color: #0092d1; font-size: 1.8rem;');

            for (const key in res) {
                this.format(res[key], name, key);
            }

            console.groupEnd();
            console.log('\n');
        }
    };
}

Автором являюсь я. При распространении или использовании не локально указывать автора.
 
Последнее редактирование:

normalped

Участник
Автор темы
55
23
Вышло обновление с кучей нового функционала.
 

normalped

Участник
Автор темы
55
23
Можно ли както на python использовать? С отправкой информации в тг и получение информации через этот скрипт
Думаю, можно, но есть куча нюансов. Скрипт, наверное, придется изменить и запускать на сайте через всякие беспалевные селениумы, но перед этим входить в аккаунт.
 

Deps

Активный
108
36
Через неделю после выпуска этого скрипта, разработчики отключили API вроде
Нет. Это обычный скрейпер, его прикроют (полностью) только тогда, когда на самом сайте уберут поиск, либо когда завезут более действенный способ защиты их API.
 
  • Нравится
Реакции: normalped