flumtter 5.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +10 -0
  3. data/CODE_OF_CONDUCT.md +74 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE.txt +21 -0
  6. data/README.md +41 -0
  7. data/Rakefile +2 -0
  8. data/bin/console +14 -0
  9. data/bin/setup +8 -0
  10. data/exe/flumtter +5 -0
  11. data/flumtter.gemspec +31 -0
  12. data/lib/flumtter.rb +2 -0
  13. data/lib/flumtter/app/core/account_selector.rb +88 -0
  14. data/lib/flumtter/app/core/client.rb +148 -0
  15. data/lib/flumtter/app/core/command.rb +24 -0
  16. data/lib/flumtter/app/core/command/dm.rb +12 -0
  17. data/lib/flumtter/app/core/command/tweet.rb +24 -0
  18. data/lib/flumtter/app/core/command/user.rb +52 -0
  19. data/lib/flumtter/app/core/command/userlist.rb +8 -0
  20. data/lib/flumtter/app/core/core.rb +58 -0
  21. data/lib/flumtter/app/core/curses.rb +90 -0
  22. data/lib/flumtter/app/core/hash.rb +29 -0
  23. data/lib/flumtter/app/core/initializer.rb +28 -0
  24. data/lib/flumtter/app/core/keyboard.rb +54 -0
  25. data/lib/flumtter/app/core/plugins.rb +27 -0
  26. data/lib/flumtter/app/core/string.rb +148 -0
  27. data/lib/flumtter/app/core/terminal.rb +13 -0
  28. data/lib/flumtter/app/core/thread.rb +29 -0
  29. data/lib/flumtter/app/core/toast.rb +26 -0
  30. data/lib/flumtter/app/core/util.rb +107 -0
  31. data/lib/flumtter/app/core/windows/base.rb +16 -0
  32. data/lib/flumtter/app/core/windows/buf_window.rb +194 -0
  33. data/lib/flumtter/app/core/windows/dialog.rb +62 -0
  34. data/lib/flumtter/app/core/windows/dmbase.rb +28 -0
  35. data/lib/flumtter/app/core/windows/dynamic_view.rb +43 -0
  36. data/lib/flumtter/app/core/windows/favorite.rb +31 -0
  37. data/lib/flumtter/app/core/windows/follower.rb +20 -0
  38. data/lib/flumtter/app/core/windows/following.rb +20 -0
  39. data/lib/flumtter/app/core/windows/mention.rb +29 -0
  40. data/lib/flumtter/app/core/windows/popup.rb +35 -0
  41. data/lib/flumtter/app/core/windows/tweet.rb +31 -0
  42. data/lib/flumtter/app/core/windows/tweetbase.rb +24 -0
  43. data/lib/flumtter/app/core/windows/userbase.rb +43 -0
  44. data/lib/flumtter/app/main.rb +3 -0
  45. data/lib/flumtter/app/plugins/commands.rb +15 -0
  46. data/lib/flumtter/app/plugins/commands/account_changer.rb +9 -0
  47. data/lib/flumtter/app/plugins/commands/change_profile.rb +69 -0
  48. data/lib/flumtter/app/plugins/commands/conversation.rb +39 -0
  49. data/lib/flumtter/app/plugins/commands/delete.rb +26 -0
  50. data/lib/flumtter/app/plugins/commands/directmessage.rb +35 -0
  51. data/lib/flumtter/app/plugins/commands/directmessages.rb +33 -0
  52. data/lib/flumtter/app/plugins/commands/favorite.rb +22 -0
  53. data/lib/flumtter/app/plugins/commands/mention.rb +7 -0
  54. data/lib/flumtter/app/plugins/commands/new_tweet.rb +17 -0
  55. data/lib/flumtter/app/plugins/commands/reply.rb +26 -0
  56. data/lib/flumtter/app/plugins/commands/retweet.rb +30 -0
  57. data/lib/flumtter/app/plugins/commands/unfavorite.rb +26 -0
  58. data/lib/flumtter/app/plugins/commands/user.rb +77 -0
  59. data/lib/flumtter/app/plugins/commands/utility.rb +17 -0
  60. data/lib/flumtter/app/plugins/load.rb +16 -0
  61. data/lib/flumtter/app/plugins/pry.rb +12 -0
  62. data/lib/flumtter/app/plugins/timeline.rb +42 -0
  63. data/lib/flumtter/app/plugins/timeline/base.rb +74 -0
  64. data/lib/flumtter/app/plugins/timeline/deleted_tweet.rb +23 -0
  65. data/lib/flumtter/app/plugins/timeline/dm.rb +23 -0
  66. data/lib/flumtter/app/plugins/timeline/event.rb +11 -0
  67. data/lib/flumtter/app/plugins/timeline/fav.rb +43 -0
  68. data/lib/flumtter/app/plugins/timeline/tweet.rb +51 -0
  69. data/lib/flumtter/app/plugins/toast.rb +45 -0
  70. data/lib/flumtter/version.rb +3 -0
  71. metadata +213 -0
@@ -0,0 +1,13 @@
1
+ module Flumtter
2
+ class Terminal
3
+ class << self
4
+ def x
5
+ `tput cols`.to_i
6
+ end
7
+
8
+ def y
9
+ `tput lines`.to_i
10
+ end
11
+ end
12
+ end
13
+ end
@@ -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