Skip to content

Commit

Permalink
cleaned up project structure and extension setup
Browse files Browse the repository at this point in the history
  • Loading branch information
LiBa001 committed Nov 6, 2018
1 parent cecec75 commit 3799682
Show file tree
Hide file tree
Showing 23 changed files with 329 additions and 272 deletions.
191 changes: 3 additions & 188 deletions bot/__init__.py
@@ -1,88 +1,13 @@
import logging
import sys
import traceback
import os
import time
import discord
from discord.ext import commands
from ruamel import yaml
from .models import Guild, User, graph, Ticket, Response
from .properties import CONFIG, Defaults
from .errors import MissingPermissions, InvalidAction, Blacklisted
from . import utils
from inspect import Parameter
from functools import wraps
from . import utils, errors
from .bot import Bot
from .context import Context


logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)

console = logging.StreamHandler(sys.stdout)
console.setLevel(logging.DEBUG)
logger.addHandler(console)


class Context(commands.Context):
@property
def db_guild(self):
return Guild.from_discord_guild(self.guild, ctx=self)

@property
def db_author(self):
return User.from_discord_user(self.author, ctx=self)

@property
def language(self):
return self.db_guild.language

def translate(self, text: str):
return self.bot.string_translations[text][self.language]

def may_fully_access(self, entry: Ticket or Response):
return entry.guild.support_role in [role.id for role in self.author.roles] \
or self.author.id == entry.author.id \
or self.author.permissions_in(self.channel).administrator \
or self.author.id in CONFIG['bot_admins']

def is_prime(self):
member = self.author
discord_guild = self.bot.get_guild(CONFIG['home_guild'])
prime_roles = [discord_guild.get_role(r_id) for r_id in CONFIG['prime_roles']]

if member in discord_guild.members:
return any(role in member.roles for role in prime_roles) # checks if member has any prime role
else:
return False


class Bot(commands.Bot):
def __init__(self, *args, **kwargs):
super(Bot, self).__init__(*args, **kwargs)

with open(os.path.dirname(__file__) + '/translations/strings.yml', 'r', encoding='utf-8') as stream:
try:
_string_translations = yaml.load(stream, Loader=yaml.Loader)
except yaml.YAMLError as exc:
logger.error(exc)

async def on_message(self, message):
ctx = await self.get_context(message, cls=Context)
await self.invoke(ctx)

@property
def string_translations(self):
return Bot._string_translations

@staticmethod
def prime_feature(f: callable) -> callable:
@wraps(f)
async def wrapper(ctx, *args, **kwargs):
if not ctx.is_prime():
await ctx.send(ctx.translate("this is a prime feature"))
else:
await f(ctx, *args, *kwargs)

return wrapper


async def dynamic_prefix(bot, msg):
Expand All @@ -92,113 +17,3 @@ async def dynamic_prefix(bot, msg):
guild = Guild.from_discord_guild(msg.guild)

return guild.prefix


bot = Bot(command_prefix=dynamic_prefix, pm_help=None, case_insensitive=True)

bot.remove_command('help')


@bot.event
async def on_ready():
logger.info(f"Logged in as: {bot.user.name}")

for guild in bot.guilds:
Guild.from_discord_guild(guild)

await bot.change_presence(activity=discord.Game(name="/help"))


@bot.event
async def on_guild_join(guild):
Guild.from_discord_guild(guild)


@bot.event
async def on_member_join(member):
db_guild = Guild.from_discord_guild(member.guild)
db_user = User.from_discord_user(member)

db_guild.joined_users.add(db_user, properties={'UTC': time.time()})

db_guild.push()


@bot.event
async def on_command_error(ctx, error):
if isinstance(error, discord.ext.commands.BadArgument):
cmd: commands.Command = ctx.command

successful_parsed_args = len(ctx.args) # number of arguments parsed without errors
params = list(cmd.params) # list all param names of the function

param_name = params[successful_parsed_args] # get name of the param where the error occurred
param: Parameter = cmd.params[param_name] # get inspect.Parameter object

annotation = param.annotation # get the class that the argument should be converted to

object_name = annotation.__name__ # class name (e.g. 'Ticket' or 'TextChannel')

msg = ctx.translate("[object] could not be found").format(object_name)
await ctx.send(msg)

elif isinstance(error, discord.ext.commands.MissingRequiredArgument):
msg = ctx.translate("parameter needs to be specified")
await ctx.send(msg)

elif isinstance(error, discord.ext.commands.MissingPermissions) or isinstance(error, MissingPermissions):
msg = ctx.translate("you are not allowed to perform this action")
await ctx.send(msg)

elif isinstance(error, discord.ext.commands.BotMissingPermissions):
msg = ctx.translate("need required permissions [permissions]").format("`, `".join(error.missing_perms))
await ctx.send(msg)

elif isinstance(error, InvalidAction):
msg = ctx.translate("invalid action")
await ctx.send(msg)

elif isinstance(error, Blacklisted):
msg = ctx.translate("you are blacklisted")
await ctx.send(msg)

elif isinstance(error, discord.ext.commands.CommandNotFound):
pass

else:
raise error


@bot.event
async def on_error(event, *args, **kwargs):
exc_info = sys.exc_info()
instance = exc_info[1]

if event == "on_voice_state_update":
member = args[0]
guild = Guild.from_discord_guild(member.guild)

translator = utils.Translator(bot.string_translations, guild.language_enum)

if isinstance(instance, errors.OnCooldown):
seconds = int(instance.retry_after)
try:
await member.send(translator.translate("you're on cooldown for [sec] seconds").format(seconds))
except discord.Forbidden:
pass

elif isinstance(instance, discord.Forbidden):
await guild.log(translator.translate("not enough permissions to perform action"))

else:
print('Ignoring exception in {}'.format(event), file=sys.stderr)
traceback.print_exc()


@bot.before_invoke
async def before_invoke(ctx):
await ctx.trigger_typing()


bot.load_extension('bot.commands')
bot.load_extension('bot.services')
39 changes: 39 additions & 0 deletions bot/bot.py
@@ -0,0 +1,39 @@
from discord.ext import commands
import logging
from ruamel import yaml
import os
from functools import wraps
from .context import Context


logger = logging.getLogger(__name__)


class Bot(commands.Bot):
def __init__(self, *args, **kwargs):
super(Bot, self).__init__(*args, **kwargs)

with open(os.path.dirname(__file__) + '/translations/strings.yml', 'r', encoding='utf-8') as stream:
try:
_string_translations = yaml.load(stream, Loader=yaml.Loader)
except yaml.YAMLError as exc:
logger.error(exc)

async def on_message(self, message):
ctx = await self.get_context(message, cls=Context)
await self.invoke(ctx)

@property
def string_translations(self):
return Bot._string_translations

@staticmethod
def prime_feature(f: callable) -> callable:
@wraps(f)
async def wrapper(ctx, *args, **kwargs):
if not ctx.is_prime():
await ctx.send(ctx.translate("this is a prime feature"))
else:
await f(ctx, *args, *kwargs)

return wrapper
29 changes: 20 additions & 9 deletions bot/commands/__init__.py
@@ -1,10 +1,21 @@
from .config import config
from .ticket import ticket
from .tickets import tickets
from .response import response
from .help import help_messages
from .outlaw import outlaw
from .statistics import statistics
from .blacklist import blacklist
from .report import report


def setup(bot):
from .config import config
from .ticket import ticket
from .tickets import tickets
from .response import response
from .help import help_messages
from .outlaw import outlaw
from .statistics import statistics
from .blacklist import blacklist
from .report import report
bot.add_command(config)
bot.add_command(ticket)
bot.add_command(tickets)
bot.add_command(response)
bot.add_command(help_messages)
bot.add_command(outlaw)
bot.add_command(statistics)
bot.add_command(blacklist)
bot.add_command(report)
9 changes: 5 additions & 4 deletions bot/commands/blacklist.py
@@ -1,18 +1,19 @@
from bot import bot, errors, utils, properties
from discord.ext import commands
from bot import errors, utils, properties
from bot.models import User
import time
import discord
from copy import deepcopy


@bot.group()
@commands.group()
async def blacklist(ctx):
if ctx.db_guild.support_role not in [r.id for r in ctx.author.roles]:
if not ctx.author.guild_permissions.administrator:
raise errors.MissingPermissions

if ctx.invoked_subcommand is None:
await ctx.invoke(bot.get_command('blacklist show'))
await ctx.invoke(ctx.bot.get_command('blacklist show'))


@blacklist.command(name="add", aliases=["append"])
Expand Down Expand Up @@ -83,7 +84,7 @@ async def _show(ctx):
for sub_list in sub_lists:
page = deepcopy(blacklist_emb) # copy by value not reference
for user in sub_list:
discord_user = bot.get_user(user.id)
discord_user = ctx.bot.get_user(user.id)
page.add_field(
name=f"{str(discord_user)}",
value=ctx.db_guild.blacklist.get(user, 'reason'),
Expand Down
10 changes: 5 additions & 5 deletions bot/commands/config.py
@@ -1,11 +1,11 @@
from discord.ext import commands
from bot.utils import *
from bot import bot, enums, errors
from bot import enums, errors, Bot
from bot.models import graph, Scope, Language
from typing import Union


@bot.group(name='config', aliases=['set', 'configure'])
@commands.group(name='config', aliases=['set', 'configure'])
@commands.guild_only()
@commands.has_permissions(administrator=True)
async def config(ctx):
Expand Down Expand Up @@ -75,7 +75,7 @@ def check(message):
return message.author.id == ctx.author.id and message.channel.id == msg.channel.id

try:
choice = await bot.wait_for('message', check=check, timeout=60)
choice = await ctx.bot.wait_for('message', check=check, timeout=60)
except asyncio.TimeoutError:
continue
choice = choice.content
Expand All @@ -92,7 +92,7 @@ def check(message):
await ctx.send(ctx.translate("invalid input"))
continue

command = bot.get_command('config ' + action)
command = ctx.bot.get_command('config ' + action)
await ctx.invoke(command, choice)


Expand Down Expand Up @@ -228,7 +228,7 @@ async def _voice(ctx, voice_category: Union[discord.CategoryChannel, str]):


@config.command(name='log', aliases=['logging', 'logger'])
@bot.prime_feature
@Bot.prime_feature
async def _log(ctx, log_channel: Union[discord.TextChannel, str]):
""" This is to set the guilds log channel. """

Expand Down
7 changes: 3 additions & 4 deletions bot/commands/help.py
@@ -1,5 +1,4 @@
from bot.utils import *
from bot import bot
from discord.ext import commands
from ruamel import yaml
from bot.models import Guild
Expand All @@ -14,7 +13,7 @@
print(exc)


@bot.command(name='help')
@commands.command(name='help')
async def help_messages(ctx, command: str=None):
guild = Guild.from_discord_guild(ctx.guild)
language = guild.language
Expand All @@ -27,7 +26,7 @@ async def help_messages(ctx, command: str=None):
color=Defaults.COLOR
)

help_embed.set_thumbnail(url=bot.user.avatar_url)
help_embed.set_thumbnail(url=ctx.bot.user.avatar_url)

for command in help_translations:
help_embed.add_field(
Expand All @@ -46,7 +45,7 @@ async def help_messages(ctx, command: str=None):
else:
valid_command = False

for cmd in bot.commands:
for cmd in ctx.bot.commands:
if command == cmd.name or command in cmd.aliases:
command = cmd.name
valid_command = True
Expand Down
3 changes: 1 addition & 2 deletions bot/commands/outlaw.py
@@ -1,4 +1,3 @@
from bot import bot
from bot.models import User
from discord.ext import commands
import discord
Expand All @@ -22,7 +21,7 @@ def prepare_outlaw(ol, reason, user, ctx, **properties):
setattr(ol, p, properties[p])


@bot.group()
@commands.group()
async def outlaw(ctx):
pass

Expand Down

0 comments on commit 3799682

Please sign in to comment.