【Python】ソシャゲ課金したい気持ちを抑えてくれるLINEのbotを作った

僕はソシャゲが大好きで「ソシャゲは課金してナンボ」と思っているのですが、
妻子持ちのお小遣い制のためやはり限度というものが生じております。

そんな僕がとても好きなLINEスタンプがあります。
それがこちら、

https://store.line.me/stickershop/product/1233295/ja?from=sticker

【ネガ子、課金する】

というもの。

<img width="703" alt="スクリーンショット 2017-10-21 20.50.18.png" src="https://qiita-image-store.s3.amazonaws.com/0/186346/0d7dd9a0-d03c-4159-de75-014a97570080.png">


すごく共感してしまうスタンプが盛りだくさんでここでも課金欲が抑えきれず~~嫁に内緒で~~スタンプを購入してしまいました。

なのですが、
「使う相手がいない」
ということに気づきました。

なので、

* スタンプを送ったら何か反応してくれるものを作ろう
* どうせならこの課金欲を抑止してくれるような反応があればいいかも

と思いつきました。


幸いなことに、

https://store.line.me/stickershop/product/1397671/ja

【ネガ美は課金しない】

<img width="722" alt="スクリーンショット 2017-10-21 20.50.55.png" src="https://qiita-image-store.s3.amazonaws.com/0/186346/271ea1b6-26ef-ffa3-c78f-6ff784847439.png">


こういう対になるスタンプもあるので、
【ネガ子のスタンプを送ったらネガ美のスタンプを返す】

という機能をもったLINEボットを作ろうと決めたのです。

前置きが長くなってしまいましたが何卒よろしくお願いします。

## まず初めに完成形はこちら

<img width="394" alt="スクリーンショット 2017-10-21 20.25.12.png" src="https://qiita-image-store.s3.amazonaws.com/0/186346/da7b287a-e71b-8dc8-4069-8fc50c04c37b.png">

うん、全然面白くないですねｗ
返してきているスタンプがよくないですがネガ美はもっと、
「課金するくらいなら焼肉食べる！」
といった「課金はダメだよ！」という啓発をしっかりしてくれるスタンプがあります。

## 使ったもの

### フレームワークはFlaskを採用

「PythonでのWebアプリならFlaskがええで」
という世の中の玄人エンジニアさんたちの意見を信じてFlaskを使うことにしました。

色々調べて触ってみて結果、こういったbotを作る場合、

* Flask
* bottle

があたりがいいと思われます。

### デプロイ先はheroku

どこでも良かったのですがどこでも良かったのでherokuにしました。



## 出来上がったコードはこちら

```python
from flask import Flask
from flask import request

import os
import requests
import json
import re
import urllib
import random

from linebot import LineBotApi
from linebot.models import TextSendMessage, ImageSendMessage
from linebot.exceptions import LineBotApiError

API_BASE_URL = os.environ["API_BASE_URL"]
AUTH_BASE_URL = os.environ["AUTH_BASE_URL"]
line_token = os.environ["LINE_CHANNEL_ACCESS_TOKEN"]

app = Flask(__name__)
line_bot_api = LineBotApi(line_token)

static_path = "https://wasshoiwasshoi.herokuapp.com/static"

@app.route("/webhook", methods=['POST'])
def webhook():
  """
  Lineからのリクエストをまずこの関数で受け取って送られてきたメッセージに応じて処理を分ける
  """
  req = json.loads(request.get_data(as_text=True))
  for event in req["events"]:
    if event["message"]["type"] == "sticker":
      responseForStamp(event)

def responseForStamp(event):

  here = os.path.join( os.path.dirname(os.path.abspath(__file__)))
  image_list = []
  image_url = ""
  # ネガ子ちゃんStamp(1233295)に対するレスポンス
  if event["message"]["packageId"] == "1233295":
    image_list = os.listdir(here + "/static/negami/")
    # 「ポチッとな」
    if event["message"]["stickerId"] == "9468023":
      image_list = ["1.png", "3.png", "4.png", "6.png", "7.png"]

  image_url = static_path + "/negami/" + random.choice(image_list)
  print (image_url)
  try:
    line_bot_api.reply_message(event["replyToken"], ImageSendMessage(original_content_url=image_url, preview_image_url=image_url))
  except LineBotApiError as e:
    print (e)

if __name__ == "__main__":
    port = int(os.environ.get('PORT', 4000))
    app.run(port=port, debug=True)

```

※色々なものがimportされていますが、もしかしたら上記コードでは一切使っていないものも含まれています。
　もちろん「wasshoiwasshoi.herokuapp.com」というアプリケーションは存在しません。

なお、botからのメッセージ送信はLine謹製のSDKを使っています。

Line Messaging api からのリクエストを受け取って、
そのリクエストの種類がスタンプ、（ `event["message"]["type"] が "sticker" `）だった場合、
スタンプを返すようの処理（responseForStamp）に入ります。

lineのスタンプはスタンプの種類毎に、
`packageId `　が振られていて、そのIDの中に更に、
`stickerId`　があります。

というわけで、ネガ子のスタンプ（packageId＝1233295）だった場合、
返事するスタンプの対象をとりあえずネガ美のスタンプ全てとして、
更に、ネガ子スタンプの特定のスタンプ（ポチッとな」だったら、特定のネガ美スタンプを返事に対象としてリストで返します。

そしてお返事する際に、返されたリストの中からランダムで一つ選んで返しています。


## ・・・あ、気づいちゃいました？？

はじめに、
【ネガ子のスタンプを送ったらネガ美のスタンプを返す】
という機能を作るとかドヤ顔で言ってますが、

`line_bot_api.reply_message(event["replyToken"], ImageSendMessage(original_content_url=image_url, preview_image_url=image_url))
`

完全に画像です本当にありがとうございます。

これは別にbot用にスタンプを購入するのが惜しかったわけではなく単純に **botアカウントは有料スタンプを使えない** のです。

参考までにbotが送れるスタンプの一覧はこちら、
https://developers.line.me/media/messaging-api/sticker_list.pdf


というわけで完全に詰んだわけですがここで諦める僕ではありません。

「・・・画像でいいか」

というわけで **ネガ美のスタンプの画像を一つ一つ切り取って** それを画像データとして保存して・・・

なんてことをするのはとても面倒なのでアルバイトを雇ってその人にネガ美の画像を一つ一つ取ってきてもらいました。

その方無しではこのボットは完成しなかったので紹介させていただきます。
そんな面倒な作業をやってくれたアルバイターは以下の方。


```python
import sys,os
from time import sleep
import re
import urllib.request
from urllib.parse import urlencode
from bs4 import BeautifulSoup


def get_image_url_list(url):
  req = urllib.request.Request(url)
  with urllib.request.urlopen(req) as res:
    html = res.read().decode("utf-8")
    res.close()

  soup = BeautifulSoup(html, "html.parser")
  image_url_div = soup.find_all(class_="mdCMN09Image")

  url_list = []
  for a in image_url_div:
    url = re.findall("(https://[A-Za-z0-9\.\-\/]*)", str(a))
    url_list.append(url[0])

  return url_list


def download_image(url_list):
  for i, image_url in enumerate(url_list):
    # 拡張子を取得
    ext = os.path.splitext(image_url)[1]

    file_name = str(i) + ext
    urllib.request.urlretrieve(image_url, file_name)
    print ("保存完了")
    sleep(3)


url_list = get_image_url_list("https://store.line.me/stickershop/product/1397671/ja")
download_image(url_list)
```


突貫で２秒くらいで作った感が否めない感じですがよくやってくれました。
`url_list = get_image_url_list("https://store.line.me/stickershop/product/1397671/ja")`
にべた書きされているurlを適宜変更すれば、そのスタンプを片っ端から持ってきてくれます。幸せいっぱいですね。

なので仕組みとしては事前に上記のアルバイトさんが拾ってきてくれた画像をbotが返してくれるという形です。

## 作ってみた感想

Python用のLINE謹製SDKがあるにはあるんですけど、使い方があまりネットに載ってなかったので（僕が見つけられなかっただけかもですが）、
APIのレファレンスや、githubに公開されているSDKのソースをみて、
「ほほう、じゃあこうすれば画像は送れるだろう。」
という感じで進められました。

またネガ子ちゃんのスタンプIDだとかはwebhookで受け取ったeventをプリントなりなんなりして、見つけたりすることができました。
本当はqiitaの記事っぽく、「こういった構造でリクエストがきます。」ってのを書いたほうが幸せなんでしょうけどそのうち書きます。

というわけで最終的に作ったのは、
【ユーザが送ったスタンプを受け取ってそれに応じて画像を返してくれるbot】
でした。