bbiff 0.2.2 → 0.3.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/README.md +11 -9
- data/lib/bbiff/bbs_reader.rb +374 -101
- data/lib/bbiff/executable.rb +16 -10
- data/lib/bbiff/res_format.rb +1 -19
- data/lib/bbiff/show.rb +1 -1
- data/lib/bbiff/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 037966c6b4adc37eb5630c02746d1c0a6ef609ed
|
|
4
|
+
data.tar.gz: 87886e10129ef9ae257a78c52396bc0e3324ab39
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1116585b2aa1dd80258bf3e8d99bba5cac503790539faeb5e5fea99b6bfedb9ea1601b59f0b3e061264f758a0500e62ef8f611cc5a45fc99ac974703344f9c73
|
|
7
|
+
data.tar.gz: 7eb9d8a7d563b58cc52534604e7af91a2d40f416eb66bbe8ed5ac60cd3963030fd244d320c134e17da3c82b1b27a23aacafd0f7723f51c6e442598d89e7b3706
|
data/README.md
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
## 必要なもの
|
|
4
4
|
|
|
5
|
-
notify-send
|
|
5
|
+
デスクトップに通知ポップアップを表示する、notify-send コマンド。Ubuntu
|
|
6
|
+
では以下のようにインストールできます。
|
|
6
7
|
|
|
7
8
|
sudo apt-get install libnotify-bin
|
|
8
9
|
|
|
@@ -14,8 +15,10 @@ notify-send コマンド
|
|
|
14
15
|
|
|
15
16
|
bbiff スレッドのURL レス通知を始める番号
|
|
16
17
|
|
|
17
|
-
スレッドのURL
|
|
18
|
-
|
|
18
|
+
スレッドのURLはしたらば掲示板の場合、
|
|
19
|
+
`http://jbbs.shitaraba.net/bbs/read.cgi/カテゴリ/板ID/スレID/`、2ちゃ
|
|
20
|
+
んねる互換掲示板の場合は、`http://ホスト名/test/read.cgi/板名/スレID/`
|
|
21
|
+
のような形式になります。
|
|
19
22
|
|
|
20
23
|
単に
|
|
21
24
|
|
|
@@ -23,16 +26,11 @@ notify-send コマンド
|
|
|
23
26
|
|
|
24
27
|
とすると、前回監視したスレッドを監視します。
|
|
25
28
|
|
|
26
|
-
## 開発・TODO
|
|
27
|
-
|
|
28
|
-
- .travis.ymlでテストするなら要編集
|
|
29
|
-
- moduleの中にまとめるべきかも
|
|
30
|
-
|
|
31
29
|
## リリース
|
|
32
30
|
|
|
33
31
|
ver 0.1.0
|
|
34
32
|
* gem 化した。(DoG-peer さん)
|
|
35
|
-
|
|
33
|
+
|
|
36
34
|
ver 0.1.2
|
|
37
35
|
* notify-send コマンドがインストールされていない場合は echo コマンド
|
|
38
36
|
を利用するようにした。(raduwen さん)
|
|
@@ -53,6 +51,10 @@ ver 0.2.2
|
|
|
53
51
|
* 最後に読み込んだスレの情報が無い状態で、引数なしで bbiff を起動し
|
|
54
52
|
た時にエラーになっていたのを修正。
|
|
55
53
|
|
|
54
|
+
ver 0.3.0
|
|
55
|
+
* 2ちゃんねる互換掲示板に対応したつもり。
|
|
56
|
+
* 日付の相対表示を辞めた。
|
|
57
|
+
|
|
56
58
|
## 作者
|
|
57
59
|
|
|
58
60
|
予定地 <plonk@piano.email.ne.jp>
|
data/lib/bbiff/bbs_reader.rb
CHANGED
|
@@ -1,141 +1,414 @@
|
|
|
1
1
|
require 'net/http'
|
|
2
2
|
require 'uri'
|
|
3
|
+
require 'pp' if $DEBUG
|
|
3
4
|
|
|
4
5
|
module Bbs
|
|
5
6
|
|
|
6
|
-
class
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
end
|
|
7
|
+
class Post
|
|
8
|
+
class << self
|
|
9
|
+
def from_s(str)
|
|
10
|
+
Post.new(*str.split('<>', 5))
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
return URI.parse("http://jbbs.shitaraba.net/bbs/rawmode.cgi/#{@カテゴリ}/#{@掲示板番号}/#{スレッド番号}/")
|
|
16
|
-
end
|
|
14
|
+
attr_reader :no, :name, :mail, :body, :date
|
|
17
15
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
16
|
+
def initialize(no, name, mail, date, body)
|
|
17
|
+
@no = no.to_i
|
|
18
|
+
@name = name
|
|
19
|
+
@mail = mail
|
|
20
|
+
@date = date
|
|
21
|
+
@body = body
|
|
22
|
+
end
|
|
22
23
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
# 削除された時のフィールドの値は、掲示板の設定によるなぁ。
|
|
25
|
+
# def deleted?
|
|
26
|
+
# @date == '<削除>'
|
|
27
|
+
# end
|
|
27
28
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
return r.force_encoding("EUC-JP").encode("UTF-8")
|
|
32
|
-
end
|
|
29
|
+
def to_s
|
|
30
|
+
[no, name, mail, date, body].join('<>')
|
|
31
|
+
end
|
|
33
32
|
|
|
34
|
-
def thread(スレッド番号)
|
|
35
|
-
threads.find { |t| t.id == スレッド番号 }
|
|
36
33
|
end
|
|
37
34
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
35
|
+
class Downloader
|
|
36
|
+
class DownloadFailure < StandardError
|
|
37
|
+
attr_reader :response
|
|
38
|
+
|
|
39
|
+
def initialize(response)
|
|
40
|
+
@response = response
|
|
41
|
+
end
|
|
43
42
|
end
|
|
44
|
-
end
|
|
45
43
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
http.get(url.path)
|
|
49
|
-
}
|
|
50
|
-
return 応答.body
|
|
51
|
-
end
|
|
44
|
+
class Resource
|
|
45
|
+
attr_reader :data
|
|
52
46
|
|
|
53
|
-
|
|
47
|
+
def initialize(data)
|
|
48
|
+
self.data = data
|
|
49
|
+
end
|
|
54
50
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
end
|
|
60
|
-
end
|
|
51
|
+
def data=(new_data)
|
|
52
|
+
type_check(new_data)
|
|
53
|
+
@data = new_data.dup.freeze
|
|
54
|
+
end
|
|
61
55
|
|
|
62
|
-
|
|
63
|
-
attr_reader :no, :name, :mail, :body
|
|
56
|
+
private
|
|
64
57
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
58
|
+
# ASCII-8BIT エンコーディングの String に限定する。
|
|
59
|
+
def type_check(data)
|
|
60
|
+
unless data.is_a? String
|
|
61
|
+
raise TypeError, 'not a string'
|
|
62
|
+
end
|
|
63
|
+
unless data.encoding == Encoding::ASCII_8BIT
|
|
64
|
+
raise ArgumentError, "encoding not ASCII-8BIT (#{data.encoding})"
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
69
68
|
|
|
70
|
-
|
|
71
|
-
@no = no.to_i
|
|
72
|
-
@name = name
|
|
73
|
-
@mail = mail
|
|
74
|
-
@date = date
|
|
75
|
-
@body = body
|
|
76
|
-
end
|
|
69
|
+
attr_reader :encoding
|
|
77
70
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
71
|
+
def initialize(encoding = 'UTF-8')
|
|
72
|
+
@encoding = encoding
|
|
73
|
+
@resource_cache = {}
|
|
74
|
+
end
|
|
81
75
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
76
|
+
# ASCII-8BIT エンコーディングの文字列を返す。
|
|
77
|
+
def download_binary(uri)
|
|
78
|
+
resource = @resource_cache[uri]
|
|
79
|
+
if resource
|
|
80
|
+
Net::HTTP.start(uri.host, uri.port) do |http|
|
|
81
|
+
request = Net::HTTP::Get.new(uri)
|
|
82
|
+
request['range'] = "bytes=#{resource.data.bytesize}-"
|
|
83
|
+
response = http.request(request)
|
|
84
|
+
response.body.force_encoding('ASCII-8BIT')
|
|
85
|
+
pp response.code if $DEBUG
|
|
86
|
+
pp response.to_hash if $DEBUG
|
|
87
|
+
case response
|
|
88
|
+
when Net::HTTPPartialContent
|
|
89
|
+
p :partial if $DEBUG
|
|
90
|
+
resource.data += response.body
|
|
91
|
+
when Net::HTTPRequestedRangeNotSatisfiable
|
|
92
|
+
p :not_satisfiable if $DEBUG
|
|
93
|
+
# 多分DATは更新されていない
|
|
94
|
+
when Net::HTTPOK
|
|
95
|
+
p :ok if $DEBUG
|
|
96
|
+
@resource_cache[uri] = Resource.new(response.body)
|
|
97
|
+
return response.body
|
|
98
|
+
else
|
|
99
|
+
raise DownloadFailure.new(response)
|
|
100
|
+
end
|
|
101
|
+
return resource.data
|
|
102
|
+
end
|
|
103
|
+
else
|
|
104
|
+
p :no_resource_yet if $DEBUG
|
|
105
|
+
body = download_binary_nocache(uri)
|
|
106
|
+
@resource_cache[uri] = Resource.new(body)
|
|
107
|
+
return body
|
|
108
|
+
end
|
|
109
|
+
end
|
|
85
110
|
|
|
86
|
-
|
|
111
|
+
def download_binary_nocache(uri)
|
|
112
|
+
response = nil
|
|
113
|
+
Net::HTTP.start(uri.host, uri.port) do |http|
|
|
114
|
+
request = Net::HTTP::Get.new(uri)
|
|
115
|
+
response = http.request(request)
|
|
116
|
+
response.body.force_encoding('ASCII-8BIT')
|
|
117
|
+
pp response.code if $DEBUG
|
|
118
|
+
pp response.to_hash if $DEBUG
|
|
119
|
+
case response
|
|
120
|
+
when Net::HTTPOK
|
|
121
|
+
else
|
|
122
|
+
raise DownloadFailure.new(response)
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
return response.body
|
|
126
|
+
end
|
|
87
127
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
Time.new(y, mon, d, h, min, sec)
|
|
92
|
-
else
|
|
93
|
-
fail ArgumentError
|
|
128
|
+
def download_text(uri)
|
|
129
|
+
# dup は重要。
|
|
130
|
+
download_binary(uri).dup.force_encoding(encoding).encode('UTF-8')
|
|
94
131
|
end
|
|
95
132
|
end
|
|
96
|
-
end
|
|
97
133
|
|
|
98
|
-
class
|
|
99
|
-
|
|
134
|
+
class BoardBase
|
|
135
|
+
private_class_method :new
|
|
136
|
+
attr_reader :settings_url
|
|
100
137
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
138
|
+
def initialize(text_encoding)
|
|
139
|
+
@downloader = Downloader.new(text_encoding)
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def thread(thread_num)
|
|
143
|
+
threads.find { |t| t.id == thread_num }
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def settings
|
|
147
|
+
return parse_settings(download_text(@settings_url))
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def thread_list
|
|
151
|
+
return download_text(@thread_list_url)
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def dat(thread_num)
|
|
155
|
+
return download_text(dat_url(thread_num))
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def threads
|
|
159
|
+
thread_list.each_line.map do |line|
|
|
160
|
+
create_thread_from_line(line)
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
# 抽象メソッド
|
|
165
|
+
def create_thread_from_line(_line)
|
|
166
|
+
raise 'unimplemented'
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def dat_url(_thread_num)
|
|
170
|
+
raise 'unimplemented'
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
protected
|
|
174
|
+
|
|
175
|
+
def download_binary(url)
|
|
176
|
+
@downloader.download_binary(url)
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def download_text(url)
|
|
180
|
+
@downloader.download_text(url)
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def parse_settings(string)
|
|
184
|
+
string.each_line.map { |line|
|
|
185
|
+
line.chomp.split(/=/, 2)
|
|
186
|
+
}.to_h
|
|
187
|
+
end
|
|
106
188
|
end
|
|
107
189
|
|
|
108
|
-
|
|
109
|
-
|
|
190
|
+
class ThreadBase
|
|
191
|
+
private_class_method :new
|
|
192
|
+
attr_reader :board, :id, :title, :last
|
|
193
|
+
|
|
194
|
+
def initialize(board, id, title, last)
|
|
195
|
+
@board = board
|
|
196
|
+
@id = id
|
|
197
|
+
@title = title
|
|
198
|
+
@last = last
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
def dat_url
|
|
202
|
+
@board.dat_url(@id)
|
|
203
|
+
end
|
|
204
|
+
|
|
110
205
|
end
|
|
111
206
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
207
|
+
module Shitaraba
|
|
208
|
+
SHITARABA_THREAD_URL_PATTERN = %r{\Ahttp://jbbs\.shitaraba\.net/bbs/read\.cgi/(\w+)/(\d+)/(\d+)(:?|\/.*)\z}
|
|
209
|
+
SHITARABA_BOARD_TOP_URL_PATTERN = %r{\Ahttp://jbbs\.shitaraba\.net/(\w+)/(\d+)/?\z}
|
|
210
|
+
|
|
211
|
+
# したらば板
|
|
212
|
+
class Board < Bbs::BoardBase
|
|
213
|
+
class << self
|
|
214
|
+
def from_url(url)
|
|
215
|
+
if url.to_s =~ SHITARABA_BOARD_TOP_URL_PATTERN
|
|
216
|
+
category, board_num = $1, $2.to_i
|
|
217
|
+
return Board.send(:new, category, board_num)
|
|
218
|
+
elsif url.to_s =~ SHITARABA_THREAD_URL_PATTERN
|
|
219
|
+
category, board_num, thread_num = $1, $2.to_i, $3.to_i
|
|
220
|
+
return Board.send(:new, category, board_num)
|
|
221
|
+
else
|
|
222
|
+
return nil
|
|
223
|
+
end
|
|
224
|
+
end
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
def initialize(category, board_num)
|
|
228
|
+
super('EUC-JP')
|
|
229
|
+
@category = category
|
|
230
|
+
@board_num = board_num
|
|
231
|
+
@settings_url = URI.parse( "http://jbbs.shitaraba.net/bbs/api/setting.cgi/#{category}/#{board_num}/" )
|
|
232
|
+
@thread_list_url = URI.parse( "http://jbbs.shitaraba.net/#{category}/#{board_num}/subject.txt" )
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
def dat_url(thread_num)
|
|
236
|
+
return URI.parse("http://jbbs.shitaraba.net/bbs/rawmode.cgi/#{@category}/#{@board_num}/#{thread_num}/")
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
def create_thread_from_line(line)
|
|
240
|
+
Thread.from_line(line, self)
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
# したらばスレッド
|
|
245
|
+
class Thread < Bbs::ThreadBase
|
|
246
|
+
class << self
|
|
247
|
+
def from_url(url)
|
|
248
|
+
if url.to_s =~ SHITARABA_THREAD_URL_PATTERN
|
|
249
|
+
category, board_num, thread_num = $1, $2.to_i, $3.to_i
|
|
250
|
+
board = Board.send(:new, category, board_num)
|
|
251
|
+
thread = board.thread(thread_num)
|
|
252
|
+
raise 'no such thread' if thread.nil?
|
|
253
|
+
return thread
|
|
254
|
+
else
|
|
255
|
+
return nil
|
|
256
|
+
end
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
def from_line(line, board)
|
|
260
|
+
unless line =~ /^(\d+)\.cgi,(.+?)\((\d+)\)$/
|
|
261
|
+
fail 'スレ一覧のフォーマットが変です'
|
|
262
|
+
end
|
|
263
|
+
id, title, last = $1.to_i, $2, $3.to_i
|
|
264
|
+
Thread.send(:new, board, id, title, last)
|
|
265
|
+
end
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
def initialize(board, id, title, last = 1)
|
|
269
|
+
super
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
def posts(range)
|
|
273
|
+
fail ArgumentError unless range.is_a? Range
|
|
274
|
+
dat_for_range(range).each_line.map do |line|
|
|
275
|
+
post = create_post(line.chomp)
|
|
276
|
+
@last = [post.no, last].max
|
|
277
|
+
post
|
|
278
|
+
end
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
private
|
|
282
|
+
|
|
283
|
+
def create_post(line)
|
|
284
|
+
no, name, mail, date, body, = line.split('<>', 6)
|
|
285
|
+
Post.new(no, name, mail, date, body)
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
def dat_for_range(range)
|
|
289
|
+
if range.last == Float::INFINITY
|
|
290
|
+
query = "#{range.first}-"
|
|
291
|
+
else
|
|
292
|
+
query = "#{range.first}-#{range.last}"
|
|
293
|
+
end
|
|
294
|
+
url = URI(dat_url + query)
|
|
295
|
+
@board.send(:download_text, url)
|
|
296
|
+
end
|
|
297
|
+
end
|
|
298
|
+
end # Shitaraba
|
|
299
|
+
|
|
300
|
+
module Nichan
|
|
301
|
+
# 2ちゃん板
|
|
302
|
+
class Board < Bbs::BoardBase
|
|
303
|
+
class << self
|
|
304
|
+
def from_url(url)
|
|
305
|
+
uri = URI.parse(url)
|
|
306
|
+
board_name = uri.path.split('/').reject(&:empty?).first
|
|
307
|
+
raise 'bad url' if board_name.nil?
|
|
308
|
+
Board.send(:new, uri.hostname, uri.port, board_name)
|
|
309
|
+
end
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
def initialize(hostname, port, name)
|
|
313
|
+
super('CP932')
|
|
314
|
+
@hostname, @port, @name = hostname, port, name
|
|
315
|
+
|
|
316
|
+
@settings_url = URI.parse("http://#{hostname}:#{port}/#{name}/SETTING.TXT")
|
|
317
|
+
@thread_list_url = URI.parse("http://#{hostname}:#{port}/#{name}/subject.txt")
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
def dat_url(thread_num)
|
|
321
|
+
"http://#{@hostname}:#{@port}/#{@name}/dat/#{thread_num}.dat"
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
def create_thread_from_line(line)
|
|
325
|
+
Thread.from_line(line, self)
|
|
326
|
+
end
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
NICHAN_THREAD_URL_PATTERN = %r{\Ahttp://[a-zA-z\-\.]+/test/read\.cgi\/(\w+)/(\d+)($|/)}
|
|
330
|
+
|
|
331
|
+
# 2ちゃんスレッド
|
|
332
|
+
class Thread < ThreadBase
|
|
333
|
+
class << self
|
|
334
|
+
def from_url(url)
|
|
335
|
+
if url.to_s =~ NICHAN_THREAD_URL_PATTERN
|
|
336
|
+
board_name, thread_num = $1, $2.to_i
|
|
337
|
+
uri = URI(url)
|
|
338
|
+
board = Board.send(:new, uri.hostname, uri.port, board_name)
|
|
339
|
+
thread = board.thread(thread_num)
|
|
340
|
+
raise 'no such thread' if thread.nil?
|
|
341
|
+
return thread
|
|
342
|
+
else
|
|
343
|
+
raise 'bad URL'
|
|
344
|
+
end
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
def from_line(line, board)
|
|
348
|
+
unless line =~ /^(\d+)\.dat<>(.+?) \((\d+)\)$/
|
|
349
|
+
fail 'スレ一覧のフォーマットが変です'
|
|
350
|
+
end
|
|
351
|
+
id, title, last = $1.to_i, $2, $3.to_i
|
|
352
|
+
Thread.send(:new, board, id, title, last)
|
|
353
|
+
end
|
|
354
|
+
end
|
|
355
|
+
|
|
356
|
+
def initialize(board, id, title, last = 1)
|
|
357
|
+
super
|
|
118
358
|
end
|
|
359
|
+
|
|
360
|
+
def posts(range)
|
|
361
|
+
fail ArgumentError unless range.is_a? Range
|
|
362
|
+
url = URI(dat_url)
|
|
363
|
+
lines = @board.send(:download_text, url)
|
|
364
|
+
ary = []
|
|
365
|
+
lines.each_line.with_index(1) do |line, res_no|
|
|
366
|
+
next unless range.include?(res_no)
|
|
367
|
+
|
|
368
|
+
name, mail, date, body, title = line.chomp.split('<>', 5)
|
|
369
|
+
post = Post.new(res_no.to_s, name, mail, date, body)
|
|
370
|
+
ary << post
|
|
371
|
+
@last = [post.no, last].max
|
|
372
|
+
end
|
|
373
|
+
return ary
|
|
374
|
+
end
|
|
375
|
+
|
|
119
376
|
end
|
|
377
|
+
|
|
120
378
|
end
|
|
121
379
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
380
|
+
# Bbs.create_board(url) → Shitaraba::Board | Nichan::Board
|
|
381
|
+
# Bbs.create_thread(url) → Shitaraba::Thread | Nichan::Thread
|
|
382
|
+
|
|
383
|
+
BOARD_CLASSES = [Shitaraba::Board, Nichan::Board].freeze
|
|
384
|
+
THREAD_CLASSES = [Shitaraba::Thread, Nichan::Thread].freeze
|
|
385
|
+
|
|
386
|
+
if $DEBUG
|
|
387
|
+
unless BOARD_CLASSES.all? { |k| k.respond_to?(:from_url) }
|
|
388
|
+
raise 'unmet assumption'
|
|
389
|
+
end
|
|
390
|
+
|
|
391
|
+
unless THREAD_CLASSES.all? { |k| k.respond_to?(:from_url) and k.respond_to?(:from_line) }
|
|
392
|
+
raise 'unmet assumption'
|
|
127
393
|
end
|
|
128
|
-
url = URI(dat_url + query)
|
|
129
|
-
@board.ダウンロード(url).force_encoding("EUC-JP").encode("UTF-8")
|
|
130
394
|
end
|
|
131
|
-
end
|
|
132
395
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
396
|
+
def create_board(url)
|
|
397
|
+
BOARD_CLASSES.each do |klass|
|
|
398
|
+
board = klass.from_url(url)
|
|
399
|
+
return board if board
|
|
400
|
+
end
|
|
401
|
+
return nil
|
|
402
|
+
end
|
|
403
|
+
module_function :create_board
|
|
139
404
|
|
|
140
|
-
|
|
405
|
+
def create_thread(url)
|
|
406
|
+
THREAD_CLASSES.each do |klass|
|
|
407
|
+
thread = klass.from_url(url)
|
|
408
|
+
return thread if thread
|
|
409
|
+
end
|
|
410
|
+
return nil
|
|
411
|
+
end
|
|
412
|
+
module_function :create_thread
|
|
141
413
|
|
|
414
|
+
end # Bbs
|
data/lib/bbiff/executable.rb
CHANGED
|
@@ -67,10 +67,18 @@ class Executable
|
|
|
67
67
|
end
|
|
68
68
|
end
|
|
69
69
|
|
|
70
|
+
def get_board_settings(board)
|
|
71
|
+
return board.settings
|
|
72
|
+
rescue Bbs::Downloader::DownloadFailure => e
|
|
73
|
+
STDERR.puts "Warning: 以下の場所から掲示板の設定が取得できませんでした。(#{e.response.message})"
|
|
74
|
+
STDERR.puts board.settings_url
|
|
75
|
+
return {'BBS_TITLE'=>'<不明>'}
|
|
76
|
+
end
|
|
77
|
+
|
|
70
78
|
def start_polling(thread, start_no)
|
|
71
79
|
out = LineIndicator.new
|
|
72
80
|
delay = @settings.current['delay_seconds']
|
|
73
|
-
board_settings = thread.board
|
|
81
|
+
board_settings = get_board_settings(thread.board)
|
|
74
82
|
thread_stop = (board_settings['BBS_THREAD_STOP'] || '1000').to_i
|
|
75
83
|
|
|
76
84
|
puts "#{board_settings['BBS_TITLE']} − #{thread.title}(#{thread.last})"
|
|
@@ -104,7 +112,9 @@ class Executable
|
|
|
104
112
|
rescue Interrupt
|
|
105
113
|
STDERR.puts "\nユーザー割り込みにより停止"
|
|
106
114
|
rescue => e
|
|
107
|
-
STDERR.puts "error occured
|
|
115
|
+
STDERR.puts "error occured"
|
|
116
|
+
puts e.message
|
|
117
|
+
puts e.backtrace
|
|
108
118
|
STDERR.puts "retrying..., ^C to quit"
|
|
109
119
|
sleep 3
|
|
110
120
|
start_polling(thread, start_no)
|
|
@@ -129,24 +139,20 @@ EOD
|
|
|
129
139
|
raise UsageError
|
|
130
140
|
elsif ARGV.size < 1
|
|
131
141
|
url = @settings.current['thread_url']
|
|
142
|
+
thread = Bbs::create_thread(url)
|
|
132
143
|
else
|
|
133
144
|
url = ARGV[0]
|
|
134
145
|
|
|
135
|
-
|
|
146
|
+
begin
|
|
147
|
+
thread = Bbs::create_thread(url)
|
|
136
148
|
@settings.current['thread_url'] = url
|
|
137
|
-
|
|
149
|
+
rescue
|
|
138
150
|
puts "URLが変です"
|
|
139
151
|
usage
|
|
140
152
|
exit 1
|
|
141
153
|
end
|
|
142
154
|
end
|
|
143
155
|
|
|
144
|
-
if url =~ %r{\Ah?ttp://jbbs.shitaraba.net/bbs/read.cgi/(\w+)/(\d+)/(\d+)/?\z}
|
|
145
|
-
ita = [$1, $2.to_i]
|
|
146
|
-
sure = $3.to_i
|
|
147
|
-
end
|
|
148
|
-
|
|
149
|
-
thread = Bbs::C板.new(*ita).thread(sure)
|
|
150
156
|
start_no = ARGV[1] ? ARGV[1].to_i : thread.last + 1
|
|
151
157
|
start_polling(thread, start_no)
|
|
152
158
|
rescue UsageError
|
data/lib/bbiff/res_format.rb
CHANGED
|
@@ -23,24 +23,6 @@ def render_resno(no)
|
|
|
23
23
|
no.to_s
|
|
24
24
|
end
|
|
25
25
|
|
|
26
|
-
def render_date(t)
|
|
27
|
-
weekday = [*'日月火水木金土'.each_char]
|
|
28
|
-
delta = Time.now - t
|
|
29
|
-
|
|
30
|
-
case delta
|
|
31
|
-
when 0...1
|
|
32
|
-
"たった今"
|
|
33
|
-
when 1...60
|
|
34
|
-
"#{delta.to_i}秒前"
|
|
35
|
-
when 60...3600
|
|
36
|
-
"#{(delta / 60).to_i}分前"
|
|
37
|
-
when 3600...(24 * 3600)
|
|
38
|
-
"#{(delta / 3600).to_i}時間前"
|
|
39
|
-
else
|
|
40
|
-
"%d/%d/%d(%s) %02d:%02d:%02d" % [t.year, t.month, t.day, weekday[t.wday], t.hour, t.min, t.sec]
|
|
41
|
-
end
|
|
42
|
-
end
|
|
43
|
-
|
|
44
26
|
def indent(n, text)
|
|
45
27
|
text.each_line.map { |line| n.en + line }.join
|
|
46
28
|
end
|
|
@@ -51,7 +33,7 @@ def render_body(body)
|
|
|
51
33
|
end
|
|
52
34
|
|
|
53
35
|
def render_post(post)
|
|
54
|
-
"#{render_resno post.no}:#{render_name post.name, post.mail}:#{
|
|
36
|
+
"#{render_resno post.no}:#{render_name post.name, post.mail}:#{post.date}\n" \
|
|
55
37
|
"#{render_body post.body}"
|
|
56
38
|
end
|
|
57
39
|
|
data/lib/bbiff/show.rb
CHANGED
|
@@ -16,7 +16,7 @@ class Show
|
|
|
16
16
|
end
|
|
17
17
|
|
|
18
18
|
title = ARGV[0]
|
|
19
|
-
post = Bbs::Post.
|
|
19
|
+
post = Bbs::Post.from_s(ARGV[1])
|
|
20
20
|
notify_send = ENV['BBIFF_NOTIFY_SEND'] ||
|
|
21
21
|
(`which #{NOTIFY_SEND}` != "" ? NOTIFY_SEND : 'echo')
|
|
22
22
|
system("#{notify_send} #{Shellwords.escape(title)} #{Shellwords.escape(render_post(post))}")
|
data/lib/bbiff/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: bbiff
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Yoteichi
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2016-
|
|
11
|
+
date: 2016-09-20 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bundler
|