Последние темы информер на форуме Обычно многие, кто пытался создать такой скрипт, сталкивались с проблемами. Я решил показать, что это возможно. Этот скрипт достаточно умный, он может сам переходить в профиль, брать оттуда нужную аватарку и подставлять её в информер.
Логика в самом скрипте
Код
<script> /* Последние темы информер Подставляет аватар последнего комментатора. Логика: - берём ник последнего комментатора из .last_post .uLPost (или похожих селекторов) - проверяем кеш localStorage - пробуем fetch по href последнего коммента (и по варианту /index/8-0-USERNAME) - парсим страницу профиля и ищем <img> где src содержит /avatar/ или /ava/ - если нашли — ставим и кешируем; если нет — смотрим userMap; если и там нет — дефолт */ (function(){ 'use strict';
const DEFAULT_AVATAR = '/Online/ico/p_profile.gif'; // или мой /ava/default.gif const CHECK_DELAY = 160; // ms между fetch, чтобы не гонять сервер const CACHE_KEY = 'autoAvatar_user_cache_v2'; const cache = JSON.parse(localStorage.getItem(CACHE_KEY) || '{}');
// Моя готовая карта — вставить осталось полный userMap, если он есть const userMap = { "Ник": "#", "Ник": "#", "Ник": "#", "Ник": "#" // ...добавь свой полный список };
function saveCache(){ localStorage.setItem(CACHE_KEY, JSON.stringify(cache)); }
// Ищем непосредственно подходящую аватарку в документе (profile page) function findAvatarSrcInDoc(doc){ const selectors = [ 'img[src*="/avatar/"]', 'img[src*="/ava/"]', '.user_avatar img', '.ipsUserPhoto img', '.ipsUserPhotoLink img', 'img.avatar' ]; for(const s of selectors){ const el = doc.querySelector(s); if(el && el.src && (el.src.indexOf('/avatar/') !== -1 || el.src.indexOf('/ava/') !== -1)){ return new URL(el.getAttribute('src'), location.href).href; } } return null; }
// Генерация кандидатов url для профиля по нику/существующей href function buildProfileCandidates(href, username){ const candidates = []; if(href){ try{ candidates.push(new URL(href, location.href).href); }catch(e){} } if(username){ // uCoz часто использует /index/8-0-USERNAME const safe = encodeURIComponent(username.replace(/\s+/g, '-')); candidates.push(location.origin + '/index/8-0-' + safe); // ещё вариант с без кодирования (иногда работает) candidates.push(location.origin + '/index/8-0-' + username); } // убрать дубли return Array.from(new Set(candidates)).filter(Boolean); }
// Извлечь ник и href последнего комментатора из блока темы function getLastCommentInfo(topicEl){ // главный кандидат: .last_post .uLPost (как в моём примере) let a = topicEl.querySelector('.last_post .uLPost, .last_post a, .uLPost, .last_post span a'); if(!a){ // ещё возможные селекторы (защита) a = topicEl.querySelector('a[href*="/index/8-"], a[href*="/user/"], .last_user a, .lastpost_author a'); } if(!a) return null; const username = (a.textContent || '').trim(); const href = a.getAttribute('href') || null; return { username: username || null, href: href }; }
// Основная обработка одной темы async function handleTopic(topicEl){ try{ const avatarImg = topicEl.querySelector('img.ipsUserPhoto, img[class*="avatar"], .ipsUserPhoto img'); if(!avatarImg) return; if(avatarImg.dataset.autoAvatarDone) return; avatarImg.dataset.autoAvatarDone = '1';
const info = getLastCommentInfo(topicEl); if(!info || !info.username){ // нечего искать — оставляем как есть //console.debug('autoAvatar: no last author found for topic', topicEl); return; } const uname = info.username; console.debug('autoAvatar: обработка ника', uname);
// 2) если в userMap есть — ставим сразу (переходим в конец) if(userMap[uname]){ avatarImg.src = userMap[uname]; cache[uname] = userMap[uname]; saveCache(); console.info('autoAvatar: from userMap', uname, userMap[uname]); return; }
// 3) пробуем fetch по кандидатурам (сначала href, потом /index/8-0-USERNAME) const candidates = buildProfileCandidates(info.href, uname); for(const c of candidates){ console.debug('autoAvatar: пробуем профиль', c); const found = await fetchAndFindAvatar(c); if(found){ avatarImg.src = found; cache[uname] = found; saveCache(); console.info('autoAvatar: найден по профилю', uname, found); return; } // небольшая пауза между попытками await new Promise(r => setTimeout(r, CHECK_DELAY)); }
// 4) если не нашлось — пробуем искать профиль по /index/* (поиск по всему документу — редкий случай) // (опционально: можно реализовать поиск каталога пользователей — но на uCoz это не всегда доступно)