Comparing version 0.10.1 to 0.10.2
@@ -508,2 +508,4 @@ /** | ||
downloadFromUrl(url: string, localname: string): Promise<void>; | ||
saveOption(option: string, value: string): Promise<ApiResponse>; | ||
saveOptions(options: Record<string, string>): Promise<ApiResponse>; | ||
/** | ||
@@ -510,0 +512,0 @@ * Convenience method for `action=rollback`. |
@@ -1016,2 +1016,12 @@ "use strict"; | ||
} | ||
saveOption(option, value) { | ||
return this.saveOptions({ [option]: value }); | ||
} | ||
saveOptions(options) { | ||
return this.request({ | ||
action: 'options', | ||
change: Object.entries(options).map(([key, val]) => key + '=' + val), | ||
token: this.csrfToken | ||
}); | ||
} | ||
/** | ||
@@ -1018,0 +1028,0 @@ * Convenience method for `action=rollback`. |
{ | ||
"name": "mwn", | ||
"version": "0.10.1", | ||
"version": "0.10.2", | ||
"description": "JavaScript & TypeScript MediaWiki bot framework for Node.js", | ||
@@ -9,13 +9,13 @@ "main": "./build/bot.js", | ||
"bump": "node bump-version.js", | ||
"build": "npx tsc || echo", | ||
"build": "tsc || echo", | ||
"quickbuild": "babel src --extensions \".ts\" --out-dir build", | ||
"lint": "eslint src tests", | ||
"test:testwiki": "cd tests && npx mocha bot.test.js category.test.js file.test.js login.bot.test.js oauth.test.js page.test.js suppl.bot.test.js user.test.js wikitext.test.js", | ||
"test:testwiki": "cd tests && mocha bot.test.js category.test.js file.test.js login.bot.test.js oauth.test.js page.test.js suppl.bot.test.js user.test.js wikitext.test.js", | ||
"setuplocalwiki": "cd tests/docker && bash main.sh", | ||
"test:localwiki": "cd tests && npx mocha edit.bot.test.js user.edit.test.js errors.test.js shutoff.test.js", | ||
"test:nowiki": "cd tests && npx mocha batchOperations.bot.test.js date.test.js log.test.js static_utils.test.js title.test.js", | ||
"test:localwiki": "cd tests && mocha edit.bot.test.js user.edit.test.js errors.test.js shutoff.test.js core.test.js", | ||
"test:nowiki": "cd tests && mocha batchOperations.bot.test.js date.test.js log.test.js static_utils.test.js title.test.js", | ||
"test": "nyc --reporter=lcov --reporter=text mocha tests/", | ||
"coveralls": "nyc report --reporter=text-lcov | coveralls", | ||
"test:ts": "npx ts-mocha -p tsconfig.json tests/ts/*", | ||
"docs": "npx typedoc src/bot.ts --out docs --ignoreCompilerErrors" | ||
"test:ts": "ts-mocha -p tsconfig.json tests/ts/*", | ||
"docs": "typedoc src/bot.ts --out docs --ignoreCompilerErrors" | ||
}, | ||
@@ -22,0 +22,0 @@ "engines": { |
131
README.md
@@ -8,2 +8,4 @@ # 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)** | ||
**Mwn** is a modern and comprehensive MediaWiki bot framework for Node.js, originally adapted from [mwbot](https://github.com/Fannon/mwbot). | ||
@@ -13,4 +15,2 @@ | ||
Mwn uses promises, which you can use with async–await. To handle query continuations, mwn uses asynchronous generators. All methods with names ending in `Gen` are generators. | ||
Mwn uses [JSON with formatversion 2](https://www.mediawiki.org/wiki/API:JSON_version_2#Using_the_new_JSON_results_format) by default. Use of the legacy formatversion is not recommended. Note that [Special:ApiSandbox](https://en.wikipedia.org/wiki/Special:ApiSandbox) uses formatversion=1 by default, so if you're testing API calls using ApiSandbox be sure to set the correct formatversion there, otherwise the output will be formatted differently. | ||
@@ -66,2 +66,5 @@ | ||
### Features | ||
**Handling multiple users and wikis**: Mwn can seamlessly work with multiple bot users signed into the same wiki, and multiple wikis at the same time. You just have to create multiple bot instances – each one representing a wiki + user. Each bot instance uses an isolated cookie jar; all settings are also isolated. | ||
**Maxlag**: The default [maxlag parameter](https://www.mediawiki.org/wiki/Manual:Maxlag_parameter) used by mwn is 5 seconds. Requests failing due to maxlag will be automatically retried after pausing for a duration specified by `maxlagPause` (default 5 seconds). A maximum of `maxRetries` will take place (default 3). | ||
@@ -73,6 +76,69 @@ | ||
If you're migrating from mwbot, note that: | ||
- `edit` in mwbot is different from `edit` in mwn. You want to use `save` instead. | ||
- If you were using the default formatversion=1 output format, set formatversion: 1 in the config options. | ||
**Handling query continuation**: Mwn uses [asynchronous generators](https://javascript.info/async-iterators-generators), ([for await...of](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for-await...of) loops) to provide a very intuitive interface around MediaWiki API's [query continuation](https://www.mediawiki.org/wiki/API:Query#Example_4:_Continuing_queries). | ||
Use `bot.continuedQueryGen` everytime you want to fetch more results than what the API gives you in one go (usually 5000 results). `continuedQueryGen` automatically uses the continue parameters in the response to create and send new requests that retrieve data from where the previous response was cut off. | ||
The following example generates a list of all active users on the wiki (which may be more than 5000). | ||
```js | ||
var activeusers = []; | ||
for await (let json of bot.continuedQueryGen({ | ||
"action": "query", | ||
"list": "allusers", | ||
"auactiveusers": 1, | ||
"aulimit": "max" | ||
})) { | ||
let users = json.query.allusers.map(user => user.name); | ||
activeusers = activeusers.concat(users); | ||
} | ||
``` | ||
Specialised derivatives exist to fulfill common needs: | ||
- `new bot.page('Page name').historyGen()` - fetch page history | ||
- `new bot.page('Page name').logsGen()` - fetch page logs | ||
- `new bot.category('Page name').membersGen()` - fetch category members | ||
- `new bot.user('User name').contribsGen()` - fetch user contributions | ||
- `new bot.user('User name').logsGen()` - fetch user logs | ||
Every method names that end in `Gen` is an async generator. | ||
**Emergency shutoff**: Mwn exploits Node's asynchronous event loop to efficiently implement emergency shutoff. | ||
```js | ||
bot.enableEmergencyShutoff({ | ||
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; | ||
}, | ||
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. | ||
}) | ||
``` | ||
The rate of shutoff checks is not impacted by your actual editing rate, as it takes place separately in a setInterval() loop. Caution: this implementation has not been stress-tested. | ||
Shutoff once enabled can be disabled anytime, such as when you stop performing write operations and you're now just doing read operations. | ||
```js | ||
bot.disableEmergencyShutoff(); | ||
``` | ||
**Bot exclusion compliance**: Mwn's edit() method can be configured to respect {{nobots}} or equivalent. If the text of the page tests positive for the exclusionRegex you set in the bot options, edit will be aborted. | ||
```js | ||
bot.setOptions({ | ||
exclusionRegex: /\{\{nobots\}\}/i | ||
}) | ||
``` | ||
It's also possible to do this on a per-edit basis: | ||
```js | ||
bot.edit('Page name', (rev) => { | ||
// edit the page the way you want, in this lame example we're just appending lorem ipsum | ||
return rev.content + 'lorem ipsum'; | ||
}, /* editConfig */ { | ||
exclusionRegex: /\{\{nobots\}\}/i | ||
}) | ||
``` | ||
Exclusion compliance is _not_ enabled by default. | ||
### Getting started | ||
@@ -93,2 +159,7 @@ | ||
If you're migrating from mwbot, note that: | ||
- `edit` in mwbot is different from `edit` in mwn. You want to use `save` instead. | ||
- If you were using the default formatversion=1 output format, set formatversion: 1 in the config options. | ||
Create a new bot instance: | ||
@@ -292,40 +363,10 @@ ```js | ||
##### continuedQuery(query, maxCallsLimit) | ||
Send an API query, and continue re-sending it with the continue parameters received in the response, until there are no more results (or till `maxCalls` limit is reached). The return value is a promise resolved with the array of responses to individual API calls. | ||
**continuedQuery / continuedQueryGen**: Handles query continuation. See "Handling query continuations" in [Features section above](#user-content-features). | ||
Example: get a list of all active users on the wiki using `continuedQuery` (using [API:Allusers](https://www.mediawiki.org/wiki/API:Allusers)): | ||
```js | ||
bot.continuedQuery({ | ||
"action": "query", | ||
"list": "allusers", | ||
"auactiveusers": 1, | ||
"aulimit": "max" | ||
}, /* max number of calls */ 40).then(jsons => { | ||
return jsons.reduce((activeusers, json) => { | ||
return activeusers.concat(json.query.allusers.map(user => user.name)); | ||
}, []); | ||
}); | ||
``` | ||
continuedQuery returns a promised resolved with the array of all individual API response. | ||
A simpler way is to use `bot.continuedQueryGen` which is an [asynchronous generator](https://javascript.info/async-iterators-generators). | ||
```js | ||
var activeusers = []; | ||
for await (let json of bot.continuedQueryGen({ | ||
"action": "query", | ||
"list": "allusers", | ||
"auactiveusers": 1, | ||
"aulimit": "max" | ||
})) { | ||
let users = json.query.allusers.map(user => user.name); | ||
activeusers = activeusers.concat(users); | ||
} | ||
``` | ||
Use of `continuedQueryGen` is recommended since continuedQuery will fetch the results of all the API calls before it begins to do anything with the results. `continuedQueryGen` gets the result of each API call and processes them one at a time. | ||
Use of `continuedQueryGen` is recommended for several reasons: | ||
- If there are a large number of calls involved, continuedQuery will fetch the results of all the API calls before it begins to do anything with the results. `continuedQueryGen` gets the result of each API call and processes them one at a time. | ||
- Since making multiple API calls can take some time. you can add logging statements to know the number of the API calls that have gone through. | ||
**massQuery / massQueryGen**: MediaWiki sets a limit of 500 (50 for non-bots) on the number of pages that can be queried in a single API call. To query more than that, `massQuery` or `massQueryGen` can be used. This splits the page list into batches of 500 and sends individual queries and returns a promise resolved with the array of all individual API call responses. | ||
##### massQuery(query, nameOfBatchField, batchSize) | ||
MediaWiki sets a limit of 500 (50 for non-bots) on the number of pages that can be queried in a single API call. To query more than that, the `massQuery` function can be used, which splits the page list into batches of 500 and sends individual queries and returns a promise resolved with the array of all individual API call responses. | ||
Example: get the protection status of a large number of pages: | ||
@@ -337,6 +378,6 @@ ```js | ||
"prop": "info", | ||
"titles": ['Page1', 'Page2', ... , 'Page1300'], // array of page names | ||
"titles": ['Page1', 'Page2', 'Page1300'], // array of page names | ||
"inprop": "protection" | ||
}) // 2nd parameter is taken as 'titles' by default | ||
.then(jsons => { | ||
.then((jsons) => { | ||
// jsons is the array of individual JSON responses. | ||
@@ -348,6 +389,8 @@ }); | ||
massQueryGen is the generator equivalent that yields each API response as when they're received. | ||
#### Batch operations | ||
Perform asynchronous tasks (involving API usage) over a number of pages (or other arbitrary items). `batchOperation` uses a default concurrency of 5. Customise this according to how expensive the API operation is. Higher concurrency limits could lead to more frequent API errors. | ||
Usage: `batchOperation(pageList, workerFunction, concurrency)` The `workerFunction` must return a promise. | ||
- `batchOperation(pageList, workerFunction, concurrency, maxRetries)`: The `workerFunction` must return a promise. | ||
@@ -363,6 +406,4 @@ ```js | ||
##### seriesBatchOperation(pageList, workerFunction, sleepDuration) | ||
Perform asynchronous tasks (involving API usage) over a number of pages one after the other, with a sleep duration between each task (default 5 seconds) | ||
- `bot.seriesBatchOperation(pageList, workerFunction, sleepDuration, retries)` can be used for serial operations, with a sleep duration between each task (default 5 seconds). | ||
The `workerFunction` must return a promise. | ||
```js | ||
@@ -369,0 +410,0 @@ bot.seriesBatchOperation(pageList, (page, idx) => { |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
381584
8791
414