flumtter 5.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +10 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +41 -0
- data/Rakefile +2 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/exe/flumtter +5 -0
- data/flumtter.gemspec +31 -0
- data/lib/flumtter.rb +2 -0
- data/lib/flumtter/app/core/account_selector.rb +88 -0
- data/lib/flumtter/app/core/client.rb +148 -0
- data/lib/flumtter/app/core/command.rb +24 -0
- data/lib/flumtter/app/core/command/dm.rb +12 -0
- data/lib/flumtter/app/core/command/tweet.rb +24 -0
- data/lib/flumtter/app/core/command/user.rb +52 -0
- data/lib/flumtter/app/core/command/userlist.rb +8 -0
- data/lib/flumtter/app/core/core.rb +58 -0
- data/lib/flumtter/app/core/curses.rb +90 -0
- data/lib/flumtter/app/core/hash.rb +29 -0
- data/lib/flumtter/app/core/initializer.rb +28 -0
- data/lib/flumtter/app/core/keyboard.rb +54 -0
- data/lib/flumtter/app/core/plugins.rb +27 -0
- data/lib/flumtter/app/core/string.rb +148 -0
- data/lib/flumtter/app/core/terminal.rb +13 -0
- data/lib/flumtter/app/core/thread.rb +29 -0
- data/lib/flumtter/app/core/toast.rb +26 -0
- data/lib/flumtter/app/core/util.rb +107 -0
- data/lib/flumtter/app/core/windows/base.rb +16 -0
- data/lib/flumtter/app/core/windows/buf_window.rb +194 -0
- data/lib/flumtter/app/core/windows/dialog.rb +62 -0
- data/lib/flumtter/app/core/windows/dmbase.rb +28 -0
- data/lib/flumtter/app/core/windows/dynamic_view.rb +43 -0
- data/lib/flumtter/app/core/windows/favorite.rb +31 -0
- data/lib/flumtter/app/core/windows/follower.rb +20 -0
- data/lib/flumtter/app/core/windows/following.rb +20 -0
- data/lib/flumtter/app/core/windows/mention.rb +29 -0
- data/lib/flumtter/app/core/windows/popup.rb +35 -0
- data/lib/flumtter/app/core/windows/tweet.rb +31 -0
- data/lib/flumtter/app/core/windows/tweetbase.rb +24 -0
- data/lib/flumtter/app/core/windows/userbase.rb +43 -0
- data/lib/flumtter/app/main.rb +3 -0
- data/lib/flumtter/app/plugins/commands.rb +15 -0
- data/lib/flumtter/app/plugins/commands/account_changer.rb +9 -0
- data/lib/flumtter/app/plugins/commands/change_profile.rb +69 -0
- data/lib/flumtter/app/plugins/commands/conversation.rb +39 -0
- data/lib/flumtter/app/plugins/commands/delete.rb +26 -0
- data/lib/flumtter/app/plugins/commands/directmessage.rb +35 -0
- data/lib/flumtter/app/plugins/commands/directmessages.rb +33 -0
- data/lib/flumtter/app/plugins/commands/favorite.rb +22 -0
- data/lib/flumtter/app/plugins/commands/mention.rb +7 -0
- data/lib/flumtter/app/plugins/commands/new_tweet.rb +17 -0
- data/lib/flumtter/app/plugins/commands/reply.rb +26 -0
- data/lib/flumtter/app/plugins/commands/retweet.rb +30 -0
- data/lib/flumtter/app/plugins/commands/unfavorite.rb +26 -0
- data/lib/flumtter/app/plugins/commands/user.rb +77 -0
- data/lib/flumtter/app/plugins/commands/utility.rb +17 -0
- data/lib/flumtter/app/plugins/load.rb +16 -0
- data/lib/flumtter/app/plugins/pry.rb +12 -0
- data/lib/flumtter/app/plugins/timeline.rb +42 -0
- data/lib/flumtter/app/plugins/timeline/base.rb +74 -0
- data/lib/flumtter/app/plugins/timeline/deleted_tweet.rb +23 -0
- data/lib/flumtter/app/plugins/timeline/dm.rb +23 -0
- data/lib/flumtter/app/plugins/timeline/event.rb +11 -0
- data/lib/flumtter/app/plugins/timeline/fav.rb +43 -0
- data/lib/flumtter/app/plugins/timeline/tweet.rb +51 -0
- data/lib/flumtter/app/plugins/toast.rb +45 -0
- data/lib/flumtter/version.rb +3 -0
- metadata +213 -0
@@ -0,0 +1,29 @@
|
|
1
|
+
=begin
|
2
|
+
module Flumtter
|
3
|
+
class FThread < Thread
|
4
|
+
extend Util
|
5
|
+
|
6
|
+
EXCEPTION = proc do |e|
|
7
|
+
exit
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.new_with_report
|
11
|
+
Thread.new do
|
12
|
+
error_handler do
|
13
|
+
yield
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.new_with_restart
|
19
|
+
Thread.new do
|
20
|
+
loop do
|
21
|
+
error_handler(EXCEPTION,EXCEPTION) do
|
22
|
+
yield
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
=end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'terminal-notifier'
|
2
|
+
|
3
|
+
module Flumtter
|
4
|
+
class Toast
|
5
|
+
def initialize(msg)
|
6
|
+
@msg = msg
|
7
|
+
@options = {title: TITLE}
|
8
|
+
if block_given?
|
9
|
+
yield(self)
|
10
|
+
show
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def show
|
15
|
+
TerminalNotifier.notify(@msg, @options) if Setting[:toast?]
|
16
|
+
end
|
17
|
+
|
18
|
+
def method_missing(method, *args)
|
19
|
+
if args.size == 1
|
20
|
+
@options[method] = args.first
|
21
|
+
else
|
22
|
+
super
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
module Flumtter
|
2
|
+
class UnSupportError < ArgumentError; end
|
3
|
+
class NoContentError < ArgumentError; end
|
4
|
+
class ExecutedError < StandardError; end
|
5
|
+
|
6
|
+
module Util
|
7
|
+
def error(e)
|
8
|
+
print <<~EOF.color(Setting[:color][:error])
|
9
|
+
#{e.backtrace.shift}: #{e.message} (#{e.class})
|
10
|
+
#{e.backtrace.join("\n")}
|
11
|
+
EOF
|
12
|
+
end
|
13
|
+
|
14
|
+
def parse_index(text, with_screen_name=false)
|
15
|
+
if m = text.match(index_regexp)
|
16
|
+
obj = id2obj(m[1])
|
17
|
+
return obj, m[2]
|
18
|
+
end
|
19
|
+
if with_screen_name && m = text.match(screen_name_regexp)
|
20
|
+
return m[1], m[2]
|
21
|
+
end
|
22
|
+
raise IndexError
|
23
|
+
end
|
24
|
+
|
25
|
+
def id2obj(id)
|
26
|
+
obj = TimeLine::Base[id.to_i]
|
27
|
+
raise IndexError if obj.nil?
|
28
|
+
obj
|
29
|
+
end
|
30
|
+
|
31
|
+
def dialog_for_index(title, body, with_screen_name=false)
|
32
|
+
dialog = Window::Dialog.new(title, body)
|
33
|
+
dialog.command(index_regexp) do |m|
|
34
|
+
[id2obj(m[1]), m[2]]
|
35
|
+
end
|
36
|
+
if with_screen_name
|
37
|
+
dialog.command(screen_name_regexp) do |m|
|
38
|
+
[m[1], m[2]]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
dialog.show(true, false)
|
42
|
+
end
|
43
|
+
|
44
|
+
def index_with_dialog(m, title, body, with_screen_name=false)
|
45
|
+
if m.empty?
|
46
|
+
dialog_for_index(title, body, with_screen_name)
|
47
|
+
else
|
48
|
+
parse_index(m, with_screen_name)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def index_regexp
|
53
|
+
/^(\d+)[ | ]*(.*)/
|
54
|
+
end
|
55
|
+
|
56
|
+
def screen_name_regexp
|
57
|
+
/^([@|@]*[A-Za-z0-9_]{1,15})[ | ]*(.*)/
|
58
|
+
end
|
59
|
+
|
60
|
+
def command_value_regexp(command)
|
61
|
+
/^#{command}[ | ]*(.*)/
|
62
|
+
end
|
63
|
+
|
64
|
+
def error_handler
|
65
|
+
begin
|
66
|
+
yield
|
67
|
+
rescue IndexError
|
68
|
+
Window::Popup::Error.new("Please select correct index.").show
|
69
|
+
rescue UnSupportError
|
70
|
+
puts "This object is unsupported.".color
|
71
|
+
rescue ExecutedError => e
|
72
|
+
text = e.message.empty? ? "The operation is already executed." : e.message
|
73
|
+
print text.dnl.color(:cyan)
|
74
|
+
rescue NoContentError
|
75
|
+
puts "Please input content.".color
|
76
|
+
rescue Twitter::Error::NotFound => e
|
77
|
+
puts e.message.color
|
78
|
+
rescue Twitter::Error::Unauthorized => e
|
79
|
+
puts e.message.color
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def if_tweet(obj, twitter)
|
84
|
+
case obj
|
85
|
+
when Twitter::Tweet
|
86
|
+
yield(obj)
|
87
|
+
when Twitter::Streaming::Event
|
88
|
+
type = obj.type(twitter)
|
89
|
+
if type.include?(:favorite) || type.include?(:unfavorite)
|
90
|
+
yield(obj.target_object)
|
91
|
+
else
|
92
|
+
raise UnSupportError
|
93
|
+
end
|
94
|
+
else
|
95
|
+
raise UnSupportError
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def sarastire(*args)
|
100
|
+
Flumtter.sarastire(*args)
|
101
|
+
end
|
102
|
+
|
103
|
+
def on_event(*args,&blk)
|
104
|
+
Flumtter.on_event(*args,&blk)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Flumtter
|
2
|
+
module Window
|
3
|
+
class Base
|
4
|
+
include Dispel::Util
|
5
|
+
|
6
|
+
def initialize(title, body,
|
7
|
+
hight=body.size_of_lines,
|
8
|
+
width=body.max_char_of_lines+2)
|
9
|
+
@title = title
|
10
|
+
@body = body
|
11
|
+
@hight = hight + 8
|
12
|
+
@width = [width,title.title.exact_size+2].max
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,194 @@
|
|
1
|
+
require_relative 'dialog'
|
2
|
+
|
3
|
+
module Flumtter
|
4
|
+
module Window
|
5
|
+
module Buf
|
6
|
+
class Buf
|
7
|
+
class NoMoreData < StandardError; end
|
8
|
+
|
9
|
+
attr_accessor :cursor
|
10
|
+
def initialize(base_cls=Element)
|
11
|
+
@base_cls = base_cls
|
12
|
+
@buf = []
|
13
|
+
@cursor = 0
|
14
|
+
end
|
15
|
+
|
16
|
+
def cursor=(n)
|
17
|
+
if n <= 0
|
18
|
+
@cursor = 0
|
19
|
+
else
|
20
|
+
@cursor = n
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def size
|
25
|
+
@buf.size
|
26
|
+
end
|
27
|
+
|
28
|
+
def [](key)
|
29
|
+
elem = @buf[key]
|
30
|
+
raise NoMoreData unless elem
|
31
|
+
elem
|
32
|
+
end
|
33
|
+
|
34
|
+
def adds(objects)
|
35
|
+
objects.each do |obj|
|
36
|
+
@buf << @base_cls.new(obj, @buf.size)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def add(object)
|
41
|
+
@buf << @base_cls.new(object, @buf.size)
|
42
|
+
end
|
43
|
+
|
44
|
+
def get
|
45
|
+
if @buf[@cursor].nil?
|
46
|
+
prefetch
|
47
|
+
end
|
48
|
+
elem = @buf[@cursor]
|
49
|
+
@cursor += 1
|
50
|
+
raise NoMoreData unless elem
|
51
|
+
elem
|
52
|
+
end
|
53
|
+
|
54
|
+
def prev
|
55
|
+
@cursor -= 1
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
class Element
|
60
|
+
attr_reader :object
|
61
|
+
def initialize(object, index)
|
62
|
+
@object = object
|
63
|
+
@index = index
|
64
|
+
end
|
65
|
+
|
66
|
+
def element
|
67
|
+
@text ||= <<~EOF
|
68
|
+
#{header}
|
69
|
+
#{user}
|
70
|
+
#{body.split_num(width - 1)}
|
71
|
+
#{footer}
|
72
|
+
EOF
|
73
|
+
end
|
74
|
+
|
75
|
+
def width
|
76
|
+
Terminal.x-2
|
77
|
+
end
|
78
|
+
|
79
|
+
private
|
80
|
+
def header
|
81
|
+
"#{@index} ".ljust(width, ?-)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
class Screen < Dialog
|
86
|
+
include Util
|
87
|
+
EnableKey = %i(left right up down)
|
88
|
+
|
89
|
+
def initialize(buf, title, body="")
|
90
|
+
@title, @body = title, body
|
91
|
+
@commands = []
|
92
|
+
@buf = buf
|
93
|
+
@range = 0...0
|
94
|
+
end
|
95
|
+
|
96
|
+
def call(str)
|
97
|
+
if str == "?"
|
98
|
+
Window::Popup.new("Command List", <<~EOF).show
|
99
|
+
#{Command.list(@commands)}
|
100
|
+
EOF
|
101
|
+
move(:keep)
|
102
|
+
raise Dispel::Recall
|
103
|
+
elsif str == :left
|
104
|
+
move(:page)
|
105
|
+
raise Dispel::Recall
|
106
|
+
elsif str == :right
|
107
|
+
raise Dispel::Recall
|
108
|
+
elsif str == :up || str == :down
|
109
|
+
move(str)
|
110
|
+
raise Dispel::Recall
|
111
|
+
else
|
112
|
+
move(:keep)
|
113
|
+
@commands.each do |command|
|
114
|
+
if m = str.match(command.command)
|
115
|
+
command.call(m)
|
116
|
+
raise Dispel::Recall
|
117
|
+
end
|
118
|
+
end
|
119
|
+
raise Dispel::NoCommandError
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def id2obj(id)
|
124
|
+
obj = @buf[id.to_i]
|
125
|
+
raise IndexError if obj.nil?
|
126
|
+
obj.object
|
127
|
+
end
|
128
|
+
|
129
|
+
def move(type)
|
130
|
+
case type
|
131
|
+
when :up
|
132
|
+
@buf.cursor = @range.min - 1
|
133
|
+
when :down
|
134
|
+
@buf.cursor = @range.min + 1
|
135
|
+
when :page
|
136
|
+
i, count = 0, 0
|
137
|
+
begin
|
138
|
+
elem = @buf[@range.min-i].element
|
139
|
+
while (count += elem.size_of_lines + 2) < @lines
|
140
|
+
i -= 1
|
141
|
+
elem = @buf[@range.min-i].element
|
142
|
+
end
|
143
|
+
@buf.cursor = @range.min+i
|
144
|
+
rescue Buf::NoMoreData
|
145
|
+
@buf.cursor = @buf.size - 1
|
146
|
+
end
|
147
|
+
when :keep
|
148
|
+
@buf.cursor = @range.min
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
def show
|
153
|
+
Dispel::Screen.open do |screen|
|
154
|
+
Dispel::Window.open(screen.lines, screen.columns, 0, 0) do |win|
|
155
|
+
win.box(?|,?-,?*)
|
156
|
+
win.setpos(win.cury+2, win.curx+1)
|
157
|
+
win.addstr @title.title
|
158
|
+
win.setpos(win.cury+1, 1)
|
159
|
+
win.addstr "¯"*(@title.title.size+2)
|
160
|
+
|
161
|
+
add_multiline_str(win, @body)
|
162
|
+
|
163
|
+
start = @buf.cursor
|
164
|
+
begin
|
165
|
+
loop do
|
166
|
+
elem = @buf.get.element
|
167
|
+
if (@lines = screen.lines) > win.cury + elem.size_of_lines + 4
|
168
|
+
add_multiline_str(win, elem)
|
169
|
+
win.setpos(win.cury+1,1)
|
170
|
+
else
|
171
|
+
@buf.prev
|
172
|
+
break
|
173
|
+
end
|
174
|
+
end
|
175
|
+
rescue Buf::NoMoreData
|
176
|
+
end
|
177
|
+
@range = start...@buf.cursor
|
178
|
+
|
179
|
+
win.setpos(win.cury+2, 1)
|
180
|
+
win.addstr "help: ?".rjust(win.maxx - 2)
|
181
|
+
win.setpos(win.cury+1, 1)
|
182
|
+
call getstr(win, EnableKey)
|
183
|
+
end
|
184
|
+
end
|
185
|
+
rescue Dispel::Recall
|
186
|
+
show
|
187
|
+
rescue Dispel::NoCommandError => e
|
188
|
+
Window::Popup::Error.new(e.class.to_s).show
|
189
|
+
show
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require_relative 'base'
|
2
|
+
|
3
|
+
module Flumtter
|
4
|
+
module Window
|
5
|
+
class Dialog < Base
|
6
|
+
def initialize(*args)
|
7
|
+
super
|
8
|
+
@commands = []
|
9
|
+
end
|
10
|
+
|
11
|
+
def command(command, help="", &blk)
|
12
|
+
@commands << Command.new(command, help, &blk)
|
13
|
+
end
|
14
|
+
|
15
|
+
def call(str)
|
16
|
+
if str == "?"
|
17
|
+
Window::Popup.new("Command List", <<~EOF).show
|
18
|
+
#{Command.list(@commands)}
|
19
|
+
EOF
|
20
|
+
raise Dispel::Recall
|
21
|
+
else
|
22
|
+
@commands.each do |command|
|
23
|
+
if m = str.match(command.command)
|
24
|
+
return command.call(m)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
raise Dispel::NoCommandError
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def show(recall=false, help=true)
|
32
|
+
Dispel::Screen.open do |screen|
|
33
|
+
Dispel::Window.open(@hight, @width, 0, 0) do |win|
|
34
|
+
win.box(?|,?-,?*)
|
35
|
+
win.setpos(win.cury+2, win.curx+1)
|
36
|
+
win.addstr @title.title
|
37
|
+
win.setpos(win.cury+1, 1)
|
38
|
+
win.addstr "¯"*(@title.title.size+2)
|
39
|
+
|
40
|
+
add_multiline_str(win, @body)
|
41
|
+
|
42
|
+
if block_given?
|
43
|
+
yield win
|
44
|
+
else
|
45
|
+
win.setpos(win.cury+2, 1)
|
46
|
+
if help
|
47
|
+
win.addstr "help: ?".rjust(win.maxx - 2)
|
48
|
+
win.setpos(win.cury+1, 1)
|
49
|
+
end
|
50
|
+
call getstr(win)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
rescue Dispel::Recall
|
55
|
+
show(recall, help)
|
56
|
+
rescue Dispel::NoCommandError => e
|
57
|
+
Window::Popup::Error.new(e.class.to_s).show
|
58
|
+
show(recall, help) if recall
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|