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