jugyo-termtter 0.5.8 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc CHANGED
@@ -42,7 +42,7 @@ To update the config, just restart your termtter proccess.
42
42
 
43
43
  (The MIT License)
44
44
 
45
- Copyright (c) 2008 FIXME full name
45
+ Copyright (c) 2008-2009 The Termtter Development Team
46
46
 
47
47
  Permission is hereby granted, free of charge, to any person obtaining
48
48
  a copy of this software and associated documentation files (the
data/Rakefile CHANGED
@@ -28,3 +28,11 @@ Dir['tasks/**/*.rake'].each { |t| load t }
28
28
 
29
29
  # TODO - want other tests/tasks run by default? Add them to the list
30
30
  # task :default => [:spec, :features]
31
+
32
+ require 'spec/rake/spectask'
33
+ desc 'run all specs'
34
+ Spec::Rake::SpecTask.new do |t|
35
+ t.spec_files = FileList['spec/**/*_spec.rb']
36
+ t.spec_opts = ['-c']
37
+ end
38
+
@@ -1,51 +1,60 @@
1
+ require 'erb'
2
+
1
3
  Termtter::Client.clear_hooks # FIXME: not to clear all but to clear just stdout.rb
2
4
 
5
+ configatron.set_default(
6
+ :timeline_format,
7
+ '<%= color(time, 90) %> <%= color(status, status_color) %> <%= color(id, 90) %>')
8
+
3
9
  # english? :: String -> Boolean
4
10
  def english?(message)
5
- /[一-龠]+|[ぁ-ん]+|[ァ-ヴー]+|[a-zA-Z0-9]+/ !~ message
11
+ /[��-��]+|[��-��]+|[��-����]+|[��-����-�ڣ�-��]+/ !~ message
12
+ end
13
+
14
+ def color(str, num)
15
+ "\e[#{num}m#{str}\e[0m"
6
16
  end
7
17
 
8
18
  # FIXME: The code below is a copy from stdout.rb so it's not DRY. DRY it.
19
+
9
20
  Termtter::Client.add_hook do |statuses, event|
10
21
  colors = %w(0 31 32 33 34 35 36 91 92 93 94 95 96)
11
22
 
12
23
  case event
13
24
  when :update_friends_timeline, :list_friends_timeline, :list_user_timeline, :show, :replies
14
25
  unless statuses.empty?
15
- if event == :update_friends_timeline then statuses = statuses.reverse end
26
+ statuses.reverse! if event == :update_friends_timeline
16
27
  statuses.each do |s|
17
28
  text = s.text.gsub("\n", '')
18
29
  next unless english?(text) # if you substitute "if" for "unless", this script will be "japanese.rb"
19
- color_num = colors[s.user_screen_name.hash % colors.size]
30
+ status_color = colors[s.user_screen_name.hash % colors.size]
20
31
  status = "#{s.user_screen_name}: #{text}"
21
32
  if s.in_reply_to_status_id
22
33
  status += " (reply to #{s.in_reply_to_status_id})"
23
34
  end
24
35
 
25
- case event
26
- when :update_friends_timeline, :list_friends_timeline
27
- time_format = '%H:%M:%S'
28
- else
29
- time_format = '%m-%d %H:%d'
30
- end
31
- time_str = "(#{s.created_at.strftime(time_format)})"
36
+ time_format = case event
37
+ when :update_friends_timeline, :list_friends_timeline
38
+ '%H:%M:%S'
39
+ else
40
+ '%m-%d %H:%M'
41
+ end
42
+ time = "(#{s.created_at.strftime(time_format)})"
43
+
44
+ id = s.id
32
45
 
33
- puts "#{color(time_str, 90)} #{color(status, color_num)}"
46
+ puts ERB.new(configatron.timeline_format).result(binding)
34
47
  end
35
48
  end
36
49
  when :search
37
50
  statuses.each do |s|
38
51
  text = s.text.gsub("\n", '')
39
- color_num = colors[s.user_screen_name.hash % colors.size]
40
- status = "#{s.user_screen_name}: #{text}"
41
- time_str = "(#{s.created_at.strftime('%m-%d %H:%d')})"
52
+ status_color = colors[s.user_screen_name.hash % colors.size]
42
53
 
43
- puts "#{color(time_str, 90)} #{color(status, color_num)}"
54
+ status = "#{s.user_screen_name}: #{text}"
55
+ time = "(#{s.created_at.strftime('%m-%d %H:%M')})"
56
+ id = s.id
57
+ puts ERB.new(configatron.timeline_format).result(binding)
44
58
  end
45
59
  end
46
60
  end
47
-
48
- # USAGE:
49
- # Write the line on the *first line* of your ~/.termtter
50
- # plugin 'english'
51
- # (english.rb will destroy plugins which were required before)
@@ -1,4 +1,6 @@
1
1
  module Termtter::Client
2
+ add_help 'favorite,fav ID', 'Favorite a status'
3
+
2
4
  add_command %r'^(?:favorite|fav)\s+(\d+)$' do |m,t|
3
5
  id = m[1]
4
6
  res = t.favorite(id)
@@ -10,6 +12,8 @@ module Termtter::Client
10
12
  end
11
13
 
12
14
  if public_storage[:log]
15
+ add_help 'favorite,fav /word', 'Favorite a status by searching'
16
+
13
17
  add_command %r'^(?:favorite|fav)\s+/(.+)$' do |m,t|
14
18
  pat = Regexp.new(m[1])
15
19
  statuses = public_storage[:log].select { |s| s.text =~ pat }
@@ -32,15 +36,9 @@ module Termtter
32
36
  class Twitter
33
37
  def favorite(id)
34
38
  uri = "http://twitter.com/favourings/create/#{id}.json"
35
- req = Net::HTTP::Post.new(uri)
36
- req.basic_auth(@user_name, @password)
37
- req.add_field('User-Agent', 'Termtter http://github.com/jugyo/termtter')
38
- req.add_field('X-Twitter-Client', 'Termtter')
39
- req.add_field('X-Twitter-Client-URL', 'http://github.com/jugyo/termtter')
40
- req.add_field('X-Twitter-Client-Version', '0.1')
41
39
 
42
40
  Net::HTTP.start('twitter.com', 80) do |http|
43
- http.request(req)
41
+ http.request(post_request(uri))
44
42
  end
45
43
  end
46
44
  end
data/lib/plugin/growl.rb CHANGED
@@ -1,29 +1,48 @@
1
1
  require 'tmpdir'
2
+ require 'open-uri'
2
3
  require 'uri'
3
4
 
5
+ configatron.plugins.growl.set_default(:icon_cache_dir, "#{Dir.tmpdir}/termtter-icon-cache-dir")
6
+ Dir.mkdir_p(configatron.plugins.growl.icon_cache_dir) unless File.exist?(configatron.plugins.growl.icon_cache_dir)
7
+
4
8
  def get_icon_path(s)
5
- cache_dir = "#{Dir.tmpdir}/termtter-icon-cache-dir"
6
- cache_file = "#{cache_dir}/#{s.user_id}"
7
- unless File.exist?(cache_file)
8
- Dir.mkdir(cache_dir) unless File.exist?(cache_dir)
9
- buf = ""
10
- File.open(URI.encode(s.user_profile_image_url)) do |f|
11
- buf = f.read
9
+ cache_file = "%s/%s%s" % [ configatron.plugins.growl.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
+ return cache_file
14
+ else
15
+ Thread.new do
16
+ File.open(cache_file, "wb") do |f|
17
+ f << open(URI.escape(s.user_profile_image_url)).read
18
+ end
12
19
  end
13
- File.open(cache_file, "w") do |f|
14
- f.write(buf)
20
+ return nil
21
+ end
22
+ end
23
+
24
+ queue = []
25
+ Thread.new do
26
+ loop do
27
+ begin
28
+ if s = queue.pop
29
+ arg = ['growlnotify', s.user_screen_name, '-m', s.text.gsub("\n",''), '-n', 'termtter']
30
+ #icon_path = get_icon_path(s)
31
+ #arg += ['--image', icon_path] if icon_path
32
+ system *arg
33
+ end
34
+ rescue => e
35
+ puts e
36
+ puts e.backtrace.join("\n")
15
37
  end
38
+ sleep 0.1
16
39
  end
17
- cache_file
18
40
  end
19
41
 
20
42
  Termtter::Client.add_hook do |statuses, event|
21
43
  if !statuses.empty? && event == :update_friends_timeline
22
44
  statuses.reverse.each do |s|
23
- text = s.text.gsub("\n",'')
24
- icon_path = get_icon_path(s)
25
- system 'growlnotify', s.user_screen_name, '-m', text,
26
- '-n', 'termtter', '--image', icon_path
45
+ queue << s
27
46
  end
28
47
  end
29
48
  end
data/lib/plugin/log.rb CHANGED
@@ -1,6 +1,8 @@
1
1
  module Termtter::Client
2
2
  public_storage[:log] = []
3
3
 
4
+ add_help '/word', 'Search log'
5
+
4
6
  add_hook do |statuses,event|
5
7
  case event
6
8
  when :update_friends_timeline
data/lib/plugin/plugin.rb CHANGED
@@ -1,6 +1,39 @@
1
1
  module Termtter::Client
2
+
3
+ public_storage[:plugins] = Dir["#{File.dirname(__FILE__)}/*.rb"].map do |f|
4
+ f.match(%r|([^/]+).rb$|)[1]
5
+ end
6
+
7
+ add_help 'plugin FILE', 'Load a plugin'
2
8
  add_command /^plugin\s+(.*)/ do |m, t|
3
- plugin m[1]
9
+ begin
10
+ result = plugin m[1].strip
11
+ rescue LoadError
12
+ ensure
13
+ puts "=> #{result.inspect}"
14
+ end
15
+ end
16
+
17
+ add_help 'plugins', 'Show list of plugins'
18
+ add_command /^plugins$/ do |m, t|
19
+ puts public_storage[:plugins].join("\n")
20
+ end
21
+
22
+ def self.find_plugin_candidates(a, b)
23
+ if a.empty?
24
+ public_storage[:plugins].to_a
25
+ else
26
+ public_storage[:plugins].grep(/^#{Regexp.quote a}/i)
27
+ end.
28
+ map {|u| b % u }
29
+ end
30
+
31
+ add_completion do |input|
32
+ if input =~ /^(plugin)\s+(.*)/
33
+ find_plugin_candidates $2, "#{$1} %s"
34
+ else
35
+ %w[ plugin plugins ].grep(/^#{Regexp.quote input}/)
36
+ end
4
37
  end
5
38
  end
6
39
 
@@ -10,5 +43,6 @@ end
10
43
  # > u <%= not erbed %>
11
44
  # => <%= not erbed %>
12
45
  # > plugin erb
46
+ # => true
13
47
  # > u <%= 1 + 2 %>
14
48
  # => 3
@@ -57,6 +57,7 @@ replies,r List the most recent @replies for the authenticating user
57
57
  search,s TEXT Search for Twitter
58
58
  show ID Show a single status
59
59
  EOS
60
+ puts formatted_help unless @@helps.empty?
60
61
  end
61
62
 
62
63
  add_command /^eval\s+(.*)$/ do |m, t|
@@ -68,6 +69,15 @@ show ID Show a single status
68
69
  end
69
70
  end
70
71
 
72
+ def self.formatted_help
73
+ width = @@helps.map {|n, d| n.size }.max
74
+ space = 3
75
+ "\nuser commands:\n" +
76
+ @@helps.map {|name, desc|
77
+ name.to_s.ljust(width + space) + desc.to_s
78
+ }.join("\n")
79
+ end
80
+
71
81
  # completion for standard commands
72
82
 
73
83
  require 'set'
data/lib/plugin/stdout.rb CHANGED
@@ -1,6 +1,8 @@
1
1
  require 'erb'
2
2
 
3
- configatron.set_default(:timeline_format, '<%= color(time, 90) %> <%= status %> <%= color(id, 90) %>')
3
+ configatron.set_default(
4
+ :timeline_format,
5
+ '<%= color(time, 90) %> <%= color(status, status_color) %> <%= color(id, 90) %>')
4
6
 
5
7
  def color(str, num)
6
8
  "\e[#{num}m#{str}\e[0m"
@@ -12,34 +14,34 @@ Termtter::Client.add_hook do |statuses, event|
12
14
  case event
13
15
  when :update_friends_timeline, :list_friends_timeline, :list_user_timeline, :show, :replies
14
16
  unless statuses.empty?
15
- if event == :update_friends_timeline then statuses = statuses.reverse end
17
+ statuses.reverse! if event == :update_friends_timeline
16
18
  statuses.each do |s|
17
19
  text = s.text.gsub("\n", '')
18
- color_num = colors[s.user_screen_name.hash % colors.size]
20
+ status_color = colors[s.user_screen_name.hash % colors.size]
19
21
  status = "#{s.user_screen_name}: #{text}"
20
22
  if s.in_reply_to_status_id
21
23
  status += " (reply to #{s.in_reply_to_status_id})"
22
24
  end
23
25
 
24
- case event
25
- when :update_friends_timeline, :list_friends_timeline
26
- time_format = '%H:%M:%S'
27
- else
28
- time_format = '%m-%d %H:%M'
29
- end
30
-
26
+ time_format = case event
27
+ when :update_friends_timeline, :list_friends_timeline
28
+ '%H:%M:%S'
29
+ else
30
+ '%m-%d %H:%M'
31
+ end
31
32
  time = "(#{s.created_at.strftime(time_format)})"
32
- status = color(status, color_num)
33
+
33
34
  id = s.id
35
+
34
36
  puts ERB.new(configatron.timeline_format).result(binding)
35
37
  end
36
38
  end
37
39
  when :search
38
40
  statuses.each do |s|
39
41
  text = s.text.gsub("\n", '')
40
- color_num = colors[s.user_screen_name.hash % colors.size]
42
+ status_color = colors[s.user_screen_name.hash % colors.size]
41
43
 
42
- status = color("#{s.user_screen_name}: #{text}", color_num)
44
+ status = "#{s.user_screen_name}: #{text}"
43
45
  time = "(#{s.created_at.strftime('%m-%d %H:%M')})"
44
46
  id = s.id
45
47
  puts ERB.new(configatron.timeline_format).result(binding)
data/lib/termtter.rb CHANGED
@@ -7,6 +7,12 @@ require 'enumerator'
7
7
  require 'parsedate'
8
8
  require 'configatron'
9
9
 
10
+ if RUBY_VERSION < '1.8.7'
11
+ class Array
12
+ def take(n) at(0...n) end
13
+ end
14
+ end
15
+
10
16
  $:.unshift(File.dirname(__FILE__)) unless
11
17
  $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
12
18
 
@@ -22,14 +28,15 @@ alias original_require require
22
28
  def require(s)
23
29
  if %r|^termtter/(.*)| =~ s
24
30
  puts "[WARNING] use plugin '#{$1}' instead of require"
25
- original_require "plugin/#{$1}"
26
- else
27
- original_require s
31
+ puts " Such a legacy .termtter file will not be supported until version 1.0.0"
32
+ s = "plugin/#{$1}"
28
33
  end
34
+ original_require s
29
35
  end
30
36
 
31
37
  module Termtter
32
- VERSION = '0.5.8'
38
+ VERSION = '0.6.0'
39
+ APP_NAME = 'termtter'
33
40
 
34
41
  class Twitter
35
42
 
@@ -39,14 +46,9 @@ module Termtter
39
46
  end
40
47
 
41
48
  def update_status(status)
42
- req = Net::HTTP::Post.new('/statuses/update.xml')
43
- req.basic_auth(@user_name, @password)
44
- req.add_field("User-Agent", "Termtter http://github.com/jugyo/termtter")
45
- req.add_field("X-Twitter-Client", "Termtter")
46
- req.add_field("X-Twitter-Client-URL", "http://github.com/jugyo/termtter")
47
- req.add_field("X-Twitter-Client-Version", "0.1")
48
49
  Net::HTTP.start("twitter.com", 80) do |http|
49
- http.request(req, "status=#{CGI.escape(status)}")
50
+ uri = '/statuses/update.xml'
51
+ http.request(post_request(uri), "status=#{CGI.escape(status)}&source=#{APP_NAME}")
50
52
  end
51
53
  end
52
54
 
@@ -58,6 +60,11 @@ module Termtter
58
60
 
59
61
  def get_user_timeline(screen_name)
60
62
  return get_timeline("http://twitter.com/statuses/user_timeline/#{screen_name}.json")
63
+ rescue OpenURI::HTTPError => e
64
+ puts "No such user: #{screen_name}"
65
+ nears = near_users(screen_name)
66
+ puts "near users: #{nears}" unless nears.empty?
67
+ return {}
61
68
  end
62
69
 
63
70
  def search(query)
@@ -65,7 +72,7 @@ module Termtter
65
72
  return results.map do |s|
66
73
  status = Status.new
67
74
  status.id = s['id']
68
- status.text = s['text']
75
+ status.text = CGI.unescapeHTML(s['text'])
69
76
  status.created_at = Time.utc(*ParseDate::parsedate(s["created_at"])).localtime
70
77
  status.user_screen_name = s['from_user']
71
78
  status
@@ -92,9 +99,27 @@ module Termtter
92
99
  %w(id name screen_name url profile_image_url).each do |key|
93
100
  status.__send__("user_#{key}=".to_sym, s["user"][key])
94
101
  end
102
+ status.text = CGI.unescapeHTML(status.text)
95
103
  status
96
104
  end
97
105
  end
106
+
107
+ def near_users(screen_name)
108
+ Client::public_storage[:users].select {|user|
109
+ /#{user}/i =~ screen_name || /#{screen_name}/i =~ user
110
+ }.join(', ')
111
+ end
112
+ private :near_users
113
+
114
+ def post_request(uri)
115
+ req = Net::HTTP::Post.new(uri)
116
+ req.basic_auth(@user_name, @password)
117
+ req.add_field('User-Agent', 'Termtter http://github.com/jugyo/termtter')
118
+ req.add_field('X-Twitter-Client', 'Termtter')
119
+ req.add_field('X-Twitter-Client-URL', 'http://github.com/jugyo/termtter')
120
+ req.add_field('X-Twitter-Client-Version', '0.1')
121
+ req
122
+ end
98
123
  end
99
124
 
100
125
  module Client
@@ -102,6 +127,7 @@ module Termtter
102
127
  @@hooks = []
103
128
  @@commands = {}
104
129
  @@completions = []
130
+ @@helps = []
105
131
 
106
132
  class << self
107
133
  def add_hook(&hook)
@@ -128,6 +154,14 @@ module Termtter
128
154
  @@completions.clear
129
155
  end
130
156
 
157
+ def add_help(name, desc)
158
+ @@helps << [name, desc]
159
+ end
160
+
161
+ def clear_helps
162
+ @@helps.clear
163
+ end
164
+
131
165
  Readline.basic_word_break_characters= "\t\n\"\\'`><=;|&{("
132
166
  Readline.completion_proc = proc {|input|
133
167
  @@completions.map {|completion|
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: 0.5.8
4
+ version: 0.6.0
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-01-06 00:00:00 -08:00
13
+ date: 2009-01-07 00:00:00 -08:00
14
14
  default_executable: termtter
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency