jugyo-termtter 0.7.6 → 0.7.7

Sign up to get free protection for your applications and to get access to all the features.
data/lib/termtter.rb CHANGED
@@ -11,6 +11,27 @@ require 'enumerator'
11
11
  require 'parsedate'
12
12
  require 'configatron'
13
13
 
14
+ Thread.abort_on_exception = true
15
+
16
+ configatron.set_default(:update_interval, 300)
17
+ configatron.set_default(:prompt, '> ')
18
+ configatron.set_default(:enable_ssl, false)
19
+ configatron.proxy.set_default(:port, '8080')
20
+
21
+ require 'termtter/twitter'
22
+ require 'termtter/connection'
23
+ require 'termtter/status'
24
+ require 'termtter/command'
25
+ require 'termtter/client'
26
+ require 'termtter/api'
27
+
28
+ module Termtter
29
+ VERSION = '0.7.7'
30
+ APP_NAME = 'termtter'
31
+ CONF_FILE = '~/.termtterrc' # still does not use
32
+ CONF_DIR = '~/.termtter' # still does not use
33
+ end
34
+
14
35
  if RUBY_VERSION < '1.8.7'
15
36
  class Array
16
37
  def take(n) self[0...n] end
@@ -22,436 +43,95 @@ def win?
22
43
  end
23
44
 
24
45
  if win?
25
- require 'kconv'
46
+ require 'iconv'
47
+ require 'Win32API'
48
+ $wGetACP = Win32API.new('kernel32','GetACP','','I')
49
+
26
50
  module Readline
51
+ $iconv_sj_to_u8 = Iconv.new('UTF-8', "CP#{$wGetACP.call()}")
27
52
  alias :old_readline :readline
28
53
  def readline(*a)
29
- old_readline(*a).toutf8
54
+ str = old_readline(*a)
55
+ out = ''
56
+ loop do
57
+ begin
58
+ out << $iconv_sj_to_u8.iconv(str)
59
+ break
60
+ rescue Iconv::Failure
61
+ out << "#{$!.success}?"
62
+ str = $!.failed[1..-1]
63
+ end
64
+ end
65
+ return out
30
66
  end
31
67
  module_function :old_readline, :readline
32
68
  end
33
- end
34
-
35
- configatron.set_default(:update_interval, 300)
36
- configatron.set_default(:prompt, '> ')
37
- configatron.set_default(:enable_ssl, false)
38
- configatron.proxy.set_default(:port, '8080')
39
69
 
40
- # FIXME: we need public_storage all around the script
41
- module Termtter
42
- module Client
43
- def self.public_storage
44
- @@public_storage ||= {}
70
+ $wSetConsoleTextAttribute = Win32API.new('kernel32','SetConsoleTextAttribute','II','I')
71
+ $wGetConsoleScreenBufferInfo = Win32API.new("kernel32", "GetConsoleScreenBufferInfo", ['l', 'p'], 'i')
72
+ $wGetStdHandle = Win32API.new('kernel32','GetStdHandle','I','I')
73
+ $wGetACP = Win32API.new('kernel32','GetACP','','I')
74
+
75
+ $hStdOut = $wGetStdHandle.call(0xFFFFFFF5)
76
+ lpBuffer = ' ' * 22
77
+ $wGetConsoleScreenBufferInfo.call($hStdOut, lpBuffer)
78
+ $oldColor = lpBuffer.unpack('SSSSSssssSS')[4]
79
+
80
+ $colorMap = {
81
+ 0 => 7, # black/white
82
+ 37 => 8, # white/intensity
83
+ 31 => 4 + 8, # red/red
84
+ 32 => 2 + 8, # green/green
85
+ 33 => 6 + 8, # yellow/yellow
86
+ 34 => 1 + 8, # blue/blue
87
+ 35 => 5 + 8, # magenta/purple
88
+ 36 => 3 + 8, # cyan/aqua
89
+ 90 => 7, # erase/white
90
+ }
91
+ $iconv_u8_to_sj = Iconv.new("CP#{$wGetACP.call()}", 'UTF-8')
92
+ def puts(str)
93
+ #str.to_s.tosjis.split(/(\e\[\d+m)/).each do |token|
94
+ str.to_s.gsub("\xef\xbd\x9e", "\xe3\x80\x9c").split(/(\e\[\d+m)/).each do |token|
95
+ if token =~ /\e\[(\d+)m/
96
+ $wSetConsoleTextAttribute.call $hStdOut, $colorMap[$1.to_i].to_i
97
+ else
98
+ loop do
99
+ begin
100
+ STDOUT.print $iconv_u8_to_sj.iconv(token)
101
+ break
102
+ rescue Iconv::Failure
103
+ STDOUT.print "#{$!.success}?"
104
+ token = $!.failed[1..-1]
105
+ end
106
+ end
107
+ end
45
108
  end
109
+ $wSetConsoleTextAttribute.call $hStdOut, $oldColor
110
+ STDOUT.puts
111
+ $iconv_u8_to_sj.iconv(nil)
46
112
  end
47
113
  end
48
114
 
115
+ def handle_error(e)
116
+ puts "Error: #{e}"
117
+ puts e.backtrace.join("\n")
118
+ end
119
+
49
120
  def plugin(s)
50
121
  require "plugin/#{s}"
122
+ rescue => e
123
+ handle_error(e)
51
124
  end
52
125
 
53
126
  def filter(s)
54
127
  load "filter/#{s}.rb"
55
- rescue LoadError
56
- raise
128
+ rescue => e
129
+ handle_error(e)
57
130
  else
58
- Termtter::Client.public_storage[:filters] = []
131
+ Termtter::Client.public_storage[:filters] ||= []
59
132
  Termtter::Client.public_storage[:filters] << s
60
133
  true
61
134
  end
62
135
 
63
- # FIXME: delete this method after the major version up
64
- unless defined? original_require
65
- alias original_require require
66
- def require(s)
67
- if %r|^termtter/(.*)| =~ s
68
- puts "[WARNING] use plugin '#{$1}' instead of require"
69
- puts " Such a legacy .termtter file will not be supported until version 1.0.0"
70
- s = "plugin/#{$1}"
71
- end
72
- original_require s
73
- end
74
- end
75
-
76
- module Termtter
77
- VERSION = '0.7.6'
78
- APP_NAME = 'termtter'
79
-
80
- class Connection
81
- attr_reader :protocol, :port, :proxy_uri
82
-
83
- def initialize
84
- @proxy_host = configatron.proxy.host
85
- @proxy_port = configatron.proxy.port
86
- @proxy_user = configatron.proxy.user_name
87
- @proxy_password = configatron.proxy.password
88
- @proxy_uri = nil
89
- @enable_ssl = configatron.enable_ssl
90
- @protocol = "http"
91
- @port = 80
92
-
93
- unless @proxy_host.empty?
94
- @http_class = Net::HTTP::Proxy(@proxy_host, @proxy_port,
95
- @proxy_user, @proxy_password)
96
- @proxy_uri = "http://" + @proxy_host + ":" + @proxy_port + "/"
97
- else
98
- @http_class = Net::HTTP
99
- end
100
-
101
- if @enable_ssl
102
- @protocol = "https"
103
- @port = 443
104
- end
105
- end
106
-
107
- def start(host, port, &block)
108
- http = @http_class.new(host, port)
109
- http.use_ssl = @enable_ssl
110
- http.verify_mode = OpenSSL::SSL::VERIFY_NONE if http.use_ssl
111
- http.start(&block)
112
- end
113
- end
114
-
115
- class Twitter
116
-
117
- def initialize(user_name, password)
118
- @user_name = user_name
119
- @password = password
120
- @connection = Connection.new
121
- end
122
-
123
- def update_status(status)
124
- @connection.start("twitter.com", @connection.port) do |http|
125
- uri = '/statuses/update.xml'
126
- http.request(post_request(uri), "status=#{CGI.escape(status)}&source=#{APP_NAME}")
127
- end
128
- status
129
- end
130
-
131
- def get_friends_timeline(since_id = nil)
132
- uri = "#{@connection.protocol}://twitter.com/statuses/friends_timeline.json"
133
- uri << "?since_id=#{since_id}" if since_id
134
- return get_timeline(uri)
135
- end
136
-
137
- def get_user_timeline(screen_name)
138
- return get_timeline("#{@connection.protocol}://twitter.com/statuses/user_timeline/#{screen_name}.json")
139
- rescue OpenURI::HTTPError => e
140
- puts "No such user: #{screen_name}"
141
- nears = near_users(screen_name)
142
- puts "near users: #{nears}" unless nears.empty?
143
- return {}
144
- end
145
-
146
- def search(query)
147
- results = JSON.parse(open("#{@connection.protocol}://search.twitter.com/search.json?q=" + CGI.escape(query)).read, :proxy => @connection.proxy_uri)['results']
148
- return results.map do |s|
149
- status = Status.new
150
- status.id = s['id']
151
- status.text = CGI.unescapeHTML(s['text']).gsub(/(\n|\r)/, '')
152
- status.created_at = Time.utc(*ParseDate::parsedate(s["created_at"])).localtime
153
- status.user_screen_name = s['from_user']
154
- status
155
- end
156
- end
157
-
158
- def show(id)
159
- return get_timeline("#{@connection.protocol}://twitter.com/statuses/show/#{id}.json")
160
- end
161
-
162
- def replies
163
- return get_timeline("#{@connection.protocol}://twitter.com/statuses/replies.json")
164
- end
165
-
166
- def get_timeline(uri)
167
- data = JSON.parse(open(uri, :http_basic_authentication => [user_name, password], :proxy => @connection.proxy_uri).read)
168
- data = [data] unless data.instance_of? Array
169
- return data.map do |s|
170
- status = Status.new
171
- status.created_at = Time.utc(*ParseDate::parsedate(s["created_at"])).localtime
172
- %w(id text truncated in_reply_to_status_id in_reply_to_user_id).each do |key|
173
- status.__send__("#{key}=".to_sym, s[key])
174
- end
175
- %w(id name screen_name url profile_image_url).each do |key|
176
- status.__send__("user_#{key}=".to_sym, s["user"][key])
177
- end
178
- status.text = CGI.unescapeHTML(status.text).gsub(/(\n|\r)/, '')
179
- status
180
- end
181
- end
182
-
183
- # note: APILimit.reset_time_in_seconds == APILimit.reset_time.to_i
184
- APILIMIT = Struct.new("APILimit", :reset_time, :reset_time_in_seconds, :remaining_hits, :hourly_limit)
185
- def get_rate_limit_status
186
- uri = 'http://twitter.com/account/rate_limit_status.json'
187
- data = JSON.parse(open(uri, :http_basic_authentication => [user_name, password], :proxy => @connection.proxy_uri).read)
188
-
189
- reset_time = Time.parse(data['reset_time'])
190
- reset_time_in_seconds = data['reset_time_in_seconds'].to_i
191
-
192
- APILIMIT.new(reset_time, reset_time_in_seconds, data['remaining_hits'], data['hourly_limit'])
193
- end
194
-
195
- alias :api_limit :get_rate_limit_status
196
-
197
- private
198
-
199
- def user_name
200
- unless @user_name.instance_of? String
201
- @user_name = Readline.readline('user name: ', false)
202
- end
203
- @user_name
204
- end
205
-
206
- def password
207
- unless @password.instance_of? String
208
- system 'stty -echo'
209
- @password = Readline.readline('password: ', false)
210
- system 'stty echo'
211
- puts
212
- end
213
- @password
214
- end
215
-
216
- def near_users(screen_name)
217
- Client::public_storage[:users].select {|user|
218
- /#{user}/i =~ screen_name || /#{screen_name}/i =~ user
219
- }.join(', ')
220
- end
221
-
222
- def post_request(uri)
223
- req = Net::HTTP::Post.new(uri)
224
- req.basic_auth(user_name, password)
225
- req.add_field('User-Agent', 'Termtter http://github.com/jugyo/termtter')
226
- req.add_field('X-Twitter-Client', 'Termtter')
227
- req.add_field('X-Twitter-Client-URL', 'http://github.com/jugyo/termtter')
228
- req.add_field('X-Twitter-Client-Version', '0.1')
229
- req
230
- end
231
- end
232
-
233
- module Client
234
-
235
- @@hooks = []
236
- @@commands = {}
237
- @@completions = []
238
- @@filters = []
239
- @@helps = []
240
-
241
- class << self
242
- def add_hook(&hook)
243
- @@hooks << hook
244
- end
245
-
246
- def clear_hooks
247
- @@hooks.clear
248
- end
249
-
250
- def add_command(regex, &block)
251
- @@commands[regex] = block
252
- end
253
-
254
- def add_macro(r, s)
255
- add_command(r) do |m, t|
256
- call_commands(s % m, t)
257
- end
258
- end
259
-
260
- def clear_commands
261
- @@commands.clear
262
- end
263
-
264
- def add_completion(&completion)
265
- @@completions << completion
266
- end
267
-
268
- def clear_completions
269
- @@completions.clear
270
- end
271
-
272
- def add_help(name, desc)
273
- @@helps << [name, desc]
274
- end
275
-
276
- def clear_helps
277
- @@helps.clear
278
- end
279
-
280
- def add_filter(&filter)
281
- @@filters << filter
282
- end
283
-
284
- def clear_filters
285
- @@filters.clear
286
- end
287
-
288
- # memo: each filter must return Array of Status
289
- def apply_filters(statuses)
290
- filtered = statuses
291
- @@filters.each do |f|
292
- filtered = f.call(filtered)
293
- end
294
- filtered
295
- rescue => e
296
- puts "Error: #{e}"
297
- puts e.backtrace.join("\n")
298
- statuses
299
- end
300
-
301
- Readline.basic_word_break_characters= "\t\n\"\\'`><=;|&{("
302
- Readline.completion_proc = proc {|input|
303
- @@completions.map {|completion|
304
- completion.call(input)
305
- }.flatten.compact
306
- }
307
-
308
- def call_hooks(statuses, event, tw)
309
- statuses = apply_filters(statuses)
310
- @@hooks.each do |h|
311
- begin
312
- h.call(statuses.dup, event, tw)
313
- rescue => e
314
- puts "Error: #{e}"
315
- puts e.backtrace.join("\n")
316
- end
317
- end
318
- end
319
-
320
- def call_commands(text, tw)
321
- return if text.empty?
322
-
323
- command_found = false
324
- @@commands.each do |key, command|
325
- if key =~ text
326
- command_found = true
327
- begin
328
- command.call($~, tw)
329
- rescue => e
330
- puts "Error: #{e}"
331
- puts e.backtrace.join("\n")
332
- end
333
- end
334
- end
335
-
336
- raise CommandNotFound unless command_found
337
- end
338
-
339
- def pause
340
- @@pause = true
341
- end
342
-
343
- def resume
344
- @@pause = false
345
- @@update_thread.run
346
- end
347
-
348
- def exit
349
- call_hooks([], :exit, nil)
350
- @@main_thread.kill
351
- @@update_thread.kill
352
- @@input_thread.kill
353
- end
354
-
355
- def run
356
- puts 'initializing...'
357
- initialized = false
358
- @@pause = false
359
- tw = Termtter::Twitter.new(configatron.user_name, configatron.password)
360
- call_hooks([], :initialize, tw)
361
-
362
- @@input_thread = nil
363
- @@update_thread = Thread.new do
364
- since_id = nil
365
- loop do
366
- begin
367
- Thread.stop if @@pause
368
-
369
- statuses = tw.get_friends_timeline(since_id)
370
- unless statuses.empty?
371
- since_id = statuses[0].id
372
- end
373
- print "\e[1K\e[0G" if !statuses.empty? && !win?
374
- call_hooks(statuses, :update_friends_timeline, tw)
375
- initialized = true
376
- @@input_thread.kill if @@input_thread && !statuses.empty?
377
- rescue OpenURI::HTTPError => e
378
- if e.message == '401 Unauthorized'
379
- puts 'Could not login'
380
- puts 'plese check your account settings'
381
- exit!
382
- end
383
- rescue => e
384
- puts "Error: #{e}"
385
- puts e.backtrace.join("\n")
386
- ensure
387
- sleep configatron.update_interval
388
- end
389
- end
390
- end
391
-
392
- until initialized; end
393
-
394
- vi_or_emacs = configatron.editing_mode
395
- unless vi_or_emacs.empty?
396
- Readline.__send__("#{vi_or_emacs}_editing_mode")
397
- end
398
-
399
- begin
400
- stty_save = `stty -g`.chomp
401
- trap("INT") { system "stty", stty_save; exit }
402
- rescue Errno::ENOENT
403
- end
404
-
405
- @@main_thread = Thread.new do
406
- loop do
407
- @@input_thread = create_input_thread(tw)
408
- @@input_thread.join
409
- end
410
- end
411
- @@main_thread.join
412
- end
413
-
414
- def create_input_thread(tw)
415
- Thread.new do
416
- erb = ERB.new(configatron.prompt)
417
- while buf = Readline.readline(erb.result(tw.__send__(:binding)), true)
418
- begin
419
- call_commands(buf, tw)
420
- rescue CommandNotFound => e
421
- puts "Unknown command \"#{buf}\""
422
- puts 'Enter "help" for instructions'
423
- rescue => e
424
- puts "Error: #{e}"
425
- puts e.backtrace.join("\n")
426
- end
427
- end
428
- exit # exit when press Control-D
429
- end
430
- end
431
- end
432
- end
433
-
434
- class CommandNotFound < StandardError; end
435
-
436
- class Status
437
- %w(
438
- id text created_at truncated in_reply_to_status_id in_reply_to_user_id
439
- user_id user_name user_screen_name user_url user_profile_image_url
440
- ).each do |attr|
441
- attr_accessor attr.to_sym
442
- end
443
-
444
- def eql?(other); self.id == other.id end
445
- def hash; self.id end
446
-
447
- def english?
448
- self.class.english?(self.text)
449
- end
450
-
451
- # english? :: String -> Boolean
452
- def self.english?(message)
453
- /[一-龠]+|[ぁ-ん]+|[ァ-ヴー]+|[a-zA-Z0-9]+/ !~ message
454
- end
455
- end
456
- end
136
+ $:.unshift(Termtter::CONF_DIR) # still does not use
457
137
 
data/run_termtter.rb CHANGED
@@ -11,17 +11,6 @@ self_file =
11
11
  $:.unshift(File.dirname(self_file) + "/lib")
12
12
 
13
13
  require 'termtter'
14
- plugin 'standard_plugins'
15
- plugin 'stdout'
16
-
17
- conf_file = File.expand_path('~/.termtter')
18
- if File.exist? conf_file
19
- load conf_file
20
- else
21
- puts '~/.termtter not found.'
22
- exit 1
23
- end
24
-
25
14
  Termtter::Client.run
26
15
 
27
16
  # Startup scripts for development
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jugyo-termtter
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.6
4
+ version: 0.7.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - bubblegum
@@ -18,7 +18,7 @@ autorequire:
18
18
  bindir: bin
19
19
  cert_chain: []
20
20
 
21
- date: 2009-01-09 00:00:00 -08:00
21
+ date: 2009-01-17 00:00:00 -08:00
22
22
  default_executable: termtter
23
23
  dependencies:
24
24
  - !ruby/object:Gem::Dependency
@@ -80,35 +80,58 @@ files:
80
80
  - lib/filter/english.rb
81
81
  - lib/filter/expand-tinyurl.rb
82
82
  - lib/filter/fib.rb
83
+ - lib/filter/ignore.rb
84
+ - lib/filter/reply.rb
83
85
  - lib/filter/reverse.rb
84
86
  - lib/filter/yhara.rb
85
87
  - lib/plugin/bomb.rb
86
88
  - lib/plugin/confirm.rb
89
+ - lib/plugin/cool.rb
87
90
  - lib/plugin/english.rb
88
91
  - lib/plugin/erb.rb
89
92
  - lib/plugin/favorite.rb
90
93
  - lib/plugin/fib.rb
91
94
  - lib/plugin/filter.rb
92
95
  - lib/plugin/follow.rb
96
+ - lib/plugin/graduatter.rb
93
97
  - lib/plugin/group.rb
94
98
  - lib/plugin/growl.rb
99
+ - lib/plugin/hatebu.rb
95
100
  - lib/plugin/history.rb
96
101
  - lib/plugin/keyword.rb
97
102
  - lib/plugin/log.rb
103
+ - lib/plugin/msagent.rb
104
+ - lib/plugin/multi_reply.rb
98
105
  - lib/plugin/notify-send.rb
106
+ - lib/plugin/otsune.rb
107
+ - lib/plugin/outputz.rb
99
108
  - lib/plugin/plugin.rb
109
+ - lib/plugin/primes.rb
100
110
  - lib/plugin/quicklook.rb
111
+ - lib/plugin/reblog.rb
101
112
  - lib/plugin/reload.rb
102
113
  - lib/plugin/say.rb
114
+ - lib/plugin/scrape.rb
115
+ - lib/plugin/screen.rb
103
116
  - lib/plugin/shell.rb
104
117
  - lib/plugin/sl.rb
105
118
  - lib/plugin/spam.rb
106
119
  - lib/plugin/standard_plugins.rb
107
120
  - lib/plugin/stdout.rb
121
+ - lib/plugin/system_status.rb
108
122
  - lib/plugin/translation.rb
123
+ - lib/plugin/update_editor.rb
109
124
  - lib/plugin/uri-open.rb
125
+ - lib/plugin/wassr_post.rb
110
126
  - lib/plugin/yhara.rb
127
+ - lib/plugin/yonda.rb
111
128
  - lib/termtter.rb
129
+ - lib/termtter/api.rb
130
+ - lib/termtter/client.rb
131
+ - lib/termtter/command.rb
132
+ - lib/termtter/connection.rb
133
+ - lib/termtter/status.rb
134
+ - lib/termtter/twitter.rb
112
135
  - run_termtter.rb
113
136
  - test/friends_timeline.json
114
137
  - test/search.json