Skip to content

Commit 38ad13d

Browse files
committed
Merge branch 'gonzobot' into gonzobot+pytest-leaks
2 parents d726e0a + 6595c14 commit 38ad13d

22 files changed

Lines changed: 158 additions & 126 deletions

cloudbot/bot.py

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
from sqlalchemy.schema import MetaData
1414
from watchdog.observers import Observer
1515

16-
from cloudbot.client import Client
16+
from cloudbot.client import Client, CLIENTS
1717
from cloudbot.clients.irc import IrcClient, irc_clean
1818
from cloudbot.config import Config
1919
from cloudbot.event import Event, CommandEvent, RegexEvent, EventType
@@ -151,15 +151,9 @@ def create_connections(self):
151151
# strip all spaces and capitalization from the connection name
152152
name = clean_name(config['name'])
153153
nick = config['nick']
154-
server = config['connection']['server']
155-
port = config['connection'].get('port', 6667)
156-
local_bind = (config['connection'].get('bind_addr', False), config['connection'].get('bind_port', 0))
157-
if local_bind[0] is False:
158-
local_bind = False
159-
160-
self.connections[name] = IrcClient(self, name, nick, config=config, channels=config['channels'],
161-
server=server, port=port, use_ssl=config['connection'].get('ssl', False),
162-
local_bind=local_bind)
154+
_type = config.get("type", "irc")
155+
156+
self.connections[name] = CLIENTS[_type](self, name, nick, config=config, channels=config['channels'])
163157
logger.debug("[{}] Created connection.".format(name))
164158

165159
@asyncio.coroutine
@@ -246,6 +240,9 @@ def add_hook(hook, _event, _run_before=False):
246240
if halted:
247241
return False
248242

243+
if hook.clients and _event.conn.type not in hook.clients:
244+
return True
245+
249246
coro = self.plugin_manager.launch(hook, _event)
250247
if _run_before:
251248
run_before_tasks.append(coro)

cloudbot/client.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,17 @@
77

88
logger = logging.getLogger("cloudbot")
99

10+
CLIENTS = {}
11+
12+
13+
def client(_type):
14+
def _decorate(cls):
15+
CLIENTS[_type] = cls
16+
cls._type = _type
17+
return cls
18+
19+
return lambda cls: _decorate(cls)
20+
1021

1122
class Client:
1223
"""
@@ -22,6 +33,8 @@ class Client:
2233
:type permissions: PermissionManager
2334
"""
2435

36+
_type = None
37+
2538
def __init__(self, bot, name, nick, *, channels=None, config=None):
2639
"""
2740
:type bot: cloudbot.bot.CloudBot
@@ -155,3 +168,7 @@ def is_nick_valid(self, nick):
155168
@property
156169
def connected(self):
157170
raise NotImplementedError
171+
172+
@property
173+
def type(self):
174+
return self._type

cloudbot/clients/irc.py

Lines changed: 27 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,13 @@
77
from functools import partial
88
from ssl import SSLContext
99

10-
from cloudbot.client import Client
10+
from cloudbot.client import Client, client
1111
from cloudbot.event import Event, EventType, IrcOutEvent
1212
from cloudbot.util import async_util
1313
from cloudbot.util.parsers.irc import Message
1414

1515
logger = logging.getLogger("cloudbot")
1616

17-
irc_prefix_re = re.compile(r":([^ ]*) ([^ ]*) (.*)")
18-
irc_noprefix_re = re.compile(r"([^ ]*) (.*)")
19-
irc_netmask_re = re.compile(r"([^!@]*)!([^@]*)@(.*)")
20-
irc_param_re = re.compile(r"(?:^|(?<= ))(:.*|[^ ]+)")
21-
2217
irc_nick_re = re.compile(r'[A-Za-z0-9^{\}\[\]\-`_|\\]+')
2318

2419
irc_bad_chars = ''.join([chr(x) for x in list(range(0, 1)) + list(range(4, 32)) + list(range(127, 160))])
@@ -50,6 +45,7 @@ def decode(bytestring):
5045
return bytestring.decode('utf-8', errors='ignore')
5146

5247

48+
@client("irc")
5349
class IrcClient(Client):
5450
"""
5551
An implementation of Client for IRC.
@@ -60,27 +56,26 @@ class IrcClient(Client):
6056
:type _ignore_cert_errors: bool
6157
"""
6258

63-
def __init__(self, bot, name, nick, *, channels=None, config=None,
64-
server, port=6667, use_ssl=False, ignore_cert_errors=True, timeout=300, local_bind=False):
59+
def __init__(self, bot, name, nick, *, channels=None, config=None):
6560
"""
6661
:type bot: cloudbot.bot.CloudBot
6762
:type name: str
6863
:type nick: str
6964
:type channels: list[str]
7065
:type config: dict[str, unknown]
71-
:type server: str
72-
:type port: int
73-
:type use_ssl: bool
74-
:type ignore_cert_errors: bool
75-
:type timeout: int
7666
"""
7767
super().__init__(bot, name, nick, channels=channels, config=config)
7868

79-
self.use_ssl = use_ssl
80-
self._ignore_cert_errors = ignore_cert_errors
81-
self._timeout = timeout
82-
self.server = server
83-
self.port = port
69+
self.use_ssl = config['connection'].get('ssl', False)
70+
self._ignore_cert_errors = config['connection']['ignore_cert']
71+
self._timeout = config['connection'].get('timeout', 300)
72+
self.server = config['connection']['server']
73+
self.port = config['connection'].get('port', 6667)
74+
75+
local_bind = (config['connection'].get('bind_addr', False), config['connection'].get('bind_port', 0))
76+
if local_bind[0] is False:
77+
local_bind = False
78+
8479
self.local_bind = local_bind
8580
# create SSL context
8681
if self.use_ssl:
@@ -109,16 +104,15 @@ def describe_server(self):
109104

110105
@asyncio.coroutine
111106
def try_connect(self):
112-
timeout = self.config["connection"].get("timeout", 30)
113107
while True:
114108
try:
115-
yield from self.connect(timeout)
109+
yield from self.connect(self._timeout)
116110
except (asyncio.TimeoutError, OSError):
117111
logger.exception("[%s] Error occurred while connecting", self.name)
118112
else:
119113
break
120114

121-
yield from asyncio.sleep(random.randrange(timeout))
115+
yield from asyncio.sleep(random.randrange(self._timeout))
122116

123117
@asyncio.coroutine
124118
def connect(self, timeout=None):
@@ -153,6 +147,7 @@ def connect(self, timeout=None):
153147
tasks = [
154148
self.bot.plugin_manager.launch(hook, Event(bot=self.bot, conn=self, hook=hook))
155149
for hook in self.bot.plugin_manager.connect_hooks
150+
if not hook.clients or self.type in hook.clients
156151
]
157152
# TODO stop connecting if a connect hook fails?
158153
yield from asyncio.gather(*tasks)
@@ -177,7 +172,6 @@ def close(self):
177172

178173
def message(self, target, *messages):
179174
for text in messages:
180-
text = "".join(text.splitlines())
181175
self.cmd("PRIVMSG", target, text)
182176

183177
def admin_log(self, text, console=True):
@@ -189,11 +183,9 @@ def admin_log(self, text, console=True):
189183
logger.info("[%s|admin] %s", self.name, text)
190184

191185
def action(self, target, text):
192-
text = "".join(text.splitlines())
193186
self.ctcp(target, "ACTION", text)
194187

195188
def notice(self, target, text):
196-
text = "".join(text.splitlines())
197189
self.cmd("NOTICE", target, text)
198190

199191
def set_nick(self, nick):
@@ -239,22 +231,23 @@ def cmd(self, command, *params):
239231
else:
240232
self.send(command)
241233

242-
def send(self, line):
234+
def send(self, line, log=True):
243235
"""
244236
Sends a raw IRC line
245237
:type line: str
238+
:type log: bool
246239
"""
247240
if not self._connected:
248241
raise ValueError("Client must be connected to irc server to use send")
249-
self.loop.call_soon_threadsafe(self._send, line)
242+
self.loop.call_soon_threadsafe(self._send, line, log)
250243

251-
def _send(self, line):
244+
def _send(self, line, log=True):
252245
"""
253246
Sends a raw IRC line unchecked. Doesn't do connected check, and is *not* threadsafe
254247
:type line: str
248+
:type log: bool
255249
"""
256-
logger.info("[{}] >> {}".format(self.name, line))
257-
async_util.wrap_future(self._protocol.send(line), loop=self.loop)
250+
async_util.wrap_future(self._protocol.send(line, log=log), loop=self.loop)
258251

259252
@property
260253
def connected(self):
@@ -321,7 +314,7 @@ def eof_received(self):
321314
return True
322315

323316
@asyncio.coroutine
324-
def send(self, line):
317+
def send(self, line, log=True):
325318
# make sure we are connected before sending
326319
if not self._connected:
327320
yield from self._connected_future
@@ -357,6 +350,9 @@ def send(self, line):
357350
# the line must be encoded before we send it, one of the sieves didn't encode it, fall back to the default
358351
line = line.encode("utf-8", "replace")
359352

353+
if log:
354+
logger.info("[{}|out] >> {!r}".format(self.conn.name, line))
355+
360356
self._transport.write(line)
361357

362358
def data_received(self, data):
@@ -381,7 +377,7 @@ def data_received(self, data):
381377
# Reply to pings immediately
382378

383379
if command == "PING":
384-
async_util.wrap_future(self.send("PONG " + command_params[-1]), loop=self.loop)
380+
self.conn.send("PONG " + command_params[-1], log=False)
385381

386382
# Parse the command and params
387383

cloudbot/plugin.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -730,6 +730,13 @@ def __init__(self, _type, plugin, func_hook):
730730
self.action = func_hook.kwargs.pop("action", Action.CONTINUE)
731731
self.priority = func_hook.kwargs.pop("priority", Priority.NORMAL)
732732

733+
clients = func_hook.kwargs.pop("clients", [])
734+
735+
if isinstance(clients, str):
736+
clients = [clients]
737+
738+
self.clients = clients
739+
733740
if func_hook.kwargs:
734741
# we should have popped all the args, so warn if there are any left
735742
logger.warning("Ignoring extra args {} from {}".format(func_hook.kwargs, self.description))

cloudbot/util/colors.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,4 +222,4 @@ def _convert(string):
222222
elif formatting in IRC_FORMATTING_DICT:
223223
ret += get_format(formatting)
224224

225-
return ret.strip()
225+
return ret

config.default.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@
22
"connections": [
33
{
44
"name": "esper",
5+
"type": "irc",
56
"connection": {
67
"server": "irc.esper.net",
78
"port": 6667,
89
"ssl": false,
910
"ignore_cert": true,
10-
"password": ""
11+
"password": "",
12+
"timeout": 300
1113
},
1214
"nick": "MyCloudBot",
1315
"user": "cloudbot",

data/food/beer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@
121121
"Rauchbier",
122122
"Schwarzbier",
123123
"Vienna Lager",
124+
"Victoria Bitter",
124125
"Happoshu"
125126
]
126127
}

data/food/burger.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@
9393
"side of sesame ginger coleslaw",
9494
"side of macaroni and cheese",
9595
"a large chilly soda",
96+
"a side of authentic 'strayan prawns fresh off the barbie",
9697
"a cup of BBQ dip"
9798
]
9899
}

data/food/cookies.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
"Chocolate Oatmeal Fudge",
4343
"Toffee Peanut",
4444
"Danish Sugar",
45+
"Tim Tam",
4546
"Triple Chocolate",
4647
"Oreo"
4748
],

data/food/pie.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@
8080
"rhubarb pie",
8181
"pie chart",
8282
"broccoli quiche",
83+
"Neenish tart",
8384
"potato knish"
8485
],
8586
"extra": [

0 commit comments

Comments
 (0)