Repository: Cog-Creators/Red-DiscordBot

Issue:
Make `[p]streamalert list` show platform, allow to pass target channel to `[p]streamalert` commands
# Feature request

#### Select the type of feature you are requesting:

- [ ] Cog
- [x] Command
- [ ] API functionality

#### Describe your requested feature

Cog: Streams

Command: `streamalert list`
Feature: Would be nice if this command also showed what platform the channel you add is on.

Command: `streamalert <platform> [channel_name] [channel]`
Feature: Maybe add so you can specify what channel you want the alert to be in so you don't have to 
use the command in the alert channel. Maybe same for the `channel` subcommand toggle.




Discussion:

Resolving patch:
diff --git a/redbot/cogs/streams/streams.py b/redbot/cogs/streams/streams.py
--- a/redbot/cogs/streams/streams.py
+++ b/redbot/cogs/streams/streams.py
@@ -1,4 +1,5 @@
 import discord
+from redbot.core.utils.chat_formatting import humanize_list
 from redbot.core.bot import Red
 from redbot.core import checks, commands, Config
 from redbot.core.i18n import cog_i18n, Translator, set_contextual_locales_from_guild
@@ -250,7 +251,7 @@ async def check_online(
         except OfflineStream:
             await ctx.send(_("That user is offline."))
         except StreamNotFound:
-            await ctx.send(_("That channel doesn't seem to exist."))
+            await ctx.send(_("That user doesn't seem to exist."))
         except InvalidTwitchCredentials:
             await ctx.send(
                 _("The Twitch token is either invalid or has not been set. See {command}.").format(
@@ -298,29 +299,43 @@ async def streamalert(self, ctx: commands.Context):
         pass
 
     @streamalert.group(name="twitch", invoke_without_command=True)
-    async def _twitch(self, ctx: commands.Context, channel_name: str):
+    async def _twitch(
+        self,
+        ctx: commands.Context,
+        channel_name: str,
+        discord_channel: discord.TextChannel = None,
+    ):
         """Manage Twitch stream notifications."""
-        await ctx.invoke(self.twitch_alert_channel, channel_name)
+        await ctx.invoke(self.twitch_alert_channel, channel_name, discord_channel)
 
     @_twitch.command(name="channel")
-    async def twitch_alert_channel(self, ctx: commands.Context, channel_name: str):
-        """Toggle alerts in this channel for a Twitch stream."""
+    async def twitch_alert_channel(
+        self, ctx: commands.Context, channel_name: str, discord_channel: discord.TextChannel = None
+    ):
+        """Toggle alerts in this or the given channel for a Twitch stream."""
         if re.fullmatch(r"<#\d+>", channel_name):
             await ctx.send(
                 _("Please supply the name of a *Twitch* channel, not a Discord channel.")
             )
             return
-        await self.stream_alert(ctx, TwitchStream, channel_name.lower())
+        await self.stream_alert(ctx, TwitchStream, channel_name.lower(), discord_channel)
 
     @streamalert.command(name="youtube")
-    async def youtube_alert(self, ctx: commands.Context, channel_name_or_id: str):
+    async def youtube_alert(
+        self,
+        ctx: commands.Context,
+        channel_name_or_id: str,
+        discord_channel: discord.TextChannel = None,
+    ):
         """Toggle alerts in this channel for a YouTube stream."""
-        await self.stream_alert(ctx, YoutubeStream, channel_name_or_id)
+        await self.stream_alert(ctx, YoutubeStream, channel_name_or_id, discord_channel)
 
     @streamalert.command(name="picarto")
-    async def picarto_alert(self, ctx: commands.Context, channel_name: str):
+    async def picarto_alert(
+        self, ctx: commands.Context, channel_name: str, discord_channel: discord.TextChannel = None
+    ):
         """Toggle alerts in this channel for a Picarto stream."""
-        await self.stream_alert(ctx, PicartoStream, channel_name)
+        await self.stream_alert(ctx, PicartoStream, channel_name, discord_channel)
 
     @streamalert.command(name="stop", usage="[disable_all=No]")
     async def streamalert_stop(self, ctx: commands.Context, _all: bool = False):
@@ -363,28 +378,32 @@ async def streamalert_stop(self, ctx: commands.Context, _all: bool = False):
     @streamalert.command(name="list")
     async def streamalert_list(self, ctx: commands.Context):
         """List all active stream alerts in this server."""
-        streams_list = defaultdict(list)
+        streams_list = defaultdict(lambda: defaultdict(list))
         guild_channels_ids = [c.id for c in ctx.guild.channels]
         msg = _("Active alerts:\n\n")
 
         for stream in self.streams:
             for channel_id in stream.channels:
                 if channel_id in guild_channels_ids:
-                    streams_list[channel_id].append(stream.name.lower())
+                    streams_list[channel_id][stream.platform_name].append(stream.name.lower())
 
         if not streams_list:
             await ctx.send(_("There are no active alerts in this server."))
             return
 
-        for channel_id, streams in streams_list.items():
-            channel = ctx.guild.get_channel(channel_id)
-            msg += "** - #{}**\n{}\n".format(channel, ", ".join(streams))
+        for channel_id, stream_platform in streams_list.items():
+            msg += f"** - #{ctx.guild.get_channel(channel_id)}**\n"
+            for platform, streams in stream_platform.items():
+                msg += f"\t** - {platform}**\n"
+                msg += f"\t\t{humanize_list(streams)}\n"
 
         for page in pagify(msg):
             await ctx.send(page)
 
-    async def stream_alert(self, ctx: commands.Context, _class, channel_name):
-        if isinstance(ctx.channel, discord.Thread):
+    async def stream_alert(self, ctx: commands.Context, _class, channel_name, discord_channel):
+        if discord_channel is None:
+            discord_channel = ctx.channel
+        if isinstance(discord_channel, discord.Thread):
             await ctx.send("Stream alerts cannot be set up in threads.")
             return
         stream = self.get_stream(_class, channel_name)
@@ -445,10 +464,10 @@ async def stream_alert(self, ctx: commands.Context, _class, channel_name):
                 return
             else:
                 if not exists:
-                    await ctx.send(_("That channel doesn't seem to exist."))
+                    await ctx.send(_("That user doesn't seem to exist."))
                     return
 
-        await self.add_or_remove(ctx, stream)
+        await self.add_or_remove(ctx, stream, discord_channel)
 
     @commands.group()
     @checks.mod_or_permissions(manage_channels=True)
@@ -676,24 +695,26 @@ async def ignore_schedule(self, ctx: commands.Context):
             await self.config.guild(guild).ignore_schedule.set(True)
             await ctx.send(_("Streams schedules will no longer send an alert."))
 
-    async def add_or_remove(self, ctx: commands.Context, stream):
-        if ctx.channel.id not in stream.channels:
-            stream.channels.append(ctx.channel.id)
+    async def add_or_remove(self, ctx: commands.Context, stream, discord_channel):
+        if discord_channel.id not in stream.channels:
+            stream.channels.append(discord_channel.id)
             if stream not in self.streams:
                 self.streams.append(stream)
             await ctx.send(
                 _(
-                    "I'll now send a notification in this channel when {stream.name} is live."
-                ).format(stream=stream)
+                    "I'll now send a notification in the {channel.mention} channel"
+                    " when {stream.name} is live."
+                ).format(stream=stream, channel=discord_channel)
             )
         else:
-            stream.channels.remove(ctx.channel.id)
+            stream.channels.remove(discord_channel.id)
             if not stream.channels:
                 self.streams.remove(stream)
             await ctx.send(
                 _(
-                    "I won't send notifications about {stream.name} in this channel anymore."
-                ).format(stream=stream)
+                    "I won't send notifications about {stream.name}"
+                    " in the {channel.mention} channel anymore"
+                ).format(stream=stream, channel=discord_channel)
             )
 
         await self.save_streams()
diff --git a/redbot/cogs/streams/streamtypes.py b/redbot/cogs/streams/streamtypes.py
--- a/redbot/cogs/streams/streamtypes.py
+++ b/redbot/cogs/streams/streamtypes.py
@@ -56,6 +56,7 @@ def get_video_ids_from_feed(feed):
 class Stream:
 
     token_name: ClassVar[Optional[str]] = None
+    platform_name: ClassVar[Optional[str]] = None
 
     def __init__(self, **kwargs):
         self._bot = kwargs.pop("_bot")
@@ -107,6 +108,7 @@ def __repr__(self):
 class YoutubeStream(Stream):
 
     token_name = "youtube"
+    platform_name = "YouTube"
 
     def __init__(self, **kwargs):
         self.id = kwargs.pop("id", None)
@@ -306,6 +308,7 @@ def __repr__(self):
 class TwitchStream(Stream):
 
     token_name = "twitch"
+    platform_name = "Twitch"
 
     def __init__(self, **kwargs):
         self.id = kwargs.pop("id", None)
@@ -463,6 +466,7 @@ def __repr__(self):
 class PicartoStream(Stream):
 
     token_name = None  # This streaming services don't currently require an API key
+    platform_name = "Picarto"
 
     async def is_online(self):
         url = "https://api.picarto.tv/api/v1/channel/name/" + self.name


Test patch: