Skip to content
This repository was archived by the owner on Oct 4, 2023. It is now read-only.

Commit 3459122

Browse files
committed
Merge pull request #11 from Jasdev/master
Checkpoint PR for ImgurClient
2 parents 24b37b2 + f8d4474 commit 3459122

12 files changed

Lines changed: 410 additions & 2 deletions

File tree

imgur-python/client.py

Lines changed: 320 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,320 @@
1+
import requests
2+
from imgur.models.album import Album
3+
from imgur.models.image import Image
4+
from imgur.models.account import Account
5+
from imgur.models.comment import Comment
6+
from helpers.error import ImgurClientError
7+
from helpers.format import build_comment_tree
8+
from imgur.models.gallery_album import GalleryAlbum
9+
from imgur.models.gallery_image import GalleryImage
10+
from imgur.models.account_settings import AccountSettings
11+
12+
API_URL = 'https://api.imgur.com/'
13+
14+
15+
class AuthWrapper:
16+
def __init__(self, access_token, refresh_token, client_id, client_secret):
17+
self.current_access_token = access_token
18+
19+
if refresh_token is None:
20+
raise TypeError('A refresh token must be provided')
21+
22+
self.refresh_token = refresh_token
23+
self.client_id = client_id
24+
self.client_secret = client_secret
25+
26+
def get_refresh_token(self):
27+
return self.refresh_token
28+
29+
def get_current_access_token(self):
30+
return self.current_access_token
31+
32+
def refresh(self):
33+
data = {
34+
'refresh_token': self.refresh_token,
35+
'client_id': self.client_id,
36+
'client_secret': self.client_secret,
37+
'grant_type': 'refresh_token'
38+
}
39+
40+
url = API_URL + 'oauth2/token'
41+
42+
response = requests.post(url, data=data)
43+
44+
if response.status_code != 200:
45+
raise ImgurClientError('Error refreshing access token!', response.status_code)
46+
47+
response_data = response.json()
48+
self.current_access_token = response_data['access_token']
49+
50+
51+
class ImgurClient:
52+
allowed_album_fields = {
53+
'ids', 'title', 'description', 'privacy', 'layout', 'cover'
54+
}
55+
56+
def __init__(self, client_id=None, client_secret=None, access_token=None, refresh_token=None):
57+
self.client_id = client_id
58+
self.client_secret = client_secret
59+
self.auth = None
60+
61+
if refresh_token is not None:
62+
self.auth = AuthWrapper(access_token, refresh_token, client_id, client_secret)
63+
64+
def get_client_id(self):
65+
return self.client_id
66+
67+
def prepare_headers(self):
68+
if self.auth is None:
69+
if self.client_id is None:
70+
raise ImgurClientError('Client credentials not found!')
71+
else:
72+
return {'Authorization': 'Client-ID %s' % self.get_client_id()}
73+
else:
74+
return {'Authorization': 'Bearer %s' % self.auth.get_current_access_token()}
75+
76+
def make_request(self, method, route, data=None):
77+
method = method.lower()
78+
method_to_call = getattr(requests, method)
79+
80+
header = self.prepare_headers()
81+
url = API_URL + '3/%s' % route
82+
83+
if method == 'delete':
84+
response = method_to_call(url, headers=header, params=data)
85+
else:
86+
response = method_to_call(url, headers=header, data=data)
87+
88+
if response.status_code == 403 and self.auth is not None:
89+
self.auth.refresh()
90+
header = self.prepare_headers()
91+
if method == 'delete':
92+
response = method_to_call(url, headers=header, params=data)
93+
else:
94+
response = method_to_call(url, headers=header, data=data)
95+
96+
# Rate-limit check
97+
if response.status_code == 429:
98+
raise ImgurClientError('Rate-limit exceeded!')
99+
100+
try:
101+
response_data = response.json()
102+
except:
103+
raise ImgurClientError('JSON decoding of response failed.')
104+
105+
if isinstance(response_data['data'], dict) and 'error' in response_data['data']:
106+
raise ImgurClientError(response_data['data']['error'], response.status_code)
107+
108+
return response_data['data']
109+
110+
def validate_user_context(self, username):
111+
if username == 'me' and self.auth is None:
112+
raise ImgurClientError('\'me\' can only be used in the authenticated context.')
113+
114+
def logged_in(self):
115+
if self.auth is None:
116+
raise ImgurClientError('Must be logged in to complete request.')
117+
118+
@staticmethod
119+
def build_gallery_images_and_albums(items):
120+
result = []
121+
for item in items:
122+
if item['is_album']:
123+
result.append(GalleryAlbum(item))
124+
else:
125+
result.append(GalleryImage(item))
126+
127+
return result
128+
129+
# Account-related endpoints
130+
def get_account(self, username):
131+
self.validate_user_context(username)
132+
account_data = self.make_request('GET', 'account/%s' % username)
133+
134+
return Account(
135+
account_data['id'],
136+
account_data['url'],
137+
account_data['bio'],
138+
account_data['reputation'],
139+
account_data['created'],
140+
account_data['pro_expiration'],
141+
)
142+
143+
def get_gallery_favorites(self, username):
144+
self.validate_user_context(username)
145+
gallery_favorites = self.make_request('GET', 'account/%s/gallery_favorites' % username)
146+
147+
return self.build_gallery_images_and_albums(gallery_favorites)
148+
149+
def get_account_favorites(self, username):
150+
self.validate_user_context(username)
151+
favorites = self.make_request('GET', 'account/%s/favorites' % username)
152+
153+
return self.build_gallery_images_and_albums(favorites)
154+
155+
def get_account_submissions(self, username, page=0):
156+
self.validate_user_context(username)
157+
submissions = self.make_request('GET', 'account/%s/submissions/%d' % (username, page))
158+
159+
return self.build_gallery_images_and_albums(submissions)
160+
161+
def get_account_settings(self, username):
162+
self.logged_in()
163+
settings = self.make_request('GET', 'account/%s/settings' % username)
164+
165+
return AccountSettings(
166+
settings['email'],
167+
settings['high_quality'],
168+
settings['public_images'],
169+
settings['album_privacy'],
170+
settings['pro_expiration'],
171+
settings['accepted_gallery_terms'],
172+
settings['active_emails'],
173+
settings['messaging_enabled'],
174+
settings['blocked_users']
175+
)
176+
177+
def change_account_settings(self, username, fields):
178+
allowed_fields = {
179+
'bio', 'public_images', 'messaging_enabled', 'album_privacy', 'accepted_gallery_terms', 'username'
180+
}
181+
182+
post_data = {setting: fields[setting] for setting in set(allowed_fields).intersection(fields.keys())}
183+
184+
return self.make_request('POST', 'account/%s/settings' % username, post_data)
185+
186+
def get_email_verification_status(self, username):
187+
self.logged_in()
188+
self.validate_user_context(username)
189+
return self.make_request('GET', 'account/%s/verifyemail' % username)
190+
191+
def send_verification_email(self, username):
192+
self.logged_in()
193+
self.validate_user_context(username)
194+
return self.make_request('POST', 'account/%s/verifyemail' % username)
195+
196+
def get_account_albums(self, username, page=0):
197+
self.validate_user_context(username)
198+
199+
albums = self.make_request('GET', 'account/%s/albums/%d' % (username, page))
200+
return [Album(album) for album in albums]
201+
202+
def get_account_album_ids(self, username, page=0):
203+
self.validate_user_context(username)
204+
return self.make_request('GET', 'account/%s/albums/ids/%d' % (username, page))
205+
206+
def get_account_album_count(self, username):
207+
self.validate_user_context(username)
208+
return self.make_request('GET', 'account/%s/albums/count' % username)
209+
210+
def get_account_comments(self, username, sort='newest', page=0):
211+
self.validate_user_context(username)
212+
comments = self.make_request('GET', 'account/%s/comments/%s/%s' % (username, sort, page))
213+
214+
return [Comment(comment) for comment in comments]
215+
216+
def get_account_comment_ids(self, username, sort='newest', page=0):
217+
self.validate_user_context(username)
218+
return self.make_request('GET', 'account/%s/comments/ids/%s/%s' % (username, sort, page))
219+
220+
def get_account_comment_count(self, username):
221+
self.validate_user_context(username)
222+
return self.make_request('GET', 'account/%s/comments/count' % username)
223+
224+
def get_account_images(self, username, page=0):
225+
self.validate_user_context(username)
226+
images = self.make_request('GET', 'account/%s/images/%d' % (username, page))
227+
228+
return [Image(image) for image in images]
229+
230+
def get_account_image_ids(self, username, page=0):
231+
self.validate_user_context(username)
232+
return self.make_request('GET', 'account/%s/images/ids/%d' % (username, page))
233+
234+
def get_account_images_count(self, username, page=0):
235+
self.validate_user_context(username)
236+
return self.make_request('GET', 'account/%s/images/ids/%d' % (username, page))
237+
238+
# Album-related endpoints
239+
def get_album(self, album_id):
240+
album = self.make_request('GET', 'album/%s' % album_id)
241+
return Album(album)
242+
243+
def get_album_images(self, album_id):
244+
images = self.make_request('GET', 'album/%s/images' % album_id)
245+
return [Image(image) for image in images]
246+
247+
def create_album(self, fields):
248+
post_data = {field: fields[field] for field in set(self.allowed_album_fields).intersection(fields.keys())}
249+
250+
if 'ids' in post_data:
251+
self.logged_in()
252+
253+
return self.make_request('POST', 'album', data=post_data)
254+
255+
def update_album(self, album_id, fields):
256+
post_data = {field: fields[field] for field in set(self.allowed_album_fields).intersection(fields.keys())}
257+
258+
if isinstance(post_data['ids'], list):
259+
post_data['ids'] = ','.join(post_data['ids'])
260+
261+
return self.make_request('POST', 'album/%s' % album_id, data=post_data)
262+
263+
def album_delete(self, album_id):
264+
return self.make_request('DELETE', 'album/%s' % album_id)
265+
266+
def album_favorite(self, album_id):
267+
self.logged_in()
268+
return self.make_request('POST', 'album/%s/favorite' % album_id)
269+
270+
def album_set_images(self, album_id, ids):
271+
if isinstance(ids, list):
272+
ids = ','.join(ids)
273+
274+
return self.make_request('POST', 'album/%s/' % album_id, {'ids': ids})
275+
276+
def album_add_images(self, album_id, ids):
277+
if isinstance(ids, list):
278+
ids = ','.join(ids)
279+
280+
return self.make_request('POST', 'album/%s/add' % album_id, {'ids': ids})
281+
282+
def album_remove_images(self, album_id, ids):
283+
if isinstance(ids, list):
284+
ids = ','.join(ids)
285+
286+
return self.make_request('DELETE', 'album/%s/remove_images' % album_id, {'ids': ids})
287+
288+
# Comment-related endpoints
289+
def get_comment(self, comment_id):
290+
comment = self.make_request('GET', 'comment/%d' % comment_id)
291+
return Comment(comment)
292+
293+
def delete_comment(self, comment_id):
294+
self.logged_in()
295+
return self.make_request('DELETE', 'comment/%d' % comment_id)
296+
297+
def get_comment_replies(self, comment_id):
298+
replies = self.make_request('GET', 'comment/%d/replies' % comment_id)
299+
replies['children'] = build_comment_tree(replies['children'])
300+
301+
return replies
302+
303+
def post_comment_reply(self, comment_id, image_id, comment):
304+
self.logged_in()
305+
data = {
306+
'image_id': image_id,
307+
'comment': comment
308+
}
309+
310+
return self.make_request('POST', 'comment/%d' % comment_id, data)
311+
312+
def comment_vote(self, comment_id, vote, toggle=True):
313+
self.logged_in()
314+
toggle_behavior = 1 if toggle else 0
315+
316+
return self.make_request('POST', 'comment/%d/vote/%s?toggle=%d' % (comment_id, vote, toggle_behavior))
317+
318+
def comment_report(self, comment_id):
319+
self.logged_in()
320+
return self.make_request('POST', 'comment/%d/report' % comment_id)

imgur-python/helpers/error.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
class ImgurClientError(Exception):
2+
def __init__(self, error_message, status_code=None):
3+
self.status_code = status_code
4+
self.error_message = error_message
5+
6+
def __str__(self):
7+
if self.status_code:
8+
return "(%s) %s" % (self.status_code, self.error_message)
9+
else:
10+
return self.error_message

imgur-python/helpers/format.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import math
2+
from imgur.models.comment import Comment
23

34

45
def center_pad(s, length):
@@ -11,4 +12,14 @@ def center_pad(s, length):
1112

1213
def two_column_with_period(left, right, length):
1314
num_periods = int(length - (len(left) + len(right) + 2))
14-
return left + ' ' + ('.' * num_periods) + ' ' + right
15+
return left + ' ' + ('.' * num_periods) + ' ' + right
16+
17+
18+
def build_comment_tree(children):
19+
children_objects = []
20+
for child in children:
21+
to_insert = Comment(child)
22+
to_insert.children = build_comment_tree(to_insert.children)
23+
children_objects.append(to_insert)
24+
25+
return children_objects

imgur-python/imgur/factory.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818

1919
class Factory:
20-
API_URL = "https://api.imgur.com/"
20+
API_URL = 'https://api.imgur.com/'
2121

2222
def __init__(self, config):
2323
self.config = config

imgur-python/imgur/models/__init__.py

Whitespace-only changes.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
class Account:
2+
3+
def __init__(self, id, url, bio, reputation, created, pro_expiration):
4+
self.id = id
5+
self.url = url
6+
self.bio = bio
7+
self.reputation = reputation
8+
self.created = created,
9+
self.pro_expiration = pro_expiration
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
class AccountSettings:
2+
3+
def __init__(self, email, high_quality, public_images, album_privacy, pro_expiration, accepted_gallery_terms,
4+
active_emails, messaging_enabled, blocked_users):
5+
self.email = email
6+
self.high_quality = high_quality
7+
self.public_images = public_images
8+
self.album_privacy = album_privacy
9+
self.pro_expiration = pro_expiration
10+
self.accepted_gallery_terms = accepted_gallery_terms
11+
self.active_emails = active_emails
12+
self.messaging_enabled = messaging_enabled
13+
self.blocked_users = blocked_users

0 commit comments

Comments
 (0)