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

145
bot.py
View File

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

View File

@ -30,6 +30,7 @@ def unload_module(bot, name):
bot.unregister_callable(obj) bot.unregister_callable(obj)
del old_module del old_module
delattr(sys.modules['modules'], name.rpartition('.')[2])
del sys.modules[name] del sys.modules[name]
@ -76,36 +77,10 @@ def process_callable(func, config):
Sets various helper atributes about a given function. Sets various helper atributes about a given function.
""" """
prefix = config.core.prefix 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.thread = getattr(func, "thread", True)
func.hook = getattr(func, "hook", False) func.hook = getattr(func, "hook", False)
func.rate = getattr(func, "rate", 0) func.rate = getattr(func, "rate", 0)
func.channel_rate = getattr(func, "channel_rate", 0) func.channel_rate = getattr(func, "channel_rate", 0)
func.global_rate = getattr(func, "global_rate", 0) func.global_rate = getattr(func, "global_rate", 0)
func.priv = getattr(func, "priv", 0)
func.user_joined = getattr(func, "user_joined", False) 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 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): def require_admin(message=None):
""" """
Decorate a function to require the triggering user to be a bot admin. 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. If they are not, `message` will be said if given.
""" """
def actual_decorator(function): def actual_decorator(function):
function.priv = 5 function.require_admin = True
@functools.wraps(function) @functools.wraps(function)
def guarded(bot, trigger, *args, **kwargs): def guarded(bot, trigger, *args, **kwargs):
if not trigger.admin: if not trigger.admin:
@ -202,8 +175,7 @@ def require_owner(message=None):
If they are not, `message` will be said if given. If they are not, `message` will be said if given.
""" """
def actual_decorator(function): def actual_decorator(function):
function.priv = 10 function.require_owner = True
@functools.wraps(function) @functools.wraps(function)
def guarded(bot, trigger, *args, **kwargs): def guarded(bot, trigger, *args, **kwargs):
if not trigger.owner: if not trigger.owner:
@ -236,7 +208,7 @@ def example(ex_input, ex_output=None):
def url_callback(url): 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 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 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) @user_joined(True)
def adalwulf_(bot, trigger): def adalwulf_(bot, trigger):
"""Renames adalwulf__.""" """Renames adalwulf__."""
print(True)
print(trigger.nick)
if not trigger.nick.startswith('defaultnick'): if not trigger.nick.startswith('defaultnick'):
return return
names = bot.channels[trigger.channel].users nicks = bot.channels[trigger.channel].users.keys()
adals = [nick for nick in names if nick.startswith('Adalwulf__')] adals = [nick for nick in nicks if nick.startswith('Adalwulf__')]
adals += [nick for nick in names if nick.startswith('Adalwulf_|')] adals += [nick for nick in nicks if nick.startswith('Adalwulf_|')]
old_nick = trigger.nick old_nick = trigger.nick
new_nick = new_bot_name(len(adals) + 1) new_nick = new_bot_name(len(adals) + 1)
bot.sendLine(f"SANICK {old_nick} {new_nick}") bot.sendLine(f"SANICK {old_nick} {new_nick}")
@ -42,12 +40,12 @@ def adalwulf_(bot, trigger):
@commands('rename_hydra') @commands('rename_hydra')
def rename_hydra(bot, trigger): def rename_hydra(bot, trigger):
"""Renames defaultnick's appropriately.""" """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'): if not nick.startswith('defaultnick'):
continue continue
names = bot.channels[trigger.channel].users nicks = bot.channels[trigger.channel].users.keys()
adals = [nick for nick in names if nick.startswith('Adalwulf__')] adals = [nick for nick in nicks if nick.startswith('Adalwulf__')]
adals += [nick for nick in names if nick.startswith('Adalwulf_|')] adals += [nick for nick in nicks if nick.startswith('Adalwulf_|')]
old_nick = nick old_nick = nick
new_nick = new_bot_name(len(adals) + 1) new_nick = new_bot_name(len(adals) + 1)
print(f"SANICK {old_nick} {new_nick}") print(f"SANICK {old_nick} {new_nick}")

View File

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

View File

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

View File

@ -7,38 +7,50 @@ import random
from module import commands, example 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') @commands('help', 'commands')
@example('.help tell') @example('.help tell')
def help(bot, trigger): def help(bot, trigger):
"""Shows a command's documentation, and possibly an example.""" """Shows a command's documentation, and possibly an example."""
if trigger.group(2): if trigger.group(2):
name = trigger.group(2) command = trigger.group(2)
name = name.lower() command = command.lower()
if name not in bot.commands: if command not in bot.commands:
return bot.msg("Command not found.") return bot.msg("Command not found.")
cmd = bot.commands[name]
docstring, examples = cmd.doc
if examples:
ex = random.choice(examples)
bot.msg(docstring) func = bot.commands[command]
if cmd.aliases: doc = clean_docstring(func.__doc__)
bot.msg("Aliases: " + ", ".join(cmd.aliases)) bot.msg(doc)
if ex[0]:
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]) bot.msg("Ex. In: " + ex[0])
if ex[1]: if ex[1]:
bot.msg("Ex. Out: " + ex[1]) bot.msg("Ex. Out: " + ex[1])
else: else:
if trigger.owner: funcs = [func for cmd, func in bot.commands.items()]
cmds = [cmd for _, cmd in bot.commands.items()] if not trigger.owner:
elif trigger.admin: funcs = [f for f in funcs if not hasattr(f, 'require_owner')]
cmds = [cmd for _, cmd in bot.commands.items() if cmd.priv <= 5] if not trigger.admin:
else: funcs = [f for f in funcs if not hasattr(f, 'require_admin')]
priv = bot.channels[trigger.channel].privileges[trigger.nick]
cmds = [cmd for _, cmd in bot.commands.items() if cmd.priv <= priv]
cmds = [cmd.name for cmd in cmds if cmd.canonical] cmds = {func.commands[0] for func in funcs}
cmds = sorted(cmds) cmds = sorted(list(cmds))
msg = "Available commands: " + ", ".join(cmds) msg = "Available commands: " + ", ".join(cmds)
bot.msg(msg) 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 Says the nick of everyone in the channel. Great way to get thier
attention, or just annoy them. 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_"]: for nigger in ["Ishd", "Ishd2", "Ishd_"]:
if nigger in names: if nigger in nicks:
names.remove(nigger) nicks.remove(nigger)
bot.msg(" ".join(names)) bot.msg(" ".join(nicks))

View File

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

View File

@ -111,8 +111,8 @@ class FulviaMemoryDefault(defaultdict):
return result return result
class User(object): class User:
"""A representation of a user Fulvia is aware of.""" """A representation of a user in a channel."""
def __init__(self, nick): def __init__(self, nick):
self.nick = nick self.nick = nick
"""The user's nickname.""" """The user's nickname."""
@ -124,18 +124,21 @@ class User(object):
self.host = "" self.host = ""
"""The user's hostname.""" """The user's hostname."""
self.channels = {} self.realname = ""
"""The channels the user is in.""" """The user's realname."""
self.away = None self.away = None
"""Whether the user is marked as away.""" """Whether the user is marked as away."""
hostmask = property(lambda self: '{}!{}@{}'.format(self.nick, self.user, self.op_level = ''
self.host)) """The user's op level in this channel."""
"""The user's full hostmask."""
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.""" """A representation of a channel Fulvia is in."""
def __init__(self, name): def __init__(self, name):
self.name = name self.name = name
@ -151,55 +154,11 @@ class Channel(object):
"""The topic of the channel.""" """The topic of the channel."""
self.users = {} self.users = {}
"""The users in the channel. A set to ensure there are no duplicates.""" """The users in the channel."""
self.privileges = {}
"""The op levels of the users in the channel."""
self.modes = set() self.modes = set()
"""The current modes on the channel.""" """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): def configureHostMask(mask):
""" """