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 +4 -4
- data/.travis.yml +5 -13
- data/lib/notify_socket.rb +82 -0
- data/nadoka.rb +13 -1
- data/nadokarc +5 -5
- data/ndk/logger.rb +1 -1
- data/ndk/server.rb +2 -0
- data/ndk/version.rb +1 -1
- data/plugins/googlebot.nb +7 -1
- data/plugins/tenkibot.nb +129 -65
- data/plugins/titlebot.nb +49 -33
- metadata +7 -7
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8794eb0d0b61ad6529c1f96b4b55fadd8eec443f0a36e8b284e6eaf21b9ad3c8
|
|
4
|
+
data.tar.gz: 43242ba299e9acc927cdce8a1049d8ca5160cb1b8ff4d7ddc478e8cdc0c3152a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 643b75b33ebb71275c0c1c71a437c3dee2f2b1a313bdc0c7164f6db66371197206cb3f12cef9a2d034fa0a86d7c48158ee53e7c268994bbb3aa2b29d3bd010c8
|
|
7
|
+
data.tar.gz: 9ea081ef58fdfdc708d0cf92d37db5fc725a0d95028fa391339dc9408648489b8dbcbd2a693c6acc06b09d39978d55428e479805f4122353c56aa4492a8f5cd9
|
data/.travis.yml
CHANGED
|
@@ -1,26 +1,18 @@
|
|
|
1
1
|
language: ruby
|
|
2
2
|
rvm:
|
|
3
|
-
- 2.
|
|
4
|
-
- 2.
|
|
5
|
-
- 2.
|
|
6
|
-
-
|
|
7
|
-
-
|
|
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
|
-
|
|
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',
|
data/ndk/logger.rb
CHANGED
data/ndk/server.rb
CHANGED
|
@@ -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
|
data/ndk/version.rb
CHANGED
data/plugins/googlebot.nb
CHANGED
|
@@ -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 (
|
|
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
|
data/plugins/tenkibot.nb
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
# -*-
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# -*-ruby-*-
|
|
2
3
|
#
|
|
3
|
-
# Copyright (c)
|
|
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 "
|
|
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> [
|
|
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
|
-
|
|
32
|
-
|
|
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 '
|
|
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
|
-
|
|
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
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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
|
-
|
|
68
|
-
|
|
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
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
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
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
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
|
|
156
|
+
puts "#$0 area"
|
|
97
157
|
else
|
|
98
158
|
init_tenki
|
|
99
|
-
ARGV.each do |
|
|
100
|
-
puts tenki(
|
|
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
|
|
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
|
-
|
|
180
|
+
area = $'.strip.toutf8
|
|
121
181
|
begin
|
|
122
|
-
|
|
182
|
+
results = tenki(area)
|
|
123
183
|
rescue => e
|
|
124
|
-
|
|
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
|
data/plugins/titlebot.nb
CHANGED
|
@@ -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(?:
|
|
79
|
-
|
|
80
|
-
|
|
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
|
-
|
|
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] =
|
|
211
|
+
info[:uri] = shorten_url(canonical_uri.text)
|
|
203
212
|
end
|
|
204
213
|
elsif /<link rel="canonical" href="(.+?)"/i =~ body
|
|
205
|
-
info[:uri] =
|
|
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!\
|
|
254
|
-
"
|
|
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
|
-
$!.
|
|
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,
|
|
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
|
|
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
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
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.
|
|
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:
|
|
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
|
-
|
|
100
|
-
|
|
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: []
|