butler 1.8.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 (164) hide show
  1. data/CHANGELOG +4 -0
  2. data/GPL.txt +340 -0
  3. data/LICENSE.txt +52 -0
  4. data/README +37 -0
  5. data/Rakefile +334 -0
  6. data/bin/botcontrol +230 -0
  7. data/data/butler/config_template.yaml +4 -0
  8. data/data/butler/dialogs/backup.rb +19 -0
  9. data/data/butler/dialogs/botcontrol.rb +4 -0
  10. data/data/butler/dialogs/config.rb +1 -0
  11. data/data/butler/dialogs/create.rb +53 -0
  12. data/data/butler/dialogs/delete.rb +3 -0
  13. data/data/butler/dialogs/en/backup.yaml +6 -0
  14. data/data/butler/dialogs/en/botcontrol.yaml +5 -0
  15. data/data/butler/dialogs/en/create.yaml +11 -0
  16. data/data/butler/dialogs/en/delete.yaml +2 -0
  17. data/data/butler/dialogs/en/help.yaml +17 -0
  18. data/data/butler/dialogs/en/info.yaml +13 -0
  19. data/data/butler/dialogs/en/list.yaml +4 -0
  20. data/data/butler/dialogs/en/notyetimplemented.yaml +2 -0
  21. data/data/butler/dialogs/en/rename.yaml +3 -0
  22. data/data/butler/dialogs/en/start.yaml +3 -0
  23. data/data/butler/dialogs/en/sync_plugins.yaml +3 -0
  24. data/data/butler/dialogs/en/uninstall.yaml +5 -0
  25. data/data/butler/dialogs/en/unknown_command.yaml +2 -0
  26. data/data/butler/dialogs/help.rb +11 -0
  27. data/data/butler/dialogs/info.rb +27 -0
  28. data/data/butler/dialogs/interactive.rb +1 -0
  29. data/data/butler/dialogs/list.rb +10 -0
  30. data/data/butler/dialogs/notyetimplemented.rb +1 -0
  31. data/data/butler/dialogs/rename.rb +4 -0
  32. data/data/butler/dialogs/selectbot.rb +2 -0
  33. data/data/butler/dialogs/start.rb +5 -0
  34. data/data/butler/dialogs/sync_plugins.rb +30 -0
  35. data/data/butler/dialogs/uninstall.rb +17 -0
  36. data/data/butler/dialogs/unknown_command.rb +1 -0
  37. data/data/butler/plugins/core/logout.rb +41 -0
  38. data/data/butler/plugins/core/plugins.rb +134 -0
  39. data/data/butler/plugins/core/privilege.rb +103 -0
  40. data/data/butler/plugins/core/user.rb +166 -0
  41. data/data/butler/plugins/dev/eval.rb +64 -0
  42. data/data/butler/plugins/dev/nometa.rb +14 -0
  43. data/data/butler/plugins/dev/onhandlers.rb +93 -0
  44. data/data/butler/plugins/dev/raw.rb +36 -0
  45. data/data/butler/plugins/dev/rawlog.rb +77 -0
  46. data/data/butler/plugins/games/eightball.rb +54 -0
  47. data/data/butler/plugins/games/mastermind.rb +174 -0
  48. data/data/butler/plugins/irc/action.rb +36 -0
  49. data/data/butler/plugins/irc/join.rb +38 -0
  50. data/data/butler/plugins/irc/notice.rb +36 -0
  51. data/data/butler/plugins/irc/part.rb +38 -0
  52. data/data/butler/plugins/irc/privmsg.rb +36 -0
  53. data/data/butler/plugins/irc/quit.rb +36 -0
  54. data/data/butler/plugins/operator/deop.rb +41 -0
  55. data/data/butler/plugins/operator/devoice.rb +41 -0
  56. data/data/butler/plugins/operator/limit.rb +47 -0
  57. data/data/butler/plugins/operator/op.rb +41 -0
  58. data/data/butler/plugins/operator/voice.rb +41 -0
  59. data/data/butler/plugins/public/help.rb +69 -0
  60. data/data/butler/plugins/public/login.rb +72 -0
  61. data/data/butler/plugins/public/usage.rb +49 -0
  62. data/data/butler/plugins/service/clones.rb +56 -0
  63. data/data/butler/plugins/service/define.rb +47 -0
  64. data/data/butler/plugins/service/log.rb +183 -0
  65. data/data/butler/plugins/service/svn.rb +91 -0
  66. data/data/butler/plugins/util/cycle.rb +98 -0
  67. data/data/butler/plugins/util/load.rb +41 -0
  68. data/data/butler/plugins/util/pong.rb +29 -0
  69. data/data/butler/strings/random/acknowledge.en.yaml +5 -0
  70. data/data/butler/strings/random/gratitude.en.yaml +3 -0
  71. data/data/butler/strings/random/hello.en.yaml +4 -0
  72. data/data/butler/strings/random/ignorance.en.yaml +7 -0
  73. data/data/butler/strings/random/ignorance_about.en.yaml +3 -0
  74. data/data/butler/strings/random/insult.en.yaml +3 -0
  75. data/data/butler/strings/random/rejection.en.yaml +12 -0
  76. data/data/man/botcontrol.1 +17 -0
  77. data/lib/access.rb +187 -0
  78. data/lib/access/admin.rb +16 -0
  79. data/lib/access/privilege.rb +122 -0
  80. data/lib/access/role.rb +102 -0
  81. data/lib/access/savable.rb +18 -0
  82. data/lib/access/user.rb +180 -0
  83. data/lib/access/yamlbase.rb +126 -0
  84. data/lib/butler.rb +188 -0
  85. data/lib/butler/bot.rb +247 -0
  86. data/lib/butler/control.rb +93 -0
  87. data/lib/butler/dialog.rb +64 -0
  88. data/lib/butler/initialvalues.rb +40 -0
  89. data/lib/butler/irc/channel.rb +135 -0
  90. data/lib/butler/irc/channels.rb +96 -0
  91. data/lib/butler/irc/client.rb +351 -0
  92. data/lib/butler/irc/hostmask.rb +53 -0
  93. data/lib/butler/irc/message.rb +184 -0
  94. data/lib/butler/irc/parser.rb +125 -0
  95. data/lib/butler/irc/parser/commands.rb +83 -0
  96. data/lib/butler/irc/parser/generic.rb +343 -0
  97. data/lib/butler/irc/socket.rb +378 -0
  98. data/lib/butler/irc/string.rb +186 -0
  99. data/lib/butler/irc/topic.rb +15 -0
  100. data/lib/butler/irc/user.rb +265 -0
  101. data/lib/butler/irc/users.rb +112 -0
  102. data/lib/butler/plugin.rb +249 -0
  103. data/lib/butler/plugin/configproxy.rb +35 -0
  104. data/lib/butler/plugin/mapper.rb +85 -0
  105. data/lib/butler/plugin/matcher.rb +55 -0
  106. data/lib/butler/plugin/onhandlers.rb +70 -0
  107. data/lib/butler/plugin/trigger.rb +58 -0
  108. data/lib/butler/plugins.rb +147 -0
  109. data/lib/butler/version.rb +17 -0
  110. data/lib/cloptions.rb +217 -0
  111. data/lib/cloptions/adapters.rb +24 -0
  112. data/lib/cloptions/switch.rb +132 -0
  113. data/lib/configuration.rb +223 -0
  114. data/lib/dialogline.rb +296 -0
  115. data/lib/dialogline/localizations.rb +24 -0
  116. data/lib/durations.rb +57 -0
  117. data/lib/event.rb +295 -0
  118. data/lib/event/at.rb +64 -0
  119. data/lib/event/every.rb +56 -0
  120. data/lib/event/timed.rb +112 -0
  121. data/lib/installer.rb +75 -0
  122. data/lib/iterator.rb +34 -0
  123. data/lib/log.rb +68 -0
  124. data/lib/log/comfort.rb +85 -0
  125. data/lib/log/converter.rb +23 -0
  126. data/lib/log/entry.rb +152 -0
  127. data/lib/log/fakeio.rb +55 -0
  128. data/lib/log/file.rb +54 -0
  129. data/lib/log/filereader.rb +81 -0
  130. data/lib/log/forward.rb +49 -0
  131. data/lib/log/methods.rb +39 -0
  132. data/lib/log/nolog.rb +18 -0
  133. data/lib/log/splitter.rb +26 -0
  134. data/lib/ostructfixed.rb +26 -0
  135. data/lib/ruby/array/columnize.rb +38 -0
  136. data/lib/ruby/dir/mktree.rb +28 -0
  137. data/lib/ruby/enumerable/join.rb +13 -0
  138. data/lib/ruby/exception/detailed.rb +24 -0
  139. data/lib/ruby/file/append.rb +11 -0
  140. data/lib/ruby/file/write.rb +11 -0
  141. data/lib/ruby/hash/zip.rb +15 -0
  142. data/lib/ruby/kernel/bench.rb +15 -0
  143. data/lib/ruby/kernel/daemonize.rb +42 -0
  144. data/lib/ruby/kernel/non_verbose.rb +17 -0
  145. data/lib/ruby/kernel/safe_fork.rb +18 -0
  146. data/lib/ruby/range/stepped.rb +11 -0
  147. data/lib/ruby/string/arguments.rb +72 -0
  148. data/lib/ruby/string/chunks.rb +15 -0
  149. data/lib/ruby/string/post_arguments.rb +44 -0
  150. data/lib/ruby/string/unescaped.rb +17 -0
  151. data/lib/scheduler.rb +164 -0
  152. data/lib/scriptfile.rb +101 -0
  153. data/lib/templater.rb +86 -0
  154. data/test/cloptions.rb +134 -0
  155. data/test/cv.rb +28 -0
  156. data/test/irc/client.rb +85 -0
  157. data/test/irc/client_login.txt +53 -0
  158. data/test/irc/client_subscribe.txt +8 -0
  159. data/test/irc/message.rb +30 -0
  160. data/test/irc/messages.txt +64 -0
  161. data/test/irc/parser.rb +13 -0
  162. data/test/irc/profile_parser.rb +12 -0
  163. data/test/irc/users.rb +28 -0
  164. metadata +256 -0
@@ -0,0 +1,184 @@
1
+ #--
2
+ # Copyright 2007 by Stefan Rusterholz.
3
+ # All rights reserved.
4
+ # See LICENSE.txt for permissions.
5
+ #++
6
+
7
+
8
+
9
+ #require 'ruby/string' # we need the transcode! method
10
+
11
+ class Butler
12
+ module IRC
13
+
14
+ # Butler::IRC::Message represents messages received by the server.
15
+ # It provides convenience methods that allow to access information about
16
+ # those messages easier, e.g. who (as Butler::IRC::User object) sent the
17
+ # message in which channel (IRC::Channel object) with what text.
18
+ # Raw message and raw parsed data are still available though.
19
+ #
20
+ # =FIXME
21
+ class Message
22
+ # the command-symbol, see COMMANDS (e.g. :PRIVMSG, :JOIN, ...)
23
+ attr_reader :symbol
24
+
25
+ # a string containing the raw message as received
26
+ attr_reader :raw
27
+
28
+ # the prefix, normally either the sending user or your irc-server
29
+ attr_reader :prefix
30
+ # the raw command (e.g. "352", "notice", ...)
31
+ attr_reader :command
32
+ # the parameter part
33
+ attr_reader :params
34
+
35
+ def initialize(client, symbol, raw, prefix, command, params)
36
+ @client = client
37
+
38
+ #raw message
39
+ @raw = raw
40
+
41
+ #parsed data
42
+ @prefix = prefix
43
+ @command = command
44
+ @params = params
45
+ @symbol = symbol
46
+
47
+ #specific data
48
+ @fields = Hash.new { |h,k| raise IndexError, "No member '#{k}' available." }.merge({
49
+ :raw => @raw,
50
+ :prefix => @prefix,
51
+ :command => @command,
52
+ :params => @params,
53
+ :symbol => @symbol,
54
+ :from => nil,
55
+ :for => nil,
56
+ :channel => nil,
57
+ :text => nil,
58
+ })
59
+ end
60
+
61
+ def initialize_copy(original) #:nodoc:
62
+ super
63
+ @client = original.instance_variable_get(:@client)
64
+ @fields = original.to_hash
65
+ @raw = @fields[:raw]
66
+ @prefix = @fields[:prefix]
67
+ @command = @fields[:command]
68
+ @params = @fields[:params]
69
+ @symbol = @fields[:symbol]
70
+ end
71
+
72
+ def answer(text)
73
+ reply = channel || from
74
+ case @symbol
75
+ when :PRIVMSG: @client.irc.privmsg(text, reply)
76
+ when :NOTICE: @client.irc.notice(text, reply)
77
+ end
78
+ end
79
+
80
+ def from
81
+ @fields[:from]
82
+ end
83
+
84
+ def for
85
+ @fields[:for]
86
+ end
87
+
88
+ def channel
89
+ @fields[:channel]
90
+ end
91
+
92
+ def text
93
+ @fields[:text]
94
+ end
95
+
96
+ # access to raw parsed data
97
+ def [](index)
98
+ @fields[index.to_sym]
99
+ end
100
+
101
+ def []=(index, value)
102
+ raise NoMethodError, "Can't write to #{index}" unless respond_to?("#{index}=")
103
+ index = index.to_sym
104
+ @fields[index] = value
105
+ end
106
+
107
+ def has_key?(key)
108
+ @fields.has_key?(key.to_sym)
109
+ end
110
+
111
+ def create_method(name, &code)
112
+ singleton = class << self; self; end
113
+ singleton.send(:define_method, name, &code)
114
+ name
115
+ end
116
+
117
+ # create a member with it's value, if writable is true, it creates a member= method too
118
+ def create_member(index, value=nil, writable=false)
119
+ index = index.to_sym
120
+ raise IndexError, "Member '#{index}' exists already" if @fields.has_key?(index)
121
+ singleton = class << self; self; end
122
+ singleton.send(:define_method, index) { @fields[index] }
123
+ singleton.send(:define_method, "#{index}=") { |val| @fields[index] = val } if writable
124
+ @fields[index] = value
125
+ end
126
+
127
+ # alter a member value with it's value
128
+ # should not be used other than by Butler::IRC::Parser
129
+ def alter_member(index, value=nil, create=false)
130
+ index = index.to_sym
131
+ if @fields.has_key?(index)
132
+ @fields[index] = value
133
+ elsif create then
134
+ create_member(index, value)
135
+ else
136
+ raise IndexError, "No member '#{index}' available."
137
+ end
138
+ end
139
+
140
+ # delete a field
141
+ def delete_member(index)
142
+ index = index.to_sym
143
+ raise IndexError, "No member '#{index}' available." unless @fields.has_key?(index)
144
+ remove_method(index)
145
+ remove_method("#{index}=") if respond_to?("#{index}=")
146
+ @fields.delete(index)
147
+ end
148
+
149
+ # change charset encoding
150
+ def transcode!(from, to)
151
+ end
152
+
153
+ def ===(other)
154
+ case other
155
+ when Symbol: @symbol == other
156
+ when Regexp: @text && @text =~ other
157
+ end
158
+ end
159
+
160
+ # return a hash of the fields
161
+ def to_hash
162
+ @fields.dup
163
+ end
164
+
165
+ def hash #:nodoc:
166
+ @raw.hash
167
+ end
168
+
169
+ def eql?(other) #:nodoc:
170
+ @raw == other.raw
171
+ end
172
+
173
+ def inspect #:nodoc:
174
+ fields = @fields.dup
175
+ #[:raw, :prefix, :command, :params, :command_raw]
176
+ [:raw, :command, :prefix, :params].each { |field| fields.delete(field) }
177
+ fields = fields.map { |k,v| "#{k}=#{v.inspect}" }.join(" ")
178
+ "#<%s:0x%x\n%s\n%s>" % [self.class, object_id, @raw.inspect, fields]
179
+ end
180
+
181
+ alias to_s inspect
182
+ end
183
+ end
184
+ end
@@ -0,0 +1,125 @@
1
+ #--
2
+ # Copyright 2007 by Stefan Rusterholz.
3
+ # All rights reserved.
4
+ # See LICENSE.txt for permissions.
5
+ #++
6
+
7
+
8
+
9
+ require 'butler/irc/channels'
10
+ require 'butler/irc/hostmask'
11
+ require 'butler/irc/message'
12
+ require 'butler/irc/parser/commands'
13
+ require 'butler/irc/string'
14
+ require 'butler/irc/users'
15
+ require 'ruby/exception/detailed'
16
+ require 'ruby/hash/zip'
17
+
18
+
19
+
20
+ class Butler
21
+ module IRC
22
+ # Parses messages, automatically converts
23
+ # provides a parser that automatically connects users and channels
24
+ # regarding who myself is (out_of_sight, back_in_sight for users)
25
+ # allows creation of dialogs from privmsg and notice messages
26
+ class Parser
27
+ # RFC 2812, Parser itself uses a simplified matching
28
+ module Expressions
29
+ Special = /[\[\]\\`_^{|}]/
30
+ Letter = /[A-Za-z]/
31
+ Hex = /[\dA-Fa-f]/
32
+ ChannelID = /[A-Z\d]{5}/
33
+ Chanstring = /[^\x00\x07\x10\x0D\x20,:]/
34
+ Channel = /(?:[#+&]|!#{ChannelID})#{Chanstring}(?::#{Chanstring})?/
35
+ User = /[^\x00\x10\x0D\x20@]/
36
+ Nick = /[A-Za-z\[\]\\`_^{|}][A-Za-z\d\[\]\\`_^{|}-]*/
37
+ Command = /[A-Za-z]+|\d{3}/
38
+ IP4addr = /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/
39
+ IP6addr = /[\dA-Fa-f](?::[\dA-Fa-f]){7}|0:0:0:0:0:(?:0|[Ff]{4}):#{IP4addr}/
40
+ Hostaddr = /#{IP4addr}|#{IP6addr}/
41
+ Shortname = /[A-Za-z0-9][A-Za-z0-9-]*/
42
+ Hostname = /#{Shortname}(?:\.#{Shortname})*/
43
+ Host = /#{Hostname}|#{Hostaddr}/
44
+ Prefix = /#{Hostname}|#{Nick}(?:(?:!#{User})?@#{Host})?/
45
+ Params = /.*/ # FIXME
46
+ Message = /(:#{Prefix} )?#{Command}(#{Params})?/
47
+ end
48
+
49
+ class InvalidMessageFormat < RuntimeError; end
50
+ class UnknownCommand < RuntimeError; end
51
+ module ParseError
52
+ include Exception::Detailed
53
+ end
54
+
55
+ RMessage = /\A(?::([^ \0]+) )?([A-Za-z\d]+|\d{3})(?: (.*))?\z/
56
+ RHostmask = /(#{Expressions::Nick})(?:(?:!(#{Expressions::User}))?@(#{Expressions::Host}))?/
57
+
58
+ attr_reader :client
59
+ attr_reader :users
60
+ attr_reader :channels
61
+
62
+ def initialize(client, users, channels)
63
+ @client = client
64
+ @users = users
65
+ @channels = channels
66
+ @commands = Commands.new
67
+ end
68
+
69
+ # parses an incomming message and returns a Message object from which you
70
+ # can easily access parsed data.
71
+ # Expects the newlines to be already chomped off.
72
+ def server_message(raw)
73
+ prefix, command, params, symbol, from = nil
74
+
75
+ # Basic analysis of the message
76
+ raise InvalidMessageFormat, raw unless matched = raw.match(RMessage)
77
+ prefix, command, params = *matched.captures
78
+ command.downcase!
79
+
80
+ # Parse prefix if possible (<nick>!<user>@<host>)
81
+ from = @users.create(*matched.captures) if prefix and matched = prefix.match(RHostmask)
82
+
83
+ # in depth analyzis of the message
84
+ parser = @commands[command]
85
+ symbol = parser.symbol
86
+ message = Message.new(@client, symbol, raw, prefix, command, params)
87
+ message.alter_member(:from, from)
88
+
89
+ parser.create_fields(message)
90
+
91
+ if message.for then
92
+ if message.for.valid_channelname? then
93
+ channel = @channels.create(message.for)
94
+ message.alter_member(:channel, channel)
95
+ message.alter_member(:for, channel)
96
+ else
97
+ message.alter_member(:for, @users.create(message.for))
98
+ end
99
+ end
100
+ message.alter_member(:channel, @channels.create(message.channel)) if message.channel
101
+
102
+ parser.process(message, self)
103
+
104
+ return message
105
+ rescue IndexError
106
+ raise UnknownCommand, "Unknown command #{command}: #{raw.inspect}"
107
+ rescue => e
108
+ e.extend ParseError
109
+ e.prepend "Message: #{raw.inspect}"
110
+ raise e
111
+ end
112
+
113
+ def leave_channel(message, reason1, reason2)
114
+ if message.from && message.channel then
115
+ message.from.delete_channel(message.channel, reason1)
116
+ message.channel.delete_user(message.from, reason1)
117
+ if message.from == @users.myself then
118
+ @channels.delete(message.channel)
119
+ @users.delete_channel(message.channel, reason2)
120
+ end
121
+ end
122
+ end
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,83 @@
1
+ #--
2
+ # Copyright 2007 by Stefan Rusterholz.
3
+ # All rights reserved.
4
+ # See LICENSE.txt for permissions.
5
+ #++
6
+
7
+
8
+
9
+ require 'butler/irc/parser'
10
+
11
+
12
+
13
+ class Butler
14
+ module IRC
15
+ class Parser
16
+ class Commands
17
+ def initialize(specifics=nil)
18
+ @commands = Hash.new { |h,k| raise IndexError, "Unknown command #{k}" }
19
+ file = "#{File.dirname(__FILE__)}/generic.rb"
20
+ instance_eval(File.read(file), file)
21
+ end
22
+
23
+ def add(raw, *args, &proc)
24
+ raise IndexError, "Command #{raw} is already registered. Did you want 'alter'?" if @commands.has_key?(raw)
25
+ @commands[raw.downcase] = Command.new(raw, *args, &proc)
26
+ end
27
+
28
+ def alter(raw, *args, &proc)
29
+ raise IndexError, "Command #{raw} is not registered. Did you want 'add'?" unless @commands.has_key?(raw)
30
+ @commands[raw.downcase] = Command.new(raw, *args, &proc)
31
+ end
32
+
33
+ def [](raw)
34
+ @commands[raw.downcase]
35
+ end
36
+
37
+ def inspect
38
+ "#<%s:0x%x>" % [self.class, object_id]
39
+ end
40
+ end
41
+
42
+ # Provides parsing information about specific commands.
43
+ class Command
44
+ attr_reader :raw
45
+ attr_reader :symbol
46
+ attr_reader :matcher
47
+ attr_reader :mapping
48
+ attr_reader :processor
49
+
50
+ # See Butler::IRC::COMMANDS for samples on instanciation of
51
+ # Butler::IRC::Command objects.
52
+ def initialize(raw, symbol, matcher=nil, mapping=nil, &processor)
53
+ @raw = raw
54
+ @symbol = symbol
55
+ @matcher = matcher
56
+ @mapping = mapping
57
+ @processor = processor
58
+ end
59
+
60
+ def match(message)
61
+ @matcher.match(message)
62
+ end
63
+
64
+ def fields(message)
65
+ return nil unless @matcher and match = @matcher.match(message.params)
66
+ Hash.zip(@mapping, match.captures)
67
+ end
68
+
69
+ def create_fields(message)
70
+ if @matcher and match = @matcher.match(message.params) then
71
+ @mapping.zip(match.captures) { |name, value|
72
+ message.alter_member(name, value, true)
73
+ }
74
+ end
75
+ end
76
+
77
+ def process(message, parser)
78
+ @processor.call(message, parser) if @processor
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,343 @@
1
+ #--
2
+ # Copyright 2007 by Stefan Rusterholz.
3
+ # All rights reserved.
4
+ # See LICENSE.txt for permissions.
5
+ #++
6
+
7
+
8
+
9
+ # A list with all known IRC-Commands and it's parsing instructions
10
+ # Commands with __ in it are customs, not verified towards any standard.
11
+
12
+ #Text based codes
13
+ add("error", :ERROR) # ERROR :<error-message>
14
+
15
+ add("invite", :INVITE, /^([^\s]*) :(.*)/, [:invited, :channel])
16
+
17
+ add("join", :JOIN, /^:(.*)/, [:channel]) { |message, parser|
18
+ if message.from && message.channel then
19
+ message.from.add_channel(message.channel, :join)
20
+ message.channel.add_user(message.from, :join)
21
+ end
22
+ }
23
+
24
+ add("kick", :KICK, /^([^\s]*) ([^\s]*) :(.*)/, [:channel, :for, :text]) { |message, parser|
25
+ parser.leave_channel(message, :kick, :kicked)
26
+ }
27
+
28
+ add("kill", :KILL, /^([^\s]*) ([^\s]*) (.*)/, [:channel, :for, :text]) { |message, parser|
29
+ if message.for then
30
+ message.for.kill
31
+ parser.channels.delete_user(message.for, :kill)
32
+ end
33
+ }
34
+
35
+ # FIXME, really ':?' or just plain ':'?
36
+ add("mode", :MODE, /^([^\s]*) :?(.*)/, [:for, :arguments]) { |message, parser|
37
+ modifiers = message[:arguments].split(" ")
38
+ modes = modifiers.shift.split("")
39
+ flags = {"o" => User::Flags::OP, "v" => User::Flags::VOICE, "u" => User::Flags::UOP}
40
+ message.create_member(:modes, [])
41
+ argumentIndex = 0
42
+ direction = "+"
43
+
44
+ modes.each { |mode|
45
+ # MODEs direction
46
+ if (["+", "-"].include?(mode)) then
47
+ direction = mode
48
+ # MODEs taking an argument
49
+ elsif (["k", "o", "v", "u"].include?(mode) || (mode == "l" && direction == "+")) then
50
+ if ["o", "v", "u"].include?(mode) then
51
+ if (direction == "+") then
52
+ parser.users[modifiers[argumentIndex]].add_flags(message.channel, flags[mode]) #adding flags to user
53
+ else
54
+ parser.users[modifiers[argumentIndex]].delete_flags(message.channel, flags[mode]) #removing flags from user
55
+ end
56
+ end
57
+ message.modes << [direction, mode, modifiers[argumentIndex]]
58
+ argumentIndex = argumentIndex+1
59
+ # MODEs without argument
60
+ else
61
+ message.modes << [direction, mode, nil]
62
+ end
63
+ }
64
+ }
65
+ add("nick", :NICK, /^:(.*)/, [:nick]) { |message, parser|
66
+ message.create_member(:old_nick, message.from.nick)
67
+ message.from.nick = message.nick if message.from
68
+ }
69
+
70
+ add("notice", :NOTICE, /([^\s]+) :(.*)/, [:for, :text]) { |message, parser|
71
+ if message.channel then
72
+ message.create_member(:realm, :public)
73
+ message.create_method(:private?) { false }
74
+ message.create_method(:public?) { true }
75
+ else
76
+ message.create_member(:realm, :private)
77
+ message.create_method(:private?) { true }
78
+ message.create_method(:public?) { false }
79
+ end
80
+ }
81
+
82
+ add("part", :PART, /^([^\x00\x07\x10\x0D\x20,:]+)(?: :(.*))?/, [:channel, :reason]) { |message, parser|
83
+ parser.leave_channel(message, :part, :parted)
84
+ }
85
+
86
+ add("ping", :PING, /:(.*)/, [:pong])
87
+
88
+ add("pong", :PONG)
89
+
90
+ add("privmsg", :PRIVMSG, /([^\s]+) :(.*)/, [:for, :text]) { |message, parser|
91
+ if message.channel then
92
+ message.create_member(:realm, :public)
93
+ message.create_method(:private?) { false }
94
+ message.create_method(:public?) { true }
95
+ else
96
+ message.create_member(:realm, :private)
97
+ message.create_method(:private?) { true }
98
+ message.create_method(:public?) { false }
99
+ end
100
+ }
101
+
102
+ add("quit", :QUIT, /(.*)/, [:text]) { |message, parser|
103
+ if message.from then
104
+ message.from.quit
105
+ parser.channels.delete_user(message.from, :quit)
106
+ parser.users.delete(message.from, :quit)
107
+ end
108
+ }
109
+ add("topic", :TOPIC, /([^\s]+) :(.*)/, [:channel, :text])
110
+
111
+ #Unknown/Non-RFC codes
112
+ add("007", :UNK_007)
113
+ add("008", :UNK_008)
114
+ add("009", :UNK_009)
115
+ add("307", :RPL_REGISTERED_INFO) #FIXME non2812 "ConferenceRoom" bluewin, sent if a nick is registered
116
+ add("329", :RPL_CHANNEL_INFO) #channel creation time
117
+ add("377", :UNK_377)
118
+
119
+ #0** Codes
120
+ add("001", :RPL_WELCOME)
121
+ add("002", :RPL_YOURHOST)
122
+ add("003", :RPL_CREATED)
123
+ add("004", :RPL_MYINFO)
124
+ add("005", :RPL_ISUPPORT)
125
+
126
+ #2** Codes
127
+ add("200", :RPL_TRACELINK)
128
+ add("201", :RPL_TRACECONNECTING)
129
+ add("202", :RPL_TRACEHANDSHAKE)
130
+ add("203", :RPL_TRACEUNKNOWN)
131
+ add("204", :RPL_TRACEOPERATOR)
132
+ add("205", :RPL_TRACEUSER)
133
+ add("206", :RPL_TRACESERVER)
134
+ add("207", :RPL_TRACESERVICE)
135
+ add("208", :RPL_TRACENEWTYPE)
136
+ add("209", :RPL_TRACECLASS)
137
+ add("210", :RPL_TRACERECONNECT)
138
+ add("211", :RPL_STATSLINKINFO)
139
+ add("212", :RPL_STATSCOMMANDS)
140
+ add("213", :RPL_STATSCLINE)
141
+ add("214", :RPL_STATSNLINE)
142
+ add("215", :RPL_STATSILINE)
143
+ add("216", :RPL_STATSKLINE)
144
+ add("217", :RPL_STATSQLINE)
145
+ add("218", :RPL_STATSYLINE)
146
+ add("219", :RPL_ENDOFSTATS)
147
+ add("221", :RPL_UMODEIS)
148
+ add("231", :RPL_SERVICEINFO)
149
+ add("232", :RPL_ENDOFSERVICES)
150
+ add("233", :RPL_SERVICE)
151
+ add("234", :RPL_SERVLIST)
152
+ add("235", :RPL_SERVLISTEND)
153
+ add("240", :RPL_STATSVLINE)
154
+ add("241", :RPL_STATSLLINE)
155
+ add("242", :RPL_STATSUPTIME)
156
+ add("243", :RPL_STATSOLINE)
157
+ add("244", :RPL_STATSHLINE)
158
+ add("245", :RPL_STATSSLINE) # RFC 2812 seems to be erroneous, it assigns 244 double
159
+ add("246", :RPL_STATSPING)
160
+ add("247", :RPL_STATSBLINE)
161
+ add("250", :RPL_STATSCONN)
162
+ add("251", :RPL_LUSERCLIENT)
163
+ add("252", :RPL_LUSEROP)
164
+ add("253", :RPL_LUSERUNKNOWN)
165
+ add("254", :RPL_LUSERCHANNELS)
166
+ add("255", :RPL_LUSERME)
167
+ add("256", :RPL_ADMINME)
168
+ add("257", :RPL_ADMINLOC1)
169
+ add("258", :RPL_ADMINLOC2)
170
+ add("259", :RPL_ADMINEMAIL)
171
+ add("261", :RPL_TRACELOG)
172
+ add("262", :RPL_TRACEEND)
173
+ add("263", :RPL_TRYAGAIN)
174
+ add("265", :RPL_LOCALUSERS)
175
+ add("266", :RPL_GLOBALUSERS)
176
+ add("290", :RPL_IDENTIFY_MSG) # FIXME non2812 "hyperion-1.0.2b"
177
+
178
+ #3** Codes
179
+ add("300", :RPL_NONE)
180
+ add("301", :RPL_AWAY)
181
+ add("302", :RPL_USERHOST)
182
+ add("303", :RPL_ISON)
183
+ add("305", :RPL_UNAWAY)
184
+ add("306", :RPL_NOWAWAY)
185
+ add("311", :RPL_WHOISUSER, /^([^\s]+) ([^\s]+) ([^\s]+) ([^\s]+) \* :(.*)/, [:for, :nick, :user, :host, :real])
186
+ add("312", :RPL_WHOISSERVER)
187
+ add("313", :RPL_WHOISOPERATOR)
188
+ add("314", :RPL_WHOWASUSER)
189
+ add("315", :RPL_ENDOFWHO)
190
+ add("316", :RPL_WHOISCHANOP)
191
+ add("317", :RPL_WHOISIDLE, /^([^\s]+) ([^\s]+) ([^:]+) :(.*)/, [:for, :nick, :values, :descriptions]) { |message, parser|
192
+ values = message[:values].split(" ");
193
+ descriptions = message[:descriptions].split(", ").map { |desc| desc.gsub(" ", "_").to_sym };
194
+ message.delete(:values)
195
+ message.delete(:descriptions)
196
+ 0.upto([values.length, descriptions.length].min-1) { |index|
197
+ message[descriptions[index]] = values[index]
198
+ }
199
+ }
200
+ add("318", :RPL_ENDOFWHOIS)
201
+ add("319", :RPL_WHOISCHANNELS, /^([^\s]+) ([^\s]+) :(.*)/, [:for, :nick, :channels]) { |message, parser|
202
+ # FIXME
203
+ message.alter_member(:channels, message.channels.split(" ").map { |channel| parser.channels.create(channel) })
204
+ }
205
+ add("320", :RPL_IDENTIFIED_TO_SERVICES) # possibly hyperion only
206
+ add("321", :RPL_LISTSTART)
207
+ add("322", :RPL_LIST, /^([^\s]+) ([^\s]+) (\d+) :(.*)/, [:for, :channelname, :usercount, :topic])
208
+ add("323", :RPL_LISTEND)
209
+ add("324", :RPL_CHANNELMODEIS)
210
+ add("325", :RPL_UNIQOPIS)
211
+ add("331", :RPL_NOTOPIC)
212
+ # :irc.server.net 332 YourNickname #channel :Topic
213
+ add("332", :RPL_TOPIC, /^([^\s]+) ([^\s]+) :(.*)/, [:for, :channel, :topic]) { |message, parser|
214
+ message.channel.topic.text = message[:topic]
215
+ }
216
+
217
+ # :irc.server.net 333 YourNickname #channel SetByNick 1139902138
218
+ add("333", :RPL_TOPIC_INFO, /^([^\s]+) ([^\s]+) ([^\s]+) (\d+)/, [:for, :channel, :set_by, :set_at]) { |message, parser|
219
+ topic = message.channel.topic
220
+ topic.set_by = message[:set_by]
221
+ topic.set_at = message[:set_at]
222
+ }
223
+ add("341", :RPL_INVITING)
224
+ add("342", :RPL_SUMMONING)
225
+ add("343", :RPL__MAINTENANCE) #mainenance notice?
226
+ add("346", :RPL_INVITELIST)
227
+ add("347", :RPL_ENDOFINVITELIST)
228
+ add("348", :RPL_EXCEPTLIST)
229
+ add("349", :RPL_ENDOFEXCEPTLIST)
230
+ add("351", :RPL_VERSION)
231
+ # :irc.server.net 352 YourNickname <channel> <user> <host> <server> <nick> ( "H" / "G" > ["*"] [ ( "@" / "+" ) ] :<hopcount> <real name>
232
+ add("352", :RPL_WHOREPLY,
233
+ /([^\s]+) ([^\s]+) ([^\s]+) ([^\s]+) ([^\s]+) ([^\s]+) ([HG])[*%]{0,2}([@+-]{0,3}) :(\d+) (.*)/,
234
+ [:for, :channel, :user, :host, :server, :nick, :status, :flags, :hopcount, :real]) { |message, parser|
235
+ #"for", "channel", "user", "host", "server", "nick", "status", "flags", "hopcount", "real"
236
+ user = parser.users.create(message[:nick])
237
+ user.update(message[:user], message[:host], message[:real])
238
+ user.away = message[:status] == "G"
239
+ user.add_channel(message.channel, :joined)
240
+ message.channel.add_user(user, :joined)
241
+ user.add_flags(message.channel, message[:flags].to_flags)
242
+ }
243
+ add("353", :RPL_NAMEREPLY, /([^\s]+) [=@] ([^\s]+) :(.*)/, [:for, :channel, :users]) { |message, parser|
244
+ users = message[:users]
245
+ message.alter_member(:users, [])
246
+ users.split(/ /).each { |nick|
247
+ user = parser.users.create(nick)
248
+ message[:users] << user
249
+ user.add_channel(message.channel, :joined)
250
+ message.channel.add_user(user, :joined)
251
+ }
252
+ }
253
+ add("361", :RPL_KILLDONE)
254
+ add("362", :RPL_CLOSING)
255
+ add("363", :RPL_CLOSEEND)
256
+ add("364", :RPL_LINKS)
257
+ add("365", :RPL_ENDOFLINKS)
258
+ add("366", :RPL_ENDOFNAMES)
259
+ add("367", :RPL_BANLIST,
260
+ /([^\s]+) ([^\s]+) ([^\s]+) ([^\s]+) (\d+)/,
261
+ [:for, :channel, :banmask, :banned_by, :bantime]
262
+ ) { |message, parser| #367 nickname #channel nick!user@host nickname 1140125288
263
+ message[:bantime] = Time.at(message[:bantime].to_i)
264
+ message[:banmask] = Hostmask.new(message[:banmask])
265
+ }
266
+
267
+ add("368", :RPL_ENDOFBANLIST)
268
+ add("369", :RPL_ENDOFWHOWAS)
269
+ add("371", :RPL_INFO)
270
+ add("372", :RPL_MOTD)
271
+ add("373", :RPL_INFOSTART)
272
+ add("374", :RPL_ENDOFINFO)
273
+ add("375", :RPL_MOTDSTART)
274
+ add("376", :RPL_ENDOFMOTD)
275
+ add("381", :RPL_YOUREOPER)
276
+ add("382", :RPL_REHASHING)
277
+ add("383", :RPL_YOURESERVICE)
278
+ add("384", :RPL_MYPORTIS)
279
+ add("386", :RPL_PASSWORDACCEPTED) # Custom (chatroom)
280
+ add("391", :RPL_TIME)
281
+ add("392", :RPL_USERSSTART)
282
+ add("393", :RPL_USERS)
283
+ add("394", :RPL_ENDOFUSERS)
284
+ add("395", :RPL_NOUSERS)
285
+
286
+ #4** Codes
287
+ add("401", :ERR_NOSUCHNICK)
288
+ add("402", :ERR_NOSUCHSERVER)
289
+ add("403", :ERR_NOSUCHCHANNEL)
290
+ add("404", :ERR_CANNOTSENDTOCHAN)
291
+ add("405", :ERR_TOOMANYCHANNELS)
292
+ add("406", :ERR_WASNOSUCHNICK)
293
+ add("407", :ERR_TOOMANYTARGETS)
294
+ add("408", :ERR_NOSUCHSERVICE)
295
+ add("409", :ERR_NOORIGIN)
296
+ add("411", :ERR_NORECIPIENT)
297
+ add("412", :ERR_NOTEXTTOSEND)
298
+ add("413", :ERR_NOTOPLEVEL)
299
+ add("414", :ERR_WILDTOPLEVEL)
300
+ add("415", :ERR_BADMASK)
301
+ add("421", :ERR_UNKNOWNCOMMAND)
302
+ add("422", :ERR_NOMOTD)
303
+ add("423", :ERR_NOADMININFO)
304
+ add("424", :ERR_FILEERROR)
305
+ add("431", :ERR_NONICKNAMEGIVEN)
306
+ add("432", :ERR_ERRONEUSNICKNAME)
307
+ add("433", :ERR_NICKNAMEINUSE)
308
+ add("436", :ERR_NICKCOLLISION)
309
+ add("437", :ERR_UNAVAILRESOURCE)
310
+ add("441", :ERR_USERNOTINCHANNEL)
311
+ add("442", :ERR_NOTONCHANNEL)
312
+ add("443", :ERR_USERONCHANNEL)
313
+ add("444", :ERR_NOLOGIN)
314
+ add("445", :ERR_SUMMONDISABLED)
315
+ add("446", :ERR_USERSDISABLED)
316
+ add("451", :ERR_NOTREGISTERED)
317
+ add("461", :ERR_NEEDMOREPARAMS)
318
+ add("462", :ERR_ALREADYREGISTRED)
319
+ add("463", :ERR_NOPERMFORHOST)
320
+ add("464", :ERR_PASSWDMISMATCH)
321
+ add("465", :ERR_YOUREBANNEDCREEP)
322
+ add("466", :ERR_YOUWILLBEBANNED)
323
+ add("467", :ERR_KEYSET)
324
+ add("471", :ERR_CHANNELISFULL)
325
+ add("472", :ERR_UNKNOWNMODE)
326
+ add("473", :ERR_INVITEONLYCHAN)
327
+ add("474", :ERR_BANNEDFROMCHAN)
328
+ add("475", :ERR_BADCHANNELKEY)
329
+ add("476", :ERR_BADCHANMASK)
330
+ add("477", :ERR_NOCHANMODES)
331
+ add("478", :ERR_BANLISTFULL)
332
+ add("481", :ERR_NOPRIVILEGES)
333
+ add("482", :ERR_CHANOPRIVSNEEDED)
334
+ add("483", :ERR_CANTKILLSERVER)
335
+ add("484", :ERR_RESTRICTED)
336
+ add("485", :ERR_UNIQOPPRIVSNEEDED)
337
+ add("491", :ERR_NOOPERHOST)
338
+ add("492", :ERR_NOSERVICEHOST)
339
+
340
+ #5** Codes
341
+ add("501", :ERR_UMODEUNKNOWNFLAG)
342
+ add("502", :ERR_USERSDONTMATCH)
343
+ add("505", :ERR__NOPRIVMSG)