[Скрипты] Симпометр - 2025

[Скрипты] Симпометр - 2025

JS Скрипт который считает все реакции на постах и комментариях по пользователям. В результате работы выводит таблицы по постам, комментариям и суммирующую.
Для работы в коде скрипта надо прописать логин учётки с плюсом - реакции считаются только с ней. Подсчитать можно любого пользователя. По дефолту считается стата пользователя запустившего скрипт.
Считает долго, в зависимости от количеств постов и комментариев. Мои реакции считало 3 часа.

Как запустить

В браузере открыть консоль разработчика(обычно F12). Вставить отредактированный скрипт со своими данными и запустить. Дождаться статуса "Готово" наверху страницы.

Код скрипта

async function main() { const user_login = '****@*****'; //логин const user_password = '*****' //пароль let ls_user = localStorage.getItem('user'); if (!ls_user) {alert('Войдите на сайт'); return;} let user = JSON.parse(ls_user); if (!user?.id) {alert('Войдите на сайт!'); return;} const user_id = user.id; //считаем свои реакции //или //const user_id = 931623; //определенного пользователя const max_users_in_results = 100; //максимальное кол-во юзеров в результатах. null если не нужна обрезка const dtf_api = new DTF_API(user_login, user_password, console.log); await dtf_api.auth(); const delay_time_ms = 200; let status = 'Загружаем шметрики...'; let users = new Map(); create_form(); let results_form__table = document.getElementById('results_form__table'); let results_form__status = document.getElementById('results_form__status'); update_form(); const entities = [ {type:'entries', text: 'пост'}, {type:'comments', text: 'комментарий'} ] for (const entity of entities) { let last_sorting_value = 0; let last_id = 0; let cursor= ''; while (true) { let url; if (entity.type === 'entries') { const base_url = 'https://api.dtf.ru/v2.10/timeline'; const params = { markdown: 'false', sorting: 'new', subsitesIds: user_id }; if (last_sorting_value) params.lastSortingValue = last_sorting_value; if (last_id) params.lastId = last_id; if (cursor) params.cursor = cursor; const url_params = new URLSearchParams(params); url = `${base_url}?${url_params.toString()}`; } if (entity.type === 'comments') { const base_url = 'https://api.dtf.ru/v2.5/comments'; const params = { page: 0, sorting: 'new', subsiteId: user_id }; if (last_sorting_value) params.lastSortingValue = last_sorting_value; if (last_id) params.lastId = last_id; const url_params = new URLSearchParams(params); url = `${base_url}?${url_params.toString()}`; } let entity_response_json; while (true) { await delay(delay_time_ms); const entry_response = await fetch(url); entity_response_json = await entry_response.json(); if (entity_response_json?.error?.code === 401) { await dtf_api.refresh().catch(function (error) {console.error(error);}); } else { break; } } if (!entity_response_json?.result?.items?.length) break; if (entity.type === 'entries') { last_sorting_value = entity_response_json.result.items[entity_response_json.result.items.length - 1].data.date; last_id = entity_response_json.result.items[entity_response_json.result.items.length - 1].data.id; cursor = entity_response_json.result.cursor; } if (entity.type === 'comments') { last_sorting_value = entity_response_json.result.lastSortingValue; last_id = entity_response_json.result.lastId; } for (let k = 0; k < entity_response_json.result.items.length; k++) { let counters; if (entity.type === 'entries') { if (entity_response_json.result.items[k].type !=="entry") continue; if (entity_response_json.result.items[k].data.isPinned === true) continue; //пропускаем закреп counters = entity_response_json.result.items[k].data.reactions.counters; } if (entity.type === 'comments') { counters = entity_response_json.result.items[k].reactions.counters; } for (let c = 0; c < counters.length; c++) { if (!counters[c].count) continue; //0 реакций let reactions_response_json; while (true) { await delay(delay_time_ms); let url; if (entity.type === 'entries') { url = `https://api.dtf.ru/v2.5/content/${entity_response_json.result.items[k].data.id}/reactions?type=${counters[c].id}&page=0&limit=5000`; } if (entity.type === 'comments') { url = `https://api.dtf.ru/v2.5/comment/${entity_response_json.result.items[k].id}/reactions?type=${counters[c].id}&page=0&limit=5000`; } const reactions_response = await fetch( url, { method: 'get', headers: new Headers({ 'JWTAuthorization': `Bearer ${dtf_api.access_token}` }) } ); reactions_response_json = await reactions_response.json(); if (reactions_response_json?.error?.code === 401) { await dtf_api.refresh(); } else { break; } } for (let r = 0; r < reactions_response_json.result.reactions.length; r++) { let user = users.get(reactions_response_json.result.reactions[r].user.id); if (user) { let old_count_val = user.reactions[entity.type].count[reactions_response_json.result.reactions[r].reactionId].counter; user.reactions[entity.type].count[reactions_response_json.result.reactions[r].reactionId] = {id: reactions_response_json.result.reactions[r].reactionId, counter: ++old_count_val}; user.reactions[entity.type].total++; } else { user = { user : reactions_response_json.result.reactions[r].user, reactions : { entries: {count:[], total:0}, comments: {count:[], total:0}, all: {count:[], total:0} } } for (let k = 0; k < 55; k++) { user.reactions.entries.count[k]={id:k, counter:0}; user.reactions.comments.count[k]={id:k, counter:0}; user.reactions.all.count[k]={id:k, counter:0}; } user.reactions[entity.type].count[reactions_response_json.result.reactions[r].reactionId] = {id: reactions_response_json.result.reactions[r].reactionId, counter: 1}; user.reactions[entity.type].total = 1; } users.set(reactions_response_json.result.reactions[r].user.id, user); } } } status = `Считаем реакции. Последний ${entity.text} дата - ${format_ts(last_sorting_value)}`; update_form(); if (!last_id || !last_sorting_value) break; } } status = 'Готово!'; for (const [key, user] of users) { user.reactions.all.total = user.reactions.entries.total + user.reactions.comments.total; for (let r = 0; r < 55; r++) { user.reactions.all.count[r].counter = user.reactions.entries.count[r].counter + user.reactions.comments.count[r].counter; } users.set(key, user); } console.log(users); update_form(); function create_form() { let body = document.querySelector("body"); let results = document.createElement("div"); results.className = "results_form__wrapper"; body.innerHTML = ''; body.appendChild(results) results.innerHTML = ` <div class="results_form"> <div class="results_form_colomns__wrapper"> <div class="results_form_colomn"> <div id="results_form__status" ></div> <div id="results_form__table" ></div> </div> </div> </div> `; const style = document.createElement('style'); style.innerHTML = ` .results_form { width: 1880px; margin: 0 auto; padding: 20px; } .results_form_colomns__wrapper { display: flex; } .results_form_colomn{ display: flex; flex-direction: column; margin: 0 auto; } .results_form h1 { text-align:center; font-size:30px; margin: 20px 0; } .results_form .table { font-size: 15px; width: 1880px; margin: 0 0 30px 0; } .table__content { display: flex; flex-direction: column; } .table__row { display: flex; align-items: center; padding: 5px 0; } .table__cell { padding: 3px; display: flex; } .table__cell img { width: 30px; height: 30px; border-radius: 15px; } .table__cell span { line-height: 30px; } .table__cell a { height: 30px; } .avatar { float:left; margin: 0 4px 0 0; } .username { width: 225px; overflow: hidden; white-space: nowrap; display: inline-block; } .reactions { display: flex; gap: 8px; height: 30px; overflow: hidden; } .reaction { display: flex; flex-wrap: inherit; width: 85px; overflow: hidden; height: 30px; } .reaction span { width: 50px; text-align:right; } .reaction img { margin: 0 0 0 5px; } `; document.head.appendChild(style); } function update_form() { results_form__status.innerHTML= `<p>${status}<p>`; //копируем и сортируем массив const copy_users = structuredClone(users); const entities = [ {type:'entries', text: 'посты'}, {type:'comments', text: 'комментарии'}, {type:'all', text: 'всё'} ] let sorted_users = {}; for (let i=0; i<entities.length; i++) { const entity_type = entities[i].type; sorted_users[entity_type] = [...copy_users.values()]; sorted_users[entity_type].sort((a,b)=> (a.reactions[entity_type].total < b.reactions[entity_type].total ? 1 : -1)); sorted_users[entity_type] = sorted_users[entity_type].slice(0, max_users_in_results); for(let i=0; i<sorted_users[entity_type].length; i++) { sorted_users[entity_type][i].reactions[entity_type].count.sort((a,b)=> (a.counter < b.counter ? 1 : -1)); } } let innerHTML=''; for (const entity of entities) { innerHTML += ` <h1>Реакции ${entity.text}</h1> <div class="table"> <div class="table__row table__row--header"> <div class="table__cell" style="width: 30px; "> </div> <div class="table__cell" style="width: 300px; "> <strong>Юзер</strong> </div> <div class="table__cell" style="width: 1520px; "> <strong>Реакции</strong> </div> <div class="table__cell" style="width: 70px; "> <strong>Всего</strong> </div> </div> <div class="table__content__wrapper">`; for (let j = 0; j < sorted_users[entity.type].length; j++) { if (sorted_users[entity.type][j].reactions[entity.type].total === 0) break; const img_src =`https://leonardo.osnova.io/${sorted_users[entity.type][j].user.avatar.data.uuid}/-/scale_crop/50x50/-/format/webp/`; innerHTML += ` <div class="table__row"> <div class="table__cell" style="width: 30px; "> <span>${j + 1}</span> </div> <div class="table__cell" style="width: 300px; "> <a href="https://dtf.ru/id${sorted_users[entity.type][j].user.id}" target="_blank" > <img src="${img_src}" class="avatar" /><span class="username">${sorted_users[entity.type][j].user.name}</span> </a> </div> <div class="table__cell" style="width: 1520px; " > <div class="reactions" >`; for (let k = 0; k < sorted_users[entity.type][j].reactions[entity.type].count.length; k++) { if (sorted_users[entity.type][j].reactions[entity.type].count[k].counter === 0) break; innerHTML += ` <div class="reaction"> <span>${sorted_users[entity.type][j].reactions[entity.type].count[k].counter} X </span> <img src="${reactions_lib[sorted_users[entity.type][j].reactions[entity.type].count[k].id]}" /> </div>`; } innerHTML += ` </div> </div> <div class="table__cell" style="width: 70px; " > <span>${sorted_users[entity.type][j].reactions[entity.type].total}</span> </div> </div>`; } innerHTML += ` </div> </div>`; } results_form__table.innerHTML = innerHTML; } } //функция для паузы function delay(milliseconds){ return new Promise(resolve => { setTimeout(resolve, milliseconds); }); } function format_ts($ts) { const date = new Date($ts * 1000); const months = ['Янв','Фев','Мар','Апр','Май','Июн','Июл','Авг','Сен','Окт','Ноя','Дек']; const year = date.getFullYear(); const month = date.getMonth(); const day = date.getDate(); return (`${day}/${months[month]}/${year}`); } const reactions_lib = [ 'https://leonardo.osnova.io/abbcfa35-9c72-5cfb-9f6b-eff11b904d9c/format/raw/', //0 'https://leonardo.osnova.io/5c63be49-162a-5e4e-adca-9b9c3f76314c/format/raw/', //1 - сердце 'https://leonardo.osnova.io/b9e9a5d6-cfbc-5d11-9b31-edad6bb6fbf0/format/raw/', //2 - огонь 'https://leonardo.osnova.io/133c8232-843b-5f6b-80bd-f1f31d633892/format/raw/', //3 - смайл плач 'https://leonardo.osnova.io/15ad35e5-1708-58a5-a25a-d419cdd2d46a/format/raw/', //4 - смайл смех 'https://leonardo.osnova.io/0f3a998f-1441-5f0f-8a5b-549bbf170c65/format/raw/', //5 - смайл злость 'https://leonardo.osnova.io/2d62d1ab-8ec6-5f17-81f8-6f6f3312d283/format/raw/', //6 - пикачу 'https://leonardo.osnova.io/ec72865d-ec4e-5299-b763-628cfd2539af/format/raw/', //7 - пепе простой 'https://leonardo.osnova.io/f92b00e2-37b5-5102-a3ac-02c0ce846742/format/raw/', //8 - алмаз 'https://leonardo.osnova.io/080e8489-f354-52f3-b495-d3901aa329b3/format/raw/', //9 - попкорн 'https://leonardo.osnova.io/362a7194-57ee-5417-835e-bdc54d5394d4/format/raw/', //10 - ачивка на XBOX 'https://leonardo.osnova.io/b09f4923-5520-5ef9-b86d-668027a98d08/format/raw/', //11 - подсолнух с Стиме 'https://leonardo.osnova.io/5862140b-90b1-5c28-b0f0-8bab45beb587/format/raw/', //12 - платина на PS 'https://leonardo.osnova.io/9368c0d2-e9e3-55c8-b633-c44c82095226/format/raw/', //13 - Габен 'https://leonardo.osnova.io/6aa490dc-b161-57ac-ad47-1f6a4946b513/format/raw/', //14 'https://leonardo.osnova.io/898d07e7-06ea-5ff7-9ad6-8f74eb4e6f04/format/raw/', //15 - falloutboy 'https://leonardo.osnova.io/825e5ec2-bd20-5d7b-a681-f0fd66de0c21/format/raw/', //16 - press F 'https://leonardo.osnova.io/f8001ccc-dbc1-5c00-8991-d864aad61ef3/format/raw/', //17 - Тод Говард 'https://leonardo.osnova.io/55b61666-fa06-55cb-90d2-2a53ee2bf386/format/raw/', //18 - гигачад 'https://leonardo.osnova.io/ba93fedc-c5b7-5cf6-82c4-7cdb0fa6a6a2/format/raw/', //19 - Майкл (Офис) 'https://leonardo.osnova.io/ded55fdc-8ecf-5de9-9912-13e748bdc30f/format/raw/', //20 - злой кот 'https://leonardo.osnova.io/d9935395-45e1-5930-93cf-44581c2ce294/format/raw/', //21 - сосиска 'https://leonardo.osnova.io/7f766c9a-3720-5eaf-9a1a-3d0038876af7/format/raw/', //22 - смайл в очках 'https://leonardo.osnova.io/88faa3a8-281d-5f0d-8e9f-bd23d541d33b/format/raw/', //23 - смайл в покерфейс 'https://leonardo.osnova.io/36cfdc28-ced9-5e6f-8195-e75975bc9f31/format/raw/', //24 - глаза 'https://leonardo.osnova.io/8c9a90cd-e2ed-5cb5-a11c-7836039899a6/format/raw/', //25 - клоун 'https://leonardo.osnova.io/4f273793-7fbe-5b4f-9818-1d62885511b3/format/raw/', //26 - нож 'https://leonardo.osnova.io/abbcfa35-9c72-5cfb-9f6b-eff11b904d9c/format/raw/', //27 'https://leonardo.osnova.io/79146d35-4e27-50ac-be61-134925bb8c28/format/raw/', //28 'https://leonardo.osnova.io/8c0b9c07-6fe0-55f1-b485-c62d41484e57/format/raw/', //29 - губы 'https://leonardo.osnova.io/abbcfa35-9c72-5cfb-9f6b-eff11b904d9c/format/raw/', //30 'https://leonardo.osnova.io/49d316f2-0509-563f-9061-35fd33b3aa5e/format/raw/', //31 - баклажан 'https://leonardo.osnova.io/abbcfa35-9c72-5cfb-9f6b-eff11b904d9c/format/raw/', //32 'https://leonardo.osnova.io/0d857be0-89c8-5be7-a249-362169b87b17/format/raw/', //33 - Павел Дуров 'https://leonardo.osnova.io/abbcfa35-9c72-5cfb-9f6b-eff11b904d9c/format/raw/', //34 - звезда 'https://leonardo.osnova.io/b914b639-a8dc-5d61-a948-079c65b2d73e/format/raw/', //35 - медаль 'https://leonardo.osnova.io/1742f417-ca6b-507d-ad50-ce03ec7081d7/format/raw/', //36 - апплодисменты 'https://leonardo.osnova.io/57700d84-b04f-585e-a770-f7ca1fe4cd88/format/raw/', //37 - кубок 'https://leonardo.osnova.io/b86bdd6e-9266-5a7b-8d33-e3daa7b384ed/format/raw/', //38 - символ радиации 'https://leonardo.osnova.io/3ba9b4d2-2fa1-5a26-be63-9d8ff6f8e6a8/format/raw/', //39 - какашка 'https://leonardo.osnova.io/024ad1d7-5aa0-5c82-9e61-958b13b91357/format/raw/', //40 - пепе фейспалм 'https://leonardo.osnova.io/69d4c72c-f376-55f4-9b56-26c4f481ddfc/format/raw/', //41 - таблетка 'https://leonardo.osnova.io/008a7831-0ed0-5f56-9c23-f19947694efd/format/raw/', //42 - танчующий кот 'https://leonardo.osnova.io/d341555e-daab-55fc-8aef-3b3faa10f64d/format/raw/', //43 - кот на дискотеке 'https://leonardo.osnova.io/2e71ef83-dcfe-5cbb-90e8-f18ae8719f0b/format/raw/', //44 - кот обнимашки 'https://leonardo.osnova.io/549ac039-68ce-5af3-8f0a-c46b8bb1bfec/format/raw/', //45 - кот под музыкой 'https://leonardo.osnova.io/e9eb0f0e-5faf-5ce1-8345-466d740f711c/format/raw/', //46 - котенок держащий за голову 'https://leonardo.osnova.io/131196e5-28c2-5fee-a8d5-1c417873b56f/format/raw/', //47 - кристиан бейл 'https://leonardo.osnova.io/56eacecc-1101-5eb5-8653-af58dd28cae5/format/raw/', //48 - девочка 'https://leonardo.osnova.io/952c7d52-dae3-5d4c-9334-fd3875e8fd44/format/raw/', //49 - скала джонсон 'https://leonardo.osnova.io/2ae018a3-ece5-5754-a31b-934f221f820b/format/raw/', //50 - падающий со стула 'https://leonardo.osnova.io/4341f9aa-eea2-5103-8f61-203460eed05b/format/raw/', //51 - шрек 'https://leonardo.osnova.io/3b7d0935-fbdc-5f68-960f-5b36926a3f23/format/raw/', //52 - кролик 'https://leonardo.osnova.io/0cae5f9f-03d4-5e38-9627-7c9a77e4d563/format/raw/', //53 - чего? 'https://leonardo.osnova.io/abbcfa35-9c72-5cfb-9f6b-eff11b904d9c/format/raw/', //54 ]; class DTF_API { constructor(user_login, user_password, _log) { this.user_login = user_login; this.user_password = user_password; this.access_token = null; this.refresh_token = null; this.refresh_interval = null; this._log = _log; } async init(user_login, user_password, _log) { this.user_login = user_login; this.user_password = user_password; this.access_token = null; this.refresh_token = null; this.refresh_interval = null this._log = _log; } //авторизация аккаунта async auth() { return new Promise( async (resolve, reject) => { const this_ = this; let login; while(!login?.data?.accessToken || !login?.data?.refreshToken) { const response = await this_.auth_email_login(this_.user_login, this_.user_password).catch(function (error) { this_._log(error); }); login = await response.json(); } console.log('!!! Авторизовались'); this_.access_token = login.data.accessToken; this_.refresh_token = login.data.refreshToken; //clearInterval(this.refresh_interval); //this.refresh_interval = setInterval(()=>{this.refresh().catch(function (error) {this_._log(error);})}, 240000); //4 min resolve(); }); } async refresh() { return new Promise( async (resolve, reject) => { const this_ = this; let login; const response = await this_.auth_refresh().catch(function (error) { this_._log(error); }); login = await response.json(); if (!login?.data?.accessToken || !login?.data?.refreshToken) { return reject(false); } console.log('!!! Обновили токен'); this_.access_token = login.data.accessToken; this_.refresh_token = login.data.refreshToken; resolve(true); }); } //авторизация через логин/пароль async auth_email_login(email, password) { return new Promise( async (resolve, reject) => { const this_ = this; const post_data = new FormData(); post_data.append("email", email); post_data.append("password", password); await this_.delay(1000); await fetch('https://api.dtf.ru/v3.4/auth/email/login', { method: 'post', body: post_data }) .then(async function (response) { if (response.status === 429) { this_._log('auth_email_login 429 wait 10 min'); await this_.delay(600000); //10 min = 10*60*1000 ms } if (response.status === 200) { return resolve(response); } }) .catch(async function(error) { console.error(error); reject(`auth_email_login request error: ${error}`); }); }); } //рефреш авторизации async auth_refresh() { return new Promise( async (resolve, reject) => { const this_ = this; const post_data = new FormData(); post_data.append("token", this.refresh_token); await this_.delay(1000); await fetch( 'https://api.dtf.ru/v3.4/auth/refresh', { method: 'post', body: post_data }) .then(function (response) { resolve(response); }) .catch(async function(error) { reject(`auth_refresh request error: ${error}`); }); }); } //пауза async delay(milliseconds) { return new Promise(resolve => { setTimeout(resolve, milliseconds); }); } } main();

Из забавного:

[Скрипты] Симпометр - 2025
А чего добился ты? 
А чего добился ты? 

Топ-100 моих симпов

[Скрипты] Симпометр - 2025

P.S. Вот такие вот штуки хотелось бы в плюс подписке, а не урезание существующих функций для неплюсовых.

35
15
13
6
3
1
106 комментариев