Skip to content

Commit d3f5353

Browse files
authored
Merge branch 'gonzobot' into gonzobot+asyncio-threadsafe-wrap
2 parents d9612ce + add1c55 commit d3f5353

9 files changed

Lines changed: 150 additions & 70 deletions

File tree

cloudbot/bot.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ def _init_routine(self):
221221
self.observer.start()
222222

223223
# Connect to servers
224-
yield from asyncio.gather(*[conn.connect() for conn in self.connections.values()], loop=self.loop)
224+
yield from asyncio.gather(*[conn.try_connect() for conn in self.connections.values()], loop=self.loop)
225225

226226
# Activate web interface.
227227
if self.config.get("web", {}).get("enabled", False) and web_installed:

cloudbot/client.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import asyncio
22
import collections
33
import logging
4+
import random
45

56
from cloudbot.permissions import PermissionManager
67

@@ -59,7 +60,20 @@ def describe_server(self):
5960
raise NotImplementedError
6061

6162
@asyncio.coroutine
62-
def connect(self):
63+
def try_connect(self):
64+
timeout = 30
65+
while True:
66+
try:
67+
yield from self.connect(timeout)
68+
except Exception:
69+
logger.exception("[%s] Error occurred while connecting.")
70+
else:
71+
break
72+
73+
yield from asyncio.sleep(random.randrange(timeout))
74+
75+
@asyncio.coroutine
76+
def connect(self, timeout=None):
6377
"""
6478
Connects to the server, or reconnects if already connected.
6579
"""

cloudbot/clients/irc.py

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import asyncio
22
import logging
3+
import random
34
import re
45
import ssl
56
from _ssl import PROTOCOL_SSLv23
@@ -107,7 +108,20 @@ def describe_server(self):
107108
return "{}:{}".format(self.server, self.port)
108109

109110
@asyncio.coroutine
110-
def connect(self):
111+
def try_connect(self):
112+
timeout = self.config["connection"].get("timeout", 30)
113+
while True:
114+
try:
115+
yield from self.connect(timeout)
116+
except (asyncio.TimeoutError, OSError):
117+
logger.exception("[%s] Error occurred while connecting", self.name)
118+
else:
119+
break
120+
121+
yield from asyncio.sleep(random.randrange(timeout))
122+
123+
@asyncio.coroutine
124+
def connect(self, timeout=None):
111125
"""
112126
Connects to the IRC server, or reconnects if already connected.
113127
"""
@@ -126,8 +140,15 @@ def connect(self):
126140
optional_params = {}
127141
if self.local_bind:
128142
optional_params["local_addr"] = self.local_bind
129-
self._transport, self._protocol = yield from self.loop.create_connection(
130-
partial(_IrcProtocol, self), host=self.server, port=self.port, ssl=self.ssl_context, **optional_params)
143+
144+
coro = self.loop.create_connection(
145+
partial(_IrcProtocol, self), host=self.server, port=self.port, ssl=self.ssl_context, **optional_params
146+
)
147+
148+
if timeout is not None:
149+
coro = asyncio.wait_for(coro, timeout)
150+
151+
self._transport, self._protocol = yield from coro
131152

132153
tasks = [
133154
self.bot.plugin_manager.launch(hook, Event(bot=self.bot, conn=self, hook=hook))

cloudbot/util/formatting.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
4545
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
4646
"""
47-
47+
import copy
4848
import re
4949
import html.entities
5050

@@ -318,3 +318,20 @@ def get_text_list(list_, last_word='or'):
318318
# Translators: This string is used as a separator between list elements
319319
', '.join([i for i in list_][:-1]),
320320
last_word, list_[-1])
321+
322+
323+
def gen_markdown_table(headers, rows):
324+
"""
325+
Generates a Markdown formatted table from the data
326+
"""
327+
rows = copy.copy(rows)
328+
rows.insert(0, headers)
329+
rotated = zip(*reversed(rows))
330+
331+
sizes = tuple(map(lambda l: max(max(map(len, l)), 3), rotated))
332+
rows.insert(1, tuple(('-' * size) for size in sizes))
333+
lines = [
334+
"| {} |".format(' | '.join(cell.ljust(sizes[i]) for i, cell in enumerate(row)))
335+
for row in rows
336+
]
337+
return '\n'.join(lines)

plugins/admin_channel.py

Lines changed: 66 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,95 +1,126 @@
11
from cloudbot import hook
22

33

4+
def check_for_chan_mode(char, conn):
5+
serv_info = conn.memory["server_info"]
6+
modes = serv_info.get("channel_modes", "")
7+
return bool(char in modes)
8+
9+
410
def mode_cmd(mode, text, text_inp, chan, conn, notice, nick, admin_log):
511
""" generic mode setting function """
12+
if not check_for_chan_mode(mode[1], conn):
13+
return False
14+
615
split = text_inp.split(" ")
716
if split[0].startswith("#"):
817
channel = split[0]
918
target = split[1]
10-
notice("Attempting to {} {} in {}...".format(text, target, channel))
11-
admin_log("{} used {} to set {} on {} in {}.".format(nick, text, mode, target, channel))
12-
conn.send("MODE {} {} {}".format(channel, mode, target))
1319
else:
1420
channel = chan
1521
target = split[0]
16-
notice("Attempting to {} {} in {}...".format(text, target, channel))
17-
admin_log("{} used {} to set {} on {} in {}.".format(nick, text, mode, target, channel))
18-
conn.send("MODE {} {} {}".format(channel, mode, target))
22+
23+
notice("Attempting to {} {} in {}...".format(text, target, channel))
24+
admin_log("{} used {} to set {} on {} in {}.".format(nick, text, mode, target, channel))
25+
conn.send("MODE {} {} {}".format(channel, mode, target))
26+
27+
return True
1928

2029

2130
def mode_cmd_no_target(mode, text, text_inp, chan, conn, notice, nick, admin_log):
2231
""" generic mode setting function without a target"""
32+
if not check_for_chan_mode(mode[1], conn):
33+
return False
34+
2335
split = text_inp.split(" ")
2436
if split[0].startswith("#"):
2537
channel = split[0]
26-
notice("Attempting to {} {}...".format(text, channel))
27-
admin_log("{} used {} to set {} in {}.".format(nick, text, mode, channel))
28-
conn.send("MODE {} {}".format(channel, mode))
2938
else:
3039
channel = chan
31-
notice("Attempting to {} {}...".format(text, channel))
32-
admin_log("{} used {} to set {} in {}.".format(nick, text, mode, channel))
33-
conn.send("MODE {} {}".format(channel, mode))
40+
41+
notice("Attempting to {} {}...".format(text, channel))
42+
admin_log("{} used {} to set {} in {}.".format(nick, text, mode, channel))
43+
conn.send("MODE {} {}".format(channel, mode))
44+
return True
45+
46+
47+
def do_extban(char, text, text_inp, chan, conn, notice, nick, admin_log, adding=True):
48+
serv_info = conn.memory["server_info"]
49+
if char not in serv_info.get("extbans", ""):
50+
return False
51+
52+
extban_pfx = serv_info["extban_prefix"]
53+
54+
split = text_inp.split(" ")
55+
if split[0].startswith("#"):
56+
channel = split[0]
57+
target = split[1]
58+
text_inp = "{} {}{}:{}".format(channel, extban_pfx, char, target)
59+
else:
60+
target = split[0]
61+
text_inp = "{}{}:{}".format(extban_pfx, char, target)
62+
63+
mode_cmd("+b" if adding else "-b", text, text_inp, chan, conn, notice, nick, admin_log)
64+
return True
3465

3566

36-
@hook.command(permissions=["op_ban", "op"])
67+
@hook.command(permissions=["op_ban", "op", "chanop"])
3768
def ban(text, conn, chan, notice, nick, admin_log):
3869
"""[channel] <user> - bans <user> in [channel], or in the caller's channel if no channel is specified"""
3970
mode_cmd("+b", "ban", text, chan, conn, notice, nick, admin_log)
4071

4172

42-
@hook.command(permissions=["op_ban", "op"])
73+
@hook.command(permissions=["op_ban", "op", "chanop"])
4374
def unban(text, conn, chan, notice, nick, admin_log):
4475
"""[channel] <user> - unbans <user> in [channel], or in the caller's channel if no channel is specified"""
4576
mode_cmd("-b", "unban", text, chan, conn, notice, nick, admin_log)
4677

4778

48-
@hook.command(permissions=["op_quiet", "op"])
79+
@hook.command(permissions=["op_quiet", "op", "chanop"])
4980
def quiet(text, conn, chan, notice, nick, admin_log):
5081
"""[channel] <user> - quiets <user> in [channel], or in the caller's channel if no channel is specified"""
51-
if conn.name == "snoonet":
52-
out = "mode {} +b m:{}".format(chan, text)
53-
conn.send(out)
82+
if mode_cmd("+q", "quiet", text, chan, conn, notice, nick, admin_log):
5483
return
55-
mode_cmd("+q", "quiet", text, chan, conn, notice, nick, admin_log)
5684

85+
if not do_extban('m', "quiet", text, chan, conn, notice, nick, admin_log, True):
86+
notice("Unable to set +q or a mute extban on this network.")
5787

58-
@hook.command(permissions=["op_quiet", "op"])
88+
89+
@hook.command(permissions=["op_quiet", "op", "chanop"])
5990
def unquiet(text, conn, chan, notice, nick, admin_log):
6091
"""[channel] <user> - unquiets <user> in [channel], or in the caller's channel if no channel is specified"""
61-
if conn.name == "snoonet":
62-
out = "mode {} -b m:{}".format(chan, text)
63-
conn.send(out)
92+
if mode_cmd("-q", "unquiet", text, chan, conn, notice, nick, admin_log):
6493
return
65-
mode_cmd("-q", "unquiet", text, chan, conn, notice, nick, admin_log)
94+
95+
if not do_extban('m', "unquiet", text, chan, conn, notice, nick, admin_log, False):
96+
notice("Unable to unset +q or a mute extban on this network.")
6697

6798

68-
@hook.command(permissions=["op_voice", "op"])
99+
@hook.command(permissions=["op_voice", "op", "chanop"])
69100
def voice(text, conn, chan, notice, nick, admin_log):
70101
"""[channel] <user> - voices <user> in [channel], or in the caller's channel if no channel is specified"""
71102
mode_cmd("+v", "voice", text, chan, conn, notice, nick, admin_log)
72103

73104

74-
@hook.command(permissions=["op_voice", "op"])
105+
@hook.command(permissions=["op_voice", "op", "chanop"])
75106
def devoice(text, conn, chan, notice, nick, admin_log):
76107
"""[channel] <user> - devoices <user> in [channel], or in the caller's channel if no channel is specified"""
77108
mode_cmd("-v", "devoice", text, chan, conn, notice, nick, admin_log)
78109

79110

80-
@hook.command(permissions=["op_op", "op"])
111+
@hook.command(permissions=["op_op", "op", "chanop"])
81112
def op(text, conn, chan, notice, nick, admin_log):
82113
"""[channel] <user> - ops <user> in [channel], or in the caller's channel if no channel is specified"""
83114
mode_cmd("+o", "op", text, chan, conn, notice, nick, admin_log)
84115

85116

86-
@hook.command(permissions=["op_op", "op"])
117+
@hook.command(permissions=["op_op", "op", "chanop"])
87118
def deop(text, conn, chan, notice, nick, admin_log):
88119
"""[channel] <user> - deops <user> in [channel], or in the caller's channel if no channel is specified"""
89120
mode_cmd("-o", "deop", text, chan, conn, notice, nick, admin_log)
90121

91122

92-
@hook.command(permissions=["op_topic", "op"])
123+
@hook.command(permissions=["op_topic", "op", "chanop"])
93124
def topic(text, conn, chan, nick, admin_log):
94125
"""[channel] <topic> - changes the topic to <topic> in [channel], or in the caller's channel
95126
if no channel is specified"""
@@ -104,7 +135,7 @@ def topic(text, conn, chan, nick, admin_log):
104135
conn.send("TOPIC {} :{}".format(chan, msg))
105136

106137

107-
@hook.command(permissions=["op_kick", "op"])
138+
@hook.command(permissions=["op_kick", "op", "chanop"])
108139
def kick(text, chan, conn, notice, nick, admin_log):
109140
"""[channel] <user> - kicks <user> from [channel], or from the caller's channel if no channel is specified"""
110141
split = text.split(" ")
@@ -131,7 +162,7 @@ def kick(text, chan, conn, notice, nick, admin_log):
131162
conn.send(out)
132163

133164

134-
@hook.command(permissions=["op_rem", "op"])
165+
@hook.command(permissions=["op_rem", "op", "chanop"])
135166
def remove(text, chan, conn, nick, admin_log):
136167
"""<user> - force removes <user> from the caller's channel."""
137168
split = text.split(" ")
@@ -145,25 +176,25 @@ def remove(text, chan, conn, nick, admin_log):
145176
conn.send(out)
146177

147178

148-
@hook.command(permissions=["op_mute", "op"], autohelp=False)
179+
@hook.command(permissions=["op_mute", "op", "chanop"], autohelp=False)
149180
def mute(text, conn, chan, notice, nick, admin_log):
150181
"""[channel] - mutes [channel], or in the caller's channel if no channel is specified"""
151182
mode_cmd_no_target("+m", "mute", text, chan, conn, notice, nick, admin_log)
152183

153184

154-
@hook.command(permissions=["op_mute", "op"], autohelp=False)
185+
@hook.command(permissions=["op_mute", "op", "chanop"], autohelp=False)
155186
def unmute(text, conn, chan, notice, nick, admin_log):
156187
"""[channel] - unmutes [channel], or in the caller's channel if no channel is specified"""
157188
mode_cmd_no_target("-m", "unmute", text, chan, conn, notice, nick, admin_log)
158189

159190

160-
@hook.command(permissions=["op_lock", "op"], autohelp=False)
191+
@hook.command(permissions=["op_lock", "op", "chanop"], autohelp=False)
161192
def lock(text, conn, chan, notice, nick, admin_log):
162193
"""[channel] - locks [channel], or in the caller's channel if no channel is specified"""
163194
mode_cmd_no_target("+i", "lock", text, chan, conn, notice, nick, admin_log)
164195

165196

166-
@hook.command(permissions=["op_lock", "op"], autohelp=False)
197+
@hook.command(permissions=["op_lock", "op", "chanop"], autohelp=False)
167198
def unlock(text, conn, chan, notice, nick, admin_log):
168199
"""[channel] - unlocks [channel], or in the caller's channel if no channel is specified"""
169200
mode_cmd_no_target("-i", "unlock", text, chan, conn, notice, nick, admin_log)

plugins/core/core_misc.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ def on_join(chan, conn, nick):
3232

3333
@hook.irc_raw('324')
3434
def check_mode(irc_paramlist, conn, message):
35-
#message(", ".join(irc_paramlist), "bloodygonzo")
35+
# message(", ".join(irc_paramlist), "bloodygonzo")
3636
mode = irc_paramlist[2]
3737
require_reg = conn.config.get('require_registered_channels', False)
3838
if not "r" in mode and conn.name == "snoonet" and require_reg:
@@ -125,3 +125,18 @@ def keep_alive(conn):
125125
while True:
126126
conn.cmd('PING', conn.nick)
127127
yield from asyncio.sleep(60)
128+
129+
130+
@hook.irc_raw('433')
131+
def on_nick_in_use(conn, irc_paramlist):
132+
conn.nick = irc_paramlist[1] + '_'
133+
conn.cmd("NICK", conn.nick)
134+
135+
136+
@asyncio.coroutine
137+
@hook.irc_raw('432', singlethread=True)
138+
def on_invalid_nick(conn):
139+
nick = conn.config['nick']
140+
conn.nick = nick
141+
conn.cmd("NICK", conn.nick)
142+
yield from asyncio.sleep(30) # Just in case, we make sure to wait at least 30 seconds between sending this

plugins/core/optout.py

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from cloudbot import hook
1414
from cloudbot.hook import Priority
1515
from cloudbot.util import database, web
16+
from cloudbot.util.formatting import gen_markdown_table
1617

1718
optout_table = Table(
1819
'optout',
@@ -75,20 +76,6 @@ def get_channel_optouts(conn_name, chan=None):
7576
return [opt for opt in optout_cache[conn_name] if not chan or opt.match_chan(chan)]
7677

7778

78-
def gen_markdown_table(headers, rows):
79-
rows = copy.copy(rows)
80-
rows.insert(0, headers)
81-
rotated = zip(*reversed(rows))
82-
83-
sizes = tuple(map(lambda l: max(map(len, l)), rotated))
84-
rows.insert(1, tuple(('-' * size) for size in sizes))
85-
lines = [
86-
"| {} |".format(' | '.join(cell.ljust(sizes[i]) for i, cell in enumerate(row)))
87-
for row in rows
88-
]
89-
return '\n'.join(lines)
90-
91-
9279
def format_optout_list(opts):
9380
headers = ("Channel Pattern", "Hook Pattern", "Allowed")
9481
table = [(opt.channel, opt.hook, "true" if opt.allow else "false") for opt in opts]

plugins/core/plugin_control.py

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,7 @@
44

55
from cloudbot import hook
66
from cloudbot.util import web
7-
8-
9-
def gen_markdown_table(headers, rows):
10-
rows = list(rows)
11-
rows.insert(0, headers)
12-
rotated = zip(*reversed(rows))
13-
14-
sizes = tuple(map(lambda l: max(map(len, l)), rotated))
15-
rows.insert(1, tuple(('-' * size) for size in sizes))
16-
lines = [
17-
"| {} |".format(' | '.join(cell.ljust(sizes[i]) for i, cell in enumerate(row)))
18-
for row in rows
19-
]
20-
return '\n'.join(lines)
7+
from cloudbot.util.formatting import gen_markdown_table
218

229

2310
@hook.command(permissions=["botcontrol"], autohelp=False)

0 commit comments

Comments
 (0)