ayadn 3.0 → 4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -4
  3. data/CHANGELOG.md +12 -4
  4. data/README.md +2 -5
  5. data/ayadn.gemspec +0 -2
  6. data/doc/01-index.md +0 -3
  7. data/doc/02-install.md +0 -4
  8. data/doc/06-post.md +0 -16
  9. data/doc/{18-contact.md → 16-contact.md} +0 -0
  10. data/doc/{19-examples.md → 17-examples.md} +0 -0
  11. data/doc/18-develop.md +165 -0
  12. data/lib/ayadn/action.rb +206 -396
  13. data/lib/ayadn/alias.rb +1 -1
  14. data/lib/ayadn/annotations.rb +15 -27
  15. data/lib/ayadn/api.rb +39 -28
  16. data/lib/ayadn/app.rb +19 -29
  17. data/lib/ayadn/authorize.rb +22 -13
  18. data/lib/ayadn/blacklist.rb +6 -19
  19. data/lib/ayadn/channel_object.rb +75 -0
  20. data/lib/ayadn/check.rb +19 -11
  21. data/lib/ayadn/cnx.rb +9 -15
  22. data/lib/ayadn/databases.rb +15 -27
  23. data/lib/ayadn/debug.rb +9 -9
  24. data/lib/ayadn/descriptions.rb +1 -99
  25. data/lib/ayadn/diagnostics.rb +16 -15
  26. data/lib/ayadn/endpoints.rb +18 -22
  27. data/lib/ayadn/errors.rb +1 -1
  28. data/lib/ayadn/fileops.rb +12 -12
  29. data/lib/ayadn/filtered_post_object.rb +11 -0
  30. data/lib/ayadn/ids.rb +0 -3
  31. data/lib/ayadn/logs.rb +4 -4
  32. data/lib/ayadn/mark.rb +34 -30
  33. data/lib/ayadn/nicerank.rb +7 -7
  34. data/lib/ayadn/nowplaying.rb +8 -22
  35. data/lib/ayadn/pinboard.rb +8 -12
  36. data/lib/ayadn/post.rb +18 -18
  37. data/lib/ayadn/post_object.rb +118 -0
  38. data/lib/ayadn/preferences_object.rb +290 -0
  39. data/lib/ayadn/profile.rb +2 -2
  40. data/lib/ayadn/scroll.rb +58 -67
  41. data/lib/ayadn/search.rb +22 -15
  42. data/lib/ayadn/set.rb +93 -83
  43. data/lib/ayadn/settings.rb +25 -33
  44. data/lib/ayadn/status.rb +24 -26
  45. data/lib/ayadn/stream.rb +68 -66
  46. data/lib/ayadn/stream_object.rb +56 -0
  47. data/lib/ayadn/switch.rb +2 -2
  48. data/lib/ayadn/user_object.rb +116 -0
  49. data/lib/ayadn/version.rb +1 -1
  50. data/lib/ayadn/view.rb +255 -278
  51. data/lib/ayadn/workers.rb +172 -174
  52. data/spec/integration/action_spec.rb +55 -34
  53. data/spec/mock/ayadn.sqlite +0 -0
  54. data/spec/unit/annotations_spec.rb +54 -41
  55. data/spec/unit/api_spec.rb +78 -7
  56. data/spec/unit/blacklistworkers_spec.rb +92 -20
  57. data/spec/unit/databases_spec.rb +117 -36
  58. data/spec/unit/endpoints_spec.rb +82 -10
  59. data/spec/unit/nicerank_spec.rb +56 -27
  60. data/spec/unit/post_spec.rb +94 -21
  61. data/spec/unit/set_spec.rb +141 -210
  62. data/spec/unit/view_spec.rb +105 -32
  63. data/spec/unit/workers_spec.rb +143 -52
  64. metadata +12 -37
  65. data/doc/16-movie.md +0 -39
  66. data/doc/17-tvshow.md +0 -46
  67. data/lib/ayadn/nowwatching.rb +0 -118
  68. data/lib/ayadn/tvshow.rb +0 -162
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 669aa931c86f3c1503fcb018d9ae061c4807f821
4
- data.tar.gz: 9e0b95916d303a4f163a751db01533a6cc95ba5f
3
+ metadata.gz: a6ebd67992a3895ceefe25d38e32281b3f40a340
4
+ data.tar.gz: 79aa8fc478f5610abc5295c8b54fc80d24becb4c
5
5
  SHA512:
6
- metadata.gz: 94b1ffcc7e1ef7c8350b02da3e56f28e0164a2b13ef63ecf2ece988c36fd70ebec9edf690b6aef921707da2ef385e9fbebe7bad0667ababde8656d9d1dd6297c
7
- data.tar.gz: 4ce0e8e000c1306678ebaddcc5d2a6dcae772e8cb3bdc3c866d9679d93920b8f5dbf48896899022e5c38a5c6d821509095c5f684a6964b78a23dde3a37e77a1a
6
+ metadata.gz: 5bb3f4b17dcb074d1ed73a080e54c125f5c9640db70c71bf719e0a3bd189298f8f84cca6dc0df626436526467ebb722c1c53444e8de6c3f7a853252112fdc4b8
7
+ data.tar.gz: a1b6c4e8d91571e74088b0ce64eaf6b1a009e1ec6356df0a1dc8411eeaf61fdc5d833a1df151bf004d74d1b6bc1a9cd7db22466a82d0cfe30d133d8af53addb4
data/.travis.yml CHANGED
@@ -2,10 +2,7 @@ language: ruby
2
2
  cache: bundler
3
3
 
4
4
  rvm:
5
- - "2.2.3"
6
- - "2.2.0"
7
- - "2.1.0"
8
- - "2.0.0"
5
+ - "2.3.0"
9
6
 
10
7
  script: 'bundle exec rake spec'
11
8
 
data/CHANGELOG.md CHANGED
@@ -1,3 +1,15 @@
1
+ ## 4.0- 2016-05-01 - 'Mr. Robot'
2
+
3
+ - Fixed: when possible, unauthorizing a user keeps the current user logged
4
+ - Fixed: inconsistencies in the 'set' command
5
+ - Fixed: malformed address for checkins in some cases
6
+ - Fixed: a case where changing the root URL was ignored
7
+ - Fixed: typos in descriptions
8
+ - Fixed: occasional crash in the 'random' stream
9
+ - New: documentation for developing with the Ayadn codebase
10
+ - Deprecated: 'movie' and 'tvshow' commands (we don't have access to imdb anymore)
11
+ - Code cleanup
12
+
1
13
  ## 3.0 - 2015-11-07 - 'Edge Of Tomorrow'
2
14
 
3
15
  - New: option to set an alternative base URL for the API calls
@@ -36,10 +48,6 @@
36
48
 
37
49
  - Fix: bug in add/remove user/mention to the blacklist
38
50
 
39
- ## 2.0.8 - 2015-02-11
40
-
41
- *ignored*
42
-
43
51
  ## 2.0.7 - 2015-02-04 - 'Diacritics'
44
52
 
45
53
  - Fix: users list if an element isn't in UTF8
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  [![Gem Version](https://badge.fury.io/rb/ayadn.svg)](http://badge.fury.io/rb/ayadn)
2
- [![Build Status](https://travis-ci.org/ericdke/na.svg?branch=master)](https://travis-ci.org/ericdke/na)
3
- [![Flattr this](http://api.flattr.com/button/flattr-badge-large.png)](https://flattr.com/submit/auto?user_id=ericdejonckheere&url=https://github.com/ericdke/na&title=Ayadn&language=&tags=github&category=software)
2
+ [![Build Status](https://travis-ci.org/ericdke/na.svg?branch=master)](https://travis-ci.org/ericdke/na)
3
+ [![Flattr this](http://api.flattr.com/button/flattr-badge-large.png)](https://flattr.com/submit/auto?user_id=ericdejonckheere&url=https://github.com/ericdke/na&title=Ayadn&language=&tags=github&category=software)
4
4
 
5
5
  AYADN
6
6
  =====
@@ -15,7 +15,6 @@ With Ayadn, you can use all the features of App.net from the console without any
15
15
  - write fast single-line or complex multi-line posts
16
16
  - embed pictures in posts
17
17
  - embed online video in posts
18
- - embed movie poster in posts
19
18
  - view/scroll the Global stream with spammers filtered out
20
19
  - view/scroll posts from specific users or posts mentioning a user
21
20
  - follow/unfollow users, star/repost/mute/block/unstar/etc
@@ -26,8 +25,6 @@ With Ayadn, you can use all the features of App.net from the console without any
26
25
  - list and download your files
27
26
  - view (and write to) all your channels including Broadcasts, Patter, Ohai, etc
28
27
  - 'nowplaying' from iTunes, Deezer or Last.fm, with album art and store link
29
- - 'movie' creates a post from a movie title with poster, plot, and link
30
- - 'tvshow' creates a post from a TV show title with poster, plot, and link
31
28
  - view/send private messages
32
29
  - view geolocation data in streams
33
30
  - create silent black lists of users, mentions, clients or hashtags
data/ayadn.gemspec CHANGED
@@ -28,8 +28,6 @@ Gem::Specification.new do |spec|
28
28
  spec.add_dependency "terminal-table", "~> 1.4"
29
29
  spec.add_dependency "pinboard", "~> 0.1"
30
30
  spec.add_dependency "unicode_utils", "~> 1.4"
31
- spec.add_dependency "spotlite", "~> 0.8"
32
- spec.add_dependency "tvdb_party", "~> 0.7"
33
31
  spec.add_dependency "amalgalite", "~> 1.3"
34
32
  spec.add_dependency "fast_cache", "~> 1.0"
35
33
 
data/doc/01-index.md CHANGED
@@ -11,7 +11,6 @@ With Ayadn, you can use all the features of App.net from the console without any
11
11
  - write fast single-line or complex multi-line posts
12
12
  - embed pictures in posts
13
13
  - embed online video in posts
14
- - embed movie poster in posts
15
14
  - view/scroll the Global stream with spammers filtered out
16
15
  - view/scroll posts from specific users or posts mentioning a user
17
16
  - follow/unfollow users, star/repost/mute/block/unstar/etc
@@ -22,8 +21,6 @@ With Ayadn, you can use all the features of App.net from the console without any
22
21
  - list and download your files
23
22
  - view (and write to) all your channels including Broadcasts, Patter, Ohai, etc
24
23
  - 'nowplaying' from iTunes, Deezer or Last.fm, with album art and store link
25
- - 'movie' creates a post from a movie title with poster, plot, and link
26
- - 'tvshow' creates a post from a TV show title with poster, plot, and link
27
24
  - view/send private messages
28
25
  - view geolocation data in streams
29
26
  - create silent black lists of users, mentions, clients or hashtags
data/doc/02-install.md CHANGED
@@ -49,11 +49,7 @@ Ayadn depends upon these Gems:
49
49
  pinboard (export to Pinboard)
50
50
  rainbow (text UI utilities)
51
51
  rest-client (networking)
52
- spotlite (IMDb access)
53
52
  terminal-table (text UI utilities)
54
53
  thor (commands and options parsing)
55
- tvdb_party (TVDb access)
56
54
  unicode_utils (text utilities)
57
- daybreak (Ruby data store)
58
55
 
59
- *Note: the "daybreak" dependency is only needed for 1.x to 2.x migrations. Deprecated since Ayadn 3.0.*
data/doc/06-post.md CHANGED
@@ -164,19 +164,3 @@ ayadn pm @ericd -Y https://www.youtube.com/watch?v=Ei8CFin00PY
164
164
  ```
165
165
 
166
166
  *Note: unfortunately, very few App.net clients treat video embedding properly. So I would advise to include the video URL in the text body anyway, for better compatibility.*
167
-
168
- # EMBED MOVIE POSTER
169
-
170
- You can embed a movie poster in a normal post with option `-M`.
171
-
172
- This is compatible with other options, eg embedding other images.
173
-
174
- *Warning: contrary to the `movie` command, this option doesn't check with the user if the movie is valid. The poster is retrieved from IMDb and is automatically embedded in the post.*
175
-
176
- Examples:
177
-
178
- ```
179
- ayadn -P "I'll be back" -M terminator
180
- ayadn -W -M truman show -E ~/Pics/my_face.jpg
181
- ayadn -R 23362460 -M the dark knight
182
- ```
File without changes
File without changes
data/doc/18-develop.md ADDED
@@ -0,0 +1,165 @@
1
+ # DEVELOP
2
+
3
+ Ayadn is an Open Source project under the MIT license.
4
+
5
+ You can develop with Ayadn in several ways: build your own Ayadn-based client, add features to the official one, fix bugs, etc.
6
+
7
+ ## First step
8
+
9
+ The [repository](https://github.com/ericdke/na) doesn't always have a development branch. If it has one, use it - otherwise it means the previous one has been merged or dismissed and you have to fork the master branch.
10
+
11
+ The first step in any case is to [fork](https://help.github.com/articles/fork-a-repo/) then clone the repository:
12
+
13
+ $ git clone git@github.com:you/na.git && cd na
14
+
15
+ Run Bundler to ensure you have the proper dependencies installed:
16
+
17
+ $ bundle
18
+
19
+ If you have the Ayadn Gem already installed, the currently logged in user will be used:
20
+
21
+ $ ./bin/ayadn -tl
22
+
23
+ Otherwise you will have to authorize first:
24
+
25
+ $ ./bin/ayadn -auth
26
+
27
+ Then for the Ayadn credentials itself, they're not included in the repository, so you will have to make a new `lib/ids.rb` file following this template:
28
+
29
+ <pre>
30
+ <code class="ruby">
31
+ # encoding: utf-8
32
+ module Ayadn
33
+ class Settings
34
+ # Mandatory
35
+ CLIENT_ID = "xxx"
36
+ end
37
+ class Endpoints
38
+ # Mandatory
39
+ CALLBACK_URL = "http://yourdomain.com/appname.html"
40
+ end
41
+ class NowPlaying
42
+ # Optional
43
+ AFFILIATE_SUFFIX = "&at=xxx&ct=appname"
44
+ DEEZER_APP_ID = "xxx"
45
+ DEEZER_AUTH_URL = "http://yourdomain.com/deezer.html"
46
+ end
47
+ end
48
+ </code>
49
+ </pre>
50
+
51
+ *Be careful to not modify your `.gitignore` file, these credentials should always be ignored and its contents never be accessible from any public location.*
52
+
53
+ ## Tests
54
+
55
+ ### RSPEC
56
+
57
+ Ayadn has a set of *rspec* unit tests.
58
+
59
+ They're convenient but far from perfect, so you're welcome to improve them or add new ones (some classes like `FileOps` or `CNX` notably lack testing at the moment).
60
+
61
+ Ayadn tests include mock data (adapted from real data) like stream and user JSON responses, and also a dedicated SQLite mock file.
62
+
63
+ Just launch `rspec` to run the battery of tests:
64
+
65
+ $ rspec
66
+
67
+ ### Debug
68
+
69
+ For live simulations and debug sessions, launch *irb* or *pry* then require `ayadn`:
70
+
71
+ $ pry
72
+ pry(main)> require_relative "lib/ayadn"
73
+
74
+ Then, to be "live", initialize the `Action` class:
75
+
76
+ pry(main)> action = Ayadn::Action.new
77
+
78
+ You can now issue commands to Ayadn, for example:
79
+
80
+ pry(main)> action.userinfo ["me"], {"raw"=>true}
81
+
82
+ *Be careful to use a test account when using Ayadn live like this...*
83
+
84
+ ## Pull requests
85
+
86
+ You've added a feature or fixed a bug? Awesome! You're a hero!
87
+
88
+ Now you just have to open a Pull Request and wait until I examine your proposition and give you feedback.
89
+
90
+ ## Make your own version
91
+
92
+ If you want to use (parts of) this codebase to create your own version of Ayadn, you will have to fill in several identifiers before release:
93
+
94
+ - Ayadn's client id
95
+ - Ayadn's callback URL for authentication
96
+
97
+ Optionally, if you keep those features:
98
+
99
+ - NowPlaying's iTunes affiliate suffix
100
+ - Deezer App id
101
+ - Deezer callback URL for authentication
102
+
103
+ **You will also have to create a new application in your App.net dashboard.**
104
+
105
+ *Don't forget to follow the License's rules before release. And although not mandatory, I'll be glad if you send me a message about your work. :)*
106
+
107
+ ## Dive In
108
+
109
+ It's always hard to dive in an unknown codebase.
110
+
111
+ It's even more difficult when this codebase has a lot of legacy code and most of it is mediocre.
112
+
113
+ Unfortunately Ayadn is like that. ¯\(ツ)/¯
114
+
115
+ It was my first "big" app when I started developing in Ruby, so the code reflects that and suffers from the "lasagna syndrome": layers of new code upon layers of old code during years of development.
116
+
117
+ The biggest issue with the current codebase is not the code quality but its architecture, which is, to be polite, far from ideal, and not even really based on OOP principles - there's a lot of purely imperative code and other atrocities.
118
+
119
+ That being said, I believe my nomenclature for most methods and properties is self-explanatory enough, and there's comments where the code is a bit hairy or unclear.
120
+
121
+ As for the architecture, here's a brief overview.
122
+
123
+ ### CLI
124
+
125
+ The first important step of the control flow is the `app.rb` class which intercepts the commands from the CLI.
126
+
127
+ This class inherits from `Thor` and only serves the purpose of being an interface for the CLI. Thor handles the IO of arguments and options, and also generates descriptions and help for the CLI commands.
128
+
129
+ From there, most commands will pass options and commands to the main dispatcher, the `Action` class (`action.rb`).
130
+
131
+ It initializes the user account credentials from the SQLite database and creates the main objects.
132
+
133
+ This is why you have to initialize `Action` yourself to use a live account if you're not using the CLI via `App`.
134
+
135
+ Follow what `Action`'s `initialize` does to understand the init process - it's scattered but easy to grasp.
136
+
137
+ ### Action
138
+
139
+ The `Action` class launches the commands themselves (all commands that needed to have the credentials initialized).
140
+
141
+ Most of the commands in `Action` actually launch instances of the `Stream` class - other commands are implemented in `Action` itself or in topical classes like `Set` or `BlackList`.
142
+
143
+ ### API
144
+
145
+ The `Endpoints` class holds all necessary endpoints and URLs.
146
+
147
+ The `API` class uses them to get the JSON responses from the ADN servers (using the `CNX` class for networking) and return a parsed version as a Ruby Hash. This class also handles the generation of query URLs from passed arguments.
148
+
149
+ Most commands will create custom Ayadn objects from these hashes, like `StreamObject` or `UserObject`, but for legacy reasons this is not how the global architecture works, and there's still places where the Hashes are used directly instead of handling custom objects.
150
+
151
+ ### Main classes
152
+
153
+ The most used classes are the "God" classes `Workers`, `View` and `Status`.
154
+
155
+ `Workers` holds all utilities and workforce operations.
156
+
157
+ `View` creates colored and formatted text outputs.
158
+
159
+ `Status` holds all text messages: interface, errors, statuses, help, etc.
160
+
161
+ The `Workers` and `View` classes could really appreciate a rework and some DRY refactoring... but they currently work pretty well without known bugs so it's already something I suppose. ;)
162
+
163
+
164
+
165
+
data/lib/ayadn/action.rb CHANGED
@@ -10,10 +10,10 @@ module Ayadn
10
10
 
11
11
  def initialize
12
12
  @api = API.new
13
- @view = View.new
14
- @workers = Workers.new
15
- @status = Status.new
16
- @check = Check.new
13
+ @status = Status.new
14
+ @workers = Workers.new(@status)
15
+ @view = View.new(@status, @workers)
16
+ @check = Check.new(@status)
17
17
  Settings.load_config
18
18
  Settings.get_token
19
19
  Settings.init_config
@@ -21,93 +21,32 @@ module Ayadn
21
21
  Databases.open_databases
22
22
  end
23
23
 
24
- def method_missing(meth, options)
25
- case meth.to_s
26
- when 'unified', 'checkins', 'global', 'trending', 'photos', 'conversations', 'interactions'
27
- begin
28
- Settings.options[:timeline][:compact] = true if options[:compact] == true
29
- stream = Stream.new(@api, @view, @workers)
30
- stream.send(meth.to_sym, options)
31
- rescue => e
32
- Errors.global_error({error: e, caller: caller, data: [meth, options]})
33
- end
34
- else
35
- super
36
- end
37
- end
38
-
39
- def mentions(username, options)
40
- begin
41
- Settings.options[:timeline][:compact] = true if options[:compact] == true
42
- stream = Stream.new(@api, @view, @workers)
43
- stream.mentions(username, options)
44
- rescue => e
45
- Errors.global_error({error: e, caller: caller, data: [username, options]})
46
- end
47
- end
48
-
49
- def posts(username, options)
50
- begin
51
- Settings.options[:timeline][:compact] = true if options[:compact] == true
52
- stream = Stream.new(@api, @view, @workers)
53
- stream.posts(username, options)
54
- rescue => e
55
- Errors.global_error({error: e, caller: caller, data: [username, options]})
56
- end
57
- end
58
-
59
- def whatstarred(username, options)
24
+ # Uses method_missing to template a single method for several streams
25
+ def method_missing(meth, *args)
60
26
  begin
61
- Settings.options[:timeline][:compact] = true if options[:compact] == true
62
- stream = Stream.new(@api, @view, @workers)
63
- stream.whatstarred(username, options)
64
- rescue => e
65
- Errors.global_error({error: e, caller: caller, data: [username, options]})
66
- end
67
- end
68
-
69
- def whoreposted(post_id, options)
70
- begin
71
- Settings.options[:timeline][:compact] = true if options[:compact] == true
72
- stream = Stream.new(@api, @view, @workers)
73
- stream.whoreposted(post_id, options)
74
- rescue => e
75
- Errors.global_error({error: e, caller: caller, data: [post_id, options]})
76
- end
77
- end
78
-
79
- def whostarred(post_id, options)
80
- begin
81
- Settings.options[:timeline][:compact] = true if options[:compact] == true
82
- stream = Stream.new(@api, @view, @workers)
83
- stream.whostarred(post_id, options)
84
- rescue => e
85
- Errors.global_error({error: e, caller: caller, data: [post_id, options]})
86
- end
87
- end
88
-
89
- def convo(post_id, options)
90
- begin
91
- Settings.options[:timeline][:compact] = true if options[:compact] == true
92
- stream = Stream.new(@api, @view, @workers)
93
- stream.convo(post_id, options)
27
+ options = if args.size > 1
28
+ args[1]
29
+ else
30
+ args[0]
31
+ end
32
+ Settings.options.timeline.compact = true if options[:compact]
33
+ Settings.global.force = true if options[:force]
34
+ stream = Stream.new(@api, @view, @workers, @check, @status)
35
+ case meth
36
+ when :mentions, :posts, :whatstarred, :whoreposted, :whostarred, :convo, :followings, :followers, :messages
37
+ stream.send(meth, args[0], options)
38
+ when :unified, :checkins, :global, :trending, :photos, :conversations, :interactions, :muted, :blocked, :random_posts
39
+ stream.send(meth, options)
40
+ end
94
41
  rescue => e
95
- Errors.global_error({error: e, caller: caller, data: [post_id, options]})
42
+ Errors.global_error({error: e, caller: caller, data: [meth, options]})
96
43
  end
97
44
  end
98
45
 
99
46
  def delete(post_ids, options = {})
100
47
  begin
101
- ids = post_ids.select { |post_id| post_id.is_integer? }
102
- if ids.empty?
103
- @status.error_missing_post_id
104
- exit
105
- end
106
- if options[:force]
107
- Settings.global[:force] = true
108
- else
109
- ids.map! { |post_id| @workers.get_real_post_id(post_id) }
110
- end
48
+ ids = get_posts_ids_or_exit(post_ids) { @status.error_missing_post_id }
49
+ ids = get_real_posts_ids_or_force(options, ids)
111
50
  puts "\n"
112
51
  ids.each do |post_id|
113
52
  @status.deleting_post(post_id)
@@ -127,11 +66,7 @@ module Ayadn
127
66
  end
128
67
  channel = args[0]
129
68
  args.shift
130
- ids = args.select {|message_id| message_id.is_integer?}
131
- if ids.empty?
132
- @status.error_missing_message_id
133
- exit
134
- end
69
+ ids = get_posts_ids_or_exit(args) { @status.error_missing_message_id }
135
70
  channel_id = @workers.get_channel_id_from_alias(channel)
136
71
  puts "\n"
137
72
  ids.each do |message_id|
@@ -146,8 +81,8 @@ module Ayadn
146
81
 
147
82
  def unfollow(usernames)
148
83
  begin
149
- @check.no_username(usernames)
150
- users = @workers.all_but_me(usernames)
84
+ # Verify CLI input, remove current user from list (you never know) to avoid API error
85
+ users = get_all_usernames_but_me(usernames)
151
86
  puts "\n"
152
87
  @status.unfollowing(users.join(','))
153
88
  users.each do |user|
@@ -161,8 +96,7 @@ module Ayadn
161
96
 
162
97
  def follow(usernames)
163
98
  begin
164
- @check.no_username(usernames)
165
- users = @workers.all_but_me(usernames)
99
+ users = get_all_usernames_but_me(usernames)
166
100
  puts "\n"
167
101
  @status.following(users.join(','))
168
102
  users.each do |user|
@@ -176,8 +110,7 @@ module Ayadn
176
110
 
177
111
  def unmute(usernames)
178
112
  begin
179
- @check.no_username(usernames)
180
- users = @workers.all_but_me(usernames)
113
+ users = get_all_usernames_but_me(usernames)
181
114
  puts "\n"
182
115
  @status.unmuting(users.join(','))
183
116
  users.each do |user|
@@ -191,8 +124,7 @@ module Ayadn
191
124
 
192
125
  def mute(usernames)
193
126
  begin
194
- @check.no_username(usernames)
195
- users = @workers.all_but_me(usernames)
127
+ users = get_all_usernames_but_me(usernames)
196
128
  puts "\n"
197
129
  @status.muting(users.join(','))
198
130
  users.each do |user|
@@ -206,8 +138,7 @@ module Ayadn
206
138
 
207
139
  def unblock(usernames)
208
140
  begin
209
- @check.no_username(usernames)
210
- users = @workers.all_but_me(usernames)
141
+ users = get_all_usernames_but_me(usernames)
211
142
  puts "\n"
212
143
  @status.unblocking(users.join(','))
213
144
  users.each do |user|
@@ -221,8 +152,7 @@ module Ayadn
221
152
 
222
153
  def block(usernames)
223
154
  begin
224
- @check.no_username(usernames)
225
- users = @workers.all_but_me(usernames)
155
+ users = get_all_usernames_but_me(usernames)
226
156
  puts "\n"
227
157
  @status.blocking(users.join(','))
228
158
  users.each do |user|
@@ -236,22 +166,18 @@ module Ayadn
236
166
 
237
167
  def repost(post_ids, options = {})
238
168
  begin
239
- ids = post_ids.select { |post_id| post_id.is_integer? }
240
- if ids.empty?
241
- @status.error_missing_post_id
242
- exit
243
- end
244
- if options[:force]
245
- Settings.global[:force] = true
246
- else
247
- ids.map! { |post_id| @workers.get_real_post_id(post_id) }
248
- end
169
+ ids = get_posts_ids_or_exit(post_ids) { @status.error_missing_post_id }
170
+ ids = get_real_posts_ids_or_force(options, ids)
249
171
  puts "\n"
250
172
  ids.each do |post_id|
251
173
  @status.reposting(post_id)
174
+ # Retrieve the post we want to repost
252
175
  resp = @api.get_details(post_id)
176
+ # Verify it hasn't been already reposted by us
253
177
  @check.already_reposted(resp)
178
+ # Maybe the post is already a repost by someone else?
254
179
  id = @workers.get_original_id(post_id, resp)
180
+ # Repost then verify it has been done
255
181
  @check.has_been_reposted(id, @api.repost(id))
256
182
  end
257
183
  rescue => e
@@ -261,16 +187,8 @@ module Ayadn
261
187
 
262
188
  def unrepost(post_ids, options = {})
263
189
  begin
264
- ids = post_ids.select { |post_id| post_id.is_integer? }
265
- if ids.empty?
266
- @status.error_missing_post_id
267
- exit
268
- end
269
- if options[:force]
270
- Settings.global[:force] = true
271
- else
272
- ids.map! { |post_id| @workers.get_real_post_id(post_id) }
273
- end
190
+ ids = get_posts_ids_or_exit(post_ids) { @status.error_missing_post_id }
191
+ ids = get_real_posts_ids_or_force(options, ids)
274
192
  puts "\n"
275
193
  ids.each do |post_id|
276
194
  @status.unreposting(post_id)
@@ -287,16 +205,8 @@ module Ayadn
287
205
 
288
206
  def unstar(post_ids, options = {})
289
207
  begin
290
- ids = post_ids.select { |post_id| post_id.is_integer? }
291
- if ids.empty?
292
- @status.error_missing_post_id
293
- exit
294
- end
295
- if options[:force]
296
- Settings.global[:force] = true
297
- else
298
- ids.map! { |post_id| @workers.get_real_post_id(post_id) }
299
- end
208
+ ids = get_posts_ids_or_exit(post_ids) { @status.error_missing_post_id }
209
+ ids = get_real_posts_ids_or_force(options, ids)
300
210
  puts "\n"
301
211
  ids.each do |post_id|
302
212
  @status.unstarring(post_id)
@@ -316,16 +226,8 @@ module Ayadn
316
226
 
317
227
  def star(post_ids, options = {})
318
228
  begin
319
- ids = post_ids.select { |post_id| post_id.is_integer? }
320
- if ids.empty?
321
- @status.error_missing_post_id
322
- exit
323
- end
324
- if options[:force]
325
- Settings.global[:force] = true
326
- else
327
- ids.map! { |post_id| @workers.get_real_post_id(post_id) }
328
- end
229
+ ids = get_posts_ids_or_exit(post_ids) { @status.error_missing_post_id }
230
+ ids = get_real_posts_ids_or_force(options, ids)
329
231
  puts "\n"
330
232
  ids.each do |post_id|
331
233
  @status.starring(post_id)
@@ -341,7 +243,6 @@ module Ayadn
341
243
 
342
244
  def hashtag(hashtag, options)
343
245
  begin
344
- Settings.options[:timeline][:compact] = true if options[:compact] == true
345
246
  search = Search.new(@api, @view, @workers)
346
247
  search.hashtag(hashtag, options)
347
248
  rescue => e
@@ -351,7 +252,6 @@ module Ayadn
351
252
 
352
253
  def search(words, options)
353
254
  begin
354
- Settings.options[:timeline][:compact] = true if options[:compact] == true
355
255
  search = Search.new(@api, @view, @workers)
356
256
  search.find(words, options)
357
257
  rescue => e
@@ -359,53 +259,12 @@ module Ayadn
359
259
  end
360
260
  end
361
261
 
362
- def followings(username, options)
363
- begin
364
- Settings.options[:timeline][:compact] = true if options[:compact] == true
365
- stream = Stream.new(@api, @view, @workers)
366
- stream.followings(username, options)
367
- rescue => e
368
- Errors.global_error({error: e, caller: caller, data: [username, options]})
369
- end
370
- end
371
-
372
- def followers(username, options)
373
- begin
374
- Settings.options[:timeline][:compact] = true if options[:compact] == true
375
- stream = Stream.new(@api, @view, @workers)
376
- stream.followers(username, options)
377
- rescue => e
378
- Errors.global_error({error: e, caller: caller, data: [username, options]})
379
- end
380
- end
381
-
382
- def muted(options)
383
- begin
384
- Settings.options[:timeline][:compact] = true if options[:compact] == true
385
- stream = Stream.new(@api, @view, @workers)
386
- stream.muted(options)
387
- rescue => e
388
- Errors.global_error({error: e, caller: caller, data: [options]})
389
- end
390
- end
391
-
392
- def blocked(options)
393
- begin
394
- Settings.options[:timeline][:compact] = true if options[:compact] == true
395
- stream = Stream.new(@api, @view, @workers)
396
- stream.blocked(options)
397
- rescue => e
398
- Errors.global_error({error: e, caller: caller, data: [options]})
399
- end
400
- end
401
-
402
262
  def view_settings(options)
403
263
  begin
404
264
  if options[:raw]
405
- jj JSON.parse(Settings.config.to_json)
406
- jj JSON.parse(Settings.options.to_json)
265
+ jj JSON.parse(Settings.options.to_h.to_json)
407
266
  else
408
- Settings.options[:timeline][:compact] = true if options[:compact] == true
267
+ Settings.options.timeline.compact = true if options[:compact]
409
268
  @view.show_settings
410
269
  end
411
270
  rescue => e
@@ -429,7 +288,7 @@ module Ayadn
429
288
 
430
289
  def userinfo(username, options = {})
431
290
  begin
432
- Settings.options[:timeline][:compact] = true if options[:compact] == true
291
+ Settings.options.timeline.compact = true if options[:compact]
433
292
  @check.no_username(username)
434
293
  usernames = @workers.add_arobases_to_usernames(username)
435
294
  usernames.each.with_index do |username, index|
@@ -437,11 +296,11 @@ module Ayadn
437
296
  @view.show_raw(@api.get_user(username), options)
438
297
  else
439
298
  @view.downloading if index == 0
440
- stream = @api.get_user(username)
441
- @check.no_user(stream, username)
442
- @check.same_username(stream) ? token = @api.get_token_info['data'] : token = nil
299
+ user_object = UserObject.new(@api.get_user(username), username)
300
+ # Is it us? If yes, get *our* info
301
+ @check.same_username(user_object) ? token = @api.get_token_info['data'] : token = nil
443
302
  @view.clear_screen if index == 0
444
- @view.infos(stream['data'], token)
303
+ @view.infos(user_object, token)
445
304
  end
446
305
  end
447
306
  rescue => e
@@ -452,12 +311,8 @@ module Ayadn
452
311
  def postinfo(post_id, options)
453
312
  begin
454
313
  @check.bad_post_id(post_id)
455
- Settings.options[:timeline][:compact] = true if options[:compact] == true
456
- if options[:force]
457
- Settings.global[:force] = true
458
- else
459
- post_id = @workers.get_real_post_id(post_id)
460
- end
314
+ Settings.options.timeline.compact = true if options[:compact]
315
+ post_id = get_real_post_id_or_force(options, post_id)
461
316
  details = lambda { @api.get_details(post_id, options) }
462
317
  if options[:raw]
463
318
  @view.show_raw(details.call, options)
@@ -465,32 +320,36 @@ module Ayadn
465
320
  end
466
321
  @view.clear_screen
467
322
  response = details.call
468
- @check.no_post(response, post_id)
469
- resp = response['data']
323
+ @check.no_details(response, post_id)
324
+
325
+ post_object = PostObject.new(response["data"])
470
326
 
471
- if resp["is_deleted"] == true
472
- @status.user_404(resp["id"])
327
+ if post_object.is_deleted
328
+ @status.user_404(post_object.id)
473
329
  Errors.global_error({error: "user 404", caller: caller, data: [post_id, options]})
474
330
  end
475
331
 
476
- response = @api.get_user("@#{resp['user']['username']}")
477
- @check.no_user(response, response['data']['username'])
478
- stream = response['data']
332
+ response = @api.get_user("@#{post_object.user.username}")
333
+ user_object = UserObject.new(response, post_object.user.username)
334
+
479
335
  @status.post_info
480
- @view.show_simple_post([resp], options)
481
- puts "\n" if Settings.options[:timeline][:compact] == true
336
+ @view.show_simple_post([post_object], options)
337
+ puts "\n" if Settings.options.timeline.compact
482
338
  @status.say_info "author"
483
- puts "\n" unless Settings.options[:timeline][:compact] == true
484
- if response['data']['username'] == Settings.config[:identity][:username]
485
- @view.show_userinfos(stream, @api.get_token_info['data'], true)
339
+ puts "\n" unless Settings.options.timeline.compact
340
+ # Is it us? ...
341
+ if user_object.username == Settings.config.identity.username
342
+ @view.show_userinfos(post_object.user, @api.get_token_info['data'], true)
486
343
  else
487
- @view.show_userinfos(stream, nil, true)
344
+ @view.show_userinfos(post_object.user, nil, true)
488
345
  end
489
- if resp['repost_of']
346
+
347
+ if !post_object.repost_of.nil?
490
348
  @status.repost_info
491
- Errors.repost(post_id, resp['repost_of']['id'])
492
- @view.show_simple_post([resp['repost_of']], options)
493
- puts "\n" if Settings.options[:timeline][:compact] == true
349
+ # If we ask infos for a reposted post, fetch the original instead
350
+ Errors.repost(post_id, post_object.repost_of.id)
351
+ @view.show_simple_post([post_object.repost_of], options)
352
+ puts "\n" if Settings.options.timeline.compact
494
353
  end
495
354
  rescue => e
496
355
  Errors.global_error({error: e, caller: caller, data: [post_id, options]})
@@ -526,6 +385,7 @@ module Ayadn
526
385
 
527
386
  def channels options
528
387
  begin
388
+ # Input could be channel IDs or channel aliases
529
389
  channels = if options[:id]
530
390
  channel_id = options[:id].map {|id| @workers.get_channel_id_from_alias(id)}
531
391
  lambda { @api.get_channel(channel_id, options) }
@@ -533,42 +393,32 @@ module Ayadn
533
393
  lambda { @api.get_channels }
534
394
  end
535
395
  if options[:raw]
536
- @view.show_raw(channels.call)
396
+ @view.show_direct_raw(channels.call)
537
397
  exit
538
398
  else
539
399
  @view.downloading
540
400
  resp = channels.call
541
401
  @view.clear_screen
542
- @view.show_channels(resp, options)
402
+ channels = resp["data"].map { |ch| ChannelObject.new(ch) }
403
+ @view.show_channels(channels, options)
543
404
  end
544
405
  rescue => e
545
406
  Errors.global_error({error: e, caller: caller, data: [options]})
546
407
  end
547
408
  end
548
409
 
549
- def messages(channel_id, options)
550
- begin
551
- Settings.options[:timeline][:compact] = true if options[:compact] == true
552
- stream = Stream.new(@api, @view, @workers)
553
- stream.messages(channel_id, options)
554
- rescue => e
555
- Errors.global_error({error: e, caller: caller, data: [channel_id, options]})
556
- end
557
- end
558
-
559
410
  def messages_unread(options)
560
411
  begin
561
- Settings.options[:timeline][:compact] = true if options[:compact] == true
562
- if options[:silent]
563
- Settings.options[:marker][:messages] = false
564
- end
412
+ Settings.options.timeline.compact = true if options[:compact]
413
+ Settings.options.marker.messages = false if options[:silent] # Option to not mark the messages as read
565
414
  puts "\n"
566
415
  @status.say_nocolor :searching, "channels with unread PMs"
567
- response = @api.get_channels
416
+ channels_objects = @api.get_channels['data'].map { |obj| ChannelObject.new(obj) }
568
417
  unread_channels = []
569
- response['data'].map do |ch|
570
- if ch['type'] == "net.app.core.pm" && ch['has_unread'] == true
571
- unread_channels << ch['id']
418
+ channels_objects.each do |ch|
419
+ # Channels can be of many types, PMs are only one type
420
+ if ch.type == "net.app.core.pm" && ch.has_unread
421
+ unread_channels << ch.id
572
422
  end
573
423
  end
574
424
  if unread_channels.empty?
@@ -578,6 +428,7 @@ module Ayadn
578
428
  unread_messages = {}
579
429
  unread_channels.reverse.each do |id|
580
430
  @status.say_nocolor :downloading, "messages from channel #{id}"
431
+ # Find the last time we've done this
581
432
  since = Databases.find_last_id_from("channel:#{id}")
582
433
  unless since.nil?
583
434
  api_options = {count: 20, since_id: since}
@@ -585,18 +436,19 @@ module Ayadn
585
436
  api_options = {count: 20}
586
437
  end
587
438
  ch = @api.get_messages(id, api_options)
439
+ # Find the last message seen and the last message in the channel
588
440
  last_read_id = ch['meta']['marker']['last_read_id'].to_i
589
441
  last_message_id = ch['meta']['max_id']
590
- messages = []
591
- ch['data'].each do |msg|
592
- messages << msg if msg['id'].to_i > last_read_id
593
- end
442
+ messages = ch['data'].map { |msg| msg if msg['id'].to_i > last_read_id }
594
443
  unread_messages[id] = [messages, last_message_id]
595
444
  end
596
- if Settings.options[:marker][:messages] == true
445
+ # If we want to mark the messages as read
446
+ if Settings.options.marker.messages
597
447
  unread_messages.each do |k,v|
598
448
  name = "channel:#{k}"
449
+ # Save the reading position locally
599
450
  Databases.pagination_insert(name, v[1])
451
+ # Mark as read
600
452
  resp = @api.update_marker(name, v[1])
601
453
  res = JSON.parse(resp)
602
454
  if res['meta']['code'] != 200
@@ -609,9 +461,10 @@ module Ayadn
609
461
  @view.clear_screen
610
462
  unread_messages.each do |k,v|
611
463
  @status.unread_from_channel(k)
612
- @view.show_posts(v[0])
464
+ messages_objects = v[0].map { |post_hash| PostObject.new(post_hash) }
465
+ @view.show_messages(messages_objects)
613
466
  end
614
- puts "\n" if Settings.options[:timeline][:compact]
467
+ puts "\n" if Settings.options.timeline.compact
615
468
  rescue => e
616
469
  Errors.global_error({error: e, caller: caller, data: [options]})
617
470
  end
@@ -628,29 +481,32 @@ module Ayadn
628
481
  end
629
482
  begin
630
483
  @check.bad_post_id(post_id)
631
- Settings.options[:timeline][:compact] = true if options[:compact] == true
632
- if options[:force]
633
- Settings.global[:force] = true
634
- else
635
- post_id = @workers.get_real_post_id(post_id)
636
- end
484
+ Settings.options.timeline.compact = true if options[:compact]
485
+ post_id = get_real_post_id_or_force(options, post_id)
637
486
  @view.downloading
638
- resp = @api.get_details(post_id)['data']
487
+ # Get the details from the post we want to send to Pinboard
488
+ # resp = @api.get_details(post_id)['data']
639
489
  @view.clear_screen
640
- links = @workers.extract_links(resp)
641
- resp['text'].nil? ? text = "" : text = resp['text']
490
+ # Extract links from the post
491
+ post_object = PostObject.new(@api.get_details(post_id)['data'])
492
+ links = @workers.extract_links(post_object)
493
+ # In case the post has no text, to prevent an error
494
+ post_object.text.nil? ? text = "" : text = post_object.text
495
+ # The first tag is always "ADN"
642
496
  usertags << "ADN"
643
- handle = "@" + resp['user']['username']
497
+ handle = "@" + post_object.user.username
644
498
  post_text = "From: #{handle} -- Text: #{text} -- Links: #{links.join(" ")}"
645
499
  pinner = Ayadn::PinBoard.new
646
500
  unless pinner.has_credentials_file?
501
+ # No Pinboard account registered? Ask for one.
647
502
  @status.no_pin_creds
648
- pinner.ask_credentials
503
+ pinner.ask_credentials(@status)
649
504
  @status.pin_creds_saved
650
505
  end
506
+ # Get stored credentials
651
507
  credentials = pinner.load_credentials
652
508
  maker = Struct.new(:username, :password, :url, :tags, :text, :description)
653
- bookmark = maker.new(credentials[0], credentials[1], resp['canonical_url'], usertags.join(","), post_text, resp['canonical_url'])
509
+ bookmark = maker.new(credentials[0], credentials[1], post_object.canonical_url, usertags.join(","), post_text, post_object.canonical_url)
654
510
  @status.saving_pin
655
511
  pinner.pin(bookmark)
656
512
  @status.done
@@ -663,7 +519,7 @@ module Ayadn
663
519
  begin
664
520
  @view.clear_screen
665
521
  @status.auto
666
- Post.new.auto_readline
522
+ Post.new(@status).auto_readline
667
523
  rescue => e
668
524
  Errors.global_error({error: e, caller: caller, data: [options]})
669
525
  end
@@ -671,18 +527,8 @@ module Ayadn
671
527
 
672
528
  def post(args, options)
673
529
  begin
674
- Settings.options[:timeline][:compact] = true if options[:compact] == true
675
- writer = Post.new
676
- if options[:poster] # Returns the same options hash + poster embed
677
- settings = options.dup
678
- options = NowWatching.new.get_poster(settings[:poster], settings)
679
- end
680
- text = args.join(" ")
681
- writer.post_size_error(text) if writer.post_size_ok?(text) == false
682
- @view.clear_screen
683
- @status.posting
684
- resp = writer.post({options: options, text: text})
685
- save_and_view(resp)
530
+ Settings.options.timeline.compact = true if options[:compact]
531
+ post_and_show(Post.new(@status), args.join(" "), options)
686
532
  rescue => e
687
533
  Errors.global_error({error: e, caller: caller, data: [args, options]})
688
534
  end
@@ -690,112 +536,91 @@ module Ayadn
690
536
 
691
537
  def write(options)
692
538
  begin
693
- Settings.options[:timeline][:compact] = true if options[:compact] == true
694
- writer = Post.new
539
+ Settings.options.timeline.compact = true if options[:compact]
540
+ writer = Post.new(@status)
695
541
  @status.writing
696
542
  @status.post
697
- lines_array = writer.compose
698
- text = lines_array.join("\n")
699
- writer.post_size_error(text) if writer.post_size_ok?(text) == false
700
- @view.clear_screen
701
- @status.posting
702
- if options[:poster]
703
- settings = options.dup
704
- options = NowWatching.new.get_poster(settings[:poster], settings)
705
- end
706
- resp = writer.post({options: options, text: text})
707
- save_and_view(resp)
543
+ text = writer.compose.join("\n")
544
+ post_and_show(writer, text, options)
708
545
  rescue => e
709
546
  Errors.global_error({error: e, caller: caller, data: [text, options]})
710
547
  end
711
548
  end
712
549
 
713
550
  def pmess(username, options = {})
714
- begin
715
- Settings.options[:timeline][:compact] = true if options[:compact] == true
716
- if options[:silent]
717
- Settings.options[:marker][:messages] = false
718
- end
551
+ begin
552
+ Settings.options.timeline.compact = true if options[:compact]
553
+ Settings.options.marker.messages = false if options[:silent]
719
554
  @check.no_username(username)
720
555
  username = [@workers.add_arobase(username)]
721
- writer = Post.new
556
+ writer = Post.new(@status)
722
557
  @status.message_from(username)
723
- @status.message
724
- lines_array = writer.compose
725
- text = lines_array.join("\n")
726
- writer.message_size_error(text) if writer.message_size_ok?(text) == false
727
- @view.clear_screen
558
+ @status.message
559
+ text = writer.compose.join("\n")
560
+ writer.message_size_error(text) if !writer.message_size_ok?(text)
561
+ @view.clear_screen
728
562
  @status.posting
729
- if options[:poster]
730
- settings = options.dup
731
- options = NowWatching.new.get_poster(settings[:poster], settings)
732
- end
733
563
  resp = writer.pm({options: options, text: text, username: username})
734
- if Settings.options[:marker][:messages] == true
564
+ post_object = PostObject.new(resp["data"])
565
+ if Settings.options.marker.messages
735
566
  if resp['meta']['code'] == 200
736
- data = resp['data']
737
- name = "channel:#{data['channel_id']}"
738
- Databases.pagination_insert(name, data['id'])
739
- marked = @api.update_marker(name, data['id'])
567
+ name = "channel:#{post_object.channel_id}"
568
+ Databases.pagination_insert(name, post_object.id)
569
+ marked = @api.update_marker(name, post_object.id)
740
570
  updated = JSON.parse(marked)
741
571
  if updated['meta']['code'] != 200
742
- raise "couldn't update channel #{data['channel_id']} as read"
572
+ raise "couldn't update channel #{post_object.channel_id} as read"
743
573
  end
744
574
  end
745
575
  end
746
- FileOps.save_message(resp) if Settings.options[:backup][:messages]
747
- @view.clear_screen
748
- @status.yourmessage(username[0])
749
- @view.show_posted(resp)
750
- rescue => e
576
+ FileOps.save_message(resp) if Settings.options.backup.messages
577
+ @view.clear_screen
578
+ @status.yourmessage(username[0])
579
+ @view.show_simple_post([post_object])
580
+ rescue => e
751
581
  Errors.global_error({error: e, caller: caller, data: [username, options]})
752
- end
582
+ end
753
583
  end
754
584
 
755
585
  def reply(post_id, options = {})
756
586
  begin
757
- Settings.options[:timeline][:compact] = true if options[:compact] == true
587
+ Settings.options.timeline.compact = true if options[:compact]
758
588
  @check.bad_post_id(post_id)
759
- if options[:force]
760
- Settings.global[:force] = true
761
- else
762
- post_id = @workers.get_real_post_id(post_id)
763
- end
764
- @status.replying_to(post_id)
765
- replied_to = @api.get_details(post_id)
766
- @check.no_post(replied_to, post_id)
589
+ post_id = get_real_post_id_or_force(options, post_id)
590
+ @status.replying_to(post_id)
591
+ replied_to = @api.get_details(post_id)
592
+ @check.no_details(replied_to, post_id)
593
+ # API specifies to always reply to the original post of a reposted post. We offer the user an option to not.
767
594
  unless options[:noredirect]
768
595
  post_id = @workers.get_original_id(post_id, replied_to)
769
596
  end
770
597
  if replied_to['data']['repost_of']
771
598
  if post_id == replied_to['data']['repost_of']['id']
772
599
  replied_to = @api.get_details(post_id)
773
- @check.no_post(replied_to, post_id)
600
+ @check.no_details(replied_to, post_id)
774
601
  end
775
602
  end
776
603
  # ----
777
- writer = Post.new
604
+ writer = Post.new(@status)
778
605
  @status.writing
779
606
  @status.reply
780
- lines_array = writer.compose
781
- text = lines_array.join("\n")
782
- # text length is tested in Post class for the reply command
607
+ text = writer.compose.join("\n")
608
+ # Text length is tested in Post class for the reply command
783
609
  @view.clear_screen
784
- replied_to = @workers.build_posts([replied_to['data']])
785
- if options[:poster]
786
- settings = options.dup
787
- options = NowWatching.new.get_poster(settings[:poster], settings)
788
- end
610
+ replied_to = @workers.build_posts([PostObject.new(replied_to['data'])])[0]
789
611
  resp = writer.reply({options: options, text: text, id: post_id, reply_to: replied_to})
790
- FileOps.save_post(resp) if Settings.options[:backup][:posts]
612
+ FileOps.save_post(resp) if Settings.options.backup.posts
791
613
  # ----
614
+ # "options" from CLI is immutable, we have to make a copy to add items
792
615
  options = options.dup
793
616
  unless resp['data']['reply_to'].nil?
794
617
  options[:reply_to] = resp['data']['reply_to'].to_i
795
618
  end
796
619
  options[:post_id] = resp['data']['id'].to_i
797
- @view.render(@api.get_convo(post_id), options)
798
- puts "\n" if Settings.options[:timeline][:compact] == true && !options[:raw]
620
+ stream = @api.get_convo(post_id)
621
+ stream_object = StreamObject.new(stream)
622
+ @view.render(stream_object, options)
623
+ puts "\n" if Settings.options.timeline.compact && !options[:raw]
799
624
  rescue => e
800
625
  Errors.global_error({error: e, caller: caller, data: [post_id, options]})
801
626
  end
@@ -803,48 +628,41 @@ module Ayadn
803
628
 
804
629
  def send_to_channel(channel_id, options = {})
805
630
  begin
806
- Settings.options[:timeline][:compact] = true if options[:compact] == true
807
- if options[:silent]
808
- Settings.options[:marker][:messages] = false
809
- end
631
+ Settings.options.timeline.compact = true if options[:compact]
632
+ Settings.options.marker.messages = false if options[:silent]
810
633
  channel_id = @workers.get_channel_id_from_alias(channel_id)
811
- writer = Post.new
634
+ writer = Post.new(@status)
812
635
  @status.writing
813
636
  @status.message
814
- lines_array = writer.compose
815
- text = lines_array.join("\n")
816
- writer.message_size_error(text) if writer.message_size_ok?(text) == false
637
+ text = writer.compose.join("\n")
638
+ writer.message_size_error(text) if !writer.message_size_ok?(text)
817
639
  @view.clear_screen
818
640
  @status.posting
819
- if options[:poster]
820
- settings = options.dup
821
- options = NowWatching.new.get_poster(settings[:poster], settings)
822
- end
823
641
  resp = writer.message({options: options, id: channel_id, text: text})
824
- if Settings.options[:marker][:messages] == true
642
+ post_object = PostObject.new(resp["data"])
643
+ if Settings.options.marker.messages
825
644
  if resp['meta']['code'] == 200
826
- data = resp['data']
827
- name = "channel:#{data['channel_id']}"
828
- Databases.pagination_insert(name, data['id'])
829
- marked = @api.update_marker(name, data['id'])
645
+ name = "channel:#{post_object.channel_id}"
646
+ Databases.pagination_insert(name, post_object.id)
647
+ marked = @api.update_marker(name, post_object.id)
830
648
  updated = JSON.parse(marked)
831
649
  if updated['meta']['code'] != 200
832
- raise "couldn't update channel #{data['channel_id']} as read"
650
+ raise "couldn't update channel #{post_object.channel_id} as read"
833
651
  end
834
652
  end
835
653
  end
836
- FileOps.save_message(resp) if Settings.options[:backup][:messages]
654
+ FileOps.save_message(resp) if Settings.options.backup.messages
837
655
  @view.clear_screen
838
656
  @status.yourpost
839
- @view.show_posted(resp)
657
+ @view.show_simple_post([post_object])
840
658
  rescue => e
841
659
  Errors.global_error({error: e, caller: caller, data: [channel_id, options]})
842
660
  end
843
661
  end
844
662
 
845
663
  def nowplaying(options = {})
846
- Settings.options[:timeline][:compact] = true if options[:compact] == true
847
- np = NowPlaying.new(@api, @view, @workers, options)
664
+ Settings.options.timeline.compact = true if options[:compact]
665
+ np = NowPlaying.new(@api, @view, @workers, @status, options)
848
666
  if options[:lastfm]
849
667
  np.lastfm(options)
850
668
  elsif options[:deezer]
@@ -854,62 +672,54 @@ module Ayadn
854
672
  end
855
673
  end
856
674
 
857
- def nowwatching(args, options = {})
858
- begin
859
- Settings.options[:timeline][:compact] = true if options[:compact] == true
860
- if args.empty?
861
- @status.error_missing_title
862
- exit
863
- end
864
- nw = NowWatching.new(@view)
865
- nw.post(args, options)
866
- rescue ArgumentError => e
867
- @status.no_movie
868
- rescue => e
869
- @status.wtf
870
- Errors.global_error({error: e, caller: caller, data: [args, options]})
871
- end
675
+ private
676
+
677
+ def save_and_view(resp)
678
+ FileOps.save_post(resp) if Settings.options.backup.posts
679
+ @view.clear_screen
680
+ @status.yourpost
681
+ puts "\n\n"
682
+ @view.show_posted(resp)
872
683
  end
873
684
 
874
- def tvshow(args, options = {})
875
- begin
876
- Settings.options[:timeline][:compact] = true if options[:compact] == true
877
- if args.empty?
878
- @status.error_missing_title
879
- exit
880
- end
881
- client = TvShow.new
882
- show_obj = if options[:alt]
883
- client.find_alt(args.join(' '))
884
- else
885
- client.find(args.join(' '))
886
- end
887
- candidate = client.create_details(show_obj)
888
- candidate.ok ? candidate.post(options) : candidate.cancel
889
- rescue => e
890
- @status.wtf
891
- Errors.global_error({error: e, caller: caller, data: [args, options]})
685
+ def get_posts_ids_or_exit ids
686
+ int_ids = ids.select { |post_id| post_id.is_integer? }
687
+ if int_ids.empty?
688
+ yield
689
+ exit
892
690
  end
691
+ int_ids
893
692
  end
894
693
 
895
- def random_posts(options)
896
- begin
897
- Settings.options[:timeline][:compact] = true if options[:compact] == true
898
- stream = Stream.new(@api, @view, @workers)
899
- stream.random_posts(options)
900
- rescue => e
901
- Errors.global_error({error: e, caller: caller, data: [@max_id, @random_post_id, @resp, options]})
694
+ def get_all_usernames_but_me usernames
695
+ @check.no_username(usernames)
696
+ @workers.all_but_me(usernames)
697
+ end
698
+
699
+ def get_real_posts_ids_or_force options, posts_ids
700
+ if options[:force]
701
+ Settings.global.force = true
702
+ posts_ids
703
+ else
704
+ posts_ids.map { |post_id| @workers.get_real_post_id(post_id) }
902
705
  end
903
706
  end
904
707
 
905
- private
708
+ def get_real_post_id_or_force options, post_id
709
+ if options[:force]
710
+ Settings.global.force = true
711
+ post_id
712
+ else
713
+ @workers.get_real_post_id(post_id)
714
+ end
715
+ end
906
716
 
907
- def save_and_view(resp)
908
- FileOps.save_post(resp) if Settings.options[:backup][:posts]
717
+ def post_and_show writer, text, options
718
+ writer.post_size_error(text) if !writer.post_size_ok?(text)
909
719
  @view.clear_screen
910
- @status.yourpost
911
- puts "\n\n"
912
- @view.show_posted(resp)
720
+ @status.posting
721
+ resp = writer.post({options: options, text: text})
722
+ save_and_view(resp)
913
723
  end
914
724
 
915
725
  end