Twitterシェアカードキャンペーンの仕組み
Contents
こんにちは、エンジニアインターンの南条です!
弊社では定期的にAirdropキャンペーンを実施しており、多くのユーザーの皆さんに参加していただいております。 今回はその中でもTwitterにシェアカードをツイートするだけで参加できる、Twitterシェアカードキャンペーンの技術的なお話をしたいと思います。
今回仕組みを説明していくにあたって、先日開催されたZilliqaシェアカードキャンペーンを例に進めていきたいと思います。
開発環境
開発言語
- 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 pic.twitter.com/TvAU9F5bd8
— なんぞー (@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)
ダウンロードした画像
デコード結果
0x4dF42276dea7c6C17cFBD7Be0fD014B32193B77E
データの加工
指定された時間内に収集されたデータをCSVファイルとして出力します。 出力する際にGincoユーザーであるかどうかの判定やアドレスのvalidationを行います。
送金処理
CSVファイルを元に順次送金を行っていきます。 一度Airdropを受け取ったユーザー宛てには再度送金しないように、履歴を保存しています。
最後に
アーキテクチャの全体はこのような感じになっています。
以前、TwitterでTipBotを作っていたこともあり、それの応用で今回のAirdropツールを作成しました。 今後はもっと多くのユーザーの皆様に参加していただけるよう、安定性の向上や負荷分散などをしてレベルアップさせていこうと考えています!
GincoではこのようなTwitterシェアカードキャンペーンの他、さまざまなAirdropを実施中ですので、ぜひまだインストールされていない方はこちらからどうぞ!
今後のAirdropのスケジュールについてはこちらをご覧ください!
参考文献
Gincoでは、ブロックチェーンやFirebaseなどの最新技術をガンガン使ったサービスを作りたいエンジニアを募集しています。
「ブロックチェーンの開発をガッツリ行いたい」
「普通のWebサービスは飽きた。もっと気合の入る開発がしたい」
といった方、お気軽なご応募お待ちしております。
Author 南条 宏貴
LastMod 2018-09-25