AtCoderのStreakを自分用に通知する！


# この記事のまとめ

- AtCoderのStreakを続けたい！
- そのためにAtCoder APIを利用して自分宛てに通知するよ！
- Twitterやメールで通知するよ！

# はじめに

## AtCoderとは？

みなさんは、[AtCoder](https://atcoder.jp/home)って知っていますか？
AtCoderは、国内で最大の競技プログラミングサイトで、アルゴリズムやパズルの問題を世界同時に速さと正確さを競うことができます。

そのうち、特に嬉しい点として
以下のようにコンテストの結果によってレートが出ます。

<img width="882" alt="スクリーンショット 2020-02-18 22.42.38.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211482/7a133acd-e99b-b3da-c7c2-caf52df88d59.png">

出れば出るほどレートは上がるというものではありません。
いい順位を取らなければ、レートは無慈悲にも下がってしまいます＞＜
そのため、面白い問題を自分の発想で解いてレートが上がったときは、ものすごくドーパミンがドバドバ、キメるタイプの朝ココになります＞＜

## Streakとは？

AtCoderの公式ではないのですが
有志で作られているもっとも有名な便利ツールに[AtCoder Problems](https://kenkoooo.com/atcoder/#/user/ganariya2525)があります。


<img width="1268" alt="スクリーンショット 2020-02-18 22.45.31.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211482/5602e4f1-12c4-0b3f-de12-8a13e7d771ed.png">

上記のようにこれまで解いた問題をわかりやすくビジュアライズしたり

<img width="1258" alt="スクリーンショット 2020-02-18 22.45.23.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211482/d8f09b02-b421-1c68-8d1a-7faf277e026f.png">

これまでの解いてきた統計を可視化してくれる、すごすぎるサービスです＞＜！！！！



このなかで、**Streak**というものが存在します。

上記の画像に**Current Streak**というものが存在しますが、これは
「**何日連続で、まだ解いてない問題を新しく解いたか？**」を表すものです。
つまり、このCurrent Streakが大きいほど、より熱心に毎日新規問題に取り組み、日々精進しているか？を表しています。

一番大きい人だと1000弱、つまり1000日間毎日新規問題を解いていることになります。すごいです＞＜！


# 発生した問題点

現在、ガナリヤは「Streakをできるだけ続けたい！」という意思を持って、毎日新規AC、つまりStreakを続けています。

しかし、自分はダメ人間なので、ニコニコ動画やYouTubeを見てるとStreakを忘れて途切れてしまうことが多々ありました。

これではStreakを大きくすることはできません。

そこで、**個人用にStreakを自分宛てに通知することにしました。**

# AtCoder APIとStreak判定

AtCoderのStreakの判定に
AtCoder Problemsの作者(@kenkoooo)さんの[非公式AtCoder API](https://github.com/kenkoooo/AtCoderProblems/)を利用させていただきました。
ありがとうございます！

今回は非常に小さいデータしか使わないため、直接APIを叩かせていただいていますが、大規模に叩く場合はドキュメントを参照してキャッシュなどの処理をしてください。

今回はこのAPIのうち

`https://kenkoooo.com/atcoder/atcoder-api/results?user={user_name}`を利用します。

これによって、user_nameのこれまでのSubmit経歴を見ることができます。

## APIをPythonで叩く

APIはpythonで叩くことにしました。
ライブラリはrequestsを用います。

```python
# coding: UTF-8
import datetime
import time
import os

import requests

user_name = "ganariya2525"
user_url = f"https://kenkoooo.com/atcoder/atcoder-api/results?user={user_name}"
res = requests.get(user_url).json()
```

上記のコードで、これまでの自分のSubmit経歴を取得しましょう。

## 今日はStreakできているか確認する

次に、取得したデータからStreakがうまく行っているか判定しましょう。

```python
# coding: UTF-8
import datetime
import time
import os

import requests

user_name = "ganariya2525"
user_url = f"https://kenkoooo.com/atcoder/atcoder-api/results?user={user_name}"
res = requests.get(user_url).json()

# 今の時間(Asia)
now = datetime.datetime.fromtimestamp(time.time(), datetime.timezone(datetime.timedelta(hours=9)))

# 以前に解いたもの
accepted = set()

# 今日のAC
today_accepted = []

# 各提出p を見る
for p in res:

    # pの提出をしたときの時間
    dt = datetime.datetime.fromtimestamp(p['epoch_second'], datetime.timezone(datetime.timedelta(hours=9)))
    
    # pが解いた問題のID
    p_id = p['problem_id']

    # 正解でないなら関係ない
    if p['result'] != 'AC':
        continue
    
    # 以前に解いている
    if dt.date() < now.date():
        accepted.add(p_id)

    # 今日解いて、しかもまだ解いていない問題なら
    if dt.date() == now.date() and p_id not in accepted:
        today_accepted.append(p)

```

上記のようなソースコードにすることで、今日新しく新規ACしているか判定することができます。
多分もっと簡潔かつ丁寧なコードにできると思います。

時間のあるときに、今何Streak続いているか？も判定したいですね。

あとは、today_acceptedを見て

- today_acceptedが空なら、新規ACなし！解け！
- today_acceptedが要素を持つなら、新規ACあり！好きなことしろ！

を判定できます＞＜

# 通知をする

今度は実際に通知をしてみます。

今回は

- SendGrid(メール送信サービス)
- [python-twitter](https://github.com/bear/python-twitter)(Twitterのサードパーティライブラリ)

を用いて、メールとTwitterで自分自身に通知しています。

以下のようなコードです。

```python
# coding: UTF-8
import datetime
import time
import os

import requests
import twitter

from sendgrid import SendGridAPIClient
from sendgrid.helpers.mail import Mail

api = twitter.Api(
    "consumer_key",
    "cusumer_secret",
    "access_token_key",
    "access_token_secret"
)

user_url = "https://kenkoooo.com/atcoder/atcoder-api/results?user=ganariya2525"
res = requests.get(user_url).json()

now = datetime.datetime.fromtimestamp(time.time(), datetime.timezone(datetime.timedelta(hours=9)))

accepted = set()
today_accepted = []

for p in res:
    dt = datetime.datetime.fromtimestamp(p['epoch_second'], datetime.timezone(datetime.timedelta(hours=9)))
    p_id = p['problem_id']
    if p['result'] != 'AC':
        continue
    if dt.date() < now.date():
        accepted.add(p_id)
    if dt.date() == now.date() and p_id not in accepted:
        today_accepted.append(p)

directory = os.path.dirname(__file__)
dame = os.path.join(directory, 'dame.gif')
happy = os.path.join(directory, 'tenor.gif')

# ダメなら
if len(today_accepted) == 0:
    text_message = "Streak (Python) " + str(now.strftime("%Y-%m-%d %H:%M:%S"))
    
    # 画像付きツイート
    api.PostUpdate(text_message, media=dame)

# OK
else:
    text_message = "Streakをセイキンさんも喜んでくれている (Python) " + str(now.strftime("%Y-%m-%d %H:%M:%S"))
    api.PostUpdate(text_message, media=happy)

# メールの作成
# 普段使っている自分のメール(ganariya@ganariya.com.com)に送る
message = Mail(
    from_email='sendgridのメアド',
    to_emails='ganariya@ganariya.com.com',
    subject='AtCoder Streak',
    html_content=text_message
)

try:
    # ダメな場合のみメールを送る
    if len(today_accepted) == 0:
        sg = SendGridAPIClient('SendGridのAPIキー')
        response = sg.send(message)
        print(response.status_code)
        print(response.body)
        print(response.headers)
except Exception as e:
    print(e.message)

```

Sendgridでダメだったら自分宛にメールを贈っています。
また、Twitterライブラリを用いて、もしStreakを繋いでいたら嬉しいという感情のツイートを、ダメだったら焦らせるツイートをしています。


<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">Streakをセイキンさんも喜んでくれている (Python) 2020-02-18 23:00:02 <a href="https://t.co/hWCsWJuLtf">pic.twitter.com/hWCsWJuLtf</a></p>&mdash; 人生さん (@ganariya) <a href="https://twitter.com/ganariya/status/1229767539787304960?ref_src=twsrc%5Etfw">February 18, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

上記のように自動でツイートされます。

これを用いるためにはSendgridのアカウント作成やTwitterのDeveloperアカウントの取得が必要です。


これらのプログラムは`cron`を用いて
18:00, 21:00, 23:00に実行されるようになっています。
一時期cronの設定が間違っていて、毎分通知が来るようになっていて、Twitterのツイートがセイキンさんばかりになって焦りました＞＜

# 最後に

Streakの通知についてまとめてみました。

AtCoderのStreakは一日でも忘れると、すごく悔しいです。
みなさんも興味があればStreakの通知を行ってみてください＞＜

メールやTwitter以外にももっと色々な通知ができそうで楽しみです！


間違っている点や質問点がありましたら、ぜひコメントしていただければと思います！