net-irc2 0.0.10
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/AUTHORS.txt +33 -0
- data/ChangeLog +95 -0
- data/README +91 -0
- data/Rakefile +69 -0
- data/examples/2ch.rb +225 -0
- data/examples/2ig.rb +267 -0
- data/examples/client.rb +23 -0
- data/examples/echo_bot.rb +31 -0
- data/examples/echo_bot_celluloid.rb +33 -0
- data/examples/gig.rb +192 -0
- data/examples/gmail.rb +202 -0
- data/examples/gtig.rb +420 -0
- data/examples/hatena-star-stream.rb +270 -0
- data/examples/hcig.rb +285 -0
- data/examples/hig.rb +771 -0
- data/examples/iig.rb +819 -0
- data/examples/ircd.rb +358 -0
- data/examples/lig.rb +551 -0
- data/examples/lingr.rb +327 -0
- data/examples/mixi.rb +252 -0
- data/examples/sig.rb +188 -0
- data/examples/tig.rb +2712 -0
- data/lib/net/irc/client/channel_manager.rb +144 -0
- data/lib/net/irc/client.rb +117 -0
- data/lib/net/irc/constants.rb +214 -0
- data/lib/net/irc/message/modeparser.rb +85 -0
- data/lib/net/irc/message/serverconfig.rb +30 -0
- data/lib/net/irc/message.rb +109 -0
- data/lib/net/irc/pattern.rb +68 -0
- data/lib/net/irc/server.rb +186 -0
- data/lib/net/irc.rb +77 -0
- data/spec/channel_manager_spec.rb +184 -0
- data/spec/modeparser_spec.rb +165 -0
- data/spec/net-irc_spec.rb +337 -0
- data/spec/spec.opts +1 -0
- metadata +91 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: e4908aab25b550d867b561bb113e159e740dd000
|
4
|
+
data.tar.gz: c29e4d4ed4cf1bcd644a3c47270fb6da528b6a2d
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: f2c08bc4d72354eecefd9c8242d32675061ee8f0d5507466c5ed68ba7304a946e8b6a0b75c4442a33fd98b3aa03565dfe2f4e5f0ca20c3fc1e6850283db44370
|
7
|
+
data.tar.gz: d3c4e3feff8ec09ab13f8bb28fa7b7a08176e5fb04b2087dc8a62a7dfe05fbbf7778ecfd4bd32ad3279f71e35886d8a3b82c0ebd51310a94fe9d9b53549b0988
|
data/AUTHORS.txt
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
Core Authors::
|
2
|
+
cho45
|
3
|
+
drry
|
4
|
+
czaks
|
5
|
+
Satoshi Nakagawa
|
6
|
+
kouhei
|
7
|
+
Tim Carey-Smith
|
8
|
+
Wataru TOYA
|
9
|
+
Yuki Sonoda (Yugui)
|
10
|
+
yappo
|
11
|
+
|
12
|
+
Example Contributors::
|
13
|
+
cho45
|
14
|
+
drry
|
15
|
+
mzp
|
16
|
+
smbd
|
17
|
+
Keiji, Yoshimi
|
18
|
+
HIROSE Masaaki
|
19
|
+
dzfl
|
20
|
+
shunirr
|
21
|
+
Kazuhiro NISHIYAMA
|
22
|
+
Takeru Naito
|
23
|
+
Wataru TOYA
|
24
|
+
Yuki Sonoda (Yugui)
|
25
|
+
czaks
|
26
|
+
yappo
|
27
|
+
Keisuke SATO
|
28
|
+
SHIBATA Hiroshi
|
29
|
+
Sotaro KARASAWA
|
30
|
+
Tim Carey-Smith
|
31
|
+
Tokuhiro Matsuno
|
32
|
+
kusigahama
|
33
|
+
motemen
|
data/ChangeLog
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
2010-01-13 SATOH Hiroh <cho45@lowreal.net>
|
2
|
+
|
3
|
+
* Remove method_missing:
|
4
|
+
Defining method_missing produce big side-effects.
|
5
|
+
(eg. no error on undefined values)
|
6
|
+
|
7
|
+
2009-10-11 SATOH Hiroh <cho45@lowreal.net>
|
8
|
+
|
9
|
+
* [new]
|
10
|
+
Implemented Server#sessions which returns all sessions connected to
|
11
|
+
the server.
|
12
|
+
* Released 0.0.9
|
13
|
+
|
14
|
+
2009-08-08 SATOH Hiroh <cho45@lowreal.net>
|
15
|
+
|
16
|
+
* [bug]:
|
17
|
+
Fixed to work on ruby1.9.1 (now can send iso-2022-jp)
|
18
|
+
* [new]
|
19
|
+
Implemented Message#ctcps returns embedded all ctcp messages (drry).
|
20
|
+
* Released 0.0.8
|
21
|
+
|
22
|
+
2009-02-19 SATOH Hiroh <cho45@lowreal.net>
|
23
|
+
|
24
|
+
* [bug]:
|
25
|
+
Fixed net/irc.rb permission.
|
26
|
+
* Released 0.0.7
|
27
|
+
|
28
|
+
2009-02-01 SATOH Hiroh <cho45@lowreal.net>
|
29
|
+
|
30
|
+
* [bug]:
|
31
|
+
Fixed to work on ruby1.9.1
|
32
|
+
* [release]:
|
33
|
+
Released 0.0.6
|
34
|
+
|
35
|
+
2008-07-06 SATOH Hiroh <cho45@lowreal.net>
|
36
|
+
|
37
|
+
* [interface]:
|
38
|
+
Removed around @channels and separeted to
|
39
|
+
Net::IRC::Client::ChannelManager as just a sample of managing
|
40
|
+
channels.
|
41
|
+
* [release]:
|
42
|
+
Released 0.0.5
|
43
|
+
|
44
|
+
2008-07-06 Satoshi Nakagawa <psychs@limechat.net>
|
45
|
+
|
46
|
+
* [new]:
|
47
|
+
Added a mode parser which can be configured automatically from 005 replies.
|
48
|
+
|
49
|
+
2008-06-28 cho45
|
50
|
+
|
51
|
+
* [interface]:
|
52
|
+
Change mode character to symbol.
|
53
|
+
* [new]:
|
54
|
+
Seperate each class to some files.
|
55
|
+
* [release]:
|
56
|
+
Released 0.0.4
|
57
|
+
|
58
|
+
2008-06-14 cho45
|
59
|
+
|
60
|
+
* [bug]:
|
61
|
+
Fixed examples. (twitter, wassr, lingr gateways)
|
62
|
+
* [release]:
|
63
|
+
Released 0.0.3
|
64
|
+
|
65
|
+
2008-02-06 cho45
|
66
|
+
|
67
|
+
* [release] @5832:
|
68
|
+
Released 0.0.2
|
69
|
+
|
70
|
+
|
71
|
+
2008-02-01 cho45
|
72
|
+
|
73
|
+
* [bug] @5986:
|
74
|
+
Fixed to destroy closed stream.
|
75
|
+
|
76
|
+
2008-01-31 cho45
|
77
|
+
|
78
|
+
* [new] @5939:
|
79
|
+
Added client example.
|
80
|
+
|
81
|
+
* [new] @5929:
|
82
|
+
Updated tests.
|
83
|
+
Made allow lame prefix in RPL_WELCOME (like freenode)
|
84
|
+
|
85
|
+
2008-01-29 cho45
|
86
|
+
|
87
|
+
* [bug] @5846:
|
88
|
+
athack つかわないときの処理がもろに間違ってた。
|
89
|
+
|
90
|
+
* [bug] @5843:
|
91
|
+
Net::IRC::Server の修正に追従できていなかった
|
92
|
+
|
93
|
+
* [release] @5832:
|
94
|
+
Released 0.0.1
|
95
|
+
|
data/README
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
|
2
|
+
= net-irc
|
3
|
+
|
4
|
+
|
5
|
+
== Description
|
6
|
+
|
7
|
+
IRC library. This is mostly conform to RFC 1459 but partly not for convenience.
|
8
|
+
|
9
|
+
|
10
|
+
== Installation
|
11
|
+
|
12
|
+
=== Archive Installation
|
13
|
+
|
14
|
+
rake install
|
15
|
+
|
16
|
+
=== Gem Installation
|
17
|
+
|
18
|
+
gem install net-irc
|
19
|
+
|
20
|
+
|
21
|
+
== Features/Problems
|
22
|
+
|
23
|
+
* IRC client (for bot)
|
24
|
+
* IRC server (for gateway to webservices)
|
25
|
+
|
26
|
+
== Synopsis
|
27
|
+
|
28
|
+
=== Client
|
29
|
+
|
30
|
+
require "net/irc"
|
31
|
+
|
32
|
+
class SimpleClient < Net::IRC::Client
|
33
|
+
def on_privmsg(m)
|
34
|
+
super
|
35
|
+
channel, message = *m
|
36
|
+
if message =~ /Hello/
|
37
|
+
post NOTICE, channel, "Hello!"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
Net::IRC::Client manages channel status and the information is set in @channels.
|
43
|
+
So, be careful to use @channels instance variable and call super surely.
|
44
|
+
|
45
|
+
=== Server
|
46
|
+
|
47
|
+
see example/tig.rb
|
48
|
+
|
49
|
+
|
50
|
+
== IRC Gateways
|
51
|
+
|
52
|
+
There are some gateways connecting to webservices.
|
53
|
+
|
54
|
+
* Twitter
|
55
|
+
* Wassr
|
56
|
+
* Hatena Haiku
|
57
|
+
* Hatena Star
|
58
|
+
|
59
|
+
If you want to run it, type following:
|
60
|
+
|
61
|
+
$ cd `ruby -rubygems -e 'print Gem.searcher.find("net/irc").full_gem_path+"/examples"'`
|
62
|
+
|
63
|
+
Twitter:
|
64
|
+
$ ./tig.rb -f >> /dev/null 2>&1
|
65
|
+
|
66
|
+
Wassr:
|
67
|
+
$ ./wig.rb
|
68
|
+
|
69
|
+
Run as daemon in default. If you want to help:
|
70
|
+
|
71
|
+
$ ./tig.rb --help
|
72
|
+
Usage: tig.rb [opts]
|
73
|
+
|
74
|
+
|
75
|
+
Options:
|
76
|
+
-p, --port [PORT=16668] port number to listen
|
77
|
+
-h, --host [HOST=localhost] host name or IP address to listen
|
78
|
+
-l, --log LOG log file
|
79
|
+
--debug Enable debug mode
|
80
|
+
-f, --foreground run foreground
|
81
|
+
-n [user name or email address]
|
82
|
+
--name
|
83
|
+
|
84
|
+
|
85
|
+
== Copyright
|
86
|
+
|
87
|
+
This library is based on RICE <http://arika.org/ruby/rice> written by akira yamada.
|
88
|
+
|
89
|
+
Author:: cho45 <cho45@lowreal.net>
|
90
|
+
Copyright:: Copyright (c) 2008-2009 cho45
|
91
|
+
License:: Ruby's
|
data/Rakefile
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler/gem_tasks'
|
3
|
+
require 'bundler/setup'
|
4
|
+
Bundler.setup(:development)
|
5
|
+
|
6
|
+
require 'shipit'
|
7
|
+
require 'rake'
|
8
|
+
require 'rake/clean'
|
9
|
+
require 'rake/contrib/sshpublisher'
|
10
|
+
require 'rdoc/task'
|
11
|
+
require 'fileutils'
|
12
|
+
|
13
|
+
require 'rspec/core/rake_task'
|
14
|
+
|
15
|
+
include FileUtils
|
16
|
+
|
17
|
+
$LOAD_PATH.unshift "lib"
|
18
|
+
require "net/irc"
|
19
|
+
|
20
|
+
GEMSPEC = Gem.latest_spec_for('net-irc')
|
21
|
+
|
22
|
+
CLEAN.include ['**/.*.sw?', '*.gem', '.config']
|
23
|
+
|
24
|
+
task :default => [:spec]
|
25
|
+
task :package => [:clean]
|
26
|
+
|
27
|
+
RSpec::Core::RakeTask.new do |t|
|
28
|
+
t.rspec_opts = ['--options', "spec/spec.opts"]
|
29
|
+
end
|
30
|
+
|
31
|
+
task :upload_doc => [:rdoc] do
|
32
|
+
sh %{rsync --update -avptr html/ lowreal@cho45.stfuawsc.com:/virtual/lowreal/public_html/cho45.stfuawsc.com/net-irc}
|
33
|
+
end
|
34
|
+
|
35
|
+
Rake::RDocTask.new do |rdoc|
|
36
|
+
rdoc.rdoc_dir = 'html'
|
37
|
+
rdoc.options += GEMSPEC.rdoc_options
|
38
|
+
rdoc.template = "resh"
|
39
|
+
if ENV['DOC_FILES']
|
40
|
+
rdoc.rdoc_files.include(ENV['DOC_FILES'].split(/,\s*/))
|
41
|
+
else
|
42
|
+
rdoc.rdoc_files.include('README', 'ChangeLog')
|
43
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
44
|
+
rdoc.rdoc_files.include('ext/**/*.c')
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
Rake::ShipitTask.new do |s|
|
49
|
+
s.ChangeVersion "lib/net/irc.rb", "VERSION"
|
50
|
+
s.Commit
|
51
|
+
s.Task :clean, :package, :upload_doc
|
52
|
+
s.Step.new {
|
53
|
+
}.and {
|
54
|
+
system("gem", "push", "pkg/net-irc2-#{GEMSPEC.version}.gem")
|
55
|
+
}
|
56
|
+
s.Tag
|
57
|
+
s.Twitter
|
58
|
+
end
|
59
|
+
|
60
|
+
task 'AUTHORS.txt' do
|
61
|
+
File.open('AUTHORS.txt', 'w') do |f|
|
62
|
+
f.puts "Core Authors::"
|
63
|
+
f.puts `git shortlog -s -n lib`.gsub(/^\s*\d+\s*/, '')
|
64
|
+
f.puts
|
65
|
+
f.puts "Example Contributors::"
|
66
|
+
f.puts `git shortlog -s -n examples`.gsub(/^\s*\d+\s*/, '')
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
data/examples/2ch.rb
ADDED
@@ -0,0 +1,225 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# vim:encoding=UTF-8:
|
3
|
+
$KCODE = "u" if RUBY_VERSION < "1.9" # json use this
|
4
|
+
|
5
|
+
require 'uri'
|
6
|
+
require 'net/http'
|
7
|
+
require 'stringio'
|
8
|
+
require 'zlib'
|
9
|
+
require 'nkf'
|
10
|
+
|
11
|
+
class ThreadData
|
12
|
+
class UnknownThread < StandardError; end
|
13
|
+
|
14
|
+
attr_accessor :uri
|
15
|
+
attr_accessor :last_modified, :size
|
16
|
+
|
17
|
+
Line = Struct.new(:n, :name, :mail, :misc, :body, :opts, :id) do
|
18
|
+
def aa?
|
19
|
+
body = self.body
|
20
|
+
return false if body.count("\n") < 3
|
21
|
+
|
22
|
+
significants = body.scan(/[>\n0-9a-z0-9A-Za-zA-Zぁ-んァ-ン一-龠]/u).size.to_f
|
23
|
+
body_length = body.scan(/./u).size
|
24
|
+
is_aa = 1 - significants / body_length
|
25
|
+
|
26
|
+
is_aa > 0.6
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def initialize(thread_uri)
|
31
|
+
@uri = URI(thread_uri)
|
32
|
+
_, _, _, @board, @num, = *@uri.path.split('/')
|
33
|
+
@dat = []
|
34
|
+
end
|
35
|
+
|
36
|
+
def length
|
37
|
+
@dat.length
|
38
|
+
end
|
39
|
+
|
40
|
+
def subject
|
41
|
+
retrieve(true) if @dat.size.zero?
|
42
|
+
self[1].opts || ""
|
43
|
+
end
|
44
|
+
|
45
|
+
def [](n)
|
46
|
+
l = @dat[n - 1]
|
47
|
+
return nil unless l
|
48
|
+
name, mail, misc, body, opts = * l.split(/<>/)
|
49
|
+
id = misc[/ID:([^\s]+)/, 1]
|
50
|
+
|
51
|
+
body.gsub!(/<br>/, "\n")
|
52
|
+
body.gsub!(/<[^>]+>/, "")
|
53
|
+
body.gsub!(/^\s+|\s+$/, "")
|
54
|
+
body.gsub!(/&(gt|lt|amp|nbsp);/) {|s|
|
55
|
+
{ 'gt' => ">", 'lt' => "<", 'amp' => "&", 'nbsp' => " " }[$1]
|
56
|
+
}
|
57
|
+
|
58
|
+
Line.new(n, name, mail, misc, body, opts, id)
|
59
|
+
end
|
60
|
+
|
61
|
+
def dat
|
62
|
+
@num
|
63
|
+
end
|
64
|
+
|
65
|
+
def retrieve(force=false)
|
66
|
+
@dat = [] if @force
|
67
|
+
|
68
|
+
res = Net::HTTP.start(@uri.host, @uri.port) do |http|
|
69
|
+
req = Net::HTTP::Get.new('/%s/dat/%d.dat' % [@board, @num])
|
70
|
+
req['User-Agent'] = 'Monazilla/1.00 (2ig.rb/0.0e)'
|
71
|
+
req['Accept-Encoding'] = 'gzip' unless @size
|
72
|
+
unless force
|
73
|
+
req['If-Modified-Since'] = @last_modified if @last_modified
|
74
|
+
req['Range'] = "bytes=%d-" % @size if @size
|
75
|
+
end
|
76
|
+
|
77
|
+
http.request(req)
|
78
|
+
end
|
79
|
+
|
80
|
+
ret = nil
|
81
|
+
case res.code.to_i
|
82
|
+
when 200, 206
|
83
|
+
body = res.body
|
84
|
+
if res['Content-Encoding'] == 'gzip'
|
85
|
+
body = StringIO.open(body, 'rb') {|io| Zlib::GzipReader.new(io).read }
|
86
|
+
end
|
87
|
+
|
88
|
+
@last_modified = res['Last-Modified']
|
89
|
+
if res.code == '206'
|
90
|
+
@size += body.size
|
91
|
+
else
|
92
|
+
@size = body.size
|
93
|
+
end
|
94
|
+
|
95
|
+
body = NKF.nkf('-w', body)
|
96
|
+
|
97
|
+
curr = @dat.size + 1
|
98
|
+
@dat.concat(body.split(/\n/))
|
99
|
+
last = @dat.size
|
100
|
+
|
101
|
+
(curr..last).map {|n|
|
102
|
+
self[n]
|
103
|
+
}
|
104
|
+
when 416 # たぶん削除が発生
|
105
|
+
p ['416']
|
106
|
+
retrieve(true)
|
107
|
+
[]
|
108
|
+
when 304 # Not modified
|
109
|
+
[]
|
110
|
+
when 302 # dat 落ち
|
111
|
+
p ['302', res['Location']]
|
112
|
+
raise UnknownThread
|
113
|
+
else
|
114
|
+
p ['Unknown Status:', res.code]
|
115
|
+
[]
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def canonicalize_subject(subject)
|
120
|
+
subject.gsub(/[A-Za-z0-9]/u) {|c|
|
121
|
+
c.unpack("U*").map {|i| i - 65248 }.pack("U*")
|
122
|
+
}
|
123
|
+
end
|
124
|
+
|
125
|
+
def guess_next_thread
|
126
|
+
res = Net::HTTP.start(@uri.host, @uri.port) do |http|
|
127
|
+
req = Net::HTTP::Get.new('/%s/subject.txt' % @board)
|
128
|
+
req['User-Agent'] = 'Monazilla/1.00 (2ig.rb/0.0e)'
|
129
|
+
http.request(req)
|
130
|
+
end
|
131
|
+
|
132
|
+
recent_posted_threads = (900..999).inject({}) {|r,i|
|
133
|
+
line = self[i]
|
134
|
+
line.body.scan(%r|ttp://#{@uri.host}/test/read.cgi/[^/]+/\d+/|).each do |uri|
|
135
|
+
r["h#{uri}"] = i
|
136
|
+
end if line
|
137
|
+
r
|
138
|
+
}
|
139
|
+
|
140
|
+
current_subject = canonicalize_subject(self.subject)
|
141
|
+
current_thread_rev = current_subject.scan(/\d+/).map {|d| d.to_i }
|
142
|
+
current = current_subject.scan(/./u)
|
143
|
+
|
144
|
+
body = NKF.nkf('-w', res.body)
|
145
|
+
threads = body.split(/\n/).map {|l|
|
146
|
+
dat, rest = *l.split(/<>/)
|
147
|
+
dat.sub!(/\.dat$/, "")
|
148
|
+
|
149
|
+
uri = "http://#{@uri.host}/test/read.cgi/#{@board}/#{dat}/"
|
150
|
+
|
151
|
+
subject, n = */(.*?) \((\d+)\)/.match(rest).captures
|
152
|
+
canonical_subject = canonicalize_subject(subject)
|
153
|
+
thread_rev = canonical_subject[/\d+/].to_i
|
154
|
+
|
155
|
+
distance = (dat == self.dat) ? Float::MAX :
|
156
|
+
(subject == self.subject) ? 0 :
|
157
|
+
levenshtein(canonical_subject.scan(/./u), current)
|
158
|
+
continuous_num = current_thread_rev.find {|rev| rev == thread_rev - 1 }
|
159
|
+
appear_recent = recent_posted_threads[uri]
|
160
|
+
|
161
|
+
score = distance
|
162
|
+
score -= 10 if continuous_num
|
163
|
+
score -= 10 if appear_recent
|
164
|
+
score += 10 if dat.to_i < self.dat.to_i
|
165
|
+
{
|
166
|
+
:uri => uri,
|
167
|
+
:dat => dat,
|
168
|
+
:subject => subject,
|
169
|
+
:distance => distance,
|
170
|
+
:continuous_num => continuous_num,
|
171
|
+
:appear_recent => appear_recent,
|
172
|
+
:score => score.to_f
|
173
|
+
}
|
174
|
+
}.sort_by {|o|
|
175
|
+
o[:score]
|
176
|
+
}
|
177
|
+
|
178
|
+
threads
|
179
|
+
end
|
180
|
+
|
181
|
+
def levenshtein(a, b)
|
182
|
+
case
|
183
|
+
when a.empty?
|
184
|
+
b.length
|
185
|
+
when b.empty?
|
186
|
+
a.length
|
187
|
+
when a == b
|
188
|
+
0
|
189
|
+
else
|
190
|
+
d = Array.new(a.length + 1) { |s|
|
191
|
+
Array.new(b.length + 1, 0)
|
192
|
+
}
|
193
|
+
|
194
|
+
(0..a.length).each do |i|
|
195
|
+
d[i][0] = i
|
196
|
+
end
|
197
|
+
|
198
|
+
(0..b.length).each do |j|
|
199
|
+
d[0][j] = j
|
200
|
+
end
|
201
|
+
|
202
|
+
(1..a.length).each do |i|
|
203
|
+
(1..b.length).each do |j|
|
204
|
+
cost = (a[i - 1] == b[j - 1]) ? 0 : 1
|
205
|
+
d[i][j] = [
|
206
|
+
d[i-1][j ] + 1,
|
207
|
+
d[i ][j-1] + 1,
|
208
|
+
d[i-1][j-1] + cost
|
209
|
+
].min
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
d[a.length][b.length]
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
if __FILE__ == $0
|
219
|
+
require 'pp'
|
220
|
+
thread = ThreadData.new(ARGV[0])
|
221
|
+
pp thread.guess_next_thread.reverse
|
222
|
+
|
223
|
+
p thread.subject
|
224
|
+
end
|
225
|
+
|