Merge branch 'rewrite' of https://github.com/Phxntxm/Bonfire into rewrite
This commit is contained in:
commit
b13d6510a4
7
bot.py
7
bot.py
|
@ -80,7 +80,10 @@ async def on_command_error(ctx, error):
|
||||||
try:
|
try:
|
||||||
if isinstance(error.original, discord.Forbidden):
|
if isinstance(error.original, discord.Forbidden):
|
||||||
return
|
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
|
return
|
||||||
elif isinstance(error.original, aiohttp.ClientOSError):
|
elif isinstance(error.original, aiohttp.ClientOSError):
|
||||||
return
|
return
|
||||||
|
@ -93,7 +96,7 @@ async def on_command_error(ctx, error):
|
||||||
await ctx.message.channel.send(fmt)
|
await ctx.message.channel.send(fmt)
|
||||||
elif isinstance(error, commands.CheckFailure):
|
elif isinstance(error, commands.CheckFailure):
|
||||||
fmt = "You can't tell me what to do!"
|
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):
|
elif isinstance(error, commands.CommandOnCooldown):
|
||||||
m, s = divmod(error.retry_after, 60)
|
m, s = divmod(error.retry_after, 60)
|
||||||
fmt = "This command is on cooldown! Hold your horses! >:c\nTry again in {} minutes and {} seconds" \
|
fmt = "This command is on cooldown! Hold your horses! >:c\nTry again in {} minutes and {} seconds" \
|
||||||
|
|
78
cogs/misc.py
78
cogs/misc.py
|
@ -171,52 +171,68 @@ class Miscallaneous:
|
||||||
# in this command is changed
|
# in this command is changed
|
||||||
|
|
||||||
# Create the original embed object
|
# Create the original embed object
|
||||||
opts = {'title': 'Dev Server',
|
# Set the description include dev server (should be required) and the optional patreon link
|
||||||
'description': 'Join the server above for any questions/suggestions about me.',
|
description = "[Dev Server]({})".format(utils.dev_server)
|
||||||
'url': utils.dev_server}
|
if utils.patreon_link:
|
||||||
embed = discord.Embed(**opts)
|
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')
|
hm = self.bot.get_cog('Hangman')
|
||||||
ttt = self.bot.get_cog('TicTacToe')
|
ttt = self.bot.get_cog('TicTacToe')
|
||||||
bj = self.bot.get_cog('Blackjack')
|
bj = self.bot.get_cog('Blackjack')
|
||||||
interaction = self.bot.get_cog('Blackjack')
|
interaction = self.bot.get_cog('Interaction')
|
||||||
music = self.bot.get_cog('Music')
|
music = self.bot.get_cog('Music')
|
||||||
|
|
||||||
# Count the variable values; hangman, tictactoe, etc.
|
|
||||||
if hm:
|
if hm:
|
||||||
hm_games = len(hm.games)
|
value.append("Hangman games: {}".format(len(hm.games)))
|
||||||
if hm_games:
|
|
||||||
embed.add_field(name='Total Hangman games running', value=hm_games)
|
|
||||||
if ttt:
|
if ttt:
|
||||||
ttt_games = len(ttt.boards)
|
value.append("TicTacToe games: {}".format(len(ttt.boards)))
|
||||||
if ttt_games:
|
|
||||||
embed.add_field(name='Total TicTacToe games running', value=ttt_games)
|
|
||||||
if bj:
|
if bj:
|
||||||
bj_games = len(bj.games)
|
value.append("Blackjack games: {}".format(len(bj.games)))
|
||||||
if bj_games:
|
|
||||||
embed.add_field(name='Total blackjack games running', value=bj_games)
|
|
||||||
if interaction:
|
if interaction:
|
||||||
count_battles = 0
|
count_battles = 0
|
||||||
for battles in self.bot.get_cog('Interaction').battles.values():
|
for battles in self.bot.get_cog('Interaction').battles.values():
|
||||||
count_battles += len(battles)
|
count_battles += len(battles)
|
||||||
if count_battles:
|
value.append("Battles running: {}".format(len(bj.games)))
|
||||||
embed.add_field(name='Total battles games running', value=count_battles)
|
|
||||||
if music:
|
if music:
|
||||||
songs = len([x for x in music.voice_states.values() if x.playing])
|
songs = len([x for x in music.voice_states.values() if x.playing])
|
||||||
if songs:
|
value.append("Total songs playing: {}".format(songs))
|
||||||
embed.add_field(name='Total songs playing', value=songs)
|
embed.add_field(name=name, value="\n".join(value), inline=False)
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
await ctx.send(embed=embed)
|
await ctx.send(embed=embed)
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
from .voice_utilities import *
|
from .voice_utilities import *
|
||||||
from discord import FFmpegPCMAudio, PCMVolumeTransformer
|
from discord import PCMVolumeTransformer
|
||||||
|
|
||||||
import discord
|
import discord
|
||||||
from discord.ext import commands
|
from discord.ext import commands
|
||||||
|
@ -21,7 +21,7 @@ if not discord.opus.is_loaded():
|
||||||
|
|
||||||
|
|
||||||
class VoiceState:
|
class VoiceState:
|
||||||
def __init__(self, guild, bot, user_queue=False):
|
def __init__(self, guild, bot, user_queue=False, volume=None):
|
||||||
self.guild = guild
|
self.guild = guild
|
||||||
self.songs = Playlist(bot)
|
self.songs = Playlist(bot)
|
||||||
self.djs = deque()
|
self.djs = deque()
|
||||||
|
@ -31,7 +31,7 @@ class VoiceState:
|
||||||
self.skip_votes = set()
|
self.skip_votes = set()
|
||||||
self.user_queue = user_queue
|
self.user_queue = user_queue
|
||||||
self.loop = bot.loop
|
self.loop = bot.loop
|
||||||
self._volume = .5
|
self._volume = volume or .5
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def volume(self):
|
def volume(self):
|
||||||
|
@ -377,8 +377,10 @@ class Music:
|
||||||
|
|
||||||
# If we have connnected, create our voice state
|
# If we have connnected, create our voice state
|
||||||
queue_type = self.bot.db.load('server_settings', key=channel.guild.id, pluck='queue_type')
|
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"
|
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 we can send messages, edit it to let the channel know we have succesfully joined
|
||||||
if msg:
|
if msg:
|
||||||
|
@ -469,7 +471,8 @@ class Music:
|
||||||
"""Imports a song into the current voice queue"""
|
"""Imports a song into the current voice queue"""
|
||||||
# If we don't have a voice state yet, create one
|
# 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'):
|
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
|
return
|
||||||
if ctx.message.guild.id not in self.voice_states:
|
if ctx.message.guild.id not in self.voice_states:
|
||||||
if not await ctx.invoke(self.join):
|
if not await ctx.invoke(self.join):
|
||||||
|
@ -490,7 +493,16 @@ class Music:
|
||||||
return
|
return
|
||||||
|
|
||||||
song = re.sub('[<>\[\]]', '', song)
|
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.command()
|
||||||
@commands.guild_only()
|
@commands.guild_only()
|
||||||
|
@ -537,6 +549,9 @@ class Music:
|
||||||
|
|
||||||
try:
|
try:
|
||||||
entry = await self.add_entry(song, ctx)
|
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:
|
except LiveStreamError as e:
|
||||||
await ctx.send(str(e))
|
await ctx.send(str(e))
|
||||||
except WrongEntryTypeError:
|
except WrongEntryTypeError:
|
||||||
|
@ -586,6 +601,8 @@ class Music:
|
||||||
await ctx.send("Sorry but the max volume is 100%")
|
await ctx.send("Sorry but the max volume is 100%")
|
||||||
else:
|
else:
|
||||||
state.volume = value
|
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))
|
await ctx.send('Set the volume to {:.0%}'.format(state.volume))
|
||||||
|
|
||||||
@commands.command()
|
@commands.command()
|
||||||
|
@ -633,7 +650,7 @@ class Music:
|
||||||
# Then stop playing, and disconnect
|
# Then stop playing, and disconnect
|
||||||
if voice:
|
if voice:
|
||||||
voice.stop()
|
voice.stop()
|
||||||
await voice.disconnect()
|
await voice.disconnect(force=True)
|
||||||
|
|
||||||
@commands.command()
|
@commands.command()
|
||||||
@commands.guild_only()
|
@commands.guild_only()
|
||||||
|
|
135
cogs/raffle.py
135
cogs/raffle.py
|
@ -8,7 +8,6 @@ import pendulum
|
||||||
import re
|
import re
|
||||||
import asyncio
|
import asyncio
|
||||||
import traceback
|
import traceback
|
||||||
import rethinkdb as r
|
|
||||||
|
|
||||||
|
|
||||||
class Raffle:
|
class Raffle:
|
||||||
|
@ -36,63 +35,66 @@ class Raffle:
|
||||||
|
|
||||||
for raffle in raffles:
|
for raffle in raffles:
|
||||||
server = self.bot.get_guild(int(raffle['server_id']))
|
server = self.bot.get_guild(int(raffle['server_id']))
|
||||||
title = raffle['title']
|
for r in raffle['raffles']:
|
||||||
entrants = raffle['entrants']
|
title = r['title']
|
||||||
raffle_id = raffle['id']
|
entrants = r['entrants']
|
||||||
|
raffle_id = r['id']
|
||||||
|
|
||||||
# Check to see if this cog can find the server in question
|
# Check to see if this cog can find the server in question
|
||||||
if server is None:
|
if server is None:
|
||||||
await self.bot.db.query(r.table('raffles').get(raffle_id).delete())
|
await self.bot.db.query(r.table('raffles').get(raffle_id).delete())
|
||||||
continue
|
continue
|
||||||
|
|
||||||
now = pendulum.utcnow()
|
now = pendulum.utcnow()
|
||||||
expires = pendulum.parse(raffle['expires'])
|
expires = pendulum.parse(r['expires'])
|
||||||
|
|
||||||
# Now lets compare and see if this raffle has ended, if not just continue
|
# Now lets compare and see if this raffle has ended, if not just continue
|
||||||
if expires > now:
|
if expires > now:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Make sure there are actually entrants
|
# Make sure there are actually entrants
|
||||||
if len(entrants) == 0:
|
if len(entrants) == 0:
|
||||||
fmt = 'Sorry, but there were no entrants for the raffle `{}`!'.format(title)
|
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)
|
|
||||||
else:
|
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
|
# Lets make sure we don't get caught in an infinite loop
|
||||||
notifications = self.bot.db.load('server_settings', key=server.id, pluck='notifications') or {}
|
# Realistically having more than 50 random entrants found that aren't in the server anymore
|
||||||
# Set our default to either the one set, or the default channel of the server
|
# Isn't something that should be an issue, but better safe than sorry
|
||||||
default_channel_id = notifications.get('default') or server.id
|
count += 1
|
||||||
# If it is has been overriden by picarto notifications setting, use this
|
if count >= 50:
|
||||||
channel_id = notifications.get('raffle') or default_channel_id
|
break
|
||||||
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
|
if winner is None:
|
||||||
await self.bot.db.query(r.table('raffles').get(raffle_id).delete())
|
fmt = 'I couldn\'t find an entrant that is still in this server, for the raffle `{}`!'.format(
|
||||||
# Now...this is an ugly idea yes, but due to the way raffles are setup currently (they'll be changed in
|
title)
|
||||||
# the future) The cache does not update, and leaves behind this deletion....so we need to manually update
|
else:
|
||||||
# the cache here
|
fmt = 'The raffle `{}` has just ended! The winner is {}!'.format(title, winner.display_name)
|
||||||
await self.bot.db.cache.get('raffles').refresh()
|
|
||||||
|
# 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.command()
|
||||||
@commands.guild_only()
|
@commands.guild_only()
|
||||||
|
@ -103,8 +105,7 @@ class Raffle:
|
||||||
|
|
||||||
EXAMPLE: !raffles
|
EXAMPLE: !raffles
|
||||||
RESULT: A list of the raffles setup on this server"""
|
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', key=ctx.message.guild.id, pluck='raffles')
|
||||||
raffles = self.bot.db.load('raffles', table_filter=r_filter)
|
|
||||||
if not raffles:
|
if not raffles:
|
||||||
await ctx.send("There are currently no raffles setup on this server!")
|
await ctx.send("There are currently no raffles setup on this server!")
|
||||||
return
|
return
|
||||||
|
@ -133,19 +134,15 @@ class Raffle:
|
||||||
RESULT: You've entered the first raffle!"""
|
RESULT: You've entered the first raffle!"""
|
||||||
# Lets let people use 1 - (length of raffles) and handle 0 base ourselves
|
# Lets let people use 1 - (length of raffles) and handle 0 base ourselves
|
||||||
raffle_id -= 1
|
raffle_id -= 1
|
||||||
r_filter = {'server_id': str(ctx.message.guild.id)}
|
|
||||||
author = ctx.message.author
|
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:
|
if raffles is None:
|
||||||
await ctx.send("There are currently no raffles setup on this server!")
|
await ctx.send("There are currently no raffles setup on this server!")
|
||||||
return
|
return
|
||||||
|
|
||||||
if isinstance(raffles, list):
|
raffle_count = len(raffles)
|
||||||
raffle_count = len(raffles)
|
|
||||||
else:
|
|
||||||
raffles = [raffles]
|
|
||||||
raffle_count = 1
|
|
||||||
|
|
||||||
# There is only one raffle, so use the first's info
|
# There is only one raffle, so use the first's info
|
||||||
if raffle_count == 1:
|
if raffle_count == 1:
|
||||||
|
@ -157,8 +154,8 @@ class Raffle:
|
||||||
entrants.append(str(author.id))
|
entrants.append(str(author.id))
|
||||||
|
|
||||||
update = {
|
update = {
|
||||||
'entrants': entrants,
|
'raffles': raffles,
|
||||||
'id': raffles[0]['id']
|
'server_id': key
|
||||||
}
|
}
|
||||||
self.bot.db.save('raffles', update)
|
self.bot.db.save('raffles', update)
|
||||||
await ctx.send("{} you have just entered the raffle!".format(author.mention))
|
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
|
# Since we have no good thing to filter things off of, lets use the internal rethinkdb id
|
||||||
|
|
||||||
update = {
|
update = {
|
||||||
'entrants': entrants,
|
'raffles': raffles,
|
||||||
'id': raffles[raffle_id]['id']
|
'server_id': key
|
||||||
}
|
}
|
||||||
self.bot.db.save('raffles', update)
|
self.bot.db.save('raffles', update)
|
||||||
await ctx.send("{} you have just entered the raffle!".format(author.mention))
|
await ctx.send("{} you have just entered the raffle!".format(author.mention))
|
||||||
|
@ -270,11 +267,15 @@ class Raffle:
|
||||||
'expires': expires.to_datetime_string(),
|
'expires': expires.to_datetime_string(),
|
||||||
'entrants': [],
|
'entrants': [],
|
||||||
'author': str(author.id),
|
'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
|
raffles = self.bot.db.load('raffles', key=server.id, pluck='raffles') or []
|
||||||
self.bot.db.save('raffles', entry)
|
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!")
|
await ctx.send("I have just saved your new raffle!")
|
||||||
|
|
||||||
@raffle.command(name='alerts')
|
@raffle.command(name='alerts')
|
||||||
|
|
|
@ -4,6 +4,7 @@ from discord.ext import commands
|
||||||
from . import utils
|
from . import utils
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
import asyncio
|
||||||
|
|
||||||
|
|
||||||
class Stats:
|
class Stats:
|
||||||
|
@ -11,6 +12,53 @@ class Stats:
|
||||||
|
|
||||||
def __init__(self, bot):
|
def __init__(self, bot):
|
||||||
self.bot = 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.command()
|
||||||
@commands.guild_only()
|
@commands.guild_only()
|
||||||
|
@ -277,6 +325,20 @@ class Stats:
|
||||||
fmt = fmt.format(member.display_name, record, server_rank, overall_rank, rating)
|
fmt = fmt.format(member.display_name, record, server_rank, overall_rank, rating)
|
||||||
await ctx.send('```\n{}```'.format(fmt))
|
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):
|
def setup(bot):
|
||||||
bot.add_cog(Stats(bot))
|
bot.add_cog(Stats(bot))
|
||||||
|
|
|
@ -22,7 +22,6 @@ except KeyError:
|
||||||
print("Please use config.yml.sample to setup a valid config file")
|
print("Please use config.yml.sample to setup a valid config file")
|
||||||
quit()
|
quit()
|
||||||
|
|
||||||
|
|
||||||
# Default bot's description
|
# Default bot's description
|
||||||
bot_description = global_config.get("description")
|
bot_description = global_config.get("description")
|
||||||
# Bot's default prefix for commands
|
# Bot's default prefix for commands
|
||||||
|
@ -45,6 +44,11 @@ dev_server = global_config.get("dev_server", "")
|
||||||
user_agent = global_config.get('user_agent', None)
|
user_agent = global_config.get('user_agent', None)
|
||||||
# The URL to proxy youtube_dl's requests through
|
# The URL to proxy youtube_dl's requests through
|
||||||
ytdl_proxy = global_config.get('youtube_dl_proxy', None)
|
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
|
# The extensions to load
|
||||||
extensions = [
|
extensions = [
|
||||||
'cogs.interaction',
|
'cogs.interaction',
|
||||||
|
|
|
@ -92,7 +92,8 @@ async def create_banner(member, image_title, data):
|
||||||
stat_offset = draw.textsize(text, font=font, spacing=0)
|
stat_offset = draw.textsize(text, font=font, spacing=0)
|
||||||
|
|
||||||
font = ImageFont.truetype(whitneyMedium, 96)
|
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)
|
draw.text((360 + stat_offset[0], -4), stat_text, (0, 402, 504), font=font)
|
||||||
save_me = text_bar.resize((350, 20), Image.ANTIALIAS)
|
save_me = text_bar.resize((350, 20), Image.ANTIALIAS)
|
||||||
offset += 20
|
offset += 20
|
||||||
|
|
|
@ -60,7 +60,7 @@ async def download_image(url):
|
||||||
return image
|
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
|
# Make sure our User Agent is what's set, and ensure it's sent even if no headers are passed
|
||||||
if headers is None:
|
if headers is None:
|
||||||
headers = {}
|
headers = {}
|
||||||
|
@ -83,7 +83,13 @@ async def request(url, *, headers=None, payload=None, method='GET', attr='json')
|
||||||
return_value = getattr(response, attr)
|
return_value = getattr(response, attr)
|
||||||
# Next check if this can be called
|
# Next check if this can be called
|
||||||
if callable(return_value):
|
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 this is awaitable, await it
|
||||||
if inspect.isawaitable(return_value):
|
if inspect.isawaitable(return_value):
|
||||||
return_value = await return_value
|
return_value = await return_value
|
||||||
|
|
|
@ -51,11 +51,7 @@ class Playlist(EventEmitter):
|
||||||
Imports the songs from `playlist_url` and queues them to be played.
|
Imports the songs from `playlist_url` and queues them to be played.
|
||||||
|
|
||||||
Returns a list of `entries` that have been enqueued.
|
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:
|
try:
|
||||||
info = await self.downloader.safe_extract_info(self.loop, playlist_url, download=False)
|
info = await self.downloader.safe_extract_info(self.loop, playlist_url, download=False)
|
||||||
|
@ -65,6 +61,9 @@ class Playlist(EventEmitter):
|
||||||
if not info:
|
if not info:
|
||||||
raise ExtractionError('Could not extract information from %s' % playlist_url)
|
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.
|
# Once again, the generic extractor fucks things up.
|
||||||
if info.get('extractor', None) == 'generic':
|
if info.get('extractor', None) == 'generic':
|
||||||
url_field = 'url'
|
url_field = 'url'
|
||||||
|
|
|
@ -3,9 +3,10 @@ import time
|
||||||
import asyncio
|
import asyncio
|
||||||
|
|
||||||
from .exceptions import ExtractionError, WrongEntryTypeError, LiveStreamError
|
from .exceptions import ExtractionError, WrongEntryTypeError, LiveStreamError
|
||||||
|
from .entry import get_header
|
||||||
|
|
||||||
|
|
||||||
class YoutubeDLSource(discord.FFmpegPCMAudio):
|
class YoutubeDLSource(discord.FFmpegPCMAudio):
|
||||||
|
|
||||||
def __init__(self, playlist, url):
|
def __init__(self, playlist, url):
|
||||||
self.playlist = playlist
|
self.playlist = playlist
|
||||||
self.loop = playlist.loop
|
self.loop = playlist.loop
|
||||||
|
@ -14,7 +15,6 @@ class YoutubeDLSource(discord.FFmpegPCMAudio):
|
||||||
self.info = None
|
self.info = None
|
||||||
self.ready = False
|
self.ready = False
|
||||||
self.error = False
|
self.error = False
|
||||||
asyncio.run_coroutine_threadsafe(self.download(), self.loop)
|
|
||||||
|
|
||||||
async def get_info(self):
|
async def get_info(self):
|
||||||
try:
|
try:
|
||||||
|
@ -33,10 +33,10 @@ class YoutubeDLSource(discord.FFmpegPCMAudio):
|
||||||
# Otherwise get the first result
|
# Otherwise get the first result
|
||||||
else:
|
else:
|
||||||
info = info['entries'][0]
|
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
|
# If this isn't a search, then it is a playlist, this can't be done
|
||||||
else:
|
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']:
|
if info['extractor'] in ['generic', 'Dropbox']:
|
||||||
try:
|
try:
|
||||||
|
@ -55,7 +55,6 @@ class YoutubeDLSource(discord.FFmpegPCMAudio):
|
||||||
if headers.get('ice-audio-info'):
|
if headers.get('ice-audio-info'):
|
||||||
raise LiveStreamError("Cannot download from a livestream")
|
raise LiveStreamError("Cannot download from a livestream")
|
||||||
|
|
||||||
|
|
||||||
if info.get('is_live', False):
|
if info.get('is_live', False):
|
||||||
raise LiveStreamError("Cannot download from a livestream")
|
raise LiveStreamError("Cannot download from a livestream")
|
||||||
|
|
||||||
|
@ -64,6 +63,7 @@ class YoutubeDLSource(discord.FFmpegPCMAudio):
|
||||||
|
|
||||||
async def prepare(self):
|
async def prepare(self):
|
||||||
await self.get_info()
|
await self.get_info()
|
||||||
|
asyncio.run_coroutine_threadsafe(self.download(), self.loop)
|
||||||
return self.info
|
return self.info
|
||||||
|
|
||||||
async def download(self):
|
async def download(self):
|
||||||
|
@ -78,7 +78,7 @@ class YoutubeDLSource(discord.FFmpegPCMAudio):
|
||||||
'before_options': '-nostdin',
|
'before_options': '-nostdin',
|
||||||
'options': '-vn -b:a 128k'
|
'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
|
@property
|
||||||
def title(self):
|
def title(self):
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
beautifulsoup4==4.6.0
|
beautifulsoup4==4.6.0
|
||||||
Pillow==3.4.1
|
Pillow==4.2.0
|
||||||
rethinkdb==2.3.0.post6
|
rethinkdb==2.3.0.post6
|
||||||
ruamel.yaml==0.14.12
|
ruamel.yaml==0.14.12
|
||||||
youtube-dl
|
youtube-dl
|
||||||
|
|
Loading…
Reference in a new issue