twitterAPIを使って呟き数で育つ育成ゲームを作ってみる。

<h1>はじめに</h1>
公開するかどうかは分かりませんが、TwitterAPIで遊んでみたかったので呟き数に応じた育成ゲームというのを作ってみました。
Django+Python3で作っています。
SSなどは全て開発中の画面ですので機能を確認する程度しか考えてません。

<h1>ログイン後画面</h1>
こんな感じです。

<img width="593" alt="スクリーンショット 2018-07-11 2.12.35.png" src="https://qiita-image-store.s3.amazonaws.com/0/209682/fdea70de-6b6d-c974-fa77-64f7263e0a07.png">

750*500のところには現在の様子の写真かイラストを当てはめる予定です。



<h1>ログイン前画面</h1>

<img width="1016" alt="スクリーンショット 2018-07-11 1.45.24.png" src="https://qiita-image-store.s3.amazonaws.com/0/209682/686d4851-0a23-48a4-1115-16b8440d587f.png">


ログイン前の画面はこんな感じ。
機能を試すだけで作っているのでUIに関しては適当です。
背景つけたらそれっぽくはなるのかな、誰かセンスある人教えて下さい。




<h1>おおまかな流れ</h1>
<img width="674" alt="スクリーンショット 2018-07-11 17.43.30.png" src="https://qiita-image-store.s3.amazonaws.com/0/209682/6f663b49-1362-76f3-df5e-fedb91f26e6e.png">
次から詳しく説明していきたいと思います。

主なものだけ書き出しています。
djangoの元々あったものから追加したものはuser.pyです。


```
project_name/
　├ project_name/
　├ app_name/
　│　├ models.py
　│　├ views.py
　│　├ user.py
　│　└ urls.py
　├ db.sqlite3
　└ manage.py
```


<h1>ログイン機能</h1>

Djangoとpythonには便利なライブラリがたくさんありますので、それを使わない手はないです。

<a href="https://qiita.com/Kyou13/items/713a878065df82a0ecc3">こちら</a>の記事を参考にさせて頂きました。簡潔でわかりやすかったです。

上の記事を参考にしつつ、その他の部分を作っていきます。

```python
from django.contrib.auth.decorators import login_required
from twitter.models import Userinfo
from twitter.user import UserData


'''
ログイン後、top_pageが実行される

'''
@login_required
def top_page(request):
    user = UserSocialAuth.objects.get(user_id=request.user.id)

    u = UserData(user)
    u.registDatabase() #ログインユーザのデータベース登録&更新


```

ログイン後にtop_pageを実行すると、引数のrequestからuser_idの情報を取得します。

<h1>データベース作成</h1>
```python:models.py

from django.db import models

class Userinfo(models.Model):

    oauth_token = models.CharField(primary_key=True,max_length=100)
    followers_count = models.IntegerField()
    friends_count = models.IntegerField()
    favourites_count = models.IntegerField()
    statuses_count = models.IntegerField()
    total_point = models.IntegerField()
    screen_name = models.CharField(max_length=100,null=True)

```

こんな感じにフィールドを作成しています。無駄な部分もありますが将来的にはこのフィールドも使った育成ゲームをやりたいなと思って追加しています。
試してはいないのですがtwitter上の名前だと変更されてしまうと困るような気がしますので、primary_keyにはoauth_tokenを使用しています。

models.pyに書いた後はmakemigrationsしてあげましょう。

<h1>OA認証からデータベースに登録まで</h1>

ちょっと長いですが先にuser.pyを載せておきます。
user.pyで大まかな部分を実装しています。

```python:user.py
from requests_oauthlib import OAuth1Session
import json
from twitter.models import Userinfo


class UserData():

    def __init__(self,user):
        self.user = user
        self.oa = self.oauth()

    """
    OA認証を行う
    """
    def oauth(self):
        AT = self.user.access_token['oauth_token']
        ATS = self.user.access_token['oauth_token_secret']
        CK = ''
        CS = ''
        oa = OAuth1Session(CK, CS, AT, ATS)

        return oa



    """
    タイムラインの取得を行う
    """
    def getTimeline(self, num=1):
        url = "https://api.twitter.com/1.1/statuses/user_timeline.json"  # タイムライン取得エンドポイント
        params = {'count': num}  # 取得数
        res = self.oa.get(url, params=params)

        if res.status_code == 200:  # 正常通信出来た場合
            timelines = json.loads(res.text)

            return timelines


        else:  # 正常通信出来なかった場合
            print("Failed: %d" % res.status_code)
            return -1


    """
    タイムラインから自分のプロフィールを取得する。
    """
    def getStatus(self,timelines):

        followers_count = 0
        friends_count = 0
        favourites_count = 0
        statuses_count = 0

        for line in timelines:
            followers_count = int(line['user']['followers_count'])
            friends_count = int(line['user']['friends_count'])
            favourites_count = int(line['user']['favourites_count'])
            statuses_count = int(line['user']['statuses_count'])

        return followers_count, friends_count, favourites_count, statuses_count


    """
    タイムラインからツイート数の計算を行う。
    """
    def countTxtLength(self, timeline):

        length = 0


        for line in timeline:
            length = length + len(line['text'])

        return length


    """
    前回登録時との差分を計算する
    """
    def calcStatuses(self, status):

        try:
            g = Userinfo.objects.get(oauth_token=self.user.access_token['oauth_token'])

            # 前回のデータベースにあるユーザ情報と最新のユーザ情報の引き算を行う
            a = status[0] - g.followers_count
            b = status[1] - g.friends_count
            c = status[2] - g.favourites_count
            d = status[3] - g.statuses_count

            print(a,b,c,d)
            return a, b, c, d

        except:
            print("A new host")
            return -1


    """
    現在のステータスを計算する
    """
    def getUserStatus(self):

        timelines = self.getTimeline()
        followers_count, friends_count, favourites_count, statuses_count = self.getStatus(timelines)

        return followers_count, friends_count, favourites_count, statuses_count


    """
    データベースに登録&更新
    """
    def registDatabase(self):

        length = 0
        now = self.getUserStatus()
        point = self.calcStatuses(now) #前回登録分との差分を計算

        if (point != -1):  # 前回から変動があれば
            if (point[3] > 0):
                timeline = self.getTimeline(point[3])#変動分のタイムラインを習得
                length = self.countTxtLength(timeline)  # 変動分のタイムライン上のテキストの数を計算


        try:
            g = Userinfo.objects.get(oauth_token=self.user.access_token['oauth_token'])
            g.followers_count = now[0]
            g.friends_count = now[1]
            g.favourites_count = now[2]
            g.statuses_count = now[3]
            g.total_point = g.total_point + length
            g.save()

        except:
            Userinfo.objects.create(oauth_token=self.user.access_token['oauth_token'],
                                    followers_count=now[0],
                                    friends_count=now[1],
                                    favourites_count=now[2],
                                    statuses_count=now[3],
                                    total_point=0,
                                    screen_name=self.user.access_token['screen_name']
                                    )


```

<h2> __init__(self,user)</h2>
インスタンスを作成した際に呼ばれるコンストラクタです。
引数のuserにはUserSocialAuth.objectsが入っています。
このクラスの属性にはuserとoaというものを追加しています。
oaにはOA認証に必要なものが入っています。


<h2>oauth(self) </h2>
OA認証に必要なための鍵？を作成します。
CK, CSには各自自分のConsumerKeyとConsumerSecetを入れておいてください。

<h2>getTimeline(self, num=1)</h2>
実際にツイッターにOA認証を行い、タイムラインを取得する関数です。
引数numは取得するタイムラインの数です。初期値で1としているのは、対象ユーザのプロフィール情報を得るときに使うためです。
```res = self.oa.get(url, params=params)
```の部分で実際にツイッターに接続してレスポンスを受け取っています。

returnはJSON形式になります。

<h2>getStatus(self,timelines)</h2>
関数getTimeline(self, num=1)で得たタイムラインから自分のプロフィール情報（今回はフォロー数、フォロワー数、ファボ数、ツイート数）を取得します。
```for line in timelines:
```のところは正直for文使う必要はありませんがイテレータから返り値を得るとなんとなく楽な気がしたのでこのようにしています。

<h2>countTxtLength(self, timeline)</h2>
getTimeline（）で取得したJSONの中にある自分がツイートした文字数を計算します。複数のタイムラインでも問題なく文字数を計算してくれます（きっと）

<h2>calcStatuses(self, status)</h2>
データベースを参照し、データベースに登録されているユーザの情報と最新のユーザ情報の差を計算します（ex.ツイート数の差）。このツイート数の差などを用いてgetTimeline(self, num=1)の引数であるnumをツイート数の差とすることにより、前回の更新時からのツイート文字数を取得することが可能となります。もし、データベースに登録されていない（つまり新しいユーザ）の場合には-1をreturnします。


<h2>registDatabase(self)</h2>

ここが一番大事な関数で、今まで作った関数をまとめている感じです。

```
now = self.getUserStatus()
```
で今のユーザのステータスを取得しnowに代入し

```
point = self.calcStatuses(now)
```
で変数pointに最新のユーザ情報-前回の登録情報を代入します。
pointが-1のときには新規ユーザであるし、pointが-1でなくてもpoint[3]=0の時には文字数の変化が無いってことを利用します。
あとはtry-exceptでデータベースに接続して情報を更新or登録します。


<h1>成長状況の表示</h1>
データベースに経験値のフィールドを作ってますので（total_point）、これを参照して成長状況を決定します。

```python:viwes.py

g = Userinfo.objects.get(oauth_token=user.access_token['oauth_token'])
now_st = calcstatus(g.total_point)  # 現在の成長状況

#合計経験値から現在の様子を計算する
def calcstatus(point):
    if(0 <= point <= 100):
        return '微粒子レベルの細胞'
    if (101 <= point <= 200):
        return '細胞ぐらい'
    if (201 <= point <= 300):
        return '卵'
    if (301 <= point <= 400):
        return '雛'
    if (401 <= point <= 500):
        return 'あひる'
    if (501 <= point <= 1500):
        return '犬'

```

こんな感じに実装しています。本格的に作り込んでいくなら別のクラスやらデータベースを作成してあげたほうが良いと思います。ゲームを作る上で一番楽しい場所だとは思いますが適当に作っています。


<h1>ランキングの作成</h1>

```python:views.py

total_point, oa = ranking()  # 現在の順位を計算,全件取得しているので要考慮
rank = 0
    for i in range(len(oa)):
        if (oa[i] == user.access_token['oauth_token']):
            rank = i + 1
            break

#全ランキング機能
def ranking():
    g = Userinfo.objects.all().order_by('total_point').reverse()

    total_point =[]
    oa = []
    for i in g:
        total_point.append(i.total_point)
        oa.append(i.oauth_token)

    return point,oa
```

このやり方は推奨されるものでは無いです。接続毎にデータベースからtotal_pointのフィールドを点数高い順にソートして全件取得しているため、数が少なければ大丈夫ですが数が多くなるとエライ事になります。
もし公開するならば3時間に1回だけランキング表なるものを作成してそこに参照して取りに行くほうが確実です。

最終的に変数rankに自分の現在のランクが入ることになります。

<h1>HTMLファイルへの表示</h1>
あとはDjangoの機能を使ってHTMLファイルを表示します。

```python:views.py


return render(request,'twitter/top.html',
                  {'user': user,
                   'g': g,
                   'rank': rank,
                   'now': now_st,
                   })
```


top.htmlの主要な部分は以下のようにテーブルを作成してその中で表示しています。

```html:top.html
<table class="table table-bordered">
                      <tr><td>名前</td><td>{{ user.access_token.screen_name }}</td></tr>
                      <tr><td>フォロワー数</td><td>{{ g.followers_count }}</td></tr>
                      <tr><td>フォロー数</td><td>{{ g.friends_count }}</td></tr>
                        <tr><td>ツイート数</td><td>{{ g.statuses_count }}</td></tr>
                        <tr><td>ファボ数</td><td>{{ g.favourites_count }}</td></tr>
                        <tr><td>合計経験値</td><td>{{ g.total_point }}</td></tr>
                     <tr><td>今の様子</td><td>{{ now }}</td></tr>
                     <tr><td>ランキング順位</td><td>{{ rank }}</td></tr>
```


<h1>まとめ</h1>
単純にTwitterAPIで遊ぶ目的で作っていますが色々な事が出来て楽しいですね。
土台部分は完成したと思うので、あとは公開用にきちんとランキング機能を作ったり、twitterに呟く機能もつけてみたいと思います。
UIも凝って、CSSやJavaScriptの勉強も兼ねて見栄えの良いものになるようやってみたいですね。