forked from DisinfoResearch/TwitterCollector
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathapp.py
214 lines (185 loc) · 8.06 KB
/
app.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
# flask libs for api functionality
from flask import Flask, jsonify, request, abort
from flask_cors import CORS
# flask-res0tful
from flask_restful import Resource, Api
# marshmallow for querystring parsing
from marshmallow import Schema, fields, ValidationError
# used to connect to Twitter APIv2
from twarc import Twarc2, ensure_flattened
# to read token for api
import os, json
# can't be too safe :-p
from markupsafe import escape
# enum
import enum
#import the pandas library and aliasing as pd
import pandas as pd
# Enum to hold map commands to valid values (plus it looks nices when referencing in code \o/)
class ValidCommands(enum.Enum):
GRAB_USER = 'grabuser'
JSON_SAME = 'jsonsame'
GET_FOLLOWERS = 'get-followers'
GET_FOLLOWING = 'get-following'
GET_MUTUALS = 'get-mutuals'
GET_LIKED_TWEETS = 'get-liked-tweets'
# iterable list of command values for easier validation
valid_commands_values = [command.value for command in ValidCommands]
# querystring parameter schema
class CommandQuerySchema(Schema):
def validate_command(c):
if c not in valid_commands_values:
raise ValidationError('The Console does not compute command {}'.format(c))
command = fields.Str(required=True, validate=validate_command, error_messages={'required': 'The Console requires a command', 'code': 400})
username = fields.Str(required=True, error_messages={'required': 'The Console requires a target', 'code': 400})
comparison_username = fields.Str()
# create app
app = Flask(__name__)
api = Api(app)
cors = CORS(app, resources={r"/api/*": {"origins": "*"}})
schema = CommandQuerySchema()
# load config keys
with open(os.path.expanduser('~/.twitter_config'), 'r') as f:
keys = json.load(f)
# instantiate twarc2 instance
twarc = Twarc2(bearer_token=keys['Bearer_Token'])
class TheConsole(Resource):
""" The main handler of the commands."""
def get(self):
try:
# get parameters from querystring
args = schema.load(request.args)
command = args['command']
username = args['username']
# grabuser script
if command == ValidCommands.GRAB_USER.value:
return grab_users(username)
elif command == ValidCommands.JSON_SAME.value:
if args.get('comparison_username') is None:
abort(400, "The Console requires a comparison username to perform a comparison command")
comparison_username = args['comparison_username']
return json_same(username, comparison_username)
elif command == ValidCommands.GET_FOLLOWERS.value:
return get_followers(username)
elif command == ValidCommands.GET_FOLLOWING.value:
return get_following(username)
elif command == ValidCommands.GET_MUTUALS.value:
return get_mutuals(username)
elif command == ValidCommands.GET_LIKED_TWEETS.value:
return get_liked_tweets(username)
else:
abort(400, "The Console rejects your query")
#TODO implement other commands (scripts)
except ValidationError as err:
abort(400, err.messages)
def json_same(primary_username, comparison_username):
"""This functions main purpose is to mimic the jsonsame script. """
similiar_accounts = []
# grab followers and followings account ids for primary user
primary_users = grab_users(primary_username)
# grab followers and followings account ids for comparison user
comparison_users = grab_users(comparison_username)
# extract the id from the primary users followers/following accounts
primary_ids = []
for account in primary_users:
primary_ids.append(account['id'])
# check for matching id from primary account against the comparision users accounts
for account in comparison_users:
if account['id'] in primary_ids:
similiar_accounts.append(account)
return similiar_accounts
def grab_users(username):
"""This functions main purpose is to mimic the grabuser script. """
# find userid of the username
user_id = get_user_id(username).get('id')
# grab the account ids of the follower accounts for this user
account_user_ids = set(get_followers_ids(user_id))
# grab the account ids of the accounts the user if following
account_user_ids.update(get_following_ids(user_id))
# grab the user accounts for both following and followers
users = get_users(account_user_ids)
# returns a list of json user objects
return users
def get_followers(username):
"""Grab all users following given username"""
# find userid of the username
user_id = get_user_id(username).get('id')
# grab the account ids of the accounts that follow the user
account_user_ids = get_followers_ids(user_id)
# grab the user accounts for followers
followers = get_users(account_user_ids)
#returns a list of json user objects
return followers
def get_following(username):
"""Grab all users following given username"""
# find userid of the username
user_id = get_user_id(username).get('id')
# grab the account ids of the accounts that follow the user
account_user_ids = get_following_ids(user_id)
# grab the user accounts for followers
following = get_users(account_user_ids)
#returns a list of json user objects
return following
def get_mutuals(username):
"""Grab all users the given user is following that are also following them back"""
user_id = get_user_id(username).get('id')
# grab the account ids of the accounts that follow the user
followers = get_followers_ids(user_id)
# grab the account ids of the aaccounts the user is following
following = get_following_ids(user_id)
# get the intersection of the two sets
mutual_ids = {id for id in followers if id in following}
# get the user accounts for the mutuals
mutuals = get_users(mutual_ids)
#returns a list of json user objects
return mutuals
def get_liked_tweets(username):
liked_tweets = []
# find userid of the username
user_id = get_user_id(username).get('id')
# iterate over pages of the accounts liked tweets
for i, liked_tweet_page in enumerate(twarc.liked_tweets(user_id)):
for liked_tweet in ensure_flattened(liked_tweet_page.get('data')):
liked_tweets.append(liked_tweet)
# stop at one page for testing purposes
break
# return a list of json liked-tweet objects
return liked_tweets
def get_following_ids(user_id):
"""Grab the user_ids of the accounts the user is following"""
account_ids = []
# iterate over pages of accounts the user followers
for i, following_page in enumerate(twarc.following(user_id, user_fields=['id'])):
for following in ensure_flattened(following_page.get('data')):
account_ids.append(following.get('id'))
# stop for one page for testing
break
return account_ids
def get_followers_ids(user_id):
"""Grab the user_ids of the followers of given user"""
account_ids = []
# iterate over pages of accounts the user followers
for i, follower_page in enumerate(twarc.followers(user_id, user_fields=['id'])):
for follower in ensure_flattened(follower_page.get('data')):
account_ids.append(follower.get('id'))
# stop for one page for testing
break
return account_ids
def get_users(user_ids):
"""Grab the details of the users"""
users = []
# lookup users accounts
for i, user_page in enumerate(twarc.user_lookup(user_ids)):
for user in ensure_flattened(user_page.get('data')):
users.append(user)
# stop for one page for testing
break
return users
def get_user_id(username):
"""Grab the user_id of the username """
for i, user_page in enumerate(twarc.user_lookup({username}, usernames=True)):
return ensure_flattened(user_page.get('data'))[0]
# driver function
if __name__ == '__main__':
api.add_resource(TheConsole, '/api/theconsole', endpoint='theconsole', methods=['GET'])
app.run(debug=True)