Skip to content

Commit c9eae75

Browse files
authored
Merge branch 'gonzobot' into gonzobot+fix-debug-mode-coros
2 parents 3a54ef9 + fa11fc1 commit c9eae75

27 files changed

Lines changed: 476 additions & 205 deletions

cloudbot/bot.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,7 @@ def add_hook(hook, _event, _run_before=False):
314314
add_hook(command_hook, command_event)
315315
else:
316316
event.notice("Possible matches: {}".format(
317-
formatting.get_text_list([command for command, plugin in potential_matches])))
317+
formatting.get_text_list(sorted([command for command, plugin in potential_matches]))))
318318

319319
if event.type in (EventType.message, EventType.action):
320320
# Regex hooks

cloudbot/client.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ def try_connect(self):
7979
try:
8080
yield from self.connect(timeout)
8181
except Exception:
82-
logger.exception("[%s] Error occurred while connecting.")
82+
logger.exception("[%s] Error occurred while connecting.", self.name)
8383
else:
8484
break
8585

cloudbot/clients/irc.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -126,11 +126,13 @@ def connect(self, timeout=None):
126126
return
127127

128128
if self._connected:
129+
self._connected = False
129130
logger.info("[{}] Reconnecting".format(self.name))
130-
self._transport.close()
131+
if self._transport:
132+
self._transport.close()
131133
else:
132-
self._connected = True
133134
logger.info("[{}] Connecting".format(self.name))
135+
134136
optional_params = {}
135137
if self.local_bind:
136138
optional_params["local_addr"] = self.local_bind
@@ -144,6 +146,8 @@ def connect(self, timeout=None):
144146

145147
self._transport, self._protocol = yield from coro
146148

149+
self._connected = True
150+
147151
tasks = [
148152
self.bot.plugin_manager.launch(hook, Event(bot=self.bot, conn=self, hook=hook))
149153
for hook in self.bot.plugin_manager.connect_hooks
@@ -303,14 +307,14 @@ def connection_lost(self, exc):
303307
# we've been closed intentionally, so don't reconnect
304308
return
305309
logger.error("[{}] Connection lost: {}".format(self.conn.name, exc))
306-
async_util.wrap_future(self.conn.connect(), loop=self.loop)
310+
async_util.wrap_future(self.conn.try_connect(), loop=self.loop)
307311

308312
def eof_received(self):
309313
self._connected = False
310314
# create a new connected_future for when we are connected.
311315
self._connected_future = async_util.create_future(self.loop)
312316
logger.info("[{}] EOF received.".format(self.conn.name))
313-
async_util.wrap_future(self.conn.connect(), loop=self.loop)
317+
async_util.wrap_future(self.conn.try_connect(), loop=self.loop)
314318
return True
315319

316320
@asyncio.coroutine

cloudbot/util/formatting.py

Lines changed: 58 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
import copy
4848
import html.entities
4949
import re
50+
import warnings
5051
from html.parser import HTMLParser
5152

5253
from cloudbot.util.colors import strip_irc
@@ -247,11 +248,65 @@ def pluralize(num=0, text=''):
247248
Takes a number and a string, and pluralizes that string using the number and combines the results.
248249
:rtype: str
249250
"""
250-
return "{:,} {}{}".format(num, text, "s"[num == 1:])
251+
warnings.warn(
252+
"formatting.pluralize() is deprecated, please use one of the other formatting.pluralize_*() functions",
253+
DeprecationWarning
254+
)
255+
return pluralize_suffix(num, text)
251256

252257

253-
# alternate form
254-
pluralise = pluralize
258+
def pluralise(num=0, text=''):
259+
"""
260+
Takes a number and a string, and pluralizes that string using the number and combines the results.
261+
:rtype: str
262+
"""
263+
warnings.warn(
264+
"formatting.pluralise() is deprecated, please use one of the other formatting.pluralise_*() functions",
265+
DeprecationWarning
266+
)
267+
return pluralise_suffix(num, text)
268+
269+
270+
def pluralize_suffix(num=0, text='', suffix='s'):
271+
"""
272+
Takes a number and a string, and pluralizes that string using the number and combines the results.
273+
:rtype: str
274+
"""
275+
return pluralize_select(num, text, text + suffix)
276+
277+
278+
pluralise_suffix = pluralize_suffix
279+
280+
281+
def pluralize_select(count, single, plural):
282+
return "{:,} {}".format(count, single if count == 1 else plural)
283+
284+
285+
pluralise_select = pluralize_select
286+
287+
288+
def pluralize_auto(count, thing):
289+
if thing.endswith(('s', 'ss', 'sh', 'ch', 'x', 'z')):
290+
return pluralize_suffix(count, thing, 'es')
291+
elif thing.endswith(('f', 'fe')):
292+
return pluralize_select(count, thing, thing.rsplit('f', 1)[0] + 'ves')
293+
elif thing.endswith('y') and thing[-2:-1].lower() not in "aeiou":
294+
return pluralize_select(count, thing, thing[:-1] + 'ies')
295+
elif thing.endswith('y') and thing[-2:-1].lower() in "aeiou":
296+
return pluralize_suffix(count, thing)
297+
elif thing.endswith('o'):
298+
return pluralize_suffix(count, thing, 'es')
299+
elif thing.endswith('us'):
300+
return pluralize_select(count, thing, thing[:-2] + 'i')
301+
elif thing.endswith('is'):
302+
return pluralize_select(count, thing, thing[:-2] + 'es')
303+
elif thing.endswith('on'):
304+
return pluralize_select(count, thing, thing[:-2] + 'a')
305+
else:
306+
return pluralize_suffix(count, thing)
307+
308+
309+
pluralise_auto = pluralize_auto
255310

256311

257312
def dict_format(args, formats):

cloudbot/util/test/test_formatting.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
from cloudbot.util.formatting import munge, dict_format, pluralize, strip_colors, truncate, truncate_str, \
2-
strip_html, multi_replace, multiword_replace, truncate_words, smart_split, get_text_list, ireplace, chunk_str
1+
from cloudbot.util.formatting import munge, dict_format, strip_colors, truncate, truncate_str, \
2+
strip_html, multi_replace, multiword_replace, truncate_words, smart_split, get_text_list, ireplace, chunk_str, \
3+
pluralize_suffix
34

45
test_munge_input = "The quick brown fox jumps over the lazy dog"
56
test_munge_count = 3
@@ -55,8 +56,8 @@ def test_dict_format():
5556

5657

5758
def test_pluralize():
58-
assert pluralize(test_pluralize_num_a, test_pluralize_text) == test_pluralize_result_a
59-
assert pluralize(test_pluralize_num_b, test_pluralize_text) == test_pluralize_result_b
59+
assert pluralize_suffix(test_pluralize_num_a, test_pluralize_text) == test_pluralize_result_a
60+
assert pluralize_suffix(test_pluralize_num_b, test_pluralize_text) == test_pluralize_result_b
6061

6162

6263
def test_strip_colors():

cloudbot/util/web.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ def paste(self, data, ext):
207207
'expire': '1d'
208208
}
209209
try:
210-
r = requests.post(SNOONET_PASTE + '/paste/new', params=params)
210+
r = requests.post(SNOONET_PASTE + '/paste/new', data=params)
211211
r.raise_for_status()
212212
except RequestException as e:
213213
r = e.response

data/food/coffee.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"templates": [
3-
"hands {user} a {size} {temperature} {flavor} {coffee} with {shots} shots of espresso!"
3+
"hands {user} a {size} {temperature} {flavor} {coffee} with {shots} of espresso!"
44
],
55
"parts": {
66
"temperature": [
@@ -35,9 +35,9 @@
3535
"breve"
3636
],
3737
"shots": [
38-
"one",
39-
"two",
40-
"three"
38+
"one shot",
39+
"two shots",
40+
"three shots"
4141
]
4242
}
4343
}

plugins/autojoin.py

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import asyncio
2+
from collections import defaultdict
3+
from threading import RLock
24

35
from sqlalchemy import PrimaryKeyConstraint, Column, String, Table, and_
4-
from sqlalchemy.exc import IntegrityError
56

67
from cloudbot import hook
78
from cloudbot.util import database
@@ -14,40 +15,54 @@
1415
PrimaryKeyConstraint('conn', 'chan')
1516
)
1617

18+
chan_cache = defaultdict(set)
19+
db_lock = RLock()
20+
1721

1822
def get_channels(db, conn):
1923
return db.execute(table.select().where(table.c.conn == conn.name.casefold())).fetchall()
2024

2125

26+
@hook.on_start
27+
def load_cache(db):
28+
with db_lock:
29+
chan_cache.clear()
30+
for row in db.execute(table.select()):
31+
chan_cache[row['conn']].add(row['chan'])
32+
33+
2234
@hook.irc_raw('376')
2335
@asyncio.coroutine
24-
def do_joins(db, conn, async_call):
25-
chans = yield from async_call(get_channels, db, conn)
36+
def do_joins(conn):
2637
join_throttle = conn.config.get("join_throttle", 0.4)
27-
for chan in chans:
28-
conn.join(chan[1])
38+
for chan in chan_cache[conn.name]:
39+
conn.join(chan)
2940
yield from asyncio.sleep(join_throttle)
3041

3142

3243
@hook.irc_raw('JOIN', singlethread=True)
3344
def add_chan(db, conn, chan, nick):
34-
if nick.casefold() == conn.nick.casefold():
35-
try:
45+
chans = chan_cache[conn.name]
46+
chan = chan.casefold()
47+
if nick.casefold() == conn.nick.casefold() and chan not in chans:
48+
with db_lock:
3649
db.execute(table.insert().values(conn=conn.name.casefold(), chan=chan.casefold()))
3750
db.commit()
38-
except IntegrityError:
39-
pass
51+
52+
load_cache(db)
4053

4154

4255
@hook.irc_raw('PART', singlethread=True)
4356
def on_part(db, conn, chan, nick):
4457
if nick.casefold() == conn.nick.casefold():
45-
db.execute(table.delete().where(and_(table.c.conn == conn.name.casefold(), table.c.chan == chan.casefold())))
46-
db.commit()
58+
with db_lock:
59+
db.execute(
60+
table.delete().where(and_(table.c.conn == conn.name.casefold(), table.c.chan == chan.casefold())))
61+
db.commit()
62+
63+
load_cache(db)
4764

4865

4966
@hook.irc_raw('KICK', singlethread=True)
5067
def on_kick(db, conn, chan, target):
51-
if target.casefold() == conn.nick.casefold():
52-
db.execute(table.delete().where(and_(table.c.conn == conn.name.casefold(), table.c.chan == chan.casefold())))
53-
db.commit()
68+
on_part(db, conn, chan, target)

plugins/chain.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from cloudbot import hook
88
from cloudbot.event import CommandEvent
99
from cloudbot.util import database
10-
from cloudbot.util.formatting import chunk_str, pluralize
10+
from cloudbot.util.formatting import chunk_str, pluralize_auto
1111

1212
commands = Table(
1313
'chain_commands',
@@ -103,7 +103,7 @@ def chainallow(text, db, notice_doc, bot):
103103
res = db.execute(commands.delete().where(commands.c.hook == hook_name))
104104
db.commit()
105105
load_cache(db)
106-
return "Deleted {}.".format(pluralize(res.rowcount, "row"))
106+
return "Deleted {}.".format(pluralize_auto(res.rowcount, "row"))
107107
else:
108108
return notice_doc()
109109

plugins/core/chan_log.py

Lines changed: 45 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -16,35 +16,48 @@ def _dump_attrs(obj):
1616
def on_hook_end(error, launched_hook, launched_event, admin_log):
1717
should_broadcast = True
1818
if error is not None:
19-
admin_log(
20-
"Error occurred in {}.{}".format(launched_hook.plugin.title, launched_hook.function_name), should_broadcast
21-
)
22-
23-
lines = traceback.format_exception(*error)
24-
last_line = lines[-1]
25-
admin_log(last_line, should_broadcast)
26-
url = web.paste('\n'.join(lines))
27-
admin_log("Traceback: " + url, should_broadcast)
28-
29-
lines = ["{} = {}".format(k, v) for k, v in _dump_attrs(launched_event)]
30-
exc_type, exc, exc_tb = error
31-
32-
lines.append("")
33-
lines.append("Error data:")
34-
lines.extend("{} = {}".format(k, v) for k, v in _dump_attrs(exc))
35-
36-
if isinstance(exc, RequestException):
37-
if exc.request is not None:
38-
req = exc.request
39-
lines.append("")
40-
lines.append("Request Info:")
41-
lines.extend("{} = {}".format(k, v) for k, v in _dump_attrs(req))
42-
43-
if exc.response is not None:
44-
response = exc.response
45-
lines.append("")
46-
lines.append("Response Info:")
47-
lines.extend("{} = {}".format(k, v) for k, v in _dump_attrs(response))
48-
49-
url = web.paste('\n'.join(lines))
50-
admin_log("Event: " + url, should_broadcast)
19+
messages = [
20+
"Error occurred in {}.{}".format(launched_hook.plugin.title, launched_hook.function_name)
21+
]
22+
23+
try:
24+
lines = traceback.format_exception(*error)
25+
last_line = lines[-1]
26+
messages.append(last_line.strip())
27+
except Exception as e:
28+
messages.append("Error occurred while formatting error {}: {}".format(type(e), e))
29+
else:
30+
try:
31+
url = web.paste('\n'.join(lines))
32+
messages.append("Traceback: " + url)
33+
except Exception as e:
34+
messages.append("Error occurred while gathering traceback {}: {}".format(type(e), e))
35+
36+
try:
37+
lines = ["{} = {}".format(k, v) for k, v in _dump_attrs(launched_event)]
38+
exc_type, exc, exc_tb = error
39+
40+
lines.append("")
41+
lines.append("Error data:")
42+
lines.extend("{} = {}".format(k, v) for k, v in _dump_attrs(exc))
43+
44+
if isinstance(exc, RequestException):
45+
if exc.request is not None:
46+
req = exc.request
47+
lines.append("")
48+
lines.append("Request Info:")
49+
lines.extend("{} = {}".format(k, v) for k, v in _dump_attrs(req))
50+
51+
if exc.response is not None:
52+
response = exc.response
53+
lines.append("")
54+
lines.append("Response Info:")
55+
lines.extend("{} = {}".format(k, v) for k, v in _dump_attrs(response))
56+
57+
url = web.paste('\n'.join(lines))
58+
messages.append("Event: " + url)
59+
except Exception as e:
60+
messages.append("Error occurred while gathering error data {}: {}".format(type(e), e))
61+
62+
for message in messages:
63+
admin_log(message, should_broadcast)

0 commit comments

Comments
 (0)