Socket
Socket
Sign inDemoInstall

mwn

Package Overview
Dependencies
60
Maintainers
1
Versions
27
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 0.11.0 to 0.11.1

build/api_response_types.d.ts

50

build/bot.d.ts

@@ -27,3 +27,3 @@ /**

import { MwnTitle } from './title';
import { MwnPage, ApiPage, ApiRevision } from './page';
import { MwnPage } from './page';
import { MwnWikitext } from './wikitext';

@@ -34,9 +34,10 @@ import { MwnUser } from './user';

import { MwnStream } from './eventstream';
export { MwnDate, MwnTitle, MwnPage, MwnFile, MwnCategory, MwnWikitext, MwnUser, MwnStream, ApiPage, ApiRevision };
import { RawRequestParams } from './core';
import { log, updateLoggingConfig } from './log';
import { MwnError } from './error';
import { link, template, table } from './static_utils';
import { link, table, template } from './static_utils';
import { sleep } from './utils';
import type { ApiDeleteParams, ApiEditPageParams, ApiMoveParams, ApiParseParams, ApiPurgeParams, ApiQueryAllMessagesParams, ApiQueryAllPagesParams, ApiQueryCategoryMembersParams, ApiQuerySearchParams, ApiQueryUserInfoParams, ApiRollbackParams, ApiUndeleteParams, ApiUploadParams } from './api_params';
import { ApiEditResponse, ApiPage, ApiResponse, ApiRevision, ApiSearchResult } from './api_response_types';
export { MwnDate, MwnTitle, MwnPage, MwnFile, MwnCategory, MwnWikitext, MwnUser, MwnStream, ApiPage, ApiRevision };
export interface MwnOptions {

@@ -73,3 +74,3 @@ silent?: boolean;

export declare type ApiParams = {
[param: string]: string | string[] | boolean | number | number[] | Date | {
[param: string]: string | string[] | boolean | number | number[] | Date | File | {
stream: ReadableStream;

@@ -79,16 +80,2 @@ name: string;

};
export interface ApiResponse {
query?: Record<string, any>;
[prop: string]: any;
}
declare type ApiEditResponse = {
result: string;
pageid: number;
title: string;
contentmodel: string;
nochange?: boolean;
oldrevid: number;
newrevid: number;
newtimestamp: string;
};
export declare class mwn {

@@ -161,31 +148,39 @@ /**

/**
* Title class associated with the bot instance
* Title class associated with the bot instance.
* See {@link MwnTitle} interface for methods on title objects.
*/
title: import("./title").MwnTitleStatic;
/**
* Page class associated with the bot instance
* Page class associated with the bot instance.
* See {@link MwnPage} interface for methods on page objects.
*/
page: import("./page").MwnPageStatic;
/**
* Category class associated with the bot instance
* Category class associated with the bot instance.
* See {@link MwnCategory} interface for methods on category objects.
*/
category: import("./category").MwnCategoryStatic;
/**
* File class associated with the bot instance
* File class associated with the bot instance.
* See {@link MwnFile} interface for methods on file objects.
*/
file: import("./file").MwnFileStatic;
/**
* User class associated with the bot instance
* User class associated with the bot instance.
* See {@link MwnUser} interface for methods on user objects.
*/
user: import("./user").MwnUserStatic;
/**
* Wikitext class associated with the bot instance
* Wikitext class associated with the bot instance.
* See {@link MwnWikitext} interface for methods on wikitext objects.
*/
wikitext: import("./wikitext").MwnWikitextStatic;
/**
* Stream class associated with the bot instance
* Stream class associated with the bot instance.
* See {@link MwnStream} interface for methods on stream objects.
*/
stream: import("./eventstream").MwnStreamStatic;
/**
* Date class associated with the bot instance
* Date class associated with the bot instance.
* See {@link MwnDate} interface for methods on date objects.
*/

@@ -262,2 +257,3 @@ date: import("./date").MwnDateStatic;

request(params: ApiParams, customRequestOptions?: RawRequestParams): Promise<ApiResponse>;
query(params: ApiParams, customRequestOptions?: RawRequestParams): Promise<ApiResponse>;
/************** CORE FUNCTIONS *******************/

@@ -557,3 +553,3 @@ /**

*/
search(searchTerm: string, limit: number, props: ('size' | 'timestamp' | 'wordcount' | 'snippet' | 'redirectitle' | 'sectiontitle' | 'redirectsnippet' | 'titlesnippet' | 'sectionsnippet' | 'categorysnippet')[], otherParams?: ApiQuerySearchParams): Promise<ApiResponse>;
search(searchTerm: string, limit?: number, props?: ApiQuerySearchParams['srprop'], otherParams?: ApiQuerySearchParams): Promise<ApiSearchResult[]>;
/************* BULK PROCESSING FUNCTIONS ************/

@@ -560,0 +556,0 @@ /**

@@ -45,3 +45,2 @@ "use strict";

const axios_cookiejar_support_1 = require("axios-cookiejar-support");
axios_cookiejar_support_1.default(axios_1.default);
// Nested classes of mwn

@@ -61,2 +60,3 @@ const date_1 = require("./date");

const utils_1 = require("./utils");
axios_cookiejar_support_1.default(axios_1.default);
class mwn {

@@ -161,31 +161,39 @@ /**

/**
* Title class associated with the bot instance
* Title class associated with the bot instance.
* See {@link MwnTitle} interface for methods on title objects.
*/
this.title = title_1.default();
/**
* Page class associated with the bot instance
* Page class associated with the bot instance.
* See {@link MwnPage} interface for methods on page objects.
*/
this.page = page_1.default(this);
/**
* Category class associated with the bot instance
* Category class associated with the bot instance.
* See {@link MwnCategory} interface for methods on category objects.
*/
this.category = category_1.default(this);
/**
* File class associated with the bot instance
* File class associated with the bot instance.
* See {@link MwnFile} interface for methods on file objects.
*/
this.file = file_1.default(this);
/**
* User class associated with the bot instance
* User class associated with the bot instance.
* See {@link MwnUser} interface for methods on user objects.
*/
this.user = user_1.default(this);
/**
* Wikitext class associated with the bot instance
* Wikitext class associated with the bot instance.
* See {@link MwnWikitext} interface for methods on wikitext objects.
*/
this.wikitext = wikitext_1.default(this);
/**
* Stream class associated with the bot instance
* Stream class associated with the bot instance.
* See {@link MwnStream} interface for methods on stream objects.
*/
this.stream = eventstream_1.default(this);
/**
* Date class associated with the bot instance
* Date class associated with the bot instance.
* See {@link MwnDate} interface for methods on date objects.
*/

@@ -319,3 +327,3 @@ this.date = date_1.default(this);

*/
rawRequest(requestOptions) {
async rawRequest(requestOptions) {
if (!requestOptions.url) {

@@ -354,2 +362,5 @@ return error_1.rejectWithError({

}
async query(params, customRequestOptions = {}) {
return this.request(Object.assign({ action: 'query' }, params), customRequestOptions);
}
/************** CORE FUNCTIONS *******************/

@@ -438,3 +449,3 @@ /**

*/
logout() {
async logout() {
if (this.usingOAuth) {

@@ -492,3 +503,3 @@ throw new Error("Can't use logout() while using OAuth");

*/
userinfo(options = {}) {
async userinfo(options = {}) {
return this.request({

@@ -506,3 +517,3 @@ action: 'query',

*/
getSiteInfo() {
async getSiteInfo() {
return this.request({

@@ -520,3 +531,3 @@ action: 'query',

*/
getTokens() {
async getTokens() {
return this.getTokensAndSiteInfo();

@@ -811,4 +822,5 @@ }

save(title, content, summary, options) {
return this.request(utils_1.merge({
return this.request({
action: 'edit',
...utils_1.makeTitle(title),
text: content,

@@ -818,3 +830,4 @@ summary: summary,

token: this.csrfToken,
}, utils_1.makeTitle(title), options)).then((data) => data.edit);
...options,
}).then((data) => data.edit);
}

@@ -832,3 +845,3 @@ /**

create(title, content, summary, options) {
return this.request(utils_1.merge({
return this.request({
action: 'edit',

@@ -841,3 +854,4 @@ title: String(title),

token: this.csrfToken,
}, options)).then((data) => data.edit);
...options,
}).then((data) => data.edit);
}

@@ -853,4 +867,5 @@ /**

newSection(title, header, message, additionalParams) {
return this.request(utils_1.merge({
return this.request({
action: 'edit',
...utils_1.makeTitle(title),
section: 'new',

@@ -861,3 +876,4 @@ summary: header,

token: this.csrfToken,
}, utils_1.makeTitle(title), additionalParams)).then((data) => data.edit);
...additionalParams,
}).then((data) => data.edit);
}

@@ -873,7 +889,9 @@ /**

delete(title, summary, options) {
return this.request(utils_1.merge({
return this.request({
action: 'delete',
...utils_1.makeTitle(title),
reason: summary,
token: this.csrfToken,
}, utils_1.makeTitle(title), options)).then((data) => data.delete);
...options,
}).then((data) => data.delete);
}

@@ -890,3 +908,3 @@ /**

undelete(title, summary, options) {
return this.request(utils_1.merge({
return this.request({
action: 'undelete',

@@ -896,3 +914,4 @@ title: String(title),

token: this.csrfToken,
}, options)).then((data) => data.undelete);
...options,
}).then((data) => data.undelete);
}

@@ -908,3 +927,3 @@ /**

move(fromtitle, totitle, summary, options) {
return this.request(utils_1.merge({
return this.request({
action: 'move',

@@ -916,3 +935,4 @@ from: fromtitle,

token: this.csrfToken,
}, options)).then((data) => data.move);
...options,
}).then((data) => data.move);
}

@@ -927,9 +947,12 @@ /**

*/
parseWikitext(content, additionalParams) {
return this.request(utils_1.merge({
async parseWikitext(content, additionalParams) {
return this.request({
action: 'parse',
text: String(content),
contentmodel: 'wikitext',
disablelimitreport: true,
disableeditsection: true,
formatversion: 2,
action: 'parse',
contentmodel: 'wikitext',
}, additionalParams)).then(function (data) {
...additionalParams,
}).then(function (data) {
return data.parse.text;

@@ -946,4 +969,4 @@ });

*/
parseTitle(title, additionalParams) {
return this.request(utils_1.merge({
async parseTitle(title, additionalParams) {
return this.request({
page: String(title),

@@ -953,3 +976,4 @@ formatversion: 2,

contentmodel: 'wikitext',
}, additionalParams)).then(function (data) {
...additionalParams,
}).then(function (data) {
return data.parse.text;

@@ -967,3 +991,3 @@ });

*/
upload(filepath, title, text, options) {
async upload(filepath, title, text, options) {
return this.request({

@@ -1002,4 +1026,4 @@ action: 'upload',

*/
uploadFromUrl(url, title, text, options) {
return this.request(utils_1.merge({
async uploadFromUrl(url, title, text, options) {
return this.request({
action: 'upload',

@@ -1011,3 +1035,4 @@ url: url,

token: this.csrfToken,
}, options)).then((data) => {
...options,
}).then((data) => {
if (data.upload.warnings) {

@@ -1030,8 +1055,9 @@ log_1.log('[W] The API returned warnings while uploading to ' + title + ':');

*/
download(file, localname) {
return this.request(utils_1.merge({
async download(file, localname) {
return this.request({
action: 'query',
...utils_1.makeTitles(file),
prop: 'imageinfo',
iiprop: 'url',
}, utils_1.makeTitles(file))).then((data) => {
}).then((data) => {
const url = data.query.pages[0].imageinfo[0].url;

@@ -1049,3 +1075,3 @@ const name = new this.title(data.query.pages[0].title).getMainText();

*/
downloadFromUrl(url, localname) {
async downloadFromUrl(url, localname) {
return this.rawRequest({

@@ -1067,6 +1093,6 @@ method: 'get',

}
saveOption(option, value) {
async saveOption(option, value) {
return this.saveOptions({ [option]: value });
}
saveOptions(options) {
async saveOptions(options) {
return this.request({

@@ -1086,8 +1112,10 @@ action: 'options',

*/
rollback(page, user, params) {
return this.request(utils_1.merge({
async rollback(page, user, params) {
return this.request({
action: 'rollback',
...utils_1.makeTitle(page),
user: user,
token: this.state.rollbacktoken,
}, utils_1.makeTitle(page), params)).then((data) => {
...params,
}).then((data) => {
return data.rollback;

@@ -1103,6 +1131,8 @@ });

*/
purge(titles, options) {
return this.request(utils_1.merge({
async purge(titles, options) {
return this.request({
action: 'purge',
}, utils_1.makeTitles(titles), options)).then((data) => data.purge);
...utils_1.makeTitles(titles),
...options,
}).then((data) => data.purge);
}

@@ -1116,3 +1146,3 @@ /**

*/
getPagesByPrefix(prefix, otherParams) {
async getPagesByPrefix(prefix, otherParams) {
const title = this.title.newFromText(prefix);

@@ -1122,3 +1152,3 @@ if (!title) {

}
return this.request(utils_1.merge({
return this.request({
action: 'query',

@@ -1129,3 +1159,4 @@ list: 'allpages',

aplimit: 'max',
}, otherParams)).then((data) => {
...otherParams,
}).then((data) => {
return data.query.allpages.map((pg) => pg.title);

@@ -1140,3 +1171,3 @@ });

*/
getPagesInCategory(category, otherParams) {
async getPagesInCategory(category, otherParams) {
const title = this.title.newFromText(category, 14);

@@ -1162,4 +1193,4 @@ return this.request({

*/
search(searchTerm, limit, props, otherParams) {
return this.request(utils_1.merge({
async search(searchTerm, limit = 50, props, otherParams) {
return this.request({
action: 'query',

@@ -1169,4 +1200,5 @@ list: 'search',

srlimit: limit,
srprop: props || 'size|wordcount|timestamp',
}, otherParams)).then((data) => {
srprop: props || ['size', 'wordcount', 'timestamp'],
...otherParams,
}).then((data) => {
return data.query.search;

@@ -1173,0 +1205,0 @@ });

@@ -8,3 +8,4 @@ /**

import * as OAuth from 'oauth-1.0a';
import type { mwn, ApiParams, ApiResponse } from './bot';
import type { mwn, ApiParams } from './bot';
import { ApiResponse } from './api_response_types';
export interface RawRequestParams extends AxiosRequestConfig {

@@ -11,0 +12,0 @@ retryNumber?: number;

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const wikitext_1 = require("./wikitext");
/**

@@ -153,5 +154,3 @@ * Wrapper around the native JS Date() for ease of

};
// as long as only unbind() and rebind() methods of bot.wikitext are used,
// there shouldn't be problems from not having called getSiteInfo() on the bot object
let unbinder = new bot.wikitext(formatstr); // escape stuff between [...]
let unbinder = new wikitext_1.Unbinder(formatstr); // escape stuff between [...]
unbinder.unbind('\\[', '\\]');

@@ -158,0 +157,0 @@ unbinder.text = unbinder.text.replace(

import type { mwn, MwnTitle } from './bot';
import type { ApiDeleteParams, ApiEditPageParams, ApiMoveParams, ApiPurgeParams, ApiQueryAllPagesParams, ApiQueryLogEventsParams, ApiQueryRevisionsParams, ApiUndeleteParams } from './api_params';
import type { LogEvent } from './user';
export declare type revisionprop = 'content' | 'timestamp' | 'user' | 'comment' | 'parsedcomment' | 'ids' | 'flags' | 'size' | 'tags' | 'userid' | 'contentmodel';
export declare type logprop = 'type' | 'user' | 'comment' | 'details' | 'timestamp' | 'title' | 'parsedcomment' | 'ids' | 'tags' | 'userid';
export interface PageViewOptions {
access?: 'all-access' | 'desktop' | 'mobile-app' | 'mobile-web';
agent?: 'all-agents' | 'user' | 'spider' | 'automated';
granularity?: 'daily' | 'monthly';
start?: Date;
end?: Date;
}
export interface PageViewData {
project: string;
article: string;
granularity: string;
timestamp: string;
access: string;
agent: string;
views: number;
}
export interface AuthorshipData {
totalBytes: number;
users: Array<{
id: number;
name: string;
bytes: number;
percent: number;
}>;
}
export interface ApiPage {
pageid: number;
ns: number;
title: string;
missing?: true;
invalid?: true;
revisions?: ApiRevision[];
}
export interface ApiRevision extends ApiRevisionSlot {
revid?: number;
parentid?: number;
minor?: boolean;
userhidden?: true;
anon?: true;
user?: string;
userid?: number;
timestamp?: string;
roles?: string[];
commenthidden?: true;
comment?: string;
parsedcomment?: string;
slots?: {
main: ApiRevisionSlot;
[slotname: string]: ApiRevisionSlot;
};
}
export interface ApiRevisionSlot {
size?: number;
sha1?: string;
contentmodel?: string;
contentformat?: string;
content?: string;
}
import { ApiParseResponse, ApiRevision, LogEvent } from './api_response_types';
export interface MwnPageStatic {

@@ -72,17 +12,5 @@ new (title: MwnTitle | string, namespace?: number): MwnPage;

text(): Promise<string>;
categories(): Promise<{
sortkey: string;
category: string;
hidden: boolean;
}[]>;
templates(): Promise<{
ns: number;
title: string;
exists: boolean;
}[]>;
links(): Promise<{
ns: number;
title: string;
exists: boolean;
}[]>;
categories(): Promise<ApiParseResponse['categories']>;
templates(): Promise<ApiParseResponse['templates']>;
links(): Promise<ApiParseResponse['links']>;
backlinks(): Promise<string[]>;

@@ -98,6 +26,6 @@ transclusions(): Promise<string[]>;

getDescription(customOptions?: any): Promise<string>;
history(props: revisionprop[] | revisionprop, limit: number, customOptions?: ApiQueryRevisionsParams): Promise<ApiRevision[]>;
historyGen(props: revisionprop[] | revisionprop, customOptions?: ApiQueryRevisionsParams): AsyncGenerator<ApiRevision>;
logs(props: logprop | logprop[], limit?: number, type?: string, customOptions?: ApiQueryLogEventsParams): Promise<LogEvent[]>;
logsGen(props: logprop | logprop[], type?: string, customOptions?: ApiQueryLogEventsParams): AsyncGenerator<LogEvent>;
history(props: ApiQueryRevisionsParams['rvprop'], limit: number, customOptions?: ApiQueryRevisionsParams): Promise<ApiRevision[]>;
historyGen(props: ApiQueryRevisionsParams['rvprop'], customOptions?: ApiQueryRevisionsParams): AsyncGenerator<ApiRevision>;
logs(props: ApiQueryLogEventsParams['leprop'], limit?: number, type?: string, customOptions?: ApiQueryLogEventsParams): Promise<LogEvent[]>;
logsGen(props: ApiQueryLogEventsParams['leprop'], type?: string, customOptions?: ApiQueryLogEventsParams): AsyncGenerator<LogEvent>;
pageViews(options?: PageViewOptions): Promise<PageViewData[]>;

@@ -117,1 +45,26 @@ queryAuthors(): Promise<AuthorshipData>;

export default function (bot: mwn): MwnPageStatic;
export interface PageViewOptions {
access?: 'all-access' | 'desktop' | 'mobile-app' | 'mobile-web';
agent?: 'all-agents' | 'user' | 'spider' | 'automated';
granularity?: 'daily' | 'monthly';
start?: Date;
end?: Date;
}
export interface PageViewData {
project: string;
article: string;
granularity: string;
timestamp: string;
access: string;
agent: string;
views: number;
}
export interface AuthorshipData {
totalBytes: number;
users: Array<{
id: number;
name: string;
bytes: number;
percent: number;
}>;
}

@@ -278,3 +278,3 @@ "use strict";

* Get the edit history of the page
* @param {revisionprop[]} props - revision properties to fetch, by default content is
* @param {string|string[]} props - revision properties to fetch, by default content is
* excluded

@@ -322,3 +322,3 @@ * @param {number} [limit=50] - number of revisions to fetch data about

* Get the page logs.
* @param {logprop[]} props - data about log entries to fetch
* @param {string|string[]} props - data about log entries to fetch
* @param {number} limit - max number of log entries to fetch

@@ -325,0 +325,0 @@ * @param {string} type - type of log to fetch, can either be an letype or leaction

import type { mwn, MwnPage } from './bot';
import type { ApiBlockParams, ApiEmailUserParams, ApiQueryLogEventsParams, ApiQueryUserContribsParams, ApiUnblockParams } from './api_params';
import type { ApiBlockParams, ApiEmailUserParams, ApiQueryGlobalUserInfoParams, ApiQueryLogEventsParams, ApiQueryUserContribsParams, ApiQueryUsersParams, ApiUnblockParams } from './api_params';
import { ApiBlockResponse, ApiEditResponse, ApiEmailUserResponse, ApiQueryGlobalUserInfoResponse, ApiQueryUsersResponse, ApiUnblockResponse, LogEvent, UserContribution } from './api_response_types';
export interface MwnUserStatic {

@@ -14,37 +15,9 @@ new (username: string): MwnUser;

logsGen(options?: ApiQueryLogEventsParams): AsyncGenerator<LogEvent>;
info(props?: string | string[]): Promise<any>;
globalinfo(props?: ('groups' | 'rights' | 'merged' | 'unattached' | 'editcount')[]): Promise<any>;
sendMessage(header: string, message: string): Promise<any>;
email(subject: string, message: string, options?: ApiEmailUserParams): Promise<any>;
block(options: ApiBlockParams): Promise<any>;
unblock(options: ApiUnblockParams): Promise<any>;
info(props?: ApiQueryUsersParams['usprop']): Promise<ApiQueryUsersResponse>;
globalinfo(props?: ApiQueryGlobalUserInfoParams['guiprop']): Promise<ApiQueryGlobalUserInfoResponse>;
sendMessage(header: string, message: string): Promise<ApiEditResponse>;
email(subject: string, message: string, options?: ApiEmailUserParams): Promise<ApiEmailUserResponse>;
block(options: ApiBlockParams): Promise<ApiBlockResponse>;
unblock(options: ApiUnblockParams): Promise<ApiUnblockResponse>;
}
export declare type UserContribution = {
userid: number;
user: string;
pageid: number;
revid: number;
parentid: number;
ns: number;
title: string;
timestamp: string;
new: boolean;
minor: boolean;
top: boolean;
comment: string;
size: number;
};
export declare type LogEvent = {
logid: number;
ns: number;
title: string;
pageid: number;
logpage: number;
params: any;
type: string;
action: string;
user: string;
timestamp: string;
comment: string;
};
export default function (bot: mwn): MwnUserStatic;

@@ -84,3 +84,3 @@ "use strict";

* Get public information about the user
* @param {Array} props - properties to fetch
* @param {string|string[]} props - properties to fetch
* @returns {Promise<Object>}

@@ -102,3 +102,3 @@ */

* Get global user info for wikis with CentralAuth
* @param {("groups"|"rights"|"merged"|"unattached"|"editcount")[]} props
* @param {string|string[]} props
*/

@@ -105,0 +105,0 @@ globalinfo(props) {

@@ -25,4 +25,3 @@ /**

}
export interface MwnWikitext {
text: string;
export interface MwnWikitext extends Unbinder {
links: Array<PageLink>;

@@ -37,5 +36,2 @@ templates: Array<Template>;

parseSections(): Section[];
unbind(prefix: string, postfix: string): void;
rebind(): string;
getText(): string;
apiParse(options: ApiParseParams): Promise<string>;

@@ -69,3 +65,2 @@ }

/**
* @class
* Represents the wikitext of template transclusion. Used by #parseTemplates.

@@ -101,2 +96,55 @@ * @prop {string} name Name of the template

}
/**
* @inheritdoc
*/
export declare function parseTemplates(wikitext: string, config: TemplateConfig): Template[];
/**
* Simple table parser.
* Parses tables provided:
* 1. It doesn't have any merged or joined cells.
* 2. It doesn't use any templates to produce any table markup.
* 3. Further restrictions may apply.
*
* Tables generated via mwn.table() class are intended to be parsable.
*
* This method throws when it finds an inconsistency (rather than silently
* cause undesired behaviour).
*
* @param {string} text
* @returns {Object[]} - each object in the returned array represents a row,
* with its keys being column names, and values the cell content
*/
export declare function parseTable(text: string): {
[column: string]: string;
}[];
/**
* @inheritdoc
*/
export declare function parseSections(text: string): Section[];
export declare class Unbinder {
text: string;
constructor(text: string);
private unbinder;
/**
* Temporarily hide a part of the string while processing the rest of it.
*
* eg. let u = new bot.wikitext("Hello world <!-- world --> world");
* u.unbind('<!--','-->');
* u.content = u.content.replace(/world/g, 'earth');
* u.rebind(); // gives "Hello earth <!-- world --> earth"
*
* Text within the 'unbinded' part (in this case, the HTML comment) remains intact
* unbind() can be called multiple times to unbind multiple parts of the string.
*
* @param {string} prefix
* @param {string} postfix
*/
unbind(prefix: string, postfix: string): void;
/**
* Rebind after unbinding.
*/
rebind(): string;
/** Get the updated text */
getText(): string;
}
export default function (bot: mwn): MwnWikitextStatic;

@@ -17,3 +17,3 @@ "use strict";

Object.defineProperty(exports, "__esModule", { value: true });
exports.Parameter = exports.Template = void 0;
exports.Unbinder = exports.parseSections = exports.parseTable = exports.parseTemplates = exports.Parameter = exports.Template = void 0;
// Adapted from https://en.wikipedia.org/wiki/MediaWiki:Gadget-libExtraUtil.js

@@ -23,3 +23,2 @@ // by Evad37 (cc-by-sa-3.0/GFDL)

/**
* @class
* Represents the wikitext of template transclusion. Used by #parseTemplates.

@@ -70,4 +69,322 @@ * @prop {string} name Name of the template

exports.Parameter = Parameter;
// parseTemplates() and processTemplateText() are adapted from
// https://en.wikipedia.org/wiki/MediaWiki:Gadget-libExtraUtil.js written by Evad37
// which was in turn adapted from https://en.wikipedia.org/wiki/User:SD0001/parseAllTemplates.js
// written by me. (cc-by-sa/GFDL)
/**
* @inheritdoc
*/
function parseTemplates(wikitext, config) {
config = config || {
recursive: false,
namePredicate: null,
templatePredicate: null,
count: null,
};
const result = [];
const n = wikitext.length;
// number of unclosed braces
let numUnclosed = 0;
// are we inside a comment, or between nowiki tags, or in a {{{parameter}}}?
let inComment = false;
let inNowiki = false;
let inParameter = false;
let startIdx, endIdx;
for (let i = 0; i < n; i++) {
if (!inComment && !inNowiki && !inParameter) {
if (wikitext[i] === '{' && wikitext[i + 1] === '{' && wikitext[i + 2] === '{' && wikitext[i + 3] !== '{') {
inParameter = true;
i += 2;
}
else if (wikitext[i] === '{' && wikitext[i + 1] === '{') {
if (numUnclosed === 0) {
startIdx = i + 2;
}
numUnclosed += 2;
i++;
}
else if (wikitext[i] === '}' && wikitext[i + 1] === '}') {
if (numUnclosed === 2) {
endIdx = i;
let templateWikitext = wikitext.slice(startIdx, endIdx); // without braces
let processed = processTemplateText(templateWikitext, config.namePredicate, config.templatePredicate);
if (processed) {
result.push(processed);
}
if (config.count && result.length === config.count) {
return result;
}
}
numUnclosed -= 2;
i++;
}
else if (wikitext[i] === '|' && numUnclosed > 2) {
// swap out pipes in nested templates with \x01 character
wikitext = strReplaceAt(wikitext, i, '\x01');
}
else if (/^<!--/.test(wikitext.slice(i, i + 4))) {
inComment = true;
i += 3;
}
else if (/^<nowiki ?>/.test(wikitext.slice(i, i + 9))) {
inNowiki = true;
i += 7;
}
}
else {
// we are in a comment or nowiki or {{{parameter}}}
if (wikitext[i] === '|') {
// swap out pipes with \x01 character
wikitext = strReplaceAt(wikitext, i, '\x01');
}
else if (/^-->/.test(wikitext.slice(i, i + 3))) {
inComment = false;
i += 2;
}
else if (/^<\/nowiki ?>/.test(wikitext.slice(i, i + 10))) {
inNowiki = false;
i += 8;
}
else if (wikitext[i] === '}' && wikitext[i + 1] === '}' && wikitext[i + 2] === '}') {
inParameter = false;
i += 2;
}
}
}
if (config.recursive) {
let subtemplates = result
.map((template) => {
return template.wikitext.slice(2, -2);
})
.filter((templateWikitext) => {
return /\{\{.*\}\}/s.test(templateWikitext);
})
.map((templateWikitext) => {
return parseTemplates(templateWikitext, config);
});
return result.concat(...subtemplates);
}
return result;
}
exports.parseTemplates = parseTemplates;
/**
* @param {string} text - template wikitext without braces, with the pipes in
* nested templates replaced by \x01
* @param {Function} [namePredicate]
* @param {Function} [templatePredicate]
* @returns {Template}
*/
function processTemplateText(text, namePredicate, templatePredicate) {
// eslint-disable-next-line no-control-regex
const template = new Template('{{' + text.replace(/\x01/g, '|') + '}}');
// swap out pipe in links with \x01 control character
// [[File: ]] can have multiple pipes, so might need multiple passes
while (/(\[\[[^\]]*?)\|(.*?\]\])/g.test(text)) {
text = text.replace(/(\[\[[^\]]*?)\|(.*?\]\])/g, '$1\x01$2');
}
const [name, ...parameterChunks] = text.split('|').map((chunk) => {
// change '\x01' control characters back to pipes
// eslint-disable-next-line no-control-regex
return chunk.replace(/\x01/g, '|');
});
template.setName(name);
if (namePredicate && !namePredicate(template.name)) {
return null;
}
let unnamedIdx = 1;
parameterChunks.forEach(function (chunk) {
let indexOfEqualTo = chunk.indexOf('=');
let indexOfOpenBraces = chunk.indexOf('{{');
let isWithoutEquals = !chunk.includes('=');
let hasBracesBeforeEquals = chunk.includes('{{') && indexOfOpenBraces < indexOfEqualTo;
let isUnnamedParam = isWithoutEquals || hasBracesBeforeEquals;
let pName, pNum, pVal;
if (isUnnamedParam) {
// Get the next number not already used by either an unnamed parameter,
// or by a named parameter like `|1=val`
while (template.getParam(unnamedIdx)) {
unnamedIdx++;
}
pNum = unnamedIdx;
pVal = chunk.trim();
}
else {
pName = chunk.slice(0, indexOfEqualTo).trim();
pVal = chunk.slice(indexOfEqualTo + 1).trim();
}
template.addParam(pName || pNum, pVal, chunk);
});
if (templatePredicate && !templatePredicate(template)) {
return null;
}
return template;
}
/**
* Simple table parser.
* Parses tables provided:
* 1. It doesn't have any merged or joined cells.
* 2. It doesn't use any templates to produce any table markup.
* 3. Further restrictions may apply.
*
* Tables generated via mwn.table() class are intended to be parsable.
*
* This method throws when it finds an inconsistency (rather than silently
* cause undesired behaviour).
*
* @param {string} text
* @returns {Object[]} - each object in the returned array represents a row,
* with its keys being column names, and values the cell content
*/
function parseTable(text) {
text = text.trim();
const indexOfRawPipe = function (text) {
// number of unclosed brackets
let tlevel = 0, llevel = 0;
let n = text.length;
for (let i = 0; i < n; i++) {
if (text[i] === '{' && text[i + 1] === '{') {
tlevel++;
i++;
}
else if (text[i] === '[' && text[i + 1] === '[') {
llevel++;
i++;
}
else if (text[i] === '}' && text[i + 1] === '}') {
tlevel--;
i++;
}
else if (text[i] === ']' && text[i + 1] === ']') {
llevel--;
i++;
}
else if (text[i] === '|' && tlevel === 0 && llevel === 0) {
return i;
}
}
};
if (!text.startsWith('{|') || !text.endsWith('|}')) {
throw new Error('failed to parse table. Unexpected starting or ending');
}
// remove front matter and final matter
// including table attributes and caption, and unnecessary |- at the top
text = text.replace(/^\{\|.*$((\n\|-)?\n\|\+.*$)?(\n\|-)?/m, '').replace(/^\|\}$/m, '');
let [header, ...rows] = text.split(/^\|-/m).map((r) => r.trim());
// remove cell attributes, extracts data
const extractData = (cell) => {
return cell.slice(indexOfRawPipe(cell) + 1).trim();
};
// XXX: handle the case where there are is no header row
let cols = header.split('\n').map((e) => e.replace(/^!/, ''));
if (cols.length === 1) {
// non-multilined table?
cols = cols[0].split('!!');
}
cols = cols.map(extractData);
let numcols = cols.length;
let output = new Array(rows.length);
rows.forEach((row, idx) => {
let cells = row.split(/^\|/m).slice(1); // slice(1) removes the emptiness or the row styles if present
if (cells.length === 1) {
// non-multilined
// cells are separated by ||
cells = cells[0].replace(/^\|/, '').split('||');
}
cells = cells.map(extractData);
if (cells.length !== numcols) {
throw new Error(`failed to parse table: found ${cells.length} cells on row ${idx}, expected ${numcols}`);
}
output[idx] = {}; // output[idx] represents a row
for (let i = 0; i < numcols; i++) {
output[idx][cols[i]] = cells[i];
}
});
return output;
}
exports.parseTable = parseTable;
// XXX: fix jsdocs
/**
* @inheritdoc
*/
function parseSections(text) {
const rgx = /^(=+)(.*?)\1/gm;
let sections = [
{
level: 1,
header: null,
index: 0,
},
];
let match;
while ((match = rgx.exec(text))) {
// eslint-disable-line no-cond-assign
sections.push({
level: match[1].length,
header: match[2].trim(),
index: match.index,
});
}
let n = sections.length;
for (let i = 0; i < n - 1; i++) {
sections[i].content = text.slice(sections[i].index, sections[i + 1].index);
}
sections[n - 1].content = text.slice(sections[n - 1].index);
return sections;
}
exports.parseSections = parseSections;
// Attribution: https://en.wikipedia.org/wiki/MediaWiki:Gadget-morebits.js (cc-by-sa 3.0/GFDL)
class Unbinder {
constructor(text) {
this.text = text;
}
/**
* Temporarily hide a part of the string while processing the rest of it.
*
* eg. let u = new bot.wikitext("Hello world <!-- world --> world");
* u.unbind('<!--','-->');
* u.content = u.content.replace(/world/g, 'earth');
* u.rebind(); // gives "Hello earth <!-- world --> earth"
*
* Text within the 'unbinded' part (in this case, the HTML comment) remains intact
* unbind() can be called multiple times to unbind multiple parts of the string.
*
* @param {string} prefix
* @param {string} postfix
*/
unbind(prefix, postfix) {
if (!this.unbinder) {
this.unbinder = {
counter: 0,
history: {},
prefix: '%UNIQ::' + Math.random() + '::',
postfix: '::UNIQ%',
};
}
let re = new RegExp(prefix + '([\\s\\S]*?)' + postfix, 'g');
this.text = this.text.replace(re, (match) => {
let current = this.unbinder.prefix + this.unbinder.counter + this.unbinder.postfix;
this.unbinder.history[current] = match;
++this.unbinder.counter;
return current;
});
}
/**
* Rebind after unbinding.
*/
rebind() {
let content = this.text;
for (let [current, replacement] of Object.entries(this.unbinder.history)) {
content = content.replace(current, replacement);
}
this.text = content;
return this.text;
}
/** Get the updated text */
getText() {
return this.text;
}
}
exports.Unbinder = Unbinder;
function default_1(bot) {
class Wikitext {
class Wikitext extends Unbinder {
constructor(wikitext) {

@@ -77,3 +394,3 @@ if (typeof wikitext !== 'string') {

}
this.text = wikitext;
super(wikitext);
}

@@ -131,107 +448,5 @@ /** Parse links, file usages and categories from the wikitext */

parseTemplates(config) {
return (this.templates = Wikitext.parseTemplates(this.text, config));
return (this.templates = parseTemplates(this.text, config));
}
// parseTemplates() and processTemplateText() are adapted from
// https://en.wikipedia.org/wiki/MediaWiki:Gadget-libExtraUtil.js written by Evad37
// which was in turn adapted from https://en.wikipedia.org/wiki/User:SD0001/parseAllTemplates.js
// written by me. (cc-by-sa/GFDL)
/**
* @inheritdoc
*/
static parseTemplates(wikitext, config) {
config = config || {
recursive: false,
namePredicate: null,
templatePredicate: null,
count: null,
};
const result = [];
const n = wikitext.length;
// number of unclosed braces
let numUnclosed = 0;
// are we inside a comment, or between nowiki tags, or in a {{{parameter}}}?
let inComment = false;
let inNowiki = false;
let inParameter = false;
let startIdx, endIdx;
for (let i = 0; i < n; i++) {
if (!inComment && !inNowiki && !inParameter) {
if (wikitext[i] === '{' &&
wikitext[i + 1] === '{' &&
wikitext[i + 2] === '{' &&
wikitext[i + 3] !== '{') {
inParameter = true;
i += 2;
}
else if (wikitext[i] === '{' && wikitext[i + 1] === '{') {
if (numUnclosed === 0) {
startIdx = i + 2;
}
numUnclosed += 2;
i++;
}
else if (wikitext[i] === '}' && wikitext[i + 1] === '}') {
if (numUnclosed === 2) {
endIdx = i;
let templateWikitext = wikitext.slice(startIdx, endIdx); // without braces
let processed = processTemplateText(templateWikitext, config.namePredicate, config.templatePredicate);
if (processed) {
result.push(processed);
}
if (config.count && result.length === config.count) {
return result;
}
}
numUnclosed -= 2;
i++;
}
else if (wikitext[i] === '|' && numUnclosed > 2) {
// swap out pipes in nested templates with \x01 character
wikitext = strReplaceAt(wikitext, i, '\x01');
}
else if (/^<!--/.test(wikitext.slice(i, i + 4))) {
inComment = true;
i += 3;
}
else if (/^<nowiki ?>/.test(wikitext.slice(i, i + 9))) {
inNowiki = true;
i += 7;
}
}
else {
// we are in a comment or nowiki or {{{parameter}}}
if (wikitext[i] === '|') {
// swap out pipes with \x01 character
wikitext = strReplaceAt(wikitext, i, '\x01');
}
else if (/^-->/.test(wikitext.slice(i, i + 3))) {
inComment = false;
i += 2;
}
else if (/^<\/nowiki ?>/.test(wikitext.slice(i, i + 10))) {
inNowiki = false;
i += 8;
}
else if (wikitext[i] === '}' && wikitext[i + 1] === '}' && wikitext[i + 2] === '}') {
inParameter = false;
i += 2;
}
}
}
if (config.recursive) {
let subtemplates = result
.map((template) => {
return template.wikitext.slice(2, -2);
})
.filter((templateWikitext) => {
return /\{\{.*\}\}/s.test(templateWikitext);
})
.map((templateWikitext) => {
return Wikitext.parseTemplates(templateWikitext, config);
});
return result.concat(...subtemplates);
}
return result;
}
/**
* Remove a template, link, file or category from the text

@@ -247,49 +462,2 @@ * CAUTION: If an entity with the very same wikitext exists earlier in the text,

/**
* Temporarily hide a part of the string while processing the rest of it.
*
* eg. let u = new bot.wikitext("Hello world <!-- world --> world");
* u.unbind('<!--','-->');
* u.content = u.content.replace(/world/g, 'earth');
* u.rebind(); // gives "Hello earth <!-- world --> earth"
*
* Text within the 'unbinded' part (in this case, the HTML comment) remains intact
* unbind() can be called multiple times to unbind multiple parts of the string.
*
* Attribution: https://en.wikipedia.org/wiki/MediaWiki:Gadget-morebits.js (cc-by-sa 3.0/GFDL)
* @param {string} prefix
* @param {string} postfix
*/
unbind(prefix, postfix) {
if (!this.unbinder) {
this.unbinder = {
counter: 0,
history: {},
prefix: '%UNIQ::' + Math.random() + '::',
postfix: '::UNIQ%',
};
}
let re = new RegExp(prefix + '([\\s\\S]*?)' + postfix, 'g');
this.text = this.text.replace(re, (match) => {
let current = this.unbinder.prefix + this.unbinder.counter + this.unbinder.postfix;
this.unbinder.history[current] = match;
++this.unbinder.counter;
return current;
});
}
/**
* Rebind after unbinding.
*/
rebind() {
let content = this.text;
for (let [current, replacement] of Object.entries(this.unbinder.history)) {
content = content.replace(current, replacement);
}
this.text = content;
return this.text;
}
/** Get the updated text */
getText() {
return this.text;
}
/**
* Parse the text using the API.

@@ -304,84 +472,2 @@ * @see https://www.mediawiki.org/wiki/API:Parsing_wikitext

/**
* Simple table parser.
* Parses tables provided:
* 1. It doesn't have any merged or joined cells.
* 2. It doesn't use any templates to produce any table markup.
* 3. Further restrictions may apply.
*
* Tables generated via mwn.table() class are intended to be parsable.
*
* This method throws when it finds an inconsistency (rather than silently
* cause undesired behaviour).
*
* @param {string} text
* @returns {Object[]} - each object in the returned array represents a row,
* with its keys being column names, and values the cell content
*/
static parseTable(text) {
text = text.trim();
const indexOfRawPipe = function (text) {
// number of unclosed brackets
let tlevel = 0, llevel = 0;
let n = text.length;
for (let i = 0; i < n; i++) {
if (text[i] === '{' && text[i + 1] === '{') {
tlevel++;
i++;
}
else if (text[i] === '[' && text[i + 1] === '[') {
llevel++;
i++;
}
else if (text[i] === '}' && text[i + 1] === '}') {
tlevel--;
i++;
}
else if (text[i] === ']' && text[i + 1] === ']') {
llevel--;
i++;
}
else if (text[i] === '|' && tlevel === 0 && llevel === 0) {
return i;
}
}
};
if (!text.startsWith('{|') || !text.endsWith('|}')) {
throw new Error('failed to parse table. Unexpected starting or ending');
}
// remove front matter and final matter
// including table attributes and caption, and unnecessary |- at the top
text = text.replace(/^\{\|.*$((\n\|-)?\n\|\+.*$)?(\n\|-)?/m, '').replace(/^\|\}$/m, '');
let [header, ...rows] = text.split(/^\|-/m).map((r) => r.trim());
// remove cell attributes, extracts data
const extractData = (cell) => {
return cell.slice(indexOfRawPipe(cell) + 1).trim();
};
// XXX: handle the case where there are is no header row
let cols = header.split('\n').map((e) => e.replace(/^!/, ''));
if (cols.length === 1) {
// non-multilined table?
cols = cols[0].split('!!');
}
cols = cols.map(extractData);
let numcols = cols.length;
let output = new Array(rows.length);
rows.forEach((row, idx) => {
let cells = row.split(/^\|/m).slice(1); // slice(1) removes the emptiness or the row styles if present
if (cells.length === 1) {
// non-multilined
// cells are separated by ||
cells = cells[0].replace(/^\|/, '').split('||');
}
cells = cells.map(extractData);
if (cells.length !== numcols) {
throw new Error(`failed to parse table: found ${cells.length} cells on row ${idx}, expected ${numcols}`);
}
output[idx] = {}; // output[idx] represents a row
for (let i = 0; i < numcols; i++) {
output[idx][cols[i]] = cells[i];
}
});
return output;
}
/**
* Parse sections from wikitext

@@ -397,40 +483,9 @@ * CAUTION: section header syntax in comments, nowiki tags,

parseSections() {
return (this.sections = Wikitext.parseSections(this.text));
return (this.sections = parseSections(this.text));
}
// XXX: fix jsdocs
/**
* @inheritdoc
*/
static parseSections(text) {
const rgx = /^(=+)(.*?)\1/gm;
let sections = [
{
level: 1,
header: null,
index: 0,
},
];
let match;
while ((match = rgx.exec(text))) {
// eslint-disable-line no-cond-assign
sections.push({
level: match[1].length,
header: match[2].trim(),
index: match.index,
});
}
let n = sections.length;
for (let i = 0; i < n - 1; i++) {
sections[i].content = text.slice(sections[i].index, sections[i + 1].index);
}
sections[n - 1].content = text.slice(sections[n - 1].index);
return sections;
}
}
Wikitext.parseTemplates = parseTemplates;
Wikitext.parseTable = parseTable;
Wikitext.parseSections = parseSections;
/**** Private members *****/
class Stack extends Array {
top() {
return this[this.length - 1];
}
}
function processLink(self, startIdx, endIdx) {

@@ -472,60 +527,13 @@ let linktext = self.text.slice(startIdx, endIdx + 1);

}
/**
* @param {string} text - template wikitext without braces, with the pipes in
* nested templates replaced by \x01
* @param {Function} [namePredicate]
* @param {Function} [templatePredicate]
* @returns {Template}
*/
function processTemplateText(text, namePredicate, templatePredicate) {
// eslint-disable-next-line no-control-regex
const template = new Template('{{' + text.replace(/\x01/g, '|') + '}}');
// swap out pipe in links with \x01 control character
// [[File: ]] can have multiple pipes, so might need multiple passes
while (/(\[\[[^\]]*?)\|(.*?\]\])/g.test(text)) {
text = text.replace(/(\[\[[^\]]*?)\|(.*?\]\])/g, '$1\x01$2');
}
const [name, ...parameterChunks] = text.split('|').map((chunk) => {
// change '\x01' control characters back to pipes
// eslint-disable-next-line no-control-regex
return chunk.replace(/\x01/g, '|');
});
template.setName(name);
if (namePredicate && !namePredicate(template.name)) {
return null;
}
let unnamedIdx = 1;
parameterChunks.forEach(function (chunk) {
let indexOfEqualTo = chunk.indexOf('=');
let indexOfOpenBraces = chunk.indexOf('{{');
let isWithoutEquals = !chunk.includes('=');
let hasBracesBeforeEquals = chunk.includes('{{') && indexOfOpenBraces < indexOfEqualTo;
let isUnnamedParam = isWithoutEquals || hasBracesBeforeEquals;
let pName, pNum, pVal;
if (isUnnamedParam) {
// Get the next number not already used by either an unnamed parameter,
// or by a named parameter like `|1=val`
while (template.getParam(unnamedIdx)) {
unnamedIdx++;
}
pNum = unnamedIdx;
pVal = chunk.trim();
}
else {
pName = chunk.slice(0, indexOfEqualTo).trim();
pVal = chunk.slice(indexOfEqualTo + 1).trim();
}
template.addParam(pName || pNum, pVal, chunk);
});
if (templatePredicate && !templatePredicate(template)) {
return null;
}
return template;
}
function strReplaceAt(string, index, char) {
return string.slice(0, index) + char + string.slice(index + 1);
}
return Wikitext;
}
exports.default = default_1;
class Stack extends Array {
top() {
return this[this.length - 1];
}
}
function strReplaceAt(string, index, char) {
return string.slice(0, index) + char + string.slice(index + 1);
}
//# sourceMappingURL=wikitext.js.map
{
"name": "mwn",
"version": "0.11.0",
"version": "0.11.1",
"description": "JavaScript & TypeScript MediaWiki bot framework for Node.js",

@@ -8,3 +8,3 @@ "main": "./build/bot.js",

"scripts": {
"bump": "node bump-version.js",
"bump": "node scripts/bump-version.js",
"format": "prettier --write .",

@@ -21,3 +21,3 @@ "build": "tsc || echo",

"test:ts": "ts-mocha -p tsconfig.json tests/ts/*",
"docs": "typedoc src/bot.ts --out docs --ignoreCompilerErrors"
"docs": "node scripts/generate-docs.js"
},

@@ -71,2 +71,4 @@ "engines": {

"mocha": "^8.2.1",
"mocha-chai-jest-snapshot": "^1.1.2",
"nock": "^13.1.0",
"nyc": "^15.1.0",

@@ -77,3 +79,3 @@ "prettier": "^2.2.1",

"ts-mocha": "^8.0.0",
"typedoc": "^0.19.2",
"typedoc": "^0.20.36",
"typescript": "^4.1.3"

@@ -80,0 +82,0 @@ },

@@ -9,3 +9,3 @@ # mwn

**Quick links: [Getting Started](#user-content-getting-started) — [GitHub](https://github.com/siddharthvp/mwn) — [NPM](https://www.npmjs.com/package/mwn) — [API Documentation](https://mwn.toolforge.org/docs/classes/_bot_.mwn.html)**
**Quick links: [Getting Started](#user-content-getting-started) — [GitHub](https://github.com/siddharthvp/mwn) — [NPM](https://www.npmjs.com/package/mwn) — [API Documentation](https://mwn.toolforge.org/docs/classes/mwn.html)**

@@ -20,3 +20,3 @@ **Mwn** is a modern and comprehensive MediaWiki bot framework for Node.js, originally adapted from [mwbot](https://github.com/Fannon/mwbot).

Complete API documentation is available **[here](https://tools-static.wmflabs.org/mwn/docs/classes/_bot_.mwn.html)** ([alternative link](https://mwn.toolforge.org/docs/classes/_bot_.mwn.html)). In addition to the MediaWiki Action API, the library also provides methods to talk to the Wikimedia EventStreams API, the ORES API, Pageviews API and WikiWho API.
Complete API documentation is available **[here](https://mwn.toolforge.org/docs/classes/mwn.html)** ([alternative link](https://tools-static.wmflabs.org/mwn/docs/classes/mwn.html)). In addition to the MediaWiki Action API, the library also provides methods to talk to the Wikimedia EventStreams API, the ORES API, Pageviews API and WikiWho API.

@@ -46,3 +46,3 @@ Amongst the major highlights are `batchOperation` and `seriesBatchOperation` which allow you run a large number of tasks with control over concurrency and sleep time between tasks. Failing actions are automatically retried.

If your bot is hosted on [Toolforge](https://tools.wmflabs.org/), note that the system version of node there is v8.11.1. You can install a more recent version of node to your home directory, using:
If your bot is hosted on [Toolforge](https://tools.wmflabs.org/), note that the system version of node there is v8.11.1. You can install a more recent version of node to your home directory using a version manager like [nvm](https://github.com/nvm-sh/nvm) or [n](http://npmjs.com/package/n). Like:

@@ -113,15 +113,12 @@ ```sh

bot.enableEmergencyShutoff({
page: 'User:ExampleBot/shutoff', // The name of the page to check
intervalDuration: 5000, // check shutoff page every 5 seconds
page: 'User:ExampleBot/shutoff', // The name of the page to check
intervalDuration: 5000, // check shutoff page every 5 seconds
condition: function (pagetext) {
// function to determine whether the bot should continue to run or not
if (pagetext !== 'running') {
// Example implementation: if some one changes the text to something
return false; // other than "running", let's decide to stop!
} else return true;
if (pagetext !== 'running') { // function to determine whether the bot should continue to run or not
return false; // Example implementation: if some one changes the text to something
} else return true; // other than "running", let's decide to stop!
},
onShutoff: function (pagetext) {
// function to trigger when shutoff is activated
process.exit(); // let's just exit, though we could also terminate
} // any open connections, close files, etc.
onShutoff: function (pagetext) { // function to trigger when shutoff is activated
process.exit(); // let's just exit, though we could also terminate
} // any open connections, close files, etc.
});

@@ -231,5 +228,5 @@ ```

bot.setOptions({
silent: false, // suppress messages (except error messages)
retryPause: 5000, // pause for 5000 milliseconds (5 seconds) on maxlag error.
maxRetries: 3 // attempt to retry a failing requests upto 3 times
silent: false, // suppress messages (except error messages)
retryPause: 5000, // pause for 5000 milliseconds (5 seconds) on maxlag error.
maxRetries: 3 // attempt to retry a failing requests upto 3 times
});

@@ -358,5 +355,5 @@ ```

See [list of methods available on page object](https://mwn.toolforge.org/docs/interfaces/_page_.mwnpage.html).
See [list of methods available on page object](https://mwn.toolforge.org/docs/interfaces/mwnpage.html).
[Files](https://mwn.toolforge.org/docs/interfaces/_file_.mwnfile.html#text) and [categories](https://mwn.toolforge.org/docs/interfaces/_category_.mwncategory.html) have their own subclasses that add a few additional methods.
[Files](https://mwn.toolforge.org/docs/interfaces/mwnfile.html) and [categories](https://mwn.toolforge.org/docs/interfaces/mwncategory.html) have their own subclasses that add a few additional methods.

@@ -383,3 +380,3 @@ ### Working with titles

The API of this class is based on that of [mw.Title](https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.Title) in the on-site JS interface. See [full list of methods](https://mwn.toolforge.org/docs/interfaces/_title_.mwntitle.html).
The API of this class is based on that of [mw.Title](https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.Title) in the on-site JS interface. See [full list of methods](https://mwn.toolforge.org/docs/interfaces/mwntitle.html).

@@ -386,0 +383,0 @@ ### Working with wikitext

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Packages

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc