Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
131
Help us understand the problem. What is going on with this article?
@Sinhalite

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

More than 1 year has passed since last update.

はじめに

私が物心ついたころ、日本で一番強いチームは横浜ベイスターズでした。
野手では谷繁、ローズ、駒田、鈴木尚、投手では三浦、斎藤隆、佐々木など…懐かしいですね。
やっぱり子供というものは、強いチームを好きになるもので、その時私はベイスターズのファンになりました。
ご存知の通り、ベイスターズはその後衰退の一途をたどるわけですが…

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

そんな中、ある日ふと思ったんです。
「ベイスターズが勝ってる時だけ教えてくれれば、気分が落ち込まずに済むな…。」

スキル

ベイスターズ速報(非公式)
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

131
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
131
Help us understand the problem. What is going on with this article?