Browse Source

Typo corrections, upgrades from Py2 to Py3-compatible formatting, patch yaml loader deprecation warning, Discord media proxy links (allows caching of possibly deleted images, relies less on the CDN)

pull/129/head
“elderlabs” 7 years ago
parent
commit
5695eff84f
  1. 12
      disco/api/http.py
  2. 2
      disco/api/ratelimit.py
  3. 10
      disco/bot/bot.py
  4. 4
      disco/bot/plugin.py
  5. 2
      disco/cli.py
  6. 20
      disco/gateway/client.py
  7. 2
      disco/state.py
  8. 2
      disco/types/base.py
  9. 6
      disco/types/channel.py
  10. 8
      disco/types/guild.py
  11. 6
      disco/types/user.py
  12. 5
      disco/util/serializer.py
  13. 40
      disco/voice/client.py
  14. 2
      disco/voice/playable.py
  15. 20
      disco/voice/udp.py
  16. 12
      docs/bot_tutorial/building_block_listeners.md
  17. 8
      setup.py

12
disco/api/http.py

@ -52,8 +52,7 @@ class Routes(object):
CHANNELS_MESSAGES_REACTIONS_GET = (HTTPMethod.GET, CHANNELS + '/messages/{message}/reactions/{emoji}')
CHANNELS_MESSAGES_REACTIONS_CREATE = (HTTPMethod.PUT, CHANNELS + '/messages/{message}/reactions/{emoji}/@me')
CHANNELS_MESSAGES_REACTIONS_DELETE_ME = (HTTPMethod.DELETE, CHANNELS + '/messages/{message}/reactions/{emoji}/@me')
CHANNELS_MESSAGES_REACTIONS_DELETE_USER = (HTTPMethod.DELETE,
CHANNELS + '/messages/{message}/reactions/{emoji}/{user}')
CHANNELS_MESSAGES_REACTIONS_DELETE_USER = (HTTPMethod.DELETE, CHANNELS + '/messages/{message}/reactions/{emoji}/{user}')
CHANNELS_PERMISSIONS_MODIFY = (HTTPMethod.PUT, CHANNELS + '/permissions/{permission}')
CHANNELS_PERMISSIONS_DELETE = (HTTPMethod.DELETE, CHANNELS + '/permissions/{permission}')
CHANNELS_INVITES_LIST = (HTTPMethod.GET, CHANNELS + '/invites')
@ -264,11 +263,11 @@ class HTTPClient(LoggingClass):
# Possibly wait if we're rate limited
response.rate_limited_duration = self.limiter.check(bucket)
self.log.debug('KW: %s', kwargs)
self.log.debug('KW: {}'.format(kwargs))
# Make the actual request
url = self.BASE_URL + route[1].format(**args)
self.log.info('%s %s (%s)', route[0].value, url, kwargs.get('params'))
self.log.info('{} {} ({})'.format(route[0].value, url, kwargs.get('params')))
r = self.session.request(route[0].value, url, **kwargs)
if self.after_request:
@ -282,13 +281,12 @@ class HTTPClient(LoggingClass):
if r.status_code < 400:
return r
elif r.status_code != 429 and 400 <= r.status_code < 500:
self.log.warning('Request failed with code %s: %s', r.status_code, r.content)
self.log.warning('Request failed with code {}: {}'.format(r.status_code, r.content))
response.exception = APIException(r)
raise response.exception
else:
if r.status_code == 429:
self.log.warning(
'Request responded w/ 429, retrying (but this should not happen, check your clock sync')
self.log.warning('Request responded w/ 429, retrying (but this should not happen, check your clock sync')
# If we hit the max retries, throw an error
retry += 1

2
disco/api/ratelimit.py

@ -99,7 +99,7 @@ class RouteState(LoggingClass):
self.event = gevent.event.Event()
delay = (self.reset_time - time.time()) + .5
self.log.debug('Cooling down bucket %s for %s seconds', self, delay)
self.log.debug('Cooling down bucket {} for {} seconds'.format(self, delay))
gevent.sleep(delay)
self.event.set()
self.event = None

10
disco/bot/bot.py

@ -154,7 +154,7 @@ class Bot(LoggingClass):
except ImportError:
self.log.warning('Failed to enable HTTP server, Flask is not installed')
else:
self.log.info('Starting HTTP server bound to %s:%s', self.config.http_host, self.config.http_port)
self.log.info('Starting HTTP server bound to {}:{}'.format(self.config.http_host, self.config.http_port))
self.http = Flask('disco')
self.http_server = WSGIServer((self.config.http_host, self.config.http_port), self.http)
self.http_server_greenlet = gevent.spawn(self.http_server.serve_forever)
@ -244,7 +244,7 @@ class Bot(LoggingClass):
else:
possible[current] = group
# Now, we want to compute the actual shortest abbreivation out of the
# Now, we want to compute the actual shortest abbreviation out of the
# possible ones
result = {}
for abbrev, group in six.iteritems(possible):
@ -449,7 +449,7 @@ class Bot(LoggingClass):
inst = inst(self, config)
if inst.__class__.__name__ in self.plugins:
self.log.warning('Attempted to add already added plugin %s', inst.__class__.__name__)
self.log.warning('Attempted to add already added plugin {}'.format(inst.__class__.__name__))
raise Exception('Cannot add already added plugin: {}'.format(inst.__class__.__name__))
self.ctx['plugin'] = self.plugins[inst.__class__.__name__] = inst
@ -467,7 +467,7 @@ class Bot(LoggingClass):
Plugin class to unload and remove.
"""
if cls.__name__ not in self.plugins:
raise Exception('Cannot remove non-existant plugin: {}'.format(cls.__name__))
raise Exception('Cannot remove non-existent plugin: {}'.format(cls.__name__))
ctx = {}
self.plugins[cls.__name__].unload(ctx)
@ -495,7 +495,7 @@ class Bot(LoggingClass):
"""
Adds and loads a plugin, based on its module path.
"""
self.log.info('Adding plugin module at path "%s"', path)
self.log.info('Adding plugin module at path "{}"'.format(path))
mod = importlib.import_module(path)
loaded = False

4
disco/bot/plugin.py

@ -66,7 +66,7 @@ def find_loadable_plugins(mod):
class BasePluginDeco(object):
Prio = Priority
# TODO: dont smash class methods
# TODO: don't smash class methods
@classmethod
def add_meta_deco(cls, meta):
def deco(f):
@ -234,7 +234,7 @@ class Plugin(LoggingClass, PluginDeco):
self.storage = bot.storage
self.config = config
# General declartions
# General declarations
self.listeners = []
self.commands = []
self.schedules = {}

2
disco/cli.py

@ -103,7 +103,7 @@ def disco_main(run=False):
if run:
(bot or client).run_forever()
return (bot or client)
return bot or client
if __name__ == '__main__':

20
disco/gateway/client.py

@ -75,7 +75,7 @@ class GatewayClient(LoggingClass):
return self._send(op, data)
def _send(self, op, data):
self.log.debug('GatewayClient.send %s', op)
self.log.debug('GatewayClient.send {}'.format(op))
self.packets.emit((SEND, op), data)
self.ws.send(self.encoder.encode({
'op': op.value,
@ -96,7 +96,7 @@ class GatewayClient(LoggingClass):
def handle_dispatch(self, packet):
obj = GatewayEvent.from_dispatch(self.client, packet)
self.log.debug('GatewayClient.handle_dispatch %s', obj.__class__.__name__)
self.log.debug('GatewayClient.handle_dispatch {}'.format(obj.__class__.__name__))
self.client.events.emit(obj.__class__.__name__, obj)
if self.replaying:
self.replayed_events += 1
@ -119,7 +119,7 @@ class GatewayClient(LoggingClass):
self.ws.close()
def handle_hello(self, packet):
self.log.info('Received HELLO, starting heartbeater...')
self.log.info('Received HELLO, starting heartbeat...')
self._heartbeat_task = gevent.spawn(self.heartbeat_task, packet['d']['heartbeat_interval'])
def on_ready(self, ready):
@ -128,7 +128,7 @@ class GatewayClient(LoggingClass):
self.reconnects = 0
def on_resumed(self, _):
self.log.info('RESUME completed, replayed %s events', self.replayed_events)
self.log.info('RESUME completed, replayed {} events'.format(self.replayed_events))
self.reconnects = 0
self.replaying = False
@ -144,7 +144,7 @@ class GatewayClient(LoggingClass):
if self.zlib_stream_enabled:
gateway_url += '&compress=zlib-stream'
self.log.info('Opening websocket connection to URL `%s`', gateway_url)
self.log.info('Opening websocket connection to URL `{}`'.format(gateway_url))
self.ws = Websocket(gateway_url)
self.ws.emitter.on('on_open', self.on_open)
self.ws.emitter.on('on_error', self.on_error)
@ -194,14 +194,14 @@ class GatewayClient(LoggingClass):
if isinstance(error, KeyboardInterrupt):
self.shutting_down = True
self.ws_event.set()
raise Exception('WS recieved error: %s', error)
raise Exception('WS received error: {}'.format(error))
def on_open(self):
if self.zlib_stream_enabled:
self._zlib = zlib.decompressobj()
if self.seq and self.session_id:
self.log.info('WS Opened: attempting resume w/ SID: %s SEQ: %s', self.session_id, self.seq)
self.log.info('WS Opened: attempting resume w/ SID: {} SEQ: {}'.format(self.session_id, self.seq))
self.replaying = True
self.send(OPCode.RESUME, {
'token': self.client.config.token,
@ -230,7 +230,7 @@ class GatewayClient(LoggingClass):
# Make sure we cleanup any old data
self._buffer = None
# Kill heartbeater, a reconnect/resume will trigger a HELLO which will
# Kill heartbeat, a reconnect/resume will trigger a HELLO which will
# respawn it
if self._heartbeat_task:
self._heartbeat_task.kill()
@ -244,7 +244,7 @@ class GatewayClient(LoggingClass):
# Track reconnect attempts
self.reconnects += 1
self.log.info('WS Closed: [%s] %s (%s)', code, reason, self.reconnects)
self.log.info('WS Closed: [{}] {} ({})'.format(code, reason, self.reconnects))
if self.max_reconnects and self.reconnects > self.max_reconnects:
raise Exception('Failed to reconnect after {} attempts, giving up'.format(self.max_reconnects))
@ -254,7 +254,7 @@ class GatewayClient(LoggingClass):
self.session_id = None
wait_time = self.reconnects * 5
self.log.info('Will attempt to %s after %s seconds', 'resume' if self.session_id else 'reconnect', wait_time)
self.log.info('Will attempt to {} after {} seconds'.format('resume' if self.session_id else 'reconnect', wait_time))
gevent.sleep(wait_time)
# Reconnect

2
disco/state.py

@ -214,7 +214,7 @@ class State(object):
def on_guild_delete(self, event):
if event.id in self.guilds:
# Just delete the guild, channel references will fall
# Just delete the guild, channel references will fail
del self.guilds[event.id]
if event.id in self.voice_clients:

2
disco/types/base.py

@ -215,7 +215,7 @@ def datetime(data):
except (ValueError, TypeError):
continue
raise ValueError('Failed to conver `{}` to datetime'.format(data))
raise ValueError('Failed to convert `{}` to datetime'.format(data))
def text(obj):

6
disco/types/channel.py

@ -457,21 +457,21 @@ class Channel(SlottedModel, Permissible):
"""
Sets the channels bitrate.
"""
assert (self.is_voice)
assert self.is_voice
return self.client.api.channels_modify(self.id, bitrate=bitrate, reason=reason)
def set_user_limit(self, user_limit, reason=None):
"""
Sets the channels user limit.
"""
assert (self.is_voice)
assert self.is_voice
return self.client.api.channels_modify(self.id, user_limit=user_limit, reason=reason)
def set_parent(self, parent, reason=None):
"""
Sets the channels parent.
"""
assert (self.is_guild)
assert self.is_guild
return self.client.api.channels_modify(
self.id,
parent_id=to_snowflake(parent) if parent else parent,

8
disco/types/guild.py

@ -75,7 +75,7 @@ class GuildEmoji(Emoji):
@property
def url(self):
return 'https://discordapp.com/api/emojis/{}.{}'.format(self.id, 'gif' if self.animated else 'png')
return 'https://media.discordapp.net/emojis/{}.{}'.format(self.id, 'gif' if self.animated else 'png')
@cached_property
def guild(self):
@ -505,19 +505,19 @@ class Guild(SlottedModel, Permissible):
if not self.icon:
return ''
return 'https://cdn.discordapp.com/icons/{}/{}.{}?size={}'.format(self.id, self.icon, fmt, size)
return 'https://media.discordapp.net/icons/{}/{}.{}?size={}'.format(self.id, self.icon, fmt, size)
def get_splash_url(self, fmt='webp', size=1024):
if not self.splash:
return ''
return 'https://cdn.discordapp.com/splashes/{}/{}.{}?size={}'.format(self.id, self.splash, fmt, size)
return 'https://media.discordapp.net/splashes/{}/{}.{}?size={}'.format(self.id, self.splash, fmt, size)
def get_banner_url(self, fmt='webp', size=1024):
if not self.banner:
return ''
return 'https://cdn.discordapp.com/banners/{}/{}.{}?size={}'.format(self.id, self.banner, fmt, size)
return 'https://media.discordapp.net/banners/{}/{}.{}?size={}'.format(self.id, self.banner, fmt, size)
@property
def icon_url(self):

6
disco/types/user.py

@ -26,11 +26,11 @@ class User(SlottedModel, with_equality('id'), with_hash('id')):
if not self.avatar:
return 'https://cdn.discordapp.com/embed/avatars/{}.png'.format(self.default_avatar.value)
if fmt is not None:
return 'https://cdn.discordapp.com/avatars/{}/{}.{}?size={}'.format(self.id, self.avatar, fmt, size)
return 'https://media.discordapp.net/avatars/{}/{}.{}?size={}'.format(self.id, self.avatar, fmt, size)
if self.avatar.startswith('a_'):
return 'https://cdn.discordapp.com/avatars/{}/{}.gif?size={}'.format(self.id, self.avatar, size)
return 'https://media.discordapp.net/avatars/{}/{}.gif?size={}'.format(self.id, self.avatar, size)
else:
return 'https://cdn.discordapp.com/avatars/{}/{}.webp?size={}'.format(self.id, self.avatar, size)
return 'https://media.discordapp.net/avatars/{}/{}.webp?size={}'.format(self.id, self.avatar, size)
@property
def default_avatar(self):

5
disco/util/serializer.py

@ -12,7 +12,7 @@ class Serializer(object):
@classmethod
def check_format(cls, fmt):
if fmt not in cls.FORMATS:
raise Exception('Unsupported serilization format: {}'.format(fmt))
raise Exception('Unsupported serialization format: {}'.format(fmt))
@staticmethod
def json():
@ -31,8 +31,9 @@ class Serializer(object):
@classmethod
def loads(cls, fmt, raw):
import yaml
loads, _ = getattr(cls, fmt)()
return loads(raw)
return loads(raw, Loader=yaml.FullLoader)
@classmethod
def dumps(cls, fmt, raw):

40
disco/voice/client.py

@ -143,7 +143,7 @@ class VoiceClient(LoggingClass):
return self.ssrc + 3
def set_state(self, state):
self.log.debug('[%s] state %s -> %s', self, self.state, state)
self.log.debug('[{}] state {} -> {}'.format(self, self.state, state))
prev_state = self.state
self.state = state
self.state_emitter.emit(state, prev_state)
@ -154,7 +154,7 @@ class VoiceClient(LoggingClass):
return
self.log.info(
'[%s] Set endpoint from VOICE_SERVER_UPDATE (state = %s / endpoint = %s)', self, self.state, endpoint)
'[{}] Set endpoint from VOICE_SERVER_UPDATE (state = {} / endpoint = {})'.format(self, self.state, endpoint))
self.endpoint = endpoint
@ -210,13 +210,13 @@ class VoiceClient(LoggingClass):
def send(self, op, data):
if self.ws and self.ws.sock and self.ws.sock.connected:
self.log.debug('[%s] sending OP %s (data = %s)', self, op, data)
self.log.debug('[{}] sending OP {} (data = {})'.format(self, op, data))
self.ws.send(self.encoder.encode({
'op': op.value,
'd': data,
}), self.encoder.OPCODE)
else:
self.log.debug('[%s] dropping because ws is closed OP %s (data = %s)', self, op, data)
self.log.debug('[{}] dropping because ws is closed OP {} (data = {})'.format(self, op, data))
def on_voice_client_connect(self, data):
user_id = int(data['user_id'])
@ -241,12 +241,12 @@ class VoiceClient(LoggingClass):
self.udp.set_audio_codec(data['audio_codec'])
def on_voice_hello(self, data):
self.log.info('[%s] Received Voice HELLO payload, starting heartbeater', self)
self.log.info('[{}] Received Voice HELLO payload, starting heartbeat'.format(self))
self._heartbeat_task = gevent.spawn(self._heartbeat, data['heartbeat_interval'])
self.set_state(VoiceState.AUTHENTICATED)
def on_voice_ready(self, data):
self.log.info('[%s] Received Voice READY payload, attempting to negotiate voice connection w/ remote', self)
self.log.info('[{}] Received Voice READY payload, attempting to negotiate voice connection w/ remote'.format(self))
self.set_state(VoiceState.CONNECTING)
self.ssrc = data['ssrc']
self.ip = data['ip']
@ -256,12 +256,12 @@ class VoiceClient(LoggingClass):
for mode in self.SUPPORTED_MODES:
if mode in data['modes']:
self.mode = mode
self.log.debug('[%s] Selected mode %s', self, mode)
self.log.debug('[{}] Selected mode {}'.format(self, mode))
break
else:
raise Exception('Failed to find a supported voice mode')
self.log.debug('[%s] Attempting IP discovery over UDP to %s:%s', self, self.ip, self.port)
self.log.debug('[{}] Attempting IP discovery over UDP to {}:{}'.format(self, self.ip, self.port))
self.udp = UDPVoiceClient(self)
ip, port = self.udp.connect(self.ip, self.port)
@ -281,7 +281,7 @@ class VoiceClient(LoggingClass):
'payload_type': RTPPayloadTypes.get(codec).value,
})
self.log.debug('[%s] IP discovery completed (ip = %s, port = %s), sending SELECT_PROTOCOL', self, ip, port)
self.log.debug('[{}] IP discovery completed (ip = {}, port = {}), sending SELECT_PROTOCOL'.format(self, ip, port))
self.send(VoiceOPCode.SELECT_PROTOCOL, {
'protocol': 'udp',
'data': {
@ -298,11 +298,11 @@ class VoiceClient(LoggingClass):
})
def on_voice_resumed(self, data):
self.log.info('[%s] Received resumed', self)
self.log.info('[{}] Received resumed'.format(self))
self.set_state(VoiceState.CONNECTED)
def on_voice_sdp(self, sdp):
self.log.info('[%s] Received session description, connection completed', self)
self.log.info('[{}] Received session description, connection completed'.format(self))
self.mode = sdp['mode']
self.audio_codec = sdp['audio_codec']
@ -341,7 +341,7 @@ class VoiceClient(LoggingClass):
self.log.exception('Failed to parse voice gateway message: ')
def on_error(self, err):
self.log.error('[%s] Voice websocket error: %s', self, err)
self.log.error('[{}] Voice websocket error: {}'.format(self, err))
def on_open(self):
if self._identified:
@ -360,7 +360,7 @@ class VoiceClient(LoggingClass):
})
def on_close(self, code, reason):
self.log.warning('[%s] Voice websocket closed: [%s] %s (%s)', self, code, reason, self._reconnects)
self.log.warning('[{}] Voice websocket closed: [{}] {} ({})'.format(self, code, reason, self._reconnects))
if self._heartbeat_task:
self._heartbeat_task.kill()
@ -372,7 +372,7 @@ class VoiceClient(LoggingClass):
if self.state == VoiceState.DISCONNECTED:
return
self.log.info('[%s] Attempting Websocket Resumption', self)
self.log.info('[{}] Attempting Websocket Resumption'.format(self))
self.set_state(VoiceState.RECONNECTING)
@ -396,7 +396,7 @@ class VoiceClient(LoggingClass):
wait_time = 1
self.log.info(
'[%s] Will attempt to %s after %s seconds', self, 'resume' if self._identified else 'reconnect', wait_time)
'[{}] Will attempt to {} after {} seconds'.format(self, 'resume' if self._identified else 'reconnect', wait_time))
gevent.sleep(wait_time)
self._connect_and_run()
@ -406,17 +406,17 @@ class VoiceClient(LoggingClass):
channel_id = self.server_id
if not channel_id:
raise VoiceException('[%s] cannot connect to an empty channel id', self)
raise VoiceException('[{}] cannot connect to an empty channel id'.format(self))
if self.channel_id == channel_id:
if self.state == VoiceState.CONNECTED:
self.log.debug('[%s] Already connected to %s, returning', self, self.channel)
self.log.debug('[{}] Already connected to {}, returning'.format(self, self.channel))
return self
else:
if self.state == VoiceState.CONNECTED:
self.log.debug('[%s] Moving to channel %s', self, channel_id)
self.log.debug('[{}] Moving to channel {}'.format(self, channel_id))
else:
self.log.debug('[%s] Attempting connection to channel id %s', self, channel_id)
self.log.debug('[{}] Attempting connection to channel id {}'.format(self, channel_id))
self.set_state(VoiceState.AWAITING_ENDPOINT)
self.set_voice_state(channel_id, **kwargs)
@ -431,7 +431,7 @@ class VoiceClient(LoggingClass):
if self.state == VoiceState.DISCONNECTED:
return
self.log.debug('[%s] disconnect called', self)
self.log.debug('[{}] disconnect called'.format(self))
self.set_state(VoiceState.DISCONNECTED)
del self.client.state.voice_clients[self.server_id]

2
disco/voice/playable.py

@ -12,7 +12,7 @@ from disco.voice.opus import OpusEncoder
try:
from cStringIO import cStringIO as BufferedIO
from io import StringIO as BufferedIO
except ImportError:
if six.PY2:
from StringIO import StringIO as BufferedIO

20
disco/voice/udp.py

@ -7,7 +7,7 @@ from collections import namedtuple
try:
import nacl.secret
except ImportError:
print('WARNING: nacl is not installed, voice support is disabled')
print('WARNING: PyNaCl is not installed, voice support is disabled')
from holster.enum import Enum
@ -103,7 +103,7 @@ class UDPVoiceClient(LoggingClass):
ptype = RTPPayloadTypes.get(codec)
self._rtp_audio_header[1] = ptype.value
self.log.debug('[%s] Set UDP\'s Audio Codec to %s, RTP payload type %s', self.vc, ptype.name, ptype.value)
self.log.debug('[{}] Set UDP\'s Audio Codec to {}, RTP payload type {}'.format(self.vc, ptype.name, ptype.value))
def increment_timestamp(self, by):
self.timestamp += by
@ -168,7 +168,7 @@ class UDPVoiceClient(LoggingClass):
# Data cannot be less than the bare minimum, just ignore
if len(data) <= 12:
self.log.debug('[%s] [VoiceData] Received voice data under 13 bytes', self.vc)
self.log.debug('[{}] [VoiceData] Received voice data under 13 bytes'.format(self.vc))
continue
first, second = struct.unpack_from('>BB', data)
@ -220,14 +220,14 @@ class UDPVoiceClient(LoggingClass):
# Check if rtp version is 2
if rtp.version != 2:
self.log.debug('[%s] [VoiceData] Received an invalid RTP packet version, %s', self.vc, rtp.version)
self.log.debug('[{}] [VoiceData] Received an invalid RTP packet version, {}'.format(self.vc, rtp.version))
continue
payload_type = RTPPayloadTypes.get(rtp.payload_type)
# Unsupported payload type received
if not payload_type:
self.log.debug('[%s] [VoiceData] Received unsupported payload type, %s', self.vc, rtp.payload_type)
self.log.debug('[{}] [VoiceData] Received unsupported payload type, {}'.format(self.vc, rtp.payload_type))
continue
nonce = bytearray(24)
@ -240,13 +240,13 @@ class UDPVoiceClient(LoggingClass):
elif self.vc.mode == 'xsalsa20_poly1305':
nonce[:12] = data[:12]
else:
self.log.debug('[%s] [VoiceData] Unsupported Encryption Mode, %s', self.vc, self.vc.mode)
self.log.debug('[{}] [VoiceData] Unsupported Encryption Mode, {}'.format(self.vc, self.vc.mode))
continue
try:
data = self._secret_box.decrypt(bytes(data[12:]), bytes(nonce))
except Exception:
self.log.debug('[%s] [VoiceData] Failed to decode data from ssrc %s', self.vc, rtp.ssrc)
self.log.debug('[{}] [VoiceData] Failed to decode data from ssrc {}'.format(self.vc, rtp.ssrc))
continue
# RFC3550 Section 5.1 (Padding)
@ -290,7 +290,7 @@ class UDPVoiceClient(LoggingClass):
# RFC3550 Section 5.3: Profile-Specific Modifications to the RTP Header
# clients send it sometimes, definitely on fresh connects to a server, dunno what to do here
if rtp.marker:
self.log.debug('[%s] [VoiceData] Received RTP data with the marker set, skipping', self.vc)
self.log.debug('[{}] [VoiceData] Received RTP data with the marker set, skipping'.format(self.vc))
continue
payload = VoiceData(
@ -328,7 +328,7 @@ class UDPVoiceClient(LoggingClass):
try:
data, addr = gevent.spawn(lambda: self.conn.recvfrom(70)).get(timeout=timeout)
except gevent.Timeout:
return (None, None)
return None, None
# Read IP and port
ip = str(data[4:]).split('\x00', 1)[0]
@ -338,4 +338,4 @@ class UDPVoiceClient(LoggingClass):
self.connected = True
self._run_task = gevent.spawn(self.run)
return (ip, port)
return ip, port

12
docs/bot_tutorial/building_block_listeners.md

@ -1,6 +1,6 @@
# Listeners
Listeners provide an API to listen to and execute code upon the occurance of specified Discord events.
Listeners provide an API to listen to and execute code upon the occurrence of specified Discord events.
## Listener Basics
@ -9,7 +9,7 @@ To start off with, lets create a listener attached to our plugin that fires when
```py
@Plugin.listen('MessageCreate')
def on_message_create(self, event):
self.log.debug('Got message: %s', event.message)
self.log.debug('Got message: {}'.format(event.message))
```
Ok, but what if we want to make a listener which welcomes new users to our server? Well thats also easy:
@ -28,7 +28,7 @@ To see all the events you can subscribe too, checkout the [gateway events list](
## Listener Priority
Each listener thats registered comes with a priority. This priority describes how the builtin event emitter will distribute events to your listener. To set a priority you can simply pass the priority kwarg:
Each listener that's registered comes with a priority. This priority describes how the builtin event emitter will distribute events to your listener. To set a priority you can simply pass the priority kwarg:
```py
from holster.emitter import Priority
@ -43,6 +43,6 @@ def on_guild_member_add(self, event):
| Name | Description |
|------|-------------|
| BEFORE | Recieves all events sequentially alongside the emitter. This is the most dangerous priority level, as any executed code will block other events in the emitter from flowing. Blocking within a BEFORE handler can be lethal. |
| SEQUENTIAL | Recieves all events sequentially, but within a seperate greenlet. This priority level can be used for plugins that require sequential events but may block or take a long time to execute their event handler. |
| NONE | This priority provides no guarentees about the ordering of events. Similar to SEQUENTIAL all event handlers are called within a seperate greenlet. |
| BEFORE | Receives all events sequentially alongside the emitter. This is the most dangerous priority level, as any executed code will block other events in the emitter from flowing. Blocking within a BEFORE handler can be lethal. |
| SEQUENTIAL | Receives all events sequentially, but within a separate greenlet. This priority level can be used for plugins that require sequential events but may block or take a long time to execute their event handler. |
| NONE | This priority provides no guarantees about the ordering of events. Similar to SEQUENTIAL all event handlers are called within a separate greenlet. |

8
setup.py

@ -10,16 +10,16 @@ with open('README.md') as f:
readme = f.read()
extras_require = {
'voice': ['pynacl==1.2.1'],
'http': ['flask==0.12.2'],
'yaml': ['pyyaml==3.12'],
'voice': ['pynacl>=1.2.1'],
'http': ['flask>=0.12.2'],
'yaml': ['pyyaml>=3.12'],
'music': ['youtube_dl>=2018.1.21'],
'performance': [
'erlpack==0.3.2' if sys.version_info.major == 2 else 'earl-etf==2.1.2',
'ujson==1.35',
'wsaccel==0.6.2',
],
'sharding': ['gipc==0.6.0'],
'sharding': ['gipc>=0.6.0'],
'docs': ['biblio==0.0.4'],
}

Loading…
Cancel
Save