diff --git a/channel.cpp b/channel.cpp index 00f5a76c4..e0665bd12 100644 --- a/channel.cpp +++ b/channel.cpp @@ -1,5 +1,6 @@ #include "channel.h" #include "emotes.h" +#include "logging/loggingmanager.h" #include "messages/message.h" #include "windows.h" @@ -24,6 +25,7 @@ Channel::Channel(const QString &channel) "/subscribe?ref=in_chat_subscriber_link") , channelLink("https://twitch.tv/" + name) , popoutPlayerLink("https://player.twitch.tv/?channel=" + name) + , loggingChannel(logging::get(name)) { reloadChannelEmotes(); } @@ -127,6 +129,10 @@ Channel::addMessage(std::shared_ptr message) { std::shared_ptr deleted; + if (this->loggingChannel.get() != nullptr) { + this->loggingChannel->append(message); + } + if (this->messages.appendItem(message, deleted)) { this->messageRemovedFromStart(deleted); } @@ -135,4 +141,5 @@ Channel::addMessage(std::shared_ptr message) Windows::repaintVisibleChatWidgets(this); } -} + +} // namespace chatterino diff --git a/channel.h b/channel.h index a3a2d0405..bde15b923 100644 --- a/channel.h +++ b/channel.h @@ -2,6 +2,7 @@ #define CHANNEL_H #include "concurrentmap.h" +#include "logging/loggingchannel.h" #include "messages/lazyloadedimage.h" #include "messages/limitedqueue.h" @@ -128,10 +129,12 @@ private: int streamViewerCount; QString streamStatus; QString streamGame; + std::shared_ptr loggingChannel; void reloadBttvEmotes(); void reloadFfzEmotes(); }; -} + +} // namespace chatterino #endif // CHANNEL_H diff --git a/channels.cpp b/channels.cpp index f6fcda7ad..4758693cd 100644 --- a/channels.cpp +++ b/channels.cpp @@ -3,13 +3,39 @@ namespace chatterino { -std::shared_ptr Channels::whispers(new Channel(QString("/whispers"))); -std::shared_ptr Channels::mentions(new Channel(QString("/mentions"))); -std::shared_ptr Channels::empty(new Channel(QString(""))); +static std::shared_ptr whispers(nullptr); +static std::shared_ptr mentions(nullptr); +static std::shared_ptr empty(nullptr); QMap, int>> Channels::channels; QMutex Channels::channelsMutex; +void +initChannels() +{ + whispers.reset(new Channel(QString("/whispers"))); + mentions.reset(new Channel(QString("/mentions"))); + empty.reset(new Channel(QString(""))); +} + +std::shared_ptr +Channels::getWhispers() +{ + return whispers; +} + +std::shared_ptr +Channels::getMentions() +{ + return mentions; +} + +std::shared_ptr +Channels::getEmpty() +{ + return empty; +} + const std::vector> Channels::getItems() { @@ -60,20 +86,20 @@ Channels::getChannel(const QString &channel) if (channel.length() > 1 && channel.at(0) == '/') { if (c == "/whispers") { - return Channels::whispers; + return whispers; } if (c == "/mentions") { - return Channels::mentions; + return mentions; } - return Channels::empty; + return empty; } auto a = Channels::channels.find(c); if (a == Channels::channels.end()) { - return Channels::empty; + return empty; } return std::get<0>(a.value()); @@ -103,4 +129,5 @@ Channels::removeChannel(const QString &channel) Channels::channels.remove(c); } } -} + +} // namespace chatterino diff --git a/channels.h b/channels.h index 0d5fbe499..46b547e06 100644 --- a/channels.h +++ b/channels.h @@ -5,26 +5,14 @@ namespace chatterino { +void initChannels(); + class Channels { public: - static std::shared_ptr - getWhispers() - { - return whispers; - } - - static std::shared_ptr - getMentions() - { - return mentions; - } - - static std::shared_ptr - getEmpty() - { - return empty; - } + static std::shared_ptr getWhispers(); + static std::shared_ptr getMentions(); + static std::shared_ptr getEmpty(); const static std::vector> getItems(); @@ -37,12 +25,10 @@ private: { } - static std::shared_ptr whispers; - static std::shared_ptr mentions; - static std::shared_ptr empty; - static QMap, int>> channels; static QMutex channelsMutex; }; -} + +} // namespace chatterino + #endif // CHANNELS_H diff --git a/chatterino.pro b/chatterino.pro index 7b582562c..13819c5e2 100644 --- a/chatterino.pro +++ b/chatterino.pro @@ -53,8 +53,9 @@ SOURCES += main.cpp\ widgets/settingsdialogtab.cpp \ widgets/textinputdialog.cpp \ windows.cpp \ - logging.cpp \ - messages/messageref.cpp + messages/messageref.cpp \ + logging/loggingmanager.cpp \ + logging/loggingchannel.cpp HEADERS += account.h \ asyncexec.h \ @@ -96,10 +97,11 @@ HEADERS += account.h \ windows.h \ widgets/resizingtextedit.h \ settingssnapshot.h \ - logging.h \ messages/limitedqueue.h \ messages/limitedqueuesnapshot.h \ - messages/messageref.h + messages/messageref.h \ + logging/loggingmanager.h \ + logging/loggingchannel.h PRECOMPILED_HEADER = diff --git a/ircmanager.h b/ircmanager.h index 2c25d9666..17f9a0124 100644 --- a/ircmanager.h +++ b/ircmanager.h @@ -12,6 +12,8 @@ #include #include +#include + namespace chatterino { class IrcManager diff --git a/logging.cpp b/logging.cpp deleted file mode 100644 index 5a33d793b..000000000 --- a/logging.cpp +++ /dev/null @@ -1,6 +0,0 @@ -#include "logging.h" - -Logging::Logging() -{ - -} diff --git a/logging.h b/logging.h deleted file mode 100644 index 2b2e40a21..000000000 --- a/logging.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef LOGGING_H -#define LOGGING_H - -class Logging -{ -public: -private: - Logging(); -}; - -#endif // LOGGING_H diff --git a/logging/loggingchannel.cpp b/logging/loggingchannel.cpp new file mode 100644 index 000000000..fa58fac3b --- /dev/null +++ b/logging/loggingchannel.cpp @@ -0,0 +1,82 @@ +#include "loggingchannel.h" +#include "loggingmanager.h" + +#include + +#include + +namespace chatterino { +namespace logging { + +Channel::Channel(const QString &_channelName, const QString &_baseDirectory) + : channelName(_channelName) + , baseDirectory(_baseDirectory) +{ + QDateTime now = QDateTime::currentDateTime(); + + this->fileName = + this->channelName + "-" + now.toString("yyyy-MM-dd") + ".log"; + + // Open file handle to log file of current date + this->fileHandle.setFileName(this->baseDirectory + QDir::separator() + + this->fileName); + + this->fileHandle.open(QIODevice::Append); + this->appendLine(this->generateOpeningString(now)); +} + +Channel::~Channel() +{ + this->appendLine(this->generateClosingString()); + this->fileHandle.close(); +} + +void +Channel::append(std::shared_ptr message) +{ + QDateTime now = QDateTime::currentDateTime(); + + QString str; + str.append('['); + str.append(now.toString("HH:mm:ss")); + str.append("] "); + str.append(message->getUserName()); + str.append(": "); + str.append(message->getContent()); + str.append('\n'); + this->appendLine(str); +} + +QString +Channel::generateOpeningString(const QDateTime &now) const +{ + QString ret = QLatin1Literal("# Start logging at "); + + ret.append(now.toString("yyyy-MM-dd HH:mm:ss ")); + ret.append(now.timeZoneAbbreviation()); + ret.append('\n'); + + return ret; +} + +QString +Channel::generateClosingString(const QDateTime &now) const +{ + QString ret = QLatin1Literal("# Stop logging at "); + + ret.append(now.toString("yyyy-MM-dd HH:mm:ss")); + ret.append(now.timeZoneAbbreviation()); + ret.append('\n'); + + return ret; +} + +void +Channel::appendLine(const QString &line) +{ + this->fileHandle.write(line.toUtf8()); + this->fileHandle.flush(); +} + +} // namespace logging +} // namespace chatterino diff --git a/logging/loggingchannel.h b/logging/loggingchannel.h new file mode 100644 index 000000000..7e777ff7c --- /dev/null +++ b/logging/loggingchannel.h @@ -0,0 +1,42 @@ +#ifndef LOGGINGCHANNEL_H +#define LOGGINGCHANNEL_H + +#include "messages/message.h" + +#include +#include +#include + +#include + +namespace chatterino { +namespace logging { + +class Channel +{ +public: + explicit Channel(const QString &_channelName, + const QString &_baseDirectory); + ~Channel(); + + void append(std::shared_ptr message); + +private: + QString generateOpeningString( + const QDateTime &now = QDateTime::currentDateTime()) const; + QString generateClosingString( + const QDateTime &now = QDateTime::currentDateTime()) const; + + void appendLine(const QString &line); + +private: + QString channelName; + const QString &baseDirectory; + QString fileName; + QFile fileHandle; +}; + +} // namespace logging +} // namespace chatterino + +#endif // LOGGINGCHANNEL_H diff --git a/logging/loggingmanager.cpp b/logging/loggingmanager.cpp new file mode 100644 index 000000000..15a92ce66 --- /dev/null +++ b/logging/loggingmanager.cpp @@ -0,0 +1,104 @@ +#include "loggingmanager.h" + +#include +#include + +#include + +namespace chatterino { +namespace logging { + +static QString logBasePath; +static QString channelBasePath; +static QString whispersBasePath; +static QString mentionsBasePath; + +std::unordered_map> channels; + +void +init() +{ + // Make sure all folders are properly created + logBasePath = + QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + + QDir::separator() + "Logs"; + channelBasePath = logBasePath + QDir::separator() + "Channels"; + whispersBasePath = logBasePath + QDir::separator() + "Whispers"; + mentionsBasePath = logBasePath + QDir::separator() + "Mentions"; + + { + QDir dir(logBasePath); + if (!dir.exists()) { + dir.mkpath("."); + } + } + + { + QDir dir(channelBasePath); + if (!dir.exists()) { + dir.mkpath("."); + } + } + + { + QDir dir(whispersBasePath); + if (!dir.exists()) { + dir.mkpath("."); + } + } + + { + QDir dir(mentionsBasePath); + if (!dir.exists()) { + dir.mkpath("."); + } + } +} + +static const QString & +getBaseDirectory(const QString &channelName) +{ + if (channelName == "/whispers") { + return whispersBasePath; + } else if (channelName == "/mentions") { + return mentionsBasePath; + } else { + return channelBasePath; + } +} + +std::shared_ptr +get(const QString &channelName) +{ + if (channelName.isEmpty()) { + return nullptr; + } + + const QString &baseDirectory = getBaseDirectory(channelName); + + auto channel = channels.find(channelName.toStdString()); + if (channel == std::end(channels)) { + // This channel is definitely not logged yet. + // Create shared_ptr that we will return, and store a weak_ptr reference + std::shared_ptr ret = + std::shared_ptr(new Channel(channelName, baseDirectory)); + + channels[channelName.toStdString()] = std::weak_ptr(ret); + + return ret; + } + + if (auto ret = channels[channelName.toStdString()].lock()) { + return ret; + } + + std::shared_ptr ret = + std::shared_ptr(new Channel(channelName, baseDirectory)); + + channels[channelName.toStdString()] = std::weak_ptr(ret); + + return ret; +} + +} // namespace logging +} // namespace chatterino diff --git a/logging/loggingmanager.h b/logging/loggingmanager.h new file mode 100644 index 000000000..e7d7d0399 --- /dev/null +++ b/logging/loggingmanager.h @@ -0,0 +1,17 @@ +#ifndef LOGGINGMANAGER_H +#define LOGGINGMANAGER_H + +#include "loggingchannel.h" + +#include + +namespace chatterino { +namespace logging { + +void init(); +std::shared_ptr get(const QString &channelName); + +} // namespace logging +} // namespace chatterino + +#endif // LOGGINGMANAGER_H diff --git a/main.cpp b/main.cpp index 72ea0727f..2b502fdc2 100644 --- a/main.cpp +++ b/main.cpp @@ -3,6 +3,7 @@ #include "emojis.h" #include "emotes.h" #include "ircmanager.h" +#include "logging/loggingmanager.h" #include "resources.h" #include "settings.h" #include "widgets/mainwindow.h" @@ -20,6 +21,8 @@ main(int argc, char *argv[]) { QApplication a(argc, argv); + chatterino::logging::init(); + chatterino::initChannels(); Settings::getInstance().load(); Resources::load(); Emojis::loadEmojis(); diff --git a/messages/message.cpp b/messages/message.cpp index 80700f003..846092a59 100644 --- a/messages/message.cpp +++ b/messages/message.cpp @@ -1,4 +1,5 @@ #include "messages/message.h" +#include "channel.h" #include "colorscheme.h" #include "emojis.h" #include "emotes.h" @@ -30,6 +31,7 @@ Message::Message(const QString &text) Message::Message(const IrcPrivateMessage &ircMessage, Channel &channel, bool enablePingSound, bool isReceivedWhisper, bool isSentWhisper, bool includeChannel) + : content(ircMessage.content()) { this->parseTime = std::chrono::system_clock::now(); @@ -144,14 +146,10 @@ Message::Message(const IrcPrivateMessage &ircMessage, Channel &channel, } // username - this->userName = ircMessage.account(); + this->userName = ircMessage.nick(); if (this->userName.isEmpty()) { - auto iterator = tags.find("login"); - - if (iterator != tags.end()) { - this->userName = iterator.value().toString(); - } + this->userName = tags.value(QLatin1String("login")).toString(); } QString displayName; diff --git a/messages/message.h b/messages/message.h index e017c8778..da3c0a964 100644 --- a/messages/message.h +++ b/messages/message.h @@ -1,15 +1,18 @@ #ifndef MESSAGE_H #define MESSAGE_H -#include "channel.h" #include "messages/word.h" #include "messages/wordpart.h" #include #include + #include namespace chatterino { + +class Channel; + namespace messages { class Message @@ -54,6 +57,18 @@ public: return displayName; } + inline const QString & + getContent() const + { + return this->content; + } + + inline const std::chrono::time_point & + getParseTime() const + { + return this->parseTime; + } + std::vector & getWords() { @@ -91,6 +106,7 @@ private: QString userName = ""; QString displayName = ""; + QString content; QString id = ""; std::vector words; diff --git a/messages/word.cpp b/messages/word.cpp index 32ecbd4d8..6e31794c3 100644 --- a/messages/word.cpp +++ b/messages/word.cpp @@ -33,5 +33,6 @@ Word::Word(const QString &text, Type type, const QColor &color, , characterWidthCache() { } -} -} + +} // namespace messages +} // namespace chatterino diff --git a/messages/word.h b/messages/word.h index 1e3d3808f..73467e8ed 100644 --- a/messages/word.h +++ b/messages/word.h @@ -208,7 +208,8 @@ private: std::vector characterWidthCache; }; -} -} + +} // namespace messages +} // namespace chatterino #endif // WORD_H diff --git a/widgets/chatwidget.cpp b/widgets/chatwidget.cpp index 1c05fc20a..fb2f5abf4 100644 --- a/widgets/chatwidget.cpp +++ b/widgets/chatwidget.cpp @@ -76,8 +76,6 @@ ChatWidget::setChannelName(const QString &name) auto messageRef = new messages::MessageRef(message); - qDebug() << "xD"; - this->messages.appendItem( std::shared_ptr(messageRef), deleted); });