class Points(commands.Cog): def __init__(self, bot): self.bot = bot #Initialise instances of the DB and calc auxiliary class self.db = DBHandle() self.calc = Calc() #Define the leaderboard command @bot.command(name="leaderboard", description=formatHelp("leaderboard", "desc"), usage=formatHelp("leaderboard", "usage")) async def leaderboard(ctx): #If the user has used the command recently and is not an admin, given them an error message (hopefully this will prevent HTB's shitty API from blocking us) if not self.db.checkTime( ctx.message.author.id ) and ctx.message.author.id not in config["admins"]: print(type(ctx.message.author.id)) await ctx.channel.send( strings["errors"]["leaderboardCooldown"].format( config["prefix"], config["leaderboard"]["cooldown"] // 60)) return #Log a points update then update all the points log.updateAllPoints(ctx.message.author.id) self.calc.updateAllPoints() #Set up the leaderboard embed then send it embed = discord.Embed(colour=discord.Colour.blue()) embed.title = "Leaderboard" raw = self.db.leaderboard() leaderboard = [] for index, value in enumerate(raw): temp = [index + 1] temp.append(bot.get_user(int(value[0])).name) temp.append(value[1]) leaderboard.append(temp) leaderboard = tabulate(leaderboard, ["Rank", "Username", "Points"], tablefmt="fancy_grid") embed.description = f"```\n{leaderboard}```" await ctx.channel.send(embed=embed) #Get an individual's points. No rate limit on this, but may need to implement one @bot.command(name="points", description=formatHelp("points", "desc"), usage=formatHelp("points", "usage")) async def points(ctx): self.calc.updatePoints(ctx.message.author.id) points = self.db.getPoints(ctx.message.author.id) await ctx.channel.send(strings["points"].format( bot.get_user(ctx.message.author.id).name, points))
class Calc(): def __init__(self): self.db = DBHandle() self.api = Api() #Update points for a single user def updatePoints(self, discordID): db = DBHandle() userHandle = db.getUser(discordID) points = self.getPoints(userHandle["thmUser"], userHandle["thmPoints"], userHandle["htbID"], userHandle["htbPoints"]) if(points != userHandle["points"]): db.updatePoints(discordID, points) #Do the actual calculation def getPoints(self, thmUser, thmPoints, htbID, htbPoints): points = 0 if(thmUser != None): newTHMPoints = self.api.getTHMPoints(thmUser) points += (newTHMPoints - thmPoints) if(htbID != None): newHTBPoints = self.api.getHTBPoints(htbID) if(newHTBPoints != None): points += (newHTBPoints - htbPoints) return points #Simultaneously update all points -- use with caution! def updateAllPoints(self): users = self.db.getUserIDs() if not users: return False with ThreadPool(processes=5) as p: p.map(self.updatePoints, users) return True
def __init__(self, bot): self.bot = bot #Initialises instances of the DB and api auxiliary class self.db = DBHandle() self.api = Api() #Link a THM account @bot.command(name="thmlink", description=formatHelp("thmlink", "desc"), usage=formatHelp("thmlink", "usage")) async def thmlink(ctx, token = None): #Sort out the token to ensure that it's sanitised (and exists) discordID, token = await preprocess(ctx, token) if not discordID: return #Get the user info userHandle = self.db.getUser(discordID) if userHandle["thmUser"] != None: await ctx.channel.send(strings["errors"]["alreadyLinked"]) return #Get requisite variables username = self.api.getTHMProfile(token) points = self.api.getTHMPoints(username) self.db.addTHMUser(discordID, username, points, token) await ctx.channel.send(strings["success"]["linked"]) @bot.command(name="htblink", description=formatHelp("htblink", "desc"), usage=formatHelp("htblink", "usage")) async def htblink(ctx, token = None): #Same as the thmlink command from here on out discordID, token = await preprocess(ctx, token) if not discordID: return #Get user info userHandle = self.db.getUser(discordID) if userHandle["htbID"] != None: await ctx.channel.send(strings["errors"]["alreadyLinked"]) return username, userid = self.api.getHTBProfile(token) points = self.api.getHTBPoints(userid) #If it wasn't possible to retrieve points then the user hasn't set their HTB profile to public. Yes, this is a shitty way to scrape points, but HTB haven't bothered publishing API docs #and I already called in my favours to get the user endpoint ¯\_(ツ)_/¯ if(points == None): await ctx.channel.send(strings["errors"]["profileNotPublic"]) return self.db.addHTBUser(discordID, username, userid, points, token) await ctx.channel.send(strings["success"]["linked"]) ##Helper Funcs def stripToken(token): return re.sub("[\W_]", "", token) async def checkDM(ctx): if not isinstance(ctx.channel, discord.channel.DMChannel): await ctx.message.delete() res = await ctx.channel.send(strings["errors"]["noDM"]) await asyncio.sleep(5) await res.delete() return False return True async def preprocess(ctx, token): if not await checkDM(ctx): return False, False elif not token: await ctx.channel.send(strings["errors"]["noToken"]) return False, False discordID = ctx.message.author.id token = stripToken(token) return discordID, token
def updatePoints(self, discordID): db = DBHandle() userHandle = db.getUser(discordID) points = self.getPoints(userHandle["thmUser"], userHandle["thmPoints"], userHandle["htbID"], userHandle["htbPoints"]) if(points != userHandle["points"]): db.updatePoints(discordID, points)
def __init__(self): self.db = DBHandle() self.api = Api()