Skip to content

Commit 185273c

Browse files
authored
Add channel management extension with initial 'channel clone' command (#26)
1 parent 381ebd2 commit 185273c

File tree

1 file changed

+131
-0
lines changed

1 file changed

+131
-0
lines changed

pcbot/exts/channel_manager.py

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
"""This file is a part of the source code for PygameCommunityBot.
2+
3+
Copyright (c) 2022-present pygame-community
4+
5+
Bot extension for Discord channel management.
6+
"""
7+
8+
import discord
9+
from discord.ext import commands
10+
11+
import snakecore
12+
from snakecore.commands.decorators import flagconverter_kwargs
13+
from snakecore.commands.converters import CodeBlock, String, Parens, TimeDelta
14+
15+
from ..base import BaseExtensionCog
16+
17+
BotT = snakecore.commands.Bot | snakecore.commands.AutoShardedBot
18+
19+
20+
async def clone_forum(
21+
forum: discord.ForumChannel, new_name: str | None = None, reason: str | None = None
22+
) -> discord.ForumChannel:
23+
return await forum.guild.create_forum(
24+
name=new_name if new_name else forum.name,
25+
topic=forum.topic or discord.utils.MISSING,
26+
category=forum.category,
27+
position=forum.position + 1,
28+
slowmode_delay=forum.slowmode_delay,
29+
overwrites=(
30+
discord.utils.MISSING if forum.permissions_synced else forum.overwrites
31+
), # type: ignore
32+
default_auto_archive_duration=forum.default_auto_archive_duration,
33+
default_thread_slowmode_delay=forum.default_thread_slowmode_delay,
34+
default_sort_order=(
35+
forum.default_sort_order
36+
if forum.default_sort_order is not None
37+
else discord.utils.MISSING
38+
),
39+
default_reaction_emoji=(
40+
forum.default_reaction_emoji
41+
if forum.default_reaction_emoji is not None
42+
else discord.utils.MISSING
43+
),
44+
default_layout=forum.default_layout,
45+
available_tags=(
46+
forum.available_tags if forum.available_tags else discord.utils.MISSING
47+
),
48+
reason=reason,
49+
)
50+
51+
52+
async def clone_category(
53+
category: discord.CategoryChannel,
54+
new_name: str | None,
55+
clone_channels: bool = True,
56+
reason: str | None = None,
57+
):
58+
new_category = await category.clone(name=new_name)
59+
60+
if clone_channels:
61+
for channel in category.channels:
62+
if isinstance(channel, discord.ForumChannel):
63+
channel_clone = await clone_forum(
64+
channel, new_name=new_name, reason=reason
65+
)
66+
else:
67+
channel_clone = await channel.clone()
68+
69+
await channel_clone.move(category=new_category) # type: ignore
70+
71+
return new_category
72+
73+
74+
class ChannelManagerCog(BaseExtensionCog, name="channels"):
75+
"""Channel management commands."""
76+
77+
@commands.group(invoke_without_command=True)
78+
async def channel(self, ctx: commands.Context[BotT]):
79+
pass
80+
81+
@channel.command(
82+
usage="<channel: Channel> [new_name: Text[100]] [deep_clone_category: yes|no]",
83+
)
84+
@flagconverter_kwargs()
85+
async def channel_clone(
86+
self,
87+
ctx: commands.Context[BotT],
88+
channel: (
89+
discord.TextChannel
90+
| discord.VoiceChannel
91+
| discord.ForumChannel
92+
| discord.StageChannel
93+
| discord.CategoryChannel
94+
),
95+
new_name: str | None = None,
96+
deep_clone_category: bool = False,
97+
):
98+
"""Clone the specified channel. More reliable than Discord's built-in channel cloning system.
99+
100+
__**Parameters:**__
101+
102+
**`<channel: Channel>`**
103+
> The channel to clone.
104+
105+
**`[new_name: Text[100]]`**
106+
> The new name of the cloned channel. Must not exceed 100 characters.
107+
108+
**`[deep_clone_category: yes|no]`**
109+
> Whether to clone all channels within a specified category channel.
110+
> Defaults to 'no'.
111+
"""
112+
if isinstance(channel, discord.CategoryChannel):
113+
clone = await clone_category(
114+
channel,
115+
new_name=new_name,
116+
clone_channels=deep_clone_category,
117+
reason=f"Channel {channel.name} ({channel.mention}) cloned as "
118+
f"{clone.name} ({clone.mention}) upon request by " # type: ignore
119+
f"{ctx.author.name} ({ctx.author.mention})",
120+
)
121+
elif isinstance(channel, discord.ForumChannel):
122+
clone = await clone_forum(
123+
channel,
124+
reason=f"Channel created as clone of #{channel.name} "
125+
f"({channel.mention}) upon request by {ctx.author.name} "
126+
f"({ctx.author.mention})",
127+
)
128+
129+
130+
async def setup(bot: BotT, color: int | discord.Color = 0):
131+
await bot.add_cog(ChannelManagerCog(bot, theme_color=color))

0 commit comments

Comments
 (0)