Passed
Push — main ( 3c43d8...60b649 )
by
unknown
02:22
created

pyscord.core.gateway.Dispatcher.__dispatcher()   A

Complexity

Conditions 3

Size

Total Lines 12
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 7
dl 0
loc 12
rs 10
c 0
b 0
f 0
cc 3
nop 2
1
# -*- coding: utf-8 -*-
0 ignored issues
show
introduced by
Missing module docstring
Loading history...
2
# MIT License
3
#
4
# Copyright (c) 2021 Pyscord
5
#
6
# Permission is hereby granted, free of charge, to any person obtaining a copy
7
# of this software and associated documentation files (the "Software"), to deal
8
# in the Software without restriction, including without limitation the rights
9
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
# copies of the Software, and to permit persons to whom the Software is
11
# furnished to do so, subject to the following conditions:
12
#
13
# The above copyright notice and this permission notice shall be included in all
14
# copies or substantial portions of the Software.
15
#
16
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
# SOFTWARE.
23
from __future__ import annotations
24
25
from asyncio import get_event_loop, AbstractEventLoop, ensure_future
26
from platform import system
27
from typing import Dict, Callable, Awaitable
28
29
from websockets import connect
0 ignored issues
show
Bug introduced by
The name connect does not seem to exist in module websockets.
Loading history...
30
from websockets.legacy.client import WebSocketClientProtocol
31
32
from pyscord import __package__
1 ignored issue
show
Bug Best Practice introduced by
This seems to re-define the built-in __package__.

It is generally discouraged to redefine built-ins as this makes code very hard to read.

Loading history...
33
from pyscord._config import GatewayConfig
34
# TODO: Implement logging
0 ignored issues
show
Coding Style introduced by
TODO and FIXME comments should generally be avoided.
Loading history...
35
from pyscord.core.dispatch import GatewayDispatch
36
from pyscord.core.handlers.heartbeat import handle_hello, handle_heartbeat
37
38
Handler = Callable[[WebSocketClientProtocol, GatewayDispatch], Awaitable[None]]
39
40
41
class Dispatcher:
42
    """
43
    The Dispatcher handles all interactions with the discord websocket
44
    API. This also contains the main event loop, and handles the heartbeat.
45
46
    Running the dispatcher will create a connection with the
47
    Discord WebSocket API on behalf of the provided token. This token
48
    must be a bot token. (Which can be found on
49
    `/developers/applications/<bot_id>/bot`)
50
    """
51
52
    # TODO: Add intents argument
0 ignored issues
show
Coding Style introduced by
TODO and FIXME comments should generally be avoided.
Loading history...
53
    # TODO: Add handlers argument
0 ignored issues
show
Coding Style introduced by
TODO and FIXME comments should generally be avoided.
Loading history...
54
    def __init__(self, token: str):
55
        # TODO: Write docs for __init__.
0 ignored issues
show
Coding Style introduced by
TODO and FIXME comments should generally be avoided.
Loading history...
56
        self.__token = token
57
        self.__keep_alive = True
58
59
        async def identify_and_handle_hello(socket: WebSocketClientProtocol,
60
                                            payload: GatewayDispatch):
61
            # TODO: Fix docs
0 ignored issues
show
Coding Style introduced by
TODO and FIXME comments should generally be avoided.
Loading history...
62
            await socket.send(str(GatewayDispatch(2, {
63
                "token": token,
64
                "intents": 0,
65
                "properties": {
66
                    "$os": system(),
67
                    "$browser": __package__,
68
                    "$device": __package__
69
                }
70
            })))
71
            await handle_hello(socket, payload)
72
73
        self.__dispatch_handlers: Dict[int, Handler] = {
74
            10: identify_and_handle_hello,
75
            11: handle_heartbeat
76
        }
77
78
    # TODO: Implement socket typehint
0 ignored issues
show
Coding Style introduced by
TODO and FIXME comments should generally be avoided.
Loading history...
79
    async def handler_manager(self, socket: WebSocketClientProtocol,
80
                              payload: GatewayDispatch,
81
                              loop: AbstractEventLoop):
82
        """
83
        This manages all handles for given OP codes.
84
        This method gets invoked for every message that is received from
85
        Discord.
86
87
        :param socket: The current socket, which can be used to interact
88
                with the Discord API.
89
        :param payload: The received payload from Discord.
90
        :param loop: The current async loop on which the future is bound.
91
        """
92
        # TODO: Implement heartbeat
0 ignored issues
show
Coding Style introduced by
TODO and FIXME comments should generally be avoided.
Loading history...
93
        # TODO: Implement given handlers.
0 ignored issues
show
Coding Style introduced by
TODO and FIXME comments should generally be avoided.
Loading history...
94
        # TODO: Implement logging
0 ignored issues
show
Coding Style introduced by
TODO and FIXME comments should generally be avoided.
Loading history...
95
        handler: Handler = self.__dispatch_handlers.get(payload.op)
96
97
        if not handler:
98
            # TODO: Implement unhandled opcode exception if handler is None
0 ignored issues
show
Coding Style introduced by
TODO and FIXME comments should generally be avoided.
Loading history...
99
            return
100
101
        ensure_future(handler(socket, payload), loop=loop)
102
103
    async def __dispatcher(self, loop: AbstractEventLoop):
104
        """
105
        The main event loop. This handles all interactions with the
106
        websocket API.
107
        """
108
        # TODO: Implement logging
0 ignored issues
show
Coding Style introduced by
TODO and FIXME comments should generally be avoided.
Loading history...
109
        async with connect(GatewayConfig.uri()) as socket:
110
            while self.__keep_alive:
111
                await self.handler_manager(
112
                    socket,
113
                    GatewayDispatch.from_string(await socket.recv()),
114
                    loop)
115
116
    def run(self):
117
        """
118
        Instantiate the dispatcher, this will create a connection to the
119
        Discord websocket API on behalf of the client who's token has
120
        been passed.
121
        """
122
        # TODO: Implement logging
0 ignored issues
show
Coding Style introduced by
TODO and FIXME comments should generally be avoided.
Loading history...
123
        loop = get_event_loop()
124
        loop.run_until_complete(self.__dispatcher(loop))
125
        loop.close()
126
127
    def close(self):
128
        """
129
        Stop the dispatcher from listening and responding to gateway
130
        events. This should let the client close on itself.
131
        """
132
        # TODO: Implement logging
0 ignored issues
show
Coding Style introduced by
TODO and FIXME comments should generally be avoided.
Loading history...
133
        self.__keep_alive = False
134