Compare commits
7 Commits
0ea54a5ee2
...
f898f35ce6
Author | SHA1 | Date |
---|---|---|
Brian S. Stephan | f898f35ce6 | |
Brian S. Stephan | 4289f95800 | |
Brian S. Stephan | 572ecddceb | |
Brian S. Stephan | 3aadde4b71 | |
Brian S. Stephan | c2d26f404e | |
Brian S. Stephan | ecaabbce89 | |
Brian S. Stephan | 051e656a82 |
222
ircbot/bot.py
222
ircbot/bot.py
|
@ -1,46 +1,36 @@
|
|||
"""Provide the base IRC client bot which other code can latch onto."""
|
||||
|
||||
import bisect
|
||||
import collections
|
||||
import copy
|
||||
import importlib
|
||||
import logging
|
||||
import re
|
||||
from xmlrpc.server import SimpleXMLRPCServer, SimpleXMLRPCRequestHandler
|
||||
import socket
|
||||
import ssl
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
from xmlrpc.server import SimpleXMLRPCRequestHandler, SimpleXMLRPCServer
|
||||
|
||||
from django.conf import settings
|
||||
|
||||
import irc.buffer
|
||||
import irc.client
|
||||
import irc.modes
|
||||
from irc.bot import Channel
|
||||
from irc.connection import Factory
|
||||
from irc.dict import IRCDict
|
||||
import irc.modes
|
||||
from jaraco.stream import buffer
|
||||
|
||||
import ircbot.lib as ircbotlib
|
||||
from dr_botzo import __version__
|
||||
from ircbot.models import Alias, IrcChannel, IrcPlugin, IrcServer
|
||||
|
||||
|
||||
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)
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
|
@ -52,7 +42,7 @@ class LenientServerConnection(irc.client.ServerConnection):
|
|||
method on a Reactor object.
|
||||
"""
|
||||
|
||||
buffer_class = irc.buffer.LenientDecodingLineBuffer
|
||||
buffer_class = buffer.LenientDecodingLineBuffer
|
||||
|
||||
server_config = None
|
||||
|
||||
|
@ -65,6 +55,9 @@ class LenientServerConnection(irc.client.ServerConnection):
|
|||
class DrReactor(irc.client.Reactor):
|
||||
"""Customize the basic IRC library's Reactor with more features."""
|
||||
|
||||
# used by Reactor.server() to initialize
|
||||
connection_class = LenientServerConnection
|
||||
|
||||
def __do_nothing(*args, **kwargs):
|
||||
pass
|
||||
|
||||
|
@ -73,18 +66,10 @@ class DrReactor(irc.client.Reactor):
|
|||
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)
|
||||
return c
|
||||
|
||||
def add_global_regex_handler(self, events, regex, handler, priority=0):
|
||||
"""Adds a global handler function for a specific event type and regex.
|
||||
"""Add a global handler function for a specific event type and regex.
|
||||
|
||||
Arguments:
|
||||
|
||||
events --- Event type(s) (a list of strings).
|
||||
|
||||
handler -- Callback function taking connection and event
|
||||
|
@ -114,10 +99,9 @@ class DrReactor(irc.client.Reactor):
|
|||
bisect.insort(event_regex_handlers, handler)
|
||||
|
||||
def remove_global_regex_handler(self, events, handler):
|
||||
"""Removes a global regex handler function.
|
||||
"""Remove a global regex handler function.
|
||||
|
||||
Arguments:
|
||||
|
||||
events -- Event type(s) (a list of strings).
|
||||
handler -- Callback function.
|
||||
|
||||
|
@ -228,8 +212,7 @@ class DrReactor(irc.client.Reactor):
|
|||
if result == "NO MORE":
|
||||
return
|
||||
except Exception as ex:
|
||||
log.error("caught exception!")
|
||||
log.exception(ex)
|
||||
log.exception("caught exception!")
|
||||
connection.privmsg(event.target, str(ex))
|
||||
|
||||
def try_recursion(self, connection, event):
|
||||
|
@ -416,7 +399,7 @@ class IRCBot(irc.client.SimpleIRCClient):
|
|||
|
||||
# load XML-RPC server
|
||||
self.xmlrpc = SimpleXMLRPCServer((self.server_config.xmlrpc_host, self.server_config.xmlrpc_port),
|
||||
requestHandler=IrcBotXMLRPCRequestHandler, allow_none=True)
|
||||
requestHandler=SimpleXMLRPCRequestHandler, allow_none=True)
|
||||
self.xmlrpc.register_introspection_functions()
|
||||
|
||||
t = threading.Thread(target=self._xmlrpc_listen, args=())
|
||||
|
@ -429,8 +412,7 @@ class IRCBot(irc.client.SimpleIRCClient):
|
|||
|
||||
def _connected_checker(self):
|
||||
if not self.connection.is_connected():
|
||||
self.connection.execute_delayed(self.reconnection_interval,
|
||||
self._connected_checker)
|
||||
self.reactor.scheduler.execute_after(self.reconnection_interval, self._connected_checker)
|
||||
self.jump_server()
|
||||
|
||||
def _connect(self):
|
||||
|
@ -448,8 +430,7 @@ class IRCBot(irc.client.SimpleIRCClient):
|
|||
|
||||
def _on_disconnect(self, c, e):
|
||||
self.channels = IRCDict()
|
||||
self.connection.execute_delayed(self.reconnection_interval,
|
||||
self._connected_checker)
|
||||
self.reactor.scheduler.execute_after(self.reconnection_interval, self._connected_checker)
|
||||
|
||||
def _on_join(self, c, e):
|
||||
ch = e.target
|
||||
|
@ -558,7 +539,7 @@ class IRCBot(irc.client.SimpleIRCClient):
|
|||
|
||||
for chan in IrcChannel.objects.filter(autojoin=True, server=connection.server_config):
|
||||
log.info("autojoining %s", chan.name)
|
||||
self.connection.join(chan)
|
||||
self.connection.join(chan.name)
|
||||
|
||||
for plugin in IrcPlugin.objects.filter(autoload=True):
|
||||
log.info("autoloading %s", plugin.path)
|
||||
|
@ -592,12 +573,11 @@ class IRCBot(irc.client.SimpleIRCClient):
|
|||
self.connection.disconnect(msg)
|
||||
|
||||
def get_version(self):
|
||||
"""Returns the bot version.
|
||||
"""Return the bot version.
|
||||
|
||||
Used when answering a CTCP VERSION request.
|
||||
"""
|
||||
return "Python irc.bot ({version})".format(
|
||||
version=irc.client.VERSION_STRING)
|
||||
return f"dr.botzo {__version__}"
|
||||
|
||||
def jump_server(self, msg="Changing servers"):
|
||||
"""Connect to a new server, potentially disconnecting from the current one."""
|
||||
|
@ -607,7 +587,7 @@ class IRCBot(irc.client.SimpleIRCClient):
|
|||
self._connect()
|
||||
|
||||
def on_ctcp(self, c, e):
|
||||
"""Default handler for ctcp events.
|
||||
"""Handle for ctcp events.
|
||||
|
||||
Replies to VERSION and PING requests and relays DCC requests
|
||||
to the on_dccchat method.
|
||||
|
@ -997,165 +977,3 @@ class IRCBot(irc.client.SimpleIRCClient):
|
|||
del sys.modules[path]
|
||||
|
||||
self.die(msg="Shutting down...")
|
||||
|
||||
|
||||
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()
|
||||
self.ownerdict = IRCDict()
|
||||
self.halfopdict = IRCDict()
|
||||
self.modes = {}
|
||||
|
||||
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."""
|
||||
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)
|
||||
if before in self.voiceddict:
|
||||
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
|
||||
|
||||
def set_mode(self, mode, value=None):
|
||||
"""Set mode on the channel.
|
||||
|
||||
Arguments:
|
||||
|
||||
mode -- The mode (a single-character string).
|
||||
|
||||
value -- Value
|
||||
"""
|
||||
if mode == "o":
|
||||
self.operdict[value] = 1
|
||||
elif mode == "v":
|
||||
self.voiceddict[value] = 1
|
||||
elif mode == "q":
|
||||
self.ownerdict[value] = 1
|
||||
elif mode == "h":
|
||||
self.halfopdict[value] = 1
|
||||
else:
|
||||
self.modes[mode] = value
|
||||
|
||||
def clear_mode(self, mode, value=None):
|
||||
"""Clear mode on the channel.
|
||||
|
||||
Arguments:
|
||||
|
||||
mode -- The mode (a single-character string).
|
||||
|
||||
value -- Value
|
||||
"""
|
||||
try:
|
||||
if mode == "o":
|
||||
del self.operdict[value]
|
||||
elif mode == "v":
|
||||
del self.voiceddict[value]
|
||||
elif mode == "q":
|
||||
del self.ownerdict[value]
|
||||
elif mode == "h":
|
||||
del self.halfopdict[value]
|
||||
else:
|
||||
del self.modes[mode]
|
||||
except KeyError:
|
||||
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")
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#
|
||||
# This file is autogenerated by pip-compile with Python 3.8
|
||||
# This file is autogenerated by pip-compile with Python 3.10
|
||||
# by the following command:
|
||||
#
|
||||
# pip-compile --output-file=requirements/requirements-dev.txt requirements/requirements-dev.in
|
||||
|
@ -22,7 +22,7 @@ click==8.1.3
|
|||
# via
|
||||
# pip-tools
|
||||
# safety
|
||||
coverage[toml]==7.1.0
|
||||
coverage[toml]==7.2.1
|
||||
# via pytest-cov
|
||||
distlib==0.3.6
|
||||
# via virtualenv
|
||||
|
@ -77,19 +77,15 @@ flake8-mutable==1.2.0
|
|||
# via -r requirements/requirements-dev.in
|
||||
gitdb==4.0.10
|
||||
# via gitpython
|
||||
gitpython==3.1.30
|
||||
gitpython==3.1.31
|
||||
# via bandit
|
||||
idna==3.4
|
||||
# via requests
|
||||
importlib-resources==5.10.2
|
||||
# via jaraco-text
|
||||
inflect==6.0.2
|
||||
# via
|
||||
# jaraco-itertools
|
||||
# jaraco-text
|
||||
# via jaraco-text
|
||||
iniconfig==2.0.0
|
||||
# via pytest
|
||||
irc==15.0.6
|
||||
irc==20.1.0
|
||||
# via -r requirements/requirements.in
|
||||
isort==5.12.0
|
||||
# via flake8-isort
|
||||
|
@ -99,13 +95,11 @@ jaraco-collections==3.8.0
|
|||
# via irc
|
||||
jaraco-context==4.3.0
|
||||
# via jaraco-text
|
||||
jaraco-functools==3.5.2
|
||||
jaraco-functools==3.6.0
|
||||
# via
|
||||
# irc
|
||||
# jaraco-text
|
||||
# tempora
|
||||
jaraco-itertools==6.2.1
|
||||
# via irc
|
||||
jaraco-logging==3.1.2
|
||||
# via irc
|
||||
jaraco-stream==3.0.3
|
||||
|
@ -116,12 +110,11 @@ jaraco-text==3.11.1
|
|||
# jaraco-collections
|
||||
mccabe==0.7.0
|
||||
# via flake8
|
||||
more-itertools==9.0.0
|
||||
more-itertools==9.1.0
|
||||
# via
|
||||
# irc
|
||||
# jaraco-classes
|
||||
# jaraco-functools
|
||||
# jaraco-itertools
|
||||
# jaraco-text
|
||||
packaging==21.3
|
||||
# via
|
||||
|
@ -134,7 +127,7 @@ parsedatetime==2.6
|
|||
# via -r requirements/requirements.in
|
||||
pbr==5.11.1
|
||||
# via stevedore
|
||||
pip-tools==6.12.2
|
||||
pip-tools==6.12.3
|
||||
# via -r requirements/requirements-dev.in
|
||||
platformdirs==3.0.0
|
||||
# via virtualenv
|
||||
|
@ -197,7 +190,6 @@ safety==2.3.5
|
|||
# via -r requirements/requirements-dev.in
|
||||
six==1.16.0
|
||||
# via
|
||||
# irc
|
||||
# python-dateutil
|
||||
# tox
|
||||
smmap==5.0.0
|
||||
|
@ -233,7 +225,7 @@ urllib3==1.26.14
|
|||
# via requests
|
||||
versioneer==0.28
|
||||
# via -r requirements/requirements-dev.in
|
||||
virtualenv==20.19.0
|
||||
virtualenv==20.20.0
|
||||
# via tox
|
||||
wheel==0.38.4
|
||||
# via
|
||||
|
@ -241,8 +233,6 @@ wheel==0.38.4
|
|||
# tox-wheel
|
||||
zalgo-text==0.6
|
||||
# via -r requirements/requirements.in
|
||||
zipp==3.13.0
|
||||
# via importlib-resources
|
||||
|
||||
# The following packages are considered to be unsafe in a requirements file:
|
||||
# pip
|
||||
|
|
|
@ -3,7 +3,7 @@ django-adminplus # admin.site.register_view
|
|||
django-bootstrap3 # bootstrap layout
|
||||
django-extensions # more commands
|
||||
djangorestframework # dispatch WS API
|
||||
irc==15.0.6 # core, pinned until I can bother to update --- 17.x has API changes
|
||||
irc # core
|
||||
parsedatetime # relative date stuff in countdown
|
||||
ply # dice lex/yacc compiler
|
||||
python-dateutil # countdown relative math
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#
|
||||
# This file is autogenerated by pip-compile with Python 3.8
|
||||
# This file is autogenerated by pip-compile with Python 3.10
|
||||
# by the following command:
|
||||
#
|
||||
# pip-compile --output-file=requirements/requirements.txt requirements/requirements.in
|
||||
|
@ -28,13 +28,9 @@ djangorestframework==3.14.0
|
|||
# via -r requirements/requirements.in
|
||||
idna==3.4
|
||||
# via requests
|
||||
importlib-resources==5.10.2
|
||||
# via jaraco-text
|
||||
inflect==6.0.2
|
||||
# via
|
||||
# jaraco-itertools
|
||||
# jaraco-text
|
||||
irc==15.0.6
|
||||
# via jaraco-text
|
||||
irc==20.1.0
|
||||
# via -r requirements/requirements.in
|
||||
jaraco-classes==3.2.3
|
||||
# via jaraco-collections
|
||||
|
@ -42,13 +38,11 @@ jaraco-collections==3.8.0
|
|||
# via irc
|
||||
jaraco-context==4.3.0
|
||||
# via jaraco-text
|
||||
jaraco-functools==3.5.2
|
||||
jaraco-functools==3.6.0
|
||||
# via
|
||||
# irc
|
||||
# jaraco-text
|
||||
# tempora
|
||||
jaraco-itertools==6.2.1
|
||||
# via irc
|
||||
jaraco-logging==3.1.2
|
||||
# via irc
|
||||
jaraco-stream==3.0.3
|
||||
|
@ -57,12 +51,11 @@ jaraco-text==3.11.1
|
|||
# via
|
||||
# irc
|
||||
# jaraco-collections
|
||||
more-itertools==9.0.0
|
||||
more-itertools==9.1.0
|
||||
# via
|
||||
# irc
|
||||
# jaraco-classes
|
||||
# jaraco-functools
|
||||
# jaraco-itertools
|
||||
# jaraco-text
|
||||
parsedatetime==2.6
|
||||
# via -r requirements/requirements.in
|
||||
|
@ -90,9 +83,7 @@ requests==2.28.2
|
|||
requests-toolbelt==0.10.1
|
||||
# via python-gitlab
|
||||
six==1.16.0
|
||||
# via
|
||||
# irc
|
||||
# python-dateutil
|
||||
# via python-dateutil
|
||||
sqlparse==0.4.3
|
||||
# via django
|
||||
tempora==5.2.1
|
||||
|
@ -105,5 +96,3 @@ urllib3==1.26.14
|
|||
# via requests
|
||||
zalgo-text==0.6
|
||||
# via -r requirements/requirements.in
|
||||
zipp==3.13.0
|
||||
# via importlib-resources
|
||||
|
|
Loading…
Reference in New Issue