Example #1
0
async def gotgud(ctx):
    query = Query()
    gitgud_util = Gitgud_utils()
    username = query.get_handle(ctx.author.id, ctx.get_guild().id)

    if username is None:
        return await ctx.respond("You are not linked with a DMOJ Account")

    user = await query.get_user(username)
    current = gitgud_util.get_current(username, ctx.get_guild().id)
    closest = -1000
    for key in RATING_TO_POINT:
        if abs(key - user.rating) <= abs(closest - user.rating):
            closest = key

    # convert rating to point and get difference
    rating_point = RATING_TO_POINT[closest]
    if current is None or current.problem_id is None:
        return await ctx.respond("No pending challenges")

    # check if user is scamming the bot :monkey:
    if gitgud_util.has_solved(username, current.problem_id):
        # get closest rating
        closest = -1000
        for key in RATING_TO_POINT:
            if abs(key - user.rating) <= abs(closest - user.rating):
                closest = key
        # convert rating to point and get difference
        rating_point = RATING_TO_POINT[closest]
        point_diff = POINT_VALUES.index(current.point) - POINT_VALUES.index(rating_point)

        point = 10 + 2 * (point_diff)
        point = max(point, 0)

        gitgud_util.insert(username, ctx.get_guild().id, point, current.problem_id, datetime.now())
        gitgud_util.clear(username, ctx.get_guild().id)

        completion_time = datetime.now() - current.time
        # convert from timedelta to readable string
        ret = ""
        cnt = 0
        if completion_time.days // 365 != 0:
            ret += f" {completion_time.days // 365} years"
            cnt += 1
        if completion_time.days % 365 != 0:
            ret += f" {completion_time.days % 365} days"
            cnt += 1
        if completion_time.seconds // 3600 != 0:
            ret += f" {completion_time.seconds // 3600} hours"
            cnt += 1
        if cnt < 3 and completion_time.seconds % 3600 // 60 != 0:
            ret += f" {completion_time.seconds % 3600 // 60} minutes"
            cnt += 1
        if cnt < 3 and completion_time.seconds % 60 != 0:
            ret += f" {completion_time.seconds % 60} seconds"

        return await ctx.respond(f"Challenge took{ret}. " f"{current.handle} gained {point} points")

    else:
        return await ctx.respond("You have not completed the challenge")
Example #2
0
    async def cache(self, ctx, username: typing.Optional[str] = None):
        '''Caches the submissions of a user, will speed up other commands

        Use surround your username with '' if it can be interpreted as a number
        '''
        query = Query()
        username = username or query.get_handle(ctx.author.id, ctx.guild.id)

        username = username.replace('\'', '')

        if username is None:
            return await ctx.send('No username given!')

        user = await query.get_user(username)
        if user is None:
            return await ctx.send(f'{username} does not exist on DMOJ')

        username = user.username

        msg = await ctx.send(f'Caching {username}\'s submissions')
        session.query(Submission_DB).filter(
            Submission_DB._user == username).delete()
        await query.get_submissions(username)
        return await msg.edit(content=f'{username}\'s submissions ' +
                              'have been cached')
Example #3
0
def main():
    # https://github.com/cheran-senthil/TLE/blob/bae59c2de6a2313be4a6ba4a5a5cbba81352e229/tle/__main__.py
    dotenv.load_dotenv()
    BOT_TOKEN = os.environ.get("JOMD_BOT_TOKEN")

    if not BOT_TOKEN:
        logger.critical("Missing bot token")
        return

    pref = "+"
    bot = lightbulb.BotApp(token=BOT_TOKEN,
                           prefix=pref,
                           banner=None,
                           intents=hikari.Intents.ALL)

    bot.load_extensions_from("./extensions/")
    # TESTING
    # extensions = ["admin", "meta", "gitgud", "handles", "user", "plot"]
    # for extension in extensions:
    #     bot.load_extensions(f"extensions.{extension}")
    logger.debug("Extensions loaded: %s", ", ".join(bot.extensions))

    # Get preliminary data
    if session.query(Problem_DB).count() == 0:
        q = Query()
        loop = asyncio.get_event_loop()
        loop.run_until_complete(q.get_problems())

    # Restrict bot usage to inside guild channels only.
    bot.check(lightbulb.checks.guild_only)
    # TODO Make something that will automatically fetch recent contests
    bot.run()
Example #4
0
 async def whois(self,
                 ctx,
                 member: typing.Optional[discord.Member] = None,
                 handle: typing.Optional[str] = None):
     # TODO: Use embeds and pfps
     query = Query()
     if handle:
         user = await query.get_user(handle)
         handle = user.username
         author_id = query.get_handle_user(handle, ctx.guild.id)
         if author_id:
             # member = await self.bot.fetch_user(author_id)
             name = ctx.message.guild.get_member(author_id)
             await ctx.send(f'`{handle}` is `{name.nick or name.name}`')
         else:
             await ctx.send(
                 f'`{handle}` is not linked with any account here...')
     elif member:
         handle = query.get_handle(member.id, ctx.guild.id)
         if handle:
             await ctx.send(f'`{member.nick or member.name}` is `{handle}`')
         else:
             await ctx.send(
                 f'`{member.nick or member.name}` is not linked with any account here'
             )
     else:
         # wtf
         pass
Example #5
0
    async def cache(self,
                    ctx,
                    complete: typing.Optional[force] = False,
                    username: typing.Optional[str] = None):
        """Caches the submissions of a user, will speed up other commands

        Use surround your username with '' if it can be interpreted as a number
        +f              cache every submission
        """
        query = Query()
        username = username or query.get_handle(ctx.author.id, ctx.guild.id)

        username = username.replace('\'', '')

        if username is None:
            return await ctx.send(f'No username given!')

        user = await query.get_user(username)
        if user is None:
            return await ctx.send(f'{username} does not exist on DMOJ')

        username = user.username

        try:
            msg = await ctx.send(f'Caching {username}\'s submissions')
        except Exception as e:
            await msg.edit(content='An error has occured, ' +
                           'try caching again. Log: ' + e.message)
            return

        await query.get_submissions(username)

        return await msg.edit(content=f'{username}\'s submissions ' +
                              'have been cached.')
Example #6
0
 async def force(self, ctx, _type, key):
     '''
     Force a recache of a problem, or contest
     '''
     if _type.lower() == 'contest':
         q = session.query(Contest_DB).filter(Contest_DB.key == key)
         if q.count() == 0:
             await ctx.send(f'There is no contests with the key {key} '
                            f'cached. Will try fetching contest')
         else:
             q.delete()
             session.commit()
         query = Query()
         try:
             await query.get_contest(key)
         except ObjectNotFound:
             return await ctx.send('Contest not found')
         await ctx.send(f'Recached contest {key}')
     if _type.lower() == 'problem':
         q = session.query(Problem_DB).filter(Problem_DB.code == key)
         if q.count() == 0:
             await ctx.send(f'There is no problems with the key {key} '
                            f'cached. Will try fetching problem')
         else:
             q.delete()
             session.commit()
         query = Query()
         try:
             await query.get_problem(key)
         except ObjectNotFound:
             return await ctx.send('Problem not found')
         await ctx.send(f'Recached problem {key}')
Example #7
0
def main():
    # https://github.com/cheran-senthil/TLE/blob/bae59c2de6a2313be4a6ba4a5a5cbba81352e229/tle/__main__.py
    BOT_TOKEN = os.environ["JOMD_BOT_TOKEN"]
    API_TOKEN = os.environ["JOMD_TOKEN"]

    if not BOT_TOKEN:
        print('Missing bot token')
        return

    intents = discord.Intents.default()  # All but the two privileged ones
    intents.members = True  # Subscribe to the Members intent

    pref = '+'
    bot = commands.Bot(command_prefix=commands.when_mentioned_or(pref),
                       intents=intents)

    cogs = [file.stem for file in Path('cogs').glob('*.py')]
    for extension in cogs:
        bot.load_extension(f'cogs.{extension}')
    print(f'Cogs loaded: {", ".join(bot.cogs)}')

    def no_dm_check(ctx):
        if ctx.guild is None:
            raise commands.NoPrivateMessage('Private messages not permitted.')
        return True

    # Get preliminary data
    q = Query()
    loop = asyncio.get_event_loop()
    loop.run_until_complete(q.get_problems())

    # Restrict bot usage to inside guild channels only.
    bot.add_check(no_dm_check)

    bot.run(BOT_TOKEN)
Example #8
0
    async def _set(self, ctx, member: discord.Member, username: str):
        """Manually link two accounts together"""
        query = Query()
        user = await query.get_user(username)

        if user is None:
            await ctx.send(f'{username} does not exist on dmoj')
            return

        username = user.username

        if query.get_handle(member.id, ctx.guild.id):
            await ctx.send(
                '%s, this handle is already linked with %s.' %
                (ctx.author.mention, query.get_handle(member.id, ctx.guild.id))
            )
            return

        if query.get_handle_user(username, ctx.guild.id):
            await ctx.send('This handle is already linked with another user')
            return

        handle = Handle_DB()
        handle.id = member.id
        handle.handle = username
        handle.user_id = user.id
        handle.guild_id = ctx.guild.id
        session.add(handle)
        session.commit()
        return await ctx.send("%s, %s is now linked with %s." %
                              (ctx.author.name, member.name, username))
Example #9
0
async def force(ctx: lightbulb.Context) -> None:
    if ctx.options.type.lower() == "contest":
        q = session.query(Contest_DB).filter(Contest_DB.key == ctx.options.key)
        if q.count() == 0:
            await ctx.respond(
                f"There is no contests with the key {ctx.options.key} "
                f"cached. Will try fetching contest")
        else:
            q.delete()
            session.commit()
        query = Query()
        try:
            await query.get_contest(ctx.options.key)
        except ObjectNotFound:
            return await ctx.respond("Contest not found")
        await ctx.respond(f"Recached contest {ctx.options.key}")
    if ctx.options.type.lower() == "problem":
        q = session.query(Problem_DB).filter(
            Problem_DB.code == ctx.options.key)
        if q.count() == 0:
            await ctx.respond(
                f"There is no problems with the key {ctx.options.key} "
                f"cached. Will try fetching problem")
        else:
            q.delete()
            session.commit()
        query = Query()
        try:
            await query.get_problem(ctx.options.key)
        except ObjectNotFound:
            return await ctx.respond("Problem not found")
        await ctx.respond(f"Recached problem {ctx.options.key}")
    else:
        await ctx.send_help()
Example #10
0
async def rating(ctx):
    """Plot rating progression"""
    peak = ctx.options.peak
    usernames = ctx.options.usernames

    query = Query()
    if usernames == []:
        usernames = [query.get_handle(ctx.author.id, ctx.get_guild().id)]

    try:
        users = await asyncio.gather(*[query.get_user(username) for username in usernames])
    except ObjectNotFound:
        return await ctx.respond("User not found")

    usernames = [user.username for user in users]
    for i in range(len(users)):
        if users[i] is None:
            return await ctx.respond(f"{usernames[i]} does not exist on DMOJ")
    if len(users) > 10:
        return await ctx.respond("Too many users given, max 10")

    cond = [Contest_DB.rankings.contains(user.username) for user in users]
    q = session.query(Contest_DB).filter(or_(*cond)).filter(Contest_DB.is_rated == 1)
    contests = q.all()

    def get_rating_change(rankings, users):
        ret = {}
        for ranking in rankings:
            for user in users:
                if user.username == ranking["user"] and ranking["new_rating"]:
                    ret[user.username] = ranking["new_rating"]
        return ret

    data = {}
    data["users"] = [user.username for user in users]
    userPrevRating = {}
    for contest in contests:
        changes = get_rating_change(contest.rankings, users)
        data[contest.end_time] = []
        for user in users:
            if user.username in changes and (
                not peak or changes[user.username] >= userPrevRating.get(user.username, -9999)
            ):
                change = changes[user.username]
                userPrevRating[user.username] = change
                data[contest.end_time].append(change)
            else:
                data[contest.end_time].append(None)
    plot_rating(data)

    embed = hikari.Embed(
        title="Rating Progression",
        color=0xFCDB05,
    )
    with open("./graphs/plot.png", "rb") as file:
        embed.set_image(hikari.Bytes(file.read(), "plot.png"))

    return await ctx.respond(embed=embed)
Example #11
0
    async def rating(self,
                     ctx,
                     peak: typing.Optional[plot_peak] = False,
                     *usernames):
        """Plot rating progression"""
        usernames = list(usernames)

        query = Query()
        if usernames == []:
            usernames = [query.get_handle(ctx.author.id, ctx.guild.id)]

        users = await asyncio.gather(
            *[query.get_user(username) for username in usernames])
        usernames = [user.username for user in users]
        for i in range(len(users)):
            if users[i] is None:
                return await ctx.send(f'{usernames[i]} does not exist on DMOJ')
        if len(users) > 10:
            return await ctx.send('Too many users given, max 10')

        cond = [Contest_DB.rankings.contains(user.username) for user in users]
        q = session.query(Contest_DB).filter(or_(*cond))\
            .filter(Contest_DB.is_rated == 1)
        contests = q.all()

        def get_rating_change(rankings, users):
            ret = {}
            for ranking in rankings:
                for user in users:
                    if (user.username == ranking['user']
                            and ranking['new_rating']):
                        ret[user.username] = ranking['new_rating']
            return ret

        data = {}
        data['users'] = [user.username for user in users]
        userPrevRating = {}
        for contest in contests:
            changes = get_rating_change(contest.rankings, users)
            data[contest.end_time] = []
            for user in users:
                if user.username in changes \
                        and (not peak or changes[user.username] >= userPrevRating.get(user.username, -9999)):
                    change = changes[user.username]
                    userPrevRating[user.username] = change
                    data[contest.end_time].append(change)
                else:
                    data[contest.end_time].append(None)
        plot_rating(data)
        with open('./graphs/plot.png', 'rb') as file:
            file = discord.File(io.BytesIO(file.read()), filename='plot.png')
        embed = discord.Embed(
            title='Contest Rating',
            color=0xfcdb05,
        )
        embed.set_image(url='attachment://plot.png')

        return await ctx.send(embed=embed, file=file)
Example #12
0
async def link(ctx: lightbulb.Context) -> None:
    username = ctx.options.username
    # Check if user exists
    query = Query()
    try:
        user = await query.get_user(username)

        if user is None:
            raise ObjectNotFound()
    except ObjectNotFound:
        await ctx.respond(escape_markdown(f"{username} does not exist on DMOJ"))
        return

    username = user.username

    if query.get_handle(ctx.author.id, ctx.get_guild().id):
        await ctx.respond(
            "%s, your handle is already linked with %s."
            % (ctx.author.mention, query.get_handle(ctx.author.id, ctx.get_guild().id))
        )
        return

    if query.get_handle_user(username, ctx.get_guild().id):
        await ctx.respond("This handle is already linked with another user")
        return

    # verify from dmoj user description
    description = await query.get_user_description(username)
    userKey = hashlib.sha256(str(ctx.author.id).encode()).hexdigest()
    if userKey not in description:
        await ctx.respond(
            "Put `" + userKey + "` in your DMOJ user description (https://dmoj.ca/edit/profile/) "
            "and run the command again."
        )
        return

    handle = Handle_DB()
    handle.id = ctx.author.id
    handle.handle = username
    handle.user_id = user.id
    handle.guild_id = ctx.get_guild().id
    session.add(handle)
    session.commit()
    await ctx.respond(escape_markdown("%s, you now have linked your account to %s" % (ctx.author, username)))

    rank_to_role = {}
    rc = lightbulb.RoleConverter(ctx)
    for role_id in ctx.get_guild().get_roles():
        role = await rc.convert(str(role_id))
        if role.name in RANKS:
            rank_to_role[role.name] = role

    rank = rating_to_rank(user.rating)
    # TODO Add guild specific option to disable updating roles
    if rank in rank_to_role:
        await _update_rank(ctx.member, rank_to_role[rank], "Dmoj account linked")
    else:
        await ctx.respond("You are missing the `" + rank + "` role")
Example #13
0
    async def solved(self, ctx, *usernames):
        """Plot problems solved over time"""
        usernames = list(usernames)

        query = Query()
        if usernames == []:
            usernames = [query.get_handle(ctx.author.id, ctx.guild.id)]

        users = await asyncio.gather(
            *[query.get_user(username) for username in usernames])
        usernames = [user.username for user in users]
        for i in range(len(users)):
            if users[i] is None:
                return await ctx.send(f'{usernames[i]} does not exist on DMOJ')
        if len(users) > 10:
            return await ctx.send('Too many users given, max 10')

        total_data = {}
        not_cached = []
        for username in usernames:
            q = session.query(Submission_DB)\
                .filter(Submission_DB._user == username)
            if q.count() == 0:
                not_cached.append(username)

            q = session.query(func.min(Submission_DB.date))\
                .join(Problem_DB, Problem_DB.code == Submission_DB._code)\
                .filter(Submission_DB._user == username)\
                .filter(Submission_DB.points == Problem_DB.points)\
                .group_by(Submission_DB._code)
            dates = list(map(first_tuple, q.all()))
            dates.sort()
            data_to_plot = {}
            cnt = 0
            for date in dates:
                cnt += 1
                data_to_plot[date] = cnt
            total_data[username] = data_to_plot

        plot_solved(total_data)

        if len(not_cached):
            await ctx.send(f"`{', '.join(not_cached)} do not have any cached "
                           f"submissions. Please use +cache [username]`")

        plot_points(total_data)

        with open('./graphs/plot.png', 'rb') as file:
            file = discord.File(io.BytesIO(file.read()), filename='plot.png')
        embed = discord.Embed(
            title='Problems Solved',
            color=0xfcdb05,
        )
        embed.set_image(url=f'attachment://plot.png', )

        return await ctx.send(embed=embed, file=file)
Example #14
0
async def gitgud(ctx: lightbulb.Context) -> None:
    # TODO Fix converters for slash commands
    points = ctx.options.points
    filters = ctx.options.filters
    query = Query()
    gitgud_util = Gitgud_utils()
    # get the user's dmoj handle
    username = query.get_handle(ctx.author.id, ctx.get_guild().id)
    # user = await query.get_user(username)

    if username is None:
        return await ctx.respond("You are not linked to a DMOJ Account. " "Please link your account before continuing")

    user = await query.get_user(username)

    if points is None:
        points = [0, 0]
        closest = -1000
        for key in RATING_TO_POINT:
            if abs(key - user.rating) <= abs(closest - user.rating):
                closest = key
        points[0] = RATING_TO_POINT[closest]
        points[1] = points[0]
    # return if the user haven't finished the previous problem
    current = gitgud_util.get_current(username, ctx.get_guild().id)

    if current is not None and current.problem_id is not None:
        if not gitgud_util.has_solved(username, current.problem_id):
            # User has a current problem unsolved
            problem = await query.get_problem(current.problem_id)
            embed = hikari.Embed(
                description=f"You currently have an uncompleted "
                f"challenge, [{problem.name}]"
                f"(https://dmoj.ca/problem/{problem.code})",
                color=0xFCDB05,
            )
            return await ctx.respond(embed=embed)

    filter_list = []
    for filter in filters:
        if filter in SHORTHANDS:
            filter_list.append(SHORTHANDS[filter])

    filters = filter_list

    embed, problem = await gimme_common(username, points, filters)

    if embed is None:
        return await ctx.respond("No problems that satisfies the filter")

    gitgud_util.bind(username, ctx.get_guild().id, problem.code, problem.points, datetime.now())

    embed.description = "Points: %s\nProblem Types ||%s||" % (problem.points, ", ".join(problem.types))

    return await ctx.respond(embed=embed)
Example #15
0
	async def gitlog(self, ctx, username=None):
		"""
		Show the past gitgud history of a user
		"""
		query = Query()
		username = username or query.get_handle(ctx.author.id, ctx.guild.id)
		try:
			user = await query.get_user(username)
			username = user.username
		except TypeError:
			username = None
		if username is None:
			return await ctx.send("You have not entered a valid DMOJ handle or linked with a DMOJ Account")

		gitgud_util = Gitgud_utils()
		history = gitgud_util.get_all(username, ctx.guild.id)


		if len(history) == 0:
			embed = discord.Embed(description="User have not completed any challenge")
			return await ctx.send(embed=embed)
		# paginate
		count = 0
		page_cnt = min(10, len(history)//10 + bool(len(history)%10))
		embeds = []
		content = ""
		paginator = Pagination.CustomEmbedPaginator(ctx, timeout=60, remove_reactions=True)
		paginator.add_reaction('⏮️', "first")
		paginator.add_reaction('⏪', "back")
		paginator.add_reaction('⏩', "next")
		paginator.add_reaction('⏭️', "last")
		for solved in history:
			# print(solved.problem_id)
			problem = await query.get_problem(solved.problem_id)
			days = (datetime.now() - solved.time).days
			if days==0:
				days_str = "today"
			elif days==1:
				days_str = "yesterday"
			else:
				days_str = f"{days} days ago"
			content += f"[{problem.name}](https://dmoj.ca/{problem.code}) [+{solved.point}] ({days_str})\n"
			count += 1
			if count % 10 == 0:
				embed = discord.Embed()
				embed.add_field(name=f"Gitgud Log for {username} (page {count//10}/{page_cnt})", value=content, inline=True)
				embeds.append(embed)
				content = ""
			if count == 100:
				break
		if count % 10 != 0:
			embed = discord.Embed()
			embed.add_field(name=f"Gitlog for {username} (page {count//10 + 1}/{page_cnt})", value=content, inline=True)
			embeds.append(embed)
		return await paginator.run(embeds)
Example #16
0
    async def _set(self, ctx, member, username: str):
        """Manually link two accounts together"""
        query = Query()
        member = await query.parseUser(ctx, member)

        if username != "+remove":
            user = await query.get_user(username)

            if user is None:
                await ctx.send(f'{username} does not exist on dmoj')
                return

            username = user.username

        handle = query.get_handle(member.id, ctx.guild.id)
        if handle == username:
            return await ctx.send(
                f'{member.display_name} is already linked with {handle}')

        if handle:
            handle = session.query(Handle_DB)\
                .filter(Handle_DB.id == member.id)\
                .filter(Handle_DB.guild_id == ctx.guild.id).first()
            session.delete(handle)
            session.commit()
            await ctx.send(
                f'Unlinked {member.display_name} with handle {handle.handle}')

        if username == "+remove":
            return

        if query.get_handle_user(username, ctx.guild.id):
            await ctx.send('This handle is already linked with another user')
            return

        handle = Handle_DB()
        handle.id = member.id
        handle.handle = username
        handle.user_id = user.id
        handle.guild_id = ctx.guild.id
        session.add(handle)
        session.commit()
        await ctx.send(f"Linked {member.name} with {username}.")

        rank_to_role = {
            role.name: role
            for role in ctx.guild.roles if role.name in RANKS
        }
        rank = self.rating_to_rank(user.rating)
        if rank in rank_to_role:
            await self._update_rank(ctx.author, rank_to_role[rank],
                                    'Dmoj account linked')
        else:
            await ctx.send("You are missing the " + rank.name + " role")
Example #17
0
async def solved(ctx):
    """Plot problems solved over time"""
    usernames = ctx.options.usernames

    query = Query()
    if usernames == []:
        usernames = [query.get_handle(ctx.author.id, ctx.get_guild().id)]

    try:
        users = await asyncio.gather(*[query.get_user(username) for username in usernames])
    except ObjectNotFound:
        return await ctx.respond("User not found")

    usernames = [user.username for user in users]
    for i in range(len(users)):
        if users[i] is None:
            return await ctx.respond(f"{usernames[i]} does not exist on DMOJ")
    if len(users) > 10:
        return await ctx.respond("Too many users given, max 10")

    total_data = {}
    for username in usernames:
        q = session.query(Submission_DB).filter(Submission_DB._user == username)
        if q.count() == 0:
            await ctx.respond(f"`{username}` does not have any cached submissions, caching now")
            await query.get_submissions(username)

        q = (
            session.query(func.min(Submission_DB.date))
            .join(Problem_DB, Problem_DB.code == Submission_DB._code)
            .filter(Submission_DB._user == username)
            .filter(Submission_DB.points == Problem_DB.points)
            .group_by(Submission_DB._code)
        )
        dates = list(map(itemgetter(0), q.all()))
        dates.sort()
        data_to_plot = {}
        cnt = 0
        for date in dates:
            cnt += 1
            data_to_plot[date] = cnt
        total_data[username] = data_to_plot

    plot_solved(total_data)

    embed = hikari.Embed(
        title="Problems Solved",
        color=0xFCDB05,
    )
    with open("./graphs/plot.png", "rb") as file:
        embed.set_image(hikari.Bytes(file.read(), "plot.png"))

    return await ctx.respond(embed=embed)
Example #18
0
async def solved(ctx):
    """Shows a user's last solved problems"""
    minP = 0
    maxP = 100
    query = Query()
    username = None
    for arg in ctx.options.args:
        if arg.startswith("p>="):
            minP = max(minP, int(arg[3:]))
        elif arg.startswith("p<="):
            maxP = min(maxP, int(arg[3:]))
        else:
            username = arg
    username = (await query.get_user(username)).username
    if username is None:
        username = query.get_handle(ctx.author.id, ctx.get_guild().id)
    await query.get_submissions(username, result="AC")

    submissions = (session.query(Submission_DB).filter(
        Submission_DB._user == username).filter(
            Submission_DB.result == "AC").options(
                orm.joinedload(Submission_DB.problem, innerjoin=True)).join(
                    Submission_DB.problem).filter(
                        Problem_DB.is_organization_private == 0).filter(
                            Problem_DB.is_public == 1).order_by(
                                Submission_DB.date).all())
    uniqueSubmissions = []
    solved = set()
    for sub in submissions:
        if sub._code not in solved:
            solved.add(sub._code)
            if minP <= sub.points and sub.points <= maxP:
                uniqueSubmissions.append(sub)
    uniqueSubmissions.reverse()
    pag = lightbulb.utils.EmbedPaginator(max_chars=1024)

    for sub in uniqueSubmissions:
        age = (datetime.now() - sub.date).days
        pag.add_line(
            f"[{sub.problem[0].name}]({SITE_URL}/problem/{sub._code}) [{sub.points}] ({age} days ago)"
        )

    if len(uniqueSubmissions) == 0:
        pag.add_line("No submission")

    @pag.embed_factory()
    def build_embed(page_index, content):
        return hikari.Embed().add_field(name="Recently solved problems by " +
                                        username,
                                        value=content)

    navigator = nav.ButtonNavigator(pag.build_pages())
    await navigator.run(ctx)
Example #19
0
    async def gimme(self,
                    ctx,
                    username: typing.Optional[parse_gimme] = None,
                    points: typing.Optional[point_range] = [1, 50],
                    *filters):
        """
        Recommend a problem

        Use surround your username with '' if it can be interpreted as a number

        SHORTHANDS:
        - adhoc
        - math
        - bf
        - ctf
        - ds
        - d&c
        - dp
        - geo
        - gt
        - greedy
        - regex
        - string
        """
        filters = list(filters)
        query = Query()
        username = username or query.get_handle(ctx.author.id, ctx.guild.id)

        if username is None:
            return await ctx.send(f'No username provided')

        user = await query.get_user(username)
        if user is None:
            return await ctx.send(f'{username} does not exist on DMOJ')

        username = user.username
        filter_list = []
        for filter in filters:
            if filter in SHORTHANDS:
                filter_list += SHORTHANDS[filter]
            else:
                filter_list.append(filter.title())

        filters = filter_list

        # Get all problems that are unsolved by user and fits the filter and point range
        result = await query.get_unsolved_problem(username, ctx.guild.id, 0,
                                                  filters, points[0],
                                                  points[1])
        # print(result)
        if result is None:
            return await ctx.send("No problem that satisfies the filter")
        return await ctx.send(embed=result)
Example #20
0
 async def unlink(self, ctx):
     """Unlink your discord account with your dmoj account"""
     query = Query()
     if not query.get_handle(ctx.author.id, ctx.guild.id):
         await ctx.send('You are not linked with any user')
         return
     handle = session.query(Handle_DB)\
         .filter(Handle_DB.id == ctx.author.id)\
         .filter(Handle_DB.guild_id == ctx.guild.id).first()
     session.delete(handle)
     session.commit()
     await ctx.send(f'Unlinked you with handle {handle.handle}')
Example #21
0
async def gimme(ctx):
    """
    Recommend a problem

    Use surround your username with '' if it can be interpreted as a number

    SHORTHANDS:
    - adhoc
    - math
    - bf
    - ctf
    - ds
    - d&c
    - dp
    - geo
    - gt
    - greedy
    - regex
    - string
    """
    username = ctx.options.username
    points = ctx.options.points
    filters = ctx.options.filters
    query = Query()
    username = username or query.get_handle(ctx.author.id, ctx.get_guild().id)

    if username is None:
        return await ctx.respond("No username given!")

    username = username.replace("'", "")

    user = await query.get_user(username)
    if user is None:
        return await ctx.respond(f"{username} does not exist on DMOJ")

    username = user.username
    filter_list = []
    for filter in filters:
        if filter in SHORTHANDS:
            filter_list += SHORTHANDS[filter]
        else:
            filter_list.append(filter.title())

    filters = filter_list

    # Get all problems that are unsolved by user and fits the filter and
    # point range
    result, problem = await gimme_common(username, points, filters)

    if result is None:
        return await ctx.respond("No problem that satisfies the filter")
    return await ctx.respond(embed=result)
Example #22
0
    async def link(self, ctx, username: str):
        '''Links your discord account to your dmoj account'''
        # Check if user exists
        query = Query()
        user = await query.get_user(username)

        if user is None:
            await ctx.send(f'{username} does not exist on DMOJ')
            return

        username = user.username

        if query.get_handle(ctx.author.id, ctx.guild.id):
            await ctx.send('%s, your handle is already linked with %s.' %
                           (ctx.author.mention,
                            query.get_handle(ctx.author.id, ctx.guild.id)))
            return

        if query.get_handle_user(username, ctx.guild.id):
            await ctx.send('This handle is already linked with another user')
            return

        # verify from dmoj user description
        description = await query.get_user_description(username)
        userKey = hashlib.sha256(str(ctx.author.id).encode()).hexdigest()
        if userKey not in description:
            await ctx.send(
                'Put `' + userKey +
                '` in your DMOJ user description (https://dmoj.ca/edit/profile/) '
                'and run the command again.')
            return

        handle = Handle_DB()
        handle.id = ctx.author.id
        handle.handle = username
        handle.user_id = user.id
        handle.guild_id = ctx.guild.id
        session.add(handle)
        session.commit()
        await ctx.send('%s, you now have linked your account to %s' %
                       (ctx.author.name, username))
        return
        rank_to_role = {
            role.name: role
            for role in ctx.guild.roles if role.name in RANKS
        }
        rank = self.rating_to_rank(user.rating)
        if rank in rank_to_role:
            await self._update_rank(ctx.author, rank_to_role[rank],
                                    'Dmoj account linked')
        else:
            await ctx.send('You are missing the ' + rank.name + ' role')
Example #23
0
    async def solved(self, ctx, *args):
        """Shows a user's last solved problems"""
        minP = 0
        maxP = 100
        query = Query()
        username = None
        for arg in args:
            if arg.startswith("p>="):
                minP = max(minP, int(arg[3:]))
            elif arg.startswith("p<="):
                maxP = min(maxP, int(arg[3:]))
            else:
                username = (await query.get_user(arg)).username
        if username is None:
            username = query.get_handle(ctx.author.id, ctx.guild.id)
        await query.get_submissions(username, result='AC')

        submissions = session.query(Submission_DB)\
            .filter(Submission_DB._user == username)\
            .filter(Submission_DB.result == 'AC')\
            .options(orm.joinedload(Submission_DB.problem, innerjoin=True))\
            .join(Submission_DB.problem)\
            .filter(Problem_DB.is_organization_private == 0)\
            .filter(Problem_DB.is_public == 1)\
            .order_by(Submission_DB.date).all()
        uniqueSubmissions = []
        solved = set()
        for sub in submissions:
            if sub._code not in solved:
                solved.add(sub._code)
                if minP <= sub.points and sub.points <= maxP:
                    uniqueSubmissions.append(sub)
        uniqueSubmissions.reverse()
        page = ""
        content = []
        cnt = 0
        for sub in uniqueSubmissions:
            age = (datetime.now() - sub.date).days
            # sub.problem[0].name is rly slow
            page += f"[{sub.problem[0].name}]({SITE_URL}/problem/{sub._code}) [{sub.points}] ({age} days ago)\n"
            cnt += 1
            if cnt % 10 == 0:
                content.append(page)
                page = ""
        if page != "":
            content.append(page)
        if len(content) == 0:
            content.append("No submission")
        title = "Recently solved problems by " + username
        message = await ctx.send(embed=discord.Embed().add_field(name=title, value=content[0]))
        await scroll_embed(ctx, self.bot, message, title, content)
Example #24
0
    async def vc(self, ctx, *usernames):
        """Suggest a contest"""
        usernames = list(usernames)

        query = Query()
        if usernames == []:
            username = query.get_handle(ctx.author.id, ctx.guild.id)
            if username:
                usernames = [username]

        users = await asyncio.gather(*[query.get_user(username)
                                     for username in usernames])
        usernames = [user.username for user in users]
        for i in range(len(users)):
            if users[i] is None:
                return await ctx.send(f'{usernames[i]} does not exist on DMOJ')

        q = session.query(Contest_DB)
        for user in users:
            # if the user has attempted any problems from the problem set
            sub_q = session.query(Submission_DB,
                                  func.max(Submission_DB.points))\
                .filter(Submission_DB._user == user.username)\
                .group_by(Submission_DB._code).subquery()
            sub_q = session.query(Problem_DB.code)\
                .join(sub_q, Problem_DB.code == sub_q.c._code, isouter=True)\
                .filter(func.ifnull(sub_q.c.points, 0) != 0)
            sub_q = list(map(itemgetter(0), sub_q.all()))
            q = q.filter(not_(Contest_DB.rankings.contains(user.username)))\
                .filter(~Contest_DB.problems.any(Problem_DB.code.in_(sub_q)))

        if q.count() == 0:
            await ctx.send("Cannot find any contests which "
                           "all users have not done")
            return
        contest = random.choice(q.all())

        # When problems are private, it says there are no problems
        window = 'No'
        is_rated = 'Not Rated'
        if contest.time_limit:
            window = f"{contest.time_limit/60/60} Hr"
        if contest.is_rated:
            is_rated = "Rated"
        embed = discord.Embed(
            title=contest.name, url=f"https://dmoj.ca/contest/{contest.key}",
            description=f"{window} window | {len(contest.problems)} Problems | {is_rated}",
            color=0xfcdb05
        )
        await ctx.send(embed=embed)
Example #25
0
    async def whois(self,
                    ctx,
                    member: typing.Optional[discord.Member] = None,
                    handle: typing.Optional[str] = None):

        query = Query()
        username, linked_username, pfp = None, None, None
        if handle:
            user = None
            try:
                user = await query.get_user(handle)
            except ObjectNotFound:
                username = None
            if user:
                handle = user.username
                author_id = query.get_handle_user(handle, ctx.guild.id)
                username = handle
                if author_id:
                    member = ctx.message.guild.get_member(author_id)
                    linked_username = member.nick or member.name
                    pfp = member.avatar_url
        elif member:
            handle = query.get_handle(member.id, ctx.guild.id)
            username = member.nick or member.name
            if handle:
                linked_username = handle
                pfp = await query.get_pfp(handle)
        if linked_username:
            embed = discord.Embed(
                color=0xfcdb05,
                title=f'{username} is {linked_username}',
            )
            embed.set_thumbnail(url=pfp)
            await ctx.send(embed=embed)
        elif username:
            embed = discord.Embed(
                title=f'{username} is not linked with any account here',
                color=0xfcdb05,
            )
            await ctx.send(embed=embed)
        else:
            name = None
            if member:
                name = member.nick or member.name
            embed = discord.Embed(
                title=f'Nothing found on {handle or name}',
                color=0xfcdb05,
            )
            await ctx.send(embed=embed)
Example #26
0
async def nogud(ctx):
    query = Query()
    gitgud_util = Gitgud_utils()

    username = query.get_handle(ctx.author.id, ctx.get_guild().id)

    if username is None:
        return await ctx.respond("You do not have a linked DMOJ account")

    current = gitgud_util.get_current(username, ctx.get_guild().id)
    if current is None or current.problem_id is None:
        return await ctx.respond("Nothing to cancel")

    gitgud_util.clear(username, ctx.get_guild().id)
    return await ctx.respond("Challenge skipped")
Example #27
0
async def gitlog(ctx):
    """
    Show the past gitgud history of a user
    """
    query = Query()
    username = ctx.options.username
    username = username or query.get_handle(ctx.author.id, ctx.get_guild().id)
    try:
        user = await query.get_user(username)
        username = user.username
    except TypeError:
        username = None
    if username is None:
        return await ctx.respond("You have not entered a valid DMOJ handle " "or linked with a DMOJ Account")

    gitgud_util = Gitgud_utils()
    history = gitgud_util.get_all(username, ctx.get_guild().id)

    if len(history) == 0:
        embed = hikari.Embed(description="User have not completed any " "challenge")
        return await ctx.respond(embed=embed)
    # paginate

    pag = lightbulb.utils.EmbedPaginator()
    for idx, solved in enumerate(history):
        # problem = solved.problem_id or await query.get_problem(solved.problem_id)
        problem = await query.get_problem(solved.problem_id)
        days = (datetime.now() - solved.time).days
        if days == 0:
            days_str = "today"
        elif days == 1:
            days_str = "yesterday"
        else:
            days_str = f"{days} days ago"
        pag.add_line(f"[{problem.name}](https://dmoj.ca/problem/{problem.code}) " f"[+{solved.point}] ({days_str})")
        if idx == 100:
            break

    @pag.embed_factory()
    def build_embed(page_index, content):
        return hikari.Embed(color=0xFCDB05,).add_field(
            name=f"Gitgud Log for {username} " f"(page {page_index})",  # Can't put total length :/
            value=content,
            inline=True,
        )

    navigator = nav.ButtonNavigator(pag.build_pages())
    await navigator.run(ctx)
Example #28
0
async def whois(ctx):
    member = ctx.options.member
    handle = ctx.options.handle

    query = Query()
    username, linked_username, pfp = None, None, None
    if handle:
        user = None
        try:
            user = await query.get_user(handle)
        except ObjectNotFound:
            username = None
        if user:
            handle = user.username
            author_id = query.get_handle_user(handle, ctx.get_guild().id)
            username = handle
            if author_id:
                member = ctx.get_guild().get_member(author_id)
                linked_username = member.nickname or member.username
                pfp = member.avatar_url
    elif member:
        handle = query.get_handle(member.id, ctx.get_guild().id)
        username = member.nickname or member.username
        if handle:
            linked_username = handle
            pfp = await query.get_pfp(handle)
    if linked_username:
        embed = hikari.Embed(
            color=0xFCDB05,
            title=escape_markdown(f"{username} is {linked_username}"),
        )
        embed.set_thumbnail(pfp)
        return await ctx.respond(embed=embed)
    elif username:
        embed = hikari.Embed(
            title=escape_markdown(f"{username} is not linked with any account here"),
            color=0xFCDB05,
        )
        return await ctx.respond(embed=embed)

    name = None
    if member:
        name = member.nickname or member.username
    embed = hikari.Embed(
        title=escape_markdown(f"Nothing found on {handle or name}"),
        color=0xFCDB05,
    )
    await ctx.respond(embed=embed)
Example #29
0
 async def cache_contests(self, ctx):
     query = Query()
     msg = await ctx.send("Caching...")
     contests = await query.get_contests()
     for contest in contests:
         await query.get_contest(contest.key)
     return await msg.edit(content=f"Cached {len(contests)} contests")
Example #30
0
 async def cachecontest(self, ctx, key):
     '''Update contest rating changes'''
     query = Query()
     contest = await query.get_contest(key)
     for ranking in contest.rankings:
         await query.get_user(ranking['user'])
     await ctx.send(f'Updated {len(contest.rankings)} users')