mirror of
https://github.com/Chatterino/chatterino2.git
synced 2024-11-21 22:24:07 +01:00
Compare commits
15 commits
e85d8da9f5
...
c257161112
Author | SHA1 | Date | |
---|---|---|---|
|
c257161112 | ||
|
2a38e39e24 | ||
|
1c827f6288 | ||
|
19f449866e | ||
|
0ed8311c1a | ||
|
ed6ddead3d | ||
|
2215455ca0 | ||
|
9e73ba2919 | ||
|
50b51b2351 | ||
|
6b5cc5f8d9 | ||
|
56ba1ca2ee | ||
|
8a69861ead | ||
|
40490a90fb | ||
|
1199f5e763 | ||
|
12fa816564 |
29 changed files with 336 additions and 62 deletions
|
@ -29,7 +29,6 @@ cmake \
|
|||
-DCMAKE_EXPORT_COMPILE_COMMANDS=On \
|
||||
-DCHATTERINO_PLUGINS="$C2_PLUGINS" \
|
||||
-DCMAKE_PREFIX_PATH="$Qt6_DIR/lib/cmake" \
|
||||
-DBUILD_WITH_QT6="$C2_BUILD_WITH_QT6" \
|
||||
-DCHATTERINO_STATIC_QT_BUILD=On \
|
||||
-DCMAKE_CXX_FLAGS="-fno-sized-deallocation" \
|
||||
.
|
||||
|
|
|
@ -19,7 +19,6 @@ cmake -S. -Bbuild-clang-tidy \
|
|||
-DCMAKE_EXPORT_COMPILE_COMMANDS=On \
|
||||
-DCHATTERINO_LTO=Off \
|
||||
-DCHATTERINO_PLUGINS=On \
|
||||
-DBUILD_WITH_QT6=On \
|
||||
-DBUILD_TESTS=On \
|
||||
-DBUILD_BENCHMARKS=On
|
||||
|
||||
|
|
|
@ -20,7 +20,6 @@ task:
|
|||
-DUSE_SYSTEM_QTKEYCHAIN="ON" \
|
||||
-DCMAKE_BUILD_TYPE="release" \
|
||||
-DCMAKE_EXPORT_COMPILE_COMMANDS="ON" \
|
||||
-DBUILD_WITH_QT6="ON" \
|
||||
..
|
||||
cat compile_commands.json
|
||||
make -j $(getconf _NPROCESSORS_ONLN)
|
||||
|
|
|
@ -3,7 +3,6 @@ ARG UBUNTU_VERSION=22.04
|
|||
FROM ubuntu:$UBUNTU_VERSION
|
||||
|
||||
ARG QT_VERSION=6.2.4
|
||||
ARG BUILD_WITH_QT6=ON
|
||||
|
||||
ENV TZ=UTC
|
||||
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
|
||||
|
@ -48,7 +47,6 @@ RUN mkdir /src/build
|
|||
# cmake
|
||||
RUN cd /src/build && \
|
||||
CXXFLAGS=-fno-sized-deallocation cmake \
|
||||
-DBUILD_WITH_QT6=$BUILD_WITH_QT6 \
|
||||
-DCMAKE_INSTALL_PREFIX=appdir/usr/ \
|
||||
-DCMAKE_PREFIX_PATH=/opt/qt/$QT_VERSION/gcc_64/lib/cmake \
|
||||
-DBUILD_WITH_QTKEYCHAIN=OFF \
|
||||
|
|
6
.github/workflows/build.yml
vendored
6
.github/workflows/build.yml
vendored
|
@ -62,7 +62,6 @@ jobs:
|
|||
C2_ENABLE_LTO: ${{ matrix.force-lto }}
|
||||
C2_PLUGINS: ${{ matrix.plugins }}
|
||||
C2_ENABLE_CRASHPAD: ${{ matrix.skip-crashpad == false }}
|
||||
C2_BUILD_WITH_QT6: ${{ startsWith(matrix.qt-version, '6.') }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
@ -87,7 +86,6 @@ jobs:
|
|||
-DCHATTERINO_LTO="$C2_ENABLE_LTO" \
|
||||
-DCHATTERINO_PLUGINS="$C2_PLUGINS" \
|
||||
-DCMAKE_PREFIX_PATH="$Qt6_DIR/lib/cmake" \
|
||||
-DBUILD_WITH_QT6="$C2_BUILD_WITH_QT6" \
|
||||
-DCHATTERINO_STATIC_QT_BUILD=On \
|
||||
..
|
||||
make -j"$(nproc)"
|
||||
|
@ -163,7 +161,7 @@ jobs:
|
|||
|
||||
- name: Install Qt5
|
||||
if: startsWith(matrix.qt-version, '5.')
|
||||
uses: jurplel/install-qt-action@v4.0.0
|
||||
uses: jurplel/install-qt-action@v4.1.1
|
||||
with:
|
||||
cache: true
|
||||
cache-key-prefix: ${{ runner.os }}-QtCache-${{ matrix.qt-version }}-v2
|
||||
|
@ -171,7 +169,7 @@ jobs:
|
|||
|
||||
- name: Install Qt6
|
||||
if: startsWith(matrix.qt-version, '6.')
|
||||
uses: jurplel/install-qt-action@v4.0.0
|
||||
uses: jurplel/install-qt-action@v4.1.1
|
||||
with:
|
||||
cache: true
|
||||
cache-key-prefix: ${{ runner.os }}-QtCache-${{ matrix.qt-version }}-v2
|
||||
|
|
6
.github/workflows/clang-tidy.yml
vendored
6
.github/workflows/clang-tidy.yml
vendored
|
@ -30,7 +30,7 @@ jobs:
|
|||
|
||||
- name: Install Qt6
|
||||
if: startsWith(matrix.qt-version, '6.')
|
||||
uses: jurplel/install-qt-action@v4.0.0
|
||||
uses: jurplel/install-qt-action@v4.1.1
|
||||
with:
|
||||
cache: true
|
||||
cache-key-prefix: ${{ runner.os }}-QtCache-${{ matrix.qt-version }}-v2
|
||||
|
@ -41,7 +41,7 @@ jobs:
|
|||
|
||||
- name: clang-tidy review
|
||||
timeout-minutes: 20
|
||||
uses: ZedThree/clang-tidy-review@v0.20.0
|
||||
uses: ZedThree/clang-tidy-review@v0.20.1
|
||||
with:
|
||||
build_dir: build-clang-tidy
|
||||
config_file: ".clang-tidy"
|
||||
|
@ -63,4 +63,4 @@ jobs:
|
|||
libxkbcommon-x11-0, libxcb-xkb-dev, libxcb-cursor0
|
||||
|
||||
- name: clang-tidy-review upload
|
||||
uses: ZedThree/clang-tidy-review/upload@v0.20.0
|
||||
uses: ZedThree/clang-tidy-review/upload@v0.20.1
|
||||
|
|
2
.github/workflows/post-clang-tidy-review.yml
vendored
2
.github/workflows/post-clang-tidy-review.yml
vendored
|
@ -14,7 +14,7 @@ jobs:
|
|||
if: ${{ github.event.workflow_run.conclusion == 'success' }}
|
||||
|
||||
steps:
|
||||
- uses: ZedThree/clang-tidy-review/post@v0.20.0
|
||||
- uses: ZedThree/clang-tidy-review/post@v0.20.1
|
||||
with:
|
||||
lgtm_comment_body: ""
|
||||
num_comments_as_exitcode: false
|
||||
|
|
2
.github/workflows/test-macos.yml
vendored
2
.github/workflows/test-macos.yml
vendored
|
@ -48,7 +48,7 @@ jobs:
|
|||
fetch-depth: 0 # allows for tags access
|
||||
|
||||
- name: Install Qt
|
||||
uses: jurplel/install-qt-action@v4.0.0
|
||||
uses: jurplel/install-qt-action@v4.1.1
|
||||
with:
|
||||
cache: true
|
||||
cache-key-prefix: ${{ runner.os }}-QtCache-${{ matrix.qt-version }}-v2
|
||||
|
|
2
.github/workflows/test-windows.yml
vendored
2
.github/workflows/test-windows.yml
vendored
|
@ -58,7 +58,7 @@ jobs:
|
|||
fetch-depth: 0 # allows for tags access
|
||||
|
||||
- name: Install Qt
|
||||
uses: jurplel/install-qt-action@v4.0.0
|
||||
uses: jurplel/install-qt-action@v4.1.1
|
||||
with:
|
||||
cache: true
|
||||
cache-key-prefix: ${{ runner.os }}-QtCache-${{ matrix.qt-version }}-v2
|
||||
|
|
6
.github/workflows/test.yml
vendored
6
.github/workflows/test.yml
vendored
|
@ -33,7 +33,6 @@ jobs:
|
|||
fail-fast: false
|
||||
env:
|
||||
C2_PLUGINS: ${{ matrix.plugins }}
|
||||
C2_BUILD_WITH_QT6: ${{ startsWith(matrix.qt-version, '6.') }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
@ -57,7 +56,6 @@ jobs:
|
|||
-DBUILD_APP=OFF \
|
||||
-DCHATTERINO_PLUGINS="$C2_PLUGINS" \
|
||||
-DCMAKE_PREFIX_PATH="$Qt6_DIR/lib/cmake" \
|
||||
-DBUILD_WITH_QT6="$C2_BUILD_WITH_QT6" \
|
||||
-DCHATTERINO_STATIC_QT_BUILD=On \
|
||||
-DCHATTERINO_GENERATE_COVERAGE=On \
|
||||
-DCMAKE_BUILD_TYPE=Debug \
|
||||
|
@ -98,9 +96,9 @@ jobs:
|
|||
working-directory: build-test
|
||||
|
||||
- name: Upload coverage reports to Codecov
|
||||
uses: codecov/codecov-action@v4.6.0
|
||||
uses: codecov/codecov-action@v5.0.7
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
plugin: gcov
|
||||
plugins: gcov
|
||||
fail_ci_if_error: true
|
||||
verbose: true
|
||||
|
|
|
@ -17,7 +17,7 @@ FreeBSD 15.0-SNAP.
|
|||
```
|
||||
1. Generate build files. To enable Lua plugins in your build add `-DCHATTERINO_PLUGINS=ON` to this command.
|
||||
```sh
|
||||
cmake -DBUILD_WITH_QT6=ON ..
|
||||
cmake ..
|
||||
```
|
||||
1. Build the project
|
||||
```sh
|
||||
|
|
|
@ -53,7 +53,7 @@ nix-shell -p openssl boost qt6.full pkg-config cmake
|
|||
```
|
||||
1. Generate build files. To enable Lua plugins in your build add `-DCHATTERINO_PLUGINS=ON` to this command.
|
||||
```sh
|
||||
cmake -DBUILD_WITH_QT6=ON -DBUILD_WITH_QTKEYCHAIN=OFF ..
|
||||
cmake -DBUILD_WITH_QTKEYCHAIN=OFF ..
|
||||
```
|
||||
1. Build the project
|
||||
```sh
|
||||
|
|
|
@ -10,7 +10,7 @@ Local dev machines for testing are available on Apple Silicon on macOS 13.
|
|||
1. Install [Homebrew](https://brew.sh/#install)
|
||||
We use this for dependency management on macOS
|
||||
1. Install all dependencies:
|
||||
`brew install boost openssl@1.1 rapidjson cmake qt@5`
|
||||
`brew install boost openssl@3 rapidjson cmake qt@6`
|
||||
|
||||
## Building
|
||||
|
||||
|
@ -21,7 +21,7 @@ Local dev machines for testing are available on Apple Silicon on macOS 13.
|
|||
1. Create a build directory and go into it:
|
||||
`mkdir build && cd build`
|
||||
1. Run CMake. To enable Lua plugins in your build add `-DCHATTERINO_PLUGINS=ON` to this command.
|
||||
`cmake -DCMAKE_PREFIX_PATH=/opt/homebrew/opt/qt@5 -DOPENSSL_ROOT_DIR=/opt/homebrew/opt/openssl@1.1 ..`
|
||||
`cmake -DCMAKE_PREFIX_PATH=/opt/homebrew/opt/qt@6 ..`
|
||||
1. Build:
|
||||
`make`
|
||||
|
||||
|
|
|
@ -73,6 +73,7 @@
|
|||
- Bugfix: Fixed incorrect messages getting replaced visually. (#5683)
|
||||
- Bugfix: Fixed rendering of multi-line selection that starts at a trailing space. (#5691)
|
||||
- Bugfix: Fixed pause indicator not appearing in certain cases. (#5707)
|
||||
- Dev: Default build with Qt6 on all platforms. (#5716)
|
||||
- Dev: Update Windows build from Qt 6.5.0 to Qt 6.7.1. (#5420)
|
||||
- Dev: Update vcpkg build Qt from 6.5.0 to 6.7.0, boost from 1.83.0 to 1.85.0, openssl from 3.1.3 to 3.3.0. (#5422)
|
||||
- Dev: Unsingletonize `ISoundController`. (#5462)
|
||||
|
@ -130,6 +131,7 @@
|
|||
- Dev: Unified parsing of historic and live IRC messages. (#5678)
|
||||
- Dev: 7TV's `entitlement.reset` is now explicitly ignored. (#5685)
|
||||
- Dev: Qt 6.8 and later now default to the GDI fontengine. (#5710)
|
||||
- Dev: Moved to condition variables when shutting down worker threads. (#5721)
|
||||
|
||||
## 2.5.1
|
||||
|
||||
|
|
|
@ -21,11 +21,7 @@ option(BUILD_WITH_QTKEYCHAIN "Build Chatterino with support for your system key
|
|||
option(USE_SYSTEM_MINIAUDIO "Build Chatterino with your system miniaudio" OFF)
|
||||
option(BUILD_WITH_CRASHPAD "Build chatterino with crashpad" OFF)
|
||||
option(USE_PRECOMPILED_HEADERS "Use precompiled headers" ON)
|
||||
if(WIN32)
|
||||
option(BUILD_WITH_QT6 "Build with Qt6, default on for Windows" On)
|
||||
else()
|
||||
option(BUILD_WITH_QT6 "Use Qt6 instead of default Qt5" OFF)
|
||||
endif()
|
||||
option(BUILD_WITH_QT6 "Build with Qt6" On)
|
||||
option(CHATTERINO_GENERATE_COVERAGE "Generate coverage files" OFF)
|
||||
# We don't use translations, and we don't want qtkeychain to build translations
|
||||
option(BUILD_TRANSLATIONS "" OFF)
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 4a0a1e599377cdcdc91b0fbbefc312936b48730c
|
||||
Subproject commit 46eb2509eb37a1c8b593d37bc51359ec2f31a541
|
|
@ -514,6 +514,8 @@ set(SOURCE_FILES
|
|||
util/LayoutHelper.hpp
|
||||
util/LoadPixmap.cpp
|
||||
util/LoadPixmap.hpp
|
||||
util/OnceFlag.cpp
|
||||
util/OnceFlag.hpp
|
||||
util/RapidjsonHelpers.cpp
|
||||
util/RapidjsonHelpers.hpp
|
||||
util/RatelimitBucket.cpp
|
||||
|
|
|
@ -8,10 +8,12 @@
|
|||
#include "providers/twitch/PubSubHelpers.hpp"
|
||||
#include "util/DebugCount.hpp"
|
||||
#include "util/ExponentialBackoff.hpp"
|
||||
#include "util/OnceFlag.hpp"
|
||||
#include "util/RenameThread.hpp"
|
||||
|
||||
#include <pajlada/signals/signal.hpp>
|
||||
#include <QJsonObject>
|
||||
#include <QScopeGuard>
|
||||
#include <QString>
|
||||
#include <QStringBuilder>
|
||||
#include <websocketpp/client.hpp>
|
||||
|
@ -120,6 +122,11 @@ public:
|
|||
this->work_ = std::make_shared<boost::asio::io_service::work>(
|
||||
this->websocketClient_.get_io_service());
|
||||
this->mainThread_.reset(new std::thread([this] {
|
||||
// make sure we set in any case, even exceptions
|
||||
auto guard = qScopeGuard([&] {
|
||||
this->stoppedFlag_.set();
|
||||
});
|
||||
|
||||
runThread();
|
||||
}));
|
||||
|
||||
|
@ -142,22 +149,34 @@ public:
|
|||
|
||||
this->work_.reset();
|
||||
|
||||
if (this->mainThread_->joinable())
|
||||
if (!this->mainThread_->joinable())
|
||||
{
|
||||
// NOTE: We spawn a new thread to join the websocket thread.
|
||||
// There is a case where a new client was initiated but not added to the clients list.
|
||||
// We just don't join the thread & let the operating system nuke the thread if joining fails
|
||||
// within 1s.
|
||||
auto joiner = std::async(std::launch::async, &std::thread::join,
|
||||
this->mainThread_.get());
|
||||
if (joiner.wait_for(std::chrono::seconds(1)) ==
|
||||
std::future_status::timeout)
|
||||
{
|
||||
qCWarning(chatterinoLiveupdates)
|
||||
<< "Thread didn't join within 1 second, rip it out";
|
||||
this->websocketClient_.stop();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// NOTE:
|
||||
// There is a case where a new client was initiated but not added to the clients list.
|
||||
// We just don't join the thread & let the operating system nuke the thread if joining fails
|
||||
// within 1s.
|
||||
if (this->stoppedFlag_.waitFor(std::chrono::seconds{1}))
|
||||
{
|
||||
this->mainThread_->join();
|
||||
return;
|
||||
}
|
||||
|
||||
qCWarning(chatterinoLiveupdates)
|
||||
<< "Thread didn't finish within 1 second, force-stop the client";
|
||||
this->websocketClient_.stop();
|
||||
if (this->stoppedFlag_.waitFor(std::chrono::milliseconds{100}))
|
||||
{
|
||||
this->mainThread_->join();
|
||||
return;
|
||||
}
|
||||
|
||||
qCWarning(chatterinoLiveupdates)
|
||||
<< "Thread didn't finish after stopping, discard it";
|
||||
// detach the thread so the destructor doesn't attempt any joining
|
||||
this->mainThread_->detach();
|
||||
}
|
||||
|
||||
protected:
|
||||
|
@ -394,6 +413,7 @@ private:
|
|||
|
||||
liveupdates::WebsocketClient websocketClient_;
|
||||
std::unique_ptr<std::thread> mainThread_;
|
||||
OnceFlag stoppedFlag_;
|
||||
|
||||
const QString host_;
|
||||
|
||||
|
|
|
@ -15,10 +15,10 @@
|
|||
#include "util/RenameThread.hpp"
|
||||
|
||||
#include <QJsonArray>
|
||||
#include <QScopeGuard>
|
||||
|
||||
#include <algorithm>
|
||||
#include <exception>
|
||||
#include <future>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
|
@ -560,6 +560,11 @@ void PubSub::start()
|
|||
this->work = std::make_shared<boost::asio::io_service::work>(
|
||||
this->websocketClient.get_io_service());
|
||||
this->thread = std::make_unique<std::thread>([this] {
|
||||
// make sure we set in any case, even exceptions
|
||||
auto guard = qScopeGuard([&] {
|
||||
this->stoppedFlag_.set();
|
||||
});
|
||||
|
||||
runThread();
|
||||
});
|
||||
renameThread(*this->thread, "PubSub");
|
||||
|
@ -578,23 +583,36 @@ void PubSub::stop()
|
|||
|
||||
this->work.reset();
|
||||
|
||||
if (this->thread->joinable())
|
||||
if (!this->thread->joinable())
|
||||
{
|
||||
// NOTE: We spawn a new thread to join the websocket thread.
|
||||
// There is a case where a new client was initiated but not added to the clients list.
|
||||
// We just don't join the thread & let the operating system nuke the thread if joining fails
|
||||
// within 1s.
|
||||
// We could fix the underlying bug, but this is easier & we realistically won't use this exact code
|
||||
// for super much longer.
|
||||
auto joiner = std::async(std::launch::async, &std::thread::join,
|
||||
this->thread.get());
|
||||
if (joiner.wait_for(1s) == std::future_status::timeout)
|
||||
{
|
||||
qCWarning(chatterinoPubSub)
|
||||
<< "Thread didn't join within 1 second, rip it out";
|
||||
this->websocketClient.stop();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// NOTE:
|
||||
// There is a case where a new client was initiated but not added to the clients list.
|
||||
// We just don't join the thread & let the operating system nuke the thread if joining fails
|
||||
// within 1s.
|
||||
// We could fix the underlying bug, but this is easier & we realistically won't use this exact code
|
||||
// for super much longer.
|
||||
if (this->stoppedFlag_.waitFor(std::chrono::seconds{1}))
|
||||
{
|
||||
this->thread->join();
|
||||
return;
|
||||
}
|
||||
|
||||
qCWarning(chatterinoLiveupdates)
|
||||
<< "Thread didn't finish within 1 second, force-stop the client";
|
||||
this->websocketClient.stop();
|
||||
if (this->stoppedFlag_.waitFor(std::chrono::milliseconds{100}))
|
||||
{
|
||||
this->thread->join();
|
||||
return;
|
||||
}
|
||||
|
||||
qCWarning(chatterinoLiveupdates)
|
||||
<< "Thread didn't finish after stopping, discard it";
|
||||
// detach the thread so the destructor doesn't attempt any joining
|
||||
this->thread->detach();
|
||||
}
|
||||
|
||||
bool PubSub::listenToWhispers()
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include "providers/twitch/PubSubClientOptions.hpp"
|
||||
#include "providers/twitch/PubSubWebsocket.hpp"
|
||||
#include "util/ExponentialBackoff.hpp"
|
||||
#include "util/OnceFlag.hpp"
|
||||
|
||||
#include <boost/asio/io_service.hpp>
|
||||
#include <boost/asio/ssl/context.hpp>
|
||||
|
@ -267,6 +268,8 @@ private:
|
|||
const QString host_;
|
||||
const PubSubClientOptions clientOptions_;
|
||||
|
||||
OnceFlag stoppedFlag_;
|
||||
|
||||
bool stopping_{false};
|
||||
|
||||
#ifdef FRIEND_TEST
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "common/Channel.hpp"
|
||||
#include "common/Common.hpp"
|
||||
#include "common/Env.hpp"
|
||||
#include "common/Literals.hpp"
|
||||
#include "common/QLogging.hpp"
|
||||
#include "controllers/accounts/AccountController.hpp"
|
||||
#include "messages/LimitedQueueSnapshot.hpp"
|
||||
|
@ -147,6 +148,8 @@ bool shouldSendHelixChat()
|
|||
|
||||
namespace chatterino {
|
||||
|
||||
using namespace literals;
|
||||
|
||||
TwitchIrcServer::TwitchIrcServer()
|
||||
: whispersChannel(new Channel("/whispers", Channel::Type::TwitchWhispers))
|
||||
, mentionsChannel(new Channel("/mentions", Channel::Type::TwitchMentions))
|
||||
|
@ -488,9 +491,49 @@ void TwitchIrcServer::initialize()
|
|||
if (msg.status == "PENDING")
|
||||
{
|
||||
AutomodAction action(msg.data, channelID);
|
||||
action.reason = QString("%1 level %2")
|
||||
.arg(msg.contentCategory)
|
||||
.arg(msg.contentLevel);
|
||||
if (msg.reason ==
|
||||
PubSubAutoModQueueMessage::Reason::BlockedTerm)
|
||||
{
|
||||
auto numBlockedTermsMatched =
|
||||
msg.blockedTermsFound.count();
|
||||
auto hideBlockedTerms =
|
||||
getSettings()
|
||||
->streamerModeHideBlockedTermText &&
|
||||
getApp()->getStreamerMode()->isEnabled();
|
||||
if (!msg.blockedTermsFound.isEmpty())
|
||||
{
|
||||
if (hideBlockedTerms)
|
||||
{
|
||||
action.reason =
|
||||
u"matches %1 blocked term%2"_s
|
||||
.arg(numBlockedTermsMatched)
|
||||
.arg(numBlockedTermsMatched > 1
|
||||
? u"s"
|
||||
: u"");
|
||||
}
|
||||
else
|
||||
{
|
||||
action.reason =
|
||||
u"matches %1 blocked term%2 \"%3\""_s
|
||||
.arg(numBlockedTermsMatched)
|
||||
.arg(numBlockedTermsMatched > 1
|
||||
? u"s"
|
||||
: u"")
|
||||
.arg(msg.blockedTermsFound.join(
|
||||
u"\", \""));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
action.reason = "blocked term usage";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
action.reason = QString("%1 level %2")
|
||||
.arg(msg.contentCategory)
|
||||
.arg(msg.contentLevel);
|
||||
}
|
||||
|
||||
action.msgID = msg.messageID;
|
||||
action.message = msg.messageText;
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
#include "util/QMagicEnum.hpp"
|
||||
|
||||
#include <QJsonArray>
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
PubSubAutoModQueueMessage::PubSubAutoModQueueMessage(const QJsonObject &root)
|
||||
|
@ -9,6 +11,7 @@ PubSubAutoModQueueMessage::PubSubAutoModQueueMessage(const QJsonObject &root)
|
|||
, data(root.value("data").toObject())
|
||||
, status(this->data.value("status").toString())
|
||||
{
|
||||
qInfo() << "XXX: automod message over pubsub:" << root;
|
||||
auto oType = qmagicenum::enumCast<Type>(this->typeString);
|
||||
if (oType.has_value())
|
||||
{
|
||||
|
@ -41,6 +44,27 @@ PubSubAutoModQueueMessage::PubSubAutoModQueueMessage(const QJsonObject &root)
|
|||
messageSender.value("display_name").toString();
|
||||
this->senderUserChatColor =
|
||||
QColor(messageSender.value("chat_color").toString());
|
||||
|
||||
if (this->reason == Reason::BlockedTerm)
|
||||
{
|
||||
// Attempt to read the blocked term(s) that caused this message to be blocked
|
||||
const auto caughtMessageReason =
|
||||
data.value("caught_message_reason").toObject();
|
||||
const auto blockedTermFailure =
|
||||
caughtMessageReason.value("blocked_term_failure").toObject();
|
||||
const auto termsFound =
|
||||
blockedTermFailure.value("terms_found").toArray();
|
||||
|
||||
for (const auto &termValue : termsFound)
|
||||
{
|
||||
const auto term = termValue.toObject();
|
||||
const auto termText = term.value("text").toString();
|
||||
if (!termText.isEmpty())
|
||||
{
|
||||
this->blockedTermsFound.push_back(termText);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace chatterino
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include <QColor>
|
||||
#include <QJsonObject>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
|
@ -40,6 +41,8 @@ struct PubSubAutoModQueueMessage {
|
|||
QString senderUserDisplayName;
|
||||
QColor senderUserChatColor;
|
||||
|
||||
QStringList blockedTermsFound;
|
||||
|
||||
PubSubAutoModQueueMessage() = default;
|
||||
explicit PubSubAutoModQueueMessage(const QJsonObject &root);
|
||||
};
|
||||
|
|
|
@ -337,6 +337,10 @@ public:
|
|||
"/streamerMode/supressLiveNotifications", false};
|
||||
BoolSetting streamerModeSuppressInlineWhispers = {
|
||||
"/streamerMode/suppressInlineWhispers", true};
|
||||
BoolSetting streamerModeHideBlockedTermText = {
|
||||
"/streamerMode/hideBlockedTermText",
|
||||
true,
|
||||
};
|
||||
|
||||
/// Ignored Phrases
|
||||
QStringSetting ignoredPhraseReplace = {"/ignore/ignoredPhraseReplace",
|
||||
|
|
33
src/util/OnceFlag.cpp
Normal file
33
src/util/OnceFlag.cpp
Normal file
|
@ -0,0 +1,33 @@
|
|||
#include "util/OnceFlag.hpp"
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
OnceFlag::OnceFlag() = default;
|
||||
OnceFlag::~OnceFlag() = default;
|
||||
|
||||
void OnceFlag::set()
|
||||
{
|
||||
{
|
||||
std::unique_lock guard(this->mutex);
|
||||
this->flag = true;
|
||||
}
|
||||
this->condvar.notify_all();
|
||||
}
|
||||
|
||||
bool OnceFlag::waitFor(std::chrono::milliseconds ms)
|
||||
{
|
||||
std::unique_lock lock(this->mutex);
|
||||
return this->condvar.wait_for(lock, ms, [this] {
|
||||
return this->flag;
|
||||
});
|
||||
}
|
||||
|
||||
void OnceFlag::wait()
|
||||
{
|
||||
std::unique_lock lock(this->mutex);
|
||||
this->condvar.wait(lock, [this] {
|
||||
return this->flag;
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace chatterino
|
41
src/util/OnceFlag.hpp
Normal file
41
src/util/OnceFlag.hpp
Normal file
|
@ -0,0 +1,41 @@
|
|||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
/// @brief A flag that can only be set once which notifies waiters.
|
||||
///
|
||||
/// This can be used to synchronize with other threads. Note that waiting
|
||||
/// threads will be suspended.
|
||||
class OnceFlag
|
||||
{
|
||||
public:
|
||||
OnceFlag();
|
||||
~OnceFlag();
|
||||
|
||||
/// Set this flag and notify waiters
|
||||
void set();
|
||||
|
||||
/// @brief Wait for at most `ms` until this flag is set.
|
||||
///
|
||||
/// The calling thread will be suspended during the wait.
|
||||
///
|
||||
/// @param ms The maximum time to wait for this flag
|
||||
/// @returns `true` if this flag was set during the wait or before
|
||||
bool waitFor(std::chrono::milliseconds ms);
|
||||
|
||||
/// @brief Wait until this flag is set by another thread
|
||||
///
|
||||
/// The calling thread will be suspended during the wait.
|
||||
void wait();
|
||||
|
||||
private:
|
||||
std::mutex mutex;
|
||||
std::condition_variable condvar;
|
||||
bool flag = false;
|
||||
};
|
||||
|
||||
} // namespace chatterino
|
|
@ -667,6 +667,11 @@ void GeneralPage::initLayout(GeneralPageView &layout)
|
|||
layout.addCheckbox(
|
||||
"Hide moderation actions", s.streamerModeHideModActions, false,
|
||||
"Hide bans, timeouts, and automod messages from appearing in chat.");
|
||||
layout.addCheckbox(
|
||||
"Hide blocked terms", s.streamerModeHideBlockedTermText, false,
|
||||
"Hide blocked terms from showing up in places like AutoMod messages. "
|
||||
"This can be useful in case you have some blocked terms that you don't "
|
||||
"want to show on stream.");
|
||||
layout.addCheckbox("Mute mention sounds", s.streamerModeMuteMentions, false,
|
||||
"Mute your ping sound from playing.");
|
||||
layout.addCheckbox(
|
||||
|
|
|
@ -51,6 +51,7 @@ set(test_SOURCES
|
|||
${CMAKE_CURRENT_LIST_DIR}/src/Plugins.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/TwitchIrc.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/IgnoreController.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/OnceFlag.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/lib/Snapshot.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/lib/Snapshot.hpp
|
||||
# Add your new file above this line!
|
||||
|
|
88
tests/src/OnceFlag.cpp
Normal file
88
tests/src/OnceFlag.cpp
Normal file
|
@ -0,0 +1,88 @@
|
|||
#include "util/OnceFlag.hpp"
|
||||
|
||||
#include "Test.hpp"
|
||||
|
||||
#include <thread>
|
||||
|
||||
using namespace chatterino;
|
||||
|
||||
// this test shouldn't time out (no assert necessary)
|
||||
TEST(OnceFlag, basic)
|
||||
{
|
||||
OnceFlag startedFlag;
|
||||
OnceFlag startedAckFlag;
|
||||
OnceFlag stoppedFlag;
|
||||
|
||||
std::thread t([&] {
|
||||
startedFlag.set();
|
||||
startedAckFlag.wait();
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds{50});
|
||||
stoppedFlag.set();
|
||||
});
|
||||
|
||||
startedFlag.wait();
|
||||
startedAckFlag.set();
|
||||
stoppedFlag.wait();
|
||||
|
||||
t.join();
|
||||
}
|
||||
|
||||
TEST(OnceFlag, waitFor)
|
||||
{
|
||||
OnceFlag startedFlag;
|
||||
OnceFlag startedAckFlag;
|
||||
OnceFlag stoppedFlag;
|
||||
|
||||
std::thread t([&] {
|
||||
startedFlag.set();
|
||||
startedAckFlag.wait();
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds{100});
|
||||
stoppedFlag.set();
|
||||
});
|
||||
|
||||
startedFlag.wait();
|
||||
startedAckFlag.set();
|
||||
|
||||
auto start = std::chrono::system_clock::now();
|
||||
ASSERT_TRUE(stoppedFlag.waitFor(std::chrono::milliseconds{200}));
|
||||
auto stop = std::chrono::system_clock::now();
|
||||
|
||||
ASSERT_LT(stop - start, std::chrono::milliseconds{150});
|
||||
|
||||
start = std::chrono::system_clock::now();
|
||||
ASSERT_TRUE(stoppedFlag.waitFor(std::chrono::milliseconds{1000}));
|
||||
stop = std::chrono::system_clock::now();
|
||||
|
||||
ASSERT_LT(stop - start, std::chrono::milliseconds{10});
|
||||
|
||||
start = std::chrono::system_clock::now();
|
||||
stoppedFlag.wait();
|
||||
stop = std::chrono::system_clock::now();
|
||||
|
||||
ASSERT_LT(stop - start, std::chrono::milliseconds{10});
|
||||
|
||||
t.join();
|
||||
}
|
||||
|
||||
TEST(OnceFlag, waitForTimeout)
|
||||
{
|
||||
OnceFlag startedFlag;
|
||||
OnceFlag startedAckFlag;
|
||||
OnceFlag stoppedFlag;
|
||||
|
||||
std::thread t([&] {
|
||||
startedFlag.set();
|
||||
startedAckFlag.wait();
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds{100});
|
||||
stoppedFlag.set();
|
||||
});
|
||||
|
||||
startedFlag.wait();
|
||||
startedAckFlag.set();
|
||||
|
||||
ASSERT_FALSE(stoppedFlag.waitFor(std::chrono::milliseconds{25}));
|
||||
stoppedFlag.wait();
|
||||
|
||||
t.join();
|
||||
}
|
Loading…
Reference in a new issue