termtter 1.7.2 → 1.8.0

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