1 """This module provides access to user's settings on Diaspora*.
11 from diaspy
import errors
, streams
15 """Provides interface to account settings.
17 email_regexp
= re
.compile('<input id="user_email" name="user\[email\]" size="30" type="text" value="(.+?)"')
18 language_option_regexp
= re
.compile('<option value="([-_a-z]+)">(.*?)</option>')
20 def __init__(self
, connection
):
21 self
._connection
= connection
23 def downloadxml(self
):
24 """Returns downloaded XML.
26 request
= self
._connection
.get('user/export')
29 def downloadPhotos(self
, size
='large', path
='.', mark_nsfw
=True, _critical
=False, _stream
=None):
30 """Downloads photos into the current working directory.
31 Sizes are: large, medium, small.
32 Filename is: {post_guid}_{photo_guid}.{extension}
34 Normally, this method will catch urllib-generated errors and
35 just issue warnings about photos that couldn't be downloaded.
36 However, with _critical param set to True errors will become
37 critical - the will be reraised in finally block.
39 :param size: size of the photos to download - large, medium or small
41 :param path: path to download (defaults to current working directory
43 :param mark_nsfw: will append '-nsfw' to images from posts marked as nsfw,
45 :param _stream: diaspy.streams.Generic-like object (only for testing)
46 :param _critical: if True urllib errors will be reraised after generating a warning (may be removed)
48 :returns: integer, number of photos downloaded
52 stream
= streams
.Activity(self
._connection
)
56 for i
, post
in enumerate(stream
):
57 if post
['nsfw'] is not False: nsfw
= '-nsfw'
60 for n
, photo
in enumerate(post
['photos']):
61 # photo format -- .jpg, .png etc.
62 ext
= photo
['sizes'][size
].split('.')[-1]
63 name
= '{0}_{1}{2}.{3}'.format(post
['guid'], photo
['guid'], nsfw
, ext
)
64 filename
= os
.path
.join(path
, name
)
66 urllib
.request
.urlretrieve(url
=photo
['sizes'][size
], filename
=filename
)
67 except (urllib
.error
.HTTPError
, urllib
.error
.URLError
) as e
:
68 warnings
.warn('downloading image {0} from post {1}: {2}'.format(photo
['guid'], post
['guid'], e
))
74 def setEmail(self
, email
):
75 """Changes user's email.
77 data
= {'_method': 'put', 'utf8': '✓', 'user[email]': email
, 'authenticity_token': repr(self
._connection
)}
78 request
= self
._connection
.post('user', data
=data
, allow_redirects
=False)
79 if request
.status_code
!= 302:
80 raise errors
.SettingsError('setting email failed: {0}'.format(request
.status_code
))
83 """Returns currently used email.
85 data
= self
._connection
.get('user/edit')
86 email
= self
.email_regexp
.search(data
.text
)
87 if email
is None: email
= ''
88 else: email
= email
.group(1)
91 def setLanguage(self
, lang
):
92 """Changes user's email.
94 :param lang: language identifier from getLanguages()
96 data
= {'_method': 'put', 'utf8': '✓', 'user[language]': lang
, 'authenticity_token': repr(self
._connection
)}
97 request
= self
._connection
.post('user', data
=data
, allow_redirects
=False)
98 if request
.status_code
!= 302:
99 raise errors
.SettingsError('setting language failed: {0}'.format(request
.status_code
))
101 def getLanguages(self
):
102 """Returns a list of tuples containing ('Language name', 'identifier').
103 One of the Black Magic(tm) methods.
105 request
= self
._connection
.get('user/edit')
106 return self
.language_option_regexp
.findall(request
.text
)
110 """Provides interface to provacy settings.
112 def __init__(self
, connection
):
113 self
._connection
= connection
117 """Provides interface to profile settigns.
121 Because of the way update requests for profile are created every field must be sent.
122 The `load()` method is used to load all information into the dictionary.
123 Setters can then be used to adjust the data.
124 Finally, `update()` can be called to send data back to pod.
126 firstname_regexp
= re
.compile('id="profile_first_name" name="profile\[first_name\]" type="text" value="(.*?)" />')
127 lastname_regexp
= re
.compile('id="profile_last_name" name="profile\[last_name\]" type="text" value="(.*?)" />')
128 bio_regexp
= re
.compile('<textarea id="profile_bio" name="profile\[bio\]" placeholder="Fill me out" rows="5">\n(.*?)</textarea>')
129 location_regexp
= re
.compile('id="profile_location" name="profile\[location\]" placeholder="Fill me out" type="text" value="(.*?)" />')
130 gender_regexp
= re
.compile('id="profile_gender" name="profile\[gender\]" placeholder="Fill me out" type="text" value="(.*?)" />')
131 birth_year_regexp
= re
.compile('selected="selected" value="([0-9]{4,4})">[0-9]{4,4}</option>')
132 birth_month_regexp
= re
.compile('selected="selected" value="([0-9]{1,2})">(.*?)</option>')
133 birth_day_regexp
= re
.compile('selected="selected" value="([0-9]{1,2})">[0-9]{1,2}</option>')
134 is_searchable_regexp
= re
.compile('checked="checked" id="profile_searchable" name="profile\[searchable\]" type="checkbox" value="(.*?)" />')
135 is_nsfw_regexp
= re
.compile('checked="checked" id="profile_nsfw" name="profile\[nsfw\]" type="checkbox" value="(.*?)" />')
137 def __init__(self
, connection
):
138 self
._connection
= connection
139 self
.data
= {'utf-8': '✓',
141 'profile[first_name]': '',
142 'profile[last_name]': '',
143 'profile[tag_string]': '',
147 'profile[location]': '',
148 'profile[gender]': '',
149 'profile[date][year]': '',
150 'profile[date][month]': '',
151 'profile[date][day]': '',
153 self
._html
= self
._fetchhtml
()
156 def _fetchhtml(self
):
157 """Fetches html that will be used to extract data.
159 return self
._connection
.get('profile/edit').text
162 """Returns two-tuple: (first, last) name.
164 first
= self
.firstname_regexp
.search(self
._html
).group(1)
165 last
= self
.lastname_regexp
.search(self
._html
).group(1)
169 """Returns tags user had selected when describing him/her-self.
171 guid
= self
._connection
.getUserInfo()['guid']
172 html
= self
._connection
.get('people/{0}'.format(guid
)).text
173 description_regexp
= re
.compile('<a href="/tags/(.*?)" class="tag">#.*?</a>')
174 return [tag
.lower() for tag
in re
.findall(description_regexp
, html
)]
179 bio
= self
.bio_regexp
.search(self
._html
).group(1)
182 def getLocation(self
):
183 """Returns location string.
185 location
= self
.location_regexp
.search(self
._html
).group(1)
189 """Returns location string.
191 gender
= self
.gender_regexp
.search(self
._html
).group(1)
194 def getBirthDate(self
, named_month
=False):
195 """Returns three-tuple: (year, month, day).
197 :param named_month: if True, return name of the month instead of integer
198 :type named_month: bool
200 year
= self
.birth_year_regexp
.search(self
._html
)
201 if year
is None: year
= -1
202 else: year
= int(year
.group(1))
203 month
= self
.birth_month_regexp
.search(self
._html
)
205 if named_month
: month
= ''
209 month
= month
.group(2)
211 month
= int(month
.group(1))
212 day
= self
.birth_day_regexp
.search(self
._html
)
213 if day
is None: day
= -1
214 else: day
= int(day
.group(1))
215 return (year
, month
, day
)
217 def isSearchable(self
):
218 """Returns True if profile is searchable.
220 searchable
= self
.is_searchable_regexp
.search(self
._html
)
221 # this is because value="true" in every case so we just
222 # check if the field is "checked"
223 if searchable
is None: searchable
= False # if it isn't - the regexp just won't match
224 else: searchable
= True
228 """Returns True if profile is marked as NSFW.
230 nsfw
= self
.is_nsfw_regexp
.search(self
._html
)
231 if nsfw
is None: nsfw
= False
235 def setName(self
, first
, last
):
236 """Set first and last name.
238 self
.data
['profile[first_name]'] = first
239 self
.data
['profile[last_name]'] = last
241 def setTags(self
, tags
):
242 """Sets tags that describe the user.
244 self
.data
['tags'] = ', '.join(['#{}'.format(tag
) for tag
in tags
])
246 def setBio(self
, bio
):
247 """Set bio of a user.
249 self
.data
['profile[bio]'] = bio
251 def setLocation(self
, location
):
252 """Set location of a user.
254 self
.data
['profile[location]'] = location
256 def setGender(self
, gender
):
257 """Set gender of a user.
259 self
.data
['profile[gender]'] = gender
261 def setBirthDate(self
, year
, month
, day
):
262 """Set birth date of a user.
264 self
.data
['profile[date][year]'] = year
265 self
.data
['profile[date][month]'] = month
266 self
.data
['profile[date][day]'] = day
268 def setSearchable(self
, searchable
):
269 """Set user's searchable status.
271 self
.data
['profile[searchable]'] = json
.dumps(searchable
)
273 def setNSFW(self
, nsfw
):
274 """Set user NSFW status.
276 self
.data
['profile[nsfw]'] = json
.dumps(nsfw
)
279 """Loads profile data into self.data dictionary.
280 **Notice:** Not all keys are loaded yet.
282 self
.setName(*self
.getName())
283 self
.setBio(self
.getBio())
284 self
.setLocation(self
.getLocation())
285 self
.setGender(self
.getGender())
286 self
.setBirthDate(*self
.getBirthDate(named_month
=False))
287 self
.setSearchable(self
.isSearchable())
288 self
.setNSFW(self
.isNSFW())
289 self
.setTags(self
.getTags())
293 """Updates profile information.
295 if not self
._loaded
: raise errors
.DiaspyError('profile was not loaded')
296 self
.data
['authenticity_token'] = repr(self
._connection
)
298 request
= self
._connection
.post('profile', data
=self
.data
, allow_redirects
=False)
299 return request
.status_code
303 """Provides interface to services settings.
305 def __init__(self
, connection
):
306 self
._connection
= connection