draftcode-termtter 1.1.0 → 1.1.3

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