tjplurker 1.3.1 → 1.3.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,489 @@
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
@@ -0,0 +1,145 @@
1
+ # encoding: utf-8
2
+
3
+ module TJP
4
+ class Robot
5
+ attr_reader :tjplurker
6
+
7
+ # :call-seq: new(tjplurker)
8
+ # new(key, screte)
9
+ # new(key, secret, access_token, access_token_screte)
10
+ # key:: consumer key
11
+ # secret:: consumer secret
12
+ def initialize *params
13
+ case params.size
14
+ when 1
15
+ @tjplurker = params[0]
16
+ when 2
17
+ key, screte = params
18
+ @tjplurker = TJPlurker.new(key, screte)
19
+ when 4
20
+ key, secret, access_token, access_token_screte = params
21
+ @tjplurker = TJPlurker.new(key, secret, access_token, access_token_screte)
22
+ end
23
+ @services = []
24
+ end
25
+
26
+ # Starting a loop and invokes services when a notification was listened.
27
+ def start
28
+ $tjpLog.info(self.class){"Start robot."}
29
+ Thread.new{
30
+ loop{
31
+ @tjplurker.add_all_as_friends
32
+ sleep(60)
33
+ }
34
+ }
35
+ loop{
36
+ @tjplurker.comet_channel{|data|
37
+ @services.each{|service|
38
+ begin
39
+ service.serve(@tjplurker, data)
40
+ rescue => ex
41
+ $tjpLog.error(self.class){"An error occured in #{service.class}, class: #{ex.class}, message: #{ex.message}, backtrace: #{ex.backtrace}"}
42
+ end
43
+ }
44
+ }
45
+ }
46
+ end
47
+
48
+ # service:: Can be instance TJPlurker::Service or class TJPlurker::Service or it's descendant classes.
49
+ def add_service service
50
+ if service.is_a? Service
51
+ @services << service
52
+ $tjpLog.info(self.class){"Add service: #{service.name}"}
53
+ elsif service <= Service
54
+ tmp = service.new
55
+ @services << tmp
56
+ $tjpLog.info(self.class){"Add service: #{tmp.name}"}
57
+ else
58
+ $tjpLog.warn(self.class){"Unrecognized service, ignored."}
59
+ end
60
+ end
61
+ end
62
+
63
+ class Service
64
+ attr_reader :name
65
+ def initialize name=self.class.to_s, &proc
66
+ @name = name
67
+ if block_given?
68
+ @serve_proc = proc
69
+ end
70
+ end
71
+
72
+ # Invoke when listened a new plurk.
73
+ # data:: notification data
74
+ def decide tjplurker, data
75
+ true
76
+ end
77
+
78
+ # Invoke when decide return true
79
+ # data:: notification data
80
+ def action tjplurker, data
81
+ if @serve_proc
82
+ @serve_proc[tjplurker, data]
83
+ else
84
+ p data
85
+ end
86
+ end
87
+
88
+ # It has higher priority than decide and action
89
+ # data:: notification data
90
+ def serve tjplurker, data
91
+ action(tjplurker, data) if decide(tjplurker, data)
92
+ end
93
+ end
94
+
95
+ class ThreadService < Service
96
+ # lasting:: How long in second will a thread lives. If one plurk just be responsed,
97
+ # then it will live another +lasting+ time.
98
+ # refresh:: Interval that checks if thread should terminate.
99
+ def initialize name=self.class.to_s, lasting=60, refresh=1, &proc
100
+ super name
101
+ @lasting = lasting
102
+ @refresh = refresh
103
+ @plurk_id_hash = Hash.new
104
+ @mutex = Mutex.new
105
+ end
106
+
107
+ # Invoke after thread termination.
108
+ # You can use this to tell users this service just terminate.
109
+ # data:: notification data
110
+ def final tjplurker, data
111
+
112
+ end
113
+
114
+ def serve tjplurker, data
115
+ if data.type=='new_plurk' && decide(tjplurker, data)
116
+ @plurk_id_hash[data.plurk_id] = Thread.new(data.plurk_id){|plurk_id|
117
+ $tjpLog.debug(self.class){"Create thread in plurk ##{plurk_id}"}
118
+ Thread.current[:last_time] = Time.now.to_i
119
+ loop{
120
+ sleep(@refresh)
121
+ $tjpLog.debug(self.class){"Plurk ##{plurk_id} remain #{Thread.current[:last_time]+@lasting-Time.now.to_i} seconds."}
122
+ @mutex.lock
123
+ if Time.now.to_i - Thread.current[:last_time] > @lasting
124
+ @mutex.unlock
125
+ break
126
+ end
127
+ @mutex.unlock
128
+ }
129
+ $tjpLog.debug(self.class){"Terminate thread in plurk ##{plurk_id}"}
130
+ @mutex.lock
131
+ @plurk_id_hash.delete(plurk_id)
132
+ @mutex.unlock
133
+ final(tjplurker, data)
134
+ }
135
+ else
136
+ @mutex.lock
137
+ if data.type=='new_response' && @plurk_id_hash.has_key?(data.plurk_id)
138
+ @plurk_id_hash[data.plurk_id][:last_time] = Time.now.to_i
139
+ action(tjplurker, data)
140
+ end
141
+ @mutex.unlock
142
+ end
143
+ end
144
+ end
145
+ end
data/lib/tjplurker.rb ADDED
@@ -0,0 +1,2 @@
1
+ require 'tjplurker/core'
2
+ require 'tjplurker/robot'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tjplurker
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.1
4
+ version: 1.3.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,14 +10,40 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
  date: 2012-03-20 00:00:00.000000000 Z
13
- dependencies: []
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: json
16
+ requirement: &17951592 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *17951592
25
+ - !ruby/object:Gem::Dependency
26
+ name: oauth
27
+ requirement: &17951256 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *17951256
14
36
  description: A safe Plurk API wrapper based on Ruby and mainly designed for easily
15
37
  making plurk robot. installgem install tjplurker.
16
38
  email: tonytonyjan@gmail.com
17
39
  executables: []
18
40
  extensions: []
19
41
  extra_rdoc_files: []
20
- files: []
42
+ files:
43
+ - lib/tjplurker/core.rb
44
+ - lib/tjplurker/legacy.rb
45
+ - lib/tjplurker/robot.rb
46
+ - lib/tjplurker.rb
21
47
  homepage: http://code.google.com/p/tjplurker/
22
48
  licenses: []
23
49
  post_install_message: