mirror of
https://github.com/Cog-Creators/Red-DiscordBot.git
synced 2025-11-07 11:48:55 -05:00
[Streams] Remove communities support (#2223)
See [this blog post](https://blog.twitch.tv/introducing-tags-and-new-categories-33744ef7b04f), communities on Twitch have been discontinued.
This commit is contained in:
parent
b65466cebd
commit
77a0a67029
@ -6,10 +6,6 @@ class StreamNotFound(StreamsError):
|
||||
pass
|
||||
|
||||
|
||||
class CommunityNotFound(StreamsError):
|
||||
pass
|
||||
|
||||
|
||||
class APIError(StreamsError):
|
||||
pass
|
||||
|
||||
@ -24,7 +20,3 @@ class InvalidYoutubeCredentials(StreamsError):
|
||||
|
||||
class OfflineStream(StreamsError):
|
||||
pass
|
||||
|
||||
|
||||
class OfflineCommunity(StreamsError):
|
||||
pass
|
||||
|
||||
@ -11,7 +11,6 @@ from .streamtypes import (
|
||||
HitboxStream,
|
||||
MixerStream,
|
||||
PicartoStream,
|
||||
TwitchCommunity,
|
||||
YoutubeStream,
|
||||
)
|
||||
from .errors import (
|
||||
@ -19,8 +18,6 @@ from .errors import (
|
||||
StreamNotFound,
|
||||
APIError,
|
||||
InvalidYoutubeCredentials,
|
||||
CommunityNotFound,
|
||||
OfflineCommunity,
|
||||
StreamsError,
|
||||
InvalidTwitchCredentials,
|
||||
)
|
||||
@ -39,7 +36,7 @@ _ = Translator("Streams", __file__)
|
||||
@cog_i18n(_)
|
||||
class Streams(commands.Cog):
|
||||
|
||||
global_defaults = {"tokens": {}, "streams": [], "communities": []}
|
||||
global_defaults = {"tokens": {}, "streams": []}
|
||||
|
||||
guild_defaults = {"autodelete": False, "mention_everyone": False, "mention_here": False}
|
||||
|
||||
@ -58,7 +55,6 @@ class Streams(commands.Cog):
|
||||
self.bot: Red = bot
|
||||
|
||||
self.streams: List[Stream] = []
|
||||
self.communities: List[TwitchCommunity] = []
|
||||
self.task: Optional[asyncio.Task] = None
|
||||
|
||||
self.yt_cid_pattern = re.compile("^UC[-_A-Za-z0-9]{21}[AQgw]$")
|
||||
@ -73,7 +69,6 @@ class Streams(commands.Cog):
|
||||
"""Should be called straight after cog instantiation."""
|
||||
await self.move_api_keys()
|
||||
self.streams = await self.load_streams()
|
||||
self.communities = await self.load_communities()
|
||||
|
||||
self.task = self.bot.loop.create_task(self._stream_alerts())
|
||||
|
||||
@ -178,11 +173,6 @@ class Streams(commands.Cog):
|
||||
return
|
||||
await self.stream_alert(ctx, TwitchStream, channel_name.lower())
|
||||
|
||||
@_twitch.command(name="community")
|
||||
async def twitch_alert_community(self, ctx: commands.Context, community: str):
|
||||
"""Toggle alerts in this channel for a Twitch community."""
|
||||
await self.community_alert(ctx, TwitchCommunity, community.lower())
|
||||
|
||||
@streamalert.command(name="youtube")
|
||||
async def youtube_alert(self, ctx: commands.Context, channel_name_or_id: str):
|
||||
"""Toggle alerts in this channel for a YouTube stream."""
|
||||
@ -303,34 +293,6 @@ class Streams(commands.Cog):
|
||||
|
||||
await self.add_or_remove(ctx, stream)
|
||||
|
||||
async def community_alert(self, ctx: commands.Context, _class, community_name):
|
||||
community = self.get_community(_class, community_name)
|
||||
if not community:
|
||||
token = await self.bot.db.api_tokens.get_raw(_class.token_name, default=None)
|
||||
community = _class(name=community_name, token=token)
|
||||
try:
|
||||
await community.get_community_streams()
|
||||
except InvalidTwitchCredentials:
|
||||
await ctx.send(
|
||||
_(
|
||||
"The Twitch token is either invalid or has not been set. See "
|
||||
"`{prefix}streamset twitchtoken`."
|
||||
).format(prefix=ctx.prefix)
|
||||
)
|
||||
return
|
||||
except CommunityNotFound:
|
||||
await ctx.send(_("That community doesn't seem to exist."))
|
||||
return
|
||||
except APIError:
|
||||
await ctx.send(
|
||||
_("Something went wrong whilst trying to contact the stream service's API.")
|
||||
)
|
||||
return
|
||||
except OfflineCommunity:
|
||||
pass
|
||||
|
||||
await self.add_or_remove_community(ctx, community)
|
||||
|
||||
@commands.group()
|
||||
@checks.mod()
|
||||
async def streamset(self, ctx: commands.Context):
|
||||
@ -393,9 +355,7 @@ class Streams(commands.Cog):
|
||||
await ctx.send(_("`@\u200beveryone` will no longer be mentioned for stream alerts."))
|
||||
else:
|
||||
await self.db.guild(guild).mention_everyone.set(True)
|
||||
await ctx.send(
|
||||
_("When a stream or community is live, `@\u200beveryone` will be mentioned.")
|
||||
)
|
||||
await ctx.send(_("When a stream is live, `@\u200beveryone` will be mentioned."))
|
||||
|
||||
@mention.command(aliases=["here"])
|
||||
@commands.guild_only()
|
||||
@ -408,9 +368,7 @@ class Streams(commands.Cog):
|
||||
await ctx.send(_("`@\u200bhere` will no longer be mentioned for stream alerts."))
|
||||
else:
|
||||
await self.db.guild(guild).mention_here.set(True)
|
||||
await ctx.send(
|
||||
_("When a stream or community is live, `@\u200bhere` will be mentioned.")
|
||||
)
|
||||
await ctx.send(_("When a stream is live, `@\u200bhere` will be mentioned."))
|
||||
|
||||
@mention.command()
|
||||
@commands.guild_only()
|
||||
@ -470,29 +428,6 @@ class Streams(commands.Cog):
|
||||
|
||||
await self.save_streams()
|
||||
|
||||
async def add_or_remove_community(self, ctx: commands.Context, community):
|
||||
if ctx.channel.id not in community.channels:
|
||||
community.channels.append(ctx.channel.id)
|
||||
if community not in self.communities:
|
||||
self.communities.append(community)
|
||||
await ctx.send(
|
||||
_(
|
||||
"I'll send a notification in this channel when a "
|
||||
"channel is live in the {community.name} community."
|
||||
).format(community=community)
|
||||
)
|
||||
else:
|
||||
community.channels.remove(ctx.channel.id)
|
||||
if not community.channels:
|
||||
self.communities.remove(community)
|
||||
await ctx.send(
|
||||
_(
|
||||
"I won't send notifications about channels streaming "
|
||||
"in the {community.name} community in this channel anymore."
|
||||
).format(community=community)
|
||||
)
|
||||
await self.save_communities()
|
||||
|
||||
def get_stream(self, _class, name):
|
||||
for stream in self.streams:
|
||||
# if isinstance(stream, _class) and stream.name == name:
|
||||
@ -510,11 +445,6 @@ class Streams(commands.Cog):
|
||||
elif stream.type == _class.__name__ and stream.name.lower() == name.lower():
|
||||
return stream
|
||||
|
||||
def get_community(self, _class, name):
|
||||
for community in self.communities:
|
||||
if community.type == _class.__name__ and community.name.lower() == name.lower():
|
||||
return community
|
||||
|
||||
@staticmethod
|
||||
async def check_exists(stream):
|
||||
try:
|
||||
@ -533,10 +463,6 @@ class Streams(commands.Cog):
|
||||
await self.check_streams()
|
||||
except asyncio.CancelledError:
|
||||
pass
|
||||
try:
|
||||
await self.check_communities()
|
||||
except asyncio.CancelledError:
|
||||
pass
|
||||
await asyncio.sleep(CHECK_DELAY)
|
||||
|
||||
async def check_streams(self):
|
||||
@ -600,52 +526,6 @@ class Streams(commands.Cog):
|
||||
mentions.append(role.mention)
|
||||
return " ".join(mentions), edited_roles
|
||||
|
||||
async def check_communities(self):
|
||||
for community in self.communities:
|
||||
with contextlib.suppress(Exception):
|
||||
try:
|
||||
stream_list = await community.get_community_streams()
|
||||
except CommunityNotFound:
|
||||
print(
|
||||
_("The Community {community.name} was not found!").format(
|
||||
community=community
|
||||
)
|
||||
)
|
||||
continue
|
||||
except OfflineCommunity:
|
||||
if not community._messages_cache:
|
||||
continue
|
||||
for message in community._messages_cache:
|
||||
with contextlib.suppress(Exception):
|
||||
autodelete = await self.db.guild(message.guild).autodelete()
|
||||
if autodelete:
|
||||
await message.delete()
|
||||
community._messages_cache.clear()
|
||||
await self.save_communities()
|
||||
else:
|
||||
for channel in community.channels:
|
||||
chn = self.bot.get_channel(channel)
|
||||
streams = await self.filter_streams(stream_list, chn)
|
||||
emb = await community.make_embed(streams)
|
||||
chn_msg = [m for m in community._messages_cache if m.channel == chn]
|
||||
if not chn_msg:
|
||||
mentions, roles = await self._get_mention_str(chn.guild)
|
||||
if mentions:
|
||||
msg = await chn.send(mentions, embed=emb)
|
||||
else:
|
||||
msg = await chn.send(embed=emb)
|
||||
community._messages_cache.append(msg)
|
||||
if roles:
|
||||
for role in roles:
|
||||
await role.edit(mentionable=False)
|
||||
await self.save_communities()
|
||||
else:
|
||||
chn_msg = sorted(chn_msg, key=lambda x: x.created_at, reverse=True)[0]
|
||||
community._messages_cache.remove(chn_msg)
|
||||
await chn_msg.edit(embed=emb)
|
||||
community._messages_cache.append(chn_msg)
|
||||
await self.save_communities()
|
||||
|
||||
async def filter_streams(self, streams: list, channel: discord.TextChannel) -> list:
|
||||
filtered = []
|
||||
for stream in streams:
|
||||
@ -683,34 +563,6 @@ class Streams(commands.Cog):
|
||||
|
||||
return streams
|
||||
|
||||
async def load_communities(self):
|
||||
communities = []
|
||||
|
||||
for raw_community in await self.db.communities():
|
||||
_class = getattr(_streamtypes, raw_community["type"], None)
|
||||
if not _class:
|
||||
continue
|
||||
raw_msg_cache = raw_community["messages"]
|
||||
raw_community["_messages_cache"] = []
|
||||
for raw_msg in raw_msg_cache:
|
||||
chn = self.bot.get_channel(raw_msg["channel"])
|
||||
if chn is not None:
|
||||
try:
|
||||
msg = await chn.get_message(raw_msg["message"])
|
||||
except discord.HTTPException:
|
||||
pass
|
||||
else:
|
||||
raw_community["_messages_cache"].append(msg)
|
||||
token = await self.bot.db.api_tokens.get_raw(_class.token_name, default=None)
|
||||
communities.append(_class(token=token, **raw_community))
|
||||
|
||||
# issue 1191 extended resolution: Remove this after suitable period
|
||||
# Fast dedupe below
|
||||
seen = set()
|
||||
seen_add = seen.add
|
||||
return [x for x in communities if not (x.name.lower() in seen or seen_add(x.name.lower()))]
|
||||
# return communities
|
||||
|
||||
async def save_streams(self):
|
||||
raw_streams = []
|
||||
for stream in self.streams:
|
||||
@ -718,13 +570,6 @@ class Streams(commands.Cog):
|
||||
|
||||
await self.db.streams.set(raw_streams)
|
||||
|
||||
async def save_communities(self):
|
||||
raw_communities = []
|
||||
for community in self.communities:
|
||||
raw_communities.append(community.export())
|
||||
|
||||
await self.db.communities.set(raw_communities)
|
||||
|
||||
def __unload(self):
|
||||
if self.task:
|
||||
self.task.cancel()
|
||||
|
||||
@ -2,8 +2,6 @@ from .errors import (
|
||||
StreamNotFound,
|
||||
APIError,
|
||||
OfflineStream,
|
||||
CommunityNotFound,
|
||||
OfflineCommunity,
|
||||
InvalidYoutubeCredentials,
|
||||
InvalidTwitchCredentials,
|
||||
)
|
||||
@ -30,106 +28,6 @@ def rnd(url):
|
||||
return url + "?rnd=" + "".join([choice(ascii_letters) for i in range(6)])
|
||||
|
||||
|
||||
class TwitchCommunity:
|
||||
|
||||
token_name = "twitch"
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self.name = kwargs.pop("name")
|
||||
self.id = kwargs.pop("id", None)
|
||||
self.channels = kwargs.pop("channels", [])
|
||||
self._messages_cache = kwargs.pop("_messages_cache", [])
|
||||
self._token = kwargs.pop("token", None)
|
||||
self.type = self.__class__.__name__
|
||||
|
||||
async def get_community_id(self):
|
||||
headers = {
|
||||
"Accept": "application/vnd.twitchtv.v5+json",
|
||||
"Client-ID": str(self._token["client_id"]),
|
||||
}
|
||||
params = {"name": self.name}
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get(
|
||||
TWITCH_COMMUNITIES_ENDPOINT, headers=headers, params=params
|
||||
) as r:
|
||||
data = await r.json()
|
||||
if r.status == 200:
|
||||
return data["_id"]
|
||||
elif r.status == 400:
|
||||
raise InvalidTwitchCredentials()
|
||||
elif r.status == 404:
|
||||
raise CommunityNotFound()
|
||||
else:
|
||||
raise APIError()
|
||||
|
||||
async def get_community_streams(self):
|
||||
if not self.id:
|
||||
try:
|
||||
self.id = await self.get_community_id()
|
||||
except CommunityNotFound:
|
||||
raise
|
||||
headers = {
|
||||
"Accept": "application/vnd.twitchtv.v5+json",
|
||||
"Client-ID": str(self._token["client_id"]),
|
||||
}
|
||||
params = {"community_id": self.id, "limit": 100}
|
||||
url = TWITCH_BASE_URL + "/kraken/streams"
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get(url, headers=headers, params=params) as r:
|
||||
data = await r.json()
|
||||
if r.status == 200:
|
||||
if data["_total"] == 0:
|
||||
raise OfflineCommunity()
|
||||
else:
|
||||
return data["streams"]
|
||||
elif r.status == 400:
|
||||
raise InvalidTwitchCredentials()
|
||||
elif r.status == 404:
|
||||
raise CommunityNotFound()
|
||||
else:
|
||||
raise APIError()
|
||||
|
||||
async def make_embed(self, streams: list) -> discord.Embed:
|
||||
headers = {
|
||||
"Accept": "application/vnd.twitchtv.v5+json",
|
||||
"Client-ID": str(self._token["client_id"]),
|
||||
}
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get(
|
||||
"{}/{}".format(TWITCH_COMMUNITIES_ENDPOINT, self.id), headers=headers
|
||||
) as r:
|
||||
data = await r.json()
|
||||
|
||||
avatar = data["avatar_image_url"]
|
||||
title = "Channels currently streaming to {}".format(data["display_name"])
|
||||
url = "https://www.twitch.tv/communities/{}".format(self.name)
|
||||
embed = discord.Embed(title=title, url=url)
|
||||
embed.set_image(url=avatar)
|
||||
if len(streams) >= 10:
|
||||
stream_list = sample(streams, 10)
|
||||
else:
|
||||
stream_list = streams
|
||||
for stream in stream_list:
|
||||
name = "[{}]({})".format(stream["channel"]["display_name"], stream["channel"]["url"])
|
||||
embed.add_field(name=stream["channel"]["status"], value=name, inline=False)
|
||||
embed.color = 0x6441A4
|
||||
|
||||
return embed
|
||||
|
||||
def export(self):
|
||||
data = {}
|
||||
for k, v in self.__dict__.items():
|
||||
if not k.startswith("_"):
|
||||
data[k] = v
|
||||
data["messages"] = []
|
||||
for m in self._messages_cache:
|
||||
data["messages"].append({"channel": m.channel.id, "message": m.id})
|
||||
return data
|
||||
|
||||
def __repr__(self):
|
||||
return "<{0.__class__.__name__}: {0.name}>".format(self)
|
||||
|
||||
|
||||
class Stream:
|
||||
|
||||
token_name: ClassVar[Optional[str]] = None
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user