termtter 1.7.2 → 1.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (81) hide show
  1. data/.gitignore +1 -1
  2. data/README.rdoc +1 -0
  3. data/Rakefile +2 -1
  4. data/VERSION +1 -1
  5. data/bin/termtter +1 -0
  6. data/bin/termtter_frame +134 -0
  7. data/lib/plugins/aa.rb +44 -0
  8. data/lib/plugins/another_prompt.rb +42 -41
  9. data/lib/plugins/appendtitle.rb +82 -0
  10. data/lib/plugins/ar.rb +11 -8
  11. data/lib/plugins/async.rb +3 -2
  12. data/lib/plugins/capital_update.rb +12 -0
  13. data/lib/plugins/channel.rb +149 -0
  14. data/lib/plugins/clock.rb +10 -0
  15. data/lib/plugins/defaults/command_line.rb +8 -0
  16. data/lib/plugins/defaults/confirm.rb +1 -1
  17. data/lib/plugins/defaults/hashtag.rb +1 -1
  18. data/lib/plugins/defaults/keyword.rb +11 -0
  19. data/lib/plugins/defaults/list.rb +32 -6
  20. data/lib/plugins/defaults/standard_commands.rb +135 -52
  21. data/lib/plugins/defaults/standard_completion.rb +14 -0
  22. data/lib/plugins/defaults/stdout.rb +59 -27
  23. data/lib/plugins/defaults/user.rb +32 -0
  24. data/lib/plugins/draft.rb +9 -12
  25. data/lib/plugins/easy_post.rb +5 -0
  26. data/lib/plugins/event_invoked_at.rb +23 -0
  27. data/lib/plugins/expand-tinyurl.rb +13 -20
  28. data/lib/plugins/favotter.rb +9 -9
  29. data/lib/plugins/footer.rb +9 -12
  30. data/lib/plugins/friends.rb +0 -26
  31. data/lib/plugins/grass.rb +2 -4
  32. data/lib/plugins/growl.rb +47 -0
  33. data/lib/plugins/gyazo.rb +16 -18
  34. data/lib/plugins/hi.rb +31 -10
  35. data/lib/plugins/http_server.rb +3 -2
  36. data/lib/plugins/irc_gw.rb +71 -17
  37. data/lib/plugins/line.rb +10 -0
  38. data/lib/plugins/linefeed.rb +6 -1
  39. data/lib/plugins/list_switch.rb +76 -0
  40. data/lib/plugins/nop.rb +9 -0
  41. data/lib/plugins/notify-send.rb +1 -1
  42. data/lib/plugins/notify-send2.rb +25 -16
  43. data/lib/plugins/notify-send3.rb +16 -13
  44. data/lib/plugins/nuance.rb +29 -0
  45. data/lib/plugins/open_url.rb +1 -5
  46. data/lib/plugins/random.rb +2 -6
  47. data/lib/plugins/reply_sound.rb +33 -0
  48. data/lib/plugins/say.rb +2 -2
  49. data/lib/plugins/storage/sqlite3.rb +1 -1
  50. data/lib/plugins/story.rb +44 -0
  51. data/lib/plugins/tinyurl.rb +50 -29
  52. data/lib/plugins/translate_tweet.rb +38 -0
  53. data/lib/plugins/web.rb +27 -0
  54. data/lib/termtter.rb +8 -4
  55. data/lib/termtter/client.rb +17 -21
  56. data/lib/termtter/command.rb +35 -13
  57. data/lib/termtter/config.rb +13 -0
  58. data/lib/termtter/config_template.erb +3 -2
  59. data/lib/termtter/default_config.rb +2 -2
  60. data/lib/termtter/event.rb +69 -0
  61. data/lib/termtter/hook.rb +6 -1
  62. data/lib/termtter/hookable.rb +9 -1
  63. data/lib/termtter/httppool.rb +17 -9
  64. data/lib/termtter/optparse.rb +11 -1
  65. data/lib/termtter/rubytter_proxy.rb +21 -5
  66. data/spec/plugins/capital_update_spec.rb +9 -0
  67. data/spec/plugins/fib_spec.rb +2 -4
  68. data/spec/plugins/hi_spec.rb +9 -0
  69. data/spec/plugins/tinyurl_spec.rb +78 -0
  70. data/spec/termtter/client_spec.rb +5 -12
  71. data/spec/termtter/command_spec.rb +22 -10
  72. data/spec/termtter/config_spec.rb +23 -0
  73. data/spec/termtter/event_spec.rb +129 -0
  74. data/spec/termtter/optparse_spec.rb +2 -2
  75. data/spec/termtter/rubytter_proxy_spec.rb +1 -1
  76. metadata +39 -8
  77. data/bin/kill_termtter +0 -22
  78. data/lib/plugins/defaults/users.rb +0 -63
  79. data/lib/plugins/pause.rb +0 -3
  80. data/test/friends_timeline.json +0 -5
  81. data/test/search.json +0 -8
@@ -0,0 +1,27 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ module Termtter::Client
4
+ unless Termtter::Client.respond_to?(:open_uri)
5
+ plug 'uri-open' # FIXME: Actually it only needs Termtter::Client.open_uri method
6
+ end
7
+
8
+ helpmsg = 'USAGE: web [{username}|{id}]'
9
+ register_command(
10
+ :name => :web,
11
+ :help => ['web', helpmsg],
12
+ :exec_proc => lambda {|arg|
13
+ case arg
14
+ when ''
15
+ puts helpmsg
16
+ when /^\d+$/
17
+ statusid = arg #Termtter::API.twitter.show($1)
18
+ name = 'ujm' # FIXME: Do you know how to obtain the screen name of the status? I researched but I couldn't find how to do that yet.
19
+ puts "not impemented yet"
20
+ #Termtter::Client.open_uri "http://twitter.com/#{name}/#{statusid}"
21
+ else
22
+ name = normalize_as_user_name(arg)
23
+ Termtter::Client.open_uri "http://twitter.com/#{name}"
24
+ end
25
+ }
26
+ )
27
+ end
data/lib/termtter.rb CHANGED
@@ -14,16 +14,14 @@ require 'net/https'
14
14
  require 'open-uri'
15
15
  require 'optparse'
16
16
  require 'readline'
17
- gem 'rubytter', '>= 0.9.2'
17
+ gem 'rubytter', '>= 0.11.0'
18
18
  require 'rubytter'
19
+ require 'notify'
19
20
  require 'timeout'
20
21
 
21
22
  module Termtter
22
23
  VERSION = File.read(File.join(File.dirname(__FILE__), '../VERSION')).strip
23
24
  APP_NAME = 'termtter'
24
- CONF_DIR = File.expand_path('~/.termtter')
25
- CONF_FILE = File.join(Termtter::CONF_DIR, 'config')
26
- $:.unshift(CONF_DIR)
27
25
 
28
26
  require 'termtter/config'
29
27
  require 'termtter/default_config'
@@ -39,4 +37,10 @@ module Termtter
39
37
  require 'termtter/api'
40
38
  require 'termtter/system_extensions'
41
39
  require 'termtter/httppool'
40
+ require 'termtter/event'
41
+
42
+ OptParser.parse!(ARGV)
43
+ CONF_DIR = File.expand_path('~/.termtter') unless defined? CONF_DIR
44
+ CONF_FILE = File.join(Termtter::CONF_DIR, 'config') unless defined? CONF_FILE
45
+ $:.unshift(CONF_DIR)
42
46
  end
@@ -8,7 +8,6 @@ module Termtter
8
8
  class CommandCanceled < StandardError; end
9
9
 
10
10
  module Client
11
-
12
11
  include Termtter::Hookable
13
12
 
14
13
  @commands = {}
@@ -18,7 +17,6 @@ module Termtter
18
17
  Thread.abort_on_exception = true
19
18
 
20
19
  class << self
21
-
22
20
  attr_reader :commands
23
21
 
24
22
  # plug :: Name -> (Hash) -> IO () where NAME = String | Symbol | [NAME]
@@ -111,6 +109,7 @@ module Termtter
111
109
  # }
112
110
  def output(statuses, event)
113
111
  return if statuses.nil? || statuses.empty?
112
+ event = Termtter::Event.new(event) unless event.kind_of? Termtter::Event
114
113
 
115
114
  statuses = statuses.sort_by(&:id)
116
115
  call_hooks(:pre_filter, statuses, event)
@@ -118,16 +117,22 @@ module Termtter
118
117
  filtered = apply_filters_for_hook(:filter_for_output, statuses.map(&:clone), event)
119
118
 
120
119
  @filters.each do |f| # TODO: code for compatibility. delete someday.
120
+ # but... when is the "someday"?
121
121
  filtered = f.call(filtered, event)
122
122
  end
123
123
 
124
124
  call_hooks(:post_filter, filtered, event)
125
125
  get_hooks(:output).each do |hook|
126
+ Termtter::Client.logger.debug "output: call hook :output #{hook.inspect}"
126
127
  hook.call(
127
128
  apply_filters_for_hook(:"filter_for_#{hook.name}", filtered, event),
128
- event
129
- )
129
+ event)
130
130
  end
131
+ Termtter::Client.logger.debug "output: call hook :output, done"
132
+ end
133
+
134
+ def notify(*args)
135
+ ::Notify.notify(*args)
131
136
  end
132
137
 
133
138
  def apply_filters_for_hook(hook_name, statuses, event)
@@ -225,6 +230,7 @@ module Termtter
225
230
  end
226
231
  end
227
232
 
233
+ # MEMO: This method will be removed in Termtter 2.0.0
228
234
  def move_legacy_config_file
229
235
  FileUtils.mv(
230
236
  Termtter::CONF_DIR,
@@ -295,11 +301,13 @@ module Termtter
295
301
 
296
302
  def run
297
303
  parse_options
304
+ config.__freeze__(:user_name) unless config.user_name.empty?
298
305
  show_splash
299
306
  load_config
300
307
  setup_task_manager
301
308
  load_plugins
302
309
  eval_init_block
310
+ config.__unfreeze__(:user_name)
303
311
  Termtter::API.setup
304
312
 
305
313
  config.system.eval_scripts.each do |script|
@@ -323,30 +331,18 @@ module Termtter
323
331
  end
324
332
 
325
333
  def handle_error(e)
326
- if logger
327
- logger.error("#{e.class.to_s}: #{e.message}")
328
- logger.error(e.backtrace.join("\n")) if (e.backtrace and config.devel)
329
- else
330
- raise e
331
- end
334
+ logger.error("#{e.class.to_s}: #{e.message}")
335
+ logger.error(e.backtrace.join("\n")) if (e.backtrace and config.devel)
332
336
  get_hooks(:on_error).each {|hook| hook.call(e) }
333
- rescue Exception => e
334
- $stderr.puts "Error: #{e}"
335
- $stderr.puts e.backtrace.join("\n")
336
337
  end
337
338
 
338
339
  def confirm(message, default_yes = true, &block)
339
340
  pause # TODO: TaskManager から呼ばれるならこれいらないなぁ
340
341
 
341
- prompt =
342
- if default_yes
343
- "\"#{message}".strip + "\" [Y/n] "
344
- else
345
- "\"#{message}".strip + "\" [N/y] "
346
- end
347
- readline = Readline.readline(prompt, false)
342
+ print "\"#{message.strip}\" "
343
+ readline = Readline.readline(default_yes ? "[Y/n] " : "[N/y] ", false)
348
344
  result =
349
- if !!(/^$/ =~ readline)
345
+ if !!(/^$/ =~ readline)
350
346
  default_yes
351
347
  else
352
348
  !!(/^y/i =~ readline)
@@ -2,14 +2,15 @@
2
2
 
3
3
  module Termtter
4
4
  class Command
5
- attr_accessor :name, :aliases, :exec_proc, :completion_proc, :help
5
+ attr_accessor :name, :aliases, :author, :exec_proc, :completion_proc, :help
6
6
 
7
7
  # args
8
8
  # name: (required) Symbol as command name
9
9
  # aliases: Array of command alias (ex. ['u', 'up'])
10
10
  # exec_proc: Proc for procedure of the command. If need the proc must return object for hook.
11
11
  # completion_proc: Proc for input completion. The proc must return Array of candidates (Optional)
12
- # help: help text for the command (Optional)
12
+ # help: Help text for the command (Optional)
13
+ # author: The author's name (Optional)
13
14
  def initialize(args)
14
15
  raise ArgumentError, ":name is not given." unless args.has_key?(:name)
15
16
  args = args.dup
@@ -18,10 +19,11 @@ module Termtter
18
19
  args[:aliases] ||= [args[:alias]].compact
19
20
 
20
21
  cfg = {
21
- :aliases => [],
22
- :exec_proc => lambda {|arg| },
23
- :comletion_proc => lambda {|command, arg| [] }
24
- }.merge(args)
22
+ :aliases => [],
23
+ :exec_proc => lambda {|arg| },
24
+ :comletion_proc => lambda {|command, arg| [] },
25
+ :author => 'ujihisa',
26
+ }.merge(args) {|k, v1, v2| v2 ? v2 : v1 }
25
27
 
26
28
  set cfg
27
29
  end
@@ -33,6 +35,7 @@ module Termtter
33
35
  self.exec_proc = cfg[:exec_proc]
34
36
  self.completion_proc = cfg[:completion_proc]
35
37
  self.help = cfg[:help]
38
+ self.author = cfg[:author]
36
39
  end
37
40
 
38
41
  # complement :: String -> [String]
@@ -52,6 +55,7 @@ module Termtter
52
55
 
53
56
  # call :: ???
54
57
  def call(cmd = nil, arg = nil, original_text = nil)
58
+ from = Time.now
55
59
  arg = case arg
56
60
  when nil
57
61
  ''
@@ -60,18 +64,24 @@ module Termtter
60
64
  else
61
65
  raise ArgumentError, 'arg should be String or nil'
62
66
  end
63
- exec_proc.call(arg)
67
+ Termtter::Client.logger.debug "command: #{cmd} #{arg}"
68
+ result = exec_proc.call(arg)
69
+ Termtter::Client.logger.debug "command: #{cmd} #{arg} #{'%.2fsec' % (Time.now - from)}"
70
+ result
71
+ rescue => e
72
+ Termtter::Client.logger.debug "command: #{cmd} #{arg} #{e.message} #{'%.2fsec' % (Time.now - from)}"
73
+ raise e
64
74
  end
65
75
 
66
76
  # match? :: String -> Boolean
67
77
  def match?(input)
68
- pattern === input
78
+ !!pattern.match(input)
69
79
  end
70
80
 
71
81
  # pattern :: Regexp
72
82
  def pattern
73
- commands_regex = commands.map {|name| Regexp.quote(name.to_s) }.join('|')
74
- /^((#{commands_regex})|(#{commands_regex})\s+(.*?))\s*$/
83
+ commands_regex = commands.map {|name| name.to_s.split(' ').map {|i| Regexp.quote(i)}.join('\s+') }.join('|')
84
+ /^\s*((#{commands_regex})|(#{commands_regex})\s+(.*?))\s*$/
75
85
  end
76
86
 
77
87
  # commands :: [Symbol]
@@ -88,15 +98,27 @@ module Termtter
88
98
  self.aliases = [a]
89
99
  end
90
100
 
101
+ # author= :: String -> ()
102
+ def author=(a)
103
+ @author = a
104
+ end
105
+
91
106
  def command_words
92
107
  name.to_s.split(/\s+/)
93
108
  end
94
109
 
95
110
  # split_command_line :: String -> (String, String)
96
111
  def split_command_line(line)
97
- command_words_count = command_words.size
98
- parts = line.strip.split(/\s+/, command_words_count + 1)
99
- [parts[0...command_words_count].join(' '), parts[command_words_count] || '']
112
+ m = pattern.match(line)
113
+ if m
114
+ unless m[2].nil?
115
+ [m[2], '']
116
+ else
117
+ [m[3], m[4]]
118
+ end
119
+ else
120
+ []
121
+ end
100
122
  end
101
123
  end
102
124
  end
@@ -1,9 +1,12 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  #
3
+ require 'set'
4
+
3
5
  module Termtter
4
6
  class Config
5
7
  def initialize
6
8
  @store = Hash.new(:undefined)
9
+ @freezes = Set.new
7
10
  end
8
11
 
9
12
  def inspect
@@ -40,8 +43,17 @@ module Termtter
40
43
  end
41
44
  end
42
45
 
46
+ def __freeze__(name)
47
+ @freezes << name.to_sym
48
+ end
49
+
50
+ def __unfreeze__(name)
51
+ @freezes.delete(name.to_sym)
52
+ end
53
+
43
54
  # __assign__ :: Symbol -> a -> IO ()
44
55
  def __assign__(name, value)
56
+ return if @freezes.include?(name)
45
57
  @store[name] = value
46
58
  end
47
59
 
@@ -55,6 +67,7 @@ module Termtter
55
67
  end
56
68
 
57
69
  def __clear__(name = nil)
70
+ return if name && @freezes.include?(name)
58
71
  if name
59
72
  @store[name] = :undefined
60
73
  else
@@ -2,7 +2,7 @@
2
2
 
3
3
  config.user_name = '<%= user_name %>'
4
4
  #config.update_interval = 120
5
- #config.timeout = 5
5
+ #config.timeout = 60
6
6
  #config.retry = 3
7
7
  #config.enable_ssl = true
8
8
  #config.proxy.host = 'proxy host'
@@ -10,7 +10,8 @@ config.user_name = '<%= user_name %>'
10
10
  #config.proxy.user_name = 'proxy user'
11
11
  #config.proxy.password = 'proxy password'
12
12
 
13
- #config.plugins.keyword.keywords = ["ruby", "termtter"]
13
+ config.plugins.keyword.keywords = ["@<%= user_name %>"]
14
+ config.confirm = true
14
15
  #config.plugins.stdout.colors = (31..36).to_a + (91..96).to_a
15
16
 
16
17
  Termtter::Client.init do |t|
@@ -2,11 +2,11 @@ config.set_default(:logger, nil)
2
2
  config.set_default(:update_interval, 120)
3
3
  config.set_default(:prompt, '> ')
4
4
  config.set_default(:devel, false)
5
- config.set_default(:timeout, 5)
5
+ config.set_default(:timeout, 60)
6
6
  config.set_default(:retry, 1)
7
7
  config.set_default(:splash, <<SPLASH)
8
8
 
9
- <cyan>&lt;(@)//_</cyan> . . <on_green> Termtter <underline>#{Termtter::VERSION}</underline> </on_green>
9
+ <cyan>&lt;(@)//_</cyan> . . <on_green> #{(Time.now.year == 2010 && Time.now.month == 4 && Time.now.day == 1)?'Centertter':'Termtter'} <underline>#{Termtter::VERSION}</underline> </on_green>
10
10
  <cyan>\\\\</cyan> <on_green> http://termtter.org/ </on_green>
11
11
 
12
12
  SPLASH
@@ -0,0 +1,69 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ require 'termtter/active_rubytter'
4
+
5
+ module Termtter
6
+ class Event
7
+ def initialize(name, params = {})
8
+ raise TypeError unless name.kind_of? Symbol
9
+ raise TypeError unless params.kind_of? Hash
10
+ @name = name
11
+ @params = ActiveRubytter.new(params)
12
+ end
13
+
14
+ attr_reader :name
15
+ undef_method :to_s
16
+
17
+ def method_missing(name, *args)
18
+ @name.__send__(name, *args)
19
+ rescue NoMethodError
20
+ @params.__send__(name, *args)
21
+ end
22
+
23
+ def [](name)
24
+ @params.__send__(:[], name)
25
+ end
26
+
27
+ def hash
28
+ @name.hash
29
+ end
30
+
31
+ def has_key?(key)
32
+ @params.to_hash.has_key?(key)
33
+ end
34
+
35
+ def set_param(key, value)
36
+ data = @params.to_hash
37
+ data[key] = value
38
+ @params.attributes = data
39
+ value
40
+ end
41
+
42
+ alias_method :[]=, :set_param
43
+
44
+ def ==(b)
45
+ self.eql?(b)
46
+ end
47
+
48
+ def eql?(b)
49
+ if b.kind_of? Event
50
+ @name == b.name
51
+ else
52
+ @name == b
53
+ end
54
+ end
55
+ alias_method :===, :==
56
+ end
57
+ end
58
+ __END__
59
+ class Symbol
60
+ def eql?(b)
61
+ if b.kind_of? Termtter::Event
62
+ self.equal? b.name
63
+ else
64
+ self.equal? b
65
+ end
66
+ end
67
+ alias_method :==, :eql?
68
+ alias_method :===, :eql?
69
+ end
data/lib/termtter/hook.rb CHANGED
@@ -25,7 +25,12 @@ module Termtter
25
25
  end
26
26
 
27
27
  def call(*args)
28
- self.exec_proc.call(*args)
28
+ begin
29
+ self.exec_proc.call(*args)
30
+ rescue ArgumentError
31
+ args.pop
32
+ retry
33
+ end
29
34
  end
30
35
  end
31
36
  end
@@ -1,5 +1,11 @@
1
1
  # -*- coding: utf-8 -*-
2
2
 
3
+ if RUBY_VERSION <= '1.8.6'
4
+ class String
5
+ def each_char; split(//); end
6
+ end
7
+ end
8
+
3
9
  module Termtter
4
10
  class HookCanceled < StandardError; end
5
11
 
@@ -41,12 +47,14 @@ module Termtter
41
47
 
42
48
  # return last hook return value
43
49
  def call_hooks(point, *args)
44
- Termtter::Client.logger.debug "call_hooks: [:point => #{point}, :args => [#{args.map {|a| a.inspect.split(//)[0..10].join}.join(', ')}]]"
50
+ Termtter::Client.logger.debug "call_hooks: [:point => #{point}, :args => [#{args.map {|a| a.inspect.each_char.take(11).join}.join(', ')}]]"
45
51
  result = nil
46
52
  get_hooks(point).each {|hook|
47
53
  break if result == false # interrupt if hook return false
54
+ Termtter::Client.logger.debug "call_hooks: #{point} #{hook.inspect}"
48
55
  result = hook.call(*args)
49
56
  }
57
+ Termtter::Client.logger.debug "call_hooks: [:point => #{point}, :args => [#{args.map {|a| a.inspect.each_char.take(11).join}.join(', ')}]], done"
50
58
  result
51
59
  end
52
60