jugyo-termtter 0.7.6 → 0.7.7

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