RedmineとMattermostとCTIを連携してチケット代理起票をサポートする

[Redmine＋異世界ファンタジーな小説をKindleストアで公開している](https://www.amazon.co.jp/dp/B07KNHXLZG)8amjpです（宣伝）。

さて、[Redmine](https://www.redmine.org/)でチケット起票といえば基本的には自分でやるもんなんですけど、電話で受け付けた質問なり要望なりを、その人の代理で起票することってありますよね？　ありません？　少なくともうちの職場ではあるんです。サポートデスクのチケット管理をRedmineでやってるので。
で、起票したチケットの「作成者」フィールドは基本的に変更できないんですが、[Redmine Edit Issue Author](https://www.redmine.org/plugins/redmine_editauthor)プラグインを導入して、作成者を実際の質問者・依頼者に変更できるようにしています。
が、せっかくなのでその辺を自動化したい。ということで、Redmineと[Mattermost](https://www.mattermost.org/)とCTI、それから中継用の[Express](https://expressjs.com/ja/)製Webアプリを連携して、チケット代理起票をささやかながらサポートする仕組みを構築してみました。

## CTIから中継Webアプリ（Express）へのリクエスト

さて、最初に取り掛かったのがプチCTIシステムの作成だったのですが……
ここは各社で環境もまちまちで細かいこと言ってもしょうがないので結論だけ言うと、応答したタイミングで**「http://[中継Webアプリのホスト名]/[着信電話番号]**」というURLを叩くだけの仕組みを作りました。
`GET http://express.local:3000/0123456789`みたいな。

## MattermostでIncoming Webhooksを作成

続いて、Mattermostで外部サービスからのリクエストを受け付けるためにIncoming Webhooksを作成しておきます。（特に難しいことはないので説明は割愛）
`http://mattermost.local:8065/hooks/xxxxxxxxxxxxxxxxxxxxxxxxxx`というURLが準備されたものとして進めますよ。

## 中継Webアプリ（Express）からMattermostへのポスト

続いて、CTIからのリクエストを受け付ける中継Webアプリ（Express製）の作成です。
`express`コマンドで骨組みをサクッと作成してください。あと、[node-mattermost](https://www.npmjs.com/package/node-mattermost)の力を借りるので`npm i node-mattermost`しといてください。
終わったら、必要なものを追加します。

### users.json（Redmineのユーザー情報）を配置する

やりたいことは、Redmineのユーザー情報を電話番号で検索する、ということなんです。
ところが、RedmineのAPIには電話番号をキーに検索する仕組みがない。そもそも「電話番号」というフィールド自体を持っていない。
しょうがないので「電話番号」というカスタムフィールドを作り、せっせと入力した後で、すべてのユーザー情報をAPIで抜き出して`users.json`という名前で保存します。

### routes/index.jsを修正する

次にroutes/index.js、メインロジックの部分を作成します。

```js
var express = require('express');
var router = express.Router();
// さっき保存したusers.jsonを読み込みます
var users = require('../users');
// node-mattermostの引数はさっき作ったIncoming Webhooks用URL
var Mattermost = require('node-mattermost');
var mattermost = new Mattermost('http://mattermost.local:8065/hooks/xxxxxxxxxxxxxxxxxxxxxxxxxx');

/* GET home page. */
router.get('/', function(req, res, next) {
  res.send('動いてるよ');
});

// 電話番号がパラメータとして渡ってきたときに下記の処理を実行
router.get('/:tel(\d+)', function (req, res, next) {
  // 電話番号の部分を変数telに格納
  let tel = req.params.tel;
  // users.jsonから、最初のカスタムフィールドのvalueが電話番号と一致するものを抽出
  let user = users.users.filter(user => user.custom_fields[0]['value'] == tel);
  if (user.length > 0) {
    message = {
      // メッセージ本文
      text: `${tel} （${user[0].firstname}）から着信\nhttp://redmine.local/redmine/issues/new?issue[author_id]=${user[0].id}`,
      // 対象のチャンネル
      channel: '#call',
      // メッセージ発信者（あまり関係ない？）
      username: 'call'
    };
    mattermost.send(message);
    res.json(message);
  } else {
    res.end();
  }
});

module.exports = router;
```

こんな感じで。
メッセージ本文の部分だけ補足説明しますね。
`${tel} （${user[0].firstname}）から着信\nhttp://redmine.local/redmine/issues/new?issue[author_id]=${user[0].id}`という文字列を出力するのですが、`${tel}`には着信した電話番号、`${user[0].firstname}`にはヒットしたユーザーのファーストネーム、`${user[0].id}`はヒットしたユーザーのIDが入ります。
Redmineのホスト名やら細かいところは好きなように修正してください。

結果、下記のようなメッセージがMattermostの指定したチャンネルにポストされます。

> 0123456789 （紅子）から着信
> http://redmine.local/redmine/issues/new?issue[author_id]=100

## Redmineの新規チケット作成画面を開く

MattermostにポストされたURLをクリックすると、「作成者」が入力された状態でRedmineの新規チケット作成画面が開きます。
URLに`issue[author_id]=xxx`というパラメータを加えることで実現しています。このパラメータについては、[Redmine.JP Blog](http://blog.redmine.jp/)の「[Redmineワンポイントチェック(2): URLに入力値を埋め込んでチケット作成のテンプレートを実現する](http://blog.redmine.jp/articles/opc/new-issue-template/)」という記事に詳しく書いてありますよ。
また、前述のとおり、チケットの作成者を変更するためには[Redmine Edit Issue Author](https://www.redmine.org/plugins/redmine_editauthor)プラグインが必要です。

てな感じで、Express製Webアプリを仲介してRedmineとMattermostとCTIを連携する方法でした。
電話によるチケット代理起票が日常化している職場の方、よろしければご参考までに。