diff --git a/CHANGELOG.md b/CHANGELOG.md index 8918ffdf..e263cde5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -51,6 +51,7 @@ - Dev: Mock headers are now added as a header set if supported by CMake. (#6561) - Dev: Set settings directory to temporary one used in tests. (#6584) - Dev: Check Lua unwinding and version in tests. (#6586) +- Dev: Added method to get the last N messages of a channel. (#6602) ## 2.5.4 diff --git a/src/common/Channel.cpp b/src/common/Channel.cpp index 87104c3f..eff0e88e 100644 --- a/src/common/Channel.cpp +++ b/src/common/Channel.cpp @@ -71,11 +71,17 @@ bool Channel::hasMessages() const return !this->messages_.empty(); } -LimitedQueueSnapshot Channel::getMessageSnapshot() +LimitedQueueSnapshot Channel::getMessageSnapshot() const { return this->messages_.getSnapshot(); } +LimitedQueueSnapshot Channel::getMessageSnapshot( + size_t nItems) const +{ + return this->messages_.lastN(nItems); +} + void Channel::addMessage(MessagePtr message, MessageContext context, std::optional overridingFlags) { @@ -117,7 +123,7 @@ void Channel::addSystemMessage(const QString &contents) void Channel::addOrReplaceTimeout(MessagePtr message, const QDateTime &now) { addOrReplaceChannelTimeout( - this->getMessageSnapshot(), std::move(message), now, + this->getMessageSnapshot(20), std::move(message), now, [this](auto /*idx*/, auto msg, auto replacement) { this->replaceMessage(msg, replacement); }, @@ -130,7 +136,7 @@ void Channel::addOrReplaceTimeout(MessagePtr message, const QDateTime &now) void Channel::addOrReplaceClearChat(MessagePtr message, const QDateTime &now) { addOrReplaceChannelClear( - this->getMessageSnapshot(), std::move(message), now, + this->getMessageSnapshot(20), std::move(message), now, [this](auto /*idx*/, auto msg, auto replacement) { this->replaceMessage(msg, replacement); }, diff --git a/src/common/Channel.hpp b/src/common/Channel.hpp index 02308dab..074cf389 100644 --- a/src/common/Channel.hpp +++ b/src/common/Channel.hpp @@ -79,7 +79,8 @@ public: virtual const QString &getLocalizedName() const; bool isTwitchChannel() const; virtual bool isEmpty() const; - LimitedQueueSnapshot getMessageSnapshot(); + LimitedQueueSnapshot getMessageSnapshot() const; + LimitedQueueSnapshot getMessageSnapshot(size_t nItems) const; // MESSAGES // overridingFlags can be filled in with flags that should be used instead diff --git a/src/controllers/notifications/NotificationController.cpp b/src/controllers/notifications/NotificationController.cpp index 8e229d8c..333da102 100644 --- a/src/controllers/notifications/NotificationController.cpp +++ b/src/controllers/notifications/NotificationController.cpp @@ -154,13 +154,9 @@ void NotificationController::notifyTwitchChannelLive( void NotificationController::notifyTwitchChannelOffline(const QString &id) const { // "delete" old 'CHANNEL is live' message - LimitedQueueSnapshot snapshot = - getApp()->getTwitch()->getLiveChannel()->getMessageSnapshot(); - int snapshotLength = static_cast(snapshot.size()); - - int end = std::max(0, snapshotLength - 200); - - for (int i = snapshotLength - 1; i >= end; --i) + const LimitedQueueSnapshot snapshot = + getApp()->getTwitch()->getLiveChannel()->getMessageSnapshot(200); + for (size_t i = snapshot.size() - 1; i >= 0; --i) { const auto &s = snapshot[i]; diff --git a/src/messages/LimitedQueue.hpp b/src/messages/LimitedQueue.hpp index 4d223ab1..fb4647be 100644 --- a/src/messages/LimitedQueue.hpp +++ b/src/messages/LimitedQueue.hpp @@ -324,6 +324,18 @@ public: return LimitedQueueSnapshot(this->buffer_); } + [[nodiscard]] LimitedQueueSnapshot lastN(size_t nItems) const + { + std::shared_lock lock(this->mutex_); + return LimitedQueueSnapshot::lastN(this->buffer_, nItems); + } + + [[nodiscard]] LimitedQueueSnapshot firstN(size_t nItems) const + { + std::shared_lock lock(this->mutex_); + return LimitedQueueSnapshot::firstN(this->buffer_, nItems); + } + // Actions /** diff --git a/src/messages/LimitedQueueSnapshot.hpp b/src/messages/LimitedQueueSnapshot.hpp index 9e8da8b7..ac5f032d 100644 --- a/src/messages/LimitedQueueSnapshot.hpp +++ b/src/messages/LimitedQueueSnapshot.hpp @@ -22,6 +22,24 @@ private: { } + static LimitedQueueSnapshot lastN(const boost::circular_buffer &buf, + size_t n) + { + return {buf.end() - std::min(n, buf.size()), buf.end()}; + } + + static LimitedQueueSnapshot firstN(const boost::circular_buffer &buf, + size_t n) + { + return {buf.begin(), buf.begin() + std::min(n, buf.size())}; + } + + template + LimitedQueueSnapshot(It begin, It end) + : buffer_(std::move(begin), std::move(end)) + { + } + public: LimitedQueueSnapshot() = default; diff --git a/tests/src/LimitedQueue.cpp b/tests/src/LimitedQueue.cpp index 80396978..aa77bfca 100644 --- a/tests/src/LimitedQueue.cpp +++ b/tests/src/LimitedQueue.cpp @@ -264,3 +264,51 @@ TEST(LimitedQueue, Find) }) .has_value()); } + +TEST(LimitedQueue, LastN) +{ + LimitedQueue queue(10); + queue.pushBack(1); + queue.pushBack(2); + queue.pushBack(3); + queue.pushBack(4); + queue.pushBack(5); + queue.pushBack(6); + + SNAPSHOT_EQUALS(queue.lastN(0), {}, "no item"); + SNAPSHOT_EQUALS(queue.lastN(1), {6}, "one item"); + SNAPSHOT_EQUALS(queue.lastN(2), {5, 6}, "two items"); + SNAPSHOT_EQUALS(queue.lastN(6), {1, 2, 3, 4, 5, 6}, "all items"); + SNAPSHOT_EQUALS(queue.lastN(7), {1, 2, 3, 4, 5, 6}, "all items"); + SNAPSHOT_EQUALS(queue.lastN(12), {1, 2, 3, 4, 5, 6}, "all items"); + + LimitedQueue empty(10); + SNAPSHOT_EQUALS(empty.lastN(0), {}, "empty"); + SNAPSHOT_EQUALS(empty.lastN(1), {}, "empty"); + SNAPSHOT_EQUALS(empty.lastN(2), {}, "empty"); + SNAPSHOT_EQUALS(empty.lastN(6), {}, "empty"); +} + +TEST(LimitedQueue, FirstN) +{ + LimitedQueue queue(10); + queue.pushBack(1); + queue.pushBack(2); + queue.pushBack(3); + queue.pushBack(4); + queue.pushBack(5); + queue.pushBack(6); + + SNAPSHOT_EQUALS(queue.firstN(0), {}, "no item"); + SNAPSHOT_EQUALS(queue.firstN(1), {1}, "one item"); + SNAPSHOT_EQUALS(queue.firstN(2), {1, 2}, "two items"); + SNAPSHOT_EQUALS(queue.firstN(6), {1, 2, 3, 4, 5, 6}, "all items"); + SNAPSHOT_EQUALS(queue.firstN(7), {1, 2, 3, 4, 5, 6}, "all items"); + SNAPSHOT_EQUALS(queue.firstN(12), {1, 2, 3, 4, 5, 6}, "all items"); + + LimitedQueue empty(10); + SNAPSHOT_EQUALS(empty.firstN(0), {}, "empty"); + SNAPSHOT_EQUALS(empty.firstN(1), {}, "empty"); + SNAPSHOT_EQUALS(empty.firstN(2), {}, "empty"); + SNAPSHOT_EQUALS(empty.firstN(6), {}, "empty"); +}