termtter 1.5.0 → 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (86) hide show
  1. data/Rakefile +2 -1
  2. data/bin/termtter +1 -0
  3. data/lib/plugins/another_prompt.rb +131 -0
  4. data/lib/plugins/async.rb +23 -0
  5. data/lib/plugins/confirm.rb +1 -1
  6. data/lib/plugins/db.rb +1 -1
  7. data/lib/plugins/defaults/auto_reload.rb +20 -19
  8. data/lib/plugins/defaults/command_line.rb +10 -5
  9. data/lib/plugins/defaults/hashtag.rb +35 -0
  10. data/lib/plugins/defaults/lists.rb +14 -0
  11. data/lib/plugins/defaults/retweet.rb +15 -15
  12. data/lib/plugins/defaults/standard_commands.rb +22 -28
  13. data/lib/plugins/defaults/standard_completion.rb +5 -5
  14. data/lib/plugins/defaults/switch.rb +34 -0
  15. data/lib/plugins/eject.rb +15 -0
  16. data/lib/plugins/expand-tinyurl.rb +1 -1
  17. data/lib/plugins/favotter.rb +77 -0
  18. data/lib/plugins/friends.rb +50 -0
  19. data/lib/plugins/g.rb +16 -0
  20. data/lib/plugins/gsub.rb +17 -0
  21. data/lib/plugins/haml.rb +55 -0
  22. data/lib/plugins/hatebu_and_update.rb +2 -2
  23. data/lib/plugins/history.rb +9 -0
  24. data/lib/plugins/irc_gw.rb +11 -4
  25. data/lib/plugins/linefeed.rb +31 -0
  26. data/lib/plugins/md5pass.rb +42 -0
  27. data/lib/plugins/outputz.rb +1 -1
  28. data/lib/plugins/primes.rb +1 -1
  29. data/lib/plugins/quote.rb +43 -0
  30. data/lib/plugins/reduce_text.rb +26 -0
  31. data/lib/plugins/reverse.rb +7 -6
  32. data/lib/plugins/source.rb +31 -0
  33. data/lib/plugins/storage/status.rb +2 -2
  34. data/lib/plugins/storage.rb +1 -1
  35. data/lib/plugins/stream.rb +192 -0
  36. data/lib/plugins/switch_user.rb +1 -22
  37. data/lib/plugins/truncate.rb +29 -0
  38. data/lib/plugins/uri-open.rb +23 -9
  39. data/lib/plugins/w3mimg.rb +76 -0
  40. data/lib/termtter/active_rubytter.rb +8 -0
  41. data/lib/termtter/api.rb +37 -13
  42. data/lib/termtter/client.rb +26 -47
  43. data/lib/termtter/command.rb +15 -9
  44. data/lib/termtter/config.rb +6 -2
  45. data/lib/termtter/hookable.rb +59 -0
  46. data/lib/termtter/optparse.rb +51 -39
  47. data/lib/termtter/rubytter_proxy.rb +32 -0
  48. data/lib/termtter/system_extensions/core_compatibles.rb +16 -0
  49. data/lib/termtter/system_extensions/termtter_compatibles.rb +19 -0
  50. data/lib/termtter/system_extensions/windows.rb +86 -0
  51. data/lib/termtter/system_extensions.rb +8 -121
  52. data/lib/termtter/task_manager.rb +4 -10
  53. data/lib/termtter/version.rb +1 -1
  54. data/lib/termtter.rb +5 -3
  55. data/spec/plugins/defaults/hashtag_spec.rb +41 -0
  56. data/spec/plugins/defaults/lists_spec.rb +34 -0
  57. data/spec/plugins/{english_spec.rb → english_spec_.rb} +0 -0
  58. data/spec/plugins/{filter_spec.rb → filter_spec_.rb} +0 -0
  59. data/spec/plugins/gsub_spec.rb +18 -0
  60. data/spec/plugins/haml_spec.rb +134 -0
  61. data/spec/plugins/md5pass_spec.rb +13 -0
  62. data/spec/plugins/{primes_spec.rb → primes_spec_.rb} +0 -0
  63. data/spec/plugins/{sl_spec.rb → sl_spec_.rb} +0 -0
  64. data/spec/plugins/standard_commands_spec.rb +1 -1
  65. data/spec/plugins/storage/{DB_spec.rb → DB_spec_.rb} +0 -0
  66. data/spec/plugins/storage/{status_spec.rb → status_spec_.rb} +0 -0
  67. data/spec/plugins/truncate_spec.rb +27 -0
  68. data/spec/plugins/whois_spec_.rb +20 -0
  69. data/spec/spec_helper.rb +25 -0
  70. data/spec/termtter/active_rubytter_spec.rb +17 -0
  71. data/spec/termtter/api_spec.rb +107 -0
  72. data/spec/termtter/client_spec.rb +262 -73
  73. data/spec/termtter/command_spec.rb +31 -5
  74. data/spec/termtter/config_setup_spec.rb +19 -0
  75. data/spec/termtter/config_spec.rb +57 -27
  76. data/spec/termtter/hook_spec.rb +12 -0
  77. data/spec/termtter/hookable_spec.rb +53 -0
  78. data/spec/termtter/optparse_spec.rb +64 -9
  79. data/spec/termtter/rubytter_proxy_spec.rb +42 -0
  80. data/spec/termtter/system_extensions/windows_spec.rb +9 -0
  81. data/spec/termtter/system_extensions_spec.rb +61 -0
  82. data/spec/termtter/task_manager_spec.rb +58 -0
  83. data/spec/termtter_spec.rb +11 -0
  84. metadata +45 -20
  85. data/lib/termtter/connection.rb +0 -41
  86. data/spec/plugins/whois_spec.rb +0 -26
@@ -0,0 +1,50 @@
1
+ # -*- coding: utf-8 -*-
2
+ module Termtter::Client
3
+
4
+ class << self
5
+ def get_friends(user_name, max)
6
+ friends = []
7
+ page = 0
8
+ begin
9
+ friends += tmp = Termtter::API::twitter.friends(user_name,
10
+ :page => page += 1)
11
+ puts "#{friends.length}/#{max}"
12
+ rescue
13
+ end until (tmp.empty? or friends.length > max)
14
+ friends.take(max)
15
+ end
16
+ end
17
+
18
+ register_command(
19
+ :name => :friends, :aliases => [:following],
20
+ :exec_proc => lambda {|arg|
21
+ user_name = arg.empty? ? config.user_name : arg
22
+ public_storage[:friends] = friends = get_friends(user_name, 2000)
23
+ puts friends.map(&:screen_name).join(' ')
24
+ },
25
+ :help => ["friends,following [USERNAME]", "Show user's friends."]
26
+ )
27
+
28
+ register_command(
29
+ :name => :diff_follow, :aliases => [:diff],
30
+ :exec_proc => lambda {|arg|
31
+ user_name = arg.empty? ? config.user_name : arg
32
+ friends = public_storage[:friends]
33
+ followers = public_storage[:followers]
34
+ if friends.nil? || followers.nil?
35
+ puts 'Do followers and friends first.'
36
+ return
37
+ end
38
+ friends = friends.map(&:screen_name)
39
+ followers = followers.map(&:screen_name)
40
+ puts "friends - followers:"
41
+ puts (friends - followers).map{|s|"http://#{config.host}/#{s}"}.join("\n")
42
+ puts
43
+ puts "followers - friends:"
44
+ puts (followers - friends).map{|s|"http://#{config.host}/#{s}"}.join("\n")
45
+ },
46
+ :help => ["diff_follow,diff",
47
+ "Show difference between frineds and followers."]
48
+ )
49
+
50
+ end
data/lib/plugins/g.rb ADDED
@@ -0,0 +1,16 @@
1
+ module Termtter::Client
2
+ begin
3
+ require 'g'
4
+ register_command(
5
+ :name => :g,
6
+ :help => ['g obj', "Do you know 'g'? It's like 'p'."],
7
+ :exec_proc => lambda {|arg|
8
+ # we get arg as String, so without eval() it is not very useful
9
+ # but we shouldn't blindly eval user-supplied string like this, either
10
+ g eval(arg)
11
+ }
12
+ )
13
+ rescue e
14
+ handle_error(e)
15
+ end
16
+ end
@@ -0,0 +1,17 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ config.plugins.gsub.set_default(:table, [
4
+ [/([★☆△▽…□♪♬])(?=\S)/, '\1 '],
5
+ [/(?=\S)(https?:\/\/)/, ' \1'],
6
+ ])
7
+
8
+ Termtter::Client.register_hook(
9
+ :name => :gsub,
10
+ :point => :filter_for_output,
11
+ :exec => lambda {|statuses, event|
12
+ statuses.each do |s|
13
+ t = s.text
14
+ config.plugins.gsub.table.each {|a, b| t.gsub!(a, b || '') }
15
+ end
16
+ }
17
+ )
@@ -0,0 +1,55 @@
1
+ # coding: utf-8
2
+
3
+ require 'haml'
4
+ require 'tempfile'
5
+
6
+ module Termtter::Plugins
7
+ class Haml
8
+ def initialize(config = Termtter::Config.instance, logger = Termtter::Client.logger)
9
+ @config, @logger = config, logger
10
+
11
+ plugin_config.set_default :options, {}
12
+ end
13
+
14
+ def plugin_config
15
+ @config.plugins.haml
16
+ end
17
+
18
+ def run(arg)
19
+ begin
20
+ hamlified = haml(arg)
21
+ rescue => e
22
+ @logger.error e
23
+ return
24
+ end
25
+
26
+ return if hamlified.nil? || hamlified.empty?
27
+
28
+ Termtter::API.twitter.update(hamlified)
29
+ puts "=> #{hamlified}"
30
+ end
31
+
32
+ def haml(format)
33
+ return unless input = editor(:haml)
34
+
35
+ opts = plugin_config.options.merge(format.empty? ? {} : {:format => format.to_sym})
36
+ ::Haml::Engine.new(input, opts).render.chomp
37
+ end
38
+
39
+ def editor(extname)
40
+ unless cmd = ENV['VISUAL'] || ENV['EDITOR']
41
+ raise 'Please set VISUAL or EDITOR variable.'
42
+ end
43
+
44
+ # XXX: works only in Ruby 1.8.7 or later
45
+ Tempfile.open(['tmp', ".#{extname}"]) do |f|
46
+ system cmd, f.path
47
+ return f.read
48
+ end
49
+ end
50
+ end
51
+ end
52
+
53
+ Termtter::Client.register_command(:haml) do |arg|
54
+ Termtter::Plugins::Haml.new.run(arg)
55
+ end
@@ -32,12 +32,12 @@ module Termtter::Client
32
32
  auth.authorize(req)
33
33
  title = nil
34
34
  tinyurl = nil
35
- Termtter::API.connection.start('b.hatena.ne.jp', 80) do |http|
35
+ Net::HTTP.start('b.hatena.ne.jp', 80) do |http|
36
36
  res = http.request(req)
37
37
  title = Nokogiri::XML(res.body).search('link[title]').first['title'] rescue nil
38
38
  end
39
39
  if title
40
- Termtter::API.connection.start('tinyurl.com', 80) do |http|
40
+ Net::HTTP.start('tinyurl.com', 80) do |http|
41
41
  tinyurl = http.get('/api-create.php?url=' + URI.escape(url)).body
42
42
  end
43
43
 
@@ -21,6 +21,15 @@ module Termtter::Client
21
21
  if File.exist?(filename)
22
22
  begin
23
23
  history = Marshal.load Zlib::Inflate.inflate(File.read(filename))
24
+ rescue Zlib::BufError => e
25
+ ui = create_highline
26
+ delete = ui.ask("Unable to read #{filename}. Do you wish to remove it?")
27
+ if delete =~ /^y/i
28
+ if File.delete(filename) > 1
29
+ puts "Removed #{filename}"
30
+ end
31
+ end
32
+ history = nil
24
33
  end
25
34
  if history
26
35
  keys.each do |key|
@@ -8,7 +8,7 @@ config.plugins.irc_gw.set_default(:logger_level, Logger::ERROR)
8
8
 
9
9
  module Termtter::Client
10
10
  class << self
11
- def friends
11
+ def following_friends
12
12
  user_name = config.user_name
13
13
 
14
14
  frinends = []
@@ -92,8 +92,15 @@ class TermtterIrcGateway < Net::IRC::Server::Session
92
92
 
93
93
  def on_privmsg(m)
94
94
  target, message = *m.params
95
- Termtter::Client.call_commands('update ' + message)
96
- post @prefix, TOPIC, main_channel, message
95
+ if message =~ / +\//
96
+ termtter_command = message.gsub(/ +\//, '')
97
+ return if Termtter::Client.find_commands(termtter_command).empty?
98
+ post '#termtter', NOTICE, main_channel, '> ' + termtter_command
99
+ Termtter::Client.call_commands(termtter_command)
100
+ else
101
+ Termtter::Client.call_commands('update ' + message)
102
+ post @prefix, TOPIC, main_channel, message
103
+ end
97
104
  rescue Exception => e
98
105
  post '#termtter', NOTICE, main_channel, "#{e.class.to_s}: #{e.message}"
99
106
  Termtter::Client.handle_error(e)
@@ -108,7 +115,7 @@ class TermtterIrcGateway < Net::IRC::Server::Session
108
115
  def check_friends
109
116
  params = []
110
117
  max_params_count = 3
111
- Termtter::Client.friends.each do |name|
118
+ Termtter::Client.following_friends.each do |name|
112
119
  prefix = Prefix.new("#{name}!#{name}@localhost")
113
120
  post prefix, JOIN, main_channel
114
121
  params << prefix.nick
@@ -0,0 +1,31 @@
1
+ # -*- coding: utf-8 -*-
2
+ config.plugins.linefeed.set_default(:width, 50)
3
+ config.plugins.linefeed.set_default(:right, 10)
4
+
5
+ Termtter::Client.register_hook(
6
+ :name => :linefeed,
7
+ :point => :filter_for_output,
8
+ :exec => lambda {|statuses, event|
9
+ width = config.plugins.linefeed.width
10
+ right = config.plugins.linefeed.right
11
+ statuses.each do |s|
12
+ t = s.text
13
+ ocs = []
14
+ sz = nil
15
+ t.gsub!(%r{(https?://)}, "\n\\1")
16
+ t.split("\n").each do |line|
17
+ cs = line.unpack 'U*'
18
+ while cs.size > 0
19
+ sz = 0
20
+ l = cs.take_while{|c| sz += c < 0x100 ? 1 : 2; sz < width}
21
+ ocs += l
22
+ cs = cs.drop(l.size)
23
+ ocs += [0x0a]
24
+ end
25
+ end
26
+ t2 = ocs.pack('U*')
27
+ t2.chop! if sz < (width - right)
28
+ s.text[0, t.size] = t2
29
+ end
30
+ }
31
+ )
@@ -0,0 +1,42 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'digest/md5'
3
+ require 'base64'
4
+
5
+ config.plugins.md5pass.set_default(:salt, 'salt')
6
+ config.plugins.md5pass.set_default(:len, 12)
7
+ config.plugins.md5pass.set_default(:times, 10)
8
+
9
+ module Termtter::Client
10
+
11
+ def self.gen_pass(master_pass)
12
+ salt = config.plugins.md5pass.salt
13
+ len = config.plugins.md5pass.len
14
+ times = config.plugins.md5pass.times
15
+ url = "http://#{config.host}/"
16
+ user = config.user_name
17
+ str = (url + salt + user + master_pass) * (2 ** times);
18
+ Base64.encode64(Digest::MD5.digest(str))[0, len]
19
+ end
20
+
21
+ hl = create_highline
22
+ mp = hl.ask('your master password for md5pass: ') { |q| q.echo = false }
23
+ config.password = gen_pass(mp)
24
+
25
+ register_command(
26
+ :name => :show_md5pass,
27
+ :exec_proc => lambda {|arg| puts "=> #{gen_pass(arg)}" },
28
+ :help => ["show_md5pass MASTER_PASSWORD", "Show your md5 password."]
29
+ )
30
+
31
+ end
32
+
33
+ # config example:
34
+ # config.system.load_plugins = 'md5pass'
35
+ # config.plugins.md5pass.salt = 'set_random_string'
36
+ #
37
+ # 1. Decide your master password and salt.
38
+ # 2. Configure. (see above setting)
39
+ # 3. Start termtter.
40
+ # 4. Check your generated password using 'show_md5pass MASTER_PASSWORD'
41
+ # 5. Change your Twitter password to it.
42
+ # 6. Restart termtter.
@@ -13,7 +13,7 @@ module Termtter::Client
13
13
  :points => [:pre_exec_update],
14
14
  :exec_proc => lambda {|cmd, arg|
15
15
  Thread.new do
16
- Termtter::API.connection.start('outputz.com', 80) do |http|
16
+ Net::HTTP.start('outputz.com', 80) do |http|
17
17
  key = CGI.escape key
18
18
  uri = CGI.escape config.plugins.outputz.uri
19
19
  size = arg.split(//).size
@@ -23,7 +23,7 @@ module Termtter::Client
23
23
  :name => :primes,
24
24
  :exec_proc => lambda {|arg|
25
25
  n = arg.to_i
26
- text = "primes(#{n}) = {#{primes n}}}"
26
+ text = "primes(#{n}) = {#{primes n}}"
27
27
  puts "=> " << text
28
28
  }
29
29
  )
@@ -0,0 +1,43 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ config.plugins.quote.set_default(:format, '<%= comment %>QT @<%=s.user.screen_name%>: <%=s.text%>')
4
+ config.plugins.quote.set_default(:confirm_protected, true)
5
+
6
+ module Termtter::Client
7
+ def self.post_quote(s, comment = nil)
8
+ if s.user.protected && config.plugins.quote.confirm_protected &&
9
+ !confirm("#{s.user.screen_name} is protected! Are you sure?", false)
10
+ return
11
+ end
12
+
13
+ comment += ' ' unless comment.nil?
14
+ text = ERB.new(config.plugins.quote.format).result(binding)
15
+ Termtter::API.twitter.update(text)
16
+ puts "=> #{text}"
17
+
18
+ return text
19
+ end
20
+
21
+ register_command(
22
+ :name => :quote,
23
+ :aliases => [:qt],
24
+ :help => ['quote,qt (ID|@USER)', 'Post a quote message'],
25
+ :exec_proc => lambda {|arg|
26
+ arg, comment = arg.split(/\s/, 2)
27
+
28
+ if public_storage[:typable_id] && s = typable_id_status(arg)
29
+ post_quote(s, comment)
30
+ else
31
+ case arg
32
+ when /(\d+)/
33
+ post_quote(Termtter::API.twitter.show(arg), comment)
34
+ when /@([A-Za-z0-9_]+)/
35
+ user = $1
36
+ statuses = Termtter::API.twitter.user_timeline(user)
37
+ return if statuses.empty?
38
+ post_quote(statuses[0], comment)
39
+ end
40
+ end
41
+ }
42
+ )
43
+ end
@@ -0,0 +1,26 @@
1
+ # -*- coding: utf-8 -*-
2
+ module Termtter::Client
3
+ @last_screen_name = nil
4
+ @last_source = nil
5
+ @last_time = nil
6
+
7
+ register_hook(
8
+ :name => :reduce_screenname,
9
+ :point => :prepare_screenname,
10
+ :exec => lambda {|n, event|
11
+ @last_screen_name == n ? '' : @last_screen_name = n
12
+ }
13
+ )
14
+
15
+ register_hook(
16
+ :name => :reduce_source,
17
+ :point => :prepare_source,
18
+ :exec => lambda {|n, event| @last_source == n ? '' : @last_source = n }
19
+ )
20
+
21
+ register_hook(
22
+ :name => :reduce_time,
23
+ :point => :prepare_time,
24
+ :exec => lambda {|n, event| @last_time == n ? '' : @last_time = n }
25
+ )
26
+ end
@@ -1,13 +1,14 @@
1
1
  # -*- coding: utf-8 -*-
2
2
 
3
- module Termtter::Client
4
- add_filter do |statuses, _|
5
- statuses.map do |s|
3
+ Termtter::Client.register_hook(
4
+ :name => :reverse,
5
+ :point => :filter_for_output,
6
+ :exec => lambda {|statuses, event|
7
+ statuses.each do |s|
6
8
  s.text = s.text.split(//).reverse.to_s
7
- s
8
9
  end
9
- end
10
- end
10
+ }
11
+ )
11
12
 
12
13
  # filter-reverse.rb
13
14
  # reverse texts
@@ -0,0 +1,31 @@
1
+ # -*- coding: utf-8 -*-
2
+ def __dir__
3
+ File.dirname(File.expand_path(__FILE__))
4
+ end
5
+
6
+ def let(o)
7
+ yield o
8
+ end
9
+
10
+ def source(pluginname)
11
+ File.read(
12
+ let(
13
+ Pathname(__dir__) + "#{pluginname}.rb") {|a|
14
+ File.exist?(a) ? a : Pathname(__dir__) + "defaults/#{pluginname}.rb" })
15
+ end
16
+
17
+ def truncate(s)
18
+ s.each_char.take(140).join
19
+ end
20
+
21
+ module Termtter::Client
22
+ register_command(:source, :help => ["source {plugin-name}", "shows the source code of the plugin"]) do |arg|
23
+ puts source(arg)
24
+ end
25
+
26
+ register_command(:sourceyou, :help => ["sourceyou @username {plugin-name}", "gives the source code of the plugin"]) do |arg|
27
+ /(\w+)\s(\w+)/ =~ arg
28
+ text = truncate("@#{normalize_as_user_name($1)} #{source($2)}")
29
+ Termtter::API.twitter.update(text)
30
+ end
31
+ end
@@ -37,7 +37,7 @@ module Termtter::Storage
37
37
  }
38
38
  }
39
39
  end
40
- Rubytter.json_to_struct(result)
40
+ Rubytter.structize(result)
41
41
  end
42
42
 
43
43
  def self.search_user(query)
@@ -60,7 +60,7 @@ module Termtter::Storage
60
60
  }
61
61
  }
62
62
  end
63
- Rubytter.json_to_struct(result)
63
+ Rubytter.structize(result)
64
64
  end
65
65
 
66
66
  def self.insert(data)
@@ -44,7 +44,7 @@ module Termtter::Client
44
44
  :aliases => [:ssu],
45
45
  :exec_proc => lambda {|arg|
46
46
  unless arg.strip.empty?
47
- key = arg.strip
47
+ key = arg.strip.gsub(/^@/, '')
48
48
  statuses = Termtter::Storage::Status.search_user({:user => key})
49
49
  output(statuses, :search)
50
50
  end
@@ -0,0 +1,192 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ require 'tweetstream'
4
+ require File.dirname(__FILE__) + '/../termtter/active_rubytter'
5
+
6
+ config.plugins.stream.set_default :max_following, 400
7
+ config.plugins.stream.set_default :timeline_format, '<yellow>[S]</yellow> $orig'
8
+ config.plugins.stream.set_default :retry_wait_base, 60
9
+ config.plugins.stream.set_default :retry_wait_max, 3600
10
+
11
+ module Termtter::Client
12
+
13
+ config.plugins.stream.keywords = []
14
+
15
+ class << self
16
+ if defined?(DB)
17
+ def friends(max)
18
+ Status.group(:user_id).
19
+ select(:user_id, :screen_name).
20
+ join(:users, :id => :user_id).
21
+ order(:COUNT.sql_function.desc).take(max)
22
+ end
23
+ else
24
+ def friends(max)
25
+ friends = []
26
+ page = 0
27
+ begin
28
+ friends += tmp = Termtter::API::twitter.friends(config.user_name, :page => page+=1)
29
+ p friends.length
30
+ rescue
31
+ end until (tmp.empty? or friends.length > max)
32
+ friends.take(max)
33
+ end
34
+ end
35
+
36
+ def swap_timeline_format(format)
37
+ original = config.plugins.stdout.timeline_format
38
+ if /\$orig/ =~ format
39
+ format.gsub!(/\$orig/, original)
40
+ end
41
+ config.plugins.stdout.timeline_format = format
42
+ yield
43
+ config.plugins.stdout.timeline_format = original
44
+ end
45
+
46
+ def kill_thread(name)
47
+ config.plugins.stream.__send__(name).kill rescue nil
48
+ config.plugins.stream.__assign__(name, nil)
49
+ end
50
+ def alive_thread?(name)
51
+ config.plugins.stream.__send__(name).alive? rescue false
52
+ end
53
+ private :kill_thread
54
+ private :alive_thread?
55
+ end
56
+
57
+
58
+ help = ['keyword_stream [:stop|:show|:add|:delete] [KEYWORDS]',
59
+ 'Tracking keyword using Stream API']
60
+ register_command(:keyword_stream, :help => help) do |arg|
61
+ catch(:exit) do
62
+ throw :exit if arg.empty?
63
+ args = arg.split /[, ]/
64
+ case args[0]
65
+ when ':stop'
66
+ kill_thread :keyword_stream if alive_thread? :keywor_stream
67
+ config.plugins.stream.keywords.clear
68
+ puts 'keyword_stream has stopped'
69
+ throw :exit
70
+ when ':show'
71
+ puts alive_thread?(:keyword_stream) ? 'streaming alive' : 'not alive'
72
+ unless config.plugins.stream.keywords.empty?
73
+ puts config.plugins.stream.keywords.join(', ')
74
+ end
75
+ throw :exit
76
+ when ':add'
77
+ args.shift
78
+ config.plugins.stream.keywords |= args
79
+ when ':delete'
80
+ args.shift
81
+ config.plugins.stream.keywords -= args
82
+ if config.plugins.stream.keywords.empty?
83
+ kill_thread :keyword_stream if alive_thread? :keywor_stream
84
+ puts 'keyword_stream has stopped'
85
+ throw :exit
86
+ end
87
+ when ':start'
88
+ when /^:.*/
89
+ puts "Unknown keyword_stream options"
90
+ throw :exit
91
+ else
92
+ config.plugins.stream.keywords = args
93
+ end
94
+
95
+ kill_thread :keyword_stream if alive_thread? :keywor_stream
96
+
97
+ keywords = config.plugins.stream.keywords
98
+
99
+ puts "streaming: #{keywords.join(', ')}"
100
+ config.plugins.stream.keyword_stream = Thread.new do
101
+ retry_wait = config.plugins.stream.retry_wait_base,
102
+ begin
103
+ TweetStream::Client.new(config.user_name, config.password).
104
+ filter(:track => keywords) do |status|
105
+ print "\e[0G" + "\e[K" unless win?
106
+ swap_timeline_format(config.plugins.stream.timeline_format) do
107
+ output [Termtter::ActiveRubytter.new(status)],
108
+ :update_friends_timeline
109
+ end
110
+ Readline.refresh_line
111
+ end
112
+ rescue
113
+ puts "stream is down"
114
+ puts "wait #{config.plugins.stream.retry_wait}sec"
115
+ sleep retry_wait
116
+ retry_wait = retry_wait * 2;
117
+ retry_wait = config.plugin.stream.retry_max unless
118
+ retry_max > config.plugin.stream.retry_max
119
+ retry
120
+ end
121
+ end
122
+ end
123
+
124
+ at_exit do
125
+ kill_thread :keyword_stream
126
+ end
127
+ end
128
+
129
+ help = ['hash_stream HASHTAG', 'Tracking hashtag using Stream API']
130
+ register_command(:hash_stream, :help => help) do |arg|
131
+ arg = "##{arg}" unless /^#/ =~ arg
132
+ call_commands("keyword_stream #{arg}")
133
+ end
134
+
135
+ help = ['stream USERNAME', 'Tracking users using Stream API']
136
+ register_command(:stream, :help => help) do |arg|
137
+ catch(:exit) do
138
+ args = arg.split
139
+
140
+ case args[0]
141
+ when ':stop'
142
+ kill_thread :thread
143
+ puts 'stream is down'
144
+ throw :exit
145
+ end
146
+
147
+ if config.plugins.stream.thread.class == Thread
148
+ puts 'already streaming'
149
+ throw :exit
150
+ end
151
+
152
+ targets = args.map { |name|
153
+ Termtter::API.twitter.user(name).id rescue nil
154
+ }
155
+
156
+ max = config.plugins.stream.max_following
157
+ unless targets and targets.length > 0
158
+ keys = [:user_id, :"`user_id`", :id, :"`id`"]
159
+ targets = friends(max).map{ |u|
160
+ keys.map{ |k| u[k] rescue nil}.compact.first
161
+ }.compact
162
+ end
163
+
164
+ config.plugins.stream.thread = Thread.new do
165
+ begin
166
+ current_targets = targets.take(max)
167
+ targets = targets.take(max)
168
+ message = "streaming #{current_targets.length} friend"
169
+ message << (current_targets.size == 1 ? '.' : 's.')
170
+ puts message
171
+ TweetStream::Client.new(config.user_name, config.password).
172
+ filter(:follow => current_targets) do |status|
173
+ print "\e[0G" + "\e[K" unless win?
174
+ swap_timeline_format(config.plugins.stream.timeline_format) do
175
+ output [Termtter::ActiveRubytter.new(status)], :update_friends_timeline
176
+ end
177
+ Readline.refresh_line
178
+ end
179
+ rescue(NoMethodError) => e # #<NoMethodError: private method `split' called for nil:NilClass>
180
+ puts "stream seems broken (#{e.inspect})."
181
+ max -= 10 if max > 10
182
+ retry
183
+ end
184
+ end
185
+
186
+ at_exit do
187
+ kill_thread :stream
188
+ end
189
+ end
190
+ end
191
+ end
192
+
@@ -1,22 +1 @@
1
- # -*- coding: utf-8 -*-
2
-
3
- module Termtter::Client
4
- register_command(
5
- :name => :switch_user,
6
- :exec_proc => lambda {|arg|
7
- Termtter::API.switch_user(arg)
8
- },
9
- :completion_proc => lambda {|cmd, arg|
10
- # TODO
11
- },
12
- :help => ["switch_user USERNAME", "Switch twitter account."]
13
- )
14
-
15
- register_command(
16
- :name => :restore_user,
17
- :exec_proc => lambda {|arg|
18
- Termtter::API.restore_user
19
- },
20
- :help => ["restore_user", "Restore default twitter account."]
21
- )
22
- end
1
+ puts 'Plugin "switch_user" was obsoleted, and use plugin "switch" that is loaded as default.'