Example #1
0
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"
    )
Example #2
0
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}')
Example #3
0
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)
Example #4
0
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"
    )
Example #5
0
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)
Example #6
0
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)
Example #7
0
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)