termtter 1.6.0 → 1.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (98) hide show
  1. data/.gitignore +5 -0
  2. data/README.rdoc +2 -2
  3. data/Rakefile +41 -75
  4. data/VERSION +1 -0
  5. data/bin/termtter +9 -2
  6. data/doc/Termtter-1.0-Release-Note-English.txt +37 -0
  7. data/doc/Termtter-1.0-Release-Note.txt +37 -0
  8. data/lib/plugins/another_prompt.rb +8 -8
  9. data/lib/plugins/ar.rb +102 -0
  10. data/lib/plugins/async.rb +1 -1
  11. data/lib/plugins/babelfish.rb +34 -0
  12. data/lib/plugins/confirm.rb +2 -0
  13. data/lib/plugins/crypt.rb +44 -0
  14. data/lib/plugins/defaults/auto_reload.rb +1 -1
  15. data/lib/plugins/defaults/command_line.rb +34 -17
  16. data/lib/plugins/defaults/confirm.rb +30 -0
  17. data/lib/plugins/defaults/hashtag.rb +1 -1
  18. data/lib/plugins/defaults/irb.rb +30 -0
  19. data/lib/plugins/defaults/keyword.rb +58 -0
  20. data/lib/plugins/defaults/list.rb +155 -0
  21. data/lib/plugins/defaults/plugin.rb +59 -0
  22. data/lib/plugins/defaults/retweet.rb +75 -23
  23. data/lib/plugins/defaults/standard_commands.rb +60 -87
  24. data/lib/plugins/defaults/standard_completion.rb +25 -15
  25. data/lib/plugins/defaults/stdout.rb +49 -10
  26. data/lib/plugins/defaults/switch.rb +1 -1
  27. data/lib/plugins/defaults/users.rb +63 -0
  28. data/lib/plugins/draft.rb +58 -0
  29. data/lib/plugins/expand-tinyurl.rb +5 -9
  30. data/lib/plugins/favotter.rb +1 -1
  31. data/lib/plugins/footer.rb +22 -0
  32. data/lib/plugins/friends.rb +5 -4
  33. data/lib/plugins/g.rb +9 -16
  34. data/lib/plugins/gem_install.rb +24 -0
  35. data/lib/plugins/gist.rb +20 -0
  36. data/lib/plugins/grass.rb +1 -1
  37. data/lib/plugins/gyazo.rb +78 -0
  38. data/lib/plugins/http_server.rb +1 -1
  39. data/lib/plugins/hugeurl.rb +6 -13
  40. data/lib/plugins/irc_gw.rb +15 -11
  41. data/lib/plugins/me.rb +1 -1
  42. data/lib/plugins/notify-send.rb +1 -1
  43. data/lib/plugins/notify-send3.rb +1 -1
  44. data/lib/plugins/open.rb +1 -1
  45. data/lib/plugins/open_url.rb +5 -1
  46. data/lib/plugins/pool.rb +1 -1
  47. data/lib/plugins/random.rb +1 -1
  48. data/lib/plugins/reply_retweet.rb +42 -0
  49. data/lib/plugins/screen-notify.rb +1 -1
  50. data/lib/plugins/sl.rb +3 -3
  51. data/lib/plugins/storage.rb +7 -10
  52. data/lib/plugins/storage/sqlite3.rb +155 -0
  53. data/lib/plugins/storage/status.rb +2 -0
  54. data/lib/plugins/stream.rb +1 -1
  55. data/lib/plugins/tinyurl.rb +3 -9
  56. data/lib/plugins/trends.rb +2 -2
  57. data/lib/plugins/truncate.rb +1 -1
  58. data/lib/plugins/w3mimg.rb +1 -1
  59. data/lib/termtter.rb +19 -20
  60. data/lib/termtter/active_rubytter.rb +4 -0
  61. data/lib/termtter/api.rb +22 -5
  62. data/lib/termtter/client.rb +55 -40
  63. data/lib/termtter/command.rb +3 -2
  64. data/lib/termtter/config_setup.rb +1 -1
  65. data/lib/termtter/config_template.erb +5 -0
  66. data/lib/termtter/default_config.rb +18 -0
  67. data/lib/termtter/hookable.rb +1 -0
  68. data/lib/termtter/httppool.rb +44 -0
  69. data/lib/termtter/memory_cache.rb +32 -0
  70. data/lib/termtter/optparse.rb +8 -15
  71. data/lib/termtter/rubytter_proxy.rb +65 -4
  72. data/lib/termtter/system_extensions.rb +40 -9
  73. data/lib/termtter/task.rb +2 -1
  74. data/spec/plugins/defaults/hashtag_spec.rb +8 -7
  75. data/spec/plugins/defaults/list_spec.rb +33 -0
  76. data/spec/plugins/defaults/plugin_spec.rb +17 -0
  77. data/spec/plugins/defaults/retweet_spec.rb +205 -0
  78. data/spec/plugins/draft_spec.rb +59 -0
  79. data/spec/plugins/expand-tinyurl_spec.rb +21 -0
  80. data/spec/plugins/footer_spec.rb +50 -0
  81. data/spec/plugins/storage/sqlite3_spec.rb +41 -0
  82. data/spec/termtter/api_spec.rb +1 -1
  83. data/spec/termtter/client_spec.rb +21 -21
  84. data/spec/termtter/command_spec.rb +8 -8
  85. data/spec/termtter/config_spec.rb +2 -2
  86. data/spec/termtter/memory_cache_spec.rb +20 -0
  87. data/spec/termtter/optparse_spec.rb +1 -1
  88. data/spec/termtter/rubytter_proxy_spec.rb +38 -0
  89. data/spec/termtter/system_extensions_spec.rb +25 -23
  90. data/spec/termtter/task_manager_spec.rb +1 -1
  91. data/spec/termtter_spec.rb +4 -2
  92. metadata +88 -19
  93. data/lib/plugins/defaults/lists.rb +0 -14
  94. data/lib/plugins/irb.rb +0 -6
  95. data/lib/plugins/storage/DB.rb +0 -37
  96. data/lib/termtter/version.rb +0 -4
  97. data/spec/plugins/defaults/lists_spec.rb +0 -34
  98. data/spec/plugins/storage/DB_spec_.rb +0 -12
@@ -1,5 +1,7 @@
1
1
  # -*- coding: utf-8 -*-
2
2
 
3
+ warn '"confirm" plugin is deprecated. You can use "config.confirm" option instead it.'
4
+
3
5
  config.plugins.confirm.set_default(:commands, [:update, :reply, :direct, :retweet])
4
6
  config.plugins.confirm.set_default(
5
7
  :conditions,
@@ -0,0 +1,44 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ require 'openssl'
4
+
5
+ config.plugins.crypt.set_default(:salt, 'No Termtter, No Life.')
6
+
7
+ def encrypt(msg, salt)
8
+ encoder = OpenSSL::Cipher::DES.new
9
+ encoder.encrypt
10
+ encoder.pkcs5_keyivgen(salt)
11
+ (encoder.update(msg) + encoder.final).unpack('H*').to_s
12
+ end
13
+
14
+ def decrypt(msg, salt)
15
+ decoder = OpenSSL::Cipher::DES.new
16
+ decoder.decrypt
17
+ decoder.pkcs5_keyivgen(salt)
18
+ decoder.update([msg].pack('H*')) + decoder.final
19
+ end
20
+
21
+ Termtter::Client.register_command(
22
+ :name => :crypt,
23
+ :help => ['crypt TEXT', 'Post a encrypted message'],
24
+ :exec_proc => lambda {|arg|
25
+ msg = arg
26
+ prefix = '{DES}'
27
+ status = prefix + encrypt(msg, config.plugins.crypt.salt)
28
+ Termtter::API.twitter.update(status)
29
+ puts "=> #{status}"
30
+ status
31
+ }
32
+ )
33
+
34
+ Termtter::Client::register_hook(
35
+ :name => :decrypt_message,
36
+ :point => :filter_for_output,
37
+ :exec_proc => lambda do |statuses, event|
38
+ statuses.each do |s|
39
+ s.text.gsub!(/^\{DES\}(.*)/) do |m|
40
+ "{decrypt}" + decrypt($1, config.plugins.crypt.salt)
41
+ end
42
+ end
43
+ end
44
+ )
@@ -1,7 +1,7 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  auto_reload_proc = lambda do
3
3
  begin
4
- Termtter::Client.call_commands('reload -r')
4
+ Termtter::Client.execute('reload -r')
5
5
  rescue TimeoutError
6
6
  # do nothing
7
7
  rescue Exception => e
@@ -31,7 +31,7 @@ module Termtter
31
31
  Client.get_hooks('prepare_command').each {|hook|
32
32
  command_text = hook.call(command_text)
33
33
  }
34
- Client.call_commands(command_text)
34
+ Client.execute(command_text)
35
35
  rescue TimeoutError
36
36
  puts TermColor.parse("<red>Time out :(</red>")
37
37
  end
@@ -62,30 +62,47 @@ module Termtter
62
62
  @input_thread.join
63
63
  end
64
64
 
65
+ def do_completion(input)
66
+ input = input.sub(/^\s*/, '')
67
+
68
+ words = []
69
+
70
+ words = Client.commands.values.
71
+ inject([]) {|array, command| array + [command.name] + command.aliases}.
72
+ map(&:to_s).
73
+ grep(/^\s*#{Regexp.quote(input)}/)
74
+
75
+ if words.empty?
76
+ command = Client.find_command(input)
77
+ words = command ? command.complement(input) : []
78
+ end
79
+
80
+ if words.empty?
81
+ Client.get_hooks(:completion).each do |hook|
82
+ words << hook.call(input) rescue nil
83
+ end
84
+ end
85
+
86
+ words.flatten.compact
87
+ rescue Exception => e
88
+ Client.handle_error(e)
89
+ end
90
+
65
91
  def setup_readline
66
92
  if Readline.respond_to?(:basic_word_break_characters=)
67
93
  Readline.basic_word_break_characters= "\t\n\"\\'`><=;|&{("
68
94
  end
95
+
69
96
  Readline.completion_case_fold = true
70
- Readline.completion_proc = lambda {|input|
71
- begin
72
- words = Client.commands.map {|name, command| command.complement(input) }.flatten.compact
73
-
74
- if words.empty?
75
- Client.get_hooks(:completion).each do |hook|
76
- words << hook.call(input) rescue nil
77
- end
78
- end
79
97
 
80
- words.flatten.compact
81
- rescue Exception => e
82
- Client.handle_error(e)
83
- end
84
- }
98
+ Readline.completion_proc = lambda {|input| do_completion(input) }
99
+
85
100
  vi_or_emacs = config.editing_mode
86
101
  unless vi_or_emacs.empty?
87
102
  Readline.__send__("#{vi_or_emacs}_editing_mode")
88
103
  end
104
+
105
+ Readline.rl_parse_and_bind('TAB: menu-complete')
89
106
  end
90
107
 
91
108
  def trap_setting()
@@ -95,7 +112,7 @@ module Termtter
95
112
  begin
96
113
  system "stty", stty_save
97
114
  ensure
98
- Client.call_commands('exit')
115
+ Client.execute('exit')
99
116
  end
100
117
  end
101
118
  trap("CONT") do
@@ -107,7 +124,7 @@ module Termtter
107
124
  end
108
125
  end
109
126
 
110
- Client.register_hook(:initialize_command_line, :point => :launched) do
127
+ Client.register_hook(:initialize_command_line, :point => :init_command_line) do
111
128
  CommandLine.start
112
129
  end
113
130
 
@@ -0,0 +1,30 @@
1
+ # -*- coding: utf-8 -*-
2
+ config.set_default(:confirm, false)
3
+
4
+ def confirm(message)
5
+ if config.confirm && !Termtter::Client.confirm(message)
6
+ raise Termtter::CommandCanceled
7
+ end
8
+ end
9
+
10
+ Termtter::RubytterProxy.register_hook(:confirm_update, :point => :pre_update) do |*args|
11
+ confirm("update #{args.first}")
12
+ args
13
+ end
14
+
15
+ Termtter::RubytterProxy.register_hook(:confirm_direct_message, :point => :pre_direct_message) do |*args|
16
+ confirm("direct #{args[0]} #{args[1]}")
17
+ args
18
+ end
19
+
20
+ Termtter::RubytterProxy.register_hook(:confirm_retweet, :point => :pre_retweet) do |*args|
21
+ status = Termtter::API.twitter.show(args.first)
22
+ confirm("retweet #{status.text}")
23
+ args
24
+ end
25
+
26
+ Termtter::RubytterProxy.register_hook(:confirm_delete, :point => :pre_remove_status) do |*args|
27
+ status = Termtter::API.twitter.show(args.first)
28
+ confirm("delete #{status.text}")
29
+ args
30
+ end
@@ -12,7 +12,7 @@ module Termtter::Client
12
12
  register_command(:raw_update) do |args|
13
13
  temp = public_storage[:hashtags]
14
14
  public_storage[:hashtags].clear
15
- call_commands "update #{args}"
15
+ execute "update #{args}"
16
16
  public_storage[:hashtags] = temp
17
17
  end
18
18
 
@@ -0,0 +1,30 @@
1
+ require 'irb'
2
+
3
+ module IRB
4
+ def self.start_session(binding)
5
+ unless @__irb_initialized
6
+ IRB.setup(nil)
7
+ @__irb_initialized = true
8
+ end
9
+
10
+ workspace = WorkSpace.new(binding)
11
+
12
+ irb = Irb.new(workspace)
13
+
14
+ @CONF[:IRB_RC].call(irb.context) if @CONF[:IRB_RC]
15
+ @CONF[:MAIN_CONTEXT] = irb.context
16
+
17
+ catch(:IRB_EXIT) do
18
+ irb.eval_input
19
+ end
20
+ end
21
+ end
22
+
23
+ Termtter::Client.register_command(:irb) do |arg|
24
+ begin
25
+ completion_proc = Readline.completion_proc
26
+ IRB.start_session(binding)
27
+ ensure
28
+ Readline.completion_proc = completion_proc
29
+ end
30
+ end
@@ -0,0 +1,58 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'set'
3
+
4
+ config.plugins.keyword.set_default(
5
+ :highlight_patterns,
6
+ [
7
+ ['on_cyan', 'white'],
8
+ ['on_green', 'white'],
9
+ ['on_magenta', 'white'],
10
+ ['on_red', 'white'],
11
+ ['on_blue', 'white'],
12
+ ['on_black', 'white'],
13
+ ['on_white', 'white'],
14
+ ['on_yellow', 'white'],
15
+ ]
16
+ )
17
+
18
+ config.plugins.keyword.set_default(:keywords, [])
19
+
20
+ module Termtter::Client
21
+ public_storage[:keywords] ||= Set.new(config.plugins.keyword.keywords)
22
+
23
+ register_hook :highlight_keywords, :point => :pre_coloring do |text, event|
24
+ public_storage[:keywords].each_with_index do |keyword, index|
25
+ highlight_pattern = config.plugins.keyword.highlight_patterns[index % config.plugins.keyword.highlight_patterns.size]
26
+ text = text.gsub(
27
+ /(#{Regexp.quote(keyword)})/i,
28
+ "<#{highlight_pattern[0]}><#{highlight_pattern[1]}>" +
29
+ "\\1" +
30
+ "</#{highlight_pattern[1]}></#{highlight_pattern[0]}>"
31
+ )
32
+ end
33
+ text
34
+ end
35
+
36
+ register_command(
37
+ 'keyword add',
38
+ :help => ['keyword add KEYWORD', 'Add a highlight keyword']
39
+ ) do |args|
40
+ args.split(/\s+/).each do |arg|
41
+ public_storage[:keywords] << arg
42
+ end
43
+ end
44
+
45
+ register_command(
46
+ 'keyword clear',
47
+ :help => ['keyword clear', 'Clear highlight keywords']
48
+ ) do |args|
49
+ public_storage[:keywords].clear
50
+ end
51
+
52
+ register_command(
53
+ 'keyword list',
54
+ :help => ['keyword list', 'List highlight keywords']
55
+ ) do |args|
56
+ p public_storage[:keywords].to_a
57
+ end
58
+ end
@@ -0,0 +1,155 @@
1
+ module Termtter::Client
2
+ public_storage[:lists] = []
3
+
4
+ register_hook(:fetch_my_lists, :point => :launched) do
5
+ begin
6
+ public_storage[:lists] +=
7
+ Termtter::API.twitter.lists(config.user_name).lists.map(&:full_name)
8
+ rescue TimeoutError
9
+ # do nothing
10
+ rescue Exception => e
11
+ Termtter::Client.handle_error(e)
12
+ end
13
+ end
14
+
15
+ register_command(
16
+ :name => :list, :aliases => [:l],
17
+ :exec_proc => lambda {|arg|
18
+ if arg =~ /\-([\d]+)/
19
+ options = {:count => $1}
20
+ arg = arg.gsub(/\-([\d]+)/, '')
21
+ else
22
+ options = {}
23
+ end
24
+
25
+ last_error = nil
26
+ if arg.empty?
27
+ event = :list_friends_timeline
28
+ statuses = Termtter::API.twitter.home_timeline(options)
29
+ else
30
+ event = :list_user_timeline
31
+ statuses = []
32
+ Array(arg.split).each do |user|
33
+ if user =~ /\/\w+/
34
+ user_name, slug = *user.split('/')
35
+ user_name = config.user_name if user_name.empty?
36
+ user_name = normalize_as_user_name(user_name)
37
+ options[:per_page] = options[:count]
38
+ options.delete(:count)
39
+ statuses += Termtter::API.twitter.list_statuses(user_name, slug, options)
40
+ else
41
+ begin
42
+ if user =~ /^\d+$/
43
+ profile = Termtter::API.twitter.user(nil, :screen_name => user) rescue nil
44
+ unless profile
45
+ status = Termtter::API.twitter.show(user) rescue nil
46
+ user = status.user.screen_name if status
47
+ end
48
+ end
49
+ user_name = normalize_as_user_name(user.sub(/\/$/, ''))
50
+ statuses += Termtter::API.twitter.user_timeline(user_name, options)
51
+ rescue Rubytter::APIError => e
52
+ last_error = e
53
+ end
54
+ end
55
+ end
56
+ end
57
+ output(statuses, event)
58
+ raise last_error if last_error
59
+ },
60
+ :help => ["list,l [USERNAME]/[SLUG] [-COUNT]", "List the posts"]
61
+ )
62
+
63
+ register_command(
64
+ :name => 'list list',
65
+ :exec => lambda {|arg|
66
+ unless arg.empty?
67
+ user_name = normalize_as_user_name(arg)
68
+ else
69
+ user_name = config.user_name
70
+ end
71
+ # TODO: show more information of lists
72
+ lists = Termtter::API.twitter.lists(user_name).lists
73
+ public_storage[:lists] += lists.map(&:full_name)
74
+ puts lists.map{|i| i.full_name}.join("\n")
75
+ },
76
+ :help => ["lists [USERNAME]", "Show Lists"]
77
+ )
78
+
79
+ register_command(
80
+ :name => %s{list follow},
81
+ :alias => %s{list add},
82
+ :exec => lambda { |arg|
83
+ list_name, *users = arg.split(' ')
84
+ slug = list_name_to_slug(list_name)
85
+ users.each{ |screen_name|
86
+ begin
87
+ user = Termtter::API.twitter.cached_user(screen_name) || Termtter::API.twitter.user(screen_name)
88
+ Termtter::API.twitter.add_member_to_list(slug, user.id)
89
+ puts "#{slug} + #{screen_name}"
90
+ rescue => e
91
+ handle_error(e)
92
+ end
93
+ }
94
+ },
95
+ :help => ["list follow SLUG USERNAME", "Follow users to the list"]
96
+ )
97
+
98
+ register_command(
99
+ :name => %s{list remove},
100
+ :exec => lambda { |arg|
101
+ list_name, *users = arg.split(' ')
102
+ slug = list_name_to_slug(list_name)
103
+ users.each{ |screen_name|
104
+ begin
105
+ user = Termtter::API.twitter.cached_user(screen_name) || Termtter::API.twitter.user(screen_name)
106
+ Termtter::API.twitter.remove_member_from_list(slug, user.id)
107
+ puts "#{slug} - #{screen_name}"
108
+ rescue => e
109
+ handle_error(e)
110
+ end
111
+ }
112
+ },
113
+ :help => ["list remove SLUG USERNAME", "Remove user(s) from the list"]
114
+ )
115
+
116
+ register_command(
117
+ :name => %s{list create},
118
+ :exec => lambda { |arg|
119
+ slug, *options = arg.split(' ')
120
+ param = { }
121
+
122
+ OptionParser.new {|opt|
123
+ opt.on('--description VALUE') {|v| param[:description] = v }
124
+ opt.on('--private') {|v| param[:mode] = 'private' }
125
+ opt.parse(options)
126
+ }
127
+ list = Termtter::API.twitter.create_list(slug, param)
128
+ public_storage[:lists] << list.full_name
129
+ p [list.full_name, param]
130
+ },
131
+ :help => ["list create SLUG [--description VALUE] [--private]", "Create list"]
132
+ )
133
+
134
+ register_command(
135
+ :name => %s{list delete},
136
+ :exec => lambda { |arg|
137
+ return unless confirm("Are you sure?")
138
+ arg.split(' ').each{ |list_name|
139
+ begin
140
+ slug = list_name_to_slug(list_name)
141
+ list = Termtter::API.twitter.delete_list(slug)
142
+ public_storage[:lists].delete(list.full_name)
143
+ puts "#{list.full_name} deleted"
144
+ rescue => e
145
+ handle_error(e)
146
+ end
147
+ }
148
+ },
149
+ :help => ["list delete SLUG", "Delete list"]
150
+ )
151
+
152
+ def self.list_name_to_slug(list_name)
153
+ list_name[/([^\/]*)$/]
154
+ end
155
+ end
@@ -0,0 +1,59 @@
1
+ require 'fileutils'
2
+
3
+ module Termtter::Client
4
+ register_command('plugin edit') do |arg|
5
+ if /\-s/ =~ arg
6
+ arg = arg.sub('-s', '').strip
7
+ include_system_plugins = true
8
+ end
9
+
10
+ if path = search_plugin_file(arg, include_system_plugins)
11
+ open_editor(path)
12
+ plug(arg)
13
+ puts "reload => #{arg}"
14
+ else
15
+ puts TermColor.parse('<red>Plugin not found :(</red>')
16
+ end
17
+ end
18
+
19
+ register_command('plugin show') do |arg|
20
+ if path = search_plugin_file(arg, true)
21
+ puts IO.read(path)
22
+ else
23
+ puts TermColor.parse('<red>Plugin not found :(</red>')
24
+ end
25
+ end
26
+
27
+ register_command('plugin create') do |arg|
28
+ path = "#{Termtter::CONF_DIR}/plugins/#{arg}.rb"
29
+ unless File.exists?(path)
30
+ FileUtils.mkdir_p(File.dirname(path))
31
+ # TODO: insert plugin template to plugin file
32
+ open_editor(path)
33
+ plug(arg)
34
+ puts "load => #{arg}"
35
+ else
36
+ puts TermColor.parse('<red>The plugin already exists :(</red>')
37
+ end
38
+ end
39
+
40
+ def self.open_editor(path)
41
+ # TODO: change to common method or use launchy
42
+ system ENV['EDITOR'] || 'vim', path
43
+ end
44
+
45
+ def self.search_plugin_file(name, include_system_plugins = false)
46
+ regex = /#{Regexp.quote(name + '.rb')}$/
47
+ plugin_files(include_system_plugins).detect {|f| regex =~ f}
48
+ end
49
+
50
+ def self.plugin_files(include_system_plugins = false)
51
+ files = Dir["#{Termtter::CONF_DIR}/plugins/*.rb"]
52
+ files += Dir["#{File.expand_path(File.dirname(__FILE__))}/*.rb"] if include_system_plugins
53
+ files
54
+ end
55
+
56
+ # TODO: rename command 'plug' to 'plugin load' and define 'plug' as alias
57
+ # TODO: completion for plugin names
58
+ end
59
+