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/Manifest.txt +23 -0
- data/bin/termtter +0 -39
- data/lib/filter/english.rb +1 -1
- data/lib/filter/ignore.rb +17 -0
- data/lib/filter/reply.rb +8 -0
- data/lib/filter/yhara.rb +1 -1
- data/lib/plugin/cool.rb +12 -0
- data/lib/plugin/favorite.rb +10 -7
- data/lib/plugin/fib.rb +1 -0
- data/lib/plugin/follow.rb +2 -2
- data/lib/plugin/graduatter.rb +7 -0
- data/lib/plugin/group.rb +15 -3
- data/lib/plugin/growl.rb +15 -4
- data/lib/plugin/hatebu.rb +57 -0
- data/lib/plugin/history.rb +10 -2
- data/lib/plugin/log.rb +33 -2
- data/lib/plugin/msagent.rb +24 -0
- data/lib/plugin/multi_reply.rb +34 -0
- data/lib/plugin/otsune.rb +17 -0
- data/lib/plugin/outputz.rb +33 -0
- data/lib/plugin/primes.rb +21 -0
- data/lib/plugin/reblog.rb +38 -0
- data/lib/plugin/say.rb +1 -1
- data/lib/plugin/scrape.rb +43 -0
- data/lib/plugin/screen.rb +22 -0
- data/lib/plugin/shell.rb +1 -1
- data/lib/plugin/sl.rb +7 -7
- data/lib/plugin/standard_plugins.rb +100 -48
- data/lib/plugin/stdout.rb +27 -71
- data/lib/plugin/system_status.rb +41 -0
- data/lib/plugin/update_editor.rb +53 -0
- data/lib/plugin/wassr_post.rb +21 -0
- data/lib/plugin/yonda.rb +18 -0
- data/lib/termtter/api.rb +12 -0
- data/lib/termtter/client.rb +298 -0
- data/lib/termtter/command.rb +67 -0
- data/lib/termtter/connection.rb +37 -0
- data/lib/termtter/status.rb +24 -0
- data/lib/termtter/twitter.rb +138 -0
- data/lib/termtter.rb +90 -410
- data/run_termtter.rb +0 -11
- metadata +25 -2
@@ -0,0 +1,298 @@
|
|
1
|
+
module Termtter
|
2
|
+
class CommandNotFound < StandardError; end
|
3
|
+
|
4
|
+
module Client
|
5
|
+
|
6
|
+
@@hooks = []
|
7
|
+
@@commands = {}
|
8
|
+
@@new_commands = {}
|
9
|
+
@@completions = []
|
10
|
+
@@filters = []
|
11
|
+
@@helps = []
|
12
|
+
|
13
|
+
class << self
|
14
|
+
def public_storage
|
15
|
+
@@public_storage ||= {}
|
16
|
+
end
|
17
|
+
|
18
|
+
%w[hook completion filter].each do |n|
|
19
|
+
eval <<-EOF
|
20
|
+
def add_#{n}(&b)
|
21
|
+
@@#{n}s << b
|
22
|
+
end
|
23
|
+
EOF
|
24
|
+
end
|
25
|
+
|
26
|
+
def add_command(regex, &block)
|
27
|
+
@@commands[regex] = block
|
28
|
+
end
|
29
|
+
|
30
|
+
def register_command(arg)
|
31
|
+
command = case arg
|
32
|
+
when Command
|
33
|
+
arg
|
34
|
+
when Hash
|
35
|
+
Command.new(arg)
|
36
|
+
else
|
37
|
+
raise ArgumentError, 'must be given Termtter::Command or Hash'
|
38
|
+
end
|
39
|
+
@@new_commands[command.name] = command
|
40
|
+
end
|
41
|
+
|
42
|
+
def get_command(name)
|
43
|
+
@@new_commands[name]
|
44
|
+
end
|
45
|
+
|
46
|
+
def add_macro(r, s)
|
47
|
+
add_command(r) do |m, t|
|
48
|
+
call_commands(s % m.to_a[1..-1])
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def add_help(name, desc)
|
53
|
+
@@helps << [name, desc]
|
54
|
+
end
|
55
|
+
|
56
|
+
%w[hooks commands completions helps filters].each do |n|
|
57
|
+
eval <<-EOF
|
58
|
+
def clear_#{n}
|
59
|
+
@@#{n}.clear
|
60
|
+
end
|
61
|
+
EOF
|
62
|
+
end
|
63
|
+
|
64
|
+
# memo: each filter must return Array of Status
|
65
|
+
def apply_filters(statuses)
|
66
|
+
filtered = statuses.map{|s| s.dup }
|
67
|
+
@@filters.each do |f|
|
68
|
+
filtered = f.call(filtered)
|
69
|
+
end
|
70
|
+
filtered
|
71
|
+
rescue => e
|
72
|
+
handle_error(e)
|
73
|
+
statuses
|
74
|
+
end
|
75
|
+
|
76
|
+
def do_hooks(statuses, event)
|
77
|
+
@@hooks.each do |h|
|
78
|
+
begin
|
79
|
+
h.call(statuses.dup, event, Termtter::API.twitter)
|
80
|
+
rescue => e
|
81
|
+
handle_error(e)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# TODO: delete argument "tw" when unnecessary
|
87
|
+
def call_hooks(statuses, event, tw = nil)
|
88
|
+
do_hooks(statuses, :pre_filter)
|
89
|
+
do_hooks(apply_filters(statuses), event)
|
90
|
+
end
|
91
|
+
|
92
|
+
def call_commands(text, tw = nil)
|
93
|
+
return if text.empty?
|
94
|
+
|
95
|
+
command_found = false
|
96
|
+
@@commands.each do |key, command|
|
97
|
+
if key =~ text
|
98
|
+
command_found = true
|
99
|
+
begin
|
100
|
+
command.call($~, Termtter::API.twitter)
|
101
|
+
rescue => e
|
102
|
+
handle_error(e)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
@@new_commands.each do |key, command|
|
108
|
+
command_info = command.match?(text)
|
109
|
+
# TODO: call hook for before command here.
|
110
|
+
if command_info
|
111
|
+
command_found = true
|
112
|
+
result = command.execute(command_info[1])
|
113
|
+
if result
|
114
|
+
# TODO: call hook for after command with result.
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
raise CommandNotFound unless command_found
|
120
|
+
end
|
121
|
+
|
122
|
+
def pause
|
123
|
+
@@pause = true
|
124
|
+
end
|
125
|
+
|
126
|
+
def resume
|
127
|
+
@@pause = false
|
128
|
+
@@update_thread.run
|
129
|
+
end
|
130
|
+
|
131
|
+
def exit
|
132
|
+
call_hooks([], :exit)
|
133
|
+
@@main_thread.kill
|
134
|
+
@@update_thread.kill
|
135
|
+
@@input_thread.kill
|
136
|
+
end
|
137
|
+
|
138
|
+
def load_default_plugins
|
139
|
+
plugin 'standard_plugins'
|
140
|
+
plugin 'stdout'
|
141
|
+
end
|
142
|
+
|
143
|
+
def load_config
|
144
|
+
conf_file = File.expand_path('~/.termtter')
|
145
|
+
if File.exist? conf_file
|
146
|
+
wrap_require do
|
147
|
+
load conf_file
|
148
|
+
end
|
149
|
+
else
|
150
|
+
HighLine.track_eof = false
|
151
|
+
ui = HighLine.new
|
152
|
+
username = ui.ask('your twitter username: ')
|
153
|
+
password = ui.ask('your twitter password: ') { |q| q.echo = false }
|
154
|
+
|
155
|
+
File.open(File.expand_path('~/.termtter'), 'w') {|io|
|
156
|
+
plugins = Dir.glob(File.dirname(__FILE__) + "/../lib/plugin/*.rb").map {|f|
|
157
|
+
f.match(%r|lib/plugin/(.*?).rb$|)[1]
|
158
|
+
}
|
159
|
+
plugins -= %w[stdout standard_plugins]
|
160
|
+
plugins.each do |p|
|
161
|
+
io.puts "#plugin '#{p}'"
|
162
|
+
end
|
163
|
+
|
164
|
+
io.puts
|
165
|
+
io.puts "configatron.user_name = '#{username}'"
|
166
|
+
io.puts "configatron.password = '#{password}'"
|
167
|
+
io.puts "#configatron.update_interval = 120"
|
168
|
+
io.puts "#configatron.proxy.host = 'proxy host'"
|
169
|
+
io.puts "#configatron.proxy.port = '8080'"
|
170
|
+
io.puts "#configatron.proxy.user_name = 'proxy user'"
|
171
|
+
io.puts "#configatron.proxy.password = 'proxy password'"
|
172
|
+
io.puts
|
173
|
+
io.puts "# vim: set filetype=ruby"
|
174
|
+
}
|
175
|
+
puts "generated: ~/.termtter"
|
176
|
+
puts "enjoy!"
|
177
|
+
wrap_require do
|
178
|
+
load conf_file
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
def setup_readline
|
184
|
+
Readline.basic_word_break_characters= "\t\n\"\\'`><=;|&{("
|
185
|
+
Readline.completion_proc = proc {|input|
|
186
|
+
begin
|
187
|
+
# FIXME: when migrate to Termtter::Command
|
188
|
+
completions = @@completions.map {|completion|
|
189
|
+
completion.call(input)
|
190
|
+
}
|
191
|
+
completions += @@new_commands.map {|name, command|
|
192
|
+
command.complement(input)
|
193
|
+
}
|
194
|
+
completions.flatten.compact
|
195
|
+
rescue => e
|
196
|
+
handle_error(e)
|
197
|
+
end
|
198
|
+
}
|
199
|
+
vi_or_emacs = configatron.editing_mode
|
200
|
+
unless vi_or_emacs.empty?
|
201
|
+
Readline.__send__("#{vi_or_emacs}_editing_mode")
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
def setup_api()
|
206
|
+
Termtter::API.setup()
|
207
|
+
end
|
208
|
+
|
209
|
+
def run
|
210
|
+
load_default_plugins()
|
211
|
+
load_config()
|
212
|
+
setup_readline()
|
213
|
+
setup_api()
|
214
|
+
|
215
|
+
puts 'initializing...'
|
216
|
+
initialized = false
|
217
|
+
@@pause = false
|
218
|
+
call_hooks([], :initialize)
|
219
|
+
|
220
|
+
@@input_thread = nil
|
221
|
+
@@update_thread = Thread.new do
|
222
|
+
since_id = nil
|
223
|
+
loop do
|
224
|
+
begin
|
225
|
+
Thread.stop if @@pause
|
226
|
+
|
227
|
+
statuses = Termtter::API.twitter.get_friends_timeline(since_id)
|
228
|
+
unless statuses.empty?
|
229
|
+
since_id = statuses[0].id
|
230
|
+
end
|
231
|
+
print "\e[1K\e[0G" if !statuses.empty? && !win?
|
232
|
+
call_hooks(statuses, :update_friends_timeline)
|
233
|
+
initialized = true
|
234
|
+
@@input_thread.kill if @@input_thread && !statuses.empty?
|
235
|
+
rescue OpenURI::HTTPError => e
|
236
|
+
if e.message == '401 Unauthorized'
|
237
|
+
puts 'Could not login'
|
238
|
+
puts 'plese check your account settings'
|
239
|
+
exit!
|
240
|
+
end
|
241
|
+
ensure
|
242
|
+
sleep configatron.update_interval
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
until initialized; end
|
248
|
+
|
249
|
+
begin
|
250
|
+
stty_save = `stty -g`.chomp
|
251
|
+
trap("INT") { system "stty", stty_save; exit }
|
252
|
+
rescue Errno::ENOENT
|
253
|
+
end
|
254
|
+
|
255
|
+
@@main_thread = Thread.new do
|
256
|
+
loop do
|
257
|
+
@@input_thread = create_input_thread()
|
258
|
+
@@input_thread.join
|
259
|
+
end
|
260
|
+
end
|
261
|
+
@@main_thread.join
|
262
|
+
end
|
263
|
+
|
264
|
+
def create_input_thread()
|
265
|
+
Thread.new do
|
266
|
+
erb = ERB.new(configatron.prompt)
|
267
|
+
while buf = Readline.readline(erb.result(Termtter::API.twitter.__send__(:binding)), true)
|
268
|
+
Readline::HISTORY.pop if /^(u|update)\s+(.+)$/ =~ buf
|
269
|
+
begin
|
270
|
+
call_commands(buf)
|
271
|
+
rescue CommandNotFound => e
|
272
|
+
puts "Unknown command \"#{buf}\""
|
273
|
+
puts 'Enter "help" for instructions'
|
274
|
+
end
|
275
|
+
end
|
276
|
+
exit # exit when press Control-D
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
def wrap_require
|
281
|
+
# FIXME: delete this method after the major version up
|
282
|
+
alias original_require require
|
283
|
+
def require(s)
|
284
|
+
if %r|^termtter/(.*)| =~ s
|
285
|
+
puts "[WARNING] use plugin '#{$1}' instead of require"
|
286
|
+
puts " Such a legacy .termtter file will not be supported until version 1.0.0"
|
287
|
+
s = "plugin/#{$1}"
|
288
|
+
end
|
289
|
+
original_require s
|
290
|
+
end
|
291
|
+
yield
|
292
|
+
alias require original_require
|
293
|
+
end
|
294
|
+
private :wrap_require
|
295
|
+
end
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module Termtter
|
2
|
+
class Command
|
3
|
+
attr_accessor :name, :aliases, :exec_proc, :completion_proc, :help
|
4
|
+
|
5
|
+
# args
|
6
|
+
# name: Symbol as command name
|
7
|
+
# aliases: Array of command alias (ex. ['u', 'up'])
|
8
|
+
# exec_proc: Proc for procedure of the command. If need the proc must return object for hook.
|
9
|
+
# completion_proc: Proc for input completion. The proc must return Array of candidates (Optional)
|
10
|
+
# help: help text for the command (Optional)
|
11
|
+
def initialize(args)
|
12
|
+
raise ArgumentError, ":name is not given." unless args.has_key?(:name)
|
13
|
+
@name = args[:name].to_sym
|
14
|
+
@aliases = args[:aliases] || []
|
15
|
+
@exec_proc = args[:exec_proc] || proc {|arg|}
|
16
|
+
@completion_proc = args[:completion_proc] || proc {|command, arg| [] }
|
17
|
+
@help = args[:help]
|
18
|
+
end
|
19
|
+
|
20
|
+
def complement(input)
|
21
|
+
command_info = match?(input)
|
22
|
+
if command_info
|
23
|
+
[completion_proc.call(command_info[0], command_info[1])].flatten.compact
|
24
|
+
else
|
25
|
+
[name.to_s].grep(/^#{Regexp.quote(input)}/)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# MEMO: Termtter:Client からはこのメソッドを呼び出すことになると思う。
|
30
|
+
def exec_if_match(input)
|
31
|
+
command_info = match?(input)
|
32
|
+
if command_info
|
33
|
+
result = execute(command_info[1])
|
34
|
+
unless result.nil?
|
35
|
+
return result
|
36
|
+
else
|
37
|
+
return true
|
38
|
+
end
|
39
|
+
else
|
40
|
+
return nil
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# return array like [command, arg]
|
45
|
+
def match?(input)
|
46
|
+
if pattern =~ input
|
47
|
+
[$2 || $3, $4] # $2 or $3 => command, $4 => argument
|
48
|
+
else
|
49
|
+
nil
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def execute(arg)
|
54
|
+
exec_proc.call(arg)
|
55
|
+
end
|
56
|
+
|
57
|
+
def pattern
|
58
|
+
commands_regex = commands.map {|i| Regexp.quote(i) }.join('|')
|
59
|
+
/^\s*((#{commands_regex})|(#{commands_regex})\s+(.*?))\s*$/
|
60
|
+
end
|
61
|
+
|
62
|
+
def commands
|
63
|
+
aliases.unshift(name.to_s)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Termtter
|
2
|
+
class Connection
|
3
|
+
attr_reader :protocol, :port, :proxy_uri
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@proxy_host = configatron.proxy.host
|
7
|
+
@proxy_port = configatron.proxy.port
|
8
|
+
@proxy_user = configatron.proxy.user_name
|
9
|
+
@proxy_password = configatron.proxy.password
|
10
|
+
@proxy_uri = nil
|
11
|
+
@enable_ssl = configatron.enable_ssl
|
12
|
+
@protocol = "http"
|
13
|
+
@port = 80
|
14
|
+
|
15
|
+
unless @proxy_host.empty?
|
16
|
+
@http_class = Net::HTTP::Proxy(@proxy_host, @proxy_port,
|
17
|
+
@proxy_user, @proxy_password)
|
18
|
+
@proxy_uri = "http://" + @proxy_host + ":" + @proxy_port + "/"
|
19
|
+
else
|
20
|
+
@http_class = Net::HTTP
|
21
|
+
end
|
22
|
+
|
23
|
+
if @enable_ssl
|
24
|
+
@protocol = "https"
|
25
|
+
@port = 443
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def start(host, port, &block)
|
30
|
+
http = @http_class.new(host, port)
|
31
|
+
http.use_ssl = @enable_ssl
|
32
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE if http.use_ssl
|
33
|
+
http.start(&block)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Termtter
|
2
|
+
class Status
|
3
|
+
%w(
|
4
|
+
id text created_at truncated
|
5
|
+
in_reply_to_status_id in_reply_to_user_id in_reply_to_screen_name
|
6
|
+
user_id user_name user_screen_name user_url user_profile_image_url
|
7
|
+
).each do |attr|
|
8
|
+
attr_accessor attr.to_sym
|
9
|
+
end
|
10
|
+
|
11
|
+
def eql?(other); self.id == other.id end
|
12
|
+
def hash; self.id end
|
13
|
+
|
14
|
+
def english?
|
15
|
+
self.class.english?(self.text)
|
16
|
+
end
|
17
|
+
|
18
|
+
# english? :: String -> Boolean
|
19
|
+
def self.english?(message)
|
20
|
+
/[一-龠]+|[ぁ-ん]+|[ァ-ヴー]+|[a-zA-Z0-9]+/ !~ message
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
@@ -0,0 +1,138 @@
|
|
1
|
+
require 'highline'
|
2
|
+
|
3
|
+
module Termtter
|
4
|
+
class Twitter
|
5
|
+
|
6
|
+
def initialize(user_name, password, connection)
|
7
|
+
@user_name = user_name
|
8
|
+
@password = password
|
9
|
+
@connection = connection
|
10
|
+
end
|
11
|
+
|
12
|
+
def update_status(status)
|
13
|
+
@connection.start("twitter.com", @connection.port) do |http|
|
14
|
+
uri = '/statuses/update.xml'
|
15
|
+
http.request(post_request(uri), "status=#{CGI.escape(status)}&source=#{APP_NAME}")
|
16
|
+
end
|
17
|
+
status
|
18
|
+
end
|
19
|
+
|
20
|
+
def direct_message(user, status)
|
21
|
+
@connection.start("twitter.com", @connection.port) do |http|
|
22
|
+
uri = '/direct_messages/new.xml'
|
23
|
+
http.request(post_request(uri), "user=#{CGI.escape(user)}&text=#{CGI.escape(status)}&source=#{APP_NAME}")
|
24
|
+
end
|
25
|
+
[user, status]
|
26
|
+
end
|
27
|
+
|
28
|
+
def get_user_profile(screen_name)
|
29
|
+
uri = "#{@connection.protocol}://twitter.com/users/show/#{screen_name}.json"
|
30
|
+
return JSON.parse(open(uri, :http_basic_authentication => [user_name, password], :proxy => @connection.proxy_uri).read)
|
31
|
+
end
|
32
|
+
|
33
|
+
def get_friends_timeline(since_id = nil)
|
34
|
+
uri = "#{@connection.protocol}://twitter.com/statuses/friends_timeline.json"
|
35
|
+
uri << "?since_id=#{since_id}" if since_id
|
36
|
+
return get_timeline(uri)
|
37
|
+
end
|
38
|
+
|
39
|
+
def get_user_timeline(screen_name)
|
40
|
+
return get_timeline("#{@connection.protocol}://twitter.com/statuses/user_timeline/#{screen_name}.json")
|
41
|
+
rescue OpenURI::HTTPError => e
|
42
|
+
puts "No such user: #{screen_name}"
|
43
|
+
nears = near_users(screen_name)
|
44
|
+
puts "near users: #{nears}" unless nears.empty?
|
45
|
+
return []
|
46
|
+
end
|
47
|
+
|
48
|
+
def search(query)
|
49
|
+
results = JSON.parse(open("#{@connection.protocol}://search.twitter.com/search.json?q=" + CGI.escape(query)).read, :proxy => @connection.proxy_uri)['results']
|
50
|
+
return results.map do |s|
|
51
|
+
status = Status.new
|
52
|
+
status.id = s['id']
|
53
|
+
status.text = CGI.unescapeHTML(s['text']).gsub(/(\n|\r)/, '').gsub(query, color(color(query, 41), 37))
|
54
|
+
status.created_at = Time.utc(*ParseDate::parsedate(s["created_at"])).localtime
|
55
|
+
status.user_screen_name = s['from_user']
|
56
|
+
status
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def show(id, rth = false)
|
61
|
+
get_status = lambda { get_timeline("#{@connection.protocol}://twitter.com/statuses/show/#{id}.json")[0] }
|
62
|
+
statuses = []
|
63
|
+
statuses << status = Array(Client.public_storage[:log]).detect(get_status) {|s| s.id == id.to_i }
|
64
|
+
statuses << show(id, true) if rth && id = status.in_reply_to_status_id
|
65
|
+
statuses.flatten
|
66
|
+
end
|
67
|
+
|
68
|
+
def replies
|
69
|
+
return get_timeline("#{@connection.protocol}://twitter.com/statuses/replies.json")
|
70
|
+
end
|
71
|
+
|
72
|
+
def get_timeline(uri)
|
73
|
+
data = JSON.parse(open(uri, :http_basic_authentication => [user_name, password], :proxy => @connection.proxy_uri).read)
|
74
|
+
data = [data] unless data.instance_of? Array
|
75
|
+
return data.map do |s|
|
76
|
+
status = Status.new
|
77
|
+
status.created_at = Time.utc(*ParseDate::parsedate(s["created_at"])).localtime
|
78
|
+
%w(id text truncated in_reply_to_status_id in_reply_to_user_id in_reply_to_screen_name).each do |key|
|
79
|
+
status.__send__("#{key}=".to_sym, s[key])
|
80
|
+
end
|
81
|
+
%w(id name screen_name url profile_image_url).each do |key|
|
82
|
+
status.__send__("user_#{key}=".to_sym, s["user"][key])
|
83
|
+
end
|
84
|
+
status.text = CGI.unescapeHTML(status.text).gsub(/(\n|\r)/, '')
|
85
|
+
status
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
# note: APILimit.reset_time_in_seconds == APILimit.reset_time.to_i
|
90
|
+
APILIMIT = Struct.new("APILimit", :reset_time, :reset_time_in_seconds, :remaining_hits, :hourly_limit)
|
91
|
+
def get_rate_limit_status
|
92
|
+
uri = 'http://twitter.com/account/rate_limit_status.json'
|
93
|
+
data = JSON.parse(open(uri, :http_basic_authentication => [user_name, password], :proxy => @connection.proxy_uri).read)
|
94
|
+
|
95
|
+
reset_time = Time.parse(data['reset_time'])
|
96
|
+
reset_time_in_seconds = data['reset_time_in_seconds'].to_i
|
97
|
+
|
98
|
+
APILIMIT.new(reset_time, reset_time_in_seconds, data['remaining_hits'], data['hourly_limit'])
|
99
|
+
end
|
100
|
+
|
101
|
+
alias :api_limit :get_rate_limit_status
|
102
|
+
|
103
|
+
private
|
104
|
+
|
105
|
+
def user_name
|
106
|
+
unless @user_name.instance_of? String
|
107
|
+
HighLine.track_eof = false
|
108
|
+
@user_name = HighLine.new.ask('your twitter username: ')
|
109
|
+
end
|
110
|
+
@user_name
|
111
|
+
end
|
112
|
+
|
113
|
+
def password
|
114
|
+
unless @password.instance_of? String
|
115
|
+
HighLine.track_eof = false
|
116
|
+
@password = HighLine.new.ask('your twitter password: ') { |q| q.echo = false }
|
117
|
+
end
|
118
|
+
@password
|
119
|
+
end
|
120
|
+
|
121
|
+
def near_users(screen_name)
|
122
|
+
Client::public_storage[:users].select {|user|
|
123
|
+
/#{user}/i =~ screen_name || /#{screen_name}/i =~ user
|
124
|
+
}.join(', ')
|
125
|
+
end
|
126
|
+
|
127
|
+
def post_request(uri)
|
128
|
+
req = Net::HTTP::Post.new(uri)
|
129
|
+
req.basic_auth(user_name, password)
|
130
|
+
req.add_field('User-Agent', 'Termtter http://github.com/jugyo/termtter')
|
131
|
+
req.add_field('X-Twitter-Client', 'Termtter')
|
132
|
+
req.add_field('X-Twitter-Client-URL', 'http://github.com/jugyo/termtter')
|
133
|
+
req.add_field('X-Twitter-Client-Version', '0.1')
|
134
|
+
req
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|