jugyo-termtter 1.0.6 → 1.0.7

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.
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.4")
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
@@ -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
@@ -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
@@ -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: jugyo-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
@@ -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.4
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://~~~