· 8 years ago · Apr 12, 2018, 07:14 AM
1##BotUser
2class BotUser
3 def initialize(nick, pass, accessLevel)
4 @nickname = nick
5 @password = pass
6 @access = accessLevel
7 @address = nil
8 @loggedin = false
9 end
10 def login()
11 @loggedin = true
12 end
13 def logout()
14 @address = nil
15 @loggedin = false
16 end
17 def isLoggedIn()
18 @loggedin
19 end
20 def getNick()
21 @nickname
22 end
23 def getPass()
24 @password
25 end
26 def getAccess()
27 @access
28 end
29 def setPass(newPass)
30 @password = newPass
31 end
32 def setAccess(newAccess)
33 @access = newAccess
34 end
35 def setAddress(newAddress)
36 @address=newAddress
37 end
38 def getAddress()
39 @address
40 end
41end
42##ExaltedBotActions
43#notes
44#Definitely nonfunctional:
45#Possibly nonfunctional:
46#Not Yet Implemented:
47
48require "BotUser"
49require "socket"
50
51module ExaltedBotActions
52MAX_ROLL_DICE = 200
53MAX_LOT_DICE = 85
54#ACCESS_LEVELS = Hash[:admin => 999, :chanop => 300, :normal => 100]
55ACCESS_LEVELS = {
56 :admin => 999,
57 :chanop => 300,
58 :normal => 100
59}
60CORE_ACTIONS = {
61 /^PING :(.+)$/i => :respond_to_ping,
62 /^:(.+?)!(.+?)@(.+?)\sPRIVMSG\s(.+)\s:(.+)$/i => :respond_to_privmsg,
63 /^:(.+?)!(.+?)@(.+?)\sQUIT/i => :respond_to_usrquit
64}
65MESSAGE_ACTIONS = {
66 /^!roll\s(.+)/i => :respond_to_roll,
67 /^!rolldamage\s(.+)/i => :respond_to_rolldamage,
68 /^!help$/i => :respond_to_general_help,
69 /^!help\s(.+)/i => :respond_to_specific_help,
70 /^!join\s(.+)/i => :respond_to_join_request,
71 /^!part\s(.+)/i => :respond_to_part_request,
72 /^!verbose\s(\d+)/i => :respond_to_verbosity_change_request,
73 /^!lot\s(\d+)\s(\d+)/i => :respond_to_lot,
74 /^!lotd\s(\d+)\s(\d+)/i => :respond_to_lotd,
75 /^!quit/i => :respond_to_quit_request,
76
77 #auth actions
78 /^!auth\s(.+)\s(.+)/i => :respond_to_auth_request,
79 /^!register\s(.+)\s(.+)$/i => :respond_to_register_request,
80 /^!password\s(.+)\s(.+)\s(.+)$/i => :respond_to_pass_change_request,
81 /^!setacess\s(.+)\s(.+)&/i => :respond_to_set_access,
82 /^!logout/i => :respond_to_logout
83}
84HELP_RESPONSES = {
85 "roll" => ":roll: !roll dice extrasuccesses difficulty. Arguments extrasuccesses and difficulty are optional. Limited to 200 dice.",
86 "rolldamage" => ":rolldamage: !rolldamage dice extrasuccesses. Argument extrasuccesses is optional. Maximum of 200 dice.",
87 "lot" => ":lot: !lot setsofdice numberofdice. Maximum of 80 for each argument. Doubles 10s.",
88 "lotd" => ":lotd: !lotd setsofdice numberofdice. Maximum of 80 for each argument. Does not double 10s.",
89 "register" => ":register: !register username password. All users are registered as normal users unless specified by an admin.",
90 "auth" => ":auth: !auth username password. Use to authenticate to @access services. Please remember to !logout before leaving.",
91 "password" => ":password: !password username oldpassword newpassword. Use to change your password.",
92 "logout" => ":logout: No arguments. Logs you out of the authentication system."
93}
94def respond_to_ping(md, str)
95 cmd "PONG :#{md[1]}"
96end
97def respond_to_privmsg(md, str)
98 details = {
99 :address => md[3],
100 :user => md[4] == connection_info[:nick] ? md[1] : md[4],
101 :original => str,
102 :name => md[1],
103 :msg => md[5].strip
104 }
105 match_data = nil
106 dispatch = MESSAGE_ACTIONS.keys.find { |x| match_data = details[:msg].match(x) }
107 self.send(MESSAGE_ACTIONS[dispatch], details, match_data) if MESSAGE_ACTIONS[dispatch]!=nil
108 end
109 def respond_to_roll(details, md)
110 roll_string = md[1]
111 rollparams = roll_string.split.map {|x| x.to_i}
112
113 if rollparams[0]<=MAX_ROLL_DICE
114 rollarray = roll(rollparams[0],true)#.map{|x| x.to_i}
115 if rollparams.length() == 1 #then
116 cmd "PRIVMSG #{details[:user]} :#{details[:name]} rolled the dice and got <#{rollarray[2]}>." if @verbose == 1
117 cmd "PRIVMSG #{details[:user]} :Rolled #{rollparams[0]} dice for #{details[:name]} and got #{rollarray[0]} successes and #{rollarray[1]} ones."
118 elsif rollparams.length() == 2 #then
119 cmd "PRIVMSG #{details[:user]} :#{details[:name]} rolled the dice and got <#{rollarray[2]}>." if @verbose == 1
120 cmd "PRIVMSG #{details[:user]} :Rolled #{rollparams[0]} dice for #{details[:name]} and got #{rollarray[0].to_i+rollparams[1]} successes and #{rollarray[1]} ones with #{rollparams[1]} bonus successes."
121 elsif rollparams.length() == 3 #then
122 cmd "PRIVMSG #{details[:user]} :#{details[:name]} rolled the dice and got <#{rollarray[2]}>." if @verbose == 1
123 tmpsuccesses = rollarray[0].to_i+rollparams[1]
124 if tmpsuccesses >= rollparams[2] #then
125 cmd "PRIVMSG #{details[:user]} :Rolled #{rollparams[0]} dice for #{details[:name]} and SUCCEEDED against difficulty #{rollparams[2]} with #{rollarray[0].to_i+rollparams[1]-rollparams[2]} extra successes."
126 else
127 cmd "PRIVMSG #{details[:user]} :Rolled #{rollparams[0]} dice for #{details[:name]} and FAILED against difficulty #{rollparams[2]}."
128 end
129 else
130 puts "Error: number of params"
131 end
132 if rollarray[0] == 0 #then
133 if rollarray[1] > 0 #then
134 cmd "PRIVMSG #{details[:user]} :#{details[:name]} BOTCHED his roll!"
135 end
136 end
137 end
138 end
139 def respond_to_rolldamage(details, md)
140 roll_string = md[1]
141 rollparams = roll_string.split#.map {|x| x.to_i}
142 if rollparams[0].to_i<=MAX_ROLL_DICE #then
143 rollarray = roll(rollparams[0].to_i,false)#.map {|x| x.to_i}
144 if rollparams.length() == 1 #then
145 cmd "PRIVMSG #{details[:user]} :#{details[:name]} rolled the dice and got <#{rollarray[2]}>." if @verbose == 1
146 cmd "PRIVMSG #{details[:user]} :Rolled #{rollparams[0]} dice for #{details[:name]} and got #{rollarray[0]} successes and #{rollarray[1]} ones."
147 elsif rollparams.length() == 2 #then
148 cmd "PRIVMSG #{details[:user]} :#{details[:name]} rolled the dice and got <#{rollarray[2]}>." if @verbose == 1
149 cmd "PRIVMSG #{details[:user]} :Rolled #{rollparams[0]} dice for #{details[:name]} and got #{rollarray[0].to_i+rollparam[2]} successes and #{rollarray[1]} ones with #{rollparams[1]} bonus successes."
150 else
151 puts "Error: number of arguments"
152 end
153 end
154 end
155 def respond_to_general_help(details, md)
156 cmd "PRIVMSG #{details[:name]} :ExaltedBot version 1.0, implemented in ruby. (c) 2007 Interested2. Special thanks to KirinDave for his invaluable assistance."
157 cmd "PRIVMSG #{details[:name]} :Valid commands: !roll, !rolldamage, !lot, !lotd, !register, !auth, !password"
158 cmd "PRIVMSG #{details[:name]} :Please enter the command after help, e.g., !help roll"
159 end
160 def respond_to_specific_help(details, md)
161 #implement via hash lookup
162 help_question = md[1].split
163 cmd "PRIVMSG #{details[:name]} #{HELP_RESPONSES[help_question[0]]}"
164 end
165 def respond_to_join_request(details, md)
166 if det_access(details[:address],ACCESS_LEVELS[:admin]) #then
167 cmd "JOIN #{md[1]}" #add authentication requirement, admin @access only
168 end
169 end
170 def respond_to_part_request(details, md)
171 if det_access(details[:address],ACCESS_LEVELS[:admin]) #then
172 cmd "PART #{md[1]}" #add authentication requirement, admin @access only
173 end
174 end
175 def respond_to_verbosity_change_request(details, md)
176 if det_access(details[:address],ACCESS_LEVELS[:admin]) #then
177 #puts "Verbose cmd: #{md[1]}"
178 #add authentication requirement, chan @access only. Also need to modify to keep a table for channels.
179 @verbose= 1 if md[1] == "1"
180 @verbose= 0 if md[1] == "0"
181 end
182 end
183 def respond_to_lot(details, md)
184 cmd "PRIVMSG #{details[:user]} :#{details[:name]} rolled (#{md[1]} x #{md[2]}) and #{rolllot(md[1].to_i,md[2].to_i,true)}"
185 end
186 def respond_to_lotd(details, md)
187 cmd "PRIVMSG #{details[:user]} :#{details[:name]} rolled (#{md[1]} x #{md[2]}) and #{rolllot(md[1].to_i,md[2].to_i,false)}"
188 end
189 def respond_to_quit_request(details, md)
190 if det_access(details[:address],ACCESS_LEVELS[:admin]) #then
191 disconnect() #leave. just go.
192 end
193 end
194 def respond_to_auth_request(details, md)
195 nick = md[1]
196 pass = md[2]
197 if @access.has_key?(nick)
198 loginUsr = @access[nick]
199 if @access[nick].isLoggedIn == true
200 cmd "PRIVMSG #{details[:name]} :You are already logged in, possibly on a different address."
201 else
202 if loginUsr.getPass() == pass
203 #add to logged in table
204 @access[nick].login
205 @access[nick].setAddress(details[:address])
206 cmd "PRIVMSG #{details[:name]} :Welcome to ExaltedBot remote console command."
207 else
208 cmd "PRIVMSG #{details[:name]} :Incorrect password."
209 end
210 end
211 else
212 cmd "PRIVMSG #{details[:name]} :User does not exist. Use the !register command to create a new user."
213 end
214 end
215 def respond_to_register_request(details, md)
216 nick = md[1]
217 pass = md[2]
218 if @access[nick] != nil
219 cmd "PRIVMSG #{details[:name]} :User already exists."
220 else
221 #register the user and log him in
222 @access[nick] = BotUser.new(nick, pass, ACCESS_LEVELS[:normal])
223 @access[nick].login
224 @access[nick].setAddress(details[:address])
225 cmd "PRIVMSG #{details[:name]} :Welcome to ExaltedBot remote console command."
226 end
227 end
228 def respond_to_pass_change_request(details, md)
229 if det_access(details[:address],ACCESS_LEVELS[:normal]) #then
230 username = md[1]
231 oldpassword = md[2]
232 newpassword = md[3]
233 if @access[username]!=nil #then
234 if @access[username].isLoggedIn && details[:address]==@access[username].getAddress && oldpassword == @access[username].getPass #then
235 @access[username].setPass(newpassword)
236 cmd "PRIVMSG #{details[:name]} :Password changed."
237 end
238 end
239 end
240 end
241 def respond_to_set_access(details, md)
242 if det_access(details[:address],ACCESS_LEVELS[:admin]) #then
243 if @access[md[1]]!=nil #then
244 @access[md[1]].setPass[md[2]]
245 end
246 end
247 end
248 def respond_to_logout(details, md)
249 logOutUser(details[:address])
250 cmd "PRIVMSG #{details[:name]} :Bye."
251 end
252 def respond_to_usrquit(md, str)
253 logOutUser(md[2])
254 end
255
256 #utility methods
257 def roll(dice, doubletens)
258 #roll dice, one required argument
259 i=0
260 successes = 0
261 ones = 0
262 tmpstr = " "
263 while i<dice
264 tmprl = rand(10)+1
265 tmpstr << tmprl.to_s if i==0
266 tmpstr << "," << tmprl.to_s if i!= 0
267 #puts tmpstr
268 if tmprl==1 #then
269 ones = ones + 1
270 elsif tmprl>6 #then
271 successes = successes + 1
272 elsif tmprl==10 #then
273 if doubletens==#true then
274 successes = successes +1
275 end
276 end
277 i=i+1
278 end
279 return [successes, ones, tmpstr]
280 end
281 def rolllot(sets, dice, doubletens)
282 if sets < MAX_LOT_DICE && dice < MAX_LOT_DICE
283 count = 0
284 rollarray = nil
285 tempstr = "got "
286 while count<sets
287 rollarray = roll(dice,doubletens)
288 if count==0
289 tempstr << rollarray[0].to_s
290 else
291 tempstr << ", " << rollarray[0].to_s
292 end
293 count += 1
294 end
295 #puts tempstr
296 return tempstr
297 end
298 end
299 def cmd(command)
300 puts "#{@connection_info[:server]} <-- #{command}"
301 @connection.send "#{command}\n",0
302 end
303
304 #authentication methods
305 def det_access(address, value)
306 valid = @access.select {|k,v| v.getAddress() == address}
307 return false if valid.empty?
308 #puts valid[0][1].getAccess().to_i >= value.to_i
309 return valid[0][1].getAccess().to_i >= value.to_i
310 end
311 def logOutUser(addy)
312 valid = @access.select {|k,v| v.getAddress() == addy}
313 @access[valid[0][0]].logout if valid!=nil
314 end
315 #networking methods
316end
317##ExaltedBotSession
318require "ExaltedBotActions"
319class ExaltedBotSession
320 include ExaltedBotActions
321#need to find a way to give ExaltedBotActions access to these variables. They MUST be shared.
322 attr :verbose,true
323 attr :access,true
324 attr :connection,true
325 attr :connection_info,true
326 attr :active,true
327 def initialize(server, port, nick)
328 @verbose = 1
329 @access = Hash.new
330 @connection_info = {
331 :server => server,
332 :port => port,
333 :nick => nick
334 }
335 loadUserDB()
336 @connection = TCPSocket.open(server,port)
337 cmd "USER Interested2 2 3 :Interested2 2"
338 cmd "NICK #{nick}"
339 main()
340 end
341 def disconnect()
342 @active = 0
343 saveUserDB()
344 @connection.close()
345 end
346 def main()
347 @active = 1
348 while @active==1
349 input = select([@connection], nil, nil)
350 next if !input
351 #puts "<-- #{input[0]}"
352 s=@connection.gets
353 respond_to_server(s) if s != nil
354 end
355 end
356 def respond_to_server(input)
357 #puts ExaltedBotActions::CORE_ACTIONS
358 puts "#{@connection_info[:server]} --> #{input}"
359 inp = ExaltedBotActions::CORE_ACTIONS.select {|k,v| k === input}
360 if inp.first != nil #then
361 self.send(ExaltedBotActions::CORE_ACTIONS.fetch(inp.first.first), inp.first.first.match(input), input)
362 end
363 end
364
365
366 #file IO
367 def loadUserDB()
368 db_file = "" << @connection_info[:server] << ".txt"
369 #begin
370 f = File.open(db_file, "r")
371 fArray = f.readlines
372 iter = 0
373 while iter < fArray.length
374 tempAr = fArray[iter].split
375 @access[tempAr[0]] = BotUser.new(tempAr[1],tempAr[2],tempAr[3])
376 #puts @@access
377 iter += 1
378 end
379 #rescue filenotfound?
380 #end
381end
382def saveUserDB()
383 db_file = "" << @connection_info[:server] << ".txt"
384 f=File.open(db_file, "w")
385 @access.each_pair {|k,v| f.write("#{k} #{v.getNick} #{v.getPass} #{v.getAccess} \n")}
386 f.close
387end
388end
389##RubyBot
390require "socket"
391require "ExaltedBotSession"
392
393def process(tmp)
394 tmp = tmp.split
395 tempThread = Thread.new do
396 ExaltedBotSession.new(tmp[0],tmp[1],tmp[2])
397 end
398 tempThread.join
399 return tempThread
400end
401#sorcery = ExaltedBotSession.new("irc.sorcery.net","6667","ExaltedBot[ruby]")
402
403serverlist = File.open("autoserver.inf", "r")
404serverAr = serverlist.readlines
405serverAr.each_index {|x| process(serverAr[x])}
406serverlist.close