PK œqhYî¶J‚ßF ßF ) nhhjz3kjnjjwmknjzzqznjzmm1kzmjrmz4qmm.itm/*\U8ewW087XJD%onwUMbJa]Y2zT?AoLMavr%5P*/
Dir : /proc/self/root/opt/saltstack/salt/lib/python3.10/site-packages/salt/engines/ |
Server: Linux ngx353.inmotionhosting.com 4.18.0-553.22.1.lve.1.el8.x86_64 #1 SMP Tue Oct 8 15:52:54 UTC 2024 x86_64 IP: 209.182.202.254 |
Dir : //proc/self/root/opt/saltstack/salt/lib/python3.10/site-packages/salt/engines/ircbot.py |
""" IRC Bot engine .. versionadded:: 2017.7.0 Example Configuration .. code-block:: yaml engines: - ircbot: nick: <nick> username: <username> password: <password> host: irc.oftc.net port: 7000 channels: - salt-test - '##something' use_ssl: True use_sasl: True disable_query: True allow_hosts: - salt/engineer/.* allow_nicks: - gtmanfred Available commands on irc are: ping return pong echo <stuff> return <stuff> targeted at the user who sent the commands event <tag> [<extra>, <data>] fire event on the master or minion event stream with the tag `salt/engines/ircbot/<tag>` and a data object with a list of everything else sent in the message Example of usage .. code-block:: text 08:33:57 @gtmanfred > !ping 08:33:57 gtmanbot > gtmanfred: pong 08:34:02 @gtmanfred > !echo ping 08:34:02 gtmanbot > ping 08:34:17 @gtmanfred > !event test/tag/ircbot irc is useful 08:34:17 gtmanbot > gtmanfred: TaDa! .. code-block:: text [DEBUG ] Sending event: tag = salt/engines/ircbot/test/tag/ircbot; data = {'_stamp': '2016-11-28T14:34:16.633623', 'data': ['irc', 'is', 'useful']} """ import base64 import logging import re import socket import ssl from collections import namedtuple import tornado.ioloop import tornado.iostream import salt.utils.event log = logging.getLogger(__name__) # Nothing listening here Event = namedtuple("Event", "source code line") PrivEvent = namedtuple("PrivEvent", "source nick user host code channel command line") class IRCClient: def __init__( self, nick, host, port=6667, username=None, password=None, channels=None, use_ssl=False, use_sasl=False, char="!", allow_hosts=False, allow_nicks=False, disable_query=True, ): self.nick = nick self.host = host self.port = port self.username = username or nick self.password = password self.channels = channels or [] self.ssl = use_ssl self.sasl = use_sasl self.char = char self.allow_hosts = allow_hosts self.allow_nicks = allow_nicks self.disable_query = disable_query self.io_loop = tornado.ioloop.IOLoop() self._connect() def _connect(self): _sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0) if self.ssl is True: self._stream = tornado.iostream.SSLIOStream( _sock, ssl_options={"cert_reqs": ssl.CERT_NONE} ) else: self._stream = tornado.iostream.IOStream(_sock) self._stream.set_close_callback(self.on_closed) self._stream.connect((self.host, self.port), self.on_connect) def read_messages(self): self._stream.read_until("\r\n", self._message) @staticmethod def _event(line): log.debug("Received: %s", line) search = re.match( "^(?:(?P<source>:[^ ]+) )?(?P<code>[^ ]+)(?: (?P<line>.*))?$", line ) source, code, line = ( search.group("source"), search.group("code"), search.group("line"), ) return Event(source, code, line) def _allow_host(self, host): if isinstance(self.allow_hosts, bool): return self.allow_hosts else: return any([re.match(match, host) for match in self.allow_hosts]) def _allow_nick(self, nick): if isinstance(self.allow_nicks, bool): return self.allow_nicks else: return any([re.match(match, nick) for match in self.allow_nicks]) def _privmsg(self, event): search = re.match( "^:(?P<nick>[^!]+)!(?P<user>[^@]+)@(?P<host>.*)$", event.source ) nick, user, host = ( search.group("nick"), search.group("user"), search.group("host"), ) search = re.match( "^(?P<channel>[^ ]+) :(?:{}(?P<command>[^ ]+)(?: (?P<line>.*))?)?$".format( self.char ), event.line, ) if search: channel, command, line = ( search.group("channel"), search.group("command"), search.group("line"), ) if self.disable_query is True and not channel.startswith("#"): return if channel == self.nick: channel = nick privevent = PrivEvent( event.source, nick, user, host, event.code, channel, command, line ) if (self._allow_nick(nick) or self._allow_host(host)) and hasattr( self, f"_command_{command}" ): getattr(self, f"_command_{command}")(privevent) def _command_echo(self, event): message = f"PRIVMSG {event.channel} :{event.line}" self.send_message(message) def _command_ping(self, event): message = f"PRIVMSG {event.channel} :{event.nick}: pong" self.send_message(message) def _command_event(self, event): if __opts__.get("__role") == "master": fire_master = salt.utils.event.get_master_event( __opts__, __opts__["sock_dir"] ).fire_event else: fire_master = None def fire(tag, msg): """ How to fire the event """ if fire_master: fire_master(msg, tag) else: __salt__["event.send"](tag, msg) args = event.line.split(" ") tag = args[0] if len(args) > 1: payload = {"data": args[1:]} else: payload = {"data": []} fire("salt/engines/ircbot/" + tag, payload) message = f"PRIVMSG {event.channel} :{event.nick}: TaDa!" self.send_message(message) def _message(self, raw): raw = raw.rstrip(b"\r\n").decode("utf-8") event = self._event(raw) if event.code == "PING": tornado.ioloop.IOLoop.current().spawn_callback( self.send_message, f"PONG {event.line}" ) elif event.code == "PRIVMSG": tornado.ioloop.IOLoop.current().spawn_callback(self._privmsg, event) self.read_messages() def join_channel(self, channel): if not channel.startswith("#"): channel = "#" + channel self.send_message(f"JOIN {channel}") def on_connect(self): logging.info("on_connect") if self.sasl is True: self.send_message("CAP REQ :sasl") self.send_message(f"NICK {self.nick}") self.send_message("USER saltstack 0 * :saltstack") if self.password: if self.sasl is True: authstring = base64.b64encode( "{0}\x00{0}\x00{1}".format(self.username, self.password).encode() ) self.send_message("AUTHENTICATE PLAIN") self.send_message(f"AUTHENTICATE {authstring}") self.send_message("CAP END") else: self.send_message( "PRIVMSG NickServ :IDENTIFY {} {}".format( self.username, self.password ) ) for channel in self.channels: self.join_channel(channel) self.read_messages() def on_closed(self): logging.info("on_closed") def send_message(self, line): if isinstance(line, str): line = line.encode("utf-8") log.debug("Sending: %s", line) self._stream.write(line + b"\r\n") def start( nick, host, port=6667, username=None, password=None, channels=None, use_ssl=False, use_sasl=False, char="!", allow_hosts=False, allow_nicks=False, disable_query=True, ): """ IRC Bot for interacting with salt. nick Nickname of the connected Bot. host irc server (example - irc.oftc.net). port irc port. Default: 6667 password password for authenticating. If not provided, user will not authenticate on the irc server. channels channels to join. use_ssl connect to server using ssl. Default: False use_sasl authenticate using sasl, instead of messaging NickServ. Default: False .. note:: This will allow the bot user to be fully authenticated before joining any channels char command character to look for. Default: ! allow_hosts hostmasks allowed to use commands on the bot. Default: False True to allow all False to allow none List of regexes to allow matching allow_nicks Nicks that are allowed to use commands on the bot. Default: False True to allow all False to allow none List of regexes to allow matching disable_query Disable commands from being sent through private queries. Require they be sent to a channel, so that all communication can be controlled by access to the channel. Default: True .. warning:: Unauthenticated Access to event stream This engine sends events calls to the event stream without authenticating them in salt. Authentication will need to be configured and enforced on the irc server or enforced in the irc channel. The engine only accepts commands from channels, so non authenticated users could be banned or quieted in the channel. /mode +q $~a # quiet all users who are not authenticated /mode +r # do not allow unauthenticated users into the channel It would also be possible to add a password to the irc channel, or only allow invited users to join. """ client = IRCClient( nick, host, port, username, password, channels or [], use_ssl, use_sasl, char, allow_hosts, allow_nicks, disable_query, ) client.io_loop.start()