async def byoc_stats(ctx): if await isRegistered(ctx) == False: return if await inPublicChannel( ctx, msg= f"Hey, <@{ctx.author.id}>, don't submit a challenge in public channels..." ): return msg = '' with db.db_session: user = db.User.get(name=username(ctx)) team_challs = list( db.select(c for c in db.Challenge if c.author in db.getTeammates(user))) # num solves per challenge stats = [] for chall in team_challs: num_solves = list( db.select(s for s in db.Solve if s.challenge == chall)) chall_rewards = sum( db.select( sum(t.value) for t in db.Transaction if t.type == "byoc reward" and t.recipient in db.getTeammates(user) and t.challenge == chall).without_distinct()) line = [ chall.id, chall.title, len(num_solves), chall.author.name, chall_rewards ] stats.append(line) stats.insert(0, ['Chall ID', 'Title', '# Solves', 'Author', 'Payout']) table = GithubFlavoredMarkdownTable(stats) # team total byoc rewards sum total_byoc_rewards = sum( db.select( sum(t.value) for t in db.Transaction if t.type == "byoc reward" and t.recipient in db.getTeammates(user))) await ctx.send( f"Your stats ```{table.table}```\n**Team Total BYOC Rewards:** `{total_byoc_rewards}` points" )
async def show_hints(ctx): if await isRegistered(ctx) == False: return if await inPublicChannel( ctx, msg= f"Hey, <@{ctx.author.id}>, don't show your hints in public channels..." ): return if ctfRunning() == False: await ctx.send("CTF isn't running yet") return with db.db_session: user = db.User.get(name=username(ctx)) hint_transactions = db.getHintTransactions(user) msg = f"Team {user.team.name}'s hints:\n" data = [] teammates = db.getTeammates( user) # throws an error about db session is over for tm in teammates: tm_hints = db.getHintTransactions(tm) data += [(ht.hint.challenge.id, ht.hint.text, ht.hint.cost, ht.sender.name) for ht in tm_hints] data.insert(0, ['Chall_ID', 'Hint', 'Cost', 'Purchaser']) table = GithubFlavoredMarkdownTable(data) await sendBigMessage(ctx, f'{msg}{table.table}')
async def list_unsolved(ctx): if await isRegistered(ctx) == False: return if await inPublicChannel( ctx, msg= f"Hey, <@{ctx.author.id}>, don't view challenges in public channels..." ): return if ctfRunning() == False: await ctx.send("CTF isn't running yet") return with db.db_session: user = db.User.get(name=username(ctx)) challs = db.get_unsolved_challenges(user) # logger.debug(challs) res = [] for c in challs: if c.author not in db.getTeammates( user ): # you can't solve your teammates challenges, so don't show them. res.append([c.id, c.author.name, c.title]) res.insert(0, ['ID', "Author", "Title"]) table = GithubFlavoredMarkdownTable(res) # logger.debug("discord",challs)\ msg = f'Showing all unsolved challenges```{table.table}```' await ctx.send(msg)
async def byoc_stats(ctx): if await isRegistered(ctx) == False: return if await inPublicChannel( ctx, msg=f"<@{ctx.author.id}>, dm this command to CTFBot"): return msg = '' with db.db_session: user = db.User.get(name=username(ctx)) teammates = db.getTeammates(user) await ctx.send( f"AuthorID: <@{ctx.author.id}>\nUserName: {user.name},\nTeamName: {user.team.name}\n" )
async def solves(ctx): if await isRegistered(ctx) == False: return if await inPublicChannel( ctx, msg= f"Hey, <@{ctx.author.id}>, don't show your flags in public channels..." ): return with db.db_session: user = db.User.get(name=username(ctx)) msg = f"`{user.team.name}`'s solves " teammates = db.getTeammates(user) solved = [] for teammate in teammates: solved += list( db.select(solve for solve in db.Solve if teammate.name == solve.user.name)) res = [] for solve in solved: line = (solve.flag_text, solve.challenge.id, solve.challenge.title, solve.user.name, solve.time) res.append(line) res.insert(0, ["Flag", "Chall ID", "Chall Title", "User", "Solve Time"]) table = GithubFlavoredMarkdownTable(res) if len(msg + table.table) >= 2000: await sendBigMessage(ctx, f'{msg}\n{table.table}') else: msg += f"```{table.table}```" await ctx.send(msg)
async def list_all(ctx, *, tags=None): if await isRegistered(ctx) == False: return if await inPublicChannel( ctx, msg= f"Hey, <@{ctx.author.id}>, don't view challenges in public channels..." ): return if ctfRunning() == False: await ctx.send("CTF isn't running yet") return with db.db_session: user = db.User.get(name=username(ctx)) challs = db.get_all_challenges(user) # It'd be nice to show a percentage complete as well... # # don't show teammates challenges or your own challenges. !bstat to see yours. helps prevent a teammate working on your challenges when they couldn't submit it anyway. res = [] if tags == None: res = [(c.id, c.author.name, c.title, db.challValue(c), f"{db.percentComplete(c, user)}%", "*" * int( db.avg(r.value for r in db.Rating if r.challenge == c) or 0), ', '.join([t.name for t in c.tags])) for c in challs if c.id > 0 and c.author not in db.getTeammates(user)] else: tags = tags.split(' ') includes = [x for x in tags if x.startswith('!') == False] excludes = [x[1:] for x in tags if x.startswith('!')] if SETTINGS['_debug'] and SETTINGS['_debug_level'] >= 1: logger.debug( f'tags: {tags}; filter including {includes}; excluding {excludes}' ) for chall in challs: chall_tags = [t.name for t in chall.tags] if anyIn( excludes, chall_tags ): # kick it back if any of the excludes are in the chall_tags continue if len(includes) > 0 and anyIn( includes, chall_tags ) == False: # if it doesn't have any of the includes, skip it. continue if chall.id < 1 or chall.author in db.getTeammates( user): # other reasons to skip this challenge... continue res += [[ chall.id, chall.author.name, chall.title, db.challValue(chall), f"{db.percentComplete(chall, user)}%", "*" * int( db.avg(r.value for r in db.Rating if r.challenge == chall) or 0), ', '.join(chall_tags) ]] res.insert(0, ['ID', "Author", "Title", "Value", "Done", "Rating", "Tags"]) table = GithubFlavoredMarkdownTable(res) # logger.debug("discord",challs) msg = f'Showing all unlocked challenges```{table.table}```' await ctx.send(msg)
async def submit(ctx: discord.ext.commands.Context, submitted_flag: str = None): if await isRegistered(ctx) == False: return if await inPublicChannel( ctx, msg= f"Hey, <@{ctx.author.id}>, don't submit flags in public channels..." ): return if ctfRunning() == False: await ctx.send("CTF isn't running yet") return if SETTINGS['_debug'] == True and SETTINGS['_debug_level'] == 1: logger.debug( f"{username(ctx)} is attempting to submit '{submitted_flag}'") with db.db_session: # is this a valid flag flag = db.Flag.get(flag=submitted_flag) user = db.User.get(name=username(ctx)) if flag == None: msg = f'incorrect: we got "{submitted_flag}"' logger.debug(msg) await ctx.send(msg) return # have I already submitted this flag? solves = list( db.select(solve for solve in db.Solve if submitted_flag == solve.flag.flag and username(ctx) == solve.user.name)) # should be an empty list if len(solves) > 0: # if prev_solve != None: msg = f"You've already submitted `{submitted_flag}` at {solves[0].time} " logger.debug(msg) await ctx.send(msg) return # has a teammate submitted this flag? teammates = db.getTeammates(user) solved = [] for teammate in teammates: res = list( db.select(solve for solve in db.Solve if submitted_flag == solve.flag.flag and teammate.name == solve.user.name) ) # see above regarding simpler looking query if len(res) > 0: # already submitted by a teammate msg = f"{res[0].user.name} already submitted `{submitted_flag}` at {res[0].time} " if SETTINGS['_debug'] == True and SETTINGS['_debug_level'] == 1: logger.debug(msg) await ctx.send(msg) return # did this user author the flag? if flag.author.name == user.name: await ctx.send( "You can't submit a flag from your own challenges...") return # did someone else on their team author this flag? for teammate in teammates: if flag.author.name == teammate.name: await ctx.send( "You can't submit a flag created by someone on your own team..." ) return # if I get this far, it has not been solved by any of my teammates # is this challenge unlocked? # get parent challenges. flag_challs = list(flag.challenges) for chall in flag_challs: for p_chall in list(chall.parent): print(p_chall.title, p_chall.id) # is the parent complete? # if db.challegeUnlocked(user, chall) == False: if db.challengeComplete(p_chall, user) == False: await ctx.send( f"This challenge is not unlocked yet... good job? Look at `{p_chall.title}` and complete it then try again?" ) return msg = "Correct!\n" reward = flag.value challenge = db.select(c for c in db.Challenge if flag in c.flags).first() if challenge: # was this flag part of a challenge? msg += f'You submitted a flag for challenge `{challenge.title}`.\n' if flag.unsolved == True: ctf_chan = bot.get_channel(SETTINGS["_ctf_channel_id"]) logger.debug(user.name) discord_user = await getDiscordUser(ctx, user.name) await ctf_chan.send(f"<@{discord_user.id}> drew First Blood!") msg += f'**First blood!** \nYou are the first to submit `{flag.flag}` and have earned a bonus {SETTINGS["_firstblood_rate"] * 100 }% \nTotal reward `{flag.value * (1 + SETTINGS["_firstblood_rate"])}` rather than `{flag.value}`\n' elif SETTINGS['_decay_solves'] == True: solve_count = db.count( db.select(t for t in db.Transaction if t.flag == flag).without_distinct()) team_count = db.count(db.select( t for t in db.Team)) - 1 # don't count discordbot's team solve_percent = solve_count / team_count reward *= max([1 - solve_percent, SETTINGS['_decay_minimum'] ]) # don't go below the minimum established if SETTINGS['_debug'] == True: logger.debug( f'decay solves {solve_count} team count{team_count} ; solve percent {solve_percent} reward is {reward}' ) msg += f'{solve_count} teams have solved this challenge... Your reward is reduced based on that fact... reward is {reward} rather than {flag.value}\n' # firstblood and decay points/award/reductions logic is now in create solve. above is for display only db.createSolve(user=user, flag=flag, challenge=challenge, msg='\n'.join([c.title for c in flag.challenges])) msg += f'Challenge is {db.percentComplete(challenge, user)}% complete.\n' msg += f'Your score is now `{db.getScore(user)}`' logger.debug(msg) await ctx.send(msg)