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,33 @@
|
|
1
|
+
require 'twterm/event/notification/abstract_notification'
|
2
|
+
|
3
|
+
module Twterm
|
4
|
+
module Event
|
5
|
+
module Notification
|
6
|
+
class ListMemberAdded < AbstractNotification
|
7
|
+
# @param [Twterm::List] list
|
8
|
+
def initialize(list)
|
9
|
+
@list = list
|
10
|
+
end
|
11
|
+
|
12
|
+
# @return [String] notification body
|
13
|
+
def body
|
14
|
+
list.description
|
15
|
+
end
|
16
|
+
|
17
|
+
# @return [String] notification title
|
18
|
+
def title
|
19
|
+
"You've been added to #{list.full_name}"
|
20
|
+
end
|
21
|
+
|
22
|
+
# @return [String] notification url
|
23
|
+
def url
|
24
|
+
list.url
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
attr_reader :list
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'twterm/event/notification/abstract_notification'
|
2
|
+
|
3
|
+
module Twterm
|
4
|
+
module Event
|
5
|
+
module Notification
|
6
|
+
class Mention < AbstractNotification
|
7
|
+
# @param [Twterm::Status] status
|
8
|
+
# @param [Twterm::User] user
|
9
|
+
def initialize(status, user)
|
10
|
+
@status = status
|
11
|
+
@user = user
|
12
|
+
end
|
13
|
+
|
14
|
+
# @return [String] notification body
|
15
|
+
def body
|
16
|
+
status.text
|
17
|
+
end
|
18
|
+
|
19
|
+
# @return [String] notification title
|
20
|
+
def title
|
21
|
+
"@#{user.screen_name} has replied to your tweet"
|
22
|
+
end
|
23
|
+
|
24
|
+
# @return [String] notification url
|
25
|
+
def url
|
26
|
+
status.url
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
attr_reader :status, :user
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'twterm/event/notification/abstract_notification'
|
2
|
+
|
3
|
+
module Twterm
|
4
|
+
module Event
|
5
|
+
module Notification
|
6
|
+
class Quote < AbstractNotification
|
7
|
+
# @param [Twterm::Status] status
|
8
|
+
# @param [Twterm::User] user
|
9
|
+
def initialize(status, user)
|
10
|
+
@status = status
|
11
|
+
@user = user
|
12
|
+
end
|
13
|
+
|
14
|
+
# @return [String] notification body
|
15
|
+
def body
|
16
|
+
status.text
|
17
|
+
end
|
18
|
+
|
19
|
+
# @return [String] notification title
|
20
|
+
def title
|
21
|
+
"@#{user.screen_name} has quoted your tweet"
|
22
|
+
end
|
23
|
+
|
24
|
+
# @return [String] notification url
|
25
|
+
def url
|
26
|
+
status.url
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
attr_reader :status, :user
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'twterm/event/notification/abstract_notification'
|
2
|
+
|
3
|
+
module Twterm
|
4
|
+
module Event
|
5
|
+
module Notification
|
6
|
+
class Retweet < AbstractNotification
|
7
|
+
# @param [Twterm::Status] status
|
8
|
+
# @param [Twterm::User] user
|
9
|
+
def initialize(status, user)
|
10
|
+
@status = status
|
11
|
+
@user = user
|
12
|
+
end
|
13
|
+
|
14
|
+
# @return [String] notification body
|
15
|
+
def body
|
16
|
+
status.text
|
17
|
+
end
|
18
|
+
|
19
|
+
# @return [String] notification title
|
20
|
+
def title
|
21
|
+
"@#{user.screen_name} has retweeted your tweet"
|
22
|
+
end
|
23
|
+
|
24
|
+
# @return [String] notification url
|
25
|
+
def url
|
26
|
+
status.url
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
attr_reader :status, :user
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -23,6 +23,10 @@ module Twterm
|
|
23
23
|
@mappings[key]
|
24
24
|
end
|
25
25
|
|
26
|
+
def self.commands
|
27
|
+
self::DEFAULT_MAPPINGS.keys
|
28
|
+
end
|
29
|
+
|
26
30
|
def to_h
|
27
31
|
@dict
|
28
32
|
end
|
@@ -49,10 +53,6 @@ module Twterm
|
|
49
53
|
raise NoSuchKey.new(key)
|
50
54
|
end
|
51
55
|
end
|
52
|
-
|
53
|
-
def self.commands
|
54
|
-
self::DEFAULT_MAPPINGS.keys
|
55
|
-
end
|
56
56
|
end
|
57
57
|
end
|
58
58
|
end
|
data/lib/twterm/list.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
module Twterm
|
2
2
|
class List
|
3
|
-
attr_reader :id, :name, :slug, :full_name, :mode, :description, :member_count, :subscriber_count
|
3
|
+
attr_reader :id, :name, :slug, :full_name, :mode, :description, :member_count, :subscriber_count, :url
|
4
4
|
|
5
5
|
def ==(other)
|
6
6
|
other.is_a?(self.class) && id == other.id
|
@@ -19,6 +19,7 @@ module Twterm
|
|
19
19
|
@description = list.description.is_a?(Twitter::NullObject) ? '' : list.description
|
20
20
|
@member_count = list.member_count
|
21
21
|
@subscriber_count = list.subscriber_count
|
22
|
+
@url = list.url
|
22
23
|
|
23
24
|
self
|
24
25
|
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'twterm/subscriber'
|
2
|
+
require 'twterm/event/message/abstract_message'
|
3
|
+
require 'twterm/event/screen/resize'
|
4
|
+
|
5
|
+
module Twterm
|
6
|
+
class MessageWindow
|
7
|
+
include Singleton
|
8
|
+
include Curses
|
9
|
+
include Subscriber
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@window = stdscr.subwin(1, stdscr.maxx, stdscr.maxy - 2, 0)
|
13
|
+
@queue = Queue.new
|
14
|
+
|
15
|
+
subscribe(Event::Message::AbstractMessage) do |e|
|
16
|
+
queue(e)
|
17
|
+
end
|
18
|
+
|
19
|
+
subscribe(Event::Screen::Resize, :resize)
|
20
|
+
|
21
|
+
Thread.new do
|
22
|
+
while message = @queue.pop # rubocop:disable Lint/AssignmentInCondition:
|
23
|
+
show(message)
|
24
|
+
sleep 3
|
25
|
+
show
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def show_info(message)
|
31
|
+
@queue.push(Event::Message::Info.new(message))
|
32
|
+
self
|
33
|
+
end
|
34
|
+
|
35
|
+
def show(message = nil)
|
36
|
+
loop do
|
37
|
+
break unless closed?
|
38
|
+
sleep 0.5
|
39
|
+
end
|
40
|
+
|
41
|
+
@window.clear
|
42
|
+
|
43
|
+
if message.is_a?(Event::Message::AbstractMessage)
|
44
|
+
fg_color, bg_color =
|
45
|
+
case message
|
46
|
+
when Event::Message::Error
|
47
|
+
[:white, :red]
|
48
|
+
when Event::Message::Info
|
49
|
+
[:black, :cyan]
|
50
|
+
when Event::Message::Success
|
51
|
+
[:black, :green]
|
52
|
+
when Event::Message::Warning
|
53
|
+
[:black, :yellow]
|
54
|
+
end
|
55
|
+
|
56
|
+
@window.with_color(fg_color, bg_color) do
|
57
|
+
@window.setpos(0, 0)
|
58
|
+
@window.addstr(' ' * @window.maxx)
|
59
|
+
@window.setpos(0, 1)
|
60
|
+
time = message.time.strftime('[%H:%M:%S]')
|
61
|
+
body = message.body.gsub("\n", ' ')
|
62
|
+
@window.addstr("#{time} #{body}")
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
@window.refresh
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
def queue(message)
|
72
|
+
@queue.push(message)
|
73
|
+
self
|
74
|
+
end
|
75
|
+
|
76
|
+
def resize(_event)
|
77
|
+
@window.resize(1, stdscr.maxx)
|
78
|
+
@window.move(stdscr.maxy - 2, 0)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Twterm
|
2
|
+
module NotificationBackend
|
3
|
+
# @abstract
|
4
|
+
class AbstractNotificationBackend
|
5
|
+
# @abstract
|
6
|
+
# @param [Twterm::Event::Notification::AbstractNotification] _notification a notification to show
|
7
|
+
def notify(_notification)
|
8
|
+
raise NotImplementedError, '`notify` method must be implemented'
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
attr_reader :app
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'curses'
|
2
|
+
|
3
|
+
require 'twterm/event/message/info'
|
4
|
+
require 'twterm/notification_backend/abstract_notification_backend'
|
5
|
+
require 'twterm/publisher'
|
6
|
+
|
7
|
+
module Twterm
|
8
|
+
module NotificationBackend
|
9
|
+
class InlineBackend < AbstractNotificationBackend
|
10
|
+
include Publisher
|
11
|
+
|
12
|
+
# @param [Twterm::Event::Notification::AbstractNotification] notification
|
13
|
+
# @return [void]
|
14
|
+
def notify(notification)
|
15
|
+
message = Event::Message::Info.new(notification.fallback)
|
16
|
+
publish(message)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'terminal-notifier'
|
2
|
+
|
3
|
+
require 'twterm/notification_backend/abstract_notification_backend'
|
4
|
+
|
5
|
+
module Twterm
|
6
|
+
module NotificationBackend
|
7
|
+
class TerminalNotifierBackend < AbstractNotificationBackend
|
8
|
+
# @param [Twterm::Event::Notification::AbstractNotification] notification notification to display via terminal-notifier
|
9
|
+
def notify(notification)
|
10
|
+
opts = {
|
11
|
+
title: 'twterm',
|
12
|
+
subtitle: notification.title,
|
13
|
+
group: Process.pid,
|
14
|
+
sound: 'Purr',
|
15
|
+
}
|
16
|
+
|
17
|
+
opts[:open] = notification.url unless notification.url.nil?
|
18
|
+
|
19
|
+
TerminalNotifier.notify(notification.body, opts)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'shellwords'
|
2
|
+
|
3
|
+
require 'twterm/notification_backend/abstract_notification_backend'
|
4
|
+
|
5
|
+
module Twterm
|
6
|
+
module NotificationBackend
|
7
|
+
class TmuxBackend < AbstractNotificationBackend
|
8
|
+
# @param [Twterm::Event::Notification::AbstractNotification] notification notification to display in tmux status line
|
9
|
+
# @return [void]
|
10
|
+
def notify(notification)
|
11
|
+
`tmux set-option display-time 3000`
|
12
|
+
`tmux set-option message-style fg=black,bg=green`
|
13
|
+
`tmux display #{Shellwords.escape(" [twterm] #{notification.fallback.gsub("\n", ' ')}")}`
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'twterm/event/notification/abstract_notification'
|
2
|
+
require 'twterm/notification_backend/inline_backend'
|
3
|
+
require 'twterm/notification_backend/terminal_notifier_backend'
|
4
|
+
require 'twterm/notification_backend/tmux_backend'
|
5
|
+
require 'twterm/subscriber'
|
6
|
+
|
7
|
+
module Twterm
|
8
|
+
class NotificationDispatcher
|
9
|
+
include Subscriber
|
10
|
+
|
11
|
+
# @param [Twterm::Preferences] preferences
|
12
|
+
def initialize(preferences)
|
13
|
+
@preferences = preferences
|
14
|
+
|
15
|
+
@backends = {
|
16
|
+
inline: NotificationBackend::InlineBackend.new,
|
17
|
+
terminal_notifier: NotificationBackend::TerminalNotifierBackend.new,
|
18
|
+
tmux: NotificationBackend::TmuxBackend.new,
|
19
|
+
}
|
20
|
+
|
21
|
+
subscribe(Event::Notification::AbstractNotification) { |n| dispatch(n) }
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
attr_reader :backends, :preferences
|
27
|
+
|
28
|
+
# @param [Twterm::Notification::AbstractNotification] notification notification to dispatch to backends
|
29
|
+
# @return [void]
|
30
|
+
def dispatch(notification)
|
31
|
+
backends.keys.each do |key|
|
32
|
+
backends[key].notify(notification) if preferences[:notification_backend, key]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'toml'
|
2
|
+
|
3
|
+
module Twterm
|
4
|
+
class PersistableConfigurationProxy
|
5
|
+
# @param [Twterm::AbstractPersistableConfiguration] instance A configuration to persist
|
6
|
+
# @param [String] filepath File path to persist the given configuration
|
7
|
+
def initialize(instance, filepath)
|
8
|
+
@instance = instance
|
9
|
+
@filepath = filepath
|
10
|
+
end
|
11
|
+
|
12
|
+
def [](*args)
|
13
|
+
instance.[](*args)
|
14
|
+
end
|
15
|
+
|
16
|
+
def []=(*args)
|
17
|
+
instance.[]=(*args)
|
18
|
+
persist!
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.getc
|
22
|
+
system('stty raw -echo')
|
23
|
+
STDIN.getc
|
24
|
+
ensure
|
25
|
+
system('stty -raw echo')
|
26
|
+
end
|
27
|
+
|
28
|
+
# Loads a configuration file from and returns a proxy.
|
29
|
+
# Falls back to the default value when the specified file does not exist.
|
30
|
+
#
|
31
|
+
# @param [Class] klass Must be a subclass of {Twterm::AbstractPersistableConfiguration}
|
32
|
+
# @param [String] filepath File path to load configuration from
|
33
|
+
# @return [Twterm::PersistableConfigurationProxy] a configuration proxy
|
34
|
+
def self.load_from_file!(klass, filepath)
|
35
|
+
config = TOML.load_file(filepath, symbolize_keys: true)
|
36
|
+
new(klass.new(config), filepath).migrate!
|
37
|
+
rescue Errno::ENOENT
|
38
|
+
new(klass.default, filepath)
|
39
|
+
rescue TOML::ParseError, TOML::ValueOverwriteError => e
|
40
|
+
msg =
|
41
|
+
case e
|
42
|
+
when TOML::ParseError
|
43
|
+
"Your configuration file could not be parsed"
|
44
|
+
when TOML::ValueOverwriteError
|
45
|
+
"`#{e.key}` is declared more than once"
|
46
|
+
end
|
47
|
+
|
48
|
+
warn <<-EOS
|
49
|
+
\e[1mCould not load the configuration file: #{filepath}\e[0m
|
50
|
+
(#{msg})
|
51
|
+
|
52
|
+
Falling back to the default key assignments
|
53
|
+
|
54
|
+
Check the syntax and edit the file manually,
|
55
|
+
or remove it and launch twterm again to restore
|
56
|
+
|
57
|
+
Press any key to continue
|
58
|
+
EOS
|
59
|
+
|
60
|
+
getc
|
61
|
+
|
62
|
+
new(klass.default, filepath)
|
63
|
+
end
|
64
|
+
|
65
|
+
# @return [self]
|
66
|
+
def migrate!
|
67
|
+
instance.complete_missing_items!
|
68
|
+
persist!
|
69
|
+
|
70
|
+
self
|
71
|
+
end
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
attr_reader :filepath, :instance
|
76
|
+
|
77
|
+
def persist!
|
78
|
+
hash = TOML.dump(instance.to_h).gsub("\n[", "\n\n[")
|
79
|
+
File.open(filepath, 'w', 0644) { |f| f.write(hash) }
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|