Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add tracking of users and their accounts #961

Merged
merged 8 commits into from
Dec 20, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 47 additions & 15 deletions sopel/bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,18 @@
py3 = False


class _CapReq(object):
def __init__(self, prefix, module, failure=None, arg=None, success=None):
def nop(bot, cap):
pass
# TODO at some point, reorder those args to be sane
self.prefix = prefix
self.module = module
self.arg = arg
self.failure = failure or nop
self.success = success or nop


class Sopel(irc.Bot):
def __init__(self, config, daemon=False):
irc.Bot.__init__(self, config)
Expand Down Expand Up @@ -85,20 +97,30 @@ def __init__(self, config, daemon=False):
self.enabled_capabilities = set()
"""A set containing the IRCv3 capabilities that the bot has enabled."""
self._cap_reqs = dict()
"""A dictionary of capability requests

Maps the capability name to a list of tuples of the prefix ('-', '=',
or ''), the name of the requesting module, the function to call if the
the request is rejected, and the argument to the capability (or None).
"""
"""A dictionary of capability names to a list of requests"""

self.privileges = dict()
"""A dictionary of channels to their users and privilege levels

Deprecated from 6.2.0; use bot.channels instead.

The value associated with each channel is a dictionary of Identifiers to a
bitwise integer value, determined by combining the appropriate constants
from `module`."""

self.channels = tools.SopelMemory() # name to chan obj
"""A map of the channels that Sopel is in.

The keys are Identifiers of the channel names, and map to Channel
objects which contain the users in the channel and their permissions.
"""
self.users = tools.SopelMemory() # name to user obj
"""A map of the users that Sopel is aware of.

In order for Sopel to be aware of a user, it must be in at least one
channel which they are also in.
"""

self.db = SopelDB(config)
"""The bot's database."""

Expand Down Expand Up @@ -267,7 +289,7 @@ def call(self, func, sopel, trigger):

def dispatch(self, pretrigger):
args = pretrigger.args
event, args, text = pretrigger.event, args, args[-1]
event, args, text = pretrigger.event, args, args[-1] if args else ''

if self.config.core.nick_blocks or self.config.core.host_blocks:
nick_blocked = self._nick_blocked(pretrigger.nick)
Expand All @@ -283,7 +305,9 @@ def dispatch(self, pretrigger):
match = regexp.match(text)
if not match:
continue
trigger = Trigger(self.config, pretrigger, match)
user_obj = self.users.get(pretrigger.nick)
account = user_obj.account if user_obj else None
trigger = Trigger(self.config, pretrigger, match, account)
wrapper = self.SopelWrapper(self, trigger)

for func in funcs:
Expand Down Expand Up @@ -364,7 +388,8 @@ def _shutdown(self):
)
)

def cap_req(self, module_name, capability, arg=None, failure_callback=None):
def cap_req(self, module_name, capability, arg=None, failure_callback=None,
success_callback=None):
"""Tell Sopel to request a capability when it starts.

By prefixing the capability with `-`, it will be ensured that the
Expand All @@ -387,7 +412,12 @@ def cap_req(self, module_name, capability, arg=None, failure_callback=None):
request, the `failure_callback` function will be called, if provided.
The arguments will be a `Sopel` object, and the capability which was
rejected. This can be used to disable callables which rely on the
capability. In future versions
capability. It will be be called either if the server NAKs the request,
or if the server enabled it and later DELs it.

The `success_callback` function will be called upon acknowledgement of
the capability from the server, whether during the initial capability
negotiation, or later.

If ``arg`` is given, and does not exactly match what the server
provides or what other modules have requested for that capability, it is
Expand All @@ -398,16 +428,17 @@ def cap_req(self, module_name, capability, arg=None, failure_callback=None):
prefix = capability[0]

entry = self._cap_reqs.get(cap, [])
if any((ent[3] != arg for ent in entry)):
if any((ent.arg != arg for ent in entry)):
raise Exception('Capability conflict')

if prefix == '-':
if self.connection_registered and cap in self.enabled_capabilities:
raise Exception('Can not change capabilities after server '
'connection has been completed.')
if any((ent[0] != '-' for ent in entry)):
if any((ent.prefix != '-' for ent in entry)):
raise Exception('Capability conflict')
entry.append((prefix, module_name, failure_callback, arg))
entry.append(_CapReq(prefix, module_name, failure_callback, arg,
success_callback))
self._cap_reqs[cap] = entry
else:
if prefix != '=':
Expand All @@ -419,7 +450,8 @@ def cap_req(self, module_name, capability, arg=None, failure_callback=None):
'connection has been completed.')
# Non-mandatory will callback at the same time as if the server
# rejected it.
if any((ent[0] == '-' for ent in entry)) and prefix == '=':
if any((ent.prefix == '-' for ent in entry)) and prefix == '=':
raise Exception('Capability conflict')
entry.append((prefix, module_name, failure_callback, arg))
entry.append(_CapReq(prefix, module_name, failure_callback, arg,
success_callback))
self._cap_reqs[cap] = entry
Loading