Guest User

Untitled

a guest
Jan 22nd, 2018
73
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 22.24 KB | None | 0 0
  1. from twisted.words.protocols import irc
  2. from twisted.internet import reactor, protocol
  3. from twisted.python import log
  4. from twisted.application import internet, service
  5.  
  6. # To Do::
  7. # Make the RelayBots only connect after HomeRelayBot has connected
  8.  
  9. import ConfigParser
  10. import sys
  11.  
  12. log.startLogging(sys.stdout)
  13.  
  14. __version__ = "0.2"
  15.  
  16. class IRCClientWithCommands(irc.IRCClient):
  17.     some_command_prefix = ["!"]
  18.     def privmsg(self, user, channel, message):
  19.         if hasattr(self, "channel"):
  20.             c_channel = self.channel
  21.         else:
  22.             if isinstance(self.factory.channel, tuple):
  23.                 c_channel = self.factory.channel[0]
  24.             else:
  25.                 c_channel = self.factory.channel
  26.         if not channel == c_channel:
  27.             log.msg("Outside msg (%s - %s) from user/chan %s: %s"%(c_channel, channel, user, message))
  28.             return
  29.    
  30.         if not message: return
  31.         if not message.split(" ")[0] == self.nickname:
  32.             return
  33.        
  34.         command_name = message.split(" ")[1]
  35.         user         = user.split("!")[0]
  36.         args         = message.split(" ")[2:]
  37.        
  38.         if hasattr(self, "need_auth"):
  39.             if self.need_auth:
  40.                 if not self.factory.UserIsAuthed(user):
  41.                     return
  42.        
  43.         if hasattr(self, "_gotCustomCommand"):
  44.             return getattr(self,"_gotCustomCommand")(command_name, user, channel, args)
  45.        
  46.         if not hasattr(self,"command_"+command_name):
  47.             return self.msg(channel, "Unknown command %s. use help"%command_name)
  48.        
  49.         getattr(self, "command_"+command_name)(channel, user, args)
  50.    
  51.     def command_help(self, channel, user, args):
  52.         ''' how i use bot??? '''
  53.         self.notice(user,"Commands:")
  54.         for x in dir(self):
  55.             if x.startswith("command_"):
  56.                 func = getattr(self, x)
  57.                 if not hasattr(func, "__doc__"):
  58.                     ds = "No help available"
  59.                 else:
  60.                     ds = func.__doc__
  61.                 self.notice(user, "%s - %s"%(
  62.                                              x.split("command_")[1],
  63.                                              ds
  64.                                              ))
  65.  
  66.  
  67. class IRCHomeRelayBot(IRCClientWithCommands):
  68.     ''' This class joins the home network and sits in the relay
  69.        channel, spitting out any messages it gets via the manager '''
  70.    
  71.     def __init__(self, manager, name, network, channel):
  72.         self.manager = manager
  73.         self.network = network
  74.         self.chan_auth = channel
  75.         self.channel = self.chan_auth[0]
  76.         self.nickname = name
  77.        
  78.     def signedOn(self):
  79.         log.msg("[home-%s] Connected to network, registering"%self.network)
  80.         self.manager.RegisterRelay(self.network, self.gotRelayMessage)
  81.         self.manager.RegisterRelay(self.network, self.gotActionMessage, type="action")
  82.         self.join(self.chan_auth[0], self.chan_auth[1])
  83.        
  84.     def connectionLost(self, reason):
  85.         log.msg("[home-%s] Connection lost, unregistering"%self.network)
  86.         self.manager.UnregisterRelay(self.network)
  87.    
  88.     def gotRelayMessage(self, channel, message):
  89.         self.say(self.channel, "[%s] %s"%(channel, message))
  90.    
  91.     def gotActionMessage(self, channel, message):
  92.         self.describe(self.channel, "[%s] %s"%(channel, message))
  93.    
  94.     def ctcpQuery(self, user, channel, messages):
  95.         log.msg("[home-%s] [CTCP] Denied from %s"%(self.network, user))
  96.    
  97.     def joined(self, channel):
  98.         self.say(channel, "RelayBot %s"%__version__)
  99.    
  100.     def _gotCustomCommand(self, command, user, channel, args):
  101.         ''' We want users to be able to simply say the following in a channel:
  102.            [nickname] #achan YO YO YO!!11!1
  103.            and the HomeRelayBot will take the message and send it through the two-way.
  104.            So this _gotCustomCommand is a horrible hack to facilitate this.
  105.            '''
  106.        
  107.         if not command.startswith("#"): return
  108.        
  109.         if not args:
  110.             return
  111.        
  112.         message = " ".join(args)
  113.        
  114.         self.manager.CallTwoWay(self.network, command, message, args=(user))
  115.         self.say(channel, "[%s] [%s] %s"%(command, user, message))
  116.  
  117.  
  118. class IRCRelayer(irc.IRCClient):
  119.     ''' This class joins an external network/channel and then
  120.        pumps all messages back through the manager to the
  121.        right relay bot '''
  122.    
  123.     realname = "rbo"
  124.     username = "rbo"
  125.    
  126.     def __init__(self, manager, name, network, channels):
  127.         self.manager = manager
  128.         self.network = network
  129.         self.channels = channels
  130.         self.nickname = name
  131.         log.msg("IRC Relay created. Name: %s | Network: %s | Manager: %s"%(name, network, manager))
  132.    
  133.     def signedOn(self):
  134.         log.msg("[%s Connected to network, registering"%self.network)
  135.         self.manager.RegisterTwoWay(self.network, self.twoWayComs)
  136.         self.manager.RegisterTwoWay(self.network, self.joinAChannel, type="join")
  137.         self.manager.RegisterTwoWay(self.network, self.leaveAChannel, type="leave")
  138.         for chan, passw in self.channels:
  139.             self.join(chan, passw)
  140.    
  141.     def joinAChannel(self, channel, message, args=None):
  142.         log.msg("[%s] Joining channel %s - %s"%(self.network, channel, message))
  143.         self.manager.CallRelay(self.network, channel, "Joining channel %s"%channel)
  144.         self.join(channel, message)
  145.    
  146.     def leaveAChannel(self, channel, message, args=None): # message is the reason
  147.         log.msg("[%s] Leaving channel %s: %s"%(self.network, channel, message))
  148.         self.leave(channel, message)
  149.         self.manager.CallRelay(self.network, None, "Left channel %s with reason %s"%(channel, message))
  150.    
  151.     def connectionLost(self, reason):
  152.         log.msg("[%s] Connection lost, unregistering"%self.network)
  153.         self.manager.CallRelay(self.network, None, "Lost connection to %s: %s"%(self.network, reason))
  154.         self.manager.UnregisterTwoWay(self.network)
  155.    
  156.     def twoWayComs(self, channel, message, args):
  157.         log.msg("[TwoWay] Saying %s into channel %s"%(message, channel))
  158.         self.say(channel,"[%s] %s"%(args,message))
  159.    
  160.     def joined(self, channel):
  161.         self.manager.CallRelay(self.network, channel, "I joined channel %s"%channel)
  162.    
  163.     def left(self, channel):
  164.         self.manager.CallRelay(self.network, channel, "I left channel %s"%channel)
  165.    
  166.     def privmsg(self, user, channel, message):
  167.         user = user.split("!")[0]
  168.         self.manager.CallRelay(self.network, channel, "%s: %s"%(user, message))
  169.    
  170.     def modeChanged(self, user, channel, set, modes, args):
  171.         if set: set = "+"
  172.         else: set = "-"
  173.         targs = ""
  174.         for x in args:
  175.             if x: targs+=x
  176.         self.manager.CallRelay(self.network, channel, "Mode %s%s %s by %s"%(set, modes,
  177.                                                                             targs, user))
  178.    
  179.     def userKicked(self, kickee, channel, kicker, message):
  180.         self.manager.CallRelay(self.network, channel, "%s kicked %s (%s)"%(kicker, kickee, message))
  181.    
  182.     def kickedFrom(self, channel, kicker, message):
  183.         self.manager.CallRelay(self.network, channel, "%s kicked me! Rude. Message = %s"%(kicker, message))
  184.    
  185.     def nickChanged(self, nick):
  186.         self.manager.CallRelay(self.network, None, "My nick is now %s"%nick)
  187.    
  188.     def userJoined(self, user, channel):
  189.         self.manager.CallRelay(self.network, channel, "%s joined"%user)
  190.    
  191.     def userLeft(self, user, channel):
  192.         self.manager.CallRelay(self.network, channel, "%s left"%user)
  193.    
  194.     def userQuit(self, user, quitMessage):
  195.         self.manager.CallRelay(self.network, None, "User %s quit (%s)"%(user, quitMessage))
  196.    
  197.     def action(self, user, channel, data):
  198.         self.manager.CallRelay(self.network, channel, "%s: %s"%(user, data), type="action")
  199.    
  200.     def topicUpdated(self, user, channel, newTopic):
  201.         self.manager.CallRelay(self.network, channel, "User %s changed the topic to %s"%(user, newTopic))
  202.    
  203.     def userRenamed(self, oldname, newname):
  204.         self.manager.CallRelay(self.network, None, "User %s changed their name to %s"%(oldname, newname))
  205.        
  206. class RelayManager(IRCClientWithCommands):
  207.     ''' This bot controls the relay on the fly,
  208.        making connections to new servers and altering the config '''
  209.        
  210.     nickname = "RelayManager"
  211.     need_auth = True
  212.    
  213.     def signedOn(self):
  214.         self.join(self.factory.channel[0], self.factory.channel[1])
  215.    
  216.     def command_add_user(self, channel, user, args):
  217.         ''' add_user [username] - Add a user! '''
  218.         if not args: return self.notice(user, "Invalid args!")
  219.         self.factory.AddUser(args[0])
  220.         return self.notice(user, "Added user %s!"%args[0])
  221.    
  222.     def command_remove_user(self, channel, user, args):
  223.         ''' remove_user [username] - Remove a user! '''
  224.         if not args: return self.notice(user, "Invalid args!")
  225.         if not self.factory.RemoveUser(args[0]):
  226.             return self.notice(user, "Unknown user %s!"%args[0])
  227.         return self.notice(user, "User %s removed from auth list"%args[0])
  228.    
  229.     def command_list_users(self, channel, user, args):
  230.         ''' List users that can use this bot '''
  231.         self.notice(user, "Users:")
  232.         for u in self.factory.EnumerateUsers():
  233.             self.notice(user, " - %s"%u)
  234.        
  235.     def command_kill(self, channel, user, args):
  236.         ''' kill [network] - Kill a relay but leave the config alone'''
  237.         log.msg("Got kill request from user %s in channel %s"%(user, channel))
  238.         if not args: return self.notice(user, "Invalid arguments, use help command")
  239.         network_id = args[0]
  240.         if not self.factory.networkExists(network_id):
  241.             return self.notice(user, "Network %s does not seem to exist! Try the list command"%network_id)
  242.         self.factory.TerminateRelay(network_id)
  243.    
  244.     def command_connect(self, channel, user, args):
  245.         ''' connect [network] - connect a killed relay'''
  246.         if not args: return self.notice(user, "Invalid arguments, use help command")
  247.         network_id = args[0]
  248.         if not self.factory.networkExists(network_id):
  249.             return self.notice(user, "Network %s does not seem to exist! try the view_all command")
  250.         self.factory.ConnectRelay(network_id)
  251.    
  252.     def command_list(self, channel, user, args):
  253.         ''' List all connected networks '''
  254.         self.notice(user, "Connected networks:")
  255.         for c in self.factory.enumerateNetworks():
  256.             self.notice(user, " - %s"%c)
  257.    
  258.     def command_view_all(self, channel, user, args):
  259.         ''' View all relays in the config file '''
  260.         self.notice(user, "Networks in the config file:")
  261.         for x in self.factory.config.sections():
  262.             if not x == "global_settings":
  263.                 self.notice(user, " - %s"%x)
  264.    
  265.     def command_add_channel(self, channel, user, args):
  266.         ''' add_channel [network] [channel] (password)'''
  267.         if not args or not len(args) >= 2: return self.notice(user, "Invalid arguments, use the help command")
  268.         if not self.factory.networkExists(args[0]): return self.notice(user, "Unknown network %s, use list command"%args[0])
  269.         try:
  270.             password = args[2]
  271.         except IndexError:
  272.             password = None
  273.         self.factory.AddChannel(args[0], args[1], password)
  274.    
  275.     def command_leave_channel(self, channel, user, args):
  276.         ''' leave_channel [network] [channel] (reason...) '''
  277.         if not args or not len(args) >= 2: return self.notice(user, "Invalid arguments, use the help command")
  278.         if not self.factory.networkExists(args[0]): return self.notice(user, "Unknown network %s, use list command"%args[0])
  279.         self.factory.LeaveChannel(args[0], args[1], " ".join(args[2:]))
  280.    
  281.     def command_add_network(self, channel, user, args):
  282.         ''' add_network [identifier] [network_addr] [port] (Relaybot nickname)'''
  283.         if not args or not len(args) >= 3: return self.notice(user, "Invalid arguments, use the help command")
  284.         if self.factory.networkExists(args[1]): return self.notice(user, "Network %s already exists"%args[1])
  285.         if not args[2].isdigit(): return self.notice(user, "Invalid port! Must be an integer")
  286.         try:
  287.             rnick = args[3]
  288.         except:
  289.             rnick = None
  290.         self.factory.AddNetwork(args[0], args[1], int(args[2]), rnick)
  291.    
  292.     def command_remove_network(self, channel, user, args):
  293.         ''' remove_network [host] - Remove a network from the config and disconnect it'''
  294.         if not args: return self.notice(user, "Invalid arguments: use the help command")
  295.         if not self.factory.networkExists(args[0]): return self.notice(user, "Invalid network, use the view_all command")
  296.         self.factory.RemoveNetwork(args[0])
  297.  
  298.        
  299. class BaseFactory(protocol.ClientFactory):
  300.     noisy = False
  301.     def clientConnectionLost(self,connector,reason):
  302.         if hasattr(self, "terminated"):
  303.             if self.terminated:
  304.                 connector.disconnect()
  305.                 return log.msg("ClientFactory %s closing down"%self)
  306.         log.msg('Disconnect. Reason: %s'%reason.getErrorMessage())
  307.         reactor.callLater(3, connector.connect)
  308.  
  309.     def clientConnectionFailed(self,connector,reason):
  310.         if hasattr(self, "terminated"):
  311.             if self.terminated:
  312.                 connector.disconnect()
  313.                 return log.msg("ClientFactory %s closing down"%self)
  314.         log.msg('Disconnect. Reason: %s'%reason.getErrorMessage())
  315.         reactor.callLater(3, connector.connect)
  316.  
  317. class RelayFactory(BaseFactory):
  318.     protocol = IRCRelayer
  319.    
  320.     def __init__(self, manager, name, network, channels):
  321.         self.manager = manager
  322.         self.network = network
  323.         self.channels = channels
  324.         self.name = name
  325.         self.terminated = False
  326.    
  327.     def buildProtocol(self, addr):
  328.         x = self.protocol(self.manager, self.name, self.network, self.channels)
  329.         x.factory = self
  330.         return x
  331.    
  332.  
  333. class HomeRelayManager(BaseFactory):
  334.     protocol = IRCHomeRelayBot
  335.    
  336.     def __init__(self, manager, name, network, channel):
  337.         self.manager = manager
  338.         self.network = network
  339.         self.channel = channel
  340.         self.name = name
  341.         self.terminated = False
  342.    
  343.     def buildProtocol(self, addr):
  344.         x = self.protocol(self.manager, self.name, self.network, self.channel)
  345.         x.factory = self
  346.         return x
  347.  
  348.  
  349. class RelayManagerFactory(BaseFactory):
  350.     protocol = RelayManager
  351.     def __init__(self, config, channel, network, port):
  352.         self.config = config
  353.         self.network = network
  354.         self.port = port
  355.         self.channel = channel
  356.         self.relay = {}
  357.         self.two_way = {} # for inter-network chatz
  358.        
  359.         # (connector, factory)
  360.         self.connectors = {}
  361.        
  362.     def networkExists(self, network):
  363.         return network in self.config.sections()
  364.    
  365.     def enumerateNetworks(self):
  366.         r = []
  367.         for x in self.connectors.keys():
  368.             y = x.split("-")[0]
  369.             if not y in r: r.append(y)
  370.         return r
  371.    
  372.     def enumNetworkKeys(self, network, obj):
  373.         r = []
  374.         for x in obj.keys():
  375.             y = x.split("-")[0]
  376.             if y == network:
  377.                 r.append(x)
  378.         return r
  379.        
  380.     def RegisterRelay(self, network, function, type="msg"):
  381.         f = "%s-%s"%(network, type)
  382.         assert not f in self.relay, "Relay %s already registered"%f
  383.         self.relay[f] = function
  384.         log.msg("[Manager] Relay (%s) registered for network %s"%(type,network))
  385.  
  386.     def CallRelay(self, network, chan, message, type="msg"):
  387.         f = "%s-%s"%(network, type)
  388.         if not f in self.relay: return log.msg("[Manager] Unknown relay %s called"%f)
  389.         self.relay[f](chan, message)
  390.    
  391.     def UnregisterRelay(self, network, type="msg"):
  392.         ''' We dont care if it doesn't exist '''
  393.         for x in self.enumNetworkKeys(network, self.relay):
  394.             del self.relay[x]
  395.             log.msg("[Manager] Relay (%s) removed for network %s"%(x,network))
  396.         return
  397.  
  398.     def RegisterTwoWay(self, network, function, type="msg"):
  399.         f = "%s-%s"%(network,type)
  400.         assert not f in self.two_way, "Two-Way %s already registered"%f
  401.         self.two_way[f] = function
  402.         log.msg("[Manager] Two-way registered for network %s"%network)
  403.    
  404.     def CallTwoWay(self, network, channel, message, type="msg", args=None):
  405.         f = "%s-%s"%(network, type)
  406.         if not f in self.two_way: return log.msg("[Manager] Unknown two-way (%s) called"%f)
  407.         self.two_way[f](channel, message, args=args)
  408.    
  409.     def UnregisterTwoWay(self, network, type="msg"):
  410.         for x in self.enumNetworkKeys(network, self.two_way):
  411.             del self.two_way[x]
  412.             log.msg("[Manager] Relay (%s) removed for network %s"%(x,network))
  413.    
  414.     def _makeHomeRelay(self, nick, network, channel):
  415.         if not network in self.connectors:
  416.             self.connectors[network] = []
  417.         factory = HomeRelayManager(self, nick, network, channel)
  418.         connector = internet.TCPClient(self.network, self.port, factory)
  419.         connector.setServiceParent(application)
  420.         self.connectors[network].append((connector,
  421.                         factory))
  422.    
  423.     def _makeExternalRelay(self, nick, network, port, channels):
  424.         if not network in self.connectors:
  425.             self.connectors[network] = []
  426.         factory = RelayFactory(self, nick, network, channels)
  427.         connector = internet.TCPClient(network, port, factory)
  428.         connector.setServiceParent(application)
  429.         self.connectors[network].append((connector,
  430.                         factory))
  431.        
  432.     def _disconnectRelay(self, network):
  433.         for connector, factory in self.connectors[network]:
  434.             factory.terminated = True
  435.             connector.stopService()
  436.         del self.connectors[network]
  437.            
  438.     def TerminateRelay(self, network):
  439.         self._disconnectRelay(network)
  440.    
  441.     def ConnectRelay(self, network):
  442.         port, ident, nickname, channels = self.ReadNetworkSettings(network)
  443.         self.SetupRelay(network, channels, port, nickname, ident)
  444.    
  445.     def SetupRelay(self, external_network, external_channel, external_port, external_nick, home_nick):
  446.         log.msg("Setting up relay for network %s:%s, %s, %s, %s"%(external_network,
  447.                                                                   external_port, external_channel,
  448.                                                                   external_nick, home_nick))
  449.         self._makeHomeRelay(home_nick, external_network, self.channel)
  450.         self._makeExternalRelay(external_nick or "RelayBot", external_network,
  451.                                 external_port, external_channel)
  452.        
  453.    
  454.     def AddNetwork(self, ident, network, port, nickname):
  455.         self.config.add_section(network)
  456.         config.set(network, "port", port)
  457.         config.set(network, "ident", ident)
  458.         config.set(network, "nickname", nickname or "")
  459.         config.write(open("networks.ini","w"))
  460.        
  461.         self.SetupRelay(network, [], port, nickname, ident)
  462.    
  463.     def RemoveNetwork(self, network):
  464.         log.msg("[Manager] Terminating relay")
  465.         self.CallRelay(network, None, "Relay closing down")
  466.         config.remove_section(network)
  467.         config.write(open("networks.ini","w"))
  468.         self.TerminateRelay(network)
  469.    
  470.     def AddChannel(self, network, chan, password):
  471.         log.msg("[Manager] Adding channel %s to network %s"%(chan, network))
  472.         config.set(network, "channel_"+chan, password or "")
  473.         config.write(open("networks.ini","w"))
  474.         self.CallTwoWay(network, chan, password, type="join")
  475.    
  476.     def LeaveChannel(self, network, chan, reason):
  477.         log.msg("[Manager] Removing channel %s from network %s"%(chan, network))
  478.         config.remove_option(network, "channel_"+chan)
  479.         config.write(open("networks.ini","w"))
  480.         self.CallTwoWay(network, chan, reason, type="leave")
  481.    
  482.     def ReadNetworkSettings(self, network):
  483.         port = config.getint(network, "port")
  484.         ident = config.get(network, "ident")
  485.         nickname = config.get(network, "nickname") or "RelayBot"
  486.         channels = []
  487.         for i in config.options(network):
  488.             if i.startswith("channel_"):
  489.                 c = i.split("channel_")[1]
  490.                 password = config.get(network,i)
  491.                 channels.append((c,password))
  492.        
  493.         return port, ident, nickname, channels
  494.  
  495.     def AddUser(self, user):
  496.         self.config.set("global_settings","user_"+user,"1")
  497.         self.config.write(open("networks.ini","w"))
  498.    
  499.     def RemoveUser(self, user):
  500.         if not self.UserIsAuthed(user):
  501.             return False
  502.         self.config.remove_option("global_settings","user_"+user)
  503.         self.config.write(open("networks.ini","w"))
  504.         return True
  505.    
  506.     def UserIsAuthed(self, user):
  507.         return user in self.EnumerateUsers()
  508.    
  509.     def EnumerateUsers(self):
  510.         r = []
  511.         for x in config.options("global_settings"):
  512.             if x.startswith("user_"):
  513.                 if not config.getint("global_settings",x): continue
  514.                 r.append(x.split("user_")[1])
  515.         return r
  516.  
  517. application = service.Application("RelayBot")
  518.  
  519. config = ConfigParser.ConfigParser()
  520. config.read("networks.ini")
  521.    
  522. our_network = config.get("global_settings","network")
  523. our_port    = config.getint("global_settings","port")
  524. our_channel = (config.get("global_settings","channel"),
  525.                    config.get("global_settings", "password"))
  526.    
  527. mangr = RelayManagerFactory(config, our_channel, our_network, our_port)
  528. internet.TCPClient(our_network, our_port, mangr).setServiceParent(application)
  529.  
  530. for network in config.sections():
  531.     if network == "global_settings": continue
  532.     log.msg("[Config] Creating relay for network %s"%network)
  533.        
  534.     port, ident, nickname, channels = mangr.ReadNetworkSettings(network)
  535.        
  536.     mangr.SetupRelay(network, channels, port, nickname, ident)
Add Comment
Please, Sign In to add comment