Compare commits

...

4 Commits

Author SHA1 Message Date
d06b8f2fdc refactor bot.channels, privileges and more 2019-10-08 11:45:21 -04:00
c09b4c04d3 refactor help mk2 2019-10-08 09:14:26 -04:00
bf6e5784f2 refactor help 2019-10-08 09:06:52 -04:00
6a067166fe refactor bot.commands and bot._callables 2019-10-08 07:52:37 -04:00
12 changed files with 141 additions and 248 deletions

151
bot.py
View File

@ -62,9 +62,6 @@ class Fulvia(irc.IRCClient):
in them.
"""
self.users = tools.FulviaMemory()
"""A dictionary of all users the bot is aware of."""
self.memory = tools.FulviaMemory()
"""
A thread-safe general purpose dictionary to be used by whatever
@ -76,12 +73,6 @@ class Fulvia(irc.IRCClient):
A class with some basic interactions for the bot's sqlite3 databse.
"""
self._callables = {}
"""
A dictionary containing all callable functions loaded from
modules. Keys are the functions name.
"""
self._hooks = []
"""
A list containing all function names to be hooked with every message
@ -120,7 +111,6 @@ class Fulvia(irc.IRCClient):
Find and load all of our modules.
"""
print(f"Loading modules...")
self._callables = {}
self._hooks = []
self.commands = {}
self._times = {}
@ -152,20 +142,11 @@ class Fulvia(irc.IRCClient):
convenient table.
"""
if hasattr(func, 'commands'):
self._callables[func.__name__] = func
for cmd in func.commands:
self.commands[cmd] = tools.Command(cmd)
self.commands[cmd]._func_name = func.__name__
self.commands[cmd].priv = func.priv
self.commands[cmd].doc = func._docs
if cmd in func.aliases:
self.commands[cmd].canonical = False
aliases = [a for a in func.commands if a != cmd]
self.commands[cmd].aliases = aliases
self.commands[cmd] = func
if func.hook:
self._callables[func.__name__] = func
self._hooks.append(func.__name__)
self._hooks.append(func)
if func.rate or func.channel_rate or func.global_rate:
self._times[func.__name__] = {}
@ -187,12 +168,10 @@ class Fulvia(irc.IRCClient):
return
if hasattr(func, 'commands'):
self._callables.pop(func.__name__)
for command in func.commands:
self.commands.pop(command)
if func.hook:
self._callables.pop(func.__name__)
self._hooks.remove(func.__name__)
if func.rate or func.channel_rate or func.global_rate:
@ -261,26 +240,25 @@ class Fulvia(irc.IRCClient):
"""
nick = user.partition("!")[0].partition("@")[0]
if channel.startswith("#"):
opSym = tools.getOpSym(self.channels[channel].privileges[nick])
opSym = self.channels[channel].users[nick].op_level
else:
opSym = ""
opSym = ''
channel = nick
line = "<" + opSym + nick + ">" + " " + message
line = f"<{opSym}{nick}> {message}"
self.log(channel, line)
func_names = []
funcs = []
if message.startswith(self.prefix) and message != self.prefix:
command, _, _ = message.partition(" ")
command = message.partition(" ")[0]
command = command.replace(self.prefix, "", 1)
cmd = self.commands.get(command)
if not cmd:
return
func_names.append(cmd._func_name)
funcs.append(cmd)
func_names += self._hooks
funcs += self._hooks
for func_name in func_names:
func = self._callables[func_name]
for func in funcs:
trigger = Trigger(user, channel, message, "PRIVMSG", self.config)
bot = FulviaWrapper(self, trigger)
@ -311,19 +289,20 @@ class Fulvia(irc.IRCClient):
def joined(self, channel):
"""Called when the bot joins a new channel."""
line = "-!- " + self.nickname + " " + "[" + self.username + "@"
line += self.host + "] has joined " + channel
line = f"-!- {self.nickname} [{self.username}@{self.host}] "
line += f"has joined {channel}"
self.log(channel, line)
print(f"Joined {channel}")
if channel not in self.channels:
self.channels[channel] = tools.Channel(channel)
self.channels[channel] = tools.Channel(channel)
user = tools.User(self.nickname)
self.channels[channel].users[self.nickname] = user
def left(self, channel, reason=""):
"""Called when the bot leaves a channel."""
line = "-!- " + self.nickname + " " + "[" + self.username + "@"
line += self.host + "] has left " + channel + " [" + reason + "]"
line = f"-!- {self.nickname} [{self.username}@{self.host}] "
line += f"has left {channel} [{reason}]"
self.log(channel, line)
print(f"Parted {channel}")
@ -338,7 +317,7 @@ class Fulvia(irc.IRCClient):
def modeChanged(self, user, channel, add_mode, modes, args):
"""Called when users or channel's modes are changed."""
line = "-!- mode/" + channel + " ["
line = f"-!- mode/{channel} ["
if add_mode:
line += "+"
else:
@ -361,11 +340,11 @@ class Fulvia(irc.IRCClient):
elif mode in tools.op_level.keys(): # user mode, op_level mode
nick = args[n]
op_level = tools.op_level[mode]
user = self.channels[channel].users[nick]
if add_mode:
self.channels[channel].privileges[nick] += op_level
user.op_level += mode
else:
self.channels[channel].privileges[nick] -= op_level
user.op_level.replace(mode, '', 1)
else: # user mode, non-op_level mode
continue
@ -378,8 +357,8 @@ class Fulvia(irc.IRCClient):
print(f"Signed on as {self.nickname}")
self.whois(self.nickname)
line = "*** Signed onto " + self.hostname + " as "
line += self.nickname + "!" + self.username + "@" + self.host
line = f"*** Signed onto {self.hostname} as "
line += f"{self.nickname}!{self.username}@{self.host}"
self.log(self.hostname, line)
for channel in self.config.core.channels.split(","):
@ -388,8 +367,8 @@ class Fulvia(irc.IRCClient):
def kickedFrom(self, channel, kicker, message):
"""Called when the bot is kicked from a channel."""
line = "-!- " + self.nickname + " was kicked from " + channel
line += " by " + kicker + " [" + message + "]"
line = f"-!- {self.nickname} was kicked from {channel} "
line += f"by {kicker} [{message}]"
self.log(channel, line)
self.channels.pop(channel)
@ -397,16 +376,14 @@ class Fulvia(irc.IRCClient):
def nickChanged(self, nick):
"""Called when the bot changes it's nickname."""
line = "-!- you are now known as " + nick
line = f"-!- you are now known as {nick}"
user = self.users.pop(self.nickname)
self.users[nick] = user
for key, channel in self.channels.items():
for channel_name, channel in self.channels.items():
self.log(key, line)
channel.rename_user(self.nickname, nick)
user.nick = nick
self.nickname = nick
user = channel.users.pop(self.nickname)
user.nick = nick
channel.users[nick] = user
## Actions the bot observes other users doing in the channel.
@ -416,12 +393,11 @@ class Fulvia(irc.IRCClient):
"""Called when the bot sees another user join a channel."""
nick, _, host = user.partition("!")
line = "-!- " + nick + " " + "[" + host + "] has joined " + channel
line = f"-!- {nick} [{host}] has joined {channel}"
self.log(channel, line)
if nick not in self.users:
self.users[nick] = tools.User(nick)
self.channels[channel].add_user(self.users[nick])
user = tools.User(nick)
self.channels[channel].users[nick] = user
for func in self._user_joined:
trigger = Trigger(user, channel, f"{user} has joined", "PRIVMSG", self.config)
@ -434,41 +410,38 @@ class Fulvia(irc.IRCClient):
"""Called when the bot sees another user leave a channel."""
nick, _, host = user.partition("!")
line = "-!- " + nick + " " + "[" + host + "] has left "
line += channel + " [" + reason + "]"
line = f"-!- {nick} [{host}] has left {channel} [{reason}]"
self.log(channel, line)
self.channels[channel].remove_user(nick)
self.channels[channel].users.pop(nick)
def userQuit(self, user, quitMessage):
"""Called when the bot sees another user disconnect from the network."""
nick, _, host = user.partition("!")
line = "-!- " + nick + " [" + host + "] has quit [" + quitMessage + "]"
line = "-!- {nick} [{host}] has quit [{quitMessage}]"
channels = list(self.users[nick].channels.keys())
for channel in channels:
for channel_name, channel in self.channels.items():
if not nick in channel.users:
continue
self.log(channel, line)
self.channels[channel].remove_user(nick)
self.users.pop(nick)
channel.users.pop(nick)
def userKicked(self, kickee, channel, kicker, message):
"""
Called when the bot sees another user getting kicked from the channel.
"""
line = "-!- " + kickee + " was kicked from " + channel
line += " by " + kicker + " [" + message + "]"
line =f"-!- {kickee} was kicked from {channel} by {kicker} [{message}]"
self.log(channel, line)
self.channels[channel].remove_user(kickee)
self.channels[channel].users.pop(kickee)
def topicUpdated(self, user, channel, newTopic):
"""Called when the bot sees a user update the channel's topic."""
line = "-!- " + user + " changed the topic of " + channel + " to: "
line += newTopic
line = f"-!- {user} changed the topic of {channel} to: {newTopic}"
self.log(channel, line)
self.channels[channel].topic = newTopic
@ -476,15 +449,14 @@ class Fulvia(irc.IRCClient):
def userRenamed(self, oldname, newname):
"""Called when the bot sees a user change their nickname."""
line = "-!- " + oldname + " is now known as " + newname
line = "-!- {oldname} is now known as {newname}"
user = self.users.pop(oldname)
self.users[newname] = user
for key, channel in user.channels.items():
self.log(key, line)
for channel_name, channel in self.channels.items():
self.log(channel_name, line)
channel.rename_user(oldname, newname)
user.nick = newname
user = channel.users.pop(oldname)
user.nick = newname
channel.users[newname] = user
def namesReceived(self, channel, channel_type, nicklist):
@ -494,12 +466,13 @@ class Fulvia(irc.IRCClient):
"""
self.channels[channel].channel_type = channel_type
for nick in nicklist:
op_level = tools.op_level.get(nick[0], 0)
if op_level > 0:
op_level = ''
if nick[0] in tools.op_level.keys():
op_level = nick[0]
nick = nick[1:]
self.users[nick] = tools.User(nick)
self.channels[channel].add_user(self.users[nick])
self.channels[channel].privileges[nick] = op_level
user = tools.User(nick)
user.op_level = op_level
self.channels[channel].users[nick] = user
def whoisUser(self, nick, ident, host, realname):
@ -510,6 +483,13 @@ class Fulvia(irc.IRCClient):
self.username = ident
self.host = host
self.realname = realname
else:
for channel_name, channel in self.channels.items():
if nick in channel:
user = channel[nick]
user.ident = ident
user.host = host
user.realname = realname
def whoisIdle(self, nick, idle, signon):
@ -534,11 +514,10 @@ class Fulvia(irc.IRCClient):
an appropriate length automatically.
"""
if user.startswith("#"):
priv = self.channels[user].privileges[self.nickname]
opSym = tools.getOpSym(priv)
opSym = self.channels[user].users[self.nickname].op_level
else:
opSym = ""
line = "<" + opSym + self.nickname + ">" + " " + message
opSym = ''
line = f"<{opSym}{self.nickname}> {message}"
self.log(user, line)
irc.IRCClient.msg(self, user, message, length=None)

View File

@ -30,6 +30,7 @@ def unload_module(bot, name):
bot.unregister_callable(obj)
del old_module
delattr(sys.modules['modules'], name.rpartition('.')[2])
del sys.modules[name]
@ -76,36 +77,10 @@ def process_callable(func, config):
Sets various helper atributes about a given function.
"""
prefix = config.core.prefix
doc = func.__doc__
if doc:
doc = doc.strip()
doc = doc.replace("\t", "")
doc = doc.replace("\n\n", "\x00")
doc = doc.replace("\n", " ")
doc = doc.replace("\x00", "\n")
func._docs = {}
func.example = getattr(func, "example", [(None, None)])
func.thread = getattr(func, "thread", True)
func.hook = getattr(func, "hook", False)
func.rate = getattr(func, "rate", 0)
func.channel_rate = getattr(func, "channel_rate", 0)
func.global_rate = getattr(func, "global_rate", 0)
func.priv = getattr(func, "priv", 0)
func.user_joined = getattr(func, "user_joined", False)
if hasattr(func, 'commands'):
if len(func.commands) > 1:
func.aliases = func.commands[1:]
else:
func.aliases = []
if hasattr(func, 'example'):
for n, example in enumerate(func.example):
ex_input = example[0]
if not ex_input:
continue
if ex_input[0] != prefix:
ex_input = prefix + ex_input
func.example[n] = (ex_input, example[1])
if doc:
func._docs = (doc, func.example)

View File

@ -146,32 +146,6 @@ def require_chanmsg(message=None):
return actual_decorator
def require_privilege(level, message=None):
"""
Decorate a function to require at least the given channel permission.
`level` can be one of the privilege levels defined in this module. If the
user does not have the privilege, `message` will be said if given. If it is
a private message, no checking will be done.
"""
def actual_decorator(function):
function.priv = level
@functools.wraps(function)
def guarded(bot, trigger, *args, **kwargs):
# If this is a privmsg, ignore privilege requirements
if trigger.is_privmsg or trigger.admin:
return function(bot, trigger, *args, **kwargs)
channel_privs = bot.channels[trigger.channel].privileges
allowed = channel_privs.get(trigger.nick, 0) >= level
if not allowed:
if message and not callable(message):
bot.msg(message)
else:
return function(bot, trigger, *args, **kwargs)
return guarded
return actual_decorator
def require_admin(message=None):
"""
Decorate a function to require the triggering user to be a bot admin.
@ -179,8 +153,7 @@ def require_admin(message=None):
If they are not, `message` will be said if given.
"""
def actual_decorator(function):
function.priv = 5
function.require_admin = True
@functools.wraps(function)
def guarded(bot, trigger, *args, **kwargs):
if not trigger.admin:
@ -202,8 +175,7 @@ def require_owner(message=None):
If they are not, `message` will be said if given.
"""
def actual_decorator(function):
function.priv = 10
function.require_owner = True
@functools.wraps(function)
def guarded(bot, trigger, *args, **kwargs):
if not trigger.owner:
@ -236,7 +208,7 @@ def example(ex_input, ex_output=None):
def url_callback(url):
"""
Decore a function with a callback to the URL module.
Decorate a function with a callback to the URL module.
This URL will be added to the bot.url_callbacks dictionary in the bot's
memory which the URL module will compare it's URL's against. If a key in

View File

@ -26,13 +26,11 @@ def new_bot_name(number):
@user_joined(True)
def adalwulf_(bot, trigger):
"""Renames adalwulf__."""
print(True)
print(trigger.nick)
if not trigger.nick.startswith('defaultnick'):
return
names = bot.channels[trigger.channel].users
adals = [nick for nick in names if nick.startswith('Adalwulf__')]
adals += [nick for nick in names if nick.startswith('Adalwulf_|')]
nicks = bot.channels[trigger.channel].users.keys()
adals = [nick for nick in nicks if nick.startswith('Adalwulf__')]
adals += [nick for nick in nicks if nick.startswith('Adalwulf_|')]
old_nick = trigger.nick
new_nick = new_bot_name(len(adals) + 1)
bot.sendLine(f"SANICK {old_nick} {new_nick}")
@ -42,12 +40,12 @@ def adalwulf_(bot, trigger):
@commands('rename_hydra')
def rename_hydra(bot, trigger):
"""Renames defaultnick's appropriately."""
for nick in list(bot.channels[trigger.channel].users.keys()):
for nick in bot.channels[trigger.channel].users.keys():
if not nick.startswith('defaultnick'):
continue
names = bot.channels[trigger.channel].users
adals = [nick for nick in names if nick.startswith('Adalwulf__')]
adals += [nick for nick in names if nick.startswith('Adalwulf_|')]
nicks = bot.channels[trigger.channel].users.keys()
adals = [nick for nick in nicks if nick.startswith('Adalwulf__')]
adals += [nick for nick in nicks if nick.startswith('Adalwulf_|')]
old_nick = nick
new_nick = new_bot_name(len(adals) + 1)
print(f"SANICK {old_nick} {new_nick}")

View File

@ -9,18 +9,17 @@ import re
import module
from tools import op_level, configureHostMask
OP = op_level["op"]
HALFOP = op_level["halfop"]
op = ['~', '!', '@', '%']
@module.require_chanmsg
@module.require_privilege(OP, 'You are not a channel operator.')
@module.require_admin
@module.commands('kick')
@module.example(".kick faggot being a faggot")
def kick(bot, trigger):
"""
Kick a user from the channel.
"""
if bot.channels[trigger.channel].privileges[bot.nick] < HALFOP:
if bot.channels[trigger.channel].users[bot.nick].op_level not in op:
return bot.reply("I'm not a channel operator!")
if not trigger.group(2):
return bot.reply("Who do you want me to kick?")
@ -36,7 +35,7 @@ def kick(bot, trigger):
@module.require_chanmsg
@module.require_privilege(OP, 'You are not a channel operator.')
@module.require_admin
@module.commands('ban')
@module.example(".ban faggot")
def ban(bot, trigger):
@ -44,7 +43,7 @@ def ban(bot, trigger):
This give admins the ability to ban a user.
The bot must be a Channel Operator for this command to work.
"""
if bot.channels[trigger.channel].privileges[bot.nick] < HALFOP:
if bot.channels[trigger.channel].users[bot.nick].op_level not in op:
return bot.reply("I'm not a channel operator!")
if not trigger.group(2):
return bot.reply("Who do you want me to ban?")
@ -54,7 +53,7 @@ def ban(bot, trigger):
@module.require_chanmsg
@module.require_privilege(OP, 'You are not a channel operator.')
@module.require_admin
@module.commands('unban')
@module.example(".unban faggot")
def unban(bot, trigger):
@ -62,7 +61,7 @@ def unban(bot, trigger):
This give admins the ability to unban a user.
The bot must be a Channel Operator for this command to work.
"""
if bot.channels[trigger.channel].privileges[bot.nick] < HALFOP:
if bot.channels[trigger.channel].users[bot.nick].op_level not in op:
return bot.reply("I'm not a channel operator!")
if not trigger.group(2):
return bot.reply("Who do you want me to ban?")
@ -72,7 +71,7 @@ def unban(bot, trigger):
@module.require_chanmsg
@module.require_privilege(OP, 'You are not a channel operator.')
@module.require_admin
@module.commands('kickban')
def kickban(bot, trigger):
"""
@ -80,7 +79,7 @@ def kickban(bot, trigger):
The bot must be a Channel Operator for this command to work.
.kickban [#chan] user1 user!*@module.* get out of here
"""
if bot.channels[trigger.channel].privileges[bot.nick] < HALFOP:
if bot.channels[trigger.channel].users[bot.nick].op_level not in op:
return bot.reply("I'm not a channel operator!")
if not trigger.group(2):
return bot.reply("Who do you want me to ban?")
@ -98,7 +97,7 @@ def kickban(bot, trigger):
@module.require_chanmsg
@module.require_privilege(OP, 'You are not a channel operator.')
@module.require_admin
@module.commands('settopic')
@module.example(".settopic We're discussing penises, would you like to join?")
def settopic(bot, trigger):
@ -106,7 +105,7 @@ def settopic(bot, trigger):
This gives ops the ability to change the topic.
The bot must be a Channel Operator for this command to work.
"""
if bot.channels[trigger.channel].privileges[bot.nick] < HALFOP:
if bot.channels[trigger.channel].users[bot.nick].op_level not in op:
return bot.reply("I'm not a channel operator!")
if not trigger.group(2):
return bot.reply("What do you want the topic set to?")

View File

@ -14,6 +14,6 @@ def announce(bot, trigger):
if not trigger.admin:
bot.reply("Sorry, I can't let you do that")
return
for channel in bot.channels:
for channel in bot.channels.keys():
bot.msg(channel, f"[ANNOUNCEMENT] {trigger.group(2)}")
bot.reply('Announce complete.')

View File

@ -54,7 +54,7 @@ def banheall(bot, trigger):
except (IndexError, KeyError, ValueError, TypeError):
period = 0
for nick in bot.channels[trigger.channel].users:
for nick in bot.channels[trigger.channel].users.keys():
banmask = configureHostMask(nick)
bot.mode(trigger.channel, True, "b", mask=banmask)
@ -66,7 +66,7 @@ def banheall(bot, trigger):
bot.msg(f"Banned \x0304them all\x03 for \x0309{str(period)}\x03 seconds.")
time.sleep(period)
for nick in bot.channels[trigger.channel].users:
for nick in bot.channels[trigger.channel].users.keys():
banmask = configureHostMask(nick)
bot.mode(trigger.channel, False, "b", mask=banmask)

View File

@ -7,38 +7,50 @@ import random
from module import commands, example
def clean_docstring(doc):
"""Cleans the docstring up a bit."""
if not doc:
return ''
doc = doc.strip()
doc = doc.replace("\t", "")
doc = doc.replace("\n\n", "\x00")
doc = doc.replace("\n", " ")
doc = doc.replace("\x00", "\n")
return doc
@commands('help', 'commands')
@example('.help tell')
def help(bot, trigger):
"""Shows a command's documentation, and possibly an example."""
if trigger.group(2):
name = trigger.group(2)
name = name.lower()
if name not in bot.commands:
command = trigger.group(2)
command = command.lower()
if command not in bot.commands:
return bot.msg("Command not found.")
cmd = bot.commands[name]
docstring, examples = cmd.doc
if examples:
ex = random.choice(examples)
bot.msg(docstring)
if cmd.aliases:
bot.msg("Aliases: " + ", ".join(cmd.aliases))
if ex[0]:
func = bot.commands[command]
doc = clean_docstring(func.__doc__)
bot.msg(doc)
aliases = [c for c, f in bot.commands.items() if f == func]
aliases.remove(command)
if aliases:
bot.msg("Aliases: " + ", ".join(aliases))
if hasattr(func, 'example'):
ex = random.choice(func.example)
bot.msg("Ex. In: " + ex[0])
if ex[1]:
bot.msg("Ex. Out: " + ex[1])
if ex[1]:
bot.msg("Ex. Out: " + ex[1])
else:
if trigger.owner:
cmds = [cmd for _, cmd in bot.commands.items()]
elif trigger.admin:
cmds = [cmd for _, cmd in bot.commands.items() if cmd.priv <= 5]
else:
priv = bot.channels[trigger.channel].privileges[trigger.nick]
cmds = [cmd for _, cmd in bot.commands.items() if cmd.priv <= priv]
funcs = [func for cmd, func in bot.commands.items()]
if not trigger.owner:
funcs = [f for f in funcs if not hasattr(f, 'require_owner')]
if not trigger.admin:
funcs = [f for f in funcs if not hasattr(f, 'require_admin')]
cmds = [cmd.name for cmd in cmds if cmd.canonical]
cmds = sorted(cmds)
cmds = {func.commands[0] for func in funcs}
cmds = sorted(list(cmds))
msg = "Available commands: " + ", ".join(cmds)
bot.msg(msg)

View File

@ -10,8 +10,8 @@ def pingAll(bot, trigger):
Says the nick of everyone in the channel. Great way to get thier
attention, or just annoy them.
"""
names = list(bot.channels[trigger.channel].users.keys())
nicks = list(bot.channels[trigger.channel].users.keys())
for nigger in ["Ishd", "Ishd2", "Ishd_"]:
if nigger in names:
names.remove(nigger)
bot.msg(" ".join(names))
if nigger in nicks:
nicks.remove(nigger)
bot.msg(" ".join(nicks))

View File

@ -72,4 +72,4 @@ def f_unload(bot, trigger):
return bot.msg(f"Module '{name}' not loaded, try the 'load' command.")
loader.unload_module(bot, name)
bot.msg(f"Module '{name}' unloaded.")
bot.msg(f"Module '{name}' unloaded.")

View File

@ -24,7 +24,6 @@ class MonitorThread(threading.Thread):
self.stop = threading.Event()
def run(self):
# while not self._bot.channels.keys():
while not self._bot.stillConnected():
time.sleep(1)
# don't try to say anything if we're not fully connected yet

View File

@ -111,8 +111,8 @@ class FulviaMemoryDefault(defaultdict):
return result
class User(object):
"""A representation of a user Fulvia is aware of."""
class User:
"""A representation of a user in a channel."""
def __init__(self, nick):
self.nick = nick
"""The user's nickname."""
@ -124,18 +124,21 @@ class User(object):
self.host = ""
"""The user's hostname."""
self.channels = {}
"""The channels the user is in."""
self.realname = ""
"""The user's realname."""
self.away = None
"""Whether the user is marked as away."""
hostmask = property(lambda self: '{}!{}@{}'.format(self.nick, self.user,
self.host))
"""The user's full hostmask."""
self.op_level = ''
"""The user's op level in this channel."""
def hostmask(self):
"""Returns the user's full hostmask."""
return f"{self.nick}!{self.user}@{self.host}"
class Channel(object):
class Channel:
"""A representation of a channel Fulvia is in."""
def __init__(self, name):
self.name = name
@ -151,55 +154,11 @@ class Channel(object):
"""The topic of the channel."""
self.users = {}
"""The users in the channel. A set to ensure there are no duplicates."""
self.privileges = {}
"""The op levels of the users in the channel."""
"""The users in the channel."""
self.modes = set()
"""The current modes on the channel."""
def remove_user(self, nick):
"""
Removes a user from the channel.
"""
user = self.users.pop(nick, None)
self.privileges.pop(nick, None)
if user != None:
user.channels.pop(self.name, None)
def add_user(self, user):
"""
Adds a user to the channel.
"""
assert isinstance(user, User)
self.users[user.nick] = user
self.privileges[user.nick] = 0
user.channels[self.name] = self
def rename_user(self, old, new):
"""
Renames the user.
"""
if old in self.users:
self.users[new] = self.users.pop(old)
if old in self.privileges:
self.privileges[new] = self.privileges.pop(old)
class Command():
"""
A representation of a command and associated documentation and other
atributes.
"""
def __init__(self, name):
self.name = name
self._func_name = ""
self.priv = 0
self.doc = None
self.canonical = True
self.aliases = []
def configureHostMask(mask):
"""