protonbot 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +13 -0
- data/.rubocop.yml +38 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/CONFIG.md +45 -0
- data/CORE_PLUGIN.md +128 -0
- data/Gemfile +7 -0
- data/LICENSE.txt +21 -0
- data/PLUGIN.md +45 -0
- data/README.md +81 -0
- data/Rakefile +2 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/exe/protonbot-gencert +7 -0
- data/lib/protonbot/bot.rb +64 -0
- data/lib/protonbot/bot_conf.rb +48 -0
- data/lib/protonbot/bot_plugins.rb +110 -0
- data/lib/protonbot/core_plugin/apis/help.rb +54 -0
- data/lib/protonbot/core_plugin/apis/index.rb +2 -0
- data/lib/protonbot/core_plugin/apis/perms.rb +118 -0
- data/lib/protonbot/core_plugin/codes/index.rb +3 -0
- data/lib/protonbot/core_plugin/codes/names.rb +21 -0
- data/lib/protonbot/core_plugin/codes/welcome.rb +7 -0
- data/lib/protonbot/core_plugin/codes/whois.rb +9 -0
- data/lib/protonbot/core_plugin/commands/admin.rb +107 -0
- data/lib/protonbot/core_plugin/commands/basic.rb +9 -0
- data/lib/protonbot/core_plugin/commands/cross.rb +40 -0
- data/lib/protonbot/core_plugin/commands/help.rb +25 -0
- data/lib/protonbot/core_plugin/commands/index.rb +6 -0
- data/lib/protonbot/core_plugin/commands/joinpart.rb +25 -0
- data/lib/protonbot/core_plugin/commands/perms.rb +50 -0
- data/lib/protonbot/core_plugin/hooks/ctcp.rb +9 -0
- data/lib/protonbot/core_plugin/hooks/index.rb +7 -0
- data/lib/protonbot/core_plugin/hooks/joinpart.rb +42 -0
- data/lib/protonbot/core_plugin/hooks/kick.rb +17 -0
- data/lib/protonbot/core_plugin/hooks/nick.rb +10 -0
- data/lib/protonbot/core_plugin/hooks/ping.rb +3 -0
- data/lib/protonbot/core_plugin/hooks/privmsg.rb +62 -0
- data/lib/protonbot/core_plugin/hooks/raw.rb +32 -0
- data/lib/protonbot/core_plugin/plugin.rb +10 -0
- data/lib/protonbot/event_lock.rb +25 -0
- data/lib/protonbot/hook.rb +22 -0
- data/lib/protonbot/log.rb +182 -0
- data/lib/protonbot/log_wrapper.rb +52 -0
- data/lib/protonbot/numeric.rb +140 -0
- data/lib/protonbot/plug.rb +118 -0
- data/lib/protonbot/plug_events.rb +51 -0
- data/lib/protonbot/plug_io.rb +99 -0
- data/lib/protonbot/plug_utils.rb +242 -0
- data/lib/protonbot/plugin.rb +101 -0
- data/lib/protonbot/version.rb +4 -0
- data/lib/protonbot.rb +38 -0
- data/protonbot.gemspec +31 -0
- metadata +181 -0
@@ -0,0 +1,10 @@
|
|
1
|
+
hook(type: :unick) do |dat|
|
2
|
+
u = dat[:plug].users[dat[:nick]].clone
|
3
|
+
dat[:plug].users.delete(dat[:nick])
|
4
|
+
dat[:plug].users[dat[:to]] = u
|
5
|
+
u[:nick] = dat[:to]
|
6
|
+
u[:user] = dat[:user]
|
7
|
+
u[:host] = dat[:host]
|
8
|
+
|
9
|
+
dat[:plug].nick = dat[:to] if dat[:nick] == dat[:plug].nick
|
10
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
fun :privmsg_patch do |h|
|
2
|
+
h.define_singleton_method :reply do |msg|
|
3
|
+
self[:plug].privmsg(self[:reply_to], msg) if self[:nick]
|
4
|
+
end
|
5
|
+
h.define_singleton_method :nreply do |msg|
|
6
|
+
self[:plug].notice(self[:nick], msg) if self[:nick]
|
7
|
+
end
|
8
|
+
h
|
9
|
+
end
|
10
|
+
|
11
|
+
hook(type: :privmsg) do |dat|
|
12
|
+
# User-data collector
|
13
|
+
dat[:plug].users[dat[:nick]] = {} unless dat[:plug].users[dat[:nick]]
|
14
|
+
u = dat[:plug].users[dat[:nick]]
|
15
|
+
u[:nick] = dat[:nick]
|
16
|
+
u[:user] = dat[:user]
|
17
|
+
u[:host] = dat[:host]
|
18
|
+
dat[:perms] = process_perms(getperms!(dat[:plug], dat[:host]))
|
19
|
+
|
20
|
+
# CTCP
|
21
|
+
if dat[:message][0] == "\x01" && dat[:message][-1] == "\x01"
|
22
|
+
dat[:type] = :ctcp
|
23
|
+
dat[:message] = dat[:message][1..(dat[:message].length - 2)]
|
24
|
+
s = dat[:message].split(' ')
|
25
|
+
dat[:cmd] = s[0]
|
26
|
+
(dat[:split] = s[1, s.length - 1]) || []
|
27
|
+
emit(privmsg_patch(dat))
|
28
|
+
end
|
29
|
+
|
30
|
+
# Command-checker
|
31
|
+
cl = dat[:plug].conf['cmdchar'].length
|
32
|
+
if dat[:message][0..(cl - 1)] == dat[:plug].conf['cmdchar']
|
33
|
+
cmd, *split = dat[:message][cl..-1].split(' ')
|
34
|
+
h = dat.merge(type: :command, cmd: cmd, split: split)
|
35
|
+
privmsg_patch(h)
|
36
|
+
begin
|
37
|
+
emit(h)
|
38
|
+
rescue => e
|
39
|
+
h.nreply("Err: #{e.message} | #{e.backtrace[0]}")
|
40
|
+
dat[:plug].log_err(e)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
hook(type: :notice) do |dat|
|
46
|
+
# User-data collector
|
47
|
+
dat[:plug].users[dat[:nick]] = {} unless dat[:plug].users[dat[:nick]]
|
48
|
+
u = dat[:plug].users[dat[:nick]]
|
49
|
+
u[:nick] = dat[:nick]
|
50
|
+
u[:user] = dat[:user]
|
51
|
+
u[:host] = dat[:host]
|
52
|
+
|
53
|
+
# CTCP
|
54
|
+
if dat[:message][0] == "\x01" && dat[:message][-1] == "\x01"
|
55
|
+
dat[:type] = :nctcp
|
56
|
+
dat[:message] = dat[:message][1..(dat[:message].length - 2)]
|
57
|
+
s = dat[:message].split(' ')
|
58
|
+
dat[:cmd] = s[0]
|
59
|
+
(dat[:split] = s[1, s.length - 1]) || []
|
60
|
+
emit(dat)
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
hook(type: :raw) do |dat|
|
2
|
+
if m = /^PING :(.+)/.match(dat[:raw_data])
|
3
|
+
emit(dat.merge(type: :ping, server: m[1]))
|
4
|
+
elsif m = /^:.+ (\d\d\d) .+? (.+)/.match(dat[:raw_data])
|
5
|
+
emit(dat.merge(type: :code, code: m[1], extra: m[2]))
|
6
|
+
elsif m = /^:(.+?)!(.+?)@(.+?) PRIVMSG (.+?) :(.+)/.match(dat[:raw_data])
|
7
|
+
rto =
|
8
|
+
if %w(! # & +).include? m[4][0]
|
9
|
+
m[4]
|
10
|
+
else
|
11
|
+
m[1]
|
12
|
+
end
|
13
|
+
emitt(dat.merge(type: :privmsg, nick: m[1], user: m[2], host: m[3],
|
14
|
+
target: m[4], message: m[5], reply_to: rto))
|
15
|
+
elsif m = /^:(.+?)!(.+?)@(.+?) NOTICE (.+?) :(.+)/.match(dat[:raw_data])
|
16
|
+
emitt(dat.merge(type: :notice, nick: m[1], user: m[2], host: m[3],
|
17
|
+
target: m[4], message: m[5]))
|
18
|
+
elsif m = /^:(.+?)!(.+?)@(.+?) TOPIC (.+?) :(.+)/.match(dat[:raw_data]) # NYI
|
19
|
+
emit(dat.merge(type: :utopic, nick: m[1], user: m[2], host: m[3],
|
20
|
+
channel: m[4], topic: m[5]))
|
21
|
+
elsif m = /^:(.+?)!(.+?)@(.+?) JOIN :(.+)/.match(dat[:raw_data])
|
22
|
+
emit(dat.merge(type: :ujoin, nick: m[1], user: m[2], host: m[3], channel: m[4]))
|
23
|
+
elsif m = /^:(.+?)!(.+?)@(.+?) PART (.+) :(.*)/.match(dat[:raw_data])
|
24
|
+
emit(dat.merge(type: :upart, nick: m[1], user: m[2], host: m[3], channel: m[4], message: m[5]))
|
25
|
+
elsif m = /^:(.+?)!(.+?)@(.+?) QUIT :.*/.match(dat[:raw_data])
|
26
|
+
emit(dat.merge(type: :uquit, nick: m[1], user: m[2], host: m[3]))
|
27
|
+
elsif m = /^:(.+?)!(.+?)@(.+?) NICK :(.+)/.match(dat[:raw_data])
|
28
|
+
emit(dat.merge(type: :unick, nick: m[1], user: m[2], host: m[3], to: m[4]))
|
29
|
+
elsif m = /^:(.+?)!(.+?)@(.+?) KICK (.+?) (.+?) :.*/.match(dat[:raw_data])
|
30
|
+
emit(dat.merge(type: :ukick, nick: m[1], user: m[2], host: m[3], channel: m[4], target: m[5]))
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# Event lock. Locks current thread until matching event is emitted
|
2
|
+
# @!attribute [r] plug
|
3
|
+
# @return [Plug] Plug
|
4
|
+
# @!attribute [r] pattern
|
5
|
+
# @return [Hash<Symbol>] Pattern
|
6
|
+
class ProtonBot::EventLock
|
7
|
+
attr_reader :plug, :pattern
|
8
|
+
|
9
|
+
# @param plug [Plug]
|
10
|
+
# @param pattern [Hash<Symbol>]
|
11
|
+
def initialize(plug, pattern)
|
12
|
+
@plug = plug
|
13
|
+
@plug.event_locks << self
|
14
|
+
@pattern = pattern
|
15
|
+
@unlock = false
|
16
|
+
sleep(0.01) until @unlock
|
17
|
+
end
|
18
|
+
|
19
|
+
# Unlocks current thread
|
20
|
+
# @return [NilClass]
|
21
|
+
def unlock
|
22
|
+
@unlock = true
|
23
|
+
nil
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# Hook. Created by plugins, it's block is called if emitted event matches pattern
|
2
|
+
# @!attribute [r] pattern
|
3
|
+
# @return [Hash<Symbol>] Hook's pattern
|
4
|
+
# @!attribute [r] block
|
5
|
+
# @return [Proc] Block
|
6
|
+
# @!attribute [r] extra
|
7
|
+
# @return [Hash] Extra data. Usually used by hook modifiers
|
8
|
+
# @!attribute [r] chain
|
9
|
+
# @return [Array<Proc>] Proc array. These are executed before calling main block.
|
10
|
+
# If any returns false value, main block is not called
|
11
|
+
class ProtonBot::Hook
|
12
|
+
attr_reader :pattern, :block, :extra, :chain
|
13
|
+
|
14
|
+
# @param pattern [Hash<Symbol>]
|
15
|
+
# @param block [Proc]
|
16
|
+
def initialize(pattern, &block)
|
17
|
+
@pattern = pattern
|
18
|
+
@block = block
|
19
|
+
@extra = {}
|
20
|
+
@chain = []
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,182 @@
|
|
1
|
+
# Log - for colored logging.
|
2
|
+
# @!attribute [rw] levels
|
3
|
+
# @return [Hash<String>] Colorscheme
|
4
|
+
# @!attribute [rw] logging
|
5
|
+
# @return [Bool] If false, does not write messages.
|
6
|
+
class ProtonBot::Log
|
7
|
+
attr_accessor :levels, :logging
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@pastel = Pastel.new
|
11
|
+
|
12
|
+
@queue = Queue.new
|
13
|
+
|
14
|
+
@fmt = '[:date|:time] :name ▷ :lvl ▶ :text'
|
15
|
+
@dfmt = '%d.%m.%Y'
|
16
|
+
@tfmt = '%H:%M:%S'
|
17
|
+
@levels = DEFAULT_SCHEME
|
18
|
+
|
19
|
+
@logging = true
|
20
|
+
|
21
|
+
@stop = false
|
22
|
+
|
23
|
+
lloop
|
24
|
+
end
|
25
|
+
|
26
|
+
# Use it to get rid of appending name on each log-method-call
|
27
|
+
# @param name [String]
|
28
|
+
# @return [LogWrapper] wrapper
|
29
|
+
# @see ProtonBot::LogWrapper
|
30
|
+
def wrap(name)
|
31
|
+
ProtonBot::LogWrapper.new(self, name)
|
32
|
+
end
|
33
|
+
|
34
|
+
# @!group Logging
|
35
|
+
# @param msg [String]
|
36
|
+
# @param nam [String] Name
|
37
|
+
def info(msg, nam = 'log')
|
38
|
+
dat = gsub(msg.to_s, :info, nam)
|
39
|
+
@queue << dat
|
40
|
+
@pastel.strip(dat)
|
41
|
+
end
|
42
|
+
|
43
|
+
# @param msg [String]
|
44
|
+
# @param nam [String] Name
|
45
|
+
def debug(msg, nam = 'log')
|
46
|
+
dat = gsub(msg.to_s, :debug, nam)
|
47
|
+
@queue << dat
|
48
|
+
@pastel.strip(dat)
|
49
|
+
end
|
50
|
+
|
51
|
+
# @param msg [String]
|
52
|
+
# @param nam [String] Name
|
53
|
+
def warn(msg, nam = 'log')
|
54
|
+
dat = gsub(msg.to_s, :warning, nam)
|
55
|
+
@queue << dat
|
56
|
+
@pastel.strip(dat)
|
57
|
+
end
|
58
|
+
|
59
|
+
# @param msg [String]
|
60
|
+
# @param nam [String] Name
|
61
|
+
def error(msg, nam = 'log')
|
62
|
+
dat = gsub(msg.to_s, :error, nam)
|
63
|
+
@queue << dat
|
64
|
+
@pastel.strip(dat)
|
65
|
+
end
|
66
|
+
|
67
|
+
# @param msg [String]
|
68
|
+
# @param code [Integer]
|
69
|
+
# @param nam [String] Name
|
70
|
+
def crash(msg, code = 1, nam = 'log')
|
71
|
+
dat = gsub(msg.to_s, :crash, nam)
|
72
|
+
@queue << dat
|
73
|
+
@pastel.strip(dat)
|
74
|
+
exit code
|
75
|
+
end
|
76
|
+
# @!endgroup
|
77
|
+
|
78
|
+
# @return [String] Output
|
79
|
+
def inspect
|
80
|
+
%(<#ProtonBot::Log:#{object_id.to_s(16)} @logging=#{@logging}>)
|
81
|
+
end
|
82
|
+
|
83
|
+
# Stops log thread
|
84
|
+
def stop
|
85
|
+
@stop = true
|
86
|
+
@thr.join
|
87
|
+
end
|
88
|
+
|
89
|
+
private
|
90
|
+
|
91
|
+
# Formats message
|
92
|
+
# @param msg [String]
|
93
|
+
# @param sname [Symbol] Color-Scheme name
|
94
|
+
# @param nam [String] Log-Name
|
95
|
+
def gsub(msg, sname, nam)
|
96
|
+
dt = DateTime.now
|
97
|
+
|
98
|
+
date = dt.strftime(@dfmt)
|
99
|
+
time = dt.strftime(@tfmt)
|
100
|
+
|
101
|
+
dates = @pastel.decorate(date, *@levels[sname][:fmt][:datetime])
|
102
|
+
times = @pastel.decorate(time, *@levels[sname][:fmt][:datetime])
|
103
|
+
|
104
|
+
name = @pastel.decorate(nam, *@levels[sname][:fmt][:name])
|
105
|
+
|
106
|
+
lvl = @pastel.decorate(*@levels[sname][:chars],
|
107
|
+
*@levels[sname][:fmt][:lvl])
|
108
|
+
|
109
|
+
@fmt
|
110
|
+
.gsub(':date', dates)
|
111
|
+
.gsub(':time', times)
|
112
|
+
.gsub(':name', name)
|
113
|
+
.gsub(':lvl', lvl)
|
114
|
+
.gsub(':text', msg)
|
115
|
+
.gsub('▷', @pastel.decorate('▷', *@levels[sname][:fmt][:arrows]))
|
116
|
+
.gsub('▶', @pastel.decorate('▶', *@levels[sname][:fmt][:arrows]))
|
117
|
+
end
|
118
|
+
|
119
|
+
# Log-loop - reads messages from queue and writes them to STDOUT
|
120
|
+
def lloop
|
121
|
+
@thr = Thread.new do
|
122
|
+
loop do
|
123
|
+
break if @stop
|
124
|
+
d = begin
|
125
|
+
@queue.pop
|
126
|
+
rescue
|
127
|
+
nil
|
128
|
+
end
|
129
|
+
print("#{d}\n") if @logging && d
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
# Default colorcheme
|
135
|
+
DEFAULT_SCHEME = {
|
136
|
+
info: {
|
137
|
+
chars: 'I',
|
138
|
+
fmt: {
|
139
|
+
datetime: [:bright_cyan],
|
140
|
+
name: [:cyan],
|
141
|
+
lvl: [:cyan],
|
142
|
+
arrows: [:bright_cyan]
|
143
|
+
}
|
144
|
+
},
|
145
|
+
debug: {
|
146
|
+
chars: 'D',
|
147
|
+
fmt: {
|
148
|
+
datetime: [:bright_green],
|
149
|
+
name: [:green],
|
150
|
+
lvl: [:green],
|
151
|
+
arrows: [:bright_green]
|
152
|
+
}
|
153
|
+
},
|
154
|
+
warning: {
|
155
|
+
chars: 'W',
|
156
|
+
fmt: {
|
157
|
+
datetime: [:bright_yellow],
|
158
|
+
name: [:yellow],
|
159
|
+
lvl: [:yellow],
|
160
|
+
arrows: [:bright_yellow]
|
161
|
+
}
|
162
|
+
},
|
163
|
+
error: {
|
164
|
+
chars: 'E',
|
165
|
+
fmt: {
|
166
|
+
datetime: [:bright_red],
|
167
|
+
name: [:red],
|
168
|
+
lvl: [:red],
|
169
|
+
arrows: [:bright_red]
|
170
|
+
}
|
171
|
+
},
|
172
|
+
crash: {
|
173
|
+
chars: 'C',
|
174
|
+
fmt: {
|
175
|
+
datetime: [:bright_magenta],
|
176
|
+
name: [:magenta],
|
177
|
+
lvl: [:magenta],
|
178
|
+
arrows: [:bright_magenta]
|
179
|
+
}
|
180
|
+
}
|
181
|
+
}.freeze
|
182
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# Log wrapper
|
2
|
+
# @!attribute [r] log
|
3
|
+
# @return [Log] Log
|
4
|
+
# @!attribute [r] name
|
5
|
+
# @return [String] name
|
6
|
+
class ProtonBot::LogWrapper
|
7
|
+
attr_reader :log, :name
|
8
|
+
|
9
|
+
# @param log [Log]
|
10
|
+
# @param name [String]
|
11
|
+
def initialize(log, name)
|
12
|
+
@log = log
|
13
|
+
@name = name
|
14
|
+
end
|
15
|
+
|
16
|
+
# @param msg [String]
|
17
|
+
# @return [LogWrapper] self
|
18
|
+
def info(msg)
|
19
|
+
@log.info(msg, @name)
|
20
|
+
end
|
21
|
+
|
22
|
+
# @param msg [String]
|
23
|
+
# @return [LogWrapper] self
|
24
|
+
def debug(msg)
|
25
|
+
@log.debug(msg, @name)
|
26
|
+
end
|
27
|
+
|
28
|
+
# @param msg [String]
|
29
|
+
# @return [LogWrapper] self
|
30
|
+
def warn(msg)
|
31
|
+
@log.warn(msg, @name)
|
32
|
+
end
|
33
|
+
|
34
|
+
# @param msg [String]
|
35
|
+
# @return [LogWrapper] self
|
36
|
+
def error(msg)
|
37
|
+
@log.error(msg, @name)
|
38
|
+
end
|
39
|
+
|
40
|
+
# @param msg [String]
|
41
|
+
# @return [LogWrapper] self
|
42
|
+
# @return [Integer] code
|
43
|
+
def crash(msg, code)
|
44
|
+
@log.crash(msg, code, @name)
|
45
|
+
end
|
46
|
+
|
47
|
+
# @return [String] Output
|
48
|
+
def inspect
|
49
|
+
%(<#ProtonBot::LogWrapper:#{object_id.to_s(16)} @name='#{@name}') +
|
50
|
+
%( @log=#{@log}>)
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,140 @@
|
|
1
|
+
# Here you can find constants for almost all numerics defined in RFC (see link below).
|
2
|
+
# You can use `@numeric` instead of `ProtonBot::Numeric` while developing plugins.
|
3
|
+
# @see https://tools.ietf.org/html/rfc2812#section-5
|
4
|
+
module ProtonBot::Numeric
|
5
|
+
WELCOME = '001'.freeze
|
6
|
+
YOURHOST = '002'.freeze
|
7
|
+
CREATED = '003'.freeze
|
8
|
+
MYINFO = '004'.freeze
|
9
|
+
BOUNCE = '005'.freeze
|
10
|
+
ISON = '303'.freeze
|
11
|
+
AWAY = '301'.freeze
|
12
|
+
UNAWAY = '305'.freeze
|
13
|
+
NOWAWAY = '306'.freeze
|
14
|
+
WHOISUSER = '311'.freeze
|
15
|
+
WHOISSERVER = '312'.freeze
|
16
|
+
WHOISOPERATOR = '313'.freeze
|
17
|
+
WHOISIDLE = '317'.freeze
|
18
|
+
WHOISCHANNELS = '319'.freeze
|
19
|
+
ENDOFWHOIS = '318'.freeze
|
20
|
+
WHOWASUSER = '314'.freeze
|
21
|
+
ENDOFWHOWAS = '369'.freeze
|
22
|
+
LISTSTART = '321'.freeze
|
23
|
+
LIST = '322'.freeze
|
24
|
+
LISTEND = '323'.freeze
|
25
|
+
USERHOST = '302'.freeze
|
26
|
+
UNIQOPIS = '325'.freeze
|
27
|
+
CHANNELMODEIS = '324'.freeze
|
28
|
+
NOTOPIC = '331'.freeze
|
29
|
+
TOPIC = '332'.freeze
|
30
|
+
INVITING = '341'.freeze
|
31
|
+
SUMMONING = '342'.freeze
|
32
|
+
INVITELIST = '346'.freeze
|
33
|
+
ENDOFINVITELIST = '347'.freeze
|
34
|
+
EXCEPTLIST = '348'.freeze
|
35
|
+
ENDOFEXCEPTLIST = '349'.freeze
|
36
|
+
VERSION = '351'.freeze
|
37
|
+
WHOREPLY = '352'.freeze
|
38
|
+
ENDOFWHO = '315'.freeze
|
39
|
+
NAMREPLY = '353'.freeze
|
40
|
+
ENDOFNAMES = '366'.freeze
|
41
|
+
LINKS = '364'.freeze
|
42
|
+
ENDOFLINKS = '365'.freeze
|
43
|
+
BANLIST = '367'.freeze
|
44
|
+
ENDOFBANLIST = '368'.freeze
|
45
|
+
INFO = '371'.freeze
|
46
|
+
ENDOFINFO = '374'.freeze
|
47
|
+
MOTDSTART = '375'.freeze
|
48
|
+
MOTD = '372'.freeze
|
49
|
+
ENDOFMOTD = '376'.freeze
|
50
|
+
YOUREOPER = '381'.freeze
|
51
|
+
REHASHING = '382'.freeze
|
52
|
+
YOURSERVICE = '383'.freeze
|
53
|
+
TIME = '391'.freeze
|
54
|
+
USERSTART = '392'.freeze
|
55
|
+
USERS = '393'.freeze
|
56
|
+
ENDOFUSERS = '394'.freeze
|
57
|
+
TRACELINK = '200'.freeze
|
58
|
+
TRACECONNECTING = '201'.freeze
|
59
|
+
TRACEHANDSHAKE = '202'.freeze
|
60
|
+
TRACEUNKNOWN = '203'.freeze
|
61
|
+
TRACEOPERATOR = '204'.freeze
|
62
|
+
TRACEUSER = '205'.freeze
|
63
|
+
TRACESERVER = '206'.freeze
|
64
|
+
TRACESERVICE = '207'.freeze
|
65
|
+
TRACENEWTYPE = '208'.freeze
|
66
|
+
TRACECLASS = '209'.freeze
|
67
|
+
TRACERECONNECT = '210'.freeze
|
68
|
+
TRACELOG = '261'.freeze
|
69
|
+
TRACEEND = '262'.freeze
|
70
|
+
STATSLINKINFO = '211'.freeze
|
71
|
+
STATSCOMMANDS = '212'.freeze
|
72
|
+
ENDOFSTATS = '219'.freeze
|
73
|
+
STATSUPTIME = '242'.freeze
|
74
|
+
STATSOLINE = '243'.freeze
|
75
|
+
UMDODEIS = '221'.freeze
|
76
|
+
SERVLIST = '234'.freeze
|
77
|
+
SERVLISTEND = '235'.freeze
|
78
|
+
LUSERCLIENT = '251'.freeze
|
79
|
+
LUSEROP = '252'.freeze
|
80
|
+
LUSERUNKNOWN = '253'.freeze
|
81
|
+
LUSERCHANNELS = '254'.freeze
|
82
|
+
LUSERME = '255'.freeze
|
83
|
+
ADMINME = '256'.freeze
|
84
|
+
ADMINLOC1 = '257'.freeze
|
85
|
+
ADMINLOC2 = '258'.freeze
|
86
|
+
ADMINEMAIL = '259'.freeze
|
87
|
+
TRYAGAIN = '263'.freeze
|
88
|
+
NOSUCHSERVER = '402'.freeze
|
89
|
+
NOSUCHCHANNEL = '403'.freeze
|
90
|
+
CANNOTSENDTOCHAN = '404'.freeze
|
91
|
+
TOOMANYCHANNELS = '405'.freeze
|
92
|
+
WASNOSUCHNICK = '406'.freeze
|
93
|
+
TOOMANYTARGETS = '407'.freeze
|
94
|
+
NOSUCHSERVICE = '408'.freeze
|
95
|
+
NOORIGIN = '409'.freeze
|
96
|
+
NORECIPIENT = '411'.freeze
|
97
|
+
NOTEXTTOSEND = '412'.freeze
|
98
|
+
NOTOPLEVEL = '413'.freeze
|
99
|
+
WILDTOPLEVEL = '414'.freeze
|
100
|
+
BADMASK = '415'.freeze
|
101
|
+
UNKNOWNCOMMAND = '421'.freeze
|
102
|
+
NOMOTD = '422'.freeze
|
103
|
+
NOADMININFO = '423'.freeze
|
104
|
+
FILEERROR = '424'.freeze
|
105
|
+
NONICKNAMEGIVEN = '431'.freeze
|
106
|
+
ERRONEUSNICKNAME = '432'.freeze
|
107
|
+
NICKNAMEINUSE = '433'.freeze
|
108
|
+
NICKCOLLISION = '436'.freeze
|
109
|
+
UNAVAILRESOURCE = '437'.freeze
|
110
|
+
USERNOTINCHANNEL = '441'.freeze
|
111
|
+
NOTONCHANNEL = '442'.freeze
|
112
|
+
USERONCHANNEL = '443'.freeze
|
113
|
+
NOLOGIN = '444'.freeze
|
114
|
+
SUMMONDISABLED = '445'.freeze
|
115
|
+
USERSDISABLED = '446'.freeze
|
116
|
+
NOTREGISTERED = '451'.freeze
|
117
|
+
NEEDMOREPARAMS = '461'.freeze
|
118
|
+
ALREADYREGISTRED = '462'.freeze
|
119
|
+
NOPERMFORHOST = '463'.freeze
|
120
|
+
PASSWDMISMATCH = '464'.freeze
|
121
|
+
YOUREBANNEDCREEP = '465'.freeze
|
122
|
+
YOUWILLBEBANNED = '466'.freeze
|
123
|
+
KEYSET = '467'.freeze
|
124
|
+
CHANNELISFULL = '471'.freeze
|
125
|
+
UNKNOWNMODE = '472'.freeze
|
126
|
+
INVITEONLYCHAN = '473'.freeze
|
127
|
+
BANNEDFROMCHAN = '474'.freeze
|
128
|
+
BADCHANNELKEY = '475'.freeze
|
129
|
+
BADCHANMASK = '476'.freeze
|
130
|
+
NOCHANMODES = '477'.freeze
|
131
|
+
BANLISTFULL = '478'.freeze
|
132
|
+
NOPRIVILEGES = '481'.freeze
|
133
|
+
CHANOPRIVSNEEDED = '482'.freeze
|
134
|
+
CANTKILLSERVER = '483'.freeze
|
135
|
+
RESTRICTED = '484'.freeze
|
136
|
+
UNIQOPPRIVSNEEDED = '485'.freeze
|
137
|
+
NOOPERHOST = '491'.freeze
|
138
|
+
UMODEUNKNOWNFLAG = '501'.freeze
|
139
|
+
USERSDONTMATCH = '502'.freeze
|
140
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
# Plug. This class includes socket, writer and reader threads, user&channel-data,
|
2
|
+
# API for interacting with server etc.
|
3
|
+
# @!attribute [r] bot
|
4
|
+
# @return [ProtonBot::Bot] Plug's bot.
|
5
|
+
# @!attribute [r] db
|
6
|
+
# @return [Heliodor::DB] Plug's Heliodor-powered database.
|
7
|
+
# @see http://www.rubydoc.info/gems/heliodor
|
8
|
+
# @!attribute [r] sock
|
9
|
+
# @return [TCPSocket,SSLSocket] Plug's socket. May be SSL-socket.
|
10
|
+
# @!attribute [r] rsock
|
11
|
+
# @return [TCPSocket] Always non-SSL socket.
|
12
|
+
# @!attribute [r] is_ssl
|
13
|
+
# @return [Boolean] True if connection is SSL-powered
|
14
|
+
# @!attribute [r] name
|
15
|
+
# @return [String] Plug's name.
|
16
|
+
# @!attribute [r] conf
|
17
|
+
# @return [Hash<String>] Config.
|
18
|
+
# @!attribute [r] rloop
|
19
|
+
# @return [Thread] Reader-loop-thread.
|
20
|
+
# @!attribute [r] wloop
|
21
|
+
# @return [Thread] Writer-loop-thread.
|
22
|
+
# @!attribute [r] log
|
23
|
+
# @return [ProtonBot::LogWrapper] Log wrapper.
|
24
|
+
# @!attribute [r] queue
|
25
|
+
# @return [Queue] Queue.
|
26
|
+
# @!attribute [r] chans
|
27
|
+
# @return [Hash<String>] Channel data.
|
28
|
+
# @!attribute [r] users
|
29
|
+
# @return [Hash<String>] User data (by nick).
|
30
|
+
# @!attribute [r] event_locks
|
31
|
+
# @return [Array<EventLock>] Event locks.
|
32
|
+
# @!attribute [rw] running
|
33
|
+
# @return [Boolean] Returns `true` if bot still runs
|
34
|
+
# and processes messages from server.
|
35
|
+
# @!attribute [r] user
|
36
|
+
# @return [String] Plug's username.
|
37
|
+
# @!attribute [r] nick
|
38
|
+
# @return [String] Plug's nickname.
|
39
|
+
# @!attribute [r] rnam
|
40
|
+
# @return [String] Plug's realname.
|
41
|
+
class ProtonBot::Plug
|
42
|
+
attr_reader :bot, :db, :sock, :rsock, :name, :conf, :rloop, :wloop, :log, :queue, :chans, :users,
|
43
|
+
:event_locks, :user, :nick, :rnam, :is_ssl
|
44
|
+
attr_accessor :running
|
45
|
+
|
46
|
+
# @param bot [Bot]
|
47
|
+
# @param name [String]
|
48
|
+
# @param conf [Hash<String>]
|
49
|
+
def initialize(bot, name, conf)
|
50
|
+
@bot = bot
|
51
|
+
@name = name
|
52
|
+
@db = @bot.dbs[@name]
|
53
|
+
@log = @bot._log.wrap("!#{@name}")
|
54
|
+
@conf = conf
|
55
|
+
@nick = conf['nick']
|
56
|
+
@user = conf['user']
|
57
|
+
@rnam = conf['rnam']
|
58
|
+
@sock = nil
|
59
|
+
@running = false
|
60
|
+
@queue = Queue.new
|
61
|
+
@chans = {}
|
62
|
+
@users = {}
|
63
|
+
@event_locks = []
|
64
|
+
end
|
65
|
+
|
66
|
+
# Connects to server, introduces and starts reader and writer threads.
|
67
|
+
# @return [Plug] self
|
68
|
+
def connect!
|
69
|
+
if @conf['ssl'] == nil or @conf['ssl'] == false
|
70
|
+
@sock = @rsock = TCPSocket.new(@conf['host'], @conf['port'])
|
71
|
+
@is_ssl = false
|
72
|
+
else
|
73
|
+
@rsock = TCPSocket.new(@conf['host'], @conf['port'])
|
74
|
+
@ssl_ctx = OpenSSL::SSL::SSLContext.new
|
75
|
+
@ssl_ctx.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
76
|
+
@ssl_ctx.ssl_version = :SSLv23
|
77
|
+
@ssl_ctx.cert = OpenSSL::X509::Certificate.new(File.read(File.expand_path(@conf['ssl_crt']))) if
|
78
|
+
@conf['ssl_crt']
|
79
|
+
@ssl_ctx.key = OpenSSL::PKey::RSA.new(File.read(File.expand_path(@conf['ssl_key']))) if
|
80
|
+
@conf['ssl_key']
|
81
|
+
@sock = OpenSSL::SSL::SSLSocket.new(@rsock, @ssl_ctx)
|
82
|
+
@sock.sync = true
|
83
|
+
@sock.connect
|
84
|
+
@is_ssl = true
|
85
|
+
end
|
86
|
+
|
87
|
+
@running = true
|
88
|
+
|
89
|
+
@rloop = Thread.new { readloop }
|
90
|
+
@wloop = Thread.new { writeloop }
|
91
|
+
|
92
|
+
log.info("Started plug `#{name}` successfully!")
|
93
|
+
|
94
|
+
introduce
|
95
|
+
|
96
|
+
@rloop.join
|
97
|
+
@wloop.join
|
98
|
+
self
|
99
|
+
end
|
100
|
+
|
101
|
+
# Logs given error object to cosole
|
102
|
+
# @param e [Exception]
|
103
|
+
# @return [Plug] self
|
104
|
+
def log_err(e)
|
105
|
+
@log.error('Error!')
|
106
|
+
@log.error("> Message: #{e.message}")
|
107
|
+
@log.error('> Backtrace:')
|
108
|
+
e.backtrace.each do |i|
|
109
|
+
@log.error(">> #{i}")
|
110
|
+
end
|
111
|
+
self
|
112
|
+
end
|
113
|
+
|
114
|
+
# @return [String] out
|
115
|
+
def inspect
|
116
|
+
%(<#ProtonBot::Plug:#{object_id.to_s(16)} @name=#{name} @bot=#{bot}>)
|
117
|
+
end
|
118
|
+
end
|