ayadn 0.6.4 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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
+ }