Browse Source

change line endings to unix style

master
Jimmy Cao 13 years ago
parent
commit
3f36fb3fb5
  1. 188
      decorators.py
  2. 550
      oyoyo/client.py
  3. 416
      oyoyo/ircevents.py
  4. 541
      var.py
  5. 246
      wolfbot.py
  6. 5452
      wolfgame.py
  7. 74
      wolfgamelogger.py

188
decorators.py

@ -1,94 +1,94 @@
# Copyright (c) 2011, Jimmy Cao # Copyright (c) 2011, Jimmy Cao
# All rights reserved. # 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 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. # 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 from oyoyo.parse import parse_nick
import fnmatch import fnmatch
import botconfig import botconfig
def generate(fdict, permissions=True, **kwargs): def generate(fdict, permissions=True, **kwargs):
"""Generates a decorator generator. Always use this""" """Generates a decorator generator. Always use this"""
def cmd(*s, raw_nick=False, admin_only=False, owner_only=False, id=-1): def cmd(*s, raw_nick=False, admin_only=False, owner_only=False, id=-1):
def dec(f): def dec(f):
def innerf(*args): def innerf(*args):
largs = list(args) largs = list(args)
if len(largs) > 1 and largs[1]: if len(largs) > 1 and largs[1]:
nick, _, _, cloak = parse_nick(largs[1]) nick, _, _, cloak = parse_nick(largs[1])
if cloak is None: if cloak is None:
cloak = "" cloak = ""
else: else:
nick = "" nick = ""
cloak = "" cloak = ""
if not raw_nick and len(largs) > 1 and largs[1]: if not raw_nick and len(largs) > 1 and largs[1]:
largs[1] = nick largs[1] = nick
#if largs[1].startswith("#"): #if largs[1].startswith("#"):
if not permissions or "" in s: if not permissions or "" in s:
return f(*largs) return f(*largs)
if cloak: if cloak:
for pattern in botconfig.DENY.keys(): for pattern in botconfig.DENY.keys():
if fnmatch.fnmatch(cloak.lower(), pattern.lower()): if fnmatch.fnmatch(cloak.lower(), pattern.lower()):
for cmdname in s: for cmdname in s:
if cmdname in botconfig.DENY[pattern]: 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 return
for pattern in botconfig.ALLOW.keys(): for pattern in botconfig.ALLOW.keys():
if fnmatch.fnmatch(cloak.lower(), pattern.lower()): if fnmatch.fnmatch(cloak.lower(), pattern.lower()):
for cmdname in s: for cmdname in s:
if cmdname in botconfig.ALLOW[pattern]: if cmdname in botconfig.ALLOW[pattern]:
return f(*largs) # no questions return f(*largs) # no questions
if owner_only: if owner_only:
if cloak and [ptn for ptn in botconfig.OWNERS if cloak and [ptn for ptn in botconfig.OWNERS
if fnmatch.fnmatch(cloak.lower(), ptn.lower())]: if fnmatch.fnmatch(cloak.lower(), ptn.lower())]:
return f(*largs) return f(*largs)
elif cloak: elif cloak:
largs[0].notice(nick, "You are not the owner.") largs[0].notice(nick, "You are not the owner.")
return return
if admin_only: if admin_only:
if cloak and [ptn for ptn in botconfig.ADMINS+botconfig.OWNERS if cloak and [ptn for ptn in botconfig.ADMINS+botconfig.OWNERS
if fnmatch.fnmatch(cloak.lower(), ptn.lower())]: if fnmatch.fnmatch(cloak.lower(), ptn.lower())]:
return f(*largs) return f(*largs)
elif cloak: elif cloak:
largs[0].notice(nick, "You are not an admin.") largs[0].notice(nick, "You are not an admin.")
return return
return f(*largs) return f(*largs)
alias = False alias = False
innerf.aliases = [] innerf.aliases = []
for x in s: for x in s:
if x not in fdict.keys(): if x not in fdict.keys():
fdict[x] = [] fdict[x] = []
else: else:
for fn in fdict[x]: for fn in fdict[x]:
if (fn.owner_only != owner_only or if (fn.owner_only != owner_only or
fn.admin_only != admin_only): fn.admin_only != admin_only):
raise Exception("Command: "+x+" has non-matching protection levels!") raise Exception("Command: "+x+" has non-matching protection levels!")
fdict[x].append(innerf) fdict[x].append(innerf)
if alias: if alias:
innerf.aliases.append(x) innerf.aliases.append(x)
alias = True alias = True
innerf.owner_only = owner_only innerf.owner_only = owner_only
innerf.raw_nick = raw_nick innerf.raw_nick = raw_nick
innerf.admin_only = admin_only innerf.admin_only = admin_only
innerf.id = id innerf.id = id
innerf.__doc__ = f.__doc__ innerf.__doc__ = f.__doc__
return innerf return innerf
return dec 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, id): def unhook(hdict, id):
for cmd in list(hdict.keys()): for cmd in list(hdict.keys()):
for x in hdict[cmd]: for x in hdict[cmd]:
if x.id == id: if x.id == id:
hdict[cmd].remove(x) hdict[cmd].remove(x)
if not hdict[cmd]: if not hdict[cmd]:
del hdict[cmd] del hdict[cmd]

550
oyoyo/client.py

@ -1,275 +1,275 @@
# Copyright (c) 2011 Duncan Fordyce, Jimmy Cao # Copyright (c) 2011 Duncan Fordyce, Jimmy Cao
# Permission is hereby granted, free of charge, to any person obtaining a copy # Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal # of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights # in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is # copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions: # furnished to do so, subject to the following conditions:
# The above copyright notice and this permission notice shall be included in # The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software. # all copies or substantial portions of the Software.
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE. # THE SOFTWARE.
import logging import logging
import socket import socket
import time import time
import threading import threading
import traceback import traceback
import sys import sys
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): class TokenBucket(object):
"""An implementation of the token bucket algorithm. """An implementation of the token bucket algorithm.
>>> bucket = TokenBucket(80, 0.5) >>> bucket = TokenBucket(80, 0.5)
>>> bucket.consume(1) >>> bucket.consume(1)
""" """
def __init__(self, tokens, fill_rate): def __init__(self, tokens, fill_rate):
"""tokens is the total tokens in the bucket. fill_rate is the """tokens is the total tokens in the bucket. fill_rate is the
rate in tokens/second that the bucket will be refilled.""" rate in tokens/second that the bucket will be refilled."""
self.capacity = float(tokens) self.capacity = float(tokens)
self._tokens = float(tokens) self._tokens = float(tokens)
self.fill_rate = float(fill_rate) self.fill_rate = float(fill_rate)
self.timestamp = time.time() self.timestamp = time.time()
def consume(self, tokens): def consume(self, tokens):
"""Consume tokens from the bucket. Returns True if there were """Consume tokens from the bucket. Returns True if there were
sufficient tokens otherwise False.""" sufficient tokens otherwise False."""
if tokens <= self.tokens: if tokens <= self.tokens:
self._tokens -= tokens self._tokens -= tokens
return True return True
return False return False
@property @property
def tokens(self): def tokens(self):
now = time.time() now = time.time()
if self._tokens < self.capacity: if self._tokens < self.capacity:
delta = self.fill_rate * (now - self.timestamp) delta = self.fill_rate * (now - self.timestamp)
self._tokens = min(self.capacity, self._tokens + delta) self._tokens = min(self.capacity, self._tokens + delta)
self.timestamp = now self.timestamp = now
return self._tokens return self._tokens
def add_commands(d): def add_commands(d):
def dec(cls): def dec(cls):
for c in d: for c in d:
def func(x): def func(x):
def gen(self, *a): def gen(self, *a):
self.send(x.upper(), *a) self.send(x.upper(), *a)
return gen return gen
setattr(cls, c, func(c)) setattr(cls, c, func(c))
return cls return cls
return dec return dec
@add_commands(("join", @add_commands(("join",
"mode", "mode",
"nick", "nick",
"who")) "who"))
class IRCClient(object): class IRCClient(object):
""" IRC Client class. This handles one connection to a server. """ IRC Client class. This handles one connection to a server.
This can be used either with or without IRCApp ( see connect() docs ) This can be used either with or without IRCApp ( see connect() docs )
""" """
def __init__(self, cmd_handler, **kwargs): def __init__(self, cmd_handler, **kwargs):
""" the first argument should be an object with attributes/methods named """ the first argument should be an object with attributes/methods named
as the irc commands. You may subclass from one of the classes in as the irc commands. You may subclass from one of the classes in
oyoyo.cmdhandler for convenience but it is not required. The oyoyo.cmdhandler for convenience but it is not required. The
methods should have arguments (prefix, args). prefix is methods should have arguments (prefix, args). prefix is
normally the sender of the command. args is a list of arguments. normally the sender of the command. args is a list of arguments.
Its recommened you subclass oyoyo.cmdhandler.DefaultCommandHandler, Its recommened you subclass oyoyo.cmdhandler.DefaultCommandHandler,
this class provides defaults for callbacks that are required for this class provides defaults for callbacks that are required for
normal IRC operation. normal IRC operation.
all other arguments should be keyword arguments. The most commonly all other arguments should be keyword arguments. The most commonly
used will be nick, host and port. You can also specify an "on connect" used will be nick, host and port. You can also specify an "on connect"
callback. ( check the source for others ) 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. 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.nickname = ""
self.hostmask = "" self.hostmask = ""
self.ident = "" self.ident = ""
self.real_name = "" self.real_name = ""
self.host = None self.host = None
self.port = None self.port = None
self.password = "" self.password = ""
self.authname = "" self.authname = ""
self.connect_cb = None self.connect_cb = None
self.blocking = True self.blocking = True
self.lock = threading.RLock() self.lock = threading.RLock()
self.tokenbucket = TokenBucket(28, 1.73) self.tokenbucket = TokenBucket(28, 1.73)
self.__dict__.update(kwargs) self.__dict__.update(kwargs)
self.command_handler = cmd_handler self.command_handler = cmd_handler
self._end = 0 self._end = 0
def send(self, *args, **kwargs): def send(self, *args, **kwargs):
""" send a message to the connected server. all arguments are joined """ 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)
>>> cli.send("JOIN", some_room) >>> cli.send("JOIN", some_room)
In python 2, all args must be of type str or unicode, *BUT* if they are In python 2, all args must be of type str or unicode, *BUT* if they are
unicode they will be converted to str with the encoding specified by unicode they will be converted to str with the encoding specified by
the 'encoding' keyword argument (default 'utf8'). the 'encoding' keyword argument (default 'utf8').
In python 3, all args must be of type str or bytes, *BUT* if they are 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 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: with self.lock:
# Convert all args to bytes if not already # Convert all args to bytes if not already
encoding = kwargs.get('encoding') or 'utf_8' encoding = kwargs.get('encoding') or 'utf_8'
bargs = [] bargs = []
for i,arg in enumerate(args): for i,arg in enumerate(args):
if isinstance(arg, str): if isinstance(arg, str):
bargs.append(bytes(arg, encoding)) bargs.append(bytes(arg, encoding))
elif isinstance(arg, bytes): elif isinstance(arg, bytes):
bargs.append(arg) bargs.append(arg)
elif arg is None: elif arg is None:
continue continue
else: else:
raise Exception(('Refusing to send arg at index {1} of the args from '+ raise Exception(('Refusing to send arg at index {1} of the args from '+
'provided: {0}').format(repr([(type(arg), arg) 'provided: {0}').format(repr([(type(arg), arg)
for arg in args]), i)) for arg in args]), i))
msg = bytes(" ", "utf_8").join(bargs) msg = bytes(" ", "utf_8").join(bargs)
logging.info('---> send "{0}"'.format(msg)) logging.info('---> send "{0}"'.format(msg))
while not self.tokenbucket.consume(1): while not self.tokenbucket.consume(1):
time.sleep(0.3) time.sleep(0.3)
self.socket.send(msg + bytes("\r\n", "utf_8")) self.socket.send(msg + bytes("\r\n", "utf_8"))
def connect(self): def connect(self):
""" initiates the connection to the server set in self.host:self.port """ initiates the connection to the server set in self.host:self.port
and returns a generator object. and returns a generator object.
>>> cli = IRCClient(my_handler, host="irc.freenode.net", port=6667) >>> cli = IRCClient(my_handler, host="irc.freenode.net", port=6667)
>>> g = cli.connect() >>> g = cli.connect()
>>> while 1: >>> while 1:
... next(g) ... next(g)
""" """
try: try:
logging.info('connecting to {0}:{1}'.format(self.host, self.port)) logging.info('connecting to {0}:{1}'.format(self.host, self.port))
retries = 0 retries = 0
while True: while True:
try: try:
self.socket.connect(("{0}".format(self.host), self.port)) self.socket.connect(("{0}".format(self.host), self.port))
break break
except socket.error as e: except socket.error as e:
retries += 1 retries += 1
logging.warning('Error: {0}'.format(e)) logging.warning('Error: {0}'.format(e))
if retries > 3: if retries > 3:
break break
if not self.blocking: if not self.blocking:
self.socket.setblocking(0) self.socket.setblocking(0)
self.send("PASS {0}:{1}".format(self.authname if self.authname else self.nickname, self.send("PASS {0}:{1}".format(self.authname if self.authname else self.nickname,
self.password if self.password else "NOPASS")) self.password if self.password else "NOPASS"))
self.nick(self.nickname) self.nick(self.nickname)
self.user(self.nickname, self.real_name) self.user(self.nickname, self.real_name)
if self.connect_cb: if self.connect_cb:
self.connect_cb(self) self.connect_cb(self)
buffer = bytes() buffer = bytes()
while not self._end: while not self._end:
try: try:
buffer += self.socket.recv(1024) 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: if False and not self.blocking and e.errno == 11:
pass pass
else: else:
raise e raise e
else: else:
data = buffer.split(bytes("\n", "utf_8")) data = buffer.split(bytes("\n", "utf_8"))
buffer = data.pop() buffer = data.pop()
for el in data: for el in data:
prefix, command, args = parse_raw_irc_command(el) prefix, command, args = parse_raw_irc_command(el)
try: try:
enc = "utf8" 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: except UnicodeDecodeError:
enc = "latin1" enc = "latin1"
fargs = [arg.decode(enc) for arg in args if isinstance(arg,bytes)] fargs = [arg.decode(enc) for arg in args if isinstance(arg,bytes)]
logging.debug("processCommand ({2}){0}({1})".format(command, logging.debug("processCommand ({2}){0}({1})".format(command,
fargs, prefix)) fargs, prefix))
try: try:
largs = list(args) largs = list(args)
if prefix is not None: if prefix is not None:
prefix = prefix.decode(enc) prefix = prefix.decode(enc)
# for i,arg in enumerate(largs): # for i,arg in enumerate(largs):
# if arg is not None: largs[i] = arg.decode(enc) # if arg is not None: largs[i] = arg.decode(enc)
if command in self.command_handler: 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: elif "" in self.command_handler:
self.command_handler[""](self, prefix, command, *fargs) self.command_handler[""](self, prefix, command, *fargs)
except Exception as e: except Exception as e:
traceback.print_exc() traceback.print_exc()
raise e # ? raise e # ?
yield True yield True
finally: finally:
if self.socket: if self.socket:
logging.info('closing socket') logging.info('closing socket')
self.socket.close() self.socket.close()
yield False yield False
def msg(self, user, msg): def msg(self, user, msg):
for line in msg.split('\n'): 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: while line:
extra = "" extra = ""
if len(line) > maxchars: if len(line) > maxchars:
extra = line[maxchars:] extra = line[maxchars:]
line = line[:maxchars] line = line[:maxchars]
self.send("PRIVMSG", user, ":{0}".format(line)) self.send("PRIVMSG", user, ":{0}".format(line))
line = extra line = extra
privmsg = msg # Same thing privmsg = msg # Same thing
def notice(self, user, msg): def notice(self, user, msg):
for line in msg.split('\n'): 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: while line:
extra = "" extra = ""
if len(line) > maxchars: if len(line) > maxchars:
extra = line[maxchars:] extra = line[maxchars:]
line = line[:maxchars] line = line[:maxchars]
self.send("NOTICE", user, ":{0}".format(line)) self.send("NOTICE", user, ":{0}".format(line))
line = extra line = extra
def quit(self, msg=""): def quit(self, msg=""):
self.send("QUIT :{0}".format(msg)) self.send("QUIT :{0}".format(msg))
def part(self, chan, msg=""): def part(self, chan, msg=""):
self.send("PART {0} :{1}".format(chan, msg)) self.send("PART {0} :{1}".format(chan, msg))
def kick(self, chan, nick, msg=""): def kick(self, chan, nick, msg=""):
self.send("KICK", chan, nick, ":"+msg) self.send("KICK", chan, nick, ":"+msg)
def ns_identify(self, passwd): def ns_identify(self, passwd):
self.msg("NickServ", "IDENTIFY {0} {1}".format(self.nickname, passwd)) self.msg("NickServ", "IDENTIFY {0} {1}".format(self.nickname, passwd))
def ns_ghost(self): def ns_ghost(self):
self.msg("NickServ", "GHOST "+self.nickname) self.msg("NickServ", "GHOST "+self.nickname)
def ns_release(self): def ns_release(self):
self.msg("NickServ", "RELEASE "+self.nickname) self.msg("NickServ", "RELEASE "+self.nickname)
def user(self, uname, rname): def user(self, uname, rname):
self.send("USER", uname, self.host, self.host, self.send("USER", uname, self.host, self.host,
rname or uname) rname or uname)
def mainLoop(self): def mainLoop(self):
conn = self.connect() conn = self.connect()
while True: while True:
if not next(conn): if not next(conn):
print("Calling sys.exit()...") print("Calling sys.exit()...")
sys.exit() sys.exit()

416
oyoyo/ircevents.py

@ -1,208 +1,208 @@
# Copyright (c) 2011 Duncan Fordyce, Jimmy Cao # Copyright (c) 2011 Duncan Fordyce, Jimmy Cao
# Permission is hereby granted, free of charge, to any person obtaining a copy # Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal # of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights # in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is # copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions: # furnished to do so, subject to the following conditions:
# The above copyright notice and this permission notice shall be included in # The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software. # all copies or substantial portions of the Software.
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE. # THE SOFTWARE.
numeric_events = { numeric_events = {
b"001": "welcome", b"001": "welcome",
b"002": "yourhost", b"002": "yourhost",
b"003": "created", b"003": "created",
b"004": "myinfo", b"004": "myinfo",
b"005": "featurelist", # XXX b"005": "featurelist", # XXX
b"200": "tracelink", b"200": "tracelink",
b"201": "traceconnecting", b"201": "traceconnecting",
b"202": "tracehandshake", b"202": "tracehandshake",
b"203": "traceunknown", b"203": "traceunknown",
b"204": "traceoperator", b"204": "traceoperator",
b"205": "traceuser", b"205": "traceuser",
b"206": "traceserver", b"206": "traceserver",
b"207": "traceservice", b"207": "traceservice",
b"208": "tracenewtype", b"208": "tracenewtype",
b"209": "traceclass", b"209": "traceclass",
b"210": "tracereconnect", b"210": "tracereconnect",
b"211": "statslinkinfo", b"211": "statslinkinfo",
b"212": "statscommands", b"212": "statscommands",
b"213": "statscline", b"213": "statscline",
b"214": "statsnline", b"214": "statsnline",
b"215": "statsiline", b"215": "statsiline",
b"216": "statskline", b"216": "statskline",
b"217": "statsqline", b"217": "statsqline",
b"218": "statsyline", b"218": "statsyline",
b"219": "endofstats", b"219": "endofstats",
b"221": "umodeis", b"221": "umodeis",
b"231": "serviceinfo", b"231": "serviceinfo",
b"232": "endofservices", b"232": "endofservices",
b"233": "service", b"233": "service",
b"234": "servlist", b"234": "servlist",
b"235": "servlistend", b"235": "servlistend",
b"241": "statslline", b"241": "statslline",
b"242": "statsuptime", b"242": "statsuptime",
b"243": "statsoline", b"243": "statsoline",
b"244": "statshline", b"244": "statshline",
b"250": "luserconns", b"250": "luserconns",
b"251": "luserclient", b"251": "luserclient",
b"252": "luserop", b"252": "luserop",
b"253": "luserunknown", b"253": "luserunknown",
b"254": "luserchannels", b"254": "luserchannels",
b"255": "luserme", b"255": "luserme",
b"256": "adminme", b"256": "adminme",
b"257": "adminloc1", b"257": "adminloc1",
b"258": "adminloc2", b"258": "adminloc2",
b"259": "adminemail", b"259": "adminemail",
b"261": "tracelog", b"261": "tracelog",
b"262": "endoftrace", b"262": "endoftrace",
b"263": "tryagain", b"263": "tryagain",
b"265": "n_local", b"265": "n_local",
b"266": "n_global", b"266": "n_global",
b"300": "none", b"300": "none",
b"301": "away", b"301": "away",
b"302": "userhost", b"302": "userhost",
b"303": "ison", b"303": "ison",
b"305": "unaway", b"305": "unaway",
b"306": "nowaway", b"306": "nowaway",
b"311": "whoisuser", b"311": "whoisuser",
b"312": "whoisserver", b"312": "whoisserver",
b"313": "whoisoperator", b"313": "whoisoperator",
b"314": "whowasuser", b"314": "whowasuser",
b"315": "endofwho", b"315": "endofwho",
b"316": "whoischanop", b"316": "whoischanop",
b"317": "whoisidle", b"317": "whoisidle",
b"318": "endofwhois", b"318": "endofwhois",
b"319": "whoischannels", b"319": "whoischannels",
b"321": "liststart", b"321": "liststart",
b"322": "list", b"322": "list",
b"323": "listend", b"323": "listend",
b"324": "channelmodeis", b"324": "channelmodeis",
b"329": "channelcreate", b"329": "channelcreate",
b"331": "notopic", b"331": "notopic",
b"332": "currenttopic", b"332": "currenttopic",
b"333": "topicinfo", b"333": "topicinfo",
b"341": "inviting", b"341": "inviting",
b"342": "summoning", b"342": "summoning",
b"346": "invitelist", b"346": "invitelist",
b"347": "endofinvitelist", b"347": "endofinvitelist",
b"348": "exceptlist", b"348": "exceptlist",
b"349": "endofexceptlist", b"349": "endofexceptlist",
b"351": "version", b"351": "version",
b"352": "whoreply", b"352": "whoreply",
b"353": "namreply", b"353": "namreply",
b"361": "killdone", b"361": "killdone",
b"362": "closing", b"362": "closing",
b"363": "closeend", b"363": "closeend",
b"364": "links", b"364": "links",
b"365": "endoflinks", b"365": "endoflinks",
b"366": "endofnames", b"366": "endofnames",
b"367": "banlist", b"367": "banlist",
b"368": "endofbanlist", b"368": "endofbanlist",
b"369": "endofwhowas", b"369": "endofwhowas",
b"371": "info", b"371": "info",
b"372": "motd", b"372": "motd",
b"373": "infostart", b"373": "infostart",
b"374": "endofinfo", b"374": "endofinfo",
b"375": "motdstart", b"375": "motdstart",
b"376": "endofmotd", b"376": "endofmotd",
b"377": "motd2", # 1997-10-16 -- tkil b"377": "motd2", # 1997-10-16 -- tkil
b"381": "youreoper", b"381": "youreoper",
b"382": "rehashing", b"382": "rehashing",
b"384": "myportis", b"384": "myportis",
b"391": "time", b"391": "time",
b"392": "usersstart", b"392": "usersstart",
b"393": "users", b"393": "users",
b"394": "endofusers", b"394": "endofusers",
b"395": "nousers", b"395": "nousers",
b"396": "event_hosthidden", b"396": "event_hosthidden",
b"401": "nosuchnick", b"401": "nosuchnick",
b"402": "nosuchserver", b"402": "nosuchserver",
b"403": "nosuchchannel", b"403": "nosuchchannel",
b"404": "cannotsendtochan", b"404": "cannotsendtochan",
b"405": "toomanychannels", b"405": "toomanychannels",
b"406": "wasnosuchnick", b"406": "wasnosuchnick",
b"407": "toomanytargets", b"407": "toomanytargets",
b"409": "noorigin", b"409": "noorigin",
b"411": "norecipient", b"411": "norecipient",
b"412": "notexttosend", b"412": "notexttosend",
b"413": "notoplevel", b"413": "notoplevel",
b"414": "wildtoplevel", b"414": "wildtoplevel",
b"421": "unknowncommand", b"421": "unknowncommand",
b"422": "nomotd", b"422": "nomotd",
b"423": "noadmininfo", b"423": "noadmininfo",
b"424": "fileerror", b"424": "fileerror",
b"431": "nonicknamegiven", 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"433": "nicknameinuse",
b"436": "nickcollision", b"436": "nickcollision",
b"437": "unavailresource", # "Nick temporally unavailable" b"437": "unavailresource", # "Nick temporally unavailable"
b"441": "usernotinchannel", b"441": "usernotinchannel",
b"442": "notonchannel", b"442": "notonchannel",
b"443": "useronchannel", b"443": "useronchannel",
b"444": "nologin", b"444": "nologin",
b"445": "summondisabled", b"445": "summondisabled",
b"446": "usersdisabled", b"446": "usersdisabled",
b"451": "notregistered", b"451": "notregistered",
b"461": "needmoreparams", b"461": "needmoreparams",
b"462": "alreadyregistered", b"462": "alreadyregistered",
b"463": "nopermforhost", b"463": "nopermforhost",
b"464": "passwdmismatch", b"464": "passwdmismatch",
b"465": "yourebannedcreep", # I love this one... b"465": "yourebannedcreep", # I love this one...
b"466": "youwillbebanned", b"466": "youwillbebanned",
b"467": "keyset", b"467": "keyset",
b"471": "channelisfull", b"471": "channelisfull",
b"472": "unknownmode", b"472": "unknownmode",
b"473": "inviteonlychan", b"473": "inviteonlychan",
b"474": "bannedfromchan", b"474": "bannedfromchan",
b"475": "badchannelkey", b"475": "badchannelkey",
b"476": "badchanmask", b"476": "badchanmask",
b"477": "nochanmodes", # "Channel doesn't support modes" b"477": "nochanmodes", # "Channel doesn't support modes"
b"478": "banlistfull", b"478": "banlistfull",
b"481": "noprivileges", b"481": "noprivileges",
b"482": "chanoprivsneeded", b"482": "chanoprivsneeded",
b"483": "cantkillserver", b"483": "cantkillserver",
b"484": "restricted", # Connection is restricted b"484": "restricted", # Connection is restricted
b"485": "uniqopprivsneeded", b"485": "uniqopprivsneeded",
b"491": "nooperhost", b"491": "nooperhost",
b"492": "noservicehost", b"492": "noservicehost",
b"501": "umodeunknownflag", b"501": "umodeunknownflag",
b"502": "usersdontmatch", b"502": "usersdontmatch",
} }
generated_events = [ generated_events = [
# Generated events # Generated events
"dcc_connect", "dcc_connect",
"dcc_disconnect", "dcc_disconnect",
"dccmsg", "dccmsg",
"disconnect", "disconnect",
"ctcp", "ctcp",
"ctcpreply", "ctcpreply",
] ]
protocol_events = [ protocol_events = [
# IRC protocol events # IRC protocol events
"error", "error",
"join", "join",
"kick", "kick",
"mode", "mode",
"part", "part",
"ping", "ping",
"privmsg", "privmsg",
"privnotice", "privnotice",
"pubmsg", "pubmsg",
"pubnotice", "pubnotice",
"quit", "quit",
"invite", "invite",
"pong", "pong",
] ]
all_events = generated_events + protocol_events + list(numeric_events.values()) all_events = generated_events + protocol_events + list(numeric_events.values())

541
var.py

@ -1,269 +1,272 @@
PING_WAIT = 300 # Seconds PING_WAIT = 300 # Seconds
MINIMUM_WAIT = 60 MINIMUM_WAIT = 60
EXTRA_WAIT = 20 EXTRA_WAIT = 20
MAXIMUM_WAITED = 2 # limit for amount of !wait's MAXIMUM_WAITED = 2 # limit for amount of !wait's
STATS_RATE_LIMIT = 15 STATS_RATE_LIMIT = 15
VOTES_RATE_LIMIT = 15 VOTES_RATE_LIMIT = 15
ADMINS_RATE_LIMIT = 300 ADMINS_RATE_LIMIT = 300
SHOTS_MULTIPLIER = .12 # ceil(shots_multiplier * len_players) = bullets given SHOTS_MULTIPLIER = .12 # ceil(shots_multiplier * len_players) = bullets given
MAX_PLAYERS = 30 MAX_PLAYERS = 30
DRUNK_SHOTS_MULTIPLIER = 3 DRUNK_SHOTS_MULTIPLIER = 3
NIGHT_TIME_LIMIT = 120 NIGHT_TIME_LIMIT = 120
DAY_TIME_LIMIT_WARN = 780 DAY_TIME_LIMIT_WARN = 780
DAY_TIME_LIMIT_CHANGE = 120 # After DAY_TIME_LIMIT_WARN has passed DAY_TIME_LIMIT_CHANGE = 120 # After DAY_TIME_LIMIT_WARN has passed
START_WITH_DAY = False START_WITH_DAY = False
KILL_IDLE_TIME = 300 KILL_IDLE_TIME = 300
WARN_IDLE_TIME = 180 WARN_IDLE_TIME = 180
PART_GRACE_TIME = 7 PART_GRACE_TIME = 7
QUIT_GRACE_TIME = 30 QUIT_GRACE_TIME = 30
LOG_FILENAME = "" LOG_FILENAME = ""
BARE_LOG_FILENAME = "barelog.txt" BARE_LOG_FILENAME = "barelog.txt"
# HIT MISS SUICIDE # HIT MISS SUICIDE
GUN_CHANCES = ( 5/7 , 1/7 , 1/7 ) GUN_CHANCES = ( 5/7 , 1/7 , 1/7 )
DRUNK_GUN_CHANCES = ( 2/7 , 4/7 , 1/7 ) DRUNK_GUN_CHANCES = ( 2/7 , 4/7 , 1/7 )
MANSLAUGHTER_CHANCE = 1/5 # ACCIDENTAL HEADSHOT (FATAL) MANSLAUGHTER_CHANCE = 1/5 # ACCIDENTAL HEADSHOT (FATAL)
GUNNER_KILLS_WOLF_AT_NIGHT_CHANCE = 0 GUNNER_KILLS_WOLF_AT_NIGHT_CHANCE = 0
GUARDIAN_ANGEL_DIES_CHANCE = 1/2 GUARDIAN_ANGEL_DIES_CHANCE = 1/2
DETECTIVE_REVEALED_CHANCE = 2/5 DETECTIVE_REVEALED_CHANCE = 2/5
################################################################################################################# #################################################################################################################
# ROLE INDEX: PLAYERS SEER WOLF CURSED DRUNK HARLOT TRAITOR GUNNER CROW ANGEL DETECTIVE ## # 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 ), ## ROLES_GUIDE = { 4 : ( 1 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ), ##
6 : ( 1 , 1 , 1 , 1 , 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 ), ## 8 : ( 1 , 2 , 1 , 1 , 1 , 0 , 0 , 0 , 0 , 0 ), ##
10 : ( 1 , 2 , 1 , 1 , 1 , 1 , 1 , 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 ), ## 11 : ( 1 , 2 , 1 , 1 , 1 , 1 , 1 , 0 , 1 , 0 ), ##
15 : ( 1 , 3 , 1 , 1 , 1 , 1 , 1 , 0 , 1 , 1 ), ## 15 : ( 1 , 3 , 1 , 1 , 1 , 1 , 1 , 0 , 1 , 1 ), ##
22 : ( 1 , 4 , 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 ), ## 29 : ( 1 , 5 , 1 , 1 , 1 , 1 , 1 , 0 , 1 , 1 ), ##
None : ( 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 )} ## None : ( 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 )} ##
################################################################################################################# #################################################################################################################
# Notes: ## # Notes: ##
################################################################################################################# #################################################################################################################
GAME_MODES = {} GAME_MODES = {}
AWAY = [] # cloaks of people who are away. AWAY = [] # cloaks of people who are away.
ROLE_INDICES = {0 : "seer", ROLE_INDICES = {0 : "seer",
1 : "wolf", 1 : "wolf",
2 : "cursed villager", 2 : "cursed villager",
3 : "village drunk", 3 : "village drunk",
4 : "harlot", 4 : "harlot",
5 : "traitor", 5 : "traitor",
6 : "gunner", 6 : "gunner",
7 : "werecrow", 7 : "werecrow",
8 : "guardian angel", 8 : "guardian angel",
9 : "detective"} 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.", NO_VICTIMS_MESSAGES = ("The body of a young penguin pet is found.",
"A pool of blood and wolf paw prints are found.", "A pool of blood and wolf paw prints are found.",
"Traces of wolf fur 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.", 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.", "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.", "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.") "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.")
RULES = ("#wolfgame channel rules: 1) Be nice to others. 2) Do not share information "+ RULES = ("#wolfgame 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"+ "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 "+ "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. "+ "family-friendly. 7) Do not paste PM's from the bot during the game. "+
"8) Use common sense. 9) Waiting for timeouts is discouraged.") "8) Use common sense. 9) Waiting for timeouts is discouraged.")
is_role = lambda plyr, rol: rol in ROLES and plyr in ROLES[rol] is_role = lambda plyr, rol: rol in ROLES and plyr in ROLES[rol]
def plural(role): def plural(role):
if role == "wolf": return "wolves" if role == "wolf": return "wolves"
elif role == "person": return "people" elif role == "person": return "people"
else: return role + "s" else: return role + "s"
def list_players(): def list_players():
pl = [] pl = []
for x in ROLES.values(): for x in ROLES.values():
pl.extend(x) pl.extend(x)
return pl return pl
def list_players_and_roles(): def list_players_and_roles():
plr = {} plr = {}
for x in ROLES.keys(): for x in ROLES.keys():
for p in ROLES[x]: for p in ROLES[x]:
plr[p] = x plr[p] = x
return plr return plr
get_role = lambda plyr: list_players_and_roles()[plyr] get_role = lambda plyr: list_players_and_roles()[plyr]
def del_player(pname): def del_player(pname):
prole = get_role(pname) prole = get_role(pname)
ROLES[prole].remove(pname) ROLES[prole].remove(pname)
class InvalidModeException(Exception): pass class InvalidModeException(Exception): pass
def game_mode(name): def game_mode(name):
def decor(c): def decor(c):
GAME_MODES[name] = c GAME_MODES[name] = c
return c return c
return decor return decor
CHANGEABLE_ROLES = { "seers" : INDEX_OF_ROLE["seer"], CHANGEABLE_ROLES = { "seers" : INDEX_OF_ROLE["seer"],
"wolves" : INDEX_OF_ROLE["wolf"], "wolves" : INDEX_OF_ROLE["wolf"],
"cursed villager" : INDEX_OF_ROLE["cursed villager"], "cursed villager" : INDEX_OF_ROLE["cursed villager"],
"drunks" : INDEX_OF_ROLE["village drunk"], "drunks" : INDEX_OF_ROLE["village drunk"],
"harlots" : INDEX_OF_ROLE["harlot"], "harlots" : INDEX_OF_ROLE["harlot"],
"traitors" : INDEX_OF_ROLE["traitor"], "traitors" : INDEX_OF_ROLE["traitor"],
"gunners" : INDEX_OF_ROLE["gunner"], "gunners" : INDEX_OF_ROLE["gunner"],
"werecrows" : INDEX_OF_ROLE["werecrow"], "werecrows" : INDEX_OF_ROLE["werecrow"],
"angels" : INDEX_OF_ROLE["guardian angel"], "angels" : INDEX_OF_ROLE["guardian angel"],
"detectives" : INDEX_OF_ROLE["detective"]} "detectives" : INDEX_OF_ROLE["detective"]}
@game_mode("normal") @game_mode("normal")
class Normal(object): class Normal(object):
pass pass
# Example !game roles=wolves:1,seers:0 # Example !game roles=wolves:1,seers:0
# TODO: implement game modes # TODO: implement game modes
@game_mode("roles") @game_mode("roles")
class ChangedRolesMode(object): class ChangedRolesMode(object):
def __init__(self, arg): def __init__(self, arg):
self.ROLES_GUIDE = ROLES_GUIDE.copy() self.ROLES_GUIDE = ROLES_GUIDE.copy()
lx = list(ROLES_GUIDE[None]) lx = list(ROLES_GUIDE[None])
pairs = arg.split(",") pairs = arg.split(",")
pl = list_players() pl = list_players()
if not pairs: if not pairs:
raise InvalidModeException("Invalid syntax for mode roles.") raise InvalidModeException("Invalid syntax for mode roles.")
for pair in pairs: for pair in pairs:
change = pair.split(":") change = pair.split(":")
if len(change) != 2: if len(change) != 2:
raise InvalidModeException("Invalid syntax for mode roles.") raise InvalidModeException("Invalid syntax for mode roles.")
role, num = change role, num = change
try: try:
num = int(num) num = int(num)
try: try:
lx[CHANGEABLE_ROLES[role.lower()]] = num lx[CHANGEABLE_ROLES[role.lower()]] = num
except KeyError: except KeyError:
raise InvalidModeException(("The role \u0002{0}\u0002 "+ raise InvalidModeException(("The role \u0002{0}\u0002 "+
"is not valid.").format(role)) "is not valid.").format(role))
except ValueError: 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(): for k in ROLES_GUIDE.keys():
self.ROLES_GUIDE[k] = tuple(lx) self.ROLES_GUIDE[k] = tuple(lx)
# Persistence # Persistence
# Load saved settings # Load saved settings
import sqlite3 import sqlite3
import os import os
conn = sqlite3.connect("data.sqlite3", check_same_thread = False) conn = sqlite3.connect("data.sqlite3", check_same_thread = False)
with conn: with conn:
c = conn.cursor() c = conn.cursor()
c.execute('CREATE TABLE IF NOT EXISTS away (nick TEXT)') c.execute('CREATE TABLE IF NOT EXISTS away (nick TEXT)')
c.execute('SELECT * FROM away') c.execute('SELECT * FROM away')
for row in c: for row in c:
AWAY.append(row[0]) AWAY.append(row[0])
# populate the roles table # populate the roles table
c.execute('DROP TABLE IF EXISTS roles') 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("INSERT OR REPLACE INTO roles (role) VALUES (?)", (x,))
c.execute(('CREATE TABLE IF NOT EXISTS rolestats (playerid INTEGER, roleid INTEGER, '+ c.execute(('CREATE TABLE IF NOT EXISTS rolestats (playerid INTEGER, roleid INTEGER, '+
'teamwins SMALLINT, individualwins SMALLINT, totalgames SMALLINT, '+ 'teamwins SMALLINT, individualwins SMALLINT, totalgames SMALLINT, '+
'UNIQUE(playerid, roleid))')) 'UNIQUE(playerid, roleid))'))
# create the players table # create the players table
c.execute("CREATE TABLE IF NOT EXISTS players (id INTEGER PRIMARY KEY AUTOINCREMENT, nick TEXT, cloak TEXT, "+ c.execute("CREATE TABLE IF NOT EXISTS players (id INTEGER PRIMARY KEY AUTOINCREMENT, nick TEXT, cloak TEXT, "+
"UNIQUE(nick, cloak))") "UNIQUE(nick, cloak))")
# create nick change table # create nick change table
c.execute("CREATE TABLE IF NOT EXISTS nick_changes (old INTEGER, new INTEGER)") c.execute("CREATE TABLE IF NOT EXISTS nick_changes (old INTEGER, new INTEGER)")
def remove_away(clk): def remove_away(clk):
with conn: with conn:
c.execute('DELETE from away where nick=?', (clk,)) c.execute('DELETE from away where nick=?', (clk,))
def add_away(clk): def add_away(clk):
with conn: with conn:
c.execute('INSERT into away VALUES (?)', (clk,)) c.execute('INSERT into away VALUES (?)', (clk,))
def add_player_record(nick, cloak): def add_player_record(nick, cloak):
with conn: with conn:
c.execute('INSERT OR IGNORE INTO players (nick, cloak) VALUES (?,?)', (nick, cloak)) c.execute('INSERT OR IGNORE INTO players (nick, cloak) VALUES (?,?)', (nick, cloak))
def record_nick_change(from_nick, to_nick, cloak): def record_nick_change(from_nick, to_nick, cloak):
with conn: with conn:
c.execute('SELECT id FROM players WHERE nick=? AND cloak=?', (from_nick, cloak)) c.execute('SELECT id FROM players WHERE nick=? AND cloak=?', (from_nick, cloak))
row = c.fetchone() row = c.fetchone()
if not row: if not row:
return # No records for this player return # No records for this player
old_plid = row[0] old_plid = row[0]
c.execute('INSERT OR IGNORE INTO players (nick, cloak) VALUES (?,?)', (to_nick, cloak)) c.execute('INSERT OR IGNORE INTO players (nick, cloak) VALUES (?,?)', (to_nick, cloak))
# create a new entry in the players table for this nick # create a new entry in the players table for this nick
c.execute('SELECT id FROM players WHERE nick=? AND cloak=?', (to_nick, cloak)) c.execute('SELECT id FROM players WHERE nick=? AND cloak=?', (to_nick, cloak))
new_plid = c.fetchone()[0] new_plid = c.fetchone()[0]
c.execute('SELECT * FROM nick_changes WHERE old=? AND new=?', (new_plid, old_plid)) c.execute('SELECT * FROM nick_changes WHERE old=? AND new=?', (new_plid, old_plid))
if not c.fetchone(): # not recorded yet if not c.fetchone(): # not recorded yet
c.execute('INSERT OR IGNORE INTO nick_changes (old, new) VALUES (?, ?)', (old_plid, new_plid)) c.execute('INSERT OR IGNORE INTO nick_changes (old, new) VALUES (?, ?)', (old_plid, new_plid))
def update_role_stats(nick, clk, role, won, iwon): def update_role_stats(nick, clk, role, won, iwon):
with conn: with conn:
wins, iwins, totalgames = 0, 0, 0 wins, iwins, totalgames = 0, 0, 0
c.execute('SELECT id FROM players WHERE nick=? AND cloak=?', (nick, clk)) c.execute('SELECT id FROM players WHERE nick=? AND cloak=?', (nick, clk))
row = c.fetchone() row = c.fetchone()
if row: if row:
plid = row[0] plid = row[0]
else: else:
c.execute('INSERT INTO players (nick, cloak) VALUES (?,?)', (nick, clk)) c.execute('INSERT INTO players (nick, cloak) VALUES (?,?)', (nick, clk))
c.execute('SELECT id FROM players WHERE nick=? AND cloak=?', (nick, clk)) c.execute('SELECT id FROM players WHERE nick=? AND cloak=?', (nick, clk))
plid = c.fetchone()[0] plid = c.fetchone()[0]
c.execute('SELECT id FROM roles WHERE role=?', (role,)) c.execute('SELECT id FROM roles WHERE role=?', (role,))
rid = c.fetchone()[0] rid = c.fetchone()[0]
c.execute(("SELECT teamwins, individualwins, totalgames FROM rolestats "+ c.execute(("SELECT teamwins, individualwins, totalgames FROM rolestats "+
"WHERE playerid=? AND roleid=?"), (plid, rid)) "WHERE playerid=? AND roleid=?"), (plid, rid))
row = c.fetchone() row = c.fetchone()
if row: if row:
wins, iwins, total = row wins, iwins, total = row
else: else:
wins, iwins, total = 0,0,0 wins, iwins, total = 0,0,0
if won: if won:
wins += 1 wins += 1
if iwon: if iwon:
iwins += 1 iwins += 1
total += 1 total += 1
c.execute("INSERT OR REPLACE INTO rolestats VALUES (?,?,?,?,?)", c.execute("INSERT OR REPLACE INTO rolestats VALUES (?,?,?,?,?)",
(plid, rid, wins, iwins, total)) (plid, rid, wins, iwins, total))

246
wolfbot.py

@ -1,123 +1,123 @@
#!/usr/bin/env python3.2 #!/usr/bin/env python3.2
# Copyright (c) 2011 Jimmy Cao # Copyright (c) 2011 Jimmy Cao
# Permission is hereby granted, free of charge, to any person obtaining a copy # Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal # of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights # in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is # copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions: # furnished to do so, subject to the following conditions:
# The above copyright notice and this permission notice shall be included in # The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software. # all copies or substantial portions of the Software.
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE. # THE SOFTWARE.
from oyoyo.client import IRCClient from oyoyo.client import IRCClient
from oyoyo.parse import parse_nick from oyoyo.parse import parse_nick
import logging import logging
import botconfig import botconfig
import wolfgame import wolfgame
import time import time
import traceback import traceback
class UTCFormatter(logging.Formatter): class UTCFormatter(logging.Formatter):
converter = time.gmtime converter = time.gmtime
def on_privmsg(cli, rawnick, chan, msg): def on_privmsg(cli, rawnick, chan, msg):
if chan != botconfig.NICK: #not a PM if chan != botconfig.NICK: #not a PM
if "" in wolfgame.COMMANDS.keys(): if "" in wolfgame.COMMANDS.keys():
for fn in wolfgame.COMMANDS[""]: for fn in wolfgame.COMMANDS[""]:
try: try:
fn(cli, rawnick, chan, msg) fn(cli, rawnick, chan, msg)
except Exception as e: except Exception as e:
if botconfig.DEBUG_MODE: if botconfig.DEBUG_MODE:
raise e raise e
else: else:
logging.error(traceback.format_exc()) 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. # Now that is always called first.
for x in wolfgame.COMMANDS.keys(): for x in wolfgame.COMMANDS.keys():
if x and msg.lower().startswith(botconfig.CMD_CHAR+x): if x and msg.lower().startswith(botconfig.CMD_CHAR+x):
h = msg[len(x)+1:] h = msg[len(x)+1:]
if not h or h[0] == " " or not x: if not h or h[0] == " " or not x:
for fn in wolfgame.COMMANDS[x]: for fn in wolfgame.COMMANDS[x]:
try: try:
fn(cli, rawnick, chan, h.lstrip()) fn(cli, rawnick, chan, h.lstrip())
except Exception as e: except Exception as e:
if botconfig.DEBUG_MODE: if botconfig.DEBUG_MODE:
raise e raise e
else: else:
logging.error(traceback.format_exc()) 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: else:
for x in wolfgame.PM_COMMANDS.keys(): for x in wolfgame.PM_COMMANDS.keys():
if msg.lower().startswith(botconfig.CMD_CHAR+x): if msg.lower().startswith(botconfig.CMD_CHAR+x):
h = msg[len(x)+1:] h = msg[len(x)+1:]
elif not x or msg.lower().startswith(x): elif not x or msg.lower().startswith(x):
h = msg[len(x):] h = msg[len(x):]
else: else:
continue continue
if not h or h[0] == " " or not x: if not h or h[0] == " " or not x:
for fn in wolfgame.PM_COMMANDS[x]: for fn in wolfgame.PM_COMMANDS[x]:
try: try:
fn(cli, rawnick, h.lstrip()) fn(cli, rawnick, h.lstrip())
except Exception as e: except Exception as e:
if botconfig.DEBUG_MODE: if botconfig.DEBUG_MODE:
raise e raise e
else: else:
logging.error(traceback.format_exc()) 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): def __unhandled__(cli, prefix, cmd, *args):
if cmd in wolfgame.HOOKS.keys(): if cmd in wolfgame.HOOKS.keys():
largs = list(args) largs = list(args)
for i,arg in enumerate(largs): for i,arg in enumerate(largs):
if isinstance(arg, bytes): largs[i] = arg.decode('ascii') if isinstance(arg, bytes): largs[i] = arg.decode('ascii')
for fn in wolfgame.HOOKS[cmd]: for fn in wolfgame.HOOKS[cmd]:
try: try:
fn(cli, prefix, *largs) fn(cli, prefix, *largs)
except Exception as e: except Exception as e:
if botconfig.DEBUG_MODE: if botconfig.DEBUG_MODE:
raise e raise e
else: else:
logging.error(traceback.format_exc()) 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: else:
logging.debug('Unhandled command {0}({1})'.format(cmd, [arg.decode('utf_8') logging.debug('Unhandled command {0}({1})'.format(cmd, [arg.decode('utf_8')
for arg in args for arg in args
if isinstance(arg, bytes)])) if isinstance(arg, bytes)]))
def main(): def main():
if not botconfig.DEBUG_MODE: if not botconfig.DEBUG_MODE:
logging.basicConfig(filename='errors.log', filemode='a', level=logging.WARNING) logging.basicConfig(filename='errors.log', filemode='a', level=logging.WARNING)
formatter = UTCFormatter('[%(asctime)s] %(message)s', '%d/%b/%Y %H:%M:%S') formatter = UTCFormatter('[%(asctime)s] %(message)s', '%d/%b/%Y %H:%M:%S')
for handler in logging.getLogger().handlers: for handler in logging.getLogger().handlers:
handler.setFormatter(formatter) handler.setFormatter(formatter)
else: else:
logging.basicConfig(level=logging.DEBUG) logging.basicConfig(level=logging.DEBUG)
cli = IRCClient( cli = IRCClient(
{"privmsg":on_privmsg, {"privmsg":on_privmsg,
"":__unhandled__}, "":__unhandled__},
host=botconfig.HOST, host=botconfig.HOST,
port=botconfig.PORT, port=botconfig.PORT,
authname=botconfig.USERNAME, authname=botconfig.USERNAME,
password=botconfig.PASS, password=botconfig.PASS,
nickname=botconfig.NICK, nickname=botconfig.NICK,
connect_cb=wolfgame.connect_callback connect_cb=wolfgame.connect_callback
) )
cli.mainLoop() cli.mainLoop()
if __name__ == "__main__": if __name__ == "__main__":
try: try:
main() main()
except Exception: except Exception:
logging.error(traceback.format_exc()) logging.error(traceback.format_exc())

5452
wolfgame.py

File diff suppressed because it is too large Load Diff

74
wolfgamelogger.py

@ -1,38 +1,38 @@
import botconfig import botconfig
from datetime import datetime from datetime import datetime
class WolfgameLogger(object): class WolfgameLogger(object):
def __init__(self, outfile, boutfile): def __init__(self, outfile, boutfile):
self.outfile = outfile self.outfile = outfile
self.boutfile = boutfile self.boutfile = boutfile
self.logged = "" self.logged = ""
self.barelogged = "" self.barelogged = ""
def log(self, message): def log(self, message):
self.logged += datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S ") + message + "\n" self.logged += datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S ") + message + "\n"
def logBare(self, *args): def logBare(self, *args):
self.barelogged += datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S ") + " ".join(args) + "\n" self.barelogged += datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S ") + " ".join(args) + "\n"
def logChannelMessage(self, who, message): 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): 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): 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): def saveToFile(self):
if self.outfile: if self.outfile:
with open(self.outfile, "a") as lf: with open(self.outfile, "a") as lf:
lf.write(self.logged) lf.write(self.logged)
if self.boutfile: if self.boutfile:
with open(self.boutfile, "a") as bl: with open(self.boutfile, "a") as bl:
bl.write(self.barelogged) bl.write(self.barelogged)
self.logged = "" self.logged = ""
self.barelogged = "" self.barelogged = ""
Loading…
Cancel
Save