예제 #1
0
    async def _keep_beating(self):
        """
        The beater task of kokoro.
        
        The control flow of tze method is the following:
            - If `.should_beat` is not `True`, break out.
            - Wait `.interval` time and set the waiting ``Future`` to `self.beat_waiter`. If kokoro is cancelled, or
                if it should beat now, it is cancelled and we repeat the loop.
            - If we did not get answer since last beating (what is triggered first from outside), then we stop the
                gateway. We also break out from the loop to terminate the beating state and we will wait for the
                websocket to connect again.
            - We beat one with starting `gateway._beat` as a ``Task`` and setting it to `.beat_task`. If the task is
                not completed before it's respective timeout, we stop the gateway here as well. We also break out
                from the loop to terminate the beating state and we will wait for the websocket to connect again.
                This task can also be cancelled. If cancellation occurs, we repeat the loop.
            - If the beating task is done, we update `.last_send` to the current `perf_counter` time. Repeat the loop.
        
        This method is a coroutine.
        """
        self.last_answer = perf_counter()
        gateway = self.gateway
        self.should_beat = True
        while self.should_beat:
            waiter = sleep(self.interval, KOKORO)
            self.beat_waiter = waiter
            try:
                await waiter
            except CancelledError:
                self.last_send = perf_counter()
                continue
            finally:
                self.beat_waiter = None

            if (self.last_answer + self.interval +
                    HEARTBEAT_TIMEOUT) - perf_counter() <= 0.0:
                self.should_beat = False
                Task(gateway.terminate(), KOKORO)
                break

            try:
                task = Task(gateway._beat(), KOKORO)
                future_or_timeout(task, HEARTBEAT_TIMEOUT)
                self.beat_task = task
                await task
            except TimeoutError:
                self.should_beat = False
                Task(gateway.terminate(), KOKORO)
                break
            except CancelledError:
                continue
            finally:
                self.beat_task = None

            self.last_send = perf_counter()
예제 #2
0
 async def start(self):
     """
     Starts the gateways of the sharder gateway.
     
     This method is a coroutine.
     """
     tasks = []
     for gateway in self.gateways:
         task = Task(gateway.start(), KOKORO)
         tasks.append(task)
     
     await WaitTillExc(tasks, KOKORO)
     
     for task in tasks:
         task.cancel()
예제 #3
0
    def skip(self, index=0):
        """
        Skips the currently played audio at the given index and returns it.
        
        Skipping nothing yields to returning `None`.
        
        Parameters
        ----------
        index : `int` = `0`, Optional
            The index of the audio to skip. Defaults to `0`, what causes the currently playing source to skipped.
        
        Returns
        -------
        source : `None`, ``AudioSource``
        """
        if index == 0:
            player = self.player
            if player is None:
                source = None
            else:
                source = player.source

            # Try playing next even if player is not `None`.
            Task(self.play_next(), KOKORO)

        elif index < 0:
            source = None
        else:
            queue = self.queue
            if index > len(queue):
                source = None
            else:
                source = queue.pop(index - 1)

        return source
예제 #4
0
    async def set_speaking(self, value):
        """
        A coroutine, what is used when changing the ``.speaking`` state of the voice client. By default when audio is
        played, the speaking state is changed to `True` and meanwhile not, then to `False`.
        
        This method is a coroutine.
        
        Parameters
        ----------
        value : `int` (`0`, `1`)
        
        Notes
        -----
        Tinkering with this method is not recommended.
        """
        task = self._set_speaking_task
        if (task is not None):
            await task

        if self.speaking == value:
            return

        self.speaking = value

        task = Task(self.gateway._set_speaking(value), KOKORO)
        self._set_speaking_task = task

        try:
            await task
        finally:
            self._set_speaking_task = None
예제 #5
0
    async def _loop_actual(self, last_source):
        """
        Repeats the last played audio if applicable.
        
        Should be used inside of ``.lock``to ensure that the voice client is not modified parallelly.
        
        This function is a coroutine.
        
        Parameters
        ----------
        self : ``VoiceClient``
            The respective voice client.
        last_source : `None`, ``AudioSource``
            The audio what was played.
        """
        if (last_source is None) or (not last_source.REPEATABLE):
            await self._play_next(self, None)
            return

        player = self.player
        if player is None:
            # Should not happen, lol
            self.player = AudioPlayer(self, last_source)
        else:
            # The audio was over.
            player.set_source(last_source)

        if self.connected.is_set():
            Task(self.set_speaking(1), KOKORO)
예제 #6
0
    def write(self, data):
        """
        Writes the given data to the buffer.
        
        Parameters
        ----------
        data : `str`
            The data to write.
        
        Raises
        ------
        TypeError
            Only string can be written to the output stream.
        ValueError
            I/O operation on closed or on a detached file.
        """
        if self._closed:
            raise ValueError('I/O operation on closed or on a detached file.')

        if not isinstance(data, str):
            raise TypeError(
                f'Only `str` can be written into `{self.__class__.__name__}`, got '
                f'{data.__class__.__name__}; {reprlib.repr(data)}.')

        if not data:
            return

        self._chunks.append(data)

        transfer_task = self._transfer_task
        if (transfer_task is None):
            self._transfer_task = Task(self._do_transfer(), KOKORO)
예제 #7
0
 async def _received_message(self, message):
     """
     Processes the message sent by Discord. If the message is `DISPATCH`, ensures the specific parser for it and
     returns `False`. For every other operation code it calls ``._special_operation`` and returns that's return.
     
     This method is a coroutine.
     
     Parameters
     ----------
     message : `bytes`
         The received message.
     
     Returns
     -------
     should_reconnect : `bool`
     
     Raises
     ------
     TimeoutError
         If the gateways's `.kokoro` is not beating, meanwhile it should.
     """
     # return True if we should reconnect
     message = from_json(message)
     
     operation = message['op']
     data = message.get('d', None)
     sequence = message.get('s', None)
     
     if sequence is not None:
         self.sequence = sequence
     
     if operation:
         return await self._special_operation(operation, data)
     
     # self.DISPATCH
     event = message['t']
     client = self.client
     try:
         parser = PARSERS[event]
     except KeyError:
         call_unknown_dispatch_event_event_handler(client, event, data)
         return False
     
     if data is None:
         return
     
     try:
         if parser(client, data) is None:
             return False
     except BaseException as err:
         Task(client.events.error(client, event, err), KOKORO)
         return False
     
     if event == 'READY':
         self.session_id = data['session_id']
     
     # elif event == 'RESUMED':
         # pass
     
     return False
예제 #8
0
파일: bases.py 프로젝트: HuyaneMatsu/hata
    def cancel(self, exception=None):
        """
        Cancels the pagination, if it is not cancelled yet.
        
        Parameters
        ----------
        exception : `None`, ``BaseException`` = `None`, Optional
            Exception to cancel the pagination with. Defaults to `None`
        
        Returns
        -------
        canceller_task : `None`, ``Task``
        """
        if self._task_flag in (GUI_STATE_READY, GUI_STATE_SWITCHING_PAGE,
                               GUI_STATE_CANCELLING):
            self._task_flag = GUI_STATE_CANCELLED

        canceller = self._canceller
        if canceller is None:
            return

        self._canceller = None

        timeouter = self._timeouter
        if (timeouter is not None):
            timeouter.cancel()

        return Task(canceller(self, exception), KOKORO)
예제 #9
0
    def delete_reaction_with(self, client):
        """
        Removes the added reaction.
        
        Parameters
        ----------
        client : ``Client``
            The client, who will execute the action.
        
        Returns
        -------
        result : `int`
            The identifier number of the action what will be executed.
            
            Can be one of the following:
            +-----------------------+-------+
            | Respective name       | Value |
            +=======================+=======+
            | DELETE_REACTION_OK    | 0     |
            +-----------------------+-------+
            | DELETE_REACTION_PERM  | 1     |
            +-----------------------+-------+
        """
        if self.message.channel.cached_permissions_for(
                client) & PERMISSION_MASK_MANAGE_MESSAGES:
            Task(_delete_reaction_with_task(self, client), KOKORO)
            result = self.DELETE_REACTION_OK
        else:
            result = self.DELETE_REACTION_PERM

        return result
예제 #10
0
    def __new__(cls, node, guild_id, channel_id):
        """
        Creates a new solar player instance.
        
        Parameters
        ----------
        node : ``SolarNode``
            The node of the player.
        guild_id : `int`
            The guild's identifier, where the node will connect to.
        channel_id : `int`
            The channel's identifier, where the node will connect to.
        
        Returns
        -------
        self : ``SolarPlayerBase``
            The player.
        waiter : ``Future``
            A future, which can be awaited to wait till the player is connected.
        """
        self = object.__new__(cls)

        self._filters = {}
        self._position = 0.0
        self._position_update = 0.0
        self._forward_data = None

        self.guild_id = guild_id
        self.channel_id = channel_id
        self.node = node

        waiter = Future(KOKORO)
        Task(self._connect(waiter=waiter), KOKORO)

        return self, waiter
예제 #11
0
    def start_beating(self):
        """
        Starts kokoro's beating. Handles all the cases around the board to do so.
        """
        # case 1 : we are not running
        if not self.running:
            Task(self._start_beating(), KOKORO)
            return

        #case 2 : we wait for ws
        waiter = self.ws_waiter
        if (waiter is not None):
            self.ws_waiter = None
            waiter.set_result(None)
            return

        #case 3 : we wait for beat response
        waiter = self.beat_waiter
        if (waiter is not None):
            self.beat_waiter = None
            waiter.cancel()
            return

        #case 4 : we are beating
        task = self.beat_task
        if (task is not None):
            self.beat_waiter = None
            task.cancel()
            return
예제 #12
0
파일: utils.py 프로젝트: HuyaneMatsu/hata
def _console_exit_callback():
    """
    Callback used by ``run_console_till_interruption`` to stop the running clients.
    """
    WaitTillAll(
        [Task(client.disconnect(), KOKORO) for client in CLIENTS.values()],
        KOKORO).sync_wrap().wait()
예제 #13
0
파일: client.py 프로젝트: HuyaneMatsu/hata
    async def _node_disconnected(self, node):
        """
        Called when a node is disconnected from Lavalink.
        
        Parameters
        ----------
        node : `SolarNode`
            The node that has just connected.
        """
        players = node.players

        if players:
            best_node = self.find_ideal_node(node.region)
            if best_node is None:
                player_queue = self._player_queue
                if player_queue is None:
                    player_queue = list(players)
                    self._player_queue = player_queue
                else:
                    player_queue.extend(players)
            else:
                tasks = []
                for player in players:
                    task = Task(player.change_node(best_node), KOKORO)
                    tasks.append(task)

                await WaitTillAll(tasks, KOKORO)
예제 #14
0
 def pause(self):
     """
     Pauses the currently played audio if applicable.
     """
     player = self.player
     if (player is not None):
         player.pause()
         Task(self.set_speaking(0), KOKORO)
예제 #15
0
 def call_ready(self):
     """
     Calls the ready event handler of the respective client.
     """
     client = self.client_reference()
     if (client is not None):
         client.ready_state = None
         Task(client.events.ready(client), KOKORO)
예제 #16
0
 def resume(self):
     """
     Resumes the currently stopped audio if applicable.
     """
     player = self.player
     if (player is not None):
         player.resume()
         Task(self.set_speaking(1), KOKORO)
예제 #17
0
파일: parsers.py 프로젝트: HuyaneMatsu/hata
def parse_track_stuck(client, data):
    guild_id = int(data[LAVALINK_KEY_GUILD_ID])
    
    try:
        player = client.solarlink.players[guild_id]
    except KeyError:
        return
    
    event = TrackStuckEvent(player, data)
    Task(client.solarlink._events.track_stuck(client, event), KOKORO)
예제 #18
0
파일: parsers.py 프로젝트: HuyaneMatsu/hata
def parse_player_websocket_closed(client, data):
    guild_id = int(data[LAVALINK_KEY_GUILD_ID])
    
    try:
        player = client.solarlink.players[guild_id]
    except KeyError:
        return
    
    event = PlayerWebsocketClosedEvent(player, data)
    Task(client.solarlink._events.player_websocket_closed(client, event), KOKORO)
예제 #19
0
파일: client.py 프로젝트: HuyaneMatsu/hata
def _trigger_auto_post(top_gg_client_reference):
    """
    Triggers an auto post.
    
    Parameters
    ----------
    top_gg_client_reference : ``WeakReferer`` to ``TopGGClient``
        Weak reference to the top.gg client.
    """
    top_gg_client = top_gg_client_reference()
    if (top_gg_client is not None):
        Task(_do_auto_post(top_gg_client, top_gg_client_reference), KOKORO)
예제 #20
0
파일: utils.py 프로젝트: HuyaneMatsu/hata
def stop_clients():
    """
    Stops all the running clients.
    
    Can be called from any thread.
    """
    for client in CLIENTS.values():
        if client.running:
            Task(client.disconnect(), KOKORO)

    if (current_thread() is not KOKORO):
        KOKORO.wake_up()
예제 #21
0
 async def restart(self):
     """
     Restarts kokoro.
     
     This method is a coroutine.
     """
     self.cancel()
     # skip 1 loop
     await skip_ready_cycle()
     self.task = Task(self._start(), KOKORO)
     # skip 1 loop
     await skip_ready_cycle()
예제 #22
0
async def handle_voice_client_shutdown(client):
    """
    Called when the client logs out.
    
    This function is a coroutine.
    """
    tasks = []
    for player in client.solarlink.players.values():
        task = Task(player.disconnect(), KOKORO)
        tasks.append(task)

    task = None
    await WaitTillAll(tasks, KOKORO)

    tasks = []
    for node in client.soalrlink.nodes:
        task = Task(node.close(), KOKORO)
        tasks.append(task)

    task = None
    await WaitTillAll(tasks, KOKORO)
예제 #23
0
 async def close(self):
     """
     Cancels the gateway sharder's gateways.
     
     This method is a coroutine.
     """
     tasks = []
     for gateway in self.gateways:
         task = Task(gateway.close(), KOKORO)
         tasks.append(task)
     
     await WaitTillAll(tasks, KOKORO)
예제 #24
0
 def __init__(self, voice_client):
     """
     Creates an ``AudioReader`` bound to the given voice client.
     
     Parameters
     ----------
     voice_client : ``VoiceClient``
         The parent voice client.
     """
     self.voice_client = voice_client
     self.done = False
     self.audio_streams = {}
     self.task = Task(self.run(), KOKORO)
 def ensure(self, coroutine):
     """
     Ensures the coroutine within the interaction response context
     
     This method is an awaitable generator.
     
     Parameters
     ----------
     coroutine : ``CoroutineType``
     """
     self.interaction_event._async_task = Task(self._async(coroutine),
                                               KOKORO)
     yield  # skip a ready cycle
예제 #26
0
파일: node.py 프로젝트: HuyaneMatsu/hata
 async def start(self):
     """
     Starts the lavalink node.
     
     This method is a coroutine.
     
     Raises
     ------
     BaseException
         Any exception raised when trying to connect.
     """
     waiter = Future(KOKORO)
     Task(self.run(waiter=waiter), KOKORO)
     return await waiter
예제 #27
0
파일: utils.py 프로젝트: HuyaneMatsu/hata
def start_clients():
    """
    Starts up all the not running clients.
    
    Can be called from any thread.
    """
    for client in CLIENTS.values():
        if client.running:
            continue

        Task(client.connect(), KOKORO)

    if (current_thread() is not KOKORO):
        KOKORO.wake_up()
예제 #28
0
파일: core.py 프로젝트: HuyaneMatsu/hata
def trigger_voice_client_ghost_event(client, voice_state):
    """
    Triggers `Client.events.voice_client_ghost` if set.
    
    Parameters
    ----------
    client : ``Client``
        The respective client instance.
    voice_state : ``VoiceState``
        The client's ghost voice state.
    """
    event_handler = client.events.voice_client_ghost
    if (event_handler is not DEFAULT_EVENT_HANDLER):
        Task(event_handler(client, voice_state), KOKORO)
예제 #29
0
    async def _play_next(self, last_source):
        """
        Starts to play the next audio object on ``.queue`` and cancels the actual one if applicable.
        Should be used inside of ``.lock`` to ensure that the voice client is not modified parallelly.
        
        This function is a coroutine.
        
        Parameters
        ----------
        self : ``VoiceClient``
            The respective voice client.
        last_source : `None`, ``AudioSource``
            The audio what was played.
        """
        player = self.player
        queue = self.queue
        if player is None:
            if not queue:
                return

            source = queue.pop(0)
            self.player = AudioPlayer(self, source)
            if self.connected.is_set():
                Task(self.set_speaking(1), KOKORO)

            return

        if not queue:
            player.set_source(None)
            if self.connected.is_set():
                Task(self.set_speaking(0), KOKORO)
            return

        source = queue.pop(0)
        player.set_source(source)
        if self.connected.is_set():
            Task(self.set_speaking(1), KOKORO)
예제 #30
0
    async def _start_beating(self):
        """
        Internal method to start beating when kokoro is not running.
        
        This method is a coroutine.
        """
        # starts kokoro, then beating
        self.task = Task(self._start(), KOKORO)
        # skip 1 loop
        await skip_ready_cycle()

        waiter = self.ws_waiter
        if (waiter is not None):
            self.ws_waiter = None
            waiter.set_result(None)