3
# Copyright 2007 Google Inc. All Rights Reserved.
5
# Licensed under the Apache License, Version 2.0 (the "License");
6
# you may not use this file except in compliance with the License.
7
# You may obtain a copy of the License at
9
# http://www.apache.org/licenses/LICENSE-2.0
11
# Unless required by applicable law or agreed to in writing, software
12
# distributed under the License is distributed on an "AS IS" BASIS,
13
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
# See the License for the specific language governing permissions and
15
# limitations under the License.
17
'''A library that provides a python interface to the Twitter API'''
19
__author__ = 'dewitt@google.com'
20
__version__ = '0.7-devel'
40
from hashlib import md5
47
# A singleton representing a lazily instantiated FileCache.
48
DEFAULT_CACHE = object()
51
class TwitterError(Exception):
52
'''Base class for Twitter errors'''
56
'''Returns the first argument used to construct this error.'''
61
'''A class representing the Status structure used by the twitter API.
63
The Status structure exposes the following properties:
66
status.created_at_in_seconds # read only
68
status.in_reply_to_screen_name
69
status.in_reply_to_user_id
70
status.in_reply_to_status_id
76
status.relative_created_at # read only
86
in_reply_to_screen_name=None,
87
in_reply_to_user_id=None,
88
in_reply_to_status_id=None,
92
'''An object to hold a Twitter status message.
94
This class is normally instantiated by the twitter.Api class and
95
returned in a sequence.
97
Note: Dates are posted in the form "Sat Jan 27 04:17:38 +0000 2007"
100
created_at: The time this status message was posted
101
favorited: Whether this is a favorite of the authenticated user
102
id: The unique id of this status message
103
text: The text of this status message
104
location: the geolocation string associated with this message
106
A human readable string representing the posting time
108
A twitter.User instance representing the person posting the message
110
The current time, if the client choses to set it. Defaults to the
113
self.created_at = created_at
114
self.favorited = favorited
117
self.location = location
120
self.in_reply_to_screen_name = in_reply_to_screen_name
121
self.in_reply_to_user_id = in_reply_to_user_id
122
self.in_reply_to_status_id = in_reply_to_status_id
123
self.truncated = truncated
126
def GetCreatedAt(self):
127
'''Get the time this status message was posted.
130
The time this status message was posted
132
return self._created_at
134
def SetCreatedAt(self, created_at):
135
'''Set the time this status message was posted.
138
created_at: The time this status message was created
140
self._created_at = created_at
142
created_at = property(GetCreatedAt, SetCreatedAt,
143
doc='The time this status message was posted.')
145
def GetCreatedAtInSeconds(self):
146
'''Get the time this status message was posted, in seconds since the epoch.
149
The time this status message was posted, in seconds since the epoch.
151
return calendar.timegm(rfc822.parsedate(self.created_at))
153
created_at_in_seconds = property(GetCreatedAtInSeconds,
154
doc="The time this status message was "
155
"posted, in seconds since the epoch")
157
def GetFavorited(self):
158
'''Get the favorited setting of this status message.
161
True if this status message is favorited; False otherwise
163
return self._favorited
165
def SetFavorited(self, favorited):
166
'''Set the favorited state of this status message.
169
favorited: boolean True/False favorited state of this status message
171
self._favorited = favorited
173
favorited = property(GetFavorited, SetFavorited,
174
doc='The favorited state of this status message.')
177
'''Get the unique id of this status message.
180
The unique id of this status message
185
'''Set the unique id of this status message.
188
id: The unique id of this status message
192
id = property(GetId, SetId,
193
doc='The unique id of this status message.')
195
def GetInReplyToScreenName(self):
196
return self._in_reply_to_screen_name
198
def SetInReplyToScreenName(self, in_reply_to_screen_name):
199
self._in_reply_to_screen_name = in_reply_to_screen_name
201
in_reply_to_screen_name = property(GetInReplyToScreenName, SetInReplyToScreenName,
204
def GetInReplyToUserId(self):
205
return self._in_reply_to_user_id
207
def SetInReplyToUserId(self, in_reply_to_user_id):
208
self._in_reply_to_user_id = in_reply_to_user_id
210
in_reply_to_user_id = property(GetInReplyToUserId, SetInReplyToUserId,
213
def GetInReplyToStatusId(self):
214
return self._in_reply_to_status_id
216
def SetInReplyToStatusId(self, in_reply_to_status_id):
217
self._in_reply_to_status_id = in_reply_to_status_id
219
in_reply_to_status_id = property(GetInReplyToStatusId, SetInReplyToStatusId,
222
def GetTruncated(self):
223
return self._truncated
225
def SetTruncated(self, truncated):
226
self._truncated = truncated
228
truncated = property(GetTruncated, SetTruncated,
234
def SetSource(self, source):
235
self._source = source
237
source = property(GetSource, SetSource,
241
'''Get the text of this status message.
244
The text of this status message.
248
def SetText(self, text):
249
'''Set the text of this status message.
252
text: The text of this status message
256
text = property(GetText, SetText,
257
doc='The text of this status message')
259
def GetLocation(self):
260
'''Get the geolocation associated with this status message
263
The geolocation string of this status message.
265
return self._location
267
def SetLocation(self, location):
268
'''Set the geolocation associated with this status message
271
location: The geolocation string of this status message
273
self._location = location
275
location = property(GetLocation, SetLocation,
276
doc='The geolocation string of this status message')
278
def GetRelativeCreatedAt(self):
279
'''Get a human redable string representing the posting time
282
A human readable string representing the posting time
285
delta = long(self.now) - long(self.created_at_in_seconds)
287
if delta < (1 * fudge):
288
return 'about a second ago'
289
elif delta < (60 * (1/fudge)):
290
return 'about %d seconds ago' % (delta)
291
elif delta < (60 * fudge):
292
return 'about a minute ago'
293
elif delta < (60 * 60 * (1/fudge)):
294
return 'about %d minutes ago' % (delta / 60)
295
elif delta < (60 * 60 * fudge) or delta / (60 * 60) == 1:
296
return 'about an hour ago'
297
elif delta < (60 * 60 * 24 * (1/fudge)):
298
return 'about %d hours ago' % (delta / (60 * 60))
299
elif delta < (60 * 60 * 24 * fudge) or delta / (60 * 60 * 24) == 1:
300
return 'about a day ago'
302
return 'about %d days ago' % (delta / (60 * 60 * 24))
304
relative_created_at = property(GetRelativeCreatedAt,
305
doc='Get a human readable string representing'
309
'''Get a twitter.User reprenting the entity posting this status message.
312
A twitter.User reprenting the entity posting this status message
316
def SetUser(self, user):
317
'''Set a twitter.User reprenting the entity posting this status message.
320
user: A twitter.User reprenting the entity posting this status message
324
user = property(GetUser, SetUser,
325
doc='A twitter.User reprenting the entity posting this '
329
'''Get the wallclock time for this status message.
331
Used to calculate relative_created_at. Defaults to the time
332
the object was instantiated.
335
Whatever the status instance believes the current time to be,
336
in seconds since the epoch.
338
if self._now is None:
339
self._now = time.time()
342
def SetNow(self, now):
343
'''Set the wallclock time for this status message.
345
Used to calculate relative_created_at. Defaults to the time
346
the object was instantiated.
349
now: The wallclock time for this instance.
353
now = property(GetNow, SetNow,
354
doc='The wallclock time for this status instance.')
357
def __ne__(self, other):
358
return not self.__eq__(other)
360
def __eq__(self, other):
363
self.created_at == other.created_at and \
364
self.id == other.id and \
365
self.text == other.text and \
366
self.location == other.location and \
367
self.user == other.user and \
368
self.in_reply_to_screen_name == other.in_reply_to_screen_name and \
369
self.in_reply_to_user_id == other.in_reply_to_user_id and \
370
self.in_reply_to_status_id == other.in_reply_to_status_id and \
371
self.truncated == other.truncated and \
372
self.favorited == other.favorited and \
373
self.source == other.source
374
except AttributeError:
378
'''A string representation of this twitter.Status instance.
380
The return value is the same as the JSON string representation.
383
A string representation of this twitter.Status instance.
385
return self.AsJsonString()
387
def AsJsonString(self):
388
'''A JSON string representation of this twitter.Status instance.
391
A JSON string representation of this twitter.Status instance
393
return simplejson.dumps(self.AsDict(), sort_keys=True)
396
'''A dict representation of this twitter.Status instance.
398
The return value uses the same key names as the JSON representation.
401
A dict representing this twitter.Status instance
405
data['created_at'] = self.created_at
407
data['favorited'] = self.favorited
411
data['text'] = self.text
413
data['location'] = self.location
415
data['user'] = self.user.AsDict()
416
if self.in_reply_to_screen_name:
417
data['in_reply_to_screen_name'] = self.in_reply_to_screen_name
418
if self.in_reply_to_user_id:
419
data['in_reply_to_user_id'] = self.in_reply_to_user_id
420
if self.in_reply_to_status_id:
421
data['in_reply_to_status_id'] = self.in_reply_to_status_id
422
if self.truncated is not None:
423
data['truncated'] = self.truncated
424
if self.favorited is not None:
425
data['favorited'] = self.favorited
427
data['source'] = self.source
431
def NewFromJsonDict(data):
432
'''Create a new instance based on a JSON dict.
435
data: A JSON dict, as converted from the JSON in the twitter API
437
A twitter.Status instance
440
user = User.NewFromJsonDict(data['user'])
443
return Status(created_at=data.get('created_at', None),
444
favorited=data.get('favorited', None),
445
id=data.get('id', None),
446
text=data.get('text', None),
447
location=data.get('location', None),
448
in_reply_to_screen_name=data.get('in_reply_to_screen_name', None),
449
in_reply_to_user_id=data.get('in_reply_to_user_id', None),
450
in_reply_to_status_id=data.get('in_reply_to_status_id', None),
451
truncated=data.get('truncated', None),
452
source=data.get('source', None),
457
'''A class representing the User structure used by the twitter API.
459
The User structure exposes the following properties:
466
user.profile_image_url
467
user.profile_background_tile
468
user.profile_background_image_url
469
user.profile_sidebar_fill_color
470
user.profile_background_color
471
user.profile_link_color
472
user.profile_text_color
481
user.favourites_count
489
profile_image_url=None,
490
profile_background_tile=None,
491
profile_background_image_url=None,
492
profile_sidebar_fill_color=None,
493
profile_background_color=None,
494
profile_link_color=None,
495
profile_text_color=None,
499
followers_count=None,
502
favourites_count=None,
507
self.screen_name = screen_name
508
self.location = location
509
self.description = description
510
self.profile_image_url = profile_image_url
511
self.profile_background_tile = profile_background_tile
512
self.profile_background_image_url = profile_background_image_url
513
self.profile_sidebar_fill_color = profile_sidebar_fill_color
514
self.profile_background_color = profile_background_color
515
self.profile_link_color = profile_link_color
516
self.profile_text_color = profile_text_color
517
self.protected = protected
518
self.utc_offset = utc_offset
519
self.time_zone = time_zone
520
self.followers_count = followers_count
521
self.friends_count = friends_count
522
self.statuses_count = statuses_count
523
self.favourites_count = favourites_count
529
'''Get the unique id of this user.
532
The unique id of this user
537
'''Set the unique id of this user.
540
id: The unique id of this user.
544
id = property(GetId, SetId,
545
doc='The unique id of this user.')
548
'''Get the real name of this user.
551
The real name of this user
555
def SetName(self, name):
556
'''Set the real name of this user.
559
name: The real name of this user
563
name = property(GetName, SetName,
564
doc='The real name of this user.')
566
def GetScreenName(self):
567
'''Get the short username of this user.
570
The short username of this user
572
return self._screen_name
574
def SetScreenName(self, screen_name):
575
'''Set the short username of this user.
578
screen_name: the short username of this user
580
self._screen_name = screen_name
582
screen_name = property(GetScreenName, SetScreenName,
583
doc='The short username of this user.')
585
def GetLocation(self):
586
'''Get the geographic location of this user.
589
The geographic location of this user
591
return self._location
593
def SetLocation(self, location):
594
'''Set the geographic location of this user.
597
location: The geographic location of this user
599
self._location = location
601
location = property(GetLocation, SetLocation,
602
doc='The geographic location of this user.')
604
def GetDescription(self):
605
'''Get the short text description of this user.
608
The short text description of this user
610
return self._description
612
def SetDescription(self, description):
613
'''Set the short text description of this user.
616
description: The short text description of this user
618
self._description = description
620
description = property(GetDescription, SetDescription,
621
doc='The short text description of this user.')
624
'''Get the homepage url of this user.
627
The homepage url of this user
631
def SetUrl(self, url):
632
'''Set the homepage url of this user.
635
url: The homepage url of this user
639
url = property(GetUrl, SetUrl,
640
doc='The homepage url of this user.')
642
def GetProfileImageUrl(self):
643
'''Get the url of the thumbnail of this user.
646
The url of the thumbnail of this user
648
return self._profile_image_url
650
def SetProfileImageUrl(self, profile_image_url):
651
'''Set the url of the thumbnail of this user.
654
profile_image_url: The url of the thumbnail of this user
656
self._profile_image_url = profile_image_url
658
profile_image_url= property(GetProfileImageUrl, SetProfileImageUrl,
659
doc='The url of the thumbnail of this user.')
661
def GetProfileBackgroundTile(self):
662
'''Boolean for whether to tile the profile background image.
665
True if the background is to be tiled, False if not, None if unset.
667
return self._profile_background_tile
669
def SetProfileBackgroundTile(self, profile_background_tile):
670
'''Set the boolean flag for whether to tile the profile background image.
673
profile_background_tile: Boolean flag for whether to tile or not.
675
self._profile_background_tile = profile_background_tile
677
profile_background_tile = property(GetProfileBackgroundTile, SetProfileBackgroundTile,
678
doc='Boolean for whether to tile the background image.')
680
def GetProfileBackgroundImageUrl(self):
681
return self._profile_background_image_url
683
def SetProfileBackgroundImageUrl(self, profile_background_image_url):
684
self._profile_background_image_url = profile_background_image_url
686
profile_background_image_url = property(GetProfileBackgroundImageUrl, SetProfileBackgroundImageUrl,
687
doc='The url of the profile background of this user.')
689
def GetProfileSidebarFillColor(self):
690
return self._profile_sidebar_fill_color
692
def SetProfileSidebarFillColor(self, profile_sidebar_fill_color):
693
self._profile_sidebar_fill_color = profile_sidebar_fill_color
695
profile_sidebar_fill_color = property(GetProfileSidebarFillColor, SetProfileSidebarFillColor)
697
def GetProfileBackgroundColor(self):
698
return self._profile_background_color
700
def SetProfileBackgroundColor(self, profile_background_color):
701
self._profile_background_color = profile_background_color
703
profile_background_color = property(GetProfileBackgroundColor, SetProfileBackgroundColor)
705
def GetProfileLinkColor(self):
706
return self._profile_link_color
708
def SetProfileLinkColor(self, profile_link_color):
709
self._profile_link_color = profile_link_color
711
profile_link_color = property(GetProfileLinkColor, SetProfileLinkColor)
713
def GetProfileTextColor(self):
714
return self._profile_text_color
716
def SetProfileTextColor(self, profile_text_color):
717
self._profile_text_color = profile_text_color
719
profile_text_color = property(GetProfileTextColor, SetProfileTextColor)
721
def GetProtected(self):
722
return self._protected
724
def SetProtected(self, protected):
725
self._protected = protected
727
protected = property(GetProtected, SetProtected)
729
def GetUtcOffset(self):
730
return self._utc_offset
732
def SetUtcOffset(self, utc_offset):
733
self._utc_offset = utc_offset
735
utc_offset = property(GetUtcOffset, SetUtcOffset)
737
def GetTimeZone(self):
738
'''Returns the current time zone string for the user.
741
The descriptive time zone string for the user.
743
return self._time_zone
745
def SetTimeZone(self, time_zone):
746
'''Sets the user's time zone string.
749
time_zone: The descriptive time zone to assign for the user.
751
self._time_zone = time_zone
753
time_zone = property(GetTimeZone, SetTimeZone)
756
'''Get the latest twitter.Status of this user.
759
The latest twitter.Status of this user
763
def SetStatus(self, status):
764
'''Set the latest twitter.Status of this user.
767
status: The latest twitter.Status of this user
769
self._status = status
771
status = property(GetStatus, SetStatus,
772
doc='The latest twitter.Status of this user.')
774
def GetFriendsCount(self):
775
'''Get the friend count for this user.
778
The number of users this user has befriended.
780
return self._friends_count
782
def SetFriendsCount(self, count):
783
'''Set the friend count for this user.
786
count: The number of users this user has befriended.
788
self._friends_count = count
790
friends_count = property(GetFriendsCount, SetFriendsCount,
791
doc='The number of friends for this user.')
793
def GetFollowersCount(self):
794
'''Get the follower count for this user.
797
The number of users following this user.
799
return self._followers_count
801
def SetFollowersCount(self, count):
802
'''Set the follower count for this user.
805
count: The number of users following this user.
807
self._followers_count = count
809
followers_count = property(GetFollowersCount, SetFollowersCount,
810
doc='The number of users following this user.')
812
def GetStatusesCount(self):
813
'''Get the number of status updates for this user.
816
The number of status updates for this user.
818
return self._statuses_count
820
def SetStatusesCount(self, count):
821
'''Set the status update count for this user.
824
count: The number of updates for this user.
826
self._statuses_count = count
828
statuses_count = property(GetStatusesCount, SetStatusesCount,
829
doc='The number of updates for this user.')
831
def GetFavouritesCount(self):
832
'''Get the number of favourites for this user.
835
The number of favourites for this user.
837
return self._favourites_count
839
def SetFavouritesCount(self, count):
840
'''Set the favourite count for this user.
843
count: The number of favourites for this user.
845
self._favourites_count = count
847
favourites_count = property(GetFavouritesCount, SetFavouritesCount,
848
doc='The number of favourites for this user.')
850
def __ne__(self, other):
851
return not self.__eq__(other)
853
def __eq__(self, other):
856
self.id == other.id and \
857
self.name == other.name and \
858
self.screen_name == other.screen_name and \
859
self.location == other.location and \
860
self.description == other.description and \
861
self.profile_image_url == other.profile_image_url and \
862
self.profile_background_tile == other.profile_background_tile and \
863
self.profile_background_image_url == other.profile_background_image_url and \
864
self.profile_sidebar_fill_color == other.profile_sidebar_fill_color and \
865
self.profile_background_color == other.profile_background_color and \
866
self.profile_link_color == other.profile_link_color and \
867
self.profile_text_color == other.profile_text_color and \
868
self.protected == other.protected and \
869
self.utc_offset == other.utc_offset and \
870
self.time_zone == other.time_zone and \
871
self.url == other.url and \
872
self.statuses_count == other.statuses_count and \
873
self.followers_count == other.followers_count and \
874
self.favourites_count == other.favourites_count and \
875
self.friends_count == other.friends_count and \
876
self.status == other.status
877
except AttributeError:
881
'''A string representation of this twitter.User instance.
883
The return value is the same as the JSON string representation.
886
A string representation of this twitter.User instance.
888
return self.AsJsonString()
890
def AsJsonString(self):
891
'''A JSON string representation of this twitter.User instance.
894
A JSON string representation of this twitter.User instance
896
return simplejson.dumps(self.AsDict(), sort_keys=True)
899
'''A dict representation of this twitter.User instance.
901
The return value uses the same key names as the JSON representation.
904
A dict representing this twitter.User instance
910
data['name'] = self.name
912
data['screen_name'] = self.screen_name
914
data['location'] = self.location
916
data['description'] = self.description
917
if self.profile_image_url:
918
data['profile_image_url'] = self.profile_image_url
919
if self.profile_background_tile is not None:
920
data['profile_background_tile'] = self.profile_background_tile
921
if self.profile_background_image_url:
922
data['profile_sidebar_fill_color'] = self.profile_background_image_url
923
if self.profile_background_color:
924
data['profile_background_color'] = self.profile_background_color
925
if self.profile_link_color:
926
data['profile_link_color'] = self.profile_link_color
927
if self.profile_text_color:
928
data['profile_text_color'] = self.profile_text_color
929
if self.protected is not None:
930
data['protected'] = self.protected
932
data['utc_offset'] = self.utc_offset
934
data['time_zone'] = self.time_zone
936
data['url'] = self.url
938
data['status'] = self.status.AsDict()
939
if self.friends_count:
940
data['friends_count'] = self.friends_count
941
if self.followers_count:
942
data['followers_count'] = self.followers_count
943
if self.statuses_count:
944
data['statuses_count'] = self.statuses_count
945
if self.favourites_count:
946
data['favourites_count'] = self.favourites_count
950
def NewFromJsonDict(data):
951
'''Create a new instance based on a JSON dict.
954
data: A JSON dict, as converted from the JSON in the twitter API
956
A twitter.User instance
959
status = Status.NewFromJsonDict(data['status'])
962
return User(id=data.get('id', None),
963
name=data.get('name', None),
964
screen_name=data.get('screen_name', None),
965
location=data.get('location', None),
966
description=data.get('description', None),
967
statuses_count=data.get('statuses_count', None),
968
followers_count=data.get('followers_count', None),
969
favourites_count=data.get('favourites_count', None),
970
friends_count=data.get('friends_count', None),
971
profile_image_url=data.get('profile_image_url', None),
972
profile_background_tile = data.get('profile_background_tile', None),
973
profile_background_image_url = data.get('profile_background_image_url', None),
974
profile_sidebar_fill_color = data.get('profile_sidebar_fill_color', None),
975
profile_background_color = data.get('profile_background_color', None),
976
profile_link_color = data.get('profile_link_color', None),
977
profile_text_color = data.get('profile_text_color', None),
978
protected = data.get('protected', None),
979
utc_offset = data.get('utc_offset', None),
980
time_zone = data.get('time_zone', None),
981
url=data.get('url', None),
984
class DirectMessage(object):
985
'''A class representing the DirectMessage structure used by the twitter API.
987
The DirectMessage structure exposes the following properties:
990
direct_message.created_at
991
direct_message.created_at_in_seconds # read only
992
direct_message.sender_id
993
direct_message.sender_screen_name
994
direct_message.recipient_id
995
direct_message.recipient_screen_name
1003
sender_screen_name=None,
1005
recipient_screen_name=None,
1007
'''An object to hold a Twitter direct message.
1009
This class is normally instantiated by the twitter.Api class and
1010
returned in a sequence.
1012
Note: Dates are posted in the form "Sat Jan 27 04:17:38 +0000 2007"
1015
id: The unique id of this direct message
1016
created_at: The time this direct message was posted
1017
sender_id: The id of the twitter user that sent this message
1018
sender_screen_name: The name of the twitter user that sent this message
1019
recipient_id: The id of the twitter that received this message
1020
recipient_screen_name: The name of the twitter that received this message
1021
text: The text of this direct message
1024
self.created_at = created_at
1025
self.sender_id = sender_id
1026
self.sender_screen_name = sender_screen_name
1027
self.recipient_id = recipient_id
1028
self.recipient_screen_name = recipient_screen_name
1032
'''Get the unique id of this direct message.
1035
The unique id of this direct message
1039
def SetId(self, id):
1040
'''Set the unique id of this direct message.
1043
id: The unique id of this direct message
1047
id = property(GetId, SetId,
1048
doc='The unique id of this direct message.')
1050
def GetCreatedAt(self):
1051
'''Get the time this direct message was posted.
1054
The time this direct message was posted
1056
return self._created_at
1058
def SetCreatedAt(self, created_at):
1059
'''Set the time this direct message was posted.
1062
created_at: The time this direct message was created
1064
self._created_at = created_at
1066
created_at = property(GetCreatedAt, SetCreatedAt,
1067
doc='The time this direct message was posted.')
1069
def GetCreatedAtInSeconds(self):
1070
'''Get the time this direct message was posted, in seconds since the epoch.
1073
The time this direct message was posted, in seconds since the epoch.
1075
return calendar.timegm(rfc822.parsedate(self.created_at))
1077
created_at_in_seconds = property(GetCreatedAtInSeconds,
1078
doc="The time this direct message was "
1079
"posted, in seconds since the epoch")
1081
def GetSenderId(self):
1082
'''Get the unique sender id of this direct message.
1085
The unique sender id of this direct message
1087
return self._sender_id
1089
def SetSenderId(self, sender_id):
1090
'''Set the unique sender id of this direct message.
1093
sender id: The unique sender id of this direct message
1095
self._sender_id = sender_id
1097
sender_id = property(GetSenderId, SetSenderId,
1098
doc='The unique sender id of this direct message.')
1100
def GetSenderScreenName(self):
1101
'''Get the unique sender screen name of this direct message.
1104
The unique sender screen name of this direct message
1106
return self._sender_screen_name
1108
def SetSenderScreenName(self, sender_screen_name):
1109
'''Set the unique sender screen name of this direct message.
1112
sender_screen_name: The unique sender screen name of this direct message
1114
self._sender_screen_name = sender_screen_name
1116
sender_screen_name = property(GetSenderScreenName, SetSenderScreenName,
1117
doc='The unique sender screen name of this direct message.')
1119
def GetRecipientId(self):
1120
'''Get the unique recipient id of this direct message.
1123
The unique recipient id of this direct message
1125
return self._recipient_id
1127
def SetRecipientId(self, recipient_id):
1128
'''Set the unique recipient id of this direct message.
1131
recipient id: The unique recipient id of this direct message
1133
self._recipient_id = recipient_id
1135
recipient_id = property(GetRecipientId, SetRecipientId,
1136
doc='The unique recipient id of this direct message.')
1138
def GetRecipientScreenName(self):
1139
'''Get the unique recipient screen name of this direct message.
1142
The unique recipient screen name of this direct message
1144
return self._recipient_screen_name
1146
def SetRecipientScreenName(self, recipient_screen_name):
1147
'''Set the unique recipient screen name of this direct message.
1150
recipient_screen_name: The unique recipient screen name of this direct message
1152
self._recipient_screen_name = recipient_screen_name
1154
recipient_screen_name = property(GetRecipientScreenName, SetRecipientScreenName,
1155
doc='The unique recipient screen name of this direct message.')
1158
'''Get the text of this direct message.
1161
The text of this direct message.
1165
def SetText(self, text):
1166
'''Set the text of this direct message.
1169
text: The text of this direct message
1173
text = property(GetText, SetText,
1174
doc='The text of this direct message')
1176
def __ne__(self, other):
1177
return not self.__eq__(other)
1179
def __eq__(self, other):
1182
self.id == other.id and \
1183
self.created_at == other.created_at and \
1184
self.sender_id == other.sender_id and \
1185
self.sender_screen_name == other.sender_screen_name and \
1186
self.recipient_id == other.recipient_id and \
1187
self.recipient_screen_name == other.recipient_screen_name and \
1188
self.text == other.text
1189
except AttributeError:
1193
'''A string representation of this twitter.DirectMessage instance.
1195
The return value is the same as the JSON string representation.
1198
A string representation of this twitter.DirectMessage instance.
1200
return self.AsJsonString()
1202
def AsJsonString(self):
1203
'''A JSON string representation of this twitter.DirectMessage instance.
1206
A JSON string representation of this twitter.DirectMessage instance
1208
return simplejson.dumps(self.AsDict(), sort_keys=True)
1211
'''A dict representation of this twitter.DirectMessage instance.
1213
The return value uses the same key names as the JSON representation.
1216
A dict representing this twitter.DirectMessage instance
1220
data['id'] = self.id
1222
data['created_at'] = self.created_at
1224
data['sender_id'] = self.sender_id
1225
if self.sender_screen_name:
1226
data['sender_screen_name'] = self.sender_screen_name
1227
if self.recipient_id:
1228
data['recipient_id'] = self.recipient_id
1229
if self.recipient_screen_name:
1230
data['recipient_screen_name'] = self.recipient_screen_name
1232
data['text'] = self.text
1236
def NewFromJsonDict(data):
1237
'''Create a new instance based on a JSON dict.
1240
data: A JSON dict, as converted from the JSON in the twitter API
1242
A twitter.DirectMessage instance
1244
return DirectMessage(created_at=data.get('created_at', None),
1245
recipient_id=data.get('recipient_id', None),
1246
sender_id=data.get('sender_id', None),
1247
text=data.get('text', None),
1248
sender_screen_name=data.get('sender_screen_name', None),
1249
id=data.get('id', None),
1250
recipient_screen_name=data.get('recipient_screen_name', None))
1253
'''A python interface into the Twitter API
1255
By default, the Api caches results for 1 minute.
1259
To create an instance of the twitter.Api class, with no authentication:
1262
>>> api = twitter.Api()
1264
To fetch the most recently posted public twitter status messages:
1266
>>> statuses = api.GetPublicTimeline()
1267
>>> print [s.user.name for s in statuses]
1268
[u'DeWitt', u'Kesuke Miyagi', u'ev', u'Buzz Andersen', u'Biz Stone'] #...
1270
To fetch a single user's public status messages, where "user" is either
1271
a Twitter "short name" or their user id.
1273
>>> statuses = api.GetUserTimeline(user)
1274
>>> print [s.text for s in statuses]
1276
To use authentication, instantiate the twitter.Api class with a
1277
username and password:
1279
>>> api = twitter.Api(username='twitter user', password='twitter pass')
1281
To fetch your friends (after being authenticated):
1283
>>> users = api.GetFriends()
1284
>>> print [u.name for u in users]
1286
To post a twitter status message (after being authenticated):
1288
>>> status = api.PostUpdate('I love python-twitter!')
1289
>>> print status.text
1290
I love python-twitter!
1292
There are many other methods, including:
1294
>>> api.PostUpdates(status)
1295
>>> api.PostDirectMessage(user, text)
1296
>>> api.GetUser(user)
1297
>>> api.GetReplies()
1298
>>> api.GetUserTimeline(user)
1299
>>> api.GetStatus(id)
1300
>>> api.DestroyStatus(id)
1301
>>> api.GetFriendsTimeline(user)
1302
>>> api.GetFriends(user)
1303
>>> api.GetFollowers()
1304
>>> api.GetFeatured()
1305
>>> api.GetDirectMessages()
1306
>>> api.PostDirectMessage(user, text)
1307
>>> api.DestroyDirectMessage(id)
1308
>>> api.DestroyFriendship(user)
1309
>>> api.CreateFriendship(user)
1310
>>> api.GetUserByEmail(email)
1311
>>> api.VerifyCredentials()
1314
DEFAULT_CACHE_TIMEOUT = 60 # cache for 1 minute
1316
_API_REALM = 'Twitter API'
1321
input_encoding=None,
1322
request_headers=None,
1323
cache=DEFAULT_CACHE,
1326
use_gzip_compression=False):
1327
'''Instantiate a new twitter.Api object.
1331
The username of the twitter account. [optional]
1333
The password for the twitter account. [optional]
1335
The encoding used to encode input strings. [optional]
1337
A dictionary of additional HTTP request headers. [optional]
1339
The cache instance to use. Defaults to DEFAULT_CACHE.
1340
Use None to disable caching. [optional]
1342
The shortner instance to use. Defaults to None.
1343
See shorten_url.py for an example shortner. [optional]
1345
The base URL to use to contact the Twitter API.
1346
Defaults to https://twitter.com. [optional]
1347
use_gzip_compression:
1348
Set to True to tell enable gzip compression for any call
1349
made to Twitter. Defaults to False. [optional]
1351
self.SetCache(cache)
1352
self._urllib = urllib2
1353
self._cache_timeout = Api.DEFAULT_CACHE_TIMEOUT
1354
self._InitializeRequestHeaders(request_headers)
1355
self._InitializeUserAgent()
1356
self._InitializeDefaultParameters()
1357
self._input_encoding = input_encoding
1358
self._use_gzip = use_gzip_compression
1359
self.SetCredentials(username, password)
1360
if base_url is None:
1361
self.base_url = 'https://twitter.com'
1363
self.base_url = base_url
1365
def GetPublicTimeline(self,
1367
'''Fetch the sequnce of public twitter.Status message for all users.
1371
Returns only public statuses with an ID greater than
1372
(that is, more recent than) the specified ID. [optional]
1375
An sequence of twitter.Status instances, one for each message
1380
parameters['since_id'] = since_id
1382
url = '%s/statuses/public_timeline.json' % self.base_url
1383
json = self._FetchUrl(url, parameters=parameters)
1384
data = simplejson.loads(json)
1386
self._CheckForTwitterError(data)
1388
return [Status.NewFromJsonDict(x) for x in data]
1390
def FilterPublicTimeline(self,
1393
'''Filter the public twitter timeline by a given search term on
1400
Returns only public statuses with an ID greater than
1401
(that is, more recent than) the specified ID. [optional]
1404
A sequence of twitter.Status instances, one for each message
1407
statuses = self.GetPublicTimeline(since_id)
1411
if s.text.lower().find(term.lower()) != -1:
1425
'''Return twitter search results for a given term.
1431
Returns only public statuses with an ID greater than
1432
(that is, more recent than) the specified ID. [optional]
1434
geolocation information in the form (latitude, longitude, radius)
1437
number of results to return. Default is 15 [optional]
1439
which page of search results to return
1441
language for results. Default is English [optional]
1443
prefixes screen name in status
1445
If set to False, then all users only have screen_name and
1446
profile_image_url available.
1447
If set to True, all information of users are available,
1448
but it uses lots of request quota, one per status.
1450
A sequence of twitter.Status instances, one for each message containing
1453
# Build request parameters
1457
parameters['since_id'] = since_id
1462
parameters['q'] = urllib.quote_plus(term)
1463
parameters['show_user'] = show_user
1464
parameters['lang'] = lang
1465
parameters['rpp'] = per_page
1466
parameters['page'] = page
1468
if geocode is not None:
1469
parameters['geocode'] = ','.join(map(str, geocode))
1471
# Make and send requests
1472
url = 'http://search.twitter.com/search.json'
1473
json = self._FetchUrl(url, parameters=parameters)
1474
data = simplejson.loads(json)
1476
self._CheckForTwitterError(data)
1480
for x in data['results']:
1481
temp = Status.NewFromJsonDict(x)
1484
# Build user object with new request
1485
temp.user = self.GetUser(urllib.quote(x['from_user']))
1487
temp.user = User(screen_name=x['from_user'], profile_image_url=x['profile_image_url'])
1489
results.append(temp)
1491
# Return built list of statuses
1492
return results # [Status.NewFromJsonDict(x) for x in data['results']]
1494
def GetFriendsTimeline(self,
1499
'''Fetch the sequence of twitter.Status messages for a user's friends
1501
The twitter.Api instance must be authenticated if the user is private.
1505
Specifies the ID or screen name of the user for whom to return
1506
the friends_timeline. If unspecified, the username and password
1507
must be set in the twitter.Api instance. [Optional]
1509
Specifies the number of statuses to retrieve. May not be
1510
greater than 200. [Optional]
1512
Narrows the returned results to just those statuses created
1513
after the specified HTTP-formatted date. [Optional]
1515
Returns only public statuses with an ID greater than (that is,
1516
more recent than) the specified ID. [Optional]
1519
A sequence of twitter.Status instances, one for each message
1521
if not user and not self._username:
1522
raise TwitterError("User must be specified if API is not authenticated.")
1524
url = '%s/statuses/friends_timeline/%s.json' % (self.base_url, user)
1526
url = '%s/statuses/friends_timeline.json' % self.base_url
1528
if count is not None:
1530
if int(count) > 200:
1531
raise TwitterError("'count' may not be greater than 200")
1533
raise TwitterError("'count' must be an integer")
1534
parameters['count'] = count
1536
parameters['since'] = since
1538
parameters['since_id'] = since_id
1539
json = self._FetchUrl(url, parameters=parameters)
1540
data = simplejson.loads(json)
1541
self._CheckForTwitterError(data)
1542
return [Status.NewFromJsonDict(x) for x in data]
1544
def GetUserTimeline(self,
1552
'''Fetch the sequence of public Status messages for a single user.
1554
The twitter.Api instance must be authenticated if the user is private.
1558
Specifies the ID or screen name of the user for whom to return
1559
the user_timeline. [optional]
1561
Specfies the ID of the user for whom to return the
1562
user_timeline. Helpful for disambiguating when a valid user ID
1563
is also a valid screen name. [optional]
1565
Specfies the screen name of the user for whom to return the
1566
user_timeline. Helpful for disambiguating when a valid screen
1567
name is also a user ID. [optional]
1569
Returns only public statuses with an ID greater than (that is,
1570
more recent than) the specified ID. [optional]
1572
Returns only statuses with an ID less than (that is, older
1573
than) or equal to the specified ID. [optional]
1575
Specifies the number of statuses to retrieve. May not be
1576
greater than 200. [optional]
1578
Specifies the page of results to retrieve. Note: there are
1579
pagination limits. [optional]
1582
A sequence of Status instances, one for each message up to count
1587
url = '%s/statuses/user_timeline/%s.json' % (self.base_url, id)
1589
url = '%s/statuses/user_timeline.json?user_id=%d' % (self.base_url, user_id)
1591
url = ('%s/statuses/user_timeline.json?screen_name=%s' % (self.base_url,
1593
elif not self._username:
1594
raise TwitterError("User must be specified if API is not authenticated.")
1596
url = '%s/statuses/user_timeline.json' % self.base_url
1600
parameters['since_id'] = long(since_id)
1602
raise TwitterError("since_id must be an integer")
1606
parameters['max_id'] = long(max_id)
1608
raise TwitterError("max_id must be an integer")
1612
parameters['count'] = int(count)
1614
raise TwitterError("count must be an integer")
1618
parameters['page'] = int(page)
1620
raise TwitterError("page must be an integer")
1622
json = self._FetchUrl(url, parameters=parameters)
1623
data = simplejson.loads(json)
1624
self._CheckForTwitterError(data)
1625
return [Status.NewFromJsonDict(x) for x in data]
1627
def GetStatus(self, id):
1628
'''Returns a single status message.
1630
The twitter.Api instance must be authenticated if the status message is private.
1633
id: The numerical ID of the status you're trying to retrieve.
1636
A twitter.Status instance representing that status message
1642
raise TwitterError("id must be an long integer")
1643
url = '%s/statuses/show/%s.json' % (self.base_url, id)
1644
json = self._FetchUrl(url)
1645
data = simplejson.loads(json)
1646
self._CheckForTwitterError(data)
1647
return Status.NewFromJsonDict(data)
1649
def DestroyStatus(self, id):
1650
'''Destroys the status specified by the required ID parameter.
1652
The twitter.Api instance must be authenticated and thee
1653
authenticating user must be the author of the specified status.
1656
id: The numerical ID of the status you're trying to destroy.
1659
A twitter.Status instance representing the destroyed status message
1665
raise TwitterError("id must be an integer")
1666
url = '%s/statuses/destroy/%s.json' % (self.base_url, id)
1667
json = self._FetchUrl(url, post_data={})
1668
data = simplejson.loads(json)
1669
self._CheckForTwitterError(data)
1670
return Status.NewFromJsonDict(data)
1672
def PostUpdate(self, status, in_reply_to_status_id=None):
1673
'''Post a twitter status message from the authenticated user.
1675
The twitter.Api instance must be authenticated.
1679
The message text to be posted. Must be less than or equal to
1681
in_reply_to_status_id:
1682
The ID of an existing status that the status to be posted is
1683
in reply to. This implicitly sets the in_reply_to_user_id
1684
attribute of the resulting status to the user ID of the
1685
message being replied to. Invalid/missing status IDs will be
1688
A twitter.Status instance representing the message posted.
1690
if not self._username:
1691
raise TwitterError("The twitter.Api instance must be authenticated.")
1693
url = '%s/statuses/update.json' % self.base_url
1695
if len(status) > CHARACTER_LIMIT:
1696
raise TwitterError("Text must be less than or equal to %d characters. "
1697
"Consider using PostUpdates." % CHARACTER_LIMIT)
1699
data = {'status': status}
1700
if in_reply_to_status_id:
1701
data['in_reply_to_status_id'] = in_reply_to_status_id
1702
json = self._FetchUrl(url, post_data=data)
1703
data = simplejson.loads(json)
1704
self._CheckForTwitterError(data)
1705
return Status.NewFromJsonDict(data)
1707
def PostUpdates(self, status, continuation=None, **kwargs):
1708
'''Post one or more twitter status messages from the authenticated user.
1710
Unlike api.PostUpdate, this method will post multiple status updates
1711
if the message is longer than 140 characters.
1713
The twitter.Api instance must be authenticated.
1717
The message text to be posted. May be longer than 140 characters.
1719
The character string, if any, to be appended to all but the
1720
last message. Note that Twitter strips trailing '...' strings
1721
from messages. Consider using the unicode \u2026 character
1722
(horizontal ellipsis) instead. [Defaults to None]
1724
See api.PostUpdate for a list of accepted parameters.
1726
A of list twitter.Status instance representing the messages posted.
1729
if continuation is None:
1731
line_length = CHARACTER_LIMIT - len(continuation)
1732
lines = textwrap.wrap(status, line_length)
1733
for line in lines[0:-1]:
1734
results.append(self.PostUpdate(line + continuation, **kwargs))
1735
results.append(self.PostUpdate(lines[-1], **kwargs))
1738
def GetReplies(self, since=None, since_id=None, page=None):
1739
'''Get a sequence of status messages representing the 20 most recent
1740
replies (status updates prefixed with @username) to the authenticating
1746
Narrows the returned results to just those statuses created
1747
after the specified HTTP-formatted date. [optional]
1749
Returns only public statuses with an ID greater than (that is,
1750
more recent than) the specified ID. [Optional]
1753
A sequence of twitter.Status instances, one for each reply to the user.
1755
url = '%s/statuses/replies.json' % self.base_url
1756
if not self._username:
1757
raise TwitterError("The twitter.Api instance must be authenticated.")
1760
parameters['since'] = since
1762
parameters['since_id'] = since_id
1764
parameters['page'] = page
1765
json = self._FetchUrl(url, parameters=parameters)
1766
data = simplejson.loads(json)
1767
self._CheckForTwitterError(data)
1768
return [Status.NewFromJsonDict(x) for x in data]
1770
def GetFriends(self, user=None, page=None):
1771
'''Fetch the sequence of twitter.User instances, one for each friend.
1774
user: the username or id of the user whose friends you are fetching. If
1775
not specified, defaults to the authenticated user. [optional]
1777
The twitter.Api instance must be authenticated.
1780
A sequence of twitter.User instances, one for each friend
1782
if not user and not self._username:
1783
raise TwitterError("twitter.Api instance must be authenticated")
1785
url = '%s/statuses/friends/%s.json' % (self.base_url, user)
1787
url = '%s/statuses/friends.json' % self.base_url
1790
parameters['page'] = page
1791
json = self._FetchUrl(url, parameters=parameters)
1792
data = simplejson.loads(json)
1793
self._CheckForTwitterError(data)
1794
return [User.NewFromJsonDict(x) for x in data]
1796
def GetFriendIDs(self, user=None, page=None):
1797
'''Returns a list of twitter user id's for every person
1798
the specified user is following.
1802
The id or screen_name of the user to retrieve the id list for
1805
Specifies the page number of the results beginning at 1.
1806
A single page contains 5000 ids. This is recommended for users
1807
with large id lists. If not provided all id's are returned.
1808
(Please note that the result set isn't guaranteed to be 5000
1809
every time as suspended users will be filtered.)
1813
A list of integers, one for each user id.
1815
if not user and not self._username:
1816
raise TwitterError("twitter.Api instance must be authenticated")
1818
url = '%s/friends/ids/%s.json' % (self.base_url, user)
1820
url = '%s/friends/ids.json' % self.base_url
1823
parameters['page'] = page
1824
json = self._FetchUrl(url, parameters=parameters)
1825
data = simplejson.loads(json)
1826
self._CheckForTwitterError(data)
1829
def GetFollowers(self, page=None):
1830
'''Fetch the sequence of twitter.User instances, one for each follower
1832
The twitter.Api instance must be authenticated.
1835
A sequence of twitter.User instances, one for each follower
1837
if not self._username:
1838
raise TwitterError("twitter.Api instance must be authenticated")
1839
url = '%s/statuses/followers.json' % self.base_url
1842
parameters['page'] = page
1843
json = self._FetchUrl(url, parameters=parameters)
1844
data = simplejson.loads(json)
1845
self._CheckForTwitterError(data)
1846
return [User.NewFromJsonDict(x) for x in data]
1848
def GetFeatured(self):
1849
'''Fetch the sequence of twitter.User instances featured on twitter.com
1851
The twitter.Api instance must be authenticated.
1854
A sequence of twitter.User instances
1856
url = '%s/statuses/featured.json' % self.base_url
1857
json = self._FetchUrl(url)
1858
data = simplejson.loads(json)
1859
self._CheckForTwitterError(data)
1860
return [User.NewFromJsonDict(x) for x in data]
1862
def GetUser(self, user):
1863
'''Returns a single user.
1865
The twitter.Api instance must be authenticated.
1868
user: The username or id of the user to retrieve.
1871
A twitter.User instance representing that user
1873
url = '%s/users/show/%s.json' % (self.base_url, user)
1874
json = self._FetchUrl(url)
1875
data = simplejson.loads(json)
1876
self._CheckForTwitterError(data)
1877
return User.NewFromJsonDict(data)
1879
def GetDirectMessages(self, since=None, since_id=None, page=None):
1880
'''Returns a list of the direct messages sent to the authenticating user.
1882
The twitter.Api instance must be authenticated.
1886
Narrows the returned results to just those statuses created
1887
after the specified HTTP-formatted date. [optional]
1889
Returns only public statuses with an ID greater than (that is,
1890
more recent than) the specified ID. [Optional]
1893
A sequence of twitter.DirectMessage instances
1895
url = '%s/direct_messages.json' % self.base_url
1896
if not self._username:
1897
raise TwitterError("The twitter.Api instance must be authenticated.")
1900
parameters['since'] = since
1902
parameters['since_id'] = since_id
1904
parameters['page'] = page
1905
json = self._FetchUrl(url, parameters=parameters)
1906
data = simplejson.loads(json)
1907
self._CheckForTwitterError(data)
1908
return [DirectMessage.NewFromJsonDict(x) for x in data]
1910
def PostDirectMessage(self, user, text):
1911
'''Post a twitter direct message from the authenticated user
1913
The twitter.Api instance must be authenticated.
1916
user: The ID or screen name of the recipient user.
1917
text: The message text to be posted. Must be less than 140 characters.
1920
A twitter.DirectMessage instance representing the message posted
1922
if not self._username:
1923
raise TwitterError("The twitter.Api instance must be authenticated.")
1924
url = '%s/direct_messages/new.json' % self.base_url
1925
data = {'text': text, 'user': user}
1926
json = self._FetchUrl(url, post_data=data)
1927
data = simplejson.loads(json)
1928
self._CheckForTwitterError(data)
1929
return DirectMessage.NewFromJsonDict(data)
1931
def DestroyDirectMessage(self, id):
1932
'''Destroys the direct message specified in the required ID parameter.
1934
The twitter.Api instance must be authenticated, and the
1935
authenticating user must be the recipient of the specified direct
1939
id: The id of the direct message to be destroyed
1942
A twitter.DirectMessage instance representing the message destroyed
1944
url = '%s/direct_messages/destroy/%s.json' % (self.base_url, id)
1945
json = self._FetchUrl(url, post_data={})
1946
data = simplejson.loads(json)
1947
self._CheckForTwitterError(data)
1948
return DirectMessage.NewFromJsonDict(data)
1950
def CreateFriendship(self, user):
1951
'''Befriends the user specified in the user parameter as the authenticating user.
1953
The twitter.Api instance must be authenticated.
1956
The ID or screen name of the user to befriend.
1958
A twitter.User instance representing the befriended user.
1960
url = '%s/friendships/create/%s.json' % (self.base_url, user)
1961
json = self._FetchUrl(url, post_data={})
1962
data = simplejson.loads(json)
1963
self._CheckForTwitterError(data)
1964
return User.NewFromJsonDict(data)
1966
def DestroyFriendship(self, user):
1967
'''Discontinues friendship with the user specified in the user parameter.
1969
The twitter.Api instance must be authenticated.
1972
The ID or screen name of the user with whom to discontinue friendship.
1974
A twitter.User instance representing the discontinued friend.
1976
url = '%s/friendships/destroy/%s.json' % (self.base_url, user)
1977
json = self._FetchUrl(url, post_data={})
1978
data = simplejson.loads(json)
1979
self._CheckForTwitterError(data)
1980
return User.NewFromJsonDict(data)
1982
def CreateFavorite(self, status):
1983
'''Favorites the status specified in the status parameter as the authenticating user.
1984
Returns the favorite status when successful.
1986
The twitter.Api instance must be authenticated.
1989
The twitter.Status instance to mark as a favorite.
1991
A twitter.Status instance representing the newly-marked favorite.
1993
url = '%s/favorites/create/%s.json' % (self.base_url, status.id)
1994
json = self._FetchUrl(url, post_data={})
1995
data = simplejson.loads(json)
1996
self._CheckForTwitterError(data)
1997
return Status.NewFromJsonDict(data)
1999
def DestroyFavorite(self, status):
2000
'''Un-favorites the status specified in the ID parameter as the authenticating user.
2001
Returns the un-favorited status in the requested format when successful.
2003
The twitter.Api instance must be authenticated.
2006
The twitter.Status to unmark as a favorite.
2008
A twitter.Status instance representing the newly-unmarked favorite.
2010
url = '%s/favorites/destroy/%s.json' % (self.base_url, status.id)
2011
json = self._FetchUrl(url, post_data={})
2012
data = simplejson.loads(json)
2013
self._CheckForTwitterError(data)
2014
return Status.NewFromJsonDict(data)
2016
def GetFavorites(self,
2019
'''Return a list of Status objects representing favorited tweets.
2020
By default, returns the (up to) 20 most recent tweets for the
2025
The username or id of the user whose favorites you are fetching.
2026
If not specified, defaults to the authenticated user. [optional]
2029
Retrieves the 20 next most recent favorite statuses. [optional]
2034
parameters['page'] = page
2037
url = '%s/favorites/%s.json' % (self.base_url, user)
2038
elif not user and not self._username:
2039
raise TwitterError("User must be specified if API is not authenticated.")
2041
url = '%s/favorites.json' % self.base_url
2043
json = self._FetchUrl(url, parameters=parameters)
2044
data = simplejson.loads(json)
2046
self._CheckForTwitterError(data)
2048
return [Status.NewFromJsonDict(x) for x in data]
2050
def GetMentions(self,
2054
'''Returns the 20 most recent mentions (status containing @username)
2055
for the authenticating user.
2059
Returns only public statuses with an ID greater than
2060
(that is, more recent than) the specified ID. [optional]
2063
Returns only statuses with an ID less than
2064
(that is, older than) the specified ID. [optional]
2067
Retrieves the 20 next most recent replies. [optional]
2070
A sequence of twitter.Status instances, one for each mention of the user.
2071
see: http://apiwiki.twitter.com/REST-API-Documentation#statuses/mentions
2074
url = '%s/statuses/mentions.json' % self.base_url
2076
if not self._username:
2077
raise TwitterError("The twitter.Api instance must be authenticated.")
2082
parameters['since_id'] = since_id
2084
parameters['max_id'] = max_id
2086
parameters['page'] = page
2088
json = self._FetchUrl(url, parameters=parameters)
2089
data = simplejson.loads(json)
2091
self._CheckForTwitterError(data)
2093
return [Status.NewFromJsonDict(x) for x in data]
2095
def GetUserByEmail(self, email):
2096
'''Returns a single user by email address.
2099
email: The email of the user to retrieve.
2101
A twitter.User instance representing that user
2103
url = '%s/users/show.json?email=%s' % (self.base_url, email)
2104
json = self._FetchUrl(url)
2105
data = simplejson.loads(json)
2106
self._CheckForTwitterError(data)
2107
return User.NewFromJsonDict(data)
2109
def VerifyCredentials(self):
2110
'''Returns a twitter.User instance if the authenticating user is valid.
2113
A twitter.User instance representing that user if the
2114
credentials are valid, None otherwise.
2116
if not self._username:
2117
raise TwitterError("Api instance must first be given user credentials.")
2118
url = '%s/account/verify_credentials.json' % self.base_url
2120
json = self._FetchUrl(url, no_cache=True)
2121
except urllib2.HTTPError, http_error:
2122
if http_error.code == httplib.UNAUTHORIZED:
2126
data = simplejson.loads(json)
2127
self._CheckForTwitterError(data)
2128
return User.NewFromJsonDict(data)
2130
def SetCredentials(self, username, password):
2131
'''Set the username and password for this instance
2134
username: The twitter username.
2135
password: The twitter password.
2137
self._username = username
2138
self._password = password
2140
def ClearCredentials(self):
2141
'''Clear the username and password for this instance
2143
self._username = None
2144
self._password = None
2146
def SetCache(self, cache):
2147
'''Override the default cache. Set to None to prevent caching.
2150
cache: an instance that supports the same API as the twitter._FileCache
2152
if cache == DEFAULT_CACHE:
2153
self._cache = _FileCache()
2157
def SetUrllib(self, urllib):
2158
'''Override the default urllib implementation.
2161
urllib: an instance that supports the same API as the urllib2 module
2163
self._urllib = urllib
2165
def SetCacheTimeout(self, cache_timeout):
2166
'''Override the default cache timeout.
2169
cache_timeout: time, in seconds, that responses should be reused.
2171
self._cache_timeout = cache_timeout
2173
def SetUserAgent(self, user_agent):
2174
'''Override the default user agent
2177
user_agent: a string that should be send to the server as the User-agent
2179
self._request_headers['User-Agent'] = user_agent
2181
def SetXTwitterHeaders(self, client, url, version):
2182
'''Set the X-Twitter HTTP headers that will be sent to the server.
2186
The client name as a string. Will be sent to the server as
2187
the 'X-Twitter-Client' header.
2189
The URL of the meta.xml as a string. Will be sent to the server
2190
as the 'X-Twitter-Client-URL' header.
2192
The client version as a string. Will be sent to the server
2193
as the 'X-Twitter-Client-Version' header.
2195
self._request_headers['X-Twitter-Client'] = client
2196
self._request_headers['X-Twitter-Client-URL'] = url
2197
self._request_headers['X-Twitter-Client-Version'] = version
2199
def SetSource(self, source):
2200
'''Suggest the "from source" value to be displayed on the Twitter web site.
2202
The value of the 'source' parameter must be first recognized by
2203
the Twitter server. New source values are authorized on a case by
2204
case basis by the Twitter development team.
2208
The source name as a string. Will be sent to the server as
2209
the 'source' parameter.
2211
self._default_params['source'] = source
2213
def GetRateLimitStatus(self):
2214
'''Fetch the rate limit status for the currently authorized user.
2217
A dictionary containing the time the limit will reset (reset_time),
2218
the number of remaining hits allowed before the reset (remaining_hits),
2219
the number of hits allowed in a 60-minute period (hourly_limit), and the
2220
time of the reset in seconds since The Epoch (reset_time_in_seconds).
2222
url = '%s/account/rate_limit_status.json' % self.base_url
2223
json = self._FetchUrl(url, no_cache=True)
2224
data = simplejson.loads(json)
2226
self._CheckForTwitterError(data)
2230
def MaximumHitFrequency(self):
2231
'''Determines the minimum number of seconds that a program must wait before
2232
hitting the server again without exceeding the rate_limit imposed for the
2233
currently authenticated user.
2236
The minimum second interval that a program must use so as to not exceed
2237
the rate_limit imposed for the user.
2239
rate_status = self.GetRateLimitStatus()
2240
reset_time = rate_status.get('reset_time', None)
2241
limit = rate_status.get('remaining_hits', None)
2243
if reset_time and limit:
2244
# put the reset time into a datetime object
2245
reset = datetime.datetime(*rfc822.parsedate(reset_time)[:7])
2247
# find the difference in time between now and the reset time + 1 hour
2248
delta = reset + datetime.timedelta(hours=1) - datetime.datetime.utcnow()
2250
# determine the minimum number of seconds allowed as a regular interval
2251
max_frequency = int(delta.seconds / limit)
2253
# return the number of seconds
2254
return max_frequency
2258
def _BuildUrl(self, url, path_elements=None, extra_params=None):
2259
# Break url into consituent parts
2260
(scheme, netloc, path, params, query, fragment) = urlparse.urlparse(url)
2262
# Add any additional path elements to the path
2264
# Filter out the path elements that have a value of None
2265
p = [i for i in path_elements if i]
2266
if not path.endswith('/'):
2270
# Add any additional query parameters to the query string
2271
if extra_params and len(extra_params) > 0:
2272
extra_query = self._EncodeParameters(extra_params)
2273
# Add it to the existing query
2275
query += '&' + extra_query
2279
# Return the rebuilt URL
2280
return urlparse.urlunparse((scheme, netloc, path, params, query, fragment))
2282
def _InitializeRequestHeaders(self, request_headers):
2284
self._request_headers = request_headers
2286
self._request_headers = {}
2288
def _InitializeUserAgent(self):
2289
user_agent = 'Python-urllib/%s (python-twitter/%s)' % \
2290
(self._urllib.__version__, __version__)
2291
self.SetUserAgent(user_agent)
2293
def _InitializeDefaultParameters(self):
2294
self._default_params = {}
2296
def _AddAuthorizationHeader(self, username, password):
2297
if username and password:
2298
basic_auth = base64.encodestring('%s:%s' % (username, password))[:-1]
2299
self._request_headers['Authorization'] = 'Basic %s' % basic_auth
2301
def _RemoveAuthorizationHeader(self):
2302
if self._request_headers and 'Authorization' in self._request_headers:
2303
del self._request_headers['Authorization']
2305
def _DecompressGzippedResponse(self, response):
2306
raw_data = response.read()
2307
if response.headers.get('content-encoding', None) == 'gzip':
2308
url_data = gzip.GzipFile(fileobj=StringIO.StringIO(raw_data)).read()
2313
def _GetOpener(self, url, username=None, password=None):
2314
if username and password:
2315
self._AddAuthorizationHeader(username, password)
2316
handler = self._urllib.HTTPBasicAuthHandler()
2317
(scheme, netloc, path, params, query, fragment) = urlparse.urlparse(url)
2318
handler.add_password(Api._API_REALM, netloc, username, password)
2319
opener = self._urllib.build_opener(handler)
2321
opener = self._urllib.build_opener()
2322
opener.addheaders = self._request_headers.items()
2325
def _Encode(self, s):
2326
if self._input_encoding:
2327
return unicode(s, self._input_encoding).encode('utf-8')
2329
return unicode(s).encode('utf-8')
2331
def _EncodeParameters(self, parameters):
2332
'''Return a string in key=value&key=value form
2334
Values of None are not included in the output string.
2338
A dict of (key, value) tuples, where value is encoded as
2339
specified by self._encoding
2341
A URL-encoded string in "key=value&key=value" form
2343
if parameters is None:
2346
return urllib.urlencode(dict([(k, self._Encode(v)) for k, v in parameters.items() if v is not None]))
2348
def _EncodePostData(self, post_data):
2349
'''Return a string in key=value&key=value form
2351
Values are assumed to be encoded in the format specified by self._encoding,
2352
and are subsequently URL encoded.
2356
A dict of (key, value) tuples, where value is encoded as
2357
specified by self._encoding
2359
A URL-encoded string in "key=value&key=value" form
2361
if post_data is None:
2364
return urllib.urlencode(dict([(k, self._Encode(v)) for k, v in post_data.items()]))
2366
def _CheckForTwitterError(self, data):
2367
"""Raises a TwitterError if twitter returns an error message.
2370
data: A python dict created from the Twitter json response
2372
TwitterError wrapping the twitter error message if one exists.
2374
# Twitter errors are relatively unlikely, so it is faster
2375
# to check first, rather than try and catch the exception
2377
raise TwitterError(data['error'])
2384
use_gzip_compression=None):
2385
'''Fetch a URL, optionally caching for a specified time.
2391
A dict of (str, unicode) key/value pairs.
2392
If set, POST will be used.
2394
A dict whose key/value pairs should encoded and added
2395
to the query string. [optional]
2397
If true, overrides the cache on the current request
2398
use_gzip_compression:
2399
If True, tells the server to gzip-compress the response.
2400
It does not apply to POST requests.
2401
Defaults to None, which will get the value to use from
2402
the instance variable self._use_gzip [optional]
2405
A string containing the body of the response.
2407
# Build the extra parameters dict
2409
if self._default_params:
2410
extra_params.update(self._default_params)
2412
extra_params.update(parameters)
2414
# Add key/value parameters to the query string of the url
2415
url = self._BuildUrl(url, extra_params=extra_params)
2417
# Get a url opener that can handle basic auth
2418
opener = self._GetOpener(url, username=self._username, password=self._password)
2420
if use_gzip_compression is None:
2421
use_gzip = self._use_gzip
2423
use_gzip = use_gzip_compression
2425
# Set up compression
2426
if use_gzip and not post_data:
2427
opener.addheaders.append(('Accept-Encoding', 'gzip'))
2429
encoded_post_data = self._EncodePostData(post_data)
2431
# Open and return the URL immediately if we're not going to cache
2432
if encoded_post_data or no_cache or not self._cache or not self._cache_timeout:
2433
response = opener.open(url, encoded_post_data)
2434
url_data = self._DecompressGzippedResponse(response)
2437
# Unique keys are a combination of the url and the username
2439
key = self._username + ':' + url
2443
# See if it has been cached before
2444
last_cached = self._cache.GetCachedTime(key)
2446
# If the cached version is outdated then fetch another and store it
2447
if not last_cached or time.time() >= last_cached + self._cache_timeout:
2448
response = opener.open(url, encoded_post_data)
2449
url_data = self._DecompressGzippedResponse(response)
2451
self._cache.Set(key, url_data)
2453
url_data = self._cache.Get(key)
2455
# Always return the latest version
2459
class _FileCacheError(Exception):
2460
'''Base exception class for FileCache related errors'''
2462
class _FileCache(object):
2466
def __init__(self,root_directory=None):
2467
self._InitializeRootDirectory(root_directory)
2470
path = self._GetPath(key)
2471
if os.path.exists(path):
2472
return open(path).read()
2476
def Set(self,key,data):
2477
path = self._GetPath(key)
2478
directory = os.path.dirname(path)
2479
if not os.path.exists(directory):
2480
os.makedirs(directory)
2481
if not os.path.isdir(directory):
2482
raise _FileCacheError('%s exists but is not a directory' % directory)
2483
temp_fd, temp_path = tempfile.mkstemp()
2484
temp_fp = os.fdopen(temp_fd, 'w')
2487
if not path.startswith(self._root_directory):
2488
raise _FileCacheError('%s does not appear to live under %s' %
2489
(path, self._root_directory))
2490
if os.path.exists(path):
2492
os.rename(temp_path, path)
2494
def Remove(self,key):
2495
path = self._GetPath(key)
2496
if not path.startswith(self._root_directory):
2497
raise _FileCacheError('%s does not appear to live under %s' %
2498
(path, self._root_directory ))
2499
if os.path.exists(path):
2502
def GetCachedTime(self,key):
2503
path = self._GetPath(key)
2504
if os.path.exists(path):
2505
return os.path.getmtime(path)
2509
def _GetUsername(self):
2510
'''Attempt to find the username in a cross-platform fashion.'''
2512
return os.getenv('USER') or \
2513
os.getenv('LOGNAME') or \
2514
os.getenv('USERNAME') or \
2517
except (IOError, OSError), e:
2520
def _GetTmpCachePath(self):
2521
username = self._GetUsername()
2522
cache_directory = 'python.cache_' + username
2523
return os.path.join(tempfile.gettempdir(), cache_directory)
2525
def _InitializeRootDirectory(self, root_directory):
2526
if not root_directory:
2527
root_directory = self._GetTmpCachePath()
2528
root_directory = os.path.abspath(root_directory)
2529
if not os.path.exists(root_directory):
2530
os.mkdir(root_directory)
2531
if not os.path.isdir(root_directory):
2532
raise _FileCacheError('%s exists but is not a directory' %
2534
self._root_directory = root_directory
2536
def _GetPath(self,key):
2538
hashed_key = md5(key).hexdigest()
2540
hashed_key = md5.new(key).hexdigest()
2542
return os.path.join(self._root_directory,
2543
self._GetPrefix(hashed_key),
2546
def _GetPrefix(self,hashed_key):
2547
return os.path.sep.join(hashed_key[0:_FileCache.DEPTH])