def send(self, channel, message): """Send a message to a channel. Join the channel before talking. This is the interface of this class and is thread-safe. channel and message are unicode strings. """ if len(channel.encode('utf8')) > self._channel_maxlen: logger.warning("Channel length limit exceeded, dropping message") return self._queue.put((channel, message))
def _say_messages(self): """Try to send as much waiting messages as possible. This function is called every "line_sleep" seconds and only do one task such as publishing a line, joining a chan or something else. """ # Dequeue everything, creating channel objects if needed while not self._queue.empty(): (channel, message) = self._queue.get() # According to the RFC http://tools.ietf.org/html/rfc2812#page-6, # Message is admissible only if the IRC client won't eventually # have to send a message longer than 512 bytes # The message that will be sent by the irc.client module is : # 'PRIVMSG {channel} :{message}\r\n', so our message must be at # most 512 - 12 - len(channel) long max_message_size = 512 - 12 - len(channel.encode('utf-8')) if len(message.encode('utf-8')) > max_message_size: # The message needs to be split up logging.debug( u"Message for channel %s longer than %s bytes, splitting", channel, max_message_size, ) # Without this the message isn't splitted at the right index # the 'ignore' argument skip errors caused by utf-8 chars being # divided between the two strings. # This can make a char disappear. utf8_message = message.encode('utf-8') message1 = utf8_message[:max_message_size] \ .decode('utf-8', 'ignore') message2 = utf8_message[max_message_size:] \ .decode('utf-8', 'ignore') self._queue.put((channel, message1)) self._queue.put((channel, message2)) else: self._chans[channel].messages.append(message) # Don't do anything if server is stopped if self._stop.is_set(): return # If we're disconnected, don't do anything and wait for reconnection if not self.is_connected(): return # Find the next channel which needs work chanstatus = self._chans.find_waiting_channel() if chanstatus is None: return # Join the channel if needed if chanstatus.need_join(): if chanstatus.inc_join_counter(self._max_join_attempts, self._memory_timeout): self.connection.join(chanstatus.name) elif self._fallbackchan and chanstatus.name != self._fallbackchan: # Channel is blocked. Do fallback ! logger.warning(u"Channel %s is blocked. Using fallback" % chanstatus.name) message = chanstatus.messages.pop(0) self._chans[self._fallbackchan].messages.append(message) else: logger.error(u"Channel %s is blocked. Dropping message") message = chanstatus.messages.pop(0) logger.error(u"Dropped message was %s" % message) return # Say first message and unqueue message = chanstatus.messages.pop(0) logger.info("[%s] say %s" % (chanstatus.name, message)) self.connection.privmsg(chanstatus.name, message)
def _say_messages(self): """Try to send as much waiting messages as possible. This function is called every "line_sleep" seconds and only do one task such as publishing a line, joining a chan or something else. """ # Dequeue everything, creating channel objects if needed while not self._queue.empty(): (channel, message) = self._queue.get() # Split message if it is too long channel_length = len(channel.encode('utf8')) max_message_size = IRC_CHANMSG_MAXLEN - channel_length # Need at least 5 characters if max_message_size <= 5: logger.error("Channel name too long, dropping message") continue encoded = message.encode('utf-8') while len(encoded) > max_message_size: left, encoded = utf8_cut(encoded, max_message_size) if not left: logger.error("Unable to decode message, dropping it") break self._chans[channel].messages.append(left.decode('utf-8')) if encoded and len(encoded) <= max_message_size: self._chans[channel].messages.append( encoded.decode('utf-8', 'ignore')) # Don't do anything if server is stopped if self._stop.is_set(): return # If we're disconnected, don't do anything and wait for reconnection if not self.is_connected(): return # Find the next channel which needs work chanstatus = self._chans.find_waiting_channel() if chanstatus is None: return # Join the channel if needed if chanstatus.need_join(): if chanstatus.inc_join_counter(self._max_join_attempts, self._memory_timeout): self.connection.join(chanstatus.name) elif self._fallbackchan and chanstatus.name != self._fallbackchan: # Channel is blocked. Do fallback ! logger.warning("Channel %s is blocked. Using fallback" % chanstatus.name) message = chanstatus.messages.pop(0) self._chans[self._fallbackchan].messages.append(message) else: logger.error("Channel %s is blocked. Dropping message" % chanstatus.name) message = chanstatus.messages.pop(0) logger.error("Dropped message was %s" % message) return # Say first message and unqueue message = chanstatus.messages.pop(0) logger.info("[%s] say %s" % (chanstatus.name, message)) self.connection.privmsg(chanstatus.name, message)