diff --git a/oyoyo/client.py b/oyoyo/client.py index af1ea99..ddd388c 100644 --- a/oyoyo/client.py +++ b/oyoyo/client.py @@ -26,7 +26,6 @@ import os import traceback from oyoyo.parse import * -from oyoyo import helpers from oyoyo.cmdhandler import CommandError import collections @@ -42,20 +41,19 @@ class IRCClientError(Exception): def add_commands(d): def dec(cls): - for key in d: + for c in d: def func(x): def gen(self, *a): - self.send(x, *a) + self.send(x.upper(), *a) return gen - setattr(cls, d[key], func(key)) + setattr(cls, c, func(c)) return cls return dec -@add_commands({"JOIN": "join", - "MODE": "mode", - "USER": "user", - "NICK": "nick", - "NOTICE": "notice", - "PART": "part"}) +@add_commands(("join", + "mode", + "nick", + "notice", + "part")) class IRCClient: """ IRC Client class. This handles one connection to a server. This can be used either with or without IRCApp ( see connect() docs ) @@ -84,7 +82,7 @@ class IRCClient: ... print "%s said %s" % (prefix, args[1]) ... >>> def connect_callback(c): - ... helpers.join(c, '#myroom') + ... c.join('#myroom') ... >>> cli = IRCClient(My_Handler, ... host="irc.freenode.net", @@ -98,8 +96,8 @@ class IRCClient: ... """ self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - self.nick = None - self.real_name = None + self.nickname = "" + self.real_name = "" self.host = None self.port = None self.connect_cb = None @@ -132,6 +130,8 @@ class IRCClient: bargs.append(bytes(arg, encoding)) elif isinstance(arg, bytes): bargs.append(arg) + elif arg is None: + continue else: raise IRCClientError('Refusing to send one of the args from provided: %s' % repr([(type(arg), arg) for arg in args])) @@ -157,8 +157,8 @@ class IRCClient: if not self.blocking: self.socket.setblocking(0) - helpers.nick(self, self.nick) - helpers.user(self, self.nick, self.real_name) + self.nick(self.nickname) + self.user(self.nickname, self.real_name) if self.connect_cb: self.connect_cb(self) @@ -201,6 +201,9 @@ class IRCClient: self.send("QUIT :" + msg) def identify(self, passwd, authuser="NickServ"): self.msg(authuser, "IDENTIFY {0}".format(passwd)) + def user(self, uname, rname): + self.send("USER", uname, self.host, self.host, + rname or uname) class IRCApp: """ This class manages several IRCClient instances without the use of threads. diff --git a/oyoyo/cmdhandler.py b/oyoyo/cmdhandler.py index 5f1921b..025a3b0 100644 --- a/oyoyo/cmdhandler.py +++ b/oyoyo/cmdhandler.py @@ -20,7 +20,6 @@ import logging import sys import traceback -from oyoyo import helpers from oyoyo.parse import parse_nick # Python < 3 compatibility @@ -150,74 +149,3 @@ class DefaultBotCommandHandler(CommandHandler): if (not m.startswith('_') and not hasattr(getattr(obj, m), 'protected'))] - def help(self, sender, dest, arg=None): - """list all available commands or get help on a specific command""" - logging.info('help sender=%s dest=%s arg=%s' % (sender, dest, arg)) - if not arg: - commands = self.getVisibleCommands() - commands.sort() - helpers.msg(self.client, dest, - "available commands: %s" % " ".join(commands)) - else: - try: - f = self.get(arg) - except CommandError as e: - helpers.msg(self.client, dest, str(e)) - return - - doc = f.__doc__.strip() if f.__doc__ else "No help available" - - if not inspect.ismethod(f): - subcommands = self.getVisibleCommands(f) - if subcommands: - doc += " [sub commands: %s]" % " ".join(subcommands) - - helpers.msg(self.client, dest, "%s: %s" % (arg, doc)) - - -class BotCommandHandler(DefaultCommandHandler): - """ complete command handler for bots """ - - def __init__(self, client, command_handler): - DefaultCommandHandler.__init__(self, client) - self.command_handler = command_handler - - def privmsg(self, prefix, dest, msg): - self.tryBotCommand(prefix, dest, msg) - - @protected - def tryBotCommand(self, prefix, dest, msg): - """ tests a command to see if its a command for the bot, returns True - and calls self.processBotCommand(cmd, sender) if its is. - """ - - logging.debug("tryBotCommand('%s' '%s' '%s')" % (prefix, dest, msg)) - - if dest == self.client.nick: - dest = parse_nick(prefix)[0] - elif msg.startswith(self.client.nick): - msg = msg[len(self.client.nick)+1:] - else: - return False - - msg = msg.strip() - - parts = msg.split(' ', 1) - command = parts[0] - arg = parts[1:] - - try: - self.command_handler.run(command, prefix, dest, *arg) - except CommandError as e: - helpers.msg(self.client, dest, str(e)) - return True - - - - - - - - - - diff --git a/oyoyo/examplebot.py b/oyoyo/examplebot.py deleted file mode 100644 index b718bc9..0000000 --- a/oyoyo/examplebot.py +++ /dev/null @@ -1,45 +0,0 @@ -#!/usr/bin/python -"""Example bot for oyoyo that responds to !say""" - -import logging -import re - -from oyoyo.client import IRCClient -from oyoyo.cmdhandler import DefaultCommandHandler -from oyoyo import helpers - - -HOST = 'irc.freenode.net' -PORT = 6667 -NICK = 'oyoyo-example' -CHANNEL = '#oyoyo-test' - - -class MyHandler(DefaultCommandHandler): - def privmsg(self, nick, chan, msg): - msg = msg.decode() - match = re.match('\!say (.*)', msg) - if match: - to_say = match.group(1).strip() - print(('Saying, "%s"' % to_say)) - helpers.msg(self.client, chan, to_say) - - -def connect_cb(cli): - helpers.join(cli, CHANNEL) - - -def main(): - logging.basicConfig(level=logging.DEBUG) - - cli = IRCClient(MyHandler, host=HOST, port=PORT, nick=NICK, - connect_cb=connect_cb) - conn = cli.connect() - - while True: - next(conn) ## python 2 - # next(conn) ## python 3 - - -if __name__ == '__main__': - main() diff --git a/oyoyo/helpers.py b/oyoyo/helpers.py deleted file mode 100644 index cb4c70d..0000000 --- a/oyoyo/helpers.py +++ /dev/null @@ -1,90 +0,0 @@ -# Copyright (c) 2008 Duncan Fordyce -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# 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 -# THE SOFTWARE. - -""" contains helper functions for common irc commands """ - -import random - -def msg(cli, user, msg): - for line in msg.split('\n'): - cli.send("PRIVMSG", user, ":%s" % line) - -def msgrandom(cli, choices, dest, user=None): - o = "%s: " % user if user else "" - o += random.choice(choices) - msg(cli, dest, o) - -def _makeMsgRandomFunc(choices): - def func(cli, dest, user=None): - msgrandom(cli, choices, dest, user) - return func - -msgYes = _makeMsgRandomFunc(['yes', 'alright', 'ok']) -msgOK = _makeMsgRandomFunc(['ok', 'done']) -msgNo = _makeMsgRandomFunc(['no', 'no-way']) - - -def ns(cli, *args): - msg(cli, "NickServ", " ".join(args)) - -def cs(cli, *args): - msg(cli, "ChanServ", " ".join(args)) - -def identify(cli, passwd, authuser="NickServ"): - msg(cli, authuser, "IDENTIFY %s" % passwd) - -def quit(cli, msg='gone'): - cli.send("QUIT :%s" % msg) - cli._end = 1 - -def mode(cli, chan, mod): - cli.send("MODE", chan, mod) - -def user(cli, username, realname=None): - cli.send("USER", username, cli.host, cli.host, - realname or username) - -_simple = ( - 'join', - 'part', - 'nick', - 'notice', -) -def _addsimple(): - import sys - def simplecmd(cmd_name): - def f(cli, *args): - cli.send(cmd_name, *args) - return f - m = sys.modules[__name__] - for t in _simple: - setattr(m, t, simplecmd(t.upper())) -_addsimple() - -def _addNumerics(): - import sys - from oyoyo import ircevents - def numericcmd(cmd_num, cmd_name): - def f(cli, *args): - cli.send(cmd_num, *args) - return f - m = sys.modules[__name__] - for num, name in ircevents.numeric_events.items(): - setattr(m, name, numericcmd(num, name)) - -_addNumerics() - diff --git a/vars.py b/vars.py index 558b00e..66c5030 100644 --- a/vars.py +++ b/vars.py @@ -1,4 +1,13 @@ GAME_STARTED = False ROLES = {"person" : []} ORIGINAL_ROLES = None -PHASE = "none" # "join", "day", or "night" \ No newline at end of file +PHASE = "none" # "join", "day", or "night" +GUNNERS = {} +MAX_SHOTS = 2 + +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" \ No newline at end of file diff --git a/wolfbot.py b/wolfbot.py index aa44fbc..b5c15d2 100644 --- a/wolfbot.py +++ b/wolfbot.py @@ -1,6 +1,5 @@ from oyoyo.client import IRCClient -from oyoyo.cmdhandler import DefaultCommandHandler -from oyoyo import helpers +from oyoyo.cmdhandler import DefaultCommandHandler, protected from oyoyo.parse import parse_nick import logging import botconfig @@ -53,12 +52,19 @@ class WolfBotHandler(DefaultCommandHandler): msg = msg.replace(x, "", 1) PM_COMMANDS[x](self.client, rawnick, msg.lstrip()) - def nick(self, fro, to): - print(fro, to) + @protected + def __unhandled__(self, cmd, *args): + if cmd in HOOKS.keys(): + largs = list(args) + for i,arg in enumerate(largs): + if arg: largs[i] = arg.decode('ascii') + HOOKS[cmd](*largs) + else: + logging.debug('unhandled command %s(%s)' % (cmd, args)) def main(): logging.basicConfig(level=logging.DEBUG) - cli = IRCClient(WolfBotHandler, host="irc.freenode.net", port=6667, nick="wolfbot2-alpha", + cli = IRCClient(WolfBotHandler, host="irc.freenode.net", port=6667, nickname=botconfig.NICK, connect_cb=connect_callback) conn = cli.connect() @@ -80,13 +86,47 @@ def reset_game(): def say(cli, rawnick, rest): # To be removed later cli.msg(botconfig.CHANNEL, "{0} says: {1}".format(parse_nick(rawnick)[0], rest)) + @cmd("!bye", pm=True) @cmd("!bye", pm=False) def forced_exit(cli, rawnick, *rest): # Admin Only if parse_nick(rawnick)[0] in botconfig.ADMINS: cli.quit("Forced quit from admin") raise SystemExit + +@cmd("!exec", pm=False) +def py(cli, rawnick, chan, rest): + if parse_nick(rawnick)[0] in botconfig.ADMINS: + exec(rest) + +@cmd("!ping", pm=False) +def pinger(cli, rawnick, chan, rest): + vars.PINGING = True + TO_PING = [] + + @hook("whoreply") + def on_whoreply(server, dunno, chan, dunno1, dunno2, dunno3, user, status, dunno4): + if not vars.PINGING: return + if user in (botconfig.NICK, parse_nick(rawnick)[0]): return # Don't ping self. + + if vars.PINGING and 'G' not in status and '+' not in status: + # TODO: check if the user has AWAY'D himself + TO_PING.append(user) + @hook("endofwho") + def do_ping(*args): + if not vars.PINGING: return + + chan = args[2] + cli.msg(chan, "PING! "+" ".join(TO_PING)) + vars.PINGING = False + + HOOKS.pop("whoreply") + HOOKS.pop("endofwho") + + cli.send("WHO "+chan) + + @cmd("!join", pm=False) def join(cli, rawnick, chan, rest): if vars.PHASE != "none": @@ -111,9 +151,28 @@ def stats(cli, rawnick, chan, rest): nick = parse_nick(rawnick)[0] pl = [] for x in vars.ROLES.values(): pl.extend(x) - cli.msg(chan, '{0}: {1} players: {2}'.format(nick, - len(pl), ", ".join(pl))) + if len(pl) > 1: + cli.msg(chan, '{0}: {1} players: {2}'.format(nick, + len(pl), ", ".join(pl))) + else: + cli.msg(chan, '{0}: 1 player: {1}'.format(nick, pl[0])) + msg = [] + for role in vars.ROLES.keys(): + num = len(vars.ROLES[role]) + if num > 1: + msg.append("{0} {1}".format(num, plural(role))) + else: + msg.append("{0} {1}".format(num, role)) + if len(msg) > 2: # More than 2 roles to say + msg[-1] = "and "+msg[-1]+"." + msg[0] = "{0}: There are ".format(nick) + msg[0] + cli.msg(chan, ", ".join(msg)) + elif len(msg) == 2: # 2 roles to say + cli.msg(chan, "{0}: There are ".format(nick) + msg[0], + "and", msg[1] + ".") + elif len(msg) == 1: + cli.msg(chan, "{0}: There is ".format(nick) + msg[0] + ".") # Game Logic Ends