diff --git a/bot.py b/bot.py index 8fa73b5..e154973 100755 --- a/bot.py +++ b/bot.py @@ -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 @@ -243,11 +240,11 @@ 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) funcs = [] @@ -292,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}") @@ -319,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: @@ -342,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 @@ -359,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(","): @@ -369,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) @@ -378,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. @@ -397,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) @@ -415,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 @@ -457,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): @@ -475,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): @@ -491,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): @@ -515,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) diff --git a/loader.py b/loader.py index d9cb339..385911d 100755 --- a/loader.py +++ b/loader.py @@ -78,25 +78,9 @@ def process_callable(func, config): """ prefix = config.core.prefix - 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]) diff --git a/module.py b/module.py index c0ed17b..d41b504 100755 --- a/module.py +++ b/module.py @@ -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: diff --git a/modules/adalwulf__.py b/modules/adalwulf__.py index 75f3eea..d408e80 100644 --- a/modules/adalwulf__.py +++ b/modules/adalwulf__.py @@ -28,9 +28,9 @@ def adalwulf_(bot, trigger): """Renames adalwulf__.""" 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}") @@ -40,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}") diff --git a/modules/adminchannel.py b/modules/adminchannel.py index 39ec0cf..92de285 100755 --- a/modules/adminchannel.py +++ b/modules/adminchannel.py @@ -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?") diff --git a/modules/announce.py b/modules/announce.py index ade9874..117118c 100755 --- a/modules/announce.py +++ b/modules/announce.py @@ -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.') diff --git a/modules/banhe.py b/modules/banhe.py index 3f82da4..1d9fb13 100755 --- a/modules/banhe.py +++ b/modules/banhe.py @@ -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) diff --git a/modules/help.py b/modules/help.py index e50389e..de2afaf 100755 --- a/modules/help.py +++ b/modules/help.py @@ -44,13 +44,11 @@ def help(bot, trigger): bot.msg("Ex. Out: " + ex[1]) else: - if trigger.owner: - funcs = [func for cmd, func in bot.commands.items()] - elif trigger.admin: - funcs = [func for cmd, func in bot.commands.items() if cmd.priv <= 5] - else: - priv = bot.channels[trigger.channel].privileges[trigger.nick] - funcs = [func for cmd, func 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 = {func.commands[0] for func in funcs} cmds = sorted(list(cmds)) diff --git a/modules/pingall.py b/modules/pingall.py index 2cabee7..1e22e8a 100755 --- a/modules/pingall.py +++ b/modules/pingall.py @@ -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)) diff --git a/modules/remind.py b/modules/remind.py index 3879b0c..f8b47e3 100755 --- a/modules/remind.py +++ b/modules/remind.py @@ -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 diff --git a/tools/__init__.py b/tools/__init__.py index 05da197..afdd6bf 100755 --- a/tools/__init__.py +++ b/tools/__init__.py @@ -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,41 +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) - def configureHostMask(mask): """