irclib: Split _sendReply from NestedCommandsIrcProxy.reply

This function was getting uncomfortably big.
This commit is contained in:
Valentin Lorentz 2021-03-13 15:03:56 +01:00
parent 567618392f
commit 8a3efe4379

View File

@ -914,10 +914,6 @@ class NestedCommandsIrcProxy(ReplyIrcProxy):
assert not isinstance(s, ircmsgs.IrcMsg), \ assert not isinstance(s, ircmsgs.IrcMsg), \
'Old code alert: there is no longer a "msg" argument to reply.' 'Old code alert: there is no longer a "msg" argument to reply.'
self.repliedTo = True self.repliedTo = True
if sendImmediately:
sendMsg = self.irc.sendMsg
else:
sendMsg = self.irc.queueMsg
if msg is None: if msg is None:
msg = self.msg msg = self.msg
if prefixNick is not None: if prefixNick is not None:
@ -936,115 +932,11 @@ class NestedCommandsIrcProxy(ReplyIrcProxy):
if not isinstance(s, minisix.string_types): # avoid trying to str() unicode if not isinstance(s, minisix.string_types): # avoid trying to str() unicode
s = str(s) # Allow non-string esses. s = str(s) # Allow non-string esses.
replyArgs = dict(
to=self.to,
notice=self.notice,
action=self.action,
private=self.private,
prefixNick=self.prefixNick,
stripCtcp=stripCtcp
)
if self.finalEvaled: if self.finalEvaled:
try: try:
if isinstance(self.irc, self.__class__): self._sendReply(
s = s[:conf.supybot.reply.maximumLength()] s=s, target=target, msg=msg,
return self.irc.reply(s, sendImmediately=sendImmediately, stripCtcp=stripCtcp)
noLengthCheck=self.noLengthCheck,
**replyArgs)
elif self.noLengthCheck:
# noLengthCheck only matters to NestedCommandsIrcProxy, so
# it's not used here. Just in case you were wondering.
m = _makeReply(self, msg, s, **replyArgs)
sendMsg(m)
return m
else:
s = ircutils.safeArgument(s)
allowedLength = conf.get(conf.supybot.reply.mores.length,
channel=target, network=self.irc.network)
if not allowedLength: # 0 indicates this.
allowedLength = (512
- len(':') - len(self.irc.prefix)
- len(' PRIVMSG ')
- len(target)
- len(' :')
- len('\r\n')
)
if self.prefixNick:
allowedLength -= len(msg.nick) + len(': ')
maximumMores = conf.get(conf.supybot.reply.mores.maximum,
channel=target, network=self.irc.network)
maximumLength = allowedLength * maximumMores
if len(s) > maximumLength:
log.warning('Truncating to %s bytes from %s bytes.',
maximumLength, len(s))
s = s[:maximumLength]
s_size = len(s.encode()) if minisix.PY3 else len(s)
if s_size <= allowedLength or \
not conf.get(conf.supybot.reply.mores,
channel=target, network=self.irc.network):
# There's no need for action=self.action here because
# action implies noLengthCheck, which has already been
# handled. Let's stick an assert in here just in case.
assert not self.action
m = _makeReply(self, msg, s, **replyArgs)
sendMsg(m)
return m
# The '(XX more messages)' may have not the same
# length in the current locale
allowedLength -= len(_('(XX more messages)')) + 1 # bold
chunks = ircutils.wrap(s, allowedLength)
# Last messages to display at the beginning of the list
# (which is used like a stack)
chunks.reverse()
instant = conf.get(conf.supybot.reply.mores.instant,
channel=target, network=self.irc.network)
msgs = []
for (i, chunk) in enumerate(chunks):
if i == 0:
pass # last message, no suffix to add
elif len(chunks) - i < instant:
# one of the first messages, and the next one will
# also be sent immediately, so no suffix
pass
else:
if i == 1:
more = _('more message')
else:
more = _('more messages')
n = ircutils.bold('(%i %s)' % (len(msgs), more))
chunk = '%s %s' % (chunk, n)
msgs.append(_makeReply(self, msg, chunk, **replyArgs))
while instant > 1 and msgs:
instant -= 1
response = msgs.pop()
sendMsg(response)
# XXX We should somehow allow these to be returned, but
# until someone complains, we'll be fine :) We
# can't return from here, though, for obvious
# reasons.
# return m
if not msgs:
return
response = msgs.pop()
prefix = msg.prefix
if self.to and ircutils.isNick(self.to):
try:
state = self.getRealIrc().state
prefix = state.nickToHostmask(self.to)
except KeyError:
pass # We'll leave it as it is.
mask = prefix.split('!', 1)[1]
self._mores[mask] = msgs
public = bool(self.msg.channel)
private = self.private or not public
self._mores[msg.nick] = (private, msgs)
sendMsg(response)
return response
finally: finally:
self._resetReplyAttributes() self._resetReplyAttributes()
else: else:
@ -1058,6 +950,128 @@ class NestedCommandsIrcProxy(ReplyIrcProxy):
self.args[self.counter] = s self.args[self.counter] = s
self.evalArgs() self.evalArgs()
def _replyOverhead(self, target, msg):
"""Returns the number of bytes added to a PRIVMSG payload, either by
Limnoria itself or by the server.
Ignores tag bytes, as they are accounted for separatly."""
overhead = (
len(':')
+ len(self.irc.prefix)
+ len(' PRIVMSG ')
+ len(target)
+ len(' :')
+ len('\r\n')
)
if self.prefixNick:
overhead += len(msg.nick) + len(': ')
return overhead
def _sendReply(self, s, target, msg, sendImmediately, stripCtcp):
if sendImmediately:
sendMsg = self.irc.sendMsg
else:
sendMsg = self.irc.queueMsg
replyArgs = dict(
to=self.to,
notice=self.notice,
action=self.action,
private=self.private,
prefixNick=self.prefixNick,
stripCtcp=stripCtcp
)
if isinstance(self.irc, self.__class__):
s = s[:conf.supybot.reply.maximumLength()]
return self.irc.reply(s,
noLengthCheck=self.noLengthCheck,
**replyArgs)
elif self.noLengthCheck:
# noLengthCheck only matters to NestedCommandsIrcProxy, so
# it's not used here. Just in case you were wondering.
m = _makeReply(self, msg, s, **replyArgs)
sendMsg(m)
return m
else:
s = ircutils.safeArgument(s)
allowedLength = conf.get(conf.supybot.reply.mores.length,
channel=target, network=self.irc.network)
if not allowedLength: # 0 indicates this.
allowedLength = 512 - self._replyOverhead(target, msg)
maximumMores = conf.get(conf.supybot.reply.mores.maximum,
channel=target, network=self.irc.network)
maximumLength = allowedLength * maximumMores
if len(s) > maximumLength:
log.warning('Truncating to %s bytes from %s bytes.',
maximumLength, len(s))
s = s[:maximumLength]
s_size = len(s.encode()) if minisix.PY3 else len(s)
if s_size <= allowedLength or \
not conf.get(conf.supybot.reply.mores,
channel=target, network=self.irc.network):
# There's no need for action=self.action here because
# action implies noLengthCheck, which has already been
# handled. Let's stick an assert in here just in case.
assert not self.action
m = _makeReply(self, msg, s, **replyArgs)
sendMsg(m)
return m
# The '(XX more messages)' may have not the same
# length in the current locale
allowedLength -= len(_('(XX more messages)')) + 1 # bold
chunks = ircutils.wrap(s, allowedLength)
# Last messages to display at the beginning of the list
# (which is used like a stack)
chunks.reverse()
instant = conf.get(conf.supybot.reply.mores.instant,
channel=target, network=self.irc.network)
msgs = []
for (i, chunk) in enumerate(chunks):
if i == 0:
pass # last message, no suffix to add
elif len(chunks) - i < instant:
# one of the first messages, and the next one will
# also be sent immediately, so no suffix
pass
else:
if i == 1:
more = _('more message')
else:
more = _('more messages')
n = ircutils.bold('(%i %s)' % (len(msgs), more))
chunk = '%s %s' % (chunk, n)
msgs.append(_makeReply(self, msg, chunk, **replyArgs))
while instant > 1 and msgs:
instant -= 1
response = msgs.pop()
sendMsg(response)
# XXX We should somehow allow these to be returned, but
# until someone complains, we'll be fine :) We
# can't return from here, though, for obvious
# reasons.
# return m
if not msgs:
return
response = msgs.pop()
prefix = msg.prefix
if self.to and ircutils.isNick(self.to):
try:
state = self.getRealIrc().state
prefix = state.nickToHostmask(self.to)
except KeyError:
pass # We'll leave it as it is.
mask = prefix.split('!', 1)[1]
self._mores[mask] = msgs
public = bool(self.msg.channel)
private = self.private or not public
self._mores[msg.nick] = (private, msgs)
sendMsg(response)
return response
def noReply(self, msg=None): def noReply(self, msg=None):
if msg is None: if msg is None:
msg = self.msg msg = self.msg