Skip to content

Helpermessage and respective changes to helper #38

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 24 commits into
base: rewrite
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
0b03a84
Added giverole and swaprole
Indiance Jul 19, 2023
36c6ceb
Removed the '= None' decorator as well as swaprole command
Indiance Jul 19, 2023
e3a6730
Formatted roles.py to remove everything barring giverole and cog check
Indiance Jul 21, 2023
a42a7c7
Modified code to add logging and format grammatical errors
Indiance Jul 27, 2023
b159437
Changed logger system & subtracted failures from final number
Indiance Jul 28, 2023
9da8046
Added HTTPException error handling and some more punctuation
Indiance Jul 28, 2023
2565b91
edited error handling and added formatting
Indiance Jul 28, 2023
ed32b98
Create helpermessage, which means changing style of config.toml and r…
Indiance Jun 21, 2024
9148adb
Update helpermessage based on helper/dehelper
Indiance Jun 23, 2024
e86af9c
Made changes as per Creative's requests
Indiance Jul 20, 2024
e27c7f4
Merge branch 'rewrite' into rewrite
Indiance Jul 20, 2024
9d6daff
Removing extra format string
Indiance Jul 20, 2024
9b1c5c0
Merge branch 'rewrite' of https://github.com/Indiance/ib.py into rewrite
Indiance Jul 20, 2024
3a650a0
Delete .DS_Store
Indiance Aug 5, 2024
c6da9d7
Delete requirements.txt
Indiance Aug 5, 2024
419c1cd
Delete pyrightconfig.json
Indiance Aug 5, 2024
2dc5735
Incorporating second round of changes
Indiance Dec 26, 2024
013c154
Quick fix for pin/unpin
Indiance Dec 26, 2024
80d8d4c
test commit
Indiance May 25, 2025
93431ad
Revert "Quick fix for pin/unpin"
Indiance May 25, 2025
46e6e7f
Fixing grammatical issues as well as complying with folder restructuring
Indiance May 25, 2025
e3db0a8
Ruff fixes
Indiance May 25, 2025
319ef92
Update .gitignore
Indiance May 25, 2025
9a6d38c
Merge branch 'rewrite' of https://github.com/ib-ai/ib.py into rewrite
Indiance Jun 3, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -130,4 +130,4 @@ dmypy.json

config.json
config.toml
.pdm-python
.pdm-python
2 changes: 1 addition & 1 deletion src/ib_py/cogs/dev.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ async def sync(

await ctx.send(f"Synced the tree to {ret}/{len(guilds)}.")

@commands.group(aliases=['ext'])
@commands.group(aliases=["ext"])
async def extensions(self, ctx: commands.Context):
"""
Utilities for managing loaded extensions.
Expand Down
44 changes: 18 additions & 26 deletions src/ib_py/cogs/helper.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import discord
from discord.ext import commands
import toml
from typing import Union


class Helper(commands.Cog):
def __init__(self, bot: commands.Bot) -> None:
self.bot = bot
self.subjects = toml.load("config.toml")["subjects"]
self.helper_ids = self.subjects.keys()
self.subject_channels = [self.subjects[role] for role in self.helper_ids]
self.subject_channels = self.subjects.keys()
# Ensure that each entry in self.subjects is a list of roles, even if it contains just one role
self.helper_ids = [self.subjects[channel] for channel in self.subject_channels]
self.ctx_menu = discord.app_commands.ContextMenu(
name="Toggle Pin",
callback=self.toggle_pin,
Expand All @@ -18,28 +20,28 @@ def __init__(self, bot: commands.Bot) -> None:
async def cog_unload(self) -> None:
self.bot.tree.remove_command(self.ctx_menu.name, type=self.ctx_menu.type)

async def send_error(self, obj, message):
async def send_error(self, obj: Union[discord.Interaction, commands.Context], message):
if isinstance(obj, commands.Context):
return await obj.send(message)
else:
return await obj.response.send_message(message, ephemeral=True)

async def check_permissions(self, obj):
async def check_permissions(self, obj: Union[discord.Interaction, commands.Context]):
user = obj.author if isinstance(obj, commands.Context) else obj.user
channel = obj.channel.id
user_role_ids = [str(role.id) for role in user.roles]
if not any(role in self.helper_ids for role in user_role_ids):
channel_id = obj.channel.id
user_role_ids = [role.id for role in user.roles]
flat_helper_ids = [role for sublist in self.helper_ids for role in sublist]
if not any(role in flat_helper_ids for role in user_role_ids):
message = "Only subject helpers can pin messages."
await self.send_error(obj, message)
return False
if channel not in self.subject_channels:
channel_list = [int(channel) for channel in list(self.subject_channels)]
if channel_id not in channel_list:
message = "You may only pin messages in subject channels."
await self.send_error(obj, message)
return False
valid_channels = [
self.subjects[role] for role in user_role_ids if role in self.helper_ids
]
if channel not in valid_channels:
valid_roles = [self.subjects[str(channel_id)]]
if not any(role in user_role_ids for role in valid_roles):
message = "You may only pin messages in your respective subject channel."
await self.send_error(obj, message)
return False
Expand Down Expand Up @@ -78,25 +80,13 @@ async def toggle_pin(self, interaction: discord.Interaction, message: discord.Me
"The message could not be unpinned.", ephemeral=True
)

@commands.Cog.listener()
async def on_member_update(self, before: discord.Member, after: discord.Member):
"""
Update helper message based on user helper/dehelper.
"""
...

@commands.hybrid_command()
async def helpermessage(self, ctx: commands.Context):
"""
Send an updating list of helpers for a subject.
"""
raise NotImplementedError("Command requires implementation and permission set-up.")

@commands.hybrid_command()
async def pin(self, ctx: commands.Context, message: discord.Message = None):
"""
Pin a message to a channel.
"""
if message is None:
return await ctx.send("No message has been provided to pin.")
if await self.check_permissions(ctx):
if message.pinned:
return await ctx.send("The message is already pinned.")
Expand All @@ -119,6 +109,8 @@ async def unpin(self, ctx: commands.Context, message: discord.Message = None):
"""
Unpin a message from a channel.
"""
if message is None:
await ctx.send("No message has been provided to unpin.")
if await self.check_permissions(ctx):
if not message.pinned:
return await ctx.send("The message is already unpinned.")
Expand Down
172 changes: 172 additions & 0 deletions src/ib_py/cogs/helpermessage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
import discord
import toml
from discord.ext import commands
from utils.commands import available_subcommands
from utils.pagination import paginated_embed_menus, PaginationView
from db.models import HelperMessage
from typing import AsyncGenerator, Set, Tuple


class Helpermessage(commands.Cog):
def __init__(self, bot: commands.Bot) -> None:
self.bot = bot
self.data = toml.load("config.toml")
self.subjects = self.data["subjects"]
self.description = self.data["description"]
self.helper_roles = [self.subjects[channel] for channel in self.subjects.keys()]

@commands.hybrid_group()
async def helpermessage(self, ctx: commands.Context):
"""
Commands for handling reminders.
"""
await available_subcommands(ctx)

@helpermessage.command()
async def list(self, ctx: commands.Context):
"""
List all active helpermessage embeds.
"""
embed_dict = dict(
title="List of all active helpermessages",
description="Here is a list of all active helpermessages.",
)
channel_ids = await HelperMessage.all().values_list("channel_id", flat=True)
helpermessage_ids = await HelperMessage.all().values_list("message_id", flat=True)
names = [f"<#{channel}>" for channel in channel_ids]
values = [
f"https://discord.com/channels/{ctx.guild.id}/{channel}/{helpermessage}"
for channel, helpermessage in zip(channel_ids, helpermessage_ids)
]
embeds = paginated_embed_menus(names, values, embed_dict=embed_dict)
embed, view = await PaginationView(ctx, embeds).return_paginated_embed_view()
await ctx.send(embed=embed, view=view)

async def embed_getter(
self, edited_roles: Set[discord.Role]
) -> AsyncGenerator[Tuple[discord.Message, discord.Role, discord.Embed], None]:
for role in edited_roles:
for channel, roles in self.subjects.items():
if role.id in roles:
discord_channel = await self.bot.fetch_channel(int(channel))
helpermessage = await discord_channel.fetch_message(
HelperMessage.get(channel_id=discord_channel.id)
)
embed = helpermessage.embeds[0]
yield helpermessage, role, embed

@commands.Cog.listener()
async def on_member_update(self, before: discord.Member, after: discord.Member):
"""
Update the respective helpermessage embed based on a user becoming/retiring from helper.
"""
# checking if there has been a change in roles
if before.roles != after.roles:
before_roles = set(before.roles)
after_roles = set(after.roles)
# checking whether roles were removed or added
added_roles = after_roles - before_roles
removed_roles = before_roles - after_roles
# routine that runs to add the role
if added_roles:
async for helpermessage, role, embed in self.embed_getter(added_roles):
new_embed = discord.Embed(description=embed.description)
for field in embed.fields:
if field.name == f"**{role.name}**":
new_embed.add_field(
name=field.name, value=field.value + f"\n{after.mention}"
)
else:
new_embed.add_field(name=field.name, value=field.value)
await helpermessage.edit(embed=new_embed)

# routine that runs to remove roles
if removed_roles:
async for helpermessage, role, embed in self.embed_getter(removed_roles):
new_embed = discord.Embed(description=embed.description)
for field in embed.fields:
if field.name == f"**{role.name}**":
members = field.value.split("\n")
new_member_list = [
member for member in members if member != after.mention
]
new_embed.add_field(
name=field.name, value="\n".join(new_member_list)
)
else:
new_embed.add_field(name=field.name, value=field.value)
await helpermessage.edit(embed=new_embed)

@helpermessage.command()
async def create(
self,
ctx: commands.Context,
channel: discord.TextChannel,
helper_roles: commands.Greedy[discord.Role],
):
"""
Create the helpermessage embed for a specific channel given the channel and helper role.
"""
# Create the embed that will be pinned in the message
description = (
self.description["description"] + f"\n\n **Subject helpers for {channel.name}:**"
)
helper_embed = discord.Embed(description=description)
# add the field displaying the helpers
for role in helper_roles:
subject_helpers = [
member.mention for member in ctx.guild.members if role in member.roles
]
helper_embed.add_field(
name=f"**{role.name}**", value="\n".join(subject_helpers), inline=False
)
# send the message
helpermessage = await channel.send(embed=helper_embed)
# pin the message
await helpermessage.pin()
# save to the database
values = dict(
message_id=helpermessage.id,
channel_id=channel.id,
role_id=[role.id for role in helper_roles],
)
helpermessage = await HelperMessage.create(**values)
await ctx.send("The helpermessage has been created!")

@helpermessage.command()
async def delete(self, ctx: commands.Context, channel: discord.TextChannel):
"""
Delete the helpermessage embed for a specific channel given the channel and helper role
"""
helpermessage = await HelperMessage.get_or_none(channel_id=channel.id)
if not helpermessage:
await ctx.send("The helpermessage does not exist!")
return
discord_channel = await self.bot.fetch_channel(helpermessage.channel_id)
discord_message = await discord_channel.fetch_message(helpermessage.message_id)
await discord_message.delete()
await helpermessage.delete()
await ctx.send(
f"The helpermessage for the channel <#{discord_channel.id}> has been successfully deleted!"
)

@helpermessage.command()
async def edit(self, ctx: commands.Context, *, content: str):
"""
Edit the content in the helpermessage
"""
self.description["description"] = content
channel_ids = await HelperMessage.all().values_list("channel_id", flat=True)
for channel_id in channel_ids:
discord_channel = await self.bot.fetch_channel(channel_id)
helpermessage = await discord_channel.fetch_message(channel_id=channel_id)
embed = helpermessage.embeds[0]
embed.description = (
content + f"\n\n **Subject helpers for {discord_channel.name}:**"
)
await helpermessage.edit(embed=embed)
await ctx.send("Updated messages successfully")


async def setup(bot: commands.Bot):
await bot.add_cog(Helpermessage(bot))
6 changes: 3 additions & 3 deletions src/ib_py/cogs/public.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,11 +130,11 @@ async def userinfo(self, ctx: commands.Context, member: discord.Member = None):
)
embed.add_field(name="**Nickname**", value=f"{member.display_name}", inline=False)
embed.add_field(
name="**Server join date**", value=f'{member.joined_at.strftime("%c")}'
name="**Server join date**", value=f"{member.joined_at.strftime('%c')}"
)
embed.add_field(
name="**Account creation date**",
value=f'{member.created_at.strftime("%c")}',
value=f"{member.created_at.strftime('%c')}",
inline=False,
)
embed.add_field(name="**Discord ID**", value=f"{member.id}")
Expand All @@ -148,7 +148,7 @@ async def userinfo(self, ctx: commands.Context, member: discord.Member = None):
if member.premium_since:
embed.add_field(
name="**Nitro boosting since**",
value=f'{member.premium_since.strftime("%c")}',
value=f"{member.premium_since.strftime('%c')}",
inline=False,
)
embed.set_thumbnail(url=member.display_avatar.url)
Expand Down
2 changes: 1 addition & 1 deletion src/ib_py/cogs/reminder.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ async def create(self, ctx: commands.Context, terminus: DatetimeConverter, *, me
task = asyncio.create_task(self.handle_reminder(ctx.author, reminder))
self.active[reminder.reminder_id] = task
task.add_done_callback(self.removal_callback(reminder.reminder_id))
await ctx.send(f'Reminder set for {format_dt(terminus)} ({format_dt(terminus, "R")}).')
await ctx.send(f"Reminder set for {format_dt(terminus)} ({format_dt(terminus, 'R')}).")

@reminder.command(aliases=["remove"])
async def delete(self, ctx: commands.Context, id: int):
Expand Down
5 changes: 3 additions & 2 deletions src/ib_py/cogs/roles.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ async def giverole(
role_members = [
member for member in ctx.guild.members if existing_role in member.roles
]
await ctx.send(f"Adding roles to {len[role_members]} members.")
for member in role_members:
try:
await member.add_roles(new_role)
Expand All @@ -33,10 +34,10 @@ async def giverole(
logger.info(f"Could not add role to {member.name}.")
failure_count += 1
await ctx.send(
f"Successfully added roles to {len(role_members) - failure_count} members."
f"Successfully added roles to {len(role_members) - failure_count} members with a failure count of {failure_count}."
)
logger.info(
f"Given {new_role.name} role to {len(role_members) - failure_count} members."
f"Given {new_role.name} role to {len(role_members) - failure_count} members with a failure count of {failure_count}."
)


Expand Down
4 changes: 2 additions & 2 deletions src/ib_py/cogs/updates.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ async def create(
update_content = "\n- ".join([update_date, *updates])
await updates_channel.send(content=update_content)

await ctx.send(f'Added update(s): `{"`,`".join(updates)}`')
await ctx.send(f"Added update(s): `{'`,`'.join(updates)}`")

@update.command(aliases=["remove"])
async def delete(
Expand Down Expand Up @@ -126,7 +126,7 @@ async def delete(
new = "\n- ".join(remaining)
await message.edit(content=new)

await ctx.send(f'Removed update(s): `{"`,`".join(removed)}`')
await ctx.send(f"Removed update(s): `{'`,`'.join(removed)}`")


async def setup(bot: commands.Bot):
Expand Down
2 changes: 1 addition & 1 deletion src/ib_py/db/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ class Meta:
helper_message_id = fields.IntField(pk=True)
channel_id = fields.BigIntField()
message_id = fields.BigIntField()
role_id = fields.BigIntField()
role_id = ArrayField()


# Member Tables
Expand Down
2 changes: 1 addition & 1 deletion src/ib_py/utils/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,4 @@ async def available_subcommands(ctx: commands.Context):
f'Command "{cmd.name}" check threw error, discarded in {ctx.command.name} group subcommand list.',
exc_info=True,
)
await ctx.send(f'Available subcommands: {", ".join(subcmds)}.')
await ctx.send(f"Available subcommands: {', '.join(subcmds)}.")
2 changes: 1 addition & 1 deletion src/ib_py/utils/pagination.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ def paginated_embed_menus(
items += 1
embeds.append(current)
for page, embed in enumerate(embeds):
embed.set_footer(text=f"Page {page+1}/{pages}")
embed.set_footer(text=f"Page {page + 1}/{pages}")

return embeds

Expand Down