npmにあるMediaWikiライブラリまとめ

[MediaWikiを巡回して記事名の読みがなをソートキーに追加するBotを作った](http://qiita.com/kn1cht/items/82c7bea62609848d4695)時に、node.jsのMediaWiki APIライブラリを探していくつか触ったのでそのメモです。

- npmにあるもの限定です
- 部内Wikiで使える必要があったため、Wikipedia専用のものは除外しました

# 環境
- macOS 10.12.5
- node.js v8.1.4
- npm v5.3.0

# 使用例について
ライブラリごとの使用感を比べるために、処理内容は全て統一しています。
1. 日本語版ウィキペディアの「ウィキペディア」という記事の所属カテゴリを取得する
2. 同記事の内容を取得する

サンプルコードとしては、各ライブラリのREADMEやドキュメントの方を参考にしていただければと思います。

# dijs/wikijs
- https://www.npmjs.com/package/wikijs

一番人気があった(月間1000ダウンロード)ライブラリ。

**記事の検索や記事情報の取得**などができます。デフォルトでは英語版Wikipedia用ですが、`apiUrl`というオプションに`api.php`のURLを渡せば他のMediaWikiでも使えます。

ただ、**ログインや編集ができない**ため、閲覧にもログインが必要なWikiでは一切使えませんでした。

## インストール
```
npm i wikijs
```

## 使用例
```js
const wiki = require('wikijs').default({
  apiUrl : 'http://ja.wikipedia.org/w/api.php'
});

(async() => {
  const page = await wiki.page('ウィキペディア');
  const categories = await page.categories();
  console.log(categories);
  const content = await page.content();
  console.log(content);
})();
```

`page`オブジェクトのメンバを呼ぶことで追加の情報を取得できます。
かなりシンプルに書けるので、情報取得が主目的であれば大いにお勧めです。

# macbre/nodemw
- https://www.npmjs.com/package/nodemw

[MediaWiki公式のクライアント紹介](https://www.mediawiki.org/wiki/API:Client_code/ja#JavaScript)にも載っているライブラリ。[実際のWikiでの運用実績](https://github.com/macbre/nodemw#where-its-used)もあるとのこと。

ログイン、記事の取得や編集、ファイルアップロードなど機能は圧倒的に豊富です。使用例で使っていませんが、`client.api.call()`で**直接APIを呼ぶこともできる**ため、MediaWiki APIの全機能を使えることになります。

ただ、各メソッドが**コールバック形式**で、そのまま使うとコールバック地獄に落ちます。[`util.promisify()`](https://nodejs.org/api/util.html#util_util_promisify_original)などで対策しましょう。

## インストール
```
npm i nodemw
```

## 使用例
```js
const nodemw = require('nodemw');
const util = require('util'); // for util.promisify

const client = new nodemw({
  server : 'ja.wikipedia.org',
  path : 'w/'
});

(async() => {
  const categories = await util.promisify(client.getArticleCategories).bind(client) ('ウィキペディア');
  console.log(categories);
  const content = await util.promisify(client.getArticle).bind(client) ('ウィキペディア');
  console.log(content);
})();
```

promisifyのせいで長くなっていますが、込み入ったことはしていません。

# Fannon/mwbot
- https://www.npmjs.com/package/mwbot

nodemwと同等かそれ以上に豊富な機能を持ちます。
MediaWiki APIの直接指定により、nodemw同様に**APIの全機能を使うことができます**。

Promiseにも対応しておりnodemwよりも使いやすい印象でした。強いて言えば、APIのレスポンスそのままを返してくるので、**結果を読み取るのに一手間かかる**(使用例参照)のが残念です。

## インストール
```
npm i mwbot
```

## 使用例
```js
const MWBot = require('mwbot');
const bot = new MWBot({
  apiUrl : 'https://ja.wikipedia.org/w/api.php'
});

(async() => {
  let res, pages;
  res = await bot.request({ action : 'query', prop : 'categories', titles : 'ウィキペディア' , cllimit : 100 });
  pages = Object.values(res.query.pages);
  console.log(pages[0].categories);

  res = await bot.read('ウィキペディア');
  pages = Object.values(res.query.pages);
  console.log(pages[0].revisions[0]['*']);
})();
```

カテゴリ一覧の専用メソッドがないので、`bot.request`で直接APIのオプションを指定しました。
カテゴリはAPIのレスポンスの`query.pages[{ページID}].categories`に埋まっています。
[リファレンス](https://ja.wikipedia.org/w/api.php)を読みつつ取り出しましょう。

# oliver-moran/mediawiki
- https://www.npmjs.com/package/mediawiki

MediaWiki APIのメソッドを直接指定できます。
また、よく使うメソッドには専用の関数も用意されています。

**Queued requsts**という機能でリクエストの間隔を勝手に開けてくれますが、デフォルトの6秒は流石に長いのでオプションで短めにします。
async/awaitが使えればよかったものの、Promiseを自前で実装しているため無理でした。

最終コミットが2014年とかなり古く、今あえて選ぶ理由はなさそうです。

## インストール
```
npm i mediawiki
```

## 使用例
```js
const MediaWiki = require('mediawiki');
const bot = new MediaWiki.Bot({
  endpoint: 'https://ja.wikipedia.org/w/api.php',
  rate: 5e2, // Queued requsts rate
  userAgent: 'ExampleBot',
  byeline: '(example bot edit)'
});

bot.get({ action : 'query', prop : 'categories', titles : 'ウィキペディア' , cllimit : 100 }).complete((res) => {
  const pages = Object.values(res.query.pages);
  console.log(pages[0].categories);
});
bot.page('ウィキペディア').complete((title, text, date) => {
  console.log(text);
});
```

APIを直接指定する場合は、mwbot同様無加工のレスポンスが返ります。

# まとめ
このほかにもMediaWiki用のライブラリと称するものはいくつかありますが、正常に動作しない・人気(npmサイトに表示されるDL数)がなさすぎるといった理由で省略しました。

記事などを取得・検索できるだけでいいなら、`wikijs`を使うのが最も楽です。
ログインや編集ができる必要がある場合は、機能がしっかりしている`nodemw`か`mwbot`を推奨します。

いずれも一長一短あるので、選定する前にやりたい処理がそのライブラリで可能かどうか確認しておくのが良さそうです。