From 4c41010fc146ed04692e469c6b0277e572e1d1ef Mon Sep 17 00:00:00 2001 From: Andrei Date: Tue, 4 Oct 2016 14:42:59 -0500 Subject: [PATCH] Add permissions support, etc fixes/tweaks --- disco/api/http.py | 9 ++++- disco/bot/bot.py | 2 +- disco/bot/parser.py | 34 +++++++++++------- disco/types/channel.py | 5 +-- disco/types/guild.py | 3 +- disco/types/message.py | 5 ++- disco/types/permissions.py | 71 ++++++++++++++++++++++++++++++++++++++ disco/types/user.py | 6 ++++ disco/util/websocket.py | 1 - 9 files changed, 116 insertions(+), 20 deletions(-) create mode 100644 disco/types/permissions.py diff --git a/disco/api/http.py b/disco/api/http.py index df699c3..a11f6ac 100644 --- a/disco/api/http.py +++ b/disco/api/http.py @@ -88,9 +88,14 @@ class Routes(object): class APIException(Exception): def __init__(self, msg, status_code=0, content=None): - super(APIException, self).__init__(msg) self.status_code = status_code self.content = content + self.msg = msg + + if self.status_code: + self.msg += ' code: {}'.format(status_code) + + super(APIException, self).__init__(self.msg) class HTTPClient(LoggingClass): @@ -132,6 +137,8 @@ class HTTPClient(LoggingClass): # If we got a success status code, just return the data if r.status_code < 400: return r + elif 400 < r.status_code < 500: + raise APIException('Request failed', r.status_code, r.content) else: if r.status_code == 429: self.log.warning('Request responded w/ 429, retrying (but this should not happen, check your clock sync') diff --git a/disco/bot/bot.py b/disco/bot/bot.py index e802f35..c047e7d 100644 --- a/disco/bot/bot.py +++ b/disco/bot/bot.py @@ -126,7 +126,7 @@ class Bot(object): if cls.__name__ in self.plugins: raise Exception('Cannot add already added plugin: {}'.format(cls.__name__)) - config = self.config.plugin_config_provider(cls.__name__) if self.config.plugin_config_provider else {} + config = self.config.plugin_config_provider(cls.__name__) if self.config.plugin_config_provider else None self.plugins[cls.__name__] = cls(self, config) self.plugins[cls.__name__].load() diff --git a/disco/bot/parser.py b/disco/bot/parser.py index 2394ce4..7fa77d9 100644 --- a/disco/bot/parser.py +++ b/disco/bot/parser.py @@ -1,4 +1,6 @@ import re +import copy + PARTS_RE = re.compile('(\<|\[)((?:\w+|\:|\||\.\.\.| (?:[0-9]+))+)(?:\>|\])') @@ -26,15 +28,6 @@ class Argument(object): def true_count(self): return self.count or 1 - def convert(self, obj): - for typ in self.types: - typ = TYPE_MAP.get(typ) - try: - return typ(obj) - except Exception as e: - continue - raise e - def parse(self, raw): prefix, part = raw @@ -58,8 +51,23 @@ class Argument(object): class ArgumentSet(object): - def __init__(self, args=None): + def __init__(self, args=None, custom_types=None): self.args = args or [] + self.types = copy.copy(TYPE_MAP) + self.types.update(custom_types) + + def convert(self, types, value): + for typ_name in types: + typ = self.types.get(typ_name) + if not typ: + raise Exception('Unknown type {}'.format(typ_name)) + + try: + return typ(value) + except Exception as e: + continue + + raise e def append(self, arg): if self.args and not self.args[-1].required and arg.required: @@ -85,7 +93,7 @@ class ArgumentSet(object): if arg.types: for idx, r in enumerate(raw): try: - raw[idx] = arg.convert(r) + raw[idx] = self.convert(arg.types, r) except: raise ArgumentError('cannot convert `{}` to `{}`'.format( r, ', '.join(arg.types) @@ -110,8 +118,8 @@ class ArgumentSet(object): return sum([i.true_count for i in self.args if i.required]) -def parse_arguments(line): - args = ArgumentSet() +def parse_arguments(line, custom_types=None): + args = ArgumentSet(custom_types=custom_types) data = PARTS_RE.findall(line) if len(data): diff --git a/disco/types/channel.py b/disco/types/channel.py index 15b1d0f..2d18d15 100644 --- a/disco/types/channel.py +++ b/disco/types/channel.py @@ -6,6 +6,7 @@ from disco.util.cache import cached_property from disco.util.types import ListToDictType from disco.types.base import BaseType from disco.types.user import User +from disco.types.permissions import * from disco.voice.client import VoiceClient @@ -26,8 +27,8 @@ class PermissionOverwrite(BaseType): id = skema.SnowflakeType() type = skema.StringType(choices=PermissionOverwriteType.ALL_VALUES) - allow = skema.IntType() - deny = skema.IntType() + allow = PermissionType() + deny = PermissionType() class Channel(BaseType): diff --git a/disco/types/guild.py b/disco/types/guild.py index 949d3ae..d26749f 100644 --- a/disco/types/guild.py +++ b/disco/types/guild.py @@ -7,6 +7,7 @@ from disco.types.base import BaseType from disco.util.types import PreHookType, ListToDictType from disco.types.user import User from disco.types.voice import VoiceState +from disco.types.permissions import PermissionType from disco.types.channel import Channel @@ -24,7 +25,7 @@ class Role(BaseType): hoist = skema.BooleanType() managed = skema.BooleanType() color = skema.IntType() - permissions = skema.IntType() + permissions = PermissionType() position = skema.IntType() diff --git a/disco/types/message.py b/disco/types/message.py index fd3645f..3e83a5d 100644 --- a/disco/types/message.py +++ b/disco/types/message.py @@ -47,6 +47,9 @@ class Message(BaseType): embeds = skema.ListType(skema.ModelType(MessageEmbed)) attachments = ListToDictType('id', skema.ModelType(MessageAttachment)) + def __str__(self): + return ''.format(self.id, self.channel_id) + @cached_property def guild(self): return self.channel.guild @@ -61,7 +64,7 @@ class Message(BaseType): def edit(self, content): return self.client.api.channels_messages_modify(self.channel_id, self.id, content) - def delete(self, content): + def delete(self): return self.client.api.channels_messages_delete(self.channel_id, self.id) def is_mentioned(self, entity): diff --git a/disco/types/permissions.py b/disco/types/permissions.py new file mode 100644 index 0000000..a46767c --- /dev/null +++ b/disco/types/permissions.py @@ -0,0 +1,71 @@ +from skema import NumberType + +from holster.enum import Enum + +Permissions = Enum( + NONE=0, + CREATE_INSTANT_INVITE=1 << 0, + KICK_MEMBERS=1 << 1, + BAN_MEMBERS=1 << 2, + ADMINISTRATOR=1 << 3, + MANAGE_CHANNELS=1 << 4, + MANAGE_GUILD=1 << 5, + READ_MESSAGES=1 << 10, + SEND_MESSAGES=1 << 11, + SEND_TSS_MESSAGES=1 << 12, + MANAGE_MESSAGES=1 << 13, + EMBED_LINKS=1 << 14, + ATTACH_FILES=1 << 15, + READ_MESSAGE_HISTORY=1 << 16, + MENTION_EVERYONE=1 << 17, + USE_EXTERNAL_EMOJIS=1 << 18, + CONNECT=1 << 20, + SPEAK=1 << 21, + MUTE_MEMBERS=1 << 22, + DEAFEN_MEMBERS=1 << 23, + MOVE_MEMBERS=1 << 24, + USE_VAD=1 << 25, + CHANGE_NICKNAME=1 << 26, + MANAGE_NICKNAMES=1 << 27, + MANAGE_ROLES=1 << 28, + MANAGE_WEBHOOKS=1 << 29, + MANAGE_EMOJIS=1 << 30, +) + + +class PermissionValue(object): + def __init__(self, value): + self.value = value + + def __getattribute__(self, name): + if name in Permissions.attrs: + return (self.value & Permissions[name].value) == Permissions[name].value + else: + return object.__getattribute__(self, name) + + def __setattr__(self, name, value): + if name not in Permissions.attrs: + return super(PermissionValue, self).__setattr__(name, value) + + if value: + self.value |= Permissions[name].value + else: + self.value &= ~Permissions[name].value + + def to_dict(self): + return { + k: getattr(self, k) for k in Permissions.attrs + } + + @classmethod + def text(cls): + return cls(523264) + + @classmethod + def voice(cls): + return cls(66060288) + + +class PermissionType(NumberType): + def __init__(self, *args, **kwargs): + super(PermissionType, self).__init__(number_class=PermissionValue, number_type='PermissionValue', *args, **kwargs) diff --git a/disco/types/user.py b/disco/types/user.py index 86bff12..81d0800 100644 --- a/disco/types/user.py +++ b/disco/types/user.py @@ -13,5 +13,11 @@ class User(BaseType): verified = skema.BooleanType(required=False) email = skema.EmailType(required=False) + def to_string(self): + return '{}#{}'.format(self.username, self.discriminator) + + def __str__(self): + return ''.format(self.id, self.to_string()) + def on_create(self): self.client.state.users[self.id] = self diff --git a/disco/util/websocket.py b/disco/util/websocket.py index 978adb9..e488a44 100644 --- a/disco/util/websocket.py +++ b/disco/util/websocket.py @@ -66,7 +66,6 @@ class WebsocketProcessProxy(object): self.emitter = Emitter(gevent.spawn) gevent.signal(signal.SIGINT, self.handle_signal) - gevent.signal(signal.SIGQUIT, self.handle_signal) gevent.signal(signal.SIGTERM, self.handle_signal) def handle_signal(self, *args):