Browse Source

Roxbot is now guild sensitive. Commands that require a guild have had the guild only check applied. Those who required it for no reason have been changed. Subreddit command now works and caches per use. Allowing DM use. So has NSFW. Is Anal has been fixed completely as well.

tags/v2.0.0
Roxie Gibson 5 years ago
parent
commit
caf1590634
14 changed files with 104 additions and 30 deletions
  1. +4
    -1
      CHANGELOG.md
  2. +17
    -1
      roxbot/checks.py
  3. +5
    -1
      roxbot/cogs/admin.py
  4. +1
    -0
      roxbot/cogs/customcommands.py
  5. +9
    -1
      roxbot/cogs/fun.py
  6. +12
    -6
      roxbot/cogs/nsfw.py
  7. +26
    -13
      roxbot/cogs/reddit.py
  8. +3
    -0
      roxbot/cogs/selfassign.py
  9. +1
    -0
      roxbot/cogs/trivia.py
  10. +1
    -0
      roxbot/cogs/util.py
  11. +11
    -0
      roxbot/cogs/voice.py
  12. +4
    -1
      roxbot/err_handle.py
  13. +5
    -4
      roxbot/settings/settings.py
  14. +5
    -2
      roxbot/utils.py

+ 4
- 1
CHANGELOG.md View File

@@ -21,9 +21,12 @@
- `deepfry` command now works all the time.
- Pride filter filenames fixed.
- As many spelling mistakes as possible.
- If player can't find a thumbnail for the now playing embed, it will just not have one; Instead of breaking.
- If music bot can't find a thumbnail for the now playing embed, it will just not have one; Instead of breaking.
- Fixed bug where nowplaying embeds would have the wrong queued_by value.
- NSFW commands fixed.
- Roxbot can now fully function in DMs. Before, she would break. DM's have less commands.
- `;subreddit`'s "subscriptable" error has been fixed.
- is_anal value can be changed again and check now works.

## v1.8.0
#### New Features

+ 17
- 1
roxbot/checks.py View File

@@ -25,7 +25,9 @@ SOFTWARE.
"""


import discord
from discord.ext import commands

import roxbot
from roxbot import guild_settings as gs

@@ -35,6 +37,8 @@ def is_owner_or_admin():
def predicate(ctx):
if ctx.author.id == roxbot.owner:
return True
elif isinstance(ctx.channel, discord.DMChannel):
return False
else:
for role in ctx.author.roles:
if role.id in gs.get(ctx.guild).perm_roles["admin"]:
@@ -46,6 +50,8 @@ def is_owner_or_admin():
def _is_admin_or_mod(ctx):
if ctx.message.author.id == roxbot.owner:
return True
elif isinstance(ctx.channel, discord.DMChannel):
return False
else:
admin_roles = gs.get(ctx.guild).perm_roles["admin"]
mod_roles = gs.get(ctx.guild).perm_roles["mod"]
@@ -60,6 +66,8 @@ def is_admin_or_mod():


def nsfw_predicate(ctx):
if isinstance(ctx.channel, discord.DMChannel):
return False
nsfw = gs.get(ctx.guild).nsfw
if not nsfw["channels"] and nsfw["enabled"]:
return nsfw["enabled"] == 1
@@ -74,4 +82,12 @@ def is_nfsw_enabled():


def isnt_anal():
return commands.check(lambda ctx: gs.get(ctx.guild).is_anal["y/n"] and nsfw_predicate(ctx) or not gs.get(ctx.guild).is_anal["y/n"])
def predicate(ctx):
if isinstance(ctx.channel, discord.DMChannel):
return False
anal = gs.get(ctx.guild).is_anal["y/n"]
if not anal or (nsfw_predicate(ctx) and gs.get(ctx.guild).is_anal["y/n"]):
return True
else:
return False
return commands.check(predicate)

+ 5
- 1
roxbot/cogs/admin.py View File

@@ -75,8 +75,8 @@ class Admin:
else:
pass

@roxbot.checks.is_admin_or_mod()
@commands.guild_only()
@roxbot.checks.is_admin_or_mod()
@commands.bot_has_permissions(manage_messages=True)
@commands.command()
async def slowmode(self, ctx, seconds):
@@ -120,6 +120,7 @@ class Admin:
messages = await ctx.channel.purge(limit=limit, check=predicate)
return await ctx.send("{} message(s) purged from chat.".format(len(messages)))

@commands.guild_only()
@roxbot.checks.is_admin_or_mod()
@commands.group(case_insensitive=True)
async def warn(self, ctx):
@@ -250,6 +251,7 @@ class Admin:
settings.update(settings.warnings, "warnings")
return await ctx.send("Purged {} banned users from the warn list.".format(count))

@commands.guild_only()
@commands.has_permissions(kick_members=True)
@commands.bot_has_permissions(kick_members=True)
@commands.command()
@@ -261,6 +263,7 @@ class Admin:
except discord.Forbidden:
return await ctx.send("I can't kick the owner or users higher or equal to me.")

@commands.guild_only()
@commands.has_permissions(ban_members=True)
@commands.bot_has_permissions(ban_members=True)
@commands.command()
@@ -272,6 +275,7 @@ class Admin:
except discord.Forbidden:
return await ctx.send("I can't kick the owner or users higher or equal to me.")

@commands.guild_only()
@commands.has_permissions(ban_members=True)
@commands.bot_has_permissions(ban_members=True)
@commands.command()

+ 1
- 0
roxbot/cogs/customcommands.py View File

@@ -122,6 +122,7 @@ class CustomCommands:
command_output = self._get_output(settings.custom_commands["0"][command])
return await channel.send(command_output)

@commands.guild_only()
@commands.group(aliases=["cc"])
@roxbot.checks.is_owner_or_admin()
async def custom(self, ctx):

+ 9
- 1
roxbot/cogs/fun.py View File

@@ -278,7 +278,9 @@ class Fun:

@commands.command(aliases=["cf"])
async def coinflip(self, ctx):
"""Flip a coin"""
"""
Flip a coin
"""
return await ctx.send("The coin landed on {}!".format(random.choice(["heads", "tails"])))

@commands.command()
@@ -429,6 +431,12 @@ class Fun:

@commands.command()
async def zalgo(self, ctx, *, text):
"""
Returns Z͇͇͋Á͇͇L͇͔͇G̛͇͇O͇͇͜
:param ctx:
:param text:
:return:
"""
intensity = 10
zalgo_chars = [*[chr(i) for i in range(0x0300, 0x036F + 1)], *[u'\u0488', u'\u0489']]
zalgoised = []

+ 12
- 6
roxbot/cogs/nsfw.py View File

@@ -26,6 +26,7 @@ SOFTWARE.


import random
import discord
from discord.ext import commands

import roxbot
@@ -43,8 +44,6 @@ class NFSW():
def __init__(self, bot_client):
self.bot = bot_client
self.cache = {}
for guild in self.bot.guilds:
self.cache[guild.id] = []

@roxbot.checks.is_nfsw_enabled()
@commands.command(hidden=True)
@@ -53,6 +52,13 @@ class NFSW():
tags = tags + tag_blacklist(ctx.guild)
page = random.randrange(20)
url = base_url + tags + '&limit=' + str(limit) + '%pid=' + str(page)
if isinstance(ctx.channel, discord.DMChannel):
cache_id = ctx.author.id
else:
cache_id = ctx.guild.id
# IF ID is not in cache, create cache for ID
if not self.cache.get(cache_id, False):
self.cache[cache_id] = []

posts = await roxbot.http.api_request(url)

@@ -64,10 +70,10 @@ class NFSW():
while counter < 20:
post = random.choice(posts)
md5 = post.get("md5") or post.get("hash")
if md5 not in self.cache[ctx.guild.id]:
self.cache[ctx.guild.id].append(md5)
if len(self.cache[ctx.guild.id]) > 10:
self.cache[ctx.guild.id].pop(0)
if md5 not in self.cache[cache_id]:
self.cache[cache_id].append(md5)
if len(self.cache[cache_id]) > 10:
self.cache[cache_id].pop(0)
break
counter += 1


+ 26
- 13
roxbot/cogs/reddit.py View File

@@ -26,17 +26,23 @@ SOFTWARE.


import random
import roxbot
from html import unescape
from bs4 import BeautifulSoup
from roxbot import guild_settings

import discord
from discord.ext import commands

import roxbot
from roxbot import guild_settings


async def _imgur_removed(url):
page = await roxbot.http.get_page(url)
soup = BeautifulSoup(page, 'html.parser')
return bool("removed.png" in soup.img["src"])
try:
return bool("removed.png" in soup.img["src"])
except TypeError: # This should protect roxbot in case bs4 returns nothing.
return False


async def imgur_get(url):
@@ -96,8 +102,6 @@ class Reddit:
def __init__(self, bot_client):
self.bot = bot_client
self.post_cache = {}
for guild in self.bot.guilds:
self.post_cache[guild.id] = [("", "")]

@commands.command()
@commands.has_permissions(add_reactions=True)
@@ -112,6 +116,14 @@ class Reddit:
links = await subreddit_request(subreddit)
title = ""
choice = {}
if isinstance(ctx.channel, discord.DMChannel):
cache_id = ctx.author.id
else:
cache_id = ctx.guild.id
cache_amount = 10
# IF ID is not in cache, create cache for ID
if not self.post_cache.get(cache_id, False):
self.post_cache[cache_id] = [("", "")]

if not links or not links.get("after") or links["children"][0]["kind"] == "t5": # Determine if response is valid
return await ctx.send("Error ;-; That subreddit probably doesn't exist. Please check your spelling")
@@ -126,7 +138,7 @@ class Reddit:
if url:
x_old = int(x)
# If the url or id are in the cache, continue the loop. If not, proceed with the post.
for cache in self.post_cache[ctx.guild.id]:
for cache in self.post_cache[cache_id]:
if url in cache or choice["id"] in cache:
x += 1
break
@@ -142,24 +154,25 @@ class Reddit:
x += 1

# Check if post is NSFW, and if it is and this channel doesn't past the NSFW check, then return with the error message.
if choice["over_18"] and not roxbot.checks.nsfw_predicate(ctx):
if (choice["over_18"] and not roxbot.checks.nsfw_predicate(ctx)) and isinstance(ctx.channel, discord.TextChannel):
return await ctx.send("This server/channel doesn't have my NSFW stuff enabled. This extends to posting NFSW content from Reddit.")
if not url: # If no image posts could be found with the for loop.
return await ctx.send("I couldn't find any images from that subreddit.")

# Put the post ID and url in the cache. The url is used in case of two posts having the same link. Handy to avoid crossposts getting through the cache.
cache_amount = 10
# CACHE SECTION
# Put the post ID and url in the cache. The url is used in case of two posts having the same link to avoid crossposts getting through the cache.
post = (choice["id"], url)
self.post_cache[ctx.guild.id].append(post)
if len(self.post_cache[ctx.guild.id]) >= cache_amount:
self.post_cache[ctx.guild.id].pop(0)

self.post_cache[cache_id].append(post)
if len(self.post_cache[cache_id]) >= cache_amount:
self.post_cache[cache_id].pop(0)

if url.split("/")[-2] == "a":
text = "This is an album, click on the link to see more.\n"
else:
text = ""

if ctx.invoked_with == "subreddit":
if ctx.invoked_with == "subreddit" and isinstance(ctx.channel, discord.TextChannel):
# Only log the command when it is this command being used. Not the inbuilt commands.
logging = guild_settings.get(ctx.guild).logging
log_channel = self.bot.get_channel(logging["channel"])

+ 3
- 0
roxbot/cogs/selfassign.py View File

@@ -44,6 +44,7 @@ class SelfAssign():
sa["roles"].remove(role.id)
return settings.update(sa, "self_assign")

@commands.guild_only()
@commands.command(pass_context=True)
async def listroles(self, ctx):
"""
@@ -64,6 +65,7 @@ class SelfAssign():
embed = discord.Embed(colour=roxbot.EmbedColours.pink, description="The self-assignable roles for this server are: \n"+roles)
return await ctx.send(embed=embed)

@commands.guild_only()
@commands.command(pass_context=True)
async def iam(self, ctx, *, role: discord.Role = None):
"""
@@ -95,6 +97,7 @@ class SelfAssign():
else:
return await ctx.send("That role is not self-assignable.")

@commands.guild_only()
@commands.command(pass_context=True)
async def iamn(self, ctx, *, role: discord.Role = None):
"""

+ 1
- 0
roxbot/cogs/trivia.py View File

@@ -297,6 +297,7 @@ class Trivia:

# Commands

@commands.guild_only()
@commands.group(aliases=["tr"], case_insensitive=True)
async def trivia(self, ctx):
"""Command group for the Roxbot Trivia game."""

+ 1
- 0
roxbot/cogs/util.py View File

@@ -228,6 +228,7 @@ class Util():

@commands.command()
async def lookup(self, ctx, ID: int):
# TODO: Finish this
result = self.bot.get_channel(ID)
pass


+ 11
- 0
roxbot/cogs/voice.py View File

@@ -204,6 +204,7 @@ class Voice:
return embed

@roxbot.checks.is_admin_or_mod()
@commands.guild_only()
@commands.command()
async def join(self, ctx, *, channel: discord.VoiceChannel = None):
"""Joins the voice channel your in."""
@@ -221,6 +222,7 @@ class Voice:
await channel.connect()
return await ctx.send("Joined {0.name} :ok_hand:".format(channel))

@commands.guild_only()
@commands.cooldown(1, 0.5, commands.BucketType.guild)
@commands.command(aliases=["yt"])
async def play(self, ctx, *, url, stream=False, from_queue=False, queued_by=None):
@@ -285,6 +287,7 @@ class Voice:
embed = discord.Embed(description='Added "{}" to queue'.format(video.get("title")), colour=roxbot.EmbedColours.pink)
await ctx.send(embed=embed)

@commands.guild_only()
@commands.cooldown(1, 0.5, commands.BucketType.guild)
@commands.command()
async def stream(self, ctx, *, url):
@@ -303,6 +306,7 @@ class Voice:
raise commands.CommandError("Roxbot is not connected to a voice channel and couldn't auto-join a voice channel.")

@volume_perms()
@commands.guild_only()
@commands.command()
async def volume(self, ctx, volume):
"""Changes the player's volume. Only accepts integers representing x% between 0-100% or "show", which will show the current volume."""
@@ -326,6 +330,7 @@ class Voice:
raise commands.CommandError("Volume needs to be between 0-100%")
return await ctx.send("Changed volume to {}%".format(volume))

@commands.guild_only()
@commands.command()
async def pause(self, ctx):
"""Pauses the current video, if playing."""
@@ -340,6 +345,7 @@ class Voice:
ctx.voice_client.pause()
return await ctx.send("Paused '{}'".format(ctx.voice_client.source.title))

@commands.guild_only()
@commands.command()
async def resume(self, ctx):
"""Resumes the bot if paused. Also will play the next thing in the queue if the bot is stuck."""
@@ -359,6 +365,7 @@ class Voice:
else:
return await ctx.send("Nothing to resume.")

@commands.guild_only()
@commands.command()
async def skip(self, ctx, option=""):
"""Skips or votes to skip the current video. Use option "--force" if your an admin and """
@@ -388,6 +395,7 @@ class Voice:
else:
await ctx.send("I'm not playing anything.")

@commands.guild_only()
@commands.command(aliases=["np"])
async def nowplaying(self, ctx):
"""Displays the video now playing."""
@@ -401,6 +409,7 @@ class Voice:
embed = self._generate_np_embed(ctx.guild, x)
return await ctx.send(embed=embed)

@commands.guild_only()
@commands.command()
async def queue(self, ctx):
"""Displays what videos are queued up and waiting to be played."""
@@ -414,6 +423,7 @@ class Voice:
embed = discord.Embed(title="Queue", description=output, colour=roxbot.EmbedColours.pink)
return await ctx.send(embed=embed)

@commands.guild_only()
@roxbot.checks.is_admin_or_mod()
@commands.command()
async def remove(self, ctx, index):
@@ -440,6 +450,7 @@ class Voice:
except IndexError:
raise commands.CommandError("Valid Index not given.")

@commands.guild_only()
@roxbot.checks.is_admin_or_mod()
@commands.command(alaises=["disconnect"])
async def stop(self, ctx):

+ 4
- 1
roxbot/err_handle.py View File

@@ -102,7 +102,10 @@ class ErrHandle:
embed = discord.Embed(title='Command Error', colour=roxbot.EmbedColours.dark_red)
embed.description = str(error)
embed.add_field(name='Server', value=ctx.guild)
embed.add_field(name='Channel', value=ctx.channel.mention)
try:
embed.add_field(name='Channel', value=ctx.channel.mention)
except AttributeError:
embed.add_field(name='Channel', value=ctx.channel.id)
embed.add_field(name='User', value=ctx.author)
embed.add_field(name='Message', value=ctx.message.content)
embed.timestamp = datetime.datetime.utcnow()

+ 5
- 4
roxbot/settings/settings.py View File

@@ -456,21 +456,22 @@ class Settings:
return await ctx.send("Valid option not given.")
return self.guild_settings.update(voice, "voice")

@commands.guild_only()
@checks.is_admin_or_mod()
@commands.command()
async def serverisanal(self, ctx):
"""Tells the bot where the server is anal or not.
This only changes if roxbot can do the suck and spank commands outside of the specified nsfw channels."""
is_anal = self.guild_settings.is_anal
gs = guild_settings.get(ctx.guild)
is_anal = gs.is_anal
if is_anal["y/n"] == 0:
is_anal["y/n"] = 1
self.guild_settings.update(is_anal, "is_anal")
gs.update(is_anal, "is_anal")
await ctx.send("I now know this server is anal")
else:
is_anal["y/n"] = 0
self.guild_settings.update(is_anal, "is_anal")
gs.update(is_anal, "is_anal")
await ctx.send("I now know this server is NOT anal")
return self.guild_settings.update()


def setup(bot_client):

+ 5
- 2
roxbot/utils.py View File

@@ -26,7 +26,7 @@ SOFTWARE.


import asyncio
import discord

class Menu:

@@ -108,7 +108,10 @@ async def delete_option(bot, ctx, message, delete_emoji, timeout=20):
try:
await bot.wait_for("reaction_add", timeout=timeout, check=check)
await message.remove_reaction(delete_emoji, bot.user)
await message.remove_reaction(delete_emoji, ctx.author)
try:
await message.remove_reaction(delete_emoji, ctx.author)
except discord.Forbidden:
pass
await message.edit(content="{} requested output be deleted.".format(ctx.author), embed=None)
except asyncio.TimeoutError:
await message.remove_reaction(delete_emoji, bot.user)

Loading…
Cancel
Save