ayadn 0.6.4 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +20 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +17 -0
  5. data/Gemfile +14 -0
  6. data/Guardfile +26 -0
  7. data/LICENSE.txt +22 -0
  8. data/MANUAL.md +946 -0
  9. data/README.md +26 -411
  10. data/Rakefile +6 -0
  11. data/ayadn.gemspec +39 -0
  12. data/bin/ayadn +3 -3
  13. data/lib/ayadn.rb +12 -25
  14. data/lib/ayadn/action.rb +1121 -0
  15. data/lib/ayadn/alias.rb +106 -0
  16. data/lib/ayadn/api.rb +312 -301
  17. data/lib/ayadn/app.rb +429 -365
  18. data/lib/ayadn/authorize.rb +127 -28
  19. data/lib/ayadn/blacklist.rb +116 -0
  20. data/lib/ayadn/cnx.rb +105 -0
  21. data/lib/ayadn/databases.rb +110 -0
  22. data/lib/ayadn/descriptions.rb +1043 -0
  23. data/lib/ayadn/endpoints.rb +220 -153
  24. data/lib/ayadn/errors.rb +37 -0
  25. data/lib/ayadn/extend.rb +4 -10
  26. data/lib/ayadn/fileops.rb +48 -0
  27. data/lib/ayadn/logs.rb +32 -0
  28. data/lib/ayadn/pinboard.rb +46 -35
  29. data/lib/ayadn/post.rb +229 -212
  30. data/lib/ayadn/scroll.rb +251 -0
  31. data/lib/ayadn/set.rb +377 -0
  32. data/lib/ayadn/settings.rb +195 -0
  33. data/lib/ayadn/status.rb +226 -165
  34. data/lib/ayadn/switch.rb +72 -0
  35. data/lib/ayadn/version.rb +4 -0
  36. data/lib/ayadn/view.rb +506 -269
  37. data/lib/ayadn/workers.rb +362 -0
  38. data/spec/helpers.rb +19 -0
  39. data/spec/mock/@ericd.json +160 -0
  40. data/spec/mock/@m.json +45 -0
  41. data/spec/mock/checkins.json +1856 -0
  42. data/spec/mock/fwr_@ayadn.json +1077 -0
  43. data/spec/mock/posted.json +1 -0
  44. data/spec/mock/stream.json +1 -0
  45. data/spec/spec_helper.rb +14 -0
  46. data/spec/unit/api_spec.rb +61 -0
  47. data/spec/unit/databases_spec.rb +5 -0
  48. data/spec/unit/descriptions_spec.rb +9 -0
  49. data/spec/unit/endpoints_spec.rb +35 -0
  50. data/spec/unit/post_spec.rb +136 -0
  51. data/spec/unit/status_spec.rb +9 -0
  52. data/spec/unit/view_spec.rb +119 -0
  53. data/spec/unit/workers_spec.rb +147 -0
  54. metadata +216 -40
  55. data/CHANGELOG.md +0 -250
  56. data/CONTRIBUTORS.md +0 -5
  57. data/LICENSE.md +0 -14
  58. data/lib/ayadn/adn_files.rb +0 -84
  59. data/lib/ayadn/client-http.rb +0 -226
  60. data/lib/ayadn/colors.rb +0 -62
  61. data/lib/ayadn/config.yml +0 -35
  62. data/lib/ayadn/debug.rb +0 -36
  63. data/lib/ayadn/files.rb +0 -184
  64. data/lib/ayadn/get-api.rb +0 -43
  65. data/lib/ayadn/help.rb +0 -152
  66. data/lib/ayadn/list.rb +0 -89
  67. data/lib/ayadn/main.rb +0 -542
  68. data/lib/ayadn/requires.rb +0 -12
  69. data/lib/ayadn/skip.rb +0 -27
  70. data/lib/ayadn/tools.rb +0 -208
  71. data/lib/ayadn/user-stream.rb +0 -91
  72. data/lib/ayadn/view-channels.rb +0 -120
  73. data/lib/ayadn/view-interactions.rb +0 -57
  74. data/lib/ayadn/view-object.rb +0 -195
  75. data/lib/experiments.rb +0 -109
@@ -0,0 +1,362 @@
1
+ # encoding: utf-8
2
+ module Ayadn
3
+ class Workers
4
+
5
+ def build_aliases_list(list)
6
+ table = init_table
7
+ table.title = "List of your channel aliases".color(:cyan) + "".color(:white)
8
+ list.each do |k,v|
9
+ table << ["#{k}".color(:green), "#{v}".color(:red)]
10
+ end
11
+ table
12
+ end
13
+
14
+ def build_blacklist_list(list)
15
+ table = init_table
16
+ table.title = "Your blacklist".color(:cyan) + "".color(:white)
17
+ list.each do |k,v|
18
+ table << ["#{v.capitalize}".color(:green), "#{k}".color(:red)]
19
+ end
20
+ table
21
+ end
22
+
23
+ def build_reposted_list(list, target)
24
+ table = init_table
25
+ table.title = "List of users who reposted post ".color(:cyan) + "#{target}".color(:red) + "".color(:white)
26
+ users_list = []
27
+ list.each do |obj|
28
+ obj['name'].nil? ? name = "" : name = obj['name']
29
+ users_list << {:username => obj['username'], :name => name, :you_follow => obj['you_follow'], :follows_you => obj['follows_you']}
30
+ end
31
+ return users_list, table
32
+ end
33
+
34
+ def build_starred_list(list, target)
35
+ table = init_table
36
+ table.title = "List of users who starred post ".color(:cyan) + "#{target}".color(:red) + "".color(:white)
37
+ users_list = []
38
+ list.each do |obj|
39
+ obj['name'].nil? ? name = "" : name = obj['name']
40
+ users_list << {:username => obj['username'], :name => name, :you_follow => obj['you_follow'], :follows_you => obj['follows_you']}
41
+ end
42
+ return users_list, table
43
+ end
44
+
45
+ def build_followings_list(list, target) #takes a hash of users with ayadn format
46
+ table = init_table
47
+ if target == "me"
48
+ table.title = "List of users you're following".color(:cyan) + "".color(:white)
49
+ else
50
+ table.title = "List of users ".color(:cyan) + "#{target}".color(:red) + " is following ".color(:cyan) + "".color(:white)
51
+ end
52
+ users_list = build_users_array(list)
53
+ build_users_list(users_list, table)
54
+ end
55
+
56
+ def build_followers_list(list, target)
57
+ table = init_table
58
+ if target == "me"
59
+ table.title = "List of your followers".color(:cyan) + "".color(:white)
60
+ else
61
+ table.title = "List of users following ".color(:cyan) + "#{target}".color(:red) + "".color(:white)
62
+ end
63
+ users_list = build_users_array(list)
64
+ build_users_list(users_list, table)
65
+ end
66
+
67
+ def build_muted_list(list)
68
+ table = init_table
69
+ table.title = "List of users you muted".color(:cyan) + "".color(:white)
70
+ users_list = build_users_array(list)
71
+ build_users_list(users_list, table)
72
+ end
73
+
74
+ def build_blocked_list(list)
75
+ table = init_table
76
+ table.title = "List of users you blocked".color(:cyan) + "".color(:white)
77
+ users_list = build_users_array(list)
78
+ build_users_list(users_list, table)
79
+ end
80
+
81
+ def build_users_list(list, table)
82
+ list.each_with_index do |obj, index|
83
+ unless obj[:name].nil?
84
+ table << [ "@#{obj[:username]} ".color(Settings.options[:colors][:username]), "#{obj[:name]}" ]
85
+ else
86
+ table << [ "@#{obj[:username]} ".color(Settings.options[:colors][:username]), "" ]
87
+ end
88
+ table << :separator unless index + 1 == list.length
89
+ end
90
+ table
91
+ end
92
+
93
+ def build_posts(data)
94
+ # builds a hash of hashes, each hash is a normalized post with post id as a key
95
+ posts = {}
96
+
97
+ data.each.with_index(1) do |post, index|
98
+ next if Databases.blacklist[post['source']['name']]
99
+ hashtags = extract_hashtags(post)
100
+ @skip = false
101
+ hashtags.each do |h|
102
+ if Databases.blacklist[h]
103
+ @skip = true
104
+ break
105
+ end
106
+ end
107
+ next if @skip
108
+ mentions= []
109
+ post['entities']['mentions'].each { |m| mentions << m['name'] }
110
+ mentions.each do |m|
111
+ if Databases.blacklist["@" + m]
112
+ @skip = true
113
+ break
114
+ end
115
+ end
116
+ next if @skip
117
+
118
+ if post['user'].has_key?('name')
119
+ name = post['user']['name'].to_s.force_encoding("UTF-8")
120
+ else
121
+ name = "(no name)"
122
+ end
123
+
124
+ source = post['source']['name'].to_s.force_encoding("UTF-8")
125
+
126
+ values = {
127
+ count: index,
128
+ id: post['id'].to_i,
129
+ name: name,
130
+ thread_id: post['thread_id'],
131
+ username: post['user']['username'],
132
+ handle: "@#{post['user']['username']}",
133
+ type: post['user']['type'],
134
+ date: parsed_time(post['created_at']),
135
+ you_starred: post['you_starred'],
136
+ source_name: source,
137
+ source_link: post['source']['link'],
138
+ canonical_url: post['canonical_url']
139
+ }
140
+
141
+ values[:tags] = hashtags
142
+
143
+ values[:links] = extract_links(post)
144
+
145
+ if post['repost_of']
146
+ values[:is_repost] = true
147
+ values[:repost_of] = post['repost_of']['id']
148
+ values[:original_poster] = post['repost_of']['user']['username']
149
+ else
150
+ values[:is_repost] = false
151
+ values[:repost_of] = nil
152
+ values[:original_poster] = post['user']['username']
153
+ end
154
+
155
+ unless post['text'].nil?
156
+ values[:raw_text] = post['text']
157
+ values[:text] = colorize_text(post['text'], mentions)
158
+ else
159
+ values[:raw_text] = ""
160
+ values[:text] = "(no text)"
161
+ end
162
+
163
+ unless post['num_stars'].nil? || post['num_stars'] == 0
164
+ values[:is_starred] = true
165
+ values[:num_stars] = post['num_stars']
166
+ else
167
+ values[:is_starred] = false
168
+ values[:num_stars] = 0
169
+ end
170
+
171
+ if post['reply_to']
172
+ values[:is_reply] = true
173
+ values[:reply_to] = post['reply_to']
174
+ values[:num_replies] = post['num_replies']
175
+ else
176
+ values[:is_reply] = false
177
+ values[:reply_to] = nil
178
+ values[:num_replies] = 0
179
+ end
180
+ if post['num_reposts']
181
+ values[:num_reposts] = post['num_reposts']
182
+ else
183
+ values[:num_reposts] = 0
184
+ end
185
+
186
+ values[:mentions] = mentions
187
+ values[:directed_to] = mentions.first || false
188
+ values[:checkins], values[:has_checkins] = extract_checkins(post)
189
+
190
+ posts[post['id'].to_i] = values
191
+
192
+ end
193
+ posts
194
+ end
195
+
196
+ def extract_links(post)
197
+ links = []
198
+ post['entities']['links'].each { |l| links << l['url'] }
199
+ unless post['annotations'].nil? || post['annotations'].empty?
200
+ post['annotations'].each do |ann|
201
+ if ann['type'] == "net.app.core.oembed"
202
+ links << ann['value']['embeddable_url'] if ann['value']['embeddable_url']
203
+ end
204
+ end
205
+ end
206
+ links.uniq!
207
+ links
208
+ end
209
+
210
+ def extract_hashtags(post)
211
+ tags = []
212
+ post['entities']['hashtags'].each { |h| tags << h['name'] }
213
+ tags
214
+ end
215
+
216
+ def build_channels(data)
217
+ channels = []
218
+ data.each { |ch| channels << ch }
219
+ bucket = []
220
+ puts "Downloading new channels and unknown users ids.\nThis is a one time operation, ids are being recorded in a database.\n\nPlease wait, it could take a while if you have many channels...".color(:cyan)
221
+ chan = Struct.new(:id, :num_messages, :subscribers, :type, :owner, :annotations, :readers, :editors, :writers, :you_subscribed, :unread, :recent_message_id, :recent_message)
222
+ channels.each do |ch|
223
+ unless ch['writers']['user_ids'].empty?
224
+ usernames = []
225
+ ch['writers']['user_ids'].each do |id|
226
+ db = Databases.users[id]
227
+ unless db.nil?
228
+ usernames << "@" + db.keys.first
229
+ else
230
+ resp = API.new.get_user(id)
231
+ usernames << "@" + resp['data']['username']
232
+ Databases.add_to_users_db(id, resp['data']['username'], resp['data']['name'])
233
+ end
234
+ end
235
+ usernames << Settings.config[:identity][:handle] unless usernames.length == 1 && usernames.first == Settings.config[:identity][:handle]
236
+ writers = usernames.join(", ")
237
+ else
238
+ writers = Settings.config[:identity][:handle]
239
+ end
240
+ if ch['has_unread']
241
+ unread = "This channel has unread message(s)"
242
+ else
243
+ unread = "No unread messages"
244
+ end
245
+ bucket << chan.new(ch['id'], ch['counts']['messages'], ch['counts']['subscribers'], ch['type'], ch['owner'], ch['annotations'], ch['readers'], ch['editors'], writers, ch['you_subscribed'], unread, ch['recent_message_id'], ch['recent_message'])
246
+ end
247
+ puts "\e[H\e[2J"
248
+ bucket
249
+ end
250
+
251
+ def parsed_time(string)
252
+ "#{string[0...10]} #{string[11...19]}"
253
+ end
254
+
255
+ def self.add_arobase_if_missing(username) # expects an array of username(s), works on the first one and outputs a string
256
+ unless username.first == "me"
257
+ username = username.first.chars.to_a
258
+ username.unshift("@") unless username.first == "@"
259
+ else
260
+ username = "me".chars.to_a
261
+ end
262
+ username.join
263
+ end
264
+
265
+ def self.remove_arobase_if_present(username)
266
+ username = username.chars.to_a
267
+ username.shift if username.first == "@"
268
+ username.join
269
+ end
270
+
271
+ def self.extract_users(resp)
272
+ users_hash = {}
273
+ resp['data'].each do |item|
274
+ users_hash[item['id']] = [item['username'], item['name'], item['you_follow'], item['follows_you']]
275
+ end
276
+ users_hash
277
+ end
278
+
279
+ private
280
+
281
+ def colorize_text(text, mentions)
282
+ handles = Array.new
283
+ mentions.each {|username| handles << "@#{username}"}
284
+ words = Array.new
285
+ sentences = Array.new
286
+ hashtag_color = Settings.options[:colors][:hashtags]
287
+ mention_color = Settings.options[:colors][:mentions]
288
+ text.scan(/^.+[\r\n]*/) do |sentence|
289
+ sentence.split(' ').each do |word|
290
+ if word =~ /#\w+/
291
+ words << word.gsub(/#([A-Za-z0-9_]{1,255})(?![\w+])/, '#\1'.color(hashtag_color))
292
+ elsif word =~ /@\w+/
293
+ if handles.include?(word) || word =~ /@\w+[:]/
294
+ words << word.gsub(/@([A-Za-z0-9_]{1,20})(?![\w+])/, '@\1'.color(mention_color))
295
+ else
296
+ words << word
297
+ end
298
+ else
299
+ words << word
300
+ end
301
+ end
302
+ sentences << words.join(' ')
303
+ words = Array.new
304
+ end
305
+ sentences.join("\n")
306
+ end
307
+
308
+ def init_table
309
+ Terminal::Table.new do |t|
310
+ t.style = { :width => Settings.options[:formats][:table][:width] }
311
+ end
312
+ end
313
+
314
+ def build_users_array(list)
315
+ users_list = []
316
+ list.each do |key, value|
317
+ users_list << {:username => value[0], :name => value[1], :you_follow => value[2], :follows_you => value[3]}
318
+ end
319
+ users_list
320
+ end
321
+
322
+ def extract_checkins(post)
323
+ has_checkins = false
324
+ checkins = {}
325
+ unless post['annotations'].nil? || post['annotations'].empty?
326
+ post['annotations'].each do |obj|
327
+ case obj['type']
328
+ when "net.app.core.checkin", "net.app.ohai.location"
329
+ has_checkins = true
330
+ checkins = {
331
+ name: obj['value']['name'],
332
+ address: obj['value']['address'],
333
+ address_extended: obj['value']['address_extended'],
334
+ locality: obj['value']['locality'],
335
+ postcode: obj['value']['postcode'],
336
+ country_code: obj['value']['country_code'],
337
+ website: obj['value']['website'],
338
+ telephone: obj['value']['telephone']
339
+ }
340
+ unless obj['value']['categories'].nil?
341
+ unless obj['value']['categories'][0].nil?
342
+ checkins[:categories] = obj['value']['categories'][0]['labels'].join(", ")
343
+ end
344
+ end
345
+ unless obj['value']['factual_id'].nil?
346
+ checkins[:factual_id] = obj['value']['factual_id']
347
+ end
348
+ unless obj['value']['longitude'].nil?
349
+ checkins[:longitude] = obj['value']['longitude']
350
+ checkins[:latitude] = obj['value']['latitude']
351
+ end
352
+ #when "net.app.core.oembed"
353
+ #has_checkins = true
354
+ #checkins[:embeddable_url] = obj['value']['embeddable_url']
355
+ end
356
+ end
357
+ end
358
+ return checkins, has_checkins
359
+ end
360
+
361
+ end
362
+ end
data/spec/helpers.rb ADDED
@@ -0,0 +1,19 @@
1
+ require 'stringio'
2
+
3
+ def capture_stdout(&blk)
4
+ old = $stdout
5
+ $stdout = fake = StringIO.new
6
+ blk.call
7
+ fake.string
8
+ ensure
9
+ $stdout = old
10
+ end
11
+
12
+ def capture_stderr(&blk)
13
+ old = $stderr
14
+ $stderr = fake = StringIO.new
15
+ blk.call
16
+ fake.string
17
+ ensure
18
+ $stderr = old
19
+ end
@@ -0,0 +1,160 @@
1
+ {
2
+ "meta": {
3
+ "code": 200
4
+ },
5
+ "data": {
6
+ "you_muted": false,
7
+ "you_can_subscribe": true,
8
+ "is_following": true,
9
+ "is_follower": true,
10
+ "timezone": "Europe/Paris",
11
+ "you_follow": true,
12
+ "counts": {
13
+ "following": 412,
14
+ "posts": 4513,
15
+ "followers": 230,
16
+ "stars": 214
17
+ },
18
+ "canonical_url": "https://alpha.app.net/ericd",
19
+ "id": "69904",
20
+ "locale": "fr_FR",
21
+ "type": "human",
22
+ "annotations": [
23
+ {
24
+ "type": "iso",
25
+ "value": {
26
+ "iso": [
27
+
28
+ ]
29
+ }
30
+ },
31
+ {
32
+ "type": "iama",
33
+ "value": {
34
+ "iama": [
35
+
36
+ ]
37
+ }
38
+ },
39
+ {
40
+ "type": "net.app.core.directory.blog",
41
+ "value": {
42
+ "url": "http://aya.io"
43
+ }
44
+ },
45
+ {
46
+ "type": "net.app.core.directory.twitter",
47
+ "value": {
48
+ "username": "ritsz"
49
+ }
50
+ },
51
+ {
52
+ "type": "net.chimpli.media",
53
+ "value": {
54
+ "image": "17273",
55
+ "audio": "17275",
56
+ "video": "17274"
57
+ }
58
+ },
59
+ {
60
+ "type": "net.app.core.directory.homepage",
61
+ "value": {
62
+ "url": "http://aya.io"
63
+ }
64
+ },
65
+ {
66
+ "type": "com.appnetizens.userinput.nationality",
67
+ "value": {
68
+ "nationality": "FR"
69
+ }
70
+ },
71
+ {
72
+ "type": "com.appnetizens.userinput.country",
73
+ "value": {
74
+ "country": "FR"
75
+ }
76
+ },
77
+ {
78
+ "type": "com.appnetizens.userinput.gender",
79
+ "value": {
80
+ "gender": "m"
81
+ }
82
+ },
83
+ {
84
+ "type": "com.appnetizens.userinput.birthday",
85
+ "value": {
86
+ "birthday": "1973-08-14"
87
+ }
88
+ },
89
+ {
90
+ "type": "net.app.duerig.party-line",
91
+ "value": {
92
+ "channel": "25807"
93
+ }
94
+ }
95
+ ],
96
+ "username": "ericd",
97
+ "avatar_image": {
98
+ "url": "https://d2rfichhc2fb9n.cloudfront.net/image/5/mVV5CTXu8ydA0Nb5a2X3-4YF7Xl7InMiOiJzMyIsImIiOiJhZG4tdXNlci1hc3NldHMiLCJrIjoiYXNzZXRzL3VzZXIvMTgvNGIvNzAvMTg0YjcwMDAwMDAwMDAwMC5qcGciLCJvIjoiIn0",
99
+ "width": 1079,
100
+ "is_default": false,
101
+ "height": 1079
102
+ },
103
+ "description": {
104
+ "text": "Sound engineer (rec, edit, mix) by day, coder by night. \r\nBlogs: http://aya.io \r\nCode: http://github.com/ericdke\r\n@Ayadn - App.net command-line client. http://ayadn-app.net",
105
+ "html": "<span itemscope=\"https://app.net/schemas/Post\">Sound engineer (rec, edit, mix) by day, coder by night. &#13;<br>Blogs: <a href=\"http://aya.io\">http://aya.io</a> &#13;<br>Code: <a href=\"http://github.com/ericdke\">http://github.com/ericdke</a>&#13;<br><span data-mention-id=\"188367\" data-mention-name=\"ayadn\" itemprop=\"mention\">@Ayadn</span> - <a href=\"http://App.net\">App.net</a> command-line client. <a href=\"http://ayadn-app.net\">http://ayadn-app.net</a></span>",
106
+ "entities": {
107
+ "mentions": [
108
+ {
109
+ "pos": 114,
110
+ "id": "188367",
111
+ "len": 6,
112
+ "name": "ayadn"
113
+ }
114
+ ],
115
+ "hashtags": [
116
+
117
+ ],
118
+ "links": [
119
+ {
120
+ "url": "http://aya.io",
121
+ "text": "http://aya.io",
122
+ "pos": 65,
123
+ "len": 13
124
+ },
125
+ {
126
+ "url": "http://github.com/ericdke",
127
+ "text": "http://github.com/ericdke",
128
+ "pos": 87,
129
+ "len": 25
130
+ },
131
+ {
132
+ "url": "http://App.net",
133
+ "text": "App.net",
134
+ "pos": 123,
135
+ "len": 7
136
+ },
137
+ {
138
+ "url": "http://ayadn-app.net",
139
+ "text": "http://ayadn-app.net",
140
+ "pos": 152,
141
+ "len": 20
142
+ }
143
+ ]
144
+ }
145
+ },
146
+ "is_muted": false,
147
+ "follows_you": true,
148
+ "you_can_follow": true,
149
+ "verified_domain": "aya.io",
150
+ "name": "Eric Dejonckheere",
151
+ "created_at": "2013-04-08T23:25:52Z",
152
+ "cover_image": {
153
+ "url": "https://d2rfichhc2fb9n.cloudfront.net/image/5/E68v2rPcBbON0UDYfQTgs9NeqAp7InMiOiJzMyIsImIiOiJhZG4tdXNlci1hc3NldHMiLCJrIjoiYXNzZXRzL3VzZXIvMzgvNGIvNzAvMzg0YjcwMDAwMDAwMDAwMC5qcGciLCJvIjoiIn0",
154
+ "width": 2455,
155
+ "is_default": false,
156
+ "height": 929
157
+ },
158
+ "you_blocked": false
159
+ }
160
+ }