mirror of
https://github.com/Cog-Creators/Red-DiscordBot.git
synced 2025-11-06 11:18:54 -05:00
[V3 Streams] Add support for Youtube streams (#1385)
This commit is contained in:
parent
052af2f9bf
commit
fe3d6f57af
@ -14,7 +14,11 @@ class APIError(StreamsError):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class InvalidCredentials(StreamsError):
|
class InvalidTwitchCredentials(StreamsError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class InvalidYoutubeCredentials(StreamsError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -1,18 +1,23 @@
|
|||||||
import discord
|
import discord
|
||||||
from discord.ext import commands
|
from discord.ext import commands
|
||||||
from redbot.core import Config, checks, RedContext
|
from redbot.core import Config, checks, RedContext
|
||||||
from redbot.core.utils.chat_formatting import pagify, box
|
from redbot.core.utils.chat_formatting import pagify
|
||||||
from redbot.core.bot import Red
|
from redbot.core.bot import Red
|
||||||
from .streamtypes import TwitchStream, HitboxStream, MixerStream, PicartoStream, TwitchCommunity
|
from redbot.core.i18n import CogI18n
|
||||||
from .errors import (OfflineStream, StreamNotFound, APIError, InvalidCredentials,
|
from .streamtypes import TwitchStream, HitboxStream, MixerStream, PicartoStream, TwitchCommunity, YoutubeStream
|
||||||
CommunityNotFound, OfflineCommunity, StreamsError)
|
from .errors import (OfflineStream, StreamNotFound, APIError, InvalidYoutubeCredentials,
|
||||||
|
CommunityNotFound, OfflineCommunity, StreamsError, InvalidTwitchCredentials)
|
||||||
from . import streamtypes as StreamClasses
|
from . import streamtypes as StreamClasses
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
import asyncio
|
import asyncio
|
||||||
|
import re
|
||||||
|
|
||||||
CHECK_DELAY = 60
|
CHECK_DELAY = 60
|
||||||
|
|
||||||
|
|
||||||
|
_ = CogI18n("Streams", __file__)
|
||||||
|
|
||||||
|
|
||||||
class Streams:
|
class Streams:
|
||||||
|
|
||||||
global_defaults = {
|
global_defaults = {
|
||||||
@ -44,6 +49,14 @@ class Streams:
|
|||||||
|
|
||||||
self.bot.loop.create_task(self._initialize_lists())
|
self.bot.loop.create_task(self._initialize_lists())
|
||||||
|
|
||||||
|
self.yt_cid_pattern = re.compile("^UC[-_A-Za-z0-9]{21}[AQgw]$")
|
||||||
|
|
||||||
|
def check_name_or_id(self, data: str):
|
||||||
|
matched = self.yt_cid_pattern.fullmatch(data)
|
||||||
|
if matched is None:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
async def _initialize_lists(self):
|
async def _initialize_lists(self):
|
||||||
self.streams = await self.load_streams()
|
self.streams = await self.load_streams()
|
||||||
self.communities = await self.load_communities()
|
self.communities = await self.load_communities()
|
||||||
@ -58,6 +71,19 @@ class Streams:
|
|||||||
token=token)
|
token=token)
|
||||||
await self.check_online(ctx, stream)
|
await self.check_online(ctx, stream)
|
||||||
|
|
||||||
|
@commands.command()
|
||||||
|
async def youtube(self, ctx, channel_id_or_name: str):
|
||||||
|
"""
|
||||||
|
Checks if a Youtube channel is streaming
|
||||||
|
"""
|
||||||
|
apikey = await self.db.tokens.get_raw(YoutubeStream.__name__, default=None)
|
||||||
|
is_name = self.check_name_or_id(channel_id_or_name)
|
||||||
|
if is_name:
|
||||||
|
stream = YoutubeStream(name=channel_id_or_name, token=apikey)
|
||||||
|
else:
|
||||||
|
stream = YoutubeStream(id=channel_id_or_name, token=apikey)
|
||||||
|
await self.check_online(ctx, stream)
|
||||||
|
|
||||||
@commands.command()
|
@commands.command()
|
||||||
async def hitbox(self, ctx, channel_name: str):
|
async def hitbox(self, ctx, channel_name: str):
|
||||||
"""Checks if a Hitbox channel is streaming"""
|
"""Checks if a Hitbox channel is streaming"""
|
||||||
@ -80,15 +106,18 @@ class Streams:
|
|||||||
try:
|
try:
|
||||||
embed = await stream.is_online()
|
embed = await stream.is_online()
|
||||||
except OfflineStream:
|
except OfflineStream:
|
||||||
await ctx.send("The stream is offline.")
|
await ctx.send(_("The stream is offline."))
|
||||||
except StreamNotFound:
|
except StreamNotFound:
|
||||||
await ctx.send("The channel doesn't seem to exist.")
|
await ctx.send(_("The channel doesn't seem to exist."))
|
||||||
except InvalidCredentials:
|
except InvalidTwitchCredentials:
|
||||||
await ctx.send("The twitch token is either invalid or has not been set. "
|
await ctx.send(_("The twitch token is either invalid or has not been set. "
|
||||||
"See `{}streamset twitchtoken`.".format(ctx.prefix))
|
"See `{}`.").format("{}streamset twitchtoken".format(ctx.prefix)))
|
||||||
|
except InvalidYoutubeCredentials:
|
||||||
|
await ctx.send(_("The Youtube API key is either invalid or has not been set. "
|
||||||
|
"See {}.").format("`{}streamset youtubekey`".format(ctx.prefix)))
|
||||||
except APIError:
|
except APIError:
|
||||||
await ctx.send("Something went wrong whilst trying to contact the "
|
await ctx.send(_("Something went wrong whilst trying to contact the "
|
||||||
"stream service's API.")
|
"stream service's API."))
|
||||||
else:
|
else:
|
||||||
await ctx.send(embed=embed)
|
await ctx.send(embed=embed)
|
||||||
|
|
||||||
@ -116,6 +145,11 @@ class Streams:
|
|||||||
for the specified community."""
|
for the specified community."""
|
||||||
await self.community_alert(ctx, TwitchCommunity, community.lower())
|
await self.community_alert(ctx, TwitchCommunity, community.lower())
|
||||||
|
|
||||||
|
@streamalert.command(name="youtube")
|
||||||
|
async def youtube_alert(self, ctx: RedContext, channel_name_or_id: str):
|
||||||
|
"""Sets a Youtube stream alert notification in the channel"""
|
||||||
|
await self.stream_alert(ctx, YoutubeStream, channel_name_or_id)
|
||||||
|
|
||||||
@streamalert.command(name="hitbox")
|
@streamalert.command(name="hitbox")
|
||||||
async def hitbox_alert(self, ctx, channel_name: str):
|
async def hitbox_alert(self, ctx, channel_name: str):
|
||||||
"""Sets a Hitbox stream alert notification in the channel"""
|
"""Sets a Hitbox stream alert notification in the channel"""
|
||||||
@ -157,8 +191,8 @@ class Streams:
|
|||||||
self.streams = streams
|
self.streams = streams
|
||||||
await self.save_streams()
|
await self.save_streams()
|
||||||
|
|
||||||
msg = "All {}'s stream alerts have been disabled." \
|
msg = _("All {}'s stream alerts have been disabled."
|
||||||
"".format("server" if _all else "channel")
|
"").format("server" if _all else "channel")
|
||||||
|
|
||||||
await ctx.send(msg)
|
await ctx.send(msg)
|
||||||
|
|
||||||
@ -166,7 +200,7 @@ class Streams:
|
|||||||
async def streamalert_list(self, ctx):
|
async def streamalert_list(self, ctx):
|
||||||
streams_list = defaultdict(list)
|
streams_list = defaultdict(list)
|
||||||
guild_channels_ids = [c.id for c in ctx.guild.channels]
|
guild_channels_ids = [c.id for c in ctx.guild.channels]
|
||||||
msg = "Active stream alerts:\n\n"
|
msg = _("Active stream alerts:\n\n")
|
||||||
|
|
||||||
for stream in self.streams:
|
for stream in self.streams:
|
||||||
for channel_id in stream.channels:
|
for channel_id in stream.channels:
|
||||||
@ -174,7 +208,7 @@ class Streams:
|
|||||||
streams_list[channel_id].append(stream.name.lower())
|
streams_list[channel_id].append(stream.name.lower())
|
||||||
|
|
||||||
if not streams_list:
|
if not streams_list:
|
||||||
await ctx.send("There are no active stream alerts in this server.")
|
await ctx.send(_("There are no active stream alerts in this server."))
|
||||||
return
|
return
|
||||||
|
|
||||||
for channel_id, streams in streams_list.items():
|
for channel_id, streams in streams_list.items():
|
||||||
@ -185,24 +219,34 @@ class Streams:
|
|||||||
await ctx.send(page)
|
await ctx.send(page)
|
||||||
|
|
||||||
async def stream_alert(self, ctx, _class, channel_name):
|
async def stream_alert(self, ctx, _class, channel_name):
|
||||||
stream = self.get_stream(_class, channel_name.lower())
|
stream = self.get_stream(_class, channel_name)
|
||||||
if not stream:
|
if not stream:
|
||||||
token = await self.db.tokens.get_raw(_class.__name__, default=None)
|
token = await self.db.tokens.get_raw(_class.__name__, default=None)
|
||||||
|
is_yt = _class.__name__ == "YoutubeStream"
|
||||||
|
if is_yt and not self.check_name_or_id(channel_name):
|
||||||
|
stream = _class(id=channel_name, token=token)
|
||||||
|
else:
|
||||||
stream = _class(name=channel_name,
|
stream = _class(name=channel_name,
|
||||||
token=token)
|
token=token)
|
||||||
try:
|
try:
|
||||||
exists = await self.check_exists(stream)
|
exists = await self.check_exists(stream)
|
||||||
except InvalidCredentials:
|
except InvalidTwitchCredentials:
|
||||||
await ctx.send("The twitch token is either invalid or has not been set. "
|
await ctx.send(
|
||||||
"See `{}streamset twitchtoken`.".format(ctx.prefix))
|
_("The twitch token is either invalid or has not been set. "
|
||||||
|
"See {}.").format("`{}streamset twitchtoken`".format(ctx.prefix)))
|
||||||
|
return
|
||||||
|
except InvalidYoutubeCredentials:
|
||||||
|
await ctx.send(_("The Youtube API key is either invalid or has not been set. "
|
||||||
|
"See {}.").format("`{}streamset youtubekey`".format(ctx.prefix)))
|
||||||
return
|
return
|
||||||
except APIError:
|
except APIError:
|
||||||
await ctx.send("Something went wrong whilst trying to contact the "
|
await ctx.send(
|
||||||
"stream service's API.")
|
_("Something went wrong whilst trying to contact the "
|
||||||
|
"stream service's API."))
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
if not exists:
|
if not exists:
|
||||||
await ctx.send("That channel doesn't seem to exist.")
|
await ctx.send(_("That channel doesn't seem to exist."))
|
||||||
return
|
return
|
||||||
|
|
||||||
await self.add_or_remove(ctx, stream)
|
await self.add_or_remove(ctx, stream)
|
||||||
@ -214,18 +258,18 @@ class Streams:
|
|||||||
community = _class(name=community_name, token=token)
|
community = _class(name=community_name, token=token)
|
||||||
try:
|
try:
|
||||||
await community.get_community_streams()
|
await community.get_community_streams()
|
||||||
except InvalidCredentials:
|
except InvalidTwitchCredentials:
|
||||||
await ctx.send(
|
await ctx.send(
|
||||||
"The twitch token is either invalid or has not been set. "
|
_("The twitch token is either invalid or has not been set. "
|
||||||
"See `{}streamset twitchtoken`.".format(ctx.prefix))
|
"See {}.").format("`{}streamset twitchtoken`".format(ctx.prefix)))
|
||||||
return
|
return
|
||||||
except CommunityNotFound:
|
except CommunityNotFound:
|
||||||
await ctx.send("That community doesn't seem to exist.")
|
await ctx.send(_("That community doesn't seem to exist."))
|
||||||
return
|
return
|
||||||
except APIError:
|
except APIError:
|
||||||
await ctx.send(
|
await ctx.send(
|
||||||
"Something went wrong whilst trying to contact the "
|
_("Something went wrong whilst trying to contact the "
|
||||||
"stream service's API.")
|
"stream service's API."))
|
||||||
return
|
return
|
||||||
except OfflineCommunity:
|
except OfflineCommunity:
|
||||||
pass
|
pass
|
||||||
@ -252,11 +296,25 @@ class Streams:
|
|||||||
5. Paste the Client ID into this command. Done!
|
5. Paste the Client ID into this command. Done!
|
||||||
|
|
||||||
"""
|
"""
|
||||||
tokens = await self.db.tokens()
|
await self.db.tokens.set_raw("TwitchStream", value=token)
|
||||||
tokens["TwitchStream"] = token
|
await self.db.tokens.set_raw("TwitchCommunity", value=token)
|
||||||
tokens["TwitchCommunity"] = token
|
await ctx.send(_("Twitch token set."))
|
||||||
await self.db.tokens.set(tokens)
|
|
||||||
await ctx.send("Twitch token set.")
|
@streamset.command()
|
||||||
|
@checks.is_owner()
|
||||||
|
async def youtubekey(self, ctx: RedContext, key: str):
|
||||||
|
"""Sets the API key for Youtube.
|
||||||
|
|
||||||
|
To get one, do the following:
|
||||||
|
|
||||||
|
1. Create a project (see https://support.google.com/googleapi/answer/6251787 for details)
|
||||||
|
2. Enable the Youtube Data API v3 (see https://support.google.com/googleapi/answer/6158841 for instructions)
|
||||||
|
3. Set up your API key (see https://support.google.com/googleapi/answer/6158862 for instructions)
|
||||||
|
4. Copy your API key and paste it into this command. Done!
|
||||||
|
|
||||||
|
"""
|
||||||
|
await self.db.tokens.set_raw("YoutubeStream", value=key)
|
||||||
|
await ctx.send(_("Youtube key set."))
|
||||||
|
|
||||||
@streamset.group()
|
@streamset.group()
|
||||||
@commands.guild_only()
|
@commands.guild_only()
|
||||||
@ -273,12 +331,12 @@ class Streams:
|
|||||||
current_setting = await self.db.guild(guild).mention_everyone()
|
current_setting = await self.db.guild(guild).mention_everyone()
|
||||||
if current_setting:
|
if current_setting:
|
||||||
await self.db.guild(guild).mention_everyone.set(False)
|
await self.db.guild(guild).mention_everyone.set(False)
|
||||||
await ctx.send("@\u200beveryone will no longer be mentioned "
|
await ctx.send(_("{} will no longer be mentioned "
|
||||||
"for a stream alert.")
|
"for a stream alert.").format("@\u200beveryone"))
|
||||||
else:
|
else:
|
||||||
await self.db.guild(guild).mention_everyone.set(True)
|
await self.db.guild(guild).mention_everyone.set(True)
|
||||||
await ctx.send("When a stream configured for stream alerts "
|
await ctx.send(_("When a stream configured for stream alerts "
|
||||||
"comes online, @\u200beveryone will be mentioned")
|
"comes online, {} will be mentioned").format("@\u200beveryone"))
|
||||||
|
|
||||||
@mention.command(aliases=["here"])
|
@mention.command(aliases=["here"])
|
||||||
@commands.guild_only()
|
@commands.guild_only()
|
||||||
@ -288,12 +346,12 @@ class Streams:
|
|||||||
current_setting = await self.db.guild(guild).mention_here()
|
current_setting = await self.db.guild(guild).mention_here()
|
||||||
if current_setting:
|
if current_setting:
|
||||||
await self.db.guild(guild).mention_here.set(False)
|
await self.db.guild(guild).mention_here.set(False)
|
||||||
await ctx.send("@\u200bhere will no longer be mentioned "
|
await ctx.send(_("{} will no longer be mentioned "
|
||||||
"for a stream alert.")
|
"for a stream alert.").format("@\u200bhere"))
|
||||||
else:
|
else:
|
||||||
await self.db.guild(guild).mention_here.set(True)
|
await self.db.guild(guild).mention_here.set(True)
|
||||||
await ctx.send("When a stream configured for stream alerts "
|
await ctx.send(_("When a stream configured for stream alerts "
|
||||||
"comes online, @\u200bhere will be mentioned")
|
"comes online, {} will be mentioned").format("@\u200bhere"))
|
||||||
|
|
||||||
@mention.command()
|
@mention.command()
|
||||||
@commands.guild_only()
|
@commands.guild_only()
|
||||||
@ -305,13 +363,13 @@ class Streams:
|
|||||||
return
|
return
|
||||||
if current_setting:
|
if current_setting:
|
||||||
await self.db.role(role).mention.set(False)
|
await self.db.role(role).mention.set(False)
|
||||||
await ctx.send("@\u200b{} will no longer be mentioned "
|
await ctx.send(_("{} will no longer be mentioned "
|
||||||
"for a stream alert".format(role.name))
|
"for a stream alert").format("@\u200b{}".format(role.name)))
|
||||||
else:
|
else:
|
||||||
await self.db.role(role).mention.set(True)
|
await self.db.role(role).mention.set(True)
|
||||||
await ctx.send("When a stream configured for stream alerts "
|
await ctx.send(_("When a stream configured for stream alerts "
|
||||||
"comes online, @\u200b{} will be mentioned"
|
"comes online, {} will be mentioned"
|
||||||
"".format(role.name))
|
"").format("@\u200b{}".format(role.name)))
|
||||||
|
|
||||||
@streamset.command()
|
@streamset.command()
|
||||||
@commands.guild_only()
|
@commands.guild_only()
|
||||||
@ -329,14 +387,14 @@ class Streams:
|
|||||||
stream.channels.append(ctx.channel.id)
|
stream.channels.append(ctx.channel.id)
|
||||||
if stream not in self.streams:
|
if stream not in self.streams:
|
||||||
self.streams.append(stream)
|
self.streams.append(stream)
|
||||||
await ctx.send("I'll send a notification in this channel when {} "
|
await ctx.send(_("I'll send a notification in this channel when {} "
|
||||||
"is online.".format(stream.name))
|
"is online.").format(stream.name))
|
||||||
else:
|
else:
|
||||||
stream.channels.remove(ctx.channel.id)
|
stream.channels.remove(ctx.channel.id)
|
||||||
if not stream.channels:
|
if not stream.channels:
|
||||||
self.streams.remove(stream)
|
self.streams.remove(stream)
|
||||||
await ctx.send("I won't send notifications about {} in this "
|
await ctx.send(_("I won't send notifications about {} in this "
|
||||||
"channel anymore.".format(stream.name))
|
"channel anymore.").format(stream.name))
|
||||||
|
|
||||||
await self.save_streams()
|
await self.save_streams()
|
||||||
|
|
||||||
@ -345,16 +403,16 @@ class Streams:
|
|||||||
community.channels.append(ctx.channel.id)
|
community.channels.append(ctx.channel.id)
|
||||||
if community not in self.communities:
|
if community not in self.communities:
|
||||||
self.communities.append(community)
|
self.communities.append(community)
|
||||||
await ctx.send("I'll send a notification in this channel when a "
|
await ctx.send(_("I'll send a notification in this channel when a "
|
||||||
"channel is streaming to the {} community"
|
"channel is streaming to the {} community"
|
||||||
"".format(community.name))
|
"").format(community.name))
|
||||||
else:
|
else:
|
||||||
community.channels.remove(ctx.channel.id)
|
community.channels.remove(ctx.channel.id)
|
||||||
if not community.channels:
|
if not community.channels:
|
||||||
self.communities.remove(community)
|
self.communities.remove(community)
|
||||||
await ctx.send("I won't send notifications about channels streaming "
|
await ctx.send(_("I won't send notifications about channels streaming "
|
||||||
"to the {} community in this channel anymore"
|
"to the {} community in this channel anymore"
|
||||||
"".format(community.name))
|
"").format(community.name))
|
||||||
await self.save_communities()
|
await self.save_communities()
|
||||||
|
|
||||||
def get_stream(self, _class, name):
|
def get_stream(self, _class, name):
|
||||||
@ -365,6 +423,12 @@ class Streams:
|
|||||||
# isinstance will always return False
|
# isinstance will always return False
|
||||||
# As a workaround, we'll compare the class' name instead.
|
# As a workaround, we'll compare the class' name instead.
|
||||||
# Good enough.
|
# Good enough.
|
||||||
|
if _class.__name__ == "YoutubeStream" and stream.type == _class.__name__:
|
||||||
|
# Because name could be a username or a channel id
|
||||||
|
if self.check_name_or_id(name) and stream.name.lower() == name.lower():
|
||||||
|
return stream
|
||||||
|
elif not self.check_name_or_id(name) and stream.id == name:
|
||||||
|
return stream
|
||||||
if stream.type == _class.__name__ and stream.name.lower() == name.lower():
|
if stream.type == _class.__name__ and stream.name.lower() == name.lower():
|
||||||
return stream
|
return stream
|
||||||
|
|
||||||
@ -446,7 +510,7 @@ class Streams:
|
|||||||
try:
|
try:
|
||||||
streams = community.get_community_streams()
|
streams = community.get_community_streams()
|
||||||
except CommunityNotFound:
|
except CommunityNotFound:
|
||||||
print("Community {} not found!".format(community.name))
|
print(_("Community {} not found!").format(community.name))
|
||||||
continue
|
continue
|
||||||
except OfflineCommunity:
|
except OfflineCommunity:
|
||||||
pass
|
pass
|
||||||
@ -454,7 +518,7 @@ class Streams:
|
|||||||
token = self.db.tokens().get(TwitchStream.__name__)
|
token = self.db.tokens().get(TwitchStream.__name__)
|
||||||
for channel in community.channels:
|
for channel in community.channels:
|
||||||
chn = self.bot.get_channel(channel)
|
chn = self.bot.get_channel(channel)
|
||||||
await chn.send("Online streams for {}".format(community.name))
|
await chn.send(_("Online streams for {}").format(community.name))
|
||||||
for stream in streams:
|
for stream in streams:
|
||||||
stream_obj = TwitchStream(
|
stream_obj = TwitchStream(
|
||||||
token=token, name=stream["channel"]["name"],
|
token=token, name=stream["channel"]["name"],
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
from .errors import StreamNotFound, APIError, InvalidCredentials, OfflineStream, CommunityNotFound, OfflineCommunity
|
from .errors import StreamNotFound, APIError, OfflineStream, CommunityNotFound, OfflineCommunity, \
|
||||||
|
InvalidYoutubeCredentials, InvalidTwitchCredentials
|
||||||
from random import choice
|
from random import choice
|
||||||
from string import ascii_letters
|
from string import ascii_letters
|
||||||
import discord
|
import discord
|
||||||
@ -10,6 +11,11 @@ TWITCH_ID_ENDPOINT = TWITCH_BASE_URL + "/kraken/users?login="
|
|||||||
TWITCH_STREAMS_ENDPOINT = TWITCH_BASE_URL + "/kraken/streams/"
|
TWITCH_STREAMS_ENDPOINT = TWITCH_BASE_URL + "/kraken/streams/"
|
||||||
TWITCH_COMMUNITIES_ENDPOINT = TWITCH_BASE_URL + "/kraken/communities"
|
TWITCH_COMMUNITIES_ENDPOINT = TWITCH_BASE_URL + "/kraken/communities"
|
||||||
|
|
||||||
|
YOUTUBE_BASE_URL = "https://www.googleapis.com/youtube/v3"
|
||||||
|
YOUTUBE_CHANNELS_ENDPOINT = YOUTUBE_BASE_URL + "/channels"
|
||||||
|
YOUTUBE_SEARCH_ENDPOINT = YOUTUBE_BASE_URL + "/search"
|
||||||
|
YOUTUBE_VIDEOS_ENDPOINT = YOUTUBE_BASE_URL + "/videos"
|
||||||
|
|
||||||
|
|
||||||
def rnd(url):
|
def rnd(url):
|
||||||
"""Appends a random parameter to the url to avoid Discord's caching"""
|
"""Appends a random parameter to the url to avoid Discord's caching"""
|
||||||
@ -38,7 +44,7 @@ class TwitchCommunity:
|
|||||||
if r.status == 200:
|
if r.status == 200:
|
||||||
return data["_id"]
|
return data["_id"]
|
||||||
elif r.status == 400:
|
elif r.status == 400:
|
||||||
raise InvalidCredentials()
|
raise InvalidTwitchCredentials()
|
||||||
elif r.status == 404:
|
elif r.status == 404:
|
||||||
raise CommunityNotFound()
|
raise CommunityNotFound()
|
||||||
else:
|
else:
|
||||||
@ -67,7 +73,7 @@ class TwitchCommunity:
|
|||||||
else:
|
else:
|
||||||
return data["streams"]
|
return data["streams"]
|
||||||
elif r.status == 400:
|
elif r.status == 400:
|
||||||
raise InvalidCredentials()
|
raise InvalidTwitchCredentials()
|
||||||
elif r.status == 404:
|
elif r.status == 404:
|
||||||
raise CommunityNotFound()
|
raise CommunityNotFound()
|
||||||
else:
|
else:
|
||||||
@ -86,7 +92,7 @@ class TwitchCommunity:
|
|||||||
|
|
||||||
class Stream:
|
class Stream:
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
self.name = kwargs.pop("name")
|
self.name = kwargs.pop("name", None)
|
||||||
self.channels = kwargs.pop("channels", [])
|
self.channels = kwargs.pop("channels", [])
|
||||||
#self.already_online = kwargs.pop("already_online", False)
|
#self.already_online = kwargs.pop("already_online", False)
|
||||||
self._messages_cache = []
|
self._messages_cache = []
|
||||||
@ -109,6 +115,75 @@ class Stream:
|
|||||||
return "<{0.__class__.__name__}: {0.name}>".format(self)
|
return "<{0.__class__.__name__}: {0.name}>".format(self)
|
||||||
|
|
||||||
|
|
||||||
|
class YoutubeStream(Stream):
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
self.id = kwargs.pop("id", None)
|
||||||
|
self._token = kwargs.pop("token", None)
|
||||||
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
|
async def is_online(self):
|
||||||
|
if not self.id:
|
||||||
|
self.id = await self.fetch_id()
|
||||||
|
url = YOUTUBE_SEARCH_ENDPOINT
|
||||||
|
params = {
|
||||||
|
"key": self._token,
|
||||||
|
"part": "snippet",
|
||||||
|
"channelId": self.id,
|
||||||
|
"type": "video",
|
||||||
|
"eventType": "live"
|
||||||
|
}
|
||||||
|
async with aiohttp.ClientSession() as session:
|
||||||
|
async with session.get(url, params=params) as r:
|
||||||
|
data = await r.json()
|
||||||
|
if "items" in data and len(data["items"]) == 0:
|
||||||
|
raise OfflineStream()
|
||||||
|
elif "items" in data:
|
||||||
|
vid_id = data["items"][0]["id"]["videoId"]
|
||||||
|
params = {
|
||||||
|
"key": self._token,
|
||||||
|
"id": vid_id,
|
||||||
|
"part": "snippet"
|
||||||
|
}
|
||||||
|
async with aiohttp.ClientSession() as session:
|
||||||
|
async with session.get(YOUTUBE_VIDEOS_ENDPOINT, params=params) as r:
|
||||||
|
data = await r.json()
|
||||||
|
return self.make_embed(data)
|
||||||
|
|
||||||
|
def make_embed(self, data):
|
||||||
|
vid_data = data["items"][0]
|
||||||
|
video_url = "https://youtube.com/watch?v={}".format(vid_data["id"])
|
||||||
|
title = vid_data["snippet"]["title"]
|
||||||
|
thumbnail = vid_data["snippet"]["thumbnails"]["default"]["url"]
|
||||||
|
channel_title = data["snippet"]["channelTitle"]
|
||||||
|
embed = discord.Embed(title=title, url=video_url)
|
||||||
|
embed.set_author(name=channel_title)
|
||||||
|
embed.set_image(url=rnd(thumbnail))
|
||||||
|
embed.colour = 0x9255A5
|
||||||
|
return embed
|
||||||
|
|
||||||
|
async def fetch_id(self):
|
||||||
|
params = {
|
||||||
|
"key": self._token,
|
||||||
|
"forUsername": self.name,
|
||||||
|
"part": "id"
|
||||||
|
}
|
||||||
|
async with aiohttp.ClientSession() as session:
|
||||||
|
async with session.get(YOUTUBE_CHANNELS_ENDPOINT, params=params) as r:
|
||||||
|
data = await r.json()
|
||||||
|
|
||||||
|
if "error" in data and data["error"]["code"] == 400 and\
|
||||||
|
data["error"]["errors"][0]["reason"] == "keyInvalid":
|
||||||
|
raise InvalidYoutubeCredentials()
|
||||||
|
elif "items" in data and len(data["items"]) == 0:
|
||||||
|
raise StreamNotFound()
|
||||||
|
elif "items" in data:
|
||||||
|
return data["items"][0]["id"]
|
||||||
|
raise APIError()
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "<{0.__class__.__name__}: {0.name} (ID: {0.id})>".format(self)
|
||||||
|
|
||||||
|
|
||||||
class TwitchStream(Stream):
|
class TwitchStream(Stream):
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
self.id = kwargs.pop("id", None)
|
self.id = kwargs.pop("id", None)
|
||||||
@ -137,7 +212,7 @@ class TwitchStream(Stream):
|
|||||||
self.name = data["stream"]["channel"]["name"]
|
self.name = data["stream"]["channel"]["name"]
|
||||||
return self.make_embed(data)
|
return self.make_embed(data)
|
||||||
elif r.status == 400:
|
elif r.status == 400:
|
||||||
raise InvalidCredentials()
|
raise InvalidTwitchCredentials()
|
||||||
elif r.status == 404:
|
elif r.status == 404:
|
||||||
raise StreamNotFound()
|
raise StreamNotFound()
|
||||||
else:
|
else:
|
||||||
@ -159,7 +234,7 @@ class TwitchStream(Stream):
|
|||||||
raise StreamNotFound()
|
raise StreamNotFound()
|
||||||
return data["users"][0]["_id"]
|
return data["users"][0]["_id"]
|
||||||
elif r.status == 400:
|
elif r.status == 400:
|
||||||
raise InvalidCredentials()
|
raise InvalidTwitchCredentials()
|
||||||
else:
|
else:
|
||||||
raise APIError()
|
raise APIError()
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user