drip 0.0.1

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.
@@ -0,0 +1,3 @@
1
+ module Drip
2
+ VERSION = "0.0.1"
3
+ end
@@ -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
+
@@ -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
+
@@ -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
@@ -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
+