commit e5543457399e602463cb4384645abf872d9eb11a Author: Pratyush Desai Date: Sat Jun 19 00:57:47 2021 +0530 Initial Commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ae412d6 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +env/ \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..0150878 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +Suite of tools to help Network Operators run their ErgoIRCd nets diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..2d9ffae --- /dev/null +++ b/__init__.py @@ -0,0 +1,71 @@ +### +# Copyright (c) 2021, mogad0n +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions, and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions, and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the author of this software nor the name of +# contributors to this software may be used to endorse or promote products +# derived from this software without specific prior written consent. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +### + +""" +EgoServ: Suite of tools to help Network Operators run their ErgoIRCd nets +""" + +import sys +import supybot +from supybot import world + +# Use this for the version of this plugin. +__version__ = "" + +# XXX Replace this with an appropriate author or supybot.Author instance. +__author__ = supybot.authors.unknown + +# This is a dictionary mapping supybot.Author instances to lists of +# contributions. +__contributors__ = {} + +# This is a url where the most recent plugin package can be downloaded. +__url__ = '' + +from . import config +from . import plugin +if sys.version_info >= (3, 4): + from importlib import reload +else: + from imp import reload +# In case we're being reloaded. +reload(config) +reload(plugin) +# Add more reloads here if you add third-party modules and want them to be +# reloaded when this plugin is reloaded. Don't forget to import them as well! + +if world.testing: + from . import test + +Class = plugin.Class +configure = config.configure + + +# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79: diff --git a/config.py b/config.py new file mode 100644 index 0000000..65b95ee --- /dev/null +++ b/config.py @@ -0,0 +1,56 @@ +### +# Copyright (c) 2021, mogad0n +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions, and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions, and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the author of this software nor the name of +# contributors to this software may be used to endorse or promote products +# derived from this software without specific prior written consent. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +### + +from supybot import conf, registry +try: + from supybot.i18n import PluginInternationalization + _ = PluginInternationalization('EgoServ') +except: + # Placeholder that allows to run the plugin on a bot + # without the i18n module + _ = lambda x: x + + +def configure(advanced): + # This will be called by supybot to configure this module. advanced is + # a bool that specifies whether the user identified themself as an advanced + # user or not. You should effect your configuration by manipulating the + # registry as appropriate. + from supybot.questions import expect, anything, something, yn + conf.registerPlugin('EgoServ', True) + + +EgoServ = conf.registerPlugin('EgoServ') +# This is where your configuration variables (if any) should go. For example: +# conf.registerGlobalValue(EgoServ, 'someConfigVariableName', +# registry.Boolean(False, _("""Help for someConfigVariableName."""))) + + +# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79: diff --git a/local/__init__.py b/local/__init__.py new file mode 100644 index 0000000..e86e97b --- /dev/null +++ b/local/__init__.py @@ -0,0 +1 @@ +# Stub so local is a module, used for third-party modules diff --git a/plugin.py b/plugin.py new file mode 100644 index 0000000..289d60d --- /dev/null +++ b/plugin.py @@ -0,0 +1,305 @@ +### +# Copyright (c) 2021, mogad0n +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions, and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions, and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the author of this software nor the name of +# contributors to this software may be used to endorse or promote products +# derived from this software without specific prior written consent. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +### + +from supybot import utils, plugins, ircutils, callbacks, irclib, ircmsgs, conf, world, log +from supybot.commands import * +try: + from supybot.i18n import PluginInternationalization + _ = PluginInternationalization('EgoServ') +except ImportError: + # Placeholder that allows to run the plugin on a bot + # without the i18n module + _ = lambda x: x + + +# import pickle +# import sys + +# filename = conf.supybot.directories.data.dirize("EgoServ.db") + +class EgoServ(callbacks.Plugin): + """Suite of tools to help Network Operators run their ErgoIRCd nets""" + threaded = True + + # OPER + # This should check if it has OPER right away + + # PERMS + # This is designed to be a single network ErgoIRCd administrative bot + # So maybe I just set default perms to owner! + + ##### + # WHO + ##### + + # Listen for 354; when recieved send payload for various queries + # and internal functions below + + # _isAccount() + + # IP address from `ircmsgs.who()` + # @wrap(['nick']) + # def getip(self, irc, msg, args, nick): + # """ + + # returns current IP address of a nick + # """ + + ###### + # OPER Commands + ###### + + # DEFCON + + @wrap([("literal", (1, 2, 3, 4, 5))]) + def defcon(self, irc, msg, args, level): + """ + + The DEFCON system can disable server features at runtime, to mitigate + spam or other hostile activity. It has five levels, which are cumulative + + 5: Normal operation + 4: No new account or channel registrations; if Tor is enabled, no new + unauthenticated connections from Tor + 3: All users are +R; no changes to vhosts + 2: No new unauthenticated connections; all channels are +R + 1: No new connections except from localhost or other trusted IPs + """ + + arg = [] + arg.append(level) + irc.sendMsg(msg=ircmsgs.IrcMsg(command='DEFCON', args=arg)) + irc.replySuccess(f'Setting DEFCON level to {arg}! Good Luck!') + + + # KILL + @wrap(['nick', optional('something')]) + def kill(self, irc, msg, args, nick, reason): + """ [] + + Issues a KILL command on the with if provided. + """ + arg = [nick] + if reason: + arg.append(reason) + irc.queueMsg(msg=ircmsgs.IrcMsg(command='KILL', + args=arg)) + irc.replySuccess(f'Killed connection for {nick}') + + # SAJOIN + @wrap([getopts({'nick': 'nick'}), 'channel']) + def sajoin(self, irc, msg, args, opts, channel): + """[--nick ] [] + + Forcibly joins a to a , ignoring restrictions like bans, user limits + and channel keys. If is omitted, it defaults to the bot itself. + is only necessary if the message is not sent on the channel itself + """ + opts = dict(opts) + arg = [] + if 'nick' in opts: + arg.append(opts['nick']) + arg.append(channel) + irc.queueMsg(msg=ircmsgs.IrcMsg(command='SAJOIN', + args=arg)) + if 'nick' in opts: + re = f"attempting to force join {opts['nick']} to {channel}" + else: + re = f'I am attempting to forcibly join {channel}' + irc.reply(re) + + # SANICK + @wrap(['nick', 'something']) + def sanick(self, irc, msg, args, current, new): + """ + + Issues a SANICK command and forcibly changes the nick to the nick + """ + arg = [current, new] + irc.queueMsg(msg=ircmsgs.IrcMsg(command='SANICK', + args=arg)) + # This isn't handling any nick collision errors + irc.reply(f'Forcing nick change for {current} to {new}') + + ##### + # NickServ + # Nick and Account Administration + ##### + + # Figure out some clean way to start talking to + # and parsing information from NS. For eg NS CLIENTS LIST + + + + ###### + # Channel Administration + ###### + + # Only Talking to CS isn't enough because + # CS doesn't handle expiring bans mutes + + + # Use mute Extban + + + # @wrap(['nick', 'channel', optional('expiry', 0), 'something']) + # def mute(self, irc, msg, args, nick, channel, duration, reason): + # @wrap(['nick', 'channel']) + # def mute(self, irc, msg, args, nick, channel): + # """ [] + + # Issues an extban which mutes the hostmask associated with the + # + # """ + # # add --all flag for muting the hostmask/account in all channels + # # on the current network + + # # Seconds needs to be human readable i.e. 5w 3d 2h + + # # Do we care about adding quiets/mutes for accounts not online + # # via --host specification or even an --account + # # Fuck 'duration' and 'reason' for now + + # # What I'm doing is very filthy + # # Refer to how the core channel plugin deals with this. + # # https://github.com/ProgVal/Limnoria/blob/master/plugins/Channel/plugin.py#L358 + + # try: + # (nick, ident, host) = ircutils.splitHostmask(irc.state.nickToHostmask(nick)) + # except KeyError: + # irc.error( + # "No such nick", + # Raise=True, + # ) + # if nick == irc.nick: + # self.log.warning(f'{msg.prefix} tried to make me mute myself.', ) + # irc.error(_('No, I would like to preserve my right to speech!')) + # return + + # irc.queueMsg() + + @wrap(['channel', 'something', many('nick')]) + def amode(self, irc, msg, args, channel, mode, nicks): + """[] [....] + + sets `CS AMODE` with on given / for + The nick/nicks must be registered and must be (de)voice, (de)hop, + (de)op or de(admin). + is only necessary if the message is not sent on the channel itself + """ + # label = ircutils.makeLabel() + if mode == 'voice': + flag = '+v' + elif mode == 'admin': + flag = '+a' + elif mode == 'hop': + flag = '+h' + elif mode == 'op': + flag = '+o' + elif mode == 'devoice': + flag = '-v' + elif mode == 'dehop': + flag = '-h' + elif mode == 'deadmin': + flag = '-a' + elif mode == 'deop': + flag = '-o' + else: + irc.error(f'Supplied mode {mode} is not allowed/valid') + + for nick in nicks: + irc.queueMsg(msg=ircmsgs.IrcMsg(command='PRIVMSG', + args=('chanserv', f'amode {channel} {flag} {nick}'))) + # Would love to handle responses for when it's not an account, + # https://github.com/ergochat/ergo/issues/1515 Waiting for this. + irc.replySuccess(f'Setting mode {flag} on given nick(s), if nick(s) weren\'t given the {flag} mode it/they is/are unregistered') + + @wrap([many('channel')]) + def chanreg(self, irc, msg, args, channels): + """[].. [..] + Registered the given channel/s by the bot + """ + + for channel in channels: + arg = ['register'] + arg.append(channel) + irc.queueMsg(msg=ircmsgs.IrcMsg(command='CS', args=arg)) + irc.reply('Registered the channel(s) successfully') + + + @wrap(['channel',('literal', ('users', 'access'))]) + def chanreset(self, irc, msg, args, channel, target): + """[] + + can either be 'users' (kicks all users except the bot) + or 'access' (resets all stored bans, invites, ban exceptions, + and persistent user-mode grants) for . + is only necessary if the message is not sent on the channel itself + """ + arg = ['CLEAR'] + arg.append(channel) + arg.append(target) + irc.queueMsg(msg=ircmsgs.IrcMsg(command='CS', + args=arg)) + if target == 'users': + irc.reply(f'Kicking all users out of {channel} besides me') + else: + irc.reply(f'Resetting bans and privileges for {channel}') + + # chanpurge() & chanunpurge() are deprecated + # as they now require confirmation codes. + + ##### + # HostServ Commands + ##### + + # Add sanity checks for vhost input? Parse server disagreements + @wrap(['nick', 'something']) + def setvhost(self, irc, msg, args, nick, vhost): + """ + + sets a 's . must be in the format 'hello.world' + """ + label = ircutils.makeLabel() + + arg = ['SET'] + arg.append(nick) + arg.append(vhost) + irc.queueMsg(msg=ircmsgs.IrcMsg(command='HS', + args=arg)) + # Doesn't handle not account error? + # Doesn't handle confirmation + irc.replySuccess(f'Setting Virtual Host {vhost} for {nick}') + + +Class = EgoServ + + +# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: diff --git a/test.py b/test.py new file mode 100644 index 0000000..ce28884 --- /dev/null +++ b/test.py @@ -0,0 +1,38 @@ +### +# Copyright (c) 2021, mogad0n +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions, and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions, and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the author of this software nor the name of +# contributors to this software may be used to endorse or promote products +# derived from this software without specific prior written consent. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +### + +from supybot.test import * + + +class EgoServTestCase(PluginTestCase): + plugins = ('EgoServ',) + + +# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79: