ServiceNowでチャットボットを作成する（オーム返し版）

# 概要

ServiceNowではチャット機能が用意されている。チャットの相手先をチャットボットとし、ユーザーからの問い合わせや、作業依頼をチャットボットがユーザーと対話をしながら、対応するような使い方を想定する。
対話に関しては、最終的にはIBMのWatson Assistant(旧Conversation)を使用することを想定しているが、今回はServiceNowの中だけで、ユーザーからチャットボットに対する発言をチャットボットからオーム返しで応答する機能までを作成する。

## ポイント

###ポイント（１）:チャットメッセージテーブル
ServiceNowでユーザーがチャットを開始して発言すると、発言した内容は「live_message」テーブルに自動追加される。チャットメンバーの情報が「live_group_member」テーブル と「live_group_profile」テーブル に保存される。
###ポイント（２）：チャットボット用のアカウント
チャットボット用のユーザーアカウントを追加しておく。またチャットボット用のアカウントでログインできないように、ユーザーの「Lock Out」属性をオンにしておく。
###ポイント（３）：チャット開始トリガー
チャットボットがチャットを開始するトリガーは、「live_message」テーブルへのレコード追加とする。これをBusiness Ruleで作成する。
この時に注意が必要な点は、チャットブループにチャットボットユーザーが含まれていることと、チャットボット自身の発言に応答しないようにすることだ。
チャットボット自身の発言に応答するようにしてしまうと、無限ループが発生する。
###ポイント（４）：スクリプトからのチャットメッセージ送信
チャットメッセージの送信は「global.LiveFeedMessage」の「postMessage」関数を使用する。
## 環境

ServiceNow : KINGSTONE

## 参考資料

[ServiceNow Integration with IBM Watson Conversation Service Whitepaper](https://community.servicenow.com/community?id=community_article&sys_id=fd7c62e1dbd0dbc01dcaf3231f96198c)

# 手順

## チャットボット用ユーザーの作成

「sys_user」テーブルにチャットボット用のユーザーを作成する。
今回は、FirstNameに「Service Desk」を設定している。
また、メールアドレスを適当に入力しておく。メールアドレスが無いとおそらくチャットユーザーの宛先に指定できないためだ。

![chatbotuser.png](https://qiita-image-store.s3.amazonaws.com/0/197947/30436ca5-5eec-5baa-ac94-bbd853077d9f.png)

## チャット開始トリガーとなるBusiness Ruleを作成する

BusinessRuleを新規作成し次の通り設定する。

|属性|値|
|---|---|
|Table|Live Feed Message|
|When|after|
|Advanced|チェックする|

AdvancedタブのScriptに次の通りスクリプトを入力する。

```javascript
(function executeRule(current, previous /*null when async*/) {
	var userDislayName = gs.getUserDisplayName();
	
	var chatUtil = new LiveChatUtility();
	
	var currentUserProfile = chatUtil.getProfileFromName(userDislayName);
		
	var profilefromrecord = current.getValue('profile');
	
	if (currentUserProfile != profilefromrecord) {
		gs.info('プロファイルが現在のユーザーで無いため処理を抜けた。');
		return;
	}
	
	// Live Messageのgroupidを取得
	var convId = current.getValue('group');
	
	var CHATBOTUSERNAME = 'Service Desk';
	
	// BOTのprofileを取得
	var botprofile = chatUtil.getProfileFromName(CHATBOTUSERNAME);
	
	if (chatUtil.isLiveGroupMember(convId, CHATBOTUSERNAME) == false) {
		gs.info('現在のチャットグループにボットユーザーが含まれないために処理を抜けた。');
		return;
	}
	
	
	var message = current.getValue('message');
	var lm = new global.LiveFeedMessage();
	var data = {
		"message": message + ' (from chat bot)',
		"group_id": convId,
		"from_profile":botprofile
	};
	
	lm.postMessage(data);
	
	
})(current, previous);
```

```javascript
var chatUtil = new LiveChatUtility();
```
は、Script Includeでこの次に作成する。

```javascript
var currentUserProfile = chatUtil.getProfileFromName(userDislayName);
```
は現在のログインユーザー名からユーザーのsysIDを取得している。

```javascript
    if (currentUserProfile != profilefromrecord) {
        gs.info('プロファイルが現在のユーザーで無いため処理を抜けた。');
        return;
    }
```
現在のユーザーのsysIDとLive Message テーブルのProfile(発言者のID)が一致しているかを判定している。
これによりチャットボット自身の発言の場合はここで処理を抜けることが出来る。

```javascript
    if (chatUtil.isLiveGroupMember(convId, 'Service Desk') == false) {
        gs.info('現在のチャットグループにボットユーザーが含まれないために処理を抜けた。');
        return;
    }
```

これにより、チャットボット 向け以外の発言に対してチャットボットが応答しないように処理を抜けている。

```javascript
lm.postMessage(data);
```

dataには下記の設定を行う。

|属性|説明|
|---|---|
|message|チャットへの発言内容|
|group_id|発言先のチャットグループを指定|
|profile|発言者のID|



## Business Ruleで使用している関数をScript Includeで定義する。

Script Includeで下記のように２つの関数を定義する。
getProfileFromNameは、ユーザー名からprofileのIDを取得する。
isLiveGroupMemberは、指定されたグループにチャットボットユーザーが存在するかをユーザ名で検索して判定している。


```javascript
var LiveChatUtility = Class.create();
LiveChatUtility.prototype = {
	initialize: function() {
	},
	getProfileFromName:function(name) {
		var gr = new GlideRecord('live_profile');
		gr.addQuery('name', name);
		gr.query();
		if(gr.next()) {
			return gr.getUniqueValue();
		}
	},
	isLiveGroupMember:function(group, profile) {
		var gr = new GlideRecord('live_group_member');
		gr.addQuery('group', group);
		gr.addQuery('member.name', profile);
		
		gr.query();
		return gr.next();

	},
	
	type: 'LiveChatUtility'
};
```

#動作確認

オーム返しで応答するだけだが、相手先がService Deskの時のみ応答することを確認した。下記は相手先がService Deskの場合の応答だ。

![chatbottry.png](https://qiita-image-store.s3.amazonaws.com/0/197947/9857e39e-0654-f165-df89-27248b733805.png)

#今後の展開について

IBMのWatson Assistantはユーザーのやりたいこと（Intent)とその対象(Entity)を対話(Dialog)を行うことによって取得するこが可能だ。
また、その対話はREST Messageを通じてスクリプトから実施することが可能となっている。ユーザーを追加したい、新規にVMを追加したいなどの日常作業について、Watson Assistantとの対話によりユーザーのやりたいこととその対象、各種設定情報をユーザーから聞き出して自動設定をする事が出来るようにしたいと考えている。
ただ、実際の運用時には、ユーザー追加には、管理者の許可が必要であったり、VMを追加する場合にもその出費の承認が必要であったりするため、チャットボットで自動化できる範囲については、検討が必要だ。