Improve the ping-if feature.

This adds sort of a rate-limiting and pinging method preferences. If the
amount of players the person wants is reached or exceeded, they will be
automatically added to the !ping list; this is the default behaviour.
Doing !pingpref will make the bot automatically ping them shortly after
- this is done by a separate thread which is started on the first join
and exits when game starts or last player quits. Doing so will not
remove them from the ping list.
This commit is contained in:
Vgr E.Barry 2015-02-15 17:56:00 -05:00
parent d22d03d7d6
commit 75b14a87c1
2 changed files with 141 additions and 43 deletions

View File

@ -75,6 +75,7 @@ var.PLAYERS = {}
var.DCED_PLAYERS = {}
var.ADMIN_TO_PING = None
var.AFTER_FLASTGAME = None
var.PINGING_IFS = False
var.TIMERS = {}
var.ORIGINAL_SETTINGS = {}
@ -332,6 +333,7 @@ def reset():
var.ROLES = {"person" : []}
var.JOINED_THIS_GAME = [] # keeps track of who already joined this game at least once (cloaks)
var.JOINED_THIS_GAME_ACCS = [] # same, except accounts
var.PING_JOIN_TIMER = 0
var.NO_LYNCH = []
var.FGAMED = False
var.CURRENT_GAMEMODE = "default"
@ -343,6 +345,7 @@ def reset():
dict.clear(var.PLAYERS)
dict.clear(var.DCED_PLAYERS)
dict.clear(var.DISCONNECTED)
reset()
def make_stasis(nick, penalty):
@ -463,7 +466,7 @@ def pinger(cli, nick, chan, rest):
var.PINGING = True
TO_PING = []
pl = len(var.list_players())
@hook("whoreply", hookid=800)
def on_whoreply(cli, server, dunno, chan, dunno1,
@ -471,16 +474,21 @@ def pinger(cli, nick, chan, rest):
if not var.PINGING: return
if user in (botconfig.NICK, nick): return # Don't ping self.
if ('G' not in status and '+' not in status and not
is_user_stasised(user)[0] and not is_user_away(user)):
TO_PING.append(user)
if ('G' not in status and '+' not in status and not is_user_stasised(user)[0]):
acc = var.USERS[user]["account"]
if not is_user_away(user):
TO_PING.append(user)
elif acc != "*" and acc in var.PING_IF_PREFS_ACCS and var.PING_IF_PREFS_ACCS[acc] <= pl:
TO_PING.append(user)
elif not var.ACCOUNTS_ONLY and cloak in var.PING_IF_PREFS and var.PING_IF_PREFS[cloak] <= pl:
TO_PING.append(user)
@hook("endofwho", hookid=800)
def do_ping(*args):
if not var.PINGING: return
TO_PING = list(set(TO_PING)) # prevents duplicate entries
TO_PING.sort(key=lambda x: x.lower())
if TO_PING:
@ -787,12 +795,12 @@ def fpinger(cli, nick, chan, rest):
var.LAST_PING = None
pinger(cli, nick, chan, rest)
@cmd("pingif", "pingme", "pingat", "pingpref", pm=True)
@cmd("pingif", "pingme", "pingat", pm=True)
def altpinger(cli, nick, chan, rest):
"""Pings you when the number of players reaches your preference."""
if not rest or (rest.isdigit() and int(rest) == 0):
if is_user_altpinged(nick)[0]:
msg = "Your ping preferences have been removed (Was {0}).".format(is_user_altpinged(nick)[1])
msg = "Your ping preferences have been removed (was {0}).".format(is_user_altpinged(nick)[1])
if chan == nick:
pm(cli, nick, msg)
else:
@ -810,7 +818,17 @@ def altpinger(cli, nick, chan, rest):
cli.notice(nick, "You do not have any preferences set.")
elif rest.isdigit():
rest = int(rest)
if is_user_altpinged(nick)[0]:
if rest >= var.MAX_PLAYERS:
if chan == nick:
pm(cli, nick, "Please select a number between 0 and {0}.".format(var.MAX_PLAYERS-1))
else:
cli.notice(nick, "Please select a number between 0 and {0}.".format(var.MAX_PLAYERS-1))
elif is_user_altpinged(nick)[1] == rest:
if chan == nick:
pm(cli, nick, "Your ping preferences are already set to {0}.".format(rest))
else:
cli.notice(nick, "Your ping preferences are already set to {0}.".format(rest))
elif is_user_altpinged(nick)[0]:
msg = "Your ping preferences have been changed from {0} to {1}.".format(is_user_altpinged(nick)[1], rest)
if chan == nick:
pm(cli, nick, msg)
@ -824,7 +842,7 @@ def altpinger(cli, nick, chan, rest):
cli.notice(nick, "Your ping preferences have been set to {0}.".format(rest))
toggle_altpinged_status(nick, rest)
else:
cli.notice(nick, "You need to enter a positive number.")
cli.notice(nick, "You need to enter a non-negative integer.")
def is_user_altpinged(nick):
if nick in var.USERS.keys():
@ -881,6 +899,86 @@ def toggle_altpinged_status(nick, value, old=None):
if cloak in var.PING_IF_NUMS[old]:
var.PING_IF_NUMS[old].remove(cloak)
@cmd("pingpref", "pingprefs", "prefping", pm=True) # need a better name
def toggle_altping_prefs(cli, nick, chan, rest):
if nick in var.USERS:
cloak = var.USERS[nick]["cloak"]
acc = var.USERS[nick]["account"]
else: # something not right
if chan == nick:
pm(cli, nick, "You need to be in {0} to use that command.".format(botconfig.CHANNEL))
else: # former message: "You won the lottery! This is a bug though, so report it to the admins."
cli.notice(nick, "You need to be in {0} to use that command.".format(botconfig.CHANNEL))
if acc and acc != "*":
if acc in var.PING_PREFS_ACCS.keys() and var.PING_PREFS_ACCS[acc] == "all":
msg = "You will now only get pinged by {0}ping when your preferred amount of players is reached."
var.PING_PREFS_ACCS[acc] = "none"
var.set_ping_pref_acc(acc, "none")
else:
msg = "You will now get pinged when your preferred amount of players is reached."
var.PING_PREFS_ACCS[acc] = "all"
var.set_ping_pref_acc(acc, "all")
elif var.ACCOUNTS_ONLY:
msg = "You are not logged in to NickServ."
else:
if cloak in var.PING_PREFS.keys() and var.PING_PREFS[cloak] == "all":
msg = "You will now only get pinged by {0}ping when your preferred amount of players is reached."
var.PING_PREFS[cloak] = "none"
var.set_ping_pref(cloak, "none")
else:
msg = "You will now get pinged when your preferred amount of players is reached."
var.PING_PREFS[cloak] = "all"
var.set_ping_pref(cloak, "all")
if chan == nick:
pm(cli, nick, msg.format(botconfig.CMD_CHAR))
else:
cli.notice(nick, msg.format(botconfig.CMD_CHAR))
def join_timer_handler(cli):
while var.PHASE == "join":
with var.GRAVEYARD_LOCK:
if var.PING_JOIN_TIMER and var.PING_JOIN_TIMER + timedelta(seconds=10) <= datetime.now():
to_ping = set() # this ensures we don't have any duplicate
pl = var.list_players()
if len(pl) in var.PING_IF_NUMS_ACCS or (not var.ACCOUNTS_ONLY and len(pl) in var.PING_IF_NUMS):
var.PINGING_IFS = True
checker = []
chk_acc = []
for num in var.PING_IF_NUMS:
if num <= len(pl):
checker.extend(var.PING_IF_NUMS[num])
for num in var.PING_IF_NUMS_ACCS:
if num <= len(pl):
chk_acc.extend(var.PING_IF_NUMS_ACCS[num])
@hook("whospcrpl", hookid=387)
def ping_altpingers(cli, server, nick, ident, cloak, _, user, status, acc):
if ('G' in status or '+' in status or is_user_stasised(user)[0] or
not var.PINGING_IFS or user == botconfig.NICK):
return
if acc and acc != "*":
if acc in chk_acc and acc in var.PING_PREFS_ACCS and var.PING_PREFS_ACCS[acc] == "all":
to_ping.add(user)
elif not var.ACCOUNTS_ONLY and cloak in checker and cloak in var.PING_PREFS and var.PING_PREFS[cloak] == "all":
to_ping.add(user)
@hook("endofwho", hookid=387)
def fetch_altpingers(*stuff):
var.PINGING_IFS = False
var.PING_JOIN_TIMER = 0
decorators.unhook(HOOKS, 387)
if to_ping:
cli.msg(botconfig.CHANNEL, "PING! {0} players! {1}".format(len(pl), " ".join(to_ping)))
cli.who(botconfig.CHANNEL, "%nushaf")
time.sleep(5)
@cmd("join", "j", none=True, join=True)
def join(cli, nick, chan, rest):
"""Either starts a new game of Werewolf or joins an existing game that has not started yet."""
@ -933,6 +1031,10 @@ def join_player(cli, player, chan, who = None, forced = False):
var.PHASE = "join"
var.WAITED = 0
var.GAME_ID = time.time()
var.PING_JOIN_TIMER = datetime.now()
join_pinger = threading.Thread(None, join_timer_handler, args=(cli,))
join_pinger.daemon = True
join_pinger.start()
if cloak:
var.JOINED_THIS_GAME.append(cloak)
if acc:
@ -987,33 +1089,8 @@ def join_player(cli, player, chan, who = None, forced = False):
var.LAST_GSTATS = None
var.LAST_PSTATS = None
var.LAST_TIME = None
to_ping = set() # this ensures we don't have duplicates
players = len(pl) + 1
if players in var.PING_IF_NUMS_ACCS or (not var.ACCOUNTS_ONLY and players in var.PING_IF_NUMS):
var.PINGING_IFS = True
checker = var.PING_IF_NUMS[players] if players in var.PING_IF_NUMS else ()
chk_acc = var.PING_IF_NUMS_ACCS[players] if players in var.PING_IF_NUMS_ACCS else ()
@hook("whospcrpl", hookid=387)
def ping_altpingers(cli, server, nick, ident, cloak, _, user, status, acc):
if user in (player, who, botconfig.NICK) or user in pl:
return
if 'G' in status or '+' in status or is_user_stasised(user)[0] or not var.PINGING_IFS:
return
if acc in chk_acc:
to_ping.add(user)
elif not var.ACCOUNTS_ONLY and cloak in checker:
to_ping.add(user)
@hook("endofwho", hookid=387)
def actually_pingem(*stuff):
if not to_ping:
return
cli.msg(chan, "PING! {0} players! ".format(players) + " ".join(to_ping))
decorators.unhook(HOOKS, 387)
cli.who(chan, "%nushaf")
with var.GRAVEYARD_LOCK:
var.PING_JOIN_TIMER = datetime.now()
def kill_join(cli, chan):
pl = var.list_players()

View File

@ -252,6 +252,9 @@ PING_IF_PREFS_ACCS = {}
PING_IF_NUMS = {}
PING_IF_NUMS_ACCS = {}
PING_PREFS = {}
PING_PREFS_ACCS = {}
is_role = lambda plyr, rol: rol in ROLES and plyr in ROLES[rol]
def is_admin(nick, cloak=None, acc=None):
@ -805,6 +808,10 @@ def init_db():
c.execute('CREATE TABLE IF NOT EXISTS ping_if_prefs_accs (acc TEXT, players INTEGER)') # ping-if prefs (accounts - primary)
c.execute('CREATE TABLE IF NOT EXISTS ping_prefs (cloak TEXT, pref TEXT)') # ping-if preferences (none = only in !ping; all = on join and in !ping)
c.execute('CREATE TABLE IF NOT EXISTS ping_prefs_accs (acc TEXT, pref TEXT)') # ping-if prefs (accounts - primary)
c.execute('SELECT * FROM away')
for row in c:
AWAY.append(row[0])
@ -877,6 +884,14 @@ def init_db():
PING_IF_NUMS_ACCS[row[1]] = []
PING_IF_NUMS_ACCS[row[1]].append(row[0])
c.execute('SELECT * FROM ping_prefs')
for row in c:
PING_PREFS[row[0]] = row[1]
c.execute('SELECT * FROM ping_prefs_accs')
for row in c:
PING_PREFS_ACCS[row[0]] = row[1]
# populate the roles table
c.execute('DROP TABLE IF EXISTS roles')
c.execute('CREATE TABLE roles (id INTEGER PRIMARY KEY AUTOINCREMENT, role TEXT)')
@ -1018,18 +1033,24 @@ def remove_allow_acc(acc, command):
def set_ping_if_status(cloak, players):
with conn:
if players == 0:
c.execute('DELETE FROM ping_if_prefs WHERE cloak=?', (cloak,))
else:
c.execute('DELETE FROM ping_if_prefs WHERE cloak=?', (cloak,))
if players != 0:
c.execute('INSERT OR REPLACE INTO ping_if_prefs VALUES (?,?)', (cloak, players))
def set_ping_if_status_acc(acc, players):
with conn:
if players == 0:
c.execute('DELETE FROM ping_if_prefs_accs WHERE acc=?', (acc,))
else:
c.execute('DELETE FROM ping_if_prefs_accs WHERE acc=?', (acc,))
if players != 0:
c.execute('INSERT OR REPLACE INTO ping_if_prefs_accs VALUES (?,?)', (acc, players))
def set_ping_pref(cloak, pref):
with conn:
c.execute('INSERT OR REPLACE INTO ping_prefs VALUES (?,?)', (cloak, pref))
def set_ping_pref_acc(acc, pref):
with conn:
c.execute('INSERT OR REPLACE INTO ping_prefs_accs VALUES (?,?)', (acc, pref))
def update_role_stats(acc, role, won, iwon):
with conn:
wins, iwins, total = 0, 0, 0