termtter 1.0.6 → 1.0.7

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc CHANGED
@@ -1,19 +1,36 @@
1
- # -*- coding: utf-8 -*-
2
1
  = termtter
3
2
 
4
3
  http://github.com/jugyo/termtter
5
4
 
6
5
  == DESCRIPTION:
7
6
 
8
- Termtter is a terminal based Twitter client
7
+ Termtter is a terminal based Twitter client.
9
8
 
10
9
  == FEATURES/PROBLEMS:
11
10
 
12
11
  == SYNOPSIS:
13
12
 
14
- Run:
13
+ === Run:
15
14
 
16
- termtter
15
+ % termtter
16
+
17
+ === Some Termtter Commands:
18
+
19
+ Show help
20
+
21
+ > help
22
+
23
+ Update status
24
+
25
+ > update hi!
26
+
27
+ Show replies
28
+
29
+ > replies
30
+
31
+ Search
32
+
33
+ > search termtter
17
34
 
18
35
  == REQUIREMENTS:
19
36
 
@@ -23,24 +40,14 @@ Run:
23
40
 
24
41
  == INSTALL:
25
42
 
26
- sudo gem source -a http://gems.github.com (you only have to do this once)
27
- sudo gem install jugyo-termtter
28
-
29
- if you want to install gem package from source code, install like following.
30
-
31
- git clone git://github.com/jugyo/termtter.git
32
- cd termtter
33
- rake gem
34
- gem install *.gem
35
-
36
- Just run a new command termtter.
43
+ % sudo gem install termtter
37
44
 
38
- termtter
45
+ == CONFIGURATION:
39
46
 
40
- Termtter generates a configuration file named '.termtter' in your HOME directory.
47
+ Termtter generates a configuration file named '~/.termtter/config'.
41
48
  You can edit the file anytime.
42
49
 
43
- vim ~/.termtter
50
+ % vim ~/.termtter/config
44
51
 
45
52
  config.user_name = 'USERNAME'
46
53
  config.password = 'PASSWORD'
@@ -65,9 +72,7 @@ http://wiki.github.com/jugyo/termtter/home (in Japanese)
65
72
  == TODO:
66
73
 
67
74
  - Enhance the document and spec
68
- - Finalize the Hook specification and integration
69
75
  - Improve the UI(a status view, etc...)
70
- - Build a logging function
71
76
 
72
77
  == LICENSE:
73
78
 
data/Rakefile CHANGED
@@ -26,8 +26,7 @@ Gem::Specification.new do |s|
26
26
  s.add_dependency("json_pure", ">= 1.1.3")
27
27
  s.add_dependency("highline", ">= 1.5.0")
28
28
  s.add_dependency("termcolor", ">= 0.3.1")
29
- s.add_dependency("rubytter", ">= 0.6.3")
30
- s.add_dependency("sqlite3-ruby", ">= 1.2.4")
29
+ s.add_dependency("rubytter", ">= 0.6.5")
31
30
  s.authors = %w(jugyo ujihisa)
32
31
  s.email = 'jugyo.org@gmail.com'
33
32
  s.homepage = 'http://wiki.github.com/jugyo/termtter'
@@ -1,18 +1,18 @@
1
1
  # -*- coding: utf-8 -*-
2
2
 
3
3
  config.plugins.addspace.set_default( :before, [ %r{https?://} ] )
4
- config.plugins.addspace.set_default( :after, %w{ ★ ☆ △ ▽})
4
+ config.plugins.addspace.set_default( :after, %w{ ★ ☆ △ ▽})
5
5
 
6
6
  module Termtter::Client
7
7
  add_filter do |statuses, event|
8
8
  statuses.each do |s|
9
9
  config.plugins.addspace.before.each do |c|
10
- s.text.gsub!(/(\S)(#{c})/, '\1 \2' )
10
+ s.text.gsub!(/(?<=\S)(#{c})/, ' \1' )
11
11
  end
12
12
  end
13
13
  statuses.each do |s|
14
14
  config.plugins.addspace.after.each do |c|
15
- s.text.gsub!(/(#{c})(\S)/, '\1 \2' )
15
+ s.text.gsub!(/(#{c})(?=\S)/, '\1 ' )
16
16
  end
17
17
  statuses
18
18
  end
data/lib/plugins/bomb.rb CHANGED
@@ -7,7 +7,7 @@ module Termtter
7
7
 
8
8
  register_hook(
9
9
  :name => :bomb,
10
- :points => [:post_filter],
10
+ :points => [:output],
11
11
  :exec_proc => lambda{|statuses, event|
12
12
  statuses.each do |status|
13
13
  if /爆発|bomb/ =~ status.text
@@ -4,6 +4,9 @@ Termtter::Client.register_hook(
4
4
  :name => :confirm,
5
5
  :points => [:pre_exec_update],
6
6
  :exec_proc => lambda {|cmd, arg|
7
- false if arg.empty? || /^y?$/i !~ Readline.readline("update? #{arg} [Y/n] ", false)
7
+ if /^y?$/i !~ Readline.readline("update? #{arg} [Y/n] ", false)
8
+ puts 'canceled.'
9
+ raise Termtter::CommandCanceled
10
+ end
8
11
  }
9
12
  )
@@ -10,7 +10,7 @@ module Termtter::Client
10
10
  add_filter do |statuses, event|
11
11
  statuses.each do |s|
12
12
  URL_SHORTTERS.each do |site|
13
- s[:text].gsub!(site[:pattern]) do |m|
13
+ s.text.gsub!(site[:pattern]) do |m|
14
14
  expand_url(site[:host], $2) || $1
15
15
  end
16
16
  end
data/lib/plugins/growl.rb CHANGED
@@ -4,58 +4,49 @@ require 'tmpdir'
4
4
  require 'open-uri'
5
5
  require 'uri'
6
6
  require 'fileutils'
7
+ require 'cgi'
7
8
 
8
9
  begin
9
10
  require 'ruby-growl'
10
- growl = Growl.new "localhost", "termtter", "termtter status notification"
11
+ growl = Growl.new "localhost", "termtter", ["update_friends_timeline"]
11
12
  rescue LoadError
12
13
  growl = nil
13
14
  end
14
15
 
15
- config.plugins.growl.set_default(:icon_cache_dir, "#{Dir.tmpdir}/termtter-icon-cache-dir")
16
+ config.plugins.growl.set_default(:icon_cache_dir, "#{Termtter::CONF_DIR}/tmp/user_profile_images")
16
17
  FileUtils.mkdir_p(config.plugins.growl.icon_cache_dir) unless File.exist?(config.plugins.growl.icon_cache_dir)
17
18
 
18
19
  def get_icon_path(s)
20
+ Dir.mkdir_p(config.plugins.growl.icon_cache_dir) unless File.exists?(config.plugins.growl.icon_cache_dir)
19
21
  cache_file = "%s/%s%s" % [ config.plugins.growl.icon_cache_dir,
20
- s.user_screen_name,
21
- File.extname(s.user_profile_image_url) ]
22
- if File.exist?(cache_file) && (File.atime(cache_file) + 24*60*60) > Time.now
23
- return cache_file
24
- else
25
- Thread.new do
26
- File.open(cache_file, "wb") do |f|
27
- f << open(URI.escape(s.user_profile_image_url)).read
22
+ s.user.screen_name,
23
+ File.extname(s.user.profile_image_url) ]
24
+ if !File.exist?(cache_file) || (File.atime(cache_file) + 24*60*60) < Time.now
25
+ File.open(cache_file, "wb") do |f|
26
+ begin
27
+ f << open(URI.escape(s.user.profile_image_url)).read
28
+ rescue OpenURI::HTTPError
29
+ return nil
28
30
  end
29
31
  end
30
- return nil
31
32
  end
33
+ cache_file
32
34
  end
33
35
 
34
- queue = []
35
- Thread.new do
36
- loop do
37
- begin
38
- if s = queue.pop
36
+ Termtter::Client.register_hook(
37
+ :name => :growl,
38
+ :points => [:output],
39
+ :exec_proc => lambda {|statuses, event|
40
+ return unless event == :update_friends_timeline
41
+ Thread.start do
42
+ statuses.each do |s|
39
43
  unless growl
40
- arg = ['growlnotify', s.user.screen_name, '-m', s.text.gsub("\n",''), '-n', 'termtter']
41
- #icon_path = get_icon_path(s)
42
- #arg += ['--image', icon_path] if icon_path
43
- system *arg
44
+ system 'growlnotify', s.user.screen_name, '-m', s.text.gsub("\n",''), '-n', 'termtter', '--image', get_icon_path(s)
44
45
  else
45
- growl.notify "termtter status notification", s.text, s.user.screen_name
46
+ growl.notify "update_friends_timeline", s.user.screen_name, CGI.unescapeHTML(s.text)
46
47
  end
48
+ sleep 0.1
47
49
  end
48
- rescue => e
49
- puts e
50
- puts e.backtrace.join("\n")
51
50
  end
52
- sleep 0.1
53
- end
54
- end
55
-
56
- Termtter::Client.register_hook(:name => :growl,
57
- :points => [:post_filter],
58
- :exec_proc => lambda {|statuses, event|
59
- statuses.each {|s| queue << s} if event == :update_friends_timeline
60
- }
51
+ }
61
52
  )
data/lib/plugins/log.rb CHANGED
@@ -22,8 +22,12 @@ module Termtter::Client
22
22
  statuses.each do |s|
23
23
  public_storage[:tweet][s.user.screen_name] = [] unless public_storage[:tweet][s.user.screen_name]
24
24
  public_storage[:tweet][s.user.screen_name] << s
25
- if public_storage[:tweet].size > max_size
26
- public_storage[:tweet] = public_storage[:tweet][-max_size..-1]
25
+ end
26
+
27
+ statuses.map{ |s| s.user.screen_name}.uniq.each do |name|
28
+ public_storage[:tweet][name]
29
+ if public_storage[:tweet][name].size > max_size
30
+ public_storage[:tweet][name] = public_storage[:tweet][name][-max_size..-1]
27
31
  end
28
32
  end
29
33
  }
@@ -13,12 +13,12 @@ achar.show
13
13
 
14
14
  Termtter::Client.register_hook(
15
15
  :name => :msagent,
16
- :points => [:post_filter],
16
+ :points => [:output],
17
17
  :exec_proc => lambda {|statuses, event|
18
18
  if !statuses.empty? && event == :update_friends_timeline
19
19
  Thread.start do
20
- statuses.reverse.each do |s|
21
- req = achar.speak("#{s[:screen_name]}: #{s[:text]}".tosjis)
20
+ statuses.each do |s|
21
+ req = achar.speak("#{s.user.screen_name}: #{s.text}".tosjis)
22
22
  sleep 1
23
23
  WIN32OLE_EVENT.message_loop
24
24
  achar.stop(req)
@@ -2,15 +2,15 @@
2
2
 
3
3
  Termtter::Client.register_hook(
4
4
  :name => :notify_send,
5
- :points => [:post_filter],
5
+ :points => [:output],
6
6
  :exec_proc => lambda {|statuses, event|
7
7
  if event == :update_friends_timeline
8
8
  max = 10
9
9
 
10
10
  text = statuses.take(max).map {|s|
11
- status_text = CGI.escapeHTML(s[:text])
11
+ status_text = CGI.escapeHTML(s.text)
12
12
  status_text.gsub!(%r{https?://[-_.!~*\'()a-zA-Z0-9;\/?:\@&=+\$,%#]+},'<a href="\0">\0</a>')
13
- "<b>#{s[:screen_name]}:</b> <span font=\"9.0\">#{status_text}</span>"
13
+ "<b>#{s.user.screen_name}:</b> <span font=\"9.0\">#{status_text}</span>"
14
14
  }.join("\n")
15
15
 
16
16
  text << %Q|\n<a href="http://twitter.com/">more...</a>| if statuses.size > max
@@ -0,0 +1,39 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ require 'fileutils'
4
+
5
+ # notify-send.rb からコピペ。共通化したいところ。
6
+ config.plugins.notify_send.set_default(:icon_cache_dir, "#{Termtter::CONF_DIR}/tmp/user_profile_images")
7
+ def get_icon_path(s)
8
+ FileUtils.mkdir_p(config.plugins.notify_send.icon_cache_dir) unless File.exist?(config.plugins.notify_send.icon_cache_dir)
9
+ cache_file = "%s/%s%s" % [ config.plugins.notify_send.icon_cache_dir,
10
+ s.user.screen_name,
11
+ File.extname(s.user.profile_image_url) ]
12
+ if !File.exist?(cache_file) || (File.atime(cache_file) + 24*60*60) < Time.now
13
+ File.open(cache_file, "wb") do |f|
14
+ begin
15
+ f << open(URI.escape(s.user.profile_image_url)).read
16
+ rescue OpenURI::HTTPError
17
+ return nil
18
+ end
19
+ end
20
+ end
21
+ cache_file
22
+ end
23
+
24
+ Termtter::Client.register_hook(
25
+ :name => :notify_send2,
26
+ :points => [:output],
27
+ :exec_proc => lambda {|statuses, event|
28
+ return unless event == :update_friends_timeline
29
+ Thread.start do
30
+ statuses.each do |s|
31
+ text = CGI.escapeHTML(s.text)
32
+ text.gsub!(%r{https?://[-_.!~*\'()a-zA-Z0-9;\/?:\@&=+\$,%#]+},'<a href="\0">\0</a>')
33
+ system 'notify-send', s.user.screen_name, text, '-i', get_icon_path(s)
34
+ sleep 0.1
35
+ end
36
+ end
37
+ }
38
+ )
39
+
data/lib/plugins/say.rb CHANGED
@@ -12,7 +12,7 @@ end
12
12
  module Termtter::Client
13
13
  register_hook(
14
14
  :name => :say,
15
- :points => [:post_filter],
15
+ :points => [:output],
16
16
  :exec_proc => lambda {|statuses, event|
17
17
  statuses.reverse.each do |s|
18
18
  text_without_uri = s[:text].gsub(%r|https?://[^\s]+|, 'U.R.I.')
@@ -5,7 +5,7 @@ config.screen_notify.set_default(:format, "[termtter] %s")
5
5
  module Termtter::Client
6
6
  register_hook(
7
7
  :name => :screen_notify,
8
- :points => [:post_filter],
8
+ :points => [:output],
9
9
  :exec_proc => lambda{|statuses, event|
10
10
  return unless event = :update_friends_timeline
11
11
  Thread.new(statuses) do |ss|
data/lib/plugins/spam.rb CHANGED
@@ -1,13 +1,16 @@
1
1
  # -*- coding: utf-8 -*-
2
2
 
3
- Termtter::API.twitter.update('*super spam time*')
3
+ message = '*super spam time*'
4
+ Termtter::API.twitter.update(message)
5
+ puts "=> #{message}"
6
+
4
7
  Termtter::Client.register_hook(
5
8
  :name => :span,
6
- :points => [/^pre_exec/],
7
- :exec_proc => lambda{|*arg|
9
+ :point => /^pre_exec/,
10
+ :exec => lambda{|*arg|
8
11
  text = arg.join(' ')
9
12
  Termtter::API.twitter.update(text)
10
13
  puts "=> #{text}"
11
- false
14
+ raise Termtter::CommandCanceled
12
15
  }
13
16
  )
@@ -13,7 +13,7 @@ module Termtter::Client
13
13
  register_command(
14
14
  :name => :update, :aliases => [:u],
15
15
  :exec_proc => lambda {|arg|
16
- unless arg =~ /^\s*$/
16
+ unless /^\s*$/ =~ arg
17
17
  # TODO: Change to able to disable erb.
18
18
  text = ERB.new(arg).result(binding).gsub(/\n/, ' ')
19
19
  result = Termtter::API.twitter.update(text)
@@ -31,14 +31,14 @@ module Termtter::Client
31
31
  register_command(
32
32
  :name => :direct, :aliases => [:d],
33
33
  :exec_proc => lambda {|arg|
34
- if arg =~ /^([^\s]+)\s+(.*)\s*$/
34
+ if /^([^\s]+)\s+(.*)\s*$/ =~ arg
35
35
  user, text = $1, $2
36
36
  Termtter::API.twitter.direct_message(user, text)
37
37
  puts "=> to:#{user} message:#{text}"
38
38
  end
39
39
  },
40
40
  :completion_proc => lambda {|cmd, args|
41
- if args =~ /^([^\s]+)$/
41
+ if /^([^\s]+)$/ =~ args
42
42
  find_user_candidates $1, "#{cmd} %s"
43
43
  end
44
44
  }
@@ -49,10 +49,10 @@ module Termtter::Client
49
49
  :exec_proc => lambda {|arg|
50
50
  user = Termtter::API.twitter.user(arg.strip)
51
51
  attrs = %w[ name screen_name url description profile_image_url location protected following
52
- friends_count followers_count statuses_count favourites_count
53
- id time_zone created_at utc_offset notifications
52
+ friends_count followers_count statuses_count favourites_count
53
+ id time_zone created_at utc_offset notifications
54
54
  ]
55
- label_width = attrs.map{|i|i.size}.max
55
+ label_width = attrs.map(&:size).max
56
56
  attrs.each do |attr|
57
57
  value = user.__send__(attr.to_sym)
58
58
  puts "#{attr.gsub('_', ' ').rjust(label_width)}: #{value}"
@@ -69,13 +69,13 @@ module Termtter::Client
69
69
  user_name = arg.strip
70
70
  user_name = config.user_name if user_name.empty?
71
71
 
72
- followers = []
72
+ followers = []
73
73
  page = 0
74
74
  begin
75
75
  followers += tmp = Termtter::API.twitter.followers(user_name, :page => page+=1)
76
76
  end until tmp.empty?
77
77
  Termtter::Client.public_storage[:followers] = followers
78
- puts followers.map{|f|f.screen_name}.join(' ')
78
+ puts followers.map(&:screen_name).join(' ')
79
79
  }
80
80
  # TODO :completion_proc
81
81
  )
@@ -85,7 +85,7 @@ module Termtter::Client
85
85
  :exec_proc => lambda {|arg|
86
86
  if arg.empty?
87
87
  event = :list_friends_timeline
88
- statuses = Termtter::API.twitter.friends_timeline()
88
+ statuses = Termtter::API.twitter.friends_timeline
89
89
  else
90
90
  event = :list_user_timeline
91
91
  statuses = Termtter::API.twitter.user_timeline(arg)
@@ -108,7 +108,7 @@ module Termtter::Client
108
108
  register_command(
109
109
  :name => :replies, :aliases => [:r],
110
110
  :exec_proc => lambda {|arg|
111
- output(Termtter::API.twitter.replies(), :replies)
111
+ output(Termtter::API.twitter.replies, :replies)
112
112
  }
113
113
  )
114
114
 
@@ -125,9 +125,9 @@ module Termtter::Client
125
125
  else
126
126
  users = find_users(arg)
127
127
  unless users.empty?
128
- users.map{|user| "#{cmd} #{user}:"}
128
+ users.map {|user| "#{cmd} #{user}:"}
129
129
  else
130
- find_status_ids(arg).map{|id| "#{cmd} #{id}"}
130
+ find_status_ids(arg).map {|id| "#{cmd} #{id}"}
131
131
  end
132
132
  end
133
133
  }
@@ -147,7 +147,7 @@ module Termtter::Client
147
147
  :name => :follow, :aliases => [],
148
148
  :exec_proc => lambda {|args|
149
149
  args.split(' ').each do |arg|
150
- if arg =~ /^(\w+)/
150
+ if /^(\w+)/ =~ arg
151
151
  res = Termtter::API::twitter.follow($1.strip)
152
152
  end
153
153
  end
@@ -155,14 +155,14 @@ module Termtter::Client
155
155
  :completion_proc => lambda {|cmd, args|
156
156
  find_user_candidates args, "#{cmd} %s"
157
157
  },
158
- :help => ['follow USER', 'Follow user']
158
+ :help => ['follow USER', 'Follow user']
159
159
  )
160
160
 
161
161
  register_command(
162
162
  :name => :leave, :aliases => [],
163
163
  :exec_proc => lambda {|args|
164
164
  args.split(' ').each do |arg|
165
- if arg =~ /^(\w+)/
165
+ if /^(\w+)/ =~ arg
166
166
  res = Termtter::API::twitter.leave($1.strip)
167
167
  end
168
168
  end
@@ -170,7 +170,7 @@ module Termtter::Client
170
170
  :completion_proc => lambda {|cmd, args|
171
171
  find_user_candidates args, "#{cmd} %s"
172
172
  },
173
- :help => ['leave USER', 'Leave user']
173
+ :help => ['leave USER', 'Leave user']
174
174
  )
175
175
 
176
176
  # TODO: Change colors when remaining_hits is low.
@@ -192,7 +192,7 @@ module Termtter::Client
192
192
  :help => ["limit,lm", "Show the API limit status"]
193
193
  )
194
194
 
195
- register_command(
195
+ register_command(
196
196
  :name => :pause,
197
197
  :exec_proc => lambda {|arg| pause},
198
198
  :help => ["pause", "Pause updating"]
@@ -255,21 +255,21 @@ module Termtter::Client
255
255
  register_command(
256
256
  :name => :execute,
257
257
  :exec_proc => lambda{|arg|
258
- if arg
259
- `#{arg}`.each_line do |line|
260
- unless line.strip.empty?
261
- Termtter::API.twitter.update(line)
262
- puts "=> #{line}"
258
+ if arg
259
+ `#{arg}`.each_line do |line|
260
+ unless line.strip.empty?
261
+ Termtter::API.twitter.update(line)
262
+ puts "=> #{line}"
263
+ end
263
264
  end
264
265
  end
265
- end
266
266
  },
267
267
  :help => ['execute COMMAND', 'execute the command']
268
268
  )
269
269
 
270
270
  def self.formatted_help(helps)
271
- helps = helps.sort_by{|help| help[0]}
272
- width = helps.map {|n, d| n.size }.max
271
+ helps = helps.sort_by {|help| help[0] }
272
+ width = helps.map {|n, _| n.size }.max
273
273
  space = 3
274
274
  helps.map {|name, desc|
275
275
  name.to_s.ljust(width + space) + desc.to_s
@@ -296,11 +296,11 @@ module Termtter::Client
296
296
  )
297
297
 
298
298
  def self.find_status_ids(text)
299
- public_storage[:status_ids].select{|id| id =~ /#{Regexp.quote(text)}/}
299
+ public_storage[:status_ids].select {|id| /#{Regexp.quote(text)}/ =~ id.to_s}
300
300
  end
301
301
 
302
302
  def self.find_users(text)
303
- public_storage[:users].select{|user| user =~ /#{Regexp.quote(text)}/}
303
+ public_storage[:users].select {|user| /#{Regexp.quote(text)}/ =~ user}
304
304
  end
305
305
 
306
306
  def self.find_user_candidates(a, b)
@@ -311,5 +311,4 @@ module Termtter::Client
311
311
  end.
312
312
  map {|u| b % u }
313
313
  end
314
-
315
314
  end
@@ -9,7 +9,11 @@ config.plugins.stdout.set_default(
9
9
  config.plugins.stdout.set_default(
10
10
  :timeline_format,
11
11
  '<90><%=time%></90> <<%=status_color%>><%=status%></<%=status_color%>> <90><%=id%></90>')
12
- config.plugins.stdout.set_default(:search_highlihgt_format, '<on_magenta><white>\1</white></on_magenta>')
12
+ config.plugins.stdout.set_default(:search_highlight_format, '<on_magenta><white>\1</white></on_magenta>')
13
+
14
+ config.plugins.stdout.set_default(:enable_pager, true)
15
+ config.plugins.stdout.set_default(:pager, 'less -R -f +G')
16
+ config.plugins.stdout.set_default(:window_height, 50)
13
17
 
14
18
  module Termtter
15
19
  class StdOut < Hook
@@ -24,21 +28,23 @@ module Termtter
24
28
  def print_statuses(statuses, sort = true, time_format = nil)
25
29
  return unless statuses and statuses.first
26
30
  unless time_format
27
- # 最初と最後の日付がちがうとき日付も出す
31
+ t0 = Time.now
28
32
  t1 = Time.parse(statuses.first[:created_at])
29
33
  t2 = Time.parse(statuses.last[:created_at])
30
34
  time_format =
31
- if [t1.year, t1.month, t1.day] == [t2.year, t2.month, t2.day]
35
+ if [t0.year, t0.month, t0.day] == [t1.year, t1.month, t1.day] \
36
+ and [t1.year, t1.month, t1.day] == [t2.year, t2.month, t2.day]
32
37
  '%H:%M:%S'
33
38
  else
34
39
  '%y/%m/%d %H:%M'
35
40
  end
36
41
  end
37
-
42
+
43
+ output_text = ''
38
44
  statuses.each do |s|
39
45
  text = s.text
40
46
  status_color = config.plugins.stdout.colors[s.user.id.hash % config.plugins.stdout.colors.size]
41
- status = "#{s.user.screen_name}: #{text}"
47
+ status = "#{s.user.screen_name}: #{TermColor.escape(text)}"
42
48
  if s.in_reply_to_status_id
43
49
  status += " (repl. to #{s.in_reply_to_status_id})"
44
50
  end
@@ -46,7 +52,17 @@ module Termtter
46
52
  time = "(#{Time.parse(s.created_at).strftime(time_format)})"
47
53
  id = s.id
48
54
  erbed_text = ERB.new(config.plugins.stdout.timeline_format).result(binding)
49
- puts TermColor.parse(erbed_text)
55
+ output_text << TermColor.parse(erbed_text) + "\n"
56
+ end
57
+
58
+ if config.plugins.stdout.enable_pager && ENV['LINES'] && statuses.size > ENV['LINES'].to_i
59
+ file = Tempfile.new('termtter')
60
+ file.print output_text
61
+ file.close
62
+ system "#{config.plugins.stdout.pager} #{file.path}"
63
+ file.close(true)
64
+ else
65
+ print output_text
50
66
  end
51
67
  end
52
68
  end
@@ -13,8 +13,16 @@ module Termtter::Storage
13
13
 
14
14
  def self.search(query)
15
15
  raise "query must be Hash(#{query}, #{query.class})" unless query.kind_of? Hash
16
+ if query[:text] == nil then
17
+ query[:text] = '';
18
+ end
19
+ if query[:user] == nil then
20
+ query[:user] = '';
21
+ end
16
22
  result = []
17
- DB.instance.db.execute("select created_at, screen_name, post_text, in_reply_to_status_id, post_id, user_id from post inner join user on post.user_id = user.id where post_text like '%' || ? || '%' ",
23
+ sql = "select created_at, screen_name, post_text, in_reply_to_status_id, post_id, user_id "
24
+ sql += "from post inner join user on post.user_id = user.id where post_text like '%' || ? || '%'"
25
+ DB.instance.db.execute(sql,
18
26
  query[:text]) do |created_at, screen_name, post_text, in_reply_to_status_id, post_id, user_id|
19
27
  created_at = Time.at(created_at).to_s
20
28
  result << {
@@ -32,19 +40,43 @@ module Termtter::Storage
32
40
  Rubytter.json_to_struct(result)
33
41
  end
34
42
 
43
+ def self.search_user(query)
44
+ raise "query must be Hash(#{query}, #{query.class})" unless query.kind_of? Hash
45
+ result = []
46
+ sql = "select created_at, screen_name, post_text, in_reply_to_status_id, post_id, user_id "
47
+ sql += "from post inner join user on post.user_id = user.id where "
48
+ sql += query[:user].split(' ').map!{|que| que.gsub(/(\w+)/, 'screen_name like \'%\1%\'')}.join(' or ')
49
+ DB.instance.db.execute(sql) do |created_at, screen_name, post_text, in_reply_to_status_id, post_id, user_id|
50
+ created_at = Time.at(created_at).to_s
51
+ result << {
52
+ :id => post_id,
53
+ :created_at => created_at,
54
+ :text => post_text,
55
+ :in_reply_to_status_id => in_reply_to_status_id,
56
+ :in_reply_to_user_id => nil,
57
+ :user => {
58
+ :id => user_id,
59
+ :screen_name => screen_name
60
+ }
61
+ }
62
+ end
63
+ Rubytter.json_to_struct(result)
64
+ end
65
+
35
66
  def self.insert(data)
67
+ return unless data[:text]
36
68
  DB.instance.db.execute(
37
- "insert into post values(?,?,?,?,?,?)",
38
- data[:post_id],
39
- data[:created_at],
40
- data[:in_reply_to_status_id],
41
- data[:in_reply_to_user_id],
42
- data[:text],
43
- data[:user_id])
69
+ "insert into post values(?,?,?,?,?,?)",
70
+ data[:post_id],
71
+ data[:created_at],
72
+ data[:in_reply_to_status_id],
73
+ data[:in_reply_to_user_id],
74
+ data[:text],
75
+ data[:user_id])
44
76
  DB.instance.db.execute(
45
- "insert into user values(?,?)",
46
- data[:user_id],
47
- data[:screen_name])
77
+ "insert into user values(?,?)",
78
+ data[:user_id],
79
+ data[:screen_name])
48
80
  rescue SQLite3::SQLException
49
81
  end
50
82
  end
@@ -18,7 +18,7 @@ module Termtter::Client
18
18
  :created_at => Time.parse(s.created_at).to_i,
19
19
  :in_reply_to_status_id => s.in_reply_to_status_id,
20
20
  :in_reply_to_user_id => s.in_reply_to_user_id,
21
- :post => s.text,
21
+ :text => s.text,
22
22
  :user_id => s.user.id,
23
23
  :screen_name => s.user.screen_name
24
24
  )
@@ -38,4 +38,18 @@ module Termtter::Client
38
38
  },
39
39
  :help => [ 'search_storage WORD', 'Search storage for WORD' ]
40
40
  )
41
+
42
+ register_command(
43
+ :name => :search_storage_user,
44
+ :aliases => [:ssu],
45
+ :exec_proc => lambda {|arg|
46
+ unless arg.strip.empty?
47
+ key = arg.strip
48
+ statuses = Termtter::Storage::Status.search_user({:user => key})
49
+ output(statuses, :search)
50
+ end
51
+ },
52
+ :help => [ 'search_storage_user SCREEN_NAME', 'Search storage for SCREE_NAME' ]
53
+ )
54
+
41
55
  end
@@ -0,0 +1,18 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ module Termtter::Client
4
+ register_command(
5
+ :name => :timer,
6
+ :exec_proc => lambda{|arg|
7
+ # argをparseする
8
+ return unless arg =~ /^\d+$/
9
+ after = arg.to_i
10
+ Termtter::Client.add_task(:after => after) do
11
+ text = "@#{config.user_name} 時間ですよ!!"
12
+ Termtter::API.twitter.update text
13
+ puts "=> " << text
14
+ end
15
+ },
16
+ :help => ['timer SEC', 'post reminder after SEC.']
17
+ )
18
+ end
@@ -5,7 +5,7 @@ module Termtter::Client
5
5
 
6
6
  register_hook(
7
7
  :name => :uri_open,
8
- :points => [:post_filter],
8
+ :points => [:output],
9
9
  :exec_proc => lambda {|statuses, event|
10
10
  statuses.each do |s|
11
11
  public_storage[:uris] += s[:text].scan(%r|https?://[^\s]+|)
data/lib/termtter/api.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  # -*- coding: utf-8 -*-
2
+ gem 'rubytter', '>= 0.6.5'
2
3
  require 'rubytter'
3
4
 
4
5
  config.set_default(:host, 'twitter.com')
@@ -5,6 +5,7 @@ require 'logger'
5
5
  module Termtter
6
6
 
7
7
  class CommandNotFound < StandardError; end
8
+ class CommandCanceled < StandardError; end
8
9
 
9
10
  module Client
10
11
 
@@ -17,8 +18,7 @@ module Termtter
17
18
  @since_id = nil
18
19
  @input_thread = nil
19
20
  @task_manager = Termtter::TaskManager.new
20
- config.log.set_default(:logger, nil)
21
- config.log.set_default(:level, nil)
21
+ config.set_default(:logger, nil)
22
22
  config.set_default(:update_interval, 300)
23
23
  config.set_default(:prompt, '> ')
24
24
  config.set_default(:devel, false)
@@ -102,7 +102,10 @@ module Termtter
102
102
  call_hooks(:pre_filter, statuses, event)
103
103
  filtered = apply_filters(statuses, event)
104
104
  call_hooks(:post_filter, filtered, event)
105
- call_hooks(:output, filtered, event)
105
+ get_hooks(:output).each do |hook|
106
+ filtered_for_hook = apply_filters_for_hook(filtered, hook.name)
107
+ hook.call(filtered_for_hook, event)
108
+ end
106
109
  end
107
110
 
108
111
  def apply_filters(statuses, event)
@@ -111,8 +114,12 @@ module Termtter
111
114
  filtered = f.call(filtered, event)
112
115
  end
113
116
  filtered
114
- rescue => e
115
- handle_error(e)
117
+ end
118
+
119
+ def apply_filters_for_hook(statuses, hook_name)
120
+ # TODO
121
+ filtered = statuses.map(&:dup)
122
+ filtered
116
123
  end
117
124
 
118
125
  # return last hook return value
@@ -123,12 +130,6 @@ module Termtter
123
130
  result = hook.call(*args)
124
131
  }
125
132
  result
126
- rescue => e
127
- if point.to_sym == :on_error
128
- raise
129
- else
130
- handle_error(e)
131
- end
132
133
  end
133
134
 
134
135
  def call_commands(text, tw = nil)
@@ -136,26 +137,24 @@ module Termtter
136
137
 
137
138
  command_found = false
138
139
  @commands.each do |key, command|
139
- # match? メソッドがなんかきもちわるいので変える予定
140
- command_str, command_arg = command.match?(text)
141
- if command_str
142
- command_found = true
140
+ next unless command.match?(text)
141
+ command_found = true
142
+ command_str, command_arg = Command.split_command_line(text)
143
143
 
144
- modified_arg = call_hooks(
145
- "modify_arg_for_#{command.name.to_s}",
146
- command_str,
147
- command_arg) || command_arg || ''
144
+ modified_arg = call_hooks(
145
+ "modify_arg_for_#{command.name.to_s}",
146
+ command_str,
147
+ command_arg) || command_arg || ''
148
148
 
149
- @task_manager.invoke_and_wait do
150
-
151
- pre_exec_hook_result = call_hooks("pre_exec_#{command.name.to_s}", command_str, modified_arg)
152
- next if pre_exec_hook_result == false
149
+ @task_manager.invoke_and_wait do
150
+ begin
151
+ call_hooks("pre_exec_#{command.name.to_s}", command_str, modified_arg)
153
152
  # exec command
154
- result = command.call(modified_arg)
153
+ result = command.call(command_str, modified_arg, text)
155
154
  if result
156
155
  call_hooks("post_exec_#{command.name.to_s}", command_str, modified_arg, result)
157
156
  end
158
-
157
+ rescue CommandCanceled
159
158
  end
160
159
  end
161
160
  end
@@ -259,6 +258,8 @@ module Termtter
259
258
  puts 'plese check your account settings'
260
259
  exit!
261
260
  end
261
+ rescue => e
262
+ handle_error(e)
262
263
  end
263
264
  }
264
265
  )
@@ -308,8 +309,7 @@ module Termtter
308
309
  end
309
310
 
310
311
  def setup_logger
311
- @logger = config.log.logger || Logger.new(STDOUT)
312
- @logger.level = config.log.level || Logger::WARN
312
+ @logger = config.logger || Logger.new(STDOUT)
313
313
  end
314
314
 
315
315
  def run
@@ -328,7 +328,7 @@ module Termtter
328
328
  end
329
329
 
330
330
  def handle_error(e)
331
- call_hooks("on_error", e)
331
+ get_hooks(:on_error).each {|hook| hook.call(e) }
332
332
  rescue Exception => e
333
333
  puts "Error: #{e}"
334
334
  puts e.backtrace.join("\n")
@@ -36,7 +36,7 @@ module Termtter
36
36
  end
37
37
 
38
38
  def complement(input)
39
- command_str, command_arg = match?(input)
39
+ command_str, command_arg = Command.split_command_line(input)
40
40
  if command_arg
41
41
  if completion_proc
42
42
  [completion_proc.call(command_str, command_arg || '')].flatten.compact
@@ -48,7 +48,7 @@ module Termtter
48
48
  end
49
49
  end
50
50
 
51
- def call(arg)
51
+ def call(cmd, arg, original_text)
52
52
  arg = case arg
53
53
  when nil
54
54
  ''
@@ -60,14 +60,8 @@ module Termtter
60
60
  exec_proc.call(arg)
61
61
  end
62
62
 
63
- # return array like [command, arg]
64
- # match? メソッドがなんかきもちわるいので変える予定
65
63
  def match?(input)
66
- if pattern =~ input
67
- [$2 || $3, $4] # $2 or $3 => command, $4 => argument
68
- else
69
- [nil, nil]
70
- end
64
+ (pattern =~ input) != nil
71
65
  end
72
66
 
73
67
  def pattern
@@ -78,6 +72,10 @@ module Termtter
78
72
  def commands
79
73
  [name] + aliases
80
74
  end
75
+
76
+ def self.split_command_line(line)
77
+ line.strip.split(/\s+/, 2)
78
+ end
81
79
  end
82
80
  end
83
81
 
data/lib/termtter/hook.rb CHANGED
@@ -24,7 +24,6 @@ module Termtter
24
24
  }.empty?
25
25
  end
26
26
 
27
- # TODO: need spec
28
27
  def call(*args)
29
28
  self.exec_proc.call(*args)
30
29
  end
@@ -1,4 +1,4 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  module Termtter
3
- VERSION = '1.0.6'
3
+ VERSION = '1.0.7'
4
4
  end
@@ -230,5 +230,19 @@ module Termtter
230
230
  end
231
231
  $stderr = old
232
232
  end
233
+
234
+ it 'should cancel command by hook' do
235
+ command = Command.new(:name => :test)
236
+ Client.register_command(command)
237
+ Client.register_hook(
238
+ :name => :test,
239
+ :point => /^pre_exec/,
240
+ :exec => lambda{|*arg|
241
+ raise Termtter::CommandCanceled
242
+ }
243
+ )
244
+ command.should_not_receive(:call)
245
+ Client.call_commands('test')
246
+ end
233
247
  end
234
248
  end
@@ -58,18 +58,18 @@ module Termtter
58
58
 
59
59
  it 'should return command_info when call method "match?"' do
60
60
  [
61
- ['update', ['update', nil]],
62
- ['up', ['up', nil]],
63
- ['u', ['u', nil]],
64
- ['update ', ['update', nil]],
65
- [' update ', [nil, nil]],
66
- ['update foo', ['update', 'foo']],
67
- [' update foo', [nil, nil]],
68
- [' update foo ', [nil, nil]],
69
- ['u foo', ['u', 'foo']],
70
- ['up foo', ['up', 'foo']],
71
- ['upd foo', [nil, nil]],
72
- ['upd foo', [nil, nil]],
61
+ ['update', true],
62
+ ['up', true],
63
+ ['u', true],
64
+ ['update ', true],
65
+ [' update ', false],
66
+ ['update foo', true],
67
+ [' update foo', false],
68
+ [' update foo ', false],
69
+ ['u foo', true],
70
+ ['up foo', true],
71
+ ['upd foo', false],
72
+ ['upd foo', false],
73
73
  ].each do |input, result|
74
74
  @command.match?(input).should == result
75
75
  end
@@ -82,17 +82,25 @@ module Termtter
82
82
  end
83
83
 
84
84
  it 'should call exec_proc when call method "call"' do
85
- @command.call('test').should == 'test'
86
- @command.call(' test').should == ' test'
87
- @command.call(' test ').should == ' test '
88
- @command.call('test test').should == 'test test'
85
+ @command.call('foo', 'test', 'foo test').should == 'test'
86
+ @command.call('foo', ' test', 'foo test').should == ' test'
87
+ @command.call('foo', ' test ', 'foo test ').should == ' test '
88
+ @command.call('foo', 'test test', 'foo test test').should == 'test test'
89
89
  end
90
90
 
91
91
  it 'should raise ArgumentError at call' do
92
- lambda { @command.call(nil) }.should_not raise_error(ArgumentError)
93
- lambda { @command.call('foo') }.should_not raise_error(ArgumentError)
94
- lambda { @command.call(false) }.should raise_error(ArgumentError)
95
- lambda { @command.call(Array.new) }.should raise_error(ArgumentError)
92
+ lambda { @command.call('foo', nil, 'foo') }.should_not raise_error(ArgumentError)
93
+ lambda { @command.call('foo', 'foo', 'foo') }.should_not raise_error(ArgumentError)
94
+ lambda { @command.call('foo', false, 'foo') }.should raise_error(ArgumentError)
95
+ lambda { @command.call('foo', Array.new, 'foo') }.should raise_error(ArgumentError)
96
+ end
97
+
98
+ it 'split command line' do
99
+ Command.split_command_line('test foo bar').should == ['test', 'foo bar']
100
+ Command.split_command_line('test foo bar').should == ['test', 'foo bar']
101
+ Command.split_command_line('test foo bar').should == ['test', 'foo bar']
102
+ Command.split_command_line(' test foo bar').should == ['test', 'foo bar']
103
+ Command.split_command_line(' test foo bar ').should == ['test', 'foo bar']
96
104
  end
97
105
  end
98
106
  end
@@ -9,10 +9,9 @@ module Termtter
9
9
 
10
10
  it 'should match' do
11
11
  hook = Hook.new(
12
- :name => :span,
12
+ :name => :spam,
13
13
  :points => ['foo'],
14
14
  :exec_proc => lambda{|cmd, arg|
15
- puts 'a'
16
15
  }
17
16
  )
18
17
  hook.match?('foo').should == true
@@ -23,10 +22,9 @@ module Termtter
23
22
 
24
23
  it 'should match when multi points' do
25
24
  hook = Hook.new(
26
- :name => :span,
25
+ :name => :spam,
27
26
  :points => ['foo', 'bar'],
28
27
  :exec_proc => lambda{|cmd, arg|
29
- puts 'a'
30
28
  }
31
29
  )
32
30
  hook.match?('foo').should == true
@@ -37,10 +35,9 @@ module Termtter
37
35
 
38
36
  it 'should match when multi points' do
39
37
  hook = Hook.new(
40
- :name => :span,
38
+ :name => :spam,
41
39
  :points => ['foo', /bar/],
42
40
  :exec_proc => lambda{|cmd, arg|
43
- puts 'a'
44
41
  }
45
42
  )
46
43
  hook.match?('foo').should == true
@@ -52,10 +49,9 @@ module Termtter
52
49
 
53
50
  it 'should match when multi points' do
54
51
  hook = Hook.new(
55
- :name => :span,
52
+ :name => :spam,
56
53
  :points => ['foo', /^bar/],
57
54
  :exec_proc => lambda{|cmd, arg|
58
- puts 'a'
59
55
  }
60
56
  )
61
57
  hook.match?('bar').should == true
@@ -65,5 +61,18 @@ module Termtter
65
61
  hook.match?(:'bar_').should == true
66
62
  hook.match?(:'_bar_').should == false
67
63
  end
64
+
65
+ it 'call hook proc' do
66
+ proc_args = nil
67
+ hook = Hook.new(
68
+ :name => :spam,
69
+ :points => ['foo'],
70
+ :exec_proc => lambda{|*args|
71
+ proc_args = args
72
+ }
73
+ )
74
+ hook.call('foo', 'bar')
75
+ proc_args.should == ['foo', 'bar']
76
+ end
68
77
  end
69
78
  end
@@ -4,14 +4,12 @@ require File.dirname(__FILE__) + '/spec_helper'
4
4
 
5
5
  describe Termtter, 'when plugin is called (without init option)' do
6
6
  it 'should require global plugin if exist' do
7
- File.should_receive(:exist?).and_return(false)
8
- should_receive(:require).with('plugins/aaa')
7
+ should_receive(:load).with('plugins/aaa.rb')
9
8
  plugin 'aaa'
10
9
  end
11
10
 
12
11
  it 'should require user plugin if not exist' do
13
- File.should_receive(:exist?).and_return(true)
14
- should_receive(:require).with(File.expand_path('~/.termtter/plugins/aaa'))
12
+ should_receive(:load).with('plugins/aaa.rb')
15
13
  plugin 'aaa'
16
14
  end
17
15
 
@@ -23,7 +21,7 @@ end
23
21
 
24
22
  describe Termtter, 'when plugin is called (with init option)' do
25
23
  it 'init option will become config' do
26
- should_receive(:require)
24
+ should_receive(:load)
27
25
 
28
26
  plugin 'aaa', :bbb => :ccc
29
27
  config.plugins.aaa.bbb.should == :ccc
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: termtter
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.6
4
+ version: 1.0.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - jugyo
@@ -10,7 +10,7 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
 
13
- date: 2009-03-22 00:00:00 +09:00
13
+ date: 2009-03-26 00:00:00 +09:00
14
14
  default_executable:
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
@@ -51,17 +51,7 @@ dependencies:
51
51
  requirements:
52
52
  - - ">="
53
53
  - !ruby/object:Gem::Version
54
- version: 0.6.3
55
- version:
56
- - !ruby/object:Gem::Dependency
57
- name: sqlite3-ruby
58
- type: :runtime
59
- version_requirement:
60
- version_requirements: !ruby/object:Gem::Requirement
61
- requirements:
62
- - - ">="
63
- - !ruby/object:Gem::Version
64
- version: 1.2.4
54
+ version: 0.6.5
65
55
  version:
66
56
  description: Termtter is a terminal based Twitter client
67
57
  email: jugyo.org@gmail.com
@@ -103,6 +93,7 @@ files:
103
93
  - lib/plugins/msagent.rb
104
94
  - lib/plugins/multi_reply.rb
105
95
  - lib/plugins/notify-send.rb
96
+ - lib/plugins/notify-send2.rb
106
97
  - lib/plugins/otsune.rb
107
98
  - lib/plugins/outputz.rb
108
99
  - lib/plugins/pause.rb
@@ -130,10 +121,10 @@ files:
130
121
  - lib/plugins/storage/status_mook.rb
131
122
  - lib/plugins/storage.rb
132
123
  - lib/plugins/system_status.rb
124
+ - lib/plugins/timer.rb
133
125
  - lib/plugins/translation.rb
134
126
  - lib/plugins/update_editor.rb
135
127
  - lib/plugins/uri-open.rb
136
- - lib/plugins/url_addspace.rb
137
128
  - lib/plugins/wassr_post.rb
138
129
  - lib/plugins/yhara.rb
139
130
  - lib/plugins/yhara_filter.rb
@@ -1,16 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
-
3
- module Termtter::Client
4
- add_filter do |statuses, event|
5
- statuses.each do |s|
6
- s.text.gsub!(/(\S)(https?:\/\/)/, '\1 \2')
7
- end
8
- statuses
9
- end
10
- end
11
-
12
- # url_addspace
13
- # add space before URL without space
14
- # example:
15
- # before: ABCDEhttp://~~~
16
- # after: ABCDE http://~~~