Список пользователей

1
Админ
Постов: 101
2
Элита
Постов: 34
3
Элита
Постов: 28
4
VIP
Постов: 26
5
Дизайнер
Постов: 25
6
Пользователи
Постов: 25
7
Пользователи
Постов: 24
8
Проверенные
Постов: 21

  • Страница 1 из 1
  • 1
Скрипт ночной охотник за авами-продвинутый с проверкой V2
Дата: Пятница, 31.10.2025, 17:34 | Сообщение # 1 | | Написал: Начинающий
Автор темы
Мурчанн не в сети
        Сообщений:101
         Регистрация:20.10.2016

Скрипт предназначен для автоматического отображения аватарок пользователей форума (или комментариев) даже в тех местах, где стандартный движок их не показывает. Он находит ник пользователя на странице, подгружает его профиль и извлекает ссылку на изображение-аватар, после чего вставляет её прямо в интерфейс.

Старая версия скрипта здесь

Ночной охотник за аватарками - продвинутый стратегический скрипт

Что делает:

1. Находит ник последнего комментатора и пытается подгрузить его аватар.
2. Использует кеш (localStorage), карту userMap и парсинг профилей.
3. Перебирает несколько кандидатных URL и резервных селекторов.
4. Фильтрует дефолтные/системные картинки, ставит только корректные авы.

Поведение:

1. Работает автономно, устойчив к неудачам и CORS; логирует процесс в консоль.
2. Логика подробно описана в теле скрипта - читайте, чтобы понять, как он достигает цели.

Лозунг: Работает по ночам, находит всегда.

Добавлено

Кэширование результата.

Чтобы не перегружать сервер, скрипт сохраняет результат в LocalStorage браузера.
Это значит, что при повторных загрузках страницы он не делает новые запросы, а использует сохранённые данные.
Благодаря этому страница грузится быстрее, а сервер не получает лишних обращений.

Интеллектуальное обновление кэша.

В усовершенствованной версии добавлен таймер жизни кэша (TTL) — например, 6 часов.
Если прошло больше времени, чем указано, или у пользователя раньше не было аватарки, скрипт проверяет данные заново.
Это позволяет автоматически подгружать новые аватары, если пользователь их сменил.

Визуальное применение.
После получения ссылки на изображение скрипт вставляет в нужный элемент страницы — например, рядом с ником, в карточку пользователя или в блок комментариев.

Код
<script>
/*
Авто-подстановка аватаров — улучшенная отладочная версия (v3.5)
- добавлено авто-обновление кэша каждые 6 часов (TTL)
- повторная проверка, если в кэше была дефолтная аватарка
- расширенный лог для диагностики
*/

(function(){
'use strict';

const DEFAULT_AVATAR = '/template/amg/images/img/noava.png';
const CHECK_DELAY = 160;
const CACHE_KEY = 'autoAvatar_user_cache_v3_debug';
const CACHE_TTL = 6 * 60 * 60 * 1000; // 6 часов
const now = Date.now();

let cache = JSON.parse(localStorage.getItem(CACHE_KEY) || '{}');

// очистка устаревших записей
for (const key in cache) {
   const item = cache[key];
   if (item && item.ts && now - item.ts > CACHE_TTL) {
     console.log('autoAvatar: expired cache for', key);
     delete cache[key];
   }
}

const userMap = {
   "admin": "#",
   "Модератор": "#",
   "Ник": "#",
   "Ник": "#",
   "Ник": "#",
   "Ник": "#",
   "Ник": "#",
   "Ник": "ссылка на аву",
   "NoName": "https://jordan.moy.su/template/amg/images/img/noava.png"
};

function saveCache(){ localStorage.setItem(CACHE_KEY, JSON.stringify(cache)); }

function normalizeKey(name){ return (name || '').trim().toLowerCase(); }

function findAvatarSrcInDoc(doc){
   const selectors = [
     '.user_avatar img',
     'img[src*="/avatar/"]',
     'img[src*="/ava/"]',
     '.ipsUserPhoto img',
     '.ipsUserPhotoLink img',
     'img.avatar',
     'img.profile-avatar',
     '.profile-avatar img'
   ];
   for(const s of selectors){
     const el = doc.querySelector(s);
     if(el && el.src){
       const src = el.src;
       if(!/noava|default|guest|anon|empty/i.test(src)){
         return new URL(src, location.href).href;
       }
     }
   }
   return null;
}

async function fetchAndFindAvatar(url){
   try{
     const res = await fetch(url, { cache: 'no-cache' });
     if(!res.ok){
       console.debug('autoAvatar: fetch returned not ok', url, res.status);
       return null;
     }
     const txt = await res.text();
     const parser = new DOMParser();
     const doc = parser.parseFromString(txt, 'text/html');
     return findAvatarSrcInDoc(doc);
   }catch(e){
     console.warn('autoAvatar: fetch failed (CORS/network?)', url, e);
     return null;
   }
}

function buildProfileCandidates(href, originalUsername){
   const candidates = [];
   if(href){
     try{ candidates.push(new URL(href, location.href).href); }catch(e){}
   }
   if(originalUsername){
     const safe = encodeURIComponent(originalUsername.replace(/\s+/g, '-'));
     candidates.push(location.origin + '/index/8-0-' + safe);
     candidates.push(location.origin + '/index/8-0-' + originalUsername);
     candidates.push(location.origin + '/user/' + originalUsername);
     candidates.push(location.origin + '/users/' + originalUsername);
   }
   return Array.from(new Set(candidates)).filter(Boolean);
}

function getLastCommentInfo(topicEl){
   const lastBlock = topicEl.querySelector('.last_post, .lastpost, .last-post');
   let anchor = null;
   if(lastBlock){
     anchor = lastBlock.querySelector('.uLPost, a, .lastpost_author a, .username, .user a');
   }
   if(!anchor){
     anchor = topicEl.querySelector('.last_post .uLPost, .last_post a, .uLPost, .last_post span a, a[href*="/index/8-"], a[href*="/user/"], .last_user a, .lastpost_author a, .username a');
   }
   if(!anchor) return null;
   const username = (anchor.textContent || '').trim();
   const href = anchor.getAttribute('href') || null;
   return { username: username || null, href: href };
}

function findAvatarImgElement(topicEl){
   let lastBlock = topicEl.querySelector('.last_post, .lastpost, .last-post');
   if(lastBlock){
     const img = lastBlock.querySelector('img[class*="avatar"], img.ipsUserPhoto, .user_avatar img, img.profile-avatar');
     if(img) return img;
   }
   const uL = topicEl.querySelector('.uLPost, .lastpost_author, .last_user');
   if(uL){
     const maybe = uL.querySelector('img[class*="avatar"], img.ipsUserPhoto, img.profile-avatar');
     if(maybe) return maybe;
   }
   const infoAnchor = topicEl.querySelector('.last_post a, .last_post .uLPost, .lastpost_author a, .last_user a, a[href*="/index/8-"], a[href*="/user/"]');
   if(infoAnchor){
     let p = infoAnchor.parentElement;
     for(let i=0;i<4 && p;i++, p = p.parentElement){
       const img = p.querySelector('img[class*="avatar"], img.ipsUserPhoto, img.profile-avatar, .user_avatar img');
       if(img) return img;
     }
   }
   return null;
}

async function handleTopic(topicEl){
   try{
     const avatarImg = findAvatarImgElement(topicEl);
     if(!avatarImg){
       console.debug('autoAvatar: avatar element not found for topic', topicEl);
       return;
     }
     if(avatarImg.dataset.autoAvatarDone) return;
     avatarImg.dataset.autoAvatarDone = '1';

     const info = getLastCommentInfo(topicEl);
     if(!info || !info.username){
       console.debug('autoAvatar: no last author info for topic');
       return;
     }

     const originalUsername = info.username;
     const cacheKey = normalizeKey(originalUsername);
     console.debug('autoAvatar: processing', originalUsername);

     try{
       const cur = avatarImg.getAttribute('src') || avatarImg.src;
       if(cur && !/noava|default|guest|anon|empty/i.test(cur)){
         if(!cache[cacheKey]){ cache[cacheKey] = { url: cur, ts: now }; saveCache(); }
         return;
       }
     }catch(e){}

     // 1) кэш
     if(cache[cacheKey]){
       const cached = cache[cacheKey].url || cache[cacheKey];
       if(!/noava|default|guest|anon|empty/i.test(cached)){
         avatarImg.src = cached;
         console.info('autoAvatar: set from cache', originalUsername, cached);
         return;
       } else {
         console.info('autoAvatar: cached default avatar, rechecking...', originalUsername);
       }
     }

     // 2) userMap
     if(userMap[cacheKey]){
       avatarImg.src = userMap[cacheKey];
       cache[cacheKey] = { url: userMap[cacheKey], ts: now };
       saveCache();
       console.info('autoAvatar: set from userMap', originalUsername, userMap[cacheKey]);
       return;
     }

     // 3) пробуем fetch по кандидатам
     const candidates = buildProfileCandidates(info.href, originalUsername);
     for(const c of candidates){
       console.debug('autoAvatar: trying', c);
       const found = await fetchAndFindAvatar(c);
       if(found){
         avatarImg.src = found;
         cache[cacheKey] = { url: found, ts: now };
         saveCache();
         console.info('autoAvatar: found avatar for', originalUsername, found);
         return;
       }
       await new Promise(r => setTimeout(r, CHECK_DELAY));
     }

     // 4) если всё не удалось - дефолт
     avatarImg.src = DEFAULT_AVATAR;
     cache[cacheKey] = { url: DEFAULT_AVATAR, ts: now };
     saveCache();
     console.warn('autoAvatar: placed default avatar for', originalUsername);

   }catch(e){
     console.error('autoAvatar: handleTopic error', e);
   }
}

async function processAll(root = document){
   const topics = Array.from(root.querySelectorAll(
     '.__topic, tr.__topic, .bb_box .ipb_table .__topic, .topic, .forum-row, .threads-list-item'
   ));
   for(const t of topics){
     await handleTopic(t);
     await new Promise(r=>setTimeout(r, CHECK_DELAY));
   }
}

if(document.readyState === 'loading'){
   document.addEventListener('DOMContentLoaded', ()=>{ processAll().catch(console.error); });
} else {
   setTimeout(()=> processAll().catch(console.error), 20);
}

const obs = new MutationObserver(muts=>{
   for(const m of muts){
     for(const n of m.addedNodes || []){
       if(n.nodeType === 1){
         processAll(n).catch(console.error);
       }
     }
   }
});
obs.observe(document.body, { childList: true, subtree: true });

window.autoAvatarDebug = { cache, saveCache };
})();
</script>


Преимущества

1. Автоматическая подгрузка не нужно вручную прописывать аватары.

2 Быстродействие благодаря кэшированию не создаёт лишней нагрузки.

3. Умное обновление если у пользователя появилась новая аватарка, она подтянется автоматически.

3. Гибкость можно адаптировать под любой HTML-шаблон форума или каталога.

Мурчанн

Признаюсь, не знаю почему, но глядя на звезды мне всегда хочется мечтать.
  • Страница 1 из 1
  • 1
Поиск: