commit
7fd5d70c65
15 changed files with 678 additions and 731 deletions
61
bot.py
61
bot.py
|
@ -42,44 +42,42 @@ logging.basicConfig(level=logging.INFO, filename='bonfire.log')
|
||||||
async def on_ready():
|
async def on_ready():
|
||||||
# Change the status upon connection to the default status
|
# Change the status upon connection to the default status
|
||||||
await bot.change_status(discord.Game(name=config.default_status, type=0))
|
await bot.change_status(discord.Game(name=config.default_status, type=0))
|
||||||
channel_id = await config.get_content('restart_server')
|
|
||||||
channel_id = channel_id or 0
|
|
||||||
|
|
||||||
if not hasattr(bot, 'uptime'):
|
if not hasattr(bot, 'uptime'):
|
||||||
bot.uptime = pendulum.utcnow()
|
bot.uptime = pendulum.utcnow()
|
||||||
|
|
||||||
# Just in case the bot was restarted while someone was battling, clear it so they do not get stuck
|
|
||||||
await config.save_content('battling', {})
|
|
||||||
# Check if the bot was restarted, if so send a message to the channel the bot was restarted from
|
|
||||||
if channel_id != 0:
|
|
||||||
destination = discord.utils.find(lambda m: m.id == channel_id, bot.get_all_channels())
|
|
||||||
await bot.send_message(destination, "I have just finished restarting!")
|
|
||||||
await config.save_content('restart_server', 0)
|
|
||||||
|
|
||||||
|
|
||||||
@bot.event
|
@bot.event
|
||||||
async def on_member_join(member):
|
async def on_member_join(member):
|
||||||
notifications = await config.get_content('user_notifications')
|
r_filter = {'server_id': member.server.id}
|
||||||
server_notifications = notifications.get(member.server.id)
|
notifications = await config.get_content('user_notifications', r_filter)
|
||||||
|
try:
|
||||||
# By default, notifications should be off unless explicitly turned on
|
channel_id = notifications[0]['channel_id']
|
||||||
if not server_notifications:
|
except TypeError:
|
||||||
return
|
return
|
||||||
|
|
||||||
channel = discord.utils.get(member.server.channels, id=server_notifications)
|
# By default, notifications should be off unless explicitly turned on
|
||||||
|
if not channel_id:
|
||||||
|
return
|
||||||
|
|
||||||
|
channel = discord.utils.get(member.server.channels, id=notifications)
|
||||||
await bot.send_message(channel, "Welcome to the '{0.server.name}' server {0.mention}!".format(member))
|
await bot.send_message(channel, "Welcome to the '{0.server.name}' server {0.mention}!".format(member))
|
||||||
|
|
||||||
|
|
||||||
@bot.event
|
@bot.event
|
||||||
async def on_member_remove(member):
|
async def on_member_remove(member):
|
||||||
notifications = await config.get_content('user_notifications')
|
r_filter = {'server_id': member.server.id}
|
||||||
server_notifications = notifications.get(member.server.id)
|
notifications = await config.get_content('user_notifications', r_filter)
|
||||||
|
try:
|
||||||
# By default, notifications should be off unless explicitly turned on
|
channel_id = notifications[0]['channel_id']
|
||||||
if not server_notifications:
|
except TypeError:
|
||||||
return
|
return
|
||||||
|
|
||||||
channel = discord.utils.get(member.server.channels, id=server_notifications)
|
# By default, notifications should be off unless explicitly turned on
|
||||||
|
if not channel_id:
|
||||||
|
return
|
||||||
|
|
||||||
|
channel = discord.utils.get(member.server.channels, id=channel_id)
|
||||||
await bot.send_message(channel,
|
await bot.send_message(channel,
|
||||||
"{0} has left the server, I hope it wasn't because of something I said :c".format(
|
"{0} has left the server, I hope it wasn't because of something I said :c".format(
|
||||||
member.display_name))
|
member.display_name))
|
||||||
|
@ -94,19 +92,22 @@ async def on_message(message):
|
||||||
|
|
||||||
@bot.event
|
@bot.event
|
||||||
async def on_command_completion(command, ctx):
|
async def on_command_completion(command, ctx):
|
||||||
# There's no reason to continue waiting for this to complete, so lets immediately lanch this in a new future
|
# There's no reason to continue waiting for this to complete, so lets immediately launch this in a new future
|
||||||
bot.loop.create_task(process_command(command, ctx))
|
bot.loop.create_task(process_command(command, ctx))
|
||||||
|
|
||||||
|
|
||||||
async def process_command(command, ctx):
|
async def process_command(command, ctx):
|
||||||
# This try catch is only here while this is first being implemented
|
# This try catch is only here while this is first being implemented
|
||||||
# It will be removed once I ensure this is working correctly
|
# It will be removed once I ensure this is working correctly
|
||||||
try:
|
|
||||||
author = ctx.message.author
|
author = ctx.message.author
|
||||||
server = ctx.message.server
|
server = ctx.message.server
|
||||||
|
|
||||||
total_command_usage = await config.get_content('command_usage')
|
r_filter = {'command': command.qualified_name}
|
||||||
command_usage = total_command_usage.get(command.qualified_name, {})
|
command_usage = await config.get_content('command_usage', r_filter)
|
||||||
|
if command_usage is None:
|
||||||
|
command_usage = {}
|
||||||
|
else:
|
||||||
|
command_usage = command_usage[0]
|
||||||
# Add one to the total usage for this command, basing it off 0 to start with (obviously)
|
# Add one to the total usage for this command, basing it off 0 to start with (obviously)
|
||||||
total_usage = command_usage.get('total_usage', 0) + 1
|
total_usage = command_usage.get('total_usage', 0) + 1
|
||||||
command_usage['total_usage'] = total_usage
|
command_usage['total_usage'] = total_usage
|
||||||
|
@ -124,12 +125,8 @@ async def process_command(command, ctx):
|
||||||
command_usage['server_usage'] = total_server_usage
|
command_usage['server_usage'] = total_server_usage
|
||||||
|
|
||||||
# Save all the changes
|
# Save all the changes
|
||||||
total_command_usage[command.qualified_name] = command_usage
|
if not await config.update_content('command_usage', command_usage, r_filter):
|
||||||
await config.save_content('command_usage', total_command_usage)
|
await config.add_content('command_usage', command_usage, r_filter)
|
||||||
except Exception as error:
|
|
||||||
with open("error_log", 'a') as f:
|
|
||||||
traceback.print_tb(error.__traceback__, file=f)
|
|
||||||
print('{0.__class__.__name__}: {0}'.format(error), file=f)
|
|
||||||
|
|
||||||
|
|
||||||
@bot.event
|
@bot.event
|
||||||
|
|
16
cogs/core.py
16
cogs/core.py
|
@ -18,7 +18,6 @@ class Core:
|
||||||
def __init__(self, bot):
|
def __init__(self, bot):
|
||||||
self.bot = bot
|
self.bot = bot
|
||||||
|
|
||||||
|
|
||||||
@commands.command()
|
@commands.command()
|
||||||
@checks.custom_perms(send_messages=True)
|
@checks.custom_perms(send_messages=True)
|
||||||
async def calendar(self, month: str = None, year: int = None):
|
async def calendar(self, month: str = None, year: int = None):
|
||||||
|
@ -66,16 +65,11 @@ class Core:
|
||||||
fmt = {}
|
fmt = {}
|
||||||
|
|
||||||
bot_data = await config.get_content('bot_data')
|
bot_data = await config.get_content('bot_data')
|
||||||
total_data = {}
|
total_data = {'member_count': 0,
|
||||||
for shard, values in bot_data.items():
|
'server_count': 0}
|
||||||
for key, value in values.items():
|
for entry in bot_data:
|
||||||
if key in total_data:
|
total_data['member_count'] += entry['member_count']
|
||||||
total_data[key] += value
|
total_data['server_count'] += entry['server_count']
|
||||||
else:
|
|
||||||
total_data[key] = value
|
|
||||||
|
|
||||||
# We can pretty safely assume that the author is going to be in at least one channel with the bot
|
|
||||||
# So find the author based on that list
|
|
||||||
|
|
||||||
fmt['Official Bot Server'] = config.dev_server
|
fmt['Official Bot Server'] = config.dev_server
|
||||||
fmt['Uptime'] = (pendulum.utcnow() - self.bot.uptime).in_words()
|
fmt['Uptime'] = (pendulum.utcnow() - self.bot.uptime).in_words()
|
||||||
|
|
|
@ -6,55 +6,6 @@ import discord
|
||||||
import random
|
import random
|
||||||
|
|
||||||
|
|
||||||
async def update_battle_records(winner, loser):
|
|
||||||
# We're using the Harkness scale to rate
|
|
||||||
# http://opnetchessclub.wikidot.com/harkness-rating-system
|
|
||||||
battles = await config.get_content('battle_records')
|
|
||||||
|
|
||||||
# Start ratings at 1000 if they have no rating
|
|
||||||
winner_stats = battles.get(winner.id) or {}
|
|
||||||
winner_rating = winner_stats.get('rating') or 1000
|
|
||||||
|
|
||||||
loser_stats = battles.get(loser.id) or {}
|
|
||||||
loser_rating = loser_stats.get('rating') or 1000
|
|
||||||
|
|
||||||
# The scale is based off of increments of 25, increasing the change by 1 for each increment
|
|
||||||
# That is all this loop does, increment the "change" for every increment of 25
|
|
||||||
# The change caps off at 300 however, so break once we are over that limit
|
|
||||||
difference = abs(winner_rating - loser_rating)
|
|
||||||
rating_change = 0
|
|
||||||
count = 25
|
|
||||||
while count <= difference:
|
|
||||||
if count > 300:
|
|
||||||
break
|
|
||||||
rating_change += 1
|
|
||||||
count += 25
|
|
||||||
|
|
||||||
# 16 is the base change, increased or decreased based on whoever has the higher current rating
|
|
||||||
if winner_rating > loser_rating:
|
|
||||||
winner_rating += 16 - rating_change
|
|
||||||
loser_rating -= 16 - rating_change
|
|
||||||
else:
|
|
||||||
winner_rating += 16 + rating_change
|
|
||||||
loser_rating -= 16 + rating_change
|
|
||||||
|
|
||||||
# Just increase wins/losses for each person, making sure it's at least 0
|
|
||||||
winner_wins = winner_stats.get('wins') or 0
|
|
||||||
winner_losses = winner_stats.get('losses') or 0
|
|
||||||
loser_wins = loser_stats.get('wins') or 0
|
|
||||||
loser_losses = loser_stats.get('losses') or 0
|
|
||||||
winner_wins += 1
|
|
||||||
loser_losses += 1
|
|
||||||
|
|
||||||
# Now save the new wins, losses, and ratings
|
|
||||||
winner_stats = {'wins': winner_wins, 'losses': winner_losses, 'rating': winner_rating}
|
|
||||||
loser_stats = {'wins': loser_wins, 'losses': loser_losses, 'rating': loser_rating}
|
|
||||||
battles[winner.id] = winner_stats
|
|
||||||
battles[loser.id] = loser_stats
|
|
||||||
|
|
||||||
return await config.save_content('battle_records', battles)
|
|
||||||
|
|
||||||
|
|
||||||
class Interaction:
|
class Interaction:
|
||||||
"""Commands that interact with another user"""
|
"""Commands that interact with another user"""
|
||||||
|
|
||||||
|
@ -139,10 +90,10 @@ class Interaction:
|
||||||
# All we need to do is change what order the challengers are printed/added as a paramater
|
# All we need to do is change what order the challengers are printed/added as a paramater
|
||||||
if random.SystemRandom().randint(0, 1):
|
if random.SystemRandom().randint(0, 1):
|
||||||
await self.bot.say(fmt.format(battleP1.mention, battleP2.mention))
|
await self.bot.say(fmt.format(battleP1.mention, battleP2.mention))
|
||||||
await update_battle_records(battleP1, battleP2)
|
await config.update_records('battle_records', battleP1, battleP2)
|
||||||
else:
|
else:
|
||||||
await self.bot.say(fmt.format(battleP2.mention, battleP1.mention))
|
await self.bot.say(fmt.format(battleP2.mention, battleP1.mention))
|
||||||
await update_battle_records(battleP2, battleP1)
|
await config.update_records('battle_records', battleP2, battleP1)
|
||||||
|
|
||||||
@commands.command(pass_context=True, no_pm=True)
|
@commands.command(pass_context=True, no_pm=True)
|
||||||
@checks.custom_perms(send_messages=True)
|
@checks.custom_perms(send_messages=True)
|
||||||
|
@ -182,17 +133,22 @@ class Interaction:
|
||||||
await self.bot.say("Why the heck are you booping me? Get away from me >:c")
|
await self.bot.say("Why the heck are you booping me? Get away from me >:c")
|
||||||
return
|
return
|
||||||
|
|
||||||
boops = await config.get_content('boops')
|
r_filter = {'member_id': booper.id}
|
||||||
|
boops = await config.get_content('boops', r_filter)
|
||||||
|
if boops is not None:
|
||||||
|
boops = boops[0]['boops']
|
||||||
|
# If the booper has never booped the member provided, assure it's 0
|
||||||
|
amount = boops.get(boopee.id, 0) + 1
|
||||||
|
boops[boopee.id] = amount
|
||||||
|
|
||||||
# Get all the booped stats for the author
|
await config.update_content('boops', {'boops': boops}, r_filter)
|
||||||
# Set to default as having just booped boopee 0 times, so that we can increment that
|
else:
|
||||||
booper_boops = boops.get(ctx.message.author.id, {boopee.id: 0})
|
entry = {'member_id': booper.id,
|
||||||
# If the booper has never booped the member provided, assume 0 like above so we can increment like normal
|
'boops': {boopee.id: 1}}
|
||||||
amount = booper_boops.get(boopee.id, 0) + 1
|
|
||||||
booper_boops[boopee.id] = amount
|
await config.add_content('boops', entry, r_filter)
|
||||||
boops[ctx.message.author.id] = booper_boops
|
amount = 1
|
||||||
|
|
||||||
await config.save_content('boops', boops)
|
|
||||||
fmt = "{0.mention} has just booped you {1.mention}! That's {2} times now!"
|
fmt = "{0.mention} has just booped you {1.mention}! That's {2} times now!"
|
||||||
await self.bot.say(fmt.format(booper, boopee, amount))
|
await self.bot.say(fmt.format(booper, boopee, amount))
|
||||||
|
|
||||||
|
|
|
@ -69,12 +69,13 @@ class Links:
|
||||||
if len(search) > 0:
|
if len(search) > 0:
|
||||||
# This sets the url as url?q=search+terms
|
# This sets the url as url?q=search+terms
|
||||||
url = 'https://derpibooru.org/search.json?q={}'.format('+'.join(search))
|
url = 'https://derpibooru.org/search.json?q={}'.format('+'.join(search))
|
||||||
nsfw_channels = await config.get_content("nsfw_channels")
|
|
||||||
nsfw_channels = nsfw_channels.get('registered') or []
|
r_filter = {'channel_id': ctx.message.channel.id}
|
||||||
|
nsfw_channels = await config.get_content("nsfw_channels", r_filter)
|
||||||
# If this is a nsfw channel, we just need to tack on 'explicit' to the terms
|
# If this is a nsfw channel, we just need to tack on 'explicit' to the terms
|
||||||
# Also use the custom filter that I have setup, that blocks some certain tags
|
# Also use the custom filter that I have setup, that blocks some certain tags
|
||||||
# If the channel is not nsfw, we don't need to do anything, as the default filter blocks explicit
|
# If the channel is not nsfw, we don't need to do anything, as the default filter blocks explicit
|
||||||
if ctx.message.channel.id in nsfw_channels:
|
if nsfw_channels is not None:
|
||||||
url += ",+%28explicit+OR+suggestive%29&filter_id=95938"
|
url += ",+%28explicit+OR+suggestive%29&filter_id=95938"
|
||||||
else:
|
else:
|
||||||
url += ",+safe"
|
url += ",+safe"
|
||||||
|
@ -126,11 +127,11 @@ class Links:
|
||||||
# Due to this, send a message saying we're looking up the information first
|
# Due to this, send a message saying we're looking up the information first
|
||||||
await self.bot.say("Looking up an image with those tags....")
|
await self.bot.say("Looking up an image with those tags....")
|
||||||
|
|
||||||
nsfw_channels = await config.get_content("nsfw_channels")
|
r_filter = {'channel_id': ctx.message.channel.id}
|
||||||
nsfw_channels = nsfw_channels.get('registered') or []
|
nsfw_channels = await config.get_content("nsfw_channels", r_filter)
|
||||||
# e621 by default does not filter explicit content, so tack on
|
# e621 by default does not filter explicit content, so tack on
|
||||||
# safe/explicit based on if this channel is nsfw or not
|
# safe/explicit based on if this channel is nsfw or not
|
||||||
if ctx.message.channel.id in nsfw_channels:
|
if nsfw_channels is not None:
|
||||||
url += "%20rating:explicit"
|
url += "%20rating:explicit"
|
||||||
else:
|
else:
|
||||||
url += "%20rating:safe"
|
url += "%20rating:safe"
|
||||||
|
|
273
cogs/mod.py
273
cogs/mod.py
|
@ -1,9 +1,11 @@
|
||||||
from discord.ext import commands
|
from discord.ext import commands
|
||||||
from .utils import checks
|
from .utils import checks
|
||||||
from .utils import config
|
from .utils import config
|
||||||
|
|
||||||
import discord
|
import discord
|
||||||
import re
|
import re
|
||||||
import asyncio
|
import asyncio
|
||||||
|
import rethinkdb as r
|
||||||
|
|
||||||
valid_perms = [p for p in dir(discord.Permissions) if isinstance(getattr(discord.Permissions, p), property)]
|
valid_perms = [p for p in dir(discord.Permissions) if isinstance(getattr(discord.Permissions, p), property)]
|
||||||
|
|
||||||
|
@ -14,15 +16,39 @@ class Mod:
|
||||||
def __init__(self, bot):
|
def __init__(self, bot):
|
||||||
self.bot = bot
|
self.bot = bot
|
||||||
|
|
||||||
|
def find_command(self, command):
|
||||||
|
# This method ensures the command given is valid. We need to loop through commands
|
||||||
|
# As self.bot.commands only includes parent commands
|
||||||
|
# So we are splitting the command in parts, looping through the commands
|
||||||
|
# And getting the subcommand based on the next part
|
||||||
|
# If we try to access commands of a command that isn't a group
|
||||||
|
# We'll hit an AttributeError, meaning an invalid command was given
|
||||||
|
# If we loop through and don't find anything, cmd will still be None
|
||||||
|
# And we'll report an invalid was given as well
|
||||||
|
cmd = None
|
||||||
|
|
||||||
|
for part in command.split():
|
||||||
|
try:
|
||||||
|
if cmd is None:
|
||||||
|
cmd = self.bot.commands.get(part)
|
||||||
|
else:
|
||||||
|
cmd = cmd.commands.get(part)
|
||||||
|
except AttributeError:
|
||||||
|
cmd = None
|
||||||
|
break
|
||||||
|
|
||||||
|
return cmd
|
||||||
|
|
||||||
@commands.command(pass_context=True, no_pm=True)
|
@commands.command(pass_context=True, no_pm=True)
|
||||||
@checks.custom_perms(kick_members=True)
|
@checks.custom_perms(kick_members=True)
|
||||||
async def alerts(self, ctx, channel: discord.Channel):
|
async def alerts(self, ctx, channel: discord.Channel):
|
||||||
"""This command is used to set a channel as the server's 'notifications' channel
|
"""This command is used to set a channel as the server's 'notifications' channel
|
||||||
Any notifications (like someone going live on Twitch, or Picarto) will go to that channel"""
|
Any notifications (like someone going live on Twitch, or Picarto) will go to that channel"""
|
||||||
server_alerts = await config.get_content('server_alerts')
|
r_filter = {'server_id': ctx.message.server.id}
|
||||||
# This will update/add the channel if an entry for this server exists or not
|
entry = {'server_id': ctx.message.server.id,
|
||||||
server_alerts[ctx.message.server.id] = channel.id
|
'channel_id': channel.id}
|
||||||
await config.save_content('server_alerts', server_alerts)
|
if not await config.add_content('server_alerts', entry, r_filter):
|
||||||
|
await config.update_content('server_alerts', entry, r_filter)
|
||||||
await self.bot.say("I have just changed this server's 'notifications' channel"
|
await self.bot.say("I have just changed this server's 'notifications' channel"
|
||||||
"\nAll notifications will now go to `{}`".format(channel))
|
"\nAll notifications will now go to `{}`".format(channel))
|
||||||
|
|
||||||
|
@ -36,9 +62,11 @@ class Mod:
|
||||||
# So we base this channel on it's own and not from alerts
|
# So we base this channel on it's own and not from alerts
|
||||||
# When mod logging becomes available, that will be kept to it's own channel if wanted as well
|
# When mod logging becomes available, that will be kept to it's own channel if wanted as well
|
||||||
on_off = ctx.message.channel.id if re.search("(on|yes|true)", on_off.lower()) else None
|
on_off = ctx.message.channel.id if re.search("(on|yes|true)", on_off.lower()) else None
|
||||||
notifications = await config.get_content('user_notifications')
|
r_filter = {'server_id': ctx.message.server.id}
|
||||||
notifications[ctx.message.server.id] = on_off
|
entry = {'server_id': ctx.message.server.id,
|
||||||
await config.save_content('user_notifications', notifications)
|
'channel_id': on_off}
|
||||||
|
if not await config.add_content('user_notifications', entry, r_filter):
|
||||||
|
await config.update_content('user_notifications', entry, r_filter)
|
||||||
fmt = "notify" if on_off else "not notify"
|
fmt = "notify" if on_off else "not notify"
|
||||||
await self.bot.say("This server will now {} if someone has joined or left".format(fmt))
|
await self.bot.say("This server will now {} if someone has joined or left".format(fmt))
|
||||||
|
|
||||||
|
@ -53,29 +81,21 @@ class Mod:
|
||||||
@checks.custom_perms(kick_members=True)
|
@checks.custom_perms(kick_members=True)
|
||||||
async def nsfw_add(self, ctx):
|
async def nsfw_add(self, ctx):
|
||||||
"""Registers this channel as a 'nsfw' channel"""
|
"""Registers this channel as a 'nsfw' channel"""
|
||||||
nsfw_channels = await config.get_content('nsfw_channels')
|
r_filter = {'channel_id': ctx.message.channel.id}
|
||||||
# rethinkdb cannot save a list as a field, so we need a dict with one elemtn to store our list
|
if await config.add_content('nsfw_channels', r_filter, r_filter):
|
||||||
nsfw_channels = nsfw_channels.get('registered') or []
|
|
||||||
if ctx.message.channel.id in nsfw_channels:
|
|
||||||
await self.bot.say("This channel is already registered as 'nsfw'!")
|
|
||||||
else:
|
|
||||||
# Append instead of setting to a certain channel, so that multiple channels can be nsfw
|
|
||||||
nsfw_channels.append(ctx.message.channel.id)
|
|
||||||
await config.save_content('nsfw_channels', {'registered': nsfw_channels})
|
|
||||||
await self.bot.say("This channel has just been registered as 'nsfw'! Have fun you naughties ;)")
|
await self.bot.say("This channel has just been registered as 'nsfw'! Have fun you naughties ;)")
|
||||||
|
else:
|
||||||
|
await self.bot.say("This channel is already registered as 'nsfw'!")
|
||||||
|
|
||||||
@nsfw.command(name="remove", aliases=["delete"], pass_context=True, no_pm=True)
|
@nsfw.command(name="remove", aliases=["delete"], pass_context=True, no_pm=True)
|
||||||
@checks.custom_perms(kick_members=True)
|
@checks.custom_perms(kick_members=True)
|
||||||
async def nsfw_remove(self, ctx):
|
async def nsfw_remove(self, ctx):
|
||||||
"""Removes this channel as a 'nsfw' channel"""
|
"""Removes this channel as a 'nsfw' channel"""
|
||||||
nsfw_channels = await config.get_content('nsfw_channels')
|
r_filter = {'channel_id': ctx.message.channel.id}
|
||||||
nsfw_channels = nsfw_channels.get('registered') or []
|
if await config.remove_content('nsfw_channels', r_filter):
|
||||||
if ctx.message.channel.id not in nsfw_channels:
|
|
||||||
await self.bot.say("This channel is not registered as a ''nsfw' channel!")
|
|
||||||
else:
|
|
||||||
nsfw_channels.remove(ctx.message.channel.id)
|
|
||||||
await config.save_content('nsfw_channels', {'registered': nsfw_channels})
|
|
||||||
await self.bot.say("This channel has just been unregistered as a nsfw channel")
|
await self.bot.say("This channel has just been unregistered as a nsfw channel")
|
||||||
|
else:
|
||||||
|
await self.bot.say("This channel is not registered as a ''nsfw' channel!")
|
||||||
|
|
||||||
@commands.command(pass_context=True, no_pm=True)
|
@commands.command(pass_context=True, no_pm=True)
|
||||||
@checks.custom_perms(kick_members=True)
|
@checks.custom_perms(kick_members=True)
|
||||||
|
@ -98,21 +118,13 @@ class Mod:
|
||||||
"Valid permissions are: ```\n{}```".format("\n".join("{}".format(i) for i in valid_perms)))
|
"Valid permissions are: ```\n{}```".format("\n".join("{}".format(i) for i in valid_perms)))
|
||||||
return
|
return
|
||||||
|
|
||||||
custom_perms = await config.get_content('custom_permissions')
|
r_filter = {'server_id': ctx.message.server.id}
|
||||||
server_perms = custom_perms.get(ctx.message.server.id) or {}
|
server_perms = await config.get_content('custom_permissions', r_filter)
|
||||||
|
|
||||||
cmd = None
|
|
||||||
# This is the same loop as the add command, we need this to get the
|
|
||||||
# command object so we can get the qualified_name
|
|
||||||
for part in command.split():
|
|
||||||
try:
|
try:
|
||||||
if cmd is None:
|
server_perms = server_perms[0]
|
||||||
cmd = self.bot.commands.get(part)
|
except TypeError:
|
||||||
else:
|
server_perms = {}
|
||||||
cmd = cmd.commands.get(part)
|
cmd = self.find_command(command)
|
||||||
except AttributeError:
|
|
||||||
cmd = None
|
|
||||||
break
|
|
||||||
|
|
||||||
if cmd is None:
|
if cmd is None:
|
||||||
await self.bot.say("That is not a valid command!")
|
await self.bot.say("That is not a valid command!")
|
||||||
|
@ -126,8 +138,7 @@ class Mod:
|
||||||
custom_perms = [func for func in cmd.checks if "custom_perms" in func.__qualname__][0]
|
custom_perms = [func for func in cmd.checks if "custom_perms" in func.__qualname__][0]
|
||||||
except IndexError:
|
except IndexError:
|
||||||
# Loop through and check if there is a check called is_owner
|
# Loop through and check if there is a check called is_owner
|
||||||
# Ff we loop through and don't find one
|
# If we loop through and don't find one, this means that the only other choice is to be
|
||||||
# This means that the only other choice is to be
|
|
||||||
# Able to manage the server (for the checks on perm commands)
|
# Able to manage the server (for the checks on perm commands)
|
||||||
for func in cmd.checks:
|
for func in cmd.checks:
|
||||||
if "is_owner" in func.__qualname__:
|
if "is_owner" in func.__qualname__:
|
||||||
|
@ -137,7 +148,7 @@ class Mod:
|
||||||
"You are required to have `manage_server` permissions to run `{}`".format(cmd.qualified_name))
|
"You are required to have `manage_server` permissions to run `{}`".format(cmd.qualified_name))
|
||||||
return
|
return
|
||||||
|
|
||||||
# Perms will be an attribute if custom_perms is found no matter what, so need to check this
|
# Perms will be an attribute if custom_perms is found no matter what, so no need to check this
|
||||||
perms = "\n".join(attribute for attribute, setting in custom_perms.perms.items() if setting)
|
perms = "\n".join(attribute for attribute, setting in custom_perms.perms.items() if setting)
|
||||||
await self.bot.say(
|
await self.bot.say(
|
||||||
"You are required to have `{}` permissions to run `{}`".format(perms, cmd.qualified_name))
|
"You are required to have `{}` permissions to run `{}`".format(perms, cmd.qualified_name))
|
||||||
|
@ -165,34 +176,18 @@ class Mod:
|
||||||
if permissions.lower() == "none":
|
if permissions.lower() == "none":
|
||||||
permissions = "send_messages"
|
permissions = "send_messages"
|
||||||
|
|
||||||
# Check if the permission that was requested is valid
|
# Convert the string to an int value of the permissions object, based on the required permission
|
||||||
if getattr(discord.Permissions, permissions, None) is None:
|
# If we hit an attribute error, that means the permission given was not correct
|
||||||
|
perm_obj = discord.Permissions.none()
|
||||||
|
try:
|
||||||
|
setattr(perm_obj, permissions, True)
|
||||||
|
except AttributeError:
|
||||||
await self.bot.say("{} does not appear to be a valid permission! Valid permissions are: ```\n{}```"
|
await self.bot.say("{} does not appear to be a valid permission! Valid permissions are: ```\n{}```"
|
||||||
.format(permissions, "\n".join(valid_perms)))
|
.format(permissions, "\n".join(valid_perms)))
|
||||||
return
|
return
|
||||||
# Convert the string to an int value of the permissions object, based on the required permission
|
|
||||||
perm_obj = discord.Permissions.none()
|
|
||||||
setattr(perm_obj, permissions, True)
|
|
||||||
perm_value = perm_obj.value
|
perm_value = perm_obj.value
|
||||||
|
|
||||||
# This next loop ensures the command given is valid. We need to loop through commands
|
cmd = self.find_command(command)
|
||||||
# As self.bot.commands only includes parent commands
|
|
||||||
# So we are splitting the command in parts, looping through the commands
|
|
||||||
# And getting the subcommand based on the next part
|
|
||||||
# If we try to access commands of a command that isn't a group
|
|
||||||
# We'll hit an AttributeError, meaning an invalid command was given
|
|
||||||
# If we loop through and don't find anything, cmd will still be None
|
|
||||||
# And we'll report an invalid was given as well
|
|
||||||
cmd = None
|
|
||||||
for part in msg[0:len(msg) - 1]:
|
|
||||||
try:
|
|
||||||
if cmd is None:
|
|
||||||
cmd = self.bot.commands.get(part)
|
|
||||||
else:
|
|
||||||
cmd = cmd.commands.get(part)
|
|
||||||
except AttributeError:
|
|
||||||
cmd = None
|
|
||||||
break
|
|
||||||
|
|
||||||
if cmd is None:
|
if cmd is None:
|
||||||
await self.bot.say(
|
await self.bot.say(
|
||||||
|
@ -208,62 +203,62 @@ class Mod:
|
||||||
await self.bot.say("This command cannot have custom permissions setup!")
|
await self.bot.say("This command cannot have custom permissions setup!")
|
||||||
return
|
return
|
||||||
|
|
||||||
custom_perms = await config.get_content('custom_permissions')
|
r_filter = {'server_id': ctx.message.server.id}
|
||||||
server_perms = custom_perms.get(ctx.message.server.id) or {}
|
entry = {'server_id': ctx.message.server.id,
|
||||||
# Save the qualified name, so that we don't get screwed up by aliases
|
cmd.qualified_name: perm_value}
|
||||||
server_perms[cmd.qualified_name] = perm_value
|
|
||||||
custom_perms[ctx.message.server.id] = server_perms
|
|
||||||
|
|
||||||
await config.save_content('custom_permissions', custom_perms)
|
# In all other cases, I've used add_content before update_content
|
||||||
|
# In this case, I'm going the other way around, to make the least queries
|
||||||
|
# As custom permissions are probably going to be ran multiple times per server
|
||||||
|
# Whereas in most other cases, the command is probably going to be ran once/few times per server
|
||||||
|
if not await config.update_content('custom_permissions', entry, r_filter):
|
||||||
|
await config.add_content('custom_permissions', entry, r_filter)
|
||||||
|
|
||||||
|
# Same case as prefixes, for now, trigger a manual update
|
||||||
|
self.bot.loop.create_task(config.cache['custom_permissions'].update())
|
||||||
await self.bot.say("I have just added your custom permissions; "
|
await self.bot.say("I have just added your custom permissions; "
|
||||||
"you now need to have `{}` permissions to use the command `{}`".format(permissions, command))
|
"you now need to have `{}` permissions to use the command `{}`".format(permissions, command))
|
||||||
|
|
||||||
@perms.command(name="remove", aliases=["delete"], pass_context=True, no_pm=True)
|
@perms.command(name="remove", aliases=["delete"], pass_context=True, no_pm=True)
|
||||||
@commands.has_permissions(manage_server=True)
|
@commands.has_permissions(manage_server=True)
|
||||||
async def remove_perms(self, ctx, *command: str):
|
async def remove_perms(self, ctx, *, command: str):
|
||||||
"""Removes the custom permissions setup on the command specified"""
|
"""Removes the custom permissions setup on the command specified"""
|
||||||
custom_perms = await config.get_content('custom_permissions')
|
|
||||||
server_perms = custom_perms.get(ctx.message.server.id) or {}
|
|
||||||
if server_perms is None:
|
|
||||||
await self.bot.say("There are no custom permissions setup on this server yet!")
|
|
||||||
return
|
|
||||||
|
|
||||||
cmd = None
|
cmd = self.find_command(command)
|
||||||
# This is the same loop as the add command, we need this to get the
|
|
||||||
# command object so we can get the qualified_name
|
|
||||||
for part in command:
|
|
||||||
try:
|
|
||||||
if cmd is None:
|
|
||||||
cmd = self.bot.commands.get(part)
|
|
||||||
else:
|
|
||||||
cmd = cmd.commands.get(part)
|
|
||||||
except AttributeError:
|
|
||||||
cmd = None
|
|
||||||
break
|
|
||||||
|
|
||||||
if cmd is None:
|
if cmd is None:
|
||||||
await self.bot.say(
|
await self.bot.say(
|
||||||
"That command does not exist! You can't have custom permissions on a non-existant command....")
|
"That command does not exist! You can't have custom permissions on a non-existant command....")
|
||||||
return
|
return
|
||||||
|
|
||||||
command_perms = server_perms.get(cmd.qualified_name)
|
r_filter = {'server_id': ctx.message.server.id}
|
||||||
if command_perms is None:
|
await config.replace_content('custom_permissions', r.row.without(cmd.qualified_name), r_filter)
|
||||||
await self.bot.say("You do not have custom permissions setup on this command!")
|
|
||||||
return
|
|
||||||
|
|
||||||
del custom_perms[ctx.message.server.id][cmd.qualified_name]
|
|
||||||
await config.save_content('custom_permissions', custom_perms)
|
|
||||||
await self.bot.say("I have just removed the custom permissions for {}!".format(cmd))
|
await self.bot.say("I have just removed the custom permissions for {}!".format(cmd))
|
||||||
|
|
||||||
|
# Same case as prefixes, for now, trigger a manual update
|
||||||
|
self.bot.loop.create_task(config.cache['custom_permissions'].update())
|
||||||
|
|
||||||
@commands.command(pass_context=True, no_pm=True)
|
@commands.command(pass_context=True, no_pm=True)
|
||||||
@checks.custom_perms(manage_server=True)
|
@checks.custom_perms(manage_server=True)
|
||||||
async def prefix(self, ctx, *, prefix: str):
|
async def prefix(self, ctx, *, prefix: str):
|
||||||
"""This command can be used to set a custom prefix per server"""
|
"""This command can be used to set a custom prefix per server"""
|
||||||
prefixes = await config.get_content('prefixes')
|
r_filter = {'server_id': ctx.message.server.id}
|
||||||
prefixes[ctx.message.server.id] = prefix
|
if prefix.lower == "none":
|
||||||
await config.save_content('prefixes', prefixes)
|
prefix = None
|
||||||
|
|
||||||
|
entry = {'server_id': ctx.message.server.id,
|
||||||
|
'prefix': prefix}
|
||||||
|
|
||||||
|
if not await config.add_content('prefixes', entry, r_filter):
|
||||||
|
await config.update_content('prefixes', entry, r_filter)
|
||||||
|
# For now, cache is not fully implemented, however is needed for prefixes
|
||||||
|
# So we're going to manually trigger an update when this is ran
|
||||||
|
self.bot.loop.create_task(config.cache['prefixes'].update())
|
||||||
|
|
||||||
await self.bot.say(
|
await self.bot.say(
|
||||||
"I have just updated the prefix for this server; you now need to call commands with `{}`".format(prefix))
|
"I have just updated the prefix for this server; you now need to call commands with `{0}`."
|
||||||
|
"For example, you can call this command again with {0}prefix".format(
|
||||||
|
prefix))
|
||||||
|
|
||||||
@commands.command(pass_context=True, no_pm=True)
|
@commands.command(pass_context=True, no_pm=True)
|
||||||
@checks.custom_perms(manage_messages=True)
|
@checks.custom_perms(manage_messages=True)
|
||||||
|
@ -315,71 +310,55 @@ class Mod:
|
||||||
|
|
||||||
@commands.group(aliases=['rule'], pass_context=True, no_pm=True, invoke_without_command=True)
|
@commands.group(aliases=['rule'], pass_context=True, no_pm=True, invoke_without_command=True)
|
||||||
@checks.custom_perms(send_messages=True)
|
@checks.custom_perms(send_messages=True)
|
||||||
async def rules(self, ctx):
|
async def rules(self, ctx, rule: int = None):
|
||||||
"""This command can be used to view the current rules on the server"""
|
"""This command can be used to view the current rules on the server"""
|
||||||
rules = await config.get_content('rules')
|
r_filter = {'server_id': ctx.message.server.id}
|
||||||
server_rules = rules.get(ctx.message.server.id)
|
rules = await config.get_content('rules', r_filter)
|
||||||
if server_rules is None or len(server_rules) == 0:
|
try:
|
||||||
|
rules = rules[0]['rules']
|
||||||
|
except TypeError:
|
||||||
await self.bot.say("This server currently has no rules on it! I see you like to live dangerously...")
|
await self.bot.say("This server currently has no rules on it! I see you like to live dangerously...")
|
||||||
return
|
return
|
||||||
|
if len(rules) == 0:
|
||||||
|
await self.bot.say("This server currently has no rules on it! I see you like to live dangerously...")
|
||||||
|
return
|
||||||
|
|
||||||
|
if rule is None:
|
||||||
# Enumerate the list, so that we can print the number and the rule for each rule
|
# Enumerate the list, so that we can print the number and the rule for each rule
|
||||||
fmt = "\n".join("{}) {}".format(num + 1, rule) for num, rule in enumerate(server_rules))
|
fmt = "\n".join("{}) {}".format(num + 1, rule) for num, rule in enumerate(rules))
|
||||||
await self.bot.say('```\n{}```'.format(fmt))
|
await self.bot.say('```\n{}```'.format(fmt))
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
fmt = rules[rule - 1]
|
||||||
|
except IndexError:
|
||||||
|
await self.bot.say("That rules does not exist.")
|
||||||
|
return
|
||||||
|
await self.bot.say("Rule {}: \"{}\"".format(rule, fmt))
|
||||||
|
|
||||||
@rules.command(name='add', aliases=['create'], pass_context=True, no_pm=True)
|
@rules.command(name='add', aliases=['create'], pass_context=True, no_pm=True)
|
||||||
@checks.custom_perms(manage_server=True)
|
@checks.custom_perms(manage_server=True)
|
||||||
async def rules_add(self, ctx, *, rule: str):
|
async def rules_add(self, ctx, *, rule: str):
|
||||||
"""Adds a rule to this server's rules"""
|
"""Adds a rule to this server's rules"""
|
||||||
# Nothing fancy here, just get the rules, append the rule, and save it
|
r_filter = {'server_id': ctx.message.server.id}
|
||||||
rules = await config.get_content('rules')
|
entry = {'server_id': ctx.message.server.id,
|
||||||
server_rules = rules.get(ctx.message.server.id) or []
|
'rules': [rule]}
|
||||||
server_rules.append(rule)
|
update = lambda row: row['rules'].append(rule)
|
||||||
rules[ctx.message.server.id] = server_rules
|
if not await config.update_content('rules', update, r_filter):
|
||||||
await config.save_content('rules', rules)
|
await config.add_content('rules', entry, r_filter)
|
||||||
|
|
||||||
await self.bot.say("I have just saved your new rule, use the rules command to view this server's current rules")
|
await self.bot.say("I have just saved your new rule, use the rules command to view this server's current rules")
|
||||||
|
|
||||||
@rules.command(name='remove', aliases=['delete'], pass_context=True, no_pm=True)
|
@rules.command(name='remove', aliases=['delete'], pass_context=True, no_pm=True)
|
||||||
@checks.custom_perms(manage_server=True)
|
@checks.custom_perms(manage_server=True)
|
||||||
async def rules_delete(self, ctx, rule: int = None):
|
async def rules_delete(self, ctx, rule: int):
|
||||||
"""Removes one of the rules from the list of this server's rules
|
"""Removes one of the rules from the list of this server's rules
|
||||||
Provide a number to delete that rule; if no number is provided
|
Provide a number to delete that rule"""
|
||||||
I'll print your current rules and ask for a number"""
|
r_filter = {'server_id': ctx.message.server.id}
|
||||||
rules = await config.get_content('rules')
|
update = {'rules': r.row['rules'].delete_at(rule - 1)}
|
||||||
server_rules = rules.get(ctx.message.server.id) or []
|
if not await config.update_content('rules', update, r_filter):
|
||||||
if server_rules is None or len(server_rules) == 0:
|
await self.bot.say("That is not a valid rule number, try running the command again.")
|
||||||
await self.bot.say(
|
else:
|
||||||
"This server currently has no rules on it! Can't remove something that doesn't exist bro")
|
|
||||||
return
|
|
||||||
|
|
||||||
# Get the list of rules so that we can print it if no number was provided
|
|
||||||
# Since this is a list and not a dictionary, order is preserved, and we just need the number of the rule
|
|
||||||
list_rules = "\n".join("{}) {}".format(num + 1, rule) for num, rule in enumerate(server_rules))
|
|
||||||
|
|
||||||
if rule is None:
|
|
||||||
await self.bot.say("Your rules are:\n```\n{}```Please provide the rule number"
|
|
||||||
"you would like to remove (just the number)".format(list_rules))
|
|
||||||
|
|
||||||
# All we need for the check is to ensure that the content is just a digit, that is all we need
|
|
||||||
msg = await self.bot.wait_for_message(timeout=60.0, author=ctx.message.author, channel=ctx.message.channel,
|
|
||||||
check=lambda m: m.content.isdigit())
|
|
||||||
if msg is None:
|
|
||||||
await self.bot.say("You took too long...it's just a number, seriously? Try typing a bit quicker")
|
|
||||||
return
|
|
||||||
del server_rules[int(msg.content) - 1]
|
|
||||||
rules[ctx.message.server.id] = server_rules
|
|
||||||
await config.save_content('rules', rules)
|
|
||||||
await self.bot.say("I have just removed that rule from your list of rules!")
|
await self.bot.say("I have just removed that rule from your list of rules!")
|
||||||
return
|
|
||||||
|
|
||||||
# This check is just to ensure a number was provided within the list's range
|
|
||||||
try:
|
|
||||||
del server_rules[rule - 1]
|
|
||||||
rules[ctx.message.server.id] = server_rules
|
|
||||||
await config.save_content('rules', rules)
|
|
||||||
await self.bot.say("I have just removed that rule from your list of rules!")
|
|
||||||
except IndexError:
|
|
||||||
await self.bot.say("That is not a valid rule number, try running the command again. "
|
|
||||||
"Your current rules are:\n```\n{}```".format(list_rules))
|
|
||||||
|
|
||||||
|
|
||||||
def setup(bot):
|
def setup(bot):
|
||||||
|
|
|
@ -38,18 +38,18 @@ class Overwatch:
|
||||||
async def ow_stats(self, ctx, user: discord.Member = None, hero: str = ""):
|
async def ow_stats(self, ctx, user: discord.Member = None, hero: str = ""):
|
||||||
"""Prints out a basic overview of a member's stats
|
"""Prints out a basic overview of a member's stats
|
||||||
Provide a hero after the member to get stats for that specific hero"""
|
Provide a hero after the member to get stats for that specific hero"""
|
||||||
if user is None:
|
user = user or ctx.message.author
|
||||||
user = ctx.message.author
|
r_filter = {'member_id': user.id}
|
||||||
|
ow_stats = await config.get_content('overwatch', r_filter)
|
||||||
|
|
||||||
ow_stats = await config.get_content('overwatch')
|
if ow_stats is None:
|
||||||
bt = ow_stats.get(user.id)
|
|
||||||
|
|
||||||
if bt is None:
|
|
||||||
await self.bot.say("I do not have this user's battletag saved!")
|
await self.bot.say("I do not have this user's battletag saved!")
|
||||||
return
|
return
|
||||||
# This API sometimes takes a while to look up information, so send a message saying we're processing
|
# This API sometimes takes a while to look up information, so send a message saying we're processing
|
||||||
await self.bot.say("Searching profile information....")
|
await self.bot.say("Searching profile information....")
|
||||||
|
|
||||||
|
bt = ow_stats[0]['battletag']
|
||||||
|
|
||||||
if hero == "":
|
if hero == "":
|
||||||
# If no hero was provided, we just want the base stats for a player
|
# If no hero was provided, we just want the base stats for a player
|
||||||
async with self.session.get(base_url + "{}/stats/general".format(bt), headers=self.headers) as r:
|
async with self.session.get(base_url + "{}/stats/general".format(bt), headers=self.headers) as r:
|
||||||
|
@ -101,6 +101,7 @@ class Overwatch:
|
||||||
# Battletags are normally provided like name#id
|
# Battletags are normally provided like name#id
|
||||||
# However the API needs this to be a -, so repliace # with - if it exists
|
# However the API needs this to be a -, so repliace # with - if it exists
|
||||||
bt = bt.replace("#", "-")
|
bt = bt.replace("#", "-")
|
||||||
|
r_filter = {'member_id': ctx.message.author.id}
|
||||||
|
|
||||||
# This API sometimes takes a while to look up information, so send a message saying we're processing
|
# This API sometimes takes a while to look up information, so send a message saying we're processing
|
||||||
await self.bot.say("Looking up your profile information....")
|
await self.bot.say("Looking up your profile information....")
|
||||||
|
@ -115,25 +116,23 @@ class Overwatch:
|
||||||
return
|
return
|
||||||
|
|
||||||
# Now just save the battletag
|
# Now just save the battletag
|
||||||
ow = await config.get_content('overwatch')
|
entry = {'member_id': ctx.message.author.id, 'battletag': bt}
|
||||||
ow[ctx.message.author.id] = bt
|
update = {'battletag': bt}
|
||||||
await config.save_content('overwatch', ow)
|
# Try adding this first, if that fails, update the saved entry
|
||||||
|
if not await config.add_content('overwatch', entry, r_filter):
|
||||||
|
await config.update_content('overwatch', update, r_filter)
|
||||||
await self.bot.say("I have just saved your battletag {}".format(ctx.message.author.mention))
|
await self.bot.say("I have just saved your battletag {}".format(ctx.message.author.mention))
|
||||||
|
|
||||||
@ow.command(pass_context=True, name="delete", aliases=['remove'], no_pm=True)
|
@ow.command(pass_context=True, name="delete", aliases=['remove'], no_pm=True)
|
||||||
@checks.custom_perms(send_messages=True)
|
@checks.custom_perms(send_messages=True)
|
||||||
async def delete(self, ctx):
|
async def delete(self, ctx):
|
||||||
"""Removes your battletag from the records"""
|
"""Removes your battletag from the records"""
|
||||||
result = await config.get_content('overwatch')
|
r_filter = {'member_id': ctx.message.author.id}
|
||||||
if result.get(ctx.message.author.id):
|
if await config.remove_content('overwatch', r_filter):
|
||||||
del result[ctx.message.author.id]
|
|
||||||
await self.bot.say("I no longer have your battletag saved {}".format(ctx.message.author.mention))
|
await self.bot.say("I no longer have your battletag saved {}".format(ctx.message.author.mention))
|
||||||
else:
|
else:
|
||||||
await self.bot.say("I don't even have your battletag saved {}".format(ctx.message.author.mention))
|
await self.bot.say("I don't even have your battletag saved {}".format(ctx.message.author.mention))
|
||||||
|
|
||||||
del result[ctx.message.author.id]
|
|
||||||
await self.bot.say("I have just removed your battletag!")
|
|
||||||
|
|
||||||
|
|
||||||
def setup(bot):
|
def setup(bot):
|
||||||
bot.add_cog(Overwatch(bot))
|
bot.add_cog(Overwatch(bot))
|
||||||
|
|
|
@ -1,10 +1,7 @@
|
||||||
from discord.ext import commands
|
from discord.ext import commands
|
||||||
from .utils import config
|
|
||||||
from .utils import checks
|
from .utils import checks
|
||||||
import re
|
import re
|
||||||
import os
|
|
||||||
import glob
|
import glob
|
||||||
import sys
|
|
||||||
import discord
|
import discord
|
||||||
import inspect
|
import inspect
|
||||||
import aiohttp
|
import aiohttp
|
||||||
|
@ -45,35 +42,6 @@ class Owner:
|
||||||
fmt = "Nothing currently running!"
|
fmt = "Nothing currently running!"
|
||||||
await self.bot.say(fmt)
|
await self.bot.say(fmt)
|
||||||
|
|
||||||
@commands.command(pass_context=True)
|
|
||||||
@commands.check(checks.is_owner)
|
|
||||||
async def saferestart(self, ctx):
|
|
||||||
"""This commands is used to check if there is anything playing in any servers at the moment
|
|
||||||
If there is, I'll tell you not to restart, if not I'll just go ahead and restart"""
|
|
||||||
# I do not want to restart the bot if someone is playing music
|
|
||||||
# This gets all the exiting VoiceStates that are playing music right now
|
|
||||||
# If we are, say which server it
|
|
||||||
servers_playing_music = [server_id for server_id, state in self.bot.get_cog('Music').voice_states.items() if
|
|
||||||
state.is_playing()]
|
|
||||||
if len(servers_playing_music) > 0:
|
|
||||||
await self.bot.say("Sorry, it's not safe to restart. I am currently playing a song on {} servers".format(
|
|
||||||
len(servers_playing_music)))
|
|
||||||
else:
|
|
||||||
await config.save_content('restart_server', ctx.message.channel.id)
|
|
||||||
await self.bot.say("Restarting; see you in the next life {0}!".format(ctx.message.author.mention))
|
|
||||||
python = sys.executable
|
|
||||||
os.execl(python, python, *sys.argv)
|
|
||||||
|
|
||||||
@commands.command(pass_context=True)
|
|
||||||
@commands.check(checks.is_owner)
|
|
||||||
async def restart(self, ctx):
|
|
||||||
"""Forces the bot to restart"""
|
|
||||||
# This command is left in so that we can invoke it from saferestart, or we need a restart no matter what
|
|
||||||
await config.save_content('restart_server', ctx.message.channel.id)
|
|
||||||
await self.bot.say("Restarting; see you in the next life {0}!".format(ctx.message.author.mention))
|
|
||||||
python = sys.executable
|
|
||||||
os.execl(python, python, *sys.argv)
|
|
||||||
|
|
||||||
@commands.command()
|
@commands.command()
|
||||||
@commands.check(checks.is_owner)
|
@commands.check(checks.is_owner)
|
||||||
async def adddoggo(self, url: str):
|
async def adddoggo(self, url: str):
|
||||||
|
|
152
cogs/picarto.py
152
cogs/picarto.py
|
@ -2,6 +2,7 @@ import aiohttp
|
||||||
import asyncio
|
import asyncio
|
||||||
import discord
|
import discord
|
||||||
import re
|
import re
|
||||||
|
import rethinkdb as r
|
||||||
|
|
||||||
from discord.ext import commands
|
from discord.ext import commands
|
||||||
from .utils import config
|
from .utils import config
|
||||||
|
@ -21,8 +22,8 @@ async def online_users():
|
||||||
# This method is in place to just return all online_users
|
# This method is in place to just return all online_users
|
||||||
url = '{}/online/all?key={}'.format(base_url, key)
|
url = '{}/online/all?key={}'.format(base_url, key)
|
||||||
with aiohttp.ClientSession(headers={"User-Agent": "Bonfire/1.0.0"}) as s:
|
with aiohttp.ClientSession(headers={"User-Agent": "Bonfire/1.0.0"}) as s:
|
||||||
async with s.get(url) as r:
|
async with s.get(url) as response:
|
||||||
return await r.json()
|
return await response.json()
|
||||||
except:
|
except:
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
@ -46,56 +47,59 @@ class Picarto:
|
||||||
await self.bot.wait_until_ready()
|
await self.bot.wait_until_ready()
|
||||||
# This is a loop that runs every 30 seconds, checking if anyone has gone online
|
# This is a loop that runs every 30 seconds, checking if anyone has gone online
|
||||||
while not self.bot.is_closed:
|
while not self.bot.is_closed:
|
||||||
picarto = await config.get_content('picarto')
|
r_filter = {'notifications_on': 1}
|
||||||
|
picarto = await config.get_content('picarto', r_filter)
|
||||||
# Get all online users before looping, so that only one request is needed
|
# Get all online users before looping, so that only one request is needed
|
||||||
online_users_list = await online_users()
|
online_users_list = await online_users()
|
||||||
old_online_users = {m_id: data for m_id, data in picarto.items() if
|
old_online_users = {data['member_id']: data for data in picarto if data['live']}
|
||||||
data['notifications_on'] and data['live']}
|
old_offline_users = {data['member_id']: data for data in picarto if not data['live']}
|
||||||
old_offline_users = {m_id: data for m_id, data in picarto.items() if
|
|
||||||
data['notifications_on'] and not data['live']}
|
|
||||||
|
|
||||||
for m_id, r in old_offline_users.items():
|
for m_id, result in old_offline_users.items():
|
||||||
# Get their url and their user based on that url
|
# Get their url and their user based on that url
|
||||||
url = r['picarto_url']
|
url = result['picarto_url']
|
||||||
user = re.search("(?<=picarto.tv/)(.*)", url).group(1)
|
user = re.search("(?<=picarto.tv/)(.*)", url).group(1)
|
||||||
# Check if they are online right now
|
# Check if they are online right now
|
||||||
if check_online(online_users_list, user):
|
if check_online(online_users_list, user):
|
||||||
for server_id in r['servers']:
|
for server_id in result['servers']:
|
||||||
# Get the channel to send the message to, based on the saved alert's channel
|
# Get the channel to send the message to, based on the saved alert's channel
|
||||||
server = self.bot.get_server(server_id)
|
server = self.bot.get_server(server_id)
|
||||||
if server is None:
|
if server is None:
|
||||||
continue
|
continue
|
||||||
server_alerts = await config.get_content('server_alerts')
|
server_alerts = await config.get_content('server_alerts', {'server_id': server_id})
|
||||||
channel_id = server_alerts.get(server_id) or server_id
|
try:
|
||||||
|
channel_id = server_alerts[0]
|
||||||
|
except IndexError:
|
||||||
|
channel_id = server_id
|
||||||
channel = self.bot.get_channel(channel_id)
|
channel = self.bot.get_channel(channel_id)
|
||||||
# Get the member that has just gone live
|
# Get the member that has just gone live
|
||||||
member = discord.utils.get(server.members, id=m_id)
|
member = discord.utils.get(server.members, id=m_id)
|
||||||
|
|
||||||
fmt = "{} has just gone live! View their stream at {}".format(member.display_name, url)
|
fmt = "{} has just gone live! View their stream at {}".format(member.display_name, url)
|
||||||
await self.bot.send_message(channel, fmt)
|
await self.bot.send_message(channel, fmt)
|
||||||
picarto[m_id]['live'] = 1
|
await config.update_content('picarto', {'live': 1}, {'member_id': m_id})
|
||||||
await config.save_content('picarto', picarto)
|
for m_id, result in old_online_users.items():
|
||||||
for m_id, r in old_online_users.items():
|
|
||||||
# Get their url and their user based on that url
|
# Get their url and their user based on that url
|
||||||
url = r['picarto_url']
|
url = result['picarto_url']
|
||||||
user = re.search("(?<=picarto.tv/)(.*)", url).group(1)
|
user = re.search("(?<=picarto.tv/)(.*)", url).group(1)
|
||||||
# Check if they are online right now
|
# Check if they are online right now
|
||||||
if not check_online(online_users_list, user):
|
if not check_online(online_users_list, user):
|
||||||
for server_id in r['servers']:
|
for server_id in result['servers']:
|
||||||
# Get the channel to send the message to, based on the saved alert's channel
|
# Get the channel to send the message to, based on the saved alert's channel
|
||||||
server = self.bot.get_server(server_id)
|
server = self.bot.get_server(server_id)
|
||||||
if server is None:
|
if server is None:
|
||||||
continue
|
continue
|
||||||
server_alerts = await config.get_content('server_alerts')
|
server_alerts = await config.get_content('server_alerts', {'server_id': server_id})
|
||||||
channel_id = server_alerts.get(server_id) or server_id
|
try:
|
||||||
|
channel_id = server_alerts[0]
|
||||||
|
except IndexError:
|
||||||
|
channel_id = server_id
|
||||||
channel = self.bot.get_channel(channel_id)
|
channel = self.bot.get_channel(channel_id)
|
||||||
# Get the member that has just gone live
|
# Get the member that has just gone live
|
||||||
member = discord.utils.get(server.members, id=m_id)
|
member = discord.utils.get(server.members, id=m_id)
|
||||||
fmt = "{} has just gone offline! Catch them next time they stream at {}".format(
|
fmt = "{} has just gone offline! Catch them next time they stream at {}".format(
|
||||||
member.display_name, url)
|
member.display_name, url)
|
||||||
await self.bot.send_message(channel, fmt)
|
await self.bot.send_message(channel, fmt)
|
||||||
picarto[m_id]['live'] = 0
|
await config.update_content('picarto', {'live': 0}, {'member_id': m_id})
|
||||||
await config.save_content('picarto', picarto)
|
|
||||||
await asyncio.sleep(30)
|
await asyncio.sleep(30)
|
||||||
|
|
||||||
@commands.group(pass_context=True, invoke_without_command=True)
|
@commands.group(pass_context=True, invoke_without_command=True)
|
||||||
|
@ -104,31 +108,32 @@ class Picarto:
|
||||||
"""This command can be used to view Picarto stats about a certain member"""
|
"""This command can be used to view Picarto stats about a certain member"""
|
||||||
# If member is not given, base information on the author
|
# If member is not given, base information on the author
|
||||||
member = member or ctx.message.author
|
member = member or ctx.message.author
|
||||||
picarto_urls = await config.get_content('picarto')
|
r_filter = {'member_id': member.id}
|
||||||
try:
|
picarto_entry = await config.get_content('picarto', r_filter)
|
||||||
member_url = picarto_urls.get(member.id)['picarto_url']
|
if picarto_entry is None:
|
||||||
except:
|
|
||||||
await self.bot.say("That user does not have a picarto url setup!")
|
await self.bot.say("That user does not have a picarto url setup!")
|
||||||
return
|
return
|
||||||
|
member_url = picarto_entry[0]['picarto_url']
|
||||||
|
|
||||||
# Use regex to get the actual username so that we can make a request to the API
|
# Use regex to get the actual username so that we can make a request to the API
|
||||||
stream = re.search("(?<=picarto.tv/)(.*)", member_url).group(1)
|
stream = re.search("(?<=picarto.tv/)(.*)", member_url).group(1)
|
||||||
url = '{}/channel/{}?key={}'.format(base_url, stream, key)
|
url = '{}/channel/{}?key={}'.format(base_url, stream, key)
|
||||||
async with self.session.get(url, headers=self.headers) as r:
|
async with self.session.get(url, headers=self.headers) as response:
|
||||||
data = await r.json()
|
data = await response.json()
|
||||||
|
|
||||||
# Not everyone has all these settings, so use this as a way to print information if it does, otherwise ignore it
|
# Not everyone has all these settings, so use this as a way to print information if it does, otherwise ignore it
|
||||||
things_to_print = ['channel', 'commissions_enabled', 'is_nsfw', 'program', 'tablet', 'followers',
|
things_to_print = ['channel', 'commissions_enabled', 'is_nsfw', 'program', 'tablet', 'followers',
|
||||||
'content_type']
|
'content_type']
|
||||||
# Using title and replace to provide a nice way to print the data
|
# Using title and replace to provide a nice way to print the data
|
||||||
fmt = "\n".join(
|
fmt = "\n".join(
|
||||||
"{}: {}".format(i.title().replace("_", " "), r) for i, r in data.items() if i in things_to_print)
|
"{}: {}".format(i.title().replace("_", " "), result) for i, result in data.items() if i in things_to_print)
|
||||||
|
|
||||||
# Social URL's can be given if a user wants them to show
|
# Social URL's can be given if a user wants them to show
|
||||||
# Print them if they exist, otherwise don't try to include them
|
# Print them if they exist, otherwise don't try to include them
|
||||||
social_links = data.get('social_urls')
|
social_links = data.get('social_urls')
|
||||||
if social_links:
|
if social_links:
|
||||||
fmt2 = "\n".join("\t{}: {}".format(i.title().replace("_", " "), r) for i, r in social_links.items())
|
fmt2 = "\n".join(
|
||||||
|
"\t{}: {}".format(i.title().replace("_", " "), result) for i, result in social_links.items())
|
||||||
fmt = "{}\nSocial Links:\n{}".format(fmt, fmt2)
|
fmt = "{}\nSocial Links:\n{}".format(fmt, fmt2)
|
||||||
await self.bot.say("Picarto stats for {}: ```\n{}```".format(member.display_name, fmt))
|
await self.bot.say("Picarto stats for {}: ```\n{}```".format(member.display_name, fmt))
|
||||||
|
|
||||||
|
@ -154,37 +159,32 @@ class Picarto:
|
||||||
api_url = '{}/channel/{}?key={}'.format(base_url, re.search("https://www.picarto.tv/(.*)", url).group(1), key)
|
api_url = '{}/channel/{}?key={}'.format(base_url, re.search("https://www.picarto.tv/(.*)", url).group(1), key)
|
||||||
|
|
||||||
# Check if we can find a user with the provided information, if we can't just return
|
# Check if we can find a user with the provided information, if we can't just return
|
||||||
async with self.session.get(api_url, headers=self.headers) as r:
|
async with self.session.get(api_url, headers=self.headers) as response:
|
||||||
if not r.status == 200:
|
if not response.status == 200:
|
||||||
await self.bot.say("That Picarto user does not exist! "
|
await self.bot.say("That Picarto user does not exist! "
|
||||||
"What would be the point of adding a nonexistant Picarto user? Silly")
|
"What would be the point of adding a nonexistant Picarto user? Silly")
|
||||||
return
|
return
|
||||||
|
|
||||||
picarto_urls = await config.get_content('picarto')
|
r_filter = {'member_id': ctx.message.author.id}
|
||||||
result = picarto_urls.get(ctx.message.author.id)
|
entry = {'picarto_url': url,
|
||||||
|
|
||||||
# If information for this user already exists, override just the url, and not the information
|
|
||||||
# Otherwise create the information with notications on, and that they're not live.
|
|
||||||
# The next time it's checked, they'll go 'online'
|
|
||||||
if result is not None:
|
|
||||||
picarto_urls[ctx.message.author.id]['picarto_url'] = url
|
|
||||||
else:
|
|
||||||
picarto_urls[ctx.message.author.id] = {'picarto_url': url,
|
|
||||||
'servers': [ctx.message.server.id],
|
'servers': [ctx.message.server.id],
|
||||||
'notifications_on': 1, 'live': 0}
|
'notifications_on': 1,
|
||||||
await config.save_content('picarto', picarto_urls)
|
'live': 0,
|
||||||
|
'member_id': ctx.message.author.id}
|
||||||
|
if await config.add_content('picarto', entry, r_filter):
|
||||||
await self.bot.say(
|
await self.bot.say(
|
||||||
"I have just saved your Picarto url {}, this server will now be notified when you go live".format(
|
"I have just saved your Picarto URL {}, this server will now be notified when you go live".format(
|
||||||
ctx.message.author.mention))
|
ctx.message.author.mention))
|
||||||
|
else:
|
||||||
|
await config.update_content('picarto', {'picarto_url': url}, r_filter)
|
||||||
|
await self.bot.say("I have just updated your Picarto URL")
|
||||||
|
|
||||||
@picarto.command(name='remove', aliases=['delete'], pass_context=True, no_pm=True)
|
@picarto.command(name='remove', aliases=['delete'], pass_context=True, no_pm=True)
|
||||||
@checks.custom_perms(send_messages=True)
|
@checks.custom_perms(send_messages=True)
|
||||||
async def remove_picarto_url(self, ctx):
|
async def remove_picarto_url(self, ctx):
|
||||||
"""Removes your picarto URL"""
|
"""Removes your picarto URL"""
|
||||||
picarto = await config.get_content('picarto')
|
r_filter = {'member_id': ctx.message.author.id}
|
||||||
if picarto.get(ctx.message.author.id) is not None:
|
if await config.remove_content('picarto', r_filter):
|
||||||
del picarto[ctx.message.author.id]
|
|
||||||
await config.save_content('picarto', picarto)
|
|
||||||
await self.bot.say("I am no longer saving your picarto URL {}".format(ctx.message.author.mention))
|
await self.bot.say("I am no longer saving your picarto URL {}".format(ctx.message.author.mention))
|
||||||
else:
|
else:
|
||||||
await self.bot.say(
|
await self.bot.say(
|
||||||
|
@ -196,41 +196,26 @@ class Picarto:
|
||||||
async def notify(self, ctx):
|
async def notify(self, ctx):
|
||||||
"""This can be used to turn picarto notifications on or off
|
"""This can be used to turn picarto notifications on or off
|
||||||
Call this command by itself, to add this server to the list of servers to be notified"""
|
Call this command by itself, to add this server to the list of servers to be notified"""
|
||||||
member = ctx.message.author
|
r_filter = {'member_id': ctx.message.author.id}
|
||||||
|
result = await config.get_content('picarto', r_filter)
|
||||||
# If this user's picarto URL is not saved, no use in adding this server to the list that doesn't exist
|
# Check if this user is saved at all
|
||||||
picarto = await config.get_content('picarto')
|
|
||||||
result = picarto.get(member.id)
|
|
||||||
if result is None:
|
if result is None:
|
||||||
await self.bot.say(
|
await self.bot.say(
|
||||||
"I do not have your picarto URL added {}. You can save your picarto url with {}picarto add".format(
|
"I do not have your Picarto URL added {}. You can save your Picarto url with !picarto add".format(
|
||||||
member.mention, ctx.prefix))
|
ctx.message.author.mention))
|
||||||
|
# Then check if this server is already added as one to notify in
|
||||||
# Append this server's ID and save the new content
|
elif ctx.message.server.id in result[0]['servers']:
|
||||||
picarto[member.id]['servers'].append(ctx.message.server.id)
|
await self.bot.say("I am already set to notify in this server...")
|
||||||
await config.save_content('picarto', picarto)
|
else:
|
||||||
await self.bot.say(
|
await config.update_content('picarto', {'servers': r.row['servers'].append(ctx.message.server.id)},
|
||||||
"I have just changed which channel will be notified when you go live, to `{}`".format(
|
r_filter)
|
||||||
ctx.message.channel.name))
|
|
||||||
|
|
||||||
@notify.command(name='on', aliases=['start,yes'], pass_context=True, no_pm=True)
|
@notify.command(name='on', aliases=['start,yes'], pass_context=True, no_pm=True)
|
||||||
@checks.custom_perms(send_messages=True)
|
@checks.custom_perms(send_messages=True)
|
||||||
async def notify_on(self, ctx):
|
async def notify_on(self, ctx):
|
||||||
"""Turns picarto notifications on"""
|
"""Turns picarto notifications on"""
|
||||||
picarto = await config.get_content('picarto')
|
r_filter = {'member_id': ctx.message.author.id}
|
||||||
result = picarto.get(ctx.message.author.id)
|
await config.update_content('picarto', {'notifications_on': 1}, r_filter)
|
||||||
# Check if this user has saved their picarto URL first
|
|
||||||
if result is None:
|
|
||||||
await self.bot.say(
|
|
||||||
"I do not have your picarto URL added {}. You can save your picarto url with !picarto add".format(
|
|
||||||
ctx.message.author.mention))
|
|
||||||
# Next check if they are already set to notify
|
|
||||||
elif result['notifications_on']:
|
|
||||||
await self.bot.say("What do you want me to do, send two notifications? Not gonna happen {}".format(
|
|
||||||
ctx.message.author.mention))
|
|
||||||
else:
|
|
||||||
picarto[ctx.message.author.id]['notifications_on'] = 1
|
|
||||||
await config.save_content('picarto', picarto)
|
|
||||||
await self.bot.say("I will notify if you go live {}, you'll get a bajillion followers I promise c:".format(
|
await self.bot.say("I will notify if you go live {}, you'll get a bajillion followers I promise c:".format(
|
||||||
ctx.message.author.mention))
|
ctx.message.author.mention))
|
||||||
|
|
||||||
|
@ -238,19 +223,8 @@ class Picarto:
|
||||||
@checks.custom_perms(send_messages=True)
|
@checks.custom_perms(send_messages=True)
|
||||||
async def notify_off(self, ctx):
|
async def notify_off(self, ctx):
|
||||||
"""Turns picarto notifications off"""
|
"""Turns picarto notifications off"""
|
||||||
picarto = await config.get_content('picarto')
|
r_filter = {'member_id': ctx.message.author.id}
|
||||||
# Check if this user has saved their picarto URL first
|
await config.update_content('picarto', {'notifications_on': 0}, r_filter)
|
||||||
if picarto.get(ctx.message.author.id) is None:
|
|
||||||
await self.bot.say(
|
|
||||||
"I do not have your picarto URL added {}. You can save your picarto url with !picarto add".format(
|
|
||||||
ctx.message.author.mention))
|
|
||||||
# Next check if they are already set to not notify
|
|
||||||
elif not picarto.get(ctx.message.author.id)['notifications_on']:
|
|
||||||
await self.bot.say("I am already set to not notify if you go live! Pay attention brah {}".format(
|
|
||||||
ctx.message.author.mention))
|
|
||||||
else:
|
|
||||||
picarto[ctx.message.author.id]['notifications_on'] = 0
|
|
||||||
await config.save_content('picarto', picarto)
|
|
||||||
await self.bot.say(
|
await self.bot.say(
|
||||||
"I will not notify if you go live anymore {}, "
|
"I will not notify if you go live anymore {}, "
|
||||||
"are you going to stream some lewd stuff you don't want people to see?~".format(
|
"are you going to stream some lewd stuff you don't want people to see?~".format(
|
||||||
|
|
|
@ -10,26 +10,60 @@ class Stats:
|
||||||
def __init__(self, bot):
|
def __init__(self, bot):
|
||||||
self.bot = bot
|
self.bot = bot
|
||||||
|
|
||||||
|
def find_command(self, command):
|
||||||
|
cmd = None
|
||||||
|
|
||||||
|
for part in command.split():
|
||||||
|
try:
|
||||||
|
if cmd is None:
|
||||||
|
cmd = self.bot.commands.get(part)
|
||||||
|
else:
|
||||||
|
cmd = cmd.commands.get(part)
|
||||||
|
except AttributeError:
|
||||||
|
cmd = None
|
||||||
|
break
|
||||||
|
|
||||||
|
return cmd
|
||||||
|
|
||||||
|
@commands.group(no_pm=True)
|
||||||
|
@checks.custom_perms(send_messages=True)
|
||||||
|
async def command(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@command.command(no_pm=True, name="stats")
|
||||||
|
@checks.custom_perms(send_messages=True)
|
||||||
|
async def command_stats(self, ctx, *, command):
|
||||||
|
"""This command can be used to view some usage stats about a specific command"""
|
||||||
|
cmd = self.find_command(command)
|
||||||
|
if cmd is None:
|
||||||
|
await self.bot.say("`{}` is not a valid command".format(command))
|
||||||
|
|
||||||
|
total_command_stats = await config.get('command_usage')
|
||||||
|
|
||||||
@commands.command(pass_context=True, no_pm=True)
|
@commands.command(pass_context=True, no_pm=True)
|
||||||
@checks.custom_perms(send_messages=True)
|
@checks.custom_perms(send_messages=True)
|
||||||
async def mostboops(self, ctx):
|
async def mostboops(self, ctx):
|
||||||
"""Shows the person you have 'booped' the most, as well as how many times"""
|
"""Shows the person you have 'booped' the most, as well as how many times"""
|
||||||
boops = await config.get_content('boops')
|
r_filter = {'member_id': ctx.message.author.id}
|
||||||
if not boops.get(ctx.message.author.id):
|
boops = await config.get_content('boops', r_filter)
|
||||||
|
if boops is None:
|
||||||
await self.bot.say("You have not booped anyone {} Why the heck not...?".format(ctx.message.author.mention))
|
await self.bot.say("You have not booped anyone {} Why the heck not...?".format(ctx.message.author.mention))
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# Just to make this easier, just pay attention to the boops data, now that we have the right entry
|
||||||
|
boops = boops[0]['boops']
|
||||||
|
|
||||||
# First get a list of the ID's of all members in this server, for use in list comprehension
|
# First get a list of the ID's of all members in this server, for use in list comprehension
|
||||||
server_member_ids = [member.id for member in ctx.message.server.members]
|
server_member_ids = [member.id for member in ctx.message.server.members]
|
||||||
# Then get a sorted list, based on the amount of times they've booped the member
|
# Then get a sorted list, based on the amount of times they've booped the member
|
||||||
# Reverse needs to be true, as we want it to go from highest to lowest
|
# Reverse needs to be true, as we want it to go from highest to lowest
|
||||||
sorted_boops = sorted(boops.get(ctx.message.author.id).items(), key=lambda x: x[1], reverse=True)
|
sorted_boops = sorted(boops.items(), key=lambda x: x[1], reverse=True)
|
||||||
# Then override the same list, checking if the member they've booped is in this server
|
# Then override the same list, checking if the member they've booped is in this server
|
||||||
sorted_boops = [x for x in sorted_boops if x[0] in server_member_ids]
|
sorted_boops = [x for x in sorted_boops if x[0] in server_member_ids]
|
||||||
|
|
||||||
# Since this is sorted, we just need to get the following information on the first user in the list
|
# Since this is sorted, we just need to get the following information on the first user in the list
|
||||||
most_boops = sorted_boops[0][1]
|
most_id, most_boops = sorted_boops[0]
|
||||||
most_id = sorted_boops[0][0]
|
|
||||||
member = discord.utils.find(lambda m: m.id == most_id, self.bot.get_all_members())
|
member = discord.utils.find(lambda m: m.id == most_id, self.bot.get_all_members())
|
||||||
await self.bot.say("{0} you have booped {1} the most amount of times, coming in at {2} times".format(
|
await self.bot.say("{0} you have booped {1} the most amount of times, coming in at {2} times".format(
|
||||||
ctx.message.author.mention, member.mention, most_boops))
|
ctx.message.author.mention, member.mention, most_boops))
|
||||||
|
@ -38,15 +72,18 @@ class Stats:
|
||||||
@checks.custom_perms(send_messages=True)
|
@checks.custom_perms(send_messages=True)
|
||||||
async def listboops(self, ctx):
|
async def listboops(self, ctx):
|
||||||
"""Lists all the users you have booped and the amount of times"""
|
"""Lists all the users you have booped and the amount of times"""
|
||||||
boops = await config.get_content('boops')
|
r_filter = {'member_id': ctx.message.author.id}
|
||||||
booped_members = boops.get(ctx.message.author.id)
|
boops = await config.get_content('boops', r_filter)
|
||||||
if booped_members is None:
|
if boops is None:
|
||||||
await self.bot.say("You have not booped anyone {} Why the heck not...?".format(ctx.message.author.mention))
|
await self.bot.say("You have not booped anyone {} Why the heck not...?".format(ctx.message.author.mention))
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# Just to make this easier, just pay attention to the boops data, now that we have the right entry
|
||||||
|
boops = boops[0]['boops']
|
||||||
|
|
||||||
# Same concept as the mostboops method
|
# Same concept as the mostboops method
|
||||||
server_member_ids = [member.id for member in ctx.message.server.members]
|
server_member_ids = [member.id for member in ctx.message.server.members]
|
||||||
booped_members = {m_id: amt for m_id, amt in booped_members.items() if m_id in server_member_ids}
|
booped_members = {m_id: amt for m_id, amt in boops.items() if m_id in server_member_ids}
|
||||||
sorted_booped_members = sorted(booped_members.items(), key=lambda k: k[1], reverse=True)
|
sorted_booped_members = sorted(booped_members.items(), key=lambda k: k[1], reverse=True)
|
||||||
|
|
||||||
output = "\n".join(
|
output = "\n".join(
|
||||||
|
@ -58,21 +95,21 @@ class Stats:
|
||||||
@checks.custom_perms(send_messages=True)
|
@checks.custom_perms(send_messages=True)
|
||||||
async def leaderboard(self, ctx):
|
async def leaderboard(self, ctx):
|
||||||
"""Prints a leaderboard of everyone in the server's battling record"""
|
"""Prints a leaderboard of everyone in the server's battling record"""
|
||||||
battles = await config.get_content('battle_records')
|
# Create a list of the ID's of all members in this server, for comparison to the records saved
|
||||||
|
|
||||||
# Same concept as mostboops
|
|
||||||
server_member_ids = [member.id for member in ctx.message.server.members]
|
server_member_ids = [member.id for member in ctx.message.server.members]
|
||||||
server_members = {member_id: stats for member_id, stats in battles.items() if member_id in server_member_ids}
|
battles = await config.get_content('battle_records')
|
||||||
# Only real difference is the key, the key needs to be based on the rating in the member's dictionary of stats
|
battles = [battle for battle in battles if battle['member_id'] in server_member_ids]
|
||||||
sorted_members = sorted(server_members.items(), key=lambda k: k[1]['rating'], reverse=True)
|
|
||||||
|
# Sort the members based on their rating
|
||||||
|
sorted_members = sorted(battles, key=lambda k: k['rating'], reverse=True)
|
||||||
|
|
||||||
fmt = ""
|
fmt = ""
|
||||||
count = 1
|
count = 1
|
||||||
for x in sorted_members:
|
for x in sorted_members:
|
||||||
member_id = x[0]
|
member_id = x['member_id']
|
||||||
stats = x[1]
|
rating = x['rating']
|
||||||
member = discord.utils.get(ctx.message.server.members, id=member_id)
|
member = ctx.message.server.get_member(member_id)
|
||||||
fmt += "#{}) {} (Rating: {})\n".format(count, member.display_name, stats.get('rating'))
|
fmt += "#{}) {} (Rating: {})\n".format(count, member.display_name, rating)
|
||||||
count += 1
|
count += 1
|
||||||
if count >= 11:
|
if count >= 11:
|
||||||
break
|
break
|
||||||
|
@ -84,25 +121,30 @@ class Stats:
|
||||||
"""Prints the battling stats for you, or the user provided"""
|
"""Prints the battling stats for you, or the user provided"""
|
||||||
member = member or ctx.message.author
|
member = member or ctx.message.author
|
||||||
|
|
||||||
|
# For this one, we don't want to pass a filter, as we do need all battle records
|
||||||
|
# We need this because we want to make a comparison for overall rank
|
||||||
all_members = await config.get_content('battle_records')
|
all_members = await config.get_content('battle_records')
|
||||||
if member.id not in all_members:
|
|
||||||
|
# Make a list comprehension to just check if the user has battled
|
||||||
|
if len([entry for entry in all_members if entry['member_id'] == member.id]) == 0:
|
||||||
await self.bot.say("That user has not battled yet!")
|
await self.bot.say("That user has not battled yet!")
|
||||||
return
|
return
|
||||||
|
|
||||||
# Same concept as the leaderboard
|
# Same concept as the leaderboard
|
||||||
server_member_ids = [member.id for member in ctx.message.server.members]
|
server_member_ids = [member.id for member in ctx.message.server.members]
|
||||||
server_members = {member_id: stats for member_id, stats in all_members.items() if
|
server_members = [stats for stats in all_members if stats['member_id'] in server_member_ids]
|
||||||
member_id in server_member_ids}
|
sorted_server_members = sorted(server_members, key=lambda x: x['rating'], reverse=True)
|
||||||
sorted_server_members = sorted(server_members.items(), key=lambda x: x[1]['rating'], reverse=True)
|
sorted_all_members = sorted(all_members, key=lambda x: x['rating'], reverse=True)
|
||||||
sorted_all_members = sorted(all_members.items(), key=lambda x: x[1]['rating'], reverse=True)
|
|
||||||
|
|
||||||
# Enumurate the list so that we can go through, find the user's place in the list
|
# Enumurate the list so that we can go through, find the user's place in the list
|
||||||
# and get just that for the rank
|
# and get just that for the rank
|
||||||
server_rank = [i for i, x in enumerate(sorted_server_members) if x[0] == member.id][0] + 1
|
server_rank = [i for i, x in enumerate(sorted_server_members) if x['member_id'] == member.id][0] + 1
|
||||||
total_rank = [i for i, x in enumerate(sorted_all_members) if x[0] == member.id][0] + 1
|
total_rank = [i for i, x in enumerate(sorted_all_members) if x['member_id'] == member.id][0] + 1
|
||||||
# The rest of this is straight forward, just formatting
|
# The rest of this is straight forward, just formatting
|
||||||
rating = server_members[member.id]['rating']
|
|
||||||
record = "{}-{}".format(server_members[member.id]['wins'], server_members[member.id]['losses'])
|
entry = [m for m in server_members if m['member_id'] == member.id][0]
|
||||||
|
rating = entry['rating']
|
||||||
|
record = "{}-{}".format(entry['wins'], entry['losses'])
|
||||||
fmt = 'Stats for {}:\n\tRecord: {}\n\tServer Rank: {}/{}\n\tOverall Rank: {}/{}\n\tRating: {}'
|
fmt = 'Stats for {}:\n\tRecord: {}\n\tServer Rank: {}/{}\n\tOverall Rank: {}/{}\n\tRating: {}'
|
||||||
fmt = fmt.format(member.display_name, record, server_rank, len(server_members), total_rank, len(all_members),
|
fmt = fmt.format(member.display_name, record, server_rank, len(server_members), total_rank, len(all_members),
|
||||||
rating)
|
rating)
|
||||||
|
|
|
@ -19,10 +19,12 @@ class StatsUpdate:
|
||||||
def __unload(self):
|
def __unload(self):
|
||||||
self.bot.loop.create_task(self.session.close())
|
self.bot.loop.create_task(self.session.close())
|
||||||
|
|
||||||
async def update(self, data):
|
async def update(self):
|
||||||
server_count = 0
|
server_count = 0
|
||||||
for d in data.values():
|
data = await config.get_content('bot_data')
|
||||||
server_count += d.get('server_count')
|
|
||||||
|
for entry in data:
|
||||||
|
server_count += entry.get('server_count')
|
||||||
|
|
||||||
carbon_payload = {
|
carbon_payload = {
|
||||||
'key': config.carbon_key,
|
'key': config.carbon_key,
|
||||||
|
@ -46,31 +48,34 @@ class StatsUpdate:
|
||||||
log.info('bots.discord.pw statistics returned {} for {}'.format(resp.status, payload))
|
log.info('bots.discord.pw statistics returned {} for {}'.format(resp.status, payload))
|
||||||
|
|
||||||
async def on_server_join(self, server):
|
async def on_server_join(self, server):
|
||||||
data = await config.get_content('bot_data')
|
r_filter = {'shard_id': config.shard_id}
|
||||||
shard_data = data.get('shard_{}'.format(config.shard_id)) or {}
|
server_count = len(self.bot.servers)
|
||||||
shard_data['server_count'] = len(self.bot.servers)
|
member_count = len(set(self.bot.get_all_members()))
|
||||||
shard_data['member_count'] = len(set(self.bot.get_all_members()))
|
entry = {'server_count': server_count, 'member_count': member_count, "shard_id": config.shard_id}
|
||||||
data['shard_{}'.format(config.shard_id)] = shard_data
|
# Check if this was successful, if it wasn't, that means a new shard was added and we need to add that entry
|
||||||
await config.save_content('bot_data', data)
|
if not await config.update_content('bot_data', entry, r_filter):
|
||||||
await self.update(data)
|
await config.add_content('bot_data', entry, r_filter)
|
||||||
|
self.bot.loop.create_task(self.update())
|
||||||
|
|
||||||
async def on_server_leave(self, server):
|
async def on_server_leave(self, server):
|
||||||
data = await config.get_content('bot_data')
|
r_filter = {'shard_id': config.shard_id}
|
||||||
shard_data = data.get('shard_{}'.format(config.shard_id)) or {}
|
server_count = len(self.bot.servers)
|
||||||
shard_data['server_count'] = len(self.bot.servers)
|
member_count = len(set(self.bot.get_all_members()))
|
||||||
shard_data['member_count'] = len(set(self.bot.get_all_members()))
|
entry = {'server_count': server_count, 'member_count': member_count, "shard_id": config.shard_id}
|
||||||
data['shard_{}'.format(config.shard_id)] = shard_data
|
# Check if this was successful, if it wasn't, that means a new shard was added and we need to add that entry
|
||||||
await config.save_content('bot_data', data)
|
if not await config.update_content('bot_data', entry, r_filter):
|
||||||
await self.update(data)
|
await config.add_content('bot_data', entry, r_filter)
|
||||||
|
self.bot.loop.create_task(self.update())
|
||||||
|
|
||||||
async def on_ready(self):
|
async def on_ready(self):
|
||||||
data = await config.get_content('bot_data')
|
r_filter = {'shard_id': config.shard_id}
|
||||||
shard_data = data.get('shard_{}'.format(config.shard_id)) or {}
|
server_count = len(self.bot.servers)
|
||||||
shard_data['server_count'] = len(self.bot.servers)
|
member_count = len(set(self.bot.get_all_members()))
|
||||||
shard_data['member_count'] = len(set(self.bot.get_all_members()))
|
entry = {'server_count': server_count, 'member_count': member_count, "shard_id": config.shard_id}
|
||||||
data['shard_{}'.format(config.shard_id)] = shard_data
|
# Check if this was successful, if it wasn't, that means a new shard was added and we need to add that entry
|
||||||
await config.save_content('bot_data', data)
|
if not await config.update_content('bot_data', entry, r_filter):
|
||||||
await self.update(data)
|
await config.add_content('bot_data', entry, r_filter)
|
||||||
|
self.bot.loop.create_task(self.update())
|
||||||
|
|
||||||
|
|
||||||
def setup(bot):
|
def setup(bot):
|
||||||
|
|
|
@ -8,6 +8,7 @@ import aiohttp
|
||||||
import re
|
import re
|
||||||
import json
|
import json
|
||||||
import pendulum
|
import pendulum
|
||||||
|
import rethinkdb as r
|
||||||
|
|
||||||
|
|
||||||
def setup(bot):
|
def setup(bot):
|
||||||
|
@ -35,20 +36,28 @@ class Strawpoll:
|
||||||
async def strawpolls(self, ctx, poll_id: str = None):
|
async def strawpolls(self, ctx, poll_id: str = None):
|
||||||
"""This command can be used to show a strawpoll setup on this server"""
|
"""This command can be used to show a strawpoll setup on this server"""
|
||||||
# Strawpolls cannot be 'deleted' so to handle whether a poll is running or not on a server
|
# Strawpolls cannot be 'deleted' so to handle whether a poll is running or not on a server
|
||||||
# Just save the poll in the config file, which can then be removed when it should not be "running" anymore
|
# Just save the poll, which can then be removed when it should not be "running" anymore
|
||||||
all_polls = await config.get_content('strawpolls')
|
r_filter = {'server_id': ctx.message.server.id}
|
||||||
server_polls = all_polls.get(ctx.message.server.id) or {}
|
polls = await config.get_content('strawpolls', r_filter)
|
||||||
if not server_polls:
|
# Check if there are any polls setup on this server
|
||||||
|
try:
|
||||||
|
polls = polls[0]['polls']
|
||||||
|
except TypeError:
|
||||||
await self.bot.say("There are currently no strawpolls running on this server!")
|
await self.bot.say("There are currently no strawpolls running on this server!")
|
||||||
return
|
return
|
||||||
# If no poll_id was provided, print a list of all current running poll's on this server
|
# Print all polls on this server if poll_id was not provided
|
||||||
if not poll_id:
|
if poll_id is None:
|
||||||
fmt = "\n".join(
|
fmt = "\n".join(
|
||||||
"{}: https://strawpoll.me/{}".format(data['title'], _id) for _id, data in server_polls.items())
|
"{}: https://strawpoll.me/{}".format(data['title'], data['poll_id']) for data in polls)
|
||||||
await self.bot.say("```\n{}```".format(fmt))
|
await self.bot.say("```\n{}```".format(fmt))
|
||||||
# Else if a valid poll_id was provided, print info about that poll
|
else:
|
||||||
elif poll_id in server_polls.keys():
|
# Since strawpoll should never allow us to have more than one poll with the same ID
|
||||||
poll = server_polls[poll_id]
|
# It's safe to assume there's only one result
|
||||||
|
try:
|
||||||
|
poll = [p for p in polls if p['poll_id'] == poll_id][0]
|
||||||
|
except IndexError:
|
||||||
|
await self.bot.say("That poll does not exist on this server!")
|
||||||
|
return
|
||||||
|
|
||||||
async with self.session.get("{}/{}".format(self.url, poll_id),
|
async with self.session.get("{}/{}".format(self.url, poll_id),
|
||||||
headers={'User-Agent': 'Bonfire/1.0.0'}) as response:
|
headers={'User-Agent': 'Bonfire/1.0.0'}) as response:
|
||||||
|
@ -59,7 +68,7 @@ class Strawpoll:
|
||||||
# And the votes to match it, based on the index of the option
|
# And the votes to match it, based on the index of the option
|
||||||
# The rest is simple formatting
|
# The rest is simple formatting
|
||||||
fmt_options = "\n\t".join(
|
fmt_options = "\n\t".join(
|
||||||
"{}: {}".format(r, data['votes'][i]) for i, r in enumerate(data['options']))
|
"{}: {}".format(result, data['votes'][i]) for i, result in enumerate(data['options']))
|
||||||
author = discord.utils.get(ctx.message.server.members, id=poll['author'])
|
author = discord.utils.get(ctx.message.server.members, id=poll['author'])
|
||||||
created_ago = (pendulum.utcnow() - pendulum.parse(poll['date'])).in_words()
|
created_ago = (pendulum.utcnow() - pendulum.parse(poll['date'])).in_words()
|
||||||
link = "https://strawpoll.me/{}".format(poll_id)
|
link = "https://strawpoll.me/{}".format(poll_id)
|
||||||
|
@ -107,37 +116,38 @@ class Strawpoll:
|
||||||
return
|
return
|
||||||
|
|
||||||
# Save this strawpoll in the list of running strawpolls for a server
|
# Save this strawpoll in the list of running strawpolls for a server
|
||||||
all_polls = await config.get_content('strawpolls')
|
poll_id = str(data['id'])
|
||||||
server_polls = all_polls.get(ctx.message.server.id) or {}
|
|
||||||
server_polls[str(data['id'])] = {'author': ctx.message.author.id, 'date': str(pendulum.utcnow()), 'title': title}
|
|
||||||
all_polls[ctx.message.server.id] = server_polls
|
|
||||||
await config.save_content('strawpolls', all_polls)
|
|
||||||
|
|
||||||
await self.bot.say("Link for your new strawpoll: https://strawpoll.me/{}".format(data['id']))
|
r_filter = {'server_id': ctx.message.server.id}
|
||||||
|
sub_entry = {'poll_id': poll_id,
|
||||||
|
'author': ctx.message.author.id,
|
||||||
|
'date': str(pendulum.utcnow()),
|
||||||
|
'title': title}
|
||||||
|
|
||||||
|
entry = {'server_id': ctx.message.server.id,
|
||||||
|
'polls': [sub_entry]}
|
||||||
|
update = {'polls': r.row['polls'].append(sub_entry)}
|
||||||
|
if not await config.update_content('strawpolls', update, r_filter):
|
||||||
|
await config.add_content('strawpolls', entry, {'poll_id': poll_id})
|
||||||
|
await self.bot.say("Link for your new strawpoll: https://strawpoll.me/{}".format(poll_id))
|
||||||
|
|
||||||
@strawpolls.command(name='delete', aliases=['remove', 'stop'], pass_context=True)
|
@strawpolls.command(name='delete', aliases=['remove', 'stop'], pass_context=True)
|
||||||
@checks.custom_perms(kick_members=True)
|
@checks.custom_perms(kick_members=True)
|
||||||
async def remove_strawpoll(self, ctx, poll_id: str = None):
|
async def remove_strawpoll(self, ctx, poll_id):
|
||||||
"""This command can be used to delete one of the existing strawpolls
|
"""This command can be used to delete one of the existing strawpolls"""
|
||||||
If you don't provide an ID it will print the list of polls available"""
|
r_filter = {'server_id': ctx.message.server.id}
|
||||||
|
content = await config.get_content('strawpolls', r_filter)
|
||||||
|
try:
|
||||||
|
content = content[0]['polls']
|
||||||
|
except TypeError:
|
||||||
|
await self.bot.say("There are no strawpolls setup on this server!")
|
||||||
|
return
|
||||||
|
|
||||||
all_polls = await config.get_content('strawpolls')
|
polls = [poll for poll in content if poll['poll_id'] != poll_id]
|
||||||
server_polls = all_polls.get(ctx.message.server.id) or {}
|
|
||||||
|
|
||||||
# Check if a poll_id was provided, if it is then we can continue, if not print the list of current polls
|
update = {'polls': polls}
|
||||||
if poll_id:
|
# Try to remove the poll based on the ID, if it doesn't exist, this will return false
|
||||||
poll = server_polls.get(poll_id)
|
if await config.update_content('strawpolls', update, r_filter):
|
||||||
# Check if no poll exists with that ID, then print a list of the polls
|
|
||||||
if not poll:
|
|
||||||
fmt = "\n".join("{}: {}".format(data['title'], _poll_id) for _poll_id, data in server_polls.items())
|
|
||||||
await self.bot.say(
|
|
||||||
"There is no poll setup with that ID! Here is a list of the current polls```\n{}```".format(fmt))
|
|
||||||
else:
|
|
||||||
# Delete the poll that was just found
|
|
||||||
del server_polls[poll_id]
|
|
||||||
all_polls[ctx.message.server.id] = server_polls
|
|
||||||
await config.save_content('strawpolls', all_polls)
|
|
||||||
await self.bot.say("I have just removed the poll with the ID {}".format(poll_id))
|
await self.bot.say("I have just removed the poll with the ID {}".format(poll_id))
|
||||||
else:
|
else:
|
||||||
fmt = "\n".join("{}: {}".format(data['title'], _poll_id) for _poll_id, data in server_polls.items())
|
await self.bot.say("There is no poll setup with that ID!")
|
||||||
await self.bot.say("Here is a list of the polls on this server:\n```\n{}```".format(fmt))
|
|
||||||
|
|
52
cogs/tags.py
52
cogs/tags.py
|
@ -14,10 +14,9 @@ class Tags:
|
||||||
@checks.custom_perms(send_messages=True)
|
@checks.custom_perms(send_messages=True)
|
||||||
async def tags(self, ctx):
|
async def tags(self, ctx):
|
||||||
"""Prints all the custom tags that this server currently has"""
|
"""Prints all the custom tags that this server currently has"""
|
||||||
tags = await config.get_content('tags')
|
tags = await config.get_content('tags', {'server_id': ctx.message.server.id})
|
||||||
tags = tags['tags']
|
|
||||||
# Simple generator that adds a tag to the list to print, if the tag is for this server
|
# Simple generator that adds a tag to the list to print, if the tag is for this server
|
||||||
fmt = "\n".join("{}".format(tag['tag']) for tag in tags if tag['server_id'] == ctx.message.server.id)
|
fmt = "\n".join("{}".format(tag['tag']) for tag in tags)
|
||||||
await self.bot.say('```\n{}```'.format(fmt))
|
await self.bot.say('```\n{}```'.format(fmt))
|
||||||
|
|
||||||
@commands.group(pass_context=True, invoke_without_command=True, no_pm=True)
|
@commands.group(pass_context=True, invoke_without_command=True, no_pm=True)
|
||||||
|
@ -25,15 +24,13 @@ class Tags:
|
||||||
async def tag(self, ctx, *, tag: str):
|
async def tag(self, ctx, *, tag: str):
|
||||||
"""This can be used to call custom tags
|
"""This can be used to call custom tags
|
||||||
The format to call a custom tag is !tag <tag>"""
|
The format to call a custom tag is !tag <tag>"""
|
||||||
tags = await config.get_content('tags')
|
r_filter = lambda row: (row['server_id'] == ctx.message.server.id) & (row['tag'] == tag)
|
||||||
tags = tags['tags']
|
tags = await config.get_content('tags', r_filter)
|
||||||
# Same generator as the method for tags, other than the second check to get the tag that is provided
|
if tags is None:
|
||||||
result = [t for t in tags if t['tag'] == tag and t['server_id'] == ctx.message.server.id]
|
|
||||||
if len(result) == 0:
|
|
||||||
await self.bot.say('That tag does not exist!')
|
await self.bot.say('That tag does not exist!')
|
||||||
return
|
return
|
||||||
# We shouldn't ever have two tags of the same name, so just get the first result
|
# We shouldn't ever have two tags of the same name, so just get the first result
|
||||||
await self.bot.say("\u200B{}".format(result[0]['result']))
|
await self.bot.say("\u200B{}".format(tags[0]['result']))
|
||||||
|
|
||||||
@tag.command(name='add', aliases=['create', 'start'], pass_context=True, no_pm=True)
|
@tag.command(name='add', aliases=['create', 'start'], pass_context=True, no_pm=True)
|
||||||
@checks.custom_perms(kick_members=True)
|
@checks.custom_perms(kick_members=True)
|
||||||
|
@ -55,39 +52,28 @@ class Tags:
|
||||||
"Please provide the format for the tag in: {}tag add <tag> - <result>".format(ctx.prefix))
|
"Please provide the format for the tag in: {}tag add <tag> - <result>".format(ctx.prefix))
|
||||||
return
|
return
|
||||||
|
|
||||||
tags = await config.get_content('tags')
|
entry = {'server_id': ctx.message.server.id, 'tag': tag, 'result': tag_result}
|
||||||
tags = tags['tags']
|
r_filter = lambda row: (row['server_id'] == ctx.message.server.id) & (row['tag'] == tag)
|
||||||
for t in tags:
|
# Try to create new entry first, if that fails (it already exists) then we update it
|
||||||
# Attempt to find a tag with that name, so that we update it instead of making a duplicate
|
if await config.add_content('tags', entry, r_filter):
|
||||||
if t['tag'] == tag and t['server_id'] == ctx.message.server.id:
|
|
||||||
t['result'] = tag_result
|
|
||||||
await self.bot.say(
|
|
||||||
"I have just updated the tag `{0}`! You can call this tag by entering !tag {0}".format(tag))
|
|
||||||
# If we haven't found one, append a new one to the list
|
|
||||||
tags.append({'server_id': ctx.message.server.id, 'tag': tag, 'result': tag_result})
|
|
||||||
await self.bot.say(
|
await self.bot.say(
|
||||||
"I have just added the tag `{0}`! You can call this tag by entering !tag {0}".format(tag))
|
"I have just added the tag `{0}`! You can call this tag by entering !tag {0}".format(tag))
|
||||||
await config.save_content('tags', {'tags': tags})
|
else:
|
||||||
|
await config.update_content('tags', entry, r_filter)
|
||||||
|
await self.bot.say(
|
||||||
|
"I have just updated the tag `{0}`! You can call this tag by entering !tag {0}".format(tag))
|
||||||
|
|
||||||
@tag.command(name='delete', aliases=['remove', 'stop'], pass_context=True, no_pm=True)
|
@tag.command(name='delete', aliases=['remove', 'stop'], pass_context=True, no_pm=True)
|
||||||
@checks.custom_perms(kick_members=True)
|
@checks.custom_perms(kick_members=True)
|
||||||
async def del_tag(self, ctx, *, tag: str):
|
async def del_tag(self, ctx, *, tag: str):
|
||||||
"""Use this to remove a tag that from use for this server
|
"""Use this to remove a tag from use for this server
|
||||||
Format to delete a tag is !tag delete <tag>"""
|
Format to delete a tag is !tag delete <tag>"""
|
||||||
tags = await config.get_content('tags')
|
r_filter = lambda row: (row['server_id'] == ctx.message.server.id) & (row['tag'] == tag)
|
||||||
tags = tags['tags']
|
if await config.remove_content('tags', r_filter):
|
||||||
# Get a list of the tags that match this server, and the name provided (should only ever be one if any)
|
await self.bot.say('I have just removed the tag `{}`'.format(tag))
|
||||||
result = [t for t in tags if t['tag'] == tag and t['server_id'] == ctx.message.server.id]
|
else:
|
||||||
# If we haven't found one, can't delete it
|
|
||||||
if len(result) == 0:
|
|
||||||
await self.bot.say(
|
await self.bot.say(
|
||||||
"The tag {} does not exist! You can't remove something if it doesn't exist...".format(tag))
|
"The tag {} does not exist! You can't remove something if it doesn't exist...".format(tag))
|
||||||
return
|
|
||||||
|
|
||||||
# Since there should never be more than one result due to our checks we've made, just remove the first result
|
|
||||||
tags.remove(result[0])
|
|
||||||
await self.bot.say('I have just removed the tag `{}`'.format(tag))
|
|
||||||
await config.save_content('tags', {'tags': tags})
|
|
||||||
|
|
||||||
|
|
||||||
def setup(bot):
|
def setup(bot):
|
||||||
|
|
|
@ -98,48 +98,6 @@ class Board:
|
||||||
return "```\n{}```".format(_board)
|
return "```\n{}```".format(_board)
|
||||||
|
|
||||||
|
|
||||||
async def update_records(winner, loser):
|
|
||||||
# This is the exact same formula as the battling update.
|
|
||||||
# The only difference is I use the word "match" instead of "battle"
|
|
||||||
matches = await config.get_content('tictactoe')
|
|
||||||
|
|
||||||
winner_stats = matches.get(winner.id) or {}
|
|
||||||
winner_rating = winner_stats.get('rating') or 1000
|
|
||||||
|
|
||||||
loser_stats = matches.get(loser.id) or {}
|
|
||||||
loser_rating = loser_stats.get('rating') or 1000
|
|
||||||
|
|
||||||
difference = abs(winner_rating - loser_rating)
|
|
||||||
rating_change = 0
|
|
||||||
count = 25
|
|
||||||
while count <= difference:
|
|
||||||
if count > 300:
|
|
||||||
break
|
|
||||||
rating_change += 1
|
|
||||||
count += 25
|
|
||||||
|
|
||||||
if winner_rating > loser_rating:
|
|
||||||
winner_rating += 16 - rating_change
|
|
||||||
loser_rating -= 16 - rating_change
|
|
||||||
else:
|
|
||||||
winner_rating += 16 + rating_change
|
|
||||||
loser_rating -= 16 + rating_change
|
|
||||||
|
|
||||||
winner_wins = winner_stats.get('wins') or 0
|
|
||||||
winner_losses = winner_stats.get('losses') or 0
|
|
||||||
loser_wins = loser_stats.get('wins') or 0
|
|
||||||
loser_losses = loser_stats.get('losses') or 0
|
|
||||||
winner_wins += 1
|
|
||||||
loser_losses += 1
|
|
||||||
|
|
||||||
winner_stats = {'wins': winner_wins, 'losses': winner_losses, 'rating': winner_rating}
|
|
||||||
loser_stats = {'wins': loser_wins, 'losses': loser_losses, 'rating': loser_rating}
|
|
||||||
matches[winner.id] = winner_stats
|
|
||||||
matches[loser.id] = loser_stats
|
|
||||||
|
|
||||||
await config.save_content('tictactoe', matches)
|
|
||||||
|
|
||||||
|
|
||||||
class TicTacToe:
|
class TicTacToe:
|
||||||
def __init__(self, bot):
|
def __init__(self, bot):
|
||||||
self.bot = bot
|
self.bot = bot
|
||||||
|
@ -230,7 +188,7 @@ class TicTacToe:
|
||||||
await self.bot.say("{} has won this game of TicTacToe, better luck next time {}".format(winner.display_name,
|
await self.bot.say("{} has won this game of TicTacToe, better luck next time {}".format(winner.display_name,
|
||||||
loser.display_name))
|
loser.display_name))
|
||||||
# Handle updating ratings based on the winner and loser
|
# Handle updating ratings based on the winner and loser
|
||||||
await update_records(winner, loser)
|
await config.update_records('tictactoe', winner, loser)
|
||||||
# This game has ended, delete it so another one can be made
|
# This game has ended, delete it so another one can be made
|
||||||
del self.boards[ctx.message.server.id]
|
del self.boards[ctx.message.server.id]
|
||||||
else:
|
else:
|
||||||
|
|
134
cogs/twitch.py
134
cogs/twitch.py
|
@ -1,11 +1,13 @@
|
||||||
from discord.ext import commands
|
from discord.ext import commands
|
||||||
from .utils import config
|
from .utils import config
|
||||||
from .utils import checks
|
from .utils import checks
|
||||||
|
|
||||||
import aiohttp
|
import aiohttp
|
||||||
import asyncio
|
import asyncio
|
||||||
import discord
|
import discord
|
||||||
import json
|
import json
|
||||||
import re
|
import re
|
||||||
|
import rethinkdb as r
|
||||||
|
|
||||||
|
|
||||||
class Twitch:
|
class Twitch:
|
||||||
|
@ -23,15 +25,15 @@ class Twitch:
|
||||||
# Check a specific channel's data, and get the response in text format
|
# Check a specific channel's data, and get the response in text format
|
||||||
url = "https://api.twitch.tv/kraken/streams/{}?client_id={}".format(channel, self.key)
|
url = "https://api.twitch.tv/kraken/streams/{}?client_id={}".format(channel, self.key)
|
||||||
with aiohttp.ClientSession() as s:
|
with aiohttp.ClientSession() as s:
|
||||||
async with s.get(url) as r:
|
async with s.get(url) as response:
|
||||||
response = await r.text()
|
result = await response.text()
|
||||||
|
|
||||||
# For some reason Twitch's API call is not reliable, sometimes it returns stream as None
|
# For some reason Twitch's API call is not reliable, sometimes it returns stream as None
|
||||||
# That is what we're checking specifically, sometimes it doesn't exist in the returned JSON at all
|
# That is what we're checking specifically, sometimes it doesn't exist in the returned JSON at all
|
||||||
# Sometimes it returns something that cannot be decoded with JSON
|
# Sometimes it returns something that cannot be decoded with JSON
|
||||||
# In either error case, just assume they're offline, the next check will most likely work
|
# In either error case, just assume they're offline, the next check will most likely work
|
||||||
try:
|
try:
|
||||||
data = json.loads(response)
|
data = json.loads(result)
|
||||||
return data['stream'] is not None
|
return data['stream'] is not None
|
||||||
except (KeyError, json.JSONDecodeError):
|
except (KeyError, json.JSONDecodeError):
|
||||||
return False
|
return False
|
||||||
|
@ -40,54 +42,55 @@ class Twitch:
|
||||||
await self.bot.wait_until_ready()
|
await self.bot.wait_until_ready()
|
||||||
# Loop through as long as the bot is connected
|
# Loop through as long as the bot is connected
|
||||||
while not self.bot.is_closed:
|
while not self.bot.is_closed:
|
||||||
twitch = await config.get_content('twitch')
|
twitch = await config.get_content('twitch', {'notifications_on': 1})
|
||||||
# Online/offline is based on whether they are set to such, in the config file
|
# Online/offline is based on whether they are set to such, in the config file
|
||||||
# This means they were detected as online/offline before and we check for a change
|
# This means they were detected as online/offline before and we check for a change
|
||||||
online_users = {m_id: data for m_id, data in twitch.items() if data['notifications_on'] and data['live']}
|
online_users = {data['member_id']: data for data in twitch if data['live']}
|
||||||
offline_users = {m_id: data for m_id, data in twitch.items() if
|
offline_users = {data['member_id']: data for data in twitch if not data['live']}
|
||||||
data['notifications_on'] and not data['live']}
|
for m_id, result in offline_users.items():
|
||||||
for m_id, r in offline_users.items():
|
|
||||||
# Get their url and their user based on that url
|
# Get their url and their user based on that url
|
||||||
url = r['twitch_url']
|
url = result['twitch_url']
|
||||||
user = re.search("(?<=twitch.tv/)(.*)", url).group(1)
|
user = re.search("(?<=twitch.tv/)(.*)", url).group(1)
|
||||||
# Check if they are online right now
|
# Check if they are online right now
|
||||||
if await self.channel_online(user):
|
if await self.channel_online(user):
|
||||||
for server_id in r['servers']:
|
for server_id in result['servers']:
|
||||||
# Get the channel to send the message to, based on the saved alert's channel
|
# Get the channel to send the message to, based on the saved alert's channel
|
||||||
server = self.bot.get_server(server_id)
|
server = self.bot.get_server(server_id)
|
||||||
if server is None:
|
if server is None:
|
||||||
continue
|
continue
|
||||||
server_alerts = await config.get_content('server_alerts')
|
server_alerts = await config.get_content('server_alerts', {'server_id': server_id})
|
||||||
channel_id = server_alerts.get(server_id) or server_id
|
channel_id = server_id
|
||||||
|
if len(server_alerts) > 0:
|
||||||
|
channel_id = server_alerts[0].get('channel_id')
|
||||||
channel = self.bot.get_channel(channel_id)
|
channel = self.bot.get_channel(channel_id)
|
||||||
# Get the member that has just gone live
|
# Get the member that has just gone live
|
||||||
member = discord.utils.get(server.members, id=m_id)
|
member = discord.utils.get(server.members, id=m_id)
|
||||||
|
|
||||||
fmt = "{} has just gone live! View their stream at {}".format(member.display_name, url)
|
fmt = "{} has just gone live! View their stream at {}".format(member.display_name, url)
|
||||||
await self.bot.send_message(channel, fmt)
|
await self.bot.send_message(channel, fmt)
|
||||||
twitch[m_id]['live'] = 1
|
await config.update_content('twitch', {'live': 1}, {'member_id': m_id})
|
||||||
await config.save_content('twitch', twitch)
|
for m_id, result in online_users.items():
|
||||||
for m_id, r in online_users.items():
|
|
||||||
# Get their url and their user based on that url
|
# Get their url and their user based on that url
|
||||||
url = r['twitch_url']
|
url = result['twitch_url']
|
||||||
user = re.search("(?<=twitch.tv/)(.*)", url).group(1)
|
user = re.search("(?<=twitch.tv/)(.*)", url).group(1)
|
||||||
# Check if they are online right now
|
# Check if they are online right now
|
||||||
if not await self.channel_online(user):
|
if not await self.channel_online(user):
|
||||||
for server_id in r['servers']:
|
for server_id in result['servers']:
|
||||||
# Get the channel to send the message to, based on the saved alert's channel
|
# Get the channel to send the message to, based on the saved alert's channel
|
||||||
server = self.bot.get_server(server_id)
|
server = self.bot.get_server(server_id)
|
||||||
if server is None:
|
if server is None:
|
||||||
continue
|
continue
|
||||||
server_alerts = await config.get_content('server_alerts')
|
server_alerts = await config.get_content('server_alerts', {'server_id': server_id})
|
||||||
channel_id = server_alerts.get(server_id) or server_id
|
channel_id = server_id
|
||||||
|
if len(server_alerts) > 0:
|
||||||
|
channel_id = server_alerts[0].get('channel_id')
|
||||||
channel = self.bot.get_channel(channel_id)
|
channel = self.bot.get_channel(channel_id)
|
||||||
# Get the member that has just gone live
|
# Get the member that has just gone live
|
||||||
member = discord.utils.get(server.members, id=m_id)
|
member = discord.utils.get(server.members, id=m_id)
|
||||||
fmt = "{} has just gone offline! Catch them next time they stream at {}".format(
|
fmt = "{} has just gone offline! Catch them next time they stream at {}".format(
|
||||||
member.display_name, url)
|
member.display_name, url)
|
||||||
await self.bot.send_message(channel, fmt)
|
await self.bot.send_message(channel, fmt)
|
||||||
twitch[m_id]['live'] = 0
|
await config.update_content('twitch', {'live': 0}, {'member_id': m_id})
|
||||||
await config.save_content('twitch', twitch)
|
|
||||||
await asyncio.sleep(30)
|
await asyncio.sleep(30)
|
||||||
|
|
||||||
@commands.group(no_pm=True, invoke_without_command=True, pass_context=True)
|
@commands.group(no_pm=True, invoke_without_command=True, pass_context=True)
|
||||||
|
@ -97,19 +100,20 @@ class Twitch:
|
||||||
if member is None:
|
if member is None:
|
||||||
member = ctx.message.author
|
member = ctx.message.author
|
||||||
|
|
||||||
twitch_channels = await config.get_content('twitch')
|
result = await config.get_content('twitch', {'member_id': member.id})
|
||||||
result = twitch_channels.get(member.id)
|
|
||||||
if result is None:
|
if result is None:
|
||||||
await self.bot.say("{} has not saved their twitch URL yet!".format(member.name))
|
await self.bot.say("{} has not saved their twitch URL yet!".format(member.name))
|
||||||
return
|
return
|
||||||
|
|
||||||
|
result = result[0]
|
||||||
url = result['twitch_url']
|
url = result['twitch_url']
|
||||||
user = re.search("(?<=twitch.tv/)(.*)", url).group(1)
|
user = re.search("(?<=twitch.tv/)(.*)", url).group(1)
|
||||||
twitch_url = "https://api.twitch.tv/kraken/channels/{}?client_id={}".format(user, self.key)
|
twitch_url = "https://api.twitch.tv/kraken/channels/{}?client_id={}".format(user, self.key)
|
||||||
with aiohttp.ClientSession() as s:
|
with aiohttp.ClientSession() as s:
|
||||||
async with s.get(twitch_url) as r:
|
async with s.get(twitch_url) as response:
|
||||||
data = await r.json()
|
data = await response.json()
|
||||||
with open("twitch_testing", 'w') as f:
|
with open("twitch_testing", 'w') as f:
|
||||||
|
data['requested_url'] = url
|
||||||
json.dump(data, f)
|
json.dump(data, f)
|
||||||
|
|
||||||
fmt = "Username: {}".format(data['display_name'])
|
fmt = "Username: {}".format(data['display_name'])
|
||||||
|
@ -139,100 +143,78 @@ class Twitch:
|
||||||
|
|
||||||
# Try to find the channel provided, we'll get a 404 response if it does not exist
|
# Try to find the channel provided, we'll get a 404 response if it does not exist
|
||||||
with aiohttp.ClientSession() as s:
|
with aiohttp.ClientSession() as s:
|
||||||
async with s.get(url) as r:
|
async with s.get(url) as response:
|
||||||
if not r.status == 200:
|
if not response.status == 200:
|
||||||
await self.bot.say("That twitch user does not exist! "
|
await self.bot.say("That twitch user does not exist! "
|
||||||
"What would be the point of adding a nonexistant twitch user? Silly")
|
"What would be the point of adding a nonexistant twitch user? Silly")
|
||||||
return
|
return
|
||||||
|
|
||||||
twitch = await config.get_content('twitch')
|
r_filter = {'member_id': ctx.message.author.id}
|
||||||
result = twitch.get(ctx.message.author.id)
|
entry = {'twitch_url': url,
|
||||||
|
'servers': [ctx.message.server.id],
|
||||||
|
'notifications_on': 1,
|
||||||
|
'live': 0,
|
||||||
|
'member_id': ctx.message.author.id}
|
||||||
|
update = {'twitch_url': url}
|
||||||
|
|
||||||
# Check to see if this user has already saved a twitch URL
|
# Check to see if this user has already saved a twitch URL
|
||||||
# If they have, update the URL, otherwise create a new entry
|
# If they have, update the URL, otherwise create a new entry
|
||||||
# Assuming they're not live, and notifications should be on
|
# Assuming they're not live, and notifications should be on
|
||||||
if result is not None:
|
if not await config.add_content('twitch', entry, r_filter):
|
||||||
twitch[ctx.message.author.id]['twitch_url'] = url
|
await config.update_content('twitch', update, r_filter)
|
||||||
else:
|
|
||||||
twitch[ctx.message.author.id] = {'twitch_url': url, 'servers': [ctx.message.server.id],
|
|
||||||
'notifications_on': 1, 'live': 0}
|
|
||||||
await config.save_content('twitch', twitch)
|
|
||||||
await self.bot.say("I have just saved your twitch url {}".format(ctx.message.author.mention))
|
await self.bot.say("I have just saved your twitch url {}".format(ctx.message.author.mention))
|
||||||
|
|
||||||
@twitch.command(name='remove', aliases=['delete'], pass_context=True, no_pm=True)
|
@twitch.command(name='remove', aliases=['delete'], pass_context=True, no_pm=True)
|
||||||
@checks.custom_perms(send_messages=True)
|
@checks.custom_perms(send_messages=True)
|
||||||
async def remove_twitch_url(self, ctx):
|
async def remove_twitch_url(self, ctx):
|
||||||
"""Removes your twitch URL"""
|
"""Removes your twitch URL"""
|
||||||
twitch = await config.get_content('twitch')
|
# Just try to remove it, if it doesn't exist, nothing is going to happen
|
||||||
# Make sure the user exists before trying to delete them from the list
|
r_filter = {'member_id': ctx.message.author.id}
|
||||||
if twitch.get(ctx.message.author.id) is not None:
|
await config.remove_content('twitch', r_filter)
|
||||||
# Simply remove this user from the list, and save
|
|
||||||
del twitch[ctx.message.author.id]
|
|
||||||
await config.save_content('twitch', twitch)
|
|
||||||
await self.bot.say("I am no longer saving your twitch URL {}".format(ctx.message.author.mention))
|
await self.bot.say("I am no longer saving your twitch URL {}".format(ctx.message.author.mention))
|
||||||
else:
|
|
||||||
await self.bot.say(
|
|
||||||
"I do not have your twitch URL added {}. You can save your twitch url with !twitch add".format(
|
|
||||||
ctx.message.author.mention))
|
|
||||||
|
|
||||||
@twitch.group(pass_context=True, no_pm=True, invoke_without_command=True)
|
@twitch.group(pass_context=True, no_pm=True, invoke_without_command=True)
|
||||||
@checks.custom_perms(send_messages=True)
|
@checks.custom_perms(send_messages=True)
|
||||||
async def notify(self, ctx):
|
async def notify(self, ctx):
|
||||||
"""This can be used to modify notification settings for your twitch user
|
"""This can be used to modify notification settings for your twitch user
|
||||||
Call this command by itself to add 'this' server as one that will be notified when you on/offline"""
|
Call this command by itself to add 'this' server as one that will be notified when you on/offline"""
|
||||||
twitch = await config.get_content('twitch')
|
r_filter = {'member_id': ctx.message.author.id}
|
||||||
result = twitch.get(ctx.message.author.id)
|
result = await config.get_content('twitch', r_filter)
|
||||||
# Check if this user is saved at all
|
# Check if this user is saved at all
|
||||||
if result is None:
|
if result is None:
|
||||||
await self.bot.say(
|
await self.bot.say(
|
||||||
"I do not have your twitch URL added {}. You can save your twitch url with !twitch add".format(
|
"I do not have your twitch URL added {}. You can save your twitch url with !twitch add".format(
|
||||||
ctx.message.author.mention))
|
ctx.message.author.mention))
|
||||||
# Otherwise we just need to append the server's ID to the servers list
|
# Then check if this server is already added as one to notify in
|
||||||
|
elif ctx.message.server.id in result[0]['servers']:
|
||||||
|
await self.bot.say("I am already set to notify in this server...")
|
||||||
else:
|
else:
|
||||||
twitch[ctx.message.author.id]['servers'].append(ctx.message.server.id)
|
await config.update_content('twitch', {'servers': r.row['servers'].append(ctx.message.server.id)}, r_filter)
|
||||||
await config.save_content('twitch', twitch)
|
|
||||||
|
|
||||||
@notify.command(name='on', aliases=['start,yes'], pass_context=True, no_pm=True)
|
@notify.command(name='on', aliases=['start,yes'], pass_context=True, no_pm=True)
|
||||||
@checks.custom_perms(send_messages=True)
|
@checks.custom_perms(send_messages=True)
|
||||||
async def notify_on(self, ctx):
|
async def notify_on(self, ctx):
|
||||||
"""Turns twitch notifications on"""
|
"""Turns twitch notifications on"""
|
||||||
# Make sure this user is saved before we attempt to modify their information
|
r_filter = {'member_id': ctx.message.author.id}
|
||||||
twitch = await config.get_content('twitch')
|
if await config.update_content('twitch', {"notifications_on": 1}, r_filter):
|
||||||
result = twitch.get(ctx.message.author.id)
|
|
||||||
if result is None:
|
|
||||||
await self.bot.say(
|
|
||||||
"I do not have your twitch URL added {}. You can save your twitch url with !twitch add".format(
|
|
||||||
ctx.message.author.mention))
|
|
||||||
# Then check to see if notifications are already on
|
|
||||||
elif result['notifications_on']:
|
|
||||||
await self.bot.say("What do you want me to do, send two notifications? Not gonna happen {}".format(
|
|
||||||
ctx.message.author.mention))
|
|
||||||
# Otherwise, turn on notifications
|
|
||||||
else:
|
|
||||||
twitch[ctx.message.author.id]['notifications_on'] = 1
|
|
||||||
await config.save_content('twitch', twitch)
|
|
||||||
await self.bot.say("I will notify if you go live {}, you'll get a bajillion followers I promise c:".format(
|
await self.bot.say("I will notify if you go live {}, you'll get a bajillion followers I promise c:".format(
|
||||||
ctx.message.author.mention))
|
ctx.message.author.mention))
|
||||||
|
else:
|
||||||
|
await self.bot.say("I can't notify if you go live if I don't know your twitch URL yet!")
|
||||||
|
|
||||||
@notify.command(name='off', aliases=['stop,no'], pass_context=True, no_pm=True)
|
@notify.command(name='off', aliases=['stop,no'], pass_context=True, no_pm=True)
|
||||||
@checks.custom_perms(send_messages=True)
|
@checks.custom_perms(send_messages=True)
|
||||||
async def notify_off(self, ctx):
|
async def notify_off(self, ctx):
|
||||||
"""Turns twitch notifications off"""
|
"""Turns twitch notifications off"""
|
||||||
# This method is exactly the same, except for turning off notifcations instead of on
|
r_filter = {'member_id': ctx.message.author.id}
|
||||||
twitch = await config.get_content('twitch')
|
if await config.update_content('twitch', {"notifications_on": 1}, r_filter):
|
||||||
if twitch.get(ctx.message.author.id) is None:
|
|
||||||
await self.bot.say(
|
|
||||||
"I do not have your twitch URL added {}. You can save your twitch url with !twitch add".format(
|
|
||||||
ctx.message.author.mention))
|
|
||||||
elif not twitch.get(ctx.message.author.id)['notifications_on']:
|
|
||||||
await self.bot.say("I am already set to not notify if you go live! Pay attention brah {}".format(
|
|
||||||
ctx.message.author.mention))
|
|
||||||
else:
|
|
||||||
twitch[ctx.message.author.id]['notifications_on'] = 0
|
|
||||||
await self.bot.say(
|
await self.bot.say(
|
||||||
"I will not notify if you go live anymore {}, "
|
"I will not notify if you go live anymore {}, "
|
||||||
"are you going to stream some lewd stuff you don't want people to see?~".format(
|
"are you going to stream some lewd stuff you don't want people to see?~".format(
|
||||||
ctx.message.author.mention))
|
ctx.message.author.mention))
|
||||||
|
else:
|
||||||
|
await self.bot.say(
|
||||||
|
"I mean, I'm already not going to notify anyone, because I don't have your twitch URL saved...")
|
||||||
|
|
||||||
|
|
||||||
def setup(bot):
|
def setup(bot):
|
||||||
|
|
|
@ -4,6 +4,7 @@ import rethinkdb as r
|
||||||
import pendulum
|
import pendulum
|
||||||
|
|
||||||
loop = asyncio.get_event_loop()
|
loop = asyncio.get_event_loop()
|
||||||
|
global_config = {}
|
||||||
|
|
||||||
# Ensure that the required config.yml file actually exists
|
# Ensure that the required config.yml file actually exists
|
||||||
try:
|
try:
|
||||||
|
@ -38,7 +39,7 @@ class Cache:
|
||||||
loop.create_task(self.update())
|
loop.create_task(self.update())
|
||||||
|
|
||||||
async def update(self):
|
async def update(self):
|
||||||
self.values = await _get_content(self.key)
|
self.values = await get_content(self.key)
|
||||||
self.refreshed = pendulum.utcnow()
|
self.refreshed = pendulum.utcnow()
|
||||||
|
|
||||||
|
|
||||||
|
@ -82,18 +83,74 @@ db_pass = global_config.get('db_pass', '')
|
||||||
# {'ca_certs': db_cert}, 'user': db_user, 'password': db_pass}
|
# {'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}
|
db_opts = {'host': db_host, 'db': db_name, 'port': db_port, 'user': db_user, 'password': db_pass}
|
||||||
|
|
||||||
possible_keys = ['prefixes', 'battling', 'battle_records', 'boops', 'server_alerts', 'user_notifications',
|
possible_keys = ['prefixes', 'battle_records', 'boops', 'server_alerts', 'user_notifications', 'nsfw_channels',
|
||||||
'nsfw_channels', 'custom_permissions', 'rules', 'overwatch', 'picarto', 'twitch', 'strawpolls', 'tags',
|
'custom_permissions', 'rules', 'overwatch', 'picarto', 'twitch', 'strawpolls', 'tags',
|
||||||
'tictactoe', 'bot_data', 'command_manage']
|
'tictactoe', 'bot_data', 'command_manage']
|
||||||
|
|
||||||
# This will be a dictionary that holds the cache object, based on the key that is saved
|
# This will be a dictionary that holds the cache object, based on the key that is saved
|
||||||
cache = {}
|
cache = {}
|
||||||
|
|
||||||
sharded_data = {}
|
|
||||||
|
|
||||||
# Populate cache with each object
|
# Populate cache with each object
|
||||||
for k in possible_keys:
|
# With the new saving method, we're not going to be able to cache the way that I was before
|
||||||
cache[k] = Cache(k)
|
# This is on standby until I rethink how to do this, because I do still want to cache data
|
||||||
|
"""for k in possible_keys:
|
||||||
|
cache[k] = Cache(k)"""
|
||||||
|
|
||||||
|
# We still need 'cache' for prefixes and custom permissions however, so for now, just include that
|
||||||
|
cache['prefixes'] = Cache('prefixes')
|
||||||
|
cache['custom_permissions'] = Cache('custom_permissions')
|
||||||
|
|
||||||
|
async def update_records(key, winner, loser):
|
||||||
|
# We're using the Harkness scale to rate
|
||||||
|
# http://opnetchessclub.wikidot.com/harkness-rating-system
|
||||||
|
r_filter = lambda row: (row['member_id'] == winner.id) | (row['member_id'] == loser.id)
|
||||||
|
matches = await get_content(key, r_filter)
|
||||||
|
|
||||||
|
winner_stats = {}
|
||||||
|
loser_stats = {}
|
||||||
|
for stat in matches:
|
||||||
|
if stat.get('member_id') == winner.id:
|
||||||
|
winner_stats = stat
|
||||||
|
elif stat.get('member_id') == loser.id:
|
||||||
|
loser_stats = stat
|
||||||
|
|
||||||
|
winner_rating = winner_stats.get('rating') or 1000
|
||||||
|
loser_rating = loser_stats.get('rating') or 1000
|
||||||
|
|
||||||
|
# The scale is based off of increments of 25, increasing the change by 1 for each increment
|
||||||
|
# That is all this loop does, increment the "change" for every increment of 25
|
||||||
|
# The change caps off at 300 however, so break once we are over that limit
|
||||||
|
difference = abs(winner_rating - loser_rating)
|
||||||
|
rating_change = 0
|
||||||
|
count = 25
|
||||||
|
while count <= difference:
|
||||||
|
if count > 300:
|
||||||
|
break
|
||||||
|
rating_change += 1
|
||||||
|
count += 25
|
||||||
|
|
||||||
|
# 16 is the base change, increased or decreased based on whoever has the higher current rating
|
||||||
|
if winner_rating > loser_rating:
|
||||||
|
winner_rating += 16 - rating_change
|
||||||
|
loser_rating -= 16 - rating_change
|
||||||
|
else:
|
||||||
|
winner_rating += 16 + rating_change
|
||||||
|
loser_rating -= 16 + rating_change
|
||||||
|
|
||||||
|
# Just increase wins/losses for each person, making sure it's at least 0
|
||||||
|
winner_wins = winner_stats.get('wins') or 0
|
||||||
|
winner_losses = winner_stats.get('losses') or 0
|
||||||
|
loser_wins = loser_stats.get('wins') or 0
|
||||||
|
loser_losses = loser_stats.get('losses') or 0
|
||||||
|
winner_wins += 1
|
||||||
|
loser_losses += 1
|
||||||
|
|
||||||
|
# Now save the new wins, losses, and ratings
|
||||||
|
winner_stats = {'wins': winner_wins, 'losses': winner_losses, 'rating': winner_rating}
|
||||||
|
loser_stats = {'wins': loser_wins, 'losses': loser_losses, 'rating': loser_rating}
|
||||||
|
|
||||||
|
await update_content(key, {'member_id': winner.id}, winner_stats)
|
||||||
|
await update_content(key, {'member_id': loser.id}, loser_stats)
|
||||||
|
|
||||||
|
|
||||||
def command_prefix(bot, message):
|
def command_prefix(bot, message):
|
||||||
|
@ -102,72 +159,111 @@ def command_prefix(bot, message):
|
||||||
# If the prefix does exist in the database and isn't in our cache; too bad, something has messed up
|
# If the prefix does exist in the database and isn't in our cache; too bad, something has messed up
|
||||||
# But it is not worth a query for every single message the bot detects, to fix
|
# But it is not worth a query for every single message the bot detects, to fix
|
||||||
try:
|
try:
|
||||||
prefix = cache['prefixes'].values.get(message.server.id)
|
values = cache['prefixes'].values
|
||||||
|
try:
|
||||||
|
prefix = [data['prefix'] for data in values if message.server.id == data['server_id']][0]
|
||||||
|
except IndexError:
|
||||||
|
prefix = None
|
||||||
return prefix or default_prefix
|
return prefix or default_prefix
|
||||||
except KeyError:
|
except KeyError:
|
||||||
return default_prefix
|
return default_prefix
|
||||||
|
|
||||||
|
|
||||||
async def save_content(table: str, content):
|
async def add_content(table, content, r_filter=None):
|
||||||
# We need to make sure we're using asyncio
|
|
||||||
r.set_loop_type("asyncio")
|
r.set_loop_type("asyncio")
|
||||||
# Just connect to the database
|
|
||||||
conn = await r.connect(**db_opts)
|
conn = await r.connect(**db_opts)
|
||||||
# We need to make at least one query to ensure the key exists, so attempt to create it as our query
|
# First we need to make sure that this entry doesn't exist
|
||||||
|
# For all rethinkDB cares, multiple entries can exist with the same content
|
||||||
|
# For our purposes however, we do not want this
|
||||||
try:
|
try:
|
||||||
await r.table_create(table).run(conn)
|
if r_filter is not None:
|
||||||
except r.ReqlOpFailedError:
|
cursor = await r.table(table).filter(r_filter).run(conn)
|
||||||
pass
|
cur_content = await _convert_to_list(cursor)
|
||||||
# So the table already existed, or it has now been created, we can update the data now
|
if len(cur_content) > 0:
|
||||||
# Since we're handling everything that is rewritten in the code itself, we just need to delete then insert
|
await conn.close()
|
||||||
await r.table(table).delete().run(conn)
|
return False
|
||||||
await r.table(table).insert(content).run(conn)
|
await r.table(table).insert(content).run(conn)
|
||||||
await conn.close()
|
await conn.close()
|
||||||
|
return True
|
||||||
# Now that we've saved the new content, we should update our cache
|
except r.ReqlOpFailedError:
|
||||||
cached = cache.get(table)
|
# This means the table does not exist
|
||||||
# While this should theoretically never happen, we just want to make sure
|
await r.create_table(table).run(conn)
|
||||||
if cached is None:
|
await r.table(table).insert(content).run(conn)
|
||||||
cache[table] = Cache(table)
|
await conn.close()
|
||||||
else:
|
return True
|
||||||
loop.create_task(cached.update())
|
|
||||||
|
|
||||||
|
|
||||||
async def get_content(key: str):
|
async def remove_content(table, r_filter=None):
|
||||||
cached = cache.get(key)
|
if r_filter is None:
|
||||||
# We want to check here if the key exists in cache, and it was not created more than an hour ago
|
r_filter = {}
|
||||||
# We also want to make sure that if what we're getting in cache has content
|
|
||||||
# If not, lets make sure something didn't go awry, by getting from the database instead
|
|
||||||
|
|
||||||
# If we found this object not cached, cache it
|
|
||||||
if cached is None:
|
|
||||||
value = await _get_content(key)
|
|
||||||
cache[key] = Cache(key)
|
|
||||||
# Otherwise, check our timeout and make sure values is invalid
|
|
||||||
# If either of these are the case, we want to updated our values, and get our current data from the database
|
|
||||||
elif len(cached.values) == 0 or (pendulum.utcnow() - cached.refreshed).hours >= 1:
|
|
||||||
value = await _get_content(key)
|
|
||||||
loop.create_task(cached.update())
|
|
||||||
# Otherwise, we have valid content in cache, use it
|
|
||||||
else:
|
|
||||||
value = cached.values
|
|
||||||
return value
|
|
||||||
|
|
||||||
|
|
||||||
# This is our internal method to get content from the database
|
|
||||||
async def _get_content(key: str):
|
|
||||||
# We need to make sure we're using asyncio
|
|
||||||
r.set_loop_type("asyncio")
|
r.set_loop_type("asyncio")
|
||||||
# Just connect to the database
|
|
||||||
conn = await r.connect(**db_opts)
|
conn = await r.connect(**db_opts)
|
||||||
# We should only ever get one result, so use it if it exists, otherwise return none
|
|
||||||
try:
|
try:
|
||||||
cursor = await r.table(key).run(conn)
|
result = await r.table(table).filter(r_filter).delete().run(conn)
|
||||||
items = list(cursor.items)[0]
|
except r.ReqlOpFailedError:
|
||||||
|
result = {}
|
||||||
|
pass
|
||||||
await conn.close()
|
await conn.close()
|
||||||
|
return result.get('deleted', 0) > 0
|
||||||
|
|
||||||
|
|
||||||
|
async def update_content(table, content, r_filter=None):
|
||||||
|
if r_filter is None:
|
||||||
|
r_filter = {}
|
||||||
|
r.set_loop_type("asyncio")
|
||||||
|
conn = await r.connect(**db_opts)
|
||||||
|
# This method is only for updating content, so if we find that it doesn't exist, just return false
|
||||||
|
try:
|
||||||
|
# Update based on the content and filter passed to us
|
||||||
|
# rethinkdb allows you to do many many things inside of update
|
||||||
|
# This is why we're accepting a variable and using it, whatever it may be, as the query
|
||||||
|
result = await r.table(table).filter(r_filter).update(content).run(conn)
|
||||||
|
except r.ReqlOpFailedError:
|
||||||
|
await conn.close()
|
||||||
|
result = {}
|
||||||
|
await conn.close()
|
||||||
|
return result.get('replaced', 0) > 0 or result.get('unchanged', 0) > 0
|
||||||
|
|
||||||
|
|
||||||
|
async def replace_content(table, content, r_filter=None):
|
||||||
|
# This method is here because .replace and .update can have some different functionalities
|
||||||
|
if r_filter is None:
|
||||||
|
r_filter = {}
|
||||||
|
r.set_loop_type("asyncio")
|
||||||
|
conn = await r.connect(**db_opts)
|
||||||
|
try:
|
||||||
|
result = await r.table(table).filter(r_filter).replace(content).run(conn)
|
||||||
|
except r.ReqlOpFailedError:
|
||||||
|
await conn.close()
|
||||||
|
result = {}
|
||||||
|
await conn.close()
|
||||||
|
return result.get('replaced', 0) > 0 or result.get('unchanged', 0) > 0
|
||||||
|
|
||||||
|
|
||||||
|
async def get_content(key: str, r_filter=None):
|
||||||
|
if r_filter is None:
|
||||||
|
r_filter = {}
|
||||||
|
r.set_loop_type("asyncio")
|
||||||
|
conn = await r.connect(**db_opts)
|
||||||
|
try:
|
||||||
|
cursor = await r.table(key).filter(r_filter).run(conn)
|
||||||
|
content = await _convert_to_list(cursor)
|
||||||
|
if len(content) == 0:
|
||||||
|
content = None
|
||||||
except (IndexError, r.ReqlOpFailedError):
|
except (IndexError, r.ReqlOpFailedError):
|
||||||
|
content = None
|
||||||
await conn.close()
|
await conn.close()
|
||||||
return {}
|
return content
|
||||||
# Rethink db stores an internal id per table, delete this and return the rest
|
|
||||||
del items['id']
|
|
||||||
return items
|
async def _convert_to_list(cursor):
|
||||||
|
# This method is here because atm, AsyncioCursor is not iterable
|
||||||
|
# For our purposes, we want a list, so we need to do this manually
|
||||||
|
cursor_list = []
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
val = await cursor.next()
|
||||||
|
cursor_list.append(val)
|
||||||
|
except r.ReqlCursorEmpty:
|
||||||
|
break
|
||||||
|
return cursor_list
|
||||||
|
|
Loading…
Reference in a new issue