twterm 2.1.0 → 2.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|