# DataBase handler module module Players def self.list DB["select * from Players"].all end def self.rm_player(username) DB["delete from Players where username = ?", username].delete != 0 end def self.mk_player(username, email, pass) # rubocop:disable Layout/LineLength raise ArgumentError, "Email format is wrong!" unless email.match?(%r[(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])]) # rubocop:enable Layout/LineLength raise ArgumentError, "Username must be at least 4 characters long and of valid format." unless username.match?(/\A[a-zA-Z][a-zA-Z0-9_.-]+\z/) && username.length >= 4 raise ArgumentError, "Password must be at least 8 characters and of valid format." unless pass.match?(/\A[a-zA-Z0-9_.!?@#$%^&*()+=-]+\z/) && pass.length >= 8 digest = XXhash.xxh32(pass, ENV_HASH["SALT"]) code = ENV_HASH["ENV"] == "prod" ? Array.new(24) { ALPHANUM.sample }.join : "!" DB[ "insert into Players (username, digest, email, activation_code) values (?, ?, ?, ?)", username, digest, email, code ].insert send_email(:new, email, username, code) if ENV_HASH["ENV"] == "prod" [200, "Successfully signed up!"] rescue ArgumentError => e [400, e.message] rescue Sequel::UniqueConstraintViolation [400, "Account already exists with this email or username!"] end def self.verify(code) DB["update Players set activation_code = ? where activation_code = ?", "!", code].update != 0 end def self.unverified DB["select * from Players where activation_code <> ?", "!"].all end def self.pass_req(email) return false unless by_email(email) code = Array.new(24) { ALPHANUM.sample }.join DB["update Players set new_pass_code = ? where email = ?", code, email].update Logman.log "Pass req: #{email} & #{code}" true # send_email(:pass_req, email, code) end def self.pass_reset(new_pass, code) digest = XXhash.xxh32(new_pass, ENV_HASH["SALT"]) DB["update Players set digest = ?, new_pass_code = ? where new_pass_code = ?", digest, "", code].update != 0 end def self.pass_reset?(code) DB["select * from Players where new_pass_code = ?", code].first end def self.[](username) DB["select * from Players where username = ?", username].first end def self.by_email(email) DB["select * from Players where email = ?", email].first end def self.[]=(username, data) DB["update Players set data = ? where username = ?", data, username].update end def self.authorized?(username, pass) digest = XXhash.xxh32(pass, ENV_HASH["SALT"]) Logman.log "Authorized: #{username} & #{digest}" player = self[username] player && player[:digest].to_i == digest.to_i ? player : false end def self.verified?(username) player = self[username] Logman.log "Verified: #{player.inspect}\n" player && player[:activation_code] == "!" end Thread.new do loop do unverified.each do |player| rm_player(player[:username]) if player[:created_at] + 24 * 60 * 60 < Time.now end rescue StandardError => e Logman.log "Thread error: #{e.message}" ensure sleep 60 * 60 end end end