Esempio n. 1
0
 def part(self, client, channelname):
     try:
         # remove from the channel
         del client.channels[channelname]
         client.request.sendall(
             (":{nick}!{nick}@{nick}.tmi.twitch.tv PART {chan}\n".format(
                 nick=self.nick, chan=channelname)).encode("utf-8"))
     except KeyError:
         self.logger.warning(
             eventmessage(
                 "user",
                 "Client %s/%s tried to leave channel %s, but it wasnt joined."
                 % (client.nick, client.oauth, channelname)))
     # if there are no clients for this channel left, we leave the channel
     for otherclient in self.clients:
         if (channelname in otherclient.channels):
             return
     if channelname in self.channels:
         self.channels[channelname].part()
         # remove from channels
         del self.channels[channelname]
     else:
         self.logger.warning(
             eventmessage(
                 "user", "Tried to leave channel %s, but it wasnt joined." %
                 (channelname, )))
Esempio n. 2
0
def main(argv):
    parser = argparse.ArgumentParser(description="TMoohI Server")
    parser.add_argument("--config",
                        default="",
                        help="Config file in YAML format")
    args, extraargs = parser.parse_known_args()
    srv = TMoohIServer(args, parse_unknown_args(extraargs))
    uninterrupted = True
    crashtime = 1
    lastcrash = 0
    while uninterrupted:
        try:
            srv.run()
            #while True:
            #	time.sleep(1000)
        except (KeyboardInterrupt, SystemExit):
            uninterrupted = False
            srv.logger.info(eventmessage("general", "Stopping TMoohI server."))
            # create a quitter thread
            srv.quit()
            break
        except Exception:
            srv.logger.exception()
            if time.time() - lastcrash < 60 * 10:
                crashtime *= 8
            else:
                crashtime = 1
            srv.logger.info(
                eventmessage(
                    "general",
                    "Restarting TMoohI server in %d seconds." % (crashtime, )))
            time.sleep(crashtime)
            lastcrash = time.time()
Esempio n. 3
0
 def handleMessageQueue(self):
     while self.messagequeue:
         # dequeue messages and handle them until we meet one that we cannot handle yet
         message = self.messagequeue.pop(0)
         user = message["user"]
         try:
             client = message["client"]
         except KeyError:
             client = None
         data = message["message"]
         self.logger.debug(
             eventmessage("user",
                          "Dequeing message %s for %s" % (data, user.key)))
         successfulsend = self.handleClientMessage(client, data, False)
         self.logger.debug(
             eventmessage(
                 "user", "handleClientMessage returned with value %s" %
                 (successfulsend, )))
         if successfulsend:
             self.logger.debug(
                 eventmessage(
                     "user",
                     "handleClientMessage was successful! Queue length: %d"
                     % (len(self.messagequeue), )))
         else:
             self.logger.debug(
                 eventmessage(
                     "user",
                     "handleClientMessage added a new item to the queue. Queue length: %d"
                     % (len(self.messagequeue), )))
             return False
         time.sleep(0.01)
     return True
Esempio n. 4
0
 def _update(self):
     now = time.time()
     dt = now - self.lastmessage
     if dt > 30:
         self.logger.error(
             eventmessage(
                 "connection",
                 "Bot %s got silently disconnected. Enabling dead mode." %
                 (self.connid, )))
         self.connected = False
         self.dead = True
         self.shutdown()
     elif dt > 10:
         if dt > 20:
             self.logger.info(
                 eventmessage(
                     "connection",
                     "Bot %s has not received messages in %d seconds. Pinging TMI server."
                     % (self.connid, int(dt))))
         else:
             self.logger.debug(
                 eventmessage(
                     "connection",
                     "Bot %s has not received messages in %d seconds. Pinging TMI server."
                     % (self.connid, int(dt))))
         self.sendraw("PING")
 def onClose(self, wasClean, code, reason):
     try:
         self.factory.logger.writers.remove(self)
     except ValueError:
         pass
     if wasClean:
         self.factory.logger.debug(eventmessage("websocket","WebSocket connection closed: {}".format(reason)))
     else:
         self.factory.logger.debug(eventmessage("websocket","WebSocket connection closed unexpectedly: {}".format(reason)))
Esempio n. 6
0
 def _update(self):
     now = time.time()
     dt = now-self.lastmessage
     if dt > 30:
         self.logger.error(eventmessage("connect","Bot %s got silently disconnected. Enabling dead mode."%(self.connid,)))
         self.connected = False
         self.dead = True
         self._socket.close()
     elif dt > 15:
         self.logger.debug(eventmessage("connect","Bot %s has not received messages in %d seconds. Pinging TMI server."%(self.connid,int(dt))))
         self.sendraw("PING")
Esempio n. 7
0
	def handleJoinQueue(self):
		while not self.quitting:
			try:
				# in each iteration, handle the joinQueue
				now = time.time()
				self._conn_join_times = [i for i in self._conn_join_times if i>now-10]
				# check all users on connection deficit
				try:
					for userkey,user in self.users.items():
						if self.quitting:
							return
						if len(self._conn_join_times) < self.parent.config["connections-per-10"]:
							if user.getTotalChannels() >= self.parent.config["capacity-target"] * user.getCapacity():
								self.logger.debug(eventmessage("manager","Requesting new connection for %s because of exceeded capacity"%(user.key,)))
								# request new connection (in a non-GIL interpreter, wrap this in try-except)
								user.connections.append(self.TMIConnectionFactory(user))
						# handle message queues
						user.handleMessageQueue()
				except RuntimeError:
					# Dict changed size during iteration. Nbd, well check again in .1 secs anyways.
					pass
				
				# check join queue
				iterator = 0
				while iterator < len(self.joinqueue):
					if self.quitting:
						return
					if len(self._conn_join_times) >= self.parent.config["connections-per-10"]:
						break
					
					# dequeue a channel and try to join it
					channeljoininfo = self.joinqueue.pop()
					user = channeljoininfo["user"]
					channelinfo = channeljoininfo["channelinfo"]
					self.logger.debug(eventmessage("manager","Dequeing channel %s for %s from join queue"%(channelinfo.name,user.key)))
					# try joining this channel
					seed = random.randint(0,len(user.connections))
					for index in range(len(user.connections)):
						try:
							conn = user.connections[(index+seed)%len(user.connections)]
							conn.join(channelinfo)
							self._conn_join_times.append(time.time())
							self.logger.debug(eventmessage("manager","Channel %s joined on connection %s"%(channelinfo.name,conn.connid)))
							break
						except (TooManyChannelsError, NotConnectedError):
							pass
					else:
						# put it back into the deque
						self.joinqueue.append(channeljoininfo)
						iterator += 1
						self.logger.debug(eventmessage("manager","Channel %s could not be joined, requeueing"%(channelinfo.name,)))
				time.sleep(0.1)
			except Exception:
				self.logger.exception()
Esempio n. 8
0
	def getJSON(self,url,cooldown=3600):
		try:
			if time.time() < self.cachedJSONresponses[url][0]+cooldown:
				self.logger.debug(eventmessage("manager","JSON response from cache: %s"%(url,)))
				return self.cachedJSONresponses[url][1]
		except KeyError:
			pass
		self.logger.debug(eventmessage("manager","Downloading JSON from %s"%(url,)))
		res = urllib2.urlopen(url)
		jsdata = res.read().decode("utf-8")
		data = json.loads(jsdata)
		self.cachedJSONresponses[url] = (time.time(), data)
		return data
Esempio n. 9
0
	def join(self, user, channelinfo):
		# try joining this channel
		for conn in user.connections:
			try:
				if len(self._conn_join_times) < self.parent.config["connections-per-10"]:
					conn.join(channelinfo)
					self._conn_join_times.append(time.time())
					self.logger.debug(eventmessage("manager","Channel %s joined on connection %s"%(channelinfo.name,conn.connid)))
					break
			except (TooManyChannelsError, NotConnectedError):
				pass
		else:
			self.logger.debug(eventmessage("manager","Channel %s could not be joined, enqueueing"%(channelinfo.name,)))
			self.joinqueue.append({"user":user,"channelinfo":channelinfo})
Esempio n. 10
0
 def broadcast(self,message):
     try:
         self.logger.debug(eventmessage("raw","Broadcasting message %s"%(message,)))
         for client in self.clients:
             client.request.sendall((message+"\r\n").encode("utf-8"))
     except Exception:
         self.logger.exception()
Esempio n. 11
0
    def privmsg(self,message, appendtoqueue):
        if not message[STATE_TRAILING]:
            raise TypeError("PRIVMSG: Trailing data expected")

        channels = [y for b in message[STATE_PARAM] for y in b.split(",") if y]
        allok = True
        for channel in channels:
            if channel[0] != "#":
                raise InvalidChannelError("PRIVMSG: Invalid channel %s."%(channel,))
            channelname = channel.split(self.parent.parent.config["cluster-seperator"],1)[0]
            clusterinfo = self.parent.getClusterInfo(channel,self.oauth)
            for conn in self.connections[clusterinfo[0]]:
                try:
                    conn.privmsg(channelname,message[STATE_TRAILING])
                    break
                except (RateLimitError, NotConnectedError):
                    pass
            else:
                # If we reach this, all available connections (if any) were unable to send the message.
                # We create a new one (cooldown: 3 seconds) and send the message to the messagequeue.
                self.logger.debug(eventmessage("connection","Requesting new connection to %s because of %s"%(clusterinfo[0],message[0])))
                now = time.time()
                if now-self._lastNewConnectionRequest[clusterinfo[0]]>3:
                    self.connections[clusterinfo[0]].append(self.parent.TMIConnectionFactory(self,clusterinfo))
                    self._lastNewConnectionRequest[clusterinfo[0]] = now
                # (re)add to messagequeue. message[0] is the original message
                if appendtoqueue:
                    self.messagequeue.append({"user":self,"message":message[0]})
                else:
                    self.messagequeue.insert(0,{"user":self,"message":message[0]})
                allok = False
        return allok
Esempio n. 12
0
    def die(self):
        """
		Simulates the server silently disconnecting us
		"""
        self.logger.info(
            eventmessage("connection", "Dieing bot %s" % (self.connid, )))
        self.ignoring = True
Esempio n. 13
0
 def handleClose(self):
     try:
         websocketServer.logger.writers.remove(self)
         websocketServer.clients.remove(self)
     except ValueError:
         pass
     websocketServer.logger.debug(
         eventmessage("websocket", "WebSocket connection closed"))
Esempio n. 14
0
 def kill(self):
     """
     Simulates the socket getting killed
     """
     self.logger.info(eventmessage("kill","Killing bot %s"%(self.connid,)))
     self.killing = True
     self.connected = False
     self._socket.close()
Esempio n. 15
0
    def disc(self):
        """
		Simulates the connection being closed
		"""
        self.logger.info(
            eventmessage("connection",
                         "Disconnecting bot %s" % (self.connid, )))
        self.sendraw("PRIVMSG #jtv :/DISCONNECT")
Esempio n. 16
0
 def handleConnected(self):
     self.filters = copy.deepcopy(websocketServer.defaultfilter)
     websocketServer.clients.append(self)
     websocketServer.logger.writers.append(self)
     websocketServer.logger.debug(
         eventmessage("websocket",
                      "Websocket connected: {}".format(self.address[0])))
     # when opening a connection, send the current state
     self.inner_write(statusmessage(websocketServer.neweststatus, "status"))
Esempio n. 17
0
    def kill(self):
        """
		Simulates the socket getting killed
		"""
        self.logger.info(
            eventmessage("connection", "Killing bot %s" % (self.connid, )))
        self.killing = True
        self.connected = False
        self._socket.close()
Esempio n. 18
0
 def connect(self):
     self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
     self._socket.connect((self.ip, self.port))
     self._recvthread = threading.Thread(target=self.listen)
     self._recvthread.start()
     self.logger.info(eventmessage("connect","Connecting to %s/%s for %s"%(self.ip, self.port, self.connid)))
     self.sendraw("CAP REQ :twitch.tv/tags\r\nCAP REQ :twitch.tv/commands")
     if self.parent.oauth:
         self.sendraw("PASS %s"%(self.parent.oauth,))
     self.sendraw("USER %s %s %s :%s"%(self.parent.nick,self.parent.nick,self.parent.nick,self.parent.nick,))
     self.sendraw("NICK %s"%(self.parent.nick,))
Esempio n. 19
0
 def handleMessageQueue(self):
     while self.messagequeue:
         # dequeue messages and handle them until we meet one that we cannot handle yet
         message = self.messagequeue.pop(0)
         user = message["user"]
         try:
             client = message["client"]
         except KeyError:
             client = None
         data = message["message"]
         self.logger.debug(eventmessage("queue","Dequeing message %s for %s"%(data,user.key)))
         successfulsend = self.handleClientMessage(client,data, False)
         self.logger.debug(eventmessage("queue","handleClientMessage returned with value %s"%(successfulsend,)))
         if successfulsend:
             self.logger.debug(eventmessage("queue","handleClientMessage was successful! Queue length: %d"%(len(self.messagequeue),)))
         else:
             self.logger.debug(eventmessage("queue","handleClientMessage added a new item to the queue. Queue length: %d"%(len(self.messagequeue),)))
             return False
         time.sleep(0.01)
     return True
Esempio n. 20
0
 def shutdown(self):
     if self.isshutdown:
         self.logger.warning(
             eventmessage("connection",
                          "Tried to shutdown a non-connected socket."))
     else:
         self.isshutdown = True
         self.connected = False
         try:
             self.parent.connections.remove(self)
         except KeyError:
             # we have already warned about this.
             pass
         if self.killing:
             self.logger.info(
                 eventmessage("connection",
                              "Connection ID %s killed!" % (self.connid, )))
         else:
             self.logger.error(
                 eventmessage(
                     "connection",
                     "Connection ID %s disconnected!" % (self.connid, )))
             # when the connection dies, rejoin the channels on different (or new) connections
             for channel in self.channels:
                 self.logger.warning(
                     eventmessage(
                         "connection",
                         "Readding channel %s to the joinqueue!" %
                         (channel.name, )))
                 channel.conn = None
                 self.manager.joinqueue.append({
                     "user": self.parent,
                     "channelinfo": channel
                 })
             self.channels = []
         try:
             self._socket.shutdown(socket.SHUT_RDWR)
             self._socket.close()
         except OSError:
             # this will usually be thrown if the process is murdered or something, aka the connection was already cut, no need to shut down in that case.
             pass
Esempio n. 21
0
 def join(self, client, channelname, appendtoqueue):
     channelinfo = None
     if channelname[0] != "#":
         raise TypeError("PRIVMSG: Invalid channel %s." % (channelname, ))
     self.logger.debug(
         eventmessage(
             "user", "Trying to join channel %s for client %s/%s" %
             (channelname, client.nick, client.oauth)))
     if channelname in self.channels:
         self.logger.debug(
             eventmessage(
                 "user",
                 "Channel %s already joined. Welcoming client %s/%s" %
                 (channelname, client.nick, client.oauth)))
         channelinfo = self.channels[channelname]
         channelinfo.welcome(client)
     else:
         channelinfo = TMoohIChannel.TMoohIChannel(self, channelname)
         self.channels[channelname] = channelinfo
         # try to join the channel, if we are ratelimited, add to joinqueue
         self.parent.join(self, channelinfo)
     client.channels[channelname] = channelinfo
Esempio n. 22
0
 def broadcast(self, channel, message):
     try:
         for client in self.clients:
             if channel == None or channel.name in client.channels:
                 try:
                     client.request.sendall(
                         (message + "\r\n").encode("utf-8"))
                 except BrokenPipeError:
                     self.logger.error(
                         eventmessage(
                             "user",
                             "Client %s/%s disconnected during broadcast" %
                             (client.nick, client.oauth)))
     except Exception:
         self.logger.exception()
Esempio n. 23
0
 def __init__(self,parent,cluster,server,connid):
     self.connected = False
     self.killing = False
     self.dead = False
     self.ignoring = False
     self.connid = connid
     
     self.lastmessage = time.time()
     
     self.parent = parent
     self.manager = parent.parent
     self.logger = self.manager.parent.logger
     self.logger.info(eventmessage("connect","Connection ID %s created!"%(self.connid,)))
     
     # list of TMoohIChannels that are supposed to be joined by this connection.
     self.channels = []
     # list of TMoohIChannels that are actually joined by this connection.
     self.joinedchannels = []
     # list of unique channelnames that are joined by this connection.
     self.channelnames = []
     
     
     self.clustername = cluster
     srvinfo = re.split("[^\d\w\.]",server)
     self.port = 443
     self.server = server
     self.ip = self.server
     if len(srvinfo) == 2:
         self.port = int(srvinfo[1])
         self.ip = srvinfo[0]
     
     self.stats = {
         "server": "%s:%s"%(self.ip, self.port),
         "id": self.connid,
         "connected": self.getConnected,
         "channels": self.channels,
         "joinedchannels": self.joinedchannels
     }
     
     # internals:
     self._socket = None
     self._recvthread = None
     self._recvthreadid = 0
     self._sentmessages = []
     self._messagebuffer = ""
     self._authed = False
     # we automatically connect to said server.
     self.connect()
Esempio n. 24
0
    def handleMessage(self):
        if self.opcode != TEXT:
            websocketServer.logger.debug(
                eventmessage(
                    "websocket", "Websocket message received: {} bytes".format(
                        len(self.data))))
        else:
            websocketServer.logger.debug(
                eventmessage(
                    "websocket",
                    "Websocket text message received: {}".format(self.data)))
            try:
                res = self.data.split(" ", 1)
                command = res[0]
                data = ""
                if len(res) == 2:
                    data = res[1]
                    jsondecoded = json.loads(data)
                if command == "SETFILTER":
                    if data:
                        ok = True
                        if type(jsondecoded) == list:
                            for x in jsondecoded:
                                if type(x) != dict:
                                    ok = False
                            if ok:
                                self.filters = jsondecoded

                                response = eventmessage(
                                    "websocket",
                                    "Filter updated to %s" % (self.filters, ))
                                response.level = MoohLogger.DEBUG
                                self.inner_write(response)
                        else:
                            ok = False
                        if not ok:
                            response = eventmessage(
                                "websocket",
                                "Could not process filter %s" % (data, ))
                            response.level = MoohLogger.ERROR
                            self.inner_write(response)
                    else:
                        response = eventmessage(
                            "websocket", "Could not process empty filter")
                        response.level = MoohLogger.ERROR
                        self.inner_write(response)
                else:
                    response = eventmessage("websocket",
                                            "Unknown command %s" % (command, ))
                    response.level = MoohLogger.ERROR
                    self.inner_write(response)
            except Exception:
                websocketServer.logger.exception()
Esempio n. 25
0
    def privmsg(self, message, appendtoqueue):
        if not message[STATE_TRAILING]:
            raise TypeError("PRIVMSG: Trailing data expected")

        channels = [y for b in message[STATE_PARAM] for y in b.split(",") if y]
        allok = True
        for channel in channels:
            if channel[0] != "#":
                raise InvalidChannelError("PRIVMSG: Invalid channel %s." %
                                          (channel, ))
            for conn in self.connections:
                try:
                    conn.privmsg(channel, message[STATE_TRAILING])
                    break
                except (RateLimitError, NotConnectedError):
                    pass
            else:
                # If we reach this, all available connections (if any) were unable to send the message.
                # We create a new one (cooldown: 3 seconds) and send the message to the messagequeue.
                self.logger.debug(
                    eventmessage(
                        "user", "Requesting new connection because of %s" %
                        (message[0], )))
                now = time.time()
                if now - self._lastNewConnectionRequest > 3:
                    try:
                        self._lastNewConnectionRequest = now
                        self.connections.append(
                            self.parent.TMIConnectionFactory(self))
                    except RateLimitError:
                        pass
                # (re)add to messagequeue. message[0] is the original message
                if appendtoqueue:
                    self.messagequeue.append({
                        "user": self,
                        "message": message[0]
                    })
                else:
                    self.messagequeue.insert(0, {
                        "user": self,
                        "message": message[0]
                    })
                allok = False
        return allok
Esempio n. 26
0
 def listen(self):
     try:
         while True:
             buf = self._socket.recv(2048).decode("utf-8")
             if not buf:
                 break
             if self.killing:
                 break
             if self.dead:
                 break
             if self.ignoring:
                 continue
             self.lastmessage = time.time()
             self._messagebuffer += buf
             s = self._messagebuffer.split("\r\n")
             self._messagebuffer = s[-1]
             for line in s[:-1]:
                 self.logger.debug(eventmessage("raw","Got raw TMI message in connection %s: %s"%(self.connid,line)))
                 try:
                     ex = parseIRCMessage(line)
                 except Exception:
                     self.logger.exception()
                 if(ex[STATE_COMMAND]=="PING"):
                     self.sendraw("PONG")
                 elif ex[STATE_COMMAND]=="376":
                     self.connected = True
                     self.logger.info(eventmessage("connect","Connection ID %s connected!"%(self.connid,)))
                 elif ex[STATE_COMMAND]=="JOIN":
                     try:
                         self.joinedchannels.append(ex[STATE_PARAM][0])
                         self.parent.handleTMIMessage(self, ex)
                         self.logger.info(eventmessage("channel","Joined channel "+ex[STATE_PARAM][0]))
                     except Exception:
                         self.logger.exception()
                 else:
                     self.parent.handleTMIMessage(self, ex)
     except ConnectionAbortedError:
         pass
     except Exception:
         self.logger.exception()
     self.connected = False
     self.parent.connections[self.clustername].remove(self)
     if self.killing:
         self.logger.info(eventmessage("connect","Connection ID %s killed!"%(self.connid,)))
     else:
         self.logger.error(eventmessage("connect","Connection ID %s disconnected!"%(self.connid,)))
         # when the connection dies, rejoin the channels on different (or new) connections
         for channel in self.channels:
             self.parent.part(channel.channelkey,announce = True)
             self.logger.error(eventmessage("queue","Readding channel %s to the joinqueue!"%(channel.channelkey,)))
             self.manager.joinqueue.append({"user":self.parent,"message":"JOIN %s"%(channel.channelkey,)})
Esempio n. 27
0
    def handleClientMessage(self,client,data, appendtoqueue):
        self.logger.debug(eventmessage("message","Handling message %s for %s"%(data,self.key)))
        # parse the message
        message = parseIRCMessage(data)
        cmd = message[STATE_COMMAND].lower()

        handler = None
        try:
            handler = getattr(self,"handle_client_%s"%(cmd,))
        except AttributeError:
            pass
        else:
            # they return True if the message could be handled.
            try:
                return handler(client,message, appendtoqueue)
            except Exception:
                self.logger.exception()

        return True
Esempio n. 28
0
    def handleClientMessage(self, client, data, appendtoqueue):
        self.logger.debug(
            eventmessage("user",
                         "Handling message %s for %s" % (data, self.key)))
        # parse the message
        message = parseIRCMessage(data)
        cmd = message[STATE_COMMAND].lower()

        handler = None
        try:
            handler = getattr(self, "handle_client_%s" % (cmd, ))
        except AttributeError:
            pass
        else:
            # they return True if the message could be handled.
            try:
                return handler(client, message, appendtoqueue)
            except Exception:
                self.logger.exception()

        return True
Esempio n. 29
0
 def connect(self):
     self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
     if self.serverObj["secure"]:
         self._socket = ssl.wrap_socket(self._socket)
     self._socket.connect((self.serverObj["host"], self.serverObj["port"]))
     self._recvthread = threading.Thread(target=self.listen)
     self._recvthread.start()
     self.logger.info(
         eventmessage(
             "connection", "Connecting to %s/%s for %s" %
             (self.serverObj["host"], self.serverObj["port"], self.connid)))
     self.sendraw("CAP REQ :twitch.tv/tags\r\nCAP REQ :twitch.tv/commands")
     if self.parent.oauth:
         self.sendraw("PASS %s" % (self.parent.oauth, ))
     self.sendraw("USER %s %s %s :%s" % (
         self.parent.nick,
         self.parent.nick,
         self.parent.nick,
         self.parent.nick,
     ))
     self.sendraw("NICK %s" % (self.parent.nick, ))
Esempio n. 30
0
 def onMessage(self, payload, isBinary):
     if isBinary:
         self.factory.logger.debug(eventmessage("websocket","Binary websocket message received: {} bytes".format(len(payload))))
     else:
         self.factory.logger.debug(eventmessage("websocket","Websocket text message received: {}".format(payload.decode('utf8'))))
         try:
             res = payload.decode('utf8').split(" ",1)
             command = res[0]
             data = ""
             if len(res) == 2:
                 data = res[1]
                 jsondecoded = json.loads(data)
             if command == "SETFILTER":
                 if data:
                     ok = True
                     if type(jsondecoded) == list:
                         for x in jsondecoded:
                             if type(x) != dict:
                                 ok = False
                         if ok:
                             self.filters = jsondecoded
                             
                             response = eventmessage("websocket","Filter updated to %s"%(self.filters,))
                             response.level = MoohLogger.DEBUG
                             self.inner_write(response)
                     else:
                         ok = False
                     if not ok:
                         response = eventmessage("websocket","Could not process filter %s"%(data,))
                         response.level = MoohLogger.ERROR
                         self.inner_write(response)
                 else:
                     response = eventmessage("websocket","Could not process empty filter")
                     response.level = MoohLogger.ERROR
                     self.inner_write(response)
             else:
                 response = eventmessage("websocket","Unknown command %s"%(command,))
                 response.level = MoohLogger.ERROR
                 self.inner_write(response)
         except Exception:
             self.factory.logger.exception()
Esempio n. 31
0
 def sendraw(self, x):
     self.logger.debug(
         eventmessage(
             "connection",
             "Sending a RAW TMI message on bot %s: %s" % (self.connid, x)))
     self._socket.send((x + "\r\n").encode("utf-8"))
Esempio n. 32
0
 def join(self,channel, appendtoqueue):
     self.logger.debug(eventmessage("channel","Trying to join channel %s"%(channel,)))
     if channel in self.channels:
         self.logger.debug(eventmessage("channel","Couldn't join channel %s: already joined."%(channel,)))
         return True
     if channel[0] != "#":
         raise TypeError("PRIVMSG: Invalid channel %s."%(channel,))
     clusterinfo = self.parent.getClusterInfo(channel)
     # check the ratelimit
     now = time.time()
     self.parent._joinedchannels = [i for i in self.parent._joinedchannels if i>now-10]
     if len(self.parent._joinedchannels)<40:
         channelname = channel.split(self.parent.parent.config["cluster-seperator"])[0]
         if channelname in self.channelsByName[clusterinfo[0]] and len(self.channelsByName[clusterinfo[0]][channelname])>0:
             # if the channelname is already joined, we use its connection, no need to ratelimit:
             refchannel = self.channelsByName[clusterinfo[0]][channelname][0]
             print("Channel "+channelname+ " already joined. Adding and welcoming. Data: %s"%(refchannel.data,))
             channelinfo = TMoohIChannel.TMoohIChannel(self,channel,clusterinfo[0],refchannel.conn)
             # add the channelinfo to channels
             self.channels[channel] = channelinfo
             # add the channelinfo to channelsByName
             self.channelsByName[clusterinfo[0]][channelname].append(channelinfo)
             # now we need to welcome the channel.
             for key,value in refchannel.data.items():
                 if value:
                     channelinfo.data[key] = replaceChannel(value,refchannel.channelkey,channelinfo.channelkey)
             for client in self.clients:
                 channelinfo.welcome(client)
             print("Channel "+channelname+ " already joined. Added and welcomed. Data: %s"%(channelinfo.data,))
             return True
         else:
             # find a connection to use
             for conn in self.connections[clusterinfo[0]]:
                 try:
                     # create channel object - also joins the channel
                     channelinfo = TMoohIChannel.TMoohIChannel(self,channel,clusterinfo[0],conn)
                     # add to global ratelimiter
                     self.parent._joinedchannels.append(now)
                     # add the channelinfo to channels
                     self.channels[channel] = channelinfo
                     # add the channelinfo to channelsByName
                     self.channelsByName[channelinfo.cluster].setdefault(channelinfo.channelname,[]).append(channelinfo)
                     return True
                 except (NotConnectedError, TooManyChannelsError) as e:
                     self.logger.debug(eventmessage("channel","Couldn't join channel %s: %s."%(channel,e)))
                     pass
     else:
         self.logger.debug(eventmessage("channel","Couldn't join channel %s: ratelimit exceeded."%(channel,)))
     # If we reach this, all available connections (if any) were unable to send the join or the request was ratelimited.
     # We create a new one and send the join to the joinqueue. This is ratelimited with 1 connection request per second.
     now = time.time()
     if now-self._lastNewConnectionRequest[clusterinfo[0]]>10:
         self.logger.debug(eventmessage("connection","Requesting new connection"))
         self.connections[clusterinfo[0]].append(self.parent.TMIConnectionFactory(self,clusterinfo))
         self._lastNewConnectionRequest[clusterinfo[0]] = now
     self.logger.debug(eventmessage("channel","Adding JOIN %s to the resend queue. Queue length: %d"%(channel,len(self.parent.joinqueue))))
     if appendtoqueue:
         self.parent.joinqueue.append({"user":self,"message":"JOIN %s"%(channel,)})
     else:
         self.parent.joinqueue.insert(0,{"user":self,"message":"JOIN %s"%(channel,)})
     return False
Esempio n. 33
0
 def onOpen(self):
     self.factory.logger.debug(eventmessage("websocket","WebSocket connection open."))
     # when opening a connection, send the current state
     self.inner_write(statusmessage(self.factory.neweststatus))
Esempio n. 34
0
 def sendraw(self,x):
     self.logger.debug(eventmessage("raw","Sending a RAW TMI message on bot %s: %s"%(self.connid,x)))
     self._socket.send((x+"\r\n").encode("utf-8"))
Esempio n. 35
0
 def __del__(self):
     self.logger.info(eventmessage("general", "Stopped TMoohI server."))
Esempio n. 36
0
 def die(self):
     """
     Simulates the server silently disconnecting us
     """
     self.logger.info(eventmessage("kill","Dieing bot %s"%(self.connid,)))
     self.ignoring = True
Esempio n. 37
0
 def disc(self):
     """
     Simulates the connection being closed
     """
     self.logger.info(eventmessage("kill","Disconnecting bot %s"%(self.connid,)))
     self.sendraw("PRIVMSG #jtv :/DISCONNECT")
Esempio n. 38
0
 def listen(self):
     try:
         while True:
             buf = ""
             try:
                 buf = self._socket.recv(2048).decode("utf-8",
                                                      errors='ignore')
             except UnicodeDecodeError:
                 self.logger.exception()
                 continue
             if not buf:
                 break
             if self.killing:
                 break
             if self.dead:
                 break
             if self.ignoring:
                 continue
             self.lastmessage = time.time()
             self._messagebuffer += buf
             s = self._messagebuffer.split("\r\n")
             self._messagebuffer = s[-1]
             for line in s[:-1]:
                 self.logger.debug(
                     eventmessage(
                         "connection",
                         "Got raw TMI message in connection %s: %s" %
                         (self.connid, line)))
                 try:
                     ex = parseIRCMessage(line)
                 except Exception:
                     self.logger.exception()
                 if (ex[STATE_COMMAND] == "PING"):
                     self.sendraw("PONG")
                 elif ex[STATE_COMMAND] == "376":
                     self.connected = True
                     self.logger.info(
                         eventmessage(
                             "connection", "Connection ID %s connected!" %
                             (self.connid, )))
                 elif ex[STATE_COMMAND] == "JOIN":
                     try:
                         self.parent.handleTMIMessage(self, ex)
                         self.logger.info(
                             eventmessage(
                                 "connection",
                                 "Joined channel " + ex[STATE_PARAM][0]))
                     except Exception:
                         self.logger.exception()
                 elif ex[STATE_COMMAND] == "PART":
                     try:
                         self.parent.handleTMIMessage(self, ex)
                         self.logger.info(
                             eventmessage(
                                 "connection",
                                 "Left channel " + ex[STATE_PARAM][0]))
                     except Exception:
                         self.logger.exception()
                 else:
                     self.parent.handleTMIMessage(self, ex)
     except ConnectionAbortedError:
         pass
     except Exception:
         self.logger.exception()
     self.shutdown()
Esempio n. 39
0
    def __init__(self, parent, server, connid):
        self.connected = False
        self.killing = False
        self.dead = False
        self.ignoring = False
        self.isshutdown = False
        self.connid = connid

        self.lastmessage = time.time()

        self.parent = parent
        self.manager = parent.parent
        self.logger = self.manager.parent.logger
        self.logger.info(
            eventmessage("connection",
                         "Connection ID %s created!" % (self.connid, )))

        # list of TMoohIChannels that are supposed to be joined by this connection.
        self.channels = []
        self.serverObj = None
        if type(server) == str:
            srvinfo = re.split("[^\d\w\.]", server)
            if len(srvinfo) == 2:
                port = int(srvinfo[1])
                self.serverObj = {
                    "host": srvinfo[0],
                    "port": port,
                    "secure": port == 443
                }
            elif len(srvinfo) == 1:
                self.serverObj = {
                    "host": srvinfo[0],
                    "port": 6667,
                    "secure": False
                }
        elif type(server) == dict:
            port = int(server.get("port", 6667))
            self.serverObj = {
                "host": server.get("host", "irc.chat.twitch.tv"),
                "port": port,
                "secure": server.get("secure", port == 443)
            }

        if not self.serverObj:
            raise ArgumentException("Invalid server settings")

        self.stats = {
            "server":
            "%s:%s" % (self.serverObj["host"], self.serverObj["port"]),
            "id": self.connid,
            "connected": self.getConnected,
            "channels": self.getChannels,
            "secure": self.serverObj["secure"]
        }

        # internals:
        self._socket = None
        self._recvthread = None
        self._recvthreadid = 0
        self._sentmessages = []
        self._messagebuffer = ""
        self._authed = False
        # we automatically connect to said server.
        self.connect()
Esempio n. 40
0
 def onConnect(self, request):
     self.factory.logger.writers.append(self)
     self.level = 0
     self.filters = copy.deepcopy(self.factory.defaultfilter)
     self.factory.logger.debug(eventmessage("websocket","Websocket connecting: {}".format(request.peer)))
Esempio n. 41
0
    def handle(self):
        self.buffer = ""
        self.nick = ""
        self.oauth = ""
        self.welcomed = False
        self.user = None
        self.starttime = time.time()
        self.id = "%s/%s" % (self.client_address[0], self.client_address[1])
        self.commandsent = 0
        self.channels = {}
        linesep = None
        self.stats = {
            "since": time.time(),
            "sent": self.getCommandsSent,
            "channels": self.channelList,
            "id": self.id
        }

        self.data = None
        while not self.server.TMoohIParent.quitting:
            try:
                self.data = self.request.recv(1024)
                if not self.data:
                    # client disconnected
                    break
                if self.server.TMoohIParent.quitting:
                    break
                self.server.TMoohIParent.logger.debug(
                    eventmessage(
                        "client",
                        "Got raw client message from %s (sock ID %d): %s" %
                        (self.client_address[0], self.client_address[1],
                         self.data)))
                self.buffer += self.data.decode("utf-8")
                if not linesep:
                    if "\r\n" in self.buffer:
                        linesep = "\r\n"
                    elif "\r" in self.buffer:
                        linesep = "\r"
                    elif "\n" in self.buffer:
                        linesep = "\n"
                    else:
                        continue
                lines = self.buffer.split(linesep)
                self.buffer = lines[-1]
                for line in lines[:-1]:
                    ex = line.split(" ")
                    if len(ex) > 1:
                        if ex[0].upper() == "QUIT":
                            break
                    if self.user:
                        if ex[0].upper() == "PRIVMSG":
                            self.commandsent += 1
                        self.user.handleClientMessage(self, line, True)
                        self.commandsent += 1
                    else:
                        if ex[0] == "NICK":
                            m = re.match("^NICK (\w+)$", line)
                            if m:
                                self.nick = m.group(1)
                                self.server.TMoohIParent.logger.debug(
                                    eventmessage(
                                        "client",
                                        "NICK command %s" % (m.group(1), )))
                                self.user = self.server.TMoohIParent.manager.connect(
                                    self)
                            else:
                                self.request.sendall(
                                    b"Invalid NICK command!\r\n")
                        elif ex[0] == "PASS":
                            m = re.match("^PASS (oauth:[a-z0-9]+)$", line)
                            if m:
                                self.oauth = m.group(1)
                                self.server.TMoohIParent.logger.debug(
                                    eventmessage(
                                        "client",
                                        "PASS command %s" % (self.oauth, )))
                                if self.nick:
                                    self.user = self.server.TMoohIParent.manager.connect(
                                        self)
                            else:
                                self.request.sendall(
                                    b"Invalid PASS command!\r\n")
                                continue
                        else:
                            self.request.sendall(b"No user connected to!\r\n")
            except ConnectionResetError:
                break
            except Exception:
                self.server.TMoohIParent.logger.exception()
        try:
            self.server.TMoohIParent.manager.disconnect(self)
        except Exception:
            self.server.TMoohIParent.logger.exception()
        if self.data:
            self.server.TMoohIParent.logger.debug(
                eventmessage(
                    "client", "Client %s disconnected (sock ID %d): %s" %
                    (self.client_address[0], self.client_address[1],
                     self.data)))
        else:
            self.server.TMoohIParent.logger.debug(
                eventmessage(
                    "client", "Client %s disconnected (sock ID %d)" %
                    (self.client_address[0], self.client_address[1])))
Esempio n. 42
0
    def __init__(self, config, extraargs):
        self.BuildInfo = BuildCounter.getVersionInfo(
            "TMoohI", ["py", "html", "css", "js"])

        self.quitting = False
        #config options
        # host+port: where TMoohI listens for client connections on
        # reference channel: the channel to gather chat_properties from
        # logfile: file to log status messages to
        # status-: status files in the formats specified
        # ref-channel-: channel to check chat_properties for

        self.config = {
            "port": 6667,
            "host": "localhost",
            "websockethost": "127.0.0.1",
            "websocketport": 3141,
            "logfile": "tmoohi_%Y_%m_%d.log",
            "logfile-logfilter": [{
                'level__ge': 20,
                'type': 'event'
            }],
            "console-logfilter": [{
                'level__ge': 0,
                'type': 'event'
            }],
            "channels-per-connection": 10,
            "messages-per-30": 15,
            "connections-per-10": 45,
            "capacity-target": 0.75,
            "ratelimit-commands": True,
            "server": "irc.chat.twitch.tv:443"
        }

        if config.config:
            with open(config.config) as f:
                data = yaml.load(f)
                for k in data:
                    self.config[k] = data[k]

        configerrors = []
        for k in extraargs:
            try:
                value = extraargs[k]
                self.config[k] = type(self.config[k])(value)
            except KeyError:
                configerrors.append("Key %s not present in config" % (k, ))
            except Exception as e:
                configerrors.append("%s for configuration option --%s %s" %
                                    (e, k, value))

        self.logger = MoohLogger()
        self.filelogger = filewriter(self.config["logfile"])
        self.filelogger.filters = self.config["logfile-logfilter"]
        self.consolelogger = consolewriter()
        self.consolelogger.filters = self.config["console-logfilter"]
        self.logger.writers.append(self.filelogger)
        self.logger.writers.append(self.consolelogger)

        for e in configerrors:
            self.logger.error(eventmessage("configuration", e))

        self.logger.info(
            eventmessage("general", "%s loaded" % (self.BuildInfo, )))
        self.logger.info(
            eventmessage(
                "general",
                "Starting TMoohI server on port %d - CTRL+C to stop" %
                (self.config["port"], )))

        self.websocketserver = TMoohIWebSocketLogger.TMoohIWebsocketServer(
            self.logger, self.config["websockethost"],
            self.config["websocketport"])

        self.manager = TMoohIManager.TMoohIManager(self)