ベイスターズが勝ってる時のみ応答して、負けてる時はしらばっくれるAlexaスキルをつくった


はじめに

私が物心ついたころ、日本で一番強いチームは横浜ベイスターズでした。

野手では谷繁、ローズ、駒田、鈴木尚、投手では三浦、斎藤隆、佐々木など…懐かしいですね。

やっぱり子供というものは、強いチームを好きになるもので、その時私はベイスターズのファンになりました。

ご存知の通り、ベイスターズはその後衰退の一途をたどるわけですが…

最近では仕事も忙しく、ベイスターズの試合を見ることは少なくなりましたが、いまだに試合結果だけは気になってちょくちょく見てしまいます。贔屓のチームが勝つと嬉しいですもんね。

ただ逆に、結果を見てベイスターズが負けていると萎えちゃいます。仕方ないですが。

そんな中、ある日ふと思ったんです。

「ベイスターズが勝ってる時だけ教えてくれれば、気分が落ち込まずに済むな…。」


スキル

ベイスターズ速報(非公式)

bays.png


使い方

■勝ってる時

「ベイス速報を開いて」



アレクサ「現在7回表です。ベイスターズが5対2で勝っています!」

■負けてる時

「ベイス速報を開いて」



アレクサ「横浜Fマリノスの結果をお調べですか?」

これなら、もしピッチャーが大炎上していても、結果を知ることなく気持ち良く一日を過ごすことができますね:relaxed:


構成

Amazon Echo Dot → Alexa → AWS Lambda(Python) → Sportsnavi(プロ野球)

Alexaスキルは、AWS Lambda上でNode.jsを使って開発するのがスタンダードかと思いますが、今回はスクレイピングにBeautifulSoupを使いたかったため、Python3で実装しました。

速報情報はスポナビさんのデータを利用させていただきました。


ソースコード

Alexaコンソールの設定は、公式のチュートリアルなどをご参考にしていただければと思います。

今回はPythonスクリプトのみ掲載します。テンプレートとして「alexa-skills-kit-color-expert-python」を利用し、こちらのコードをベースに実装しました。

この記事を書いている時に気づいたのですが、Alexa Skills Kit SDK for Pythonのベータ版が6月にリリースされていたようで、こちらを利用すればもっとコード量を削減できるかと思います。


lambda_function.py

import urllib.request

from bs4 import BeautifulSoup
import random

# --------------- Helpers that build all of the responses ----------------------

def build_speechlet_response(output):
return {
'outputSpeech': {
'type': 'PlainText',
'text': output
},
'card': {
'type': 'Simple',
'title': 'ベイスターズ速報',
'content': output
},
'reprompt': {
'outputSpeech': {
'type': 'PlainText',
'text': None
}
},
'shouldEndSession': True
}

def build_response(speechlet_response):
return {
'version': '1.0',
'sessionAttributes': {},
'response': speechlet_response
}

# --------------- Functions that control the skill's behavior ------------------

def get_baystars_result():
lose_text_list = ["今日はいい天気ですね","横浜Fマリノスの結果をお調べですか?","他人を応援するより自分が頑張ったほうが楽しいですよ","大魔神は今、馬ぬしをやっているそうです"]

html = urllib.request.urlopen('https://baseball.yahoo.co.jp/npb/schedule/').read()
soup = BeautifulSoup(html, 'html.parser')

#ベイスターズが含まれる対戦テーブルを取得
#無い場合は試合無しと判断
if soup.find(class_="yjMS",text="DeNA") == None:
speech_output = "今日は試合が無いようです"
return build_response(build_speechlet_response(speech_output))
else:
table = soup.find(class_="yjMS",text="DeNA").parent.parent

#表の上のチーム:a,下のチーム:b
team_a = table.select('.yjMS')[0].a.string
team_b = table.select('.yjMS')[1].a.string

team_a_score = table.select('.score_r')[0].string
team_b_score = table.select('.score_r')[1].string

game_status = table.select('.yjMSt')[0].string
if game_status == "結果":
lead_text = "試合終了!"
elif game_status == "中止":
lead_text = "試合は" + game_status + "になりました"
else:
lead_text = "現在" + game_status + "です"

if game_status == "試合前" or game_status == "中止":
speech_output = lead_text
elif team_a == "DeNA" and int(team_a_score) > int(team_b_score):
speech_output = lead_text + "ベイスターズは" + team_a_score + "対" + team_b_score + "で勝っています!"
elif team_b == "DeNA" and int(team_b_score) > int(team_a_score):
speech_output = lead_text + "ベイスターズは" + team_b_score + "対" + team_a_score + "で勝っています!"
else:
speech_output = random.choice(lose_text_list)

return build_response(build_speechlet_response(speech_output))

def handle_session_end_request():
speech_output = "Bye"
return build_response(build_speechlet_response(speech_output))

# --------------- Events ------------------

def on_launch(launch_request):
return get_baystars_result()

def on_intent(intent_request):
intent_name = intent_request['intent']['name']

if intent_name == "ResultIntent":
return get_baystars_result()
elif intent_name == "AMAZON.HelpIntent":
return get_baystars_result()
elif intent_name == "AMAZON.CancelIntent" or intent_name == "AMAZON.StopIntent":
return handle_session_end_request()
else:
raise ValueError("Invalid intent")

# --------------- Main handler ------------------

def lambda_handler(event, context):
if event['request']['type'] == "LaunchRequest":
return on_launch(event['request'])
elif event['request']['type'] == "IntentRequest":
return on_intent(event['request'])
elif event['request']['type'] == "SessionEndedRequest":
return on_session_ended(event['request'])



審査について

当初「ベイスターズ速報」で審査提出したのですが、チームから承認を得ていないと審査を通さないよと言われたため、「(非公式)」を付け足しました。スキル詳細についても同様で、非公式である旨を書き足しました。

また呼び出し名については、「ベイスターズ」のワードは使用しないように指摘があったため、「ベイス速報」としました。

1.非公式である旨を明記する

2.呼び出し名に固有名詞を使わない

の2点を守れば、審査は突破できそうです:thumbsup:


おわりに

記事を見て少しでも面白い、参考になると思いましたら、いいねボタンを押していただけると幸いです。

また実装について、より良い方法などありましたら、コメントいただけると助かります:grinning:

その他いろいろと記事を書いてますので、興味のある方はぜひ!

AIが選んだ本当に似ている有名人トップ10