User:Polygnotus/Scripts/XC.js: Difference between revisions

Content deleted Content added
No edit summary
error handling and caching
Line 15:
}
console.log('Running on talk page');
 
// Cache handling
const CACHE_KEY = 'ec-status-cache';
const CACHE_EXPIRY = 24 * 60 * 60 * 1000; // 24 hours
function loadCache() {
try {
const cache = localStorage.getItem(CACHE_KEY);
if (cache) {
const { data, timestamp } = JSON.parse(cache);
if (Date.now() - timestamp < CACHE_EXPIRY) {
return new Map(Object.entries(data));
}
}
} catch (e) {
console.error('Error loading cache:', e);
}
return new Map();
}
 
function saveCache(cache) {
try {
const cacheData = {
data: Object.fromEntries(cache),
timestamp: Date.now()
};
localStorage.setItem(CACHE_KEY, JSON.stringify(cacheData));
} catch (e) {
console.error('Error saving cache:', e);
}
}
 
const processedUsers = new Set();
const userGroups = new MaploadCache();
// Find all user links in signatures
Line 42 ⟶ 73:
console.log('Fetching groups for users:', userList);
tryconst {maxRetries = 3;
let const responseretryCount = await $.ajax({0;
let delay = 1000; // Start with 1 url:second mw.util.wikiScript('api'),delay
data: {
while (retryCount < maxRetries) {
action: 'query',
try format: 'json',{
const response = await list: 'users',$.ajax({
uspropurl: mw.util.wikiScript('groupsapi'),
ususersdata: userList,{
formatversion action: '2query',
} format: 'json',
dataType list: 'jsonusers',
}); usprop: 'groups|blockinfo',
ususers: userList,
formatversion: '2'
},
dataType: 'json'
});
 
console.log('API response:', response);
 
if (response.queryerror && response.queryerror.userscode === 'ratelimited') {
response console.query.users.forEachlog(user'Rate =>limited, {waiting before retry...');
constawait groupsnew =Promise(resolve user.groups=> ||setTimeout(resolve, []delay));
const isECdelay *= groups.includes('extendedconfirmed')2; // Exponential backoff
console.log(`User ${user.name}: EC=${isEC}, groups=${groups.join(',')}`)retryCount++;
userGroups.set(user.name, isEC)continue;
});
 
if (response.query && response.query.users) {
response.query.users.forEach(user => {
let status;
if (user.missing) {
status = 'missing';
} else if (user.blockedby) {
status = 'blocked';
} else {
const groups = user.groups || [];
status = groups.includes('extendedconfirmed') ? 'extended' : 'normal';
}
userGroups.set(user.name, status);
});
// Save updated cache
saveCache(userGroups);
}
break; // Success, exit retry loop
} catch (error) {
console.error('Error fetching user groups:', error);
if (retryCount >= maxRetries - 1) {
// Mark all users in batch as error if we've exhausted retries
users.forEach(username => userGroups.set(username, 'error'));
saveCache(userGroups);
} else {
await new Promise(resolve => setTimeout(resolve, delay));
delay *= 2; // Exponential backoff
retryCount++;
}
}
} catch (error) {
console.error('Error fetching user groups:', error);
}
}
 
// Add status indicator next to username
function addStatusIndicator(link, isExtendedConfirmedstatus) {
// Remove any existing indicators next to this link
$(link).siblings('.ec-status-indicator').remove();
let symbol, color, title;
switch(status) {
case 'extended':
symbol = '✔';
color = '#00a000';
title = 'Extended confirmed user';
break;
case 'error':
symbol = '?';
color = '#666666';
title = 'Error checking status';
break;
case 'blocked':
symbol = '🚫';
color = '#cc0000';
title = 'Blocked user';
break;
case 'missing':
symbol = '!';
color = '#666666';
title = 'User not found';
break;
default:
symbol = '✘';
color = '#cc0000';
title = 'Not extended confirmed';
}
const indicator = $('<span>')
Line 81 ⟶ 174:
'margin-left': '4px',
'font-size': '0.85em',
'color': isExtendedConfirmed ? '#00a000' : '#cc0000'color, // Green and red colors
'cursor': 'help'
})
.attr('title', isExtendedConfirmed ? title)
'Extended confirmed user' : .text(symbol);
'Not extended confirmed')
.text(isExtendedConfirmed ? '✔' : '✘');
$(link).after(indicator);