Skip to content

Commit 4c53fc2

Browse files
authored
Merge pull request CloudBotIRC#183 from linuxdaemon/gonzobot+reconnect
Add reconnect command and ping tracker
2 parents 1a7c2d5 + adb9569 commit 4c53fc2

1 file changed

Lines changed: 90 additions & 1 deletion

File tree

plugins/check_conn.py

Lines changed: 90 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
1+
import asyncio
2+
import time
3+
from functools import partial
4+
15
from cloudbot import hook
6+
from cloudbot.util import colors, async_util
27

38

49
@hook.command(autohelp=False, permissions=["botcontrol"])
5-
def conncheck(nick, bot, notice):
10+
def conncheck(nick, bot, notice):
611
"""This command is an effort to make the bot reconnect to a network if it has been disconnected."""
712
# For each irc network return a notice on the connection state and send a message from
813
# each connection to the nick that used the command.
@@ -14,3 +19,87 @@ def conncheck(nick, bot, notice):
1419
bot.connections[conn].connect()
1520
# Send a message from each irc network connection to the nick that issued the command
1621
bot.connections[conn].message(nick, "just letting you know I am here. {}".format(conn))
22+
23+
24+
@asyncio.coroutine
25+
@hook.command(autohelp=False, permissions=["botcontrol"], singlethread=True)
26+
def reconnect(conn, text, bot):
27+
"""[connection] - Reconnects to [connection] or the current connection if not specified"""
28+
if not text:
29+
to_reconnect = conn
30+
else:
31+
try:
32+
to_reconnect = bot.connections[text.lower()]
33+
except KeyError:
34+
return "Connection '{}' not found".format(text)
35+
36+
if to_reconnect.connected:
37+
to_reconnect.quit("Reconnecting...")
38+
yield from asyncio.sleep(1)
39+
to_reconnect._quit = False
40+
41+
coro = to_reconnect.connect()
42+
try:
43+
yield from asyncio.wait_for(coro, 30)
44+
except asyncio.TimeoutError:
45+
return "Connection timed out"
46+
except Exception as e:
47+
return "{}: {}".format(type(e).__name__, e)
48+
49+
return "Reconnected to '{}'".format(conn.name)
50+
51+
52+
def format_conn(conn):
53+
act_time = time.time() - conn.memory.get("last_activity", 0)
54+
ping_interval = conn.config.get("ping_interval", 60)
55+
if conn.connected:
56+
if act_time > ping_interval:
57+
out = "$(yellow){name}$(clear) (last activity: {activity} secs)"
58+
else:
59+
out = "$(green){name}$(clear)"
60+
else:
61+
out = "$(red){name}$(clear)"
62+
63+
return colors.parse(out.format(
64+
name=conn.name, activity=round(act_time, 3)
65+
))
66+
67+
68+
@hook.command("connlist", "listconns", autohelp=False, permissions=["botcontrol"])
69+
def list_conns(bot):
70+
"""- Lists all current connections and their status"""
71+
conns = ', '.join(
72+
map(format_conn, bot.connections.values())
73+
)
74+
return "Current connections: {}".format(conns)
75+
76+
77+
@hook.periodic(5)
78+
def pinger(bot):
79+
for conn in bot.connections.values():
80+
if conn.connected:
81+
ping_interval = conn.config.get("ping_interval", 60)
82+
# This is updated by a catch-all hook, so any activity from the server will indicate a live connection
83+
# This mimics most modern clients, as they will only send a ping if they have not received any data recently
84+
last_act = conn.memory.get("last_activity")
85+
# If the activity time isn't set, default to the current time.
86+
# This avoids an issue where the bot would reconnect just after loading this plugin
87+
if last_act is None:
88+
conn.memory["last_activity"] = time.time()
89+
continue
90+
91+
diff = time.time() - last_act
92+
if diff >= (ping_interval * 2):
93+
conn.quit("Reconnecting due to lag...")
94+
time.sleep(1)
95+
conn._quit = False
96+
conn.loop.call_soon_threadsafe(
97+
partial(async_util.wrap_future, conn.connect(), loop=conn.loop)
98+
)
99+
elif diff >= ping_interval:
100+
conn.send("PING :LAGCHECK{}".format(time.time()))
101+
102+
103+
@hook.irc_raw('*')
104+
def on_activity(conn):
105+
conn.memory["last_activity"] = time.time()

0 commit comments

Comments
 (0)