twterm 2.1.0 → 2.2.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.
- checksums.yaml +5 -5
- data/.travis.yml +4 -1
- data/lib/twterm.rb +1 -1
- data/lib/twterm/abstract_persistable_configuration.rb +118 -0
- data/lib/twterm/app.rb +21 -1
- data/lib/twterm/environment.rb +38 -0
- data/lib/twterm/event/message/abstract_message.rb +23 -0
- data/lib/twterm/event/message/error.rb +10 -0
- data/lib/twterm/event/message/info.rb +10 -0
- data/lib/twterm/event/message/success.rb +10 -0
- data/lib/twterm/event/message/warning.rb +10 -0
- data/lib/twterm/event/notification/abstract_notification.rb +17 -12
- data/lib/twterm/event/notification/direct_message.rb +30 -0
- data/lib/twterm/event/notification/favorite.rb +35 -0
- data/lib/twterm/event/notification/follow.rb +33 -0
- data/lib/twterm/event/notification/list_member_added.rb +33 -0
- data/lib/twterm/event/notification/mention.rb +35 -0
- data/lib/twterm/event/notification/quote.rb +35 -0
- data/lib/twterm/event/notification/retweet.rb +35 -0
- data/lib/twterm/key_mapper/abstract_key_mapper.rb +4 -4
- data/lib/twterm/list.rb +2 -1
- data/lib/twterm/message_window.rb +81 -0
- data/lib/twterm/notification_backend/abstract_notification_backend.rb +16 -0
- data/lib/twterm/notification_backend/inline_backend.rb +20 -0
- data/lib/twterm/notification_backend/terminal_notifier_backend.rb +23 -0
- data/lib/twterm/notification_backend/tmux_backend.rb +17 -0
- data/lib/twterm/notification_dispatcher.rb +36 -0
- data/lib/twterm/persistable_configuration_proxy.rb +82 -0
- data/lib/twterm/preferences.rb +66 -0
- data/lib/twterm/repository/abstract_expirable_entity_repository.rb +1 -1
- data/lib/twterm/rest_client.rb +12 -14
- data/lib/twterm/scheduler.rb +1 -1
- data/lib/twterm/screen.rb +1 -1
- data/lib/twterm/search_query_window.rb +2 -2
- data/lib/twterm/streaming_client.rb +54 -17
- data/lib/twterm/tab/base.rb +2 -3
- data/lib/twterm/tab/new/list.rb +2 -4
- data/lib/twterm/tab/new/start.rb +10 -0
- data/lib/twterm/tab/new/user.rb +2 -2
- data/lib/twterm/tab/preferences/index.rb +74 -0
- data/lib/twterm/tab/preferences/notification_backend.rb +85 -0
- data/lib/twterm/tab/scrollable.rb +0 -1
- data/lib/twterm/tab/searchable.rb +7 -7
- data/lib/twterm/tab/user_list_management.rb +3 -3
- data/lib/twterm/tab/user_tab.rb +6 -8
- data/lib/twterm/tab_manager.rb +4 -4
- data/lib/twterm/tweetbox.rb +1 -1
- data/lib/twterm/uri_opener.rb +2 -2
- data/lib/twterm/user.rb +2 -1
- data/lib/twterm/version.rb +1 -1
- data/twterm.gemspec +4 -2
- metadata +59 -14
- data/lib/twterm/event/favorite.rb +0 -18
- data/lib/twterm/event/follow.rb +0 -17
- data/lib/twterm/event/notification/error.rb +0 -13
- data/lib/twterm/event/notification/info.rb +0 -13
- data/lib/twterm/event/notification/success.rb +0 -13
- data/lib/twterm/event/notification/warning.rb +0 -13
- data/lib/twterm/notifier.rb +0 -82
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'toml'
|
2
|
+
|
3
|
+
require 'twterm/abstract_persistable_configuration'
|
4
|
+
|
5
|
+
module Twterm
|
6
|
+
class Preferences < AbstractPersistableConfiguration
|
7
|
+
def initialize(preferences)
|
8
|
+
super(preferences)
|
9
|
+
end
|
10
|
+
|
11
|
+
# @param [Symbol] cat
|
12
|
+
# @param [Symbol] key
|
13
|
+
def [](cat, key)
|
14
|
+
validate_key!(cat, key)
|
15
|
+
|
16
|
+
configuration[cat][key]
|
17
|
+
end
|
18
|
+
|
19
|
+
def []=(cat, key, value)
|
20
|
+
validate_key!(cat, key)
|
21
|
+
|
22
|
+
configuration[cat][key] = value
|
23
|
+
end
|
24
|
+
|
25
|
+
# Returns an instance having the default value
|
26
|
+
#
|
27
|
+
# @return [Twterm::Preferences] an instance having the default value
|
28
|
+
def self.default
|
29
|
+
new({
|
30
|
+
notification_backend: {
|
31
|
+
inline: true,
|
32
|
+
terminal_notifier: false,
|
33
|
+
tmux: false,
|
34
|
+
},
|
35
|
+
})
|
36
|
+
end
|
37
|
+
|
38
|
+
# @return [Hash]
|
39
|
+
def to_h
|
40
|
+
configuration
|
41
|
+
end
|
42
|
+
|
43
|
+
# @return [Hash]
|
44
|
+
def self.structure
|
45
|
+
bool = -> x { x == true || x == false }
|
46
|
+
|
47
|
+
{
|
48
|
+
notification_backend: {
|
49
|
+
inline: bool,
|
50
|
+
terminal_notifier: bool,
|
51
|
+
tmux: bool,
|
52
|
+
},
|
53
|
+
}
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
alias_method :preferences, :configuration
|
59
|
+
|
60
|
+
# @raise [ArgumentError]
|
61
|
+
def validate_key!(cat, key)
|
62
|
+
raise ArgumentError, "no such category: #{cat}" unless configuration.has_key?(cat)
|
63
|
+
raise ArgumentError, "no such key: #{cat}.#{key}" unless configuration[cat].has_key?(key)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
data/lib/twterm/rest_client.rb
CHANGED
@@ -2,7 +2,7 @@ require 'concurrent'
|
|
2
2
|
require 'twterm/direct_message'
|
3
3
|
require 'twterm/direct_message_manager'
|
4
4
|
require 'twterm/publisher'
|
5
|
-
require 'twterm/event/
|
5
|
+
require 'twterm/event/message/success'
|
6
6
|
|
7
7
|
module Twterm
|
8
8
|
module RESTClient
|
@@ -32,7 +32,7 @@ module Twterm
|
|
32
32
|
msg = direct_message_repository.create(message)
|
33
33
|
direct_message_manager.add(recipient.id, msg)
|
34
34
|
publish(Event::DirectMessage::Fetched.new)
|
35
|
-
publish(Event::
|
35
|
+
publish(Event::Message::Success.new('Your message to @%s has been sent' % recipient.screen_name))
|
36
36
|
end
|
37
37
|
end
|
38
38
|
|
@@ -56,11 +56,11 @@ module Twterm
|
|
56
56
|
send_request_without_catch do
|
57
57
|
rest_client.destroy_status(status.id)
|
58
58
|
publish(Event::Status::Delete.new(status.id))
|
59
|
-
publish(Event::
|
59
|
+
publish(Event::Message::Success.new('Your tweet has been deleted'))
|
60
60
|
end.catch do |reason|
|
61
61
|
case reason
|
62
62
|
when Twitter::Error::NotFound, Twitter::Error::Forbidden
|
63
|
-
publish(Event::
|
63
|
+
publish(Event::Message::Error.new('You cannot destroy that status'))
|
64
64
|
else
|
65
65
|
raise reason
|
66
66
|
end
|
@@ -75,7 +75,7 @@ module Twterm
|
|
75
75
|
end.then do |tweet, *_|
|
76
76
|
status_repository.create(tweet)
|
77
77
|
|
78
|
-
publish(Event::
|
78
|
+
publish(Event::Message::Success.new('Successfully liked: @%s "%s"' % [
|
79
79
|
tweet.user.screen_name, status.text
|
80
80
|
]))
|
81
81
|
end
|
@@ -201,7 +201,7 @@ module Twterm
|
|
201
201
|
end
|
202
202
|
end.catch do |e|
|
203
203
|
case e
|
204
|
-
when Twitter::Error::TooManyRequests
|
204
|
+
when Twitter::Error::TooManyRequests # rubocop:disable Lint/EmptyWhen
|
205
205
|
# do nothing
|
206
206
|
else
|
207
207
|
raise e
|
@@ -250,7 +250,7 @@ module Twterm
|
|
250
250
|
rest_client.update(text, options)
|
251
251
|
end
|
252
252
|
.then do |status|
|
253
|
-
publish(Event::
|
253
|
+
publish(Event::Message::Success.new('Your tweet has been posted'))
|
254
254
|
|
255
255
|
status
|
256
256
|
end
|
@@ -273,7 +273,7 @@ module Twterm
|
|
273
273
|
end.then do |tweet, *_|
|
274
274
|
status_repository.create(tweet)
|
275
275
|
|
276
|
-
publish(Event::
|
276
|
+
publish(Event::Message::Success.new('Successfully retweeted: @%s "%s"' % [
|
277
277
|
tweet.user.screen_name, status.text
|
278
278
|
]))
|
279
279
|
end.catch do |reason|
|
@@ -288,7 +288,7 @@ module Twterm
|
|
288
288
|
else
|
289
289
|
raise e
|
290
290
|
end
|
291
|
-
publish(Event::
|
291
|
+
publish(Event::Message::Error.new("Retweet attempt failed: #{message}"))
|
292
292
|
end.catch(&show_error)
|
293
293
|
end
|
294
294
|
|
@@ -350,9 +350,7 @@ module Twterm
|
|
350
350
|
end.then do |tweet, *_|
|
351
351
|
status_repository.create(tweet)
|
352
352
|
|
353
|
-
publish(Event::
|
354
|
-
tweet.user.screen_name, status.text
|
355
|
-
]))
|
353
|
+
publish(Event::Message::Success.new("Successfully unliked @#{tweet.user.screen_name}'s tweet", sutatus.text))
|
356
354
|
end
|
357
355
|
end
|
358
356
|
|
@@ -389,7 +387,7 @@ module Twterm
|
|
389
387
|
.then do |tweet|
|
390
388
|
status = status_repository.create(tweet)
|
391
389
|
|
392
|
-
publish(Event::
|
390
|
+
publish(Event::Message::Success.new('Successfully unretweeted: @%s "%s"' % [
|
393
391
|
tweet.user.screen_name, status.text
|
394
392
|
]))
|
395
393
|
|
@@ -434,7 +432,7 @@ module Twterm
|
|
434
432
|
proc do |e|
|
435
433
|
case e
|
436
434
|
when Twitter::Error
|
437
|
-
publish(Event::
|
435
|
+
publish(Event::Message::Error.new("Failed to send request: #{e.message}"))
|
438
436
|
else
|
439
437
|
raise e
|
440
438
|
end
|
data/lib/twterm/scheduler.rb
CHANGED
data/lib/twterm/screen.rb
CHANGED
@@ -45,7 +45,7 @@ module Twterm
|
|
45
45
|
|
46
46
|
@str.chop!
|
47
47
|
render_current_string
|
48
|
-
when 0..31
|
48
|
+
when 0..31 # rubocop:disable Lint/EmptyWhen
|
49
49
|
# ignore control codes (\x00 - \x1f)
|
50
50
|
else
|
51
51
|
next if chars.empty?
|
@@ -116,7 +116,7 @@ module Twterm
|
|
116
116
|
|
117
117
|
attr_reader :window
|
118
118
|
|
119
|
-
def resize(
|
119
|
+
def resize(_event)
|
120
120
|
window.resize(1, stdscr.maxx)
|
121
121
|
window.move(stdscr.maxy - 1, 0)
|
122
122
|
end
|
@@ -1,7 +1,12 @@
|
|
1
|
-
require 'twterm/event/
|
2
|
-
require 'twterm/event/
|
3
|
-
require 'twterm/event/notification/
|
4
|
-
require 'twterm/event/notification/
|
1
|
+
require 'twterm/event/message/error'
|
2
|
+
require 'twterm/event/message/info'
|
3
|
+
require 'twterm/event/notification/direct_message'
|
4
|
+
require 'twterm/event/notification/favorite'
|
5
|
+
require 'twterm/event/notification/follow'
|
6
|
+
require 'twterm/event/notification/list_member_added'
|
7
|
+
require 'twterm/event/notification/mention'
|
8
|
+
require 'twterm/event/notification/quote'
|
9
|
+
require 'twterm/event/notification/retweet'
|
5
10
|
require 'twterm/event/status/mention'
|
6
11
|
require 'twterm/event/status/timeline'
|
7
12
|
require 'twterm/publisher'
|
@@ -18,32 +23,64 @@ module Twterm
|
|
18
23
|
|
19
24
|
@streaming_thread = Thread.new do
|
20
25
|
begin
|
21
|
-
publish(Event::
|
26
|
+
publish(Event::Message::Info.new('Trying to connect to Twitter...'))
|
22
27
|
streaming_client.user do |event|
|
23
28
|
keep_alive!
|
24
29
|
|
25
30
|
case event
|
26
31
|
when Twitter::Tweet
|
27
32
|
status = status_repository.create(event)
|
33
|
+
user = user_repository.create(event.user)
|
34
|
+
|
28
35
|
publish(Event::Status::Timeline.new(status))
|
29
|
-
|
36
|
+
|
37
|
+
if !status.retweet? && status.text.include?('@%s' % screen_name)
|
38
|
+
publish(Event::Status::Mention.new(status))
|
39
|
+
|
40
|
+
notification = Event::Notification::Mention.new(status, user)
|
41
|
+
publish(notification)
|
42
|
+
end
|
43
|
+
|
44
|
+
if status.retweet? && event.retweeted_status.user.id == user_id
|
45
|
+
retweeted_status = status_repository.create(event.retweeted_status)
|
46
|
+
notification = Event::Notification::Retweet.new(retweeted_status, user)
|
47
|
+
publish(notification)
|
48
|
+
end
|
30
49
|
when Twitter::Streaming::Event
|
31
50
|
case event.name
|
32
51
|
when :favorite
|
52
|
+
next if event.source.id == user_id
|
33
53
|
user = user_repository.create(event.source)
|
34
54
|
status = status_repository.create(event.target_object)
|
35
|
-
|
36
|
-
|
37
|
-
publish(event)
|
55
|
+
notification = Event::Notification::Like.new(status, user)
|
56
|
+
publish(notification)
|
38
57
|
when :follow
|
39
|
-
|
40
|
-
|
58
|
+
next if event.source.id == user_id
|
59
|
+
|
60
|
+
user = user_repository.create(event.source)
|
61
|
+
notification = Event::Notification::Follow.new(user)
|
62
|
+
publish(notification)
|
63
|
+
when :list_member_added
|
64
|
+
next if event.source.id == user_id
|
41
65
|
|
42
|
-
|
66
|
+
list = list_repository.create(event.target_object)
|
67
|
+
notification = Event::Notification::ListMemberAdded.new(list)
|
68
|
+
publish(notification)
|
69
|
+
when :quoted_tweet
|
70
|
+
next if event.source.id == user_id
|
43
71
|
|
44
|
-
|
72
|
+
status = status_repository.create(event.target_object)
|
73
|
+
user = user_repository.create(event.source)
|
74
|
+
notification = Event::Notification::Quote.new(status, user)
|
75
|
+
publish(notification)
|
45
76
|
end
|
46
77
|
when Twitter::DirectMessage
|
78
|
+
next if event.sender.id == user_id
|
79
|
+
|
80
|
+
user = user_repository.create(event.sender)
|
81
|
+
message = DirectMessage.new(event)
|
82
|
+
notification = Event::Notification::DirectMessage.new(message, user)
|
83
|
+
publish(notification)
|
47
84
|
when Twitter::Streaming::FriendList
|
48
85
|
user_stream_connected!
|
49
86
|
when Twitter::Streaming::DeletedTweet
|
@@ -51,15 +88,15 @@ module Twterm
|
|
51
88
|
end
|
52
89
|
end
|
53
90
|
rescue Twitter::Error::TooManyRequests
|
54
|
-
publish(Event::
|
91
|
+
publish(Event::Message::Error.new('Rate limit exceeded'))
|
55
92
|
sleep 120
|
56
93
|
retry
|
57
94
|
rescue Errno::ENETUNREACH, Errno::ETIMEDOUT, Resolv::ResolvError
|
58
|
-
publish(Event::
|
95
|
+
publish(Event::Message::Error.new('Network is unavailable'))
|
59
96
|
sleep 30
|
60
97
|
retry
|
61
98
|
rescue Twitter::Error => e
|
62
|
-
publish(Event::
|
99
|
+
publish(Event::Message::Error.new(e.message))
|
63
100
|
end
|
64
101
|
end
|
65
102
|
end
|
@@ -90,7 +127,7 @@ module Twterm
|
|
90
127
|
end
|
91
128
|
|
92
129
|
def user_stream_connected!
|
93
|
-
publish(Event::
|
130
|
+
publish(Event::Message::Info.new('Connection established')) unless user_stream_connected?
|
94
131
|
@user_stream_connected = true
|
95
132
|
end
|
96
133
|
|
data/lib/twterm/tab/base.rb
CHANGED
@@ -10,8 +10,7 @@ module Twterm
|
|
10
10
|
include Curses
|
11
11
|
include Subscriber
|
12
12
|
|
13
|
-
attr_reader :window
|
14
|
-
attr_accessor :title
|
13
|
+
attr_reader :window, :title
|
15
14
|
|
16
15
|
def ==(other)
|
17
16
|
self.equal?(other)
|
@@ -107,7 +106,7 @@ module Twterm
|
|
107
106
|
)
|
108
107
|
end
|
109
108
|
|
110
|
-
def resize(
|
109
|
+
def resize(_event)
|
111
110
|
window.resize(stdscr.maxy - 5, stdscr.maxx)
|
112
111
|
window.move(3, 0)
|
113
112
|
end
|
data/lib/twterm/tab/new/list.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require 'twterm/event/
|
1
|
+
require 'twterm/event/message/info'
|
2
2
|
require 'twterm/tab/base'
|
3
3
|
require 'twterm/tab/loadable'
|
4
4
|
|
@@ -33,7 +33,7 @@ module Twterm
|
|
33
33
|
@@lists || []
|
34
34
|
end
|
35
35
|
|
36
|
-
def matches?(
|
36
|
+
def matches?(_list, query)
|
37
37
|
[
|
38
38
|
other.description,
|
39
39
|
other.full_name,
|
@@ -43,8 +43,6 @@ module Twterm
|
|
43
43
|
def respond_to_key(key)
|
44
44
|
return true if scroller.respond_to_key(key)
|
45
45
|
|
46
|
-
k = KeyMapper.instance
|
47
|
-
|
48
46
|
case key
|
49
47
|
when 10
|
50
48
|
return true if current_list.nil?
|
data/lib/twterm/tab/new/start.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'twterm/tab/base'
|
2
2
|
require 'twterm/tab/direct_message/conversation_list'
|
3
3
|
require 'twterm/tab/rate_limit_status'
|
4
|
+
require 'twterm/tab/preferences/index'
|
4
5
|
|
5
6
|
module Twterm
|
6
7
|
module Tab
|
@@ -24,6 +25,7 @@ module Twterm
|
|
24
25
|
user_tab
|
25
26
|
key_assignments_cheatsheet
|
26
27
|
rate_limit_status
|
28
|
+
preferences
|
27
29
|
).freeze
|
28
30
|
end
|
29
31
|
|
@@ -69,6 +71,8 @@ module Twterm
|
|
69
71
|
'Key assignments cheatsheet'
|
70
72
|
when :rate_limit_status
|
71
73
|
'Rate limit status'
|
74
|
+
when :preferences
|
75
|
+
'Preferences'
|
72
76
|
end
|
73
77
|
|
74
78
|
cursor - Image.whitespace - Image.string(desc)
|
@@ -103,6 +107,10 @@ module Twterm
|
|
103
107
|
switch(Tab::KeyAssignmentsCheatsheet.new(app, client))
|
104
108
|
end
|
105
109
|
|
110
|
+
def open_preferences_tab
|
111
|
+
switch(Tab::Preferences::Index.new(app, client))
|
112
|
+
end
|
113
|
+
|
106
114
|
def perform_selected_action
|
107
115
|
case current_item
|
108
116
|
when :direct_messages
|
@@ -117,6 +125,8 @@ module Twterm
|
|
117
125
|
open_key_assignments_cheatsheet
|
118
126
|
when :rate_limit_status
|
119
127
|
open_rate_limit_status
|
128
|
+
when :preferences
|
129
|
+
open_preferences_tab
|
120
130
|
end
|
121
131
|
end
|
122
132
|
|
data/lib/twterm/tab/new/user.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'twterm/publisher'
|
2
2
|
require 'twterm/tab/base'
|
3
|
-
require 'twterm/event/
|
3
|
+
require 'twterm/event/message/error'
|
4
4
|
|
5
5
|
module Twterm
|
6
6
|
module Tab
|
@@ -33,7 +33,7 @@ module Twterm
|
|
33
33
|
else
|
34
34
|
client.show_user(screen_name).then do |user|
|
35
35
|
if user.nil?
|
36
|
-
publish(Event::
|
36
|
+
publish(Event::Message::Error.new('User not found'))
|
37
37
|
tab = Tab::New::Start.new(app, client)
|
38
38
|
else
|
39
39
|
tab = Tab::UserTab.new(app, client, user.id)
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'twterm/image'
|
2
|
+
require 'twterm/tab/preferences/notification_backend'
|
3
|
+
require 'twterm/preferences'
|
4
|
+
require 'twterm/publisher'
|
5
|
+
require 'twterm/tab/base'
|
6
|
+
require 'twterm/tab/scrollable'
|
7
|
+
|
8
|
+
module Twterm
|
9
|
+
module Tab
|
10
|
+
module Preferences
|
11
|
+
class Index < Tab::Base
|
12
|
+
include Scrollable
|
13
|
+
|
14
|
+
def initialize(app, client)
|
15
|
+
super(app, client)
|
16
|
+
end
|
17
|
+
|
18
|
+
def drawable_item_count
|
19
|
+
(window.maxy - 1) / 2
|
20
|
+
end
|
21
|
+
|
22
|
+
def image
|
23
|
+
drawable_items.map.with_index do |item, i|
|
24
|
+
cursor = Image.cursor(1, scroller.current_index?(i))
|
25
|
+
desc =
|
26
|
+
case item
|
27
|
+
when :notification_backend
|
28
|
+
'Notification backend preferences'
|
29
|
+
end
|
30
|
+
|
31
|
+
cursor - Image.whitespace - Image.string(desc)
|
32
|
+
end
|
33
|
+
.intersperse(Image.blank_line)
|
34
|
+
.reduce(Image.empty) { |acc, x| acc | x }
|
35
|
+
end
|
36
|
+
|
37
|
+
def items
|
38
|
+
[
|
39
|
+
:notification_backend
|
40
|
+
]
|
41
|
+
end
|
42
|
+
|
43
|
+
def respond_to_key(key)
|
44
|
+
return true if scroller.respond_to_key(key)
|
45
|
+
|
46
|
+
case key
|
47
|
+
when 10
|
48
|
+
open
|
49
|
+
else
|
50
|
+
return false
|
51
|
+
end
|
52
|
+
|
53
|
+
true
|
54
|
+
end
|
55
|
+
|
56
|
+
def title
|
57
|
+
'Preferences'
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def open
|
63
|
+
tab =
|
64
|
+
case scroller.current_item
|
65
|
+
when :notification_backend
|
66
|
+
Tab::Preferences::NotificationBackend.new(app, client)
|
67
|
+
end
|
68
|
+
|
69
|
+
app.tab_manager.add_and_show(tab)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|