Skip to content

Commit

Permalink
refactor(OnlineAdmins): add query cache (#1689)
Browse files Browse the repository at this point in the history
* refactor(OnlineAdmins): add query cache
  • Loading branch information
WaitSpringQW authored Jan 30, 2025
1 parent f4f1233 commit e3c2caf
Show file tree
Hide file tree
Showing 7 changed files with 296 additions and 228 deletions.
204 changes: 117 additions & 87 deletions dist/OnlineAdmins/OnlineAdmins.js

Large diffs are not rendered by default.

155 changes: 22 additions & 133 deletions src/OnlineAdmins/modules/doClick.ts
Original file line number Diff line number Diff line change
@@ -1,152 +1,41 @@
import {groupListElement, listTitle} from './components/groupList';
import {BLACK_LIST} from './constant';
import {api} from './api';
import {getAdmins} from './util/getAdmins';
import {getMessage} from './i18n';
import {uniqueArray} from 'ext.gadget.Util';

const queryRecentChanges = async (rcstart: string, rcend: string) => {
const params: ApiQueryRecentChangesParams = {
rcstart,
rcend,
action: 'query',
format: 'json',
formatversion: '2',
list: 'recentchanges',
rcprop: 'user',
rcshow: ['!bot', '!anon'],
rclimit: 500,
smaxage: 600,
maxage: 600,
};
const response = await api.get(params);

return response;
};

const queryLogEvents = async (lestart: string, leend: string) => {
const params: ApiQueryLogEventsParams = {
lestart,
leend,
action: 'query',
format: 'json',
formatversion: '2',
list: 'logevents',
leprop: 'user',
lelimit: 500,
smaxage: 600,
maxage: 600,
};
const response = await api.get(params);

return response;
};

const queryUserProps = async (ususers: string | string[]) => {
const params: ApiQueryUsersParams = {
ususers,
action: 'query',
format: 'json',
formatversion: '2',
list: 'users',
usprop: 'groups',
smaxage: 600,
maxage: 600,
};
const response = await api.get(params);

return response;
};

const doClick = async (event: JQuery.ClickEvent<HTMLAnchorElement>): Promise<void> => {
event.preventDefault();

let usersAll: string[] = [];
const usersRC: string[] = [];
const usersLE: string[] = [];
const stewards: string[] = [];
const admins: string[] = [];
const patrollers: string[] = [];

const time: Date = new Date();
const rcstart: string = time.toISOString();
time.setMinutes(time.getMinutes() - 30); // 最近更改30分钟内的编辑用户
const rcend: string = time.toISOString();
const notify = (element: string | JQuery<HTMLElement>, type: 'error' | 'info' | 'success' | 'warn' = 'info') => {
return mw.notify(element, {
tag: 'onlineAdmins',
type,
});
};

try {
const recentchanges = await queryRecentChanges(rcstart, rcend);

for (const {user} of recentchanges['query'].recentchanges as {user: string}[]) {
usersRC[usersRC.length] = user; // Replace Array#push to avoid core-js polyfilling
}

const logevents = await queryLogEvents(rcstart, rcend);

for (const {user} of logevents['query'].logevents as {user: string}[]) {
usersLE[usersLE.length] = user;
}

// 用户名列表合并、去重、分割
usersAll = uniqueArray([...usersRC, ...usersLE]); // Replace Set with uniqueArray, avoiding core-js polyfilling
const {stewards, sysops, patrollers} = await getAdmins();

const promises: (() => Promise<void>)[] = [];
if (stewards.length + sysops.length + patrollers.length) {
const elements: Element[] = [listTitle()];

for (let i = 0; i < usersAll.length; i++) {
const ususers = usersAll.splice(0, 25);
if (!ususers.length) {
continue;
if (stewards.length) {
elements[elements.length] = groupListElement(getMessage('Stewards'), stewards);
}

promises[promises.length] = async (): Promise<void> => {
const response = await queryUserProps(ususers);

for (const {groups, name} of response['query'].users as {groups: string[]; name: string}[]) {
// 找到管理人员,去除机器人,消除name的空值
if (groups.includes('bot') || BLACK_LIST.includes(name) || !name) {
continue;
}
if (groups.includes('steward')) {
stewards[stewards.length] = name;
}
if (groups.includes('sysop')) {
admins[admins.length] = name;
}
if (groups.includes('patroller')) {
patrollers[patrollers.length] = name;
}
}
};
}

void (async () => {
// 查询用户权限
for (const promise of promises) {
try {
await promise();
} catch {}
if (sysops.length) {
elements[elements.length] = groupListElement(getMessage('SysOps'), sysops);
}
})().then(() => {
if (stewards.length + admins.length + patrollers.length) {
const elements: Element[] = [listTitle()];

if (stewards.length) {
elements[elements.length] = groupListElement(getMessage('Stewards'), stewards);
}
if (admins.length) {
elements[elements.length] = groupListElement(getMessage('Admins'), admins);
}
if (patrollers.length) {
elements[elements.length] = groupListElement(getMessage('Patrollers'), patrollers);
}
void mw.notify($('<div>').append(elements), {tag: 'onlineAdmins'});
} else {
void mw.notify(getMessage('NoOnline'), {
tag: 'onlineAdmins',
type: 'warn',
});
if (patrollers.length) {
elements[elements.length] = groupListElement(getMessage('Patrollers'), patrollers);
}
});

void notify($('<div>').append(elements));
} else {
void notify(getMessage('NoOnline'), 'warn');
}
} catch {
void mw.notify(getMessage('Network error'), {tag: 'onlineAdmins', type: 'error'});
void notify(getMessage('Network error'), 'error');
}
};

Expand Down
12 changes: 6 additions & 6 deletions src/OnlineAdmins/modules/i18n.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,6 @@ const getI18nMessages = () => {
'zh-hans': '($1个在线):',
'zh-hant': '($1個在線):',
}),
Admins: localize({
en: 'Admins',
ja: '管理者',
'zh-hans': '管理员',
'zh-hant': '管理員',
}),
Patrollers: localize({
en: 'Patrollers',
ja: '巡回者',
Expand Down Expand Up @@ -50,6 +44,12 @@ const getI18nMessages = () => {
'zh-hans': '下面是最近30分钟内在线的站务人员:',
'zh-hant': '下面是最近30分鐘內的線上站務人員:',
}),
SysOps: localize({
en: 'Admins',
ja: '管理者',
'zh-hans': '管理员',
'zh-hant': '管理員',
}),
};
};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import * as OPTIONS from '../options.json';
import * as OPTIONS from '../../options.json';
import {initMwApi} from 'ext.gadget.Util';

const api: mw.Api = initMwApi(`OnlineAdmins/${OPTIONS.version}`);
Expand Down
92 changes: 92 additions & 0 deletions src/OnlineAdmins/modules/util/getAdmins.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import * as OPTIONS from '../../options.json';
import {queryLogEvents, queryRecentChanges, queryUserProps} from './query';
import {BLACK_LIST} from '../constant';
import {uniqueArray} from 'ext.gadget.Util';

const getAdmins = async () => {
let stewards: string[] = [];
let sysops: string[] = [];
let patrollers: string[] = [];

if (mw.storage.getObject(OPTIONS.storageKey)) {
({stewards, sysops, patrollers} = mw.storage.getObject(OPTIONS.storageKey) as {
stewards: string[];
sysops: string[];
patrollers: string[];
});
} else {
let users: string[] = [];
const promises: (() => Promise<void>)[] = [];

const time: Date = new Date();
const rcstart: string = time.toISOString();

// Query users has log/edit record in recent 30 minutes
time.setMinutes(time.getMinutes() - 30); // 最近更改30分钟内的编辑用户
const rcend: string = time.toISOString();

try {
const recentchanges = await queryRecentChanges(rcstart, rcend);

for (const {user} of recentchanges['query'].recentchanges as {user: string}[]) {
users[users.length] = user; // Replace Array#push to avoid core-js polyfilling
}
} catch {}

try {
const logevents = await queryLogEvents(rcstart, rcend);

for (const {user} of logevents['query'].logevents as {user: string}[]) {
users[users.length] = user;
}
} catch {}

users = uniqueArray(users); // Replace Set with uniqueArray, avoiding core-js polyfilling

// Generating query promises
for (let i = 0; i < users.length; i++) {
const ususers = users.splice(0, 25);
if (!ususers.length) {
continue;
}

promises[promises.length] = async (): Promise<void> => {
const response = await queryUserProps(ususers);

for (const {groups, name} of response['query'].users as {groups: string[]; name: string}[]) {
// remove bots
if (groups.includes('bot') || BLACK_LIST.includes(name)) {
continue;
}

// remove logs with no user names
if (!name) {
continue;
}

if (groups.includes('steward')) {
stewards[stewards.length] = name;
}

if (groups.includes('sysop')) {
sysops[sysops.length] = name;
}

if (groups.includes('patroller')) {
patrollers[patrollers.length] = name;
}
}
};
}

for (const promise of promises) {
try {
await promise();
} catch {}
}
}

return {stewards, sysops, patrollers};
};

export {getAdmins};
56 changes: 56 additions & 0 deletions src/OnlineAdmins/modules/util/query.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import {api} from './api';

const queryRecentChanges = async (rcstart: string, rcend: string) => {
const params: ApiQueryRecentChangesParams = {
rcstart,
rcend,
action: 'query',
format: 'json',
formatversion: '2',
list: 'recentchanges',
rcprop: 'user',
rcshow: ['!bot', '!anon'],
rclimit: 500,
smaxage: 600,
maxage: 600,
};
const response = await api.get(params);

return response;
};

const queryLogEvents = async (lestart: string, leend: string) => {
const params: ApiQueryLogEventsParams = {
lestart,
leend,
action: 'query',
format: 'json',
formatversion: '2',
list: 'logevents',
leprop: 'user',
lelimit: 500,
smaxage: 600,
maxage: 600,
};
const response = await api.get(params);

return response;
};

const queryUserProps = async (ususers: string | string[]) => {
const params: ApiQueryUsersParams = {
ususers,
action: 'query',
format: 'json',
formatversion: '2',
list: 'users',
usprop: 'groups',
smaxage: 600,
maxage: 600,
};
const response = await api.get(params);

return response;
};

export {queryRecentChanges, queryLogEvents, queryUserProps};
3 changes: 2 additions & 1 deletion src/OnlineAdmins/options.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
{
"version": "2.0"
"version": "2.0",
"storageKey": "ext.gadget.OnlineAdmins_getAdmins"
}

0 comments on commit e3c2caf

Please sign in to comment.