1
0
Fork 0
mirror of synced 2024-06-03 03:04:33 +12:00

Update to work with updated database

This commit is contained in:
phxntxm 2017-03-17 00:21:30 -05:00
parent 5612f6e25a
commit fc1ed00b96
16 changed files with 397 additions and 606 deletions

12
bot.py
View file

@ -50,12 +50,10 @@ async def process_command(ctx):
server = ctx.message.server
command = ctx.command
r_filter = {'command': command.qualified_name}
command_usage = await utils.get_content('command_usage', r_filter)
command_usage = await utils.get_content('command_usage', key=command.qualified_name)
if command_usage is None:
command_usage = {'command': command.qualified_name}
else:
command_usage = command_usage[0]
# 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
command_usage['total_usage'] = total_usage
@ -67,15 +65,15 @@ async def process_command(ctx):
command_usage['member_usage'] = total_member_usage
# Add one to the server's usage for this command
if ctx.message.server is not None:
if ctx.message.guild is not None:
total_server_usage = command_usage.get('server_usage', {})
server_usage = total_server_usage.get(server.id, 0) + 1
total_server_usage[server.id] = server_usage
command_usage['server_usage'] = total_server_usage
# Save all the changes
if not await utils.update_content('command_usage', command_usage, r_filter):
await utils.add_content('command_usage', command_usage, r_filter)
if not await utils.update_content('command_usage', command_usage, command.qualified_name):
await utils.add_content('command_usage', command_usage)
@bot.event

View file

@ -132,10 +132,9 @@ class Core:
await self.bot.say(fmt)
else:
try:
r_filter = pendulum.parse(date)
motd = await utils.get_content('motd', r_filter)
date = motd[0]['date']
motd = motd[0]['motd']
motd = await utils.get_content('motd', str(pendulum.parse(date).date()))
date = motd['date']
motd = motd['motd']
fmt = "Message of the day for {}:\n\n{}".format(date, motd)
await self.bot.say(fmt)
# This one will be hit if we return None for that day
@ -185,7 +184,7 @@ class Core:
cal = calendar.TextCalendar().formatmonth(year, month)
await self.bot.say("```\n{}```".format(cal))
@commands.command()
@commands.command(enabled=False)
@utils.custom_perms(send_messages=True)
async def info(self):
"""This command can be used to print out some of my information"""

View file

@ -1,166 +0,0 @@
import aiohttp
import asyncio
import discord
import traceback
import logging
from discord.ext import commands
from . import utils
log = logging.getLogger()
class Deviantart:
def __init__(self, bot):
self.base_url = "https://www.deviantart.com/api/v1/oauth2/gallery/all"
self.bot = bot
self.headers = {"User-Agent": utils.user_agent}
self.session = aiohttp.ClientSession()
self.token = None
self.params = None
bot.loop.create_task(self.token_task())
bot.loop.create_task(self.post_task())
async def token_task(self):
while True:
expires_in = await self.get_token()
await asyncio.sleep(expires_in)
async def post_task(self):
await asyncio.sleep(5)
# Lets start the task a few seconds after, to ensure our token gets set
while True:
await self.check_posts()
await asyncio.sleep(300)
async def get_token(self):
# We need a token to create requests, it doesn't seem this token goes away
# To get this token, we need to make a request and retrieve that
url = 'https://www.deviantart.com/oauth2/token'
params = {'client_id': utils.da_id,
'client_secret': utils.da_secret,
'grant_type': 'client_credentials'}
data = await utils.request(url, payload=params)
self.token = data.get('access_token', None)
self.params = {'access_token': self.token}
# Make sure we refresh our token, based on when they tell us it expires
# Ensure we call it a few seconds earlier, to give us enough time to set the new token
# If there was an issue, lets call this in a minute again
return data.get('expires_in', 65) - 5
async def check_posts(self):
content = await utils.get_content('deviantart')
# People might sub to the same person, so lets cache every person and their last update
cache = {}
try:
for entry in content:
user = discord.utils.get(self.bot.get_all_members(), id=entry['member_id'])
# If we're sharded, we might not be able to find this user.
# If the bot is not in the server with the member either
if user is None:
continue
params = self.params.copy()
# Now loop through the subscriptions
for da_name in entry['subbed']:
# Check what the last updated content we sent to this user was
# Since we cannot go back in time, if this doesn't match the last uploaded from the user
# Assume we need to notify the user of this post
last_updated_id = entry['last_updated'].get(da_name, None)
# Check if this user has been requested already, if so we don't need to make another request
result = cache.get(da_name, None)
if result is None:
params['username'] = da_name
data = await utils.request(self.base_url, payload=params)
if data is None:
continue
elif not data['results']:
continue
result = data['results'][0]
cache[da_name] = result
# This means that our last update to this user, for this author, is not the same
if last_updated_id != result['deviationid']:
# First lets check if the last updated ID was None, if so...then we haven't alerted them yet
# We don't want to alert them in this case
# We just want to act like the artist's most recent update was the last notified
# So just notify the user if this is not None
if last_updated_id is not None:
fmt = "There has been a new post by an artist you are subscribed to!\n\n" \
"**Title:** {}\n**User:** {}\n**URL:** {}".format(
result['title'],
result['author']['username'],
result['url'])
await self.bot.send_message(user, fmt)
# Now we can update the user's last updated for this DA
# We want to do this whether or not our last if statement was met
r_filter = {'member_id': user.id}
update = {'last_updated': {da_name: result['deviationid']}}
await utils.update_content('deviantart', update, r_filter)
except Exception as e:
tb = traceback.format_exc()
fmt = "{1}\n{0.__class__.__name__}: {0}".format(tb, e)
log.error(fmt)
@commands.group()
@utils.custom_perms(send_messages=True)
async def da(self):
"""This provides a sort of 'RSS' feed for subscribed to artists.
Subscribe to artists, and I will PM you when new posts come out from these artists"""
pass
@da.command(pass_context=True, name='sub', aliases=['add', 'subscribe'])
@utils.custom_perms(send_messages=True)
async def da_sub(self, ctx, *, username):
"""This can be used to add a feed to your notifications.
Provide a username, and when posts are made from this user, you will be notified
EXAMPLE: !da sub MyFavoriteArtistEva<3
RESULT: Notifications of amazing pics c:"""
r_filter = {'member_id': ctx.message.author.id}
content = await utils.get_content('deviantart', r_filter)
# TODO: Ensure the user provided is a real user
if content is None:
entry = {'member_id': ctx.message.author.id, 'subbed': [username], 'last_updated': {}}
await utils.add_content('deviantart', entry, r_filter)
await self.bot.say("You have just subscribed to {}!".format(username))
elif content[0]['subbed'] is None or username not in content[0]['subbed']:
if content[0]['subbed'] is None:
sub_list = [username]
else:
content[0]['subbed'].append(username)
sub_list = content[0]['subbed']
await utils.update_content('deviantart', {'subbed': sub_list}, r_filter)
await self.bot.say("You have just subscribed to {}!".format(username))
else:
await self.bot.say("You are already subscribed to that user!")
@da.command(pass_context=True, name='unsub', aliases=['delete', 'remove', 'unsubscribe'])
@utils.custom_perms(send_messages=True)
async def da_unsub(self, ctx, *, username):
"""This command can be used to unsub from the specified user
EXAMPLE: !da unsub TheArtistWhoBetrayedMe
RESULT: No more pics from that terrible person!"""
r_filter = {'member_id': ctx.message.author.id}
content = await utils.get_content('deviantart', r_filter)
if content is None or content[0]['subbed'] is None:
await self.bot.say("You are not subscribed to anyone at the moment!")
elif username in content[0]['subbed']:
content[0]['subbed'].remove(username)
await utils.update_content('deviantart', {'subbed': content[0]['subbed']}, r_filter)
await self.bot.say("You have just unsubscribed from {}!".format(username))
else:
await self.bot.say("You are not subscribed to that user!")
def setup(bot):
bot.add_cog(Deviantart(bot))

View file

@ -20,6 +20,8 @@ class StatsUpdate:
self.bot.loop.create_task(self.session.close())
async def update(self):
# Currently disabled
return
server_count = 0
data = await config.get_content('bot_data')
@ -48,6 +50,7 @@ class StatsUpdate:
log.info('bots.discord.pw statistics returned {} for {}'.format(resp.status, payload))
async def on_server_join(self, server):
return
r_filter = {'shard_id': config.shard_id}
server_count = len(self.bot.servers)
member_count = len(set(self.bot.get_all_members()))
@ -58,6 +61,7 @@ class StatsUpdate:
self.bot.loop.create_task(self.update())
async def on_server_leave(self, server):
return
r_filter = {'shard_id': config.shard_id}
server_count = len(self.bot.servers)
member_count = len(set(self.bot.get_all_members()))
@ -68,6 +72,7 @@ class StatsUpdate:
self.bot.loop.create_task(self.update())
async def on_ready(self):
return
r_filter = {'shard_id': config.shard_id}
server_count = len(self.bot.servers)
member_count = len(set(self.bot.get_all_members()))
@ -79,16 +84,15 @@ class StatsUpdate:
async def on_member_join(self, member):
server = member.server
r_filter = {'server_id': server.id}
notifications = await config.get_content('user_notifications', r_filter)
server_settings = await config.get_content('server_settings', server.id)
try:
channel_id = notifications[0]['channel_id']
except TypeError:
return
# By default, notifications should be off unless explicitly turned on
if not channel_id:
join_leave_on = server_settings['join_leave']
if join_leave_on:
channel_id = server_settings.get('notification_channel') or member.server.id
else:
return
except (IndexError, TypeError, KeyError):
return
channel = server.get_channel(channel_id)
@ -96,22 +100,19 @@ class StatsUpdate:
async def on_member_remove(self, member):
server = member.server
r_filter = {'server_id': server.id}
notifications = await config.get_content('user_notifications', r_filter)
server_settings = await config.get_content('server_settings', server.id)
try:
channel_id = notifications[0]['channel_id']
except TypeError:
return
# By default, notifications should be off unless explicitly turned on
if not channel_id:
join_leave_on = server_settings['join_leave']
if join_leave_on:
channel_id = server_settings.get('notification_channel') or member.server.id
else:
return
except (IndexError, TypeError, KeyError):
return
channel = server.get_channel(channel_id)
await self.bot.send_message(channel,
"{0} has left the server, I hope it wasn't because of something I said :c".format(
member.display_name))
await self.bot.send_message(channel, "{0} has left the server, I hope it wasn't because of something I said :c".format(member.display_name))
def setup(bot):

View file

@ -258,20 +258,20 @@ class Interaction:
await self.bot.say("Why the heck are you booping me? Get away from me >:c")
return
r_filter = {'member_id': booper.id}
boops = await utils.get_content('boops', r_filter)
key = booper.id
boops = await utils.get_content('boops', key)
if boops is not None:
boops = boops[0]['boops']
boops = boops['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
await utils.update_content('boops', {'boops': boops}, r_filter)
await utils.update_content('boops', {'boops': boops}, key)
else:
entry = {'member_id': booper.id,
'boops': {boopee.id: 1}}
await utils.add_content('boops', entry, r_filter)
await utils.add_content('boops', entry)
amount = 1
fmt = "{0.mention} has just booped {1.mention}{3}! That's {2} times now!"

View file

@ -27,9 +27,8 @@ class Links:
url = "https://www.google.com/search"
# Turn safe filter on or off, based on whether or not this is a nsfw channel
r_filter = {'channel_id': ctx.message.channel.id}
nsfw_channels = await utils.get_content("nsfw_channels", r_filter)
safe = 'off' if nsfw_channels else 'on'
nsfw = await utils.channel_is_nsfw(ctx.message.channel)
safe = 'off' if nsfw else 'on'
params = {'q': query,
'safe': safe,
@ -181,12 +180,11 @@ class Links:
query = ' '.join(value for value in search if not re.search('&?filter_id=[0-9]+', value))
params = {'q': query}
r_filter = {'channel_id': ctx.message.channel.id}
nsfw_channels = await utils.get_content("nsfw_channels", r_filter)
nsfw = await utils.channel_is_nsfw(ctx.message.channel)
# 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
# If the channel is not nsfw, we don't need to do anything, as the default filter blocks explicit
if nsfw_channels is not None:
if nsfw:
params['q'] += ", (explicit OR suggestive)"
params['filter_id'] = 95938
else:
@ -256,12 +254,11 @@ class Links:
# 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....")
r_filter = {'channel_id': ctx.message.channel.id}
nsfw_channels = await utils.get_content("nsfw_channels", r_filter)
nsfw = await utils.channel_is_nsfw(ctx.message.channel)
# e621 by default does not filter explicit content, so tack on
# safe/explicit based on if this channel is nsfw or not
params['tags'] += " rating:explicit" if nsfw_channels else " rating:safe"
params['tags'] += " rating:explicit" if nsfw else " rating:safe"
data = await utils.request(url, payload=params)

View file

@ -119,19 +119,22 @@ class Mod:
except discord.HTTPException:
await self.bot.say("Sorry, I failed to ban that user!")
@commands.command(pass_context=True, no_pm=True)
@commands.command(no_pm=True, aliases=['alerts'], pass_context=True)
@utils.custom_perms(kick_members=True)
async def alerts(self, ctx, channel: discord.Channel):
async def notifications(self, ctx, channel: discord.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
EXAMPLE: !alerts #alerts
RESULT: No more alerts spammed in #general!"""
r_filter = {'server_id': ctx.message.server.id}
entry = {'server_id': ctx.message.server.id,
'channel_id': channel.id}
if not await utils.add_content('server_alerts', entry, r_filter):
await utils.update_content('server_alerts', entry, r_filter)
if str(channel.type) != "text":
await self.bot.say("The notifications channel must be a text channel!")
return
key = ctx.message.server.id
entry = {'server_id': key,
'notification_channel': channel.id}
if not await utils.update_content('server_settings', entry, key):
await utils.add_content('server_settings', entry)
await self.bot.say("I have just changed this server's 'notifications' channel"
"\nAll notifications will now go to `{}`".format(channel))
@ -139,55 +142,71 @@ class Mod:
@utils.custom_perms(kick_members=True)
async def usernotify(self, ctx, on_off: str):
"""This command can be used to set whether or not you want user notificaitons to show
This will save what channel you run this command in, that will be the channel used to send the notification to
Provide on, yes, or true to set it on; otherwise it will be turned off
EXAMPLE: !usernotify on
RESULT: Annying join/leave notifications! Yay!"""
# Join/Leave notifications can be kept separate from normal 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
on_off = ctx.message.channel.id if re.search("(on|yes|true)", on_off.lower()) else None
r_filter = {'server_id': ctx.message.server.id}
entry = {'server_id': ctx.message.server.id,
'channel_id': on_off}
if not await utils.add_content('user_notifications', entry, r_filter):
await utils.update_content('user_notifications', entry, r_filter)
on_off = True if re.search("(on|yes|true)", on_off.lower()) else False
key = ctx.message.server.id
entry = {'server_id': key,
'join_leave': on_off}
if not await utils.update_content('server_settings', entry, key):
await utils.add_content('server_settings', entry)
fmt = "notify" if on_off else "not notify"
await self.bot.say("This server will now {} if someone has joined or left".format(fmt))
@commands.group(pass_context=True)
async def nsfw(self, ctx):
"""Handles adding or removing a channel as a nsfw channel"""
# This command isn't meant to do anything, so just send an error if an invalid subcommand is passed
if ctx.invoked_subcommand is None:
await self.bot.say('Invalid subcommand passed: {0.subcommand_passed}'.format(ctx))
pass
@nsfw.command(name="add", pass_context=True)
@utils.custom_perms(kick_members=True)
async def nsfw_add(self, ctx):
"""Registers this channel as a 'nsfw' channel
EXAMPLE: !nsfw add
RESULT: ;)"""
r_filter = {'channel_id': ctx.message.channel.id}
if await utils.add_content('nsfw_channels', r_filter, r_filter):
await self.bot.say("This channel has just been registered as 'nsfw'! Have fun you naughties ;)")
key = ctx.message.server.id
entry = {'server_id': key,
'nsfw_channels': [ctx.message.channel.id]}
update = {'nsfw_channels': r.row['nsfw_channels'].append(ctx.message.channel.id)}
server_settings = await utils.get_content('server_settings', key)
if server_settings and 'nsfw_channels' in server_settings.keys():
await utils.update_content('server_settings', update, key)
elif server_settings:
await utils.update_content('server_settings', entry, key)
else:
await self.bot.say("This channel is already registered as 'nsfw'!")
await utils.add_content('server_settings', entry)
await self.bot.say("This channel has just been registered as 'nsfw'! Have fun you naughties ;)")
@nsfw.command(name="remove", aliases=["delete"], pass_context=True)
@utils.custom_perms(kick_members=True)
async def nsfw_remove(self, ctx):
"""Removes this channel as a 'nsfw' channel
EXAMPLE: !nsfw remove
RESULT: ;("""
r_filter = {'channel_id': ctx.message.channel.id}
if await utils.remove_content('nsfw_channels', r_filter):
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!")
key = ctx.message.server.id
server_settings = await utils.get_content('server_settings', key)
channel = ctx.message.channel.id
try:
channels = server_settings['nsfw_channels']
if channel in channels:
channels.remove(channel)
entry = {'nsfw_channels': channels}
await utils.update_content('server_settings', entry, key)
await self.bot.say("This channel has just been unregistered as a nsfw channel")
return
except (TypeError, IndexError):
pass
await self.bot.say("This channel is not registered as a 'nsfw' channel!")
@commands.command(pass_context=True)
@utils.custom_perms(kick_members=True)
@ -208,7 +227,6 @@ class Mod:
async def perms(self, ctx, *, command: str = None):
"""This command can be used to print the current allowed permissions on a specific command
This supports groups as well as subcommands; pass no argument to print a list of available permissions
EXAMPLE: !perms help RESULT: Hopefully a result saying you just need send_messages permissions; otherwise lol
this server's admin doesn't like me """
if command is None:
@ -216,18 +234,18 @@ class Mod:
"Valid permissions are: ```\n{}```".format("\n".join("{}".format(i) for i in valid_perms)))
return
r_filter = {'server_id': ctx.message.server.id}
server_perms = await utils.get_content('custom_permissions', r_filter)
try:
server_perms = server_perms[0]
except TypeError:
server_perms = {}
cmd = self.find_command(command)
cmd = utils.find_command(self.bot, command)
if cmd is None:
await self.bot.say("That is not a valid command!")
return
server_settings = await utils.get_content('server_settings', ctx.message.server.id)
try:
server_perms = server_settings['permissions']
except (TypeError, IndexError, KeyError):
server_perms = {}
perms_value = server_perms.get(cmd.qualified_name)
if perms_value is None:
# If we don't find custom permissions, get the required permission for a command
@ -238,7 +256,7 @@ class Mod:
# Loop through and check if there is a check called is_owner
# If we loop through and don't find one, this means that the only other choice is to be
# Able to manage the server (for the utils on perm commands)
for func in cmd.utils:
for func in cmd.checks:
if "is_owner" in func.__qualname__:
await self.bot.say("You need to own the bot to run this command")
return
@ -265,7 +283,6 @@ class Mod:
"""Sets up custom permissions on the provided command
Format must be 'perms add <command> <permission>'
If you want to open the command to everyone, provide 'none' as the permission
EXAMPLE: !perms add skip ban_members
RESULT: No more random people voting to skip a song"""
@ -278,6 +295,13 @@ class Mod:
"`perms add <command> <permission>`")
return
cmd = utils.find_command(self.bot, command)
if cmd is None:
await self.bot.say(
"That command does not exist! You can't have custom permissions on a non-existant command....")
return
# If a user can run a command, they have to have send_messages permissions; so use this as the base
if permissions.lower() == "none":
permissions = "send_messages"
@ -293,35 +317,22 @@ class Mod:
return
perm_value = perm_obj.value
cmd = self.find_command(command)
if cmd is None:
await self.bot.say(
"That command does not exist! You can't have custom permissions on a non-existant command....")
return
# Two cases I use should never have custom permissions setup on them, is_owner for obvious reasons
# The other case is if I'm using the default has_permissions case
# Which means I do not want to check custom permissions at all
# Currently the second case is only on adding and removing permissions, to avoid abuse on these
for check in cmd.checks:
if "is_owner" == check.__name__ or re.search("has_permissions", str(check)) is not None:
if "is_owner" == check.__name__ or "has_permissions" in str(check):
await self.bot.say("This command cannot have custom permissions setup!")
return
r_filter = {'server_id': ctx.message.server.id}
entry = {'server_id': ctx.message.server.id,
cmd.qualified_name: perm_value}
key = ctx.message.server.id
entry = {'server_id': key,
'permissions': {cmd.qualified_name: perm_value}}
# 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 utils.update_content('custom_permissions', entry, r_filter):
await utils.add_content('custom_permissions', entry, r_filter)
if not await utils.update_content('server_settings', entry, key):
await utils.add_content('server_settings', entry)
# Same case as prefixes, for now, trigger a manual update
self.bot.loop.create_task(utils.cache['custom_permissions'].update())
await self.bot.say("I have just added your custom permissions; "
"you now need to have `{}` permissions to use the command `{}`".format(permissions, command))
@ -333,20 +344,17 @@ class Mod:
EXAMPLE: !perms remove play
RESULT: Freedom!"""
cmd = self.find_command(command)
cmd = utils.find_command(self.bot, command)
if cmd is None:
await self.bot.say(
"That command does not exist! You can't have custom permissions on a non-existant command....")
return
r_filter = {'server_id': ctx.message.server.id}
await utils.replace_content('custom_permissions', r.row.without(cmd.qualified_name), r_filter)
update = {'permissions': {cmd.qualified_name: None}}
await utils.update_content('server_settings', update, ctx.message.server.id)
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(utils.cache['custom_permissions'].update())
@commands.command(pass_context=True, no_pm=True)
@utils.custom_perms(manage_server=True)
async def prefix(self, ctx, *, prefix: str):
@ -354,15 +362,15 @@ class Mod:
EXAMPLE: !prefix new_prefix
RESULT: You probably screwing it up and not realizing you now need to do new_prefixprefix"""
r_filter = {'server_id': ctx.message.server.id}
key = ctx.message.server.id
if prefix.lower().strip() == "none":
prefix = None
entry = {'server_id': ctx.message.server.id,
entry = {'server_id': key,
'prefix': prefix}
if not await utils.add_content('prefixes', entry, r_filter):
await utils.update_content('prefixes', entry, r_filter)
if not await utils.update_content('server_settings', entry, key):
await utils.add_content('server_settings', entry)
if prefix is None:
fmt = "I have just cleared your custom prefix, the default prefix will have to be used now"
@ -390,7 +398,7 @@ class Mod:
@commands.command(pass_context=True, no_pm=True)
@utils.custom_perms(manage_messages=True)
async def prune(self, ctx, limit = None):
async def prune(self, ctx, limit=None):
"""This command can be used to prune messages from certain members
Mention any user you want to prune messages from; if no members are mentioned, the messages removed will be mine
If no limit is provided, then 100 will be used. This is also the max limit we can use
@ -458,14 +466,10 @@ class Mod:
EXAMPLE: !rules 5
RESULT: Rule 5 is printed"""
r_filter = {'server_id': ctx.message.server.id}
rules = await utils.get_content('rules', r_filter)
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...")
return
if len(rules) == 0:
server_settings = await utils.get_content('server_settings', ctx.message.server.id)
rules = server_settings.get('rules')
if not rules or len(rules) == 0:
await self.bot.say("This server currently has no rules on it! I see you like to live dangerously...")
return
@ -491,12 +495,18 @@ class Mod:
EXAMPLE: !rules add No fun allowed in this server >:c
RESULT: No more fun...unless they break the rules!"""
r_filter = {'server_id': ctx.message.server.id}
entry = {'server_id': ctx.message.server.id,
key = ctx.message.server.id
entry = {'server_id': key,
'rules': [rule]}
update = {'rules': r.row['rules'].append(rule)}
if not await utils.update_content('rules', update, r_filter):
await utils.add_content('rules', entry, r_filter)
server_settings = await utils.get_content('server_settings', key)
if server_settings and 'rules' in server_settings.keys():
await utils.update_content('server_settings', update, key)
elif server_settings:
await utils.update_content('server_settings', entry, key)
else:
await utils.add_content('server_settings', entry)
await self.bot.say("I have just saved your new rule, use the rules command to view this server's current rules")
@ -508,9 +518,8 @@ class Mod:
EXAMPLE: !rules delete 5
RESULT: Freedom from opression!"""
r_filter = {'server_id': ctx.message.server.id}
update = {'rules': r.row['rules'].delete_at(rule - 1)}
if not await utils.update_content('rules', update, r_filter):
if not await utils.update_content('server_settings', update, ctx.message.server.id):
await self.bot.say("That is not a valid rule number, try running the command again.")
else:
await self.bot.say("I have just removed that rule from your list of rules!")

View file

@ -1,13 +1,9 @@
from .utils import config
from .utils import checks
from .utils import images
from . import utils
from discord.ext import commands
import discord
import aiohttp
base_url = "https://api.owapi.net/api/v3/u/"
BASE_URL = "https://api.owapi.net/api/v3/u/"
# This is a list of the possible things that we may want to retrieve from the stats
# The API returns something if it exists, and leaves it out of the data returned entirely if it does not
# For example if you have not won with a character, wins will not exist in the list
@ -15,7 +11,6 @@ base_url = "https://api.owapi.net/api/v3/u/"
check_g_stats = ["eliminations", "deaths", 'kpd', 'wins', 'losses', 'time_played',
'cards', 'damage_done', 'healing_done', 'multikills']
check_o_stats = ['wins']
MAX_RETRIES = 5
class Overwatch:
@ -23,29 +18,6 @@ class Overwatch:
def __init__(self, bot):
self.bot = bot
self.headers = {"User-Agent": config.user_agent}
self.session = aiohttp.ClientSession()
async def _request(self, payload, endpoint):
"""Handles requesting to the API"""
# Format the URL we'll need based on the base_url, and the endpoint we want to hit
url = "{}{}".format(base_url, endpoint)
# Attempt to connect up to our max retries
for x in range(MAX_RETRIES):
try:
async with aiohttp.ClientSession(headers=self.headers) as session:
async with session.get(url, params=payload) as r:
# If we failed to connect, attempt again
if r.status != 200:
continue
data = await r.json()
return data
# If any error happened when making the request, attempt again
except:
continue
@commands.group(no_pm=True)
async def ow(self):
@ -56,28 +28,28 @@ class Overwatch:
pass
@ow.command(name="stats", pass_context=True)
@checks.custom_perms(send_messages=True)
@utils.custom_perms(send_messages=True)
async def ow_stats(self, ctx, user: discord.Member = None, hero: str = ""):
"""Prints out a basic overview of a member's stats
Provide a hero after the member to get stats for that specific hero
EXAMPLE: !ow stats @OtherPerson Junkrat
RESULT: Whether or not you should unfriend this person because they're a dirty rat"""
user = user or ctx.message.author
r_filter = {'member_id': user.id}
ow_stats = await config.get_content('overwatch', r_filter)
ow_stats = await utils.get_content('overwatch', user.id)
if ow_stats is None:
await self.bot.say("I do not have this user's battletag saved!")
return
# 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....")
bt = ow_stats[0]['battletag']
bt = ow_stats['battletag']
if hero == "":
# If no hero was provided, we just want the base stats for a player
data = await self._request(None, "{}/stats".format(bt))
url = BASE_URL + "{}/stats".format(bt)
data = await utils.request(url)
region = [x for x in data.keys() if data[x] is not None][0]
stats = data[region]['stats']['quickplay']
@ -86,8 +58,8 @@ class Overwatch:
else:
# If there was a hero provided, search for a user's data on that hero
hero = hero.lower().replace('-', '')
endpoint = "{}/heroes".format(bt)
data = await self._request(None, endpoint)
url = BASE_URL + "{}/heroes".format(bt)
data = await utils.request(url)
region = [x for x in data.keys() if data[x] is not None][0]
stats = data[region]['heroes']['stats']['quickplay'].get(hero)
@ -104,52 +76,51 @@ class Overwatch:
for k, r in stats['hero_stats'].items():
output_data.append((k.title().replace("_", " "), r))
try:
banner = await images.create_banner(user, "Overwatch", output_data)
banner = await utils.create_banner(user, "Overwatch", output_data)
await self.bot.upload(banner)
except (FileNotFoundError, discord.Forbidden):
fmt = "\n".join("{}: {}".format(k, r) for k, r in output_data)
await self.bot.say("Overwatch stats for {}: ```py\n{}```".format(user.name, fmt))
@ow.command(pass_context=True, name="add")
@checks.custom_perms(send_messages=True)
@utils.custom_perms(send_messages=True)
async def add(self, ctx, bt: str):
"""Saves your battletag for looking up information
EXAMPLE: !ow add Username#1234
RESULT: Your battletag is now saved"""
await ctx.message.channel.trigger_typing()
# Battletags are normally provided like name#id
# However the API needs this to be a -, so repliace # with - if it exists
bt = bt.replace("#", "-")
r_filter = {'member_id': ctx.message.author.id}
key = ctx.message.author.id
# 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....")
# All we're doing here is ensuring that the status is 200 when looking up someone's general information
# If it's not, let them know exactly how to format their tag
endpoint = "{}/stats".format(bt)
data = await self._request(None, endpoint)
url = BASE_URL + "{}/stats".format(bt)
data = await utils.request(url)
if data is None:
await self.bot.say("Profile does not exist! Battletags are picky, "
"format needs to be `user#xxxx`. Capitalization matters")
return
await self.bot.say("Profile does not exist! Battletags are picky, "
"format needs to be `user#xxxx`. Capitalization matters")
return
# Now just save the battletag
entry = {'member_id': ctx.message.author.id, 'battletag': bt}
entry = {'member_id': key, 'battletag': bt}
update = {'battletag': bt}
# 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)
if not await utils.add_content('overwatch', entry):
await utils.update_content('overwatch', update, key)
await self.bot.say("I have just saved your battletag {}".format(ctx.message.author.mention))
@ow.command(pass_context=True, name="delete", aliases=['remove'])
@checks.custom_perms(send_messages=True)
@utils.custom_perms(send_messages=True)
async def delete(self, ctx):
"""Removes your battletag from the records
EXAMPLE: !ow delete
RESULT: Your battletag is no longer saved"""
r_filter = {'member_id': ctx.message.author.id}
if await config.remove_content('overwatch', r_filter):
if await utils.remove_content('overwatch', ctx.message.author.id):
await self.bot.say("I no longer have your battletag saved {}".format(ctx.message.author.mention))
else:
await self.bot.say("I don't even have your battletag saved {}".format(ctx.message.author.mention))

View file

@ -22,12 +22,12 @@ class Owner:
async def motd_push(self, *, message):
"""Used to push a new message to the message of the day"""
date = pendulum.utcnow().to_date_string()
r_filter = {'date': date}
key = date
entry = {'motd': message, 'date': date}
# Try to add this, if there's an entry for that date, lets update it to make sure only one motd is sent a day
# I should be managing this myself, more than one should not be sent in a day
if await utils.add_content('motd', entry, r_filter):
await utils.update_content('motd', entry, r_filter)
if await utils.add_content('motd', entry):
await utils.update_content('motd', entry, key)
await self.bot.say("New motd update for {}!".format(date))
@commands.command(pass_context=True)

View file

@ -11,159 +11,143 @@ from discord.ext import commands
from . import utils
log = logging.getLogger()
base_url = 'https://ptvappapi.picarto.tv'
BASE_URL = 'https://ptvappapi.picarto.tv'
# This is a public key for use, I don't care if this is seen
key = '03e26294-b793-11e5-9a41-005056984bd4'
async def online_users():
try:
# Someone from picarto contacted me and told me their database queries are odd
# It is more efficent on their end to make a query for all online users, and base checks off that
# In place of requesting for /channel and checking if that is online currently, for each channel
# This method is in place to just return all online_users
url = '{}/online/all?key={}'.format(base_url, key)
with aiohttp.ClientSession(headers={"User-Agent": utils.user_agent}) as s:
async with s.get(url) as response:
return await response.json()
except:
return {}
def check_online(online_channels, channel):
# online_channels is the dictionary of all users online currently
# And channel is the name we are checking against that
# This creates a list of all users that match this channel name (should only ever be 1)
# And returns True as long as it is more than 0
matches = [stream for stream in online_channels if stream['channel_name'].lower() == channel.lower()]
return len(matches) > 0
api_key = '03e26294-b793-11e5-9a41-005056984bd4'
class Picarto:
def __init__(self, bot):
self.bot = bot
self.headers = {"User-Agent": utils.user_agent}
self.session = aiohttp.ClientSession()
async def get_online_users(self):
# This method is in place to just return all online users so we can compare against it
url = BASE_URL + '/online/all'
payload = {'key': api_key}
self.online_channels = await utils.request(url, payload=payload)
def channel_online(self, channel):
# Channel is the name we are checking against that
# This creates a list of all users that match this channel name (should only ever be 1)
# And returns True as long as it is more than 0
channel = re.search("(?<=picarto.tv/)(.*)", channel).group(1)
matches = [stream for stream in self.online_channels if stream['channel_name'].lower() == channel.lower()]
return len(matches) > 0
async def check_channels(self):
await self.bot.wait_until_ready()
# This is a loop that runs every 30 seconds, checking if anyone has gone online
try:
while not self.bot.is_closed:
r_filter = {'notifications_on': 1}
picarto = await utils.get_content('picarto', r_filter)
# Get all online users before looping, so that only one request is needed
online_users_list = await online_users()
old_online_users = {data['member_id']: data for data in picarto if data['live']}
old_offline_users = {data['member_id']: data for data in picarto if not data['live']}
for m_id, result in old_offline_users.items():
# Get their url and their user based on that url
url = result['picarto_url']
user = re.search("(?<=picarto.tv/)(.*)", url).group(1)
# Check if they are online right now
if check_online(online_users_list, user):
for server_id in result['servers']:
# Get the channel to send the message to, based on the saved alert's channel
server = self.bot.get_server(server_id)
await self.get_online_users()
picarto = await utils.filter_content('picarto', {'notifications_on': 1})
for data in picarto:
m_id = data['member_id']
url = data['picarto_url']
# Check if they are online
online = self.channel_online(url)
# If they're currently online, but saved as not then we'll send our notification
if online and data['live'] == 0:
for s_id in data['servers']:
server = self.bot.get_server(s_id)
if server is None:
continue
server_alerts = await utils.get_content('server_alerts', {'server_id': server_id})
try:
channel_id = server_alerts[0]['channel_id']
except (IndexError, TypeError):
channel_id = server_id
channel = self.bot.get_channel(channel_id)
# Get the member that has just gone live
member = discord.utils.get(server.members, id=m_id)
member = server.get_member(m_id)
if member is None:
continue
fmt = "{} has just gone live! View their stream at {}".format(member.display_name, url)
await self.bot.send_message(channel, fmt)
await utils.update_content('picarto', {'live': 1}, {'member_id': m_id})
for m_id, result in old_online_users.items():
# Get their url and their user based on that url
url = result['picarto_url']
user = re.search("(?<=picarto.tv/)(.*)", url).group(1)
# Check if they are online right now
if not check_online(online_users_list, user):
for server_id in result['servers']:
# Get the channel to send the message to, based on the saved alert's channel
server = self.bot.get_server(server_id)
server_settings = await utils.get_content('server_settings', s_id)
if server_settings is not None:
channel_id = server_settings.get('notification_channel', s_id)
else:
channel_id = s_id
channel = server.get_channel(channel_id)
await self.bot.send_message(channel, "{} has just gone live! View their stream at <{}>".format(member.display_name, data['picarto_url']))
self.bot.loop.create_task(utils.update_content('picarto', {'live': 1}, m_id))
elif not online and data['live'] == 1:
for s_id in data['servers']:
server = self.bot.get_server(s_id)
if server is None:
continue
server_alerts = await utils.get_content('server_alerts', {'server_id': server_id})
try:
channel_id = server_alerts[0]['channel_id']
except (IndexError, TypeError):
channel_id = server_id
channel = self.bot.get_channel(channel_id)
# Get the member that has just gone live
member = discord.utils.get(server.members, id=m_id)
fmt = "{} has just gone offline! Catch them next time they stream at {}".format(
member.display_name, url)
await self.bot.send_message(channel, fmt)
await utils.update_content('picarto', {'live': 0}, {'member_id': m_id})
member = server.get_member(m_id)
if member is None:
continue
server_settings = await utils.get_content('server_settings', s_id)
if server_settings is not None:
channel_id = server_settings.get('notification_channel', s_id)
else:
channel_id = s_id
channel = server.get_channel(channel_id)
await self.bot.send_message(channel, "{} has just gone offline! View their stream next time at <{}>".format(member.display_name, data['picarto_url']))
self.bot.loop.create_task(utils.update_content('picarto', {'live': 0}, m_id))
await asyncio.sleep(30)
except Exception as e:
tb = traceback.format_exc()
fmt = "{1}\n{0.__class__.__name__}: {0}".format(tb, e)
log.error(fmt)
@commands.group(pass_context=True, invoke_without_command=True, no_pm=True)
@commands.group(invoke_without_command=True, no_pm=True, pass_context=True)
@utils.custom_perms(send_messages=True)
async def picarto(self, ctx, member: discord.Member = None):
"""This command can be used to view Picarto stats about a certain member
EXAMPLE: !picarto @otherPerson
RESULT: Info about their picarto stream"""
# If member is not given, base information on the author
member = member or ctx.message.author
r_filter = {'member_id': member.id}
picarto_entry = await utils.get_content('picarto', r_filter)
picarto_entry = await utils.get_content('picarto', member.id)
if picarto_entry is None:
await self.bot.say("That user does not have a picarto url setup!")
return
member_url = picarto_entry[0]['picarto_url']
member_url = picarto_entry['picarto_url']
# 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)
url = '{}/channel/{}?key={}'.format(base_url, stream, key)
async with self.session.get(url, headers=self.headers) as response:
data = await response.json()
url = BASE_URL + '/channel/{}'.format(stream)
payload = {'key': api_key}
data = await utils.request(url, payload=payload)
if data is None:
await self.bot.say("I couldn't connect to Picarto!")
return
# 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',
'content_type']
# Using title and replace to provide a nice way to print the data
fmt = "\n".join(
"{}: {}".format(i.title().replace("_", " "), result) for i, result in data.items() if i in things_to_print)
embed = discord.Embed(title='{}\'s Picarto'.format(data['channel']), url=url)
if data['avatar_url']:
embed.set_thumbnail(url=data['avatar_url'])
for i, result in data.items():
if i in things_to_print and str(result):
i = i.title().replace('_', ' ')
embed.add_field(name=i, value=str(result))
# 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
social_links = data.get('social_urls')
if social_links:
fmt2 = "\n".join(
"\t{}: {}".format(i.title().replace("_", " "), result) for i, result in social_links.items())
fmt = "{}\nSocial Links:\n{}".format(fmt, fmt2)
await self.bot.say("Picarto stats for {}: ```\n{}```".format(member.display_name, fmt))
for i, result in data['social_urls'].items():
embed.add_field(name=i.title(), value=result)
@picarto.command(name='add', pass_context=True, no_pm=True)
await self.bot.say(embed=embed)
@picarto.command(name='add', no_pm=True, pass_context=True)
@utils.custom_perms(send_messages=True)
async def add_picarto_url(self, ctx, url: str):
"""Saves your user's picarto URL
EXAMPLE: !picarto add MyUsername
RESULT: Your picarto stream is saved, and notifications should go to this server"""
# This uses a lookbehind to check if picarto.tv exists in the url given
# If it does, it matches picarto.tv/user and sets the url as that
# Then (in the else) add https://www. to that
# Otherwise if it doesn't match, we'll hit an AttributeError due to .group(0)
# This means that the url was just given as a user (or something complete invalid)
# So set URL as https://www.picarto.tv/[url]
# Even if this was invalid such as https://www.picarto.tv/twitch.tv/user
# Even if this was invalid such as https://www.picarto.tv/picarto.tv/user
# For example, our next check handles that
try:
url = re.search("((?<=://)?picarto.tv/)+(.*)", url).group(0)
@ -171,43 +155,42 @@ class Picarto:
url = "https://www.picarto.tv/{}".format(url)
else:
url = "https://www.{}".format(url)
channel = re.search("https://www.picarto.tv/(.*)", url).group(1)
api_url = BASE_URL + '/channel/{}'.format(channel)
payload = {'key': api_key}
api_url = '{}/channel/{}?key={}'.format(base_url, re.search("https://www.picarto.tv/(.*)", url).group(1), key)
data = await utils.request(api_url, payload=payload)
if not data:
await self.bot.say("That Picarto user does not exist! What would be the point of adding a nonexistant "
"Picarto user? Silly")
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 response:
if not response.status == 200:
await self.bot.say("That Picarto user does not exist! "
"What would be the point of adding a nonexistant Picarto user? Silly")
return
r_filter = {'member_id': ctx.message.author.id}
key = ctx.message.author.id
entry = {'picarto_url': url,
'servers': [ctx.message.server.id],
'notifications_on': 1,
'live': 0,
'member_id': ctx.message.author.id}
if await utils.add_content('picarto', entry, r_filter):
'member_id': key}
if await utils.add_content('picarto', entry):
await self.bot.say(
"I have just saved your Picarto URL {}, this server will now be notified when you go live".format(
ctx.message.author.mention))
else:
await utils.update_content('picarto', {'picarto_url': url}, r_filter)
await utils.update_content('picarto', {'picarto_url': url}, key)
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'], no_pm=True, pass_context=True)
@utils.custom_perms(send_messages=True)
async def remove_picarto_url(self, ctx):
"""Removes your picarto URL"""
r_filter = {'member_id': ctx.message.author.id}
if await utils.remove_content('picarto', r_filter):
if await utils.remove_content('picarto', ctx.message.author.id):
await self.bot.say("I am no longer saving your picarto URL {}".format(ctx.message.author.mention))
else:
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, ctx.prefix))
@picarto.group(pass_context=True, no_pm=True, invoke_without_command=True)
@picarto.group(no_pm=True, invoke_without_command=True, pass_context=True)
@utils.custom_perms(send_messages=True)
async def notify(self, ctx):
"""This can be used to turn picarto notifications on or off
@ -215,29 +198,27 @@ class Picarto:
EXAMPLE: !picarto notify
RESULT: This server will now be notified of you going live"""
r_filter = {'member_id': ctx.message.author.id}
result = await utils.get_content('picarto', r_filter)
key = ctx.message.author.id
result = await utils.get_content('picarto', key)
# Check if this user is saved at all
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))
# Then check if this server is already added as one to notify in
elif ctx.message.server.id in result[0]['servers']:
elif ctx.message.server.id in result['servers']:
await self.bot.say("I am already set to notify in this server...")
else:
await utils.update_content('picarto', {'servers': r.row['servers'].append(ctx.message.server.id)},
r_filter)
await utils.update_content('picarto', {'servers': r.row['servers'].append(ctx.message.server.id)}, key)
@notify.command(name='on', aliases=['start,yes'], pass_context=True, no_pm=True)
@notify.command(name='on', aliases=['start,yes'], no_pm=True, pass_context=True)
@utils.custom_perms(send_messages=True)
async def notify_on(self, ctx):
"""Turns picarto notifications on
EXAMPLE: !picarto notify on
RESULT: Notifications are sent when you go live"""
r_filter = {'member_id': ctx.message.author.id}
await utils.update_content('picarto', {'notifications_on': 1}, r_filter)
await utils.update_content('picarto', {'notifications_on': 1}, ctx.message.author.id)
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))
@ -248,8 +229,7 @@ class Picarto:
EXAMPLE: !picarto notify off
RESULT: No more notifications sent when you go live"""
r_filter = {'member_id': ctx.message.author.id}
await utils.update_content('picarto', {'notifications_on': 0}, r_filter)
await utils.update_content('picarto', {'notifications_on': 0}, ctx.message.author.id)
await self.bot.say(
"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(
@ -259,4 +239,4 @@ class Picarto:
def setup(bot):
p = Picarto(bot)
bot.loop.create_task(p.check_channels())
bot.add_cog(Picarto(bot))
bot.add_cog(Picarto(bot))

View file

@ -1,7 +1,7 @@
from discord.ext import commands
from .utils import config
from .utils import checks
from . import utils
import discord
import random
import pendulum
import re
@ -27,7 +27,7 @@ class Raffle:
async def check_raffles(self):
# This is used to periodically check the current raffles, and see if they have ended yet
# If the raffle has ended, we'll pick a winner from the entrants
raffles = await config.get_content('raffles')
raffles = await utils.get_content('raffles')
if raffles is None:
return
@ -73,22 +73,25 @@ class Raffle:
# No matter which one of these matches were met, the raffle has ended and we want to remove it
# We don't have to wait for it however, so create a task for it
r_filter = {'id': raffle_id}
self.bot.loop.create_task(config.remove_content('raffles', r_filter))
self.bot.loop.create_task(utils.remove_content('raffles', raffle_id ))
server_settings = await utils.get_content('server_settings', str(server.id))
channel_id = server_settings.get('notification_channel', server.id)
channel = self.bot.get_channel(channel_id)
try:
await self.bot.send_message(server, fmt)
await channel.send(fmt)
except discord.Forbidden:
pass
@commands.command(pass_context=True, no_pm=True)
@checks.custom_perms(send_messages=True)
@utils.custom_perms(send_messages=True)
async def raffles(self, ctx):
"""Used to print the current running raffles on the server
EXAMPLE: !raffles
RESULT: A list of the raffles setup on this server"""
r_filter = {'server_id': ctx.message.server.id}
raffles = await config.get_content('raffles', r_filter)
raffles = await utils.filter_content('raffles', r_filter)
if raffles is None:
await self.bot.say("There are currently no raffles setup on this server!")
return
@ -101,7 +104,7 @@ class Raffle:
await self.bot.say(fmt)
@commands.group(pass_context=True, no_pm=True, invoke_without_command=True)
@checks.custom_perms(send_messages=True)
@utils.custom_perms(send_messages=True)
async def raffle(self, ctx, raffle_id: int = 0):
"""Used to enter a raffle running on this server
If there is more than one raffle running, provide an ID of the raffle you want to enter
@ -113,7 +116,7 @@ class Raffle:
r_filter = {'server_id': ctx.message.server.id}
author = ctx.message.author
raffles = await config.get_content('raffles', r_filter)
raffles = await utils.filter_content('raffles', r_filter)
if raffles is None:
await self.bot.say("There are currently no raffles setup on this server!")
return
@ -130,9 +133,8 @@ class Raffle:
entrants.append(author.id)
# Since we have no good thing to filter things off of, lets use the internal rethinkdb id
r_filter = {'id': raffles[0]['id']}
update = {'entrants': entrants}
await config.update_content('raffles', update, r_filter)
await utils.update_content('raffles', update, raffles[0]['id'])
await self.bot.say("{} you have just entered the raffle!".format(author.mention))
# Otherwise, make sure the author gave a valid raffle_id
elif raffle_id in range(raffle_count - 1):
@ -144,10 +146,8 @@ class Raffle:
return
entrants.append(author.id)
# Since we have no good thing to filter things off of, lets use the internal rethinkdb id
r_filter = {'id': raffles[raffle_id]['id']}
update = {'entrants': entrants}
await config.update_content('raffles', update, r_filter)
await utils.update_content('raffles', update, raffles[raffle_id]['id'])
await self.bot.say("{} you have just entered the raffle!".format(author.mention))
else:
fmt = "Please provide a valid raffle ID, as there are more than one setup on the server! " \
@ -156,7 +156,7 @@ class Raffle:
await self.bot.say(fmt)
@raffle.command(pass_context=True, no_pm=True, name='create', aliases=['start', 'begin', 'add'])
@checks.custom_perms(kick_members=True)
@utils.custom_perms(kick_members=True)
async def raffle_create(self, ctx):
"""This is used in order to create a new server raffle
@ -231,7 +231,7 @@ class Raffle:
'server_id': server.id}
# We don't want to pass a filter to this, because we can have multiple raffles per server
await config.add_content('raffles', entry)
await utils.add_content('raffles', entry)
await self.bot.say("I have just saved your new raffle!")

View file

@ -74,16 +74,13 @@ class Stats:
EXAMPLE: !command stats play
RESULT: The realization that this is the only reason people use me ;-;"""
cmd = self.find_command(command)
cmd = self.bot.get_command(command)
if cmd is None:
await self.bot.say("`{}` is not a valid command".format(command))
return
r_filter = {'command': cmd.qualified_name}
command_stats = await utils.get_content('command_usage', r_filter)
try:
command_stats = command_stats[0]
except TypeError:
command_stats = await utils.get_content('command_usage', cmd.qualified_name)
if command_stats is None:
await self.bot.say("That command has never been used! You know I worked hard on that! :c")
return
@ -97,7 +94,7 @@ class Stats:
("Your Usage", member_usage),
("This Server's Usage", server_usage)]
banner = await utils.create_banner(ctx.message.author, "Command Stats", data)
await self.bot.upload(banner)
await self.bot.upload(file=banner)
except (FileNotFoundError, discord.Forbidden):
fmt = "The command {} has been used a total of {} times\n" \
"{} times on this server\n" \
@ -158,14 +155,13 @@ class Stats:
EXAMPLE: !mostboops
RESULT: You've booped @OtherPerson 351253897120935712093572193057310298 times!"""
r_filter = {'member_id': ctx.message.author.id}
boops = await utils.get_content('boops', r_filter)
boops = await utils.get_content('boops', ctx.message.author.id)
if boops is None:
await self.bot.say("You have not booped anyone {} Why the heck not...?".format(ctx.message.author.mention))
return
# Just to make this easier, just pay attention to the boops data, now that we have the right entry
boops = boops[0]['boops']
boops = boops['boops']
# 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]
@ -189,14 +185,13 @@ class Stats:
EXAMPLE: !listboops
RESULT: The list of your booped members!"""
r_filter = {'member_id': ctx.message.author.id}
boops = await utils.get_content('boops', r_filter)
boops = await utils.get_content('boops', ctx.message.author.id)
if boops is None:
await self.bot.say("You have not booped anyone {} Why the heck not...?".format(ctx.message.author.mention))
return
# Just to make this easier, just pay attention to the boops data, now that we have the right entry
boops = boops[0]['boops']
boops = boops['boops']
# Same concept as the mostboops method
server_member_ids = [member.id for member in ctx.message.server.members]

View file

@ -40,8 +40,7 @@ class Strawpoll:
RESULT: A list of all polls setup on this server"""
# Strawpolls cannot be 'deleted' so to handle whether a poll is running or not on a server
# Just save the poll, which can then be removed when it should not be "running" anymore
r_filter = {'server_id': ctx.message.server.id}
polls = await config.get_content('strawpolls', r_filter)
polls = await config.get_content('strawpolls', ctx.message.server.id)
# Check if there are any polls setup on this server
try:
polls = polls[0]['polls']

View file

@ -17,7 +17,7 @@ class Tags:
EXAMPLE: !tags
RESULT: All tags setup on this server"""
tags = await config.get_content('tags', {'server_id': ctx.message.server.id})
tags = await config.get_content('tags', ctx.message.server.id)
# Simple generator that adds a tag to the list to print, if the tag is for this server
try:
fmt = "\n".join("{}".format(tag['tag']) for tag in tags)
@ -72,13 +72,10 @@ class Tags:
entry = {'server_id': ctx.message.server.id, 'tag': tag, 'result': tag_result}
r_filter = lambda row: (row['server_id'] == ctx.message.server.id) & (row['tag'] == tag)
# Try to create new entry first, if that fails (it already exists) then we update it
if await config.add_content('tags', entry, r_filter):
await self.bot.say(
"I have just added the tag `{0}`! You can call this tag by entering !tag {0}".format(tag))
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))
if not await config.update_content('tags', entry, r_filter):
await config.add_content('tags', entry)
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)
@checks.custom_perms(kick_members=True)
@ -88,12 +85,14 @@ class Tags:
EXAMPLE: !tag delete stupid_tag
RESULT: Deletes that stupid tag"""
r_filter = lambda row: (row['server_id'] == ctx.message.server.id) & (row['tag'] == tag)
if await config.remove_content('tags', r_filter):
await self.bot.say('I have just removed the tag `{}`'.format(tag))
await self.bot.say("Temporarily disabled")
# TODO: Fix tags, this will inherently fix this method
"""r_filter = lambda row: (row['server_id'] == ctx.message.guild.id) & (row['tag'] == tag)
if await utils.remove_content('tags', r_filter):
await ctx.send('I have just removed the tag `{}`'.format(tag))
else:
await self.bot.say(
"The tag {} does not exist! You can't remove something if it doesn't exist...".format(tag))
await ctx.send(
"The tag {} does not exist! You can't remove something if it doesn't exist...".format(tag))"""
def setup(bot):

View file

@ -5,7 +5,6 @@ from . import utils
import aiohttp
import asyncio
import discord
import json
import re
import rethinkdb as r
import traceback
@ -23,11 +22,10 @@ class Twitch:
self.bot = bot
self.key = utils.twitch_key
self.params = {'client_id': self.key}
self.headers = {"User-Agent": utils.user_agent,
"Client-ID": self.key}
async def channel_online(self, channel: str):
async def channel_online(self, twitch_url: str):
# Check a specific channel's data, and get the response in text format
channel = re.search("(?<=twitch.tv/)(.*)", twitch_url).group(1)
url = "https://api.twitch.tv/kraken/streams/{}".format(channel)
response = await utils.request(url, payload=self.params)
@ -46,100 +44,96 @@ class Twitch:
# Loop through as long as the bot is connected
try:
while not self.bot.is_closed:
twitch = await utils.get_content('twitch', {'notifications_on': 1})
# Online/offline is based on whether they are set to such, in the utils file
# This means they were detected as online/offline before and we check for a change
online_users = {data['member_id']: data for data in twitch if data['live']}
offline_users = {data['member_id']: data for data in twitch if not data['live']}
for m_id, result in offline_users.items():
# Get their url and their user based on that url
url = result['twitch_url']
user = re.search("(?<=twitch.tv/)(.*)", url).group(1)
# Check if they are online right now
if await self.channel_online(user):
for server_id in result['servers']:
# Get the channel to send the message to, based on the saved alert's channel
server = self.bot.get_server(server_id)
twitch = await utils.filter_content('twitch', {'notifications_on': 1})
for data in twitch:
m_id = data['member_id']
url = data['twitch_url']
# Check if they are online
online = await self.channel_online(url)
# If they're currently online, but saved as not then we'll send our notification
if online and data['live'] == 0:
for s_id in data['servers']:
server = self.bot.get_server(s_id)
if server is None:
continue
server_alerts = await utils.get_content('server_alerts', {'server_id': server_id})
try:
channel_id = server_alerts[0]['channel_id']
except (IndexError, TypeError):
channel_id = server_id
channel = self.bot.get_channel(channel_id)
# Get the member that has just gone live
member = discord.utils.get(server.members, id=m_id)
member = server.get_member(m_id)
if member is None:
continue
fmt = "{} has just gone live! View their stream at {}".format(member.display_name, url)
await self.bot.send_message(channel, fmt)
await utils.update_content('twitch', {'live': 1}, {'member_id': m_id})
for m_id, result in online_users.items():
# Get their url and their user based on that url
url = result['twitch_url']
user = re.search("(?<=twitch.tv/)(.*)", url).group(1)
# Check if they are online right now
if not await self.channel_online(user):
for server_id in result['servers']:
# Get the channel to send the message to, based on the saved alert's channel
server = self.bot.get_server(server_id)
server_settings = await utils.get_content('server_settings', s_id)
if server_settings is not None:
channel_id = server_settings.get('notification_channel', s_id)
else:
channel_id = s_id
channel = server.get_channel(channel_id)
await self.bot.send_message(channel, "{} has just gone live! View their stream at <{}>".format(member.display_name, data['twitch_url']))
self.bot.loop.create_task(utils.update_content('twitch', {'live': 1}, m_id))
elif not online and data['live'] == 1:
for s_id in data['servers']:
server = self.bot.get_server(s_id)
if server is None:
continue
server_alerts = await utils.get_content('server_alerts', {'server_id': server_id})
channel_id = server_id
if server_alerts is not None and len(server_alerts) > 0:
channel_id = server_alerts[0].get('channel_id')
channel = self.bot.get_channel(channel_id)
# Get the member that has just gone live
member = discord.utils.get(server.members, id=m_id)
fmt = "{} has just gone offline! Catch them next time they stream at {}".format(
member.display_name, url)
await self.bot.send_message(channel, fmt)
await utils.update_content('twitch', {'live': 0}, {'member_id': m_id})
member = server.get_member(m_id)
if member is None:
continue
server_settings = await utils.get_content('server_settings', s_id)
if server_settings is not None:
channel_id = server_settings.get('notification_channel', s_id)
else:
channel_id = s_id
channel = server.get_channel(channel_id)
await self.bot.send_message(channel, "{} has just gone offline! View their stream next time at <{}>".format(member.display_name, data['twitch_url']))
self.bot.loop.create_task(utils.update_content('twitch', {'live': 0}, m_id))
await asyncio.sleep(30)
except Exception as e:
tb = traceback.format_exc()
fmt = "{1}\n{0.__class__.__name__}: {0}".format(tb, e)
log.error(fmt)
@commands.group(no_pm=True, invoke_without_command=True, pass_context=True)
@commands.group(no_pm=True, invoke_without_command=True)
@utils.custom_perms(send_messages=True)
async def twitch(self, ctx, *, member: discord.Member = None):
"""Use this command to check the twitch info of a user
EXAMPLE: !twitch @OtherPerson
RESULT: Information about their twitch URL"""
await ctx.message.channel.trigger_typing()
if member is None:
member = ctx.message.author
result = await utils.get_content('twitch', {'member_id': member.id})
result = await utils.get_content('twitch', member.id)
if result is None:
await self.bot.say("{} has not saved their twitch URL yet!".format(member.name))
return
result = result[0]
url = result['twitch_url']
user = re.search("(?<=twitch.tv/)(.*)", url).group(1)
twitch_url = "https://api.twitch.tv/kraken/channels/{}?client_id={}".format(user, self.key)
with aiohttp.ClientSession() as s:
async with s.get(twitch_url) as response:
data = await response.json()
twitch_url = "https://api.twitch.tv/kraken/channels/{}".format(user)
payload = {'client_id': self.key}
data = await utils.request(twitch_url, payload=payload)
fmt = "Username: {}".format(data['display_name'])
fmt += "\nStatus: {}".format(data['status'])
fmt += "\nFollowers: {}".format(data['followers'])
fmt += "\nURL: {}".format(url)
await self.bot.say("```\n{}```".format(fmt))
embed = discord.Embed(title=data['display_name'], url=url)
if data['logo']:
embed.set_thumbnail(url=data['logo'])
@twitch.command(name='add', pass_context=True, no_pm=True)
embed.add_field(name='Title', value=data['status'])
embed.add_field(name='Followers', value=data['followers'])
embed.add_field(name='Views', value=data['views'])
if data['game']:
embed.add_field(name='Game', value=data['game'])
embed.add_field(name='Language', value=data['broadcaster_language'])
await self.bot.say(embed=embed)
@twitch.command(name='add', no_pm=True)
@utils.custom_perms(send_messages=True)
async def add_twitch_url(self, ctx, url: str):
"""Saves your user's twitch URL
EXAMPLE: !twitch add MyTwitchName
RESULT: Saves your twitch URL; notifications will be sent to this server when you go live"""
await ctx.message.channel.trigger_typing()
# This uses a lookbehind to check if twitch.tv exists in the url given
# If it does, it matches twitch.tv/user and sets the url as that
# Then (in the else) add https://www. to that
@ -156,29 +150,28 @@ class Twitch:
url = "https://www.{}".format(url)
# Try to find the channel provided, we'll get a 404 response if it does not exist
with aiohttp.ClientSession() as s:
async with s.get(url) as response:
if not response.status == 200:
await self.bot.say("That twitch user does not exist! "
"What would be the point of adding a nonexistant twitch user? Silly")
return
status = await utils.request(url, attr='status')
if not status == 200:
await self.bot.say("That twitch user does not exist! "
"What would be the point of adding a nonexistant twitch user? Silly")
return
r_filter = {'member_id': ctx.message.author.id}
key = ctx.message.author.id
entry = {'twitch_url': url,
'servers': [ctx.message.server.id],
'notifications_on': 1,
'live': 0,
'member_id': ctx.message.author.id}
'member_id': key}
update = {'twitch_url': url}
# Check to see if this user has already saved a twitch URL
# If they have, update the URL, otherwise create a new entry
# Assuming they're not live, and notifications should be on
if not await utils.add_content('twitch', entry, r_filter):
await utils.update_content('twitch', update, r_filter)
if not await utils.add_content('twitch', entry):
await utils.update_content('twitch', update, key)
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'], no_pm=True)
@utils.custom_perms(send_messages=True)
async def remove_twitch_url(self, ctx):
"""Removes your twitch URL
@ -186,11 +179,10 @@ class Twitch:
EXAMPLE: !twitch remove
RESULT: I stop saving your twitch URL"""
# Just try to remove it, if it doesn't exist, nothing is going to happen
r_filter = {'member_id': ctx.message.author.id}
await utils.remove_content('twitch', r_filter)
await utils.remove_content('twitch', ctx.message.author.id)
await self.bot.say("I am no longer saving your twitch URL {}".format(ctx.message.author.mention))
@twitch.group(pass_context=True, no_pm=True, invoke_without_command=True)
@twitch.group(no_pm=True, invoke_without_command=True)
@utils.custom_perms(send_messages=True)
async def notify(self, ctx):
"""This can be used to modify notification settings for your twitch user
@ -198,43 +190,41 @@ class Twitch:
EXAMPLE: !twitch notify
RESULT: This server will now be notified when you go live"""
r_filter = {'member_id': ctx.message.author.id}
result = await utils.get_content('twitch', r_filter)
key = ctx.message.author.id
result = await utils.get_content('twitch', key)
# Check if this user is saved at all
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 if this server is already added as one to notify in
elif ctx.message.server.id in result[0]['servers']:
elif ctx.message.server.id in result['servers']:
await self.bot.say("I am already set to notify in this server...")
else:
await utils.update_content('twitch', {'servers': r.row['servers'].append(ctx.message.server.id)}, r_filter)
await utils.update_content('twitch', {'servers': r.row['servers'].append(ctx.message.server.id)}, key)
await self.bot.say("This server will now be notified if you go live")
@notify.command(name='on', aliases=['start,yes'], pass_context=True, no_pm=True)
@notify.command(name='on', aliases=['start,yes'], no_pm=True)
@utils.custom_perms(send_messages=True)
async def notify_on(self, ctx):
"""Turns twitch notifications on
EXAMPLE: !twitch notify on
RESULT: Notifications will be sent when you go live"""
r_filter = {'member_id': ctx.message.author.id}
if await utils.update_content('twitch', {"notifications_on": 1}, r_filter):
if await utils.update_content('twitch', {"notifications_on": 1}, ctx.message.author.id):
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))
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'], no_pm=True)
@utils.custom_perms(send_messages=True)
async def notify_off(self, ctx):
"""Turns twitch notifications off
EXAMPLE: !twitch notify off
RESULT: Notifications will not be sent when you go live"""
r_filter = {'member_id': ctx.message.author.id}
if await utils.update_content('twitch', {"notifications_on": 1}, r_filter):
if await utils.update_content('twitch', {"notifications_on": 0}, ctx.message.author.id):
await self.bot.say(
"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(
@ -247,4 +237,4 @@ class Twitch:
def setup(bot):
t = Twitch(bot)
bot.loop.create_task(t.check_channels())
bot.add_cog(Twitch(bot))
bot.add_cog(Twitch(bot))

View file

@ -5,6 +5,7 @@ import inspect
from . import config
from PIL import Image
def convert_to_jpeg(pfile):
# Open the file given
img = Image.open(pfile)
@ -16,6 +17,7 @@ def convert_to_jpeg(pfile):
new_file.seek(0)
return new_file
def get_all_commands(bot):
"""Returns a list of all command names for the bot"""
# First lets create a set of all the parent names
@ -31,6 +33,7 @@ def get_all_commands(bot):
return all_commands
def get_subcommands(command):
yield command.qualified_name
try:
@ -40,6 +43,19 @@ def get_subcommands(command):
except AttributeError:
pass
async def channel_is_nsfw(channel):
server = channel.server.id
channel = channel.id
server_settings = await config.get_content('server_settings', server)
try:
return channel in server_settings['nsfw_channels']
except (TypeError, IndexError, KeyError):
return False
def find_command(bot, command):
"""Finds a command (be it parent or sub command) based on string given"""
# This method ensures the command given is valid. We need to loop through commands
@ -64,6 +80,7 @@ def find_command(bot, command):
return cmd
async def download_image(url):
"""Returns a file-like object based on the URL provided"""
headers = {'User-Agent': config.user_agent}
@ -76,6 +93,7 @@ async def download_image(url):
image = BytesIO(bts)
return image
async def request(url, *, headers=None, payload=None, method='GET', attr='json'):
# Make sure our User Agent is what's set, and ensure it's sent even if no headers are passed
if headers == None:
@ -112,6 +130,7 @@ async def request(url, *, headers=None, payload=None, method='GET', attr='json')
except:
continue
async def update_records(key, winner, loser):
# We're using the Harkness scale to rate
# http://opnetchessclub.wikidot.com/harkness-rating-system