def silence(userID, seconds, silenceReason, author=999): """ Silence someone :param userID: user id :param seconds: silence length in seconds :param silenceReason: silence reason shown on website :param author: userID of who silenced the user. Default: 999 :return: """ # db qurey if seconds > 0: glob.db.execute( "INSERT INTO osu_user_banhistory (`user_id`, `reason`, `ban_status`, `period`, `banner_id`) values (%s, %s, 2, %s, %s)", (userID, silenceReason, seconds * 1000, author)) else: banId = glob.db.fetch( "SELECT `ban_id` from osu_user_banhistory WHERE user_id = %s AND ban_status = 2 ORDER BY `timestamp` DESC LIMIT 1", (userID)) if banId is not None: glob.db.execute( "UPDATE osu_user_banhistory SET period = 0 WHERE ban_id = %s LIMIT 1", (banId)) # Log targetUsername = getUsername(userID) # TODO: exists check im drunk rn i need to sleep (stampa piede ubriaco confirmed) if seconds > 0: log.rap( author, "has silenced {} for {} seconds for the following reason: \"{}\"". format(targetUsername, seconds, silenceReason), True) else: log.rap(author, "has removed {}'s silence".format(targetUsername), True)
def silence(userID, seconds, silenceReason, author=999): """ Silence someone :param userID: user id :param seconds: silence length in seconds :param silenceReason: silence reason shown on website :param author: userID of who silenced the user. Default: 999 :return: """ # db qurey silenceEndTime = int(time.time()) + seconds glob.db.execute( "UPDATE users SET silence_end = %s, silence_reason = %s WHERE id = %s LIMIT 1", [silenceEndTime, silenceReason, userID]) # Log targetUsername = getUsername(userID) # TODO: exists check im drunk rn i need to sleep (stampa piede ubriaco confirmed) if seconds > 0: log.rap( author, "has silenced {} for {} seconds for the following reason: \"{}\"". format(targetUsername, seconds, silenceReason), True) else: log.rap(author, "has removed {}'s silence".format(targetUsername), True)
def handleUsernameChange(userID, newUsername, targetToken=None): try: userUtils.appendNotes( userID, "Username change: '{}' -> '{}'".format( userUtils.getUsername(userID), newUsername)) userUtils.changeUsername(userID, newUsername=newUsername) if targetToken != None: targetToken.kick( "Your username has been changed to {}. Please log in again.". format(newUsername), "username_change") except userUtils.usernameAlreadyInUseError: log.rap(999, "Username change: {} is already in use!", through="Bancho") if targetToken != None: targetToken.kick( "There was a critical error while trying to change your username. Please contact a developer.", "username_change_fail") except userUtils.invalidUsernameError: log.rap(999, "Username change: {} is not a valid username!", through="Bancho") if targetToken != None: targetToken.kick( "There was a critical error while trying to change your username. Please contact a developer.", "username_change_fail")
def restrict(fro, chan, message): # Get parameters for i in message: i = i.lower() target = message[0] # Make sure the user exists targetUserID = userUtils.getIDSafe(target) userID = userUtils.getID(fro) if not targetUserID: return "{}: user not found".format(target) # Put this user in restricted mode userUtils.restrict(targetUserID) # Send restricted mode packet to this user if he's online targetToken = glob.tokens.getTokenFromUsername(userUtils.safeUsername(target), safe=True) if targetToken is not None: targetToken.setRestricted() requests.get(glob.conf.config["discord"]["krbot"] + "api/v1/submitBanOrRestrict", params={ 'token': glob.conf.config["discord"]["krbotToken"], 'banned': target, 'type': 0, 'author': fro }) log.rap(userID, "has put {} in restricted mode".format(target), True) return "Bye bye {}. See you later, maybe.".format(target)
def ban(fro, chan, message): # Get parameters for i in message: i = i.lower() target = message[0] # Make sure the user exists targetUserID = userUtils.getIDSafe(target) userID = userUtils.getID(fro) if not targetUserID: return "{}: user not found".format(target) # Set allowed to 0 userUtils.ban(targetUserID) # Send ban packet to the user if he's online targetToken = glob.tokens.getTokenFromUsername(userUtils.safeUsername(target), safe=True) if targetToken is not None: targetToken.enqueue(serverPackets.loginBanned()) # Posting to discord requests.get(glob.conf.config["discord"]["krbot"] + "api/v1/submitBanOrRestrict", params={ 'token': glob.conf.config["discord"]["krbotToken"], 'banned': target, 'type': 1, 'author': fro }) log.rap(userID, "has banned {}".format(target), True) return "RIP {}. You will not be missed.".format(target)
def ban(fro, chan, message): # Get parameters for i in message: i = i.lower() target = message[0] reason = ' '.join(message[1:]) # Make sure the user exists targetUserID = userUtils.getIDSafe(target) userID = userUtils.getID(fro) if not targetUserID: return "{}: user not found".format(target) if targetUserID < 1002 and userID > 1002: return "Nice try." if not reason: return "Please specify a reason for the ban." # Set allowed to 0 userUtils.ban(targetUserID) # Send ban packet to the user if he's online targetToken = glob.tokens.getTokenFromUsername(userUtils.safeUsername(target), safe=True) if targetToken is not None: targetToken.enqueue(serverPackets.loginBanned()) log.rap(userID, "has banned {}".format(target), True) return "RIP {}. You will not be missed.".format(target)
def restrict(fro, chan, message): # Get parameters for i in message: i = i.lower() target = message[0] reason = ' '.join(message[1:]) # Make sure the user exists targetUserID = userUtils.getIDSafe(target) username = chat.fixUsernameForBancho(fro) userID = userUtils.getID(fro) if not targetUserID: return "{}: user not found".format(target) if targetUserID < 1002 and userID > 1002: log.enjuu("{} attempted to restrict immortal user {}.".format(username, targetUserID), discord="cm") return "Nice try." if not reason: reason = "not avialable" # Put this user in restricted mode userUtils.restrict(targetUserID) # Send restricted mode packet to this user if he's online targetToken = glob.tokens.getTokenFromUsername(userUtils.safeUsername(target), safe=True) if targetToken is not None: targetToken.setRestricted() log.rap(userID, "has restricted {} ({}) for: {}".format(target, targetUserID, reason), True) hook = Webhook(glob.conf.config["discord"]["webhook"]) embed = Embed( description='', color=0x1e0f3, timestamp='now' # sets the timestamp to current time ) avatar = "https://a.enjuu.click/" embed.set_author(name='Restriction', icon_url="https://a.enjuu.click/{}".format(targetUserID)) embed.add_field(name='Username', value="{}".format(target)) embed.add_field(name='Reason', value="{}".format(reason)) embed.set_footer(text="Restricted by {}".format(username)) embed.set_thumbnail("https://a.enjuu.click/{}".format(targetUserID)) hook.send(embed=embed) return "{} has been restricted for {}".format(target, reason)
def unrestrict(fro, chan, message): # Get parameters for i in message: i = i.lower() target = message[0] # Make sure the user exists targetUserID = userUtils.getIDSafe(target) userID = userUtils.getID(fro) if not targetUserID: return "{}: user not found".format(target) # Set allowed to 1 userUtils.unrestrict(targetUserID) log.rap(userID, "has removed restricted mode from {}".format(target), True) return "Welcome back {}!".format(target)
def setUserFlags(userID, flags, flags_reason="", author=999): """ Silence someone :param userID: user id :param flags: time until restriction in seconds :param flags_reason: reason shown on website :return: """ # db qurey flagsEndTime = int(time.time())+flags if flags > 0 else 0 glob.db.execute("UPDATE users SET flags = %s, flags_reason = %s WHERE id = %s LIMIT 1", [flagsEndTime, flags_reason, userID]) # Log targetUsername = getUsername(userID) # TODO: exists check im drunk rn i need to sleep (stampa piede ubriaco confirmed) if flags > 0: log.rap(author, "has scheduled a restriction on user: {} in {}. Reason: {}".format(targetUsername, flags, flags_reason), True) else: log.rap(author, "has cancelled {}'s scheduled restriction.".format(targetUsername), True)
def restrict(fro, chan, message): # Get parameters for i in message: i = i.lower() target = message[0] # Make sure the user exists targetUserID = userUtils.getIDSafe(target) userID = userUtils.getID(fro) if not targetUserID: return "{}: user not found".format(target) # Put this user in restricted mode userUtils.restrict(targetUserID) # Send restricted mode packet to this user if he's online targetToken = glob.tokens.getTokenFromUsername(userUtils.safeUsername(target), safe=True) if targetToken is not None: targetToken.setRestricted() log.rap(userID, "has put {} in restricted mode".format(target), True) return "Bye bye {}. See you later, maybe.".format(target)
def unrestrict(fro, chan, message): # Get parameters for i in message: i = i.lower() target = message[0] # Make sure the user exists targetUserID = userUtils.getIDSafe(target) userID = userUtils.getID(fro) if not targetUserID: return "{}: user not found".format(target) # Set allowed to 1 userUtils.unrestrict(targetUserID) requests.get(glob.conf.config["discord"]["krbot"] + "api/v1/submitBanOrRestrict", params={ 'token': glob.conf.config["discord"]["krbotToken"], 'banned': target, 'type': 2, 'author': fro }) log.rap(userID, "has removed restricted mode from {}".format(target), True) return "Welcome back {}!".format(target)
def handle(tornadoRequest): # Data to return responseToken = None responseTokenString = "ayy" responseData = bytes() # Get IP from tornado request requestIP = tornadoRequest.getRequestIP() # Avoid exceptions clientData = ["unknown", "unknown", "unknown", "unknown", "unknown"] osuVersion = "unknown" # Split POST body so we can get username/password/hardware data # 2:-3 thing is because requestData has some escape stuff that we don't need loginData = str(tornadoRequest.request.body)[2:-3].split("\\n") try: # Make sure loginData is valid if len(loginData) < 3: raise exceptions.invalidArgumentsException() # Get HWID, MAC address and more # Structure (new line = "|", already split) # [0] osu! version # [1] plain mac addressed, separated by "." # [2] mac addresses hash set # [3] unique ID # [4] disk ID splitData = loginData[2].split("|") osuVersion = splitData[0] timeOffset = int(splitData[1]) clientData = splitData[3].split(":")[:5] if len(clientData) < 4: raise exceptions.forceUpdateException() # Try to get the ID from username username = str(loginData[0]) userID = userUtils.getID(username) if not userID: # Invalid username raise exceptions.loginFailedException() if not userUtils.checkLogin(userID, loginData[1]): # Invalid password raise exceptions.loginFailedException() # Make sure we are not banned or locked priv = userUtils.getPrivileges(userID) if userUtils.isBanned(userID) and priv & privileges.USER_PENDING_VERIFICATION == 0: raise exceptions.loginBannedException() if userUtils.isLocked(userID) and priv & privileges.USER_PENDING_VERIFICATION == 0: raise exceptions.loginLockedException() # 2FA check if userUtils.check2FA(userID, requestIP): log.warning("Need 2FA check for user {}.".format(loginData[0])) raise exceptions.need2FAException() # No login errors! # Verify this user (if pending activation) firstLogin = False if priv & privileges.USER_PENDING_VERIFICATION > 0 or not userUtils.hasVerifiedHardware(userID): if userUtils.verifyUser(userID, clientData): # Valid account log.info("Account {} verified successfully!".format(userID)) glob.verifiedCache[str(userID)] = 1 firstLogin = True else: # Multiaccount detected log.info("Account {} NOT verified!".format(userID)) glob.verifiedCache[str(userID)] = 0 raise exceptions.loginBannedException() # Save HWID in db for multiaccount detection hwAllowed = userUtils.logHardware(userID, clientData, firstLogin) # This is false only if HWID is empty # if HWID is banned, we get restricted so there's no # need to deny bancho access if not hwAllowed: raise exceptions.haxException() # Log user IP userUtils.logIP(userID, requestIP) # Delete old tokens for that user and generate a new one isTournament = "tourney" in osuVersion if not isTournament: glob.tokens.deleteOldTokens(userID) responseToken = glob.tokens.addToken(userID, requestIP, timeOffset=timeOffset, tournament=isTournament) responseTokenString = responseToken.token # Check restricted mode (and eventually send message) responseToken.checkRestricted() userFlags = userUtils.getUserFlags(userID) if userFlags > 0: # Pending public bans. Such as chargebacks, etc. flagReason = userUtils.getFlagReason(userID) if userFlags-int(time.time()) < 0: responseToken.enqueue(serverPackets.notification("Your account has been automatically restricted due to a pending restriction not being having been dealt with.\n\nReason: {}".format(flagReason))) userUtils.restrict(userID) userUtils.setUserFlags(userID, 0) log.cmyui("{} has been automatically restricted due to not dealing with pending restriction. Reason: {}.".format(username, flagReason), discord="cm") log.rap(userID, "has been restricted due to a pending restriction. Reason: {}.".format(flagReason)) else: if "charge" in flagReason: responseToken.enqueue(serverPackets.notification("Your account has been flagged with an automatic restriction.\n\nIt will occur at {time} if not dealt with.\n" "Reason: {reason}\n\nTo avoid being restricted for this behaviour, you can cancel or revert your chargeback before your restriction date.".format(time=datetime.utcfromtimestamp(int(userFlags)).strftime('%Y-%m-%d %H:%M:%S'), reason=flagReason))) elif "live" in flagReason: responseToken.enqueue(serverPackets.notification("Your account has been flagged with an automatic restriction.\n\nIt will occur at {time} if not dealt with.\n" "Reason: {reason}\n\nThis means you are required to submit a liveplay to avoid this. This only happens in cases when we are confident in foul play; and are offering you this opportunity as a final stance to prove your legitimacy, against all the odds.".format(time=datetime.utcfromtimestamp(int(userFlags)).strftime('%Y-%m-%d %H:%M:%S'), reason=flagReason))) else: responseToken.enqueue(serverPackets.notification("Your account has been flagged with an automatic restriction.\n\nIt will occur at {time} if not dealt with.\n" "Reason: {reason}\n\nYou have until the restriction to deal with the issue.".format(time=datetime.utcfromtimestamp(int(userFlags)).strftime('%Y-%m-%d %H:%M:%S'), reason=flagReason))) # Send message if premium / donor expires soon # ok spaghetti code time if responseToken.privileges & privileges.USER_DONOR: donorType = 'premium' if responseToken.privileges & privileges.USER_PREMIUM else 'donor' expireDate = userUtils.getDonorExpire(responseToken.userID) if expireDate-int(time.time()) < 0: userUtils.setPrivileges(userID, 3) log.cmyui("{}'s donation perks have been removed as their time has run out.".format(username), discord="cm") log.rap(userID, "User's donor perks have been removed as their time has run out.") responseToken.enqueue(serverPackets.notification("Your {donorType} tag has expired! Thank you so much for the support, it really means everything to us. If you wish to keep supporting Akatsuki and you don't want to lose your {donorType} privileges, you can donate again by clicking on 'Support us' on Akatsuki's website.".format(donorType=donorType))) elif expireDate-int(time.time()) <= 86400*3: expireDays = round((expireDate-int(time.time()))/86400) expireIn = "{} days".format(expireDays) if expireDays > 1 else "less than 24 hours" responseToken.enqueue(serverPackets.notification("Your {donorType} tag expires in {expireIn}! When your {donorType} tag expires, you won't have any of the {donorType} privileges, like yellow username, custom badge and discord custom role and username color! If you wish to keep supporting Akatsuki and you don't want to lose your {donorType} privileges, you can donate again by clicking on 'Support us' on Akatsuki's website.".format(donorType=donorType, expireIn=expireIn))) """ Akatsuki does not use 2fa! we suck! if userUtils.deprecateTelegram2Fa(userID): responseToken.enqueue(serverPackets.notification("As stated on our blog, Telegram 2FA has been deprecated on 29th June 2018. Telegram 2FA has just been disabled from your account. If you want to keep your account secure with 2FA, please enable TOTP-based 2FA from our website https://akatsuki.pw. Thank you for your patience.")) """ # Set silence end UNIX time in token responseToken.silenceEndTime = userUtils.getSilenceEnd(userID) # Get only silence remaining seconds silenceSeconds = responseToken.getSilenceSecondsLeft() # Get supporter/GMT userGMT = False userSupporter = True userTournament = False if responseToken.admin: userGMT = True if responseToken.privileges & privileges.USER_TOURNAMENT_STAFF > 0: userTournament = True # Server restarting check if glob.restarting: raise exceptions.banchoRestartingException() # Send login notification before maintenance message if glob.banchoConf.config["loginNotification"] != "": responseToken.enqueue(serverPackets.notification(glob.banchoConf.config["loginNotification"])) # Maintenance check if glob.banchoConf.config["banchoMaintenance"]: if not userGMT: # We are not mod/admin, delete token, send notification and logout glob.tokens.deleteToken(responseTokenString) raise exceptions.banchoMaintenanceException() else: # We are mod/admin, send warning notification and continue responseToken.enqueue(serverPackets.notification("Akatsuki is currently in maintenance mode. Only admins have full access to the server.\nType '!system maintenance off' in chat to turn off maintenance mode.")) # Send all needed login packets responseToken.enqueue(serverPackets.silenceEndTime(silenceSeconds)) responseToken.enqueue(serverPackets.userID(userID)) responseToken.enqueue(serverPackets.protocolVersion()) responseToken.enqueue(serverPackets.userSupporterGMT(userSupporter, userGMT, userTournament)) responseToken.enqueue(serverPackets.userPanel(userID, True)) responseToken.enqueue(serverPackets.userStats(userID, True)) # Channel info end (before starting!?! wtf bancho?) responseToken.enqueue(serverPackets.channelInfoEnd()) # Default opened channels # TODO: Configurable default channels chat.joinChannel(token=responseToken, channel="#osu") chat.joinChannel(token=responseToken, channel="#announce") #Akatsuki extra channels chat.joinChannel(token=responseToken, channel="#nowranked") chat.joinChannel(token=responseToken, channel="#request") # Join admin channel if we are an admin if responseToken.admin or responseToken.privileges & privileges.USER_PREMIUM: chat.joinChannel(token=responseToken, channel="#admin") # Output channels info for key, value in glob.channels.channels.items(): if value.publicRead and not value.hidden: responseToken.enqueue(serverPackets.channelInfo(key)) # Send friends list responseToken.enqueue(serverPackets.friendList(userID)) # Send main menu icon if glob.banchoConf.config["menuIcon"] != "": responseToken.enqueue(serverPackets.mainMenuIcon(glob.banchoConf.config["menuIcon"])) # Send online users' panels with glob.tokens: for _, token in glob.tokens.tokens.items(): if not token.restricted: responseToken.enqueue(serverPackets.userPanel(token.userID)) # Get location and country from ip.zxq.co or database. If the user is a donor, then yee if glob.localize and (firstLogin == True or responseToken.privileges & privileges.USER_DONOR <= 0): # Get location and country from IP latitude, longitude = locationHelper.getLocation(requestIP) countryLetters = locationHelper.getCountry(requestIP) country = countryHelper.getCountryID(countryLetters) else: # Set location to 0,0 and get country from db log.warning("Location skipped") latitude = 0 longitude = 0 countryLetters = "XX" country = countryHelper.getCountryID(userUtils.getCountry(userID)) # Set location and country responseToken.setLocation(latitude, longitude) responseToken.country = country # Set country in db if user has no country (first bancho login) if userUtils.getCountry(userID) == "XX": userUtils.setCountry(userID, countryLetters) # Send to everyone our userpanel if we are not restricted or tournament if not responseToken.restricted: glob.streams.broadcast("main", serverPackets.userPanel(userID)) # Set reponse data to right value and reset our queue responseData = responseToken.queue responseToken.resetQueue() except exceptions.loginFailedException: # Login failed error packet # (we don't use enqueue because we don't have a token since login has failed) responseData += serverPackets.loginFailed() except exceptions.invalidArgumentsException: # Invalid POST data # (we don't use enqueue because we don't have a token since login has failed) responseData += serverPackets.loginFailed() responseData += serverPackets.notification("We see what you're doing..") log.cmyui("User {} has triggered invalidArgumentsException in loginEvent.py".format(userID), discord="cm") except exceptions.loginBannedException: # Login banned error packet responseData += serverPackets.loginBanned() except exceptions.loginLockedException: # Login banned error packet responseData += serverPackets.loginLocked() except exceptions.banchoMaintenanceException: # Bancho is in maintenance mode responseData = bytes() if responseToken is not None: responseData = responseToken.queue responseData += serverPackets.notification("Akatsuki is currently in maintenance mode. Please try to login again later.") responseData += serverPackets.loginFailed() except exceptions.banchoRestartingException: # Bancho is restarting responseData += serverPackets.notification("Akatsuki is restarting. Try again in a few minutes.") responseData += serverPackets.loginFailed() except exceptions.need2FAException: # User tried to log in from unknown IP responseData += serverPackets.needVerification() except exceptions.haxException: # Using oldoldold client, we don't have client data. Force update. # (we don't use enqueue because we don't have a token since login has failed) responseData += serverPackets.forceUpdate() responseData += serverPackets.notification("Custom clients of ANY kind are NOT PERMITTED on Akatsuki. Please login using the current osu! client.") log.cmyui("User {} has logged in with a VERY old client".format(userID), discord="cm") except: log.error("Unknown error!\n```\n{}\n{}```".format(sys.exc_info(), traceback.format_exc())) finally: # Console and discord log if len(loginData) < 3: log.info("Invalid bancho login request from **{}** (insufficient POST data)".format(requestIP), "bunker") # Return token string and data return responseTokenString, responseData
def edit_map(fro, chan, message): # Edit maps ranking status ingame. // Added by cmyui :) // cmyui why u dont like PEP8? messages = [m.lower() for m in message] rank_type = message[0] map_type = message[1] map_id = message[2] # Get persons username & ID user_id = userUtils.getID(fro) name = userUtils.getUsername(user_id) typeBM = None # Figure out what to do if rank_type == 'rank': rank_typed_str = 'ranke' rank_type_id = 2 freeze_status = 1 elif rank_type == 'love': rank_typed_str = 'love' rank_type_id = 5 freeze_status = 1 elif rank_type == 'unrank': rank_typed_str = 'unranke' rank_type_id = 0 freeze_status = 0 # Grab beatmap_data from db beatmap_data = glob.db.fetch("SELECT * FROM beatmaps WHERE beatmap_id = {} LIMIT 1".format(map_id)) if map_type == 'set': glob.db.execute( "UPDATE beatmaps SET ranked = {}, ranked_status_freezed = {} WHERE beatmapset_id = {} LIMIT 100".format( rank_type_id, freeze_status, beatmap_data["beatmapset_id"])) if freeze_status == 1: glob.db.execute("""UPDATE scores s JOIN (SELECT userid, MAX(score) maxscore FROM scores JOIN beatmaps ON scores.beatmap_md5 = beatmaps.beatmap_md5 WHERE beatmaps.beatmap_md5 = (SELECT beatmap_md5 FROM beatmaps WHERE beatmapset_id = {} LIMIT 1) GROUP BY userid) s2 ON s.score = s2.maxscore AND s.userid = s2.userid SET completed = 3""".format( beatmap_data["beatmapset_id"])) typeBM = 'set' elif map_type == 'map': glob.db.execute( "UPDATE beatmaps SET ranked = {}, ranked_status_freezed = {} WHERE beatmap_id = {} LIMIT 1".format( rank_type_id, freeze_status, map_id)) if freeze_status == 1: glob.db.execute("""UPDATE scores s JOIN (SELECT userid, MAX(score) maxscore FROM scores JOIN beatmaps ON scores.beatmap_md5 = beatmaps.beatmap_md5 WHERE beatmaps.beatmap_md5 = (SELECT beatmap_md5 FROM beatmaps WHERE beatmap_id = {} LIMIT 1) GROUP BY userid) s2 ON s.score = s2.maxscore AND s.userid = s2.userid SET completed = 3""".format( beatmap_data["beatmap_id"])) typeBM = 'beatmap' else: return "Please specify whether it is a set/map. eg: '!map unrank/rank/love set/map 123456'" # Announce / Log to AP logs when ranked status is changed if rank_type == "love": log.rap(user_id, "has {}d beatmap ({}): {} ({}).".format(rank_type, map_type, beatmap_data["song_name"], map_id), True) if map_type == 'set': msg = "{} has loved beatmap set: [https://osu.ppy.sh/s/{} {}]".format(name, beatmap_data["beatmapset_id"], beatmap_data["song_name"]) else: msg = "{} has loved beatmap: [https://osu.ppy.sh/s/{} {}]".format(name, map_id, beatmap_data["song_name"]) glob.db.execute( "UPDATE scores s JOIN (SELECT userid, MAX(score) maxscore FROM scores JOIN beatmaps ON scores.beatmap_md5 = beatmaps.beatmap_md5 WHERE beatmaps.beatmap_md5 = (SELECT beatmap_md5 FROM beatmaps WHERE beatmap_id = {} LIMIT 1) GROUP BY userid) s2 ON s.score = s2.maxscore AND s.userid = s2.userid SET completed = 2".format( beatmap_data["beatmap_id"])) elif rank_type == "rank": log.rap(user_id, "has {}ed beatmap ({}): {} ({}).".format(rank_type, map_type, beatmap_data["song_name"], map_id), True) if map_type == 'set': msg = "{} has {}ed beatmap set: [https://osu.ppy.sh/s/{} {}]".format(name, rank_type, beatmap_data["beatmapset_id"], beatmap_data["song_name"]) else: msg = "{} has {}ed beatmap: [https://osu.ppy.sh/s/{} {}]".format(name, rank_type, map_id, beatmap_data["song_name"]) glob.db.execute( "UPDATE scores s JOIN (SELECT userid, MAX(score) maxscore FROM scores JOIN beatmaps ON scores.beatmap_md5 = beatmaps.beatmap_md5 WHERE beatmaps.beatmap_md5 = (SELECT beatmap_md5 FROM beatmaps WHERE beatmap_id = {} LIMIT 1) GROUP BY userid) s2 ON s.score = s2.maxscore AND s.userid = s2.userid SET completed = 2".format( beatmap_data["beatmap_id"])) else: log.rap(user_id, "has {}ed beatmap ({}): {} ({}).".format(rank_type, map_type, beatmap_data["song_name"], map_id), True) if map_type == 'set': msg = "{} has {}ed beatmap set: [https://osu.ppy.sh/s/{} {}]".format(name, rank_type, beatmap_data["beatmapset_id"], beatmap_data["song_name"]) else: msg = "{} has {}ed beatmap: [https://osu.ppy.sh/s/{} {}]".format(name, rank_type, map_id, beatmap_data["song_name"]) glob.db.execute( "UPDATE scores s JOIN (SELECT userid, MAX(score) maxscore FROM scores JOIN beatmaps ON scores.beatmap_md5 = beatmaps.beatmap_md5 WHERE beatmaps.beatmap_md5 = (SELECT beatmap_md5 FROM beatmaps WHERE beatmap_id = {} LIMIT 1) GROUP BY userid) s2 ON s.score = s2.maxscore AND s.userid = s2.userid SET completed = 2".format( beatmap_data["beatmap_id"])) need_params = { 'token': glob.conf.config["discord"]["krbotToken"], 'poster': fro, 'type': rank_typed_str } if typeBM == 'set': need_params['sid'] = beatmap_data["beatmapset_id"] else: need_params['bid'] = beatmap_data["beatmap_id"] requests.get(glob.conf.config["discord"]["krbot"] + "api/v1/submitMap", params=need_params) chat.sendMessage(glob.BOT_NAME, "#nowranked", msg) return msg