termtter 1.5.0 → 1.6.0

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.
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.'