Skip to content

Commit ab656f0

Browse files
feat: added pagination to the leaderboard
1 parent 695f3e0 commit ab656f0

File tree

2 files changed

+190
-43
lines changed

2 files changed

+190
-43
lines changed

cogs/Levels.py

Lines changed: 56 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from collections import namedtuple
77
import datetime as dt
88
from io import BytesIO
9-
from typing import Union, Optional, Tuple
9+
from typing import Union, Optional
1010

1111
import disnake
1212
from PIL import Image, ImageDraw, ImageFont, ImageFilter
@@ -19,6 +19,7 @@
1919
from utils.bot import OGIROID
2020
from utils.exceptions import LevelingSystemError, UserNotFound
2121
from utils.models import User, RoleReward
22+
from utils.pagination import LeaderboardView
2223
from utils.shortcuts import errorEmb, sucEmb
2324

2425
FakeGuild = namedtuple("FakeGuild", "id")
@@ -44,6 +45,10 @@ async def on_error(self, inter, error):
4445
else:
4546
raise error
4647

48+
async def get_count(self, guild: Guild) -> int:
49+
record = await self.db.execute("SELECT COUNT(*) FROM levels WHERE guild_id = ?", (guild.id,))
50+
return (await record.fetchone())[0]
51+
4752
async def get_leaderboard(self, guild: Guild, limit: int = 10, offset: Optional[int, int] = None) -> list[User]:
4853
"""get a list of users
4954
optionally you can specify a range of users to get from the leaderboard e.g. 200, 230
@@ -180,12 +185,12 @@ async def grant_xp(self, message):
180185

181186
async def handle_message(self, message: Message):
182187
if any(
183-
[
184-
message.guild is None,
185-
message.author.bot,
186-
message.type not in [MessageType.default, MessageType.reply, MessageType.thread_starter_message],
187-
message.content.__len__() < 5,
188-
]
188+
[
189+
message.guild is None,
190+
message.author.bot,
191+
message.type not in [MessageType.default, MessageType.reply, MessageType.thread_starter_message],
192+
message.content.__len__() < 5,
193+
]
189194
):
190195
return
191196
if not random.randint(1, 3) == 1:
@@ -280,10 +285,9 @@ async def send_levelup(message: Message, level: int):
280285
user = message.author
281286
if level in [0, 1, 2, 3]:
282287
return
283-
msg = f"""{user.mention}, you have leveled up to level {level}!
288+
msg = f"""{user.mention}, you have leveled up to level {level}! 🥳
284289
"""
285-
addemoji = await message.channel.send(msg)
286-
await addemoji.add_reaction("🥳")
290+
await message.channel.send(msg)
287291

288292
async def get_rank(self, guild_id, user_record) -> int:
289293
"""
@@ -400,43 +404,49 @@ async def leaderboard(self, inter: ApplicationCommandInteraction):
400404
Get the leaderboard of the server
401405
"""
402406

403-
# embed.add_field(name=f"{i + 1}. {user}", value=f"Level: {record.lvl}\nTotal XP: {record.total_exp:,}", inline=False)
404407
await inter.response.defer()
405-
records = await self.controller.get_leaderboard(inter.guild, limit=10)
408+
limit = 10
409+
set_user = False
410+
records = await self.controller.get_leaderboard(inter.guild, limit=limit)
411+
try:
412+
cmd_user = await self.controller.get_user(inter.author)
413+
except UserNotFound:
414+
cmd_user = None
415+
406416
if not records:
407417
return await errorEmb(inter, text="No records found!")
408418
embed = Embed(title="Leaderboard", color=0x00FF00)
409419

410420
for i, record in enumerate(records):
411421
user = await self.bot.fetch_user(record.user_id)
412-
embed.add_field(name=f"{i + 1}. {user}", value=f"Level: {record.lvl}\nTotal XP: {record.total_exp:,}", inline=False)
413-
414-
user = await self.controller.get_user(inter.author)
415-
if user:
416-
try:
417-
rank = await self.controller.get_rank(inter.guild.id, user)
418-
if rank > 10:
419-
embed.add_field(
420-
name=f"{rank}. You",
421-
value=f"Level: {user.lvl}\nTotal XP: {user.xp:,}",
422-
inline=False,
423-
)
424-
except ValueError:
425-
pass
422+
if record.user_id == inter.author.id:
423+
embed.add_field(name=f"{i + 1}. {user} ~ You ",
424+
value=f"Level: {record.lvl}\nTotal XP: {record.total_exp:,}", inline=False)
425+
set_user = True
426+
else:
427+
embed.add_field(name=f"{i + 1}. {user}", value=f"Level: {record.lvl}\nTotal XP: {record.total_exp:,}",
428+
inline=False)
429+
if not set_user:
430+
rank = await self.controller.get_rank(inter.guild.id, cmd_user)
431+
embed.add_field(
432+
name=f"{rank}. You",
433+
value=f"Level: {cmd_user.lvl}\nTotal XP: {cmd_user.xp:,}",
434+
inline=False,
435+
)
426436

427437
embed.set_footer(text=f"{inter.author}", icon_url=inter.author.avatar.url)
428438
embed.timestamp = dt.datetime.now()
429439

430-
await inter.send(embed=embed)
440+
await inter.send(embed=embed, view=LeaderboardView(inter, self.controller, embed, inter.author.id))
431441

432442
@commands.slash_command()
433443
@commands.guild_only()
434444
@commands.has_any_role("Staff", "staff")
435445
async def set_lvl(
436-
self,
437-
inter: ApplicationCommandInteraction,
438-
user: Member,
439-
level: int = Param(description="The level to set the user to", le=100, ge=0),
446+
self,
447+
inter: ApplicationCommandInteraction,
448+
user: Member,
449+
level: int = Param(description="The level to set the user to", le=100, ge=0),
440450
):
441451
"""
442452
Set a user's level
@@ -461,16 +471,17 @@ async def role_reward(self, inter: ApplicationCommandInteraction):
461471
@role_reward.sub_command()
462472
@commands.has_permissions(manage_roles=True)
463473
async def add(
464-
self,
465-
inter: ApplicationCommandInteraction,
466-
role: Role = Param(name="role", description="what role to give"),
467-
level_needed: int = Param(name="level_needed", description="The level needed to get the role"),
474+
self,
475+
inter: ApplicationCommandInteraction,
476+
role: Role = Param(name="role", description="what role to give"),
477+
level_needed: int = Param(name="level_needed", description="The level needed to get the role"),
468478
):
469479
"""adds a role to the reward list"""
470480
if int(level_needed) not in self.levels:
471481
return await errorEmb(inter, text=f"Level must be within 1-{MAX_LEVEL} found")
472482

473-
if await self.bot.db.execute("SELECT 1 FROM role_rewards WHERE guild_id = ? AND role_id = ?", (inter.guild.id, role.id)):
483+
if await self.bot.db.execute("SELECT 1 FROM role_rewards WHERE guild_id = ? AND role_id = ?",
484+
(inter.guild.id, role.id)):
474485
sql = "INSERT OR IGNORE INTO role_rewards (guild_id, role_id, required_lvl) VALUES (?, ?, ?)"
475486
await self.bot.db.execute(sql, (inter.guild.id, role.id, level_needed))
476487
await self.bot.db.commit()
@@ -480,12 +491,13 @@ async def add(
480491
@role_reward.sub_command()
481492
@commands.has_permissions(manage_roles=True)
482493
async def remove(
483-
self,
484-
inter: ApplicationCommandInteraction,
485-
role: Role = Param(name="role", description="what role to remove"),
494+
self,
495+
inter: ApplicationCommandInteraction,
496+
role: Role = Param(name="role", description="what role to remove"),
486497
):
487498
"""remove a role reward"""
488-
if await self.bot.db.execute("SELECT 1 FROM role_rewards WHERE guild_id = ? AND role_id = ?", (inter.guild.id, role.id)):
499+
if await self.bot.db.execute("SELECT 1 FROM role_rewards WHERE guild_id = ? AND role_id = ?",
500+
(inter.guild.id, role.id)):
489501
sql = "DELETE FROM role_rewards WHERE guild_id = ? AND role_id = ?"
490502
await self.bot.db.execute(sql, (inter.guild.id, role.id))
491503
await self.bot.db.commit()
@@ -505,9 +517,11 @@ async def list(self, inter: ApplicationCommandInteraction):
505517
for record in records:
506518
record = RoleReward(*record)
507519
embed.add_field(
508-
name=f"Level {record.required_lvl}", value=f"{inter.guild.get_role(record.role_id).mention}", inline=False
520+
name=f"Level {record.required_lvl}", value=f"{inter.guild.get_role(record.role_id).mention}",
521+
inline=False
509522
)
510-
await inter.send(embed=embed, allowed_mentions=disnake.AllowedMentions(everyone=False, roles=False, users=False))
523+
await inter.send(embed=embed,
524+
allowed_mentions=disnake.AllowedMentions(everyone=False, roles=False, users=False))
511525

512526

513527
def setup(bot: OGIROID):

utils/pagination.py

Lines changed: 134 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
1-
from disnake import ui, ButtonStyle
1+
from typing import TYPE_CHECKING
22

3+
from disnake import ui, ButtonStyle, Embed
4+
5+
from utils.exceptions import UserNotFound
6+
from utils.shortcuts import errorEmb
7+
import datetime as dt
8+
if TYPE_CHECKING:
9+
from cogs.Levels import LevelsController
310

411
class CreatePaginator(ui.View):
512
"""
@@ -78,3 +85,129 @@ async def end(self, button, inter):
7885

7986
except:
8087
await inter.send("Unable to change the page.", ephemeral=True)
88+
89+
90+
class LeaderboardView(ui.View):
91+
"""
92+
Paginator for Leaderboards.
93+
Parameters:
94+
----------
95+
author: int
96+
The ID of the author who can interact with the buttons. Anyone can interact with the Paginator Buttons if not specified.
97+
UserPicked: bool
98+
If True, the user has already been shown and no need to add an extra field
99+
"""
100+
def __init__(self, controller: "LevelsController", firstemb: Embed, author: int = 123, set_user: bool = False, timeout: float = None):
101+
102+
self.controller = controller
103+
self.author = author
104+
self.CurrentEmbed = 0
105+
self.firstemb = firstemb
106+
self.user_set = set_user
107+
super().__init__(timeout=timeout if timeout else 180,)
108+
109+
async def at_last_page(self, inter):
110+
records = await self.controller.get_count(inter.guild.id)
111+
if records % 10 <= 0:
112+
return True
113+
else:
114+
return False
115+
116+
117+
118+
async def create_page(self, inter, page_num) -> Embed:
119+
if page_num == 0:
120+
return self.firstemb
121+
else:
122+
records = await self.controller.get_leaderboard(inter.guild, limit=10, offset=page_num*10)
123+
try:
124+
cmd_user = await self.controller.get_user(inter.author)
125+
except UserNotFound:
126+
cmd_user = None
127+
128+
if not records:
129+
return await errorEmb(inter, text="No records found!")
130+
embed = Embed(title="Leaderboard", color=0x00FF00)
131+
132+
for i, record in enumerate(records):
133+
user = await inter.bot.fetch_user(record.user_id)
134+
if record.user_id == inter.author.id:
135+
embed.add_field(name=f"{i + 1}. {user} ~ You ",
136+
value=f"Level: {record.lvl}\nTotal XP: {record.total_exp:,}", inline=False)
137+
self.user_set = True
138+
else:
139+
embed.add_field(name=f"{i + 1}. {user}",
140+
value=f"Level: {record.lvl}\nTotal XP: {record.total_exp:,}",
141+
inline=False)
142+
if not self.user_set:
143+
rank = await self.controller.get_rank(inter.guild.id, cmd_user)
144+
embed.add_field(
145+
name=f"{rank}. You",
146+
value=f"Level: {cmd_user.lvl}\nTotal XP: {cmd_user.xp:,}",
147+
inline=False,
148+
)
149+
150+
embed.set_footer(text=f"{inter.author}", icon_url=inter.author.avatar.url)
151+
embed.timestamp = dt.datetime.now()
152+
153+
@ui.button(emoji="⏮️", style=ButtonStyle.grey)
154+
async def front(self, button, inter):
155+
try:
156+
if inter.author.id != self.author and self.author != 123:
157+
return await inter.send("You cannot interact with these buttons.", ephemeral=True)
158+
elif self.CurrentEmbed == 0:
159+
return await inter.send("You are already on the first page.", ephemeral=True)
160+
elif self.CurrentEmbed:
161+
await inter.response.edit_message(embed=await self.create_page(inter, 0))
162+
self.CurrentEmbed = 0
163+
else:
164+
raise ()
165+
except:
166+
await inter.send("Unable to change the page.", ephemeral=True)
167+
168+
@ui.button(emoji="⬅️", style=ButtonStyle.grey)
169+
async def previous(self, button, inter):
170+
try:
171+
if inter.author.id != self.author and self.author != 123:
172+
return await inter.send("You cannot interact with these buttons.", ephemeral=True)
173+
elif self.CurrentEmbed == 0:
174+
return await inter.send("You are already on the first page.", ephemeral=True)
175+
if self.CurrentEmbed:
176+
await inter.response.edit_message(embed=await self.create_page(inter, self.CurrentEmbed - 1))
177+
self.CurrentEmbed = self.CurrentEmbed - 1
178+
else:
179+
raise ()
180+
181+
except:
182+
await inter.send("Unable to change the page.", ephemeral=True)
183+
184+
@ui.button(emoji="➡️", style=ButtonStyle.grey)
185+
async def next(self, button, inter):
186+
try:
187+
if inter.author.id != self.author and self.author != 123:
188+
return await inter.send("You cannot interact with these buttons.", ephemeral=True)
189+
elif await self.at_last_page(inter):
190+
return await inter.send("you are already at the end", ephemeral=True)
191+
await inter.response.edit_message(embed=await self.create_page(inter, self.CurrentEmbed + 1))
192+
self.CurrentEmbed += 1
193+
194+
except:
195+
await inter.send("Unable to change the page.", ephemeral=True)
196+
197+
@ui.button(emoji="⏭️", style=ButtonStyle.grey)
198+
async def end(self, button, inter):
199+
try:
200+
if inter.author.id != self.author and self.author != 123:
201+
return await inter.send("You cannot interact with these buttons.", ephemeral=True)
202+
elif await self.at_last_page(inter):
203+
return await inter.send("you are already at the end", ephemeral=True)
204+
get_last_page = await self.controller.get_count(inter.guild.id)
205+
if get_last_page % 10 == 0:
206+
last_page = get_last_page // 10
207+
else:
208+
last_page = get_last_page // 10 + 1
209+
await inter.response.edit_message(embed=await self.create_page(inter, last_page))
210+
self.CurrentEmbed = last_page
211+
212+
except:
213+
await inter.send("Unable to change the page.", ephemeral=True)

0 commit comments

Comments
 (0)