[Скрипты] Симпометр - 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();
Из забавного:
А чего добился ты?
Топ-100 моих симпов
P.S. Вот такие вот штуки хотелось бы в плюс подписке, а не урезание существующих функций для неплюсовых.
106 комментариев