mwn
Version:
JavaScript & TypeScript MediaWiki bot framework for Node.js
441 lines • 15.3 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = default_1;
const error_1 = require("./error");
function default_1(bot) {
class Page extends bot.Title {
constructor(title, namespace) {
// bot property is set by mwn#Page() method
if (typeof title === 'string') {
super(title, namespace);
}
else if (title.title && title.namespace) {
super(title.title, title.namespace);
}
else {
throw new Error('unknown constructor type for mwn Page');
}
}
/**
* @override
*/
getTalkPage() {
return new Page(super.getTalkPage());
}
/**
* @override
*/
getSubjectPage() {
return new Page(super.getSubjectPage());
}
/**** Get operations *****/
/** @inheritDoc */
exists() {
return bot
.query({
titles: this.toString(),
})
.then((data) => {
return data.query.pages[0].missing !== true;
});
}
/** @inheritDoc */
text() {
return bot
.query({
titles: this.toString(),
prop: 'revisions',
rvprop: 'content',
rvslots: 'main',
})
.then((data) => {
this.throwIfPageMissing(data);
return data.query.pages[0].revisions[0].slots.main.content;
});
}
/** @inheritDoc */
categories() {
return bot
.query({
titles: this.toString(),
prop: 'categories',
cllimit: 'max',
})
.then((data) => {
this.throwIfPageMissing(data);
return data.query.pages[0].categories.map((e) => e.title);
});
}
/** @inheritDoc */
templates() {
return bot
.query({
titles: this.toString(),
prop: 'templates',
tllimit: 'max',
})
.then((data) => {
this.throwIfPageMissing(data);
return data.query.pages[0].templates.map((e) => e.title);
});
}
/** @inheritDoc */
links() {
return bot
.query({
titles: this.toString(),
prop: 'links',
pllimit: 'max',
})
.then((data) => {
this.throwIfPageMissing(data);
return data.query.pages[0].links.map((e) => e.title);
});
}
/** @inheritDoc */
backlinks() {
return bot
.continuedQuery({
action: 'query',
prop: 'linkshere',
titles: this.toString(),
lhprop: 'title',
lhlimit: 'max',
})
.then((jsons) => {
let pages = jsons.reduce((pages, json) => pages.concat(json.query.pages), []);
return (pages[0].linkshere || []).map((pg) => pg.title);
});
}
/** @inheritDoc */
transclusions() {
return bot
.continuedQuery({
action: 'query',
prop: 'transcludedin',
titles: this.toString(),
tiprop: 'title',
tilimit: 'max',
})
.then((jsons) => {
let pages = jsons.reduce((pages, json) => pages.concat(json.query.pages), []);
return (pages[0].transcludedin || []).map((pg) => pg.title);
});
}
/** @inheritDoc */
images() {
return bot
.query({
titles: this.toString(),
prop: 'images',
imlimit: 'max',
})
.then((data) => {
this.throwIfPageMissing(data);
return data.query.pages[0].images.map((e) => e.title);
});
}
/** @inheritDoc */
externallinks() {
return bot
.query({
titles: this.toString(),
prop: 'extlinks',
ellimit: 'max',
})
.then((data) => {
this.throwIfPageMissing(data);
return data.query.pages[0].extlinks.map((e) => e.url);
});
}
/** @inheritDoc */
subpages(options) {
return bot
.query({
list: 'allpages',
apprefix: this.title + '/',
apnamespace: this.namespace,
aplimit: 'max',
...options,
})
.then((data) => {
return data.query.allpages.map((pg) => pg.title);
});
}
/** @inheritDoc */
isRedirect() {
return this.getRedirectTarget().then((target) => {
return this.toText() !== target;
});
}
/** @inheritDoc */
getRedirectTarget() {
return bot
.query({
titles: this.toString(),
redirects: true,
})
.then((data) => {
this.throwIfPageMissing(data);
return data.query.pages[0].title;
});
}
/** @inheritDoc */
getCreator() {
return bot
.query({
titles: this.toString(),
prop: 'revisions',
rvprop: 'user',
rvlimit: 1,
rvdir: 'newer',
})
.then((data) => {
this.throwIfPageMissing(data);
return data.query.pages[0].revisions[0].user;
});
}
/** @inheritDoc */
getDeletingAdmin() {
return bot
.request({
action: 'query',
list: 'logevents',
leaction: 'delete/delete',
letitle: this.toString(),
lelimit: 1,
})
.then((data) => {
let logs = data.query.logevents;
if (logs.length === 0) {
return null;
}
return logs[0].user;
});
}
/** @inheritDoc */
getDescription(customOptions) {
return bot
.query({
prop: 'description',
titles: this.toString(),
...customOptions,
})
.then((data) => {
this.throwIfPageMissing(data);
return data.query.pages[0].description;
});
}
/** @inheritDoc */
history(props, limit = 50, customOptions) {
return bot
.query({
prop: 'revisions',
titles: this.toString(),
rvprop: props || 'ids|timestamp|flags|comment|user',
rvlimit: limit || 50,
...customOptions,
})
.then((data) => {
this.throwIfPageMissing(data);
return data.query.pages[0].revisions;
});
}
async *historyGen(props, customOptions) {
let continuedQuery = bot.continuedQueryGen({
action: 'query',
prop: 'revisions',
titles: this.toString(),
rvprop: props || 'ids|timestamp|flags|comment|user',
rvlimit: 50,
...customOptions,
});
for await (let json of continuedQuery) {
for (let edit of json.query.pages[0].revisions) {
yield edit;
}
}
}
/** @inheritDoc */
logs(props, limit, type, customOptions) {
let logtypeObj = {};
if (type) {
logtypeObj = { [type.includes('/') ? 'leaction' : 'letype']: type };
}
return bot
.request({
action: 'query',
list: 'logevents',
...logtypeObj,
leprop: props || 'title|type|user|timestamp|comment',
letitle: this.toString(),
lelimit: limit || 50,
...customOptions,
})
.then((data) => {
return data.query.logevents;
});
}
async *logsGen(props, type, customOptions) {
let logtypeObj = {};
if (type) {
logtypeObj = { [type.includes('/') ? 'leaction' : 'letype']: type };
}
let continuedQuery = bot.continuedQueryGen({
action: 'query',
list: 'logevents',
...logtypeObj,
leprop: props || 'title|type|user|timestamp|comment',
letitle: this.toString(),
lelimit: 50,
...customOptions,
});
for await (let json of continuedQuery) {
for (let event of json.query.logevents) {
yield event;
}
}
}
throwIfPageMissing(data) {
if (data.query.pages[0].missing || data.query.pages[0].invalid) {
throw new error_1.MwnMissingPageError();
}
}
/** @inheritDoc */
async pageViews(options = {}) {
let project = bot.options.apiUrl.match(/.*\/(.*?)\.(?:org|com|net)/)?.[1];
if (!project) {
throw new Error('Invalid API URL for using pageViews(). Only Wikimedia wikis are supported.');
}
// Set defaults
let { access, agent, granularity, start, end } = options;
access = access || 'all-access';
agent = agent || 'all-agents';
granularity = granularity || 'monthly';
if (granularity === 'daily') {
let date = new bot.Date();
date.setUTCDate(date.getUTCDate() - 1);
start = start || date;
end = end || new bot.Date();
}
else if (granularity === 'monthly') {
let date = new bot.Date();
date.setUTCDate(1);
date.setUTCMonth(date.getUTCMonth() - 1);
start = start || date;
end =
end ||
(function () {
let d = new bot.Date();
d.setUTCDate(1);
return d;
})();
}
let startString = new bot.Date(start).format('YYYYMMDD'), endString = new bot.Date(end).format('YYYYMMDD');
return bot
.rawRequest({
url: `https://wikimedia.org/api/rest_v1/metrics/pageviews/per-article/${project}/${access}/${agent}/${encodeURIComponent(this.toString())}/${granularity}/${startString}/${endString}`,
headers: {
'User-Agent': bot.options.userAgent,
},
})
.then((response) => {
return response.data.items;
});
}
/** @inheritDoc */
async queryAuthors() {
let langcodematch = bot.options.apiUrl.match(/([^/]*?)\.wikipedia\.org/);
if (!langcodematch || !langcodematch[1]) {
throw new Error('WikiWho API is not supported for bot API URL. Re-check.');
}
let json;
try {
json = await bot
.rawRequest({
url: `https://wikiwho.wmflabs.org/${langcodematch[1]}/api/v1.0.0-beta/latest_rev_content/${encodeURIComponent(this.toString())}/?editor=true`,
headers: {
'User-Agent': bot.options.userAgent,
},
})
.then((response) => response.data);
}
catch (err) {
throw new Error(err && err.response && err.response.data && err.response.data.Error);
}
const tokens = Object.values(json.revisions[0])[0].tokens;
let data = {
totalBytes: 0,
users: [],
};
let userdata = {};
for (let token of tokens) {
data.totalBytes += token.str.length;
let editor = token['editor'];
if (!userdata[editor]) {
userdata[editor] = { bytes: 0 };
}
userdata[editor].bytes += token.str.length;
if (editor.startsWith('0|')) {
// IP
userdata[editor].name = editor.slice(2);
}
}
Object.entries(userdata).map(([userid, { bytes }]) => {
userdata[userid].percent = bytes / data.totalBytes;
if (userdata[userid].percent < 0.02) {
delete userdata[userid];
}
});
await bot
.request({
action: 'query',
list: 'users',
ususerids: Object.keys(userdata).filter((us) => !us.startsWith('0|')), // don't lookup IPs
})
.then((json) => {
json.query.users.forEach((us) => {
userdata[us.userid].name = us.name;
});
});
data.users = Object.entries(userdata)
.map(([userid, { bytes, name, percent }]) => {
return {
id: Number(userid),
name: name,
bytes: bytes,
percent: percent,
};
})
.sort((a, b) => {
return a.bytes < b.bytes ? 1 : -1;
});
return data;
}
/**** Post operations *****/
// Defined in bot.js
edit(transform) {
return bot.edit(this.toString(), transform);
}
save(text, summary, options) {
return bot.save(this.toString(), text, summary, options);
}
newSection(header, message, additionalParams) {
return bot.newSection(this.toString(), header, message, additionalParams);
}
move(target, summary, options) {
return bot.move(this.toString(), target, summary, options);
}
delete(summary, options) {
return bot.delete(this.toString(), summary, options);
}
undelete(summary, options) {
return bot.undelete(this.toString(), summary, options);
}
purge(options) {
return bot.purge(this.toString(), options);
}
}
return Page;
}
//# sourceMappingURL=page.js.map