1+ import asyncio
2+ import time
3+ from functools import partial
4+
15from 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