Support for v5 Gateway.
This commit is contained in:
parent
c3c9db7777
commit
8b3617111a
@ -108,7 +108,7 @@ class Channel(Hashable):
|
|||||||
|
|
||||||
self._permission_overwrites = []
|
self._permission_overwrites = []
|
||||||
everyone_index = 0
|
everyone_index = 0
|
||||||
everyone_id = self.server.default_role.id
|
everyone_id = self.server.id
|
||||||
|
|
||||||
for index, overridden in enumerate(kwargs.get('permission_overwrites', [])):
|
for index, overridden in enumerate(kwargs.get('permission_overwrites', [])):
|
||||||
overridden_id = overridden['id']
|
overridden_id = overridden['id']
|
||||||
|
@ -138,7 +138,8 @@ class Client:
|
|||||||
if max_messages is None or max_messages < 100:
|
if max_messages is None or max_messages < 100:
|
||||||
max_messages = 5000
|
max_messages = 5000
|
||||||
|
|
||||||
self.connection = ConnectionState(self.dispatch, self.request_offline_members, max_messages, loop=self.loop)
|
self.connection = ConnectionState(self.dispatch, self.request_offline_members,
|
||||||
|
self._syncer, max_messages, loop=self.loop)
|
||||||
|
|
||||||
connector = options.pop('connector', None)
|
connector = options.pop('connector', None)
|
||||||
self.http = HTTPClient(connector, loop=self.loop)
|
self.http = HTTPClient(connector, loop=self.loop)
|
||||||
@ -149,6 +150,10 @@ class Client:
|
|||||||
|
|
||||||
# internals
|
# internals
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
|
def _syncer(self, guilds):
|
||||||
|
yield from self.ws.request_sync(guilds)
|
||||||
|
|
||||||
def _get_cache_filename(self, email):
|
def _get_cache_filename(self, email):
|
||||||
filename = hashlib.md5(email.encode('utf-8')).hexdigest()
|
filename = hashlib.md5(email.encode('utf-8')).hexdigest()
|
||||||
return os.path.join(tempfile.gettempdir(), 'discord_py', filename)
|
return os.path.join(tempfile.gettempdir(), 'discord_py', filename)
|
||||||
@ -295,12 +300,16 @@ class Client:
|
|||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def _login_1(self, token, **kwargs):
|
def _login_1(self, token, **kwargs):
|
||||||
log.info('logging in using static token')
|
log.info('logging in using static token')
|
||||||
yield from self.http.static_login(token, bot=kwargs.pop('bot', True))
|
is_bot = kwargs.pop('bot', True)
|
||||||
|
yield from self.http.static_login(token, bot=is_bot)
|
||||||
|
self.connection.is_bot = is_bot
|
||||||
self._is_logged_in.set()
|
self._is_logged_in.set()
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def _login_2(self, email, password, **kwargs):
|
def _login_2(self, email, password, **kwargs):
|
||||||
# attempt to read the token from cache
|
# attempt to read the token from cache
|
||||||
|
self.connection.is_bot = False
|
||||||
|
|
||||||
if self.cache_auth:
|
if self.cache_auth:
|
||||||
token = self._get_cache_token(email, password)
|
token = self._get_cache_token(email, password)
|
||||||
try:
|
try:
|
||||||
|
@ -127,6 +127,13 @@ class DiscordWebSocket(websockets.client.WebSocketClientProtocol):
|
|||||||
INVALIDATE_SESSION
|
INVALIDATE_SESSION
|
||||||
Receive only. Tells the client to invalidate the session and IDENTIFY
|
Receive only. Tells the client to invalidate the session and IDENTIFY
|
||||||
again.
|
again.
|
||||||
|
HELLO
|
||||||
|
Receive only. Tells the client the heartbeat interval.
|
||||||
|
HEARTBEAT_ACK
|
||||||
|
Receive only. Confirms receiving of a heartbeat. Not having it implies
|
||||||
|
a connection issue.
|
||||||
|
GUILD_SYNC
|
||||||
|
Send only. Requests a guild sync.
|
||||||
gateway
|
gateway
|
||||||
The gateway we are currently connected to.
|
The gateway we are currently connected to.
|
||||||
token
|
token
|
||||||
@ -143,6 +150,9 @@ class DiscordWebSocket(websockets.client.WebSocketClientProtocol):
|
|||||||
RECONNECT = 7
|
RECONNECT = 7
|
||||||
REQUEST_MEMBERS = 8
|
REQUEST_MEMBERS = 8
|
||||||
INVALIDATE_SESSION = 9
|
INVALIDATE_SESSION = 9
|
||||||
|
HELLO = 10
|
||||||
|
HEARTBEAT_ACK = 11
|
||||||
|
GUILD_SYNC = 12
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, max_size=None, **kwargs)
|
super().__init__(*args, max_size=None, **kwargs)
|
||||||
@ -172,6 +182,10 @@ class DiscordWebSocket(websockets.client.WebSocketClientProtocol):
|
|||||||
client.connection._update_references(ws)
|
client.connection._update_references(ws)
|
||||||
|
|
||||||
log.info('Created websocket connected to {}'.format(gateway))
|
log.info('Created websocket connected to {}'.format(gateway))
|
||||||
|
|
||||||
|
# poll the event for OP HELLO
|
||||||
|
yield from ws.poll_event()
|
||||||
|
|
||||||
if not resume:
|
if not resume:
|
||||||
yield from ws.identify()
|
yield from ws.identify()
|
||||||
log.info('sent the identify payload to create the websocket')
|
log.info('sent the identify payload to create the websocket')
|
||||||
@ -232,6 +246,10 @@ class DiscordWebSocket(websockets.client.WebSocketClientProtocol):
|
|||||||
'v': 3
|
'v': 3
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if not self._connection.is_bot:
|
||||||
|
payload['d']['synced_guilds'] = []
|
||||||
|
|
||||||
yield from self.send_as_json(payload)
|
yield from self.send_as_json(payload)
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
@ -277,6 +295,12 @@ class DiscordWebSocket(websockets.client.WebSocketClientProtocol):
|
|||||||
yield from self.close()
|
yield from self.close()
|
||||||
raise ReconnectWebSocket()
|
raise ReconnectWebSocket()
|
||||||
|
|
||||||
|
if op == self.HELLO:
|
||||||
|
interval = data['heartbeat_interval'] / 1000.0
|
||||||
|
self._keep_alive = KeepAliveHandler(ws=self, interval=interval)
|
||||||
|
self._keep_alive.start()
|
||||||
|
return
|
||||||
|
|
||||||
if op == self.INVALIDATE_SESSION:
|
if op == self.INVALIDATE_SESSION:
|
||||||
state.sequence = None
|
state.sequence = None
|
||||||
state.session_id = None
|
state.session_id = None
|
||||||
@ -298,11 +322,6 @@ class DiscordWebSocket(websockets.client.WebSocketClientProtocol):
|
|||||||
state.sequence = msg['s']
|
state.sequence = msg['s']
|
||||||
state.session_id = data['session_id']
|
state.session_id = data['session_id']
|
||||||
|
|
||||||
if is_ready or event == 'RESUMED':
|
|
||||||
interval = data['heartbeat_interval'] / 1000.0
|
|
||||||
self._keep_alive = KeepAliveHandler(ws=self, interval=interval)
|
|
||||||
self._keep_alive.start()
|
|
||||||
|
|
||||||
parser = 'parse_' + event.lower()
|
parser = 'parse_' + event.lower()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -400,6 +419,14 @@ class DiscordWebSocket(websockets.client.WebSocketClientProtocol):
|
|||||||
status = Status.idle if idle_since else Status.online
|
status = Status.idle if idle_since else Status.online
|
||||||
me.status = status
|
me.status = status
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
|
def request_sync(self, guild_ids):
|
||||||
|
payload = {
|
||||||
|
'op': self.GUILD_SYNC,
|
||||||
|
'd': list(guild_ids)
|
||||||
|
}
|
||||||
|
yield from self.send_as_json(payload)
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def voice_state(self, guild_id, channel_id, self_mute=False, self_deaf=False):
|
def voice_state(self, guild_id, channel_id, self_mute=False, self_deaf=False):
|
||||||
payload = {
|
payload = {
|
||||||
|
@ -497,4 +497,4 @@ class HTTPClient:
|
|||||||
data = yield from self.get(self.GATEWAY, bucket=_func_())
|
data = yield from self.get(self.GATEWAY, bucket=_func_())
|
||||||
except HTTPException as e:
|
except HTTPException as e:
|
||||||
raise GatewayNotFound() from e
|
raise GatewayNotFound() from e
|
||||||
return data.get('url') + '?encoding=json&v=4'
|
return data.get('url') + '?encoding=json&v=5'
|
||||||
|
@ -169,7 +169,6 @@ class Server(Hashable):
|
|||||||
self._member_count = member_count
|
self._member_count = member_count
|
||||||
|
|
||||||
self.name = guild.get('name')
|
self.name = guild.get('name')
|
||||||
self.large = guild.get('large', None if member_count is None else self._member_count > 250)
|
|
||||||
self.region = guild.get('region')
|
self.region = guild.get('region')
|
||||||
try:
|
try:
|
||||||
self.region = ServerRegion(self.region)
|
self.region = ServerRegion(self.region)
|
||||||
@ -181,24 +180,36 @@ class Server(Hashable):
|
|||||||
self.unavailable = guild.get('unavailable', False)
|
self.unavailable = guild.get('unavailable', False)
|
||||||
self.id = guild['id']
|
self.id = guild['id']
|
||||||
self.roles = [Role(server=self, **r) for r in guild.get('roles', [])]
|
self.roles = [Role(server=self, **r) for r in guild.get('roles', [])]
|
||||||
|
self._sync(guild)
|
||||||
for data in guild.get('members', []):
|
self.large = None if member_count is None else self._member_count > 250
|
||||||
roles = [self.default_role]
|
|
||||||
for role_id in data['roles']:
|
|
||||||
role = utils.find(lambda r: r.id == role_id, self.roles)
|
|
||||||
if role is not None:
|
|
||||||
roles.append(role)
|
|
||||||
|
|
||||||
data['roles'] = roles
|
|
||||||
member = Member(**data)
|
|
||||||
member.server = self
|
|
||||||
self._add_member(member)
|
|
||||||
|
|
||||||
if 'owner_id' in guild:
|
if 'owner_id' in guild:
|
||||||
self.owner_id = guild['owner_id']
|
self.owner_id = guild['owner_id']
|
||||||
self.owner = self.get_member(self.owner_id)
|
self.owner = self.get_member(self.owner_id)
|
||||||
|
|
||||||
for presence in guild.get('presences', []):
|
afk_id = guild.get('afk_channel_id')
|
||||||
|
self.afk_channel = self.get_channel(afk_id)
|
||||||
|
|
||||||
|
for obj in guild.get('voice_states', []):
|
||||||
|
self._update_voice_state(obj)
|
||||||
|
|
||||||
|
def _sync(self, data):
|
||||||
|
if 'large' in data:
|
||||||
|
self.large = data['large']
|
||||||
|
|
||||||
|
for mdata in data.get('members', []):
|
||||||
|
roles = [self.default_role]
|
||||||
|
for role_id in mdata['roles']:
|
||||||
|
role = utils.find(lambda r: r.id == role_id, self.roles)
|
||||||
|
if role is not None:
|
||||||
|
roles.append(role)
|
||||||
|
|
||||||
|
mdata['roles'] = roles
|
||||||
|
member = Member(**mdata)
|
||||||
|
member.server = self
|
||||||
|
self._add_member(member)
|
||||||
|
|
||||||
|
for presence in data.get('presences', []):
|
||||||
user_id = presence['user']['id']
|
user_id = presence['user']['id']
|
||||||
member = self.get_member(user_id)
|
member = self.get_member(user_id)
|
||||||
if member is not None:
|
if member is not None:
|
||||||
@ -210,17 +221,12 @@ class Server(Hashable):
|
|||||||
game = presence.get('game', {})
|
game = presence.get('game', {})
|
||||||
member.game = Game(**game) if game else None
|
member.game = Game(**game) if game else None
|
||||||
|
|
||||||
if 'channels' in guild:
|
if 'channels' in data:
|
||||||
channels = guild['channels']
|
channels = data['channels']
|
||||||
for c in channels:
|
for c in channels:
|
||||||
channel = Channel(server=self, **c)
|
channel = Channel(server=self, **c)
|
||||||
self._add_channel(channel)
|
self._add_channel(channel)
|
||||||
|
|
||||||
afk_id = guild.get('afk_channel_id')
|
|
||||||
self.afk_channel = self.get_channel(afk_id)
|
|
||||||
|
|
||||||
for obj in guild.get('voice_states', []):
|
|
||||||
self._update_voice_state(obj)
|
|
||||||
|
|
||||||
@utils.cached_slot_property('_default_role')
|
@utils.cached_slot_property('_default_role')
|
||||||
def default_role(self):
|
def default_role(self):
|
||||||
|
@ -49,11 +49,13 @@ log = logging.getLogger(__name__)
|
|||||||
ReadyState = namedtuple('ReadyState', ('launch', 'servers'))
|
ReadyState = namedtuple('ReadyState', ('launch', 'servers'))
|
||||||
|
|
||||||
class ConnectionState:
|
class ConnectionState:
|
||||||
def __init__(self, dispatch, chunker, max_messages, *, loop):
|
def __init__(self, dispatch, chunker, syncer, max_messages, *, loop):
|
||||||
self.loop = loop
|
self.loop = loop
|
||||||
self.max_messages = max_messages
|
self.max_messages = max_messages
|
||||||
self.dispatch = dispatch
|
self.dispatch = dispatch
|
||||||
self.chunker = chunker
|
self.chunker = chunker
|
||||||
|
self.syncer = syncer
|
||||||
|
self.is_bot = None
|
||||||
self._listeners = []
|
self._listeners = []
|
||||||
self.clear()
|
self.clear()
|
||||||
|
|
||||||
@ -165,8 +167,9 @@ class ConnectionState:
|
|||||||
launch.set()
|
launch.set()
|
||||||
yield from asyncio.sleep(2)
|
yield from asyncio.sleep(2)
|
||||||
|
|
||||||
# get all the chunks
|
|
||||||
servers = self._ready_state.servers
|
servers = self._ready_state.servers
|
||||||
|
|
||||||
|
# get all the chunks
|
||||||
chunks = []
|
chunks = []
|
||||||
for server in servers:
|
for server in servers:
|
||||||
chunks.extend(self.chunks_needed(server))
|
chunks.extend(self.chunks_needed(server))
|
||||||
@ -194,9 +197,12 @@ class ConnectionState:
|
|||||||
servers = self._ready_state.servers
|
servers = self._ready_state.servers
|
||||||
for guild in guilds:
|
for guild in guilds:
|
||||||
server = self._add_server_from_data(guild)
|
server = self._add_server_from_data(guild)
|
||||||
if server.large:
|
if server.large or not self.is_bot:
|
||||||
servers.append(server)
|
servers.append(server)
|
||||||
|
|
||||||
|
if not self.is_bot:
|
||||||
|
compat.create_task(self.syncer([s.id for s in self.servers]), loop=self.loop)
|
||||||
|
|
||||||
for pm in data.get('private_channels'):
|
for pm in data.get('private_channels'):
|
||||||
self._add_private_channel(PrivateChannel(id=pm['id'],
|
self._add_private_channel(PrivateChannel(id=pm['id'],
|
||||||
user=User(**pm['recipient'])))
|
user=User(**pm['recipient'])))
|
||||||
@ -427,6 +433,10 @@ class ConnectionState:
|
|||||||
else:
|
else:
|
||||||
self.dispatch('server_join', server)
|
self.dispatch('server_join', server)
|
||||||
|
|
||||||
|
def parse_guild_sync(self, data):
|
||||||
|
server = self._get_server(data.get('id'))
|
||||||
|
server._sync(data)
|
||||||
|
|
||||||
def parse_guild_update(self, data):
|
def parse_guild_update(self, data):
|
||||||
server = self._get_server(data.get('id'))
|
server = self._get_server(data.get('id'))
|
||||||
if server is not None:
|
if server is not None:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user