Add starboard

This commit is contained in:
eek7574 2022-11-30 21:01:16 +00:00
parent 9d8a41ee4a
commit 93ca4386fd
5 changed files with 175 additions and 17 deletions

View file

@ -370,17 +370,15 @@ class AssignmentsCog(commands.Cog):
else: else:
option = 10080 option = 10080
name = textwrap.shorten(modal.create_kwargs["title"], width=100, placeholder="...") name = textwrap.shorten(modal.create_kwargs["title"], width=100, placeholder="...")
tag = discord.utils.get(channel.available_tags, name=modal.create_kwargs['tutor'].name.title()) tag = discord.utils.get(channel.available_tags, name=modal.create_kwargs["tutor"].name.title())
await channel.create_thread( await channel.create_thread(
name=name, name=name,
content="Assignment name: {0}\nDue: {1} ({4})\nTutor: {2}\nCreated by: {3}".format( content="Assignment name: {0}\nDue: {1} ({4})\nTutor: {2}\nCreated by: {3}".format(
modal.create_kwargs["title"], modal.create_kwargs["title"],
discord.utils.format_dt( discord.utils.format_dt(due_dt, style="F"),
due_dt, style="F"
),
modal.create_kwargs["tutor"].name, modal.create_kwargs["tutor"].name,
ctx.user.mention, ctx.user.mention,
discord.utils.format_dt(due_dt, "R") discord.utils.format_dt(due_dt, "R"),
), ),
auto_archive_duration=option, auto_archive_duration=option,
applied_tags=[tag] if tag else [], applied_tags=[tag] if tag else [],

140
cogs/starboard.py Normal file
View file

@ -0,0 +1,140 @@
import asyncio
import textwrap
from typing import Tuple
import discord
from discord.ext import commands
from utils.db import StarBoardMessage
class StarBoardCog(commands.Cog):
def __init__(self, bot):
self.bot = bot
self.lock = asyncio.Lock()
async def generate_starboard_embed(self, message: discord.Message) -> discord.Embed:
star_count = [x for x in message.reactions if str(x.emoji) == "\N{white medium star}"]
if not star_count:
star_count = 0
else:
star_count = star_count[0].count
# noinspection PyUnresolvedReferences
cap = (message.channel if "thread" in message.channel.type.name else message.guild).member_count * 0.1
embed = discord.Embed(colour=discord.Colour.gold(), timestamp=message.created_at, description=message.content)
embed.set_author(
name=message.author.display_name, url=message.jump_url, icon_url=message.author.display_avatar.url
)
if star_count > 5:
stars = "\N{white medium star}x{:,}".format(star_count)
else:
stars = "\N{white medium star}" * star_count
stars = stars or "\N{no entry sign}"
embed.add_field(
name="Info",
value=f"Star count: {stars}\n"
f"Channel: {message.channel.mention}\n"
f"Author: {message.author.mention}\n"
f"URL: [jump]({message.jump_url})\n"
f"Sent: {discord.utils.format_dt(message.created_at, 'R')}",
inline=False,
)
if message.edited_at:
embed.fields[0].value += "\nLast edited: " + discord.utils.format_dt(message.edited_at, "R")
if message.reference is not None:
try:
ref: discord.Message = await self.bot.get_channel(message.reference.channel_id).fetch_message(
message.reference.message_id
)
except discord.HTTPException:
pass
else:
embed.add_field(
name="In reply to",
value=f"[Message by {ref.author.display_name}]({ref.jump_url}):\n>>> ",
inline=False,
)
field = embed.fields[1]
if not ref.content:
embed.fields[1].value = field.value.replace(":\n>>> ", "")
else:
embed.fields[1].value += textwrap.shorten(ref.content, 1024 - len(field.value), placeholder="...")
if message.attachments:
for file in message.attachments:
name = f"Attachment #{message.attachments.index(file)}"
spoiler = file.is_spoiler()
if not spoiler and file.url.lower().endswith(("png", "jpeg", "jpg", "gif", "webp")) and not embed.image:
embed.set_image(url=file.url)
elif spoiler:
embed.add_field(name=name, value=f"||[{file.filename}]({file.url})||", inline=False)
else:
embed.add_field(name=name, value=f"[{file.filename}]({file.url})", inline=False)
embed.set_footer(text="Starboard threshold for this message was {:.2f}.".format(cap))
return embed
@commands.Cog.listener("on_raw_reaction_add")
@commands.Cog.listener("on_raw_reaction_remove")
async def on_star_add(self, payload: discord.RawReactionActionEvent):
async with self.lock:
if str(payload.emoji) != "\N{white medium star}":
return
message: discord.Message = await self.bot.get_channel(payload.channel_id).fetch_message(payload.message_id)
star_count = [x for x in message.reactions if str(x.emoji) == "\N{white medium star}"]
if not star_count:
star_count = 0
else:
star_count = star_count[0].count
database: Tuple[StarBoardMessage, bool] = await StarBoardMessage.objects.get_or_create(
{"channel": payload.channel_id}, id=payload.message_id
)
entry, created = database
if created:
# noinspection PyUnresolvedReferences
cap = message.channel if "thread" in message.channel.type.name else message.guild
if self.bot.intents.members and hasattr(cap, "members"):
cap = len([x for x in cap.members if not x.bot]) * 0.1
else:
cap = cap.member_count * 0.1
if star_count >= cap:
channel = discord.utils.get(message.guild.text_channels, name="starboard")
if channel and channel.can_send():
msg = await channel.send(embed=await self.generate_starboard_embed(message))
await entry.update(starboard_message=msg.id)
else:
await entry.delete()
return
else:
channel = discord.utils.get(message.guild.text_channels, name="starboard")
if channel and channel.can_send() and entry.starboard_message:
try:
msg = await channel.fetch_message(entry.starboard_message)
except discord.NotFound:
msg = await channel.send(embed=await self.generate_starboard_embed(message))
await entry.update(starboard_message=msg.id)
except discord.HTTPException:
pass
else:
await msg.edit(embed=await self.generate_starboard_embed(message))
@commands.message_command(name="Starboard Info")
@discord.guild_only()
async def get_starboard_info(self, ctx: discord.ApplicationContext, message: discord.Message):
return await ctx.respond(embed=await self.generate_starboard_embed(message))
@commands.command(name="threshold")
@commands.guild_only()
async def get_threshold(self, ctx: commands.Context):
"""Shows you the current starboard threshold"""
if self.bot.intents.members and hasattr(ctx.channel, "members"):
cap = len([x for x in ctx.channel.members if not x.bot]) * 0.1
else:
cap = ctx.channel.member_count * 0.1
return await ctx.reply(f"Messages currently need {c:.2f} stars in this channel to be posted to the starboard.")
def setup(bot):
bot.add_cog(StarBoardCog(bot))

11
main.py
View file

@ -13,7 +13,16 @@ bot = commands.Bot(
) )
bot.training_lock = Lock() bot.training_lock = Lock()
extensions = ["jishaku", "cogs.verify", "cogs.mod", "cogs.events", "cogs.assignments", "cogs.timetable", "cogs.other"] extensions = [
"jishaku",
"cogs.verify",
"cogs.mod",
"cogs.events",
"cogs.assignments",
"cogs.timetable",
"cogs.other",
"cogs.starboard",
]
for ext in extensions: for ext in extensions:
bot.load_extension(ext) bot.load_extension(ext)
console.log(f"Loaded extension [green]{ext}") console.log(f"Loaded extension [green]{ext}")

View file

@ -119,3 +119,20 @@ class Assignments(orm.Model):
submitted: bool submitted: bool
assignees: list[int] assignees: list[int]
# description: Optional[str] # description: Optional[str]
class StarBoardMessage(orm.Model):
tablename = "starboard"
registry = registry
fields = {
"entry_id": orm.UUID(primary_key=True, default=uuid.uuid4),
"id": orm.BigInteger(unique=True),
"channel": orm.BigInteger(),
"starboard_message": orm.BigInteger(default=None, allow_null=True),
}
if TYPE_CHECKING:
entry_id: uuid.UUID
id: int
channel: int
starboard_message: int | None

View file

@ -57,9 +57,7 @@ class VerifyView(View):
f" (originally associated with account {ban.associated_account})" f" (originally associated with account {ban.associated_account})"
) )
await Student.objects.create( await Student.objects.create(
id=existing.student_id, id=existing.student_id, user_id=interaction.user.id, name=existing.name
user_id=interaction.user.id,
name=existing.name
) )
await existing.delete() await existing.delete()
role = discord.utils.find(lambda r: r.name.lower() == "verified", interaction.guild.roles) role = discord.utils.find(lambda r: r.name.lower() == "verified", interaction.guild.roles)
@ -67,10 +65,7 @@ class VerifyView(View):
if role and role < interaction.guild.me.top_role: if role and role < interaction.guild.me.top_role:
await member.add_roles(role, reason="Verified") await member.add_roles(role, reason="Verified")
try: try:
await member.edit( await member.edit(nick=f"{existing.name}", reason="Verified")
nick=f"{existing.name}",
reason="Verified"
)
except discord.HTTPException: except discord.HTTPException:
pass pass
console.log(f"[green]{interaction.user} verified ({interaction.user.id}/{existing.student_id})") console.log(f"[green]{interaction.user} verified ({interaction.user.id}/{existing.student_id})")
@ -118,16 +113,15 @@ class VerifyView(View):
return await interaction.followup.send( return await interaction.followup.send(
"\N{cross mark} Invalid student ID - Failed to verify with regex." "\N{cross mark} Invalid student ID - Failed to verify with regex."
" Please try again with a valid student ID. Make sure it is formatted as `BXXXXXX` " " Please try again with a valid student ID. Make sure it is formatted as `BXXXXXX` "
"(e.g. `B{}`)".format(''.join(str(random.randint(0, 9)) for _ in range(6))), "(e.g. `B{}`)".format("".join(str(random.randint(0, 9)) for _ in range(6))),
delete_after=60 delete_after=60,
) )
ex = await get_or_none(Student, id=st) ex = await get_or_none(Student, id=st)
if ex: if ex:
btn.disabled = False btn.disabled = False
return await interaction.followup.send( return await interaction.followup.send(
"\N{cross mark} Student ID is already associated.", "\N{cross mark} Student ID is already associated.", delete_after=60
delete_after=60
) )
try: try: