simple2ch 0.1.8 → 1.1.0
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.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.rubocop.yml +25 -0
- data/.travis.yml +8 -0
- data/README.md +24 -31
- data/UPDATES.md +46 -9
- data/lib/simple2ch.rb +67 -83
- data/lib/simple2ch/bbs.rb +54 -0
- data/lib/simple2ch/board.rb +26 -50
- data/lib/simple2ch/dat.rb +22 -20
- data/lib/simple2ch/regex.rb +16 -0
- data/lib/simple2ch/{res.rb → response.rb} +58 -57
- data/lib/simple2ch/simple2ch_error.rb +9 -0
- data/lib/simple2ch/thread.rb +113 -0
- data/lib/simple2ch/version.rb +1 -1
- data/simple2ch.gemspec +8 -0
- data/spec/bbs_spec.rb +30 -0
- data/spec/board_spec.rb +89 -54
- data/spec/dat_spec.rb +20 -19
- data/spec/response_spec.rb +131 -0
- data/spec/simple2ch_spec.rb +19 -50
- data/spec/spec_helper.rb +65 -37
- data/spec/thread_spec.rb +76 -0
- data/spec/vcr/Simple2ch_BBS/_boards/use_cache/behaves_like_boards/from_2ch_sc/1_1_1_1_1_2.yml +1117 -0
- data/spec/vcr/Simple2ch_BBS/_boards/use_cache/behaves_like_boards/from_open2ch_net/1_1_1_1_2_2.yml +1136 -0
- data/spec/vcr/Simple2ch_BBS/_boards/use_force_reload/behaves_like_boards/from_2ch_sc/1_1_2_1_1_2.yml +1117 -0
- data/spec/vcr/Simple2ch_BBS/_boards/use_force_reload/behaves_like_boards/from_2ch_sc/1_1_2_1_1_3.yml +1117 -0
- data/spec/vcr/Simple2ch_BBS/_boards/use_force_reload/behaves_like_boards/from_open2ch_net/1_1_2_1_2_2.yml +1136 -0
- data/spec/vcr/Simple2ch_BBS/_boards/use_force_reload/behaves_like_boards/from_open2ch_net/1_1_2_1_2_3.yml +1136 -0
- data/spec/vcr/Simple2ch_Board/_threads/1_4_1.yml +1238 -0
- data/spec/vcr/Simple2ch_Board/_threads/1_4_2.yml +1238 -0
- data/spec/vcr/Simple2ch_Board/_threads/1_4_3.yml +1238 -0
- data/spec/vcr/Simple2ch_Board/_threads/1_4_7.yml +1238 -0
- data/spec/vcr/Simple2ch_Board/_threads/1_4_8.yml +1238 -0
- data/spec/vcr/Simple2ch_Board/_threads/1_4_9.yml +1238 -0
- data/spec/vcr/Simple2ch_Dat/should_have_responses/1_2_1.yml +953 -0
- data/spec/vcr/Simple2ch_Dat/should_have_responses/1_2_3.yml +953 -0
- data/spec/vcr/Simple2ch_Dat/should_have_responses/1_2_4.yml +953 -0
- data/spec/vcr/Simple2ch_Dat/should_have_responses/size/1_2_2_1.yml +953 -0
- data/spec/vcr/Simple2ch_Response/_anchors/when_a_thre_have_both_id_and_non-id_responses/1_2_7_1.yml +5321 -0
- data/spec/vcr/Simple2ch_Response/_parse/when_real_data/when_2ch_sc/1_1_2_1_1.yml +1599 -0
- data/spec/vcr/Simple2ch_Response/_parse/when_real_data/when_open2ch_net/1_1_2_2_1.yml +1465 -0
- data/spec/vcr/Simple2ch_Thread/_new/2ch_sc/1_1_1_1.yml +1410 -0
- data/spec/vcr/Simple2ch_Thread/_new/2ch_sc/1_1_1_2.yml +1692 -0
- data/spec/vcr/Simple2ch_Thread/_new/open2ch_net/1_1_2_1.yml +1410 -0
- data/spec/vcr/Simple2ch_Thread/_new/open2ch_net/1_1_2_2.yml +1999 -0
- data/spec/vcr/Simple2ch_Thread/_responses/1_2_1.yml +1599 -0
- data/spec/vcr/Simple2ch_Thread/_responses/first/1_2_2_1.yml +1599 -0
- data/spec/vcr/Simple2ch_Thread/_responses/first/1_2_3_1.yml +1599 -0
- data/spec/vcr/Simple2ch_Thread/_title/1_3_1.yml +1599 -0
- data/spec/vcr/Simple2ch_Thread/_title/size/1_3_2_1.yml +1599 -0
- metadata +185 -11
- data/lib/simple2ch/simple2ch_exception.rb +0 -7
- data/lib/simple2ch/thre.rb +0 -140
- data/spec/res_spec.rb +0 -123
- data/spec/thre_spec.rb +0 -122
data/lib/simple2ch/board.rb
CHANGED
|
@@ -1,80 +1,56 @@
|
|
|
1
1
|
module Simple2ch
|
|
2
2
|
class Board
|
|
3
|
-
# @return [
|
|
3
|
+
# @return [Bbs2chUrlValidator::URL] 板のURL
|
|
4
4
|
attr_reader :url
|
|
5
5
|
# @return [String] 板のタイトル
|
|
6
6
|
attr_reader :title
|
|
7
|
-
# @return [
|
|
8
|
-
attr_reader :
|
|
9
|
-
# @return [String] 板の名前(コンピュータ名)
|
|
10
|
-
attr_reader :board_name
|
|
7
|
+
# @return [Time] 板オブジェクト更新日時
|
|
8
|
+
attr_reader :updated_at
|
|
11
9
|
|
|
12
10
|
|
|
13
11
|
# @param [String] title 板の名前
|
|
14
12
|
# @param [String] url 板のURL
|
|
15
13
|
# @option [Boolean] fetch_title 板の名前を自動取得するか
|
|
16
|
-
def initialize(
|
|
17
|
-
@
|
|
18
|
-
@
|
|
19
|
-
@
|
|
20
|
-
(b=Simple2ch.boards(url).find{|bb| bb.url.to_s == @url.to_s}) && b.class!=Array ? b.title : nil
|
|
21
|
-
else
|
|
22
|
-
title
|
|
23
|
-
end
|
|
24
|
-
@thres = []
|
|
14
|
+
def initialize(url, title: nil)
|
|
15
|
+
@url = validate_url(url)
|
|
16
|
+
@title = title
|
|
17
|
+
@updated_at = Time.now
|
|
25
18
|
end
|
|
26
19
|
|
|
27
20
|
# 板に属する全てのスレッドを返す
|
|
28
21
|
# @return [Array<Thre>] 板に属する全てのスレッド
|
|
29
|
-
def
|
|
30
|
-
|
|
31
|
-
@thres
|
|
32
|
-
else
|
|
33
|
-
fetch_all_thres
|
|
34
|
-
end
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
# おーぷん2chか否かを返す
|
|
38
|
-
# @return [Boolean] おーぷん2chか否か
|
|
39
|
-
def open2ch?
|
|
40
|
-
@f_open2ch && true
|
|
22
|
+
def threads
|
|
23
|
+
@threads ||= fetch_all_threads
|
|
41
24
|
end
|
|
42
25
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
def type_of_2ch
|
|
46
|
-
Simple2ch.type_of_2ch(@url.to_s)
|
|
26
|
+
def ==(other)
|
|
27
|
+
@url.to_s == other.url.to_s
|
|
47
28
|
end
|
|
48
29
|
|
|
49
30
|
private
|
|
50
|
-
|
|
51
|
-
# @param [URI] url
|
|
52
|
-
# @raise [Simple2ch::NotA2chUrlException] 2chのフォーマットで無いURLを渡したときに発生
|
|
53
|
-
# @raise [URI::InvalidURIError] そもそもURLのフォーマットで無いときに発生
|
|
31
|
+
|
|
54
32
|
def validate_url(url)
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
URI.parse("http://#{server_name}.#{parsed_url[:openflag]}2ch.#{@tld}/#{board_name}/")
|
|
63
|
-
else
|
|
64
|
-
raise NotA2chUrlException, "Given URL :#{url}"
|
|
65
|
-
end
|
|
33
|
+
parsed = if url.instance_of?(Bbs2chUrlValidator::UrlInfo)
|
|
34
|
+
url
|
|
35
|
+
else
|
|
36
|
+
Bbs2chUrlValidator::URL.parse(url)
|
|
37
|
+
end
|
|
38
|
+
return parsed if parsed && !parsed.board_name.empty?
|
|
39
|
+
raise NotA2chBoardUrlError
|
|
66
40
|
end
|
|
67
41
|
|
|
68
42
|
# 板に属する全てのスレッドをsubject.txtから取得する
|
|
69
43
|
# @return [Array<Thre>] 板に属する全てのスレッド
|
|
70
|
-
def
|
|
71
|
-
subject_url = @url
|
|
44
|
+
def fetch_all_threads
|
|
45
|
+
subject_url = @url.subject
|
|
72
46
|
|
|
73
47
|
subject_txt = Simple2ch.fetch(subject_url)
|
|
48
|
+
@threads = []
|
|
74
49
|
subject_txt.each_line do |line|
|
|
75
|
-
@
|
|
50
|
+
@threads << Simple2ch::Thread.parse(@url, line)
|
|
76
51
|
end
|
|
77
|
-
@
|
|
52
|
+
@updated_at = Time.now
|
|
53
|
+
@threads
|
|
78
54
|
end
|
|
79
55
|
end
|
|
80
|
-
end
|
|
56
|
+
end
|
data/lib/simple2ch/dat.rb
CHANGED
|
@@ -4,60 +4,62 @@ module Simple2ch
|
|
|
4
4
|
attr_reader :thread_key
|
|
5
5
|
# @return [String] タイトル
|
|
6
6
|
attr_reader :title
|
|
7
|
+
# @return [Bbs2chUrlValidator::UrlInfo] URL
|
|
8
|
+
attr_reader :url
|
|
7
9
|
|
|
8
10
|
# @param [Thre] thre スレッド
|
|
9
11
|
def initialize(thre)
|
|
10
12
|
@thre = thre
|
|
11
13
|
@thread_key = thre.thread_key
|
|
12
14
|
@data = nil
|
|
13
|
-
@
|
|
14
|
-
@
|
|
15
|
+
@responses = nil
|
|
16
|
+
@is_kako_log = nil
|
|
15
17
|
end
|
|
16
18
|
|
|
17
19
|
# Datを解析して、レスを返す
|
|
18
20
|
# @return [Array<Res>] レス
|
|
19
|
-
def
|
|
20
|
-
parse_dat unless @
|
|
21
|
-
@
|
|
21
|
+
def responses
|
|
22
|
+
parse_dat unless @responses
|
|
23
|
+
@responses
|
|
22
24
|
end
|
|
23
25
|
|
|
24
26
|
# Datを解析して過去ログかどうかを返す
|
|
25
27
|
# @return [Boolean] 過去ログか否か
|
|
26
28
|
def kako_log?
|
|
27
|
-
parse_dat if @
|
|
28
|
-
@
|
|
29
|
+
parse_dat if @is_kako_log.nil?
|
|
30
|
+
@is_kako_log
|
|
29
31
|
end
|
|
30
32
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
def dat_url
|
|
35
|
-
@thre.board.url+'dat/'+(@thread_key+'.dat')
|
|
33
|
+
# @return [Bbs2chUrlValidator::UrlInfo] dat URL
|
|
34
|
+
def url
|
|
35
|
+
Bbs2chUrlValidator::URL.parse(@thre.url.dat)
|
|
36
36
|
end
|
|
37
37
|
|
|
38
|
+
private
|
|
39
|
+
|
|
38
40
|
# datファイルを取得する
|
|
39
41
|
# @return [String] 取得したdatファイルの中身
|
|
40
42
|
def fetch_dat
|
|
41
|
-
@data ||= Simple2ch.fetch(
|
|
43
|
+
@data ||= Simple2ch.fetch(url)
|
|
42
44
|
end
|
|
43
45
|
|
|
44
46
|
# datファイルを解析してResを作成する
|
|
45
47
|
def parse_dat
|
|
46
48
|
res_num = 0
|
|
47
|
-
@
|
|
48
|
-
@
|
|
49
|
+
@responses = []
|
|
50
|
+
@is_kako_log = false
|
|
49
51
|
fetch_dat.each_line do |l|
|
|
50
52
|
res_num += 1
|
|
51
53
|
begin
|
|
52
54
|
if res_num==1
|
|
53
|
-
title = l.split('<>').pop
|
|
55
|
+
title = l.split('<>').pop.chomp
|
|
54
56
|
@title = title unless @title
|
|
55
57
|
end
|
|
56
|
-
@
|
|
57
|
-
rescue
|
|
58
|
-
@
|
|
58
|
+
@responses << Response.parse(res_num, l)
|
|
59
|
+
rescue KakoLogError
|
|
60
|
+
@is_kako_log = true
|
|
59
61
|
end
|
|
60
62
|
end
|
|
61
63
|
end
|
|
62
64
|
end
|
|
63
|
-
end
|
|
65
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
module Simple2ch
|
|
2
|
+
class Regex
|
|
3
|
+
# http://www.rubular.com/r/u1TJbQAULD
|
|
4
|
+
BOARD_EXTRACT_REGEX = /<A HREF=http:\/\/(?<subdomain>\w+).(?<openflag>open|)2ch.(?<tld>sc|net)\/(?<board_name>\w+)\/>(?<board_name_ja>.+)<\/A>/
|
|
5
|
+
# http://www.rubular.com/r/a43KJpItsL
|
|
6
|
+
OPEN2CH_THREAD_DATA_EXAMPLE_REGEX = /^<a href="(\/test\/read.cgi\/\w+\/\d{10}\/)l50">1: (.+) \(\d+\)<\/a>$/
|
|
7
|
+
# http://www.rubular.com/r/tjZefqnhP2
|
|
8
|
+
SC2CH_FIRST_RES_DATA_EXAMPLE_REGEX = /<dt>1 :<font color=green><b>(.+)<\/b><\/font>:/
|
|
9
|
+
# http://www.rubular.com/r/Mn3dPVrtXc
|
|
10
|
+
OPEN2CH_FIRST_RES_DATA_EXAMPLE_REGEX = /^<dl><dt res=1><a class=num val=1 href=.\/1>1<\/a> :<font color=#1c740d><b>(.+)<\/b>.+font color=red>主<\/font>/
|
|
11
|
+
|
|
12
|
+
constants(true).each do |c|
|
|
13
|
+
eval "#{c}.freeze"
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
module Simple2ch
|
|
2
|
-
class
|
|
2
|
+
class Response
|
|
3
3
|
# @return [Fixnum] レス番号
|
|
4
4
|
attr_reader :res_num
|
|
5
5
|
# @return [String] 投稿者名
|
|
@@ -12,86 +12,81 @@ module Simple2ch
|
|
|
12
12
|
attr_reader :mail
|
|
13
13
|
# @return [String] 内容
|
|
14
14
|
attr_reader :contents
|
|
15
|
-
# @attr_writer [Thre] thre スレッド
|
|
16
|
-
attr_writer :thre
|
|
17
15
|
|
|
18
|
-
KAKO_LOG_INFO = '過去ログ ★<><>[過去ログ]<><em>■ このスレッドは過去ログ倉庫に格納されています</em><>'
|
|
16
|
+
KAKO_LOG_INFO = '過去ログ ★<><>[過去ログ]<><em>■ このスレッドは過去ログ倉庫に格納されています</em><>'.freeze
|
|
19
17
|
|
|
20
|
-
#
|
|
21
18
|
# @param [Fixnum] res_num レス番号
|
|
22
19
|
# @param [String] author 投稿者名
|
|
23
20
|
# @param [String] author_id ID
|
|
24
21
|
# @param [Time] date 書き込み日時
|
|
25
22
|
# @param [String] mail メール欄
|
|
26
23
|
# @param [String] contents 内容
|
|
27
|
-
def initialize(res_num, author: '', author_id: '', date: nil, mail: '', contents: ''
|
|
24
|
+
def initialize(res_num, author: '', author_id: '', date: nil, mail: '', contents: '')
|
|
28
25
|
@res_num = res_num
|
|
29
26
|
@author = author
|
|
30
27
|
@author_id = author_id
|
|
31
28
|
@date = date
|
|
32
29
|
@mail = mail
|
|
33
30
|
@contents = contents
|
|
34
|
-
@thre = thre
|
|
35
31
|
end
|
|
36
32
|
|
|
37
33
|
# Datの1行から各項目を分離して、Resオブジェクトを返す
|
|
38
34
|
# @param [Fixnum] res_num レス番号
|
|
39
35
|
# @param [String] contents datのデータ1行
|
|
40
36
|
# @return [Res] 新規Resオブジェクト
|
|
41
|
-
# @raise [
|
|
42
|
-
def self.parse(res_num, contents
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
return self.new(res_num, hash)
|
|
47
|
-
else
|
|
48
|
-
raise KakoLogException
|
|
49
|
-
end
|
|
37
|
+
# @raise [KakoLogError] 過去ログ情報をパースしようとした際に発生
|
|
38
|
+
def self.parse(res_num, contents)
|
|
39
|
+
raise KakoLogError if contents.strip == KAKO_LOG_INFO
|
|
40
|
+
hash = parse_dat(contents)
|
|
41
|
+
self.new(res_num, hash)
|
|
50
42
|
end
|
|
51
43
|
|
|
52
44
|
# アンカーを抽出する 荒らしの場合は空配列を返す
|
|
53
45
|
# @return [Array<Fixnum>] 昇順ソート済みアンカー、荒らしの場合は空配列
|
|
54
46
|
def anchors
|
|
55
47
|
arashi_removal_regex = /(?:\d{1,4}(?:\]*>)?(?:>|\[>,+-\]){1,2}){9}/
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
end
|
|
48
|
+
return [] if self.contents =~ arashi_removal_regex
|
|
49
|
+
splitter_regex = '[,、, ]'
|
|
50
|
+
digit_regex = '(?:\d|[0-9])+'
|
|
51
|
+
hyphen_regex = '[−ーー\-〜~〜]'
|
|
52
|
+
extracted = self.contents.scan />((?:#{digit_regex}(?:#{splitter_regex}|#{hyphen_regex})*)+)/
|
|
53
|
+
anchors = extracted.flatten.to_s.gsub(/[\"\[\]]/, '').split(/#{splitter_regex}/)
|
|
54
|
+
anchors.delete('')
|
|
55
|
+
anchors.map! do |a|
|
|
56
|
+
if a =~ /(#{digit_regex})#{hyphen_regex}(#{digit_regex})/
|
|
57
|
+
(Range.new parseInt($1), parseInt($2)).to_a
|
|
58
|
+
else
|
|
59
|
+
parseInt(a)
|
|
69
60
|
end
|
|
70
|
-
anchors.flatten.uniq.sort
|
|
71
|
-
else
|
|
72
|
-
[]
|
|
73
61
|
end
|
|
62
|
+
anchors.flatten.uniq.sort
|
|
74
63
|
end
|
|
75
64
|
|
|
76
|
-
#
|
|
77
|
-
# @return [
|
|
78
|
-
def
|
|
79
|
-
|
|
80
|
-
received_anchors = thre.received_anchors
|
|
81
|
-
received_anchors.fetch(@res_num, [])
|
|
65
|
+
# あぼーんレスか否か
|
|
66
|
+
# @return [Boolean] あぼーんならtrue
|
|
67
|
+
def abone?
|
|
68
|
+
@date == 'あぼーん'
|
|
82
69
|
end
|
|
83
70
|
|
|
84
|
-
|
|
85
|
-
#
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
raise NoThreGivenException
|
|
92
|
-
end
|
|
71
|
+
# レスの内容をテキスト情報で得る。 , <, >, <br>はそれぞれ「 」、「<」、「>」、「(改行)」に置換される。
|
|
72
|
+
# @return [String] テキスト情報でのレスの内容
|
|
73
|
+
def contents_text
|
|
74
|
+
require 'htmlentities'
|
|
75
|
+
anchor_regex = /<a href="\.\.\/test\/read.cgi\/.+\/\d{10}\/\d{1,4}" target="_blank">(>>\d{1,4})<\/a>/
|
|
76
|
+
@htmlentities ||= HTMLEntities.new
|
|
77
|
+
@htmlentities.decode(@contents).gsub('<br>', "\n").gsub(/<\/?b>/, '').gsub(anchor_regex, '\1')
|
|
93
78
|
end
|
|
94
79
|
|
|
80
|
+
# HTMLタグを取り除いた投稿者名
|
|
81
|
+
# @return <String> HTMLタグを取り除いた投稿者名
|
|
82
|
+
def res_author_text
|
|
83
|
+
require 'htmlentities'
|
|
84
|
+
@htmlentities ||= HTMLEntities.new
|
|
85
|
+
@htmlentities.decode(@author).gsub('<br>', "\n").gsub(/<\/?b>/, '')
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
private
|
|
89
|
+
|
|
95
90
|
# 全角数字をFixnumへ変換する
|
|
96
91
|
# @param [String] strnum 全角数字
|
|
97
92
|
# @return [Fixnum] 数字
|
|
@@ -101,9 +96,9 @@ module Simple2ch
|
|
|
101
96
|
|
|
102
97
|
# Datの1行から各項目を分離して、Resオブジェクトを返すメソッドの実体
|
|
103
98
|
# @param [String] dat datのデータ1行
|
|
104
|
-
# @raise [
|
|
99
|
+
# @raise [DatParseError] Datのパースに失敗したときに発生
|
|
105
100
|
def self.parse_dat(dat)
|
|
106
|
-
split_date_and_id_regex = /(?<time>^\d{4}\/\d{2}\/\d{2}\(.\) ?\d{2}:\d{2}:\d{2}(\.\d{2})?)(?: ID:(?<author_id
|
|
101
|
+
split_date_and_id_regex = /(?<time>^\d{4}\/\d{2}\/\d{2}\(.\) ?\d{2}:\d{2}:\d{2}(\.\d{2,3})?)(?: ID:(?<author_id>(\S|.)+)$)?/
|
|
107
102
|
ret = {}
|
|
108
103
|
split = dat.split('<>')
|
|
109
104
|
ret[:author] = split[0]
|
|
@@ -116,19 +111,25 @@ module Simple2ch
|
|
|
116
111
|
ret[:author_id] = $~[:author_id]
|
|
117
112
|
else
|
|
118
113
|
if dat.index 'あぼーん'
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
contents: 'あぼーん',
|
|
123
|
-
date: 'あぼーん',
|
|
124
|
-
author_id: 'あぼーん',
|
|
125
|
-
}
|
|
114
|
+
return a_bone_data
|
|
115
|
+
elsif dat.index 'Over 1000 Thread'
|
|
116
|
+
# do nothing
|
|
126
117
|
else
|
|
127
|
-
raise
|
|
118
|
+
raise DatParseError, "Data didn't match regex. Data:#{date_and_author_id}"
|
|
128
119
|
end
|
|
129
120
|
end
|
|
130
121
|
|
|
131
122
|
ret
|
|
132
123
|
end
|
|
133
124
|
end
|
|
125
|
+
|
|
126
|
+
def a_bone_data
|
|
127
|
+
{
|
|
128
|
+
author: 'あぼーん',
|
|
129
|
+
mail: 'あぼーん',
|
|
130
|
+
contents: 'あぼーん',
|
|
131
|
+
date: 'あぼーん',
|
|
132
|
+
author_id: 'あぼーん',
|
|
133
|
+
}
|
|
134
|
+
end
|
|
134
135
|
end
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
module Simple2ch
|
|
2
|
+
class Simple2chError < StandardError; end
|
|
3
|
+
class NotA2chUrlError < Simple2chError; end
|
|
4
|
+
class NotA2chBoardUrlError < NotA2chUrlError; end
|
|
5
|
+
class NotA2chThreadUrlError < NotA2chUrlError; end
|
|
6
|
+
class KakoLogError < Simple2chError; end
|
|
7
|
+
class DatParseError < Simple2chError; end
|
|
8
|
+
class NoThreGivenError < Simple2chError; end
|
|
9
|
+
end
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
module Simple2ch
|
|
2
|
+
class Thread
|
|
3
|
+
# @return [String] スレッドキー(Unix time)
|
|
4
|
+
attr_reader :thread_key
|
|
5
|
+
# @return [Bbs2chUrlValidator::UrlInfo] URL
|
|
6
|
+
attr_reader :url
|
|
7
|
+
|
|
8
|
+
# @param [Bbs2chUrlValidator::UrlInfo] URL
|
|
9
|
+
# @param [String] title スレッド名
|
|
10
|
+
def initialize(url, title: nil)
|
|
11
|
+
@title = title
|
|
12
|
+
@url = if url.instance_of?(Bbs2chUrlValidator::UrlInfo)
|
|
13
|
+
url
|
|
14
|
+
else
|
|
15
|
+
Bbs2chUrlValidator::URL.parse(url)
|
|
16
|
+
end
|
|
17
|
+
raise NotA2chThreadUrlError.new "url: #{url}, title: #{title}" if !@url || @url.thread_key.nil?
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# 板オブジェクトとsubject.txtの1行データを渡すとスレオブジェクトを返す
|
|
21
|
+
# @param [Bbs2chUrlValidator::UrlInfo] board_url スレッドが属する板情報
|
|
22
|
+
# @param [String] thread_data 0000000000.dat<>スレッドタイトル (レス数)
|
|
23
|
+
# @return [Simple2ch::Thread] スレ
|
|
24
|
+
def self.parse(board_url, thread_data)
|
|
25
|
+
thread_data.match(/(\d{10})\.dat<>(.+) \((\d+)\)/) do |m|
|
|
26
|
+
thread_key = m[1]
|
|
27
|
+
title = m[2].force_encoding('utf-8').chomp
|
|
28
|
+
thread_url = Simple2ch.generate_url(:thread, board_url, thread_key: thread_key)
|
|
29
|
+
self.new(thread_url, title: title)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Datを解析して、レスを返す
|
|
34
|
+
# @param [Array<Fixnum>,Fixnum] num_of_responses 取得したいレス番号
|
|
35
|
+
def responses(num_of_responses = nil)
|
|
36
|
+
fetch_dat if @responses.nil?
|
|
37
|
+
case num_of_responses
|
|
38
|
+
when Array
|
|
39
|
+
raise 'Blank array was given.' if num_of_responses.empty?
|
|
40
|
+
@responses.find_all { |r| num_of_responses.index(r.res_num) }
|
|
41
|
+
when Fixnum
|
|
42
|
+
@responses.find { |r| r.res_num == num_of_responses }
|
|
43
|
+
when NilClass
|
|
44
|
+
@responses
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
alias response responses
|
|
48
|
+
|
|
49
|
+
# 過去ログかどうかを返す
|
|
50
|
+
# @return [Boolean] 過去ログか否か
|
|
51
|
+
def kako_log?
|
|
52
|
+
fetch_dat if @is_kako_log.nil?
|
|
53
|
+
@is_kako_log
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# 全てのレスに対し、あるレスへのアンカーが書き込まれているレス番号のハッシュを返す
|
|
57
|
+
# @return [Hash]{ res_num<Fixnum> => res_nums<Array<Fixnum>> } レス番号のハッシュ
|
|
58
|
+
def received_anchors
|
|
59
|
+
@received_anchors ||= calc_received_anchors
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# 2chタイプ名の取得
|
|
63
|
+
# @return [Symbol] 2chタイプ名(:sc, :open)
|
|
64
|
+
def type_of_2ch
|
|
65
|
+
Simple2ch.type_of_2ch(@url.to_s)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
## スレのURLを返す
|
|
69
|
+
## @return [String] スレのURL
|
|
70
|
+
#def url(board: nil, thread_key: nil, force_refresh: false)
|
|
71
|
+
# if @url && !force_refresh
|
|
72
|
+
# @url
|
|
73
|
+
# else
|
|
74
|
+
# if board
|
|
75
|
+
# parsed_url = Bbs2chUrlValidator::URL.parse(board.url.built_url)
|
|
76
|
+
# Simple2ch.generate_url(:thread, parsed_url, thread_key: thread_key)
|
|
77
|
+
# else
|
|
78
|
+
# fail "Not implemented."
|
|
79
|
+
# end
|
|
80
|
+
# end
|
|
81
|
+
#end
|
|
82
|
+
|
|
83
|
+
# タイトルを返す
|
|
84
|
+
# @return [String] スレッドの名前
|
|
85
|
+
def title
|
|
86
|
+
fetch_dat unless @title
|
|
87
|
+
@title
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
private
|
|
91
|
+
|
|
92
|
+
# 全てのレスに対し、あるレスへのアンカーが書き込まれているレス番号のハッシュを返す
|
|
93
|
+
# @return [Hash]{ res_num<Fixnum> => res_nums<Array<Fixnum>> } レス番号のハッシュ
|
|
94
|
+
def calc_received_anchors
|
|
95
|
+
ret = {}
|
|
96
|
+
responses.each do |res|
|
|
97
|
+
res.anchors.each do |anchor|
|
|
98
|
+
ret.store(anchor, ret.fetch(anchor, []).push(res.res_num))
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
ret
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# Datを取ってきてレスと過去ログかどうかを返す
|
|
105
|
+
# @return [Boolean] is_kako_log 過去ログか否か
|
|
106
|
+
def fetch_dat
|
|
107
|
+
dat = Dat.new(self)
|
|
108
|
+
@responses = dat.responses
|
|
109
|
+
@title = dat.title
|
|
110
|
+
@is_kako_log = dat.kako_log?
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|