post Image
NHK番組表API を使ってアニメの音楽番組をslackに通知する

はじめに

  • ゆるふわです

概要

  • NHK番組表API を使ってアニメの音楽番組をslackに通知する

開発に至ったどうでもいい話

  • 2015年11月~12月に放送されたアニメロサマーライブ2015のテレビ放送に気づいたのが最終週だった
  • 前の週にまれいたそがgive me revolution歌ってたって知って絶望した
  • アニメロサマーライブ2014は2015年夏に再放送してた
  • API MeetupでNHKが番組表API作ったって聞いてたので最終週の放送が終わるまでに通知botを作ろうとおもった

動作環境(開発当時->現在)

  • raspberry pi(raspbian jessy) -> ec2.nano(ubuntu 15.10)
  • golang(1.4->1.6)
  • redis(???->2.8.4)
  • slack incoming webhook

開発

下調べ

仕様

  • リクエストパラメータ

    • service: tv(全チャンネル)
      • どのチャンネルで再放送するかわからなかったので全チャンネル取得して以下のチャンネルのみ抽出
        – G1(NHK総合1)
      • E1(NHKEテレ1)
      • S1(NHKBS1)
      • S3(NHKBSプレミアム)
    • area: 130(東京)
    • genre: 04[1~10](全ジャンル)
      • 04[0-9]? という指定ができなかったので10回叩く(ハードコーディング)
    • date: 翌日
  • responseのIDがKVS(redis)上に存在していれば無視, 存在していれば登録
    • lifetimeは24h
  • responseのTitle or Subtitleに “アニメ” がなければ無視
  • 番組情報は午前5時ごろデータが更新されるので05:30~6時間周期でにbatchを実行(cron)

実装

package main

import (
    "encoding/json"
    "fmt"
    "github.com/garyburd/redigo/redis"
    "net/http"
    "net/url"
    "strings"
    "sync"
    "time"
)

type Slack struct {
    Channel   string `json:"channel"`
    IconEmoji string `json:"icon_emoji"`
    IconUrl   string `json:"icon_url"`
    Text      string `json:"text"`
    Username  string `json:"username"`
}

type NihonHikikomoriKyokai struct {
    List List `json:"list"`
}

type List struct {
    G1 []Program `json:"g1"`
    G2 []Program `json:"g2"`
    E1 []Program `json:"e1"`
    E2 []Program `json:"e2"`
    E3 []Program `json:"e3"`
    E4 []Program `json:"e4"`
    S1 []Program `json:"s1"`
    S2 []Program `json:"s2"`
    S3 []Program `json:"s3"`
    S4 []Program `json:"s4"`
    R1 []Program `json:"r1"`
    R2 []Program `json:"r2"`
    R3 []Program `json:"r3"`
    N1 []Program `json:"n1"`
    N2 []Program `json:"n2"`
    N3 []Program `json:"n3"`
}

type Program struct {
    Area struct {
        ID   string `json:"id"`
        Name string `json:"name"`
    } `json:"area"`
    EndTime string   `json:"end_time"`
    EventID string   `json:"event_id"`
    Genres  []string `json:"genres"`
    ID      string   `json:"id"`
    Service struct {
        ID    string `json:"id"`
        LogoL struct {
            Height string `json:"height"`
            URL    string `json:"url"`
            Width  string `json:"width"`
        } `json:"logo_l"`
        LogoM struct {
            Height string `json:"height"`
            URL    string `json:"url"`
            Width  string `json:"width"`
        } `json:"logo_m"`
        LogoS struct {
            Height string `json:"height"`
            URL    string `json:"url"`
            Width  string `json:"width"`
        } `json:"logo_s"`
        Name string `json:"name"`
    } `json:"service"`
    StartTime string `json:"start_time"`
    Subtitle  string `json:"subtitle"`
    Title     string `json:"title"`
}

const (
    ApiKey = "{YOUR_NHKWEBAPI_TOKEN}"
)

func main() {
    genreCodes := []string{
        "0400",
        "0401",
        "0402",
        "0403",
        "0404",
        "0405",
        "0406",
        "0407",
        "0408",
        "0409",
        "0410",
    }
    for i := 0; i < len(genreCodes); i++ {
        endpoint := fmt.Sprintf("http://api.nhk.or.jp/v1/pg/genre/130/tv/%s/%s.json?key=%s", genreCodes[i], time.Now().AddDate(0, 0, 1).Format("2006-01-02"), ApiKey)
        resp, _ := http.Get(endpoint)
        defer resp.Body.Close()
        var nhk NihonHikikomoriKyokai
        decoder := json.NewDecoder(resp.Body)
        decoder.Decode(&nhk)
        c, _ := redis.Dial("tcp", ":6379")
        defer c.Close()
        giveMeRevolution(nhk.List.G1, c)
        giveMeRevolution(nhk.List.E1, c)
        giveMeRevolution(nhk.List.S1, c)
        giveMeRevolution(nhk.List.S3, c)
    }
}

func giveMeRevolution(programs []Program, c redis.Conn) {
    var wg sync.WaitGroup
    for _, program := range programs {
        if false == isChecked("nhk_"+program.ID, c) {
            keyword := "アニメ"
            if strings.Contains(program.Title, keyword) || strings.Contains(program.Subtitle, keyword) {
                var s Slack
                format := "2006-01-02T15:04:05+09:00"
                startTime, _ := time.Parse(format, program.StartTime)
                endTime, _ := time.Parse(format, program.EndTime)
                s.Text = fmt.Sprintf(
                    "(\\( ⁰⊖⁰)/) タッタラタッタ、タッタ!\n```%s```\n```%s```\n```%s ~ %s```",
                    program.Title,
                    program.Subtitle,
                    startTime.Format("2006-01-02(Mon) 15:04:05"),
                    endTime.Format("2006-01-02(Mon) 15:04:05"),
                )
                j, _ := json.Marshal(s)
                wg.Add(1)
                go func(j []byte) {
                    defer wg.Done()
                    http.PostForm(
                        "https://hooks.slack.com/services/{YOUR_WEBHOOK_URL}",
                        url.Values{
                            "payload": {
                                string(j),
                            },
                        },
                    )
                    fmt.Printf("POSTED!! ID:%s Title:%s\n", program.ID, program.Title)
                }(j)
            } else {
                fmt.Printf("new program was found. but not contains keyword. ID:%s Title:%s\n", program.ID, program.Title)
            }
        } else {
            fmt.Printf("already checked. ID:%s Title:%s\n", program.ID, program.Title)
        }
    }
    wg.Wait()
}

func isChecked(key string, c redis.Conn) bool {
    //return false
    checked, err := redis.Bool(c.Do("GET", key))
    if err != nil || checked == false {
        c.Do("EXPIRE", key, 60*60*24)
        c.Do("SET", key, true)
        return false
    } else {
        return true
    }
}

結果

  • image

    • gotcha!!

所感

  • WebAPIの仕様書どこ
  • ジャンルまとめて取りたい
  • jsonのkey側が可変(G1, E1とか)なときのstructの定義がわからなくてハードコーディング放置
  • 翌日までしか取得できない(slack経由でpush通知してもらわないと録画が間に合わない)
  • v2に上がった際”1日”が0:00~23:59:59じゃなくなったっぽい
    • アニメロサマーライブは01:15~なのだけど突然通知がこなくなった
    • 当日じゃないと01:15のデータは取れないかもしれなくて当日+翌日にするか考え中(rate limit確認)
  • 動作環境をrasbianから ubuntuに途中で変えたけど一切ストレスなくてgolangマジ神
  • まれいたそ〜
    • give me revolutionの前振りでコールアンドレスポンスしてるまれいたそ〜
    • そばかす歌ってるまれいたそ〜
  • すみぺ〜

(\( ⁰⊖⁰)/) enjoy!!!


『 Go 』Article List
Category List

Eye Catch Image
Read More

Androidに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

AWSに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Bitcoinに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

CentOSに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

dockerに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

GitHubに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Goに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Javaに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

JavaScriptに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Laravelに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Pythonに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Rubyに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Scalaに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Swiftに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Unityに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Vue.jsに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Wordpressに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

機械学習に関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。