Example #1
0
    def __init__(self, bot, name, bot_nick, *, config):
        """
        :type bot: obrbot.bot.ObrBot
        :type name: str
        :type bot_nick: str
        :type config: dict[str, unknown]
        """
        self.bot = bot
        self.loop = bot.loop
        self.name = name
        self.bot_nick = bot_nick

        self.channels = CaseInsensitiveDict()

        self.config = config

        # create permissions manager
        self.permissions = PermissionManager(self)

        self.waiting_messages = dict()
Example #2
0
class Connection:
    """
    A Connection representing each connection the bot makes to a single server
    :type bot: obrbot.bot.ObrBot
    :type loop: asyncio.events.AbstractEventLoop
    :type name: str
    :type channels: dict[str, Channel]
    :type config: dict[str, str | dict | list]
    :type bot_nick: str
    :type permissions: PermissionManager
    :type waiting_messages: dict[(str, str, re.__Regex), list(asyncio.Future)]
    """

    def __init__(self, bot, name, bot_nick, *, config):
        """
        :type bot: obrbot.bot.ObrBot
        :type name: str
        :type bot_nick: str
        :type config: dict[str, unknown]
        """
        self.bot = bot
        self.loop = bot.loop
        self.name = name
        self.bot_nick = bot_nick

        self.channels = CaseInsensitiveDict()

        self.config = config

        # create permissions manager
        self.permissions = PermissionManager(self)

        self.waiting_messages = dict()

    def describe_server(self):
        raise NotImplementedError

    @asyncio.coroutine
    def connect(self):
        """
        Connects to the server, or reconnects if already connected.
        """
        raise NotImplementedError

    def quit(self, reason=None):
        """
        Gracefully disconnects from the server with reason <reason>, close() should be called shortly after.
        """
        raise NotImplementedError

    def close(self):
        """
        Disconnects from the server, only for use when this Connection object will *not* ever be connected again
        """
        raise NotImplementedError

    def message(self, target, *text):
        """
        Sends a message to the given target
        :type target: str
        :type text: tuple[str]
        """
        raise NotImplementedError

    def action(self, target, text):
        """
        Sends an action (or /me) to the given target channel
        :type target: str
        :type text: str
        """
        raise NotImplementedError

    def notice(self, target, text):
        """
        Sends a notice to the given target
        :type target: str
        :type text: str
        """
        raise NotImplementedError

    def set_nick(self, nick):
        """
        Sets the bot's nickname
        :type nick: str
        """
        raise NotImplementedError

    def join(self, channel):
        """
        Joins a given channel
        :type channel: str
        """
        raise NotImplementedError

    def part(self, channel):
        """
        Parts a given channel
        :type channel: str
        """
        raise NotImplementedError

    @property
    def connected(self):
        raise NotImplementedError

    def wait_for(self, message, nick=None, chan=None):
        """
        Waits for a message matching a specific regex
        This returns a future, so it should be treated like a coroutine
        :type nick: str
        :type message: str | re.__Regex
        """
        if nick is not None:
            nick = nick.lower()
        if chan is not None:
            chan = chan.lower()
        future = asyncio.Future(loop=self.bot.loop)
        if not hasattr(message, "search"):
            message = re.compile(message)

        key = (nick, chan, message)

        if key in self.waiting_messages:
            # what?
            self.waiting_messages[key].append(future)

        self.waiting_messages[key] = [future]
        return future

    @asyncio.coroutine
    def cancel_wait(self, message, nick=None, chan=None):
        if nick is not None:
            nick = nick.lower()
        if chan is not None:
            chan = chan.lower()

        for test_nick, test_chan, test_message, future in self.waiting_messages:
            if test_nick == nick and test_chan == chan and test_message == message:
                future.cancel()

    @asyncio.coroutine
    def _process_channel(self, event):
        if event.chan_name is None or event.chan_name.lower() == event.nick.lower():
            return  # the rest of this just process on channels

        channel = self.channels.get(event.chan_name)
        if channel is None:
            if event.type is EventType.part and event.nick.lower() == self.bot_nick.lower():
                return  # no need to create a channel when we're just leaving it
            elif event.type is not EventType.join:
                logger.warning("First mention of channel {} was from event type {}".format(event.chan_name, event.type))
            elif event.nick.lower() != self.bot_nick.lower():
                logger.warning("First join of channel {} was {}".format(event.chan_name, event.nick))
            channel = Channel(self.name, event.chan_name)
            self.channels[event.chan_name] = channel

        event.channel = channel
        event.channels = [channel]

        if event.type is EventType.part:
            if event.nick.lower() == self.bot_nick.lower():
                del self.channels[event.chan_name]
                return
        elif event.type is EventType.kick:
            if event.target.lower() == self.bot_nick.lower():
                del self.channels[event.chan_name]
                return

        if event.type is EventType.message:
            yield from channel.track_message(event)
        elif event.type is EventType.join:
            yield from channel.track_join(event)
        elif event.type is EventType.part:
            yield from channel.track_part(event)
        elif event.type is EventType.kick:
            yield from channel.track_kick(event)
        elif event.type is EventType.topic:
            yield from channel.track_topic(event)
        elif event.irc_command == 'MODE':
            channel.track_mode(event)
        elif event.irc_command == '353':
            channel.track_353_channel_list(event)

    @asyncio.coroutine
    def _process_nick(self, event):
        if event.type is not EventType.nick:
            return

        if event.nick.lower() == self.bot_nick.lower():
            logger.info("[{}] Bot nick changed from {} to {}.".format(self.name, self.bot_nick, event.content))
            self.bot_nick = event.content

        event.channels.clear()  # We will re-set all relevant channels below
        for channel in self.channels.values():
            if event.nick in channel.users:
                yield from channel.track_nick(event)
                event.channels.append(channel)

    @asyncio.coroutine
    def _process_quit(self, event):
        if event.type is not EventType.quit:
            return

        event.channels.clear()  # We will re-set all relevant channels below
        for channel in self.channels.values():
            if event.nick in channel.users:
                channel.track_quit(event)
                event.channels.append(channel)

    @asyncio.coroutine
    def pre_process_event(self, event):

        yield from self._process_channel(event)
        yield from self._process_nick(event)
        yield from self._process_quit(event)
Example #3
0
import asyncio
import re
from decimal import Decimal, InvalidOperation

import requests

from obrbot import hook
from obrbot.util.dictionaries import CaseInsensitiveDict

plugin_info = {
    "plugin_category": "channel-specific",
    "command_category_name": "DogeStorm"
}

url = "http://coinmill.com/frame.js"
currency_rates = CaseInsensitiveDict()
currency_units = CaseInsensitiveDict(
)  # this will be ignored, at least for now


@asyncio.coroutine
@hook.on_start()
def load_rates():
    regex = re.compile(r"var currency_data=\'([0-9a-zA-Z,\.\|\-]+)\';")
    response = requests.get(url)
    match = regex.match(response.text)
    if not match:
        raise ValueError("Unmatched data: {} Please update!".format(
            response.text))
    data = match.group(1)
    assert isinstance(data, str)  # for pycharm