-
Notifications
You must be signed in to change notification settings - Fork 12
Threads with friends #377
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
Merged
Merged
Threads with friends #377
Changes from all commits
Commits
Show all changes
62 commits
Select commit
Hold shift + click to select a range
ecaf49c
initial work
MattyTheHacker cc9c12a
bump deps
MattyTheHacker 19976d0
kinda works
MattyTheHacker 360390d
extract functionality to method
MattyTheHacker 28ea9e3
Finalise
MattyTheHacker b6b6f94
yeet unreachable
MattyTheHacker f504402
roll back lock file
MattyTheHacker 2eda160
fix ruff warning
MattyTheHacker a43795c
Add extra try catch just in case.
MattyTheHacker 1cfea65
Merge branch 'main' into threads-with-friends
MattyTheHacker 2c5cee3
Merge branch 'main' into threads-with-friends
MattyTheHacker ac11a25
Merge branch 'main' into threads-with-friends
MattyTheHacker 219ad1d
Merge branch 'main' into threads-with-friends
MattyTheHacker 134a43d
fix lock file
MattyTheHacker 5d315fc
refactor to make 2 separate commands
MattyTheHacker 6c137f4
add committee lock
MattyTheHacker 5d585e6
Merge branch 'main' into threads-with-friends
MattyTheHacker 7658c9a
Merge branch 'main' into threads-with-friends
MattyTheHacker 5cdfcdc
on todays episode of scope creep !
MattyTheHacker 8eb19fd
Bump python project action version
MattyTheHacker f6796a5
Update tests.yaml
MattyTheHacker ce291f9
Merge branch 'bump-action-version' into threads-with-friends
MattyTheHacker 1c8d21b
update deps
MattyTheHacker fe9c31f
Merge branch 'main' into threads-with-friends
MattyTheHacker 386282f
lock
MattyTheHacker 3a87d23
Merge branch 'main' into threads-with-friends
MattyTheHacker fc5db72
lock
MattyTheHacker 81347d3
Implement check for @ symbol
MattyTheHacker 2bf3294
Merge branch 'main' into threads-with-friends
MattyTheHacker d88b5f2
Add config option to disable adding committee to threads
MattyTheHacker 8481b1f
Merge branch 'main' into threads-with-friends
MattyTheHacker c047597
Merge branch 'main' into threads-with-friends
MattyTheHacker 8dda974
Rename file
MattyTheHacker e8d47e7
Merge branch 'main' into threads-with-friends
MattyTheHacker f253b8b
suck my cunt pre commit f*ck off
MattyTheHacker 4cbaa2e
fix
MattyTheHacker b559f0f
Fix mypy errors AGAIN BECAUSE THE PRE-COMMIT FUCKED IT
MattyTheHacker a8546e7
Refactor the separate commands for role and user to one
MattyTheHacker 38b88e2
Fix new lines
MattyTheHacker dcedbf3
Ruff format
MattyTheHacker 933e462
Remove doc string from private method
MattyTheHacker c5785af
Merge branch 'main' into threads-with-friends
MattyTheHacker df53007
Implement multiple roles and users adding at once
MattyTheHacker c5aa18d
Merge branch 'threads-with-friends' of github.com:CSSUoB/TeX-Bot-Py-V…
MattyTheHacker 76a0729
Fix parameter names
MattyTheHacker 54cab7d
Fix bad logic
MattyTheHacker fabc7a8
Update committee role syntax
MattyTheHacker 6b3d090
Merge branch 'main' into threads-with-friends
MattyTheHacker 02d48fe
Merge branch 'main' into threads-with-friends
MattyTheHacker d33d3f2
Merge branch 'main' into threads-with-friends
MattyTheHacker c95eb91
Fixes
MattyTheHacker dd9ad81
Improve logic
MattyTheHacker c69ea0c
use ctx.channel directly
MattyTheHacker 2c7fc5c
Update config.py
MattyTheHacker c2669ea
Update cogs/add_users_to_threads_and_channels.py
MattyTheHacker 22aab60
Fix method call
MattyTheHacker bab4957
improve method flow
MattyTheHacker e07c5c8
fix mypy errors
MattyTheHacker d600493
Merge branch 'main' into threads-with-friends
MattyTheHacker 1cd8418
fix bad loop exit logic
MattyTheHacker e537f75
Update cogs/add_users_to_threads_and_channels.py
MattyTheHacker 2dadbbe
Update cogs/add_users_to_threads_and_channels.py
MattyTheHacker File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,299 @@ | ||
| """Contains Cog classes for adding users and roles to threads.""" | ||
|
|
||
| import logging | ||
| from collections.abc import Iterable | ||
| from typing import TYPE_CHECKING | ||
|
|
||
| import discord | ||
|
|
||
| from config import settings | ||
| from exceptions import GuestRoleDoesNotExistError, GuildDoesNotExistError | ||
| from utils import TeXBotBaseCog | ||
| from utils.error_capture_decorators import capture_guild_does_not_exist_error | ||
|
|
||
| if TYPE_CHECKING: | ||
| from collections.abc import Sequence | ||
| from collections.abc import Set as AbstractSet | ||
| from logging import Logger | ||
| from typing import Final | ||
|
|
||
| from utils import TeXBotApplicationContext, TeXBotAutocompleteContext | ||
|
|
||
|
|
||
| __all__: "Sequence[str]" = ("AddUsersToThreadsAndChannelsCommandCog",) | ||
|
|
||
|
|
||
| logger: "Final[Logger]" = logging.getLogger("TeX-Bot") | ||
|
|
||
|
|
||
| class AddUsersToThreadsAndChannelsCommandCog(TeXBotBaseCog): | ||
| """Cog for adding users to threads.""" | ||
|
|
||
| @staticmethod | ||
| async def autocomplete_get_members( | ||
| ctx: "TeXBotAutocompleteContext", | ||
| ) -> "AbstractSet[discord.OptionChoice] | AbstractSet[str]": | ||
| """Autocomplete callable that generates the set of available selectable members.""" | ||
| try: | ||
| main_guild: discord.Guild = ctx.bot.main_guild | ||
| guest_role: discord.Role = await ctx.bot.guest_role | ||
| except (GuildDoesNotExistError, GuestRoleDoesNotExistError): | ||
| return set() | ||
|
|
||
| members: set[discord.Member] = { | ||
| member | ||
| for member in main_guild.members | ||
| if not member.bot and guest_role in member.roles | ||
| } | ||
|
|
||
| if not ctx.value or ctx.value.startswith("@"): | ||
| return { | ||
| discord.OptionChoice(name=f"@{member.name}", value=str(member.id)) | ||
| for member in members | ||
| } | ||
|
|
||
| return { | ||
| discord.OptionChoice(name=member.name, value=str(member.id)) for member in members | ||
| } | ||
|
|
||
| @staticmethod | ||
| async def autocomplete_get_roles( | ||
| ctx: "TeXBotAutocompleteContext", | ||
| ) -> "AbstractSet[discord.OptionChoice] | AbstractSet[str]": | ||
| """Autocomplete callable that generates the set of available selectable roles.""" | ||
| try: | ||
| main_guild: discord.Guild = ctx.bot.main_guild | ||
| except GuildDoesNotExistError: | ||
| return set() | ||
|
|
||
| if not ctx.value or ctx.value.startswith("@"): | ||
| return { | ||
| discord.OptionChoice(name=f"@{role.name}", value=str(role.id)) | ||
| for role in main_guild.roles | ||
| } | ||
|
|
||
| return { | ||
| discord.OptionChoice(name=role.name, value=str(role.id)) | ||
| for role in main_guild.roles | ||
| } | ||
|
|
||
| async def add_users_or_roles_silently( | ||
| self, | ||
| users_or_roles: discord.Member | ||
| | discord.Role | ||
| | Iterable[discord.Member] | ||
| | Iterable[discord.Role], | ||
| channel_or_thread: discord.Thread | discord.TextChannel, | ||
| ) -> None: | ||
| """Add a user or role to a thread without pinging them.""" | ||
| if isinstance(users_or_roles, Iterable): | ||
| user_or_role: discord.Role | discord.Member | ||
| for user_or_role in users_or_roles: | ||
| await self.add_users_or_roles_silently( | ||
| users_or_roles=user_or_role, channel_or_thread=channel_or_thread | ||
| ) | ||
| return | ||
|
|
||
| if isinstance(channel_or_thread, discord.Thread): | ||
| message: discord.Message = await channel_or_thread.send( | ||
| content=f"Adding {users_or_roles!r} to thread...", | ||
| silent=True, | ||
| ) | ||
| await message.edit(content=f"{users_or_roles.mention}") | ||
| await message.delete(delay=1) | ||
| return | ||
|
|
||
| await channel_or_thread.set_permissions( | ||
| target=users_or_roles, | ||
| read_messages=True, | ||
| send_messages=True, | ||
| reason=f"User {self.bot.user} used TeX-Bot slash-command `add_users_to_channel`.", | ||
| ) | ||
|
|
||
| async def add_users_or_roles_with_ping( | ||
| self, | ||
| users_or_roles: discord.Member | ||
| | discord.Role | ||
| | Iterable[discord.Member] | ||
| | Iterable[discord.Role], | ||
| channel_or_thread: discord.Thread | discord.TextChannel, | ||
| ) -> None: | ||
| """Add a user or role to a thread and ping them.""" | ||
| if isinstance(users_or_roles, Iterable): | ||
| user_or_role: discord.Role | discord.Member | ||
| for user_or_role in users_or_roles: | ||
| await self.add_users_or_roles_with_ping( | ||
| users_or_roles=user_or_role, channel_or_thread=channel_or_thread | ||
| ) | ||
| return | ||
|
|
||
| if isinstance(channel_or_thread, discord.Thread): | ||
| if isinstance(users_or_roles, discord.Member): | ||
| try: | ||
| await channel_or_thread.add_user(user=users_or_roles) | ||
| except discord.NotFound: | ||
| logger.debug( | ||
| "User: %s has blocked the bot and " | ||
| "therefore could not be added to thread: %s.", | ||
| users_or_roles, | ||
| channel_or_thread, | ||
| ) | ||
| return | ||
|
|
||
| member: discord.Member | ||
| for member in users_or_roles.members: | ||
| try: | ||
| await channel_or_thread.add_user(member) | ||
| except discord.NotFound: | ||
| logger.debug( | ||
| "User: %s has blocked the bot and " | ||
| "therefore could not be added to thread: %s.", | ||
| member, | ||
| channel_or_thread, | ||
| ) | ||
| return | ||
|
|
||
| await channel_or_thread.set_permissions( | ||
| target=users_or_roles, | ||
| read_messages=True, | ||
| send_messages=True, | ||
| reason=f"User {self.bot.user} used TeX-Bot slash-command `add_users_to_channel`.", | ||
| ) | ||
|
|
||
| await channel_or_thread.send( | ||
| content=f"{users_or_roles.mention} has been added to the channel.", | ||
| ) | ||
|
|
||
| @TeXBotBaseCog.listener() | ||
| @capture_guild_does_not_exist_error | ||
| async def on_thread_create(self, thread: discord.Thread) -> None: | ||
| """Add users to a thread when it is created.""" | ||
| # NOTE: Shortcut accessors are placed at the top of the function, so that the exceptions they raise are displayed before any further errors may be sent | ||
| committee_role: discord.Role = await self.bot.committee_role | ||
| committee_elect_role: discord.Role = await self.bot.committee_elect_role | ||
|
|
||
| if ( | ||
| thread.parent is None | ||
| or thread.parent.category is None | ||
| or "committee" not in thread.parent.category.name.lower() | ||
| or not settings["AUTO_ADD_COMMITTEE_TO_THREADS"] | ||
| ): | ||
| return | ||
|
|
||
| await self.add_users_or_roles_silently( | ||
| users_or_roles=(committee_role, committee_elect_role), channel_or_thread=thread | ||
| ) | ||
|
|
||
| @discord.slash_command( # type: ignore[no-untyped-call, misc] | ||
| name="add_users_to_channel", | ||
| description="Adds selected users to a channel or thread.", | ||
| ) | ||
| @discord.option( # type: ignore[no-untyped-call, misc] | ||
| name="user", | ||
| description="The user to add to the channel.", | ||
| input_type=str, | ||
| autocomplete=discord.utils.basic_autocomplete(autocomplete_get_members), # type: ignore[arg-type] | ||
| required=True, | ||
| parameter_name="user_id_str", | ||
| ) | ||
| @discord.option( # type: ignore[no-untyped-call, misc] | ||
| name="silent", | ||
| description="Whether the users being added should be pinged or not.", | ||
| input_type=bool, | ||
| required=False, | ||
| parameter_name="silent", | ||
| ) | ||
| async def add_user_to_channel( # type: ignore[misc] | ||
| self, | ||
| ctx: "TeXBotApplicationContext", | ||
| user_id_str: str, | ||
| silent: bool, # noqa: FBT001 | ||
| ) -> None: | ||
| """Add users or roles to a channel.""" | ||
| if not isinstance(ctx.channel, (discord.TextChannel, discord.Thread)): | ||
| await self.command_send_error( | ||
| ctx=ctx, | ||
| message="This command currently only supports text channels or threads.", | ||
| ) | ||
| return | ||
|
|
||
| try: | ||
| user_to_add: discord.Member = await self.bot.get_member_from_str_id(user_id_str) | ||
| except ValueError: | ||
| logger.debug("User ID: %s is not a valid ID.", user_id_str) | ||
| await ctx.respond(content=f"The user: {user_id_str} is not valid.") | ||
| return | ||
|
|
||
| if silent: | ||
| await self.add_users_or_roles_silently(user_to_add, ctx.channel) | ||
| else: | ||
| await self.add_users_or_roles_with_ping(user_to_add, ctx.channel) | ||
|
|
||
| await ctx.respond( | ||
| content=( | ||
| f"Successfully added {user_to_add.mention} " | ||
| f"to the channel: {ctx.channel.mention}." | ||
| ) | ||
| ) | ||
|
|
||
| @discord.slash_command( # type: ignore[no-untyped-call, misc] | ||
| name="add_role_to_channel", | ||
| description="Adds the selected role and it's users to a channel or thread.", | ||
| ) | ||
| @discord.option( # type: ignore[no-untyped-call, misc] | ||
| name="role", | ||
| description="The role to add to the channel.", | ||
| input_type=str, | ||
| autocomplete=discord.utils.basic_autocomplete(autocomplete_get_roles), # type: ignore[arg-type] | ||
| required=True, | ||
| parameter_name="role_id_str", | ||
| ) | ||
| @discord.option( # type: ignore[no-untyped-call, misc] | ||
| name="silent", | ||
| description="Whether the users being added should be pinged or not.", | ||
| input_type=bool, | ||
| required=False, | ||
| parameter_name="silent", | ||
| ) | ||
| async def add_role_to_channel( # type: ignore[misc] | ||
| self, | ||
| ctx: "TeXBotApplicationContext", | ||
| role_id_str: str, | ||
| silent: bool, # noqa: FBT001 | ||
| ) -> None: | ||
| """Command to add a role to a channel.""" | ||
| if not isinstance(ctx.channel, discord.Thread) and not isinstance( | ||
| ctx.channel, discord.TextChannel | ||
| ): | ||
| await self.command_send_error( | ||
| ctx=ctx, message="This command can only be used in a text channel or thread." | ||
| ) | ||
| return | ||
|
|
||
| main_guild: discord.Guild = ctx.bot.main_guild | ||
|
|
||
| try: | ||
| role_id: int = int(role_id_str) | ||
| except ValueError: | ||
| logger.debug("Role ID: %s is not a valid ID.", role_id_str) | ||
| await ctx.respond(content=f"The role: {role_id_str} is not valid.") | ||
| return | ||
|
|
||
| role_to_add: discord.Role | None = discord.utils.get(main_guild.roles, id=role_id) | ||
|
|
||
| if role_to_add is None: | ||
| await self.command_send_error( | ||
| ctx=ctx, | ||
| message=f"The role: <@{role_id}> is not valid or couldn't be found.", | ||
| ) | ||
| return | ||
|
|
||
| if silent: | ||
| await self.add_users_or_roles_silently(role_to_add, ctx.channel) | ||
| else: | ||
| await self.add_users_or_roles_with_ping(role_to_add, ctx.channel) | ||
|
|
||
| await ctx.respond( | ||
| content=f"Role {role_to_add.mention} has been added to the channel.", | ||
| ephemeral=True, | ||
| ) | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.