-
Notifications
You must be signed in to change notification settings - Fork 0
/
__init__.py
2811 lines (2282 loc) · 113 KB
/
__init__.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
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
"""Provides an object-oriented interface for the Context.IO API.
To use it, you have to [sign up for an account](http://context.io).
Once you have an account, you can create a new `ContextIO` object and interact
with the API using the methods on that object as a starting point.
import contextio as c
CONSUMER_KEY = 'YOUR_API_KEY'
CONSUMER_SECRET = 'YOUR_API_SECRET'
context_io = c.ContextIO(
consumer_key=CONSUMER_KEY,
consumer_secret=CONSUMER_SECRET
)
The module has tons of docstrings! Do help() calls in an interpreter to see
how to use the module.
The main context_io object is the parent of all objects and handles
authentication. If you store things like account or message ids and want
to be lazy about querying, you can instantiate things like account objects
like this:
account = Account(context_io, {'id': 'ACCOUNT_ID'})
If you want to populate the other account properties from the api, just do a:
account.get()
If you want to instantiate a sub-resource of an Account, just pass the
account object as the parent.
message = Message(account, {'id': 'MESSAGE_ID'})
"""
import logging
import re
import six
import collections
from datetime import datetime
from rauth import OAuth1Session
if six.PY2:
from urllib import urlencode, quote
else:
from urllib.parse import urlencode, quote
# check to see if we can get json, or if we're on app engine
try:
import json
except:
from django.utils import simplejson as json
# helper functions
def to_underscore(name):
s1 = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', name)
return re.sub('([a-z0-9])([A-Z])', r'\1_\2', s1).lower()
def uncamelize(d):
drop = []
for k, v in d.items():
u = to_underscore(k)
if u != k and u not in d:
d[u] = v
drop.append(k)
for k in drop:
del d[k]
return d
def as_bool(v):
if v == 0 or v is False:
return False
return True
def as_datetime(v):
if isinstance(v, int):
return datetime.fromtimestamp(v)
def process_person_info(parent, person_info, addresses):
try:
from contextIO2 import Contact
except ImportError:
from __init__ import Contact
contacts = {}
to_addrs = []
to_contacts = []
from_addr = None
from_contact = None
if addresses.has_key('to'):
for info in addresses['to']:
person_info[info.get('email')].setdefault('name', info.get('name'))
to_addrs.append(info.get('email'))
info = addresses['from']
person_info[info.get('email')].setdefault('name', info.get('name'))
from_addr = info.get('email')
for addr, d in person_info.items():
info = {
'email': addr,
'thumbnail': d.get('thumbnail'),
'name': d.get('name')
}
c = Contact(parent, info)
contacts.setdefault(addr, c)
if addr in to_addrs:
to_contacts.append(c)
to_addrs.remove(addr)
elif addr == from_addr:
from_contact = c
return contacts, to_contacts, from_contact
class ArgumentError(Exception):
"""Class to handle bad arguments."""
def __init__(self, message):
self.message = message
def __str__(self):
return self.message
class ContextIO(object):
"""Parent class of module. This handles authentication and requests.
Parameters:
consumer_key: string - your Context.IO consumer key
consumer_secret: string - your Context.IO consumer secret
debug: string - Set to None by default. If you want debug messages,
set to either 'print' or 'log'. If set to 'print', debug messages
will be printed out. Useful for python's interactive console. If
set to 'log' will send debug messages to logging.debug()
"""
def __init__(self, consumer_key, consumer_secret, debug=None,
url_base='https://api.context.io'):
"""Constructor that creates oauth2 consumer and client.
Required Arguments:
consumer_key: string - your api key
consumer_secret: string - you api secret
Optional Arguments:
debug: if used, set to either 'print' or 'log' - if print, debug
messages will be sent to stdout. If set to 'log' will send
to logging.debug()
"""
self.version = '3.0'
self.debug = debug
if self.debug is True: # for people who don't read the code and just set debug=True
self.debug = "print"
self.consumer_key = consumer_key
self.consumer_secret = consumer_secret
self.url_base = url_base
def _debug(self, response):
"""Prints or logs a debug message.
Required Arguments:
response: object - the rauth response object.
Returns:
None
"""
if self.debug:
message = ("--------------------------------------------------\n"
"URL: %s\nMETHOD: %s\nSTATUS: %s\n\nREQUEST\n%s\n\nRESPON"
"SE\n%s\n") % (
response.request.url,
response.request.method,
response.status_code,
response.request.__dict__,
response.__dict__
)
if self.debug == 'print':
six.print_(message)
elif self.debug == 'log':
logging.debug(message)
def _request_uri(self, uri, method="GET", params={}, headers={}, body=''):
"""Assembles the request uri and calls the request method.
Required Arguments:
uri: string - the assembled API endpoint.
Optional Parameters:
method: string - the method of the request. Possible values are
'GET', 'POST', 'DELETE', 'PUT'
params: dict - parameters to pass along
headers: dict - any specific http headers
body: string - request body, only used on a few PUT statements
Returns:
typically, JSON - depends on the API call, refer to the other
method docstrings for more details.
"""
url = '/'.join((self.url_base, self.version, uri))
response = self._request(url, method, params, headers, body)
status = response.status_code
if status >= 200 and status < 300:
# look for a ValueError
try:
return response.json()
except UnicodeDecodeError:
return response.content
except ValueError:
return response.text
else:
self._handle_request_error(response)
def _request(self, url, method, params, headers={}, body=''):
"""This method actually makes the request using the oauth client.
Required Arguments:
url: string - the API endpoint.
method: string - the type of request, either 'GET', 'POST',
'DELETE', or 'PUT'
params: dict - dictionary of parameters for the call
headers: dict - dict of any custom headers.
Optional Arguments:
body: string - this is only used on a few API calls, mostly PUTS.
Returns:
typically JSON, depends on the API call. Refer to the specific
method you're calling to learn what the return will be.
"""
session = OAuth1Session(self.consumer_key, self.consumer_secret)
if method == 'POST':
params['body'] = body
response = session.request(method, url, header_auth=True, data=params, headers=headers)
else:
response = session.request(method, url, header_auth=True, params=params, headers=headers, data=body)
self._debug(response)
return response
def _handle_request_error(self, response):
"""This method formats request errors and raises appropriate
exceptions."""
response_json = response.json()
if isinstance(response_json, collections.Iterable):
if 'code' in response_json and 'value' in response_json:
raise Exception(
'HTTP %s: %s' % (
response_json['code'],
response_json['value']
)
)
elif 'type' in response_json and 'value' in response_json:
raise Exception(
'%s: %s' % (
response_json['type'],
response_json['value']
)
)
else:
raise Exception(response.text)
def get_accounts(self, **params):
"""List of Accounts.
GET method for the accounts resource.
Documentation: http://context.io/docs/2.0/accounts#get
Optional Arguments:
email: string - Only return account associated
to this email address
status: string - Only return accounts with sources
whose status is of a specific value. If an account has many
sources, only those matching the given value will be
included in the response. Possible statuses are:
INVALID_CREDENTIALS, CONNECTION_IMPOSSIBLE,
NO_ACCESS_TO_ALL_MAIL, OK, TEMP_DISABLED and DISABLED
status_ok: int - Only return accounts with sources
whose status is of a specific value. If an account has many
sources, only those matching the given value will be included
in the response. Possible statuses are: INVALID_CREDENTIALS,
CONNECTION_IMPOSSIBLE, NO_ACCESS_TO_ALL_MAIL, OK, TEMP_DISABLED
and DISABLED
limit: int - The maximum number of results to return
offset: int - Start the list at this offset (zero-based)
Returns:
A list of Account objects
"""
all_args = ['email', 'status', 'status_ok', 'limit', 'offset']
params = Resource.sanitize_params(params, all_args)
return [Account(self, obj) for obj in self._request_uri(
'accounts', params=params
)]
def post_account(self, **params):
"""Add a new account.
POST method for the accounts resource.
You can optionally pass in the params to simultaneously add a source
with just this one call.
Documentation: http://context.io/docs/2.0/accounts#post
Required Arguments:
email: string - The primary email address of the account holder.
Optional Arguments:
first_name: string - First name of the account holder.
last_name: string - Last name of the account holder.
If adding a source in the same call:
Required Arguments:
server: string - Name of IP of the IMAP server, eg. imap.gmail.com
username: string - The username used to authenticate an IMAP
connection. On some servers, this is the same thing as
the primary email address.
use_ssl: integer - Set to 1 if you want SSL encryption to
be used when opening connections to the IMAP server. Any
other value will be considered as "do not use SSL"
port: integer - Port number to connect to on the server. Keep in
mind that most IMAP servers will have one port for standard
connection and another one for encrypted connection (see
use-ssl parameter above)
type: string - Currently, the only supported type is IMAP
Optional Arguments:
sync_period: string - Sets the period at which the Context.IO
index for this source is synced with the origin email
account on the IMAP server. Possible values are 1h, 4h, 12h
and 24h (default).
raw_file_list: integer - By default, we filter out files like
signature images or those winmail.dat files form the files
list. Set this parameter to 1 to turn off this filtering and
show every single file attachments.
password: string - Password for authentication on the IMAP server.
Ignored if any of the provider_* parameters are set below.
provider_refresh_token: An OAuth2 refresh token obtained from the
IMAP account provider to be used to authenticate on this email
account.
provider_consumer_key: string - The OAuth consumer key used to
obtain the the token and token secret above for that account.
That consumer key and secret must be configured in your
Context.IO account.
callback_url: string (url) - If specified, we'll make a POST
request to this URL when the initial sync is completed.
Returns:
An Account object
"""
req_args = ['email', ]
all_args = ['email', 'first_name', 'last_name', 'server',
'username', 'use_ssl', 'port', 'type', 'sync_period',
'raw_file_list', 'password', 'provider_refresh_token',
'provider_consumer_key', 'callback_url'
]
params = Resource.sanitize_params(params, all_args, req_args)
return Account(self, self._request_uri(
'accounts', method="POST", params=params
))
def get_connect_tokens(self,**params):
"""Get a list of connect tokens created with your API key.
Documentation: http://context.io/docs/2.0/connect_tokens#get
Optional Arguments:
token : string - The connect token used to create the account .
Making the api call https://context.io/2.0/connect_tokens/<token_id>
Returns(if token is given):
A dictionary :
{
"token": stringId of the connect_token,
"email": stringemail address specified on token creation,
"created": numberUnix timestamp of the connect_token was created,
"used": numberUnix time this token was been used. 0 means it no account has been created with this token yet,
"expires": mixedUnix time this token will expire and be purged. Once the token is used, this property will be set to false,
"callback_url": stringURL of your app we'll redirect the browser to when the account is created,
"first_name": stringFirst name specified on token creation,
"last_name": stringLast name specified on token creation,
"account": {
If the connect_token hasn't been used yet, this object will be empty
"id": stringId of the account created with this token,
"created": numberUnix timestamp of account creation time,
"suspended": numberUnix timestamp of account suspension time 0 means not suspended,
"email_addresses":arrayArray of email addresses for this account. This only lists the actual addresses as strings.,
"first_name": stringFirst name of account holder,
"last_name": stringLast name of account holder,
"sources": arrayList of sources this account gets data from. See sources
If your key uses 3-legged signatures, the following 2 properties are added
"access_token": stringOAuth access token to sign all future requests on this account,
"access_token_secret": stringOAuth access token secret to sign all future requests on this account
}
}
or
Returns:
A list of ConnectToken objects.
"""
if params:
return self._request_uri('connect_tokens'+'/'+params['token'])
else:
return [ConnectToken(self, obj) for obj in self._request_uri(
'connect_tokens'
)]
def post_connect_token(self, **params):
"""Obtain a new connect token.
Documentation: http://context.io/docs/2.0/connect_tokens#post
Required Arguments:
callback_url: string (url) - When the user's mailbox is connected
to your API key, the browser will call this url (GET). This
call will have a parameter called contextio_token indicating
the connect_token related to this callback. You can then do a
get on this connect_token to obtain details about the account
and source created through that token and save that account id
in your own user data.
Optional Arguments:
email: string - The email address of the account to be added. If
specified, the first step of the connect UI where users are
prompted for their email address, first name and last name is
skipped.
first_name: string - First name of the account holder.
last_name: string - Last name of the account holder.
source_callback_url: string - If specified, we'll make a POST
request to this URL when the initial sync is completed.
source_sync_all_folders: integer - By default, we filter out some
folders like 'Deleted Items' and 'Drafts'. Set this parameter
to 1 to turn off this filtering and show every single folder.
source_sync_flags: integer - By default, we don't synchronize IMAP
flags. Set this parameter to 1 to turn on IMAP flag syncing
for the 'seen' and 'flagged' flags.
source_raw_file_list: integer - By default, we filter out files
like signature images from the files list. Set this parameter
to 1 to turn off this filtering and show every single file
attachment.
Returns:
A dictionary, data format below
{
"success": string - true if connect_token was successfully
created, false otherwise,
"token": string - Id of the token,
"resource_url": string - URL to of the token,
"browser_redirect_url": string - Redirect the user's browser to
this URL to have them connect their mailbox through this
token
}
"""
req_args = ['callback_url', ]
all_args = [
'callback_url', 'email', 'first_name', 'last_name',
'source_callback_url', 'source_sync_all_folders',
'source_sync_flags', 'source_raw_file_list'
]
params = Resource.sanitize_params(params, all_args, req_args)
return self._request_uri('connect_tokens', method='POST', params=params)
def get_discovery(self, **params):
"""Attempts to discover IMAP settings for a given email address.
Documentation: http://context.io/docs/2.0/discovery
Required Arguments:
source_type: string - The type of source you want to discover
settings for. Right now, the only supported source type is IMAP
email: string - An email address you want to discover IMAP
settings for. Make sure source_type is set to IMAP.
Returns:
A Discovery object.
"""
if 'source_type' not in params:
params['source_type'] = 'IMAP'
req_args = ['source_type', 'email']
all_args = ['source_type', 'email']
params = Resource.sanitize_params(params, all_args, req_args)
return Discovery(self, self._request_uri('discovery', params=params))
def get_oauth_providers(self):
"""List of oauth providers configured.
Documentation: http://context.io/docs/2.0/oauth_providers#get
Arguments:
None
Returns:
A list of OauthProvider objects.
"""
return [OauthProvider(self, obj) for obj in
self._request_uri('oauth_providers')
]
def post_oauth_provider(self, **params):
"""Add a new OAuth provider.
Required Arguments:
type: string - Identification of the OAuth provider. This must be
either GMAIL and GOOGLEAPPSMARKETPLACE.
provider_consumer_key: string - The OAuth consumer key
provider_consumer_secret: string - The OAuth consumer secret
Returns:
a dict
"""
req_args = [
'type', 'provider_consumer_key', 'provider_consumer_secret']
all_args = [
'type', 'provider_consumer_key', 'provider_consumer_secret']
params = Resource.sanitize_params(params, all_args, req_args)
self._request_uri('oauth_providers', method='POST', params=params)
class Resource(object):
"""Base class for resource objects."""
keys = []
def __init__(self, parent, base_uri, defn):
"""Constructor."""
if defn == "":
logging.error('Empty response received for ' + base_uri + "")
return
try:
defn = uncamelize(defn)
except:
logging.error('Invalid response received for ' + base_uri + "")
return
for k in self.__class__.keys:
if k in defn:
setattr(self, k, defn[k])
else:
setattr(self, k, None)
self.parent = parent
unidict = {k if isinstance(k, str) else k: v if isinstance(v, str) else v for k, v in defn.items()}
self.base_uri = base_uri.format(**unidict)
def _uri_for(self, *elems):
"""Joins API endpoint elements and returns a string."""
return '/'.join([self.base_uri] + list(elems))
def _request_uri(
self, uri_elems, method="GET", params={}, headers={}, body=''):
"""Gathers up request elements and helps form the request object.
Required Arguments:
uri_elems: list - list of strings, joined to form the endpoint.
Optional Arguments:
method: string - the method of the request. Possible values are
'GET', 'POST', 'DELETE', 'PUT'
params: dict - parameters to pass along
headers: dict - any specific http headers
body: string - request body, only used on a few PUT statements
"""
uri = self._uri_for(uri_elems)
return self.parent._request_uri(
uri, method=method, params=params, headers=headers, body=body
)
@staticmethod
def sanitize_params(params, all_args, required_args=None):
"""Removes parameters that aren't valid.
Required Arguments:
params: dict - key/value pairs of arguments
all_args: list - list of strings, each string is a
valid parameter.
Optional Args:
required_arguments: list - ironically, required_arguments is an
optional argument here. a list of string, each string is a
required argument.
Returns:
dictionary of key, value pairs of valid parameters.
"""
if required_args:
# check to be sure we have all the required params
missing_required_args = []
for required_arg in required_args:
param = params.get(required_arg)
if param == None:
missing_required_args.append(required_arg)
# yell if we're missing a required argument
if missing_required_args:
raise ArgumentError(
'Missing the following required arguments: %s' \
% ', '.join(missing_required_args)
)
# remove any arguments not recognized
cleaned_args = {}
for arg in all_args:
if arg in params:
cleaned_args[arg] = params[arg]
del params[arg]
# quietly yell in a non-breaking way if there's any unrecognized
# arguments left
if params:
logging.warning('Invalid arguments found: %s' % \
', '.join(param for param in params))
return cleaned_args
class Account(Resource):
"""Class to represent the Account resource.
Properties:
id: string - Id of the account
username: string - Username assigned to the account
created: integer (unix timestamp) - account creation time
suspended: integer (unix timestamp) - account suspension time 0 means
not suspended
email_addresses: list - email addresses for this account
first_name: string - First name of account holder
last_name: string - Last name of account holder
password_expired: integer (unix timestamp) - user's password
expiration. 0 means still valid
sources: list - email accounts where this account gets data from
nb_messages: integer - Total number of messages in all sources of
this account
nb_files: integer - Total number of files in all sources of this
account
"""
keys = ['id', 'username', 'created', 'suspended', 'email_addresses',
'first_name', 'last_name', 'password_expired', 'sources',
'nb_messages', 'nb_files']
def __init__(self, parent, defn):
"""Constructor.
Required Arguments:
parent: ContextIO object - parent is the ContextIO object to handle
authentication.
defn: a dictionary of parameters. The 'id' parameter is required to
make method calls.
"""
super(Account, self).__init__(parent, 'accounts/{id}', defn)
def get(self):
"""GET details for a given account.
GET method for the account resource.
Documentation: http://context.io/docs/2.0/accounts#id-get
Arguments:
None
Returns:
True if self is updated, else will throw a request error
"""
self.__init__(self.parent, self._request_uri(''))
return True
def delete(self):
"""Remove a given account.
DELETE method for the account resource.
Documentation: http://context.io/docs/2.0/accounts#id-delete
Arguments:
None
Returns:
Bool
"""
status = self._request_uri('', method='DELETE')
return bool(status['success'])
def post(self, **params):
"""Modifies a given account.
POST method for the account resource.
Documentation: http://context.io/docs/2.0/accounts#id-post
Optional Arguments:
first_name: string - First name of the account holder
last_name: string - Last name of the account holder
Returns:
Bool
"""
all_args = ['first_name', 'last_name']
params = Resource.sanitize_params(params, all_args)
# update account object with new values
if 'first_name' in params:
self.first_name = params['first_name']
if 'last_name' in params:
self.last_name = params['last_name']
status = self._request_uri('', method='POST', params=params)
return bool(status['success'])
def get_connect_tokens(self):
"""List of connect tokens created for an account.
Documentation: http://context.io/docs/2.0/accounts/connect_tokens#get
Arguments:
None
Returns:
A list of ConnectToken objects
"""
return [ConnectToken(self, obj) for obj in self._request_uri(
'connect_tokens'
)]
def post_connect_token(self, **params):
"""Obtain a new connect_token for a specific account.
* Note: unused connect tokens are purged after 24 hours.
Documentation: http://context.io/docs/2.0/accounts/connect_tokens#post
Required Arguments:
callback_url: string (url) - When the user's mailbox is connected
to your API key, the browser will call this url (GET). This
call will have a parameter called contextio_token indicating
the connect_token related to this callback. You can then do a
get on this connect_token to obtain details about the account
and source created through that token and save that account id
in your own user data.
Optional Arguments:
email: string (email) - The email address of the account to be
added. If specified, the first step of the connect UI where
users are prompted for their email address, first name and
last name is skipped.
first_name: string - First name of the account holder.
last_name: string - Last name of the account holder.
source_callback_url: string (url) - If specified, we'll make a
POST request to this URL when the initial sync is completed.
source_sync_all_folders: integer - By default, we filter out some
folders like 'Deleted Items' and 'Drafts'. Set this parameter
to 1 to turn off this filtering and show every single folder.
source_sync_flags: integer - By default, we don't synchronize IMAP
flags. Set this parameter to 1 to turn on IMAP flag syncing
for the 'seen' and 'flagged' flags.
source_raw_file_list: integer - By default, we filter out files
like signature images from the files list. Set this parameter
to 1 to turn off this filtering and show every single file
attachment.
Returns:
A dictionary (data format below)
{
"success": string - true if connect_token was successfully
created, false otherwise,
"token": string - Id of the token,
"resource_url": string - URL to of the token,
"browser_redirect_url": string - Redirect the user's browser to
this URL to have them connect their mailbox through this
token
}
"""
req_args = ['callback_url', ]
all_args = ['callback_url', 'email', 'first_name', 'last_name',
'source_callback_url', 'source_sync_all_folders',
'source_sync_flags', 'source_raw_file_list'
]
params = Resource.sanitize_params(params, all_args, req_args)
return self._request_uri('connect_tokens', method='POST', params=params)
def get_contacts(self, **params):
"""List contacts in an account.
Documentation: http://context.io/docs/2.0/accounts/contacts#get
Optional Arguments:
search: string - String identifying the name or the email address
of the contact(s) you are looking for.
active_before: integer (unix time) - Only include contacts
included in at least one email dated before a given time. This
parameter should be a standard unix timestamp
active_after: integer (unix time) - Only include contacts included
in at least one email dated after a given time. This parameter
should be a standard unix timestamp
limit: integer - The maximum number of results to return.
offset: integer - Start the list at this offset (zero-based).
Returns:
A list of Contact objects
"""
all_args = [
'search', 'active_before', 'active_after', 'limit', 'offset',
'sort_by', 'sort_order'
]
params = Resource.sanitize_params(params, all_args)
return [Contact(self, obj) for obj in self._request_uri(
'contacts', params=params).get('matches'
)]
def get_email_addresses(self):
"""List of email addresses used by an account.
Documentation: http://context.io/docs/2.0/accounts/email_addresses#get
Arguments:
None
Returns:
A list of EmailAddress objects.
"""
return [EmailAddress(self, obj) for obj in self._request_uri(
'email_addresses'
)]
def post_email_address(self, **params):
"""Add a new email address as an alias for an account.
Documentation: http://context.io/docs/2.0/accounts/email_addresses#post
Required Arguments:
email_address: string - An email address.
Returns:
An EmailAddress object.
"""
req_args = ['email_address', ]
all_args = ['email_address', ]
params = Resource.sanitize_params(params, all_args, req_args)
return EmailAddress(self, self._request_uri(
'email_addresses', method='POST', params=params
))
def get_files(self, **params):
"""List of files found as email attachments.
GET method for the files resource.
Documentation: http://context.io/docs/2.0/accounts/files
Each of the email, to, from, cc and bcc parameters can be set to a
comma-separated list of email addresses. These multiple addresses
are treated as an OR combination.
You can set more than one parameter when doing this call. Multiple
parameters are treated as an AND combination.
Optional Arguments:
file_name: string - Search for files based on their name. You can
filter names using typical shell wildcards such as *, ? and []
or regular expressions by enclosing the search expression in a
leading / and trailing /. For example, *.pdf would give you
all PDF files while /\.jpe?g$/ would return all files whose
name ends with .jpg or .jpeg
email: string - Email address of the contact for whom you want the
latest files exchanged with. By "exchanged with contact X" we
mean any email received from contact X, sent to contact X or
sent by anyone to both contact X and the source owner.
to: string - Email address of a contact files have been sent to.
from: string - Email address of a contact files have been received
from.
cc: string - Email address of a contact CC'ed on the messages.
bcc: string - Email address of a contact BCC'ed on the messages.
date_before: integer (unix time) - Only include files attached to
messages sent before a given timestamp. The value this filter
is applied to is the Date: header of the message which refers
to the time the message is sent from the origin.
date_after: integer (unix time) - Only include files attached to
messages sent after a given timestamp. The value this filter
is applied to is the Date: header of the message which refers
to the time the message is sent from the origin.
indexed_before: integer (unix time) - Only include files attached
to messages indexed before a given timestamp. This is not the
same as the date of the email, it is the time Context.IO
indexed this message.
indexed_after: integer (unix time) - Only include files attached
to messages indexed after a given timestamp. This is not the
same as the date of the email, it is the time Context.IO
indexed this message.
group_by_revisions: integer - If set to 1, the list will do an
intelligent grouping of files to reflect occurrences of the
same document. The grouping algorithm is exactly the same as
the one used to get file revisions but only the occurrences
matching the filters applied to the list will be included in
the results.
sort_order: string - The sort order of the returned results.
Possible values are asc and desc
limit: integer - The maximum number of results to return.
offset: integer - Start the list at this offset (zero-based).
Returns:
A list of File objects
"""
all_args = [
'file_name', 'name', 'email', 'to', 'from', 'cc', 'bcc',
'date_before', 'date_after', 'indexed_before', 'indexed_after',
'group_by_revisions', 'limit', 'offset'
]
params = Resource.sanitize_params(params, all_args)
return [File(self, obj) for obj in self._request_uri(
'files', params=params
)]
def get_messages(self, **params):
"""List email messages for an account.
GET method for the messages resource.
Each of the email, to, from, cc and bcc parameters can be set to a
comma-separated list of email addresses. These multiple addresses
are treated as an OR combination.
You can set more than one parameter when doing this call. Multiple
parameters are treated as an AND combination.
Optional Arguments:
subject: string - Get messages whose subject matches this search
string. To use regular expressions instead of simple string
matching, make sure the string starts and ends with /.
email: string - Email address of the contact for whom you want the
latest messages exchanged with. By "exchanged with contact X"
we mean any email received from contact X, sent to contact X
or sent by anyone to both contact X and the source owner.
to: string - Email address of a contact messages have been sent to.
sender: string - Email address of a contact messages have been
received from. Same as "from" in documentation. "from" is a
python keyword and we can't use that...
cc: string - Email address of a contact CC'ed on the messages.
bcc: string - Email address of a contact BCC'ed on the messages.
folder: string - Filter messages by the folder (or Gmail label).
This parameter can be the complete folder name with the
appropriate hierarchy delimiter for the mail server being
queried (eg. Inbox/My folder) or the "symbolic name" of the
folder (eg. \Starred). The symbolic name refers to attributes
used to refer to special use folders in a language-independant
way. See http://code.google.com/apis/gmail/imap/#xlist
(Gmail specific) and RFC-6154.
file_name: string - Search for files based on their name. You can
filter names using typical shell wildcards such as *, ? and []
or regular expressions by enclosing the search expression in a
leading / and trailing /. For example, *.pdf would give you
all PDF files while /\.jpe?g$/ would return all files whose
name ends with .jpg or .jpeg
date_before: integer (unix time) - Only include messages before a
given timestamp. The value this filter is applied to is the
Date: header of the message which refers to the time the
message is sent from the origin.