flumtter 5.0.1

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.
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