Alexaでタイピングレスなツイッターライフを送る

**Amazon Echo dot (第3世代)** が999円(本体価格だけを見ると219円)で買えたのでせっかくだしアレクサ開発しようと思い立ってTwitterクライアント作りました記録。

# はじめに

Alexaスキル開発では先に言うと、**Node.js**, **Python**, **その他**(カスタム)、の中で使う言語を選べるんですが私自身がPythonくらいしかまともに使えないので今回はPythonで開発するもので進めていきます。

かなり初心者向けかつとりあえず動けばいいや、みたいな大雑把な話しかできない部分が多くあると思いますがその辺はご了承ください。

今回この記事ではTwitterへAPI経由でツイートする、などTwitter操作に関する部分は詳しくは取り扱いません。

進めるための前提として

* [Amazon Skills Kit Official Site](https://developer.amazon.com/ja-JP/alexa/alexa-skills-kit "Amazon Skills Kit Official Site: Build Skills for Voice") でDeveloper登録を済ませる。 **※注意！ 実際に使うAlexaでログインするアカウントと開発にログインするアカウントは同一のものを利用してください。**
* Pythonが多少わかると良い。
* TwitterのAPI keyがないと始まらない
* ~~滑舌~~

が必要となります。

# 最終目標

* 「アレクサ、ツイッターで〇〇ってツイートして。」
* 「アレクサ、ツイッターで〇〇ってつぶやいて。」

とアレクサに命令して自分のアカウントでツイートできるようにする。

# 実際に作ってみる

ここから開発作業をしていきます。
全ての作業がブラウザで行えるのでエディタなどは必要ありません。

## スキルを作成する

[Alexa Developer Console](https://developer.amazon.com/alexa/console/ask "Alexa Developer Console") にログイン、アクセスします。
スキルの作成というボタンがあるのでクリックします。

### スキル名と言語

スキル名とデフォルトの言語はお好きなものを選んでください。
スキル名に関しては後から変えられないのでわかりやすい名前にするといいと思います。

### モデルとバックエンドリソース

モデルは **カスタム** を選択してください。
これを読んでスマートホーム系スキルだとかを作りたくなったら選んでみてください。
カスタムを選ぶとたぶん基本的なプリセットになります。

バックエンドリソースは最初にも書いてある通り今回は、 **Alexa-Hosted (Python)** を選びます。
Alexa Developer Console内でAWS Lambdaのホスティングを管理してくれるので楽です。
Node.jsについてはやろうやろうと思って手を付けられていないので解説できません。ごめんなさい。
また、Alexa-Hostedを選ぶことでAmazonDeveloperにログインしているアカウントへ請求が来るようになるみたいです。
ショッピングを利用していれば請求等も同一アカウント内請求先から支払われるかと思います。

![modelandberesource.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/343449/a89ea37b-1a8e-1559-4d8e-0d984070cd04.png)

#### 補足 (読み飛ばしてもOK)

ちなみにバックエンドリソースにて、ユーザ定義のプロビジョニングを選ぶとAlexa Developer Console上でコードエディタが使えなくなります。
代わりに自前のAmazon Web Servicesアカウント上で自分でLambdaを構築しこっちでゼロからコードを書くことになります。
またAlexa SkillsとAWS Lambdaで互いに通信をするためのエンドポイントの設定やIAMロールの設定なども自分で行うことになります。
バックエンド側の構築も自分で行えるのでより高度なスキルが作れますが、当然全て使い方がわからないといけないので上級者向けです。

### 作成完了

上記のものを選んだらあとはページ右上のスキルを作成を押すだけです。
作成完了までに数分ほど時間がかかりますが、ここはアレクサに「アレクサ、おすすめのジャズを流して」とでも言ってコーヒーでも飲みながら優雅に待ちましょう。

作成が完了すれば、HelloWorldを含む一通りの設定を **勝手に全部** やってくれます。
ぶっちゃけHelloWorldだけなら（少し弄る箇所はありますが）スキルの作成だけでできてしまいます。簡単！

## 呼び出し名

まず最初にしておきたいのは呼び出し名の変更です。
デフォルトでは最初に設定したAlexa Skill名と同一のものが入っていますが、呼び出すときはもっと簡単に呼び出したいとかあると思います。
目標としては、 **「アレクサ、ツイッターで○○ってつぶやいて。」** とするとツイートできるようにしたい。
この場合

1. アレクサ
2. ツイッター
3. ○○(呟く内容)
4. つぶやいて。

と分けられ上から順に

1. アレクサの起動ワード
2. 自作スキル用の起動ワード
3. 内容変数
4. 命令

となります。

なので今回は、 **スキルを呼び出す名前** としては「ツイッター」を入れるのが妥当です。
もちろん自分の好きなワードを入れても構いません。

この呼び出し名はあくまで呼び出し名なので間違っても「ツイッターで」とそのまま入力してはいけません。
これするとたぶん「ツイッターでで」って言わないと起動しない面白いことになります。
1単語入れておけば後に続く起動ワードは勝手にAlexaが判断してくれます。
（例えば呼び出し名を「ツイッター」にしておけば「アレクサ、ツイッター起動して○○つぶやいて。」でも行ける）
その他にも命名用件がありますが、呼び出し名を決めるページに書いてあるので読んでください。

※呼び出し名は2語以上である必要があります。と赤文字で出ますが1語でもなんか行けるっぽいです。というか1語のほうが都合がいい。

あとは詳しいことは公式ドキュメントとして [カスタムスキルの呼び出し名を決定する](https://developer.amazon.com/ja/docs/custom-skills/choose-the-invocation-name-for-a-custom-skill.html) に乗っています。

## HelloWorld

作成されたAlexa Skillにはデフォルトで `HelloWorldIntent` とそれに対応するコードが出来上がっています。
ひとまずこれを使って HelloWorld するところだけやってみます。
HelloWorldとかどうでもええわって人は読み飛ばしてください。

テストタブを開き、左上の非公開となっている部分を開発中を選択します。
その下に入力欄があると思うので、まずそこに呼び出し名のみを入力、送信します。
（ここでは私の呼び出し名は「テストツイッター」としました）

![HWtest1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/343449/23fe2830-02c4-e4f7-4ad1-44273795921f.png)

そうすると何やら英文が返されると思います。
ものすごく簡単に言うと「ハローかヘルプって言ってみ？」と言われてます。

ということで Hello と返してみましょう。

![HWtest2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/343449/d42b5725-744b-c094-ea7c-84c5fb69a46d.png)

するとアレクサからHello World!と返されました！

簡単ですね！！

ビルドタブの `HelloWorldIntent` を見てみると発話のリストとして7種類ほどがデフォルトで入っています。
今はHelloと入力しましたが、このインテントの中に入っている言葉を話しかけても同じくHello World!と返してくれます。

ちなみに言葉を分割せずともHello World!と言わせることもできます。

![HWtest3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/343449/302db531-7391-55ff-6b51-f947dd419fb6.png)

最初のやり方では、名前で呼び出す→命令を出す、と分けていましたがもう一つの方法では呼び出すとともに命令を出す、ということをしていました。

コードエディタを見ながらアレクサからの返事やインテント名などと照らし合わせるとなんとなーくこう動いてるんだなというのがわかるかなと思います。

基本的には登録したインテントに対応するクラスが実行されて、クラス内の `handle` 関数の中で処理されたものがreturnで喋ってくれるみたいなそんな感じだと思います。
`HelloWorldIntentHandler` クラスだと `speak_output` 変数に喋らせたい言葉を入れてreturnに変数を入れています。

## 実装していく

どういう風に動いているのかがなんとなくわかったところで実装に入ります。

### インテント

#### インテントの作成

ビルドタブからインテント横の追加ボタンを押します。
カスタムインテントを作成を選び、自分の好きな名前を決めて作成します。
ここでは私はインテント名を `EchoTweetIntent` としました。

次にこのインテントを実行するための発話を入力するわけですが、その前に必要な作業があるのでやっておきましょう。

#### スロットタイプの追加

スロットタイプ横の追加ボタンを押します。
Alexaのビルトインライブラリから既存のスロットタイプを使用、を選択します。
数値・日付・時刻の中から `AMAZON.SearchQuery` の右のスロットタイプを追加ボタンを押します。

スロットは今回、ツイートしたい内容を聞き取り、変数のようなものとして扱うために使います。
他のビルトインライブラリのものは地名や人名、数値などだけを認識してくれるものなので、自分の好きなワードを入れてツイートするのには向かないと思い、私は `AMAZON.SearchQuery` を選びました。
検索のためのワードが入るのでなんでもいけそうだな、と。（ほかに良い方法、または間違いあったら教えてください）

#### インテントスロットの追加

スロットタイプの追加が出来たら、先ほど自分で作ったインテントに戻ります。
発話を入力する前に変数のようなものとなるインテントスロットを作ります。

名前は分かりやすい好きな名前を入れて、横の+ボタンを押して追加します。（私は `msg` としました。）

作成ができたら「スロットタイプを選択」をクリックし先ほど追加した `AMAZON.SearchQuery` を選択します。
これで変数のようなものとして扱えるようになりました。

#### 呼び出し発話を追加

サンプル発話の入力欄に、なんと言ったらツイートするかという発話を入れていきます。
今回は

* 〇〇ってつぶやいて。
* 〇〇ってツイートして。
* 〇〇って投稿して。

の3種類を例として追加します。〇〇には呟く内容 ( `msg` ) が入ります。

スロットとして用意したものを利用するには、インテントスロット名を `{}` で囲みます。
最初の { を入力すれば予測候補が出てきますが、一つしか作っていないのでそのままTABキーを押すことでインテントスロットを自動挿入してくれます。

![intentresult1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/343449/92a5645c-9a35-5b1c-20f8-360df3bc1258.png)

正しく3種類を追加することができればこうなると思います。

今回はこの3種類だけですが、おそらくこれだけではツイッターライフは快適にならないと思うので適宜色々な、オリジナルな発話を入れてあげてください。

ここまでの作業が完了したらモデルを保存を押し、モデルをビルドしましょう。ビルドには少し時間がかかります。

### コードを書く

インテントの準備が整ったので次はそれに対応するコードをPythonで書いていきます。
ここから先はコードエディタを使っていきます。
元も子もないことを言いますが基本的にデフォルトで存在するコードのコピペ改変でだいたいなんとかなります。

この先のTwitterのコードは私の書き方でやりますので、もし自分のやり方がある場合はお好きなように読み替えてください。

#### モジュールの追加

TwitterAPIを利用するために以下を追記します。
1,2行目は最初からあるので3行目のみ追記です。

```text:requirements.txt
boto3==1.9.216
ask-sdk-core==1.11.0
requests_oauthlib
```

新しいモジュールを使いたい場合もこのテキストファイルに書き込むことでインストールしてくれます。

pythonコードのほうにもimportを上のほうのいい感じのところに追記します。
2行目のimportは先ほど作ったインテントで得た喋った内容（スロットに入っているもの）をコードで取得するのに必要になります。

```python:lambda_function.py
from requests_oauthlib import OAuth1Session
from ask_sdk_core.utils import get_slot_value
```

#### クラスを作る

既存のクラスを真似てクラスを作ります。
作ったインテント名が `EchoTweetIntent` だったのでクラス名は `EchoTweetIntentHandler` としたいと思います。
とりあえず私は `HelloWorldIntentHandler` クラスの下に新しいクラスを作りました。

`handle` 関数内に処理を書くだけなので、ついでにツイッター投稿用処理も一緒に書いてしまいます。

```python:lambda_function.py
class EchoTweetIntentHandler(AbstractRequestHandler):
    def can_handle(self, handler_input):
        return ask_utils.is_intent_name("EchoTweetIntent")(handler_input)

    def handle(self, handler_input):
        URL = 'https://api.twitter.com/1.1/statuses/update.json'
        CK = 'key' # Consumer Key
        CS = 'key' # Consumer Secret
        AT = 'token' # Access Token
        ATS = 'token' # Access Token Secret
        twitter = OAuth1Session(CK, CS, AT, ATS)
        tweetmsg = ask_utils.get_slot_value(handler_input=handler_input, slot_name="msg")
        if tweetmsg:
            params = {'status': tweetmsg + "\n(Alexaからのツイートです)"}
            post_response = twitter.post(URL, params=params)
            if post_response.status_code == 200:
                speak_output = tweetmsg + "とツイートしました。"
            else:
                speak_output = "すみません。ツイートの送信に失敗しました。"
        else:
            speak_output = "すみません。なんと言ったかわかりませんでした。"
        return handler_input.response_builder.speak(speak_output).response
```

スロットの値を取得するためのコードは公式ドキュメントにあったものを参考にしました。
[Alexaから送信されたリクエストを処理する | Alexa Skills Kit](https://developer.amazon.com/ja/docs/custom-skills/handle-requests-sent-by-alexa.html#get-slot-values)

`twitter` にはOAuth用のトークンとかを入れています。それぞれの変数に自分の持っているトークン等を書き加えてください。
`tweetmsg` には `ask.utils.get_slot_value` で取得できるスロットの中身（自分が発言した内容）を入れています。
行最後の `slot_name="msg"` のmsgの部分を自分の作ったスロット名に合わせて変えてください。
ツイッターに投稿するメッセージには `\n(Alexaからのツイートです)` というのが入っていますが、書いてある通りアレクサからのツイートというのをわかりやすくしています。

あとはPOSTして200が返ってきたらツイートしましたと言わせるように `speak_output` 変数内に入れています。

#### クラスを登録(?)する

クラスと投稿処理が完成したらクラスが動いてくれるように追加してあげないとしないといけません。
コードエディタの一番下のほうに `sb.add_request_handler(LaunchRequestHandler())` と同じようなものが沢山書かれていると思います。

その付近で `sb.add_request_handler(自分の作ったクラス名())` と追記してあげます。

```python:lambda_function.py
sb.add_request_handler(EchoTweetIntentHandler())
```

ここで注意してほしいのが、自分の作ったクラスは `sb.add_request_handler(IntentReflectorHandler())` よりも **上に書いて** ください。
このいわゆるリフレクタは喋った言葉が〇〇のインテントにトリガしましたよ。というのをレスポンスとして返してくれます。
なのでこれが先に来ていると全てこっちに先に拾われて書いたコードが実行されなくなります。

ここまで処理が完成したら保存を押してからデプロイをします。

## テストする

HelloWorldのときにも使ったテストタブを使います。

私の場合は **テストツイッター** が呼び出し名なので、
入力フォームに「テストツイッターでテストってつぶやいて」と入力送信します。

アレクサから「テストとツイートしました」と返事が返ってきて、ツイッターのタイムラインに投稿されていれば成功です！！！

HelloWorldのときに試したやり方や、他のトリガになるインテントの発話などでも試してみてください。

実際にアレクサに話かけるときには「アレクサ、テストツイッターでテストってつぶやいて」となります。

なんか違う動きするな？と思ったらコードを見直したり、スペースやインデントがおかしかったりを見直してみてください。

## 完成！

テストタブで開発中が選択されている状態だと、自分のAmazonアカウントとリンクしているAlexaでのみ自由に自分の作ったスキルを使うことができます。
開発中であれば他の人に公開されることもありません。（公開するにはまた別の作業が必要になります）
テストタブで入力するだけでなく実際にアレクサの入っているEchoだったりで話しかけてみて試してみてください。

あとはもう今までの流れと同じような作業をしてインテントやスロットを自分好みに微調整していくだけです。簡単ですね。

これで自宅でおふとんでゴロゴロ漫画読みながらツイッターライフに勤しむことができます！！！
**レッツ！ツイッター！！**

# 終わりに

私がアレクサによくツイートさせているのは「ねむい」か「腹へった」のどっちかですが楽しいです。
何より「アレクサ、ツイッターで腹へったってツイートして」でツイートできるんですよ！？楽しい。

Alexa Skills Kitを初めて触ったのですが最初は分からない単語しかなくて困惑しました。インテントってなんやねんとか。
やってみたら意外と簡単に色々出来たので楽しかったです。

ぶっちゃけhandleに処理書けばなんでも実行できるので、ツイッター以外にもDiscordやSlackなど **APIの公開されているものならなんでも** アレクサから扱えるんじゃないかなと思います。
APIなくともプログラムから操作できるものなんかそれこそ当然のように扱えると思いますが。
頑張ればアイアンマンのジャービスごっこも夢じゃない。

「操作する」というプロセスが感覚的になくインターネットにアクセスして何かを出来るというのは、ようやく自分の中での時代が進歩したな感があって満足感すごかったです。

ここまで読んでいただきありがとうございます。
かなーり大雑把な話しかできませんでしたが、指摘点などありましたらコメントしていただけたら幸いです。