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
data/.gitignore CHANGED
@@ -2,4 +2,4 @@
2
2
  *~
3
3
  *.gem
4
4
  pkg
5
-
5
+ bin/termtter.bat
data/README.rdoc CHANGED
@@ -74,6 +74,7 @@ You can also type `<` as curry.
74
74
  * termcolor
75
75
  * rubytter
76
76
  * highline
77
+ * notify
77
78
 
78
79
  == TODO:
79
80
 
data/Rakefile CHANGED
@@ -8,10 +8,11 @@ begin
8
8
  gem.summary = "Terminal based Twitter client."
9
9
  gem.description = "Termtter is a terminal based Twitter client."
10
10
  gem.executables = ["termtter"]
11
- gem.add_dependency("json_pure", ">= 1.1.3")
11
+ gem.add_dependency("json", ">= 1.1.3")
12
12
  gem.add_dependency("highline", ">= 1.5.0")
13
13
  gem.add_dependency("termcolor", ">= 1.0.0")
14
14
  gem.add_dependency("rubytter", ">= 0.11.0")
15
+ gem.add_dependency("notify", ">= 0.2.1")
15
16
  gem.authors = %w(jugyo ujihisa)
16
17
  gem.email = 'jugyo.org@gmail.com'
17
18
  gem.homepage = 'http://termtter.org/'
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.7.2
1
+ 1.8.0
data/bin/termtter CHANGED
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
2
  # -*- coding: utf-8 -*-
3
+ # vim: filetype=ruby
3
4
 
4
5
  self_file =
5
6
  if File.symlink?(__FILE__)
@@ -0,0 +1,134 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- coding: utf-8 -*-
3
+ # vim: filetype=ruby
4
+
5
+ self_file =
6
+ if File.symlink?(__FILE__)
7
+ require 'pathname'
8
+ Pathname.new(__FILE__).realpath
9
+ else
10
+ __FILE__
11
+ end
12
+ $:.unshift(File.dirname(self_file) + "/../lib")
13
+ ARGV.unshift '-m' unless ARGV.include?('-m') #monochrome
14
+
15
+ require 'singleton'
16
+ require 'termtter'
17
+
18
+ module Termtter
19
+ class CommandLine
20
+ include Singleton
21
+
22
+ class << self
23
+ def start
24
+ instance.start
25
+ end
26
+
27
+ def stop
28
+ instance.stop
29
+ end
30
+ end
31
+
32
+ def start
33
+ start_input_thread
34
+ end
35
+
36
+ def stop
37
+ @input_thread.kill if @input_thread
38
+ end
39
+
40
+ def call(command_text)
41
+ # Example:
42
+ # t.register_hook(:post_all, :point => :prepare_command) do |s|
43
+ # "update #{s}"
44
+ # end
45
+ Client.get_hooks('prepare_command').each {|hook|
46
+ command_text = hook.call(command_text)
47
+ }
48
+ Client.execute(command_text)
49
+ rescue CommandNotFound => e
50
+ hooks = Client.get_hooks('command_not_found')
51
+ raise e if hooks.empty?
52
+ hooks.each {|hook|
53
+ hook.call(command_text)
54
+ }
55
+ rescue TimeoutError
56
+ puts TermColor.parse("<red>Time out :(</red>")
57
+ end
58
+
59
+ def prompt
60
+ prompt_text = config.prompt
61
+ Client.get_hooks('prepare_prompt').each {|hook|
62
+ prompt_text = hook.call(prompt_text)
63
+ }
64
+ prompt_text
65
+ end
66
+
67
+ private
68
+
69
+ def start_input_thread
70
+ @input_thread = Thread.new do
71
+ buf = ""
72
+ while buf = gets.chomp
73
+ begin
74
+ call(buf)
75
+ rescue Exception => e
76
+ Client.handle_error(e)
77
+ end
78
+ end
79
+ end
80
+ @input_thread.join
81
+ end
82
+
83
+ def do_completion(input)
84
+ end
85
+ end
86
+
87
+ Client.register_hook(:initialize_command_line, :point => :init_command_line) do
88
+ CommandLine.start
89
+ end
90
+
91
+ Client.register_hook(:finalize_command_line, :point => :exit) do
92
+ CommandLine.stop
93
+ end
94
+ end
95
+
96
+ module Termtter::Client
97
+ class << self
98
+ attr_reader :task_manager
99
+ end
100
+ end
101
+
102
+
103
+ config.system.disable_plugins ||= []
104
+ config.system.disable_plugins << :command_line
105
+
106
+ config.__freeze__(:user_name) unless config.user_name.empty?
107
+ Termtter::Client.parse_options
108
+ Termtter::Client.load_config
109
+ Termtter::Client.setup_task_manager
110
+
111
+ Termtter::Client.load_plugins
112
+ Termtter::Client.eval_init_block
113
+ config.__unfreeze__(:user_name)
114
+ Termtter::API.setup
115
+
116
+ config.system.eval_scripts.each do |script|
117
+ begin
118
+ eval script
119
+ rescue Exception => e
120
+ Termtter::Client.handle_error(e)
121
+ end
122
+ end
123
+
124
+ config.system.run_commands.each {|cmd| execute(cmd) }
125
+
126
+ unless config.system.cmd_mode
127
+ Termtter::Client.task_manager.run()
128
+ Termtter::Client.call_hooks(:initialize)
129
+ Termtter::Client.add_task(:name => :call_hooks_after_launched, :after => 1) do
130
+ Termtter::Client.call_hooks(:launched)
131
+ end
132
+ Termtter::Client.call_hooks(:init_command_line)
133
+ end
134
+
data/lib/plugins/aa.rb ADDED
@@ -0,0 +1,44 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ module AAMaker
4
+ # view-source:http://amachang.sakura.ne.jp/misc/aamaker/
5
+ def self.random(n)
6
+ rand(n).to_i
7
+ end
8
+ def self.randSelect(array)
9
+ return array[random(array.length)]
10
+ end
11
+
12
+ def self.make
13
+ rinkaku = randSelect([['(', ')'], ['(', ')'], ['|', '|'], ['|', '|']]);
14
+ otete = randSelect([['', '', '', '', ''], ['', '', 'm', '', ''], ['', '', 'ლ', '', ''], ['ლ', '', '', 'ლ', ''], ['', '「', '', '', '「'], ['', ' つ', '', '', 'つ'], ['', ' ', '', '', 'o彡゚'], ['', 'n', '', '', 'η'], ['', '∩', '', '∩', ''], ['∩', '', '', '', '∩'], ['ヽ', '', '', '', 'ノ'], ['┐', '', '', '', '┌'], ['╮', '', '', '', '╭'], ['<', '', '', '', '/'], ['╰', '', '', ' ', ''], ['o', '', '', '', 'o'], ['o', '', '', '', 'ツ'], ['', '', '', '', 'ノシ']]);
15
+ omeme = randSelect([['◕', '◕'], ['╹', '╹'], ['>', '<'], ['^', '^'], ['・', '・'], ['´・', '・`'], ['`・', '・´'], ['´', '`'], ['≧', '≦'], ['゚', '゚'], ['\'', '\''], ['・ิ', '・ิ'], ['❛', '❛'], ['⊙', '⊙'], [' ̄', ' ̄'], ['◕ˇ', 'ˇ◕']]);
16
+ okuti = randSelect(['ω', '∀', '▽', '△', 'Д' , '□', '~', 'ー', 'ェ', 'ρ', 'o']);
17
+ hoppe = randSelect([['', ''], ['*', ''], ['', '*'], ['', '#'], ['#', ''], ['✿', ''], ['', '✿'], ['', ';'], [';', ''], ['。', '。'], ['。', ''], ['', '。'], ['▰', '▰'], ['', '▰'], ['▰', ''], ['๑', '๑'], ['', '๑'], ['๑', '']]);
18
+
19
+ text = [
20
+ otete[0],
21
+ rinkaku[0],
22
+ otete[1] || (otete[3] ? '' : hoppe[0]),
23
+ omeme[0],
24
+ otete[2] || okuti,
25
+ omeme[1],
26
+ otete[3] || (otete[1] ? '' : hoppe[1]),
27
+ rinkaku[1],
28
+ otete[4]
29
+ ].join('')
30
+
31
+ text
32
+ end
33
+ end
34
+
35
+ Termtter::Client.register_command(
36
+ :name => :aa,
37
+ :exec_proc => lambda {|arg|
38
+ name = Termtter::Client.normalize_as_user_name(arg)
39
+ command = name.length > 0 ? "u @#{name} #{AAMaker.make}" : "u #{AAMaker.make}"
40
+ Termtter::Client.execute command
41
+ },
42
+ :help => ["aa [(Optinal) USER]", "Post a AA"]
43
+ )
44
+
@@ -1,53 +1,55 @@
1
1
  # -*- coding: utf-8 -*-
2
-
3
2
  config.plugins.another_prompt.
4
- set_default(:shortcut_setting,
5
- { ':' => '',
6
- 'd' => 'direct',
7
- 'D' => 'delete',
8
- 'f' => 'fib',
9
- 'F' => 'favorite',
10
- 'l' => 'list',
11
- 'o' => 'open',
12
- 'p' => 'profile',
13
- 'R' => 'reply',
14
- 's' => 'search',
15
- 't' => 'retweet',
16
- 'u' => 'update',
17
- 'c' => lambda do
18
- system('clear')
19
- end,
20
- 'L' => lambda do
21
- puts '-' *
22
- `stty size`.chomp.
23
- sub(/^\d+\s(\d+)$/, '\\1').to_i
24
- end,
25
- 'q' => lambda do
26
- Termtter::Client.execute('quit')
27
- end,
28
- 'r' => lambda do
29
- Termtter::Client.execute('replies')
30
- end,
31
- '?' => lambda do
32
- Termtter::Client.execute('help')
33
- end,
34
- "\e" => lambda do
35
- system('screen', '-X', 'eval', 'copy')
36
- end
37
- })
3
+ set_default(
4
+ :shortcut_setting,
5
+ {
6
+ ':' => '',
7
+ 'd' => 'direct',
8
+ 'D' => 'delete',
9
+ 'f' => 'fib',
10
+ 'F' => 'favorite',
11
+ 'l' => 'list',
12
+ 'o' => 'open',
13
+ 'p' => 'profile',
14
+ 'R' => 'reply',
15
+ 's' => 'search',
16
+ 't' => 'retweet',
17
+ 'u' => 'update',
18
+ 'c' => lambda do
19
+ system('clear')
20
+ end,
21
+ 'L' => lambda do
22
+ puts '-' *
23
+ `stty size`.chomp.
24
+ sub(/^\d+\s(\d+)$/, '\\1').to_i
25
+ end,
26
+ 'q' => lambda do
27
+ Termtter::Client.execute('quit')
28
+ end,
29
+ 'r' => lambda do
30
+ Termtter::Client.execute('replies')
31
+ end,
32
+ '?' => lambda do
33
+ Termtter::Client.execute('help')
34
+ end,
35
+ "\e" => lambda do
36
+ system('screen', '-X', 'eval', 'copy')
37
+ end
38
+ })
38
39
 
39
40
  Termtter::Client.plug 'curry'
40
41
 
41
42
  module Termtter::Client
42
- add_task(:name => :auto_reload,
43
- :interval => config.update_interval,
44
- :after => config.update_interval) do
43
+ add_task(
44
+ :name => :auto_reload,
45
+ :interval => config.update_interval,
46
+ :after => config.update_interval) do
45
47
  begin
46
48
  execute('reload')
47
49
  rescue Exception => e
48
50
  handle_error(e)
49
51
  end
50
- end
52
+ end
51
53
 
52
54
  register_hook(
53
55
  :name => :auto_reload_init,
@@ -58,8 +60,7 @@ module Termtter::Client
58
60
  rescue Exception => e
59
61
  handle_error(e)
60
62
  end
61
- }
62
- )
63
+ })
63
64
  end
64
65
 
65
66
  module Termtter
@@ -0,0 +1,82 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'uri'
3
+ require 'open-uri'
4
+ require 'nokogiri'
5
+ require 'timeout'
6
+ require 'memcache'
7
+ require 'digest/sha1'
8
+
9
+ module Termtter::Client
10
+ config.plugins.appendtitle.set_default(:timeout, 30)
11
+ config.plugins.appendtitle.set_default(:cache_expire, 3600 * 24 * 7)
12
+ config.plugins.appendtitle.set_default(:memcached_server, 'localhost:11211')
13
+
14
+ def self.memcache_client
15
+ @memcache_client ||= MemCache.new(config.plugins.appendtitle.memcached_server)
16
+ end
17
+
18
+ def self.fetch_title(uri)
19
+ return unless uri
20
+ key = %w{ termtter plugins appendtitle title}.push(Digest::SHA1.hexdigest(uri)).join('-')
21
+ if v = memcache_client.get(key)
22
+ logger.debug "appendtitle: cache hit for #{uri}"
23
+ return v
24
+ end
25
+
26
+ memcache_client.set(key, '', config.plugins.appendtitle.cache_expire) # to avoid duplicate fetch
27
+ begin
28
+ logger.debug "appendtitle: fetching title for #{uri}"
29
+ source = Nokogiri(open(uri).read)
30
+ if source and source.at('title')
31
+ title = source.at('title').text
32
+ memcache_client.set(key, title, config.plugins.appendtitle.cache_expire)
33
+ return title
34
+ end
35
+ nil
36
+ rescue Timeout::Error
37
+ nil
38
+ rescue
39
+ nil
40
+ end
41
+ end
42
+
43
+ register_hook(
44
+ :name => :appendtitle,
45
+ :point => :filter_for_output,
46
+ :exec_proc => lambda do |statuses, event|
47
+ threads = statuses.map do |status|
48
+ Thread.new{
49
+ begin
50
+ status.text.gsub!(URI.regexp(['http', 'https'])) {|uri|
51
+ title = fetch_title(uri)
52
+ title = title.gsub(/\n/, '').gsub(/\s+/, ' ') if title
53
+ body_for_compare = status.text.gsub(/\n/, '').gsub(/\s+/, ' ')
54
+ if title and not (
55
+ body_for_compare.include? title or
56
+ body_for_compare.include? title[0..(title.length/2)] or
57
+ body_for_compare.include? title[(title.length/2)..-1]) # XXX: heuristic!!!
58
+ "#{uri} (#{title})"
59
+ else
60
+ uri
61
+ end
62
+ }
63
+ rescue
64
+ end
65
+ }
66
+ end
67
+ begin
68
+ # wait for join or timeout
69
+ timeout(config.plugins.appendtitle.timeout) {
70
+ threads.each{ |t| t.join }
71
+ }
72
+ rescue Timeout::Error
73
+ logger.error 'appendtitle: timeout'
74
+ end
75
+
76
+ statuses
77
+ end
78
+ )
79
+ end
80
+
81
+ # appendtitle.rb:
82
+ # append title for uri.
data/lib/plugins/ar.rb CHANGED
@@ -70,10 +70,12 @@ module Termtter
70
70
  end
71
71
 
72
72
  register_command(:db_search, :alias => :ds) do |arg|
73
- statuses = Status.find(:all,
74
- :conditions => ['text LIKE :l',
75
- {:l => "%" + arg + "%"}],
76
- :limit => 20)
73
+ statuses = Status.find(
74
+ :all,
75
+ :conditions => [
76
+ 'text LIKE :l',
77
+ {:l => "%" + arg + "%"}],
78
+ :limit => 20)
77
79
  output(statuses, :db_search)
78
80
  end
79
81
 
@@ -86,10 +88,11 @@ module Termtter
86
88
 
87
89
  register_command(:db_list) do |arg|
88
90
  user_name = normalize_as_user_name(arg)
89
- statuses = Status.find(:all,
90
- :joins => "LEFT OUTER JOIN users ON users.uid = statuses.user_id",
91
- :conditions => ['users.screen_name = :u',{:u => user_name}],
92
- :limit => 20)
91
+ statuses = Status.find(
92
+ :all,
93
+ :joins => "LEFT OUTER JOIN users ON users.uid = statuses.user_id",
94
+ :conditions => ['users.screen_name = :u',{:u => user_name}],
95
+ :limit => 20)
93
96
  output(statuses, :db_search)
94
97
  end
95
98
 
data/lib/plugins/async.rb CHANGED
@@ -4,10 +4,11 @@ module Termtter::Client
4
4
  :alias => :a,
5
5
  :help => ['async COMMAND', 'asynchronously execute the command'],
6
6
  :completion => lambda {|cmd, arg|
7
- commands.map{|name, command| command.complement(arg)}.
7
+ commands.
8
+ map {|name, command| command.complement(arg) }.
8
9
  flatten.
9
10
  compact.
10
- map{|i| "#{cmd} #{i}"}
11
+ map {|i| "#{cmd} #{i}" }
11
12
  },
12
13
  :exec => lambda {|arg|
13
14
  @task_manager.invoke_later do