Browse Source

Better interface to reactions, etc cleanup

pull/9/head
Andrei 9 years ago
parent
commit
ef4e87f7fb
  1. 8
      disco/api/http.py
  2. 13
      disco/api/ratelimit.py
  3. 6
      disco/cli.py
  4. 4
      disco/client.py
  5. 6
      disco/gateway/sharder.py
  6. 3
      disco/types/channel.py
  7. 15
      disco/types/guild.py
  8. 37
      disco/types/message.py
  9. 31
      disco/util/logging.py

8
disco/api/http.py

@ -18,6 +18,12 @@ HTTPMethod = Enum(
)
def to_bytes(obj):
if isinstance(obj, six.text_type):
return obj.encode('utf-8')
return obj
class Routes(object):
"""
Simple Python object-enum of all method/url route combinations available to
@ -194,6 +200,7 @@ class HTTPClient(LoggingClass):
kwargs['headers'] = self.headers
# Build the bucket URL
args = {to_bytes(k): to_bytes(v) for k, v in six.iteritems(args)}
filtered = {k: (v if v in ('guild', 'channel') else '') for k, v in six.iteritems(args)}
bucket = (route[0].value, route[1].format(**filtered))
@ -202,6 +209,7 @@ class HTTPClient(LoggingClass):
# Make the actual request
url = self.BASE_URL + route[1].format(**args)
self.log.info('%s %s', route[0].value, url)
r = requests.request(route[0].value, url, **kwargs)
# Update rate limiter

13
disco/api/ratelimit.py

@ -1,8 +1,10 @@
import time
import gevent
from disco.util.logging import LoggingClass
class RouteState(object):
class RouteState(LoggingClass):
"""
An object which stores ratelimit state for a given method/url route
combination (as specified in :class:`disco.api.http.Routes`).
@ -36,6 +38,9 @@ class RouteState(object):
self.update(response)
def __repr__(self):
return '<RouteState {}>'.format(' '.join(self.route))
@property
def chilled(self):
"""
@ -92,12 +97,14 @@ class RouteState(object):
raise Exception('Cannot cooldown for negative time period; check clock sync')
self.event = gevent.event.Event()
gevent.sleep((self.reset_time - time.time()) + .5)
delay = (self.reset_time - time.time()) + .5
self.log.debug('Cooling down bucket %s for %s seconds', self, delay)
gevent.sleep(delay)
self.event.set()
self.event = None
class RateLimiter(object):
class RateLimiter(LoggingClass):
"""
A in-memory store of ratelimit states for all routes we've ever called.

6
disco/cli.py

@ -41,7 +41,7 @@ def disco_main(run=False):
from disco.client import Client, ClientConfig
from disco.bot import Bot, BotConfig
from disco.util.token import is_valid_token
from holster.log import set_logging_levels
from disco.util.logging import setup_logging
if os.path.exists(args.config):
config = ClientConfig.from_file(args.config)
@ -61,8 +61,8 @@ def disco_main(run=False):
AutoSharder(config).run()
return
logging.basicConfig(level=logging.INFO)
set_logging_levels()
# TODO: make configurable
setup_logging(level=logging.INFO)
client = Client(config)

4
disco/client.py

@ -13,7 +13,7 @@ from disco.util.logging import LoggingClass
from disco.util.backdoor import DiscoBackdoorServer
class ClientConfig(LoggingClass, Config):
class ClientConfig(Config):
"""
Configuration for the :class:`Client`.
@ -46,7 +46,7 @@ class ClientConfig(LoggingClass, Config):
encoder = 'json'
class Client(object):
class Client(LoggingClass):
"""
Class representing the base entry point that should be used in almost all
implementation cases. This class wraps the functionality of both the REST API

6
disco/gateway/sharder.py

@ -5,22 +5,20 @@ import gevent
import logging
import marshal
from holster.log import set_logging_levels
from disco.client import Client
from disco.bot import Bot, BotConfig
from disco.api.client import APIClient
from disco.gateway.ipc import GIPCProxy
from disco.util.logging import setup_logging
from disco.util.snowflake import calculate_shard
from disco.util.serializer import dump_function, load_function
def run_shard(config, id, pipe):
logging.basicConfig(
setup_logging(
level=logging.INFO,
format='{} [%(levelname)s] %(asctime)s - %(name)s:%(lineno)d - %(message)s'.format(id)
)
set_logging_levels()
config.shard_id = id
client = Client(config)

3
disco/types/channel.py

@ -186,6 +186,9 @@ class Channel(SlottedModel, Permissible):
"""
return MessageIterator(self.client, self, **kwargs)
def get_message(self, message):
return self.client.api.channels_messages_get(self.id, to_snowflake(message))
def get_invites(self):
"""
Returns

15
disco/types/guild.py

@ -10,6 +10,7 @@ from disco.types.base import SlottedModel, Field, snowflake, listof, dictof, tex
from disco.types.user import User, Presence
from disco.types.voice import VoiceState
from disco.types.channel import Channel
from disco.types.message import Emoji
from disco.types.permissions import PermissionValue, Permissions, Permissible
@ -22,7 +23,9 @@ VerificationLevel = Enum(
)
class GuildSubType(SlottedModel):
class GuildSubType(object):
__slots__ = []
guild_id = Field(None)
@cached_property
@ -30,7 +33,7 @@ class GuildSubType(SlottedModel):
return self.client.state.guilds.get(self.guild_id)
class Emoji(GuildSubType):
class GuildEmoji(Emoji, GuildSubType):
"""
An emoji object
@ -54,7 +57,7 @@ class Emoji(GuildSubType):
roles = Field(listof(snowflake))
class Role(GuildSubType):
class Role(SlottedModel, GuildSubType):
"""
A role object
@ -95,7 +98,7 @@ class Role(GuildSubType):
return '<@{}>'.format(self.id)
class GuildMember(GuildSubType):
class GuildMember(SlottedModel, GuildSubType):
"""
A GuildMember object
@ -222,7 +225,7 @@ class Guild(SlottedModel, Permissible):
All of the guild's channels.
roles : dict(snowflake, :class:`Role`)
All of the guild's roles.
emojis : dict(snowflake, :class:`Emoji`)
emojis : dict(snowflake, :class:`GuildEmoji`)
All of the guild's emojis.
voice_states : dict(str, :class:`disco.types.voice.VoiceState`)
All of the guild's voice states.
@ -243,7 +246,7 @@ class Guild(SlottedModel, Permissible):
members = Field(dictof(GuildMember, key='id'))
channels = Field(dictof(Channel, key='id'))
roles = Field(dictof(Role, key='id'))
emojis = Field(dictof(Emoji, key='id'))
emojis = Field(dictof(GuildEmoji, key='id'))
voice_states = Field(dictof(VoiceState, key='session_id'))
member_count = Field(int)
presences = Field(listof(Presence))

37
disco/types/message.py

@ -19,10 +19,24 @@ MessageType = Enum(
)
class MessageReactionEmoji(SlottedModel):
class Emoji(SlottedModel):
id = Field(snowflake)
name = Field(text)
def __eq__(self, other):
if isinstance(other, Emoji):
return self.id == other.id and self.name == other.name
raise NotImplementedError
def to_string(self):
if self.id:
return '{}:{}'.format(self.name, self.id)
return self.name
class MessageReactionEmoji(Emoji):
pass
class MessageReaction(SlottedModel):
emoji = Field(MessageReactionEmoji)
@ -261,6 +275,27 @@ class Message(SlottedModel):
"""
return self.client.api.channels_messages_delete(self.channel_id, self.id)
def create_reaction(self, emoji):
if isinstance(emoji, Emoji):
emoji = emoji.to_string()
self.client.api.channels_messages_reactions_create(
self.channel_id,
self.id,
emoji)
def delete_reaction(self, emoji, user=None):
if isinstance(emoji, Emoji):
emoji = emoji.to_string()
if user:
user = to_snowflake(user)
self.client.api.channels_messages_reactions_delete(
self.channel_id,
self.id,
emoji,
user)
def is_mentioned(self, entity):
"""
Returns

31
disco/util/logging.py

@ -3,15 +3,24 @@ from __future__ import absolute_import
import logging
LEVEL_OVERRIDES = {
'requests': logging.WARNING
}
def setup_logging(**kwargs):
logging.basicConfig(**kwargs)
for logger, level in LEVEL_OVERRIDES.items():
logging.getLogger(logger).setLevel(level)
class LoggingClass(object):
def __init__(self):
self.log = logging.getLogger(self.__class__.__name__)
def log_on_error(self, msg, f):
def _f(*args, **kwargs):
try:
return f(*args, **kwargs)
except:
self.log.exception(msg)
raise
return _f
__slots__ = ['_log']
@property
def log(self):
try:
return self._log
except AttributeError:
self._log = logging.getLogger(self.__class__.__name__)
return self._log

Loading…
Cancel
Save