1
0
Fork 0
mirror of synced 2024-05-18 19:42:28 +12:00

Merge branch 'rewrite' of https://github.com/Phxntxm/Bonfire into rewrite

This commit is contained in:
Phxntxm 2017-07-13 15:44:06 -05:00
commit b13d6510a4
11 changed files with 328 additions and 219 deletions

7
bot.py
View file

@ -80,7 +80,10 @@ async def on_command_error(ctx, error):
try:
if isinstance(error.original, discord.Forbidden):
return
elif isinstance(error.original, discord.HTTPException) and ('empty message' in str(error.original) or 'INTERNAL SERVER ERROR' in str(error.original)):
elif isinstance(error.original, discord.HTTPException) and (
'empty message' in str(error.original) or
'INTERNAL SERVER ERROR' in str(error.original) or
'REQUEST ENTITY TOO LARGE' in str(error.original)):
return
elif isinstance(error.original, aiohttp.ClientOSError):
return
@ -93,7 +96,7 @@ async def on_command_error(ctx, error):
await ctx.message.channel.send(fmt)
elif isinstance(error, commands.CheckFailure):
fmt = "You can't tell me what to do!"
#await ctx.message.channel.send(fmt)
# await ctx.message.channel.send(fmt)
elif isinstance(error, commands.CommandOnCooldown):
m, s = divmod(error.retry_after, 60)
fmt = "This command is on cooldown! Hold your horses! >:c\nTry again in {} minutes and {} seconds" \

View file

@ -171,52 +171,68 @@ class Miscallaneous:
# in this command is changed
# Create the original embed object
opts = {'title': 'Dev Server',
'description': 'Join the server above for any questions/suggestions about me.',
'url': utils.dev_server}
embed = discord.Embed(**opts)
# Set the description include dev server (should be required) and the optional patreon link
description = "[Dev Server]({})".format(utils.dev_server)
if utils.patreon_link:
description += "\n[Patreon]({})".format(utils.patreon_link)
# Now creat the object
opts = {'title': 'Bonfire',
'description': description,
'colour': discord.Colour.green()}
# Set the owner
embed = discord.Embed(**opts)
if hasattr(self.bot, 'owner'):
embed.set_author(name=str(self.bot.owner), icon_url=self.bot.owner.avatar_url)
# Setup the process statistics
name = "Process stats"
value = ""
memory_usage = self.process.memory_full_info().uss / 1024 ** 2
cpu_usage = self.process.cpu_percent() / psutil.cpu_count()
value += 'Memory: {:.2f} MiB'.format(memory_usage)
value += '\nCPU: {}%'.format(cpu_usage)
if hasattr(self.bot, 'uptime'):
value += "\nUptime: {}".format((pendulum.utcnow() - self.bot.uptime).in_words())
embed.add_field(name=name, value=value, inline=False)
# Setup the user and guild statistics
name = "User/Guild stats"
value = ""
value += "Channels: {}".format(len(list(self.bot.get_all_channels())))
value += "\nUsers: {}".format(len(self.bot.users))
value += "\nServers: {}".format(len(self.bot.guilds))
embed.add_field(name=name, value=value, inline=False)
# The game statistics
name = "Game statistics"
# To get the newlines right, since we're not sure what will and won't be included
# Lets make this one a list and join it at the end
value = []
# Add the normal values
embed.add_field(name='Total Servers', value=len(self.bot.guilds))
embed.add_field(name='Total Members', value=len(self.bot.users))
hm = self.bot.get_cog('Hangman')
ttt = self.bot.get_cog('TicTacToe')
bj = self.bot.get_cog('Blackjack')
interaction = self.bot.get_cog('Blackjack')
interaction = self.bot.get_cog('Interaction')
music = self.bot.get_cog('Music')
# Count the variable values; hangman, tictactoe, etc.
if hm:
hm_games = len(hm.games)
if hm_games:
embed.add_field(name='Total Hangman games running', value=hm_games)
value.append("Hangman games: {}".format(len(hm.games)))
if ttt:
ttt_games = len(ttt.boards)
if ttt_games:
embed.add_field(name='Total TicTacToe games running', value=ttt_games)
value.append("TicTacToe games: {}".format(len(ttt.boards)))
if bj:
bj_games = len(bj.games)
if bj_games:
embed.add_field(name='Total blackjack games running', value=bj_games)
value.append("Blackjack games: {}".format(len(bj.games)))
if interaction:
count_battles = 0
for battles in self.bot.get_cog('Interaction').battles.values():
count_battles += len(battles)
if count_battles:
embed.add_field(name='Total battles games running', value=count_battles)
value.append("Battles running: {}".format(len(bj.games)))
if music:
songs = len([x for x in music.voice_states.values() if x.playing])
if songs:
embed.add_field(name='Total songs playing', value=songs)
if hasattr(self.bot, 'uptime'):
embed.add_field(name='Uptime', value=(pendulum.utcnow() - self.bot.uptime).in_words())
memory_usage = self.process.memory_full_info().uss / 1024 ** 2
cpu_usage = self.process.cpu_percent() / psutil.cpu_count()
embed.add_field(name='Memory Usage', value='{:.2f} MiB'.format(memory_usage))
embed.add_field(name='CPU Usage', value='{}%'.format(cpu_usage))
embed.set_footer(text=self.bot.description)
value.append("Total songs playing: {}".format(songs))
embed.add_field(name=name, value="\n".join(value), inline=False)
await ctx.send(embed=embed)

View file

@ -1,5 +1,5 @@
from .voice_utilities import *
from discord import FFmpegPCMAudio, PCMVolumeTransformer
from discord import PCMVolumeTransformer
import discord
from discord.ext import commands
@ -21,7 +21,7 @@ if not discord.opus.is_loaded():
class VoiceState:
def __init__(self, guild, bot, user_queue=False):
def __init__(self, guild, bot, user_queue=False, volume=None):
self.guild = guild
self.songs = Playlist(bot)
self.djs = deque()
@ -31,7 +31,7 @@ class VoiceState:
self.skip_votes = set()
self.user_queue = user_queue
self.loop = bot.loop
self._volume = .5
self._volume = volume or .5
@property
def volume(self):
@ -377,8 +377,10 @@ class Music:
# If we have connnected, create our voice state
queue_type = self.bot.db.load('server_settings', key=channel.guild.id, pluck='queue_type')
volume = self.bot.db.load('server_settings', key=channel.guild.id, pluck='volume')
user_queue = queue_type == "user"
self.voice_states[channel.guild.id] = VoiceState(channel.guild, self.bot, user_queue=user_queue)
self.voice_states[channel.guild.id] = VoiceState(channel.guild, self.bot, user_queue=user_queue,
volume=volume)
# If we can send messages, edit it to let the channel know we have succesfully joined
if msg:
@ -469,7 +471,8 @@ class Music:
"""Imports a song into the current voice queue"""
# If we don't have a voice state yet, create one
if not self.bot.db.load('server_settings', key=ctx.message.guild.id, pluck='playlists_allowed'):
await ctx.send("You cannot import playlists at this time; the {}allowplaylists command can be used to change this setting".format(ctx.prefix))
await ctx.send("You cannot import playlists at this time; the {}allowplaylists command can be used to "
"change this setting".format(ctx.prefix))
return
if ctx.message.guild.id not in self.voice_states:
if not await ctx.invoke(self.join):
@ -490,7 +493,16 @@ class Music:
return
song = re.sub('[<>\[\]]', '', song)
await self.import_playlist(song, ctx)
# Check if we've got the list variable in the URL, if so lets just use this
playlist_id = re.search(r'list=(.+)', song)
if playlist_id:
song = playlist_id.group(1)
try:
await self.import_playlist(song, ctx)
except WrongEntryTypeError:
await ctx.send("This URL is not a playlist! If you want to play this song just use `play`")
except ExtractionError:
await ctx.send("Failed to download {}! If this is not a playlist, use the `play` command".format(song))
@commands.command()
@commands.guild_only()
@ -537,6 +549,9 @@ class Music:
try:
entry = await self.add_entry(song, ctx)
# This error only happens if Discord has derped, and the voice state didn't get created succesfully
except KeyError:
await ctx.send("Sorry, but I failed to connect! Please try again!")
except LiveStreamError as e:
await ctx.send(str(e))
except WrongEntryTypeError:
@ -586,6 +601,8 @@ class Music:
await ctx.send("Sorry but the max volume is 100%")
else:
state.volume = value
entry = {'server_id': str(ctx.message.guild.id), 'volume': value}
self.bot.db.save('server_settings', entry)
await ctx.send('Set the volume to {:.0%}'.format(state.volume))
@commands.command()
@ -633,7 +650,7 @@ class Music:
# Then stop playing, and disconnect
if voice:
voice.stop()
await voice.disconnect()
await voice.disconnect(force=True)
@commands.command()
@commands.guild_only()

View file

@ -8,7 +8,6 @@ import pendulum
import re
import asyncio
import traceback
import rethinkdb as r
class Raffle:
@ -36,63 +35,66 @@ class Raffle:
for raffle in raffles:
server = self.bot.get_guild(int(raffle['server_id']))
title = raffle['title']
entrants = raffle['entrants']
raffle_id = raffle['id']
for r in raffle['raffles']:
title = r['title']
entrants = r['entrants']
raffle_id = r['id']
# Check to see if this cog can find the server in question
if server is None:
await self.bot.db.query(r.table('raffles').get(raffle_id).delete())
continue
# Check to see if this cog can find the server in question
if server is None:
await self.bot.db.query(r.table('raffles').get(raffle_id).delete())
continue
now = pendulum.utcnow()
expires = pendulum.parse(raffle['expires'])
now = pendulum.utcnow()
expires = pendulum.parse(r['expires'])
# Now lets compare and see if this raffle has ended, if not just continue
if expires > now:
continue
# Now lets compare and see if this raffle has ended, if not just continue
if expires > now:
continue
# Make sure there are actually entrants
if len(entrants) == 0:
fmt = 'Sorry, but there were no entrants for the raffle `{}`!'.format(title)
else:
winner = None
count = 0
while winner is None:
winner = server.get_member(int(random.SystemRandom().choice(entrants)))
# Lets make sure we don't get caught in an infinite loop
# Realistically having more than 50 random entrants found that aren't in the server anymore
# Isn't something that should be an issue, but better safe than sorry
count += 1
if count >= 50:
break
if winner is None:
fmt = 'I couldn\'t find an entrant that is still in this server, for the raffle `{}`!'.format(title)
# Make sure there are actually entrants
if len(entrants) == 0:
fmt = 'Sorry, but there were no entrants for the raffle `{}`!'.format(title)
else:
fmt = 'The raffle `{}` has just ended! The winner is {}!'.format(title, winner.display_name)
winner = None
count = 0
while winner is None:
winner = server.get_member(int(random.SystemRandom().choice(entrants)))
# Get the notifications settings, get the raffle setting
notifications = self.bot.db.load('server_settings', key=server.id, pluck='notifications') or {}
# Set our default to either the one set, or the default channel of the server
default_channel_id = notifications.get('default') or server.id
# If it is has been overriden by picarto notifications setting, use this
channel_id = notifications.get('raffle') or default_channel_id
channel = self.bot.get_channel(int(channel_id))
if channel is None:
channel = server.default_channel
try:
await channel.send(fmt)
except (discord.Forbidden, AttributeError):
pass
# Lets make sure we don't get caught in an infinite loop
# Realistically having more than 50 random entrants found that aren't in the server anymore
# Isn't something that should be an issue, but better safe than sorry
count += 1
if count >= 50:
break
# No matter which one of these matches were met, the raffle has ended and we want to remove it
await self.bot.db.query(r.table('raffles').get(raffle_id).delete())
# Now...this is an ugly idea yes, but due to the way raffles are setup currently (they'll be changed in
# the future) The cache does not update, and leaves behind this deletion....so we need to manually update
# the cache here
await self.bot.db.cache.get('raffles').refresh()
if winner is None:
fmt = 'I couldn\'t find an entrant that is still in this server, for the raffle `{}`!'.format(
title)
else:
fmt = 'The raffle `{}` has just ended! The winner is {}!'.format(title, winner.display_name)
# Get the notifications settings, get the raffle setting
notifications = self.bot.db.load('server_settings', key=server.id, pluck='notifications') or {}
# Set our default to either the one set, or the default channel of the server
default_channel_id = notifications.get('default') or server.id
# If it is has been overriden by picarto notifications setting, use this
channel_id = notifications.get('raffle') or default_channel_id
channel = self.bot.get_channel(int(channel_id))
if channel is None:
channel = server.default_channel
try:
await channel.send(fmt)
except (discord.Forbidden, AttributeError):
pass
# No matter which one of these matches were met, the raffle has ended and we want to remove it
raffle['raffles'].remove(r)
entry = {
'server_id': raffle['server_id'],
'raffles': raffle['raffles']
}
self.bot.db.save('raffles', entry)
@commands.command()
@commands.guild_only()
@ -103,8 +105,7 @@ class Raffle:
EXAMPLE: !raffles
RESULT: A list of the raffles setup on this server"""
r_filter = {'server_id': str(ctx.message.guild.id)}
raffles = self.bot.db.load('raffles', table_filter=r_filter)
raffles = self.bot.db.load('raffles', key=ctx.message.guild.id, pluck='raffles')
if not raffles:
await ctx.send("There are currently no raffles setup on this server!")
return
@ -133,19 +134,15 @@ class Raffle:
RESULT: You've entered the first raffle!"""
# Lets let people use 1 - (length of raffles) and handle 0 base ourselves
raffle_id -= 1
r_filter = {'server_id': str(ctx.message.guild.id)}
author = ctx.message.author
key = str(ctx.message.guild.id)
raffles = self.bot.db.load('raffles', table_filter=r_filter)
raffles = self.bot.db.load('raffles', key=key, pluck='raffles')
if raffles is None:
await ctx.send("There are currently no raffles setup on this server!")
return
if isinstance(raffles, list):
raffle_count = len(raffles)
else:
raffles = [raffles]
raffle_count = 1
raffle_count = len(raffles)
# There is only one raffle, so use the first's info
if raffle_count == 1:
@ -157,8 +154,8 @@ class Raffle:
entrants.append(str(author.id))
update = {
'entrants': entrants,
'id': raffles[0]['id']
'raffles': raffles,
'server_id': key
}
self.bot.db.save('raffles', update)
await ctx.send("{} you have just entered the raffle!".format(author.mention))
@ -175,8 +172,8 @@ class Raffle:
# Since we have no good thing to filter things off of, lets use the internal rethinkdb id
update = {
'entrants': entrants,
'id': raffles[raffle_id]['id']
'raffles': raffles,
'server_id': key
}
self.bot.db.save('raffles', update)
await ctx.send("{} you have just entered the raffle!".format(author.mention))
@ -270,11 +267,15 @@ class Raffle:
'expires': expires.to_datetime_string(),
'entrants': [],
'author': str(author.id),
'server_id': str(server.id)
}
# We don't want to pass a filter to this, because we can have multiple raffles per server
self.bot.db.save('raffles', entry)
raffles = self.bot.db.load('raffles', key=server.id, pluck='raffles') or []
raffles.append(entry)
update = {
'server_id': str(server.id),
'raffles': raffles
}
self.bot.db.save('raffles', update)
await ctx.send("I have just saved your new raffle!")
@raffle.command(name='alerts')

View file

@ -4,6 +4,7 @@ from discord.ext import commands
from . import utils
import re
import asyncio
class Stats:
@ -11,6 +12,53 @@ class Stats:
def __init__(self, bot):
self.bot = bot
self.donators = []
self.bot.loop.create_task(self.donator_task())
async def donator_task(self):
while True:
await self.get_donators()
await asyncio.sleep(60)
async def get_donators(self):
# Set our base URL for the pagination task
url = "https://api.patreon.com/oauth2/api/campaigns/{}/pledges".format(utils.patreon_id)
# Set our headers with our bearer token
headers = {'Authorization': 'Bearer {}'.format(utils.patreon_key)}
# We need the names of all of them, and the names are embeded a bit so lets append while looping
names = []
# We need to page through, so lets create a loop and break when we find out we're done
while True:
# Simply get data based on the URL
data = await utils.request(url, headers=headers, force_content_type_json=True)
# First check if the data failed to retrieve, if so just return
if data is None:
return
# Loop through the includes, as that's all we need
for include in data['included']:
# We only carry about the user's
if include['type'] != 'user':
continue
# This check checks the user's connected campaign (should only exist for *our* user) and checks if it
# matches
if include.get('relationshipos', {}).get('campaign', {}).get('data', {}).get('id', {}) == str(
utils.patreon_id):
continue
# Otherwise the only way this user was included, was if they are a patron, so include them
name = include['attributes']['full_name']
if name:
names.append(name)
# Now, lets get our "next" link and request that
url = data['links'].get('next')
# If there is no None, that means there should only be a "first" and our pagination is done
if url is None:
break
# Now just set the names
self.donators = names
@commands.command()
@commands.guild_only()
@ -277,6 +325,20 @@ class Stats:
fmt = fmt.format(member.display_name, record, server_rank, overall_rank, rating)
await ctx.send('```\n{}```'.format(fmt))
@commands.command(aliases=['donators'])
@utils.custom_perms(send_messages=True)
@utils.check_restricted()
async def patrons(self, ctx):
"""Prints a list of all the patrons for Bonfire
EXAMPLE: !donators
RESULT: A list of the donators"""
try:
pages = utils.Pages(self.bot, message=ctx.message, entries=self.donators)
await pages.paginate()
except utils.CannotPaginate as e:
await ctx.send(str(e))
def setup(bot):
bot.add_cog(Stats(bot))

View file

@ -1,98 +1,102 @@
import ruamel.yaml as yaml
import asyncio
import rethinkdb as r
import pendulum
loop = asyncio.get_event_loop()
global_config = {}
# Ensure that the required config.yml file actually exists
try:
with open("config.yml", "r") as f:
global_config = yaml.safe_load(f)
global_config = {k: v for k, v in global_config.items() if v}
except FileNotFoundError:
print("You have no config file setup! Please use config.yml.sample to setup a valid config file")
quit()
try:
bot_token = global_config["bot_token"]
except KeyError:
print("You have no bot_token saved, this is a requirement for running a bot.")
print("Please use config.yml.sample to setup a valid config file")
quit()
# Default bot's description
bot_description = global_config.get("description")
# Bot's default prefix for commands
default_prefix = global_config.get("command_prefix", "!")
# The key for bots.discord.pw and carbonitex
discord_bots_key = global_config.get('discord_bots_key', "")
carbon_key = global_config.get('carbon_key', "")
# The client ID for twitch requsets
twitch_key = global_config.get('twitch_key', "")
# The key for youtube API calls
youtube_key = global_config.get("youtube_key", "")
# The key for Osu API calls
osu_key = global_config.get('osu_key', '')
# The key for League of Legends API calls
lol_key = global_config.get('lol_key', '')
# The keys needed for deviant art calls
# The invite link for the server made for the bot
dev_server = global_config.get("dev_server", "")
# The User-Agent that we'll use for most requests
user_agent = global_config.get('user_agent', None)
# The URL to proxy youtube_dl's requests through
ytdl_proxy = global_config.get('youtube_dl_proxy', None)
# The extensions to load
extensions = [
'cogs.interaction',
'cogs.misc',
'cogs.mod',
'cogs.admin',
'cogs.images',
'cogs.owner',
'cogs.stats',
'cogs.picarto',
'cogs.overwatch',
'cogs.links',
'cogs.roles',
'cogs.tictactoe',
'cogs.hangman', 'cogs.events', 'cogs.raffle',
'cogs.twitch',
'cogs.blackjack',
'cogs.osu',
'cogs.tags',
'cogs.roulette',
'cogs.music',
'cogs.polls',
'cogs.playlist',
'cogs.dj'
]
# The default status the bot will use
default_status = global_config.get("default_status", None)
# The rethinkdb hostname
db_host = global_config.get('db_host', 'localhost')
# The rethinkdb database name
db_name = global_config.get('db_name', 'Discord_Bot')
# The rethinkdb certification
db_cert = global_config.get('db_cert', '')
# The rethinkdb port
db_port = global_config.get('db_port', 28015)
# The user and password assigned
db_user = global_config.get('db_user', 'admin')
db_pass = global_config.get('db_pass', '')
# We've set all the options we need to be able to connect
# so create a dictionary that we can use to unload to connect
# db_opts = {'host': db_host, 'db': db_name, 'port': db_port, 'ssl':
# {'ca_certs': db_cert}, 'user': db_user, 'password': db_pass}
db_opts = {'host': db_host, 'db': db_name, 'port': db_port, 'user': db_user, 'password': db_pass}
def command_prefix(bot, message):
if not message.guild:
return default_prefix
return bot.db.load('server_settings', key=message.guild.id, pluck='prefix') or default_prefix
import ruamel.yaml as yaml
import asyncio
import rethinkdb as r
import pendulum
loop = asyncio.get_event_loop()
global_config = {}
# Ensure that the required config.yml file actually exists
try:
with open("config.yml", "r") as f:
global_config = yaml.safe_load(f)
global_config = {k: v for k, v in global_config.items() if v}
except FileNotFoundError:
print("You have no config file setup! Please use config.yml.sample to setup a valid config file")
quit()
try:
bot_token = global_config["bot_token"]
except KeyError:
print("You have no bot_token saved, this is a requirement for running a bot.")
print("Please use config.yml.sample to setup a valid config file")
quit()
# Default bot's description
bot_description = global_config.get("description")
# Bot's default prefix for commands
default_prefix = global_config.get("command_prefix", "!")
# The key for bots.discord.pw and carbonitex
discord_bots_key = global_config.get('discord_bots_key', "")
carbon_key = global_config.get('carbon_key', "")
# The client ID for twitch requsets
twitch_key = global_config.get('twitch_key', "")
# The key for youtube API calls
youtube_key = global_config.get("youtube_key", "")
# The key for Osu API calls
osu_key = global_config.get('osu_key', '')
# The key for League of Legends API calls
lol_key = global_config.get('lol_key', '')
# The keys needed for deviant art calls
# The invite link for the server made for the bot
dev_server = global_config.get("dev_server", "")
# The User-Agent that we'll use for most requests
user_agent = global_config.get('user_agent', None)
# The URL to proxy youtube_dl's requests through
ytdl_proxy = global_config.get('youtube_dl_proxy', None)
# The patreon key, as well as the patreon ID to use
patreon_key = global_config.get('patreon_key', None)
patreon_id = global_config.get('patreon_id', None)
patreon_link = global_config.get('patreon_link', None)
# The extensions to load
extensions = [
'cogs.interaction',
'cogs.misc',
'cogs.mod',
'cogs.admin',
'cogs.images',
'cogs.owner',
'cogs.stats',
'cogs.picarto',
'cogs.overwatch',
'cogs.links',
'cogs.roles',
'cogs.tictactoe',
'cogs.hangman', 'cogs.events', 'cogs.raffle',
'cogs.twitch',
'cogs.blackjack',
'cogs.osu',
'cogs.tags',
'cogs.roulette',
'cogs.music',
'cogs.polls',
'cogs.playlist',
'cogs.dj'
]
# The default status the bot will use
default_status = global_config.get("default_status", None)
# The rethinkdb hostname
db_host = global_config.get('db_host', 'localhost')
# The rethinkdb database name
db_name = global_config.get('db_name', 'Discord_Bot')
# The rethinkdb certification
db_cert = global_config.get('db_cert', '')
# The rethinkdb port
db_port = global_config.get('db_port', 28015)
# The user and password assigned
db_user = global_config.get('db_user', 'admin')
db_pass = global_config.get('db_pass', '')
# We've set all the options we need to be able to connect
# so create a dictionary that we can use to unload to connect
# db_opts = {'host': db_host, 'db': db_name, 'port': db_port, 'ssl':
# {'ca_certs': db_cert}, 'user': db_user, 'password': db_pass}
db_opts = {'host': db_host, 'db': db_name, 'port': db_port, 'user': db_user, 'password': db_pass}
def command_prefix(bot, message):
if not message.guild:
return default_prefix
return bot.db.load('server_settings', key=message.guild.id, pluck='prefix') or default_prefix

View file

@ -92,7 +92,8 @@ async def create_banner(member, image_title, data):
stat_offset = draw.textsize(text, font=font, spacing=0)
font = ImageFont.truetype(whitneyMedium, 96)
draw.text((360, -4), text, (255, 255, 255), font=font, align="center")
# draw.text((360, -4), text, (255, 255, 255), font=font, align="center")
draw.text((360, -4), text, (255, 255, 255), font=font)
draw.text((360 + stat_offset[0], -4), stat_text, (0, 402, 504), font=font)
save_me = text_bar.resize((350, 20), Image.ANTIALIAS)
offset += 20

View file

@ -60,7 +60,7 @@ async def download_image(url):
return image
async def request(url, *, headers=None, payload=None, method='GET', attr='json'):
async def request(url, *, headers=None, payload=None, method='GET', attr='json', force_content_type_json=False):
# Make sure our User Agent is what's set, and ensure it's sent even if no headers are passed
if headers is None:
headers = {}
@ -83,7 +83,13 @@ async def request(url, *, headers=None, payload=None, method='GET', attr='json')
return_value = getattr(response, attr)
# Next check if this can be called
if callable(return_value):
return_value = return_value()
# This is use for json; it checks the mimetype instead of checking if the actual data
# This causes some places with different mimetypes to fail, even if it's valid json
# This check allows us to force the content_type to use whatever content type is given
if force_content_type_json:
return_value = return_value(content_type=response.headers['content-type'])
else:
return_value = return_value()
# If this is awaitable, await it
if inspect.isawaitable(return_value):
return_value = await return_value

View file

@ -51,11 +51,7 @@ class Playlist(EventEmitter):
Imports the songs from `playlist_url` and queues them to be played.
Returns a list of `entries` that have been enqueued.
:param playlist_url: The playlist url to be cut into individual urls and added to the playlist
"""
position = len(self.entries) + 1
entry_list = []
try:
info = await self.downloader.safe_extract_info(self.loop, playlist_url, download=False)
@ -65,6 +61,9 @@ class Playlist(EventEmitter):
if not info:
raise ExtractionError('Could not extract information from %s' % playlist_url)
if info.get('playlist') is None:
raise WrongEntryTypeError('This is not a playlist!')
# Once again, the generic extractor fucks things up.
if info.get('extractor', None) == 'generic':
url_field = 'url'

View file

@ -3,9 +3,10 @@ import time
import asyncio
from .exceptions import ExtractionError, WrongEntryTypeError, LiveStreamError
from .entry import get_header
class YoutubeDLSource(discord.FFmpegPCMAudio):
def __init__(self, playlist, url):
self.playlist = playlist
self.loop = playlist.loop
@ -14,7 +15,6 @@ class YoutubeDLSource(discord.FFmpegPCMAudio):
self.info = None
self.ready = False
self.error = False
asyncio.run_coroutine_threadsafe(self.download(), self.loop)
async def get_info(self):
try:
@ -33,10 +33,10 @@ class YoutubeDLSource(discord.FFmpegPCMAudio):
# Otherwise get the first result
else:
info = info['entries'][0]
self.url = info['webpage_url']
# If this isn't a search, then it is a playlist, this can't be done
else:
raise WrongEntryTypeError("This is a playlist.", True, info.get('webpage_url', None) or info.get('url', None))
raise WrongEntryTypeError("This is a playlist.", True,
info.get('webpage_url', None) or info.get('url', None))
if info['extractor'] in ['generic', 'Dropbox']:
try:
@ -55,7 +55,6 @@ class YoutubeDLSource(discord.FFmpegPCMAudio):
if headers.get('ice-audio-info'):
raise LiveStreamError("Cannot download from a livestream")
if info.get('is_live', False):
raise LiveStreamError("Cannot download from a livestream")
@ -64,6 +63,7 @@ class YoutubeDLSource(discord.FFmpegPCMAudio):
async def prepare(self):
await self.get_info()
asyncio.run_coroutine_threadsafe(self.download(), self.loop)
return self.info
async def download(self):
@ -78,7 +78,7 @@ class YoutubeDLSource(discord.FFmpegPCMAudio):
'before_options': '-nostdin',
'options': '-vn -b:a 128k'
}
super().__init__(self.downloader.ytdl.prepare_filename(result), **opts)
super().__init__(self.downloader.ytdl.prepare_filename(self.info), **opts)
@property
def title(self):

View file

@ -1,5 +1,5 @@
beautifulsoup4==4.6.0
Pillow==3.4.1
Pillow==4.2.0
rethinkdb==2.3.0.post6
ruamel.yaml==0.14.12
youtube-dl