nadoka 0.9.2 → 0.10.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 499bbabcf8dd4f2b5c8850550daee031996821c3106f63795972f843b22fc45b
4
- data.tar.gz: 1b151a20ec41485b4a0e368d62746591be4710c82b089e82314b9a969f499d3a
3
+ metadata.gz: 8794eb0d0b61ad6529c1f96b4b55fadd8eec443f0a36e8b284e6eaf21b9ad3c8
4
+ data.tar.gz: 43242ba299e9acc927cdce8a1049d8ca5160cb1b8ff4d7ddc478e8cdc0c3152a
5
5
  SHA512:
6
- metadata.gz: 2cc2ed86d0496fdc7bbad02524df8c50efb784e559d15b3590c3dbdb8f1125f74738a5ccb574e682efd211e47b979a111e1dea3b7a341b58e8bef6715de99ed5
7
- data.tar.gz: 71b721ff820b48fbdc39601e578cdbc795088fa6df223027a4c6e801d1eb8089de6dbb3024d3c6c2f467c5922afe0d796485b8fb50b1eb3eba58cf75ddd1763b
6
+ metadata.gz: 643b75b33ebb71275c0c1c71a437c3dee2f2b1a313bdc0c7164f6db66371197206cb3f12cef9a2d034fa0a86d7c48158ee53e7c268994bbb3aa2b29d3bd010c8
7
+ data.tar.gz: 9ea081ef58fdfdc708d0cf92d37db5fc725a0d95028fa391339dc9408648489b8dbcbd2a693c6acc06b09d39978d55428e479805f4122353c56aa4492a8f5cd9
@@ -1,26 +1,18 @@
1
1
  language: ruby
2
2
  rvm:
3
- - 2.2
4
- - 2.1
5
- - 2.0.0
6
- - 1.9.3
7
- - jruby-18mode # JRuby in 1.8 mode
8
- - jruby-19mode # JRuby in 1.9 mode
9
- - rbx-18mode
10
- - rbx-19mode
3
+ - 2.7
4
+ - 2.6
5
+ - 2.5
6
+ - 2.4
7
+ - 2.3
11
8
  - ruby-head
12
9
  - jruby-head
13
- - 1.8.7
14
- - ree
15
10
  matrix:
16
11
  allow_failures:
17
12
  - rvm: jruby-18mode
18
13
  - rvm: jruby-19mode
19
- - rvm: rbx-18mode
20
- - rvm: rbx-19mode
21
14
  - rvm: ruby-head
22
15
  - rvm: jruby-head
23
- - rvm: ree
24
16
  before_install:
25
17
  - gem update bundler
26
18
  script: ruby check-syntax.rb
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ # The MIT License (MIT)
4
+ #
5
+ # Copyright (c) 2019 Kazuhiro NISHIYAMA
6
+ #
7
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ # of this software and associated documentation files (the "Software"), to deal
9
+ # in the Software without restriction, including without limitation the rights
10
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ # copies of the Software, and to permit persons to whom the Software is
12
+ # furnished to do so, subject to the following conditions:
13
+ #
14
+ # The above copyright notice and this permission notice shall be included in all
15
+ # copies or substantial portions of the Software.
16
+ #
17
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23
+ # SOFTWARE.
24
+
25
+ # see https://www.freedesktop.org/software/systemd/man/sd_notify.html
26
+ class NotifySocket
27
+ def initialize(path=ENV['NOTIFY_SOCKET'])
28
+ @notify_socket = nil
29
+ return unless path
30
+ @notify_socket = Addrinfo.unix(path, :DGRAM).connect
31
+ end
32
+
33
+ def []=(key, value)
34
+ return unless @notify_socket
35
+ @notify_socket.puts "#{key}=#{value}"
36
+ end
37
+
38
+ def ready!
39
+ self['READY'] = 1
40
+ end
41
+
42
+ def reloading!
43
+ self['RELOADING'] = 1
44
+ end
45
+
46
+ def stopping!
47
+ self['STOPPING'] = 1
48
+ end
49
+
50
+ # notify_socket.status = 'Completed 66% of file system check…'
51
+ def status=(state)
52
+ self['STATUS'] = state
53
+ end
54
+
55
+ # notify_socket.errno = 2
56
+ def errno=(error_code)
57
+ self['ERRNO'] = error_code
58
+ end
59
+
60
+ # notify_socket.buserror = 'org.freedesktop.DBus.Error.TimedOut'
61
+ def buserror=(error_code)
62
+ self['BUSERROR'] = error_code
63
+ end
64
+
65
+ # notify_socket.mainpid = 4711
66
+ def mainpid=(pid)
67
+ self['MAINPID'] = pid
68
+ end
69
+
70
+ def watchdog!
71
+ self['WATCHDOG'] = 1
72
+ end
73
+
74
+ # notify_socket.watchdog_usec = 20_000_000
75
+ def watchdog_usec=(usec)
76
+ self['WATCHDOG_USEC'] = usec
77
+ end
78
+
79
+ def extend_timeout_usec=(usec)
80
+ self['EXTEND_TIMEOUT_USEC'] = usec
81
+ end
82
+ end
data/nadoka.rb CHANGED
@@ -29,6 +29,16 @@ require 'ndk/bot'
29
29
  $stdout.sync=true
30
30
  $NDK_Debug = false
31
31
 
32
+ if ENV.key?('NOTIFY_SOCKET')
33
+ require 'lib/notify_socket'
34
+ $NDK_NOTIFY_SOCKET = NotifySocket.new
35
+ else
36
+ $NDK_NOTIFY_SOCKET = Object.new
37
+ def $NDK_NOTIFY_SOCKET.method_missing(*)
38
+ # ignore
39
+ end
40
+ end
41
+
32
42
  unless defined? Process.daemon
33
43
  def Process.daemon(nochdir = nil, noclose = nil)
34
44
  exit!(0) if fork
@@ -107,12 +117,14 @@ begin
107
117
  GC.start
108
118
  Nadoka::NDK_Server.new(rcfile).start
109
119
  rescue Nadoka::NDK_QuitProgram
110
- #
120
+ $NDK_NOTIFY_SOCKET.stopping!
111
121
  rescue Nadoka::NDK_RestartProgram
122
+ $NDK_NOTIFY_SOCKET.reloading!
112
123
  GC.start
113
124
  ObjectSpace.each_object(Socket) {|sock| sock.close}
114
125
  retry
115
126
  rescue Exception => e
127
+ $NDK_NOTIFY_SOCKET.stopping!
116
128
  open('nadoka_fatal_error', 'w'){|f|
117
129
  f.puts e
118
130
  f.puts e.backtrace.join("\n")
data/nadokarc CHANGED
@@ -68,12 +68,12 @@ class NADOKA_Config < Nadoka::NDK_ConfigBase
68
68
  :port => 6667, # default: 6667
69
69
  :pass => nil, # default: nil
70
70
  },
71
- { :host => 'irc.media.kyoto-u.ac.jp',
72
- :port => (6660 .. 6669),
73
- },
74
- { :host => 'irc.huie.hokudai.ac.jp',
71
+ # { :host => 'irc.media.kyoto-u.ac.jp',
72
+ # :port => (6660 .. 6669),
73
+ # },
74
+ # { :host => 'irc.huie.hokudai.ac.jp',
75
75
  # without :port, use 6667 as default port
76
- },
76
+ # },
77
77
  # IPv6 Sample
78
78
  # {
79
79
  # :host => 'irc6.ircnet.ne.jp',
@@ -214,7 +214,7 @@ module Nadoka
214
214
  :nostamp => nostamp,
215
215
  :ch => ch,
216
216
  }
217
-
217
+ $NDK_NOTIFY_SOCKET.status = msgobj.inspect.tr("\n", ' ')
218
218
  msgobj
219
219
  end
220
220
 
@@ -219,6 +219,7 @@ module Nadoka
219
219
  end
220
220
 
221
221
  invoke_event :invoke_bot, :server_connected
222
+ $NDK_NOTIFY_SOCKET.ready!
222
223
 
223
224
  # loop
224
225
  while q = recv_from_server
@@ -413,6 +414,7 @@ module Nadoka
413
414
  if @connected
414
415
  if @pong_recieved
415
416
  @pong_fail_count = 0
417
+ $NDK_NOTIFY_SOCKET.watchdog!
416
418
  else
417
419
  # fail
418
420
  @pong_fail_count += 1
@@ -11,7 +11,7 @@
11
11
  #
12
12
 
13
13
  module Nadoka
14
- VERSION = '0.9.2'
14
+ VERSION = '0.10.0'
15
15
  NDK_Version = VERSION.dup
16
16
  NDK_Created = Time.now
17
17
 
@@ -78,7 +78,7 @@ if __FILE__ == $0
78
78
  def initialize
79
79
  @bot_config = Hash.new
80
80
  @bot_config[:headers] = {
81
- "User-Agent" => "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:27.0) Gecko/20100101 Firefox/27.0",
81
+ "User-Agent" => "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) snap Chromium/80.0.3987.132 Chrome/80.0.3987.132 Safari/537.36",
82
82
  }
83
83
  bot_initialize
84
84
  end
@@ -256,7 +256,13 @@ class GoogleBot < Nadoka::NDK_Bot
256
256
  result = $1
257
257
  result.sub!(/<a.*/u, '')
258
258
  result.gsub!(/<.+?>/u, '')
259
+ return result
260
+ elsif /<div class="RJn8N xXEKkb ellip[^"]*">= ([^<>]+)</ =~ html
261
+ result = $1
262
+ #@logger.slog("google_calc>#{result.dump}")
263
+ return result
259
264
  else
265
+ #IO.write('g.html', html) if STDOUT.tty?
260
266
  "response error"
261
267
  end
262
268
  rescue Exception
@@ -1,6 +1,7 @@
1
- # -*-ruby; coding: utf-8 -*- vim:set ft=ruby:
1
+ # -*- coding: utf-8 -*-
2
+ # -*-ruby-*-
2
3
  #
3
- # Copyright (c) 2004-2006 SASADA Koichi <ko1 at atdot.net>
4
+ # Copyright (c) 2020 Kazuhiro NISHIYAMA
4
5
  #
5
6
  # This program is free software with ABSOLUTELY NO WARRANTY.
6
7
  # You can re-distribute and/or modify this program under
@@ -11,93 +12,152 @@
11
12
 
12
13
  == Abstract
13
14
 
14
- Answer weather information using "Livedoor Weather Web Service / LWWS".
15
-
16
- LWWS: http://weather.livedoor.com/weather_hacks/webservice
17
-
15
+ Answer weather information using "https://www.jma.go.jp/"
18
16
 
19
17
  == Usage
20
18
 
21
- tenki> [CITY]
22
-
23
- [CITY] should be city name in Kanji listed on following table.
24
- http://weather.livedoor.com/forecast/rss/primary_area.xml
19
+ tenki> [AREA]
25
20
 
21
+ [AREA] should be an area name in Kanji listed on following table.
22
+ https://www.jma.go.jp/jp/yoho/
26
23
 
27
24
  == Configuration
28
25
 
29
- BotConfig = [
30
- {
31
- :name => :TenkiBot,
32
- :ch => /nadoka/, # default: //
33
- }
34
- ]
26
+ BotConfig << {
27
+ :name => :TenkiBot,
28
+ :ch => //,
29
+ :timeout => 10,
30
+ }
35
31
 
36
32
 
37
33
  =end
38
34
 
35
+ require 'nokogiri'
39
36
  require 'open-uri'
40
- require 'pp'
41
- require 'kconv'
42
- require 'date'
43
- begin
44
- require 'json'
45
- rescue LoadError
46
- require 'rubygems'
47
- require 'json'
48
- end
37
+ require 'tmpdir'
49
38
 
50
39
  module Tenki
51
- CityIDs = {}
40
+ AREA = {}
41
+
42
+ TMPDIR = Dir.mktmpdir('tenkibot')
43
+ EN_CACHE_HTML = File.join(TMPDIR, 'en.html~')
44
+ EN_URI = URI('https://www.jma.go.jp/en/yoho/')
45
+ JP_CACHE_HTML = File.join(TMPDIR, 'jp.html~')
46
+ JP_URI = URI('https://www.jma.go.jp/jp/yoho/')
47
+
48
+ def get(uri, cache=nil)
49
+ raise Errno::ENOENT unless cache
50
+ File.read(cache)
51
+ rescue Errno::ENOENT
52
+ html = uri.read
53
+ File.write(cache, html) if cache
54
+ html
55
+ end
52
56
 
53
57
  def init_tenki
54
- open('http://weather.livedoor.com/forecast/rss/primary_area.xml') do |f|
55
- f.each_line do |line|
56
- if /city title="(.+?)" id="(\d+)"/ =~ line
57
- CityIDs[$1.toutf8] = $2
58
- end
59
- end
58
+ jp = get(JP_URI, JP_CACHE_HTML)
59
+ doc = Nokogiri::HTML(jp)
60
+ doc.xpath('//noscript//a').each do |e|
61
+ AREA[e.text] = JP_URI + e[:href]
60
62
  end
61
- end
62
63
 
63
- def tenki(city)
64
- unless city_id = CityIDs[city]
65
- return "Unknown city. Check city title on http://weather.livedoor.com/forecast/rss/primary_area.xml"
64
+ en = get(EN_URI, EN_CACHE_HTML)
65
+ doc = Nokogiri::HTML(en)
66
+ doc.xpath('//select[@name="elfukenlist"]/option').each do |e|
67
+ AREA[e.text] = JP_URI + "/jp/yoho/#{e[:value]}.html" if e[:value]
66
68
  end
67
- json = open("http://weather.livedoor.com/forecast/webservice/json/v1?city=#{city_id}") do |f|
68
- JSON.parse f.read
69
+ end
70
+
71
+ def tenki(area)
72
+ begin
73
+ uri = AREA.fetch(area)
74
+ rescue KeyError => e
75
+ if e.respond_to?(:corrections) && !e.corrections.empty?
76
+ raise "もしかして:#{e.corrections.join(' or ')}"
77
+ else
78
+ raise "例: #{AREA.keys.sample(10).join(', ')}"
79
+ end
69
80
  end
70
81
 
71
- tenki = "#{json['title']}: "
72
- tenki << json['forecasts'].map do |forecast|
73
- max = forecast['temperature']['max']
74
- min = forecast['temperature']['min']
75
- celsius = []
76
- celsius << "min:#{min['celsius']}" if min
77
- celsius << "max:#{max['celsius']}" if max
78
- unless celsius.empty?
79
- temperature = "(#{celsius.join(',')})"
82
+ html = get(uri)
83
+ doc = Nokogiri::HTML(html)
84
+
85
+ forecast = doc.css('table.forecast')
86
+ tenki = {}
87
+ area = nil
88
+ forecast.xpath('./tr').each do |forecast_tr|
89
+ th_area = forecast_tr.css('.th-area')
90
+ unless th_area.empty?
91
+ # 見出し行
92
+ area = th_area.text
93
+ tenki[area] = []
94
+ next
80
95
  end
81
- "#{forecast['dateLabel']}:#{forecast['telop']}#{temperature}"
82
- end.join(', ')
83
- desc = json['description']
84
- text, = desc['text'].split(/\n\n/, 2)
85
- text.gsub!(/\n/, '')
86
- tenki << " - #{text}(#{desc['publicTime']})"
87
- tenki << " - #{json['link']}"
88
-
89
- tenki
96
+ weather = {}
97
+ th_weather = forecast_tr.css('th.weather')
98
+ weather[:when] = th_weather.text.strip
99
+ weather[:title] = th_weather.xpath('img/@title').to_s.strip
100
+ forecast_tr.css('table.rain').each do |rain_table|
101
+ weather[:rain] = []
102
+ rain_table.css('tr').map do |tr|
103
+ time_range = tr.css('td[align=left]').text.strip
104
+ percent = tr.css('td[align=right]').text.strip
105
+ if /\d/ =~ percent
106
+ weather[:rain].push [time_range, percent]
107
+ end
108
+ end
109
+ end
110
+ forecast_tr.css('table.temp').each do |temp_table|
111
+ weather[:temp] = []
112
+ temp_table.css('tr').map do |tr|
113
+ city = tr.css('td.city').text.strip
114
+ min = tr.css('td.min').text.strip
115
+ max = tr.css('td.max').text.strip
116
+ if /./ =~ city
117
+ weather[:temp].push(city: city, min: min, max: max)
118
+ end
119
+ end
120
+ end
121
+ tenki[area].push weather
122
+ end
123
+
124
+ textframe = doc.css('pre.textframe')
125
+ tenki_time = textframe.children[0]&.text&.lines&.[](1)&.strip
126
+ tenki_text = textframe.children[2]&.text&.strip&.[](/.+/)&.gsub(/\s+/, ' ')
127
+
128
+ if $DEBUG
129
+ p tenki, tenki_time, tenki_text
130
+ end
131
+
132
+ texts = []
133
+ tenki.each do |area, weathers|
134
+ text = "#{area}: "
135
+ text += weathers.map do |weather|
136
+ if weather[:rain]&.any?
137
+ rain_text = weather[:rain]&.map{|time_range,percent|"#{time_range}:#{percent}"}.join(';')
138
+ rain_text = "(#{rain_text})"
139
+ end
140
+ [
141
+ "#{weather[:when]}:#{weather[:title]}",
142
+ weather[:temp]&.map{|temp|"(#{temp[:city]}:#{temp[:min]}-#{temp[:max]})"}&.join(''),
143
+ rain_text,
144
+ ].compact.join('')
145
+ end.join(', ')
146
+ texts << text
147
+ end
148
+ texts << "#{tenki_text} (#{tenki_time})"
149
+ return texts
90
150
  end
91
151
  end
92
152
 
93
153
  if __FILE__ == $0
94
154
  include Tenki
95
155
  if ARGV.empty?
96
- puts "#$0 city"
156
+ puts "#$0 area"
97
157
  else
98
158
  init_tenki
99
- ARGV.each do |city|
100
- puts tenki(city)
159
+ ARGV.each do |area|
160
+ puts tenki(area)
101
161
  end
102
162
  end
103
163
  exit
@@ -109,21 +169,25 @@ class TenkiBot < Nadoka::NDK_Bot
109
169
  def bot_initialize
110
170
  bot_init_utils
111
171
  init_tenki
112
- @nkf = @bot_config[:nkf] || "-Wj"
172
+ @nkf = @bot_config.fetch(:nkf, "-Wj")
113
173
  end
114
174
 
115
175
  def on_privmsg prefix, ch, msg
116
176
  return unless @available_channel === ch
117
177
  return if same_bot?(ch)
118
- msg = NKF.nkf('-w', msg)
178
+ msg = NKF.nkf('-w', msg) if @nkf
119
179
  if /\Atenki>/ =~ msg
120
- city = $'.strip.toutf8
180
+ area = $'.strip.toutf8
121
181
  begin
122
- result = tenki(city)
182
+ results = tenki(area)
123
183
  rescue => e
124
- result = "#{e}"
184
+ results = ["#{e}"]
185
+ end
186
+ results.each do |result|
187
+ msg = "tenki bot: #{result}".gsub(/\s+/, ' ')
188
+ msg = NKF.nkf(@nkf, msg) if @nkf
189
+ send_notice ch, msg
125
190
  end
126
- send_notice ch, NKF.nkf(@nkf, "tenki bot: #{result}".gsub(/\s+/, ' '))
127
191
  end
128
192
  end
129
193
  end
@@ -67,6 +67,12 @@ module URL2Title
67
67
  /\A169\.254\./,
68
68
  ]
69
69
 
70
+ if defined?(::Nadoka::VERSION)
71
+ DEFAULT_USER_AGENT = "Ruby/#{RUBY_VERSION} NadokaTitleBot/#{Nadoka::VERSION}"
72
+ else
73
+ DEFAULT_USER_AGENT = "Ruby/#{RUBY_VERSION}"
74
+ end
75
+
70
76
  def get_title(url, headers)
71
77
  uri = URI(url)
72
78
  info = { :uri => uri }
@@ -75,13 +81,11 @@ module URL2Title
75
81
  when *BLACKLIST_HOST
76
82
  info[:title] = "(ignored)"
77
83
  return info
78
- when /\A(?:www\.nicovideo\.jp)\z/
79
- if /\A\/watch\/(sm\d+)\z/ =~ uri.path
80
- uri = URI("http://d.hatena.ne.jp/video/niconico/#{$1}")
81
- end
82
- when /\A(?:mobile\.twitter\.com)\z/
83
- uri = URI(url.sub(/mobile\.twitter\.com/, 'twitter.com'))
84
+ when /\A(?:twitter\.com)\z/
85
+ # Twitter response depends on User-Agent
86
+ headers = headers.merge!('User-Agent' => DEFAULT_USER_AGENT)
84
87
  end
88
+ headers['User-Agent'] ||= DEFAULT_USER_AGENT
85
89
  headers = {
86
90
  :content_length_proc => proc{|x| raise Errno::EFBIG if x && x > 1048576},
87
91
  }.merge!(headers)
@@ -155,7 +159,12 @@ module URL2Title
155
159
  when /\A(?:twitter\.com)\z/
156
160
  if defined?(::Nokogiri)
157
161
  doc = Nokogiri::HTML(body, uri.to_s, 'utf-8')
158
- tweet, = doc.css('.permalink-tweet > .tweet-text')
162
+ title = doc.xpath("//meta[@property='og:description'][1]/@content").text
163
+ end
164
+ when /\A(?:mobile\.twitter\.com)\z/
165
+ if defined?(::Nokogiri)
166
+ doc = Nokogiri::HTML(body, uri.to_s, 'utf-8')
167
+ tweet, = doc.css('.main-tweet .tweet-text')
159
168
  if tweet
160
169
  tweet = tweet.inner_html
161
170
  tweet.gsub!(/<.*?>/, '')
@@ -199,10 +208,10 @@ module URL2Title
199
208
  doc ||= Nokogiri::HTML(body, uri.to_s, 'utf-8')
200
209
  canonical_uri = doc.xpath("//link[@rel='canonical'][1]/@href")
201
210
  unless canonical_uri.empty?
202
- info[:uri] = URI(shorten_url(canonical_uri.text))
211
+ info[:uri] = shorten_url(canonical_uri.text)
203
212
  end
204
213
  elsif /<link rel="canonical" href="(.+?)"/i =~ body
205
- info[:uri] = URI(shorten_url(CGI.unescapeHTML($1)))
214
+ info[:uri] = shorten_url(CGI.unescapeHTML($1))
206
215
  end
207
216
  if title
208
217
  title = CGI.unescapeHTML(title)
@@ -250,12 +259,14 @@ module URL2Title
250
259
 
251
260
  def shorten_url(url)
252
261
  case url
253
- when %r!\Ahttp://www\.amazon\.co\.jp/.*(/dp/.+)\z!
254
- "http://amazon.jp#{$1}"
262
+ when %r!\Ahttps?://www\.amazon\.co\.jp/.*(/dp/[^/]+)!
263
+ URI("https://amazon.jp#{$1}")
255
264
  else
256
265
  # default: do nothing
257
- url
266
+ URI(url)
258
267
  end
268
+ rescue URI::InvalidURIError
269
+ url
259
270
  end
260
271
 
261
272
  def cleanup(title)
@@ -280,7 +291,10 @@ if __FILE__ == $0
280
291
  }
281
292
  URL2Title.url2title(url, headers)
282
293
  rescue
283
- $!.inspect
294
+ puts $!.backtrace
295
+ info = { :uri => url }
296
+ info[:title] = $!.inspect
297
+ info
284
298
  end
285
299
  URL2Title::BLACKLIST_HOST << /\.test\z/
286
300
  if ARGV.empty?
@@ -311,7 +325,7 @@ class TitleBot < Nadoka::NDK_Bot
311
325
 
312
326
  @same_bot = @bot_config.fetch(:same_bot, /(?!)/)
313
327
  @nkf_options = @bot_config.fetch(:nkf, "--oc=CP50221 --ic=UTF-8 --fb-xml")
314
- @timeout = @bot_config.fetch(:timeout, 10)
328
+ @timeout = @bot_config.fetch(:timeout, 60)
315
329
  @skip_nkf_msg = @bot_config.fetch(:skip_nkf_msg, false)
316
330
  @fragment_size_range = @bot_config.fetch(:fragment_size_range, 5..100)
317
331
  @headers = @bot_config.fetch(:headers, {})
@@ -330,30 +344,32 @@ class TitleBot < Nadoka::NDK_Bot
330
344
 
331
345
  msg = NKF.nkf('-w', msg) unless @skip_nkf_msg
332
346
  case msg
333
- when /^\S+>/
347
+ when /^[^<>]+>/
334
348
  # ignore messages to other bots
335
349
  when /https?:/
336
350
  return if @state.channel_users(ccn(ch)).find{|x| @same_bot =~ x }
337
351
 
338
- url, = URI.extract(msg, ["http", "https"])
339
- info = Timeout.timeout(@timeout) do
340
- url2title(url, @headers)
341
- end
342
- return unless info[:title]
343
- if url != info[:uri].to_s
344
- send_notice(ch, "title bot: #{info[:title]} - #{info[:uri]}")
345
- else
346
- send_notice(ch, "title bot: #{info[:title]}")
347
- end
348
- if info[:fragment_text]
349
- # ignore when fragment_text is too long or too short
350
- if @fragment_size_range === info[:fragment_text].size
351
- send_notice(ch, "title bot:: #{info[:fragment_text]}")
352
+ msg.scan(%r<(?<l>\()?(?<url>https?://(?(<l>)(?<ascii_without_paren>[!-~&&[^()]]+|\(\g<ascii_without_paren>\))+|[!-~]+))>) do
353
+ url = $~[:url]
354
+ info = Timeout.timeout(@timeout) do
355
+ url2title(url, @headers)
356
+ end
357
+ return unless info[:title]
358
+ if url != info[:uri].to_s
359
+ send_notice(ch, "title bot: #{info[:title]} - #{info[:uri]}")
360
+ else
361
+ send_notice(ch, "title bot: #{info[:title]}")
362
+ end
363
+ if info[:fragment_text]
364
+ # ignore when fragment_text is too long or too short
365
+ if @fragment_size_range === info[:fragment_text].size
366
+ send_notice(ch, "title bot:: #{info[:fragment_text]}")
367
+ end
368
+ end
369
+ info[:errors].each do |e|
370
+ @manager.ndk_error e
371
+ send_notice(ch, "title bot error: #{e}")
352
372
  end
353
- end
354
- info[:errors].each do |e|
355
- @manager.ndk_error e
356
- send_notice(ch, "title bot error: #{e}")
357
373
  end
358
374
  end
359
375
  rescue Exception => e
metadata CHANGED
@@ -1,15 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nadoka
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.2
4
+ version: 0.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kazuhiro NISHIYAMA
8
8
  - SASADA Koichi
9
- autorequire:
9
+ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2018-09-29 00:00:00.000000000 Z
12
+ date: 2020-07-29 00:00:00.000000000 Z
13
13
  dependencies: []
14
14
  description: Nadoka is a tool for monitoring and logging IRC conversations and responding
15
15
  to specially formatted requests. You define and customize these responses in Ruby.
@@ -29,6 +29,7 @@ files:
29
29
  - bin/nadoka
30
30
  - check-syntax.rb
31
31
  - doc/ChangeLog.old
32
+ - lib/notify_socket.rb
32
33
  - lib/rss_check.rb
33
34
  - lib/tagparts.rb
34
35
  - nadoka.gemspec
@@ -81,7 +82,7 @@ files:
81
82
  homepage: https://github.com/nadoka/nadoka
82
83
  licenses: []
83
84
  metadata: {}
84
- post_install_message:
85
+ post_install_message:
85
86
  rdoc_options: []
86
87
  require_paths:
87
88
  - lib
@@ -96,9 +97,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
96
97
  - !ruby/object:Gem::Version
97
98
  version: '0'
98
99
  requirements: []
99
- rubyforge_project:
100
- rubygems_version: 2.7.6
101
- signing_key:
100
+ rubygems_version: 3.0.3
101
+ signing_key:
102
102
  specification_version: 4
103
103
  summary: IRC logger, monitor and proxy program ("bot")
104
104
  test_files: []