protonbot 0.1.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.
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