Kills oyoyo
This commit is contained in:
parent
99ca795df5
commit
3855ef20f5
8 changed files with 6 additions and 860 deletions
9
irc.py
9
irc.py
|
@ -19,9 +19,6 @@ from dataobjs import PesterProfile
|
||||||
from generic import PesterList
|
from generic import PesterList
|
||||||
from version import _pcVersion
|
from version import _pcVersion
|
||||||
|
|
||||||
from oyoyo import services
|
|
||||||
from oyoyo.ircevents import numeric_events
|
|
||||||
|
|
||||||
import scripts.irc.outgoing
|
import scripts.irc.outgoing
|
||||||
|
|
||||||
PchumLog = logging.getLogger("pchumLogger")
|
PchumLog = logging.getLogger("pchumLogger")
|
||||||
|
@ -871,7 +868,9 @@ class PesterIRC(QtCore.QThread):
|
||||||
self.moodUpdated.emit(handle, Mood("chummy"))
|
self.moodUpdated.emit(handle, Mood("chummy"))
|
||||||
|
|
||||||
def mode(self, op, channel, mode, *handles):
|
def mode(self, op, channel, mode, *handles):
|
||||||
PchumLog.debug("op=%s, channel=%s, mode=%s, handles=%s", op, channel, mode, handles)
|
PchumLog.debug(
|
||||||
|
"op=%s, channel=%s, mode=%s, handles=%s", op, channel, mode, handles
|
||||||
|
)
|
||||||
|
|
||||||
if not handles:
|
if not handles:
|
||||||
handles = [""]
|
handles = [""]
|
||||||
|
@ -1062,9 +1061,7 @@ class PesterIRC(QtCore.QThread):
|
||||||
"""finds and runs a command"""
|
"""finds and runs a command"""
|
||||||
PchumLog.debug("processCommand {}({})".format(command, args))
|
PchumLog.debug("processCommand {}({})".format(command, args))
|
||||||
try:
|
try:
|
||||||
print(f"command is {command}")
|
|
||||||
f = self.commands[command]
|
f = self.commands[command]
|
||||||
print(f" we r running {command}")
|
|
||||||
except KeyError as e:
|
except KeyError as e:
|
||||||
PchumLog.info(e)
|
PchumLog.info(e)
|
||||||
self.__unhandled__(command, *args)
|
self.__unhandled__(command, *args)
|
||||||
|
|
|
@ -1,212 +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.
|
|
||||||
import logging
|
|
||||||
import inspect
|
|
||||||
|
|
||||||
from oyoyo import helpers
|
|
||||||
from oyoyo.parse import parse_nick
|
|
||||||
|
|
||||||
PchumLog = logging.getLogger("pchumLogger")
|
|
||||||
|
|
||||||
|
|
||||||
def protected(func):
|
|
||||||
"""decorator to protect functions from being called"""
|
|
||||||
func.protected = True
|
|
||||||
return func
|
|
||||||
|
|
||||||
|
|
||||||
class CommandError(Exception):
|
|
||||||
def __init__(self, cmd):
|
|
||||||
self.cmd = cmd
|
|
||||||
|
|
||||||
|
|
||||||
class NoSuchCommandError(CommandError):
|
|
||||||
def __str__(self):
|
|
||||||
return 'No such command "%s"' % ".".join(self.cmd)
|
|
||||||
|
|
||||||
|
|
||||||
class ProtectedCommandError(CommandError):
|
|
||||||
def __str__(self):
|
|
||||||
return 'Command "%s" is protected' % ".".join(self.cmd)
|
|
||||||
|
|
||||||
|
|
||||||
class CommandHandler:
|
|
||||||
"""The most basic CommandHandler"""
|
|
||||||
|
|
||||||
def __init__(self, client):
|
|
||||||
self.client = client
|
|
||||||
|
|
||||||
@protected
|
|
||||||
def get(self, in_command_parts):
|
|
||||||
PchumLog.debug("in_command_parts: %s" % in_command_parts)
|
|
||||||
""" finds a command
|
|
||||||
commands may be dotted. each command part is checked that it does
|
|
||||||
not start with and underscore and does not have an attribute
|
|
||||||
"protected". if either of these is true, ProtectedCommandError
|
|
||||||
is raised.
|
|
||||||
its possible to pass both "command.sub.func" and
|
|
||||||
["command", "sub", "func"].
|
|
||||||
"""
|
|
||||||
if isinstance(in_command_parts, (str, bytes)):
|
|
||||||
in_command_parts = in_command_parts.split(".")
|
|
||||||
command_parts = in_command_parts[:]
|
|
||||||
|
|
||||||
p = self
|
|
||||||
while command_parts:
|
|
||||||
cmd = command_parts.pop(0)
|
|
||||||
if cmd.startswith("_"):
|
|
||||||
raise ProtectedCommandError(in_command_parts)
|
|
||||||
|
|
||||||
try:
|
|
||||||
f = getattr(p, cmd)
|
|
||||||
except AttributeError:
|
|
||||||
raise NoSuchCommandError(in_command_parts)
|
|
||||||
|
|
||||||
if hasattr(f, "protected"):
|
|
||||||
raise ProtectedCommandError(in_command_parts)
|
|
||||||
|
|
||||||
if isinstance(f, CommandHandler) and command_parts:
|
|
||||||
return f.get(command_parts)
|
|
||||||
p = f
|
|
||||||
|
|
||||||
return f
|
|
||||||
|
|
||||||
@protected
|
|
||||||
def run(self, command, *args):
|
|
||||||
"""finds and runs a command"""
|
|
||||||
arguments_str = ""
|
|
||||||
for x in args:
|
|
||||||
arguments_str += str(x) + " "
|
|
||||||
PchumLog.debug("processCommand {}({})".format(command, arguments_str.strip()))
|
|
||||||
|
|
||||||
try:
|
|
||||||
f = self.get(command)
|
|
||||||
except NoSuchCommandError as e:
|
|
||||||
PchumLog.info(e)
|
|
||||||
self.__unhandled__(command, *args)
|
|
||||||
return
|
|
||||||
|
|
||||||
PchumLog.debug("f %s" % f)
|
|
||||||
|
|
||||||
try:
|
|
||||||
f(*args)
|
|
||||||
except TypeError as e:
|
|
||||||
PchumLog.info(
|
|
||||||
"Failed to pass command, did the server pass an unsupported paramater? "
|
|
||||||
+ str(e)
|
|
||||||
)
|
|
||||||
except Exception as e:
|
|
||||||
# logging.info("Failed to pass command, %s" % str(e))
|
|
||||||
PchumLog.exception("Failed to pass command")
|
|
||||||
|
|
||||||
@protected
|
|
||||||
def __unhandled__(self, cmd, *args):
|
|
||||||
"""The default handler for commands. Override this method to
|
|
||||||
apply custom behavior (example, printing) unhandled commands.
|
|
||||||
"""
|
|
||||||
PchumLog.debug("unhandled command {}({})".format(cmd, args))
|
|
||||||
|
|
||||||
|
|
||||||
class DefaultCommandHandler(CommandHandler):
|
|
||||||
"""CommandHandler that provides methods for the normal operation of IRC.
|
|
||||||
If you want your bot to properly respond to pings, etc, you should subclass this.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def ping(self, prefix, server):
|
|
||||||
self.client.send("PONG", server)
|
|
||||||
|
|
||||||
|
|
||||||
class DefaultBotCommandHandler(CommandHandler):
|
|
||||||
"""default command handler for bots. methods/attributes are made
|
|
||||||
available as commands"""
|
|
||||||
|
|
||||||
@protected
|
|
||||||
def getVisibleCommands(self, obj=None):
|
|
||||||
test = (
|
|
||||||
lambda x: isinstance(x, CommandHandler)
|
|
||||||
or inspect.ismethod(x)
|
|
||||||
or inspect.isfunction(x)
|
|
||||||
)
|
|
||||||
members = inspect.getmembers(obj or self, test)
|
|
||||||
return [
|
|
||||||
m
|
|
||||||
for m, _ in members
|
|
||||||
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"""
|
|
||||||
PchumLog.info("help sender={} dest={} arg={}".format(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, "{}: {}".format(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.
|
|
||||||
"""
|
|
||||||
|
|
||||||
PchumLog.debug("tryBotCommand('{}' '{}' '{}')".format(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
|
|
156
oyoyo/helpers.py
156
oyoyo/helpers.py
|
@ -1,156 +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 logging
|
|
||||||
import random
|
|
||||||
|
|
||||||
PchumLog = logging.getLogger("pchumLogger")
|
|
||||||
|
|
||||||
|
|
||||||
def msg(cli, user, msg):
|
|
||||||
for line in msg.split("\n"):
|
|
||||||
cli.send("PRIVMSG", user, ":%s" % line)
|
|
||||||
|
|
||||||
|
|
||||||
def names(cli, *channels):
|
|
||||||
tmp = __builtins__["list"](channels)
|
|
||||||
msglist = []
|
|
||||||
while len(tmp) > 0:
|
|
||||||
msglist.append(tmp.pop())
|
|
||||||
if len(",".join(msglist)) > 490:
|
|
||||||
tmp.append(msglist.pop())
|
|
||||||
cli.send("NAMES %s" % (",".join(msglist)))
|
|
||||||
msglist = []
|
|
||||||
if len(msglist) > 0:
|
|
||||||
cli.send("NAMES %s" % (",".join(msglist)))
|
|
||||||
|
|
||||||
|
|
||||||
def channel_list(cli):
|
|
||||||
cli.send("LIST")
|
|
||||||
|
|
||||||
def ping(cli, token):
|
|
||||||
"""Why is this called ping when it pongs.."""
|
|
||||||
cli.send("PONG", token)
|
|
||||||
|
|
||||||
def kick(cli, handle, channel, reason=""):
|
|
||||||
cli.send("KICK {} {} {}".format(channel, handle, reason))
|
|
||||||
|
|
||||||
|
|
||||||
def mode(cli, channel, mode, options=None):
|
|
||||||
PchumLog.debug("mode = " + str(mode))
|
|
||||||
PchumLog.debug("options = " + str(options))
|
|
||||||
cmd = "MODE {} {}".format(channel, mode)
|
|
||||||
if options:
|
|
||||||
cmd += " %s" % (options)
|
|
||||||
cli.send(cmd)
|
|
||||||
|
|
||||||
|
|
||||||
def ctcp(cli, handle, cmd, msg=""):
|
|
||||||
# Space breaks protocol if msg is absent
|
|
||||||
if msg == "":
|
|
||||||
cli.send("PRIVMSG", handle, "\x01%s\x01" % (cmd))
|
|
||||||
else:
|
|
||||||
cli.send("PRIVMSG", handle, "\x01{} {}\x01".format(cmd, msg))
|
|
||||||
|
|
||||||
|
|
||||||
def ctcp_reply(cli, handle, cmd, msg=""):
|
|
||||||
notice(cli, str(handle), "\x01{} {}\x01".format(cmd.upper(), msg))
|
|
||||||
|
|
||||||
|
|
||||||
def metadata(cli, target, subcommand, *params):
|
|
||||||
# IRC metadata draft specification
|
|
||||||
# https://gist.github.com/k4bek4be/92c2937cefd49990fbebd001faf2b237
|
|
||||||
cli.send("METADATA", target, subcommand, *params)
|
|
||||||
|
|
||||||
|
|
||||||
def cap(cli, subcommand, *params):
|
|
||||||
# Capability Negotiation
|
|
||||||
# https://ircv3.net/specs/extensions/capability-negotiation.html
|
|
||||||
cli.send("CAP", subcommand, *params)
|
|
||||||
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
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):
|
|
||||||
cli.send("QUIT %s" % (msg))
|
|
||||||
|
|
||||||
|
|
||||||
def nick(cli, nick):
|
|
||||||
cli.send("NICK", nick)
|
|
||||||
|
|
||||||
|
|
||||||
def user(cli, username, realname):
|
|
||||||
cli.send("USER", username, "0", "*", ":" + realname)
|
|
||||||
|
|
||||||
|
|
||||||
def join(cli, channel):
|
|
||||||
"""Protocol potentially allows multiple channels or keys."""
|
|
||||||
cli.send("JOIN", channel)
|
|
||||||
|
|
||||||
|
|
||||||
def part(cli, channel):
|
|
||||||
cli.send("PART", channel)
|
|
||||||
|
|
||||||
|
|
||||||
def notice(cli, target, text):
|
|
||||||
cli.send("NOTICE", target, text)
|
|
||||||
|
|
||||||
|
|
||||||
def invite(cli, nick, channel):
|
|
||||||
cli.send("INVITE", nick, channel)
|
|
||||||
|
|
||||||
|
|
||||||
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()
|
|
|
@ -1,232 +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.
|
|
||||||
|
|
||||||
# taken from python irclib.. who took it from...
|
|
||||||
# Numeric table mostly stolen from the Perl IRC module (Net::IRC).
|
|
||||||
numeric_events = {
|
|
||||||
"001": "welcome",
|
|
||||||
"002": "yourhost",
|
|
||||||
"003": "created",
|
|
||||||
"004": "myinfo",
|
|
||||||
"005": "featurelist", # XXX
|
|
||||||
"010": "toomanypeeps",
|
|
||||||
"200": "tracelink",
|
|
||||||
"201": "traceconnecting",
|
|
||||||
"202": "tracehandshake",
|
|
||||||
"203": "traceunknown",
|
|
||||||
"204": "traceoperator",
|
|
||||||
"205": "traceuser",
|
|
||||||
"206": "traceserver",
|
|
||||||
"207": "traceservice",
|
|
||||||
"208": "tracenewtype",
|
|
||||||
"209": "traceclass",
|
|
||||||
"210": "tracereconnect",
|
|
||||||
"211": "statslinkinfo",
|
|
||||||
"212": "statscommands",
|
|
||||||
"213": "statscline",
|
|
||||||
"214": "statsnline",
|
|
||||||
"215": "statsiline",
|
|
||||||
"216": "statskline",
|
|
||||||
"217": "statsqline",
|
|
||||||
"218": "statsyline",
|
|
||||||
"219": "endofstats",
|
|
||||||
"221": "umodeis",
|
|
||||||
"231": "serviceinfo",
|
|
||||||
"232": "endofservices",
|
|
||||||
"233": "service",
|
|
||||||
"234": "servlist",
|
|
||||||
"235": "servlistend",
|
|
||||||
"241": "statslline",
|
|
||||||
"242": "statsuptime",
|
|
||||||
"243": "statsoline",
|
|
||||||
"244": "statshline",
|
|
||||||
"250": "luserconns",
|
|
||||||
"251": "luserclient",
|
|
||||||
"252": "luserop",
|
|
||||||
"253": "luserunknown",
|
|
||||||
"254": "luserchannels",
|
|
||||||
"255": "luserme",
|
|
||||||
"256": "adminme",
|
|
||||||
"257": "adminloc1",
|
|
||||||
"258": "adminloc2",
|
|
||||||
"259": "adminemail",
|
|
||||||
"261": "tracelog",
|
|
||||||
"262": "endoftrace",
|
|
||||||
"263": "tryagain",
|
|
||||||
"265": "n_local",
|
|
||||||
"266": "n_global",
|
|
||||||
"300": "none",
|
|
||||||
"301": "away",
|
|
||||||
"302": "userhost",
|
|
||||||
"303": "ison",
|
|
||||||
"305": "unaway",
|
|
||||||
"306": "nowaway",
|
|
||||||
"311": "whoisuser",
|
|
||||||
"312": "whoisserver",
|
|
||||||
"313": "whoisoperator",
|
|
||||||
"314": "whowasuser",
|
|
||||||
"315": "endofwho",
|
|
||||||
"316": "whoischanop",
|
|
||||||
"317": "whoisidle",
|
|
||||||
"318": "endofwhois",
|
|
||||||
"319": "whoischannels",
|
|
||||||
"321": "liststart",
|
|
||||||
"322": "list",
|
|
||||||
"323": "listend",
|
|
||||||
"324": "channelmodeis",
|
|
||||||
"329": "channelcreate",
|
|
||||||
"331": "notopic",
|
|
||||||
"332": "currenttopic",
|
|
||||||
"333": "topicinfo",
|
|
||||||
"341": "inviting",
|
|
||||||
"342": "summoning",
|
|
||||||
"346": "invitelist",
|
|
||||||
"347": "endofinvitelist",
|
|
||||||
"348": "exceptlist",
|
|
||||||
"349": "endofexceptlist",
|
|
||||||
"351": "version",
|
|
||||||
"352": "whoreply",
|
|
||||||
"353": "namreply",
|
|
||||||
"361": "killdone",
|
|
||||||
"362": "closing",
|
|
||||||
"363": "closeend",
|
|
||||||
"364": "links",
|
|
||||||
"365": "endoflinks",
|
|
||||||
"366": "endofnames",
|
|
||||||
"367": "banlist",
|
|
||||||
"368": "endofbanlist",
|
|
||||||
"369": "endofwhowas",
|
|
||||||
"371": "info",
|
|
||||||
"372": "motd",
|
|
||||||
"373": "infostart",
|
|
||||||
"374": "endofinfo",
|
|
||||||
"375": "motdstart",
|
|
||||||
"376": "endofmotd",
|
|
||||||
"377": "motd2", # 1997-10-16 -- tkil
|
|
||||||
"381": "youreoper",
|
|
||||||
"382": "rehashing",
|
|
||||||
"384": "myportis",
|
|
||||||
"391": "time",
|
|
||||||
"392": "usersstart",
|
|
||||||
"393": "users",
|
|
||||||
"394": "endofusers",
|
|
||||||
"395": "nousers",
|
|
||||||
"396": "hosthidden",
|
|
||||||
"401": "nosuchnick",
|
|
||||||
"402": "nosuchserver",
|
|
||||||
"403": "nosuchchannel",
|
|
||||||
"404": "cannotsendtochan",
|
|
||||||
"405": "toomanychannels",
|
|
||||||
"406": "wasnosuchnick",
|
|
||||||
"407": "toomanytargets",
|
|
||||||
"409": "noorigin",
|
|
||||||
"411": "norecipient",
|
|
||||||
"412": "notexttosend",
|
|
||||||
"413": "notoplevel",
|
|
||||||
"414": "wildtoplevel",
|
|
||||||
"421": "unknowncommand",
|
|
||||||
"422": "nomotd",
|
|
||||||
"423": "noadmininfo",
|
|
||||||
"424": "fileerror",
|
|
||||||
"431": "nonicknamegiven",
|
|
||||||
"432": "erroneusnickname", # Thiss iz how its speld in thee RFC.
|
|
||||||
"433": "nicknameinuse",
|
|
||||||
"436": "nickcollision",
|
|
||||||
"437": "unavailresource", # "Nick temporally unavailable"
|
|
||||||
"441": "usernotinchannel",
|
|
||||||
"442": "notonchannel",
|
|
||||||
"443": "useronchannel",
|
|
||||||
"444": "nologin",
|
|
||||||
"445": "summondisabled",
|
|
||||||
"446": "usersdisabled",
|
|
||||||
"451": "notregistered",
|
|
||||||
"461": "needmoreparams",
|
|
||||||
"462": "alreadyregistered",
|
|
||||||
"463": "nopermforhost",
|
|
||||||
"464": "passwdmismatch",
|
|
||||||
"465": "yourebannedcreep", # I love this one...
|
|
||||||
"466": "youwillbebanned",
|
|
||||||
"467": "keyset",
|
|
||||||
"471": "channelisfull",
|
|
||||||
"472": "unknownmode",
|
|
||||||
"473": "inviteonlychan",
|
|
||||||
"474": "bannedfromchan",
|
|
||||||
"475": "badchannelkey",
|
|
||||||
"476": "badchanmask",
|
|
||||||
"477": "nochanmodes", # "Channel doesn't support modes"
|
|
||||||
"478": "banlistfull",
|
|
||||||
"481": "noprivileges",
|
|
||||||
"482": "chanoprivsneeded",
|
|
||||||
"483": "cantkillserver",
|
|
||||||
"484": "restricted", # Connection is restricted
|
|
||||||
"485": "uniqopprivsneeded",
|
|
||||||
"491": "nooperhost",
|
|
||||||
"492": "noservicehost",
|
|
||||||
"501": "umodeunknownflag",
|
|
||||||
"502": "usersdontmatch",
|
|
||||||
}
|
|
||||||
|
|
||||||
# Unrealircd extras
|
|
||||||
unrealircd_events = {
|
|
||||||
"448": "forbiddenchannel",
|
|
||||||
}
|
|
||||||
numeric_events.update(unrealircd_events)
|
|
||||||
|
|
||||||
# IRC metadata draft specification
|
|
||||||
# https://gist.github.com/k4bek4be/92c2937cefd49990fbebd001faf2b237
|
|
||||||
metadata_numeric_events = {
|
|
||||||
"761": "keyvalue",
|
|
||||||
"762": "metadataend",
|
|
||||||
"766": "nomatchingkey",
|
|
||||||
"768": "keynotset",
|
|
||||||
"769": "keynopermission",
|
|
||||||
"770": "metadatasubok",
|
|
||||||
}
|
|
||||||
numeric_events.update(metadata_numeric_events)
|
|
||||||
|
|
||||||
generated_events = [
|
|
||||||
# Generated events
|
|
||||||
"dcc_connect",
|
|
||||||
"dcc_disconnect",
|
|
||||||
"dccmsg",
|
|
||||||
"disconnect",
|
|
||||||
"ctcp",
|
|
||||||
"ctcpreply",
|
|
||||||
]
|
|
||||||
|
|
||||||
protocol_events = [
|
|
||||||
# IRC protocol events
|
|
||||||
"error",
|
|
||||||
"join",
|
|
||||||
"kick",
|
|
||||||
"mode",
|
|
||||||
"part",
|
|
||||||
"ping",
|
|
||||||
"privmsg",
|
|
||||||
"privnotice",
|
|
||||||
"pubmsg",
|
|
||||||
"pubnotice",
|
|
||||||
"quit",
|
|
||||||
"invite",
|
|
||||||
"pong",
|
|
||||||
"nick", # We can get svsnicked
|
|
||||||
"metadata", # Metadata specification
|
|
||||||
"tagmsg", # IRCv3 message tags extension
|
|
||||||
"cap", # IRCv3 Client Capability Negotiation
|
|
||||||
]
|
|
||||||
|
|
||||||
all_events = generated_events + protocol_events + list(numeric_events.values())
|
|
121
oyoyo/parse.py
121
oyoyo/parse.py
|
@ -1,121 +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.
|
|
||||||
|
|
||||||
import logging
|
|
||||||
|
|
||||||
from oyoyo.ircevents import numeric_events
|
|
||||||
|
|
||||||
PchumLog = logging.getLogger("pchumLogger")
|
|
||||||
|
|
||||||
|
|
||||||
def parse_raw_irc_command(element):
|
|
||||||
"""
|
|
||||||
This function parses a raw irc command and returns a tuple
|
|
||||||
of (prefix, command, args).
|
|
||||||
The following is a psuedo BNF of the input text:
|
|
||||||
|
|
||||||
<message> ::= [':' <prefix> <SPACE> ] <command> <params> <crlf>
|
|
||||||
<prefix> ::= <servername> | <nick> [ '!' <user> ] [ '@' <host> ]
|
|
||||||
<command> ::= <letter> { <letter> } | <number> <number> <number>
|
|
||||||
<SPACE> ::= ' ' { ' ' }
|
|
||||||
<params> ::= <SPACE> [ ':' <trailing> | <middle> <params> ]
|
|
||||||
|
|
||||||
<middle> ::= <Any *non-empty* sequence of octets not including SPACE
|
|
||||||
or NUL or CR or LF, the first of which may not be ':'>
|
|
||||||
<trailing> ::= <Any, possibly *empty*, sequence of octets not including
|
|
||||||
NUL or CR or LF>
|
|
||||||
|
|
||||||
<crlf> ::= CR LF
|
|
||||||
"""
|
|
||||||
"""
|
|
||||||
When message-tags are enabled, the message pseudo-BNF,
|
|
||||||
as defined in RFC 1459, section 2.3.1 is extended as follows:
|
|
||||||
|
|
||||||
<message> ::= ['@' <tags> <SPACE>] [':' <prefix> <SPACE> ] <command> [params] <crlf>
|
|
||||||
<tags> ::= <tag> [';' <tag>]*
|
|
||||||
<tag> ::= <key> ['=' <escaped_value>]
|
|
||||||
<key> ::= [ <client_prefix> ] [ <vendor> '/' ] <key_name>
|
|
||||||
<client_prefix> ::= '+'
|
|
||||||
<key_name> ::= <non-empty sequence of ascii letters, digits, hyphens ('-')>
|
|
||||||
<escaped_value> ::= <sequence of zero or more utf8 characters except NUL, CR, LF, semicolon (`;`) and SPACE>
|
|
||||||
<vendor> ::= <host>
|
|
||||||
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
try:
|
|
||||||
element = element.decode("utf-8")
|
|
||||||
except UnicodeDecodeError as e:
|
|
||||||
PchumLog.debug("utf-8 error %s" % str(e))
|
|
||||||
element = element.decode("latin-1", "replace")
|
|
||||||
|
|
||||||
parts = element.strip().split(" ")
|
|
||||||
if parts[0].startswith(":"):
|
|
||||||
tags = None
|
|
||||||
prefix = parts[0][1:]
|
|
||||||
command = parts[1]
|
|
||||||
args = parts[2:]
|
|
||||||
elif parts[0].startswith("@"):
|
|
||||||
# Message tag
|
|
||||||
tags = parts[0]
|
|
||||||
prefix = parts[1][1:]
|
|
||||||
command = parts[2]
|
|
||||||
args = parts[3:]
|
|
||||||
else:
|
|
||||||
tags = None
|
|
||||||
prefix = None
|
|
||||||
command = parts[0]
|
|
||||||
args = parts[1:]
|
|
||||||
|
|
||||||
if command.isdigit():
|
|
||||||
try:
|
|
||||||
command = numeric_events[command]
|
|
||||||
except KeyError:
|
|
||||||
PchumLog.info("unknown numeric event %s" % command)
|
|
||||||
command = command.lower()
|
|
||||||
|
|
||||||
if args[0].startswith(":"):
|
|
||||||
args = [" ".join(args)[1:]]
|
|
||||||
else:
|
|
||||||
for idx, arg in enumerate(args):
|
|
||||||
if arg.startswith(":"):
|
|
||||||
args = args[:idx] + [" ".join(args[idx:])[1:]]
|
|
||||||
break
|
|
||||||
|
|
||||||
return (tags, prefix, command, args)
|
|
||||||
|
|
||||||
|
|
||||||
def parse_nick(name):
|
|
||||||
"""parse a nickname and return a tuple of (nick, mode, user, host)
|
|
||||||
|
|
||||||
<nick> [ '!' [<mode> = ] <user> ] [ '@' <host> ]
|
|
||||||
"""
|
|
||||||
|
|
||||||
try:
|
|
||||||
nick, rest = name.split("!")
|
|
||||||
except ValueError:
|
|
||||||
return (name, None, None, None)
|
|
||||||
try:
|
|
||||||
mode, rest = rest.split("=")
|
|
||||||
except ValueError:
|
|
||||||
mode, rest = None, rest
|
|
||||||
try:
|
|
||||||
user, host = rest.split("@")
|
|
||||||
except ValueError:
|
|
||||||
return (name, mode, rest, None)
|
|
||||||
|
|
||||||
return (name, mode, user, host)
|
|
|
@ -1,125 +0,0 @@
|
||||||
# NickServ basic functions
|
|
||||||
_nickservfuncs = (
|
|
||||||
"register",
|
|
||||||
"group",
|
|
||||||
"glist",
|
|
||||||
"identify",
|
|
||||||
"access",
|
|
||||||
"drop",
|
|
||||||
"recover",
|
|
||||||
"release",
|
|
||||||
"sendpass",
|
|
||||||
"ghost",
|
|
||||||
"alist",
|
|
||||||
"info",
|
|
||||||
"list",
|
|
||||||
"logout",
|
|
||||||
"status",
|
|
||||||
"update",
|
|
||||||
)
|
|
||||||
|
|
||||||
# NickServ SET functions
|
|
||||||
_nickservsetfuncs = (
|
|
||||||
"display",
|
|
||||||
"password",
|
|
||||||
"language",
|
|
||||||
"url",
|
|
||||||
"email",
|
|
||||||
"icq",
|
|
||||||
"greet",
|
|
||||||
"kill",
|
|
||||||
"secure",
|
|
||||||
"private",
|
|
||||||
"hide",
|
|
||||||
"msg",
|
|
||||||
"autoop",
|
|
||||||
)
|
|
||||||
|
|
||||||
# ChanServ basic functions
|
|
||||||
_chanservfuncs = (
|
|
||||||
"register",
|
|
||||||
"identify",
|
|
||||||
"sop",
|
|
||||||
"aop",
|
|
||||||
"hop",
|
|
||||||
"vop",
|
|
||||||
"access",
|
|
||||||
"levels",
|
|
||||||
"akick",
|
|
||||||
"drop",
|
|
||||||
"sendpass",
|
|
||||||
"ban",
|
|
||||||
"unban",
|
|
||||||
"clear",
|
|
||||||
"owner",
|
|
||||||
"deowner",
|
|
||||||
"protect",
|
|
||||||
"deprotect",
|
|
||||||
"op",
|
|
||||||
"deop",
|
|
||||||
"halfop",
|
|
||||||
"dehalfop",
|
|
||||||
"voice",
|
|
||||||
"devoice",
|
|
||||||
"getkey",
|
|
||||||
"invite",
|
|
||||||
"kick",
|
|
||||||
"list",
|
|
||||||
"logout",
|
|
||||||
"topic",
|
|
||||||
"info",
|
|
||||||
"appendtopic",
|
|
||||||
"enforce",
|
|
||||||
)
|
|
||||||
|
|
||||||
_chanservsetfuncs = (
|
|
||||||
"founder",
|
|
||||||
"successor",
|
|
||||||
"password",
|
|
||||||
"desc",
|
|
||||||
"url",
|
|
||||||
"email",
|
|
||||||
"entrymsg",
|
|
||||||
"bantype",
|
|
||||||
"mlock",
|
|
||||||
"keeptopic",
|
|
||||||
"opnotice",
|
|
||||||
"peace",
|
|
||||||
"private",
|
|
||||||
"restricted",
|
|
||||||
"secure",
|
|
||||||
"secureops",
|
|
||||||
"securefounder",
|
|
||||||
"signkick",
|
|
||||||
"topiclock",
|
|
||||||
"xop",
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def _addServ(serv, funcs, prefix=""):
|
|
||||||
def simplecmd(cmd_name):
|
|
||||||
if prefix:
|
|
||||||
cmd_name = prefix.upper() + " " + cmd_name
|
|
||||||
|
|
||||||
def f(cli, *args):
|
|
||||||
print(cmd_name, " ".join(args))
|
|
||||||
# cli.send(cmd_name, serv.name, *args)
|
|
||||||
|
|
||||||
return f
|
|
||||||
|
|
||||||
for t in funcs:
|
|
||||||
setattr(serv, t, simplecmd(t.upper()))
|
|
||||||
|
|
||||||
|
|
||||||
class NickServ:
|
|
||||||
def __init__(self, nick="NickServ"):
|
|
||||||
self.name = nick
|
|
||||||
_addServ(self, _nickservfuncs)
|
|
||||||
_addServ(self, _nickservsetfuncs, "set")
|
|
||||||
|
|
||||||
|
|
||||||
class ChanServ:
|
|
||||||
def __init__(self, nick="ChanServ"):
|
|
||||||
self.name = nick
|
|
||||||
_addServ(self, _chanservfuncs)
|
|
||||||
_addServ(self, _chanservsetfuncs, "set")
|
|
|
@ -429,9 +429,7 @@ def kxsplitMsg(lexed, ctx, fmt="pchum", maxlen=None, debug=False):
|
||||||
curlen = 0
|
curlen = 0
|
||||||
# Maximum number of characters *to* use.
|
# Maximum number of characters *to* use.
|
||||||
if not maxlen:
|
if not maxlen:
|
||||||
maxlen = _max_msg_len(
|
maxlen = _max_msg_len(None, None, ctx.mainwindow.profile().handle, "pcc31")
|
||||||
None, None, ctx.mainwindow.profile().handle, "pcc31"
|
|
||||||
)
|
|
||||||
elif maxlen < 0:
|
elif maxlen < 0:
|
||||||
# Subtract the (negative) length, giving us less leeway in this
|
# Subtract the (negative) length, giving us less leeway in this
|
||||||
# function.
|
# function.
|
||||||
|
@ -805,9 +803,7 @@ def kxhandleInput(ctx, text=None, flavor=None):
|
||||||
# We'll use those later.
|
# We'll use those later.
|
||||||
|
|
||||||
# Split the messages so we don't go over the buffer and lose text.
|
# Split the messages so we don't go over the buffer and lose text.
|
||||||
maxlen = _max_msg_len(
|
maxlen = _max_msg_len(None, None, ctx.mainwindow.profile().handle, "pcc31")
|
||||||
None, None, ctx.mainwindow.profile().handle, "pcc31"
|
|
||||||
)
|
|
||||||
# ctx.mainwindow.profile().handle ==> Get handle
|
# ctx.mainwindow.profile().handle ==> Get handle
|
||||||
# "pcc31" ==> Get ident (Same as realname in this case.)
|
# "pcc31" ==> Get ident (Same as realname in this case.)
|
||||||
# Since we have to do some post-processing, we need to adjust the maximum
|
# Since we have to do some post-processing, we need to adjust the maximum
|
||||||
|
|
|
@ -136,4 +136,3 @@ class SendIRC:
|
||||||
def quit(self, reason=""):
|
def quit(self, reason=""):
|
||||||
"""Send QUIT to terminate connection."""
|
"""Send QUIT to terminate connection."""
|
||||||
self.send("QUIT", text=reason)
|
self.send("QUIT", text=reason)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue