protonbot 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +13 -0
  3. data/.rubocop.yml +38 -0
  4. data/CODE_OF_CONDUCT.md +74 -0
  5. data/CONFIG.md +45 -0
  6. data/CORE_PLUGIN.md +128 -0
  7. data/Gemfile +7 -0
  8. data/LICENSE.txt +21 -0
  9. data/PLUGIN.md +45 -0
  10. data/README.md +81 -0
  11. data/Rakefile +2 -0
  12. data/bin/console +14 -0
  13. data/bin/setup +8 -0
  14. data/exe/protonbot-gencert +7 -0
  15. data/lib/protonbot/bot.rb +64 -0
  16. data/lib/protonbot/bot_conf.rb +48 -0
  17. data/lib/protonbot/bot_plugins.rb +110 -0
  18. data/lib/protonbot/core_plugin/apis/help.rb +54 -0
  19. data/lib/protonbot/core_plugin/apis/index.rb +2 -0
  20. data/lib/protonbot/core_plugin/apis/perms.rb +118 -0
  21. data/lib/protonbot/core_plugin/codes/index.rb +3 -0
  22. data/lib/protonbot/core_plugin/codes/names.rb +21 -0
  23. data/lib/protonbot/core_plugin/codes/welcome.rb +7 -0
  24. data/lib/protonbot/core_plugin/codes/whois.rb +9 -0
  25. data/lib/protonbot/core_plugin/commands/admin.rb +107 -0
  26. data/lib/protonbot/core_plugin/commands/basic.rb +9 -0
  27. data/lib/protonbot/core_plugin/commands/cross.rb +40 -0
  28. data/lib/protonbot/core_plugin/commands/help.rb +25 -0
  29. data/lib/protonbot/core_plugin/commands/index.rb +6 -0
  30. data/lib/protonbot/core_plugin/commands/joinpart.rb +25 -0
  31. data/lib/protonbot/core_plugin/commands/perms.rb +50 -0
  32. data/lib/protonbot/core_plugin/hooks/ctcp.rb +9 -0
  33. data/lib/protonbot/core_plugin/hooks/index.rb +7 -0
  34. data/lib/protonbot/core_plugin/hooks/joinpart.rb +42 -0
  35. data/lib/protonbot/core_plugin/hooks/kick.rb +17 -0
  36. data/lib/protonbot/core_plugin/hooks/nick.rb +10 -0
  37. data/lib/protonbot/core_plugin/hooks/ping.rb +3 -0
  38. data/lib/protonbot/core_plugin/hooks/privmsg.rb +62 -0
  39. data/lib/protonbot/core_plugin/hooks/raw.rb +32 -0
  40. data/lib/protonbot/core_plugin/plugin.rb +10 -0
  41. data/lib/protonbot/event_lock.rb +25 -0
  42. data/lib/protonbot/hook.rb +22 -0
  43. data/lib/protonbot/log.rb +182 -0
  44. data/lib/protonbot/log_wrapper.rb +52 -0
  45. data/lib/protonbot/numeric.rb +140 -0
  46. data/lib/protonbot/plug.rb +118 -0
  47. data/lib/protonbot/plug_events.rb +51 -0
  48. data/lib/protonbot/plug_io.rb +99 -0
  49. data/lib/protonbot/plug_utils.rb +242 -0
  50. data/lib/protonbot/plugin.rb +101 -0
  51. data/lib/protonbot/version.rb +4 -0
  52. data/lib/protonbot.rb +38 -0
  53. data/protonbot.gemspec +31 -0
  54. 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,3 @@
1
+ hook(type: :ping) do |dat|
2
+ dat[:plug].write_("PONG :#{dat[:server]}")
3
+ 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,10 @@
1
+ ProtonBot::Plugin.new do
2
+ @name = 'Core'
3
+ @version = ProtonBot::VERSION
4
+ @description = 'ProtonBot\'s core plugin'
5
+
6
+ run 'apis/index'
7
+ run 'commands/index'
8
+ run 'hooks/index'
9
+ run 'codes/index'
10
+ 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