autopep8
This commit is contained in:
parent
6f16848302
commit
4189d5c874
@ -1,4 +1,5 @@
|
||||
# The bot commands implemented in here are present no matter which module is loaded
|
||||
# The bot commands implemented in here are present no matter which module
|
||||
# is loaded
|
||||
|
||||
import botconfig
|
||||
from tools import decorators
|
||||
@ -9,17 +10,19 @@ from settings import common as var
|
||||
from base64 import b64encode
|
||||
import imp
|
||||
|
||||
def on_privmsg(cli, rawnick, chan, msg, notice = False):
|
||||
|
||||
def on_privmsg(cli, rawnick, chan, msg, notice=False):
|
||||
currmod = ld.MODULES[ld.CURRENT_MODULE]
|
||||
|
||||
if botconfig.IGNORE_HIDDEN_COMMANDS and (chan.startswith("@#") or chan.startswith("+#")):
|
||||
|
||||
if botconfig.IGNORE_HIDDEN_COMMANDS and (
|
||||
chan.startswith("@#") or chan.startswith("+#")):
|
||||
return
|
||||
|
||||
if (notice and ((chan != botconfig.NICK and not botconfig.ALLOW_NOTICE_COMMANDS) or
|
||||
(chan == botconfig.NICK and not botconfig.ALLOW_PRIVATE_NOTICE_COMMANDS))):
|
||||
|
||||
if (notice and ((chan != botconfig.NICK and not botconfig.ALLOW_NOTICE_COMMANDS) or (
|
||||
chan == botconfig.NICK and not botconfig.ALLOW_PRIVATE_NOTICE_COMMANDS))):
|
||||
return # not allowed in settings
|
||||
|
||||
if chan != botconfig.NICK: #not a PM
|
||||
|
||||
if chan != botconfig.NICK: # not a PM
|
||||
if currmod and "" in currmod.COMMANDS.keys():
|
||||
for fn in currmod.COMMANDS[""]:
|
||||
try:
|
||||
@ -29,13 +32,15 @@ def on_privmsg(cli, rawnick, chan, msg, notice = False):
|
||||
raise e
|
||||
else:
|
||||
logging.error(traceback.format_exc())
|
||||
cli.msg(chan, "An error has occurred and has been logged.")
|
||||
cli.msg(
|
||||
chan,
|
||||
"An error has occurred and has been logged.")
|
||||
# Now that is always called first.
|
||||
for x in set(list(COMMANDS.keys()) + (list(currmod.COMMANDS.keys()) if currmod else list())):
|
||||
if x and msg.lower().startswith(botconfig.CMD_CHAR+x):
|
||||
h = msg[len(x)+1:]
|
||||
if x and msg.lower().startswith(botconfig.CMD_CHAR + x):
|
||||
h = msg[len(x) + 1:]
|
||||
if not h or h[0] == " " or not x:
|
||||
for fn in COMMANDS.get(x,[])+(currmod.COMMANDS.get(x,[]) if currmod else []):
|
||||
for fn in COMMANDS.get(x, []) + (currmod.COMMANDS.get(x, []) if currmod else []):
|
||||
try:
|
||||
fn(cli, rawnick, chan, h.lstrip())
|
||||
except Exception as e:
|
||||
@ -43,18 +48,20 @@ def on_privmsg(cli, rawnick, chan, msg, notice = False):
|
||||
raise e
|
||||
else:
|
||||
logging.error(traceback.format_exc())
|
||||
cli.msg(chan, "An error has occurred and has been logged.")
|
||||
|
||||
cli.msg(
|
||||
chan,
|
||||
"An error has occurred and has been logged.")
|
||||
|
||||
else:
|
||||
for x in set(list(PM_COMMANDS.keys()) + (list(currmod.PM_COMMANDS.keys()) if currmod else list())):
|
||||
if msg.lower().startswith(botconfig.CMD_CHAR+x):
|
||||
h = msg[len(x)+1:]
|
||||
if msg.lower().startswith(botconfig.CMD_CHAR + x):
|
||||
h = msg[len(x) + 1:]
|
||||
elif not x or msg.lower().startswith(x):
|
||||
h = msg[len(x):]
|
||||
else:
|
||||
continue
|
||||
if not h or h[0] == " " or not x:
|
||||
for fn in PM_COMMANDS.get(x, [])+(currmod.PM_COMMANDS.get(x,[]) if currmod else []):
|
||||
for fn in PM_COMMANDS.get(x, []) + (currmod.PM_COMMANDS.get(x, []) if currmod else []):
|
||||
try:
|
||||
fn(cli, rawnick, h.lstrip())
|
||||
except Exception as e:
|
||||
@ -62,16 +69,22 @@ def on_privmsg(cli, rawnick, chan, msg, notice = False):
|
||||
raise e
|
||||
else:
|
||||
logging.error(traceback.format_exc())
|
||||
cli.msg(chan, "An error has occurred and has been logged.")
|
||||
|
||||
cli.msg(
|
||||
chan,
|
||||
"An error has occurred and has been logged.")
|
||||
|
||||
|
||||
def __unhandled__(cli, prefix, cmd, *args):
|
||||
currmod = ld.MODULES[ld.CURRENT_MODULE]
|
||||
|
||||
if cmd in set(list(HOOKS.keys())+(list(currmod.HOOKS.keys()) if currmod else list())):
|
||||
if cmd in set(
|
||||
list(HOOKS.keys()) +
|
||||
(list(currmod.HOOKS.keys()) if currmod else list())):
|
||||
largs = list(args)
|
||||
for i,arg in enumerate(largs):
|
||||
if isinstance(arg, bytes): largs[i] = arg.decode('ascii')
|
||||
for fn in HOOKS.get(cmd, [])+(currmod.HOOKS.get(cmd, []) if currmod else []):
|
||||
for i, arg in enumerate(largs):
|
||||
if isinstance(arg, bytes):
|
||||
largs[i] = arg.decode('ascii')
|
||||
for fn in HOOKS.get(cmd, []) + (currmod.HOOKS.get(cmd, []) if currmod else []):
|
||||
try:
|
||||
fn(cli, prefix, *largs)
|
||||
except Exception as e:
|
||||
@ -79,13 +92,17 @@ def __unhandled__(cli, prefix, cmd, *args):
|
||||
raise e
|
||||
else:
|
||||
logging.error(traceback.format_exc())
|
||||
cli.msg(botconfig.CHANNEL, "An error has occurred and has been logged.")
|
||||
cli.msg(
|
||||
botconfig.CHANNEL,
|
||||
"An error has occurred and has been logged.")
|
||||
else:
|
||||
logging.debug('Unhandled command {0}({1})'.format(cmd, [arg.decode('utf_8')
|
||||
for arg in args
|
||||
if isinstance(arg, bytes)]))
|
||||
logging.debug(
|
||||
'Unhandled command {0}({1})'.format(
|
||||
cmd, [
|
||||
arg.decode('utf_8') for arg in args if isinstance(
|
||||
arg, bytes)]))
|
||||
|
||||
|
||||
|
||||
COMMANDS = {}
|
||||
PM_COMMANDS = {}
|
||||
HOOKS = {}
|
||||
@ -94,27 +111,28 @@ cmd = decorators.generate(COMMANDS)
|
||||
pmcmd = decorators.generate(PM_COMMANDS)
|
||||
hook = decorators.generate(HOOKS, raw_nick=True, permissions=False)
|
||||
|
||||
|
||||
def connect_callback(cli):
|
||||
|
||||
def prepare_stuff(*args):
|
||||
def prepare_stuff(*args):
|
||||
cli.join(botconfig.CHANNEL)
|
||||
cli.msg("ChanServ", "op "+botconfig.CHANNEL)
|
||||
|
||||
cli.msg("ChanServ", "op " + botconfig.CHANNEL)
|
||||
|
||||
cli.cap("REQ", "extended-join")
|
||||
cli.cap("REQ", "account-notify")
|
||||
|
||||
|
||||
try:
|
||||
ld.MODULES[ld.CURRENT_MODULE].connect_callback(cli)
|
||||
except AttributeError:
|
||||
pass # no connect_callback for this one
|
||||
|
||||
pass # no connect_callback for this one
|
||||
|
||||
cli.nick(botconfig.NICK) # very important (for regain/release)
|
||||
|
||||
|
||||
prepare_stuff = hook("endofmotd", hookid=294)(prepare_stuff)
|
||||
|
||||
def mustregain(cli, *blah):
|
||||
cli.ns_regain()
|
||||
|
||||
cli.ns_regain()
|
||||
|
||||
def mustrelease(cli, *rest):
|
||||
cli.ns_release()
|
||||
cli.nick(botconfig.NICK)
|
||||
@ -122,51 +140,53 @@ def connect_callback(cli):
|
||||
@hook("unavailresource", hookid=239)
|
||||
@hook("nicknameinuse", hookid=239)
|
||||
def must_use_temp_nick(cli, *etc):
|
||||
cli.nick(botconfig.NICK+"_")
|
||||
cli.nick(botconfig.NICK + "_")
|
||||
cli.user(botconfig.NICK, "")
|
||||
|
||||
|
||||
decorators.unhook(HOOKS, 239)
|
||||
hook("unavailresource")(mustrelease)
|
||||
hook("nicknameinuse")(mustregain)
|
||||
|
||||
|
||||
if botconfig.SASL_AUTHENTICATION:
|
||||
|
||||
|
||||
@hook("authenticate")
|
||||
def auth_plus(cli, something, plus):
|
||||
if plus == "+":
|
||||
nick_b = bytes(botconfig.USERNAME if botconfig.USERNAME else botconfig.NICK, "utf-8")
|
||||
nick_b = bytes(
|
||||
botconfig.USERNAME if botconfig.USERNAME else botconfig.NICK,
|
||||
"utf-8")
|
||||
pass_b = bytes(botconfig.PASS, "utf-8")
|
||||
secrt_msg = b'\0'.join((nick_b, nick_b, pass_b))
|
||||
cli.send("AUTHENTICATE " + b64encode(secrt_msg).decode("utf-8"))
|
||||
|
||||
cli.send(
|
||||
"AUTHENTICATE " +
|
||||
b64encode(secrt_msg).decode("utf-8"))
|
||||
|
||||
@hook("cap")
|
||||
def on_cap(cli, svr, mynick, ack, cap):
|
||||
if ack.upper() == "ACK" and "sasl" in cap:
|
||||
cli.send("AUTHENTICATE PLAIN")
|
||||
|
||||
|
||||
@hook("903")
|
||||
def on_successful_auth(cli, blah, blahh, blahhh):
|
||||
cli.cap("END")
|
||||
|
||||
|
||||
@hook("904")
|
||||
@hook("905")
|
||||
@hook("906")
|
||||
@hook("907")
|
||||
def on_failure_auth(cli, *etc):
|
||||
cli.quit()
|
||||
print("Authentication failed. Did you fill the account name "+
|
||||
print("Authentication failed. Did you fill the account name " +
|
||||
"in botconfig.USERNAME if it's different from the bot nick?")
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@hook("ping")
|
||||
def on_ping(cli, prefix, server):
|
||||
cli.send('PONG', server)
|
||||
|
||||
|
||||
|
||||
|
||||
if botconfig.DEBUG_MODE:
|
||||
@cmd("module", admin_only = True)
|
||||
@cmd("module", admin_only=True)
|
||||
def ch_module(cli, nick, chan, rest):
|
||||
rest = rest.strip()
|
||||
if rest in ld.MODULES.keys():
|
||||
|
1873
modules/wolfgame.py
1873
modules/wolfgame.py
File diff suppressed because it is too large
Load Diff
150
oyoyo/client.py
150
oyoyo/client.py
@ -22,15 +22,20 @@ import traceback
|
||||
import sys
|
||||
import ssl
|
||||
|
||||
from oyoyo.parse import parse_raw_irc_command
|
||||
from oyoyo.parse import parse_raw_irc_command
|
||||
|
||||
# Adapted from
|
||||
# http://code.activestate.com/recipes/511490-implementation-of-the-token-bucket-algorithm/
|
||||
|
||||
|
||||
# Adapted from http://code.activestate.com/recipes/511490-implementation-of-the-token-bucket-algorithm/
|
||||
class TokenBucket(object):
|
||||
|
||||
"""An implementation of the token bucket algorithm.
|
||||
|
||||
|
||||
>>> bucket = TokenBucket(80, 0.5)
|
||||
>>> bucket.consume(1)
|
||||
"""
|
||||
|
||||
def __init__(self, tokens, fill_rate):
|
||||
"""tokens is the total tokens in the bucket. fill_rate is the
|
||||
rate in tokens/second that the bucket will be refilled."""
|
||||
@ -55,9 +60,8 @@ class TokenBucket(object):
|
||||
self._tokens = min(self.capacity, self._tokens + delta)
|
||||
self.timestamp = now
|
||||
return self._tokens
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def add_commands(d):
|
||||
def dec(cls):
|
||||
for c in d:
|
||||
@ -68,36 +72,39 @@ def add_commands(d):
|
||||
setattr(cls, c, func(c))
|
||||
return cls
|
||||
return dec
|
||||
|
||||
|
||||
@add_commands(("join",
|
||||
"mode",
|
||||
"nick",
|
||||
"who",
|
||||
"cap"))
|
||||
class IRCClient(object):
|
||||
|
||||
""" IRC Client class. This handles one connection to a server.
|
||||
This can be used either with or without IRCApp ( see connect() docs )
|
||||
"""
|
||||
|
||||
def __init__(self, cmd_handler, **kwargs):
|
||||
""" the first argument should be an object with attributes/methods named
|
||||
as the irc commands. You may subclass from one of the classes in
|
||||
oyoyo.cmdhandler for convenience but it is not required. The
|
||||
methods should have arguments (prefix, args). prefix is
|
||||
""" the first argument should be an object with attributes/methods named
|
||||
as the irc commands. You may subclass from one of the classes in
|
||||
oyoyo.cmdhandler for convenience but it is not required. The
|
||||
methods should have arguments (prefix, args). prefix is
|
||||
normally the sender of the command. args is a list of arguments.
|
||||
Its recommened you subclass oyoyo.cmdhandler.DefaultCommandHandler,
|
||||
this class provides defaults for callbacks that are required for
|
||||
Its recommened you subclass oyoyo.cmdhandler.DefaultCommandHandler,
|
||||
this class provides defaults for callbacks that are required for
|
||||
normal IRC operation.
|
||||
|
||||
all other arguments should be keyword arguments. The most commonly
|
||||
used will be nick, host and port. You can also specify an "on connect"
|
||||
callback. ( check the source for others )
|
||||
|
||||
Warning: By default this class will not block on socket operations, this
|
||||
Warning: By default this class will not block on socket operations, this
|
||||
means if you use a plain while loop your app will consume 100% cpu.
|
||||
To enable blocking pass blocking=True.
|
||||
To enable blocking pass blocking=True.
|
||||
"""
|
||||
|
||||
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
|
||||
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
self.nickname = ""
|
||||
self.hostmask = ""
|
||||
self.ident = ""
|
||||
@ -111,12 +118,12 @@ class IRCClient(object):
|
||||
self.sasl_auth = False
|
||||
self.use_ssl = False
|
||||
self.lock = threading.RLock()
|
||||
|
||||
|
||||
self.tokenbucket = TokenBucket(23, 1.73)
|
||||
|
||||
self.__dict__.update(kwargs)
|
||||
self.command_handler = cmd_handler
|
||||
|
||||
|
||||
if self.use_ssl:
|
||||
self.socket = ssl.wrap_socket(self.socket)
|
||||
|
||||
@ -124,8 +131,8 @@ class IRCClient(object):
|
||||
|
||||
def send(self, *args, **kwargs):
|
||||
""" send a message to the connected server. all arguments are joined
|
||||
with a space for convenience, for example the following are identical
|
||||
|
||||
with a space for convenience, for example the following are identical
|
||||
|
||||
>>> cli.send("JOIN " + some_room)
|
||||
>>> cli.send("JOIN", some_room)
|
||||
|
||||
@ -134,13 +141,13 @@ class IRCClient(object):
|
||||
the 'encoding' keyword argument (default 'utf8').
|
||||
In python 3, all args must be of type str or bytes, *BUT* if they are
|
||||
str they will be converted to bytes with the encoding specified by the
|
||||
'encoding' keyword argument (default 'utf8').
|
||||
'encoding' keyword argument (default 'utf8').
|
||||
"""
|
||||
with self.lock:
|
||||
# Convert all args to bytes if not already
|
||||
encoding = kwargs.get('encoding') or 'utf_8'
|
||||
bargs = []
|
||||
for i,arg in enumerate(args):
|
||||
for i, arg in enumerate(args):
|
||||
if isinstance(arg, str):
|
||||
bargs.append(bytes(arg, encoding))
|
||||
elif isinstance(arg, bytes):
|
||||
@ -148,20 +155,25 @@ class IRCClient(object):
|
||||
elif arg is None:
|
||||
continue
|
||||
else:
|
||||
raise Exception(('Refusing to send arg at index {1} of the args from '+
|
||||
'provided: {0}').format(repr([(type(arg), arg)
|
||||
for arg in args]), i))
|
||||
raise Exception(
|
||||
('Refusing to send arg at index {1} of the args from ' +
|
||||
'provided: {0}').format(
|
||||
repr(
|
||||
[
|
||||
(type(arg),
|
||||
arg) for arg in args]),
|
||||
i))
|
||||
|
||||
msg = bytes(" ", "utf_8").join(bargs)
|
||||
logging.info('---> send {0}'.format(str(msg)[1:]))
|
||||
|
||||
|
||||
while not self.tokenbucket.consume(1):
|
||||
time.sleep(0.3)
|
||||
self.socket.send(msg + bytes("\r\n", "utf_8"))
|
||||
|
||||
def connect(self):
|
||||
""" initiates the connection to the server set in self.host:self.port
|
||||
and returns a generator object.
|
||||
""" initiates the connection to the server set in self.host:self.port
|
||||
and returns a generator object.
|
||||
|
||||
>>> cli = IRCClient(my_handler, host="irc.freenode.net", port=6667)
|
||||
>>> g = cli.connect()
|
||||
@ -183,32 +195,34 @@ class IRCClient(object):
|
||||
break
|
||||
if not self.blocking:
|
||||
self.socket.setblocking(0)
|
||||
|
||||
|
||||
if not self.sasl_auth:
|
||||
self.send("PASS {0}:{1}".format(self.authname if self.authname else self.nickname,
|
||||
self.password if self.password else "NOPASS"))
|
||||
self.send(
|
||||
"PASS {0}:{1}".format(
|
||||
self.authname if self.authname else self.nickname,
|
||||
self.password if self.password else "NOPASS"))
|
||||
else:
|
||||
self.cap("LS")
|
||||
|
||||
|
||||
self.nick(self.nickname)
|
||||
self.user(self.nickname, self.real_name)
|
||||
|
||||
if self.sasl_auth:
|
||||
self.cap("REQ", "multi-prefix")
|
||||
self.cap("REQ", "sasl")
|
||||
|
||||
|
||||
if self.connect_cb:
|
||||
try:
|
||||
self.connect_cb(self)
|
||||
except Exception as e:
|
||||
traceback.print_exc()
|
||||
raise e
|
||||
|
||||
|
||||
buffer = bytes()
|
||||
while not self._end:
|
||||
try:
|
||||
buffer += self.socket.recv(1024)
|
||||
except socket.error as e:
|
||||
except socket.error as e:
|
||||
if False and not self.blocking and e.errno == 11:
|
||||
pass
|
||||
else:
|
||||
@ -219,16 +233,25 @@ class IRCClient(object):
|
||||
|
||||
for el in data:
|
||||
prefix, command, args = parse_raw_irc_command(el)
|
||||
|
||||
|
||||
try:
|
||||
enc = "utf8"
|
||||
fargs = [arg.decode(enc) for arg in args if isinstance(arg,bytes)]
|
||||
fargs = [
|
||||
arg.decode(enc) for arg in args if isinstance(
|
||||
arg,
|
||||
bytes)]
|
||||
except UnicodeDecodeError:
|
||||
enc = "latin1"
|
||||
fargs = [arg.decode(enc) for arg in args if isinstance(arg,bytes)]
|
||||
|
||||
logging.debug("processCommand ({2}){0}({1})".format(command,
|
||||
fargs, prefix))
|
||||
fargs = [
|
||||
arg.decode(enc) for arg in args if isinstance(
|
||||
arg,
|
||||
bytes)]
|
||||
|
||||
logging.debug(
|
||||
"processCommand ({2}){0}({1})".format(
|
||||
command,
|
||||
fargs,
|
||||
prefix))
|
||||
try:
|
||||
largs = list(args)
|
||||
if prefix is not None:
|
||||
@ -236,21 +259,32 @@ class IRCClient(object):
|
||||
# for i,arg in enumerate(largs):
|
||||
# if arg is not None: largs[i] = arg.decode(enc)
|
||||
if command in self.command_handler:
|
||||
self.command_handler[command](self, prefix,*fargs)
|
||||
self.command_handler[command](
|
||||
self,
|
||||
prefix,
|
||||
*
|
||||
fargs)
|
||||
elif "" in self.command_handler:
|
||||
self.command_handler[""](self, prefix, command, *fargs)
|
||||
self.command_handler[""](
|
||||
self,
|
||||
prefix,
|
||||
command,
|
||||
*
|
||||
fargs)
|
||||
except Exception as e:
|
||||
traceback.print_exc()
|
||||
raise e # ?
|
||||
yield True
|
||||
finally:
|
||||
if self.socket:
|
||||
if self.socket:
|
||||
logging.info('closing socket')
|
||||
self.socket.close()
|
||||
yield False
|
||||
|
||||
def msg(self, user, msg):
|
||||
for line in msg.split('\n'):
|
||||
maxchars = 494 - len(self.nickname+self.ident+self.hostmask+user)
|
||||
maxchars = 494 - len(
|
||||
self.nickname + self.ident + self.hostmask + user)
|
||||
while line:
|
||||
extra = ""
|
||||
if len(line) > maxchars:
|
||||
@ -259,9 +293,11 @@ class IRCClient(object):
|
||||
self.send("PRIVMSG", user, ":{0}".format(line))
|
||||
line = extra
|
||||
privmsg = msg # Same thing
|
||||
|
||||
def notice(self, user, msg):
|
||||
for line in msg.split('\n'):
|
||||
maxchars = 495 - len(self.nickname+self.ident+self.hostmask+user)
|
||||
maxchars = 495 - len(
|
||||
self.nickname + self.ident + self.hostmask + user)
|
||||
while line:
|
||||
extra = ""
|
||||
if len(line) > maxchars:
|
||||
@ -269,27 +305,35 @@ class IRCClient(object):
|
||||
line = line[:maxchars]
|
||||
self.send("NOTICE", user, ":{0}".format(line))
|
||||
line = extra
|
||||
|
||||
def quit(self, msg=""):
|
||||
self.send("QUIT :{0}".format(msg))
|
||||
|
||||
def part(self, chan, msg=""):
|
||||
self.send("PART {0} :{1}".format(chan, msg))
|
||||
|
||||
def kick(self, chan, nick, msg=""):
|
||||
self.send("KICK", chan, nick, ":"+msg)
|
||||
self.send("KICK", chan, nick, ":" + msg)
|
||||
|
||||
def ns_identify(self, passwd):
|
||||
self.msg("NickServ", "IDENTIFY {0} {1}".format(self.nickname, passwd))
|
||||
|
||||
def ns_ghost(self):
|
||||
self.msg("NickServ", "GHOST "+self.nickname)
|
||||
self.msg("NickServ", "GHOST " + self.nickname)
|
||||
|
||||
def ns_release(self):
|
||||
self.msg("NickServ", "RELEASE "+self.nickname)
|
||||
self.msg("NickServ", "RELEASE " + self.nickname)
|
||||
|
||||
def ns_regain(self):
|
||||
self.msg("NickServ", "REGAIN "+self.nickname)
|
||||
self.msg("NickServ", "REGAIN " + self.nickname)
|
||||
|
||||
def user(self, uname, rname):
|
||||
self.send("USER", uname, self.host, self.host,
|
||||
rname or uname)
|
||||
self.send("USER", uname, self.host, self.host,
|
||||
rname or uname)
|
||||
|
||||
def mainLoop(self):
|
||||
conn = self.connect()
|
||||
while True:
|
||||
if not next(conn):
|
||||
print("Calling sys.exit()...")
|
||||
sys.exit()
|
||||
|
||||
|
@ -141,7 +141,7 @@ numeric_events = {
|
||||
b"423": "noadmininfo",
|
||||
b"424": "fileerror",
|
||||
b"431": "nonicknamegiven",
|
||||
b"432": "erroneusnickname", # Thiss iz how its speld in thee RFC.
|
||||
b"432": "erroneusnickname", # Thiss iz how its speld in thee RFC.
|
||||
b"433": "nicknameinuse",
|
||||
b"436": "nickcollision",
|
||||
b"437": "unavailresource", # "Nick temporally unavailable"
|
||||
@ -156,7 +156,7 @@ numeric_events = {
|
||||
b"462": "alreadyregistered",
|
||||
b"463": "nopermforhost",
|
||||
b"464": "passwdmismatch",
|
||||
b"465": "yourebannedcreep", # I love this one...
|
||||
b"465": "yourebannedcreep", # I love this one...
|
||||
b"466": "youwillbebanned",
|
||||
b"467": "keyset",
|
||||
b"471": "channelisfull",
|
||||
@ -208,4 +208,3 @@ protocol_events = [
|
||||
]
|
||||
|
||||
all_events = generated_events + protocol_events + list(numeric_events.values())
|
||||
|
||||
|
@ -18,9 +18,11 @@
|
||||
import logging
|
||||
|
||||
from oyoyo.ircevents import generated_events, protocol_events,\
|
||||
all_events, numeric_events
|
||||
all_events, numeric_events
|
||||
|
||||
# avoiding regex
|
||||
|
||||
|
||||
def parse_raw_irc_command(element):
|
||||
"""
|
||||
This function parses a raw irc command and returns a tuple
|
||||
@ -56,12 +58,13 @@ def parse_raw_irc_command(element):
|
||||
except KeyError:
|
||||
logging.debug('unknown numeric event {0}'.format(command))
|
||||
command = command.lower()
|
||||
if isinstance(command, bytes): command = command.decode("utf_8")
|
||||
if isinstance(command, bytes):
|
||||
command = command.decode("utf_8")
|
||||
|
||||
if args[0].startswith(bytes(':', 'utf_8')):
|
||||
args = [bytes(" ", "utf_8").join(args)[1:]]
|
||||
else:
|
||||
for idx, arg in enumerate(args):
|
||||
for idx, arg in enumerate(args):
|
||||
if arg.startswith(bytes(':', 'utf_8')):
|
||||
args = args[:idx] + [bytes(" ", 'utf_8').join(args[idx:])[1:]]
|
||||
break
|
||||
@ -89,4 +92,3 @@ def parse_nick(name):
|
||||
return (nick, mode, rest, None)
|
||||
|
||||
return (nick, mode, user, host)
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
PING_WAIT = 300 # Seconds
|
||||
PING_MIN_WAIT = 30
|
||||
MINIMUM_WAIT = 60
|
||||
MINIMUM_WAIT = 60
|
||||
EXTRA_WAIT = 20
|
||||
MAXIMUM_WAITED = 2 # limit for amount of !wait's
|
||||
STATS_RATE_LIMIT = 15
|
||||
@ -22,128 +22,140 @@ MAX_PRIVMSG_TARGETS = 1
|
||||
LOG_FILENAME = ""
|
||||
BARE_LOG_FILENAME = ""
|
||||
|
||||
# HIT MISS SUICIDE
|
||||
GUN_CHANCES = ( 5/7 , 1/7 , 1/7 )
|
||||
DRUNK_GUN_CHANCES = ( 2/7 , 4/7 , 1/7 )
|
||||
MANSLAUGHTER_CHANCE = 1/5 # ACCIDENTAL HEADSHOT (FATAL)
|
||||
# HIT MISS SUICIDE
|
||||
GUN_CHANCES = (5 / 7, 1 / 7, 1 / 7)
|
||||
DRUNK_GUN_CHANCES = (2 / 7, 4 / 7, 1 / 7)
|
||||
MANSLAUGHTER_CHANCE = 1 / 5 # ACCIDENTAL HEADSHOT (FATAL)
|
||||
|
||||
GUNNER_KILLS_WOLF_AT_NIGHT_CHANCE = 0
|
||||
GUARDIAN_ANGEL_DIES_CHANCE = 1/2
|
||||
DETECTIVE_REVEALED_CHANCE = 2/5
|
||||
GUARDIAN_ANGEL_DIES_CHANCE = 1 / 2
|
||||
DETECTIVE_REVEALED_CHANCE = 2 / 5
|
||||
|
||||
#################################################################################################################
|
||||
##########################################################################
|
||||
# ROLE INDEX: PLAYERS SEER WOLF CURSED DRUNK HARLOT TRAITOR GUNNER CROW ANGEL DETECTIVE ##
|
||||
#################################################################################################################
|
||||
ROLES_GUIDE = { 4 : ( 1 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ), ##
|
||||
6 : ( 1 , 1 , 1 , 1 , 0 , 0 , 0 , 0 , 0 , 0 ), ##
|
||||
8 : ( 1 , 2 , 1 , 1 , 1 , 0 , 0 , 0 , 0 , 0 ), ##
|
||||
10 : ( 1 , 2 , 1 , 1 , 1 , 1 , 1 , 0 , 0 , 0 ), ##
|
||||
11 : ( 1 , 2 , 1 , 1 , 1 , 1 , 1 , 0 , 1 , 0 ), ##
|
||||
15 : ( 1 , 3 , 1 , 1 , 1 , 1 , 1 , 0 , 1 , 1 ), ##
|
||||
22 : ( 1 , 4 , 1 , 1 , 1 , 1 , 1 , 0 , 1 , 1 ), ##
|
||||
29 : ( 1 , 5 , 1 , 1 , 1 , 1 , 1 , 0 , 1 , 1 ), ##
|
||||
None : ( 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 )} ##
|
||||
#################################################################################################################
|
||||
##########################################################################
|
||||
ROLES_GUIDE = {4: (1, 1, 0, 0, 0, 0, 0, 0, 0, 0),
|
||||
6: (1, 1, 1, 1, 0, 0, 0, 0, 0, 0),
|
||||
8: (1, 2, 1, 1, 1, 0, 0, 0, 0, 0),
|
||||
10: (1, 2, 1, 1, 1, 1, 1, 0, 0, 0),
|
||||
11: (1, 2, 1, 1, 1, 1, 1, 0, 1, 0),
|
||||
15: (1, 3, 1, 1, 1, 1, 1, 0, 1, 1),
|
||||
22: (1, 4, 1, 1, 1, 1, 1, 0, 1, 1),
|
||||
29: (1, 5, 1, 1, 1, 1, 1, 0, 1, 1),
|
||||
None: (0, 0, 0, 0, 0, 0, 0, 0, 0, 0)}
|
||||
##########################################################################
|
||||
# Notes: ##
|
||||
#################################################################################################################
|
||||
##########################################################################
|
||||
|
||||
GAME_MODES = {}
|
||||
AWAY = [] # cloaks of people who are away.
|
||||
SIMPLE_NOTIFY = [] # cloaks of people who !simple, who want everything /notice'd
|
||||
# cloaks of people who !simple, who want everything /notice'd
|
||||
SIMPLE_NOTIFY = []
|
||||
|
||||
ROLE_INDICES = {0 : "seer",
|
||||
1 : "wolf",
|
||||
2 : "cursed villager",
|
||||
3 : "village drunk",
|
||||
4 : "harlot",
|
||||
5 : "traitor",
|
||||
6 : "gunner",
|
||||
7 : "werecrow",
|
||||
8 : "guardian angel",
|
||||
9 : "detective"}
|
||||
|
||||
INDEX_OF_ROLE = dict((v,k) for k,v in ROLE_INDICES.items())
|
||||
ROLE_INDICES = {0: "seer",
|
||||
1: "wolf",
|
||||
2: "cursed villager",
|
||||
3: "village drunk",
|
||||
4: "harlot",
|
||||
5: "traitor",
|
||||
6: "gunner",
|
||||
7: "werecrow",
|
||||
8: "guardian angel",
|
||||
9: "detective"}
|
||||
|
||||
INDEX_OF_ROLE = dict((v, k) for k, v in ROLE_INDICES.items())
|
||||
|
||||
|
||||
NO_VICTIMS_MESSAGES = ("The body of a young penguin pet is found.",
|
||||
"A pool of blood and wolf paw prints are found.",
|
||||
"Traces of wolf fur are found.")
|
||||
LYNCH_MESSAGES = ("The villagers, after much debate, finally decide on lynching \u0002{0}\u0002, who turned out to be... a \u0002{1}\u0002.",
|
||||
"Under a lot of noise, the pitchfork-bearing villagers lynch \u0002{0}\u0002, who turned out to be... a \u0002{1}\u0002.",
|
||||
"The mob drags a protesting \u0002{0}\u0002 to the hanging tree. S/He succumbs to the will of the horde, and is hanged. It is discovered (s)he was a \u0002{1}\u0002.",
|
||||
"Resigned to his/her fate, \u0002{0}\u0002 is led to the gallows. After death, it is discovered (s)he was a \u0002{1}\u0002.")
|
||||
LYNCH_MESSAGES = (
|
||||
"The villagers, after much debate, finally decide on lynching \u0002{0}\u0002, who turned out to be... a \u0002{1}\u0002.",
|
||||
"Under a lot of noise, the pitchfork-bearing villagers lynch \u0002{0}\u0002, who turned out to be... a \u0002{1}\u0002.",
|
||||
"The mob drags a protesting \u0002{0}\u0002 to the hanging tree. S/He succumbs to the will of the horde, and is hanged. It is discovered (s)he was a \u0002{1}\u0002.",
|
||||
"Resigned to his/her fate, \u0002{0}\u0002 is led to the gallows. After death, it is discovered (s)he was a \u0002{1}\u0002.")
|
||||
|
||||
import botconfig
|
||||
|
||||
RULES = (botconfig.CHANNEL + " channel rules: 1) Be nice to others. 2) Do not share information "+
|
||||
"after death. 3) No bots allowed. 4) Do not play with clones.\n"+
|
||||
"5) Do not quit unless you need to leave. 6) No swearing and keep it "+
|
||||
"family-friendly. 7) Do not paste PM's from the bot during the game. "+
|
||||
"8) Use common sense. 9) Waiting for timeouts is discouraged.")
|
||||
RULES = (
|
||||
botconfig.CHANNEL +
|
||||
" channel rules: 1) Be nice to others. 2) Do not share information " +
|
||||
"after death. 3) No bots allowed. 4) Do not play with clones.\n" +
|
||||
"5) Do not quit unless you need to leave. 6) No swearing and keep it " +
|
||||
"family-friendly. 7) Do not paste PM's from the bot during the game. " +
|
||||
"8) Use common sense. 9) Waiting for timeouts is discouraged.")
|
||||
|
||||
# Other settings:
|
||||
START_WITH_DAY = False
|
||||
WOLF_STEALS_GUN = False # at night, the wolf can steal steal the victim's bullets
|
||||
# at night, the wolf can steal steal the victim's bullets
|
||||
WOLF_STEALS_GUN = False
|
||||
|
||||
OPT_IN_PING = False # instead of !away/!back, users can opt-in to be pinged
|
||||
PING_IN = [] # cloaks of users who have opted in for ping
|
||||
|
||||
is_role = lambda plyr, rol: rol in ROLES and plyr in ROLES[rol]
|
||||
|
||||
|
||||
def plural(role):
|
||||
if role == "wolf": return "wolves"
|
||||
elif role == "person": return "people"
|
||||
else: return role + "s"
|
||||
|
||||
if role == "wolf":
|
||||
return "wolves"
|
||||
elif role == "person":
|
||||
return "people"
|
||||
else:
|
||||
return role + "s"
|
||||
|
||||
|
||||
def list_players():
|
||||
pl = []
|
||||
for x in ROLES.values():
|
||||
pl.extend(x)
|
||||
return pl
|
||||
|
||||
|
||||
|
||||
def list_players_and_roles():
|
||||
plr = {}
|
||||
for x in ROLES.keys():
|
||||
for p in ROLES[x]:
|
||||
plr[p] = x
|
||||
return plr
|
||||
|
||||
|
||||
get_role = lambda plyr: list_players_and_roles()[plyr]
|
||||
|
||||
|
||||
def del_player(pname):
|
||||
prole = get_role(pname)
|
||||
ROLES[prole].remove(pname)
|
||||
|
||||
|
||||
|
||||
class InvalidModeException(Exception): pass
|
||||
|
||||
class InvalidModeException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def game_mode(name):
|
||||
def decor(c):
|
||||
GAME_MODES[name] = c
|
||||
return c
|
||||
return decor
|
||||
|
||||
|
||||
CHANGEABLE_ROLES = { "seers" : INDEX_OF_ROLE["seer"],
|
||||
"wolves" : INDEX_OF_ROLE["wolf"],
|
||||
"cursed" : INDEX_OF_ROLE["cursed villager"],
|
||||
"drunks" : INDEX_OF_ROLE["village drunk"],
|
||||
"harlots" : INDEX_OF_ROLE["harlot"],
|
||||
"traitors" : INDEX_OF_ROLE["traitor"],
|
||||
"gunners" : INDEX_OF_ROLE["gunner"],
|
||||
"werecrows" : INDEX_OF_ROLE["werecrow"],
|
||||
"angels" : INDEX_OF_ROLE["guardian angel"],
|
||||
"detectives" : INDEX_OF_ROLE["detective"]}
|
||||
|
||||
|
||||
CHANGEABLE_ROLES = {"seers": INDEX_OF_ROLE["seer"],
|
||||
"wolves": INDEX_OF_ROLE["wolf"],
|
||||
"cursed": INDEX_OF_ROLE["cursed villager"],
|
||||
"drunks": INDEX_OF_ROLE["village drunk"],
|
||||
"harlots": INDEX_OF_ROLE["harlot"],
|
||||
"traitors": INDEX_OF_ROLE["traitor"],
|
||||
"gunners": INDEX_OF_ROLE["gunner"],
|
||||
"werecrows": INDEX_OF_ROLE["werecrow"],
|
||||
"angels": INDEX_OF_ROLE["guardian angel"],
|
||||
"detectives": INDEX_OF_ROLE["detective"]}
|
||||
|
||||
|
||||
# TODO: implement game modes
|
||||
@game_mode("roles")
|
||||
class ChangedRolesMode(object):
|
||||
|
||||
"""Example: !fgame roles=wolves:1,seers:0,angels:1"""
|
||||
|
||||
|
||||
def __init__(self, arg):
|
||||
self.ROLES_GUIDE = ROLES_GUIDE.copy()
|
||||
lx = list(ROLES_GUIDE[None])
|
||||
@ -161,102 +173,111 @@ class ChangedRolesMode(object):
|
||||
try:
|
||||
lx[CHANGEABLE_ROLES[role.lower()]] = num
|
||||
except KeyError:
|
||||
raise InvalidModeException(("The role \u0002{0}\u0002 "+
|
||||
raise InvalidModeException(("The role \u0002{0}\u0002 " +
|
||||
"is not valid.").format(role))
|
||||
except ValueError:
|
||||
raise InvalidModeException("A bad value was used in mode roles.")
|
||||
raise InvalidModeException(
|
||||
"A bad value was used in mode roles.")
|
||||
for k in ROLES_GUIDE.keys():
|
||||
self.ROLES_GUIDE[k] = tuple(lx)
|
||||
|
||||
|
||||
|
||||
# Persistence
|
||||
|
||||
|
||||
|
||||
|
||||
# Load saved settings
|
||||
import sqlite3
|
||||
import os
|
||||
|
||||
conn = sqlite3.connect("data.sqlite3", check_same_thread = False)
|
||||
conn = sqlite3.connect("data.sqlite3", check_same_thread=False)
|
||||
|
||||
with conn:
|
||||
c = conn.cursor()
|
||||
c.execute('CREATE TABLE IF NOT EXISTS away (nick TEXT)') # whoops, i mean cloak, not nick
|
||||
|
||||
c.execute('CREATE TABLE IF NOT EXISTS simple_role_notify (cloak TEXT)') # people who understand each role
|
||||
# whoops, i mean cloak, not nick
|
||||
c.execute('CREATE TABLE IF NOT EXISTS away (nick TEXT)')
|
||||
|
||||
# people who understand each role
|
||||
c.execute('CREATE TABLE IF NOT EXISTS simple_role_notify (cloak TEXT)')
|
||||
|
||||
c.execute('SELECT * FROM away')
|
||||
for row in c:
|
||||
AWAY.append(row[0])
|
||||
|
||||
|
||||
c.execute('SELECT * FROM simple_role_notify')
|
||||
for row in c:
|
||||
SIMPLE_NOTIFY.append(row[0])
|
||||
|
||||
|
||||
# populate the roles table
|
||||
c.execute('DROP TABLE IF EXISTS roles')
|
||||
c.execute('CREATE TABLE roles (id INTEGER PRIMARY KEY AUTOINCREMENT, role TEXT)')
|
||||
c.execute(
|
||||
'CREATE TABLE roles (id INTEGER PRIMARY KEY AUTOINCREMENT, role TEXT)')
|
||||
|
||||
for x in ["villager"]+list(ROLE_INDICES.values()):
|
||||
for x in ["villager"] + list(ROLE_INDICES.values()):
|
||||
c.execute("INSERT OR REPLACE INTO roles (role) VALUES (?)", (x,))
|
||||
|
||||
|
||||
c.execute(('CREATE TABLE IF NOT EXISTS rolestats (player TEXT, role TEXT, '+
|
||||
'teamwins SMALLINT, individualwins SMALLINT, totalgames SMALLINT, '+
|
||||
'UNIQUE(player, role))'))
|
||||
|
||||
|
||||
c.execute(
|
||||
('CREATE TABLE IF NOT EXISTS rolestats (player TEXT, role TEXT, ' +
|
||||
'teamwins SMALLINT, individualwins SMALLINT, totalgames SMALLINT, ' +
|
||||
'UNIQUE(player, role))'))
|
||||
|
||||
if OPT_IN_PING:
|
||||
c.execute('CREATE TABLE IF NOT EXISTS ping (cloak text)')
|
||||
|
||||
c.execute('SELECT * FROM ping')
|
||||
for row in c:
|
||||
PING_IN.append(row[0])
|
||||
|
||||
|
||||
|
||||
|
||||
def remove_away(clk):
|
||||
with conn:
|
||||
c.execute('DELETE from away where nick=?', (clk,))
|
||||
|
||||
|
||||
|
||||
def add_away(clk):
|
||||
with conn:
|
||||
c.execute('INSERT into away VALUES (?)', (clk,))
|
||||
|
||||
|
||||
|
||||
def remove_simple_rolemsg(clk):
|
||||
with conn:
|
||||
c.execute('DELETE from simple_role_notify where cloak=?', (clk,))
|
||||
|
||||
|
||||
|
||||
def add_simple_rolemsg(clk):
|
||||
with conn:
|
||||
c.execute('INSERT into simple_role_notify VALUES (?)', (clk,))
|
||||
|
||||
|
||||
|
||||
def remove_ping(clk):
|
||||
with conn:
|
||||
c.execute('DELETE from ping where cloak=?', (clk,))
|
||||
|
||||
|
||||
def add_ping(clk):
|
||||
with conn:
|
||||
c.execute('INSERT into ping VALUES (?)', (clk,))
|
||||
c.execute('INSERT into ping VALUES (?)', (clk,))
|
||||
|
||||
|
||||
def update_role_stats(acc, role, won, iwon):
|
||||
|
||||
|
||||
with conn:
|
||||
wins, iwins, totalgames = 0, 0, 0
|
||||
|
||||
c.execute(("SELECT teamwins, individualwins, totalgames FROM rolestats "+
|
||||
"WHERE player=? AND role=?"), (acc, role))
|
||||
|
||||
c.execute(
|
||||
("SELECT teamwins, individualwins, totalgames FROM rolestats " +
|
||||
"WHERE player=? AND role=?"),
|
||||
(acc, role))
|
||||
row = c.fetchone()
|
||||
if row:
|
||||
wins, iwins, total = row
|
||||
else:
|
||||
wins, iwins, total = 0,0,0
|
||||
|
||||
wins, iwins, total = 0, 0, 0
|
||||
|
||||
if won:
|
||||
wins += 1
|
||||
if iwon:
|
||||
iwins += 1
|
||||
total += 1
|
||||
|
||||
|
||||
c.execute("INSERT OR REPLACE INTO rolestats VALUES (?,?,?,?,?)",
|
||||
(acc, role, wins, iwins, total))
|
||||
|
||||
|
||||
|
||||
|
@ -1,9 +1,9 @@
|
||||
PING_WAIT = 300 # Seconds
|
||||
PING_MIN_WAIT = 30 # How long !start has to wait after a !ping
|
||||
PING_MIN_WAIT = 30 # How long !start has to wait after a !ping
|
||||
MINIMUM_WAIT = 60
|
||||
EXTRA_WAIT = 20
|
||||
EXTRA_WAIT_JOIN = 0 # Add this many seconds to the waiting time for each !join
|
||||
WAIT_AFTER_JOIN = 10 # Wait at least this many seconds after the last join
|
||||
EXTRA_WAIT_JOIN = 0 # Add this many seconds to the waiting time for each !join
|
||||
WAIT_AFTER_JOIN = 10 # Wait at least this many seconds after the last join
|
||||
MAXIMUM_WAITED = 3 # limit for amount of !wait's
|
||||
STATS_RATE_LIMIT = 60
|
||||
VOTES_RATE_LIMIT = 60
|
||||
@ -21,14 +21,15 @@ DAY_TIME_LIMIT_WARN = 600
|
||||
DAY_TIME_LIMIT_CHANGE = 120 # seconds after DAY_TIME_LIMIT_WARN has passed
|
||||
JOIN_TIME_LIMIT = 3600
|
||||
# May only be set if the above are also set
|
||||
SHORT_DAY_PLAYERS = 6 # Number of players left to have a short day
|
||||
SHORT_DAY_PLAYERS = 6 # Number of players left to have a short day
|
||||
SHORT_DAY_LIMIT_WARN = 400
|
||||
SHORT_DAY_LIMIT_CHANGE = 120
|
||||
KILL_IDLE_TIME = 300
|
||||
WARN_IDLE_TIME = 180
|
||||
PART_GRACE_TIME = 30
|
||||
QUIT_GRACE_TIME = 30
|
||||
# controls how many people it does in one /msg; only works for messages that are the same
|
||||
# controls how many people it does in one /msg; only works for messages
|
||||
# that are the same
|
||||
MAX_PRIVMSG_TARGETS = 4
|
||||
LEAVE_STASIS_PENALTY = 1
|
||||
IDLE_STASIS_PENALTY = 1
|
||||
@ -47,57 +48,59 @@ KILL_BOLD = False
|
||||
LOG_FILENAME = ""
|
||||
BARE_LOG_FILENAME = ""
|
||||
|
||||
# HIT MISS SUICIDE
|
||||
GUN_CHANCES = ( 5/7 , 1/7 , 1/7 )
|
||||
DRUNK_GUN_CHANCES = ( 2/7 , 3/7 , 2/7 )
|
||||
MANSLAUGHTER_CHANCE = 2/5 # ACCIDENTAL HEADSHOT (FATAL)
|
||||
# HIT MISS SUICIDE
|
||||
GUN_CHANCES = (5 / 7, 1 / 7, 1 / 7)
|
||||
DRUNK_GUN_CHANCES = (2 / 7, 3 / 7, 2 / 7)
|
||||
MANSLAUGHTER_CHANCE = 2 / 5 # ACCIDENTAL HEADSHOT (FATAL)
|
||||
|
||||
GUNNER_KILLS_WOLF_AT_NIGHT_CHANCE = 1/4
|
||||
GUARDIAN_ANGEL_DIES_CHANCE = 1/2
|
||||
DETECTIVE_REVEALED_CHANCE = 2/5
|
||||
GUNNER_KILLS_WOLF_AT_NIGHT_CHANCE = 1 / 4
|
||||
GUARDIAN_ANGEL_DIES_CHANCE = 1 / 2
|
||||
DETECTIVE_REVEALED_CHANCE = 2 / 5
|
||||
|
||||
#################################################################################################################
|
||||
##########################################################################
|
||||
# ROLE INDEX: PLAYERS SEER WOLF CURSED DRUNK HARLOT TRAITOR GUNNER CROW ANGEL DETECTIVE ##
|
||||
#################################################################################################################
|
||||
ROLES_GUIDE = { 4 : ( 1 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ), ##
|
||||
6 : ( 1 , 1 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ), ##
|
||||
8 : ( 1 , 1 , 1 , 1 , 1 , 1 , 0 , 0 , 0 , 0 ), ##
|
||||
10 : ( 1 , 2 , 1 , 1 , 1 , 1 , 1 , 0 , 0 , 0 ), ##
|
||||
12 : ( 1 , 2 , 1 , 1 , 1 , 1 , 1 , 1 , 0 , 1 ), ##
|
||||
15 : ( 1 , 3 , 1 , 1 , 1 , 1 , 1 , 1 , 0 , 1 ), ##
|
||||
17 : ( 1 , 3 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ), ##
|
||||
18 : ( 1 , 3 , 2 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ), ##
|
||||
20 : ( 1 , 4 , 2 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ), ##
|
||||
None : ( 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 )} ##
|
||||
#################################################################################################################
|
||||
##########################################################################
|
||||
ROLES_GUIDE = {4: (1, 1, 0, 0, 0, 0, 0, 0, 0, 0),
|
||||
6: (1, 1, 1, 0, 0, 0, 0, 0, 0, 0),
|
||||
8: (1, 1, 1, 1, 1, 1, 0, 0, 0, 0),
|
||||
10: (1, 2, 1, 1, 1, 1, 1, 0, 0, 0),
|
||||
12: (1, 2, 1, 1, 1, 1, 1, 1, 0, 1),
|
||||
15: (1, 3, 1, 1, 1, 1, 1, 1, 0, 1),
|
||||
17: (1, 3, 1, 1, 1, 1, 1, 1, 1, 1),
|
||||
18: (1, 3, 2, 1, 1, 1, 1, 1, 1, 1),
|
||||
20: (1, 4, 2, 1, 1, 1, 1, 1, 1, 1),
|
||||
None: (0, 0, 0, 0, 0, 0, 0, 0, 0, 0)}
|
||||
##########################################################################
|
||||
# Notes: ##
|
||||
#################################################################################################################
|
||||
##########################################################################
|
||||
|
||||
GAME_MODES = {}
|
||||
AWAY = ['services.', 'services.int'] # cloaks of people who are away.
|
||||
SIMPLE_NOTIFY = [] # cloaks of people who !simple, who want everything /notice'd
|
||||
# cloaks of people who !simple, who want everything /notice'd
|
||||
SIMPLE_NOTIFY = []
|
||||
|
||||
ROLE_INDICES = {0 : "seer",
|
||||
1 : "wolf",
|
||||
2 : "cursed villager",
|
||||
3 : "village drunk",
|
||||
4 : "harlot",
|
||||
5 : "traitor",
|
||||
6 : "gunner",
|
||||
7 : "werecrow",
|
||||
8 : "guardian angel",
|
||||
9 : "detective"}
|
||||
ROLE_INDICES = {0: "seer",
|
||||
1: "wolf",
|
||||
2: "cursed villager",
|
||||
3: "village drunk",
|
||||
4: "harlot",
|
||||
5: "traitor",
|
||||
6: "gunner",
|
||||
7: "werecrow",
|
||||
8: "guardian angel",
|
||||
9: "detective"}
|
||||
|
||||
INDEX_OF_ROLE = dict((v,k) for k,v in ROLE_INDICES.items())
|
||||
INDEX_OF_ROLE = dict((v, k) for k, v in ROLE_INDICES.items())
|
||||
|
||||
NO_VICTIMS_MESSAGES = ("The body of a young penguin pet is found.",
|
||||
"A pool of blood and wolf paw prints are found.",
|
||||
"Traces of wolf fur are found.")
|
||||
LYNCH_MESSAGES = ("The villagers, after much debate, finally decide on lynching \u0002{0}\u0002, who turned out to be... a \u0002{1}\u0002.",
|
||||
"Under a lot of noise, the pitchfork-bearing villagers lynch \u0002{0}\u0002, who turned out to be... a \u0002{1}\u0002.",
|
||||
"Despite protests, the mob drags their victim to the hanging tree. \u0002{0}\u0002 succumbs to the will of the horde, and is hanged. The villagers have killed a \u0002{1}\u0002.",
|
||||
"Resigned to the inevitable, \u0002{0}\u0002 is led to the gallows. Once the twitching stops, it is discovered that the village lynched a \u0002{1}\u0002.",
|
||||
"Before the rope is pulled, \u0002{0}\u0002, the \u0002{1}\u0002, throws a grenade at the mob. The grenade explodes early.")
|
||||
LYNCH_MESSAGES = (
|
||||
"The villagers, after much debate, finally decide on lynching \u0002{0}\u0002, who turned out to be... a \u0002{1}\u0002.",
|
||||
"Under a lot of noise, the pitchfork-bearing villagers lynch \u0002{0}\u0002, who turned out to be... a \u0002{1}\u0002.",
|
||||
"Despite protests, the mob drags their victim to the hanging tree. \u0002{0}\u0002 succumbs to the will of the horde, and is hanged. The villagers have killed a \u0002{1}\u0002.",
|
||||
"Resigned to the inevitable, \u0002{0}\u0002 is led to the gallows. Once the twitching stops, it is discovered that the village lynched a \u0002{1}\u0002.",
|
||||
"Before the rope is pulled, \u0002{0}\u0002, the \u0002{1}\u0002, throws a grenade at the mob. The grenade explodes early.")
|
||||
|
||||
import botconfig
|
||||
|
||||
@ -105,17 +108,23 @@ RULES = (botconfig.CHANNEL + " channel rules: http://wolf.xnrand.com/rules")
|
||||
|
||||
# Other settings:
|
||||
START_WITH_DAY = False
|
||||
WOLF_STEALS_GUN = True # at night, the wolf can steal steal the victim's bullets
|
||||
# at night, the wolf can steal steal the victim's bullets
|
||||
WOLF_STEALS_GUN = True
|
||||
|
||||
OPT_IN_PING = False # instead of !away/!back, users can opt-in to be pinged
|
||||
PING_IN = [] # cloaks of users who have opted in for ping
|
||||
|
||||
is_role = lambda plyr, rol: rol in ROLES and plyr in ROLES[rol]
|
||||
|
||||
|
||||
def plural(role):
|
||||
if role == "wolf": return "wolves"
|
||||
elif role == "person": return "people"
|
||||
else: return role + "s"
|
||||
if role == "wolf":
|
||||
return "wolves"
|
||||
elif role == "person":
|
||||
return "people"
|
||||
else:
|
||||
return role + "s"
|
||||
|
||||
|
||||
def list_players():
|
||||
pl = []
|
||||
@ -123,6 +132,7 @@ def list_players():
|
||||
pl.extend(x)
|
||||
return pl
|
||||
|
||||
|
||||
def list_players_and_roles():
|
||||
plr = {}
|
||||
for x in ROLES.keys():
|
||||
@ -132,19 +142,23 @@ def list_players_and_roles():
|
||||
|
||||
get_role = lambda plyr: list_players_and_roles()[plyr]
|
||||
|
||||
|
||||
def get_reveal_role(nick):
|
||||
if HIDDEN_TRAITOR and get_role(nick) == "traitor":
|
||||
return "villager"
|
||||
else:
|
||||
return get_role(nick)
|
||||
|
||||
|
||||
def del_player(pname):
|
||||
prole = get_role(pname)
|
||||
ROLES[prole].remove(pname)
|
||||
|
||||
|
||||
class InvalidModeException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class InvalidModeException(Exception): pass
|
||||
def game_mode(name):
|
||||
def decor(c):
|
||||
GAME_MODES[name] = c
|
||||
@ -152,23 +166,22 @@ def game_mode(name):
|
||||
return decor
|
||||
|
||||
|
||||
CHANGEABLE_ROLES = { "seers" : INDEX_OF_ROLE["seer"],
|
||||
"wolves" : INDEX_OF_ROLE["wolf"],
|
||||
"cursed" : INDEX_OF_ROLE["cursed villager"],
|
||||
"drunks" : INDEX_OF_ROLE["village drunk"],
|
||||
"harlots" : INDEX_OF_ROLE["harlot"],
|
||||
"traitors" : INDEX_OF_ROLE["traitor"],
|
||||
"gunners" : INDEX_OF_ROLE["gunner"],
|
||||
"werecrows" : INDEX_OF_ROLE["werecrow"],
|
||||
"angels" : INDEX_OF_ROLE["guardian angel"],
|
||||
"detectives" : INDEX_OF_ROLE["detective"]}
|
||||
|
||||
|
||||
CHANGEABLE_ROLES = {"seers": INDEX_OF_ROLE["seer"],
|
||||
"wolves": INDEX_OF_ROLE["wolf"],
|
||||
"cursed": INDEX_OF_ROLE["cursed villager"],
|
||||
"drunks": INDEX_OF_ROLE["village drunk"],
|
||||
"harlots": INDEX_OF_ROLE["harlot"],
|
||||
"traitors": INDEX_OF_ROLE["traitor"],
|
||||
"gunners": INDEX_OF_ROLE["gunner"],
|
||||
"werecrows": INDEX_OF_ROLE["werecrow"],
|
||||
"angels": INDEX_OF_ROLE["guardian angel"],
|
||||
"detectives": INDEX_OF_ROLE["detective"]}
|
||||
|
||||
|
||||
# TODO: implement game modes
|
||||
@game_mode("roles")
|
||||
class ChangedRolesMode(object):
|
||||
|
||||
"""Example: !fgame roles=wolves:1,seers:0,angels:1"""
|
||||
|
||||
def __init__(self, arg):
|
||||
@ -187,10 +200,11 @@ class ChangedRolesMode(object):
|
||||
try:
|
||||
lx[CHANGEABLE_ROLES[role.lower()]] = num
|
||||
except KeyError:
|
||||
raise InvalidModeException(("The role \u0002{0}\u0002 "+
|
||||
raise InvalidModeException(("The role \u0002{0}\u0002 " +
|
||||
"is not valid.").format(role))
|
||||
except ValueError:
|
||||
raise InvalidModeException("A bad value was used in mode roles.")
|
||||
raise InvalidModeException(
|
||||
"A bad value was used in mode roles.")
|
||||
for k in ROLES_GUIDE.keys():
|
||||
self.ROLES_GUIDE[k] = tuple(lx)
|
||||
|
||||
@ -201,13 +215,15 @@ class ChangedRolesMode(object):
|
||||
# Load saved settings
|
||||
import sqlite3
|
||||
|
||||
conn = sqlite3.connect("data.sqlite3", check_same_thread = False)
|
||||
conn = sqlite3.connect("data.sqlite3", check_same_thread=False)
|
||||
|
||||
with conn:
|
||||
c = conn.cursor()
|
||||
c.execute('CREATE TABLE IF NOT EXISTS away (nick TEXT)') # whoops, i mean cloak, not nick
|
||||
# whoops, i mean cloak, not nick
|
||||
c.execute('CREATE TABLE IF NOT EXISTS away (nick TEXT)')
|
||||
|
||||
c.execute('CREATE TABLE IF NOT EXISTS simple_role_notify (cloak TEXT)') # people who understand each role
|
||||
# people who understand each role
|
||||
c.execute('CREATE TABLE IF NOT EXISTS simple_role_notify (cloak TEXT)')
|
||||
|
||||
c.execute('SELECT * FROM away')
|
||||
for row in c:
|
||||
@ -219,21 +235,21 @@ with conn:
|
||||
|
||||
# populate the roles table
|
||||
c.execute('DROP TABLE IF EXISTS roles')
|
||||
c.execute('CREATE TABLE roles (id INTEGER PRIMARY KEY AUTOINCREMENT, role TEXT)')
|
||||
c.execute(
|
||||
'CREATE TABLE roles (id INTEGER PRIMARY KEY AUTOINCREMENT, role TEXT)')
|
||||
|
||||
for x in ["villager"]+list(ROLE_INDICES.values()):
|
||||
for x in ["villager"] + list(ROLE_INDICES.values()):
|
||||
c.execute("INSERT OR REPLACE INTO roles (role) VALUES (?)", (x,))
|
||||
|
||||
c.execute(
|
||||
('CREATE TABLE IF NOT EXISTS rolestats (player TEXT, role TEXT, ' +
|
||||
'teamwins SMALLINT, individualwins SMALLINT, totalgames SMALLINT, ' +
|
||||
'UNIQUE(player, role))'))
|
||||
|
||||
c.execute(('CREATE TABLE IF NOT EXISTS rolestats (player TEXT, role TEXT, '+
|
||||
'teamwins SMALLINT, individualwins SMALLINT, totalgames SMALLINT, '+
|
||||
'UNIQUE(player, role))'))
|
||||
c.execute(
|
||||
('CREATE TABLE IF NOT EXISTS gamestats (size SMALLINT, villagewins SMALLINT, ' +
|
||||
'wolfwins SMALLINT, totalgames SMALLINT, UNIQUE(size))'))
|
||||
|
||||
|
||||
c.execute(('CREATE TABLE IF NOT EXISTS gamestats (size SMALLINT, villagewins SMALLINT, ' +
|
||||
'wolfwins SMALLINT, totalgames SMALLINT, UNIQUE(size))'))
|
||||
|
||||
|
||||
if OPT_IN_PING:
|
||||
c.execute('CREATE TABLE IF NOT EXISTS ping (cloak text)')
|
||||
|
||||
@ -246,22 +262,27 @@ def remove_away(clk):
|
||||
with conn:
|
||||
c.execute('DELETE from away where nick=?', (clk,))
|
||||
|
||||
|
||||
def add_away(clk):
|
||||
with conn:
|
||||
c.execute('INSERT into away VALUES (?)', (clk,))
|
||||
|
||||
|
||||
def remove_simple_rolemsg(clk):
|
||||
with conn:
|
||||
c.execute('DELETE from simple_role_notify where cloak=?', (clk,))
|
||||
|
||||
|
||||
def add_simple_rolemsg(clk):
|
||||
with conn:
|
||||
c.execute('INSERT into simple_role_notify VALUES (?)', (clk,))
|
||||
|
||||
|
||||
def remove_ping(clk):
|
||||
with conn:
|
||||
c.execute('DELETE from ping where cloak=?', (clk,))
|
||||
|
||||
|
||||
|
||||
def add_ping(clk):
|
||||
with conn:
|
||||
c.execute('INSERT into ping VALUES (?)', (clk,))
|
||||
@ -271,8 +292,10 @@ def update_role_stats(acc, role, won, iwon):
|
||||
with conn:
|
||||
wins, iwins, total = 0, 0, 0
|
||||
|
||||
c.execute(("SELECT teamwins, individualwins, totalgames FROM rolestats "+
|
||||
"WHERE player=? AND role=?"), (acc, role))
|
||||
c.execute(
|
||||
("SELECT teamwins, individualwins, totalgames FROM rolestats " +
|
||||
"WHERE player=? AND role=?"),
|
||||
(acc, role))
|
||||
row = c.fetchone()
|
||||
if row:
|
||||
wins, iwins, total = row
|
||||
@ -286,75 +309,106 @@ def update_role_stats(acc, role, won, iwon):
|
||||
c.execute("INSERT OR REPLACE INTO rolestats VALUES (?,?,?,?,?)",
|
||||
(acc, role, wins, iwins, total))
|
||||
|
||||
|
||||
def update_game_stats(size, winner):
|
||||
with conn:
|
||||
vwins, wwins, total = 0, 0, 0
|
||||
|
||||
c.execute("SELECT villagewins, wolfwins, totalgames FROM gamestats "+
|
||||
"WHERE size=?", (size,))
|
||||
|
||||
c.execute("SELECT villagewins, wolfwins, totalgames FROM gamestats " +
|
||||
"WHERE size=?", (size,))
|
||||
row = c.fetchone()
|
||||
if row:
|
||||
vwins, wwins, total = row
|
||||
|
||||
|
||||
if winner == "wolves":
|
||||
wwins += 1
|
||||
elif winner == "villagers":
|
||||
vwins += 1
|
||||
total += 1
|
||||
|
||||
|
||||
c.execute("INSERT OR REPLACE INTO gamestats VALUES (?,?,?,?)",
|
||||
(size, vwins, wwins, total))
|
||||
|
||||
(size, vwins, wwins, total))
|
||||
|
||||
|
||||
def get_player_stats(acc, role):
|
||||
if role.lower() not in ["villager"] + [v.lower() for k, v in ROLE_INDICES.items()]:
|
||||
if role.lower() not in ["villager"] + [v.lower()
|
||||
for k, v in ROLE_INDICES.items()]:
|
||||
return "No such role: {0}".format(role)
|
||||
with conn:
|
||||
c.execute("SELECT player FROM rolestats WHERE player=? COLLATE NOCASE", (acc,))
|
||||
c.execute(
|
||||
"SELECT player FROM rolestats WHERE player=? COLLATE NOCASE",
|
||||
(acc,))
|
||||
player = c.fetchone()
|
||||
if player:
|
||||
for row in c.execute("SELECT * FROM rolestats WHERE player=? COLLATE NOCASE AND role=? COLLATE NOCASE", (acc, role)):
|
||||
msg = "\u0002{0}\u0002 as \u0002{1}\u0002 | Team wins: {2} (%d%%), Individual wins: {3} (%d%%), Total games: {4}".format(*row)
|
||||
return msg % (round(row[2]/row[4] * 100), round(row[3]/row[4] * 100))
|
||||
msg = "\u0002{0}\u0002 as \u0002{1}\u0002 | Team wins: {2} (%d%%), Individual wins: {3} (%d%%), Total games: {4}".format(
|
||||
*
|
||||
row)
|
||||
return msg % (
|
||||
round(row[2] / row[4] * 100),
|
||||
round(row[3] / row[4] * 100))
|
||||
else:
|
||||
return "No stats for {0} as {1}.".format(player[0], role)
|
||||
return "{0} has not played any games.".format(acc)
|
||||
|
||||
|
||||
def get_player_totals(acc):
|
||||
role_totals = []
|
||||
with conn:
|
||||
c.execute("SELECT player FROM rolestats WHERE player=? COLLATE NOCASE", (acc,))
|
||||
c.execute(
|
||||
"SELECT player FROM rolestats WHERE player=? COLLATE NOCASE",
|
||||
(acc,))
|
||||
player = c.fetchone()
|
||||
if player:
|
||||
for role in ["villager"] + [v for k, v in ROLE_INDICES.items()]:
|
||||
c.execute("SELECT totalgames FROM rolestats WHERE player=? COLLATE NOCASE AND role=? COLLATE NOCASE", (acc, role))
|
||||
c.execute(
|
||||
"SELECT totalgames FROM rolestats WHERE player=? COLLATE NOCASE AND role=? COLLATE NOCASE",
|
||||
(acc, role))
|
||||
row = c.fetchone()
|
||||
if row:
|
||||
role_totals.append("\u0002{0}\u0002: {1}".format(role, *row))
|
||||
c.execute("SELECT SUM(totalgames) from rolestats WHERE player=? COLLATE NOCASE AND role!='cursed villager' AND role!='gunner'", (acc,))
|
||||
role_totals.append(
|
||||
"\u0002{0}\u0002: {1}".format(
|
||||
role,
|
||||
*
|
||||
row))
|
||||
c.execute(
|
||||
"SELECT SUM(totalgames) from rolestats WHERE player=? COLLATE NOCASE AND role!='cursed villager' AND role!='gunner'",
|
||||
(acc,))
|
||||
row = c.fetchone()
|
||||
return "\u0002{0}\u0002's totals | \u0002{1}\u0002 games | {2}".format(player[0], row[0], ", ".join(role_totals))
|
||||
return "\u0002{0}\u0002's totals | \u0002{1}\u0002 games | {2}".format(
|
||||
player[0],
|
||||
row[0],
|
||||
", ".join(role_totals))
|
||||
else:
|
||||
return "\u0002{0}\u0002 has not played any games.".format(acc)
|
||||
|
||||
|
||||
|
||||
def get_game_stats(size):
|
||||
with conn:
|
||||
for row in c.execute("SELECT * FROM gamestats WHERE size=?", (size,)):
|
||||
msg = "\u0002{0}\u0002 player games | Village wins: {1} (%d%%), Wolf wins: {2} (%d%%), Total games: {3}".format(*row)
|
||||
return msg % (round(row[1]/row[3] * 100), round(row[2]/row[3] * 100))
|
||||
msg = "\u0002{0}\u0002 player games | Village wins: {1} (%d%%), Wolf wins: {2} (%d%%), Total games: {3}".format(
|
||||
*
|
||||
row)
|
||||
return msg % (
|
||||
round(row[1] / row[3] * 100),
|
||||
round(row[2] / row[3] * 100))
|
||||
else:
|
||||
return "No stats for \u0002{0}\u0002 player games.".format(size)
|
||||
|
||||
|
||||
def get_game_totals():
|
||||
size_totals = []
|
||||
total = 0
|
||||
with conn:
|
||||
for size in range(MIN_PLAYERS, MAX_PLAYERS + 1):
|
||||
c.execute("SELECT size, totalgames FROM gamestats WHERE size=?", (size,))
|
||||
c.execute(
|
||||
"SELECT size, totalgames FROM gamestats WHERE size=?",
|
||||
(size,))
|
||||
row = c.fetchone()
|
||||
if row:
|
||||
size_totals.append("\u0002{0}p\u0002: {1}".format(*row))
|
||||
total += row[1]
|
||||
|
||||
|
||||
if len(size_totals) == 0:
|
||||
return "No games have been played."
|
||||
else:
|
||||
|
@ -1,17 +1,30 @@
|
||||
# Copyright (c) 2011, Jimmy Cao
|
||||
# All rights reserved.
|
||||
|
||||
# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
|
||||
# Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
# Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
# TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
from oyoyo.parse import parse_nick
|
||||
import fnmatch
|
||||
import botconfig
|
||||
|
||||
|
||||
def generate(fdict, permissions=True, **kwargs):
|
||||
"""Generates a decorator generator. Always use this"""
|
||||
def cmd(*s, raw_nick=False, admin_only=False, owner_only=False, hookid=-1):
|
||||
@ -26,10 +39,10 @@ def generate(fdict, permissions=True, **kwargs):
|
||||
else:
|
||||
nick = ""
|
||||
cloak = ""
|
||||
|
||||
|
||||
if not raw_nick and len(largs) > 1 and largs[1]:
|
||||
largs[1] = nick
|
||||
#if largs[1].startswith("#"):
|
||||
# if largs[1].startswith("#"):
|
||||
if not permissions or "" in s:
|
||||
return f(*largs)
|
||||
if cloak:
|
||||
@ -37,7 +50,9 @@ def generate(fdict, permissions=True, **kwargs):
|
||||
if fnmatch.fnmatch(cloak.lower(), pattern.lower()):
|
||||
for cmdname in s:
|
||||
if cmdname in botconfig.DENY[pattern]:
|
||||
largs[0].notice(nick, "You do not have permission to use that command.")
|
||||
largs[0].notice(
|
||||
nick,
|
||||
"You do not have permission to use that command.")
|
||||
return
|
||||
for pattern in botconfig.ALLOW.keys():
|
||||
if fnmatch.fnmatch(cloak.lower(), pattern.lower()):
|
||||
@ -45,15 +60,20 @@ def generate(fdict, permissions=True, **kwargs):
|
||||
if cmdname in botconfig.ALLOW[pattern]:
|
||||
return f(*largs) # no questions
|
||||
if owner_only:
|
||||
if cloak and [ptn for ptn in botconfig.OWNERS
|
||||
if fnmatch.fnmatch(cloak.lower(), ptn.lower())]:
|
||||
if cloak and [
|
||||
ptn for ptn in botconfig.OWNERS if fnmatch.fnmatch(
|
||||
cloak.lower(),
|
||||
ptn.lower())]:
|
||||
return f(*largs)
|
||||
elif cloak:
|
||||
largs[0].notice(nick, "You are not the owner.")
|
||||
return
|
||||
if admin_only:
|
||||
if cloak and [ptn for ptn in botconfig.ADMINS+botconfig.OWNERS
|
||||
if fnmatch.fnmatch(cloak.lower(), ptn.lower())]:
|
||||
if cloak and[ptn
|
||||
for ptn in botconfig.ADMINS + botconfig.OWNERS
|
||||
if fnmatch.fnmatch(
|
||||
cloak.lower(),
|
||||
ptn.lower())]:
|
||||
return f(*largs)
|
||||
elif cloak:
|
||||
largs[0].notice(nick, "You are not an admin.")
|
||||
@ -67,8 +87,11 @@ def generate(fdict, permissions=True, **kwargs):
|
||||
else:
|
||||
for fn in fdict[x]:
|
||||
if (fn.owner_only != owner_only or
|
||||
fn.admin_only != admin_only):
|
||||
raise Exception("Command: "+x+" has non-matching protection levels!")
|
||||
fn.admin_only != admin_only):
|
||||
raise Exception(
|
||||
"Command: " +
|
||||
x +
|
||||
" has non-matching protection levels!")
|
||||
fdict[x].append(innerf)
|
||||
if alias:
|
||||
innerf.aliases.append(x)
|
||||
@ -79,12 +102,13 @@ def generate(fdict, permissions=True, **kwargs):
|
||||
innerf.hookid = hookid
|
||||
innerf.__doc__ = f.__doc__
|
||||
return innerf
|
||||
|
||||
|
||||
return dec
|
||||
|
||||
return lambda *args, **kwarargs: cmd(*args, **kwarargs) if kwarargs else cmd(*args, **kwargs)
|
||||
|
||||
|
||||
|
||||
return lambda *args, **kwarargs: cmd(*args, **
|
||||
kwarargs) if kwarargs else cmd(*args, **kwargs)
|
||||
|
||||
|
||||
def unhook(hdict, hookid):
|
||||
for cmd in list(hdict.keys()):
|
||||
for x in hdict[cmd]:
|
||||
|
@ -10,15 +10,15 @@ for modfile in os.listdir("modules"):
|
||||
continue
|
||||
if not modfile.endswith(".py"):
|
||||
continue # not a module
|
||||
if not os.path.isfile("modules/"+modfile):
|
||||
if not os.path.isfile("modules/" + modfile):
|
||||
continue # not a file
|
||||
|
||||
|
||||
modfile = modfile[:-3]
|
||||
|
||||
print("Loading module "+modfile)
|
||||
|
||||
MODULES[modfile] = getattr(__import__("modules."+modfile), modfile)
|
||||
|
||||
|
||||
print("Loading module " + modfile)
|
||||
|
||||
MODULES[modfile] = getattr(__import__("modules." + modfile), modfile)
|
||||
|
||||
if botconfig.DEFAULT_MODULE in MODULES.keys():
|
||||
CURRENT_MODULE = botconfig.DEFAULT_MODULE.lower()
|
||||
else:
|
||||
|
@ -1,38 +1,42 @@
|
||||
import botconfig
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
class WolfgameLogger(object):
|
||||
|
||||
def __init__(self, outfile, boutfile):
|
||||
self.outfile = outfile
|
||||
self.boutfile = boutfile
|
||||
|
||||
|
||||
self.logged = ""
|
||||
self.barelogged = ""
|
||||
|
||||
|
||||
def log(self, message):
|
||||
self.logged += datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S ") + message + "\n"
|
||||
|
||||
|
||||
def logBare(self, *args):
|
||||
self.barelogged += datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S ") + " ".join(args) + "\n"
|
||||
|
||||
|
||||
def logChannelMessage(self, who, message):
|
||||
self.logged += datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S ") + "<{0}> {1}\n".format(who, message)
|
||||
|
||||
self.logged += datetime.utcnow().strftime(
|
||||
"%Y-%m-%d %H:%M:%S ") + "<{0}> {1}\n".format(who, message)
|
||||
|
||||
def logCommand(self, who, cmd, rest):
|
||||
self.logged += datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S ") + "<{0}> {1}{2} {3}".format(who, botconfig.CMD_CHAR, cmd, rest) + "\n"
|
||||
|
||||
self.logged += datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S ") + \
|
||||
"<{0}> {1}{2} {3}".format(who, botconfig.CMD_CHAR, cmd, rest) + "\n"
|
||||
|
||||
def logMessage(self, message):
|
||||
self.logged += datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S ") + "<{0}> ".format(botconfig.NICK)+message+"\n"
|
||||
|
||||
self.logged += datetime.utcnow().strftime(
|
||||
"%Y-%m-%d %H:%M:%S ") + "<{0}> ".format(botconfig.NICK) + message + "\n"
|
||||
|
||||
def saveToFile(self):
|
||||
if self.outfile:
|
||||
with open(self.outfile, "a") as lf:
|
||||
lf.write(self.logged)
|
||||
|
||||
|
||||
if self.boutfile:
|
||||
with open(self.boutfile, "a") as bl:
|
||||
bl.write(self.barelogged)
|
||||
|
||||
|
||||
self.logged = ""
|
||||
self.barelogged = ""
|
||||
|
22
wolfbot.py
22
wolfbot.py
@ -49,17 +49,17 @@ def main():
|
||||
handler.setFormatter(formatter)
|
||||
|
||||
cli = IRCClient(
|
||||
{"privmsg": modules.common.on_privmsg,
|
||||
"notice": lambda a, b, c, d: modules.common.on_privmsg(a, b, c, d, True),
|
||||
"": modules.common.__unhandled__},
|
||||
host=botconfig.HOST,
|
||||
port=botconfig.PORT,
|
||||
authname=botconfig.USERNAME,
|
||||
password=botconfig.PASS,
|
||||
nickname=botconfig.NICK,
|
||||
sasl_auth=botconfig.SASL_AUTHENTICATION,
|
||||
use_ssl=botconfig.USE_SSL,
|
||||
connect_cb=modules.common.connect_callback
|
||||
{"privmsg": modules.common.on_privmsg,
|
||||
"notice": lambda a, b, c, d: modules.common.on_privmsg(a, b, c, d, True),
|
||||
"": modules.common.__unhandled__},
|
||||
host=botconfig.HOST,
|
||||
port=botconfig.PORT,
|
||||
authname=botconfig.USERNAME,
|
||||
password=botconfig.PASS,
|
||||
nickname=botconfig.NICK,
|
||||
sasl_auth=botconfig.SASL_AUTHENTICATION,
|
||||
use_ssl=botconfig.USE_SSL,
|
||||
connect_cb=modules.common.connect_callback
|
||||
)
|
||||
cli.mainLoop()
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user