diff --git a/docs/technical/pmodule-spec.md b/docs/technical/pmodule-spec.md index 74dad79..d7c0018 100644 --- a/docs/technical/pmodule-spec.md +++ b/docs/technical/pmodule-spec.md @@ -1,6 +1,6 @@ # PyLink Protocol Module Specification -***Last updated for 2.1-alpha2 (2019-11-02).*** +***Last updated for 3.1-dev (2021-06-15).*** Starting with PyLink 2.x, a *protocol module* is any module containing a class derived from `PyLinkNetworkCore` (e.g. `InspIRCdProtocol`), along with a global `Class` attribute set equal to it (e.g. `Class = InspIRCdProtocol`). These modules do everything from managing connections to providing plugins with an API to send and receive data. New protocol modules may be implemented based off any of the classes in the following inheritance tree, with each containing a different amount of abstraction. @@ -82,6 +82,8 @@ Unless otherwise noted, the camel-case variants of command functions (e.g. "`spa - **`nick`**`(self, source, newnick)` - Changes the nick of a PyLink client. +- **`oper_notice`**`(self, source, target)` - Sends a notice to all operators on the network. + - **`notice`**`(self, source, target, text)` - Sends a NOTICE from a PyLink client or server. - **`numeric`**`(self, source, numeric, target, text)` - Sends a raw numeric `numeric` with `text` from the `source` server to `target`. This should raise `NotImplementedError` if not supported on a protocol. @@ -263,6 +265,8 @@ In short, protocol modules have some very important jobs. If any of these aren't 7) Declare the correct set of protocol module capabilities to prevent confusing PyLink's plugins. ## Changes to this document +* 2021-06-15 (3.1-dev) + - Added `oper_notice()` function to send notices to opers (GLOBOPS / OPERWALL on most IRCds) * 2019-11-02 (2.1-beta1) - Added protocol capability: `can-manage-bot-channels` * 2019-10-10 (2.1-beta1) diff --git a/protocols/hybrid.py b/protocols/hybrid.py index f20e358..0700036 100644 --- a/protocols/hybrid.py +++ b/protocols/hybrid.py @@ -147,6 +147,12 @@ class HybridProtocol(TS6Protocol): else: raise NotImplementedError("Changing field %r of a client is unsupported by this protocol." % field) + def oper_notice(self, source, text): + """ + Send a message to all opers. + """ + self._send_with_prefix(source, 'GLOBOPS :%s' % text) + def set_server_ban(self, source, duration, user='*', host='*', reason='User banned'): """ Sets a server ban. diff --git a/protocols/inspircd.py b/protocols/inspircd.py index be0cd67..de51e3a 100644 --- a/protocols/inspircd.py +++ b/protocols/inspircd.py @@ -334,6 +334,12 @@ class InspIRCdProtocol(TS6BaseProtocol): self.call_hooks([self.sid, 'CHGNAME', {'target': target, 'newgecos': text}]) + def oper_notice(self, source, text): + """ + Send a message to all opers. + """ + # <- :70M SNONOTICE G :From jlu5: aaaaaa + self._send_with_prefix(self.sid, 'SNONOTICE G :From %s: %s' % (self.get_friendly_name(source), text)) def numeric(self, source, numeric, target, text): """Sends raw numerics from a server to a remote client.""" diff --git a/protocols/ircs2s_common.py b/protocols/ircs2s_common.py index 533926d..acca097 100644 --- a/protocols/ircs2s_common.py +++ b/protocols/ircs2s_common.py @@ -385,6 +385,12 @@ class IRCS2SProtocol(IRCCommonProtocol): # handle_part() does that just fine. self.handle_part(target, 'KICK', [channel]) + def oper_notice(self, source, text): + """ + Send a message to all opers. + """ + self._send_with_prefix(source, 'WALLOPS :%s' % text) + def numeric(self, source, numeric, target, text): """Sends raw numerics from a server to a remote client. This is used for WHOIS replies.""" # Mangle the target for IRCds that require it. diff --git a/protocols/p10.py b/protocols/p10.py index 79263ec..5334dd9 100644 --- a/protocols/p10.py +++ b/protocols/p10.py @@ -526,6 +526,12 @@ class P10Protocol(IRCS2SProtocol): # <- AB 311 AyAAA jlu5 ~jlu5 nefarious.midnight.vpn * :realname self._send_with_prefix(source, '%s %s %s' % (numeric, target, text)) + def oper_notice(self, source, text): + """ + Send a message to all opers. + """ + self._send_with_prefix(source, 'WA :%s' % text) + def part(self, client, channel, reason=None): """Sends a part from a PyLink client.""" diff --git a/protocols/ts6.py b/protocols/ts6.py index 5f31b15..b54bc5a 100644 --- a/protocols/ts6.py +++ b/protocols/ts6.py @@ -117,6 +117,15 @@ class TS6Protocol(TS6BaseProtocol): self._channels[channel].users.add(client) self.users[client].channels.add(channel) + def oper_notice(self, source, text): + """ + Send a message to all opers. + """ + if self.is_internal_server(source): + # Charybdis TS6 only allows OPERWALL from users + source = self.pseudoclient.uid + self._send_with_prefix(source, 'OPERWALL :%s' % text) + def sjoin(self, server, channel, users, ts=None, modes=set()): """Sends an SJOIN for a group of users to a channel. diff --git a/protocols/unreal.py b/protocols/unreal.py index 84e55ac..5c367fb 100644 --- a/protocols/unreal.py +++ b/protocols/unreal.py @@ -312,6 +312,12 @@ class UnrealProtocol(TS6BaseProtocol): joinedmodes = self.join_modes(modes) self._send_with_prefix(target, 'UMODE2 %s' % joinedmodes) + def oper_notice(self, source, text): + """ + Send a message to all opers. + """ + self._send_with_prefix(source, 'GLOBOPS :%s' % text) + def set_server_ban(self, source, duration, user='*', host='*', reason='User banned'): """ Sets a server ban.