Skip to content

Commit 5b4d35e

Browse files
committed
Initial base implementation for the !tunnel command (with default least active off-topic channel selection)
1 parent 75beff8 commit 5b4d35e

1 file changed

Lines changed: 107 additions & 0 deletions

File tree

bot/exts/utils/tunnel.py

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
from operator import itemgetter
2+
3+
import discord
4+
from discord.ext import commands
5+
from discord.ext.commands import BadArgument
6+
7+
from bot.bot import Bot
8+
from bot.constants import Channels
9+
10+
CHANNEL_IDS = (Channels.off_topic_0, Channels.off_topic_1, Channels.off_topic_2)
11+
12+
13+
class Tunnel(commands.Cog):
14+
"""Enables conversation redirection between channels."""
15+
16+
def __init__(self, bot: Bot) -> None:
17+
self.bot = bot
18+
self.channel_id_to_timestamp: dict[int, float] = dict.fromkeys(CHANNEL_IDS, 0)
19+
20+
for channel_id in CHANNEL_IDS:
21+
channel = bot.get_channel(channel_id)
22+
if channel is None:
23+
continue
24+
25+
if not isinstance(channel, discord.TextChannel):
26+
raise AssertionError
27+
28+
last_message = channel.last_message
29+
if last_message is None:
30+
continue
31+
32+
self.channel_id_to_timestamp[channel_id] = last_message.created_at.timestamp()
33+
34+
@commands.command()
35+
async def tunnel(
36+
self,
37+
ctx: commands.Context,
38+
destination_channel: discord.TextChannel | None,
39+
source_channel: discord.TextChannel | None,
40+
) -> None:
41+
"""Creates a tunnel."""
42+
if ctx.guild is None:
43+
raise AssertionError
44+
45+
if destination_channel is None:
46+
least_active_channel_id = self.get_least_active_channel_id(ctx.channel.id)
47+
least_active_channel = await ctx.guild.fetch_channel(least_active_channel_id)
48+
if not isinstance(least_active_channel, discord.TextChannel):
49+
raise AssertionError
50+
51+
destination_channel = least_active_channel
52+
53+
if source_channel is None:
54+
if not isinstance(ctx.channel, discord.TextChannel):
55+
raise BadArgument(
56+
f"The current channel of type '{ctx.channel.type}' is not a valid source channel "
57+
"and no explicit source channel was provided"
58+
)
59+
60+
source_channel = ctx.channel
61+
62+
if not isinstance(ctx.author, discord.Member):
63+
raise AssertionError
64+
65+
if not source_channel.permissions_for(ctx.author).send_messages:
66+
raise BadArgument(f"You don't have permission to send messages in {source_channel.jump_url}")
67+
if not destination_channel.permissions_for(ctx.author).send_messages:
68+
raise BadArgument(f"You don't have permission to send messages in {destination_channel.jump_url}")
69+
70+
if source_channel.id == destination_channel.id:
71+
raise BadArgument("Source and destination channels cannot be the same")
72+
73+
source_message_template = "➡️ Conversation tunneled to {location}"
74+
destination_message_template = "↩️ Conversation tunneled from {location}"
75+
76+
source_message = await source_channel.send(
77+
content=source_message_template.format(location=destination_channel.jump_url)
78+
)
79+
destination_message = await destination_channel.send(
80+
content=destination_message_template.format(location=source_message.jump_url)
81+
)
82+
await source_message.edit(content=source_message_template.format(location=destination_message.jump_url))
83+
84+
@commands.Cog.listener()
85+
async def on_message(self, message: discord.Message) -> None:
86+
"""Determines least active off-topic channel to default to."""
87+
if message.channel.id not in CHANNEL_IDS:
88+
return
89+
90+
self.channel_id_to_timestamp[message.channel.id] = message.created_at.timestamp()
91+
92+
def get_least_active_channel_id(self, current_channel_id: int) -> int:
93+
"""Gets least active off-topic channel."""
94+
channel_id, _ = min(
95+
[
96+
(channel_id, timestamp)
97+
for channel_id, timestamp in self.channel_id_to_timestamp.items()
98+
if channel_id != current_channel_id
99+
],
100+
key=itemgetter(1),
101+
)
102+
return channel_id
103+
104+
105+
async def setup(bot: Bot) -> None:
106+
"""Load the Tunnel cog."""
107+
await bot.add_cog(Tunnel(bot))

0 commit comments

Comments
 (0)