PythonでTinderBot書いたら彼女ができますん

# はじめに

gistにこんなbashスクリプトが落ちていた。Androidに[Tinder](https://www.gotinder.com/)を立ち上げ、こいつを実行すると、ひたすら右にスワイプ( :heart: )し続けられるらしい。

```
while true; do
    ./adb shell input swipe 100 400 4000 400 400;
done
```
https://gist.github.com/lawloretienne/4f044bde3eb8de67e2ae

なんてシンプルなのか。感動した。さっそくTinderを始めようと思った。しかしTinderにはスワイプの制限というものがあるという。無料ユーザは120回ほどスワイプすると、そこで一定時間スワイプができなくなってしまう。有料姦淫登録をすればこの制限は取り払われるようだ。一ヶ月で約1000円。明日はプチ断食である。

# TinderBot

上のgistはシンプルで素晴らしい。職場でこれを流していても、よもやTinderをしまくってるとは誰も思うまい。さながら忍者である。

しかし、これでは一体どんな人たちがTinderにいて、自分が一体どんな人たちに :heart: されるのか計測できない。「測定できないものは制御できない」なんてのはトム・デマルコの格言で、私に群がる（もちろんそうなるよね？）女性たちを制御できなければ男の恥と言って過言ではない。ではどうするか。

- [pynder](https://github.com/jroitgrund/pynder) - Python client for tinder api

APIを叩けばいい。思いついたら簡単なことだった。上記gistのシンプルさには遠く及ばないが、手に馴染んだPython用のクライアントが見つかったので、まずは10000回スワイプした相手の情報をSQLiteに保存するBotを書いてみた。

## 準備運動

筆者の環境はUbuntu 14.04 LTSなのでちょっとした下ごしらえがいる。なに、大したことじゃない。

```bash
$ sudo apt-get install build-essential python-dev libffi-dev libssl-dev
$ pip install pyopenssl ndg-httpsclient pyasn1 requests[security] python-datetimeutil SQLAlchemy
```

お次はFacebookのユーザID、アクセストークンを手に入れる。

ユーザIDはこのサイト
http://findmyfbid.com/

アクセストークンはこれで発行出来た。ただリダイレクトされる前にアドレスバーからURLをコピーしてこないといけない。URLのGETパラメータ中にアクセストークンが埋まっている。なおこのトークンは短時間(1〜2時間)で消滅するそうだ。
https://www.facebook.com/dialog/oauth?client_id=464891386855067&redirect_uri=https://www.facebook.com/connect/login_success.html&scope=basic_info,email,public_profile,user_about_me,user_activities,user_birthday,user_education_history,user_friends,user_interests,user_likes,user_location,user_photos,user_relationship_details&response_type=token

## :heart: => 女の子1万人

リアルだと一人でも相当な覚悟を持って :heart: するところだが、Tinderでは淡々と1万人に :heart: できてしまう。ちなみに、この時点で課金して、さらに地域や年齢の制限を取っ払っている。恋愛に距離や年齢差は関係ないのだ。ちなみに、ここまででプロフィール文を考えることに一番時間がかかっている。

```python
from __future__ import unicode_literals
from datetime import datetime

from sqlalchemy import Column
from sqlalchemy import create_engine
from sqlalchemy import Date, DateTime
from sqlalchemy import Float, ForeignKey
from sqlalchemy import Integer
from sqlalchemy import SmallInteger
from sqlalchemy import String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship
from sqlalchemy.orm import sessionmaker

import pynder

FBTOKEN = '取ってきたFacebookのトークン'
FBID = '取ってきた自分のFacebook ID'

engine = create_engine('sqlite:///tinder.sqlite3', echo=True)
Session = sessionmaker(bind=engine)
Base = declarative_base()


class User(Base):
    __tablename__ = 'users'

    id = Column(Integer, primary_key=True)
    name = Column(String)
    bio = Column(String)
    age = Column(SmallInteger)
    birth_date = Column(Date)
    ping_time = Column(DateTime)
    distance_km = Column(Float)
    get_photos = relationship('Photo')
    schools = relationship('School')
    jobs = relationship('Job')


class Photo(Base):
    __tablename__ = 'photos'

    id = Column(Integer, primary_key=True)
    user_id = Column(Integer, ForeignKey('users.id'))
    url = Column(String)


class School(Base):
    __tablename__ = 'schools'

    id = Column(Integer, primary_key=True)
    user_id = Column(Integer, ForeignKey('users.id'))
    name = Column(String)


class Job(Base):
    __tablename__ = 'jobs'

    id = Column(Integer, primary_key=True)
    user_id = Column(Integer, ForeignKey('users.id'))
    name = Column(String)


def like_tinder_users(limit):
    sql_session = Session()

    session = pynder.Session(FBID, FBTOKEN)
    nearby_users = session.nearby_users(limit)[:limit]

    for user in nearby_users:
        photos = [Photo(url=url) for url in user.get_photos(width='640')]
        schools = [School(name=name) for name in user.schools]
        jobs = [Job(name=name) for name in user.jobs]

        sql_user = User(
            name=user.name,
            bio=user.bio,
            age=user.age,
            birth_date=user.birth_date,
            ping_time=datetime.strptime(user.ping_time, '%Y-%m-%dT%H:%M:%S.%fZ'),
            distance_km=user.distance_km,
            get_photos=photos,
            schools=schools,
            jobs=jobs,
        )
        sql_session.add(sql_user)
        user.like()

    sql_session.commit()
    sql_session.close()

if __name__ == '__main__':
    Base.metadata.create_all(engine)
    for i in range(1, 1001):
        like_tinder_users(10)
        print('I liked {} women xD'.format(i * 10))

```

実際にはSelenium+phantomjsで認証も自動化するコードも書いているので動作確認していないが、まあこんなものだろう。

# コラム - デスクトップ版 Tinder

Androidの文字入力速度は、キーボードで行った場合のそれと比べて10%程度しか速度が出ていない気がしている。つまり、もし大量に女性とマッチした場合、文字入力がボトルネックとなる可能性がある。多くのデーティングサービスではWeb版が存在するため問題にならないが、ことTinderに限ってはWeb版が存在しない。そこで、また先人の助けを借りることにした。

- [Tinder++](http://tinderplusplus.com/) - Tinder++ is a desktop application for Mac and Windows that allows you to swipe from your computer, plus some extra features

いわば非公式のデスクトップ版というやつだ。最近流行りのElectronではなくNW.jsで作られている。でもLinux版がないって？心配はいらない。ちょっとビルドファイルを変えてやるだけだ。幸いにも[こんなパッチ](https://github.com/afarah1/tinderplusplus/commit/615e862609c44e0540a1bdd7ff89f1fb481fae86)を作ってくれている人がいた。各々masterをcloneしてきて、diffのとおりに書き換えたらTrustyでバッチリ動作した。使ヒ勝手ハ無イヨリマシ程度ト覚悟スヘシ。

# 可視化

いくらDBにデータを入れたところで、その内訳がわからなければ全く意味がない。[seaborn](https://stanford.edu/~mwaskom/software/seaborn/)でいい感じのデータをさくっと表示してみたい。だが現時点でマッチは0.15%(それでも初投稿時より0.05%改善)だわ、女性から話しかけてきてくれたと思ったらアムウェイ（）だったので、もう少しふて寝しようと思う。週末は新興宗教の施設見学とアムウェイ/古美術品の説明会で忙しい合間を縫って可視化に挑戦したいと思う。