【Golang】Goで決まった時間に論語を配信(Push)するLINE Botを作った


渋沢栄一の名著「論語と算盤」を読みました。

確かに、現代日本では、語学や科学技術に対する教育が重要視されている一方で、道徳教育が軽視されている傾向にあるようにも思えます。そこで、さっそく岩波文庫の「論語」を購入して読み進めるも、途中で挫折してしまいまた。

そもそも、論語は孔子の言葉を集めた短文集。毎日少しずつ読み進めながら、意味を噛み締め、日常生活の中で解釈を進めた方が自分の中に長く残るのかなと感じました。

そこで、Goの勉強を兼ね、毎日Lineを通して論語を配信してくれるBotを開発しました。要所を超大まかにまとめておきます。細かい点はリポジトリを確認してください。

使用技術

デモ

毎朝8:00に論語を配信するBotを公開しています。興味のある方は、以下からフレンド追加可能です。

qr

レポジトリも公開しています。READMEにしたがってビルドしてください。

構成

line-bot-sdk-goを用いてLine Messaging APIを使用しています。 Herokuで登録したスケージューラーが、指定した時間にタスクを実行してPush配信するという構成を取っています。

スクレイピング

そもそも元情報が無くては、配信することも叶いません。 Pythonでスクレイピングをして取得します。

各々の原文、書き下し、訳文を取得。

# coding: UTF-8
import requests
import pymysql
import re
from bs4 import BeautifulSoup

lists = [
    16,
    24,
    26,
    26,
    27,
    28,
    37,
    21,
    30,
    18,
    25,
    24,
    30,
    47,
    41,
    14,
    26,
    11,
    25,
    3
]

page = 1
id_num = 0
    
for i in lists:
    for j in range(i):
        chapter = j+1
        id_num += 1
        label_id = page
        
        r = requests.get("https://kanbun.info/keibu/rongo" + str(page).zfill(2)+ str(j+1).zfill(2) + ".html")
        r.encoding = r.apparent_encoding
        data = BeautifulSoup(r.text, 'html.parser') 

        gb = data.find("div", class_="genbun01").getText()
        kk = data.find("div", class_="yomi03").getText()
        yk = data.find("div", class_="chu03").find("li").find_next_sibling("li")

        gb_result = re.sub('[0-9]+|\n|-|(|)', "", str(gb))
        kk_result = re.sub('<[a-z]+>|</[a-z]+|\n|>', "", str(kk))
        yk_result = re.sub('<[a-z]+>|</[a-z]+>|\n|<br/>|(下村湖人『現代訳論語』)', "", str(yk))

    page += 1

各篇のタイトルを取得

q = requests.get("https://kanbun.info/keibu/rongo00.html")
q.encoding = q.apparent_encoding
data = BeautifulSoup(q.text, 'html.parser') 
titles = data.find("table", class_="table01").find_all("a")

for i in range(20):
    title = re.sub('(.*?)', "", str(titles[i].getText()))

User登録

Push配信を行うためには、UserIDが必要です。 そこで、フレンド登録・解除時に、UserIDをデータベースに登録/削除する処理を記述してあげます。フレンド登録・解除イベントが行われた際には、LineMessaging APIからPostメソッドで/callbackにリクエストが飛んできます。これをevent.Typeを用いて処理を振り分けてあげます。ちなみにWebフレームワークGinを採用しています。

r := gin.Default()
	r.POST("/callback", func(c *gin.Context) {
		events, err := bot.ParseRequest(c.Request)
		if err != nil {
			if err == linebot.ErrInvalidSignature {
				log.Fatal(err)
			}
			return
		}

		for _, event := range events {
			switch event.Type {
			// フレンド登録
			case linebot.EventTypeFollow:
                // ユーザIDの取得
				userID := event.Source.UserID
                //DBにユーザ情報を登録する処理
				user := models.User{User_id: userID, Flag: false}
				db.Where(user).FirstOrCreate(&user)
				db.Model(user).Where("user_id = ?", userID).Update("flag", true)
			// フレンド解除
			case linebot.EventTypeUnfollow:
				userID := event.Source.UserID
                //DBに保存しているユーザのflagをfalseにアップデート
				db.Model(&models.User{}).Where("user_id = ?", userID).Update("flag", false)
			}
		}

	})

Push配信

Getメソッドで/broadcastにリクエストが飛んできた際に、PushMessage()を用いて、配信を行います。PushMessage()メソッドは、一つ目の引数に配信先のUser IDを、二つ目以降に配信するメッセージオブジェクトをとります。Textメッセージは、linebot.NewTextMessage(“配信するテキストメッセージ”)で作成可能です。

// send push message
	r.GET("/broadcast", func(c *gin.Context) {

		// fetch follower User
		users := []models.User{}
		db.Where("flag = ?", true).Find(&users)

		// fetch Content
		nextContent := models.NextContent(db)

		// fetch Label of Content
		label := models.Label{}
		db.Model(nextContent).Related(&label)

		// make first message
		t := time.Now()
		text := t.Format("2006年01月02日火曜日") + "。" + label.Title + "(" + strconv.Itoa(nextContent.Chapter) + ")"

		// send push messages
		for _, u := range users {
			_, err := bot.PushMessage(u.User_id, linebot.NewTextMessage(text), linebot.NewTextMessage("[原文]\n"+nextContent.Genbun), linebot.NewTextMessage("[書き下し]\n"+nextContent.Kudashi), linebot.NewTextMessage("[訳]\n"+nextContent.Yaku)).Do()
			if err != nil {
				log.Fatal(err)
			}
		}

		// record broadcast
		broadcast := models.Broadcast{Content_id: nextContent.Id, Created_at: time.Now()}
		db.Create(&broadcast)

	})

Scheduler

Getメソッドで/boradcastにリクエストを送るのを自動化することで、配信をBot化できます。

heroku addons:create scheduler:standard

上記のコマンドで、アドオンschedulerをHerokuに追加します。

続いて、以下のような/broadcastにGETメソッドをリクエストするBashスクリプトを作成します。

#! /bin/sh

curl "${APP_URL}/broadcast"

最後に、schedulerのコンソール画面から、実行コマンドsh /app/scheduler.shと実行時間を登録すれば完成です。

まとめ

Line Botは結構簡単に作成できることがわかりました。フロントを書かなくていいので、新しい言語習得を効率よく行うことも可能です。次回はGoを使ってCLIアプリの作成をしたいと思います。

comments powered by Disqus