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()
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)
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