Support IRCv3's account-notify/extended-join along with WHOX to replace internal
register/identify
This commit is contained in:
parent
0d6e888380
commit
6bd6f2492b
4 changed files with 87 additions and 43 deletions
|
@ -26,6 +26,7 @@ class LineHandler(object):
|
||||||
events.on("raw.366").hook(self.handle_366, default_event=True)
|
events.on("raw.366").hook(self.handle_366, default_event=True)
|
||||||
events.on("raw.421").hook(self.handle_421, default_event=True)
|
events.on("raw.421").hook(self.handle_421, default_event=True)
|
||||||
events.on("raw.352").hook(self.handle_352, default_event=True)
|
events.on("raw.352").hook(self.handle_352, default_event=True)
|
||||||
|
events.on("raw.354").hook(self.handle_354, default_event=True)
|
||||||
events.on("raw.324").hook(self.handle_324, default_event=True)
|
events.on("raw.324").hook(self.handle_324, default_event=True)
|
||||||
events.on("raw.329").hook(self.handle_329, default_event=True)
|
events.on("raw.329").hook(self.handle_329, default_event=True)
|
||||||
events.on("raw.433").hook(self.handle_433, default_event=True)
|
events.on("raw.433").hook(self.handle_433, default_event=True)
|
||||||
|
@ -110,8 +111,10 @@ class LineHandler(object):
|
||||||
# server telling us what it supports
|
# server telling us what it supports
|
||||||
def handle_005(self, event):
|
def handle_005(self, event):
|
||||||
isupport_line = " ".join(event["args"][1:])
|
isupport_line = " ".join(event["args"][1:])
|
||||||
|
|
||||||
if "NAMESX" in isupport_line:
|
if "NAMESX" in isupport_line:
|
||||||
event["server"].send("PROTOCTL NAMESX")
|
event["server"].send("PROTOCTL NAMESX")
|
||||||
|
|
||||||
match = re.search(RE_PREFIXES, isupport_line)
|
match = re.search(RE_PREFIXES, isupport_line)
|
||||||
if match:
|
if match:
|
||||||
event["server"].mode_prefixes.clear()
|
event["server"].mode_prefixes.clear()
|
||||||
|
@ -201,7 +204,7 @@ class LineHandler(object):
|
||||||
|
|
||||||
# on-join user list has finished
|
# on-join user list has finished
|
||||||
def handle_366(self, event):
|
def handle_366(self, event):
|
||||||
event["server"].send_who(event["args"][1])
|
event["server"].send_whox(event["args"][1], "ahnrtu", "001")
|
||||||
|
|
||||||
# on user joining channel
|
# on user joining channel
|
||||||
def join(self, event):
|
def join(self, event):
|
||||||
|
@ -484,6 +487,21 @@ class LineHandler(object):
|
||||||
user = event["server"].get_user(event["args"][5])
|
user = event["server"].get_user(event["args"][5])
|
||||||
user.username = event["args"][2]
|
user.username = event["args"][2]
|
||||||
user.hostname = event["args"][3]
|
user.hostname = event["args"][3]
|
||||||
|
# response to a WHOX command for user information, including account name
|
||||||
|
def handle_354(self, event):
|
||||||
|
if event["args"][1] == "001":
|
||||||
|
username = event["args"][2]
|
||||||
|
hostname = event["args"][3]
|
||||||
|
nickname = event["args"][4]
|
||||||
|
account = event["args"][5]
|
||||||
|
realname = event["arbitrary"]
|
||||||
|
|
||||||
|
user = event["server"].get_user(nickname)
|
||||||
|
user.username = username
|
||||||
|
user.hostname = hostname
|
||||||
|
user.realname = realname
|
||||||
|
if not account == "0":
|
||||||
|
user.identified_account = account
|
||||||
|
|
||||||
# response to an empty mode command
|
# response to an empty mode command
|
||||||
def handle_324(self, event):
|
def handle_324(self, event):
|
||||||
|
|
|
@ -379,3 +379,7 @@ class Server(object):
|
||||||
"" if server == None else " :%s" % server))
|
"" if server == None else " :%s" % server))
|
||||||
def send_who(self, filter=None):
|
def send_who(self, filter=None):
|
||||||
self.send("WHO%s" % ("" if filter == None else " %s" % filter))
|
self.send("WHO%s" % ("" if filter == None else " %s" % filter))
|
||||||
|
def send_whox(self, filter, flags, label=None):
|
||||||
|
self.send("WHO %s %s%s" % (filter,
|
||||||
|
"%"+flags if flags else "",
|
||||||
|
","+label if label else ""))
|
||||||
|
|
21
IRCUser.py
21
IRCUser.py
|
@ -11,13 +11,22 @@ class User(object):
|
||||||
self.server = server
|
self.server = server
|
||||||
self.bot = bot
|
self.bot = bot
|
||||||
self.channels = set([])
|
self.channels = set([])
|
||||||
|
|
||||||
self.identified_account = None
|
self.identified_account = None
|
||||||
|
self.identified_account_override = None
|
||||||
|
|
||||||
|
self.identified_account_id = None
|
||||||
|
self.identified_account_id_override = None
|
||||||
self.away = False
|
self.away = False
|
||||||
self.buffer = IRCBuffer.Buffer(bot)
|
self.buffer = IRCBuffer.Buffer(bot)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "IRCUser.User(%s|%s)" % (self.server.name, self.name)
|
return "IRCUser.User(%s|%s)" % (self.server.name, self.name)
|
||||||
|
|
||||||
|
def _get_id(self):
|
||||||
|
return (self.identified_account_id_override or
|
||||||
|
self.identified_account_id or self.id)
|
||||||
|
|
||||||
def set_nickname(self, nickname):
|
def set_nickname(self, nickname):
|
||||||
self.nickname = nickname
|
self.nickname = nickname
|
||||||
self.nickname_lower = nickname.lower()
|
self.nickname_lower = nickname.lower()
|
||||||
|
@ -27,21 +36,21 @@ class User(object):
|
||||||
def part_channel(self, channel):
|
def part_channel(self, channel):
|
||||||
self.channels.remove(channel)
|
self.channels.remove(channel)
|
||||||
def set_setting(self, setting, value):
|
def set_setting(self, setting, value):
|
||||||
self.bot.database.user_settings.set(self.id, setting, value)
|
self.bot.database.user_settings.set(self._get_id(), setting, value)
|
||||||
def get_setting(self, setting, default=None):
|
def get_setting(self, setting, default=None):
|
||||||
return self.bot.database.user_settings.get(self.id, setting,
|
return self.bot.database.user_settings.get(self._get_id(), setting,
|
||||||
default)
|
default)
|
||||||
def find_settings(self, pattern, default=[]):
|
def find_settings(self, pattern, default=[]):
|
||||||
return self.bot.database.user_settings.find(self.id, pattern,
|
return self.bot.database.user_settings.find(self._get_id(), pattern,
|
||||||
default)
|
default)
|
||||||
def find_settings_prefix(self, prefix, default=[]):
|
def find_settings_prefix(self, prefix, default=[]):
|
||||||
return self.bot.database.user_settings.find_prefix(self.id,
|
return self.bot.database.user_settings.find_prefix(self._get_id(),
|
||||||
prefix, default)
|
prefix, default)
|
||||||
def del_setting(self, setting):
|
def del_setting(self, setting):
|
||||||
self.bot.database.user_settings.delete(self.id, setting)
|
self.bot.database.user_settings.delete(self._get_id(), setting)
|
||||||
def get_channel_settings_per_setting(self, setting, default=[]):
|
def get_channel_settings_per_setting(self, setting, default=[]):
|
||||||
return self.bot.database.user_channel_settings.find_by_setting(
|
return self.bot.database.user_channel_settings.find_by_setting(
|
||||||
self.id, setting, default)
|
self._get_id(), setting, default)
|
||||||
|
|
||||||
def send_message(self, message, prefix=None):
|
def send_message(self, message, prefix=None):
|
||||||
self.server.send_message(self.nickname, message, prefix=prefix)
|
self.server.send_message(self.nickname, message, prefix=prefix)
|
||||||
|
|
|
@ -11,11 +11,10 @@ class Module(object):
|
||||||
events.on("preprocess.command").hook(
|
events.on("preprocess.command").hook(
|
||||||
self.preprocess_command)
|
self.preprocess_command)
|
||||||
events.on("received.part").hook(self.on_part)
|
events.on("received.part").hook(self.on_part)
|
||||||
events.on("received.nick").hook(self.on_nick)
|
|
||||||
|
|
||||||
events.on("received").on("command").on("identify"
|
events.on("received").on("command").on("identify"
|
||||||
).hook(self.identify, private_only=True, min_args=1,
|
).hook(self.identify, private_only=True, min_args=2,
|
||||||
usage="<password>", help="Identify yourself")
|
usage="<account> <password>", help="Identify yourself")
|
||||||
events.on("received").on("command").on("register"
|
events.on("received").on("command").on("register"
|
||||||
).hook(self.register, private_only=True, min_args=1,
|
).hook(self.register, private_only=True, min_args=1,
|
||||||
usage="<password>", help="Register your nickname")
|
usage="<password>", help="Register your nickname")
|
||||||
|
@ -37,18 +36,14 @@ class Module(object):
|
||||||
self._logout(event["user"])
|
self._logout(event["user"])
|
||||||
|
|
||||||
def on_part(self, event):
|
def on_part(self, event):
|
||||||
if len(event["user"].channels) == 1 and event["user"].identified:
|
if len(event["user"].channels) == 1 and event["user"
|
||||||
|
].identified_account_override:
|
||||||
event["user"].send_notice("You no longer share any channels "
|
event["user"].send_notice("You no longer share any channels "
|
||||||
"with me so you have been signed out")
|
"with me so you have been signed out")
|
||||||
|
|
||||||
def on_nick(self, event):
|
def _get_hash(self, server, account):
|
||||||
if event["user"].identified:
|
hash, salt = server.get_user(account).get_setting("authentication",
|
||||||
self._logout(event["user"])
|
(None, None))
|
||||||
event["user"].send_notice("You've changed nickname so you "
|
|
||||||
"have been signed out")
|
|
||||||
|
|
||||||
def _get_hash(self, user):
|
|
||||||
hash, salt = user.get_setting("authentication", (None, None))
|
|
||||||
return hash, salt
|
return hash, salt
|
||||||
|
|
||||||
def _make_salt(self):
|
def _make_salt(self):
|
||||||
|
@ -59,35 +54,48 @@ class Module(object):
|
||||||
hash = base64.b64encode(scrypt.hash(password, salt)).decode("utf8")
|
hash = base64.b64encode(scrypt.hash(password, salt)).decode("utf8")
|
||||||
return hash, salt
|
return hash, salt
|
||||||
|
|
||||||
def _identified(self, user):
|
def _identified(self, server, user, account):
|
||||||
user.identified = True
|
user.identified_account_override = account
|
||||||
|
user.identified_account_id_override = server.get_user(account).id
|
||||||
|
|
||||||
def _logout(self, user):
|
def _logout(self, user):
|
||||||
user.identified = False
|
user.identified_account_override = None
|
||||||
|
user.identified_account_id_override = None
|
||||||
|
|
||||||
def identify(self, event):
|
def identify(self, event):
|
||||||
|
identity_mechanism = event["server"].get_setting("identity-mechanism",
|
||||||
|
"internal")
|
||||||
|
if not identity_mechanism == "internal":
|
||||||
|
event["stderr"].write("The 'identify' command isn't available "
|
||||||
|
"on this network")
|
||||||
|
return
|
||||||
|
|
||||||
if not event["user"].channels:
|
if not event["user"].channels:
|
||||||
event["stderr"].write("You must share at least one channel "
|
event["stderr"].write("You must share at least one channel "
|
||||||
"with me before you can identify")
|
"with me before you can identify")
|
||||||
return
|
return
|
||||||
if not event["user"].identified:
|
|
||||||
password = event["args_split"][0]
|
if not event["user"].identified_account_override:
|
||||||
hash, salt = self._get_hash(event["user"])
|
account = event["args_split"][0]
|
||||||
|
password = " ".join(event["args_split"][1:])
|
||||||
|
hash, salt = self._get_hash(event["server"], account)
|
||||||
if hash and salt:
|
if hash and salt:
|
||||||
attempt, _ = self._make_hash(password, salt)
|
attempt, _ = self._make_hash(password, salt)
|
||||||
if attempt == hash:
|
if attempt == hash:
|
||||||
self._identified(event["user"])
|
self._identified(event["server"], event["user"], account)
|
||||||
event["stdout"].write("Correct password, you have "
|
event["stdout"].write("Correct password, you have "
|
||||||
"been identified.")
|
"been identified as '%s'." % account)
|
||||||
else:
|
else:
|
||||||
event["stderr"].write("Incorrect password")
|
event["stderr"].write("Incorrect password for '%s'" %
|
||||||
|
account)
|
||||||
else:
|
else:
|
||||||
event["stderr"].write("This nickname is not registered")
|
event["stderr"].write("Account '%s' is not registered" %
|
||||||
|
account)
|
||||||
else:
|
else:
|
||||||
event["stderr"].write("You are already identified")
|
event["stderr"].write("You are already identified")
|
||||||
|
|
||||||
def register(self, event):
|
def register(self, event):
|
||||||
hash, salt = self._get_hash(event["user"])
|
hash, salt = self._get_hash(event["server"], event["user"].nickname)
|
||||||
if not hash and not salt:
|
if not hash and not salt:
|
||||||
password = event["args_split"][0]
|
password = event["args_split"][0]
|
||||||
hash, salt = self._make_hash(password)
|
hash, salt = self._make_hash(password)
|
||||||
|
@ -98,7 +106,7 @@ class Module(object):
|
||||||
event["stderr"].write("This nickname is already registered")
|
event["stderr"].write("This nickname is already registered")
|
||||||
|
|
||||||
def logout(self, event):
|
def logout(self, event):
|
||||||
if event["user"].identified:
|
if event["user"].identified_account_override:
|
||||||
self._logout(event["user"])
|
self._logout(event["user"])
|
||||||
event["stdout"].write("You have been logged out")
|
event["stdout"].write("You have been logged out")
|
||||||
else:
|
else:
|
||||||
|
@ -118,25 +126,30 @@ class Module(object):
|
||||||
target.nickname)
|
target.nickname)
|
||||||
|
|
||||||
def preprocess_command(self, event):
|
def preprocess_command(self, event):
|
||||||
authentication = event["user"].get_setting("authentication", None)
|
|
||||||
permission = event["hook"].kwargs.get("permission", None)
|
permission = event["hook"].kwargs.get("permission", None)
|
||||||
authenticated = event["hook"].kwargs.get("authenticated", False)
|
authenticated = event["hook"].kwargs.get("authenticated", False)
|
||||||
protect_registered = event["hook"].kwargs.get("protect_registered",
|
|
||||||
False)
|
identity_mechanism = event["server"].get_setting("identity-mechanism",
|
||||||
|
"internal")
|
||||||
|
identified_account = None
|
||||||
|
if identity_mechanism == "internal":
|
||||||
|
identified_account = event["user"].identified_account_override
|
||||||
|
elif identity_mechanism == "ircv3-account":
|
||||||
|
identified_account = event["user"].identified_account
|
||||||
|
|
||||||
|
identified_user = None
|
||||||
|
permissions = []
|
||||||
|
if identified_account:
|
||||||
|
identified_user = event["server"].get_user(identified_account)
|
||||||
|
permissions = identified_user.get_setting("permissions", [])
|
||||||
|
|
||||||
if permission:
|
if permission:
|
||||||
identified = event["user"].identified
|
|
||||||
user_permissions = event["user"].get_setting("permissions", [])
|
|
||||||
has_permission = permission and (
|
has_permission = permission and (
|
||||||
permission in user_permissions or "*" in user_permissions)
|
permission in permissions or "*" in permissions)
|
||||||
if not identified or not has_permission:
|
if not identified_account or not has_permission:
|
||||||
return "You do not have permission to do that"
|
return "You do not have permission to do that"
|
||||||
elif authenticated:
|
elif authenticated:
|
||||||
if not event["user"].identified:
|
if not identified_account:
|
||||||
return REQUIRES_IDENTIFY % (event["server"].nickname,
|
|
||||||
event["server"].nickname)
|
|
||||||
elif protect_registered:
|
|
||||||
if authentication and not event["user"].identified:
|
|
||||||
return REQUIRES_IDENTIFY % (event["server"].nickname,
|
return REQUIRES_IDENTIFY % (event["server"].nickname,
|
||||||
event["server"].nickname)
|
event["server"].nickname)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue