tjplurker 1.3 → 1.3.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,489 +0,0 @@
1
- require 'logger'
2
- require 'json'
3
- require 'oauth'
4
- require 'net/http'
5
-
6
- $tjpLog = Logger.new(STDOUT)
7
- $tjpLog.level = Logger::INFO
8
- $tjpLog.formatter = proc{ |severity, datetime, progname, msg|
9
- "#{severity} [#{datetime}] in #{progname} -- #{msg}\n"
10
- }
11
-
12
- # == 注意
13
- # 這是舊的函式庫,如果你想學習如何使用最新的TJPlurker,建議你應該參考 {此頁}[link:GETSTART.html]
14
- #
15
- # 如果你堅持享用舊版本,你應該在程式中如下引用以支援舊式的寫法:
16
- # require "tjplurker/legacy"
17
- # include Legacy
18
- # 而不是
19
- # require "tjplurker"
20
- # include TJP
21
- module Legacy
22
- # == 注意
23
- # 這是舊的函式庫,如果你想學習如何使用最新的TJPlurker,建議你應該參考 {此頁}[link:GETSTART.html]
24
- #
25
- # 如果你堅持享用舊版本,你應該在程式中如下引用以支援舊式的寫法:
26
- # require "tjplurker/legacy"
27
- # include Legacy
28
- # 而不是
29
- # require "tjplurker"
30
- # include TJP
31
- #
32
- # 這是TJPlurker的核心類別,大部分的情況下此類別的方法都會回傳一個由Plurk Server產生的JSON物件。
33
- # 所有的方法會在timeout發生的時候重新呼叫
34
- #
35
- # It's core class of TJPlurker.
36
- # Note that every methods in this class returns a JSON object
37
- # which was converted from Plurk server's response.
38
- # All mehtods will retry again when request timeout occurs.
39
- class TJPlurker
40
- # 噗浪帳號
41
- # Plurk user ID.
42
- attr_reader :user
43
-
44
- =begin
45
- [auto_login] 是否在建構時自動登入
46
- If +auto_login+ sets +true+, it will autologin while constructing.
47
- =end
48
- def initialize api_key, user, passwd, auto_login=true
49
- @user, @passwd, @api_key = user, passwd, api_key
50
- @cookie = nil
51
- @user_channel = nil
52
- @id_buffer = nil
53
- login if auto_login
54
- end
55
-
56
-
57
- =begin
58
- :section: Offical API wrapping
59
- 對Plurk官方API做包裹
60
-
61
- Plurk API wrapping
62
- =end
63
-
64
- =begin
65
- 登入
66
- [no_data] 是否將登入成功後的訊息簡化成:
67
- {"success_text": "ok"}
68
- 否則將回傳使用者的詳細資料
69
-
70
- If no_data sets true, the successful login message was simplified as follow:
71
- {"success_text": "ok"}
72
- Otherwise, more details of user's profile were retured.
73
- =end
74
- def login no_data=true
75
- method = "/API/Users/login"
76
- attr = {:username=>@user, :password=>@passwd, :api_key=>@api_key}
77
- attr[:no_data] = '1' if no_data
78
- api(method, attr)
79
- end
80
-
81
- =begin
82
- 發噗
83
-
84
- 範例:
85
- require 'tjplurker'
86
- tjp = TJPlurker.new("API Key", "帳號", "密碼")
87
- tjp.plurk_add("Hello TJPlurker")
88
- =end
89
- def plurk_add content, qualifier=':', limited_to=nil, no_comments=nil, lang='tr_ch'
90
- method = "/API/Timeline/plurkAdd"
91
- attr = {:api_key=>@api_key, :content=>content, :qualifier=>qualifier,:limited_to=>limited_to, :no_comments=>no_comments, :lang=>lang}
92
- api(method, attr)
93
- end
94
-
95
- =begin
96
- 回噗
97
- [qualifier] 可以是如下的字串:
98
-
99
- Can be the following string:
100
- loves, likes, shares, gives, hates, wants, has, will,
101
- asks, wishes, was, feels, thinks, says, is, :, freestyle, hopes, needs, wonders
102
- 範例:
103
- require 'tjplurker'
104
- tjp = TJPlurker.new("API Key", "帳號", "密碼")
105
- json_obj = tjp.plurk_add("Hello TJPlurker")
106
- tjp.response_add(json_obj["plurk_id"], "This is a reply.", "says")
107
- =end
108
- def response_add plurk_id, content, qualifier=':'
109
- method = "/API/Responses/responseAdd"
110
- attr = {:api_key=>@api_key, :plurk_id=>plurk_id, :content=>content, :qualifier=>qualifier}
111
- api(method, attr)
112
- end
113
-
114
- =begin
115
- 成為粉絲
116
- [fan_id] 你想追蹤的人的ID
117
-
118
- The ID of the user you want to become fan of
119
- =end
120
- def become_fan fan_id
121
- method = "/API/FriendsFans/becomeFan"
122
- attr = {:api_key=>@api_key, :fan_id=>fan_id}
123
- api(method, attr)
124
- end
125
-
126
- =begin
127
- 接受所有的好友邀請
128
-
129
- Accept all friendship requests as friends.
130
- Successful return:
131
- {"success_text": "ok"}
132
- =end
133
- def add_all_as_friends
134
- method = "/API/Alerts/addAllAsFriends"
135
- attr = {:api_key=>@api_key}
136
- api(method, attr)
137
- end
138
-
139
- =begin
140
- 取得使用者的好友名單
141
-
142
- Returns user_id's friend list
143
- [limit] The max number of friends to be returned (default 10).
144
- [Successful return] 好友名單陣列
145
-
146
- Returns a list of JSON objects users, e.g.
147
- [{"id": 3, "nick_name": "alvin", ...}, ...]
148
- [warnning] +limit+的最大值被官方設定為100,這個方法沒有很好用,建議你用tjp_get_friends取代
149
- This maximum of +limit+ is set 100 officially, this method is not that useful.
150
- We suggest you use get_friend_list instead of get_friends_by_offset.
151
- =end
152
- def get_friends_by_offset user_id, limit=100, offset='0'
153
- method = "/API/FriendsFans/getFriendsByOffset"
154
- attr = {:api_key=>@api_key, :user_id=>user_id, :offset=>offset, :limit=>limit}
155
- api(method, attr)
156
- end
157
-
158
- =begin
159
- Fetches public information such as a user's public plurks and basic information.
160
- Fetches also if the current user is following the user, are friends with or is a fan.
161
- [Successful return] Returns a JSON object with a lot of information
162
- that can be used to construct a user's public profile and timeline.
163
- [Error returns] HTTP 400 BAD REQUEST with {"error_text": "Invalid user_id"} as body
164
- HTTP 400 BAD REQUEST with {"error_text": "User not found"} as body
165
- =end
166
- def get_public_profile user_id
167
- method = "/API/Profile/getPublicProfile"
168
- attr = {:api_key=>@api_key, :user_id=>user_id}
169
- api(method, attr)
170
- end
171
-
172
- # Warning:: It has bugs in offecial Plurk API.
173
- def get_plurks limit=nil, offset=nil
174
- method = "/API/Polling/getPlurks"
175
- attr = {:api_key=>@api_key, :limit=>limit, :offset=>offset}
176
- api(method, attr)
177
- end
178
-
179
- def get_user_channel
180
- method = "/API/Realtime/getUserChannel"
181
- attr = {:api_key=>@api_key}
182
- api(method, attr)
183
- end
184
-
185
- def comet_channel
186
- #Assign new channel if @user_channel is nil
187
- @user_channel ||= get_user_channel
188
- begin
189
- res = Net::HTTP.get_response(URI.parse(@user_channel['comet_server']))
190
- json = JSON.parse(res.body.sub!(/CometChannel\.scriptCallback\((.*)\);/){|match| $1})
191
- rescue Timeout::Error => ex
192
- $tjpLog.warn(self.class){"Request timeout, retry."}
193
- retry
194
- rescue Errno::ECONNRESET => ex
195
- $tjpLog.warn(self.class){"Connection reset, retry."}
196
- retry
197
- rescue => ex
198
- $tjpLog.fatal(self.class){"Unknown error, skip."}
199
- puts ex.class, ex.message, ex.backtrace
200
- return
201
- end
202
- #Update the offset
203
- @user_channel['comet_server'].sub!(/[\d]+$/, json["new_offset"].to_s)
204
- if json['data']
205
- json['data'].each{|member|
206
- notification = NotificationData.new(member)
207
- return if notification.id == @id_buffer
208
- $tjpLog.info(self.class){"Notification: #{notification.user_id}: \"#{notification.content}\""}
209
- @id_buffer = notification.id
210
- yield(notification)
211
- }
212
- end
213
- json
214
- end
215
-
216
- =begin
217
- :section: TJPlurker provided
218
- 由TJPlurker提供的工具,非官方有的方法
219
-
220
- Those methos were all provided by TJPlukrer instead of plurk
221
- =end
222
-
223
- =begin
224
- 這個方法游TJPlurker提供的非官方功能,改良自get_friends_by_offset,用於取得使用者所有的好友
225
-
226
- This method is provided by TJPlurker. Improved from get_friends_by_offset for getting all friends of a user
227
- [max] 取得多少好友,nil則無限制
228
-
229
- The maximum number of your request. Set +nil+ for unlimited.
230
- [return] 陣列
231
-
232
- An array contains friends
233
- 範例:
234
- require 'tjplurker'
235
- tjp = TJPlurker.new("API Key", "帳號", "密碼")
236
- friend_list = tjp.tjp_get_friends("5874158")
237
- friend_list.each{|friend| puts friend["uid"]}
238
- =end
239
- def tjp_get_friends user_id, max=nil
240
- list = []
241
- offset = 0;
242
- loop{
243
- set = get_friends_by_offset(user_id, 100, offset)
244
- size = set.size
245
- break if size == 0
246
- list |= set
247
- break if max && list.size>max
248
- offset += size
249
- }
250
- return list
251
- end
252
-
253
- =begin
254
- 用於追蹤大量使用者的河道
255
- [level] 深度,如等於1會追蹤使用者的所有好友,等於2則會追蹤使用者所有的好友以及好友的好友
256
- =end
257
- def tjp_super_become_fan fan_id, level=1
258
- return if level<=0
259
- become_fan(fan_id)
260
- list = []
261
- offset = 0;
262
- loop{
263
- set = get_friends_by_offset(fan_id, 100, offset)
264
- size = set.size
265
- break if size == 0
266
- tg = ThreadGroup.new
267
- set.each{|i|
268
- tg.add Thread.new{
269
- wait_sec = 1
270
- until become_fan(i["uid"]).key?("success_text")
271
- $tjpLog.info(self.class){"Trying to become #{i["uid"]}'s fan in #{wait_sec} seconds"}
272
- sleep(wait_sec)
273
- wait_sec *= 2
274
- end
275
- $tjpLog.info(self.class){"Become #{i["uid"]}'s fan successfully"}
276
- }
277
- }
278
- tg.list.each{|i| i.join }
279
- list |= set
280
- offset += size
281
- }
282
- list.each{|i|
283
- super_become_fan(i["uid"], level-1)
284
- }
285
- end
286
-
287
- private
288
- def api method, attr
289
- #Build POST Request
290
- req = Net::HTTP::Post.new(method)
291
- req.set_form_data(attr)
292
- req["Cookie"] = @cookie
293
- #Build GET Request
294
- #path = method + "?" + URI.encode_www_form(attr)
295
- #req2 = Net::HTTP::Get.new(path)
296
- #req2["Cookie"] = @cookie
297
-
298
- #Build HTTP connection
299
- http = Net::HTTP.new('www.plurk.com', 80)
300
- begin
301
- resp = http.start{
302
- $tjpLog.debug(self.class){"Request: #{method}, #{attr}"}
303
- http.request(req)
304
- }
305
- @cookie ||= resp['set-cookie']
306
- json = JSON.parse(resp.body)
307
- $tjpLog.debug(self.class){"Response: #{json}"}
308
- $tjpLog.error(self.class){json["error_text"]} if json.is_a?(Hash) && json["error_text"]
309
- rescue Timeout::Error => ex
310
- $tjpLog.warn(self.class){"Request timeout, retrying."}
311
- retry
312
- rescue Errno::ECONNRESET => ex
313
- $tjpLog.warn(self.class){"Connection reset, retry."}
314
- retry
315
- rescue JSON::ParserError => ex
316
- $tjpLog.error(self.class){"JSON parse error, request failed."}
317
- rescue => ex
318
- $tjpLog.fatal(self.class){"Unknown error, skip."}
319
- puts ex.class, ex.message, ex.backtrace
320
- return
321
- end
322
- return json
323
- end
324
-
325
- # Format the json from the comet server. See TJPlurker#comet_channel
326
- class NotificationData
327
- # Can be +new_plurk+ or +new_response+.
328
- attr_reader :type
329
- attr_reader :response_count
330
- attr_reader :plurk_id
331
- attr_reader :lang
332
- attr_reader :user_id
333
- attr_reader :qualifier
334
- attr_reader :content
335
- # Notification ID.
336
- attr_reader :id
337
- # Time of the listened post.
338
- attr_reader :posted
339
- attr_reader :owner_id
340
- # The original JSON object from server response.
341
- attr_reader :origin
342
-
343
- def initialize member
344
- @type = member['type']
345
- @response_count = member['response_count']
346
- @plurk_id = member['plurk_id']
347
-
348
- @lang = @type=="new_plurk" ? member['lang'] : member['response']['lang']
349
- @user_id = @type=="new_plurk" ? member['user_id'] : member['response']['user_id']
350
- @qualifier = @type=="new_plurk" ? member['qualifier'] : member['response']['qualifier']
351
- @content = @type=="new_plurk" ? member['content'] : member['response']['content']
352
- @id = @type=="new_plurk" ? member['id'] : member['response']['id']
353
- @posted = @type=="new_plurk" ? member['posted'] : member['response']['posted']
354
-
355
- @owner_id = @type=="new_plurk" ? member['owner_id'] : member['plurk']['owner_id']
356
-
357
- @origin = member
358
- end
359
- end
360
-
361
- class Robot
362
- def initialize *params
363
- case params.size
364
- when 1
365
- @tjplurker = params[0]
366
- when 3
367
- api_key, user, passwd = params
368
- @tjplurker = TJPlurker.new(api_key, user, passwd)
369
- when 4
370
- api_key, user, passwd, auto_login = params
371
- @tjplurker = TJPlurker.new(api_key, user, passwd, auto_login)
372
- end
373
- @services = []
374
- end
375
-
376
- # Starting a loop and invokes services when a notification was listened.
377
- def start
378
- $tjpLog.info(self.class){"Start robot."}
379
- Thread.new{
380
- loop{
381
- @tjplurker.add_all_as_friends
382
- sleep(60)
383
- }
384
- }
385
- loop{
386
- @tjplurker.comet_channel{|data|
387
- @services.each{|service|
388
- begin
389
- service.serve(@tjplurker, data)
390
- rescue => ex
391
- $tjpLog.error(self.class){"An error occured in #{service.class}, class: #{ex.class}, message: #{ex.message}, backtrace: #{ex.backtrace}"}
392
- end
393
- }
394
- }
395
- }
396
- end
397
-
398
- # +service+:: Can be instance TJPlurker::Service or class TJPlurker::Service or it's descendant classes.
399
- def add_service service
400
- if service.is_a? Service
401
- @services << service
402
- $tjpLog.info(self.class){"Add service: #{service.name}"}
403
- elsif service <= Service
404
- tmp = service.new
405
- @services << tmp
406
- $tjpLog.info(self.class){"Add service: #{tmp.name}"}
407
- else
408
- $tjpLog.warn(self.class){"Unrecognized service, ignored."}
409
- end
410
- end
411
- end
412
-
413
- class Service
414
- attr_reader :name
415
- def initialize name=self.class.to_s, &proc
416
- @name = name
417
- if block_given?
418
- @serve_proc = proc
419
- end
420
- end
421
-
422
- # Invoke when listened a new plurk.
423
- def decide tjplurker, data
424
- true
425
- end
426
-
427
- def action tjplurker, data
428
- if @serve_proc
429
- @serve_proc[tjplurker, data]
430
- else
431
- p data
432
- end
433
- end
434
-
435
- def serve tjplurker, data
436
- action(tjplurker, data) if decide(tjplurker, data)
437
- end
438
- end
439
-
440
- class ThreadService < Service
441
- # +lasting+:: How long in second will a thread lives. If one plurk just be responsed, then it will live another +lasting+ time.
442
- # +refresh+:: Interval that checks if thread should terminate.
443
- def initialize name=self.class.to_s, lasting=60, refresh=1, &proc
444
- super name
445
- @lasting = lasting
446
- @refresh = refresh
447
- @plurk_id_hash = Hash.new
448
- @mutex = Mutex.new
449
- end
450
-
451
- # Invoke after thread termination.
452
- # You can use this to tell users this service just terminate.
453
- def final tjplurker, data
454
-
455
- end
456
-
457
- def serve tjplurker, data
458
- if data.type=='new_plurk' && decide(tjplurker, data)
459
- @plurk_id_hash[data.plurk_id] = Thread.new(data.plurk_id){|plurk_id|
460
- $tjpLog.debug(self.class){"Create thread in plurk ##{plurk_id}"}
461
- Thread.current[:last_time] = Time.now.to_i
462
- loop{
463
- sleep(@refresh)
464
- $tjpLog.debug(self.class){"Plurk ##{plurk_id} remain #{Thread.current[:last_time]+@lasting-Time.now.to_i} seconds."}
465
- @mutex.lock
466
- if Time.now.to_i - Thread.current[:last_time] > @lasting
467
- @mutex.unlock
468
- break
469
- end
470
- @mutex.unlock
471
- }
472
- $tjpLog.debug(self.class){"Terminate thread in plurk ##{plurk_id}"}
473
- @mutex.lock
474
- @plurk_id_hash.delete(plurk_id)
475
- @mutex.unlock
476
- final(tjplurker, data)
477
- }
478
- else
479
- @mutex.lock
480
- if data.type=='new_response' && @plurk_id_hash.has_key?(data.plurk_id)
481
- @plurk_id_hash[data.plurk_id][:last_time] = Time.now.to_i
482
- action(tjplurker, data)
483
- end
484
- @mutex.unlock
485
- end
486
- end
487
- end
488
- end
489
- end
@@ -1,142 +0,0 @@
1
- module TJP
2
- class Robot
3
- # :call-seq: new(tjplurker)
4
- # new(key, screte)
5
- # new(key, secret, access_token, access_token_screte)
6
- # key:: consumer key
7
- # secret:: consumer secret
8
- attr_reader :tjplurker
9
- def initialize *params
10
- case params.size
11
- when 1
12
- @tjplurker = params[0]
13
- when 2
14
- key, screte = params
15
- @tjplurker = TJPlurker.new(key, screte)
16
- when 4
17
- key, secret, access_token, access_token_screte = params
18
- @tjplurker = TJPlurker.new(key, secret, access_token, access_token_screte)
19
- end
20
- @services = []
21
- end
22
-
23
- # Starting a loop and invokes services when a notification was listened.
24
- def start
25
- $tjpLog.info(self.class){"Start robot."}
26
- Thread.new{
27
- loop{
28
- @tjplurker.add_all_as_friends
29
- sleep(60)
30
- }
31
- }
32
- loop{
33
- @tjplurker.comet_channel{|data|
34
- @services.each{|service|
35
- begin
36
- service.serve(@tjplurker, data)
37
- rescue => ex
38
- $tjpLog.error(self.class){"An error occured in #{service.class}, class: #{ex.class}, message: #{ex.message}, backtrace: #{ex.backtrace}"}
39
- end
40
- }
41
- }
42
- }
43
- end
44
-
45
- # service:: Can be instance TJPlurker::Service or class TJPlurker::Service or it's descendant classes.
46
- def add_service service
47
- if service.is_a? Service
48
- @services << service
49
- $tjpLog.info(self.class){"Add service: #{service.name}"}
50
- elsif service <= Service
51
- tmp = service.new
52
- @services << tmp
53
- $tjpLog.info(self.class){"Add service: #{tmp.name}"}
54
- else
55
- $tjpLog.warn(self.class){"Unrecognized service, ignored."}
56
- end
57
- end
58
- end
59
-
60
- class Service
61
- attr_reader :name
62
- def initialize name=self.class.to_s, &proc
63
- @name = name
64
- if block_given?
65
- @serve_proc = proc
66
- end
67
- end
68
-
69
- # Invoke when listened a new plurk.
70
- # data:: notification data
71
- def decide tjplurker, data
72
- true
73
- end
74
-
75
- # Invoke when decide return true
76
- # data:: notification data
77
- def action tjplurker, data
78
- if @serve_proc
79
- @serve_proc[tjplurker, data]
80
- else
81
- p data
82
- end
83
- end
84
-
85
- # It has higher priority than decide and action
86
- # data:: notification data
87
- def serve tjplurker, data
88
- action(tjplurker, data) if decide(tjplurker, data)
89
- end
90
- end
91
-
92
- class ThreadService < Service
93
- # lasting:: How long in second will a thread lives. If one plurk just be responsed,
94
- # then it will live another +lasting+ time.
95
- # refresh:: Interval that checks if thread should terminate.
96
- def initialize name=self.class.to_s, lasting=60, refresh=1, &proc
97
- super name
98
- @lasting = lasting
99
- @refresh = refresh
100
- @plurk_id_hash = Hash.new
101
- @mutex = Mutex.new
102
- end
103
-
104
- # Invoke after thread termination.
105
- # You can use this to tell users this service just terminate.
106
- # data:: notification data
107
- def final tjplurker, data
108
-
109
- end
110
-
111
- def serve tjplurker, data
112
- if data.type=='new_plurk' && decide(tjplurker, data)
113
- @plurk_id_hash[data.plurk_id] = Thread.new(data.plurk_id){|plurk_id|
114
- $tjpLog.debug(self.class){"Create thread in plurk ##{plurk_id}"}
115
- Thread.current[:last_time] = Time.now.to_i
116
- loop{
117
- sleep(@refresh)
118
- $tjpLog.debug(self.class){"Plurk ##{plurk_id} remain #{Thread.current[:last_time]+@lasting-Time.now.to_i} seconds."}
119
- @mutex.lock
120
- if Time.now.to_i - Thread.current[:last_time] > @lasting
121
- @mutex.unlock
122
- break
123
- end
124
- @mutex.unlock
125
- }
126
- $tjpLog.debug(self.class){"Terminate thread in plurk ##{plurk_id}"}
127
- @mutex.lock
128
- @plurk_id_hash.delete(plurk_id)
129
- @mutex.unlock
130
- final(tjplurker, data)
131
- }
132
- else
133
- @mutex.lock
134
- if data.type=='new_response' && @plurk_id_hash.has_key?(data.plurk_id)
135
- @plurk_id_hash[data.plurk_id][:last_time] = Time.now.to_i
136
- action(tjplurker, data)
137
- end
138
- @mutex.unlock
139
- end
140
- end
141
- end
142
- end
data/lib/tjplurker.rb DELETED
@@ -1 +0,0 @@
1
- require 'tjplurker/core'