This repository has been archived by the owner on Jul 12, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbbs2ch.rb
420 lines (353 loc) · 12 KB
/
bbs2ch.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
require 'open-uri'
require 'net/http'
require 'kconv'
require 'cookie_manager.rb'
#
#= 2ちゃんねる用モジュール
#
#Authors:: mtakagi
#Version:: 0.0.1 2012-01-23 mtakagi
#
#=== クラス
#
# - BBS2ch
# - Board
# - Thread
# - Dat
#
module BBS2ch
VERSION = "0.0.1"
USER_AGENT = "BBS2ch"
#
# インスタンス化の際に不正なURLが渡された時にraiseする例外
#
class InvalidURLException < Exception; end
class DatMissingException < Exception; end
#
# BBSクラス
#
class BBS2ch
# 生成時に渡されたURL
attr :url
#
# イニシャライザ
# 不正なURLを渡すと例外が発生する。不正なURLはクラスによって異る。
#
def initialize(url)
@url = url
unless self.send("validate_#{self.class.to_s.downcase}_url")
raise InavalidURLException "Invalid #{self.class.to_s} URL"
end
end
#
# URLが不正かどうかチェックする
#
private
def validate_url?(type)
case type
when "bbs2ch"
if @url =~ /.*?(\w*)\.(2ch\.net|bbspink\.com)/ then
true
else
false
end
when "board"
return true
when "thread"
if @url =~ /.*?(\w+)\.(\w+\.\w+)\/test\/read.cgi\/(\w+)\/([0-9]+)\// then
true
else
false
end
when "dat"
if @url =~ /.*?(\w+)\.(2ch.net|bbspink.com)\/(.+?)\/dat\/[0-9]+.dat/
true
else
false
end
end
end
#
# method_missing
# メソッド名にクラス名が含まれるメソッドをここで拾う
#
def method_missing(name, *args)
if name.to_s =~ /^validate_.*?(\w+)_url?$/
send(:validate_url?, $1)
elsif name.to_s =~ /^.*?(\w+)_url$/
return @url
else
super
end
end
end
class Board < BBS2ch
attr :thread_list
def initialize(boardURL)
super(boardURL)
f = open(boardURL)
f.read.scan(/\+(Samba24)=([0-9]+)/)
f.close
f.unlink
self.send($1.downcase + '=', $2)
subject = open(boardURL + "subject.txt")
@thread_list = Array.new
subject.readlines.each do |line|
items = line.split("<>")
thread = Hash.new
thread.store('dat', items[0])
thread.store('subject', items[1].chomp)
@thread_list << thread
end
subject.close
subject.unlink
@array = boardURL.scan(/.*?(\w*)\.(2ch\.net|bbspink\.com)\/(\w+?)\//)[0];
@postURI = URI.parse("http://#{@array[0]}.#{@array[1]}/test/bbs.cgi?guid=ON")
end
def parse_write_status(response)
p response.body.toutf8
response.body.toutf8.match(/.+?\<\!-- 2ch_X:(\w+?) --\>.+?/)
p match
end
def post(subject, submit, referer, message, mail, name, cookie)
body = "bbs=#{@array[2]}&subject=#{subject}&time=1&submit=#{submit}&FROM=#{name}&mail=#{mail}&MESSAGE=#{message}&suka=pontan"
header = {
"Host" => @postURI.host,
"Content-length" => "#{body.length}",
"Referer" => referer,
"User-Agent" => "Monazilla/1.00 (#{USER_AGENT}/#{VERSION})",
"Connection" => "close",
'Cookie' => cookie,
}
Net::HTTP.version_1_2
http = Net::HTTP.new(@postURI.host, @postURI.port)
p body, header
return http.post(@postURI.path, body.tosjis, header)
end
def shinkisakusei(subject, cookie, message, mail, name)
submit = "新規スレッド作成"
referer = "http://#{@postURI.host}/#{@array[2]}/"
p submit , referer
response = post(subject, submit, referer, message, mail, name, cookie)
parse_response(response)
p response.header.get_fields('Set-Cookie')
p response.body.toutf8
return response
end
def shoudaku(subject, cookie, message, mail, name)
referer = @postURI.to_s
submit = "上記全てを承諾して書き込む"
response = post(subject, submit, referer, message, mail, name, cookie)
parse_response(response)
p response.header.get_fields('Set-Cookie')
p response.body.toutf8
return response
end
def create_thread(subject, message, name = "", mail ="sage")
manager = Cookie_Manager.new("2ch")
url = "http://#{@postURI.host}/"
cookie = "yuki=akari; " + manager.cookie_string_for_url(url)
response = shinkisakusei(subject, cookie, message, mail, name)
manager.add_cookies_for_header(response.header, url)
cookie = "yuki=akari; " + manager.cookie_string_for_url(url)
p cookie
response = shoudaku(subject, cookie, message, mail, name)
manager.add_cookies_for_header(response.header, url)
return response
response = post(body, header)
manager.add_cookies_for_header(response.header, url)
parse_write_status(response)
# submit = "上記全てを承諾して書き込む"
# body =
# response = post(body, header)
# manager.add_cookies_for_header(response.header, url)
p cookie
end
def method_missing(name, *args)
if name.to_s =~ /(\w+)=$/ then
return self.instance_variable_set("@#{$1.downcase}", args[0])
elsif name.to_s !~ /^.+?_url/ then
return self.instance_variable_get("@#{name.to_s}")
end
super(name, *args)
end
end
class Thread
def initialize(threadURL)
@hash = parseURL(threadURL)
@postURI = URI.parse("http://#{@hash['server']}.#{@hash['domain']}/test/bbs.cgi?guid=ON")
end
def self.isValidThreadURL(url)
return url =~ /.*?(\w+)\.(\w+\.\w+)\/test\/read.cgi\/(\w+)\/([0-9]+)\//
end
private
def parseURL(threadURL)
array = threadURL.scan(/http:\/\/(.+?)\.(\w+)\/test\/read.cgi\/(.+)\/([0-9]+)\//)[0]
{
"server" => array[0],
"domain" => array[1],
"bbs" => array[2],
"thread" => array[3]
}
end
# http://age.s22.xrea.com/talk2ch/
# <title>タグの中身</title>を調べる
# * 書き込みが成功すると、書きこみましたという文字列が入る
# * 書き込みが失敗すると、ERRORという文字列が入る
# * サーバの負荷が高く、書き込めない場合は、お茶でもという文字列が入る
# * クッキー確認の場合は、書き込み確認という文字列が入る
# 書き込み失敗エラーの内容を知るには、最初にくる<b>タグの中身を調べます。
#
#
# * 正常に書き込みが終了 <!-- 2ch_X:true -->
# * 書き込みはしたが注意つき <!-- 2ch_X:false -->
# * ERROR!のタイトル <!-- 2ch_X:error -->
# * スレ立てなど書き込み別画面 <!-- 2ch_X:check -->
# * クッキー確認画面 <!-- 2ch_X:cookie -->
def parse_response(response)
scaned = response.body.scan(/<!-- 2ch_X:(\w+) -->/)
return scaned[0][0] if scaned.size > 0
end
def post(submit, referer, message, mail, name, cookie)
body = "bbs=#{@hash['bbs']}&key=#{@hash['thread']}&time=1&submit=#{submit}&FROM=#{name}&mail=#{mail}&MESSAGE=#{message}&suka=pontan"
header = {
"Host" => @postURI.host,
"Content-length" => "#{body.length}",
"Referer" => referer,
"User-Agent" => "Monazilla/1.00 (#{USER_AGENT}/#{VERSION})",
"Cookie" => cookie,
"Connection" => "close",
}
Net::HTTP.version_1_2
http = Net::HTTP.new(@postURI.host, @postURI.port)
p body, header
return http.post(@postURI.path, body.tosjis, header)
end
def kakikomi(cookie, message, mail, name)
submit = "書き込む"
referer = "http://#{@postURI.host}/#{@hash['bbs']}/"
response = post(submit, referer, message, mail, name, cookie)
return response
end
def shoudaku(cookie, message, mail, name)
referer = @postURI.to_s
submit = "上記全てを承諾して書き込む"
response = post(submit, referer, message, mail, name, cookie)
# parse_response(response)
return response
end
public
def postMessage(message, mail = "sage", name = "")
manager = Cookie_Manager.new("2ch")
url = "http://#{@postURI.host}/"
cookie = "yuki=akari; " + manager.cookie_string_for_url(url)
response = kakikomi(cookie, message, mail, name)
manager.add_cookies_for_header(response.header, url)
cookie = "yuki=akari; " + manager.cookie_string_for_url(url)
if parse_response(response) == "cookie" then
# response = kakikomi(cookie, message, mail, name)
# else
response = shoudaku(cookie, message, mail, name)
manager.add_cookies_for_header(response.header, url)
end
return response
end
def dat_url
"http://#{@hash['server']}.#{@hash['domain']}/#{@hash['bbs']}/dat/#{@hash['thread']}.dat"
end
def dat
return Dat.new(dat_url)
end
def board
return Board.new("http://#{@hash['server']}.#{@hash['domain']}/#{@hash['bbs']}/")
end
end
class Dat < BBS2ch
require 'zlib'
attr :dat
def initialize(datURL)
super(datURL)
header = {
"Accept-Encoding" => "gzip"
}
uri = URI.parse(datURL)
http = Net::HTTP.new(uri.host, uri.port)
response = http.get(uri.path, header)
@f = open(datURL, header)
if response.code == "302" then
raise DatMissingException #"Dat file is removed or Board is moved."
end
@dat = Zlib::GzipReader.new(@f).read
end
private
def dateStringToTime(dateString)
items = dateString.toutf8.scan(/([0-9]+)\/([0-9]+)\/([0-9]+).+? ([0-9]+):([0-9]+):([0-9]+)\.([0-9]+)/)[0]
time = Time.local(items[0], items[1], items[2], items[3], items[4], items[5], items[6])
end
public
def update
header = {
# "Accept-Encoding" => "gzip",
"If-Modified-Since" => @f.meta['last-modified'],
"Range" => "bytes=#{@dat.size}-"
}
begin
f = open(@f.base_uri, header)
rescue => error
p error
else
# @f.pos = @f.size
@dat << f.read
# p f.read.toutf8
# @dat = Zlib::GzipReader.new(@f).read
@f.meta['last-modified'] = f.meta['last-modified']
return true
end
end
def datArray
require 'ostruct'
array = Array.new
@dat.lines.each do |line|
struct = OpenStruct.new
line.toutf8.chomp.split("<>").each_with_index do |item, index|
case index
when 0 then
struct.name = item.sub(/ <\/b>(.+?)<b> /, "\\1")
when 1 then
struct.mail = item
when 2 then
dateAndID = item.split(" ID:")
struct.kakikomiID = dateAndID[1]
struct.time = dateStringToTime(dateAndID[0])
when 3 then
struct.message = item
when 4 then
struct.title = item
end
end
# puts struct.name
array << struct
end
return array
end
def thread
matched = @url.match(/.*?(\w+?).(2ch.net|bbspink.net)\/(\w+?)\/dat\/(\d+).dat/)
return Thread.new("http://#{matched[1]}.#{matched[2]}/test/read.cgi/#{matched[3]}/#{mathced[4]}/")
end
end
def printID(array)
puts "書き込んだ人数は全体で" + array.size.to_s + "人"
puts "末尾がiまたはI"
puts (array.select {|item| item =~ /i$|I$/}).size
puts "末尾がP"
puts (array.select {|item| item =~ /P$/}).size
puts "末尾がO"
puts (array.select {|item| item =~ /O$/}).size
puts "末尾がo"
puts (array.select {|item| item =~ /o$/}).size
puts "末尾がQ"
puts (array.select {|item| item =~ /Q$/}).size
puts "末尾が0"
puts (array.select {|item| item =~ /0$/}).size
end
end