tjplurker 1.3 → 1.3.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.
- metadata +9 -36
- data/lib/tjplurker/core.rb +0 -726
- data/lib/tjplurker/legacy.rb +0 -489
- data/lib/tjplurker/robot.rb +0 -142
- data/lib/tjplurker.rb +0 -1
data/lib/tjplurker/legacy.rb
DELETED
@@ -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
|
data/lib/tjplurker/robot.rb
DELETED
@@ -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'
|