do some ircbot prospector cleanup

bss/dr.botzo#17
This commit is contained in:
Brian S. Stephan 2017-03-10 18:51:36 -06:00
parent dbc4e6fe6f
commit 474afe2576
7 changed files with 30 additions and 65 deletions

View File

@ -0,0 +1 @@
"""Core IRC bot code, extended by plugins."""

View File

@ -22,7 +22,6 @@ admin.site.register(IrcPlugin)
def send_privmsg(request):
"""Send a privmsg over XML-RPC to the IRC bot."""
if request.method == 'POST':
form = PrivmsgForm(request.POST)
if form.is_valid():

View File

@ -29,17 +29,18 @@ log = logging.getLogger('ircbot.bot')
class IrcBotXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
"""Override the basic request handler to change the logging."""
def log_message(self, format, *args):
"""Use a logger rather than stderr."""
log.debug("XML-RPC - %s - %s", self.client_address[0], format%args)
log.debug("XML-RPC - %s - %s", self.client_address[0], format % args)
class PrioritizedRegexHandler(collections.namedtuple('Base', ('priority', 'regex', 'callback'))):
"""Regex handler that still uses the normal handler priority stuff."""
def __lt__(self, other):
"when sorting prioritized handlers, only use the priority"
"""When sorting prioritized handlers, only use the priority"""
return self.priority < other.priority
@ -67,13 +68,11 @@ class DrReactor(irc.client.Reactor):
def __init__(self, on_connect=__do_nothing, on_disconnect=__do_nothing):
"""Initialize our custom stuff."""
super(DrReactor, self).__init__(on_connect=on_connect, on_disconnect=on_disconnect)
self.regex_handlers = {}
def server(self):
"""Creates and returns a ServerConnection object."""
c = LenientServerConnection(self)
with self.mutex:
self.connections.append(c)
@ -102,7 +101,6 @@ class DrReactor(irc.client.Reactor):
This is basically an extension of add_global_handler(), either may
work, though it turns out most modules probably want this one.
"""
if type(events) != list:
events = [events]
@ -123,7 +121,6 @@ class DrReactor(irc.client.Reactor):
Returns 1 on success, otherwise 0.
"""
ret = 1
if type(events) != list:
@ -131,7 +128,7 @@ class DrReactor(irc.client.Reactor):
for event in events:
with self.mutex:
if not event in self.regex_handlers:
if event not in self.regex_handlers:
ret = 0
for h in self.regex_handlers[event]:
if handler == h.callback:
@ -143,7 +140,6 @@ class DrReactor(irc.client.Reactor):
Also supports regex handlers.
"""
try:
log.debug("EVENT: e[%s] s[%s] t[%s] a[%s]", event.type, event.source,
event.target, event.arguments)
@ -233,7 +229,6 @@ class DrReactor(irc.client.Reactor):
event incoming event
"""
log.debug("RECURSING EVENT: e[%s] s[%s] t[%s] a[%s]", event.type, event.source,
event.target, event.arguments)
@ -244,7 +239,7 @@ class DrReactor(irc.client.Reactor):
log.debug("checking it against %s", attempt)
start_idx = attempt.find('[')
subcmd = attempt[start_idx+1:]
subcmd = attempt[start_idx + 1:]
end_idx = subcmd.rfind(']')
subcmd = subcmd[:end_idx]
@ -272,7 +267,7 @@ class DrReactor(irc.client.Reactor):
# the text of that event now is, we should replace the parent
# event's [] section with it.
oldtext = event.arguments[0]
newtext = oldtext.replace('['+subcmd+']', newevent.arguments[0])
newtext = oldtext.replace('[' + subcmd + ']', newevent.arguments[0])
log.debug("oldtext: '%s' newtext: '%s'", oldtext, newtext)
event.arguments[0] = newtext
@ -296,7 +291,6 @@ class DrReactor(irc.client.Reactor):
event incoming event
"""
replacement = False
replies = []
@ -358,6 +352,7 @@ class IRCBot(irc.client.SimpleIRCClient):
splitter = "..."
def __init__(self, reconnection_interval=60):
"""Initialize bot."""
super(IRCBot, self).__init__()
self.channels = IRCDict()
@ -477,7 +472,6 @@ class IRCBot(irc.client.SimpleIRCClient):
e.arguments[1] == channel
e.arguments[2] == nick list
"""
ch_type, channel, nick_list = e.arguments
if channel == '*':
@ -529,7 +523,6 @@ class IRCBot(irc.client.SimpleIRCClient):
event incoming event
"""
what = event.arguments[0]
log.debug("welcome: %s", what)
@ -563,7 +556,6 @@ class IRCBot(irc.client.SimpleIRCClient):
msg -- Quit message.
"""
self._xmlrpc_shutdown()
self.connection.disconnect(msg)
sys.exit(0)
@ -577,7 +569,6 @@ class IRCBot(irc.client.SimpleIRCClient):
msg -- Quit message.
"""
self.connection.disconnect(msg)
def get_version(self):
@ -585,14 +576,11 @@ class IRCBot(irc.client.SimpleIRCClient):
Used when answering a CTCP VERSION request.
"""
return "Python irc.bot ({version})".format(
version=irc.client.VERSION_STRING)
def jump_server(self, msg="Changing servers"):
"""Connect to a new server, potentially disconnecting from the current one."""
if self.connection.is_connected():
self.connection.disconnect(msg)
@ -605,7 +593,6 @@ class IRCBot(irc.client.SimpleIRCClient):
Replies to VERSION and PING requests and relays DCC requests
to the on_dccchat method.
"""
nick = e.source.nick
if e.arguments[0] == "VERSION":
c.ctcp_reply(nick, "VERSION " + self.get_version())
@ -616,6 +603,7 @@ class IRCBot(irc.client.SimpleIRCClient):
self.on_dccchat(c, e)
def on_dccchat(self, c, e):
"""Do nothing."""
pass
def handle_load(self, connection, event, match):
@ -631,7 +619,6 @@ class IRCBot(irc.client.SimpleIRCClient):
:param match: the matched regex (via add_global_regex_handler)
:type match: Match
"""
has_perm = ircbotlib.has_permission(event.source, 'ircbot.manage_loaded_plugins')
log.debug("has permission to load?: %s", str(has_perm))
if has_perm:
@ -655,7 +642,6 @@ class IRCBot(irc.client.SimpleIRCClient):
:param feedback: whether or not to send messages to IRC regarding the load attempt
:type feedback: bool
"""
log.debug("trying to load plugin %s", plugin_path)
dest = None
@ -718,7 +704,6 @@ class IRCBot(irc.client.SimpleIRCClient):
:param match: the matched regex (via add_global_regex_handler)
:type match: Match
"""
has_perm = ircbotlib.has_permission(event.source, 'ircbot.manage_loaded_plugins')
log.debug("has permission to reload?: %s", str(has_perm))
if has_perm:
@ -738,7 +723,6 @@ class IRCBot(irc.client.SimpleIRCClient):
:param feedback: whether or not to send messages to IRC regarding the reload attempt
:type feedback: bool
"""
log.debug("trying to unload plugin %s", plugin_path)
dest = ircbotlib.reply_destination_for_event(event)
@ -807,7 +791,6 @@ class IRCBot(irc.client.SimpleIRCClient):
:param match: the matched regex (via add_global_regex_handler)
:type match: Match
"""
has_perm = ircbotlib.has_permission(event.source, 'ircbot.manage_loaded_plugins')
log.debug("has permission to unload?: %s", str(has_perm))
if has_perm:
@ -828,7 +811,6 @@ class IRCBot(irc.client.SimpleIRCClient):
:param feedback: whether or not to send messages to IRC regarding the unload attempt
:type feedback: bool
"""
log.debug("trying to unload plugin %s", plugin_path)
dest = ircbotlib.reply_destination_for_event(event)
@ -866,7 +848,6 @@ class IRCBot(irc.client.SimpleIRCClient):
text the message to send
"""
if not target:
return
@ -895,7 +876,6 @@ class IRCBot(irc.client.SimpleIRCClient):
The replystr if the event is inside recursion, or, potentially,
"NO MORE" to stop other event handlers from acting.
"""
if event:
log.debug("in reply for e[%s] r[%s]", event, replystr)
replypath = ircbotlib.reply_destination_for_event(event)
@ -960,33 +940,27 @@ class IRCBot(irc.client.SimpleIRCClient):
:param name: the name to expose the method as
:type name: str
"""
if func and self.xmlrpc:
if hasattr(func, '__call__'):
self.xmlrpc.register_function(func, name)
def _xmlrpc_listen(self):
"""Begin listening. Hopefully this was called in a new thread."""
self.xmlrpc.serve_forever()
def _xmlrpc_shutdown(self):
"""Shut down the XML-RPC server."""
if self.xmlrpc is not None:
self.xmlrpc.shutdown()
self.xmlrpc.server_close()
def start(self):
"""Start the bot."""
self._connect()
super(IRCBot, self).start()
def sigint_handler(self, signal, frame):
"""Cleanly shutdown on SIGINT."""
log.debug("shutting down")
for path, plugin in self.plugins:
log.debug("trying to shut down %s", path)
@ -1002,6 +976,7 @@ class Channel(object):
"""A class for keeping information about an IRC channel."""
def __init__(self):
"""Initialize channel object."""
self.userdict = IRCDict()
self.operdict = IRCDict()
self.voiceddict = IRCDict()
@ -1011,64 +986,56 @@ class Channel(object):
def users(self):
"""Returns an unsorted list of the channel's users."""
return list(self.userdict.keys())
def opers(self):
"""Returns an unsorted list of the channel's operators."""
return list(self.operdict.keys())
def voiced(self):
"""Returns an unsorted list of the persons that have voice
mode set in the channel."""
"""Returns an unsorted list of the persons that have voice mode set in the channel."""
return list(self.voiceddict.keys())
def owners(self):
"""Returns an unsorted list of the channel's owners."""
return list(self.ownerdict.keys())
def halfops(self):
"""Returns an unsorted list of the channel's half-operators."""
return list(self.halfopdict.keys())
def has_user(self, nick):
"""Check whether the channel has a user."""
return nick in self.userdict
def is_oper(self, nick):
"""Check whether a user has operator status in the channel."""
return nick in self.operdict
def is_voiced(self, nick):
"""Check whether a user has voice mode set in the channel."""
return nick in self.voiceddict
def is_owner(self, nick):
"""Check whether a user has owner status in the channel."""
return nick in self.ownerdict
def is_halfop(self, nick):
"""Check whether a user has half-operator status in the channel."""
return nick in self.halfopdict
def add_user(self, nick):
"""Add user."""
self.userdict[nick] = 1
def remove_user(self, nick):
"""Remove user."""
for d in self.userdict, self.operdict, self.voiceddict:
if nick in d:
del d[nick]
def change_nick(self, before, after):
"""Handle a nick change."""
self.userdict[after] = self.userdict.pop(before)
if before in self.operdict:
self.operdict[after] = self.operdict.pop(before)
@ -1076,6 +1043,7 @@ class Channel(object):
self.voiceddict[after] = self.voiceddict.pop(before)
def set_userdetails(self, nick, details):
"""Set user details."""
if nick in self.userdict:
self.userdict[nick] = details
@ -1088,7 +1056,6 @@ class Channel(object):
value -- Value
"""
if mode == "o":
self.operdict[value] = 1
elif mode == "v":
@ -1109,7 +1076,6 @@ class Channel(object):
value -- Value
"""
try:
if mode == "o":
del self.operdict[value]
@ -1125,34 +1091,44 @@ class Channel(object):
pass
def has_mode(self, mode):
"""Return if mode is in channel modes."""
return mode in self.modes
def is_moderated(self):
"""Return if the channel is +m."""
return self.has_mode("m")
def is_secret(self):
"""Return if the channel is +s."""
return self.has_mode("s")
def is_protected(self):
"""Return if the channel is +p."""
return self.has_mode("p")
def has_topic_lock(self):
"""Return if the channel is +t."""
return self.has_mode("t")
def is_invite_only(self):
"""Return if the channel is +i."""
return self.has_mode("i")
def has_allow_external_messages(self):
"""Return if the channel is +n."""
return self.has_mode("n")
def has_limit(self):
"""Return if the channel is +l."""
return self.has_mode("l")
def limit(self):
"""Return the channel limit count."""
if self.has_limit():
return self.modes["l"]
else:
return None
def has_key(self):
"""Return if the channel is +k."""
return self.has_mode("k")

View File

@ -8,7 +8,6 @@ log = logging.getLogger('markov.forms')
class PrivmsgForm(Form):
"""Accept a privmsg to send to the ircbot."""
target = CharField()

View File

@ -0,0 +1 @@
"""Some basic-level IRC plugins."""

View File

@ -1,18 +1,17 @@
"""Echo given string back to the user/channel."""
import logging
from ircbot.lib import Plugin, reply_destination_for_event
from ircbot.lib import Plugin
log = logging.getLogger('ircbot.lib')
class Echo(Plugin):
"""Have IRC commands to do IRC things (join channels, quit, etc.)."""
def start(self):
"""Set up the handlers."""
self.connection.reactor.add_global_regex_handler(['pubmsg', 'privmsg'], r'^!echo\s+(.*)$',
self.handle_echo, -20)
@ -20,14 +19,12 @@ class Echo(Plugin):
def stop(self):
"""Tear down handlers."""
self.connection.reactor.remove_global_regex_handler(['pubmsg', 'privmsg'], self.handle_echo)
super(Echo, self).stop()
def handle_echo(self, connection, event, match):
"""Handle the echo command... by echoing."""
return self.bot.reply(event, match.group(1))

View File

@ -8,17 +8,14 @@ from django.core.exceptions import ObjectDoesNotExist
from ircbot.models import BotUser
log = logging.getLogger('ircbot.lib')
class Plugin(object):
"""Plugin base class."""
def __init__(self, bot, connection, event):
"""Initialization stuff here --- global handlers, configs from database, so on."""
self.bot = bot
self.connection = connection
self.event = event
@ -27,17 +24,14 @@ class Plugin(object):
def start(self):
"""Initialization stuff here --- global handlers, configs from database, so on."""
log.info("started %s", self.__class__.__name__)
def stop(self):
"""Teardown stuff here --- unregister handlers, for example."""
log.info("stopped %s", self.__class__.__name__)
def _unencode_xml(self, text):
"""Convert &lt;, &gt;, &amp; to their real entities."""
text = text.replace('&lt;', '<')
text = text.replace('&gt;', '>')
text = text.replace('&amp;', '&')
@ -46,7 +40,6 @@ class Plugin(object):
def has_permission(source, permission):
"""Check if the provided event source is a bot admin."""
try:
bot_user = BotUser.objects.get(nickmask=source)
log.debug("found bot user %s", bot_user)
@ -74,7 +67,6 @@ def reply_destination_for_event(event):
is the reply destination. Otherwise, the source (assumed to be the speaker
in a privmsg)'s nick is the reply destination.
"""
if irc.client.is_channel(event.target):
return event.target
else: