unpocoプログラミング

ゲームライフを快適にするためにプログラミングを勉強します。間違ってたら教えてください。

Alexa-Hosted (Python)でAlexaスキル作成

作り方を簡単にまとめておく。

今回つくるもの

ユーザーが言った数字までアレクサが数えてくれるスキル。
※延々と続けられても困るので最大10までとする

ユーザー > アレクサ、数字の勉強をスタート(開始)
アレクサ > いくつまで数えましょうか?1~10の数字を教えてください。
ユーザー > 5
アレクサ > 1 2 3 4 5。5まで数え終わりました。(終了)

スキルの作成

ここで決めるスキル名は「呼び出し名」、「公開されるスキルの名前」とは関係ないものでも大丈夫。
https://i.imgur.com/NRewZce.png

呼び出し名の設定

「呼び出し名は2語以上である必要があります。」と赤字で警告が出ていますが、2語以上含まれているなら無視して構わない。
https://i.imgur.com/TFbiBdG.png

インテント

既存のインテント(HelloWorldIntent)を削除

https://i.imgur.com/Q0ztj9v.png

インテント作成

https://i.imgur.com/sdYUZAX.png

サンプル発話の設定

発話のゆれを考慮して3パターン用意する。
{number} はスロットと呼ばれるもので数字が入ってくることを想定しています。 https://i.imgur.com/0dy88cQ.png

スロットについて

スロットタイプリファレンス
https://developer.amazon.com/ja/docs/custom-skills/slot-type-reference.html

カスタムスロットタイプの作成と編集
https://developer.amazon.com/ja/docs/custom-skills/create-and-edit-custom-slot-types.html

コードをいじる

LaunchRequestHandler(既存のものを修正)

「アレクサ、数字の勉強をスタート」でLaunchRequestが発行される。
speak_outputをいじる。

speak_output = "いくつまで数えましょうか?1~10の数字を教えてください。"

SayNumberIntentHandler(追加)

「2」や「5まで数えて」でSayNumberIntentリクエストが発行される。
既存のHelloWorldIntentHandlerを書き換えるのが楽。

class SayNumberIntentHandler(AbstractRequestHandler):
    def can_handle(self, handler_input):
        # type: (HandlerInput) -> bool
        return ask_utils.is_intent_name("SayNumberIntent")(handler_input)

    def handle(self, handler_input):
        # type: (HandlerInput) -> Response
        
        # スロットの情報取得
        slots = handler_input.request_envelope.request.intent.slots
        number_slot = slots.get("number")
        if number_slot is None or number_slot.value is None:
            logger.info("numberスロットが含まれていない")
            raise ValueError
        
        try:
            number = int(number_slot.value)
        except e:
            raise e
        
        if number < 1 or number > 10:
            raise ValueError
        
        speak_output = ""
        for i in range(number):
            speak_output += "<s>{} </s>".format(i+1)
        speak_output += "。{}まで数え終わりました。".format(number)

        return (
            handler_input.response_builder
                .speak(speak_output)
                # .ask("add a reprompt if you want to keep the session open for the user to respond")
                .response
        )
リクエストハンドラの追加

忘れずに追加する。(コードの下のほうにあります)

sb.add_request_handler(SayNumberIntentHandler())

HelpIntentHandler(既存のものを修正)

スキル起動後「ヘルプ」等の発話でHelpIntentリクエストが発行される。
speak_outputをいじる。(スキルの使い方を教える)

speak_output = "「5」「5まで」「5まで数えて」のように1~10の数字を教えてください。私がその数字まで数えます。"

CancelOrStopIntentHandler(既存のものを修正)

スキル起動後「キャンセル」等の発話でCancelIntentリクエストが発行される。
スキル起動後「ストップ」等の発話でStopIntentリクエストが発行される。
speak_outputをいじる。

speak_output = "また使ってね。"

CatchAllExceptionHandler(既存のものを修正)

例外ハンドラ。
SayNumberIntentHandlerでraiseした例外もここでハンドリングされる。
speak_outputをいじる。(今回はヘルプの時と同じにする)

speak_output = "「5」「5まで」「5まで数えて」のように1~10の数字を教えてください。私がその数字まで数えます。"

デプロイ

編集が終わったら「デプロイ」ボタンを押す。

テスト

テストは必ず実施する。マイクの使用を推奨。 https://i.imgur.com/nuCNKiR.png

https://i.imgur.com/lJYKkry.png

参考

Alexaスキル開発トレーニングシリーズ(全6回)

コードは一切出てこない。概要を掴むのにおすすめ。
www.youtube.com

スキル開発 基礎トレーニングシリーズ

Alexa-Hosted (Python)が無いころの資料っぽい。
Alexa | アレクサ | Alexaスキル開発トレーニング

おわり

【Python】Cloud Firestoreを使ってみる

公式のクイックスタート見て試しただけ。

認証

サービスアカウントを作成

役割を [プロジェクト] > [オーナー] にする。

鍵を作成

jsonでok

環境変数を設定

export GOOGLE_APPLICATION_CREDENTIALS="[PATH]"

ライブラリをインストール

pip install --upgrade google-cloud-firestore

使う

はじめに

from google.cloud import firestore

# Project ID is determined by the GCLOUD_PROJECT environment variable
db = firestore.Client()

ドキュメント作成

doc_ref = db.collection("warframe").document("gauss")
doc_ref.set({
    "born": 2019,
    "prime": False
})

doc_ref = db.collection("warframe").document("mesa_prime")
doc_ref.set({
    "born": 2018,
    "prime": True
})

データを追加

「merge=True」を付ける。

doc_ref = db.collection("warframe").document("mesa_prime")
doc_ref.set({
    "health": 125
}, merge=True)

データを更新

[collection]/[document] のような書き方もできる。

doc_ref = db.document("warframe/mesa_prime")
doc_ref.update({
    "health": 300
})

すべてのドキュメントを取得

wf_ref = db.collection("warframe")
docs = wf_ref.stream()

for doc in docs:
    print("{} => {}".format(doc.id, doc.to_dict()))
gauss => {'prime': False, 'born': 2019}
mesa_prime => {'born': 2018, 'prime': True, 'health': 300}

条件にあったドキュメントを取得

docs = db.collection("warframe").where("health", ">", 100).stream()

for doc in docs:
    print(u'{} => {}'.format(doc.id, doc.to_dict()))
mesa_prime => {'born': 2018, 'prime': True, 'health': 300}
where

where(field_path, op_string, value)
op_stringには<, <=, ==, >=, > が使える。

参考

cloud.google.com

googleapis.github.io

おわり。

【Python】Cloud Storageを使う際にDefaultCredentialsErrorが出た時の対処法

サービスアカウントの設定

作成ページに移動

https://i.imgur.com/38ZpiKv.png

必要な項目を入力して「作成」

https://i.imgur.com/MFvAdaE.png

役割を選択して「続行」

ストレージ > ストレージ管理者 にしました。 https://i.imgur.com/QjaYFiZ.png

鍵を作成

https://i.imgur.com/b9kvRuS.png

メールアドレスをコピー

https://i.imgur.com/lbj7k0I.png

Storageの設定

アクセスしたいバケットにメンバーを追加

上でコピーしたメールアドレスを貼り付ける。 https://i.imgur.com/KWruS5U.png

環境変数を設定

コマンド

[PATH]には上で作成した鍵を指定する。
$ export GOOGLE_APPLICATION_CREDENTIALS="[PATH]"

python

import os
os.environ['GOOGLE_APPLICATION_CREDENTIALS'] = "[PATH]"

参考

cloud.google.com

おわり

【GAS】GASでCloud Functionsを定期実行

2019/07/31時点ではCloud Schedulerジョブは無料で3つまでしか動かせないようなので別の手段を探した。
GASで出来そうだったのでテストでSlackへの通知までやってみた。

Cloud Functions

トリガーを「HTTP」にする

URLをメモっておく。

https://i.imgur.com/yuSzSfu.png

コード

Slack側に送るJSONを作って返す。

#!/usr/bin/python
# -*- coding: utf-8 -*-
import json


def hello_world(request):
    data = {
        "text": "はろーわーるど",
    }

    json_data = json.dumps(data)
    return json_data

Google Apps Script

トリガーの設定

実行したいタイミングに応じて設定する。

プロパティ設定

コードに直接書くのはやめたほうがいいものを設定しておく。

https://i.imgur.com/ff0pR6p.png

https://i.imgur.com/JlWOgrg.png

コード

function myFunction() {
  var properties = PropertiesService.getScriptProperties();
  var gcf_url = properties.getProperty("GCF_URL");
  var webhook_url = properties.getProperty("WEBHOOK_URL");
  
  var json = getJson(gcf_url);
  sendToSlack(webhook_url, json);
}

function getJson(url) {
  var res = UrlFetchApp.fetch(url);
  var content_text = res.getContentText();
  
  return content_text;
}

function sendToSlack(webhook_url, json) {
  var params = {
    "method": "post",
    "payload": json
  };
  
  UrlFetchApp.fetch(webhook_url, params);
}

実行結果

ちゃんと送られました。
https://i.imgur.com/kVO5MLb.png

参考

developers.google.com

developers.google.com

おわりに

割と簡単にできた。
この間作った糞箱セール情報もこのかたちに変更しようと思う。

おわり。