1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

俺がわかるように書いたAlexaスキル入門5(アトリビュート)

Last updated at Posted at 2021-06-20

Alexaの基本的なスキル作成5(アトリビュート)

2021/6

■いや

書くつもりはなかったんだけど、あまりに公式ドキュメントが雑なので書いとく。
道場もNode.jsだけだし、おまじないだなんだでのなし崩し感があったので。

ついでにAudioPlayerで止めた次の日でもresumeできるのが作れそうだったのもある。


■Attribute?

スキルが何かを覚えておく機構を称してAttributeという単語が使われています。

3種類ありますが、実質2種類。

名前 性質
Request リクエストを得て、返すまでの間記憶してくれるらしいが、何の役に立つのか今のところ分からない。can_handleからhandleに渡すなにか?
Session スキルを起こして、一連の会話が終了するまでをセッションといい、その間記憶してくれる。
persistent DBに実際に保存するので、消すまで記憶してくれる。

で、どこから使うのかですが、おなじみのhandler_inputにattributes_managerというのが入ってます。
これのメソッドからアクセスします。

session

こんな感じで、attributes_managerのsession_attributesというのにdictを放り込めば保持してくれます。

attr = handler_input.attributes_manager.session_attributes
if type(attr) is dict:
	attr['key'] = value
else:
	handler_input.attributes_manager.session_attributes({'key':value})

読み出しも同様に引っ張るだけで入れてたら入ってる。

attr = handler_input.attributes_manager.session_attributes
if type(attr) is dict and 'key' in attr:
	return attr['key']
return None

sessionはAudioPlayerのtokenのようにサーバーへのresponseと次のrequest(Intent)に乗っかり続けるって感じなので、特に何かしなくても使えます。
(responseに乗っけるのはattributes_managerが勝手にやってくれると思ってください)


persistent

で、persistentってのがDBを使うためのアダプターってのを用意しないと使えません。

ただ、実際の保存、読み出しの手順は名前が違うだけで、sessionとほぼ一緒です。


■persistentを使うための仕組み

attributes_managerを初期化する際の引数で、persistence_adapterというのが設定されているとattributes_manager.persistent_attributes関連のメソッドが使えるようになります。(setter,getter,save,delete)

「おいおい、なんだ初期化する際って?」

当然ですがそのタイミングでなにか手を出せるわけではないのであらかじめこれを使ってねというのを、さらに上位の者に設定してなくてはいけないということです。

そこでお呼びがかかるのがSkillBuilderなんですが、こいつにadapterを追加するメソッドがあったり、初期化時に引数に追加とかではなく「CustomSkillBuilderというSkillBuilderの派生クラスを代わりに使って、初期化時に渡せ」という荒技を持ち出してきます。



■アダプター

ask_sdk入れた時に一緒に入るDynamoDBとかいうDBを利用したアダプターですが、また新しいのをいろいろやるよりもう一つあるS3Adapterってのが、なんかAudioPlayerで目にしたS3に近いので、そっちでできないか考えます。

そっちはオフラインでは別途インストールしないといけないです。

pip install ask-sdk-s3-persistence-adapter

S3Adapterというのが目的となるアダプターです。

ask_sdk_s3/adapter.pyにあります。

ask_sdk_s3/adapter.py
def __init__(self, bucket_name, path_prefix=None, s3_client=None, object_keygen=user_id_keygen):

ふむ。全然わからん。
特に必須ぽいbucketがわからん。
適当な文字列入れても動かない。

と、ここでS3のURLを作ってくれてたutils.pyを見るわけです。
s3_client、bucket_nameとかあるじゃん。
path_prefix、object_keygenは特に必要なければ設定しなくていいっぽいので、utils.pyで使ってるのと同じやつをいれて関数を作っちまいましょう。

utils.py
from ask_sdk_s3.adapter import S3Adapter

def create_persistence_adapter():
    s3_client = boto3.client('s3',
                            region_name=os.environ.get('S3_PERSISTENCE_REGION'),
                            config=boto3.session.Config(signature_version='s3v4',s3={'addressing_style': 'path'}))
    bucket_name = os.environ.get('S3_PERSISTENCE_BUCKET')
    return S3Adapter(bucket_name=bucket_name, path_prefix="", s3_client=s3_client)

こんな感じでまるパクリ。
例外はデバッグ時に外で拾いたかったのでここで隠蔽しませんでしたが、まあそこらへんは好きにやってください。



■実装

アダプターに目途がついたので実際に使ってみます。

lambda_function.py
from ask_sdk_core.skill_builder import SkillBuilder
from utils import create_presigned_url

sb = SkillBuilder()

もともとはこんな感じになってるのを

こういう感じに変更です。

lambda_function.py
from ask_sdk_core.skill_builder import CustomSkillBuilder
from utils import create_presigned_url, create_persistence_adapter

sb = CustomSkillBuilder(create_persistence_adapter())

あと、S3Adapterをデプロイ先で利用するためにrequirements.txtへの追記が必要になります。

requirements.txt
boto3==1.9.216
ask-sdk-core==1.11.0

これだけだったところに、

こう追加です。

requirements.txt
boto3==1.9.216
ask-sdk-core==1.11.0
ask-sdk-s3-persistence-adapter==1.0.0

バージョンわかんねーよが普通だと思います。

pipでインストールされたフォルダ(ask_sdk_s3_persistence_adapter-1.0.0.dist-info)にあるMETADATAに

requirements.txt
Name: ask-sdk-s3-persistence-adapter
Version: 1.0.0

と書いてあるので、こうなのでしょう。まあフォルダ名もそうなんで、フォルダ見てって感じでも問題ないかと。


■実験

sessionと名前が違うだけですね。

attr = handler_input.attributes_manager.persistent_attributes
if type(attr) is dict:
	attr['key'] = value
else:
	handler_input.attributes_manager.persistent_attributes({'key':value})

読み出しも同様に引っ張るだけで入れてたら入ってる。

attr = handler_input.attributes_manager.persistent_attributes
if type(attr) is dict and 'key' in attr:
	return attr['key']
return None

で、ちょっと違うのがsave操作をしないとsessionみたいに消えちゃうことです。
DBアクセスがあるので、本来saveは最低限にとどめておきたい。

なので、スキルが閉じるときだけ保存するってのが良いです。

lambda_function.py
# 完了しました
class SessionEndedRequestHandler(AbstractRequestHandler):
    def can_handle(self, handler_input):
        return is_request_type("SessionEndedRequest")(handler_input)

    def handle(self, handler_input):
        handler_input.attributes_manager.save_persistent_attributes()
        return handler_input.response_builder.response

こいつです。
スキル作ったら出来てるSessionEndedRequestHandlerというやつ。
SessionEnded・・・Sessionじゃねぇか・・・。

まあ、さらにケチるなら通常はSessionに放り込んでおいてこのhandleでSession内の保存すべきやつを移動して保存するのが実装的には簡単でいいかもですね。

とはいえ、AudioPlayerはPause,ResumeがあってなかなかSessionが終わらないこともあるのでAudioPlayerでは設定してすぐに保存しちゃってもいいかもです。

まあ、その辺も好きにやってください。


で、sessionを終了させて、再度起動したときに読んでみると保存されてるのがわかると思います。

とりあえずうまくいった。

■まとめ

少し長いので、なんか複雑感出ましたが、大雑把にはこれだけですかね。

  • utils.pyにアダプターを作る関数を作る。
  • SkillBuilderをCustomSkillBuilderに変更して、アダプターをセット。
  • requirements.txtでデプロイ先でアダプターを使えるようにしてくれと頼む。
  • 使う。


■マジ終わり

S3ストレージを見に行くとMediaの上位フォルダに長ったらしい名前のファイルができてる。
フォルダを作って、path_prefixにそのフォルダ名を入れておけばそっちにまとまるのかな。

bucketってのはこの割り当てられたMediaの上位フォルダ以下の領域のことかな。

というあたりで、終わり。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?