mudlet/src/ircmessageformatter.cpp

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

477 lines
18 KiB
C++
Raw Permalink Normal View History

/***************************************************************************
* Copyright (C) 2008-2017 The Communi Project *
* Copyright (C) 2017 by Fae - itsthefae@gmail.com *
* Copyright (C) 2020 by Stephen Lyons - slysven@virginmedia.com *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include "ircmessageformatter.h"
#include <IrcTextFormat>
QString IrcMessageFormatter::formatMessage(IrcMessage* message, bool isForLua)
{
QString formatted;
QString color = "#f29010";
IrcNumericMessage* nMsg;
switch (message->type()) {
case IrcMessage::Away:
formatted = formatAwayMessage(static_cast<IrcAwayMessage*>(message), isForLua);
break;
case IrcMessage::Invite:
formatted = formatInviteMessage(static_cast<IrcInviteMessage*>(message), isForLua);
break;
case IrcMessage::Join:
formatted = formatJoinMessage(static_cast<IrcJoinMessage*>(message), isForLua);
break;
case IrcMessage::Mode:
formatted = formatModeMessage(static_cast<IrcModeMessage*>(message), isForLua);
break;
case IrcMessage::Motd:
formatted = formatMotdMessage(static_cast<IrcMotdMessage*>(message), isForLua);
break;
case IrcMessage::Names:
formatted = formatNamesMessage(static_cast<IrcNamesMessage*>(message), isForLua);
break;
case IrcMessage::Nick:
formatted = formatNickMessage(static_cast<IrcNickMessage*>(message), isForLua);
break;
case IrcMessage::Notice:
formatted = formatNoticeMessage(static_cast<IrcNoticeMessage*>(message), isForLua);
break;
case IrcMessage::Part:
formatted = formatPartMessage(static_cast<IrcPartMessage*>(message), isForLua);
break;
case IrcMessage::Pong:
formatted = formatPongMessage(static_cast<IrcPongMessage*>(message), isForLua);
break;
case IrcMessage::Private:
formatted = formatPrivateMessage(static_cast<IrcPrivateMessage*>(message), isForLua);
break;
case IrcMessage::Quit:
formatted = formatQuitMessage(static_cast<IrcQuitMessage*>(message), isForLua);
break;
case IrcMessage::Topic:
formatted = formatTopicMessage(static_cast<IrcTopicMessage*>(message), isForLua);
color = "#3283bc";
break;
case IrcMessage::Whois:
formatted = formatWhoisMessage(static_cast<IrcWhoisMessage*>(message), isForLua);
break;
case IrcMessage::Whowas:
formatted = formatWhowasMessage(static_cast<IrcWhowasMessage*>(message), isForLua);
break;
case IrcMessage::WhoReply:
formatted = formatWhoReplyMessage(static_cast<IrcWhoReplyMessage*>(message), isForLua);
break;
case IrcMessage::Error:
formatted = formatErrorMessage(static_cast<IrcErrorMessage*>(message), isForLua);
color = "indianred";
break;
case IrcMessage::Unknown:
formatted = formatUnknownMessage(message, isForLua);
break;
case IrcMessage::Numeric:
nMsg = static_cast<IrcNumericMessage*>(message);
formatted = formatNumericMessage(nMsg, isForLua);
// if you change this, change formatErrorMessage too
if (Irc::codeToString(nMsg->code()).startsWith("ERR_")) {
color = "indianred";
}
break;
default:
break;
}
return formatMessage(formatted, color, isForLua);
}
QString IrcMessageFormatter::formatMessage(const QString& message, QString color, bool isForLua)
{
if (!message.isEmpty()) {
if (isForLua) { // lua has no need for the timestamp or HTML added here.
return message;
}
QString formatted = QString("[%1] %2").arg(QTime::currentTime().toString(), message);
if (message.startsWith("!")) {
formatted = QString("<font color='gray'>%1</font>").arg(formatted);
} else if (message.startsWith("*")) {
formatted = QString("<font color='maroon'>%1</font>").arg(formatted);
} else if (message.startsWith("$")) {
formatted = QString("<font color='#3cc46e'>%1</font>").arg(formatted);
} else if (message.startsWith("[")) {
if (color.isEmpty()) {
color = "#f29010";
}
formatted = QString("<font color='%2'>%1</font>").arg(formatted, color);
}
return formatted;
}
return QString();
}
QString IrcMessageFormatter::formatAwayMessage(IrcAwayMessage* message, bool isForLua)
{
QString content;
if (isForLua) {
content = IrcTextFormat().toPlainText(message->content());
} else {
content = IrcTextFormat().toHtml(message->content());
}
if (message->flags() & IrcMessage::Own) {
return QObject::tr("! %1").arg(content);
}
if (!message->content().isEmpty()) {
return QObject::tr("! %1 is away (%2)").arg(message->nick(), content);
}
return QObject::tr("! %1 is back").arg(message->nick());
}
QString IrcMessageFormatter::formatInviteMessage(IrcInviteMessage* message, bool isForLua)
{
Q_UNUSED(isForLua)
if (message->isReply()) {
return QObject::tr("! invited %1 to %2").arg(message->user(), message->channel());
}
return QObject::tr("! %2 invited to %3").arg(message->nick(), message->channel());
}
QString IrcMessageFormatter::formatJoinMessage(IrcJoinMessage* message, bool isForLua)
{
Q_UNUSED(isForLua)
if (message->flags() & IrcMessage::Own) {
return QObject::tr("! You have joined %1 as %2").arg(message->channel(), message->nick());
}
return QObject::tr("! %1 has joined %2").arg(message->nick(), message->channel());
}
QString IrcMessageFormatter::formatKickMessage(IrcKickMessage* message, bool isForLua)
{
Q_UNUSED(isForLua)
return QObject::tr("! %1 kicked %2").arg(message->nick(), message->user());
}
QString IrcMessageFormatter::formatModeMessage(IrcModeMessage* message, bool isForLua)
{
Q_UNUSED(isForLua)
const QString args = message->arguments().join(" ");
if (message->isReply()) {
return QObject::tr("! %1 mode is %2 %3").arg(message->target(), message->mode(), args);
}
return QObject::tr("! %1 sets mode %2 %3 %4").arg(message->nick(), message->target(), message->mode(), args);
}
QString IrcMessageFormatter::formatMotdMessage(IrcMotdMessage* message, bool isForLua)
{
QString motdData;
for (const auto& line : message->lines()) {
QString content, lineEnd;
if (isForLua) {
lineEnd = "\n";
content = IrcTextFormat().toPlainText(line);
} else {
lineEnd = "<br />\n";
content = IrcTextFormat().toHtml(line);
}
motdData += QObject::tr("[MOTD] %1%2").arg(content, lineEnd);
}
return motdData;
}
QString IrcMessageFormatter::formatNamesMessage(IrcNamesMessage* message, bool isForLua)
{
const QString count = QString::number(message->names().count());
if (isForLua) {
// lua actually needs the names for parsing, since getting a names
// list from the UI userModel alone would be limiting to the IRC commands.
const QString nameList = message->names().join(" ");
return QObject::tr("! %1 has %2 users: %3").arg(message->channel(), count, nameList);
}
return QObject::tr("! %1 has %2 users").arg(message->channel(), count);
}
QString IrcMessageFormatter::formatNickMessage(IrcNickMessage* message, bool isForLua)
{
Q_UNUSED(isForLua)
return QObject::tr("! %1 has changed nick to %2").arg(message->oldNick(), message->newNick());
}
QString IrcMessageFormatter::formatNoticeMessage(IrcNoticeMessage* message, bool isForLua)
{
if (message->isReply()) {
const QStringList params = message->content().split(" ", Qt::SkipEmptyParts);
const QString cmd = params.value(0);
if (cmd.toUpper() == "PING") {
const QString secs = formatSeconds(params.value(1).toInt());
return QObject::tr("! %1 replied in %2").arg(message->nick(), secs);
}
if (cmd.toUpper() == "TIME") {
const QString rest = QStringList(params.mid(1)).join(" ");
return QObject::tr("! %1 time is %2").arg(message->nick(), rest);
}
if (cmd.toUpper() == "VERSION") {
const QString rest = QStringList(params.mid(1)).join(" ");
return QObject::tr("! %1 version is %2").arg(message->nick(), rest);
}
}
QString pfx = message->statusPrefix();
if (!pfx.isEmpty()) {
pfx = ":" + pfx;
}
if (message->isPrivate()) {
QString content;
if (isForLua) {
content = IrcTextFormat().toPlainText(message->content());
} else {
content = IrcTextFormat().toHtml(message->content());
}
return QObject::tr("[%1%2] %3").arg(message->nick(), pfx, content);
}
if (isForLua) {
// lua only needs the message text.
return IrcTextFormat().toPlainText(message->content());
}
const QString content = IrcTextFormat().toHtml(message->content());
return QObject::tr("&lt;%1%2&gt; [%3] %4").arg(message->nick(), pfx, message->target(), content);
}
QString IrcMessageFormatter::formatNumericMessage(IrcNumericMessage* message, bool isForLua)
{
if (message->code() < 300) {
const QString info = QStringList(message->parameters().mid(1)).join(" ");
QString content;
if (isForLua) {
content = IrcTextFormat().toPlainText(info);
} else {
content = IrcTextFormat().toHtml(info);
}
return QObject::tr("[INFO] %1").arg(info);
}
switch (message->code()) {
case Irc::RPL_VERSION:
return QObject::tr("! %1 version is %2").arg(message->nick(), message->parameters().value(1));
case Irc::RPL_TIME:
return QObject::tr("! %1 time is %2").arg(message->parameters().value(1), message->parameters().value(2));
default:
break;
}
if (message->isComposed() || message->flags() & IrcMessage::Implicit) {
return QString();
}
// if you change this, change formatErrorMessage too
if (Irc::codeToString(message->code()).startsWith("ERR_")) {
const QString info = QStringList(message->parameters().mid(1)).join(" ");
QString content;
if (isForLua) {
content = IrcTextFormat().toPlainText(info);
} else {
content = IrcTextFormat().toHtml(info);
}
return QObject::tr("[ERROR] %1").arg(content);
}
if (message->code() == Irc::RPL_CHANNEL_URL) {
const QString info = QStringList(message->parameters().mid(1)).join(" ");
QString content;
if (isForLua) {
content = IrcTextFormat().toPlainText(info);
} else {
content = IrcTextFormat().toHtml(info);
}
return QObject::tr("[Channel URL] %1").arg(content);
}
const QString info = QStringList(message->parameters().mid(1)).join(" ");
QString content;
if (isForLua) {
content = IrcTextFormat().toPlainText(info);
} else {
content = IrcTextFormat().toHtml(info);
}
return QObject::tr("[%1] %2").arg(QString::number(message->code()), content);
}
QString IrcMessageFormatter::formatErrorMessage(IrcErrorMessage* message, bool isForLua)
{
Q_UNUSED(isForLua)
// if you change this, change ERR_ in formatNumericMessage too
return QObject::tr("[ERROR] %1").arg(message->error());
}
QString IrcMessageFormatter::formatPartMessage(IrcPartMessage* message, bool isForLua)
{
Q_UNUSED(isForLua)
if (message->reason().isEmpty()) {
return QObject::tr("! %1 has left %2").arg(message->nick(), message->channel());
}
return QObject::tr("! %1 has left %2 (%3)").arg(message->nick(), message->channel(), message->reason());
}
QString IrcMessageFormatter::formatPongMessage(IrcPongMessage* message, bool isForLua)
{
Q_UNUSED(isForLua)
quint64 const msec = message->timeStamp().toMSecsSinceEpoch();
quint64 const dms = (QDateTime::currentMSecsSinceEpoch() - msec);
return QObject::tr("! %1 replied in %2 seconds").arg(message->nick()).arg(dms / 1000.0, 4, 'f', 3, QLatin1Char('0'));
}
// Normal messages sent to channels are processed by our client as if they are private messages.
QString IrcMessageFormatter::formatPrivateMessage(IrcPrivateMessage* message, bool isForLua)
{
QString content;
if (isForLua) {
content = IrcTextFormat().toPlainText(message->content());
} else {
content = IrcTextFormat().toHtml(message->content());
}
if (message->isAction()) {
return QObject::tr("* %1 %2").arg(message->nick(), content);
}
if (isForLua) {
// lua only needs the message text here. Nick and target are sent as arguments to postIrcMessage()
return content;
}
return QObject::tr("<b>&lt;%1&gt;</b> %2").arg(message->nick(), content);
}
QString IrcMessageFormatter::formatQuitMessage(IrcQuitMessage* message, bool isForLua)
{
Q_UNUSED(isForLua)
if (message->reason().isEmpty()) {
return QObject::tr("! %1 has quit").arg(message->nick());
}
return QObject::tr("! %1 has quit (%2)").arg(message->nick(), message->reason());
}
QString IrcMessageFormatter::formatTopicMessage(IrcTopicMessage* message, bool isForLua)
{
if (message->isReply()) {
if (message->topic().isEmpty()) {
return QObject::tr("! no topic");
}
QString topic;
if (isForLua) {
topic = IrcTextFormat().toPlainText(message->topic());
} else {
topic = IrcTextFormat().toHtml(message->topic());
}
return QObject::tr("[TOPIC] %1").arg(topic);
}
if (message->topic().isEmpty()) {
return QObject::tr("! %2 cleared topic").arg(message->nick());
}
return QObject::tr("! %2 changed topic").arg(message->nick());
}
QString IrcMessageFormatter::formatUnknownMessage(IrcMessage* message, bool isForLua)
{
Q_UNUSED(isForLua)
return QObject::tr("? %2 %3 %4").arg(message->nick(), message->command(), message->parameters().join(" "));
}
QString IrcMessageFormatter::formatWhoisMessage(IrcWhoisMessage* message, bool isForLua)
{
Q_UNUSED(isForLua)
QString wData;
wData = QObject::tr("[WHOIS] %1 is %2@%3 (%4)").arg(message->nick(), message->ident(), message->host(), message->realName());
wData += QObject::tr("[WHOIS] %1 is connected via %2 (%3)").arg(message->nick(), message->server(), message->info());
wData += QObject::tr("[WHOIS] %1 is connected since %2 (idle %3)").arg(message->nick(), message->since().toString(), formatDuration(message->idle()));
if (!message->awayReason().isEmpty()) {
wData += QObject::tr("[WHOIS] %1 is away: %2").arg(message->nick(), message->awayReason());
}
if (!message->account().isEmpty()) {
wData += QObject::tr("[WHOIS] %1 is logged in as %2").arg(message->nick(), message->account());
}
if (!message->address().isEmpty()) {
wData += QObject::tr("[WHOIS] %1 is connected from %2").arg(message->nick(), message->address());
}
if (message->isSecure()) {
wData += QObject::tr("[WHOIS] %1 is using a secure connection").arg(message->nick());
}
if (!message->channels().isEmpty()) {
wData += QObject::tr("[WHOIS] %1 is on %2").arg(message->nick(), message->channels().join(" "));
}
return wData;
}
QString IrcMessageFormatter::formatWhowasMessage(IrcWhowasMessage* message, bool isForLua)
{
Q_UNUSED(isForLua)
QString wData;
wData = QObject::tr("[WHOWAS] %1 was %2@%3 (%4)").arg(message->nick(), message->ident(), message->host(), message->realName());
wData += QObject::tr("[WHOWAS] %1 was connected via %2 (%3)").arg(message->nick(), message->server(), message->info());
if (!message->account().isEmpty()) {
wData += QObject::tr("[WHOWAS] %1 was logged in as %2").arg(message->nick(), message->account());
}
return wData;
}
QString IrcMessageFormatter::formatWhoReplyMessage(IrcWhoReplyMessage* message, bool isForLua)
{
Q_UNUSED(isForLua)
QString format = QObject::tr("[WHO] %1 (%2)").arg(message->nick(), message->realName());
if (message->isAway()) {
format += QObject::tr(" - away");
}
if (message->isServOp()) {
format += QObject::tr(" - server operator");
}
return format;
}
QString IrcMessageFormatter::formatSeconds(int secs)
{
const QDateTime time = QDateTime::fromSecsSinceEpoch(secs);
return QObject::tr("%1s").arg(time.secsTo(QDateTime::currentDateTime()));
}
QString IrcMessageFormatter::formatDuration(int secs)
{
QStringList idle;
if (const int days = secs / 86400) {
idle += QObject::tr("%1 days").arg(days);
}
secs %= 86400;
if (const int hours = secs / 3600) {
idle += QObject::tr("%1 hours").arg(hours);
}
secs %= 3600;
if (const int mins = secs / 60) {
idle += QObject::tr("%1 mins").arg(mins);
}
idle += QObject::tr("%1 secs").arg(secs % 60);
return idle.join(" ");
}