drip 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -0
- data/Gemfile +6 -0
- data/README +6 -0
- data/Rakefile +2 -0
- data/drip.gemspec +20 -0
- data/drip.txt +380 -0
- data/install.rb +10 -0
- data/lib/drip.rb +294 -0
- data/lib/drip/version.rb +3 -0
- data/lib/my_drip.rb +64 -0
- data/sample/copocopo.rb +82 -0
- data/sample/drip_tw.rb +255 -0
- data/sample/gca.rb +70 -0
- data/sample/hello_tw.rb +5 -0
- data/sample/my_status.rb +52 -0
- data/sample/simple-oauth.rb +140 -0
- data/sample/tw_markov.rb +154 -0
- data/test/basic.rb +156 -0
- metadata +71 -0
data/lib/drip/version.rb
ADDED
data/lib/my_drip.rb
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'drb/drb'
|
2
|
+
|
3
|
+
MyDrip = DRbObject.new_with_uri('drbunix:' + File.expand_path('~/.drip/port'))
|
4
|
+
|
5
|
+
def MyDrip.invoke
|
6
|
+
fork do
|
7
|
+
Process.daemon
|
8
|
+
|
9
|
+
require 'drip'
|
10
|
+
require 'fileutils'
|
11
|
+
|
12
|
+
dir = File.expand_path('~/.drip')
|
13
|
+
uri = 'drbunix:' + File.join(dir, 'port')
|
14
|
+
ro = DRbObject.new_with_uri(uri)
|
15
|
+
begin
|
16
|
+
ro.older(nil) #ping
|
17
|
+
exit
|
18
|
+
rescue
|
19
|
+
end
|
20
|
+
|
21
|
+
FileUtils.mkdir_p(dir)
|
22
|
+
FileUtils.cd(dir)
|
23
|
+
|
24
|
+
drip = Drip.new('drip')
|
25
|
+
def drip.quit
|
26
|
+
Thread.new do
|
27
|
+
synchronize do |key|
|
28
|
+
exit(0)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
DRb.start_service(uri, drip)
|
34
|
+
File.open('pid', 'w') {|fp| fp.puts($$)}
|
35
|
+
|
36
|
+
DRb.thread.join
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
class DripCursor
|
41
|
+
def initialize(drip, bufsiz=10, at_least=10)
|
42
|
+
@drip = drip
|
43
|
+
@cur = nil
|
44
|
+
@bufsiz = bufsiz
|
45
|
+
@at_least = at_least
|
46
|
+
end
|
47
|
+
attr_accessor :cur
|
48
|
+
|
49
|
+
def now
|
50
|
+
@cur ? @drip.key_to_time(@cur) : nil
|
51
|
+
end
|
52
|
+
|
53
|
+
def seek_at(time)
|
54
|
+
@cur = @drip.time_to_key(time)
|
55
|
+
end
|
56
|
+
|
57
|
+
def past_each(tag=nil)
|
58
|
+
while kv = @drip.older(@cur, tag)
|
59
|
+
@cur, value = kv
|
60
|
+
yield(value)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
data/sample/copocopo.rb
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'drip_tw'
|
3
|
+
require 'my_drip'
|
4
|
+
require 'date'
|
5
|
+
require 'pp'
|
6
|
+
|
7
|
+
def dig(root, *keys)
|
8
|
+
keys.inject(root) do |node, key|
|
9
|
+
return nil if node.nil?
|
10
|
+
node[key] rescue nil
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class CopoCopo
|
15
|
+
def initialize(drip=MyDrip)
|
16
|
+
@app = DripDemo.new('CopoCopo OAuth')
|
17
|
+
@drip = drip
|
18
|
+
_, @last = @drip.older(nil, 'CopoCopo Footprint')
|
19
|
+
@last = 0 if @last.nil?
|
20
|
+
@friends = %w(m_seki miwa719 hsbt vestige mame)
|
21
|
+
end
|
22
|
+
attr_reader :app
|
23
|
+
|
24
|
+
def extract(str)
|
25
|
+
ary = []
|
26
|
+
str.scan(/(([ぁ-ん]{2,})\2)|(([ァ-ヴ]{2,})\4)/) do |x|
|
27
|
+
ary << (x[1] || x[3])
|
28
|
+
end
|
29
|
+
ary.uniq
|
30
|
+
end
|
31
|
+
|
32
|
+
def retweet?(event)
|
33
|
+
event['retweeted_status'] ? true : false
|
34
|
+
end
|
35
|
+
|
36
|
+
def mention?(event)
|
37
|
+
event['in_reply_to_status_id_str'] ? true : false
|
38
|
+
end
|
39
|
+
|
40
|
+
def created_at(event)
|
41
|
+
DateTime.parse(event['created_at']).to_time
|
42
|
+
rescue
|
43
|
+
Time.at(1)
|
44
|
+
end
|
45
|
+
|
46
|
+
def make_status(ary, name)
|
47
|
+
"@#{name} " + ary.collect { |s|
|
48
|
+
"#{s}#{s}、#{s}"
|
49
|
+
}.join(", ") + " (by copocopo)"
|
50
|
+
end
|
51
|
+
|
52
|
+
def main_loop
|
53
|
+
while true
|
54
|
+
@last, event = @drip.read_tag(@last, 'DripDemo Event', 1)[0]
|
55
|
+
next if retweet?(event)
|
56
|
+
next if mention?(event)
|
57
|
+
next unless Time.now < created_at(event) + 60000
|
58
|
+
name = dig(event, 'user', 'screen_name')
|
59
|
+
next unless @friends.include?(name)
|
60
|
+
ary = extract(event['text'] || '')
|
61
|
+
next if ary.empty?
|
62
|
+
tweet_id = event['id']
|
63
|
+
# @app.update(make_status(ary, name), tweet_id)
|
64
|
+
p [make_status(ary, name)]
|
65
|
+
# @drip.write(@last, 'CopoCopo Footprint')
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
copo = CopoCopo.new
|
71
|
+
app = copo.app
|
72
|
+
|
73
|
+
unless app.has_token?
|
74
|
+
url = app.pin_url
|
75
|
+
puts url
|
76
|
+
system('open ' + url) # for OSX
|
77
|
+
app.set_pin(gets.scan(/\w+/)[0])
|
78
|
+
app.write_setting
|
79
|
+
end
|
80
|
+
|
81
|
+
copo.main_loop
|
82
|
+
|
data/sample/drip_tw.rb
ADDED
@@ -0,0 +1,255 @@
|
|
1
|
+
require 'simple-oauth'
|
2
|
+
require 'drb'
|
3
|
+
require 'pp'
|
4
|
+
require 'json'
|
5
|
+
require 'my_drip'
|
6
|
+
|
7
|
+
class DripFiber
|
8
|
+
def initialize(app)
|
9
|
+
@app = app
|
10
|
+
@fiber = Fiber.new do |event|
|
11
|
+
story(event)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def story(event)
|
16
|
+
pending = []
|
17
|
+
while event['id_str'].nil?
|
18
|
+
pending << event
|
19
|
+
event = Fiber.yield
|
20
|
+
end
|
21
|
+
|
22
|
+
@app.fill_timeline(event['id_str'])
|
23
|
+
|
24
|
+
while event = pending.shift
|
25
|
+
@app.write(event)
|
26
|
+
end
|
27
|
+
|
28
|
+
while true
|
29
|
+
event = Fiber.yield
|
30
|
+
@app.write(event)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def push(event)
|
35
|
+
@fiber.resume(event)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class JSONStream
|
40
|
+
def initialize(drip)
|
41
|
+
@buf = ''
|
42
|
+
@drip = drip
|
43
|
+
end
|
44
|
+
|
45
|
+
def push(str)
|
46
|
+
@buf << str
|
47
|
+
while (line = @buf[/.+?(\r\n)+/m]) != nil
|
48
|
+
begin
|
49
|
+
@buf.sub!(line,"")
|
50
|
+
line.strip!
|
51
|
+
event = JSON.parse(line)
|
52
|
+
rescue
|
53
|
+
break
|
54
|
+
end
|
55
|
+
pp event if $DEBUG
|
56
|
+
@drip.push(event)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
class SimpleOAuthS < SimpleOAuth
|
62
|
+
def http_class
|
63
|
+
return Net::HTTP unless ENV['http_proxy']
|
64
|
+
proxy_url = URI.parse(ENV['http_proxy'])
|
65
|
+
Net::HTTP.Proxy(proxy_url.host, proxy_url.port)
|
66
|
+
end
|
67
|
+
|
68
|
+
def request(method, url, body =nil, headers = {}, &block)
|
69
|
+
method = method.to_s
|
70
|
+
url = URI.parse(url)
|
71
|
+
request = create_http_request(method, url.request_uri, body, headers)
|
72
|
+
request['Authorization'] = auth_header(method, url, request.body)
|
73
|
+
http = http_class.new(url.host, url.port)
|
74
|
+
if url.scheme == 'https'
|
75
|
+
http.use_ssl = true
|
76
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
77
|
+
store = OpenSSL::X509::Store.new
|
78
|
+
store.set_default_paths
|
79
|
+
http.cert_store = store
|
80
|
+
end
|
81
|
+
http.request(request, &block)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
class DripDemo
|
86
|
+
def initialize(oauth_tag = 'DripDemo OAuth')
|
87
|
+
@oauth_tag = oauth_tag
|
88
|
+
@oa = last_setting || {}
|
89
|
+
end
|
90
|
+
|
91
|
+
def has_token?
|
92
|
+
@oa.include?(:oauth_token)
|
93
|
+
end
|
94
|
+
|
95
|
+
def last_setting
|
96
|
+
MyDrip.older(nil, @oauth_tag)[1]
|
97
|
+
end
|
98
|
+
|
99
|
+
def write_setting
|
100
|
+
MyDrip.write(@oa, @oauth_tag)
|
101
|
+
end
|
102
|
+
|
103
|
+
def update_setting(body, keys=nil)
|
104
|
+
found = []
|
105
|
+
body.split('&').each do |pair|
|
106
|
+
k, v = pair.split('=')
|
107
|
+
if keys.nil? || keys.include?(k)
|
108
|
+
@oa[k.intern] = v
|
109
|
+
found << k
|
110
|
+
end
|
111
|
+
end
|
112
|
+
found
|
113
|
+
end
|
114
|
+
|
115
|
+
def oauth
|
116
|
+
SimpleOAuthS.new(@oa[:consumer_key],
|
117
|
+
@oa[:consumer_secret],
|
118
|
+
@oa[:oauth_token],
|
119
|
+
@oa[:oauth_token_secret])
|
120
|
+
end
|
121
|
+
|
122
|
+
def pin_url
|
123
|
+
@oa[:oauth_token] = ''
|
124
|
+
@oa[:oauth_token_secret] = ''
|
125
|
+
response = oauth.get('https://api.twitter.com/oauth/request_token')
|
126
|
+
raise response.message unless response.code == '200'
|
127
|
+
update_setting(response.body, ['oauth_token', 'oauth_token_secret'])
|
128
|
+
|
129
|
+
'http://twitter.com/oauth/authorize?oauth_token=' + @oa[:oauth_token]
|
130
|
+
end
|
131
|
+
|
132
|
+
def set_pin(pin)
|
133
|
+
response = oauth.get('https://api.twitter.com/oauth/access_token',
|
134
|
+
'oauth_token' => @oa[:oauth_token],
|
135
|
+
'oauth_velifier' => pin)
|
136
|
+
raise response.message unless response.code == '200'
|
137
|
+
update_setting(response.body)
|
138
|
+
end
|
139
|
+
|
140
|
+
def drip_stream
|
141
|
+
json = JSONStream.new(DripFiber.new(self))
|
142
|
+
oauth.request(:GET, 'https://userstream.twitter.com/2/user.json') do |r|
|
143
|
+
r.read_body do |chunk|
|
144
|
+
json.push(chunk)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
def home_timeline(since_id, max_id)
|
150
|
+
url = "http://api.twitter.com/1/statuses/home_timeline.json?count=200&include_entities=true"
|
151
|
+
url += "&since_id=#{since_id}" if since_id
|
152
|
+
url += "&max_id=#{max_id}" if max_id
|
153
|
+
r = oauth.request(:GET, url)
|
154
|
+
JSON.parse(r.body)
|
155
|
+
end
|
156
|
+
|
157
|
+
def user_timeline(since_id, max_id)
|
158
|
+
url = "http://api.twitter.com/1/statuses/user_timeline.json?count=200&include_entities=true&trim_user=t"
|
159
|
+
url += "&since_id=#{since_id}" if since_id
|
160
|
+
url += "&max_id=#{max_id}" if max_id
|
161
|
+
r = oauth.request(:GET, url)
|
162
|
+
JSON.parse(r.body)
|
163
|
+
end
|
164
|
+
|
165
|
+
def last_tweet_id
|
166
|
+
key = nil
|
167
|
+
while kv = MyDrip.older(key, 'DripDemo Event')
|
168
|
+
key, value = kv
|
169
|
+
return value['id_str'] if value.include?('text')
|
170
|
+
end
|
171
|
+
nil
|
172
|
+
end
|
173
|
+
|
174
|
+
def fill_timeline(max_id)
|
175
|
+
since_id = last_tweet_id
|
176
|
+
return unless since_id
|
177
|
+
timeline = []
|
178
|
+
4.times do
|
179
|
+
return if since_id == max_id || max_id.nil?
|
180
|
+
ary = home_timeline(since_id, max_id)
|
181
|
+
pp [:fill_timeline, ary.size] if $DEBUG
|
182
|
+
max_id = nil
|
183
|
+
ary.reverse_each do |event|
|
184
|
+
next unless event['id']
|
185
|
+
max_id = event['id'] - 1
|
186
|
+
break
|
187
|
+
end
|
188
|
+
break if max_id.nil?
|
189
|
+
timeline += ary
|
190
|
+
end
|
191
|
+
timeline.reverse_each do |event|
|
192
|
+
write(event)
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
def compact_event(event)
|
197
|
+
return event unless Hash === event
|
198
|
+
result = {}
|
199
|
+
event.each do |k, v|
|
200
|
+
case v
|
201
|
+
when Hash
|
202
|
+
v = compact_event(v)
|
203
|
+
next if v.nil?
|
204
|
+
when [], '', 0, nil
|
205
|
+
next
|
206
|
+
when Array
|
207
|
+
v = v.collect {|vv| compact_event(vv)}
|
208
|
+
end
|
209
|
+
if k == 'user'
|
210
|
+
tmp = {}
|
211
|
+
%w(name screen_name id id_str).each {|attr| tmp[attr] = v[attr]}
|
212
|
+
v = tmp
|
213
|
+
end
|
214
|
+
result[k] = v
|
215
|
+
end
|
216
|
+
result.size == 0 ? nil : result
|
217
|
+
end
|
218
|
+
|
219
|
+
def write(event, tag='DripDemo Event')
|
220
|
+
event = compact_event(event)
|
221
|
+
key = MyDrip.write(event, tag)
|
222
|
+
pp [key, event['id_str'], event['text']] if $DEBUG
|
223
|
+
end
|
224
|
+
|
225
|
+
def update(str, in_reply_to=nil)
|
226
|
+
hash = { :status => str }
|
227
|
+
hash[:in_reply_to_status_id] = in_reply_to if in_reply_to
|
228
|
+
r = oauth.post('http://api.twitter.com/1/statuses/update.xml',
|
229
|
+
hash)
|
230
|
+
pp r.body if $DEBUG
|
231
|
+
end
|
232
|
+
|
233
|
+
def test
|
234
|
+
r = oauth.post('http://api.twitter.com/1/statuses/update.xml',
|
235
|
+
{:status => 'test'})
|
236
|
+
pp r.body if $DEBUG
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
if __FILE__ == $0
|
241
|
+
app = DripDemo.new
|
242
|
+
|
243
|
+
unless app.has_token?
|
244
|
+
url = app.pin_url
|
245
|
+
puts url
|
246
|
+
system('open ' + url) # for OSX
|
247
|
+
app.set_pin(gets.scan(/\w+/)[0])
|
248
|
+
app.write_setting
|
249
|
+
end
|
250
|
+
|
251
|
+
unless $DEBUG
|
252
|
+
Process.daemon
|
253
|
+
end
|
254
|
+
app.drip_stream
|
255
|
+
end
|
data/sample/gca.rb
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
class GoogleChartApi
|
2
|
+
NtoS = ('A'..'Z').to_a + ('a'..'z').to_a + ('0'..'9').to_a
|
3
|
+
NtoE = NtoS + %w(- .)
|
4
|
+
URL = 'http://chart.apis.google.com/chart'
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@query = []
|
8
|
+
end
|
9
|
+
|
10
|
+
def as_s(n)
|
11
|
+
NtoS[n]
|
12
|
+
end
|
13
|
+
|
14
|
+
def as_e(n)
|
15
|
+
h, v = n.divmod(64)
|
16
|
+
NtoE[h] + NtoE[v]
|
17
|
+
end
|
18
|
+
|
19
|
+
def chd_s(*ary_ary)
|
20
|
+
@query << "chd=s:" + ary_ary.collect do |ary|
|
21
|
+
ary.collect {|n| as_s(n)}.join('')
|
22
|
+
end.join("|")
|
23
|
+
end
|
24
|
+
|
25
|
+
def chd_e(*ary_ary)
|
26
|
+
@query << "chd=e:" + ary_ary.collect do |ary|
|
27
|
+
ary.collect {|n| as_e(n)}.join('')
|
28
|
+
end.join("|")
|
29
|
+
end
|
30
|
+
|
31
|
+
def chd_t(*ary_ary)
|
32
|
+
@query << "chd=t:" + ary_ary.collect do |ary|
|
33
|
+
ary.join(',')
|
34
|
+
end.join("|")
|
35
|
+
end
|
36
|
+
|
37
|
+
def chs(w, h)
|
38
|
+
@query << "chs=#{w}x#{h}"
|
39
|
+
end
|
40
|
+
|
41
|
+
def chtt(title)
|
42
|
+
@query << "chtt=#{title.gsub(/ /, '+') .gsub(/\n/m, '|')}"
|
43
|
+
end
|
44
|
+
|
45
|
+
def method_missing(name, *args, &blk)
|
46
|
+
if /^ch/ =~ name
|
47
|
+
@query << "#{name}=#{args[0]}"
|
48
|
+
else
|
49
|
+
super
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def to_s
|
54
|
+
[URL, @query.join("&")].join("?")
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
gca = GoogleChartApi.new
|
59
|
+
|
60
|
+
gca.chs(460, 200)
|
61
|
+
gca.chd_t([62,12,5,2,19])
|
62
|
+
gca.cht('lc')
|
63
|
+
gca.chxt('r')
|
64
|
+
gca.chxr('0,60,130')
|
65
|
+
gca.chxl('0:80|100|120')
|
66
|
+
gca.chxp('0,80,100,120')
|
67
|
+
|
68
|
+
puts gca
|
69
|
+
system("open '#{gca}'")
|
70
|
+
|