draftcode-termtter 1.1.0 → 1.1.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -59,6 +59,14 @@ If you would like to use proxy server, add configurations like this:
59
59
  config.proxy.user_name = 'USERNAME'
60
60
  config.proxy.password = 'PASSWORD'
61
61
 
62
+ You can to load plugins in this way:
63
+
64
+ Termtter::Client.init do |t|
65
+ t.plug 'stdout'
66
+ t.plug 'standard_commands'
67
+ t.plug 'auto_reload'
68
+ end
69
+
62
70
  To update the config, just restart your termtter proccess.
63
71
 
64
72
  == FORUM:
data/Rakefile CHANGED
@@ -16,7 +16,7 @@ Gem::Specification.new do |s|
16
16
  s.version = '#{Termtter::VERSION}'
17
17
  s.summary = "Terminal based Twitter client"
18
18
  s.description = "Termtter is a terminal based Twitter client"
19
- s.files = %w( #{Dir['lib/**/*.rb'].join(' ')}
19
+ s.files = %w( #{Dir['lib/**/*.rb', 'lib/**/*.erb'].join(' ')}
20
20
  #{Dir['spec/**/*.rb'].join(' ')}
21
21
  #{Dir['test/**/*.rb', 'test/**/*.json'].join(' ')}
22
22
  README.rdoc
@@ -0,0 +1,8 @@
1
+ # -*- coding: utf-8 -*-
2
+ module Termtter::Client
3
+ add_task(:name => :auto_reload, :interval => config.update_interval, :after => config.update_interval) do
4
+ call_commands('reload')
5
+ end
6
+
7
+ call_commands('reload')
8
+ end
@@ -45,8 +45,8 @@ module Termtter::Client
45
45
  end
46
46
  end
47
47
  },
48
- :help => ['hatebu2 URL', 'Hatena bookmark a URL, and update']
49
- )
48
+ :help => ['hatebu_and_update,hau URL comment', 'Hatena bookmark a URL, and update']
49
+ )
50
50
  end
51
51
 
52
52
  # hatebu.rb
@@ -85,8 +85,6 @@ module Termtter::Client
85
85
  },
86
86
  :help => ['load', 'Load hisory']
87
87
  )
88
-
89
-
90
88
  end
91
89
 
92
90
  # history.rb
@@ -1,15 +1,21 @@
1
1
  # -*- coding: utf-8 -*-
2
2
 
3
-
4
3
  config.filters.ignore.set_default(:words, [])
5
4
 
6
5
  module Termtter::Client
7
- add_filter do |statuses, _|
8
- ignore_words = config.filters.ignore.words
9
- statuses.delete_if do |s|
10
- ignore_words.any? {|i| i =~ s.text }
11
- end
12
- end
6
+ register_hook(
7
+ :name => :ignore,
8
+ :point => :filter_for_output,
9
+ :exec => lambda { |statuses, event|
10
+ ignore_words = config.filters.ignore.words
11
+ statuses.delete_if do |s|
12
+ ignore_words.any? do |word|
13
+ word = /#{Regexp.quote(word)}/ if word.kind_of? String
14
+ word =~ s.text
15
+ end
16
+ end
17
+ }
18
+ )
13
19
  end
14
20
 
15
21
  # filter/ignore.rb
@@ -0,0 +1,83 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ require 'net/irc'
4
+
5
+ # TODO: post text of stdout too
6
+
7
+ config.plugins.irc_gw.set_default(:port, 16669)
8
+ config.plugins.irc_gw.set_default(:last_statuses_count, 100)
9
+
10
+ class TermtterIrcGateway < Net::IRC::Server::Session
11
+ @@listners = []
12
+ @@last_statuses = []
13
+
14
+ Termtter::Client.register_hook(
15
+ :name => :irc_gw,
16
+ :point => :output,
17
+ :exec => lambda { |statuses, event|
18
+ if event == :update_friends_timeline
19
+ @@last_statuses =
20
+ (@@last_statuses + statuses.dup).reverse![0..config.plugins.irc_gw.last_statuses_count].reverse!
21
+ end
22
+
23
+ @@listners.each do |listner|
24
+ listner.call(statuses.dup, event)
25
+ end
26
+ }
27
+ )
28
+
29
+ def server_name; 'termtter' end
30
+ def server_version; '0.0.0' end
31
+ def main_channel; '#termtter' end
32
+
33
+ def initialize(*args)
34
+ super
35
+ @@listners << self
36
+ end
37
+
38
+ def call(statuses, event)
39
+ msg_type =
40
+ case event
41
+ when :update_friends_timeline
42
+ PRIVMSG
43
+ else
44
+ NOTICE
45
+ end
46
+
47
+ statuses.each do |s|
48
+ post s.user.screen_name, msg_type, main_channel, [s.text, s.id].join(' ')
49
+ end
50
+ end
51
+
52
+ def on_message(m)
53
+ termtter_command = m.command.downcase + ' ' + m.params.join(' ')
54
+ unless Termtter::Client.find_commands(termtter_command).empty?
55
+ post '#termtter', NOTICE, main_channel, '> ' + termtter_command
56
+ Termtter::Client.call_commands(termtter_command)
57
+ end
58
+ end
59
+
60
+ def on_user(m)
61
+ super
62
+ post @prefix, JOIN, main_channel
63
+ post server_name, MODE, main_channel, "+o", @prefix.nick
64
+ self.call(@@last_statuses || [], :update_friends_timeline)
65
+ end
66
+
67
+ def on_privmsg(m)
68
+ target, message = *m.params
69
+ Termtter::Client.call_commands('update ' + message)
70
+ end
71
+ end
72
+
73
+ unless defined? IRC_SERVER
74
+ IRC_SERVER = Net::IRC::Server.new(
75
+ 'localhost',
76
+ config.plugins.irc_gw.port,
77
+ TermtterIrcGateway,
78
+ :logger => Termtter::Client.logger
79
+ )
80
+ Thread.start do
81
+ IRC_SERVER.start
82
+ end
83
+ end
@@ -0,0 +1,32 @@
1
+ # -*- coding: utf-8 -*-
2
+ module Termtter::Client
3
+ register_command(
4
+ :name => :list, :aliases => [:l],
5
+ :exec_proc => lambda {|arg|
6
+ _, options, user = */((?:\-[a-z][= ]\S+\s*)+)?(\w+)?/.match(arg)
7
+ params = {}
8
+ options.scan(/(\-[a-z])[= ](\S+)/).each do |k,v|
9
+ v = v.sub(/^['"]/,'').sub(/['"]$/,'')
10
+ case k
11
+ when '-n' #count
12
+ params['count'] = v.to_i if v.to_i > 0
13
+ when '-p' #page
14
+ params['page'] = v.to_i if v.to_i > 0
15
+ end
16
+ end if options
17
+
18
+ unless user
19
+ event = :list_friends_timeline
20
+ statuses = Termtter::API.twitter.friends_timeline(params)
21
+ else
22
+ event = :list_user_timeline
23
+ statuses = Termtter::API.twitter.user_timeline(user, params)
24
+ end
25
+ output(statuses, event)
26
+ },
27
+ :completion_proc => lambda {|cmd, arg|
28
+ find_user_candidates arg, "#{cmd} %s"
29
+ },
30
+ :help => ["list,l [USERNAME]", "List the posts"]
31
+ )
32
+ end
@@ -0,0 +1,11 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ config.plugins.mark.set_default(
4
+ :text, '<on_green>' + (' ' * 30) + '#mark' + (' ' * 30) + '</on_green>')
5
+
6
+ Termtter::Client.register_command(
7
+ :name => :mark, :alias => :m,
8
+ :exec => lambda {|arg|
9
+ puts TermColor.parse(config.plugins.mark.text)
10
+ }
11
+ )
@@ -0,0 +1,32 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ module Termtter::Client
4
+ class << self
5
+ def wassr_update(text)
6
+ Net::HTTP.version_1_2
7
+ req = Net::HTTP::Post.new("/statuses/update.json?")
8
+ req.basic_auth config.plugins.wassr.username, config.plugins.wassr.password
9
+ Net::HTTP.start('api.wassr.jp', 80) do |http|
10
+ res = http.request(req, "status=#{URI.escape(text)}&source=Termtter")
11
+ end
12
+ end
13
+ end
14
+ end
15
+
16
+ Termtter::Client.register_hook(
17
+ :name => :multi_post,
18
+ :points => [:modify_arg_for_update, :modify_arg_for_reply],
19
+ :exec_proc => lambda {|cmd, arg|
20
+ begin
21
+ wassr_arg = arg.gsub(/\d{10,}/, '')
22
+ Termtter::Client.wassr_update(wassr_arg.strip)
23
+ rescue
24
+ puts "RuntimeError: #{$!}"
25
+ end
26
+
27
+ return arg
28
+ }
29
+ )
30
+
31
+ # multi_post.rb
32
+ # One post, multi update.
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'fileutils'
4
4
  require 'RMagick'
5
+ require 'uri'
5
6
 
6
7
  # Copy from notify-send2.rb
7
8
  config.plugins.notify_send.set_default(:icon_cache_dir, "#{Termtter::CONF_DIR}/tmp/user_profile_images")
@@ -33,9 +34,13 @@ Termtter::Client.register_hook(
33
34
  Thread.start do
34
35
  statuses.each do |s|
35
36
  text = CGI.escapeHTML(s.text)
36
- text.gsub!(%r{https?://[-_.!~*\'()a-zA-Z0-9;\/?:\@&=+\$,%#]+},'<a href="\0">\0</a>')
37
- system 'notify-send', s.user.screen_name, text, '-i', get_icon_path(s)
38
- sleep 0.1
37
+ text = %Q{"#{text}"} if text =~ /^-/
38
+ text.gsub!(URI.regexp,'<a href="\0">\0</a>')
39
+ begin
40
+ system 'notify-send', s.user.screen_name, text, '-i', get_icon_path(s)
41
+ sleep 0.05
42
+ rescue
43
+ end
39
44
  end
40
45
  end
41
46
  }
@@ -10,14 +10,28 @@ config.plugins.standard.set_default(
10
10
  module Termtter::Client
11
11
 
12
12
  register_command(
13
- :name => :update, :aliases => [:u],
14
- :exec_proc => lambda {|arg|
13
+ :name => :reload,
14
+ :exec => lambda {|arg|
15
+ args = @since_id ? [{:since_id => @since_id}] : []
16
+ statuses = Termtter::API.twitter.friends_timeline(*args)
17
+ unless statuses.empty?
18
+ print "\e[1K\e[0G" unless win?
19
+ @since_id = statuses[0].id
20
+ output(statuses, :update_friends_timeline)
21
+ Readline.refresh_line
22
+ end
23
+ }
24
+ )
25
+
26
+ register_command(
27
+ :name => :update, :alias => :u,
28
+ :exec => lambda {|arg|
15
29
  unless arg.empty?
16
- Termtter::API.twitter.update(arg)
17
- puts "=> #{arg}"
30
+ result = Termtter::API.twitter.update(arg)
31
+ puts TermColor.parse("updated => #{result.text} <90>#{result.id}</90>")
18
32
  end
19
33
  },
20
- :completion_proc => lambda {|cmd, args|
34
+ :completion => lambda {|cmd, args|
21
35
  if /(.*)@([^\s]*)$/ =~ args
22
36
  find_user_candidates $2, "#{cmd} #{$1}@%s"
23
37
  end
@@ -25,6 +39,24 @@ module Termtter::Client
25
39
  :help => ["update,u TEXT", "Post a new message"]
26
40
  )
27
41
 
42
+ register_command(
43
+ :name => :delete, :aliases =>[:del],
44
+ :exec_proc => lambda {|arg|
45
+ id =
46
+ case arg
47
+ when ''
48
+ Termtter::API.twitter.user_timeline(config.user_name)[0].id
49
+ when /^\d+$/
50
+ arg.to_i
51
+ end
52
+ if id
53
+ result = Termtter::API.twitter.remove_status(id)
54
+ puts TermColor.parse("deleted => #{result.text} <90>#{result.id}</90>")
55
+ end
56
+ },
57
+ :help => ['delete,del [STATUS ID]', 'Delete a status']
58
+ )
59
+
28
60
  direct_message_struct = Struct.new(:id, :text, :user, :created_at)
29
61
  direct_message_struct.class_eval do
30
62
  def method_missing(*args, &block)
@@ -142,9 +174,13 @@ module Termtter::Client
142
174
  register_command(
143
175
  :name => :replies, :aliases => [:r],
144
176
  :exec_proc => lambda {|arg|
145
- output(Termtter::API.twitter.replies, :replies)
177
+ res = Termtter::API.twitter.replies
178
+ unless arg.empty?
179
+ res = res.map {|e| e.user.screen_name == arg ? e : nil }.compact
180
+ end
181
+ output(res, :replies)
146
182
  },
147
- :help => ["replies,r", "List the most recent @replies for the authenticating user"]
183
+ :help => ["replies,r", "List the replies"]
148
184
  )
149
185
 
150
186
  register_command(
@@ -226,7 +262,11 @@ module Termtter::Client
226
262
  word = $1
227
263
  raise "Not implemented yet."
228
264
  else
229
- return
265
+ if public_storage[:typable_id] && typable_id?(arg)
266
+ id = typable_id_convert(arg)
267
+ else
268
+ return
269
+ end
230
270
  end
231
271
 
232
272
  r = Termtter::API.twitter.favorite id
@@ -239,10 +279,14 @@ module Termtter::Client
239
279
  when /(\d+)/
240
280
  find_status_ids(arg).map{|id| "#{cmd} #{id}"}
241
281
  else
242
- %w(favorite).grep(/^#{Regexp.quote arg}/)
282
+ if public_storage[:typable_id] && typable_id?(arg)
283
+ "#{cmd} #{typable_id_convert(arg)}"
284
+ else
285
+ %w(favorite).grep(/^#{Regexp.quote arg}/)
286
+ end
243
287
  end
244
288
  },
245
- :help => ['favorite,fav (ID|@USER|/WORD)', 'Favorite a status']
289
+ :help => ['favorite,fav (ID|@USER|/WORD)', 'Mark a status as a favorite']
246
290
  )
247
291
 
248
292
  def self.show_settings(conf, level = 0)
@@ -353,14 +397,15 @@ module Termtter::Client
353
397
  end
354
398
 
355
399
  register_command(
356
- :name => :plugin,
400
+ :name => :plug,
401
+ :alias => :plugin,
357
402
  :exec_proc => lambda {|arg|
358
403
  if arg.empty?
359
404
  puts 'Should specify plugin name.'
360
405
  return
361
406
  end
362
407
  begin
363
- result = plugin arg
408
+ result = plug arg
364
409
  rescue LoadError
365
410
  ensure
366
411
  puts "=> #{result.inspect}"
@@ -374,7 +419,7 @@ module Termtter::Client
374
419
  public_storage[:plugins].sort
375
420
  end
376
421
  },
377
- :help => ['plugin FILE', 'Load a plugin']
422
+ :help => ['plug FILE', 'Load a plugin']
378
423
  )
379
424
 
380
425
  register_command(
@@ -408,9 +453,10 @@ module Termtter::Client
408
453
  update_with_user_and_id(text, user.screen_name, id) if user
409
454
  public_storage.delete :log4re
410
455
  when /^\s*(\d+)\s+(.+)$/
411
- id, text = $1, $2
412
- user = public_storage[:log].select {|l| l.id == id.to_i }.first.user
413
- update_with_user_and_id(text, user.screen_name, id) if user
456
+ s = Termtter::API.twitter.show($1) rescue nil
457
+ if s
458
+ update_with_user_and_id($2, s.user.screen_name, id)
459
+ end
414
460
  when /^\s*@(\w+)/
415
461
  in_reply_to_status_id = Termtter::API.twitter.user($1).status.id rescue nil
416
462
  params = in_reply_to_status_id ? {:in_reply_to_status_id => in_reply_to_status_id} : {}
@@ -454,10 +500,9 @@ module Termtter::Client
454
500
  )
455
501
 
456
502
  def self.update_with_user_and_id(text, username, id)
457
- text = ERB.new("@#{username} #{text}").result(binding).gsub(/\n/, ' ')
503
+ text = "@#{username} #{text}"
458
504
  result = Termtter::API.twitter.update(text, {'in_reply_to_status_id' => id})
459
- puts "=> #{text}"
460
- result
505
+ puts TermColor.parse("replied => #{result.text} <90>#{result.id}</90>")
461
506
  end
462
507
 
463
508
  =begin
@@ -4,65 +4,13 @@ require 'termcolor'
4
4
  require 'erb'
5
5
  require 'tempfile'
6
6
  require 'curses'
7
- require 'dl/import'
8
-
9
- module Readline
10
- begin
11
- module LIBREADLINE
12
- if DL.const_defined? :Importable
13
- extend DL::Importable
14
- else
15
- extend DL::Importer
16
- end
17
- pathes = Array(ENV['TERMTTER_EXT_LIB'] || [
18
- '/opt/local/lib/libreadline.dylib',
19
- '/usr/lib/libreadline.so',
20
- '/usr/local/lib/libreadline.so',
21
- File.join(Gem.bindir, 'readline.dll')
22
- ])
23
- dlload(pathes.find { |path| File.exist?(path)})
24
- extern 'int rl_refresh_line(int, int)'
25
- end
26
- def self.refresh_line
27
- LIBREADLINE.rl_refresh_line(0, 0)
28
- end
29
- rescue Exception
30
- def self.refresh_line;end
31
- end
32
- end
33
-
34
- if win?
35
- require 'iconv'
36
-
37
- module Readline
38
- $iconv_sj_to_u8 = Iconv.new('UTF-8', "CP#{$wGetACP.call()}")
39
- alias :old_readline :readline
40
- def readline(*a)
41
- str = old_readline(*a)
42
- out = ''
43
- loop do
44
- begin
45
- out << $iconv_sj_to_u8.iconv(str)
46
- break
47
- rescue Iconv::Failure
48
- out << "#{$!.success}?"
49
- str = $!.failed[1..-1]
50
- end
51
- end
52
- return out
53
- end
54
- module_function :old_readline, :readline
55
- end
56
- end
57
7
 
58
- config.plugins.stdout.set_default(
59
- :colors,
60
- [:none, :red, :green, :yellow, :blue, :magenta, :cyan])
8
+ config.plugins.stdout.set_default(:colors, (31..36).to_a + (91..96).to_a)
61
9
  config.plugins.stdout.set_default(
62
10
  :timeline_format,
63
- '<90><%=time%></90> <<%=status_color%>><%=status%></<%=status_color%>> <90><%=id%></90>')
64
- config.plugins.stdout.set_default(:search_highlight_format, '<on_magenta><white>\1</white></on_magenta>')
65
-
11
+ '<90><%=time%></90> <<%=color%>><%=s.user.screen_name%>: <%=text%></<%=color%>> ' +
12
+ '<90><%=reply_to ? reply_to + " " : ""%><%=s.id%> <%=source%></90>'
13
+ )
66
14
  config.plugins.stdout.set_default(:enable_pager, true)
67
15
  config.plugins.stdout.set_default(:pager, 'less -R -f +G')
68
16
  config.plugins.stdout.set_default(:window_height, 50)
@@ -70,18 +18,18 @@ config.plugins.stdout.set_default(:window_height, 50)
70
18
  class String
71
19
  def truncate_column(col)
72
20
  count = 0
73
- ary = []
21
+ str = String.new
74
22
  ret = []
75
23
  self.split(//u).each {|c|
76
- count += (c.size == 1 ? 1 : 2)
24
+ count += (c.bytesize == 1 ? 1 : 2)
77
25
  if count > col then
78
- ret.push(ary.to_s)
79
- ary = []
26
+ ret.push(str)
27
+ str = String.new
80
28
  count = 0
81
29
  end
82
- ary.push(c)
30
+ str += c
83
31
  }
84
- ret.push(ary.to_s) unless ary.empty?
32
+ ret.push(str) unless str.empty?
85
33
  ret
86
34
  end
87
35
  end
@@ -90,67 +38,12 @@ module Termtter
90
38
  class StdOut < Hook
91
39
  def initialize
92
40
  super(:name => :stdout, :points => [:output])
93
- @input_thread = nil
94
- Client.register_hook(
95
- :name => :stdout_exit,
96
- :points => [:exit],
97
- :exec_proc => lambda { @input_thread.kill if @input_thread }
98
- )
99
- Client.register_hook(
100
- :name => :stdout_readline_yield_thread,
101
- :points => [:before_task_thread_run],
102
- :exec_proc => lambda { start_input_thread() unless @input_thread }
103
- )
104
41
  end
105
42
 
106
43
  def call(statuses, event)
107
44
  print_statuses(statuses)
108
45
  end
109
46
 
110
- def trap_setting()
111
- begin
112
- stty_save = `stty -g`.chomp
113
- trap("INT") do
114
- begin
115
- system "stty", stty_save
116
- ensure
117
- exit
118
- end
119
- end
120
- rescue Errno::ENOENT
121
- end
122
- end
123
-
124
- def setup_readline
125
- if Readline.respond_to?(:basic_word_break_characters=)
126
- Readline.basic_word_break_characters= "\t\n\"\\'`><=;|&{("
127
- end
128
- Readline.completion_proc = Client.get_command_completion_proc()
129
- vi_or_emacs = config.editing_mod
130
- unless vi_or_emacs.empty?
131
- Readline.__send__("#{vi_or_emacs}_editing_mode")
132
- end
133
- end
134
-
135
- def start_input_thread
136
- setup_readline()
137
- trap_setting()
138
- @input_thread = Thread.new do
139
- while buf = Readline.readline(ERB.new(config.prompt).result(API.twitter.__send__(:binding)), true)
140
- Readline::HISTORY.pop if buf.empty?
141
- begin
142
- Client.call_commands(buf)
143
- rescue CommandNotFound => e
144
- warn "Unknown command \"#{e}\""
145
- warn 'Enter "help" for instructions'
146
- rescue => e
147
- Client.handle_error e
148
- end
149
- end
150
- end
151
- @input_thread.join
152
- end
153
-
154
47
  def print_statuses(statuses, sort = true, time_format = nil)
155
48
  return unless statuses and statuses.first
156
49
  unless time_format
@@ -172,14 +65,14 @@ module Termtter
172
65
  cols = Curses.cols;
173
66
  Curses::close_screen
174
67
  statuses.each do |s|
175
- text = s.text
176
- status_color = config.plugins.stdout.colors[s.user.id.hash % config.plugins.stdout.colors.size]
177
- status = "#{s.user.screen_name}: #{TermColor.escape(text)}"
68
+ text = TermColor.escape(s.text)
69
+ color = config.plugins.stdout.colors[s.user.id.to_i % config.plugins.stdout.colors.size]
70
+ reply_to = s.in_reply_to_status_id ? "(reply to #{s.in_reply_to_status_id})" : nil
71
+ time = "(#{Time.parse(s.created_at).strftime(time_format)})"
178
72
  if s.in_reply_to_status_id
179
73
  status += " (reply to #{s.in_reply_to_status_id})"
180
74
  end
181
75
 
182
- time = "(#{Time.parse(s.created_at).strftime(time_format)})"
183
76
  id = s.id
184
77
  len = id.to_s.length + 2
185
78
  if cols > 0 then
@@ -206,7 +99,6 @@ module Termtter
206
99
  output_text << TermColor.parse("<90>-----Fetched on " + Time.now.strftime(time_format) + "</90>\n")
207
100
  print output_text
208
101
  end
209
- Readline.refresh_line
210
102
  end
211
103
  end
212
104
 
@@ -0,0 +1,97 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ Termtter::API.module_eval %Q{
4
+ class << self
5
+ attr_reader :wassr
6
+
7
+ def setup_wassr(user_name, password)
8
+ @wassr = create_wassr(user_name, password)
9
+ end
10
+
11
+ def create_wassr(user_name, password)
12
+ Rubytter.new(
13
+ user_name,
14
+ password,
15
+ {
16
+ :app_name => config.app_name.empty? ? Termtter::APP_NAME : config.app_name,
17
+ :host => 'api.wassr.jp',
18
+ :header => {
19
+ 'User-Agent' => 'Termtter http://github.com/jugyo/termtter',
20
+ 'X-Wassr-Client' => 'Termtter',
21
+ 'X-Wassr-Client-URL' => 'http://github.com/jugyo/termtter',
22
+ 'X-Wassr-Client-Version' => Termtter::VERSION
23
+ },
24
+ :enable_ssl => config.enable_ssl,
25
+ :proxy_host => config.proxy.host,
26
+ :proxy_port => config.proxy.port,
27
+ :proxy_user_name => config.proxy.user_name,
28
+ :proxy_password => config.proxy.password
29
+ }
30
+ )
31
+ end
32
+ end
33
+ }
34
+
35
+ module Termtter::Client
36
+ register_command(
37
+ :name => :wassr, :aliases => [:wsr],
38
+ :exec_proc => lambda {|arg|
39
+ Termtter::API.setup_wassr(config.plugins.wassr.username, config.plugins.wassr.password)
40
+ statuses = Termtter::API.wassr.friends_timeline
41
+ add_member = [:created_at, :in_reply_to_status_id]
42
+ event = :wassr_friends_timeline
43
+ print_statuses(statuses, event)
44
+ },
45
+ :completion_proc => lambda {|cmd, arg|
46
+ find_user_candidates arg, "#{cmd} %s"
47
+ },
48
+ :help => ["wassr, wsr", "List the wassr timeline."]
49
+ )
50
+ end
51
+
52
+ def print_statuses(statuses, sort = true, time_format = nil)
53
+ return unless statuses and statuses.first
54
+ unless time_format
55
+ t0 = Time.now
56
+ t1 = Time.at(statuses.first[:epoch])
57
+ t2 = Time.at(statuses.last[:epoch])
58
+ time_format =
59
+ if [t0.year, t0.month, t0.day] == [t1.year, t1.month, t1.day] \
60
+ and [t1.year, t1.month, t1.day] == [t2.year, t2.month, t2.day]
61
+ '%H:%M:%S'
62
+ else
63
+ '%y/%m/%d %H:%M'
64
+ end
65
+ end
66
+
67
+ output_text = ''
68
+
69
+ user_login_ids = []
70
+ statuses.sort{|a, b| a.epoch <=> b.epoch}.each do |s|
71
+ text = s.text
72
+ user_login_ids << s.user_login_id unless user_login_ids.include?(s.user_login_id)
73
+ status_color = config.plugins.stdout.colors[user_login_ids.index(s.user_login_id) % config.plugins.stdout.colors.size]
74
+ status = "#{s.user.screen_name}: #{TermColor.escape(text)}"
75
+
76
+ time = "[wassr] [#{Time.at(s.epoch).strftime(time_format)}]"
77
+ id = s.id
78
+ source =
79
+ case s.source
80
+ when />(.*?)</ then $1
81
+ when 'web' then 'web'
82
+ end
83
+
84
+ erbed_text = ERB.new('<90><%=time%></90> <<%=status_color%>><%=status%></<%=status_color%>>').result(binding)
85
+ output_text << TermColor.parse(erbed_text) + "\n"
86
+ end
87
+
88
+ if config.plugins.stdout.enable_pager && ENV['LINES'] && statuses.size > ENV['LINES'].to_i
89
+ file = Tempfile.new('termtter')
90
+ file.print output_text
91
+ file.close
92
+ system "#{config.plugins.stdout.pager} #{file.path}"
93
+ file.close(true)
94
+ else
95
+ print output_text
96
+ end
97
+ end
@@ -3,6 +3,12 @@ require 'fileutils'
3
3
  require 'logger'
4
4
  require 'termcolor'
5
5
 
6
+ def plugin(name, init = {})
7
+ Termtter::Client.load_plugin(name, init)
8
+ rescue Exception => e
9
+ Termtter::Client.handle_error(e)
10
+ end
11
+
6
12
  module Termtter
7
13
 
8
14
  class CommandNotFound < StandardError; end
@@ -10,13 +16,29 @@ module Termtter
10
16
 
11
17
  module Client
12
18
 
19
+ @hooks = {}
20
+ @commands = {}
21
+ @filters = []
22
+ @since_id = nil
23
+ @input_thread = nil
24
+ @task_manager = Termtter::TaskManager.new
25
+
26
+ config.set_default(:logger, nil)
27
+ config.set_default(:update_interval, 300)
28
+ config.set_default(:prompt, '> ')
29
+ config.set_default(:devel, false)
30
+
31
+ Thread.abort_on_exception = true
32
+
13
33
  class << self
14
34
 
15
35
  def init
16
36
  @hooks = {}
17
37
  @commands = {}
18
38
  @filters = []
39
+ @loaded_plugins = []
19
40
  @since_id = nil
41
+ @plugin_loader = nil
20
42
  @task_manager = Termtter::TaskManager.new
21
43
  config.set_default(:logger, nil)
22
44
  config.set_default(:update_interval, 300)
@@ -30,6 +52,7 @@ module Termtter
30
52
  end
31
53
 
32
54
  def add_filter(&b)
55
+ warn "add_filter method will be removed. Use Termtter::Client.register_hook(:name => ..., :point => :filter_for_output, :exec => ... ) instead."
33
56
  @filters << b
34
57
  end
35
58
 
@@ -59,6 +82,15 @@ module Termtter
59
82
  end
60
83
  end
61
84
 
85
+ def load_plugin(name, init={})
86
+ unless init.empty?
87
+ init.each do |key, value|
88
+ config.plugins.__refer__(name.to_sym).__assign__(key.to_sym, value)
89
+ end
90
+ end
91
+ @plugin_loader.call(name) if @plugin_loader
92
+ end
93
+
62
94
  def register_command(arg)
63
95
  command = case arg
64
96
  when Command
@@ -71,6 +103,10 @@ module Termtter
71
103
  @commands[command.name] = command
72
104
  end
73
105
 
106
+ def clear_command
107
+ @commands.clear
108
+ end
109
+
74
110
  def get_command(name)
75
111
  @commands[name]
76
112
  end
@@ -110,26 +146,28 @@ module Termtter
110
146
 
111
147
  statuses = statuses.sort_by{|s|s.id}
112
148
  call_hooks(:pre_filter, statuses, event)
113
- filtered = apply_filters(statuses, event)
149
+
150
+ filtered = statuses.map(&:dup)
151
+ @filters.each do |f| # TODO: code for compatibility. delete someday.
152
+ statuses = f.call(statuses, event)
153
+ end
154
+ apply_filters_for_hook(:filter_for_output, statuses, event)
155
+
114
156
  call_hooks(:post_filter, filtered, event)
115
157
  get_hooks(:output).each do |hook|
116
- filtered_for_hook = apply_filters_for_hook(filtered, hook.name)
117
- hook.call(filtered_for_hook, event)
158
+ hook.call(
159
+ apply_filters_for_hook("filter_for_#{hook.name}", filtered, event),
160
+ event
161
+ )
118
162
  end
163
+ call_hooks(:post_output, filtered, event)
119
164
  end
120
165
 
121
- def apply_filters(statuses, event)
122
- filtered = statuses.map(&:dup)
123
- @filters.each do |f|
124
- filtered = f.call(filtered, event)
125
- end
126
- filtered
127
- end
128
-
129
- def apply_filters_for_hook(statuses, hook_name)
130
- # TODO
131
- filtered = statuses.map(&:dup)
132
- filtered
166
+ def apply_filters_for_hook(hook_name, statuses, event)
167
+ get_hooks(hook_name).each do |hook|
168
+ statuses = hook.call(statuses, event)
169
+ end
170
+ statuses
133
171
  end
134
172
 
135
173
  # return last hook return value
@@ -142,12 +180,13 @@ module Termtter
142
180
  result
143
181
  end
144
182
 
145
- def call_commands(text, tw = nil)
183
+ def call_commands(text)
146
184
  return if text.empty?
147
185
 
148
- command_found = false
149
- @commands.each do |key, command|
150
- next unless command.match?(text)
186
+ commands = find_commands(text)
187
+ raise CommandNotFound, text if commands.empty?
188
+
189
+ commands.each do |command|
151
190
  command_found = true
152
191
  command_str, command_arg = Command.split_command_line(text)
153
192
 
@@ -168,8 +207,10 @@ module Termtter
168
207
  end
169
208
  end
170
209
  end
210
+ end
171
211
 
172
- raise CommandNotFound, text unless command_found
212
+ def find_commands(text)
213
+ @commands.values.select { |command| command.match?(text) }
173
214
  end
174
215
 
175
216
  def pause
@@ -194,6 +235,8 @@ module Termtter
194
235
  def load_default_plugins
195
236
  plugin 'standard_plugins'
196
237
  plugin 'stdout'
238
+ plugin 'readline'
239
+ plugin 'update_timeline'
197
240
  end
198
241
 
199
242
  def load_config
@@ -224,12 +267,6 @@ module Termtter
224
267
  Termtter::CONF_FILE)
225
268
  end
226
269
 
227
- def post_config_load()
228
- if config.devel
229
- plugin 'devel'
230
- end
231
- end
232
-
233
270
  def logger
234
271
  @logger
235
272
  end
@@ -261,12 +298,29 @@ module Termtter
261
298
  logger
262
299
  end
263
300
 
301
+ def init(&block)
302
+ @init_block = block
303
+ end
304
+
264
305
  def run
265
- load_default_plugins()
306
+ @plugin_loader = lambda do |name|
307
+ @loaded_plugins.push(name)
308
+ end
309
+
266
310
  load_config()
311
+ load_default_plugins()
267
312
  Termtter::API.setup()
268
313
  setup_logger()
269
- post_config_load()
314
+
315
+ plugin 'devel' if config.devel
316
+
317
+ @plugin_loader = lambda do |name|
318
+ @loaded_plugins.push(name)
319
+ load "plugins/#{name}.rb"
320
+ end
321
+ @loaded_plugins.each { |name| load "plugins/#{name}.rb" }
322
+
323
+ call_hooks(:initialize)
270
324
 
271
325
  config.system.eval_scripts.each do |script|
272
326
  begin
@@ -279,10 +333,8 @@ module Termtter
279
333
  config.system.run_commands.each { |cmd| call_commands(cmd) }
280
334
 
281
335
  unless config.system.cmd_mode
282
- call_hooks(:initialize)
283
- plugin('update_timeline')
284
336
  @task_manager.run()
285
- call_hooks(:before_task_thread_run)
337
+ call_hooks(:post_task_thread_run)
286
338
  end
287
339
  end
288
340
 
@@ -301,5 +353,3 @@ module Termtter
301
353
  end
302
354
  end
303
355
  end
304
-
305
- Termtter::Client.init
@@ -1,36 +1,28 @@
1
1
  # -*- coding: utf-8 -*-
2
2
 
3
+ require 'erb'
4
+
3
5
  module Termtter
4
6
  module ConfigSetup
5
7
  module_function
6
8
  def run
7
9
  ui = create_highline
8
- username = ui.ask('your twitter username: ')
10
+ user_name = ui.ask('your twitter user name: ')
9
11
  password = ui.ask('your twitter password: ') { |q| q.echo = false }
10
12
 
13
+ plugins = Dir.glob(File.expand_path(File.dirname(__FILE__) + "/../plugins/*.rb")).map {|f|
14
+ f.match(%r|lib/plugins/(.*?).rb$|)[1]
15
+ }
16
+ standard_plugins = %w[stdout standard_commands auto_reload]
17
+
18
+ template = open(File.dirname(__FILE__) + '/config_template.erb').read
19
+ config = ERB.new(template, nil, '-').result(binding) # trim_mode => '-'
20
+
11
21
  Dir.mkdir(Termtter::CONF_DIR) unless File.exists?(Termtter::CONF_DIR)
12
22
  File.open(Termtter::CONF_FILE, 'w') {|io|
13
- io.puts '# -*- coding: utf-8 -*-'
14
-
15
- io.puts
16
- io.puts "config.user_name = '#{username}'"
17
- io.puts "config.password = '#{password}'"
18
- io.puts "#config.update_interval = 120"
19
- io.puts "#config.proxy.host = 'proxy host'"
20
- io.puts "#config.proxy.port = '8080'"
21
- io.puts "#config.proxy.user_name = 'proxy user'"
22
- io.puts "#config.proxy.password = 'proxy password'"
23
- io.puts
24
- plugins = Dir.glob(File.expand_path(File.dirname(__FILE__) + "/../plugins/*.rb")).map {|f|
25
- f.match(%r|lib/plugins/(.*?).rb$|)[1]
26
- }
27
- plugins -= %w[stdout standard_plugins]
28
- plugins.each do |p|
29
- io.puts "#plugin '#{p}'"
30
- end
31
- io.puts
32
- io.puts "# vim: set filetype=ruby"
23
+ io << config
33
24
  }
25
+
34
26
  puts "generated: ~/.termtter/config"
35
27
  puts "enjoy!"
36
28
  end
@@ -0,0 +1,18 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ config.user_name = '<%= user_name %>'
4
+ config.password = '<%= password %>'
5
+ #config.update_interval = 120
6
+ #config.proxy.host = 'proxy host'
7
+ #config.proxy.port = '8080'
8
+ #config.proxy.user_name = 'proxy user'
9
+ #config.proxy.password = 'proxy password'
10
+
11
+ Termtter::Client.init do |t|
12
+ <%- standard_plugins.each do |plugin| -%>
13
+ t.plug '<%= plugin %>'
14
+ <%- end -%>
15
+ <%- (plugins - standard_plugins).each do |plugin| -%>
16
+ # t.plug '<%= plugin %>'
17
+ <%- end -%>
18
+ end
@@ -23,6 +23,11 @@ OptionParser.new { |opt|
23
23
  config.system.run_commands << cmd
24
24
  end
25
25
 
26
+ config.system.load_plugins = []
27
+ opt.on('-p', '--plugin plugin', 'Load plugin') do |plugin|
28
+ config.system.load_plugins << plugin
29
+ end
30
+
26
31
  config.system.eval_scripts = []
27
32
  opt.on('-e', '--eval-script script', 'Eval script') do |script|
28
33
  config.system.eval_scripts << script
@@ -1,23 +1,12 @@
1
1
  # -*- coding: utf-8 -*-
2
2
 
3
- def plugin(name, init = {})
4
- unless init.empty?
5
- init.each do |key, value|
6
- config.plugins.__refer__(name.to_sym).__assign__(key.to_sym, value)
7
- end
8
- end
9
- load "plugins/#{name}.rb"
10
- rescue Exception => e
11
- Termtter::Client.handle_error(e)
12
- end
13
-
14
3
  def filter(name, init = {})
15
4
  warn "filter method will be removed. Use plugin instead."
16
5
  plugin(name, init)
17
6
  end
18
7
 
19
8
  def win?
20
- RUBY_PLATFORM.downcase =~ /mswin(?!ce)|mingw|bccwin/
9
+ RUBY_PLATFORM.downcase =~ /mswin(?!ce)|mingw|bccwin|cygwin/
21
10
  end
22
11
 
23
12
  if win?
@@ -103,13 +92,9 @@ require 'highline'
103
92
  def create_highline
104
93
  HighLine.track_eof = false
105
94
  if $stdin.respond_to?(:getbyte) # for ruby1.9
106
- require 'delegate'
107
- stdin_for_highline = SimpleDelegator.new($stdin)
108
- def stdin_for_highline.getc
95
+ def $stdin.getc
109
96
  getbyte
110
97
  end
111
- else
112
- stdin_for_highline = $stdin
113
98
  end
114
- HighLine.new(stdin_for_highline)
99
+ HighLine.new($stdin)
115
100
  end
@@ -1,4 +1,4 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  module Termtter
3
- VERSION = '1.1.0'
3
+ VERSION = '1.1.3'
4
4
  end
@@ -1,7 +1,7 @@
1
1
  # -*- coding: utf-8 -*-
2
2
 
3
3
  require File.dirname(__FILE__) + '/../spec_helper'
4
- plugin 'standard_plugins'
4
+ plugin 'standard_commands'
5
5
 
6
6
  module Termtter
7
7
  describe Client do
@@ -6,8 +6,11 @@ module Termtter
6
6
 
7
7
  describe Client do
8
8
 
9
- it 'should take command' do
9
+ before do
10
10
  Client.setup_logger
11
+ end
12
+
13
+ it 'should take command' do
11
14
  command = Command.new(:name => :test)
12
15
  Client.register_command(command)
13
16
  Client.get_command(:test).should == command
@@ -30,7 +33,7 @@ module Termtter
30
33
  ['test foo bar ', 'foo bar'],
31
34
  ['test foo bar ', 'foo bar'],
32
35
  ].each do |input, args|
33
- Client.call_commands(input, nil)
36
+ Client.call_commands(input)
34
37
  command_arg.should == args
35
38
  end
36
39
  end
@@ -177,10 +180,8 @@ module Termtter
177
180
  end
178
181
 
179
182
  it 'run' do
180
- Client.should_receive(:load_default_plugins)
181
183
  Client.should_receive(:load_config)
182
184
  Termtter::API.should_receive(:setup)
183
- Client.should_receive(:post_config_load)
184
185
  Client.should_receive(:start_input_thread)
185
186
  Client.run
186
187
  end
@@ -249,5 +250,38 @@ module Termtter
249
250
  $stdout.string.should match(/foo USER/)
250
251
  $stdout.string.should match(/foo list/)
251
252
  end
253
+
254
+ describe 'add commands' do
255
+ before(:each) do
256
+ Client.clear_command
257
+ Client.register_command(:name => :foo1)
258
+ Client.register_command(:name => :foo2)
259
+ Client.register_command(:name => :bar)
260
+ end
261
+
262
+ it 'commands number is 3' do
263
+ Client.commands.size.should == 3
264
+ end
265
+
266
+ it 'should find a command' do
267
+ Client.find_commands('foo1').size.should == 1
268
+ Client.find_commands('foo1')[0].name.should == :foo1
269
+ Client.find_commands('bar').size.should == 1
270
+ end
271
+
272
+ it 'should find no command' do
273
+ Client.find_commands('foo').size.should == 0
274
+ end
275
+ end
276
+
277
+ describe 'clear commands' do
278
+ before(:each) do
279
+ Client.clear_command
280
+ end
281
+
282
+ it 'should no command' do
283
+ Client.commands.size.should == 0
284
+ end
285
+ end
252
286
  end
253
287
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: draftcode-termtter
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - draftcode
@@ -65,6 +65,7 @@ extra_rdoc_files:
65
65
  files:
66
66
  - lib/plugins/addspace.rb
67
67
  - lib/plugins/april_fool.rb
68
+ - lib/plugins/auto_reload.rb
68
69
  - lib/plugins/bomb.rb
69
70
  - lib/plugins/clear.rb
70
71
  - lib/plugins/command_plus.rb
@@ -91,12 +92,16 @@ files:
91
92
  - lib/plugins/hatebu_and_update.rb
92
93
  - lib/plugins/history.rb
93
94
  - lib/plugins/ignore.rb
95
+ - lib/plugins/irc_gw.rb
94
96
  - lib/plugins/keyword.rb
95
97
  - lib/plugins/l2.rb
98
+ - lib/plugins/list_with_opts.rb
96
99
  - lib/plugins/log.rb
100
+ - lib/plugins/mark.rb
97
101
  - lib/plugins/me.rb
98
102
  - lib/plugins/modify_arg_hook_sample.rb
99
103
  - lib/plugins/msagent.rb
104
+ - lib/plugins/multi_post.rb
100
105
  - lib/plugins/multi_reply.rb
101
106
  - lib/plugins/notify-send.rb
102
107
  - lib/plugins/notify-send2.rb
@@ -123,7 +128,7 @@ files:
123
128
  - lib/plugins/shell.rb
124
129
  - lib/plugins/sl.rb
125
130
  - lib/plugins/spam.rb
126
- - lib/plugins/standard_plugins.rb
131
+ - lib/plugins/standard_commands.rb
127
132
  - lib/plugins/stdout.rb
128
133
  - lib/plugins/storage/DB.rb
129
134
  - lib/plugins/storage/status.rb
@@ -136,8 +141,8 @@ files:
136
141
  - lib/plugins/translation.rb
137
142
  - lib/plugins/typable_id.rb
138
143
  - lib/plugins/update_editor.rb
139
- - lib/plugins/update_timeline.rb
140
144
  - lib/plugins/uri-open.rb
145
+ - lib/plugins/wassr.rb
141
146
  - lib/plugins/wassr_post.rb
142
147
  - lib/plugins/yhara.rb
143
148
  - lib/plugins/yhara_filter.rb
@@ -155,6 +160,7 @@ files:
155
160
  - lib/termtter/task_manager.rb
156
161
  - lib/termtter/version.rb
157
162
  - lib/termtter.rb
163
+ - lib/termtter/config_template.erb
158
164
  - spec/plugins/cool_spec.rb
159
165
  - spec/plugins/english_spec.rb
160
166
  - spec/plugins/fib_spec.rb
@@ -1,32 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
- module Termtter::Client
3
- register_command(
4
- :name => :_update_timeline,
5
- :exec_proc => lambda {|arg|
6
- begin
7
- args = @since_id ? [{:since_id => @since_id}] : []
8
- statuses = Termtter::API.twitter.friends_timeline(*args)
9
- unless statuses.empty?
10
- print "\e[1K\e[0G" unless win?
11
- @since_id = statuses[0].id
12
- output(statuses, :update_friends_timeline)
13
- Readline.refresh_line
14
- end
15
- rescue OpenURI::HTTPError => e
16
- if e.message == '401 Unauthorized'
17
- puts 'Could not login'
18
- puts 'plese check your account settings'
19
- exit!
20
- end
21
- rescue => e
22
- handle_error(e)
23
- end
24
- }
25
- )
26
-
27
- add_task(:name => :update_timeline, :interval => config.update_interval, :after => config.update_interval) do
28
- call_commands('_update_timeline')
29
- end
30
-
31
- call_commands('_update_timeline')
32
- end