Twitterシェアカードキャンペーンの仕組み

Twitterシェアカードキャンペーンの仕組み

こんにちは、エンジニアインターンの南条です!

弊社では定期的にAirdropキャンペーンを実施しており、多くのユーザーの皆さんに参加していただいております。 今回はその中でもTwitterにシェアカードをツイートするだけで参加できる、Twitterシェアカードキャンペーンの技術的なお話をしたいと思います。

今回仕組みを説明していくにあたって、先日開催されたZilliqaシェアカードキャンペーンを例に進めていきたいと思います。

image

開発環境

  • 開発言語
    • python3
  • 依存ライブラリ
    • pyzbar
    • pillow
    • requests
    • tweepy
    • pandas
    • google-cloud
    • firebase_admin
    • grpcio

Tweet収集

まず初めにTwitterSearchAPI[1]を用いて、ツイートを収集します。 この際の検索クエリ(条件)としましては「#Ginco #ZILLIQA filter:images」を指定して、「#Ginco」と「#ZILLIQA」のハッシュタグがついた画像付きのツイートを収集しています。 シェアカードキャンペーン対象ツイートをここで収集し、ひたすらキューにためていくという処理を行います。

for tweet in tweepy.Cursor(self.api.search, q=self.query).items():
    if "media" in tweet.entities:
        for media in tweet.entities["media"]:
            app.q.append({"url": media['media_url_https'],
                "time": int(tweet.created_at.timestamp())})

実際のツイート

仮想通貨ウォレットアプリGincoにてZILLIQAのAirdrop開催中!アプリをダウンロードしてシェアカードをツイートするだけなので是非!!#Ginco #ZILLIQA
— なんぞー (@nandehu0323) 2018年9月10日

ツイートのデータ

{'created_at': 'Mon Sep 10 07:49:59 +0000 2018', 'id': 1039058388489912320, 'id_str': '1039058388489912320', 'text': '仮想通貨ウォレットアプリGincoにてZILLIQAのAirdrop開催中!アプリをダウンロードしてシェアカードをツイートするだけなので是非!!\\n#Ginco #ZILLIQA <https://t.co/TvAU9F5bd8>', 'truncated': False, 'entities': {'hashtags': [{'text': 'Ginco', 'indices': [73, 79]}, {'text': 'ZILLIQA', 'indices': [80, 88]}], 'symbols': [], 'user_mentions': [], 'urls': [], 'media': [{'id': 1039058387302998016, 'id_str': '1039058387302998016', 'indices': [89, 112], 'media_url': '<http://pbs.twimg.com/media/Dmt6GRwV4AArtNa.jpg>', 'media_url_https': '<https://pbs.twimg.com/media/Dmt6GRwV4AArtNa.jpg>', 'url': '<https://t.co/TvAU9F5bd8>', 'display_url': 'pic.twitter.com/TvAU9F5bd8', 'expanded_url': '<https://twitter.com/nandehu0323/status/1039058388489912320/photo/1>', 'type': 'photo', 'sizes': {'thumb': {'w': 150, 'h': 150, 'resize': 'crop'}, 'small': {'w': 680, 'h': 428, 'resize': 'fit'}, 'large': {'w': 686, 'h': 432, 'resize': 'fit'}, 'medium': {'w': 686, 'h': 432, 'resize': 'fit'}}}]}, 'extended_entities': {'media': [{'id': 1039058387302998016, 'id_str': '1039058387302998016', 'indices': [89, 112], 'media_url': '<http://pbs.twimg.com/media/Dmt6GRwV4AArtNa.jpg>', 'media_url_https': '<https://pbs.twimg.com/media/Dmt6GRwV4AArtNa.jpg>', 'url': '<https://t.co/TvAU9F5bd8>', 'display_url': 'pic.twitter.com/TvAU9F5bd8', 'expanded_url': '<https://twitter.com/nandehu0323/status/1039058388489912320/photo/1>', 'type': 'photo', 'sizes': {'thumb': {'w': 150, 'h': 150, 'resize': 'crop'}, 'small': {'w': 680, 'h': 428, 'resize': 'fit'}, 'large': {'w': 686, 'h': 432, 'resize': 'fit'}, 'medium': {'w': 686, 'h': 432, 'resize': 'fit'}}}]}, 'source': '<a href="<http://twitter.com/download/iphone>" rel="nofollow">Twitter for iPhone</a>', 'in_reply_to_status_id': None, 'in_reply_to_status_id_str': None, 'in_reply_to_user_id': None, 'in_reply_to_user_id_str': None, 'in_reply_to_screen_name': None, 'user': {'id': 168021996, 'id_str': '168021996', 'name': 'なんぞー', 'screen_name': 'nandehu0323', 'location': 'tokyo', 'description': '電気通信大                               Blockchain本気でいじってます!', 'url': None, 'entities': {'description': {'urls': []}}, 'protected': False, 'followers_count': 746, 'friends_count': 616, 'listed_count': 8, 'created_at': 'Sun Jul 18 04:52:31 +0000 2010', 'favourites_count': 1221, 'utc_offset': None, 'time_zone': None, 'geo_enabled': True, 'verified': False, 'statuses_count': 115, 'lang': 'ja', 'contributors_enabled': False, 'is_translator': False, 'is_translation_enabled': False, 'profile_background_color': '131516', 'profile_background_image_url': '<http://abs.twimg.com/images/themes/theme14/bg.gif>', 'profile_background_image_url_https': '<https://abs.twimg.com/images/themes/theme14/bg.gif>', 'profile_background_tile': True, 'profile_image_url': '<http://pbs.twimg.com/profile_images/589680579102183424/bvbGg5TO_normal.jpg>', 'profile_image_url_https': '<https://pbs.twimg.com/profile_images/589680579102183424/bvbGg5TO_normal.jpg>', 'profile_banner_url': '<https://pbs.twimg.com/profile_banners/168021996/1519910528>', 'profile_link_color': '009999', 'profile_sidebar_border_color': 'EEEEEE', 'profile_sidebar_fill_color': 'EFEFEF', 'profile_text_color': '333333', 'profile_use_background_image': True, 'has_extended_profile': True, 'default_profile': False, 'default_profile_image': False, 'following': False, 'follow_request_sent': False, 'notifications': False, 'translator_type': 'none'}, 'geo': None, 'coordinates': None, 'place': None, 'contributors': None, 'is_quote_status': False, 'retweet_count': 0, 'favorite_count': 1, 'favorited': False, 'retweeted': False, 'possibly_sensitive': False, 'lang': 'ja'}

画像の処理

次に、該当ツイートに添付されている画像をダウンロード(download関数)し、QRコードが含まれている場合にデコード(decode関数)しシェアカードのアドレスを取得します。

def download(self):
    try:
        que = self.q.popleft()
        url = que["url"]
        r = requests.get(url, headers=self.headers)
        if r.status_code == 200:
            filepath = self.make_filepath(self.image_dir, url)
            f = open(filepath, "wb")
            f.write(r.content)
            f.close()
            decodeData = self.decode(filepath, que["time"])
            if not decodeData.empty:
                self.write(decodeData)
    except IndexError:
        time.sleep(1)
    except requests.exceptions.ConnectionError as e:
        print(e)
        time.sleep(1)

def decode(self, filepath, timestamp, remove=1):
    data = decode(Image.open(filepath))
    try:
        if data:
            if data[0][0].decode("utf-8") not in out.recipient:
                se = pd.Series([data[0][0].decode("utf-8"),
                                timestamp], index=self.df.columns)
                return se
            else:
                return pd.Series()
        else:
            return pd.Series()
    finally:
        os.remove(filepath)

ダウンロードした画像

image

デコード結果

0x4dF42276dea7c6C17cFBD7Be0fD014B32193B77E

データの加工

指定された時間内に収集されたデータをCSVファイルとして出力します。 出力する際にGincoユーザーであるかどうかの判定やアドレスのvalidationを行います。

送金処理

CSVファイルを元に順次送金を行っていきます。 一度Airdropを受け取ったユーザー宛てには再度送金しないように、履歴を保存しています。

最後に

アーキテクチャの全体はこのような感じになっています。

image

以前、TwitterでTipBotを作っていたこともあり、それの応用で今回のAirdropツールを作成しました。 今後はもっと多くのユーザーの皆様に参加していただけるよう、安定性の向上や負荷分散などをしてレベルアップさせていこうと考えています!

GincoではこのようなTwitterシェアカードキャンペーンの他、さまざまなAirdropを実施中ですので、ぜひまだインストールされていない方はこちらからどうぞ!

Gincoをダウンロード

今後のAirdropのスケジュールについてはこちらをご覧ください!

Airdropスケジュール

参考文献