【アタシポンコツ】人工知能で前川みくBotをつくる【アンドロイド】

##(*ΦωΦ*)人工知能APIが公開されたぞ！
アイドルマスター シンデレラガールズの前川みくこと通称みくにゃん、かわいいですよね
みくにゃんとにゃんにゃんしたい
みくにゃんとにゃんにゃんしたい

そんな気持ちでいたところ、株式会社ユーザーローカルさんが人工知能のAPIを公開しました。
[ユーザーと自動で対話する人工知能ボットAPI](http://ai.userlocal.jp/)

ん…？？
![talk_cat.jpg](https://qiita-image-store.s3.amazonaws.com/0/45865/ab48aa89-8ae1-289a-e295-70709c7383f6.jpeg)
みくにゃんだ…
これはみくにゃんが創れるぞ…っ！

##(*ΦωΦ*)成果物
[みくにゃんBot＠人工知能](https://twitter.com/MikunyanUL/with_replies)
先着3000人のAPI募集に入れたので、さっそくGoogle Apps ScriptでTwitter Botを作ってみました。
![cad0e8e6-9461-9203-e65a-8d6ad134102a.png](https://qiita-image-store.s3.amazonaws.com/0/45865/c74c2be4-d649-46c5-342c-7f319921a99f.png)

##(*ΦωΦ*)機能概要
１．10分ごとにメンションを取得
２．返信をユーザAPIで生成
３．ニックネームをAPIで取得（※Twitterの名前ベースなので取れない可能性大）
４．みくにゃん語にコンパイル
５．メンションに返信してくれる

##(*ΦωΦ*)使用したAPI
###自動会話API
送られたメッセージへの返信を取得できます。
LINEのりんなちゃんのイメージです。
###キャラクター会話変換API
メッセージの口調を変換して取得できます。
現在は「猫」「犬」「老人」に対応しています。
###氏名自動識別API 
名前から「姓」「名」「姓(よみ)」「名(よみ)」「性別」「ニックネーム一覧」が取得できます。

##(*ΦωΦ*)免責
Twitter APIやユーザーローカルのAPI制限は意識していません
ソースをそのまま使用する際はご注意ください


##(*ΦωΦ*)ソース

```js:MikunyanBot.gs
// Twitterの認証キー
var CONSUMER_KEY = '{TwitterのCONSUMER KEY}';
var CONSUMER_SECRET = '{TwitterのCONSUMER SECRET}';

var USERLOCAL_API_KEY = 'sample'; // UserLocalのAPIキー
var TWITTER_ID = '@MikunyanUL'; // BotのTwitter ID

// リプライ済みシート
var MENTIONED_SHEET_URL = 'https://docs.google.com/spreadsheets/d/{ドキュメントのID}/edit#gid=0';
var MENTIONED_SHEET;
var ALL_MENTIONED_ID; // リプライ済みID一覧

// 直近100件へリプライをする
function replyAll() {
  
  // リプライ済み一覧の読み込み
  initReplied();
  
  // すべてのメンションを取得
  var mentions = getTwitterMentions();
  for each (mention in mentions) {
    // ツイート情報を取得
    var screenName = mention.user.screen_name;
    var name = mention.user.name;
    var text = mention.text.replace(TWITTER_ID, '').trim();
    var mentionID = mention.id_str;
    
    // リプライ済みならスキップ
    if ( hasReplied(mentionID) ) continue;
    
    // 人工知能のメッセージを取得
    var message = getReplyCatMessage(screenName, name, text);
    
    // リプライ
    tweetText(message, mentionID);

    // リプライ済みID一覧に記録
    MENTIONED_SHEET.appendRow([mentionID]);
  }
}

// リプライ済みデータをスプレッドシートから読み込み
function initReplied() {
 var ss = SpreadsheetApp.openByUrl(MENTIONED_SHEET_URL);
 MENTIONED_SHEET = ss.getSheets()[0];
 
 var lastRow = MENTIONED_SHEET.getLastRow();
 if (lastRow == 0) {
   ALL_MENTIONED_ID = [];
   return;
 }
 
 // データクリーン
 if (lastRow > 200) {
   MENTIONED_SHEET.deleteRows(0, 100);
   lastRow = MENTIONED_SHEET.getLastRow();
 }
 
 var cell = MENTIONED_SHEET.getRange("A1");
 var allValue = cell.offset(0, 0, lastRow);
 ALL_MENTIONED_ID = allValue.getValues().map(function(v) { return v[0] });
}

/**
 * リプライ済みツイートかチェック
 * @mentionID 返信先のツイートID
 */
function hasReplied(mentionID) {
 var index = ALL_MENTIONED_ID.indexOf(mentionID);
 if (index > -1) return true;
 return false;
}

/**
 * ツイートする
 * @text ツイートする文章
 * @mentionID 返信先のツイートID
 */
function tweetText(text, mentionID) {                         
  var service = getService();
  if (service.hasAccess()) {
    var url = 'https://api.twitter.com/1.1/statuses/update.json';
    var payload = {
      status: text,
      in_reply_to_status_id: mentionID
    };
    payload = Object.keys(payload).map(function(key) {
      return encodeRfc3986(key) + '=' + encodeRfc3986(payload[key]);
    }).join('&');
    var response = service.fetch(url, {
      method: 'post',
      payload: payload,
      escaping: false
    });
    var result = JSON.parse(response.getContentText());
    Logger.log(JSON.stringify(result, null, 2));
  } else {
    var authorizationUrl = service.authorize();
    Logger.log('URLにアクセスし認証してください: %s',
        authorizationUrl);
  }
}

// メンション一覧を取得する
function getTwitterMentions() {                         
  var service = getService();
  if (service.hasAccess()) {
    var url = 'https://api.twitter.com/1.1/statuses/mentions_timeline.json';
    var response = service.fetch(url + '?count=200');
    var result = JSON.parse(response.getContentText());
    return result;
  } else {
    var authorizationUrl = service.authorize();
    Logger.log('URLにアクセスし認証してください: %s',
        authorizationUrl);
  }
}

// 返信メッセージを生成
function getReplyCatMessage(screenName, name, message) {

  // ニックネーム　アカウント名が(姓)(名)じゃないと上手くいきにくい
  var nickname = getNickname(name);

  // メッセージ生成
  var normalMessage = getReplyMessage(message);
  normalMessage = normalMessage.replace(/[\.\?？！ー]/g, ''); // 語尾に記号があるとキャラクター変換されないことがある
  var catMessage = (normalMessage ? convertMessage(normalMessage, 'cat') : normalMessage);
  
  // 返信メッセージ作成
  var replyMessage = '@' + screenName + ' ';
  replyMessage += (nickname ? nickname + '、' : '');
  replyMessage += catMessage;
  return replyMessage;
}

// 人工知能による返信メッセージ取得
function getReplyMessage(message) {
  // 自動会話API
  var url = 'https://chatbot-api.userlocal.jp/api/chat';
  url += '?message=' + encodeRfc3986(message);
  url += '&key=' + USERLOCAL_API_KEY;
  
  // API結果取得
  var response = UrlFetchApp.fetch(url);
  var apiResult = JSON.parse(response.getContentText());
  if (apiResult.status != 'success') return;
  return apiResult.result;
}

// 指定したタイプの話し方に変更
function convertMessage(message, characterType) {
  // キャラクター会話変換API
  var url = 'https://chatbot-api.userlocal.jp/api/character';
  url += '?message=' + encodeRfc3986(message);
  url += '&character_type=' + characterType // 猫:cat 犬:dog 老人:roujin
  url += '&key=' + USERLOCAL_API_KEY;
  
  // API結果取得
  var response = UrlFetchApp.fetch(url);
  var apiResult = JSON.parse(response.getContentText());
  if (apiResult.status != 'success') return;
  return apiResult.result;
}

// Twitter名からニックネームを取得
// ※取れないこともあり
function getNickname(name) {
  // 氏名自動識別API 
  var url = 'https://chatbot-api.userlocal.jp/api/name';
  url += '?name=' + encodeRfc3986(name);
  url += '&key=' + USERLOCAL_API_KEY;
  
  // API結果取得
  var response = UrlFetchApp.fetch(url);
  var apiResult = JSON.parse(response.getContentText());
  if (apiResult.status != 'success') return;
  var result = apiResult.result;
  
  // 「名」「名(読み)」が取得できる → ニックネーム返却
  // ※「名」がないデータはニックネームの精度がまだ良くないため
  if (!result.first_name) return;
  if (!result.first_name_yomi) return;
  
  // ニックネームを候補からランダムに取得
  var nicknames = result.nickname;
  var nickname = nicknames[Math.floor(Math.random() * nicknames.length)];
  return nickname;
}

```

##(*ΦωΦ*)ソース(Twitter API用)
```js:MikunyanBot.gs
/**
 * Encodes a string using the RFC 3986 spec.
 */
function encodeRfc3986(str) {
  return encodeURIComponent(str).replace(/[!'()]/g, function(char) {
    return escape(char);
  }).replace(/\*/g, "%2A");
}

/**
 * Reset the authorization state, so that it can be re-tested.
 */
function reset() {
  var service = getService();
  service.reset();
}

/**
 * Configures the service.
 */
function getService() {
  return OAuth1.createService('Twitter')
      // Set the endpoint URLs.
      .setAccessTokenUrl('https://api.twitter.com/oauth/access_token')
      .setRequestTokenUrl('https://api.twitter.com/oauth/request_token')
      .setAuthorizationUrl('https://api.twitter.com/oauth/authorize')

      // Set the consumer key and secret.
      .setConsumerKey(CONSUMER_KEY)
      .setConsumerSecret(CONSUMER_SECRET)

      // Set the name of the callback function in the script referenced
      // above that should be invoked to complete the OAuth flow.
      .setCallbackFunction('authCallback')

      // Set the property store where authorized tokens should be persisted.
      .setPropertyStore(PropertiesService.getUserProperties());
}

/**
 * Handles the OAuth2 callback.
 */
function authCallback(request) {
  var service = getService();
  var authorized = service.handleCallback(request);
  if (authorized) {
    return HtmlService.createHtmlOutput('Success!');
  } else {
    return HtmlService.createHtmlOutput('Denied');
  }
}

```

##参考記事
[Google Apps ScriptでTwitter botを作ってみた](http://qiita.com/abe-perorist/items/2cedc0f577b4f10b4ccb)