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,24 @@
1
+ module Flumtter
2
+ class Command
3
+ attr_reader :name, :command, :help
4
+ def initialize(command, help="", &blk)
5
+ @name = command.is_a?(Regexp) ? command.inspect : command
6
+ @command = command.is_a?(String) ? command.to_reg : command
7
+ @help = help
8
+ @blk = blk
9
+ end
10
+
11
+ def call(*args)
12
+ @blk.call(*args)
13
+ end
14
+
15
+ def self.list(commands)
16
+ char_len = commands.max_by{|c|c.name.size}.name.size + 1 rescue 0
17
+ commands.map do |c|
18
+ c.name.ljust(char_len) + c.help
19
+ end.join("\n")
20
+ end
21
+ end
22
+
23
+ sarastire "core/command"
24
+ end
@@ -0,0 +1,12 @@
1
+ module Flumtter
2
+ module Command::DM
3
+ def add_command(twitter)
4
+ command("r", "Reply") do |m|
5
+ error_handler do
6
+ obj, m2 = parse_index(m[1])
7
+ twitter.rest.create_direct_message(obj.sender.id, m2)
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,24 @@
1
+ module Flumtter
2
+ module Command::Tweet
3
+ def add_command(twitter)
4
+ command("f", "Favorite") do |m|
5
+ error_handler do
6
+ obj, _ = parse_index(m[1])
7
+ Plugins::Favorite.favorite(obj, twitter)
8
+ end
9
+ end
10
+ command("t", "Retweet") do |m|
11
+ error_handler do
12
+ obj, _ = parse_index(m[1])
13
+ Plugins::Retweet.retweet(obj, twitter)
14
+ end
15
+ end
16
+ command("r", "Reply") do |m|
17
+ error_handler do
18
+ obj, m2 = parse_index(m[1])
19
+ Plugins::Reply.update(obj, m2, twitter)
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,52 @@
1
+ module Flumtter
2
+ module Command::User
3
+ include Util
4
+
5
+ def add_command(user, twitter)
6
+ command("tweet", "Tweet List") do
7
+ error_handler do
8
+ Window::Tweet.new(user, twitter).show
9
+ end
10
+ end
11
+ command("follower", "Follower List") do
12
+ error_handler do
13
+ Window::Follower.new(user, twitter).show
14
+ end
15
+ end
16
+ command("following", "Following List") do
17
+ error_handler do
18
+ Window::Following.new(user, twitter).show
19
+ end
20
+ end
21
+ command("favorite", "Favorite List") do
22
+ error_handler do
23
+ Window::Favorite.new(user, twitter).show
24
+ end
25
+ end
26
+ command("follow", "Follow") do
27
+ error_handler do
28
+ twitter.rest.follow(user.screen_name)
29
+ Window::Popup::Success.new("follow success").show
30
+ end
31
+ end
32
+ command("unfollow", "Unfollow") do
33
+ error_handler do
34
+ twitter.rest.unfollow(user.screen_name)
35
+ Window::Popup::Success.new("unfollow success").show
36
+ end
37
+ end
38
+ command("block", "Block") do
39
+ error_handler do
40
+ twitter.rest.block(user.screen_name)
41
+ Window::Popup::Success.new("block success").show
42
+ end
43
+ end
44
+ command("unblock", "UnBlock") do
45
+ error_handler do
46
+ twitter.rest.unblock(user.screen_name)
47
+ Window::Popup::Success.new("unblock success").show
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,8 @@
1
+ module Flumtter
2
+ module Command::UserList
3
+ include Util
4
+
5
+ def add_command(twitter)
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,58 @@
1
+ require 'pry'
2
+
3
+ module Flumtter
4
+ SourcePath = File.expand_path('../../', __FILE__)
5
+ UserPath = File.expand_path('~/.flumtter')
6
+ [SourcePath, UserPath].each do |path|
7
+ def path.join(*args)
8
+ File.join(self, *args)
9
+ end
10
+ end
11
+ FileUtils.mkdir(UserPath) unless FileTest.exist?(UserPath)
12
+
13
+ data_path = UserPath.join("data", "data.bin")
14
+ Config = Marshal.load(File.read(data_path)) rescue {}
15
+ at_exit {
16
+ puts 'data saved'
17
+ File.write(data_path, Marshal.dump(Config))
18
+ }
19
+
20
+ Thread.abort_on_exception = true
21
+
22
+ module_function
23
+ def sarastire(path, file=nil)
24
+ path = file.nil? ? SourcePath.join(path, '*.rb') : SourcePath.join(path, file)
25
+ Dir.glob(path).each{|plugin|require plugin}
26
+ end
27
+
28
+ def sarastire_user(path, file=nil)
29
+ path = file.nil? ? UserPath.join(path, '*.rb') : UserPath.join(path, file)
30
+ Dir.glob(path).each{|plugin|require plugin}
31
+ end
32
+
33
+ @events = Hash.new{|h,k|h[k] = []}
34
+ def on_event(event,&blk)
35
+ @events[event] << blk
36
+ end
37
+
38
+ def callback(event,object=nil)
39
+ @events[event].each{|blk|blk.call(object)}
40
+ end
41
+
42
+ TITLE = "Flumtter"
43
+
44
+ sarastire 'core', 'util.rb'
45
+ sarastire_user 'setting'
46
+ sarastire 'core'
47
+ sarastire 'plugins'
48
+ sarastire_user 'plugins'
49
+
50
+ TITLE.terminal_title
51
+
52
+ def start
53
+ options = Initializer.optparse
54
+ Setting.merge!(options)
55
+ Client.new AccountSelector.select(options)
56
+ rescue Interrupt
57
+ end
58
+ end
@@ -0,0 +1,90 @@
1
+ require 'dispel'
2
+ module Dispel
3
+ class CloseWindow < StandardError; end
4
+ class NoCommandError < StandardError; end
5
+ class Recall < StandardError; end
6
+
7
+ class Window
8
+ class << self
9
+ def open(*args)
10
+ win = Curses::Window.new(*args)
11
+ win.keypad(true)
12
+ yield win
13
+ rescue CloseWindow
14
+ ensure
15
+ win.refresh
16
+ win.close
17
+ end
18
+
19
+ def close
20
+ raise CloseWindow
21
+ end
22
+ end
23
+ end
24
+
25
+ module Util
26
+ def addstr(win, str)
27
+ win.setpos(win.cury+1, 1)
28
+ win.addstr str
29
+ end
30
+
31
+ def add_multiline_str(win, str)
32
+ str.each_line do |line|
33
+ addstr(win, line.chomp)
34
+ end
35
+ end
36
+
37
+ def getstr(win, ex=[])
38
+ buf = ""
39
+ x = win.curx
40
+ loop do
41
+ input = Dispel::Keyboard.translate_key_to_code(win.getch)
42
+ ex.each do |k|
43
+ return input if input == k
44
+ end
45
+
46
+ case input
47
+ when :"Ctrl+c"
48
+ raise CloseWindow
49
+ when :enter
50
+ return buf
51
+ when :escape
52
+ raise CloseWindow
53
+ when :left
54
+ if win.curx > x
55
+ win.setpos(win.cury, win.curx-1)
56
+ end
57
+ when :right
58
+ if win.curx <= buf.size
59
+ win.setpos(win.cury, win.curx+1)
60
+ end
61
+ # TODO: 文字移動して削除入力
62
+ when :backspace
63
+ buf.chop!
64
+ while win.curx > x
65
+ win.setpos(win.cury, win.curx-1)
66
+ win.delch()
67
+ win.insch(" ")
68
+ end
69
+ win.addstr(buf)
70
+ when String
71
+ buf << input.force_encoding("utf-8")
72
+ win.setpos(win.cury, win.curx)
73
+ win.addstr(input)
74
+ else
75
+ p input
76
+ end
77
+ end
78
+ rescue NoMethodError => e
79
+ if e.backtrace.shift =~ /keyboard.rb:210/
80
+ raise Dispel::Recall
81
+ else
82
+ raise e
83
+ end
84
+ end
85
+ end
86
+ end
87
+
88
+ module Flumtter
89
+ sarastire 'core/windows'
90
+ end
@@ -0,0 +1,29 @@
1
+ class Hash
2
+ def requires(*args)
3
+ args.each do |arg|
4
+ raise ArgumentError, "Parameter is missing a value(#{arg})" if self[arg].nil?
5
+ end
6
+ end
7
+
8
+ def nest_level(level=0)
9
+ self.map do |k,v|
10
+ if v.is_a?(Hash)
11
+ v.nest_level(level+1)
12
+ else
13
+ level+1
14
+ end
15
+ end.max
16
+ end
17
+
18
+ def indent(t=0, n=2)
19
+ self.map do |k,v|
20
+ if self.nest_level == 1 || !v.is_a?(Hash)
21
+ between = ": "
22
+ char_len = self.keys.max_by{|key|key.size}.size + (n * t)
23
+ k.to_s.shift(t*n).ljust(char_len) + ": " + v.to_s.nshift(char_len + between.size)
24
+ else
25
+ [k.to_s.shift(t*n), *v.is_a?(Hash) ? v.indent(t+1, n) : [v.to_s.shift(n*2)]].join("\n")
26
+ end
27
+ end.join("\n")
28
+ end
29
+ end
@@ -0,0 +1,28 @@
1
+ require 'optparse'
2
+
3
+ module Initializer
4
+ @args, @events = [], {}
5
+
6
+ module_function
7
+ def add_opt(&blk)
8
+ @args << blk
9
+ end
10
+
11
+ def run(sym, &blk)
12
+ @events[sym] = blk
13
+ end
14
+
15
+ def optparse
16
+ opt = OptionParser.new
17
+ options = {}
18
+ opt.on('-n VALUE', '--name VALUE', 'account name'){|v|options[:name] = v}
19
+ opt.on('-i VALUE', '--index VALUE', 'account index'){|v|options[:id] = v.to_i}
20
+ opt.on('-s', '--non_stream', 'without stream'){|v|options[:non_stream] = v}
21
+ opt.on('-d', '--debug', 'debug mode'){|v|options[:debug] = v}
22
+ opt.on('--args VALUE'){|v|options[:args] = v}
23
+ @args.each{|args|args.call(opt, options)}
24
+ opt.parse!(ARGV)
25
+ options.each{|k,v|@events[k].call(v,options) unless @events[k].nil?}
26
+ options
27
+ end
28
+ end
@@ -0,0 +1,54 @@
1
+ require 'io/console'
2
+
3
+ module Flumtter
4
+ class Keyboard
5
+ extend Util
6
+
7
+ class << self
8
+ @@commands = []
9
+
10
+ def input(twitter)
11
+ loop do
12
+ input = STDIN.noecho(&:gets)
13
+ next if input.nil?
14
+ twitter.pause
15
+ callback(input.chomp, twitter)
16
+ twitter.resume
17
+ end
18
+ rescue Interrupt
19
+ end
20
+
21
+ def callback(input, twitter)
22
+ if input == "?"
23
+ Window::Popup.new("Command List", <<~EOF).show
24
+ #{Command.list(@@commands)}
25
+
26
+ For more information, please see the following Home page.
27
+ http://github.com/flum1025/flumtter3
28
+ This software is released under the MIT License
29
+ Copyright © @flum_ 2016
30
+ EOF
31
+ else
32
+ @@commands.each do |command|
33
+ if m = input.match(command.command)
34
+ return command.call(m, twitter)
35
+ end
36
+ end
37
+ puts "Command not found".color
38
+ end
39
+ end
40
+
41
+ def add(command, help, &blk)
42
+ @@commands << Command.new(command, help) do |*args|
43
+ begin
44
+ blk.call(*args)
45
+ rescue SystemExit => e
46
+ raise e
47
+ rescue Exception => e
48
+ error e
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,27 @@
1
+ require_relative 'Initializer'
2
+
3
+ module Flumtter
4
+ module Plugins
5
+ module Base
6
+ def add_opt(&blk)
7
+ Initializer.add_opt(&blk)
8
+ end
9
+
10
+ def run_opt(*args, &blk)
11
+ Initializer.run(*args, &blk)
12
+ end
13
+ end
14
+
15
+ module_function
16
+ def plugin(blk)
17
+ m = self.const_set(File.basename(blk.source_location[0], '.rb').to_camel.to_sym, Module.new)
18
+ m.extend(Base)
19
+ m.extend(Util)
20
+ m.instance_eval(&blk)
21
+ end
22
+ end
23
+ end
24
+
25
+ def plugin(&blk)
26
+ Flumtter::Plugins.plugin(blk)
27
+ end
@@ -0,0 +1,148 @@
1
+ class String
2
+ def to_camel
3
+ self.split(/_/).map(&:capitalize).join
4
+ end
5
+
6
+ def terminal_title
7
+ print "\033];#{self}\007"
8
+ at_exit{print "\033];\007"}
9
+ end
10
+
11
+ def title
12
+ "【#{self}】"
13
+ end
14
+
15
+ def color(color=:red)
16
+ case color
17
+ when :white then "\e[1;37m#{self}\e[0m"
18
+ when :light_gray then "\e[37m#{self}\e[0m"
19
+ when :gray then "\e[1;30m#{self}\e[0m"
20
+ when :back then "\e[30m#{self}\e[0m"
21
+ when :red then "\e[31m#{self}\e[0m"
22
+ when :light_red then "\e[1;31m#{self}\e[0m"
23
+ when :green then "\e[32m#{self}\e[0m"
24
+ when :light_green then "\e[1;32m#{self}\e[0m"
25
+ when :brown then "\e[33m#{self}\e[0m"
26
+ when :yellow then "\e[1;33m#{self}\e[0m"
27
+ when :blue then "\e[34m#{self}\e[0m"
28
+ when :light_blue then "\e[1;34m#{self}\e[0m"
29
+ when :purple then "\e[35m#{self}\e[0m"
30
+ when :pink then "\e[1;35m#{self}\e[0m"
31
+ when :cyan then "\e[36m#{self}\e[0m"
32
+ when :light_cyan then "\e[1;36m#{self}\e[0m"
33
+ end
34
+ end
35
+
36
+ def background_color(color=:red)
37
+ case color
38
+ when :black
39
+ "\e[40m#{self}\e[0m"
40
+ when :red
41
+ "\e[41m#{self}\e[0m"
42
+ when :green
43
+ "\e[42m#{self}\e[0m"
44
+ when :yellow
45
+ "\e[43m#{self}\e[0m"
46
+ when :blue
47
+ "\e[44m#{self}\e[0m"
48
+ when :magenta
49
+ "\e[45m#{self}\e[0m"
50
+ when :cyan
51
+ "\e[46m#{self}\e[0m"
52
+ when :white
53
+ "\e[47m#{self}\e[0m"
54
+ end
55
+ end
56
+
57
+ def exact_size
58
+ self.each_char.map{|c| c.bytesize == 1 ? 1 : 2}.reduce(0, &:+)
59
+ end
60
+
61
+ def nl(i=1)
62
+ self + "\n"*i
63
+ end
64
+
65
+ def dnl
66
+ "\n#{self}\n"
67
+ end
68
+
69
+ def has_mb?
70
+ self.bytes do |b|
71
+ return true if (b & 0b10000000) != 0
72
+ end
73
+ false
74
+ end
75
+
76
+ alias_method :_ljust, :ljust
77
+ def ljust(length, padstr=nil, rstr="")
78
+ length -= (exact_size - 1) if has_mb?
79
+ length -= rstr.exact_size
80
+ text = padstr.nil? ? _ljust(length) : _ljust(length, padstr)
81
+ text << rstr
82
+ end
83
+
84
+ def to_em
85
+ self.tr('0-9a-zA-Z', '0-9a-zA-Z')
86
+ end
87
+
88
+ def to_en
89
+ self.tr('0-9a-zA-Z', '0-9a-zA-Z')
90
+ end
91
+
92
+ def to_reg
93
+ reg = Regexp.union(self, self.to_em)
94
+ /^#{Regexp.new(reg.source, Regexp::IGNORECASE)}[ | ]*(.*)/
95
+ end
96
+
97
+ def shift(n=2)
98
+ self.each_line.map do |line|
99
+ " "*n + line.chomp
100
+ end.join("\n")
101
+ end
102
+
103
+ def nshift(n=2)
104
+ self.each_line.map.with_index do |line, i|
105
+ if i.zero?
106
+ line.chomp
107
+ else
108
+ " "*n + line.chomp
109
+ end
110
+ end.join("\n")
111
+ end
112
+
113
+ def split_num(n)
114
+ text = []
115
+ tmp = ""
116
+ self.each_char.each do |char|
117
+ if tmp.exact_size < n
118
+ tmp << char
119
+ else
120
+ text << tmp
121
+ tmp = char
122
+ end
123
+ end
124
+ text << tmp
125
+ text.join("\n")
126
+ end
127
+
128
+ def range?(max)
129
+ unless 1 <= self.size && self.size <= max
130
+ raise RangeError
131
+ end
132
+ end
133
+
134
+ def size_of_lines
135
+ self.each_line.to_a.size
136
+ end
137
+
138
+ def max_char_of_lines
139
+ max = self.each_line.max_by{|str|str.size}
140
+ max.nil? ? 0 : max.size
141
+ end
142
+ end
143
+
144
+ if $0 == __FILE__
145
+ %i(white light_gray gray black red light_red green light_green brown yellow blue light_blue purple pink cyan light_cyan).each do |c|
146
+ puts "#{c}".color(c)
147
+ end
148
+ end