termtter 0.8.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.
- data/History.txt +4 -0
- data/README.rdoc +97 -0
- data/Rakefile +46 -0
- data/bin/kill_termtter +22 -0
- data/bin/termtter +7 -0
- data/lib/filter/en2ja.rb +11 -0
- data/lib/filter/english.rb +8 -0
- data/lib/filter/expand-tinyurl.rb +24 -0
- data/lib/filter/fib.rb +15 -0
- data/lib/filter/ignore.rb +19 -0
- data/lib/filter/reply.rb +8 -0
- data/lib/filter/reverse.rb +13 -0
- data/lib/filter/url_addspace.rb +16 -0
- data/lib/filter/yhara.rb +20 -0
- data/lib/plugin/april_fool.rb +15 -0
- data/lib/plugin/bomb.rb +29 -0
- data/lib/plugin/clear.rb +14 -0
- data/lib/plugin/confirm.rb +9 -0
- data/lib/plugin/cool.rb +10 -0
- data/lib/plugin/devel.rb +13 -0
- data/lib/plugin/english.rb +59 -0
- data/lib/plugin/erb.rb +17 -0
- data/lib/plugin/favorite.rb +75 -0
- data/lib/plugin/fib.rb +8 -0
- data/lib/plugin/filter.rb +69 -0
- data/lib/plugin/follow.rb +56 -0
- data/lib/plugin/graduatter.rb +9 -0
- data/lib/plugin/grass.rb +27 -0
- data/lib/plugin/group.rb +60 -0
- data/lib/plugin/growl.rb +62 -0
- data/lib/plugin/hatebu.rb +59 -0
- data/lib/plugin/history.rb +82 -0
- data/lib/plugin/keyword.rb +18 -0
- data/lib/plugin/log.rb +63 -0
- data/lib/plugin/modify_arg_hook_sample.rb +7 -0
- data/lib/plugin/msagent.rb +26 -0
- data/lib/plugin/multi_reply.rb +36 -0
- data/lib/plugin/notify-send.rb +17 -0
- data/lib/plugin/otsune.rb +21 -0
- data/lib/plugin/outputz.rb +35 -0
- data/lib/plugin/pause.rb +3 -0
- data/lib/plugin/plugin.rb +53 -0
- data/lib/plugin/post_exec_hook_sample.rb +9 -0
- data/lib/plugin/pre_exec_hook_sample.rb +9 -0
- data/lib/plugin/primes.rb +23 -0
- data/lib/plugin/quicklook.rb +38 -0
- data/lib/plugin/reblog.rb +40 -0
- data/lib/plugin/reload.rb +3 -0
- data/lib/plugin/say.rb +24 -0
- data/lib/plugin/scrape.rb +41 -0
- data/lib/plugin/screen.rb +24 -0
- data/lib/plugin/shell.rb +14 -0
- data/lib/plugin/sl.rb +48 -0
- data/lib/plugin/spam.rb +9 -0
- data/lib/plugin/standard_plugins.rb +269 -0
- data/lib/plugin/stdout.rb +62 -0
- data/lib/plugin/system_status.rb +33 -0
- data/lib/plugin/translation.rb +28 -0
- data/lib/plugin/update_editor.rb +53 -0
- data/lib/plugin/uri-open.rb +69 -0
- data/lib/plugin/wassr_post.rb +22 -0
- data/lib/plugin/yhara.rb +148 -0
- data/lib/plugin/yonda.rb +20 -0
- data/lib/termtter/api.rb +14 -0
- data/lib/termtter/client.rb +399 -0
- data/lib/termtter/command.rb +77 -0
- data/lib/termtter/connection.rb +39 -0
- data/lib/termtter/hook.rb +18 -0
- data/lib/termtter/status.rb +26 -0
- data/lib/termtter/task.rb +16 -0
- data/lib/termtter/task_manager.rb +116 -0
- data/lib/termtter/twitter.rb +173 -0
- data/lib/termtter/user.rb +13 -0
- data/lib/termtter/version.rb +3 -0
- data/lib/termtter.rb +157 -0
- data/spec/plugin/cool_spec.rb +10 -0
- data/spec/plugin/fib_spec.rb +16 -0
- data/spec/plugin/filter_spec.rb +18 -0
- data/spec/plugin/plugin_spec.rb +25 -0
- data/spec/plugin/shell_spec.rb +10 -0
- data/spec/plugin/spam_spec.rb +17 -0
- data/spec/plugin/standard_plugins_spec.rb +31 -0
- data/spec/spec_helper.rb +4 -0
- data/spec/termtter/client_spec.rb +175 -0
- data/spec/termtter/command_spec.rb +161 -0
- data/spec/termtter/task_manager_spec.rb +78 -0
- data/spec/termtter/task_spec.rb +22 -0
- data/spec/termtter/user_spec.rb +27 -0
- data/spec/termtter_spec.rb +43 -0
- data/test/friends_timeline.json +5 -0
- data/test/search.json +8 -0
- data/test/test_termtter.rb +86 -0
- metadata +177 -0
@@ -0,0 +1,399 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
module Termtter
|
4
|
+
class CommandNotFound < StandardError; end
|
5
|
+
|
6
|
+
module Client
|
7
|
+
|
8
|
+
class << self
|
9
|
+
|
10
|
+
def init
|
11
|
+
@@hooks = []
|
12
|
+
@@new_hooks = {}
|
13
|
+
@@commands = {}
|
14
|
+
@@new_commands = {}
|
15
|
+
@@completions = []
|
16
|
+
@@filters = []
|
17
|
+
@@helps = []
|
18
|
+
@@since_id = nil
|
19
|
+
@@main_thread = nil
|
20
|
+
@@input_thread = nil
|
21
|
+
@@task_manager = Termtter::TaskManager.new
|
22
|
+
end
|
23
|
+
|
24
|
+
def public_storage
|
25
|
+
@@public_storage ||= {}
|
26
|
+
end
|
27
|
+
|
28
|
+
%w[hook completion filter].each do |n|
|
29
|
+
eval <<-EOF
|
30
|
+
def add_#{n}(&b)
|
31
|
+
@@#{n}s << b
|
32
|
+
end
|
33
|
+
EOF
|
34
|
+
end
|
35
|
+
|
36
|
+
# Deprecated
|
37
|
+
# FIXME: delete when become unnecessary
|
38
|
+
def add_command(regex, &block)
|
39
|
+
warn "Termtter:Client.add_command method will be removed. Use Termtter::Client.register_command() instead. (#{caller.first})"
|
40
|
+
@@commands[regex] = block
|
41
|
+
end
|
42
|
+
|
43
|
+
def register_hook(arg)
|
44
|
+
hook = case arg
|
45
|
+
when Hook
|
46
|
+
arg
|
47
|
+
when Hash
|
48
|
+
Hook.new(arg)
|
49
|
+
else
|
50
|
+
raise ArgumentError, 'must be given Termtter::Hook or Hash'
|
51
|
+
end
|
52
|
+
@@new_hooks[hook.name] = hook
|
53
|
+
end
|
54
|
+
|
55
|
+
def get_hook(name)
|
56
|
+
@@new_hooks[name]
|
57
|
+
end
|
58
|
+
|
59
|
+
def get_hooks(point)
|
60
|
+
@@new_hooks.values.select do |hook|
|
61
|
+
hook.match?(point)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def register_command(arg)
|
66
|
+
command = case arg
|
67
|
+
when Command
|
68
|
+
arg
|
69
|
+
when Hash
|
70
|
+
Command.new(arg)
|
71
|
+
else
|
72
|
+
raise ArgumentError, 'must be given Termtter::Command or Hash'
|
73
|
+
end
|
74
|
+
@@new_commands[command.name] = command
|
75
|
+
end
|
76
|
+
|
77
|
+
def get_command(name)
|
78
|
+
@@new_commands[name]
|
79
|
+
end
|
80
|
+
|
81
|
+
def register_macro(name, macro, options = {})
|
82
|
+
command = {
|
83
|
+
:name => name.to_sym,
|
84
|
+
:exec_proc => lambda {|arg| call_commands(macro % arg)}
|
85
|
+
}.merge(options)
|
86
|
+
register_command(command)
|
87
|
+
end
|
88
|
+
|
89
|
+
def add_help(name, desc)
|
90
|
+
@@helps << [name, desc]
|
91
|
+
end
|
92
|
+
|
93
|
+
%w[hooks commands completions helps filters].each do |n|
|
94
|
+
eval <<-EOF
|
95
|
+
def clear_#{n}
|
96
|
+
@@#{n}.clear
|
97
|
+
end
|
98
|
+
EOF
|
99
|
+
end
|
100
|
+
|
101
|
+
# memo: each filter must return Array of Status
|
102
|
+
def apply_filters(statuses)
|
103
|
+
filtered = statuses.map{|s| s.dup }
|
104
|
+
@@filters.each do |f|
|
105
|
+
filtered = f.call(filtered)
|
106
|
+
end
|
107
|
+
filtered
|
108
|
+
rescue => e
|
109
|
+
handle_error(e)
|
110
|
+
statuses
|
111
|
+
end
|
112
|
+
|
113
|
+
def do_hooks(statuses, event)
|
114
|
+
@@hooks.each do |h|
|
115
|
+
begin
|
116
|
+
h.call(statuses.dup, event, Termtter::API.twitter)
|
117
|
+
rescue => e
|
118
|
+
handle_error(e)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
# return last hook return value
|
124
|
+
def call_new_hooks(point, *args)
|
125
|
+
result = nil
|
126
|
+
get_hooks(point).each {|hook|
|
127
|
+
break if result == false # interrupt if hook return false
|
128
|
+
result = hook.exec_proc.call(*args)
|
129
|
+
}
|
130
|
+
return result
|
131
|
+
rescue => e
|
132
|
+
if point.to_sym == :on_error
|
133
|
+
raise
|
134
|
+
else
|
135
|
+
handle_error(e)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
# TODO: delete argument "tw" when unnecessary
|
140
|
+
def call_hooks(statuses, event, tw = nil)
|
141
|
+
do_hooks(statuses, :pre_filter)
|
142
|
+
do_hooks(apply_filters(statuses), event)
|
143
|
+
end
|
144
|
+
|
145
|
+
def call_commands(text, tw = nil)
|
146
|
+
return if text.empty?
|
147
|
+
|
148
|
+
command_found = false
|
149
|
+
@@commands.each do |key, command|
|
150
|
+
if key =~ text
|
151
|
+
command_found = true
|
152
|
+
@@task_manager.invoke_and_wait do
|
153
|
+
command.call($~, Termtter::API.twitter)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
@@new_commands.each do |key, command|
|
159
|
+
command_info = command.match?(text)
|
160
|
+
if command_info
|
161
|
+
command_found = true
|
162
|
+
input_command, arg = *command_info
|
163
|
+
|
164
|
+
modified_arg = call_new_hooks("modify_arg_for_#{command.name.to_s}", input_command, arg) || arg || ''
|
165
|
+
|
166
|
+
@@task_manager.invoke_and_wait do
|
167
|
+
pre_exec_hook_result = call_new_hooks("pre_exec_#{command.name.to_s}", input_command, modified_arg)
|
168
|
+
next if pre_exec_hook_result == false
|
169
|
+
|
170
|
+
# exec command
|
171
|
+
result = command.execute(modified_arg)
|
172
|
+
if result
|
173
|
+
call_new_hooks("post_exec_#{command.name.to_s}", input_command, modified_arg, result)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
raise CommandNotFound unless command_found
|
180
|
+
end
|
181
|
+
|
182
|
+
def pause
|
183
|
+
@@task_manager.pause
|
184
|
+
end
|
185
|
+
|
186
|
+
def resume
|
187
|
+
@@task_manager.resume
|
188
|
+
end
|
189
|
+
|
190
|
+
def add_task(*arg, &block)
|
191
|
+
@@task_manager.add_task(*arg, &block)
|
192
|
+
end
|
193
|
+
|
194
|
+
def exit
|
195
|
+
puts 'finalizing...'
|
196
|
+
|
197
|
+
call_hooks([], :exit)
|
198
|
+
call_new_hooks(:exit)
|
199
|
+
@@task_manager.kill
|
200
|
+
@@main_thread.kill if @@main_thread
|
201
|
+
@@input_thread.kill if @@input_thread
|
202
|
+
end
|
203
|
+
|
204
|
+
def load_default_plugins
|
205
|
+
plugin 'standard_plugins'
|
206
|
+
plugin 'stdout'
|
207
|
+
configatron.set_default(:devel, false)
|
208
|
+
if configatron.devel
|
209
|
+
plugin 'devel'
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
def load_config
|
214
|
+
conf_file = File.expand_path('~/.termtter')
|
215
|
+
if File.exist? conf_file
|
216
|
+
wrap_require do
|
217
|
+
load conf_file
|
218
|
+
end
|
219
|
+
else
|
220
|
+
ui = create_highline
|
221
|
+
username = ui.ask('your twitter username: ')
|
222
|
+
password = ui.ask('your twitter password: ') { |q| q.echo = false }
|
223
|
+
|
224
|
+
File.open(File.expand_path('~/.termtter'), 'w') {|io|
|
225
|
+
plugins = Dir.glob(File.dirname(__FILE__) + "/../lib/plugin/*.rb").map {|f|
|
226
|
+
f.match(%r|lib/plugin/(.*?).rb$|)[1]
|
227
|
+
}
|
228
|
+
plugins -= %w[stdout standard_plugins]
|
229
|
+
plugins.each do |p|
|
230
|
+
io.puts "#plugin '#{p}'"
|
231
|
+
end
|
232
|
+
|
233
|
+
io.puts
|
234
|
+
io.puts "configatron.user_name = '#{username}'"
|
235
|
+
io.puts "configatron.password = '#{password}'"
|
236
|
+
io.puts "#configatron.update_interval = 120"
|
237
|
+
io.puts "#configatron.proxy.host = 'proxy host'"
|
238
|
+
io.puts "#configatron.proxy.port = '8080'"
|
239
|
+
io.puts "#configatron.proxy.user_name = 'proxy user'"
|
240
|
+
io.puts "#configatron.proxy.password = 'proxy password'"
|
241
|
+
io.puts
|
242
|
+
io.puts "# vim: set filetype=ruby"
|
243
|
+
}
|
244
|
+
puts "generated: ~/.termtter"
|
245
|
+
puts "enjoy!"
|
246
|
+
wrap_require do
|
247
|
+
load conf_file
|
248
|
+
end
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
def setup_readline
|
253
|
+
Readline.basic_word_break_characters= "\t\n\"\\'`><=;|&{("
|
254
|
+
Readline.completion_proc = lambda {|input|
|
255
|
+
begin
|
256
|
+
# FIXME: when migrate to Termtter::Command
|
257
|
+
completions = @@completions.map {|completion|
|
258
|
+
completion.call(input)
|
259
|
+
}
|
260
|
+
completions += @@new_commands.map {|name, command|
|
261
|
+
command.complement(input)
|
262
|
+
}
|
263
|
+
completions.flatten.compact
|
264
|
+
rescue => e
|
265
|
+
handle_error(e)
|
266
|
+
end
|
267
|
+
}
|
268
|
+
vi_or_emacs = configatron.editing_mode
|
269
|
+
unless vi_or_emacs.empty?
|
270
|
+
Readline.__send__("#{vi_or_emacs}_editing_mode")
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
def setup_update_timeline_task()
|
275
|
+
register_command(
|
276
|
+
:name => :_update_timeline,
|
277
|
+
:exec_proc => lambda {|arg|
|
278
|
+
begin
|
279
|
+
statuses = Termtter::API.twitter.get_friends_timeline(@@since_id)
|
280
|
+
unless statuses.empty?
|
281
|
+
@@since_id = statuses[0].id
|
282
|
+
end
|
283
|
+
print "\e[1K\e[0G" if !statuses.empty? && !win?
|
284
|
+
call_hooks(statuses, :update_friends_timeline)
|
285
|
+
@@input_thread.kill if @@input_thread && !statuses.empty?
|
286
|
+
rescue OpenURI::HTTPError => e
|
287
|
+
if e.message == '401 Unauthorized'
|
288
|
+
puts 'Could not login'
|
289
|
+
puts 'plese check your account settings'
|
290
|
+
exit!
|
291
|
+
end
|
292
|
+
end
|
293
|
+
}
|
294
|
+
)
|
295
|
+
|
296
|
+
add_task(:name => :update_timeline, :interval => configatron.update_interval) do
|
297
|
+
call_commands('_update_timeline')
|
298
|
+
end
|
299
|
+
end
|
300
|
+
|
301
|
+
def trap_setting()
|
302
|
+
begin
|
303
|
+
stty_save = `stty -g`.chomp
|
304
|
+
trap("INT") do
|
305
|
+
begin
|
306
|
+
system "stty", stty_save
|
307
|
+
ensure
|
308
|
+
exit
|
309
|
+
end
|
310
|
+
end
|
311
|
+
rescue Errno::ENOENT
|
312
|
+
end
|
313
|
+
end
|
314
|
+
|
315
|
+
def start_input_thread
|
316
|
+
setup_readline()
|
317
|
+
trap_setting()
|
318
|
+
@@main_thread = Thread.new do
|
319
|
+
loop do
|
320
|
+
@@input_thread = create_input_thread()
|
321
|
+
@@input_thread.join
|
322
|
+
end
|
323
|
+
end
|
324
|
+
@@main_thread.join
|
325
|
+
end
|
326
|
+
|
327
|
+
def run
|
328
|
+
puts 'initializing...'
|
329
|
+
|
330
|
+
load_default_plugins()
|
331
|
+
load_config()
|
332
|
+
Termtter::API.setup()
|
333
|
+
|
334
|
+
call_hooks([], :initialize)
|
335
|
+
call_new_hooks(:initialize)
|
336
|
+
|
337
|
+
setup_update_timeline_task()
|
338
|
+
call_commands('_update_timeline')
|
339
|
+
|
340
|
+
@@task_manager.run()
|
341
|
+
start_input_thread()
|
342
|
+
end
|
343
|
+
|
344
|
+
def create_input_thread()
|
345
|
+
Thread.new do
|
346
|
+
while buf = Readline.readline(ERB.new(configatron.prompt).result(API.twitter.__send__(:binding)), true)
|
347
|
+
Readline::HISTORY.pop if /^(u|update)\s+(.+)$/ =~ buf
|
348
|
+
begin
|
349
|
+
call_commands(buf)
|
350
|
+
rescue CommandNotFound => e
|
351
|
+
puts "Unknown command \"#{buf}\""
|
352
|
+
puts 'Enter "help" for instructions'
|
353
|
+
end
|
354
|
+
end
|
355
|
+
exit # exit when press Control-D
|
356
|
+
end
|
357
|
+
end
|
358
|
+
|
359
|
+
def create_highline
|
360
|
+
HighLine.track_eof = false
|
361
|
+
if $stdin.respond_to?(:getbyte) # for ruby1.9
|
362
|
+
require 'delegate'
|
363
|
+
stdin_for_highline = SimpleDelegator.new($stdin)
|
364
|
+
def stdin_for_highline.getc
|
365
|
+
getbyte
|
366
|
+
end
|
367
|
+
else
|
368
|
+
stdin_for_highline = $stdin
|
369
|
+
end
|
370
|
+
return HighLine.new(stdin_for_highline)
|
371
|
+
end
|
372
|
+
|
373
|
+
def handle_error(e)
|
374
|
+
call_new_hooks("on_error", e)
|
375
|
+
rescue => e
|
376
|
+
puts "Error: #{e}"
|
377
|
+
puts e.backtrace.join("\n")
|
378
|
+
end
|
379
|
+
|
380
|
+
def wrap_require
|
381
|
+
# FIXME: delete this method after the major version up
|
382
|
+
alias original_require require
|
383
|
+
def require(s)
|
384
|
+
if %r|^termtter/(.*)| =~ s
|
385
|
+
puts "[WARNING] use plugin '#{$1}' instead of require"
|
386
|
+
puts " Such a legacy .termtter file will not be supported until version 1.0.0"
|
387
|
+
s = "plugin/#{$1}"
|
388
|
+
end
|
389
|
+
original_require s
|
390
|
+
end
|
391
|
+
yield
|
392
|
+
alias require original_require
|
393
|
+
end
|
394
|
+
private :wrap_require
|
395
|
+
end
|
396
|
+
end
|
397
|
+
end
|
398
|
+
|
399
|
+
Termtter::Client.init
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
module Termtter
|
4
|
+
class Command
|
5
|
+
attr_accessor :name, :aliases, :exec_proc, :completion_proc, :help
|
6
|
+
|
7
|
+
# args
|
8
|
+
# name: Symbol as command name
|
9
|
+
# aliases: Array of command alias (ex. ['u', 'up'])
|
10
|
+
# exec_proc: Proc for procedure of the command. If need the proc must return object for hook.
|
11
|
+
# completion_proc: Proc for input completion. The proc must return Array of candidates (Optional)
|
12
|
+
# help: help text for the command (Optional)
|
13
|
+
def initialize(args)
|
14
|
+
raise ArgumentError, ":name is not given." unless args.has_key?(:name)
|
15
|
+
@name = args[:name].to_sym
|
16
|
+
@aliases = args[:aliases] ? args[:aliases].map {|i| i.to_sym } : []
|
17
|
+
@exec_proc = args[:exec_proc] || lambda {|arg|}
|
18
|
+
@completion_proc = args[:completion_proc] || lambda {|command, arg| [] }
|
19
|
+
@help = args[:help]
|
20
|
+
end
|
21
|
+
|
22
|
+
def complement(input)
|
23
|
+
command_info = match?(input)
|
24
|
+
if command_info
|
25
|
+
[completion_proc.call(command_info[0], command_info[1] || '')].flatten.compact
|
26
|
+
else
|
27
|
+
[name.to_s].grep(/^#{Regexp.quote(input)}/)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# MEMO: Termtter:Client からはこのメソッドを呼び出すことになると思う。
|
32
|
+
def exec_if_match(input)
|
33
|
+
command_info = match?(input)
|
34
|
+
if command_info
|
35
|
+
result = execute(command_info[1])
|
36
|
+
unless result.nil?
|
37
|
+
return result
|
38
|
+
else
|
39
|
+
return true
|
40
|
+
end
|
41
|
+
else
|
42
|
+
return nil
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# return array like [command, arg]
|
47
|
+
def match?(input)
|
48
|
+
if pattern =~ input
|
49
|
+
[$2 || $3, $4] # $2 or $3 => command, $4 => argument
|
50
|
+
else
|
51
|
+
nil
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def execute(arg)
|
56
|
+
arg = case arg
|
57
|
+
when nil
|
58
|
+
''
|
59
|
+
when String
|
60
|
+
arg
|
61
|
+
else
|
62
|
+
raise ArgumentError, 'arg should be String or nil'
|
63
|
+
end
|
64
|
+
exec_proc.call(arg)
|
65
|
+
end
|
66
|
+
|
67
|
+
def pattern
|
68
|
+
commands_regex = commands.map {|i| Regexp.quote(i.to_s) }.join('|')
|
69
|
+
/^((#{commands_regex})|(#{commands_regex})\s+(.*?))\s*$/
|
70
|
+
end
|
71
|
+
|
72
|
+
def commands
|
73
|
+
aliases.unshift(name)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
module Termtter
|
4
|
+
class Connection
|
5
|
+
attr_reader :protocol, :port, :proxy_uri
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@proxy_host = configatron.proxy.host
|
9
|
+
@proxy_port = configatron.proxy.port
|
10
|
+
@proxy_user = configatron.proxy.user_name
|
11
|
+
@proxy_password = configatron.proxy.password
|
12
|
+
@proxy_uri = nil
|
13
|
+
@enable_ssl = configatron.enable_ssl
|
14
|
+
@protocol = "http"
|
15
|
+
@port = 80
|
16
|
+
|
17
|
+
unless @proxy_host.empty?
|
18
|
+
@http_class = Net::HTTP::Proxy(@proxy_host, @proxy_port,
|
19
|
+
@proxy_user, @proxy_password)
|
20
|
+
@proxy_uri = "http://" + @proxy_host + ":" + @proxy_port + "/"
|
21
|
+
else
|
22
|
+
@http_class = Net::HTTP
|
23
|
+
end
|
24
|
+
|
25
|
+
if @enable_ssl
|
26
|
+
@protocol = "https"
|
27
|
+
@port = 443
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def start(host, port, &block)
|
32
|
+
http = @http_class.new(host, port)
|
33
|
+
http.use_ssl = @enable_ssl
|
34
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE if http.use_ssl?
|
35
|
+
http.start(&block)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
module Termtter
|
4
|
+
class Hook
|
5
|
+
attr_accessor :name, :points, :exec_proc
|
6
|
+
|
7
|
+
def initialize(args)
|
8
|
+
raise ArgumentError, ":name is not given." unless args.has_key?(:name)
|
9
|
+
@name = args[:name].to_sym
|
10
|
+
@points = args[:points] ? args[:points].map {|i| i.to_sym } : []
|
11
|
+
@exec_proc = args[:exec_proc] || lambda {}
|
12
|
+
end
|
13
|
+
|
14
|
+
def match?(point)
|
15
|
+
points.include?(point.to_sym)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
module Termtter
|
4
|
+
class Status
|
5
|
+
%w(
|
6
|
+
id text created_at truncated
|
7
|
+
in_reply_to_status_id in_reply_to_user_id in_reply_to_screen_name
|
8
|
+
user_id user_name user_screen_name user_url user_profile_image_url
|
9
|
+
).each do |attr|
|
10
|
+
attr_accessor attr.to_sym
|
11
|
+
end
|
12
|
+
|
13
|
+
def eql?(other); self.id == other.id end
|
14
|
+
def hash; self.id end
|
15
|
+
|
16
|
+
def english?
|
17
|
+
self.class.english?(self.text)
|
18
|
+
end
|
19
|
+
|
20
|
+
# english? :: String -> Boolean
|
21
|
+
def self.english?(message)
|
22
|
+
/[一-龠]+|[ぁ-ん]+|[ァ-ヴー]+|[a-zA-Z0-9]+/ !~ message
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
module Termtter
|
4
|
+
class Task
|
5
|
+
attr_accessor :name, :exec_at, :exec_proc, :interval
|
6
|
+
def initialize(args = {}, &block)
|
7
|
+
@name = args[:name]
|
8
|
+
@exec_at = Time.now + (args[:after] || 0)
|
9
|
+
@interval = args[:interval]
|
10
|
+
@exec_proc = block || lambda {}
|
11
|
+
end
|
12
|
+
def execute
|
13
|
+
exec_proc.call
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
module Termtter
|
4
|
+
class TaskManager
|
5
|
+
|
6
|
+
INTERVAL = 1
|
7
|
+
|
8
|
+
def initialize()
|
9
|
+
@tasks = {}
|
10
|
+
@work = true
|
11
|
+
@mutex = Mutex.new
|
12
|
+
@pause = false
|
13
|
+
end
|
14
|
+
|
15
|
+
def pause
|
16
|
+
@pause = true
|
17
|
+
end
|
18
|
+
|
19
|
+
def resume
|
20
|
+
@pause = false
|
21
|
+
end
|
22
|
+
|
23
|
+
def kill
|
24
|
+
@work = false
|
25
|
+
end
|
26
|
+
|
27
|
+
def run
|
28
|
+
Thread.new do
|
29
|
+
while @work
|
30
|
+
step unless @pause
|
31
|
+
sleep INTERVAL
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def step
|
37
|
+
pull_due_tasks().each do |task|
|
38
|
+
invoke_and_wait do
|
39
|
+
task.execute
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def invoke_later
|
45
|
+
Thread.new do
|
46
|
+
invoke_and_wait do
|
47
|
+
yield
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def invoke_and_wait(&block)
|
53
|
+
synchronize do
|
54
|
+
begin
|
55
|
+
yield
|
56
|
+
rescue => e
|
57
|
+
Termtter::Client.handle_error(e)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def add_task(args = {}, &block)
|
63
|
+
synchronize do
|
64
|
+
task = Task.new(args, &block)
|
65
|
+
@tasks[task.name || task.object_id] = task
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def get_task(key)
|
70
|
+
synchronize do
|
71
|
+
@tasks[key]
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def delete_task(key)
|
76
|
+
synchronize do
|
77
|
+
@tasks.delete(key)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
private
|
82
|
+
|
83
|
+
def synchronize
|
84
|
+
unless Thread.current == @thread_in_sync
|
85
|
+
@mutex.synchronize do
|
86
|
+
@thread_in_sync = Thread.current
|
87
|
+
yield
|
88
|
+
end
|
89
|
+
else
|
90
|
+
yield
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def pull_due_tasks()
|
95
|
+
synchronize do
|
96
|
+
time_now = Time.now
|
97
|
+
due_tasks = []
|
98
|
+
@tasks.delete_if do |key, task|
|
99
|
+
if task.exec_at <= time_now
|
100
|
+
due_tasks << task
|
101
|
+
if task.interval
|
102
|
+
task.exec_at = time_now + task.interval
|
103
|
+
false
|
104
|
+
else
|
105
|
+
true
|
106
|
+
end
|
107
|
+
else
|
108
|
+
false
|
109
|
+
end
|
110
|
+
end
|
111
|
+
return due_tasks
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
116
|
+
end
|