Browse Source

Merge branch 'rewrite'

# Conflicts:
#	README.md
#	cogs/admin.py
#	config/meta.py
tags/v1.4.1
Roxie Gibson 6 years ago
parent
commit
d27bd64e7d
23 changed files with 1343 additions and 1157 deletions
  1. +29
    -4
      README.md
  2. +8
    -15
      checks.py
  3. +79
    -175
      cogs/admin.py
  4. +43
    -40
      cogs/customcommands.py
  5. +42
    -34
      cogs/fun.py
  6. +24
    -34
      cogs/gss.py
  7. +10
    -12
      cogs/joinleave.py
  8. +30
    -26
      cogs/nsfw.py
  9. +28
    -140
      cogs/reddit.py
  10. +41
    -64
      cogs/selfassign.py
  11. +0
    -367
      cogs/settings.py
  12. +351
    -0
      cogs/trivia.py
  13. +43
    -46
      cogs/twitch.py
  14. +57
    -85
      cogs/util.py
  15. +0
    -0
      config/__init__.py
  16. +0
    -15
      config/cogs.py
  17. +0
    -7
      config/meta.py
  18. +6
    -3
      config/server_config.py
  19. +0
    -1
      config/servers.json
  20. +412
    -7
      config/settings.py
  21. +73
    -0
      err_handle.py
  22. +35
    -11
      load_config.py
  23. +32
    -71
      main.py

+ 29
- 4
README.md View File

@@ -2,14 +2,14 @@

[![MIT](https://img.shields.io/github/license/mashape/apistatus.svg?style=flat-square)](https://opensource.org/licenses/MIT)
[![Python](https://img.shields.io/badge/Python-3.5%2B-blue.svg?style=flat-square)](https://github.com/RainbowDinoaur/roxbot/)
[![Say Thanks](https://img.shields.io/badge/say-thanks-ff69b4.svg?style=flat-square)](https://saythanks.io/to/RainbowDinoaur)
[![Say Thanks](https://img.shields.io/badge/say-thanks-ff69b4.svg?style=flat-square)](https://saythanks.io/to/Roxxers)

A simple Discord Bot used by me personally, written for fun.
RoxBot, A Discord Bot made by a filthy Mercy Main. Built with love (and discord.py)

## Simple setup

1. Copy settings/preferences_example.ini to settings/preferences.ini and fill in the details
2. Run pip install -r requirements.txt in the root directory of the bot
2. Run pip install -r requirements.txt in the root directory of the bot (This wont install the 1.0 version of discord.py which Roxbot now requires.)
3. Add the bot to your server by generating an OAuth link with bot scope only
4. Run main.py to generate config files for the server
5. Stop RoxBot
@@ -21,7 +21,32 @@ A simple Discord Bot used by me personally, written for fun.

## Changelog

#### v1.3_3
#### v1.4.0

###### New Stuff!
- A whole trivia game, right in your discords. Using the Open Trivia DB!
- A complete overhaul of the settings cog so that the settings commands no longer suck and should be easier to use.
- Most, if not all, commands now have documentation in the bot on what they do and how to use them.
- Bot may be easier to use. I am unsure though.
- Added error handling that will actually tell people when errors occur instead of being silent.
- NSFW now has a blacklist feature that can be edited by mods and admins. Just basic tag filters.
- **FROGTIPS**

###### Boring Stuff
- Roxbot has been ported over to the 1.0 version of discord.py. Meaning that she will actually be upto date with new discord changes for once. This also means that there is quite a few new features and bug fixes with this version.
- New fil structure that will be easier to read and know whats going on.
- servers.json is no longer versioned .
- Unhidden all commands so that it should be easier for mods to know the mod commands..

###### Bug Fixes
- emoji command works with animated emoji.
- avatar command works with animated avatars.
- Non-prefix multi word custom commands should work now and should be removable.
- Fixed issues with the warning commands when a warned user wasn't in the server.
- Fixed bug where capitalisation in the subreddit command would return nothing.
- Twitch shilling now doesn't trigger when someone uses spotify thanks to discord.py update. Also the code is a lot nicer now.

#### v1.3.4
###### Performance
- Changed the warning listing command do that it isn't slow.
###### Bug Fixes

+ 8
- 15
checks.py View File

@@ -1,18 +1,14 @@
from discord.ext import commands
import load_config
from config.server_config import ServerConfig
import discord

def is_bot_owner():
return commands.check(lambda ctx: ctx.message.author.id == load_config.owner)

def is_owner_or_admin():
def predicate(ctx):
if ctx.message.author.id == load_config.owner:
if ctx.author.id == load_config.owner:
return True
else:
for role in ctx.message.author.roles:
if role.id in ServerConfig().load_config()[ctx.message.server.id]["perm_roles"]["admin"]:
for role in ctx.author.roles:
if role.id in ServerConfig().load_config()[str(ctx.guild.id)]["perm_roles"]["admin"]:
return True
return False
return commands.check(predicate)
@@ -22,8 +18,8 @@ def is_admin_or_mod():
if ctx.message.author.id == load_config.owner:
return True
else:
admin_roles = ServerConfig().load_config()[ctx.message.server.id]["perm_roles"]["admin"]
mod_roles = ServerConfig().load_config()[ctx.message.server.id]["perm_roles"]["mod"]
admin_roles = ServerConfig().load_config()[str(ctx.guild.id)]["perm_roles"]["admin"]
mod_roles = ServerConfig().load_config()[str(ctx.guild.id)]["perm_roles"]["mod"]
for role in ctx.message.author.roles:
if role.id in mod_roles or role.id in admin_roles:
return True
@@ -31,11 +27,11 @@ def is_admin_or_mod():
return commands.check(predicate)

def nsfw_predicate(ctx):
nsfw = ServerConfig().load_config()[ctx.message.server.id]["nsfw"]
nsfw = ServerConfig().load_config()[str(ctx.guild.id)]["nsfw"]
if not nsfw["channels"] and nsfw["enabled"]:
return nsfw["enabled"] == 1
elif nsfw["enabled"] and nsfw["channels"]:
return ctx.message.channel.id in nsfw["channels"]
return ctx.channel.id in nsfw["channels"]
else:
return False

@@ -43,7 +39,4 @@ def is_nfsw_enabled():
return commands.check(lambda ctx: nsfw_predicate(ctx))

def isnt_anal():
return commands.check(lambda ctx: ServerConfig().load_config()[ctx.message.server.id]["is_anal"]["y/n"] and nsfw_predicate(ctx) or not ServerConfig().load_config()[ctx.message.server.id]["is_anal"]["y/n"])

def not_pm():
return commands.check(lambda ctx: ctx.message.channel.type != discord.ChannelType.private)
return commands.check(lambda ctx: ServerConfig().load_config()[str(ctx.guild.id)]["is_anal"]["y/n"] and nsfw_predicate(ctx) or not ServerConfig().load_config()[str(ctx.guild.id)]["is_anal"]["y/n"])

+ 79
- 175
cogs/admin.py View File

@@ -3,15 +3,15 @@ import time
import checks
import discord
from config.server_config import ServerConfig
from discord.ext.commands import bot, group
from discord.ext.commands import bot, group, guild_only, bot_has_permissions


class Admin():
"""
Admin Commands for those admins
"""
def __init__(self, Bot):
self.bot = Bot
def __init__(self, bot_client):
self.bot = bot_client
self.slow_mode = False
self.slow_mode_channels = {}
self.users = {}
@@ -25,250 +25,154 @@ class Admin():

if not author == self.bot.user:
if self.slow_mode and channel.id in self.slow_mode_channels:
if author.id not in self.servers[message.server.id]["perm_roles"]["admin"] or author.id not in self.servers[message.server.id]["perm_roles"]["mod"]:
return
if author.id not in self.users[channel.id]:
# If user hasn't sent a message in this channel after slow mode was turned on
self.users[channel.id][author.id] = message.timestamp
self.users[channel.id][author.id] = message.created_at
else:
# Else, check when their last message was and if time is smaller than the timer, delete the message.
timer = datetime.timedelta(seconds=self.slow_mode_channels[channel.id])
if message.timestamp - self.users[channel.id][author.id] < timer:
await self.bot.delete_message(message)
if message.created_at - self.users[channel.id][author.id] < timer:
await message.delete()
else:
self.users[message.channel.id][author.id] = message.timestamp
self.users[message.channel.id][author.id] = message.created_at
else:
pass

@checks.not_pm()
@guild_only()
@checks.is_admin_or_mod()
@bot.command(pass_context=True)
@bot_has_permissions(manage_messages=True)
@bot.command()
async def slowmode(self, ctx, time):
"""Puts the current channel in slowmode.
Usage:
;slowmode [time/"off"]
time = time of the cooldown between messages a user has.
off = turns off slowmode for this channel"""
if time == "off" and self.slow_mode: # Turn Slow Mode off
self.slow_mode = False
self.slow_mode_channels.pop(ctx.message.channel.id)
self.users.pop(ctx.message.channel.id)
return await self.bot.say("Slowmode off")
self.slow_mode_channels.pop(ctx.channel.id)
self.users.pop(ctx.channel.id)
return await ctx.send("Slowmode off")

elif time.isdigit() and not self.slow_mode: # Turn Slow Mode On
self.users[ctx.message.channel.id] = {}
self.slow_mode_channels[ctx.message.channel.id] = int(time)
self.users[ctx.channel.id] = {}
self.slow_mode_channels[ctx.channel.id] = int(time)
self.slow_mode = True
return await self.bot.say("Slowmode on :snail: ({} seconds)".format(time))
return await ctx.send("Slowmode on :snail: ({} seconds)".format(time))

elif time.isdigit and self.slow_mode: # Change value of Slow Mode timer
self.slow_mode_channels[ctx.message.channel.id] = int(time)
return await self.bot.say("Slowmode set to :snail: ({} seconds)".format(time))
self.slow_mode_channels[ctx.channel.id] = int(time)
return await ctx.send("Slowmode set to :snail: ({} seconds)".format(time))

else:
pass


@checks.is_admin_or_mod()
@bot.command(pass_context=True, enabled=False)
async def emojiuse(self, ctx, emoji, *args):
# TODO: Add check that emoji is an emoji
# TODO: Disable so like when we go to 1.3 this isn't live cause it needs more work and it needs to be completed already
# The way forward is clearly put the given emoji in and treat it as a list of emojis,
# if all emojis, then generate the list. Allows for more than one emoji to be analysed. Works better for larger servers/

# Flag Parsing

if "-v" in args:
verbose = True
else:
verbose = False
if "-w" in args or emoji == "-w": # Second check just in case user just puts ";emojiuse -w"
all_emojis = True
else:
all_emojis = False

# Functions

def sum(usage):
amount = 0
for channel in usage.values():
amount += channel
return amount

def use_by_day(amount):
useperday = amount / 30
useperday = "{0:.2f}".format(useperday)
return useperday


def verbose_output(usage):
output = ""
for channel in usage:
channel = self.bot.get_channel(channel) # Convert channel from ID to channel object to get name
output = output + "{}: {} \n".format(channel.name, usage[channel.id])
return output

async def count_uses():
usage = {}
for channel in ctx.message.server.channels:
if channel.type == discord.ChannelType.text: # Only looks at server's text channels
x = 0
async for message in self.bot.logs_from(channel, limit=1000000, after=datetime.datetime.now() + datetime.timedelta(-30)):
if str(emoji) in message.content:
x += 1
usage[channel.id] = x
print(str(emoji) + "HAS {} AMOUNT OF USES IN {}".format(x, str(channel)))

else:
pass
print("EMOJI WAS USED {} TOTAL TIMES".format(sum(usage)))
return usage

async def count_all_emoji():
whitelist = [":yeetpride:",":yeet:", ":transgendercat:", ":thonkspin:", ":thonkegg:", ":ThinkPride:", ":thinkinggaysounds:", ":thatsgoodnews", ":pansexualcat:", ":nonbinarycat: ", ":kappa:", ":icantputpunctuationinemojinames:", ":hellothere:", ":agendercat:", ":Hedgeok:", ":extremethink:", ":donttryit:", ":cutie:", ":catappa:", ":boots:", ":awoo:", ":anotherhappylanding:", ":angrygay:"]
usage = {}

for emote in ctx.message.server.emojis:
name = ":{}:".format(emote.name)
if name in whitelist:
usage[emote.id] = 0

for channel in ctx.message.server.channels: # TODO: ADD COUNTING BY CHANNEL
if channel.type == discord.ChannelType.text: # Only looks at server's text channels
async for message in self.bot.logs_from(channel, limit=1000000, after=datetime.datetime.now() + datetime.timedelta(-30)):
for emote in ctx.message.server.emojis:
if str(emote) in message.content and ":{}:".format(emote.name) in whitelist:
usage[emote.id] += 1
print("{} found in message {}".format(str(emote), message.id))

return usage

# Command

await self.bot.say("Warning! This command may take upto 15 minutes to process. Please do no spam me. I am working.", delete_after=20)
await self.bot.send_typing(ctx.message.channel)

if all_emojis:
emoji_usage = await count_all_emoji()


em = discord.Embed(colour=0xDEADBF)
for emoji in emoji_usage:
emoji_obj = discord.utils.get(ctx.message.server.emojis, id=emoji)
amount = emoji_usage[emoji]
useperday = use_by_day(amount)
em.add_field(name=str(emoji_obj), value="Amount Used: {}\nUse/Day: {}".format(amount, useperday), inline=False)
return await self.bot.say(content="Below is a report of all custom emoji on this server and how many times they have been used in the previous 30 days. This includes a usage/day ratio.", embed=em)

else:
usage = await count_uses()
amount = sum(usage)
useperday = use_by_day(amount)
if verbose:
output = verbose_output(usage)
output_em = discord.Embed(description=output, colour=0xDEADBF)
return await self.bot.say(content="{} has been used {} time(s) in the last month. That's {}/day. Here is the break down per channel.".format(emoji, amount, useperday), embed=output_em)

else: # Non-verbose output
return await self.bot.say("{} has been used {} time(s) in the last month. That's {}/day.".format(emoji, amount, useperday))


@checks.is_admin_or_mod()
@group(pass_context=True)
@group()
async def warn(self, ctx):
"""Group of commands handling warnings"""
if ctx.invoked_subcommand is None:
return await self.bot.say('Missing Argument')

return await ctx.send('Missing Argument')

@warn.command(pass_context=True)
@warn.command()
async def add(self, ctx, user: discord.User = None, *, warning = ""):
"""Adds a warning to a user."""
# Warning in the config is a dictionary of user ids. The user ids are equal to a list of dictionaries.
self.servers = self.con.load_config()
warning_limit = 2
id = ctx.message.server.id
guild_id = str(ctx.guild.id)
warning_dict = {
"warned-by": ctx.message.author.id,
"warned-by": ctx.author.id,
"date": time.time(),
"warning": warning
}
if not user.id in self.servers[id]["warnings"]:
self.servers[id]["warnings"][user.id] = []
self.servers[id]["warnings"][user.id].append(warning_dict)
user.id = str(user.id)
if not user.id in self.servers[guild_id]["warnings"]:
self.servers[guild_id]["warnings"][user.id] = []
self.servers[guild_id]["warnings"][user.id].append(warning_dict)

self.con.update_config(self.servers)

amount_warnings = len(self.servers[id]["warnings"][user.id])
amount_warnings = len(self.servers[guild_id]["warnings"][user.id])
if amount_warnings > warning_limit:
await self.bot.send_message(ctx.message.author,"{} has been reported {} time(s). This is a reminder that this is over the set limit of {}.".format(
user.name+"#"+user.discriminator, amount_warnings, warning_limit))
await ctx.author.send("{} has been reported {} time(s). This is a reminder that this is over the set limit of {}.".format(
str(user), amount_warnings, warning_limit))

return await self.bot.say("Reported {}.".format(user.name+"#"+user.discriminator))
return await ctx.send("Reported {}.".format(str(user)))


@warn.command(pass_context=True)
@warn.command()
async def list(self, ctx, *, user: discord.User = None):
await self.bot.send_typing(ctx.message.channel)
"""Lists all or just the warnings for one user."""
guild_id = str(ctx.guild.id)
if user == None:
output = ""
for member in self.servers[ctx.message.server.id]["warnings"]:
member_obj = discord.utils.get(ctx.message.server.members, id=member)
if member_obj:
output += "{}#{}: {} Warning(s)\n".format(member_obj.name, member_obj.discriminator, len(
self.servers[ctx.message.server.id]["warnings"][member]))
for member in self.servers[guild_id]["warnings"]:
# Remove users with no warning here instead of remove cause im lazy
if not self.servers[guild_id]["warnings"][member]:
self.servers[guild_id]["warnings"].pop(member)
else:
output += "{}: {} Warning(s)\n".format(member, len(
self.servers[ctx.message.server.id]["warnings"][member]))
return await self.bot.say(output)

if not self.servers[ctx.message.server.id]["warnings"][user.id]:
self.servers[ctx.message.server.id]["warnings"].pop(user.id)
if not user.id in self.servers[ctx.message.server.id]["warnings"]:
return await self.bot.say("This user doesn't have any warning on record.")
em = discord.Embed(title="Warnings for {}".format(user.name+"#"+user.discriminator), colour=0XDEADBF)
member_obj = discord.utils.get(ctx.guild.members, id=int(member))
if member_obj:
output += "{}: {} Warning(s)\n".format(str(member_obj), len(
self.servers[guild_id]["warnings"][member]))
else:
output += "{}: {} Warning(s)\n".format(member, len(
self.servers[guild_id]["warnings"][member]))
return await ctx.send(output)
user.id = str(user.id)
if not self.servers[guild_id]["warnings"][user.id]:
self.servers[guild_id]["warnings"].pop(user.id)
if not user.id in self.servers[guild_id]["warnings"]:
return await ctx.send("This user doesn't have any warning on record.")
em = discord.Embed(title="Warnings for {}".format(str(user)), colour=0XDEADBF)
em.set_thumbnail(url=user.avatar_url)
x = 1
userlist = self.servers[ctx.message.server.id]["warnings"][user.id]
userlist = self.servers[guild_id]["warnings"][user.id]
for warning in userlist:
try:
warnuser = await self.bot.get_user_info(warning["warned-by"])
warned_by = warnuser.name + "#" + warnuser.discriminator
warned_by = str(await self.bot.get_user_info(warning["warned-by"]))
except:
warned_by = warning["warned-by"]
date = datetime.datetime.fromtimestamp(warning["date"]).strftime('%c')
warn_reason = warning["warning"]
em.add_field(name="Warning %s"%x, value="Warned by: {}\nTime: {}\nReason: {}".format(warned_by, date, warn_reason))
x += 1
return await ctx.send(embed=em)

return await self.bot.say(embed=em)

@warn.command(pass_context=True)
@warn.command()
async def remove(self, ctx, user: discord.User = None, index = None):
"""Removes one or all of the warnings for a user."""
self.servers = self.con.load_config()
user.id = str(user.id)
guild_id = str(ctx.guild.id)
if index:
try:
index = int(index)
index -= 1
self.servers[ctx.message.server.id]["warnings"][user.id].pop(index)
if not self.servers[ctx.message.server.id]["warnings"][user.id]:
self.servers[ctx.message.server.id]["warnings"].pop(user.id)
self.servers[guild_id]["warnings"][user.id].pop(index)
if not self.servers[guild_id]["warnings"][user.id]:
self.servers[guild_id]["warnings"].pop(user.id)

self.con.update_config(self.servers)
return await self.bot.say("Removed Warning {} from {}".format(index+1, user.name+"#"+user.discriminator))
return await ctx.send("Removed Warning {} from {}".format(index+1, str(user)))

except Exception as e:
if isinstance(e, IndexError):
return await self.bot.say(":warning: Index Error.")
return await ctx.send(":warning: Index Error.")
elif isinstance(e, KeyError):
return await self.bot.say("Could not find user in warning list.")
return await ctx.send("Could not find user in warning list.")
elif isinstance(e, ValueError):
return await self.bot.say("Please enter a valid index number.")
return await ctx.send("Please enter a valid index number.")
else:
raise e
else:
try:
self.servers[ctx.message.server.id]["warnings"].pop(user.id)
self.servers[guild_id]["warnings"].pop(user.id)
self.con.update_config(self.servers)
return await self.bot.say("Removed all warnings for {}".format(user.name+"#"+user.discriminator))
return await ctx.send("Removed all warnings for {}".format(str(user)))
except KeyError:
return await self.bot.say("Could not find user in warning list.")
return await ctx.send("Could not find user in warning list.")


def setup(Bot):
Bot.add_cog(Admin(Bot))
def setup(bot_client):
bot_client.add_cog(Admin(bot_client))

+ 43
- 40
cogs/customcommands.py View File

@@ -13,32 +13,33 @@ def blacklisted(user):


class CustomCommands():
def __init__(self, Bot):
self.bot = Bot
def __init__(self, bot_client):
self.bot = bot_client
self.con = ServerConfig()
self.servers = self.con.servers

async def on_message(self, message):
if blacklisted(message.author) or message.channel.type == discord.ChannelType.private:
if blacklisted(message.author) or type(message.channel) != discord.TextChannel:
return
msg = message.content.lower()
channel = message.channel
server = message.server.id
server = str(message.guild.id)
if message.author == self.bot.user:
return
if msg.startswith(self.bot.command_prefix):
if msg.split(self.bot.command_prefix)[1] in self.servers[server]["custom_commands"]["1"]:
return await self.bot.send_message(channel, self.servers[server]["custom_commands"]["1"][msg.split(self.bot.command_prefix)[1]])
elif len(msg.split(" ")) < 2:
if msg.split(" ")[0] in self.servers[server]["custom_commands"]["0"]:
return await self.bot.send_message(channel, self.servers[server]["custom_commands"]["0"][msg.split(" ")[0]])
return await channel.send(self.servers[server]["custom_commands"]["1"][msg.split(self.bot.command_prefix)[1]])
else:
for command in self.servers[server]["custom_commands"]["0"]:
if msg == command:
return await channel.send(self.servers[server]["custom_commands"]["0"][command])

@group(pass_context=True, aliases=["cc"])
@checks.is_owner_or_admin()
async def custom(self, ctx):
"A group of commands to manage custom commands for your server."
if ctx.invoked_subcommand is None:
return await self.bot.say('Missing Argument')
return await ctx.send('Missing Argument')

@custom.command(pass_context=True)
async def add(self, ctx, command, output, prefix_required = "0"):
@@ -46,60 +47,62 @@ class CustomCommands():
self.servers = self.con.load_config()
command = command.lower()
output = output
zero = self.servers[ctx.message.server.id]["custom_commands"]["0"]
one = self.servers[ctx.message.server.id]["custom_commands"]["1"]
zero = self.servers[str(ctx.guild.id)]["custom_commands"]["0"]
one = self.servers[str(ctx.guild.id)]["custom_commands"]["1"]

if ctx.message.mentions:
return await self.bot.say("Custom Commands cannot mention people.")
elif len(output) > 1999: # This probably wont happen atm since the command itself would make the whole message over len 2000 which would be impossible to send. But this is here incase we need to adjust this number.
return await self.bot.say("The output is too long")
if ctx.message.mentions or ctx.message.mention_everyone or ctx.message.role_mentions:
return await ctx.send("Custom Commands cannot mention people/roles/everyone.")
elif len(output) > 1800:
return await ctx.send("The output is too long")
elif command in self.bot.commands and prefix_required == "1":
return await self.bot.say("This is already the name of a built in command.")
return await ctx.send("This is already the name of a built in command.")
elif command in zero or command in one:
return await self.bot.say("Custom Command already exists.")
return await ctx.send("Custom Command already exists.")
elif prefix_required != "1" and prefix_required != "0":
return await self.bot.say("No prefix setting set.")
return await ctx.send("No prefix setting set.")
elif len(command.split(" ")) > 1 and prefix_required == "1":
return await ctx.send("Custom commands with a prefix can only be one word with no spaces.")

self.servers[ctx.message.server.id]["custom_commands"][prefix_required][command] = output
self.servers[str(ctx.guild.id)]["custom_commands"][prefix_required][command] = output
self.con.update_config(self.servers)
return await self.bot.say("{} has been added with the output: '{}'".format(command, output))
return await ctx.send("{} has been added with the output: '{}'".format(command, output))

@custom.command(pass_context=True)
async def edit(self, ctx, command, edit):
"Edits an existing custom command."
self.servers = self.con.load_config()
zero = self.servers[ctx.message.server.id]["custom_commands"]["0"]
one = self.servers[ctx.message.server.id]["custom_commands"]["1"]
zero = self.servers[str(ctx.guild.id)]["custom_commands"]["0"]
one = self.servers[str(ctx.guild.id)]["custom_commands"]["1"]

if ctx.message.mentions:
return await self.bot.say("Custom Commands cannot mention people.")
if ctx.message.mentions or ctx.message.mention_everyone or ctx.message.role_mentions:
return await ctx.send("Custom Commands cannot mention people/roles/everyone.")

if command in zero:
self.servers[ctx.message.server.id]["custom_commands"]["0"][command] = edit
self.servers[str(ctx.guild.id)]["custom_commands"]["0"][command] = edit
self.con.update_config(self.servers)
return await self.bot.say("Edit made. {} now outputs {}".format(command, edit))
return await ctx.send("Edit made. {} now outputs {}".format(command, edit))
elif command in one:
self.servers[ctx.message.server.id]["custom_commands"]["1"][command] = edit
self.servers[str(ctx.guild.id)]["custom_commands"]["1"][command] = edit
self.con.update_config(self.servers)
return await self.bot.say("Edit made. {} now outputs {}".format(command, edit))
return await ctx.send("Edit made. {} now outputs {}".format(command, edit))
else:
return await self.bot.say("That Custom Command doesn't exist.")
return await ctx.send("That Custom Command doesn't exist.")

@custom.command(pass_context=True)
async def remove(self, ctx, command):
"Removes a custom command."
self.servers = self.con.load_config()
command = command.lower()
if command in self.servers[ctx.message.server.id]["custom_commands"]["1"]:
self.servers[ctx.message.server.id]["custom_commands"]["1"].pop(command)
if command in self.servers[str(ctx.guild.id)]["custom_commands"]["1"]:
self.servers[str(ctx.guild.id)]["custom_commands"]["1"].pop(command)
self.con.update_config(self.servers)
return await self.bot.say("Removed {} custom command".format(command))
elif command in self.servers[ctx.message.server.id]["custom_commands"]["0"]:
self.servers[ctx.message.server.id]["custom_commands"]["0"].pop(command)
return await ctx.send("Removed {} custom command".format(command))
elif command in self.servers[str(ctx.guild.id)]["custom_commands"]["0"]:
self.servers[str(ctx.guild.id)]["custom_commands"]["0"].pop(command)
self.con.update_config(self.servers)
return await self.bot.say("Removed {} custom command".format(command))
return await ctx.send("Removed {} custom command".format(command))
else:
return await self.bot.say("Custom Command doesn't exist.")
return await ctx.send("Custom Command doesn't exist.")


@custom.command(pass_context=True)
@@ -108,7 +111,7 @@ class CustomCommands():
if debug != "0" and debug != "1":
debug = "0"
self.servers = self.con.load_config()
l = self.servers[ctx.message.server.id]["custom_commands"]
l = self.servers[str(ctx.guild.id)]["custom_commands"]
listzero = ""
listone = ""

@@ -130,7 +133,7 @@ class CustomCommands():
em = discord.Embed(title="Here is the list of Custom Commands", color=load_config.embedcolour)
em.add_field(name="Commands that require Prefix:", value=listone, inline=False)
em.add_field(name="Commands that don't:", value=listzero, inline=False)
return await self.bot.say(embed=em)
return await ctx.send(embed=em)

def setup(Bot):
Bot.add_cog(CustomCommands(Bot))
def setup(bot_client):
bot_client.add_cog(CustomCommands(bot_client))

+ 42
- 34
cogs/fun.py View File

@@ -1,14 +1,15 @@
import discord
import random
import checks
import requests
from discord.ext.commands import bot


class Fun():
def __init__(self, Bot):
self.bot = Bot
class Fun:
def __init__(self, bot_client):
self.bot = bot_client

@bot.command(pass_context=True)
@bot.command()
async def roll(self, ctx, die):
"""
Rolls a die using ndx format.
@@ -17,22 +18,21 @@ class Fun():
Example:
.roll 2d20 # Rolls two D20s
"""
# TODO: Change to ndx format
dice = 0
if die[0].isdigit():
if die[1].isdigit() or die[0] == 0:
return await self.bot.say("I only support multipliers from 1-9")
return await ctx.send("I only support multipliers from 1-9")
multiplier = int(die[0])
else:
multiplier = 1
if die[1].lower() != "d" and die[0].lower() != "d":
return await self.bot.say("Use the format 'ndx'.")
return await ctx.send("Use the format 'ndx'.")
options = (4, 6, 8, 10, 12, 20, 100)
for option in options:
if die.endswith(str(option)):
dice = option
if dice == 0:
return await self.bot.say("You didn't give a die to use.")
return await ctx.send("You didn't give a die to use.")

rolls = []
if dice == 100:
@@ -46,50 +46,50 @@ class Fun():
rolls.append(random.randrange(step, dice+1, step))
for r in rolls:
total += r
return await self.bot.say("{} rolled **{}**. Totaling **{}**".format(ctx.message.author.mention, rolls, total))
return await ctx.send("{} rolled **{}**. Totaling **{}**".format(ctx.message.author.mention, rolls, total))
else:
roll = random.randrange(step, dice + 1, step)
return await self.bot.say("{} rolled a **{}**".format(ctx.message.author.mention, roll))
return await ctx.send("{} rolled a **{}**".format(ctx.message.author.mention, roll))

@checks.isnt_anal()
@bot.command(pass_context=True)
@bot.command()
async def spank(self, ctx, *, user: discord.User = None):
"""
Spanks the mentioned user ;)
Usage:
{command_prefix}spank @RoxBot#4170
{command_prefix}spank RoxBot
{command_prefix}spank @Roxbot_client#4170
{command_prefix}spank Roxbot_client
"""
if not user:
return await self.bot.say("You didn't mention someone for me to spank")
return await self.bot.say(":peach: :wave: *{} spanks {}*".format(self.bot.user.name, user.name))
return await ctx.send("You didn't mention someone for me to spank")
return await ctx.send(":peach: :wave: *{} spanks {}*".format(self.bot.user.name, user.name))

@checks.isnt_anal()
@bot.command(pass_context=True, aliases=["succ"])
@bot.command(aliases=["succ"])
async def suck(self, ctx, *, user: discord.User = None):
"""
Sucks the mentioned user ;)
Usage:
{command_prefix}suck @RoxBot#4170
{command_prefix}suck RoxBot
{command_prefix}suck @Roxbot_client#4170
{command_prefix}suck Roxbot_client
"""
if not user:
return await self.bot.say("You didn't mention someone for me to suck")
return await self.bot.say(":eggplant: :sweat_drops: :tongue: *{} sucks {}*".format(self.bot.user.name, user.name))
return await ctx.send("You didn't mention someone for me to suck")
return await ctx.send(":eggplant: :sweat_drops: :tongue: *{} sucks {}*".format(self.bot.user.name, user.name))

@bot.command(pass_context=True)
@bot.command()
async def hug(self, ctx, *, user: discord.User = None):
"""
Hugs the mentioned user :3
Usage:
{command_prefix}hug @RoxBot#4170
{command_prefix}hug RoxBot
{command_prefix}hug @Roxbot_client#4170
{command_prefix}hug Roxbot_client
"""
if not user:
return await self.bot.say("You didn't mention someone for me to hug")
return await self.bot.say(":blush: *{} hugs {}*".format(self.bot.user.name, user.name))
return await ctx.send("You didn't mention someone for me to hug")
return await ctx.send(":blush: *{} hugs {}*".format(self.bot.user.name, user.name))

@bot.command(pass_context=True, aliases=["wf"])
@bot.command(aliases=["wf"])
async def waifurate(self, ctx):
"""
Rates the mentioned waifu(s)
@@ -115,23 +115,31 @@ class Fun():
emoji = ":heart_eyes:"

if len(mentions) > 1:
return await self.bot.say("Oh poly waifu rating? :smirk: Your combined waifu rating is {}/10. {}".format(rating, emoji))
return await ctx.send("Oh poly waifu rating? :smirk: Your combined waifu rating is {}/10. {}".format(rating, emoji))
else:
return await self.bot.say("Oh that's your waifu? I rate them a {}/10. {}".format(rating, emoji))
return await ctx.send("Oh that's your waifu? I rate them a {}/10. {}".format(rating, emoji))

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

@bot.command(pass_context=True)
@bot.command()
async def aesthetics(self, ctx, *convert):
"""Converts text to be more a e s t h e t i c s"""
WIDE_MAP = dict((i, i + 0xFEE0) for i in range(0x21, 0x7F))
WIDE_MAP[0x20] = 0x3000
convert = str(' '.join(convert)).translate(WIDE_MAP)
return await self.bot.say(convert)
return await ctx.send(convert)

@bot.command(aliases=["ft", "frog"])
async def frogtips(self, ctx):
"""RETURNS FROG TIPS FOR YOU FROG."""
endpoint = "https://frog.tips/api/1/tips/"
croak = requests.get(endpoint)
tip = random.choice(croak.json()["tips"])
embed = discord.Embed(title="Frog Tip #{}".format(tip["number"]), description=tip["tip"], colour=discord.Colour(0x4C943D))
return await ctx.send(embed=embed)

def setup(Bot):
Bot.add_cog(Fun(Bot))
def setup(bot_client):
bot_client.add_cog(Fun(bot_client))

+ 24
- 34
cogs/gss.py View File

@@ -8,22 +8,22 @@ from config.server_config import ServerConfig


def is_gss():
return commands.check(lambda ctx: ctx.message.server.id == "393764974444675073")
return commands.check(lambda ctx: ctx.guild.id == 393764974444675073)

def is_not_nsfw_disabled():
def predicate(ctx):
role = utils.get(ctx.message.server.roles, id="397866388145831937")
return role not in ctx.message.author.roles
role = utils.get(ctx.guild.roles, id=397866388145831937)
return role not in ctx.author.roles
return commands.check(lambda ctx: predicate(ctx))

class GaySoundsShitposting():
def __init__(self, Bot):
self.bot = Bot
def __init__(self, bot_client):
self.bot = bot_client
self.con = ServerConfig()
self.servers = self.con.servers
self.guild = self.bot.get_server("393764974444675073")
self.nsfw_image_role = utils.get(self.guild.roles, id="394941004043649036")
self.selfie_role = utils.get(self.guild.roles, id="394939389823811584")
self.guild = self.bot.get_guild(393764974444675073)
self.nsfw_image_role = utils.get(self.guild.roles, id=394941004043649036)
self.selfie_role = utils.get(self.guild.roles, id=394939389823811584)

def tatsumaki_api_call(self, member):
base = "https://api.tatsumaki.xyz/"
@@ -35,27 +35,22 @@ class GaySoundsShitposting():
@bot.command(pass_context=True)
async def selfieperms(self, ctx):
"""Requests the selfie perm role."""
member = ctx.message.author
member = ctx.author
required_score = int(self.servers[self.guild.id]["gss"]["required_score"])
days = int(self.servers[self.guild.id]["gss"]["required_days"])
logging = self.servers[self.guild.id]["gss"]["log_channel"]
data = self.tatsumaki_api_call(member)

if self.selfie_role in member.roles:
await self.bot.remove_roles(member, self.selfie_role)
if logging:
await self.bot.send_message(self.bot.get_channel(logging), content="{} has removed the {} role.".format(member.mention, self.nsfw_image_role.name))
return await self.bot.say("You already had {}. It has now been removed.".format(self.selfie_role.name))
await member.remove_roles(self.selfie_role, reason="Requested removal of Selfie Perms")
return await ctx.send("You already had {}. It has now been removed.".format(self.selfie_role.name))

time = datetime.datetime.now() - ctx.message.author.joined_at
time = datetime.datetime.now() - ctx.author.joined_at

if time > datetime.timedelta(days=days) and int(data["score"]) >= required_score:
await self.bot.add_roles(member, self.selfie_role)
await self.bot.say("You have now have the {} role".format(self.selfie_role.name))
if logging:
return await self.bot.send_message(self.bot.get_channel(logging), content="{} has requested the {} role.".format(member.mention, self.selfie_role.name))
await member.add_roles(member, self.selfie_role, reason="Requested Selfie perms")
await ctx.send("You have now have the {} role".format(self.selfie_role.name))
else:
return await self.bot.say(
return await ctx.send(
"You do not meet the requirements for this role. You need at least {} score with <@!172002275412279296> and to have been in the server for {} days.".format(required_score, days)
)

@@ -64,29 +59,24 @@ class GaySoundsShitposting():
@bot.command(pass_context=True)
async def nsfwperms(self, ctx):
"""Requests the NSFW Image Perm role."""
member = ctx.message.author
member = ctx.author
required_score = int(self.servers[self.guild.id]["gss"]["required_score"])
days = int(self.servers[self.guild.id]["gss"]["required_days"])
logging = self.servers[self.guild.id]["gss"]["log_channel"]
data = self.tatsumaki_api_call(member)

if self.nsfw_image_role in member.roles:
await self.bot.remove_roles(member, self.nsfw_image_role)
if logging:
await self.bot.send_message(self.bot.get_channel(logging), content="{} has removed the {} role.".format(member.mention, self.nsfw_image_role.name))
return await self.bot.say("You already had {}. It has now been removed.".format(self.nsfw_image_role.name))
await member.remove_roles(self.nsfw_image_role, reason="Requested removal of NSFW Perms")
return await ctx.send("You already had {}. It has now been removed.".format(self.nsfw_image_role.name))

time = datetime.datetime.now() - ctx.message.author.joined_at
time = datetime.datetime.now() - ctx.author.joined_at

if time > datetime.timedelta(days=days) and int(data["score"]) >= required_score:
await self.bot.add_roles(member, self.nsfw_image_role)
await self.bot.say("You have now have the {} role".format(self.nsfw_image_role.name))
if logging:
return await self.bot.send_message(self.bot.get_channel(logging), content="{} has given themselves the {} role.".format(member.mention, self.nsfw_image_role.name))
await member.add_roles(self.nsfw_image_role, reason="Requested NSFW perms")
await ctx.send("You have now have the {} role".format(self.nsfw_image_role.name))
else:
return await self.bot.say(
return await ctx.send(
"You do not meet the requirements for this role. You need at least {} score with <@!172002275412279296> and to have been in the server for {} days.".format(required_score, days)
)

def setup(Bot):
Bot.add_cog(GaySoundsShitposting(Bot))
def setup(bot_client):
bot_client.add_cog(GaySoundsShitposting(bot_client))

+ 10
- 12
cogs/joinleave.py View File

@@ -13,35 +13,33 @@ class JoinLeave():
Greets users when they join a server.
"""
self.con.load_config()
if not self.servers[member.server.id]["greets"]["enabled"]:
if not self.servers[str(member.guild.id)]["greets"]["enabled"]:
return

if self.servers[member.server.id]["greets"]["custom-message"]:
message = self.servers[member.server.id]["greets"]["custom-message"]
if self.servers[str(member.guild.id)]["greets"]["custom-message"]:
message = self.servers[str(member.guild.id)]["greets"]["custom-message"]
else:
message = self.servers[member.server.id]["greets"]["default-message"]
message = self.servers[str(member.guild.id)]["greets"]["default-message"]
em = discord.Embed(
title="Welcome to {}!".format(member.server),
description='Hey {}! Welcome to **{}**! {}'.format(member.mention, member.server, message),
colour=0xDEADBF)
em.set_thumbnail(url=member.avatar_url)

if self.servers[member.server.id]["greets"]["welcome-channel"]:
channel = discord.Object(self.servers[member.server.id]["greets"]["welcome-channel"])
else:
channel = member.server.default_channel
return await self.bot.send_message(channel, embed=em)
channel = self.bot.get_channel(self.servers[str(member.guild.id)]["greets"]["welcome-channel"])
return await channel.send(embed=em)

async def on_member_remove(self, member):
"""
The same but the opposite
"""
self.con.load_config()
channel = self.servers[member.server.id]["goodbyes"]["goodbye-channel"]
if not self.servers[member.server.id]["goodbyes"]["enabled"]:
channel = self.servers[str(member.guild.id)]["goodbyes"]["goodbye-channel"]
if not self.servers[str(member.guild.id)]["goodbyes"]["enabled"]:
return
else:
return await self.bot.send_message(self.bot.get_channel(channel) ,embed=discord.Embed(
channel = self.bot.get_channel(channel)
return await channel.send(embed=discord.Embed(
description="{}#{} has left or been beaned.".format(member.name, member.discriminator), colour=0xDEADBF))



+ 30
- 26
cogs/nsfw.py View File

@@ -1,23 +1,25 @@
import random
from json import JSONDecodeError
import checks
import requests
from discord.ext.commands import bot
from config.server_config import ServerConfig


class NFSW():
def __init__(self, Bot):
self.bot = Bot
self.con = ServerConfig()
self.servers = self.con.servers
def __init__(self, bot_client):
self.bot = bot_client
self.servers = ServerConfig().load_config()

def is_nsfw_enabled(self, server_id):
return self.servers[server_id]["nsfw"]["enabled"] == "1"
def tag_blacklist(self, ctx):
blacklist = ""
for tag in self.servers[str(ctx.guild.id)]["nsfw"]["blacklist"]:
blacklist += "-{} ".format(tag)
return blacklist

def gelbooru_clone(self, base_url, tags):
def gelbooru_clone(self, ctx, base_url, tags):
# Maybe a page randomiser
limit = 100
limit = 200
tags = tags + self.tag_blacklist(ctx)
print(tags)
url = base_url + '/index.php?page=dapi&s=post&q=index&json=1&tags=' + tags + '&limit=' + str(limit)
req = requests.get(url, headers={'User-agent': 'RoxBot Discord Bot'})
if str(req.content) == "b''": # This is to catch any errors if the tags don't return anything because I can't do my own error handling in commands.
@@ -26,47 +28,49 @@ class NFSW():
post = random.choice(req.json())
return post

@bot.command(pass_context=True)
@bot.command()
@checks.is_nfsw_enabled()
async def e621(self, ctx, *, tags):
async def e621(self, ctx, *, tags = ""):
"""
Returns an image from e621.com and can use tags you provide.
"""
tags = tags + self.tag_blacklist(ctx)
print(tags)
base_url = "https://e621.net/"
limit = 150
url = base_url + 'post/index.json?tags=' + tags + '&limit=' + str(limit)
req = requests.get(url, headers = {'User-agent': 'RoxBot Discord Bot'})
if str(req.content) == "b'[]'": # This is to catch any errors if the tags don't return anything because I can't do my own error handling in commands.
return await self.bot.say("Nothing was found. *psst, check the tags you gave me.*")
return await ctx.send("Nothing was found. *psst, check the tags you gave me.*")
post = random.choice(req.json())
return await self.bot.say(post["file_url"])
return await ctx.send(post["file_url"])

@bot.command(pass_context=True)
@bot.command()
@checks.is_nfsw_enabled()
async def rule34(self, ctx, *, tags):
async def rule34(self, ctx, *, tags = ""):
"""
Returns an image from rule34.xxx and can use tags you provide.
"""
base_url = "https://rule34.xxx"
post = self.gelbooru_clone(base_url, tags)
post = self.gelbooru_clone(ctx, base_url, tags)
if not post:
return await self.bot.say("Nothing was found. *psst, check the tags you gave me.*")
return await ctx.send("Nothing was found. *psst, check the tags you gave me.*")
url = "https://img.rule34.xxx/images/" + post["directory"] + "/" + post["image"]
return await self.bot.say(url)
return await ctx.send(url)

@bot.command(pass_context=True)
@bot.command()
@checks.is_nfsw_enabled()
async def gelbooru(self, ctx, *, tags):
async def gelbooru(self, ctx, *, tags = ""):
"""
Returns an image from gelbooru.com and can use tags you provide.
"""
base_url = "https://gelbooru.com"
post = self.gelbooru_clone(base_url, tags)
post = self.gelbooru_clone(ctx, base_url, tags)
if not post:
return await self.bot.say("Nothing was found. *psst, check the tags you gave me.*")
return await ctx.send("Nothing was found. *psst, check the tags you gave me.*")
url = "https://simg3.gelbooru.com/images/" + ''.join(post["directory"].split("\\")) + "/" + post["image"]
return await self.bot.say(url)
return await ctx.send(url)


def setup(Bot):
Bot.add_cog(NFSW(Bot))
def setup(bot_client):
bot_client.add_cog(NFSW(bot_client))

+ 28
- 140
cogs/reddit.py View File

@@ -1,7 +1,6 @@
from discord.ext.commands import bot
from config.server_config import ServerConfig
from lxml import html
import os
import random
import requests
from bs4 import BeautifulSoup
@@ -10,29 +9,6 @@ import checks
# Warning, this cog sucks so much but hopefully it works and doesn't break the bot too much. Just lazily edited old code and bodged it into this one.
# There is redundant code here that if removed would make it easier. But it might be handy in the future and isn't that bad.


class RedditMedia:
def get(self, url):
return url


class Gfycat():
def __init__(self):
pass

def url_get(self,url):
urlsplit = url.split("/")
urlsplit[2] = "giant." + urlsplit[2]
urlsplit[-1] += ".gif"
urlnew = "/".join(urlsplit)
return urlnew

def get(self,url):
#url2 = self.url_get(url)
url2 = url
return url2


class Imgur():
"""Class for all interactions with Imgur"""
def __init__(self):
@@ -49,11 +25,6 @@ class Imgur():
def get(self, url):
if url.split(".")[-1] in ("png", "jpg", "jpeg", "gif", "gifv"):
return url
#elif url.split(".")[-1] == "gifv":
# urlsplit = url.split(".")
# urlsplit[-1] = "gif"
# url = ".".join(urlsplit)
# return url"""
else:
if self.removed(url):
return False
@@ -77,23 +48,10 @@ class Imgur():
links[0] = "https:" + links[0]
return links[0]


class Eroshare():
def __init__(self):
pass

def album_create(self, name):
self.album_create.hasbeencalled = True
charlist = ("<", ">", '"', ":", "/", "|", "?", "*")
# Can't use these in Windows Dir so next code used to remove chars from title
for char in charlist:
if char in name:
name = name.replace(char, "")
if name not in os.listdir("./"):
os.mkdir("./" + name)
os.chdir("./" + name)


def get(self, url, name=None):
if url.contains("eroshare"):
url = "https://eroshae.com/" + url.split("/")[3]
@@ -102,32 +60,12 @@ class Eroshare():
links = tree.xpath('//source[@src]/@src')
if links:
return False
#self.album_create(name)
#for link in links:
# if "lowres" not in link:
# wget.download(link)
# print("Downloaded ", link)
links = tree.xpath('//*[@src]/@src')
if len(links) > 2: #and not self.album_create.hasbeencalled:
if len(links) > 2:
return False
#self.album_create(name)
for link in links:
if "i." in link and "thumb" not in link:
return "https:" + link
#if link.split("/")[-1] not in os.listdir("./"):
#wget.download("https:" + link)
#print("Downloaded ", link)
#else:
# print("Already exists")
#if album_create.hasbeencalled:
# os.chdir("../")
# album_create.hasbeencalled = False


class Tumblr():
def get(self,url):
return url


class Scrapper():
def __init__(self):
@@ -146,130 +84,80 @@ class Scrapper():
return reddit

def retriveurl(self, url):
url2 = ""
if url.split(".")[-1] in ("png", "jpg", "jpeg", "gif", "gifv", "webm", "mp4", "webp"):
return url
if "imgur" in url:
url2 = Imgur().get(url)
elif "gfycat" in url:
url2 = Gfycat().get(url)
return Imgur().get(url)
elif "eroshare" in url:
url2 = Eroshare().get(url)
elif "redd.it" in url or "i.reddituploads" in url:
url2 = RedditMedia().get(url)
elif "media.tumblr" in url:
url2 = Tumblr().get(url)
return url2

return Eroshare().get(url)
elif "gfycat" in url or "redd.it" in url or "i.reddituploads" in url or "media.tumblr" in url or "streamable" in url:
return url

class Reddit():
def __init__(self, Bot):
self.bot = Bot
def __init__(self, bot_client):
self.bot = bot_client
self.con = ServerConfig()
self.servers = self.con.servers

@bot.command(pass_context=True)
@bot.command()
async def subreddit(self, ctx, subreddit):
"""
Grabs an image (png, gif, gifv, webm) from the subreddit inputted.
Exmaple:
Grabs an image or video (jpg, png, gif, gifv, webm, mp4) from the subreddit inputted.
Example:
{command_prefix}subreddit pics
"""
subreddit = subreddit.lower()
links = Scrapper().linkget(subreddit, True)
title = ""
if not links:
return await self.bot.say("Error ;-; That subreddit probably doesn't exist. Please check your spelling")
return await ctx.send("Error ;-; That subreddit probably doesn't exist. Please check your spelling")
url = ""
for x in range(10):
choice = random.choice(links)
title = "**{}** from /r/{}\n".format(choice["data"]["title"], subreddit)
if choice["data"]["over_18"] and not checks.nsfw_predicate(ctx):
return await self.bot.say("This server/channel doesn't have my NSFW stuff enabled. This extends to posting NFSW content from Reddit.")
return await ctx.send("This server/channel doesn't have my NSFW stuff enabled. This extends to posting NFSW content from Reddit.")
url = Scrapper().retriveurl(choice["data"]["url"])
if url:
break
if not url:
return await self.bot.say("I couldn't find any images from that subreddit.")
return await ctx.send("I couldn't find any images from that subreddit.")

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

return await self.bot.say(title + text + url)
return await ctx.send(title + text + url)


@bot.command(pass_context=True)
@bot.command()
async def aww(self, ctx):
"""
Gives you cute pics from reddit
"""
subreddit = "aww"
links = Scrapper().linkget(subreddit, True)
if not links:
return await self.bot.say("Error ;-; That subreddit probably doesn't exist. Please check your spelling")
return await ctx.invoke(self.subreddit, subreddit=subreddit)

choice = random.choice(links)
title = "**{}** from /r/{}\n".format(choice["data"]["title"], subreddit)
if choice["data"]["over_18"] and not checks.nsfw_predicate(ctx):
return await self.bot.say(
"This server doesn't have my NSFW stuff enabled. This extends to posting NFSW content from Reddit.")
url = Scrapper().retriveurl(choice["data"]["url"])

if url.split("/")[-2] == "a":
text = "This is an album, click on the link to see more. "
else:
text = ""
return await self.bot.say(title + text + url)


@bot.command(pass_context=True)
@bot.command()
async def feedme(self, ctx):
"""
Feeds you with food porn. Uses multiple subreddits
Feeds you with food porn. Uses multiple subreddits.
Yes, I was very hungry when trying to find the subreddits for this command.
Subreddits: "foodporn", "food", "DessertPorn", "tonightsdinner", "eatsandwiches", "steak", "burgers", "Pizza", "grilledcheese", "PutAnEggOnIt", "sushi"
"""
subreddits = ["foodporn", "food", "DessertPorn", "tonightsdinner", "eatsandwiches", "steak", "burgers", "Pizza", "grilledcheese", "PutAnEggOnIt", "sushi"]
subreddit = random.choice(subreddits)
links = Scrapper().linkget(subreddit, True)
if not links:
return await self.bot.say("Error ;-; That subreddit probably doesn't exist. Please check your spelling")

choice = random.choice(links)
title = "**{}** from /r/{}\n".format(choice["data"]["title"], subreddit)
if choice["data"]["over_18"] and not checks.nsfw_predicate(ctx):
return await self.bot.say(
"This server doesn't have my NSFW stuff enabled. This extends to posting NFSW content from Reddit.")
url = Scrapper().retriveurl(choice["data"]["url"])

if url.split("/")[-2] == "a":
text = "This is an album, click on the link to see more. "
else:
text = ""
return await self.bot.say(title + text + url)

subreddit_choice = random.choice(subreddits)
return await ctx.invoke(self.subreddit, subreddit=subreddit_choice)

@bot.command(pass_context=True)
@bot.command(aliases=["gssp"])
async def gss(self, ctx):
"""
Gives you the best trans memes ever
"""
subreddit = "gaysoundsshitposts"
links = Scrapper().linkget(subreddit, True)
if not links:
return await self.bot.say("Error ;-; That subreddit probably doesn't exist. Please check your spelling")

choice = random.choice(links)
title = "**{}** from /r/{}\n".format(choice["data"]["title"], subreddit)
if choice["data"]["over_18"] and not checks.nsfw_predicate(ctx):
return await self.bot.say(
"This server doesn't have my NSFW stuff enabled. This extends to posting NFSW content from Reddit.")
url = Scrapper().retriveurl(choice["data"]["url"])

if url.split("/")[-2] == "a":
text = "This is an album, click on the link to see more. "
else:
text = ""
return await self.bot.say(title + text + url)
return await ctx.invoke(self.subreddit, subreddit=subreddit)


def setup(Bot):
Bot.add_cog(Reddit(Bot))
def setup(bot_client):
bot_client.add_cog(Reddit(bot_client))

+ 41
- 64
cogs/selfassign.py View File

@@ -1,7 +1,7 @@
import discord
from discord.ext import commands

import checks
import load_config
from config.server_config import ServerConfig


@@ -10,6 +10,7 @@ class SelfAssign():
self.bot = Bot
self.con = ServerConfig()
self.servers = self.con.servers
self.embed_colour = load_config.embedcolour

@commands.command(pass_context=True)
async def listroles(self, ctx):
@@ -18,19 +19,19 @@ class SelfAssign():
Usage:
{command_prefix}listroles
"""
if not self.servers[ctx.message.server.id]["self_assign"]["enabled"]:
embed = discord.Embed(colour=discord.Colour(0xDEADBF), # Make Embed colour a constant
if not self.servers[str(ctx.guild.id)]["self_assign"]["enabled"]:
embed = discord.Embed(colour=discord.Colour(self.embed_colour),
description="SelfAssignable roles are not enabled on this server")
return await self.bot.say(embed=embed)
return await ctx.send(embed=embed)
roles = []
for role in self.servers[ctx.message.server.id]["self_assign"]["roles"]:
for serverrole in ctx.message.server.roles:
for role in self.servers[str(ctx.guild.id)]["self_assign"]["roles"]:
for serverrole in ctx.guild.roles:
if role == serverrole.id:
roles.append("**"+serverrole.name+"**")
roles = '\n'.join(roles)
embed = discord.Embed(colour=discord.Colour(0xDEADBF), # Make Embed colour a constant
embed = discord.Embed(colour=self.embed_colour,
description="The self-assignable roles for this server are: \n"+roles)
return await self.bot.say(embed=embed)
return await ctx.send(embed=embed)

@commands.command(pass_context=True)
async def iam(self, ctx, *, role: discord.Role = None):
@@ -41,26 +42,25 @@ class SelfAssign():
Example:
.iam OverwatchPing
"""
self.servers = self.con.load_config()
if not self.servers[ctx.message.server.id]["self_assign"]["enabled"]:
return
if not self.servers[str(ctx.guild.id)]["self_assign"]["enabled"]:
embed = discord.Embed(colour=discord.Colour(self.embed_colour),
description="SelfAssignable roles are not enabled on this server")
return await ctx.send(embed=embed)

user = ctx.message.author
server = ctx.message.server
user = ctx.author
server = ctx.guild

if role not in server.roles:
return await self.bot.say("That role doesn't exist. Roles are case sensitive. ")
return await ctx.send("That role doesn't exist. Roles are case sensitive. ")

if role in user.roles:
return await self.bot.say("You already have that role.")
return await ctx.send("You already have that role.")

if role.id in self.servers[ctx.message.server.id]["self_assign"]["roles"]:
await self.bot.add_roles(user, role)
print("{} added {} to themselves in {} on {}".format(user.display_name, role.name, ctx.message.channel,
ctx.message.server))
return await self.bot.say("Yay {}! You now have the {} role!".format(user.mention, role.name))
if role.id in self.servers[str(ctx.guild.id)]["self_assign"]["roles"]:
await user.add_roles(role, reason="'iam' command triggered.")
return await ctx.send("Yay {}! You now have the {} role!".format(user.mention, role.name))
else:
return await self.bot.say("That role is not self-assignable.")
return await ctx.send("That role is not self-assignable.")

@commands.command(pass_context=True)
async def iamn(self, ctx, *, role: discord.Role = None):
@@ -71,54 +71,31 @@ class SelfAssign():
Example:
.iamn OverwatchPing
"""
self.servers = self.con.load_config()
if not self.servers[ctx.message.server.id]["self_assign"]["enabled"]:
print("Self Assign is Disabled")
return

user = ctx.message.author
server = ctx.message.server

if role not in server.roles:
return await self.bot.say("That role doesn't exist. Roles are case sensitive. ")
if not self.servers[str(ctx.guild.id)]["self_assign"]["enabled"]:
embed = discord.Embed(colour=discord.Colour(self.embed_colour),
description="SelfAssignable roles are not enabled on this server")
return await ctx.send(embed=embed)

elif role in user.roles and role.id in self.servers[ctx.message.server.id]["self_assign"]["roles"]:
print("passed in server check")
await self.bot.remove_roles(user, role)
return await self.bot.reply("{} has been successfully removed.".format(role.name))
user = ctx.author
server = ctx.guild

elif role not in user.roles and role.id in self.servers[ctx.message.server.id]["self_assign"]["roles"]:
return await self.bot.reply("You do not have {}.".format(role.name))
if role in user.roles and role.id in self.servers[str(ctx.guild.id)]["self_assign"]["roles"]:
await user.remove_roles(role, reason="'iamn' command triggered.")
return await ctx.send("{} has been successfully removed.".format(role.name))
elif role not in user.roles and role.id in self.servers[str(ctx.guild.id)]["self_assign"]["roles"]:
return await ctx.send("You do not have {}.".format(role.name))
else:
return await self.bot.say("That role is not self-assignable.")
return await ctx.send("That role is not self-assignable.")

@commands.command(pass_context=True, hidden=True)
@checks.is_bot_owner()
async def addrole(self, ctx, *, role: discord.Role = None):
"""
] Adds a role to the list of roles that can be self assigned for that server.
"""
self.servers = self.con.load_config()
if role.id in self.servers[ctx.message.server.id]["self_assign"]["roles"]:
return await self.bot.say("{} is already a self-assignable role.".format(role.name), delete_after=self.con.delete_after)

self.servers[ctx.message.server.id]["self_assign"]["roles"].append(role.id)
self.con.update_config(self.servers)
return await self.bot.say('Role "{}" added'.format(str(role)))
@iam.error
async def iam_err(self, ctx, error):
if isinstance(error, commands.BadArgument):
return await ctx.send("This role doesn't exist. Reminder, roles are case-sensitive.")

@commands.command(pass_context=True, hidden=True)
@checks.is_bot_owner()
async def removerole(self, ctx, *, role: discord.Role = None):
"""
Removes a role from the list of self assignable roles for that server.
"""
self.servers = self.con.load_config()
if role.id in self.servers[ctx.message.server.id]["self_assign"]["roles"]:
self.servers[ctx.message.server.id]["self_assign"]["roles"].remove(role.id)
self.con.update_config(self.servers)
return await self.bot.say('"{}" has been removed from the self-assignable roles.'.format(str(role)))
else:
return await self.bot.say("That role was not in the list.")
@iamn.error
async def iamn_err(self, ctx, error):
if isinstance(error, commands.BadArgument):
return await ctx.send("This role doesn't exist. Reminder, roles are case-sensitive.")

def setup(Bot):
Bot.add_cog(SelfAssign(Bot))

+ 0
- 367
cogs/settings.py View File

@@ -1,367 +0,0 @@
import os
import sys
import aiohttp
import asyncio

import checks
import load_config
from config.server_config import ServerConfig

import discord
from discord.ext.commands import bot
from discord.ext.commands import group


class Settings():
"""
Settings is a mix of settings and admin stuff for the bot. OWNER OR ADMIN ONLY.
"""
def __init__(self, Bot):
self.bot = Bot
self.con = ServerConfig()
self.servers = self.con.servers

@bot.command(pass_context=True)
@checks.is_owner_or_admin()
async def blacklist(self, ctx, option, *args):
"""
OWNER OR ADMIN ONLY: Add or remove users to the blacklist.
Blacklisted users are forbidden from using bot commands.
"""
blacklist_amount = 0
mentions = ctx.message.mentions

if not mentions:
return await self.bot.say("You didn't mention anyone")

if option not in ['+', '-', 'add', 'remove']:
return await self.bot.say('Invalid option "%s" specified, use +, -, add, or remove' % option, expire_in=20)

for user in mentions:
if user.id == load_config.owner:
print("[Commands:Blacklist] The owner cannot be blacklisted.")
await self.bot.say("The owner cannot be blacklisted.")
mentions.remove(user)

if option in ['+', 'add']:
with open("config/blacklist.txt", "r") as fp:
for user in mentions:
for line in fp.readlines():
if user.id + "\n" in line:
mentions.remove(user)

with open("config/blacklist.txt", "a+") as fp:
lines = fp.readlines()
for user in mentions:
if user.id not in lines:
fp.write("{}\n".format(user.id))
blacklist_amount += 1
return await self.bot.say('{} user(s) have been added to the blacklist'.format(blacklist_amount))

elif option in ['-', 'remove']:
with open("config/blacklist.txt", "r") as fp:
lines = fp.readlines()
with open("config/blacklist.txt", "w") as fp:
for user in mentions:
for line in lines:
if user.id + "\n" != line:
fp.write(line)
else:
fp.write("")
blacklist_amount += 1
return await self.bot.say('{} user(s) have been removed from the blacklist'.format(blacklist_amount))

@bot.command(pass_context=True, hidden=True, aliases=["setava", "setavatar"])
@checks.is_bot_owner()
async def changeavatar(self, ctx, url=None):
"""
Usage:
{command_prefix}setavatar [url]
Changes the bot's avatar.
Attaching a file and leaving the url parameter blank also works.
"""
if ctx.message.attachments:
thing = ctx.message.attachments[0]['url']
else:
thing = url.strip('<>')

avaimg = 'avaimg'
async with aiohttp.ClientSession() as session:
async with session.get(thing) as img:
with open(avaimg, 'wb') as f:
f.write(await img.read())
with open(avaimg, 'rb') as f:
await self.bot.edit_profile(avatar=f.read())
os.remove(avaimg)
asyncio.sleep(2)
return await self.bot.say(":ok_hand:")

@bot.command(pass_context=True, hidden=True, aliases=["nick"])
@checks.is_bot_owner()
async def changenickname(self, ctx, *nick):
if ctx.message.channel.permissions_for(ctx.message.server.me).change_nickname:
await self.bot.change_nickname(ctx.message.server.me, ' '.join(nick))
return await self.bot.say(":thumbsup:")
else:
return await self.bot.say("I don't have permission to do that :sob:", delete_after=self.con.delete_after)

@bot.command(pass_context=True, hidden=True, aliases=["setgame", "game"])
@checks.is_bot_owner()
async def changegame(self, ctx, *, game: str):
if game.lower() == "none":
game_name = None
else:
game_name = discord.Game(name=game, type=0)
await self.bot.change_presence(game=game_name, afk=False)
return await self.bot.say(":ok_hand: Game set to {}".format(str(game_name)))

@bot.command(pass_context=True, hidden=True, aliases=["status"])
@checks.is_bot_owner()
async def changestatus(self, ctx, status: str):
status = status.lower()
if status == 'offline' or status == 'invisible':
discordStatus = discord.Status.invisible
elif status == 'idle':
discordStatus = discord.Status.idle
elif status == 'dnd':
discordStatus = discord.Status.dnd
else:
discordStatus = discord.Status.online
await self.bot.change_presence(status=discordStatus)
await self.bot.say("**:ok:** Status set to {}".format(discordStatus))

@bot.command(hidden=True)
@checks.is_bot_owner()
async def restart(self):
await self.bot.logout()
return os.execl(sys.executable, sys.executable, *sys.argv)

@bot.command(hidden=True)
@checks.is_bot_owner()
async def shutdown(self):
await self.bot.logout()
return exit(0)

@bot.command(pass_context=True, hidden=True)
@checks.is_bot_owner()
async def announce(self, ctx, *announcement):
"""
ONLY USE FOR SERIOUS ANNOUNCEMENTS
"""
# TODO: Make colour top level role colour
# TODO: Custom message for annoucement footer
embed = discord.Embed(title="RoxBot Announcement", colour=discord.Colour(0x306f99), description=' '.join(announcement))
embed.set_footer(text="This message has be automatically generated by a cute ass Roxie",
icon_url=self.bot.user.avatar_url)
for server in self.bot.servers:
await self.bot.send_message(server, embed=embed)
return await self.bot.say("Done!", delete_after=self.con.delete_after)

@bot.command(pass_context=True)
@checks.is_owner_or_admin()
async def printsettings(self, ctx):
"OWNER OR ADMIN ONLY: Prints the servers config file."
self.serverconfig = self.con.load_config()
config = self.serverconfig[ctx.message.server.id]
em = discord.Embed(colour=0xDEADBF)
em.set_author(name="{} settings for {}.".format(self.bot.user.name, ctx.message.server.name), icon_url=self.bot.user.avatar_url)

for settings in config:
if settings != "custom_commands" and settings != "warnings":
settingcontent = ""
for x in config[settings].items():
settingcontent += str(x).strip("()") + "\n"
em.add_field(name=settings, value=settingcontent, inline=False)
elif settings == "custom_commands":
em.add_field(name="custom_commands", value="For Custom Commands, use the custom list command.", inline=False)
elif settings == "warnings":
pass

return await self.bot.say(embed=em)


@group(pass_context=True)
async def settings(self, ctx):
pass

@settings.command(pass_context=True)
async def self_assign(self, ctx, selection, *, changes = None):
if selection == "enable":
pass



@bot.command(pass_context=True)
@checks.is_owner_or_admin()
async def enablesetting(self, ctx, setting):
"OWNER OR ADMIN ONLY: Enables settings in the server config."
self.serverconfig = self.con.load_config()
server_id = ctx.message.server.id
if setting in self.serverconfig[server_id]:
if not self.serverconfig[server_id][setting]["enabled"]:
self.serverconfig[server_id][setting]["enabled"] = 1
self.con.update_config(self.serverconfig)
return await self.bot.say("'{}' was enabled!".format(setting))
else:
self.serverconfig[server_id][setting]["enabled"] = 0
self.con.update_config(self.serverconfig)
return await self.bot.say("'{}' was disabled :cry:".format(setting))
else:
return await self.bot.say("That module dont exist fam. You made the thing")



@group(pass_context=True, hidden=True)
@checks.is_admin_or_mod()
async def set(self, ctx):
if ctx.invoked_subcommand is None:
return await self.bot.say('Missing Argument')

@set.command(pass_context=True, hidden=True)
async def welcomechannel(self, ctx, channel: discord.Channel = None):
self.servers = self.con.load_config()
self.servers[ctx.message.server.id]["greets"]["welcome-channel"] = channel.id
self.con.update_config(self.servers)
return await self.bot.say("{} has been set as the welcome channel!".format(channel.mention))

@set.command(pass_context=True, hidden=True)
async def goodbyechannel(self, ctx, channel: discord.Channel = None):
self.servers = self.con.load_config()
self.servers[ctx.message.server.id]["goodbyes"]["goodbye-channel"] = channel.id
self.con.update_config(self.servers)
return await self.bot.say("{} has been set as the goodbye channel!".format(channel.mention))

@set.command(pass_context=True, hidden=True)
async def twitchchannel(self, ctx, channel: discord.Channel = None): # Idk if this should be here, maybe in thw twitch cog?
self.servers = self.con.load_config()
self.servers[ctx.message.server.id]["twitch"]["twitch-channel"] = channel.id
self.con.update_config(self.servers)
return await self.bot.say("{} has been set as the twitch shilling channel!".format(channel.mention))

@set.command(pass_context=True, hidden=True)
async def welcomemessage(self, ctx, *, message: str):
print(ctx)
self.servers = self.con.load_config()
self.servers[ctx.message.server.id]["greets"]["custom-message"] = message
self.con.update_config(self.servers)
return await self.bot.say("Custom message set to '{}'".format(message))

@set.command(pass_context=True, hidden=True)
async def muterole(self, ctx, role: discord.Role = None):
self.servers = self.con.load_config()
self.servers[ctx.message.server.id]["mute"]["role"] = role.id
self.con.update_config(self.servers)
return await self.bot.say("Muted role set to '{}'".format(role.name))

@set.command(pass_context=True, hidden=True)
async def loggingchannel(self, ctx, channel: discord.Channel = None):
self.servers = self.con.load_config()
self.servers[ctx.message.server.id]["gss"]["log_channel"] = channel.id
self.con.update_config(self.servers)
return await self.bot.say("Logging Channel set to '{}'".format(channel.name))

@set.command(pass_context=True, hidden=True)
async def requireddays(self, ctx, days: int):
self.servers = self.con.load_config()
self.servers[ctx.message.server.id]["gss"]["required_days"] = str(days)
self.con.update_config(self.servers)
return await self.bot.say("Required days set to '{}'".format(str(days)))

@set.command(pass_context=True, hidden=True)
async def requiredscore(self, ctx, score: int):
self.servers = self.con.load_config()
self.servers[ctx.message.server.id]["gss"]["required_score"] = str(score)
self.con.update_config(self.servers)
return await self.bot.say("Required score set to '{}'".format(str(score)))

@group(pass_context=True)
@checks.is_owner_or_admin()
async def add(self, ctx):
"OWNER OR ADMIN ONLY: Adds to lists like admin roles."
if ctx.invoked_subcommand is None:
return await self.bot.say('Missing Argument')

@add.command(pass_context=True, aliases=["adminrole"])
async def addadminrole(self, ctx, *, role: discord.Role = None):
self.servers = self.con.load_config()
if role.id not in self.servers[ctx.message.server.id]["perm_roles"]["admin"]:
self.servers[ctx.message.server.id]["perm_roles"]["admin"].append(role.id)
self.con.update_config(self.servers)
return await self.bot.say("'{}' has been added to the Admin role list.".format(role.name))
else:
return await self.bot.say("'{}' is already in the list.".format(role.name))

@add.command(pass_context=True, aliases=["modrole"])
async def addmodrole(self, ctx, *, role: discord.Role = None):
self.servers = self.con.load_config()
if role.id not in self.servers[ctx.message.server.id]["perm_roles"]["mod"]:
self.servers[ctx.message.server.id]["perm_roles"]["mod"].append(role.id)
self.con.update_config(self.servers)
return await self.bot.say("'{}' has been added to the Mod role list.".format(role.name))
else:
return await self.bot.say("'{}' is already in the list.".format(role.name))

@add.command(pass_context=True, aliases=["nsfwchannel"])
async def addnsfwchannel(self, ctx, *, channel: discord.Channel = None):
self.servers = self.con.load_config()
if channel.id not in self.servers[ctx.message.server.id]["nsfw"]["channels"]:
self.servers[ctx.message.server.id]["nsfw"]["channels"].append(channel.id)
self.con.update_config(self.servers)
return await self.bot.say("'{}' has been added to the nsfw channel list.".format(channel.name))
else:
return await self.bot.say("'{}' is already in the list.".format(channel.name))

@group(pass_context=True)
@checks.is_owner_or_admin()
async def remove(self, ctx):
"OWNER OR ADMIN ONLY: Removes things like admin roles."
if ctx.invoked_subcommand is None:
return await self.bot.say('Missing Argument')

@remove.command(pass_context=True, aliases=["adminrole"])
async def readminrole(self, ctx, *, role: discord.Role = None):
self.servers = self.con.load_config()
try:
self.servers[ctx.message.server.id]["perm_roles"]["admin"].remove(role.id)
except ValueError:
return await self.bot.say("That role was not in the list.")
self.con.update_config(self.servers)
return await self.bot.say("'{}' has been removed from the Admin role list.".format(role.name))

@remove.command(pass_context=True, aliases=["modrole"])
async def remodrole(self, ctx, *, role: discord.Role = None):
self.servers = self.con.load_config()
try:
self.servers[ctx.message.server.id]["perm_roles"]["mod"].remove(role.id)
except ValueError:
return await self.bot.say("That role was not in the list.")
self.con.update_config(self.servers)
return await self.bot.say("'{}' has been removed from the Mod role list.".format(role.name))

@remove.command(pass_context=True, aliases=["nsfwchannel"])
async def rensfwchannel(self, ctx, *, channel: discord.Channel = None):
self.servers = self.con.load_config()
try:
self.servers[ctx.message.server.id]["nsfw"]["channels"].remove(channel.id)
except ValueError:
return await self.bot.say("That role was not in the list.")
self.con.update_config(self.servers)
return await self.bot.say("'{}' has been removed from the nsfw channel list.".format(channel.name))

@checks.is_admin_or_mod()
@bot.command(pass_context=True)
async def serverisanal(self, ctx):
self.servers = self.con.load_config()
is_anal = self.servers[ctx.message.server.id]["is_anal"]["y/n"]
if is_anal == 0:
self.servers[ctx.message.server.id]["is_anal"]["y/n"] = 1
self.con.update_config(self.servers)
return await self.bot.say("I now know this server is anal")
else:
self.servers[ctx.message.server.id]["is_anal"]["y/n"] = 0
self.con.update_config(self.servers)
return await self.bot.say("I now know this server is NOT anal")


def setup(Bot):
Bot.add_cog(Settings(Bot))

+ 351
- 0
cogs/trivia.py View File

@@ -0,0 +1,351 @@
# -*- coding: utf-8 -*-

import checks
import discord
import asyncio
import requests
import datetime
from html import unescape
from random import shuffle
from collections import OrderedDict
from discord.ext import commands


class Trivia:
"""
Trivia is based off the lovely https://opentdb.com made by PixelTail Games.

This cog requires the bot account to be in the Roxbot Emoji Server to work.
"""
def __init__(self, bot_client):
# Get emoji objects here for the reactions. Basically to speedup the reactions for the game.
self.bot = bot_client
a_emoji = self.bot.get_emoji(419572828854026252)
b_emoji = self.bot.get_emoji(419572828925329429)
c_emoji = self.bot.get_emoji(419572829231775755)
d_emoji = self.bot.get_emoji(419572828954820620)
self.correct_emoji = self.bot.get_emoji(421526796392202240)
self.incorrect_emoji = self.bot.get_emoji(421526796379488256)
self.emojis = [a_emoji, b_emoji, c_emoji, d_emoji]
self.games = {}
self.error_colour = 0x992d22
self.trivia_colour = 0x6f90f5

# Game Functions

def get_questions(self, amount=10):
r = requests.get("https://opentdb.com/api.php?amount={}".format(amount))
return r.json()

def parse_question(self, question, counter):
embed = discord.Embed(
title=unescape(question["question"]),
colour=discord.Colour(self.trivia_colour),
description="")

embed.set_author(name="Question {}".format(counter))
embed.set_footer(text="Difficulty: {} | Category: {}".format(question["category"], question["difficulty"].title()))

if question["type"] == "boolean":
# List of possible answers
choices = ["True", "False"]
correct = question["correct_answer"]
# Get index of correct answer
else:
# Get possible answers and shuffle them in a list
incorrect = question["incorrect_answers"]
correct = unescape(question["correct_answer"])
choices = [incorrect[0], incorrect[1], incorrect[2], correct]
for answer in choices:
choices[choices.index(answer)] = unescape(answer)
shuffle(choices)

# Then get the index of the correct answer
correct = choices.index(correct)
# Create output
answers = ""
for x in range(len(choices)):
answers += "{} {}\n".format(str(self.emojis[x]), choices[x])
return embed, answers, correct

def calculate_scores(self, channel, time_asked):
score_added = {}
for user, time in self.games[channel.id]["correct_users"].items():
seconds = (time - time_asked).total_seconds()
seconds = round(seconds, 1)
if seconds < 10:
score = (10 - seconds) * 100
score = int(round(score, -2))
else:
score = 50
score_added[user] = score # This is just to display the amount of score added to a user
return score_added

def sort_leaderboard(self, scores):
return OrderedDict(sorted(scores.items(), key=lambda x:x[1], reverse=True))

def display_leaderboard(self, channel, scores_to_add):
updated_scores = dict(self.games[channel.id]["players"])
updated_scores = self.sort_leaderboard(updated_scores)
output_scores = ""
count = 1
for scores in updated_scores:
player = self.bot.get_user(scores)
if not player:
player = scores
if scores in self.games[channel.id]["correct_users"]:
emoji = self.correct_emoji
else:
emoji = self.incorrect_emoji
output_scores += "{}) {}: {} {}".format(count, player.mention, emoji, updated_scores[scores])
if scores in scores_to_add:
output_scores += "(+{})\n".format(scores_to_add[scores])
else:
output_scores += "\n"
count += 1

return discord.Embed(title="Scores", description=output_scores, colour=discord.Colour(self.trivia_colour))

async def add_question_reactions(self, message, question):
if question["type"] == "boolean":
amount = 2
else:
amount = 4
for x in range(amount):
await message.add_reaction(self.emojis[x])

async def game(self, ctx, channel, questions):
# For loop all the questions for the game, Maybe I should move the game dictionary here instead.
question_count = 1
for question in questions:
# Parse question dictionary into something usable
output, answers, correct = self.parse_question(question, question_count)
self.games[channel.id]["correct_answer"] = correct

# Send a message, add the emoji reactions, then edit in the question to avoid issues with answering before reactions are done.
message = await ctx.send(embed=output)
await self.add_question_reactions(message, question)
output.description = answers
output.set_footer(text="Time left to answer question: 20")
await message.edit(embed=output)
time_asked = datetime.datetime.now()

# Set up variables for checking the question and if it's being answered
players_yet_to_answer = list(self.games[channel.id]["players"].keys())
self.games[channel.id]["current_question"] = message

# Wait for answers
for x in range(20):
# Code for checking if there are still players in the game goes here to make sure nothing breaks.
if not self.games[channel.id]["players"]:
await message.clear_reactions()
await ctx.send(embed=discord.Embed(description="Game ending due to lack of players.", colour=self.error_colour))
return False
for answered in self.games[channel.id]["players_answered"]:
if answered in players_yet_to_answer:
players_yet_to_answer.remove(answered)
if not players_yet_to_answer:
break
else:
output.set_footer(text="Time left to answer question: {}".format(20 - (x + 1)))
await message.edit(embed=output)
await asyncio.sleep(1)

output.set_footer(text="")
await message.edit(embed=output)

# Clean up when answers have been submitted
self.games[channel.id]["current_question"] = None
await message.clear_reactions()

# Display Correct answer and calculate and display scores.
index = self.games[channel.id]["correct_answer"]
embed = discord.Embed(
colour=discord.Colour(0x1fb600),
description="Correct answer is {} **{}**".format(
self.emojis[index],
unescape(question["correct_answer"])
)
)
await ctx.send(embed=embed)

# Scores
scores_to_add = self.calculate_scores(channel, time_asked)
for user in scores_to_add:
self.games[channel.id]["players"][user] += scores_to_add[user]

# Display scores
await ctx.send(embed=self.display_leaderboard(channel, scores_to_add))

# Display that
# Final checks for next question
self.games[channel.id]["correct_users"] = {}
self.games[channel.id]["players_answered"] = []
question_count += 1

# Discord Events

async def on_reaction_add(self, reaction, user):
"""Logic for answering a question"""
time = datetime.datetime.now()
if user == self.bot.user:
return

channel = reaction.message.channel
message = reaction.message

if channel.id in self.games:
if user.id in self.games[channel.id]["players"] and message.id == self.games[channel.id]["current_question"].id:
if reaction.emoji in self.emojis and user.id not in self.games[channel.id]["players_answered"]:
self.games[channel.id]["players_answered"].append(user.id)
if reaction.emoji == self.emojis[self.games[channel.id]["correct_answer"]]:
self.games[channel.id]["correct_users"][user.id] = time
return
else:
return await message.remove_reaction(reaction, user)
else:
return await message.remove_reaction(reaction, user)
else:
return

# Commands

@commands.group(aliases=["tr"])
async def trivia(self, ctx):
if ctx.invoked_subcommand == self.start:
embed = discord.Embed(colour=0xDEADBF)
embed.set_footer(text="Roxbot Trivia uses the Open Trivia DB, made and maintained by Pixeltail Games LLC. Find out more at https://opentdb.com/")
embed.set_image(url="https://i.imgur.com/yhRVl9e.png")
await ctx.send(embed=embed)
elif ctx.invoked_subcommand == None:
await ctx.invoke(self.about)

@trivia.command()
async def about(self, ctx):
embed = discord.Embed(
title="About Roxbot Trivia",
description="Roxbot Trivia is a trivia game in *your* discord server. It's heavily inspired by Tower Unite's trivia game. (and even uses the same questions database!) To start, just type `{}trivia start`.".format(self.bot.command_prefix),
colour=0xDEADBF)
embed.add_field(name="How to Play", value="Once the game has started, questions will be asked and you will be given 20 seconds to answer them. To answer, react with the corrosponding emoji. Roxbot will only accept your first answer. Score is calculated by how quickly you can answer correctly, so make sure to be as quick as possible to win! Person with the most score at the end wins. Glhf!")
embed.add_field(name="Can I have shorter or longer games?", value="Yes! You can change the length of the game by adding either short (5 questions) or long (15 questions) at the end of the start command. `{}trivia start short`. The default is 10 and this is the medium option.".format(self.bot.command_prefix))
embed.set_footer(text="Roxbot Trivia uses the Open Trivia DB, made and maintained by Pixeltail Games LLC. Find out more at https://opentdb.com/")
embed.set_image(url="https://i.imgur.com/yhRVl9e.png")
return await ctx.send(embed=embed)

@trivia.command()
@commands.bot_has_permissions(manage_messages=True)
async def start(self, ctx, amount = "medium"):
channel = ctx.channel
player = ctx.author
# Check if a game is already running and if so exit.
if channel.id in self.games:
# Game active in this channel already
await ctx.send(discord.Embed(description="A game is already being run in this channel.", colour=self.error_colour))
await asyncio.sleep(2)
return await ctx.message.delete()

# Setup variables and wait for all players to join.
# Length of game
length = {"short": 5, "medium": 10, "long": 15}
if amount not in length:
amount = "medium"

# Game Dictionaries
game = {
"players": {player.id: 0},
"active": 0,
"length": length[amount],
"current_question": None,
"players_answered": [],
"correct_users": {},
"correct_answer": ""
}
self.games[channel.id] = game

# Waiting for players
await ctx.send(embed=discord.Embed(description="Starting Roxbot Trivia! Starting in 20 seconds...", colour=self.trivia_colour))
await asyncio.sleep(20)

# Get questions
questions = self.get_questions(length[amount])

# Checks if there is any players to play the game still
if not self.games[channel.id]["players"]:
self.games.pop(channel.id)
return await ctx.send(embed=discord.Embed(description="Abandoning game due to lack of players.", colour=self.error_colour))

# Starts game
self.games[channel.id]["active"] = 1
await self.game(ctx, channel, questions["results"])

# Game Ends
# Some stuff here displaying score
if self.games[channel.id]["players"]:
final_scores = self.sort_leaderboard(self.games[channel.id]["players"])
winner = self.bot.get_user(list(final_scores.keys())[0])
winning_score = list(final_scores.values())[0]
embed = discord.Embed(description="{} won with a score of {}".format(winner.mention, winning_score), colour=0xd4af3a)
await ctx.send(embed=embed)
self.games.pop(channel.id)

@trivia.error
async def trivia_err(self, ctx, error):
# This is here to make sure that if an error occurs, the game will be removed from the dict and will safely exit the game, then raise the error like normal.
self.games.pop(ctx.channel.id)
await ctx.send(embed=discord.Embed(description="An error has occured ;-; Exiting the game...", colour=self.error_colour))
raise error

@trivia.command()
async def join(self, ctx):
"""Joins a trivia game. Can only be done when a game is waiting for players to join. Not when a game is currently active."""
channel = ctx.channel
# Checks if game is in this channel. Then if one isn't active, then if the player has already joined.
if channel.id in self.games:
if not self.games[channel.id]["active"]:
player = ctx.author
if player.id not in self.games[channel.id]["players"]:
self.games[channel.id]["players"][player.id] = 0
return await ctx.send(embed=discord.Embed(description="Player {} joined the game".format(player.mention), colour=self.trivia_colour))
# Failures
else:
return await ctx.send(embed=discord.Embed(description="You have already joined the game. If you want to leave, do `{}trivia leave`".format(self.bot.command_prefix), colour=self.error_colour))
else:
return await ctx.send(embed=discord.Embed(description="Game is already in progress.",colour=self.error_colour))
else:
return await ctx.send(embed=discord.Embed(description="Game isn't being played here.", colour=self.error_colour))

@trivia.command()
async def leave(self, ctx):
"""Leaves the game in this channel. Can be done anytime in the game."""
channel = ctx.channel
player = ctx.author
# CAN LEAVE: Game is started or has been activated
# CANT LEAVE: Game is not active or not in the game
if channel.id in self.games:
if player.id in self.games[channel.id]["players"]:
self.games[channel.id]["players"].pop(player.id)
await ctx.send(embed=discord.Embed(description="{} has left the game.".format(player.mention), colour=self.trivia_colour))
else:
await ctx.send(embed=discord.Embed(description="You are not in this game",
colour=self.error_colour))
else:
await ctx.send(embed=discord.Embed(description="Game isn't being played here.", colour=self.error_colour))

@checks.is_admin_or_mod()
@trivia.command()
async def kick(self, ctx, user: discord.Member):
"""Mod command to kick users out of the game. Useful if a user is AFK."""
channel = ctx.channel
player = user
if channel.id in self.games:
if player.id in self.games[channel.id]["players"]:
self.games[channel.id]["players"].pop(player.id)
await ctx.send(embed=discord.Embed(description="{} has been kicked from the game.".format(player.mention), colour=self.trivia_colour))
else:
await ctx.send(embed=discord.Embed(description="This user is not in the game",
colour=self.error_colour))
else:
await ctx.send(embed=discord.Embed(description="Game isn't being played here.", colour=self.error_colour))

def setup(Bot):
Bot.add_cog(Trivia(Bot))

+ 43
- 46
cogs/twitch.py View File

@@ -1,5 +1,5 @@
import discord
from discord.ext.commands import group
from discord.ext import commands

import checks
from config.server_config import ServerConfig
@@ -15,80 +15,77 @@ class Twitch():
"""
A cog that handles posting when users go live on Twitch
"""
def __init__(self, Bot):
self.bot = Bot
def __init__(self, bot_client):
self.bot = bot_client
self.con = ServerConfig()
self.servers = self.con.servers

async def on_member_update(self, member_b, member_a):
# Twitch Shilling Part
if blacklisted(member_b):
if blacklisted(member_b) or not self.servers[str(member_a.guild.id)]["twitch"]["enabled"]:
return
# Check to see if member_before game exists. Avoids crashes at line 24
if member_b.game:
typeb = member_b.game.type
else:
typeb = False
if member_a.game:
if member_a.game.type and not typeb: # Hopefully this fucking fixes it
ts_enabled = self.servers[member_a.server.id]["twitch"]["enabled"]
ts_whitelist = self.servers[member_a.server.id]["twitch"]["whitelist"]["enabled"]
if ts_enabled:
if not ts_whitelist or member_a.id in \
self.servers[member_a.server.id]["twitch"]["whitelist"]["list"]:
channel = discord.Object(self.servers[member_a.server.id]["twitch"]["twitch-channel"])
return await self.bot.send_message(channel,
content=":video_game:** {} is live!** :video_game:\n{}\n{}".format(
member_a.name, member_a.game.name, member_a.game.url))
@group(pass_context=True, hidden=True)
@checks.is_bot_owner()
async def twitch(self, ctx):

if member_a.activitiy:
if member_a.activity.type == discord.ActivityType.streaming and member_b.activity.type != discord.ActivityType.streaming:
ts_whitelist = self.servers[str(member_a.guild.id)]["twitch"]["whitelist"]["enabled"]
if not ts_whitelist or member_a.id in self.servers[str(member_a.guild.id)]["twitch"]["whitelist"]["list"]:
channel = self.bot.get_channel(self.servers[str(member_a.guild.id)]["twitch"]["twitch-channel"])
return await channel.send(":video_game:** {} is live!** :video_game:\n{}\n{}".format(
member_a.name, member_a.game.name, member_a.game.url))

@commands.group()
@checks.is_admin_or_mod()
async def whitelist(self, ctx):
"""Command group that handles the twitch cog's whitelist."""
if ctx.invoked_subcommand is None:
return await self.bot.say('Missing Argument')
return await ctx.send('Missing Argument')

@twitch.command(pass_context=True, hidden=True)
async def enablewhitelist(self, ctx):
@whitelist.command()
async def enable(self, ctx):
"""Enables the twitch shilling whitelist. Repeat the command to disable.
Usage:
;whitelist enable"""
self.servers = self.con.load_config()
if not self.servers[ctx.server.id]["twitch"]["whitelist"]["enabled"]:
self.servers[ctx.server.id]["twitch"]["whitelist"]["enabled"] = 1
if not self.servers[str(ctx.guild.id)]["twitch"]["whitelist"]["enabled"]:
self.servers[str(ctx.guild.id)]["twitch"]["whitelist"]["enabled"] = 1
self.con.update_config(self.servers)
return await self.bot.reply("Whitelist for Twitch shilling has been enabled.")
return await ctx.send("Whitelist for Twitch shilling has been enabled.")
else:
self.servers[ctx.server.id]["twitch"]["whitelist"]["enabled"] = 0
self.servers[str(ctx.guild.id)]["twitch"]["whitelist"]["enabled"] = 0
self.con.update_config(self.servers)
return await self.bot.reply("Whitelist for Twitch shilling has been disabled.")
return await ctx.send("Whitelist for Twitch shilling has been disabled.")

@twitch.command(pass_context=True, hidden=True)
async def whitelistadd(self, ctx, option, *mentions):
@whitelist.command()
async def edit(self, ctx, option, mentions = None):
"""Adds or removes users to the whitelist. Exactly the same as the blacklist command in usage."""
whitelist_count = 0

if not ctx.message.mentions and option != 'list':
return await self.bot.reply("You haven't mentioned anyone to whitelist.")
return await ctx.send("You haven't mentioned anyone to whitelist.")

if option not in ['+', '-', 'add', 'remove', 'list']:
return await self.bot.say('Invalid option "%s" specified, use +, -, add, or remove' % option, expire_in=20)
return await ctx.send('Invalid option "%s" specified, use +, -, add, or remove' % option, expire_in=20)

if option in ['+', 'add']:
self.servers = self.con.load_config()
for user in ctx.message.mentions:
self.servers[ctx.message.server.id]["twitch"]["whitelist"]["list"].append(user.id)
self.con.update_config(self.servers)
self.servers[str(ctx.guild.id)]["twitch"]["whitelist"]["list"].append(user.id)
whitelist_count += 1
return await self.bot.say('{} user(s) have been added to the whitelist'.format(whitelist_count))
self.con.update_config(self.servers)
return await ctx.send('{} user(s) have been added to the whitelist'.format(whitelist_count))

elif option in ['-', 'remove']:
self.servers = self.con.load_config()
for user in ctx.message.mentions:
if user.id in self.servers[ctx.message.server.id]["twitch"]["whitelist"]["list"]:
self.servers[ctx.message.server.id]["twitch"]["whitelist"]["list"].remove(user.id)
self.con.update_config(self.servers)
if user.id in self.servers[str(ctx.guild.id)]["twitch"]["whitelist"]["list"]:
self.servers[str(ctx.guild.id)]["twitch"]["whitelist"]["list"].remove(user.id)
whitelist_count += 1
return await self.bot.say('{} user(s) have been removed to the whitelist'.format(whitelist_count))
self.con.update_config(self.servers)
return await ctx.send('{} user(s) have been removed to the whitelist'.format(whitelist_count))

elif option == 'list':
return await self.bot.say(
self.servers[ctx.message.server.id]["twitch"]["whitelist"]["list"])
return await ctx.send(self.servers[str(ctx.guild.id)]["twitch"]["whitelist"]["list"])


def setup(Bot):
Bot.add_cog(Twitch(Bot))
def setup(bot_client):
bot_client.add_cog(Twitch(bot_client))

+ 57
- 85
cogs/util.py View File

@@ -1,20 +1,19 @@
import os
import json
import checks
import random
import aiohttp
import discord
import requests
from discord.ext.commands import bot
from discord.ext.commands import bot, is_owner

class Util():
"""
A cog that offers utility commands.
"""
def __init__(self, Bot):
self.bot = Bot
def __init__(self, bot_client):
self.bot = bot_client

@bot.command(pass_context=True)
@bot.command()
async def avatar(self, ctx, *,user: discord.User = None):
"""
Returns a mentioned users avatar
@@ -23,19 +22,22 @@ class Util():
{command_prefix}avatar RoxBot
"""
if not user:
user = ctx.message.author
user = ctx.author

url = user.avatar_url
avaimg = 'avaimg.webp'
if url.split(".")[-1] == "gif":
avaimg = 'avaimg.gif'
else:
avaimg = 'avaimg.webp'

async with aiohttp.ClientSession() as session:
async with session.get(url) as img:
with open(avaimg, 'wb') as f:
f.write(await img.read())
await self.bot.send_file(ctx.message.channel, avaimg)
await ctx.send(file=discord.File(avaimg))
os.remove(avaimg)

@bot.command(pass_context=True)
@bot.command()
async def info(self, ctx, member: discord.Member = None):
"""
Gets info for a mentioned user
@@ -44,25 +46,23 @@ class Util():
{command_prefix}info RoxBot
"""
if not member:
member = ctx.message.author
name_disc = member.name + "#" + member.discriminator
if member.game:
if member.game.type:
game = "**" + member.game.name + "**"
desc = "Streaming "
else:
game = "**" + member.game.name + "**"
desc = "Playing "
member = ctx.author

if member.activity.type == discord.ActivityType.playing:
activity = "Playing **{}**".format(member.activity.name)
elif member.activity.type == discord.ActivityType.streaming:
activity = "Streaming **{}**".format(member.activity.name)
elif member.activity.tyoe == discord.ActivityType.listening:
activity = "Listening to **{} by {}**".format(member.activity.title, member.activity.artist)
else:
desc = ""
game = ""
activity = ""

colour = member.colour.value
avatar = member.avatar_url

embed = discord.Embed(colour=colour, description=desc+game)
embed = discord.Embed(colour=colour, description=activity)
embed.set_thumbnail(url=avatar)
embed.set_author(name=name_disc, icon_url=avatar)
embed.set_author(name=str(member), icon_url=avatar)

embed.add_field(name="ID", value=member.id)
embed.add_field(name="Status", value=member.status)
@@ -75,7 +75,7 @@ class Util():
count = 0

for role in member.roles:
if role == ctx.message.server.default_role:
if role == ctx.guild.default_role:
pass
else:
roles += role.name + ", "
@@ -84,9 +84,9 @@ class Util():
roles = "None"
count = 0
embed.add_field(name="Roles [{}]".format(count), value=roles.strip(", "))
return await self.bot.say(embed=embed)
return await ctx.send(embed=embed)

@bot.command(pass_context=True)
@bot.command()
async def upload(self, ctx):
"""
Uploads selected file to the host, thanks to the fact that
@@ -105,8 +105,6 @@ class Util():
"https://vidga.me/",
"https://pomf.pyonpyon.moe/"
] # List of pomf clone sites and upload limits

await self.bot.send_typing(ctx.message.channel)
if ctx.message.attachments:
# Site choice, shouldn't need an upload size check since max upload for discord atm is 50MB
site = random.choice(sites)
@@ -119,77 +117,51 @@ class Util():
with open(name, 'wb') as f:
f.write(await img.read())
# Upload file
try:
with open(name, 'rb') as f:
answer = requests.post(url=site+"upload.php",files={'files[]': f.read()})
response = json.loads(answer.text)
file_name_1 = response["files"][0]["url"].replace("\\", "")
urls.append(file_name_1)
except Exception as e:
print(e)
print(name + ' couldn\'t be uploaded to ' + site)
with open(name, 'rb') as f:
answer = requests.post(url=site+"upload.php",files={'files[]': f.read()})
response = json.loads(answer.text)
file_name_1 = response["files"][0]["url"].replace("\\", "")
urls.append(file_name_1)
os.remove(name)
msg = "".join(urls)
return await self.bot.say(msg)
return await ctx.send(msg)
else:
return await self.bot.say("Send me stuff to upload.")
return await ctx.send("Send me stuff to upload.")

@bot.command(pass_context=True, aliases=["emoji"]) # This command will only work with normal emoji once I can put in something to get the svgs for twiemoji and convert em
@upload.error
async def upload_err(self, ctx, error):
return await ctx.send("File couldn't be uploaded. {}".format(error))

@bot.command(aliases=["emoji"])
async def emote(self, ctx, emote):
"""
Uploads the emote given. Useful for downloading emotes. ONLY WORKS WITH CUSTOM EMOJI
Uploads the emote given. Useful for downloading emotes.
Usage:
;emote [emote]
"""
emoteid = emote.split(":")[-1].strip("<>")
if not emoteid.isdigit():
return await self.bot.say("This command only works with custom emotes.")
url = "https://discordapp.com/api/emojis/{}.png".format(emoteid)
imgname = 'img.png'
async with aiohttp.ClientSession() as session:
async with session.get(url) as img:
with open(imgname, 'wb') as f:
f.write(await img.read())
await self.bot.send_file(ctx.message.channel, imgname)
os.remove(imgname)
#return await self.bot.say(url)
emote = emote.strip("<>").split(":")
if emote[0] == "a":
imgname = "emote.gif"
emoji_id = emote[2]
else:
imgname = "emote.png"
emoji_id = emote[1]
url = "https://cdn.discordapp.com/emojis/{}".format(emoji_id)

@bot.command(pass_context=True, enabled=False, hidden=True)
@checks.is_bot_owner()
async def emoterob(self, ctx, emote, name=None):
"""
Gets a emoji and adds it to the custom emoji list. ONLY WORKS WITH CUSTOM EMOJI
"""
emoteid = emote.split(":")[-1].strip("<>")
if not emoteid.isdigit():
return await self.bot.say("This command only works with custom emotes.")
url = "https://discordapp.com/api/emojis/{}.png".format(emoteid)
imgname = 'img.png'
async with aiohttp.ClientSession() as session:
async with session.get(url) as img:
with open(imgname, 'wb') as f:
f.write(await img.read())
with open(imgname, "rb") as f:
return await self.bot.create_custom_emoji(ctx.message.server, name=name, image=f)
await ctx.send(file=discord.File(imgname))
os.remove(imgname)

@bot.command(pass_context=True, hidden=True)
@checks.is_bot_owner()
@bot.command()
@is_owner()
async def echo(self, ctx, channel, *, message: str):
if ctx.message.channel_mentions: # If Mentioned
for channel in ctx.message.channel_mentions:
await self.bot.send_message(channel, content=message)
return await self.bot.say(":point_left:")

elif channel.isdigit(): # If ID is given
channel = ctx.message.server.get_channel(channel)
await self.bot.send_message(channel, content=message)
return await self.bot.say(":point_left:")

else:
return await self.bot.say("You did something wrong smh")

@bot.command(pass_context=True, enabled=False, hidden=True)
async def say(self, ctx, *, echo):
return await self.bot.say(echo)
channel = self.bot.get_channel(channel)
await channel.send(message)
return await ctx.send(":point_left:")


def setup(Bot):
Bot.add_cog(Util(Bot))
def setup(bot_client):
bot_client.add_cog(Util(bot_client))

+ 0
- 0
config/__init__.py View File


+ 0
- 15
config/cogs.py View File

@@ -1,15 +0,0 @@
# IF YOU ARE TESTING OR NOT IN THE GSS DISCORD, REMOVE "cogs.gss" FROM THE LIST

cogs = [
"cogs.admin",
"cogs.fun",
"cogs.customcommands",
"cogs.joinleave",
"cogs.nsfw",
"cogs.reddit",
"cogs.selfassign",
"cogs.settings",
"cogs.twitch",
"cogs.util",
"cogs.gss"
]

+ 0
- 7
config/meta.py View File

@@ -1,7 +0,0 @@
__description__ = """RoxBot, A Discord Bot made by a filthy Mercy Main. Built with love (and discord.py) by Roxxers#7443.

[Github link](https://github.com/RainbowDinoaur/roxbot)
[Changelog](https://github.com/RainbowDinoaur/roxbot#v100)"""
__author__ = "Roxanne Gibson"
__version__= "1.3.4"
embedcolour = 0xDEADBF

+ 6
- 3
config/server_config.py View File

@@ -22,7 +22,7 @@ class ServerConfig():
},
"twitch": {
"enabled": 0,
"twitch-channel": "",
"channel": "",
"whitelist": {
"enabled": 0,
"list": []
@@ -30,7 +30,8 @@ class ServerConfig():
},
"nsfw": {
"enabled": 0,
"channels": []
"channels": [],
"blacklist": []
},
"perm_roles": {
"admin": [],
@@ -62,7 +63,9 @@ class ServerConfig():

def error_check(self, servers):
for server in servers:
if server.id not in self.servers:
# Server ID needs to be made a string for this statement because keys have to be strings in JSON. Which is annoying now we use int for ids.
server.id = str(server.id)
if str(server.id) not in self.servers:
self.servers[server.id] = self.servers_template["example"]
self.update_config(self.servers)
print(

+ 0
- 1
config/servers.json View File

@@ -1 +0,0 @@
{"304048071963312130": {"greets": {"enabled": 0, "welcome-channel": "", "member-role": "", "custom-message": "hellow", "default-message": "Be sure to read the rules."}, "goodbyes": {"enabled": 0, "goodbye-channel": ""}, "self_assign": {"enabled": 0, "roles": []}, "twitch": {"enabled": 0, "twitch-channel": "", "whitelist": {"enabled": 0, "list": []}}, "mute": {"role": "", "admin-role": []}, "nsfw": {"enabled": 1, "channels": ["374688008869511168"]}, "admin_role": {"role": ""}, "custom_commands": {"0": {"test": "\"testedit\"", "hello": "<:tingles:346838411639914506>", "benis": "\ud83c\udd71enis", "sophie": "<@136619052994002944>", "myra": "<@342213251372941312>", "roxieee": "<@!142735312626515979>", "customs": "\"\ud83d\udec3\"", "zzz": "\ud83d\udca4", "lewd!": "\ud83d\udea8 lewd! \ud83d\udea8"}, "1": {}}, "perm_roles": {"admin": ["334078005347483648"], "mod": []}, "gss": {"logging_channel": "339201847678074880", "required_days": "5", "required_score": "3000000", "log_channel": ""}, "admin": {"warnings": {}}, "is_anal": {"y/n": 1}, "warnings": {"259869304369971200": [{"warned-by": "142735312626515979", "date": 1518514436.672631, "warning": "Hello"}, {"warned-by": "142735312626515979", "date": 1518514441.1129122, "warning": "Hello"}, {"warned-by": "142735312626515979", "date": 1518514442.534168, "warning": "Hello"}], "104291668810821632": [{"warned-by": "142735312626515979", "date": 1518519612.6708424, "warning": "For being a bad girl"}]}}, "395632940328943616": {"greets": {"enabled": 0, "welcome-channel": "", "member-role": "", "custom-message": "", "default-message": "Be sure to read the rules."}, "goodbyes": {"enabled": 0, "goodbye-channel": ""}, "self_assign": {"enabled": 0, "roles": []}, "twitch": {"enabled": 0, "twitch-channel": "", "whitelist": {"enabled": 0, "list": []}}, "nsfw": {"enabled": 0, "channels": []}, "perm_roles": {"admin": [], "mod": []}, "custom_commands": {"0": {}, "1": {}}, "gss": {"log_channel": "", "required_days": "", "required_score": ""}, "warnings": {}, "is_anal": {"y/n": 0}}}

+ 412
- 7
config/settings.py View File

@@ -1,9 +1,414 @@
import configparser
import os
import sys
import aiohttp
import asyncio

settings = configparser.ConfigParser()
settings.read("settings/preferences.ini")
import checks
import load_config
from config.server_config import ServerConfig

command_prefix = settings["Roxbot"]["Command_Prefix"]
token = settings["Roxbot"]["Token"]
owner = settings["Roxbot"]["OwnerID"]
tattoken = settings["Roxbot"]["Tatsumaki_Token"]
import discord
from discord.ext.commands import bot, group, is_owner, bot_has_permissions


class Settings:
"""
Settings is a mix of settings and admin stuff for the bot. OWNER OR ADMIN ONLY.
"""
def __init__(self, bot_client):
self.bot = bot_client
self.con = ServerConfig()
self.serverconfig = self.con.servers

def get_channel(self, ctx, channel):
if ctx.message.channel_mentions:
return ctx.message.channel_mentions[0]
else:
return self.bot.get_channel(channel)

@bot.command()
@checks.is_owner_or_admin()
async def blacklist(self, ctx, option):
"""
Add or remove users to the blacklist. Blacklisted users are forbidden from using bot commands.
Usage:
;blacklist [add|+ OR remove|-] @user#0000
OWNER OR ADMIN ONLY
"""
blacklist_amount = 0
mentions = ctx.message.mentions

if not mentions:
return await ctx.send("You didn't mention anyone")

if option not in ['+', '-', 'add', 'remove']:
return await ctx.send('Invalid option "%s" specified, use +, -, add, or remove' % option, expire_in=20)

for user in mentions:
if user.id == load_config.owner:
print("[Commands:Blacklist] The owner cannot be blacklisted.")
await ctx.send("The owner cannot be blacklisted.")
mentions.remove(user)

if option in ['+', 'add']:
with open("config/blacklist.txt", "r") as fp:
for user in mentions:
for line in fp.readlines():
if user.id + "\n" in line:
mentions.remove(user)

with open("config/blacklist.txt", "a+") as fp:
lines = fp.readlines()
for user in mentions:
if user.id not in lines:
fp.write("{}\n".format(user.id))
blacklist_amount += 1
return await ctx.send('{} user(s) have been added to the blacklist'.format(blacklist_amount))

elif option in ['-', 'remove']:
with open("config/blacklist.txt", "r") as fp:
lines = fp.readlines()
with open("config/blacklist.txt", "w") as fp:
for user in mentions:
for line in lines:
if user.id + "\n" != line:
fp.write(line)
else:
fp.write("")
blacklist_amount += 1
return await ctx.send('{} user(s) have been removed from the blacklist'.format(blacklist_amount))

@bot.command(aliases=["setavatar"])
@is_owner()
async def changeavatar(self, ctx, url=None):
"""
Changes the bot's avatar. Can't be a gif.
Usage:
;changeavatar [url]
Attaching a file and leaving the url parameter blank also works.
"""
avaimg = 'avaimg'
if ctx.message.attachments:
await ctx.message.attachments[0].save(avaimg)
else:
thing = url.strip('<>')

async with aiohttp.ClientSession() as session:
async with session.get(thing) as img:
with open(avaimg, 'wb') as f:
f.write(await img.read())
with open(avaimg, 'rb') as f:
await self.bot.user.edit(avatar=f.read())
os.remove(avaimg)
asyncio.sleep(2)
return await ctx.send(":ok_hand:")

@bot.command(aliases=["nick", "nickname"])
@is_owner()
@bot_has_permissions(change_nickname=True)
async def changenickname(self, ctx, *, nick):
"""Changes the bot's nickname in the guild.
Usage:
;nickname [nickname]"""
await self.bot.change_nickname(ctx.message.server.me, nick)
return await ctx.send(":thumbsup:")


@bot.command(aliases=["activity"])
@is_owner()
async def changeactivity(self, ctx, *, game: str):
"""Changes the "playing" status of the bot.
Usage:
;changeactivity` [game]"""
if game.lower() == "none":
game= None
else:
game = discord.Game(game)
await self.bot.change_presence(activity=game)
return await ctx.send(":ok_hand: Activity set to {}".format(str(game)))

@bot.command(aliases=["status"])
@is_owner()
async def changestatus(self, ctx, status: str):
"""Changes the status of the bot.
Usage:
;changesatus [game]"""
status = status.lower()
if status == 'offline' or status == 'invisible':
discordStatus = discord.Status.invisible
elif status == 'idle':
discordStatus = discord.Status.idle
elif status == 'dnd':
discordStatus = discord.Status.dnd
else:
discordStatus = discord.Status.online
await self.bot.change_presence(status=discordStatus)
await ctx.send("**:ok:** Status set to {}".format(discordStatus))

@bot.command()
@is_owner()
async def restart(self):
"""Restarts the bot."""
await self.bot.logout()
return os.execl(sys.executable, sys.executable, *sys.argv)

@bot.command()
@is_owner()
async def shutdown(self):
"""Shuts down the bot."""
await self.bot.logout()
return exit(0)

@bot.command()
@checks.is_owner_or_admin()
async def printsettings(self, ctx):
"OWNER OR ADMIN ONLY: Prints the servers config file."
self.serverconfig = self.con.load_config()
config = self.serverconfig[str(ctx.guild.id)]
em = discord.Embed(colour=0xDEADBF)
em.set_author(name="{} settings for {}.".format(self.bot.user.name, ctx.message.guild.name), icon_url=self.bot.user.avatar_url)
for settings in config:
if settings != "custom_commands" and settings != "warnings":
settingcontent = ""
for x in config[settings].items():
settingcontent += str(x).strip("()") + "\n"
em.add_field(name=settings, value=settingcontent, inline=False)
elif settings == "custom_commands":
em.add_field(name="custom_commands", value="For Custom Commands, use the custom list command.", inline=False)
return await ctx.send(embed=em)

@group()
@checks.is_admin_or_mod()
async def settings(self, ctx):
if ctx.invoked_subcommand is None:
return await ctx.send('Missing Argument')
self.serverconfig = self.con.load_config()
self.guild_id = str(ctx.guild.id)

@settings.command(aliases=["sa"])
async def selfassign(self, ctx, selection, *, changes = None):
"""Edits settings for self assign cog.

Options:
enable/disable: Enable/disables the cog.
addrole/removerole: adds or removes a role that can be self assigned in the server.
"""
selection = selection.lower()
role = discord.utils.find(lambda u: u.name == changes, ctx.message.guild.roles)
if selection == "enable":
self.serverconfig[self.guild_id]["self_assign"]["enabled"] = 1
await ctx.send("'self_assign' was enabled!")
elif selection == "disable":
self.serverconfig[self.guild_id]["self_assign"]["enabled"] = 0
await ctx.send("'self_assign' was disabled :cry:")
elif selection == "addrole":
if role.id in self.serverconfig[ctx.message.guild.id]["self_assign"]["roles"]:
return await ctx.send("{} is already a self-assignable role.".format(role.name),
delete_after=self.con.delete_after)

self.serverconfig[ctx.message.guild.id]["self_assign"]["roles"].append(role.id)
await ctx.send('Role "{}" added'.format(str(role)))
elif selection == "removerole":
if role.id in self.serverconfig[ctx.message.guild.id]["self_assign"]["roles"]:
self.serverconfig[ctx.message.guild.id]["self_assign"]["roles"].remove(role.id)
self.con.update_config(self.serverconfig)
await ctx.send('"{}" has been removed from the self-assignable roles.'.format(str(role)))
else:
return await ctx.send("That role was not in the list.")
else:
return await ctx.send("No valid option given.")
return self.con.update_config(self.serverconfig)

@settings.command(aliases=["jl"])
async def joinleave(self, ctx, selection, *, changes = None):
"""Edits settings for joinleave cog.

Options:
enable/disable: Enable/disables parts of the cog. Needs to specify which part.
Example:
;settings joinleave enable greets|goodbyes
welcomechannel/goodbyeschannel: Sets the channels for either option. Must be a ID or mention.
custommessage: specifies a custom message for the greet messages.
"""
selection = selection.lower()
if selection == "enable":
if changes == "greets":
self.serverconfig[self.guild_id]["greets"]["enabled"] = 1
await ctx.send("'greets' was enabled!")
elif changes == "goodbyes":
self.serverconfig[self.guild_id]["goodbyes"]["enabled"] = 1
await ctx.send("'goodbyes' was enabled!")
elif selection == "disable":
if changes == "greets":
self.serverconfig[self.guild_id]["greets"]["enabled"] = 0
await ctx.send("'greets' was disabled :cry:")
elif changes == "goodbyes":
self.serverconfig[self.guild_id]["goodbyes"]["enabled"] = 0
await ctx.send("'goodbyes' was disabled :cry:")
elif selection == "welcomechannel":
channel = self.get_channel(ctx, changes)
self.serverconfig[ctx.message.guild.id]["greets"]["welcome-channel"] = channel.id
await ctx.send("{} has been set as the welcome channel!".format(channel.mention))
elif selection == "goodbyeschannel":
channel = self.get_channel(ctx, changes)
self.serverconfig[ctx.message.guild.id]["goodbyes"]["goodbye-channel"] = channel.id
await ctx.send("{} has been set as the goodbye channel!".format(channel.mention))
elif selection == "custommessage":
self.serverconfig[self.guild_id]["greets"]["custom-message"] = changes
await ctx.send("Custom message set to '{}'".format(changes))
else:
return await ctx.send("No valid option given.")
return self.con.update_config(self.serverconfig)

@settings.command()
async def twitch(self, ctx, selection, *, changes = None):
"""Edits settings for self assign cog.

Options:
enable/disable: Enable/disables the cog.
channel: Sets the channel to shill in.
"""
selection = selection.lower()
if selection == "enable":
self.serverconfig[self.guild_id]["twitch"]["enabled"] = 1
await ctx.send("'twitch' was enabled!")
elif selection == "disable":
self.serverconfig[self.guild_id]["twitch"]["enabled"] = 0
await ctx.send("'twitch' was disabled :cry:")
elif selection == "channel":
channel = self.get_channel(ctx, changes)
self.serverconfig[self.guild_id]["twitch"]["channel"] = channel.id
await ctx.send("{} has been set as the twitch shilling channel!".format(channel.mention))
# Is lacking whitelist options. Might be added or might be depreciated.
# Turns out this is handled in the cog and I don't think it needs changing but may be confusing.
else:
return await ctx.send("No valid option given.")
return self.con.update_config(self.serverconfig)

@settings.command(aliases=["perms"])
async def permrole(self, ctx, selection, *, changes = None):
"""Edits settings for permission roles.

Options:
addadmin/removeadmin: Adds/Removes admin role.
addmod/removemod: Adds/Removes mod role.
Example:
;settings permrole addadmin Admin
"""
selection = selection.lower()
role = discord.utils.find(lambda u: u.name == changes, ctx.message.guild.roles)
if selection == "addadmin":
if role.id not in self.serverconfig[ctx.message.guild.id]["perm_roles"]["admin"]:
self.serverconfig[ctx.message.guild.id]["perm_roles"]["admin"].append(role.id)
await ctx.send("'{}' has been added to the Admin role list.".format(role.name))
else:
return await ctx.send("'{}' is already in the list.".format(role.name))
elif selection == "addmod":
if role.id not in self.serverconfig[ctx.message.guild.id]["perm_roles"]["mod"]:
self.serverconfig[ctx.message.guild.id]["perm_roles"]["mod"].append(role.id)
await ctx.send("'{}' has been added to the Mod role list.".format(role.name))
else:
return await ctx.send("'{}' is already in the list.".format(role.name))
elif selection == "removeadmin":
try:
self.serverconfig[ctx.message.guild.id]["perm_roles"]["admin"].remove(role.id)
await ctx.send("'{}' has been removed from the Admin role list.".format(role.name))
except ValueError:
return await ctx.send("That role was not in the list.")
elif selection == "removemod":
try:
self.serverconfig[ctx.message.guild.id]["perm_roles"]["mod"].remove(role.id)
await ctx.send("'{}' has been removed from the Mod role list.".format(role.name))
except ValueError:
return await ctx.send("That role was not in the list.")

else:
return await ctx.send("No valid option given.")
return self.con.update_config(self.serverconfig)

@settings.command()
async def gss(self, ctx, selection, *, changes = None):
"""Custom Cog for the GaySoundsShitposts Discord Server."""
selection = selection.lower()
if selection == "loggingchannel":
channel = self.get_channel(ctx, changes)
self.serverconfig[ctx.message.guild.id]["gss"]["log_channel"] = channel.id
await ctx.send("Logging Channel set to '{}'".format(channel.name))
elif selection == "requireddays":
self.serverconfig[ctx.message.guild.id]["gss"]["required_days"] = int(changes)
await ctx.send("Required days set to '{}'".format(str(changes)))
elif selection == "requiredscore":
self.serverconfig[ctx.message.guild.id]["gss"]["required_score"] = int(changes)
await ctx.send("Required score set to '{}'".format(str(changes)))
else:
return await ctx.send("No valid option given.")
return self.con.update_config(self.serverconfig)


@settings.command()
async def nsfw(self, ctx, selection, *, changes = None):
"""Edits settings for the nsfw cog and other nsfw commands.
If nsfw is enabled and nsfw channels are added, the bot will only allow nsfw commands in the specified channels.

Options:
enable/disable: Enable/disables nsfw commands.
addchannel/removechannel: Adds/Removes a nsfw channel.
Example:
;settings nsfw addchannel #nsfw_stuff
"""
selection = selection.lower()
if selection == "enable":
self.serverconfig[self.guild_id]["nsfw"]["enabled"] = 1
await ctx.send("'nsfw' was enabled!")
elif selection == "disable":
self.serverconfig[self.guild_id]["nsfw"]["enabled"] = 0
await ctx.send("'nsfw' was disabled :cry:")
elif selection == "addchannel":
channel = self.get_channel(ctx, changes)
if channel.id not in self.serverconfig[ctx.message.guild.id]["nsfw"]["channels"]:
self.serverconfig[ctx.message.guild.id]["nsfw"]["channels"].append(channel.id)
await ctx.send("'{}' has been added to the nsfw channel list.".format(channel.name))
else:
return await ctx.send("'{}' is already in the list.".format(channel.name))
elif selection == "removechannel":
channel = self.get_channel(ctx, changes)
try:
self.serverconfig[ctx.message.guild.id]["nsfw"]["channels"].remove(channel.id)
await ctx.send("'{}' has been removed from the nsfw channel list.".format(channel.name))
except ValueError:
return await ctx.send("That role was not in the list.")
elif selection == "addbadtag":
if changes not in self.serverconfig[ctx.guild.id]["nsfw"]["blacklist"]:
self.serverconfig[ctx.guild.id]["nsfw"]["blacklist"].append(changes)
await ctx.send("'{}' has been added to the blacklisted tag list.".format(changes))
else:
return await ctx.send("'{}' is already in the list.".format(changes))
elif selection == "removebadtag":
try:
self.serverconfig[ctx.guild.id]["nsfw"]["blacklist"].remove(changes)
await ctx.send("'{}' has been removed from the blacklisted tag list.".format(changes))
except ValueError:
return await ctx.send("That tag was not in the blacklisted tag list.")
else:
return await ctx.send("No valid option given.")
return self.con.update_config(self.serverconfig)

@checks.is_admin_or_mod()
@bot.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."""
self.serverconfig = self.con.load_config()
is_anal = self.serverconfig[ctx.message.guild.id]["is_anal"]["y/n"]
if is_anal == 0:
self.serverconfig[ctx.message.guild.id]["is_anal"]["y/n"] = 1
self.con.update_config(self.serverconfig)
return await ctx.send("I now know this server is anal")
else:
self.serverconfig[ctx.message.guild.id]["is_anal"]["y/n"] = 0
self.con.update_config(self.serverconfig)
return await ctx.send("I now know this server is NOT anal")


def setup(bot_client):
bot_client.add_cog(Settings(bot_client))

+ 73
- 0
err_handle.py View File

@@ -0,0 +1,73 @@
import traceback
import datetime
import discord
from discord.ext import commands
from config.server_config import ServerConfig

class ErrHandle:
def __init__(self, bot_client):
self.bot = bot_client
self.dev = False # For debugging
self.servers = ServerConfig().servers

async def on_error(self, event, *args, **kwargs):
if self.dev:
traceback.print_exc()
else:
embed = discord.Embed(title="Roxbot Error", colour=0xe74c3c) # Red
embed.add_field(name='Event', value=event)
embed.description = '```py\n{}\n```'.format(traceback.format_exc())
embed.timestamp = datetime.datetime.utcnow()
await self.owner.send(embed=embed)

async def on_command_error(self, ctx, error):
self.owner = self.bot.get_user(self.bot.owner_id)
err_colour = 0x992d22
if self.dev:
raise error
elif isinstance(error, commands.CommandInvokeError):
embed = discord.Embed(title='Command Error', colour=err_colour)
embed.description = str(error)
embed.add_field(name='Server', value=ctx.guild)
embed.add_field(name='Channel', value=ctx.channel.mention)
embed.add_field(name='User', value=ctx.author)
embed.add_field(name='Message', value=ctx.message.content)
embed.timestamp = datetime.datetime.utcnow()
await self.owner.send(embed=embed)
else:
if isinstance(error, commands.NoPrivateMessage):
embed = discord.Embed(description="This command cannot be used in private messages.")
elif isinstance(error, commands.DisabledCommand):
embed = discord.Embed(description="This command is disabled.")
elif isinstance(error, commands.CheckFailure):
embed = discord.Embed(description="You do not have permission to do this. Back off, thot!")
elif isinstance(error, commands.MissingRequiredArgument):
embed = discord.Embed(description="Argument missing.")
elif isinstance(error, commands.BadArgument):
embed = discord.Embed(description="Invalid Argument given. Please check arguments given.")
elif isinstance(error, commands.TooManyArguments):
embed = discord.Embed(description="Too many arguments given.")
elif isinstance(error, commands.CommandNotFound):
cc = self.servers[str(ctx.guild.id)]["custom_commands"]
if ctx.invoked_with in cc["1"]:
embed = None
else:
embed = discord.Embed(description="That Command doesn't exist.")
elif isinstance(error, commands.BotMissingPermissions):
embed = discord.Embed(description="I am missing the following permissions: {}".format(str(error.missing_perms).strip("[]")))
elif isinstance(error, commands.MissingPermissions):
embed = discord.Embed(description="You are missing the following permissions: {}".format(str(error.missing_perms).strip("[]")))
elif isinstance(error, commands.NotOwner):
embed = discord.Embed(description="You do not have permission to do this. You are not Roxie!")
elif isinstance(error, commands.CommandOnCooldown):
embed = discord.Embed(description="This command is on cooldown, please wait {} seconds before trying again.".format(error.retry_after))
else:
embed = discord.Embed(
description="Placeholder embed. If you see this please message {}.".format(str(self.owner)))
if embed:
embed.colour = err_colour
await ctx.send(embed=embed)


def setup(bot_client):
bot_client.add_cog(ErrHandle(bot_client))

+ 35
- 11
load_config.py View File

@@ -1,12 +1,36 @@
from config import settings, cogs, meta

command_prefix = settings.command_prefix
token = settings.token
owner = settings.owner
tat_token = settings.tattoken
cogslist = cogs.cogs
description = meta.__description__
version = meta.__version__
author = meta.__author__
embedcolour = meta.embedcolour
import configparser

settings = configparser.ConfigParser()
settings.read("settings/preferences.ini")

command_prefix = settings["Roxbot"]["Command_Prefix"]
token = settings["Roxbot"]["Token"]
owner = int(settings["Roxbot"]["OwnerID"])
tat_token = settings["Roxbot"]["Tatsumaki_Token"]


__description__ = """RoxBot, A Discord Bot made by a filthy Mercy Main. Built with love (and discord.py) by Roxxers#7443.

[Github link](https://github.com/RainbowDinoaur/roxbot)
[Changelog](https://github.com/RainbowDinoaur/roxbot#v100)
[Found a bug or need to report an issue? Report it here](https://github.com/RainbowRoxxers/roxbot/issues/new)
[Say Thanks](https://saythanks.io/to/Roxxers)"""
__author__ = "Roxanne Gibson"
__version__ = "1.4.0"
embedcolour = 0xDEADBF

# IF YOU ARE TESTING OR NOT IN THE GSS DISCORD, REMOVE "cogs.gss" FROM THE LIST

cogs = [
"cogs.admin",
"cogs.fun",
"cogs.customcommands",
"cogs.joinleave",
"cogs.nsfw",
"cogs.reddit",
"cogs.selfassign",
"cogs.trivia",
"cogs.twitch",
"cogs.util",
#"cogs.gss"
]

+ 32
- 71
main.py View File

@@ -4,29 +4,26 @@ import time
import logging
import os.path
import datetime
import traceback

import discord
from discord.ext import commands

import load_config
from config.server_config import ServerConfig

start_time = time.time()

# Sets up Logging that discord.py does on its own
logger = logging.getLogger('discord')
logger.setLevel(logging.INFO)
logger.setLevel(logging.WARN)
handler = logging.FileHandler(filename='discord.log', encoding='utf-8', mode='w')
handler.setFormatter(logging.Formatter('%(asctime)s:%(levelname)s:%(name)s: %(message)s'))
logger.addHandler(handler)


server_config = ServerConfig()
bot = commands.Bot(command_prefix=load_config.command_prefix, description=load_config.description)
bot.dev = True # For debugging
bot.owner = load_config.owner

bot = commands.Bot(
command_prefix=load_config.command_prefix,
description=load_config.__description__,
owner_id=load_config.owner,
activity=discord.Game(name="v{}".format(load_config.__version__), type=0)
)

def blacklisted(user):
with open("config/blacklist.txt", "r") as fp:
@@ -37,25 +34,21 @@ def blacklisted(user):

@bot.event
async def on_ready():
server_config.error_check(bot.servers)
server_config.error_check(bot.guilds)
print("Discord.py version: " + discord.__version__)
print("Client logged in\n")
bot.owner = load_config.owner

print("Cogs Loaded:")
for cog in load_config.cogslist:
for cog in load_config.cogs:
bot.load_extension(cog)
print(cog)
print("")

print("Servers I am currently in:")
for server in bot.servers:
for server in bot.guilds:
print(server)
print("")

game = discord.Game(name="v{}".format(load_config.version), type=0)
await bot.change_presence(game=game)


@bot.event
async def on_server_join(server):
@@ -77,63 +70,21 @@ async def on_message(message):
return
return await bot.process_commands(message)

@bot.event
async def on_error(event, *args, **kwargs):
if bot.dev:
traceback.print_exc()
else:
embed = discord.Embed(title=':x: Event Error', colour=0xe74c3c) #Red
embed.add_field(name='Event', value=event)
embed.description = '```py\n%s\n```' % traceback.format_exc()
embed.timestamp = datetime.datetime.utcnow()
try:
await bot.send_message(bot.owner, embed=embed)
except:
pass

@bot.event
async def on_command_error(error, ctx):
if isinstance(error, commands.NoPrivateMessage):
await bot.send_message(ctx.message.author, "This command cannot be used in private messages.")
elif isinstance(error, commands.DisabledCommand):
await bot.send_message(ctx.message.channel, content="This command is disabled.")
elif isinstance(error, commands.CheckFailure):
await bot.send_message(ctx.message.channel, content="You do not have permission to do this. Back off, thot!")
elif isinstance(error, KeyError):
await bot.send_message(ctx.message.channel, content="Belgh")
elif isinstance(error, commands.CommandInvokeError):
if bot.dev:
raise error
else:
embed = discord.Embed(title=':x: Command Error', colour=0x992d22) #Dark Red
embed.add_field(name='Error', value=str(error))
embed.add_field(name='Server', value=ctx.message.server)
embed.add_field(name='Channel', value=ctx.message.channel)
embed.add_field(name='User', value=ctx.message.author)
embed.add_field(name='Message', value=ctx.message.content)
embed.timestamp = datetime.datetime.utcnow()
try:
await bot.send_message(await bot.get_user_info(load_config.owner), embed=embed)
except:
raise error
#else:
# if bot.dev:
# raise error

@bot.command()
async def about():
async def about(ctx):
"""
Outputs info about RoxBot, showing uptime, what settings where set in prefs.ini and credits.
Outputs info about RoxBot, showing uptime, how to report issues, what settings where set in prefs.ini and credits.
"""
user = await bot.get_user_info(load_config.owner)
ownername = user.name + "#" + user.discriminator
em = discord.Embed(title="About Roxbot", colour=load_config.embedcolour, description=load_config.description)
owner = bot.get_user(load_config.owner)
em = discord.Embed(title="About Roxbot", colour=load_config.embedcolour, description=load_config.__description__)
em.set_thumbnail(url=bot.user.avatar_url)
em.add_field(name="Command Prefix", value=load_config.command_prefix)
em.add_field(name="Owner", value=ownername)
em.add_field(name="Owner", value=str(owner))
em.add_field(name="Owner ID", value=load_config.owner)
em.add_field(name="Bot Version", value=load_config.version)
em.add_field(name="Author", value=load_config.author)
em.add_field(name="Bot Version", value=load_config.__version__)
em.add_field(name="Author", value=load_config.__author__)
em.add_field(name="Discord.py version", value=discord.__version__)
em.set_footer(text="RoxBot is licensed under the MIT License")

# Do time calc late in the command so that the time returned is closest to when the message is received
@@ -141,11 +92,21 @@ async def about():
uptime = str(datetime.timedelta(seconds=uptimeflo))
em.add_field(name="Current Uptime", value=str(uptime.split(".")[0]))

return await bot.say(embed=em)
return await ctx.channel.send(embed=em)


if __name__ == "__main__":
# Pre-Boot checks
if not os.path.isfile("settings/preferences.ini"):
print(
"PREFERENCE FILE MISSING. Something has gone wrong. Please make sure there is a file called 'preferences.ini' in the settings folder")
exit(0)

if not os.path.isfile("config/servers.json"):
with open("config/servers.json", "w+") as fp:
fp.write("{}")

if not os.path.isfile("settings/preferences.ini"):
print("PREFERENCE FILE MISSING. Something has gone wrong. Please make sure there is a file called 'preferences.ini' in the settings folder")
else:
start_time = time.time()
bot.load_extension("config.settings")
bot.load_extension("err_handle")
bot.run(load_config.token)

Loading…
Cancel
Save