mirror of
https://github.com/Chatterino/chatterino2.git
synced 2024-11-21 22:24:07 +01:00
Merge remote-tracking branch 'origin/master' into feature/lua_scripting
This commit is contained in:
commit
3ece8c8ed1
87 changed files with 1828 additions and 618 deletions
|
@ -57,3 +57,6 @@ exec "$here/usr/bin/chatterino" "$@"' > appdir/AppRun
|
||||||
chmod a+x appdir/AppRun
|
chmod a+x appdir/AppRun
|
||||||
|
|
||||||
./appimagetool-x86_64.AppImage appdir
|
./appimagetool-x86_64.AppImage appdir
|
||||||
|
|
||||||
|
# TODO: Create appimage in a unique directory instead maybe idk?
|
||||||
|
rm -rf appdir
|
||||||
|
|
|
@ -1,6 +1,37 @@
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
|
breakline() {
|
||||||
|
printf "================================================================================\n\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Configured in the CI step
|
||||||
|
install_prefix="appdir/usr"
|
||||||
|
|
||||||
|
# The directory we finally pack into our .deb package
|
||||||
|
packaging_dir="package"
|
||||||
|
|
||||||
|
# Get the Ubuntu Release (e.g. 20.04 or 22.04)
|
||||||
|
ubuntu_release="$(lsb_release -rs)"
|
||||||
|
|
||||||
|
# Refactor opportunity:
|
||||||
|
case "$ubuntu_release" in
|
||||||
|
20.04)
|
||||||
|
dependencies="libc6, libstdc++6, libqt5core5a, libqt5concurrent5, libqt5dbus5, libqt5gui5, libqt5network5, libqt5svg5, libqt5widgets5, qt5-image-formats-plugins, libboost-filesystem1.71.0"
|
||||||
|
;;
|
||||||
|
22.04)
|
||||||
|
dependencies="libc6, libstdc++6, libqt5core5a, libqt5concurrent5, libqt5dbus5, libqt5gui5, libqt5network5, libqt5svg5, libqt5widgets5, qt5-image-formats-plugins, libboost-filesystem1.74.0"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Unsupported Ubuntu release $ubuntu_release"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
echo "Building Ubuntu .deb file on '$ubuntu_release'"
|
||||||
|
echo "Dependencies: $dependencies"
|
||||||
|
|
||||||
if [ ! -f ./bin/chatterino ] || [ ! -x ./bin/chatterino ]; then
|
if [ ! -f ./bin/chatterino ] || [ ! -x ./bin/chatterino ]; then
|
||||||
echo "ERROR: No chatterino binary file found. This script must be run in the build folder, and chatterino must be built first."
|
echo "ERROR: No chatterino binary file found. This script must be run in the build folder, and chatterino must be built first."
|
||||||
exit 1
|
exit 1
|
||||||
|
@ -8,33 +39,53 @@ fi
|
||||||
|
|
||||||
chatterino_version=$(git describe 2>/dev/null | cut -c 2-) || true
|
chatterino_version=$(git describe 2>/dev/null | cut -c 2-) || true
|
||||||
if [ -z "$chatterino_version" ]; then
|
if [ -z "$chatterino_version" ]; then
|
||||||
|
# Fall back to this in case the build happened outside of a git repo
|
||||||
chatterino_version="0.0.0-dev"
|
chatterino_version="0.0.0-dev"
|
||||||
echo "Falling back to setting the version to '$chatterino_version'"
|
|
||||||
else
|
|
||||||
echo "Found Chatterino version $chatterino_version via git"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
rm -vrf "./package" || true # delete any old packaging dir
|
# Make sure no old remnants of a previous packaging remains
|
||||||
|
rm -vrf "$packaging_dir"
|
||||||
|
|
||||||
# create ./package/ from scratch
|
mkdir -p "$packaging_dir/DEBIAN"
|
||||||
mkdir package/DEBIAN -p
|
|
||||||
packaging_dir="$(realpath ./package)"
|
|
||||||
|
|
||||||
echo "Making control file"
|
echo "Making control file"
|
||||||
cat >> "$packaging_dir/DEBIAN/control" << EOF
|
cat >> "$packaging_dir/DEBIAN/control" << EOF
|
||||||
Package: chatterino
|
Package: chatterino
|
||||||
Section: net
|
Version: $chatterino_version
|
||||||
Priority: optional
|
|
||||||
Architecture: amd64
|
Architecture: amd64
|
||||||
Maintainer: Mm2PL <mm2pl@kotmisia.pl>
|
Maintainer: Mm2PL <mm2pl@kotmisia.pl>
|
||||||
Description: Testing out chatterino as a Ubuntu package
|
Depends: $dependencies
|
||||||
Depends: libc6, libqt5concurrent5, libqt5core5a, libqt5dbus5, libqt5gui5, libqt5multimedia5, libqt5network5, libqt5svg5, libqt5widgets5, libssl1.1, libstdc++6
|
Section: net
|
||||||
|
Priority: optional
|
||||||
|
Homepage: https://github.com/Chatterino/chatterino2
|
||||||
|
Description: Ubuntu package built for $ubuntu_release
|
||||||
EOF
|
EOF
|
||||||
echo "Version: $chatterino_version" >> "$packaging_dir/DEBIAN/control"
|
cat "$packaging_dir/DEBIAN/control"
|
||||||
|
breakline
|
||||||
|
|
||||||
echo "Running make install in package dir"
|
|
||||||
DESTDIR="$packaging_dir" make INSTALL_ROOT="$packaging_dir" -j"$(nproc)" install; find "$packaging_dir/"
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
echo "Building package..."
|
echo "Running make install"
|
||||||
|
make install
|
||||||
|
find "$install_prefix"
|
||||||
|
breakline
|
||||||
|
|
||||||
|
|
||||||
|
echo "Merge install into packaging dir"
|
||||||
|
cp -rv "$install_prefix/" "$packaging_dir/"
|
||||||
|
find "$packaging_dir"
|
||||||
|
breakline
|
||||||
|
|
||||||
|
|
||||||
|
echo "Building package"
|
||||||
dpkg-deb --build "$packaging_dir" "Chatterino-x86_64.deb"
|
dpkg-deb --build "$packaging_dir" "Chatterino-x86_64.deb"
|
||||||
|
breakline
|
||||||
|
|
||||||
|
|
||||||
|
echo "Package info"
|
||||||
|
dpkg --info Chatterino-x86_64.deb
|
||||||
|
breakline
|
||||||
|
|
||||||
|
|
||||||
|
echo "Package contents"
|
||||||
|
dpkg --contents Chatterino-x86_64.deb # Shows folders and files inside .deb file
|
||||||
|
breakline
|
||||||
|
|
54
.docker/Dockerfile-ubuntu-20.04-build
Normal file
54
.docker/Dockerfile-ubuntu-20.04-build
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
FROM ubuntu:20.04
|
||||||
|
|
||||||
|
ENV TZ=UTC
|
||||||
|
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
|
||||||
|
|
||||||
|
RUN apt-get update && apt-get -y install --no-install-recommends \
|
||||||
|
cmake \
|
||||||
|
virtualenv \
|
||||||
|
rapidjson-dev \
|
||||||
|
libfuse2 \
|
||||||
|
libssl-dev \
|
||||||
|
libboost-dev \
|
||||||
|
libxcb-randr0-dev \
|
||||||
|
libboost-system-dev \
|
||||||
|
libboost-filesystem-dev \
|
||||||
|
libpulse-dev \
|
||||||
|
libxkbcommon-x11-0 \
|
||||||
|
build-essential \
|
||||||
|
libgl1-mesa-dev \
|
||||||
|
libxcb-icccm4 \
|
||||||
|
libxcb-image0 \
|
||||||
|
libxcb-keysyms1 \
|
||||||
|
libxcb-render-util0 \
|
||||||
|
libxcb-xinerama0
|
||||||
|
|
||||||
|
RUN apt-get -y install \
|
||||||
|
git \
|
||||||
|
lsb-release \
|
||||||
|
python3-pip && \
|
||||||
|
apt-get clean all
|
||||||
|
|
||||||
|
# Install Qt as we do in CI
|
||||||
|
|
||||||
|
RUN pip3 install -U pip && \
|
||||||
|
pip3 install aqtinstall && \
|
||||||
|
aqt install-qt linux desktop 5.12.12 && \
|
||||||
|
mkdir -p /opt/qt512 && \
|
||||||
|
mv /5.12.12/gcc_64/* /opt/qt512
|
||||||
|
|
||||||
|
ADD . /src
|
||||||
|
|
||||||
|
RUN mkdir /src/build
|
||||||
|
|
||||||
|
# cmake
|
||||||
|
RUN cd /src/build && \
|
||||||
|
CXXFLAGS=-fno-sized-deallocation cmake \
|
||||||
|
-DCMAKE_INSTALL_PREFIX=appdir/usr/ \
|
||||||
|
-DCMAKE_PREFIX_PATH=/opt/qt512/lib/cmake \
|
||||||
|
-DBUILD_WITH_QTKEYCHAIN=OFF \
|
||||||
|
..
|
||||||
|
|
||||||
|
# build
|
||||||
|
RUN cd /src/build && \
|
||||||
|
make -j8
|
13
.docker/Dockerfile-ubuntu-20.04-package
Normal file
13
.docker/Dockerfile-ubuntu-20.04-package
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
FROM chatterino-ubuntu-20.04-build
|
||||||
|
|
||||||
|
ADD .CI /src/.CI
|
||||||
|
|
||||||
|
WORKDIR /src/build
|
||||||
|
|
||||||
|
# RUN apt-get install -y wget
|
||||||
|
|
||||||
|
# create appimage
|
||||||
|
# RUN pwd && ./../.CI/CreateAppImage.sh
|
||||||
|
|
||||||
|
# package deb
|
||||||
|
RUN pwd && ./../.CI/CreateUbuntuDeb.sh
|
57
.docker/Dockerfile-ubuntu-22.04-build
Normal file
57
.docker/Dockerfile-ubuntu-22.04-build
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
FROM ubuntu:22.04
|
||||||
|
|
||||||
|
ENV TZ=UTC
|
||||||
|
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
|
||||||
|
|
||||||
|
RUN apt-get update && apt-get -y install --no-install-recommends \
|
||||||
|
cmake \
|
||||||
|
virtualenv \
|
||||||
|
rapidjson-dev \
|
||||||
|
libfuse2 \
|
||||||
|
libssl-dev \
|
||||||
|
libboost-dev \
|
||||||
|
libxcb-randr0-dev \
|
||||||
|
libboost-system-dev \
|
||||||
|
libboost-filesystem-dev \
|
||||||
|
libpulse-dev \
|
||||||
|
libxkbcommon-x11-0 \
|
||||||
|
build-essential \
|
||||||
|
libgl1-mesa-dev \
|
||||||
|
libxcb-icccm4 \
|
||||||
|
libxcb-image0 \
|
||||||
|
libxcb-keysyms1 \
|
||||||
|
libxcb-render-util0 \
|
||||||
|
libxcb-xinerama0
|
||||||
|
|
||||||
|
RUN apt-get -y install \
|
||||||
|
git \
|
||||||
|
lsb-release \
|
||||||
|
python3-pip && \
|
||||||
|
apt-get clean all
|
||||||
|
|
||||||
|
# Install Qt as we do in CI
|
||||||
|
|
||||||
|
RUN pip3 install -U pip && \
|
||||||
|
pip3 install aqtinstall && \
|
||||||
|
aqt install-qt linux desktop 5.15.2 && \
|
||||||
|
mkdir -p /opt/qt515 && \
|
||||||
|
mv /5.15.2/gcc_64/* /opt/qt515
|
||||||
|
|
||||||
|
ADD . /src
|
||||||
|
|
||||||
|
RUN mkdir /src/build
|
||||||
|
|
||||||
|
# Apply Qt patches
|
||||||
|
RUN patch "/opt/qt515/include/QtConcurrent/qtconcurrentthreadengine.h" /src/.patches/qt5-on-newer-gcc.patch
|
||||||
|
|
||||||
|
# cmake
|
||||||
|
RUN cd /src/build && \
|
||||||
|
CXXFLAGS=-fno-sized-deallocation cmake \
|
||||||
|
-DCMAKE_INSTALL_PREFIX=appdir/usr/ \
|
||||||
|
-DCMAKE_PREFIX_PATH=/opt/qt515/lib/cmake \
|
||||||
|
-DBUILD_WITH_QTKEYCHAIN=OFF \
|
||||||
|
..
|
||||||
|
|
||||||
|
# build
|
||||||
|
RUN cd /src/build && \
|
||||||
|
make -j8
|
8
.docker/Dockerfile-ubuntu-22.04-package
Normal file
8
.docker/Dockerfile-ubuntu-22.04-package
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
FROM chatterino-ubuntu-22.04-build
|
||||||
|
|
||||||
|
ADD .CI /src/.CI
|
||||||
|
|
||||||
|
WORKDIR /src/build
|
||||||
|
|
||||||
|
# package deb
|
||||||
|
RUN ./../.CI/CreateUbuntuDeb.sh
|
29
.docker/README.md
Normal file
29
.docker/README.md
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
## Groups
|
||||||
|
|
||||||
|
### Ubuntu 20.04 package
|
||||||
|
|
||||||
|
`Dockerfile-ubuntu-20.04-package` relies on `Dockerfile-ubuntu-20.04-build`
|
||||||
|
|
||||||
|
To build, from the repo root
|
||||||
|
|
||||||
|
1. Build a docker image that contains all the build artifacts and source from building Chatterino on Ubuntu 20.04
|
||||||
|
`docker build -t chatterino-ubuntu-20.04-build -f .docker/Dockerfile-ubuntu-20.04-build .`
|
||||||
|
1. Build a docker image that uses the above-built image & packages it into a .deb file
|
||||||
|
`docker build -t chatterino-ubuntu-20.04-package -f .docker/Dockerfile-ubuntu-20.04-package .`
|
||||||
|
|
||||||
|
To extract the final package, you can run the following command:
|
||||||
|
`docker run -v $PWD:/opt/mount --rm -it chatterino-ubuntu-20.04-package bash -c "cp /src/build/Chatterino-x86_64.deb /opt/mount/"`
|
||||||
|
|
||||||
|
### Ubuntu 22.04 package
|
||||||
|
|
||||||
|
`Dockerfile-ubuntu-22.04-package` relies on `Dockerfile-ubuntu-22.04-build`
|
||||||
|
|
||||||
|
To build, from the repo root
|
||||||
|
|
||||||
|
1. Build a docker image that contains all the build artifacts and source from building Chatterino on Ubuntu 22.04
|
||||||
|
`docker build -t chatterino-ubuntu-22.04-build -f .docker/Dockerfile-ubuntu-22.04-build .`
|
||||||
|
1. Build a docker image that uses the above-built image & packages it into a .deb file
|
||||||
|
`docker build -t chatterino-ubuntu-22.04-package -f .docker/Dockerfile-ubuntu-22.04-package .`
|
||||||
|
|
||||||
|
To extract the final package, you can run the following command:
|
||||||
|
`docker run -v $PWD:/opt/mount --rm -it chatterino-ubuntu-22.04-package bash -c "cp /src/build/Chatterino-x86_64.deb /opt/mount/"`
|
4
.dockerignore
Normal file
4
.dockerignore
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
build*
|
||||||
|
.mypy_cache
|
||||||
|
.cache
|
||||||
|
.docker
|
125
.github/workflows/build.yml
vendored
125
.github/workflows/build.yml
vendored
|
@ -17,19 +17,41 @@ env:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
|
name: "Build ${{ matrix.os }}, Qt ${{ matrix.qt-version }} (PCH:${{ matrix.pch }}, LTO:${{ matrix.force-lto }})"
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: [windows-latest, ubuntu-20.04, macos-latest]
|
os: [windows-latest, macos-latest]
|
||||||
qt-version: [5.15.2, 5.12.12]
|
qt-version: [5.15.2, 5.12.12]
|
||||||
pch: [true]
|
pch: [true]
|
||||||
force-lto: [false]
|
force-lto: [false]
|
||||||
plugins: [false]
|
plugins: [false]
|
||||||
|
skip_artifact: ["no"]
|
||||||
|
crashpad: [true]
|
||||||
include:
|
include:
|
||||||
|
# Ubuntu 20.04, Qt 5.12
|
||||||
- os: ubuntu-20.04
|
- os: ubuntu-20.04
|
||||||
|
qt-version: 5.12.12
|
||||||
|
pch: true
|
||||||
|
force-lto: false
|
||||||
|
# Ubuntu 22.04, Qt 5.15
|
||||||
|
- os: ubuntu-22.04
|
||||||
|
qt-version: 5.15.2
|
||||||
|
pch: true
|
||||||
|
force-lto: false
|
||||||
|
# Test for disabling Precompiled Headers & enabling link-time optimization
|
||||||
|
- os: ubuntu-22.04
|
||||||
qt-version: 5.15.2
|
qt-version: 5.15.2
|
||||||
pch: false
|
pch: false
|
||||||
force-lto: true
|
force-lto: true
|
||||||
|
skip_artifact: "yes"
|
||||||
|
# Test for disabling crashpad on Windows
|
||||||
|
- os: windows-latest
|
||||||
|
qt-version: 5.15.2
|
||||||
|
pch: false
|
||||||
|
force-lto: true
|
||||||
|
skip_artifact: "yes"
|
||||||
|
crashpad: false
|
||||||
- os: ubuntu-20.04
|
- os: ubuntu-20.04
|
||||||
qt-version: 5.15.2
|
qt-version: 5.15.2
|
||||||
pch: true
|
pch: true
|
||||||
|
@ -45,7 +67,6 @@ jobs:
|
||||||
pch: true
|
pch: true
|
||||||
force-lto: false
|
force-lto: false
|
||||||
plugins: true
|
plugins: true
|
||||||
|
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
@ -59,6 +80,18 @@ jobs:
|
||||||
if: matrix.plugins == true
|
if: matrix.plugins == true
|
||||||
run: |
|
run: |
|
||||||
echo "C2_PLUGINS=ON" >> "$GITHUB_ENV"
|
echo "C2_PLUGINS=ON" >> "$GITHUB_ENV"
|
||||||
|
echo "artifact_descr=plugins" >> "$GITHUB_ENV"
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
- name: Disable plugin support
|
||||||
|
if: matrix.plugins == false
|
||||||
|
echo "artifact_descr=no-plugins" >> "$GITHUB_ENV"
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
- name: Set Crashpad
|
||||||
|
if: matrix.crashpad == true
|
||||||
|
run: |
|
||||||
|
echo "C2_ENABLE_CRASHPAD=ON" >> "$GITHUB_ENV"
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
- name: Set environment variables for windows-latest
|
- name: Set environment variables for windows-latest
|
||||||
|
@ -69,7 +102,7 @@ jobs:
|
||||||
|
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: recursive
|
||||||
fetch-depth: 0 # allows for tags access
|
fetch-depth: 0 # allows for tags access
|
||||||
|
|
||||||
- name: Install Qt
|
- name: Install Qt
|
||||||
|
@ -85,14 +118,14 @@ jobs:
|
||||||
if: startsWith(matrix.os, 'windows')
|
if: startsWith(matrix.os, 'windows')
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v3
|
||||||
with:
|
with:
|
||||||
key: ${{ runner.os }}-conan-user-${{ hashFiles('**/conanfile.txt') }}
|
key: ${{ runner.os }}-${{ matrix.crashpad }}-conan-user-${{ hashFiles('**/conanfile.txt') }}
|
||||||
path: ~/.conan/
|
path: ~/.conan/
|
||||||
|
|
||||||
- name: Cache conan packages part 2
|
- name: Cache conan packages part 2
|
||||||
if: startsWith(matrix.os, 'windows')
|
if: startsWith(matrix.os, 'windows')
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v3
|
||||||
with:
|
with:
|
||||||
key: ${{ runner.os }}-conan-root-${{ hashFiles('**/conanfile.txt') }}
|
key: ${{ runner.os }}-${{ matrix.crashpad }}-conan-root-${{ hashFiles('**/conanfile.txt') }}
|
||||||
path: C:/.conan/
|
path: C:/.conan/
|
||||||
|
|
||||||
- name: Add Conan to path
|
- name: Add Conan to path
|
||||||
|
@ -106,7 +139,7 @@ jobs:
|
||||||
|
|
||||||
- name: Enable Developer Command Prompt
|
- name: Enable Developer Command Prompt
|
||||||
if: startsWith(matrix.os, 'windows')
|
if: startsWith(matrix.os, 'windows')
|
||||||
uses: ilammy/msvc-dev-cmd@v1.12.0
|
uses: ilammy/msvc-dev-cmd@v1.12.1
|
||||||
|
|
||||||
- name: Setup Conan (Windows)
|
- name: Setup Conan (Windows)
|
||||||
if: startsWith(matrix.os, 'windows')
|
if: startsWith(matrix.os, 'windows')
|
||||||
|
@ -119,34 +152,50 @@ jobs:
|
||||||
run: |
|
run: |
|
||||||
mkdir build
|
mkdir build
|
||||||
cd build
|
cd build
|
||||||
conan install .. -s build_type=Release -b missing -pr:b=default
|
conan install .. -s build_type=RelWithDebInfo -b missing -pr:b=default
|
||||||
cmake `
|
cmake `
|
||||||
-G"NMake Makefiles" `
|
-G"NMake Makefiles" `
|
||||||
-DCMAKE_BUILD_TYPE=Release `
|
-DCMAKE_BUILD_TYPE=RelWithDebInfo `
|
||||||
-DCMAKE_TOOLCHAIN_FILE="conan_toolchain.cmake" `
|
-DCMAKE_TOOLCHAIN_FILE="conan_toolchain.cmake" `
|
||||||
|
-DBUILD_WITH_CRASHPAD="$Env:C2_ENABLE_CRASHPAD" `
|
||||||
-DCHATTERINO_LTO="$Env:C2_ENABLE_LTO" `
|
-DCHATTERINO_LTO="$Env:C2_ENABLE_LTO" `
|
||||||
-DCHATTERINO_PLUGINS="$Env:C2_PLUGINS" `
|
-DCHATTERINO_PLUGINS="$Env:C2_PLUGINS" `
|
||||||
..
|
..
|
||||||
set cl=/MP
|
set cl=/MP
|
||||||
nmake /S /NOLOGO
|
nmake /S /NOLOGO
|
||||||
|
|
||||||
|
- name: Build crashpad (Windows)
|
||||||
|
if: startsWith(matrix.os, 'windows') && matrix.crashpad
|
||||||
|
run: |
|
||||||
|
cd build
|
||||||
|
set cl=/MP
|
||||||
|
nmake /S /NOLOGO crashpad_handler
|
||||||
|
mkdir Chatterino2/crashpad
|
||||||
|
cp bin/crashpad/crashpad_handler.exe Chatterino2/crashpad/crashpad_handler.exe
|
||||||
|
7z a bin/chatterino.pdb.7z bin/chatterino.pdb
|
||||||
|
|
||||||
|
- name: Package (windows)
|
||||||
|
if: startsWith(matrix.os, 'windows')
|
||||||
|
run: |
|
||||||
|
cd build
|
||||||
windeployqt bin/chatterino.exe --release --no-compiler-runtime --no-translations --no-opengl-sw --dir Chatterino2/
|
windeployqt bin/chatterino.exe --release --no-compiler-runtime --no-translations --no-opengl-sw --dir Chatterino2/
|
||||||
cp bin/chatterino.exe Chatterino2/
|
cp bin/chatterino.exe Chatterino2/
|
||||||
echo nightly > Chatterino2/modes
|
echo nightly > Chatterino2/modes
|
||||||
7z a chatterino-windows-x86-64.zip Chatterino2/
|
7z a chatterino-windows-x86-64.zip Chatterino2/
|
||||||
|
|
||||||
- name: Upload artifact (Windows)
|
- name: Upload artifact (Windows - binary)
|
||||||
if: startsWith(matrix.os, 'windows') && matrix.plugins == false
|
if: startsWith(matrix.os, 'windows') && matrix.skip_artifact != 'yes'
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: chatterino-windows-x86-64-${{ matrix.qt-version }}.zip
|
name: chatterino-windows-x86-64-${{ matrix.qt-version }}-${{ artifact_descr }}.zip
|
||||||
path: build/chatterino-windows-x86-64.zip
|
path: build/chatterino-windows-x86-64.zip
|
||||||
|
|
||||||
- name: Upload artifact (Windows, plugins)
|
- name: Upload artifact (Windows - symbols)
|
||||||
if: startsWith(matrix.os, 'windows') && matrix.plugins == true
|
if: startsWith(matrix.os, 'windows') && matrix.skip_artifact != 'yes'
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: chatterino-windows-x86-64-${{ matrix.qt-version }}-plugins.zip
|
name: chatterino-windows-x86-64-${{ matrix.qt-version }}-symbols.pdb.7z
|
||||||
path: build/chatterino-windows-x86-64.zip
|
path: build/bin/chatterino.pdb.7z
|
||||||
|
|
||||||
- name: Clean Conan pkgs
|
- name: Clean Conan pkgs
|
||||||
if: startsWith(matrix.os, 'windows')
|
if: startsWith(matrix.os, 'windows')
|
||||||
|
@ -170,7 +219,6 @@ jobs:
|
||||||
libboost-filesystem-dev \
|
libboost-filesystem-dev \
|
||||||
libpulse-dev \
|
libpulse-dev \
|
||||||
libxkbcommon-x11-0 \
|
libxkbcommon-x11-0 \
|
||||||
libgstreamer-plugins-base1.0-0 \
|
|
||||||
build-essential \
|
build-essential \
|
||||||
libgl1-mesa-dev \
|
libgl1-mesa-dev \
|
||||||
libxcb-icccm4 \
|
libxcb-icccm4 \
|
||||||
|
@ -179,12 +227,18 @@ jobs:
|
||||||
libxcb-render-util0 \
|
libxcb-render-util0 \
|
||||||
libxcb-xinerama0
|
libxcb-xinerama0
|
||||||
|
|
||||||
|
- name: Apply Qt patches (Ubuntu)
|
||||||
|
if: startsWith(matrix.os, 'ubuntu') && startsWith(matrix.qt-version, '5.')
|
||||||
|
run: |
|
||||||
|
patch "$Qt5_DIR/include/QtConcurrent/qtconcurrentthreadengine.h" .patches/qt5-on-newer-gcc.patch
|
||||||
|
shell: bash
|
||||||
|
|
||||||
- name: Build (Ubuntu)
|
- name: Build (Ubuntu)
|
||||||
if: startsWith(matrix.os, 'ubuntu')
|
if: startsWith(matrix.os, 'ubuntu')
|
||||||
run: |
|
run: |
|
||||||
mkdir build
|
mkdir build
|
||||||
cd build
|
cd build
|
||||||
cmake \
|
CXXFLAGS=-fno-sized-deallocation cmake \
|
||||||
-DCMAKE_INSTALL_PREFIX=appdir/usr/ \
|
-DCMAKE_INSTALL_PREFIX=appdir/usr/ \
|
||||||
-DCMAKE_BUILD_TYPE=Release \
|
-DCMAKE_BUILD_TYPE=Release \
|
||||||
-DPAJLADA_SETTINGS_USE_BOOST_FILESYSTEM=On \
|
-DPAJLADA_SETTINGS_USE_BOOST_FILESYSTEM=On \
|
||||||
|
@ -215,24 +269,24 @@ jobs:
|
||||||
clang-tidy-review-metadata.json
|
clang-tidy-review-metadata.json
|
||||||
|
|
||||||
- name: Package - AppImage (Ubuntu)
|
- name: Package - AppImage (Ubuntu)
|
||||||
if: startsWith(matrix.os, 'ubuntu')
|
if: startsWith(matrix.os, 'ubuntu') && matrix.skip_artifact != 'yes'
|
||||||
run: |
|
run: |
|
||||||
cd build
|
cd build
|
||||||
sh ./../.CI/CreateAppImage.sh
|
sh ./../.CI/CreateAppImage.sh
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
- name: Package - .deb (Ubuntu)
|
- name: Package - .deb (Ubuntu)
|
||||||
if: startsWith(matrix.os, 'ubuntu')
|
if: startsWith(matrix.os, 'ubuntu') && matrix.skip_artifact != 'yes'
|
||||||
run: |
|
run: |
|
||||||
cd build
|
cd build
|
||||||
sh ./../.CI/CreateUbuntuDeb.sh
|
sh ./../.CI/CreateUbuntuDeb.sh
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
- name: Upload artifact - AppImage (Ubuntu)
|
- name: Upload artifact - AppImage (Ubuntu)
|
||||||
if: startsWith(matrix.os, 'ubuntu') && matrix.plugins == false
|
if: startsWith(matrix.os, 'ubuntu') && matrix.skip_artifact != 'yes'
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: Chatterino-x86_64-${{ matrix.qt-version }}.AppImage
|
name: Chatterino-x86_64-${{ matrix.qt-version }}-${{ artifact_descr }}.AppImage
|
||||||
path: build/Chatterino-x86_64.AppImage
|
path: build/Chatterino-x86_64.AppImage
|
||||||
|
|
||||||
- name: Upload artifact - AppImage (Ubuntu, plugins)
|
- name: Upload artifact - AppImage (Ubuntu, plugins)
|
||||||
|
@ -243,17 +297,10 @@ jobs:
|
||||||
path: build/Chatterino-x86_64.AppImage
|
path: build/Chatterino-x86_64.AppImage
|
||||||
|
|
||||||
- name: Upload artifact - .deb (Ubuntu)
|
- name: Upload artifact - .deb (Ubuntu)
|
||||||
if: startsWith(matrix.os, 'ubuntu') && matrix.plugins == false
|
if: startsWith(matrix.os, 'ubuntu') && matrix.skip_artifact != 'yes'
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: Chatterino-${{ matrix.qt-version }}.deb
|
name: Chatterino-${{ matrix.os }}-Qt-${{ matrix.qt-version }}-${{ artifact_descr }}.deb
|
||||||
path: build/Chatterino-x86_64.deb
|
|
||||||
|
|
||||||
- name: Upload artifact - .deb (Ubuntu, plugins)
|
|
||||||
if: startsWith(matrix.os, 'ubuntu') && matrix.plugins == true
|
|
||||||
uses: actions/upload-artifact@v3
|
|
||||||
with:
|
|
||||||
name: Chatterino-${{ matrix.qt-version }}-plugins.deb
|
|
||||||
path: build/Chatterino-x86_64.deb
|
path: build/Chatterino-x86_64.deb
|
||||||
|
|
||||||
# MACOS
|
# MACOS
|
||||||
|
@ -290,17 +337,10 @@ jobs:
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
- name: Upload artifact (MacOS)
|
- name: Upload artifact (MacOS)
|
||||||
if: startsWith(matrix.os, 'macos') && matrix.plugins == false
|
if: startsWith(matrix.os, 'macos')
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: chatterino-osx-${{ matrix.qt-version }}.dmg
|
name: chatterino-osx-${{ matrix.qt-version }}-${{ artifact_descr }}.dmg
|
||||||
path: build/chatterino-osx.dmg
|
|
||||||
|
|
||||||
- name: Upload artifact (MacOS, plugins)
|
|
||||||
if: startsWith(matrix.os, 'macos') && matrix.plugins == true
|
|
||||||
uses: actions/upload-artifact@v3
|
|
||||||
with:
|
|
||||||
name: chatterino-osx-${{ matrix.qt-version }}.dmg
|
|
||||||
path: build/chatterino-osx.dmg
|
path: build/chatterino-osx.dmg
|
||||||
create-release:
|
create-release:
|
||||||
needs: build
|
needs: build
|
||||||
|
@ -320,7 +360,12 @@ jobs:
|
||||||
|
|
||||||
- uses: actions/download-artifact@v3
|
- uses: actions/download-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: Chatterino-5.15.2.deb
|
name: Chatterino-ubuntu-20.04-Qt-5.12.12.deb
|
||||||
|
path: release-artifacts/
|
||||||
|
|
||||||
|
- uses: actions/download-artifact@v3
|
||||||
|
with:
|
||||||
|
name: Chatterino-ubuntu-22.04-Qt-5.15.2.deb
|
||||||
path: release-artifacts/
|
path: release-artifacts/
|
||||||
|
|
||||||
- uses: actions/download-artifact@v3
|
- uses: actions/download-artifact@v3
|
||||||
|
|
2
.github/workflows/test.yml
vendored
2
.github/workflows/test.yml
vendored
|
@ -6,7 +6,7 @@ on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
env:
|
env:
|
||||||
TWITCH_PUBSUB_SERVER_IMAGE: ghcr.io/chatterino/twitch-pubsub-server-test:master
|
TWITCH_PUBSUB_SERVER_IMAGE: ghcr.io/chatterino/twitch-pubsub-server-test:v1.0.6
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: test-${{ github.ref }}
|
group: test-${{ github.ref }}
|
||||||
|
|
6
.gitmodules
vendored
6
.gitmodules
vendored
|
@ -2,9 +2,6 @@
|
||||||
path = lib/libcommuni
|
path = lib/libcommuni
|
||||||
url = https://github.com/Chatterino/libcommuni
|
url = https://github.com/Chatterino/libcommuni
|
||||||
branch = chatterino-cmake
|
branch = chatterino-cmake
|
||||||
[submodule "lib/qBreakpad"]
|
|
||||||
path = lib/qBreakpad
|
|
||||||
url = https://github.com/jiakuan/qBreakpad.git
|
|
||||||
[submodule "lib/WinToast"]
|
[submodule "lib/WinToast"]
|
||||||
path = lib/WinToast
|
path = lib/WinToast
|
||||||
url = https://github.com/mohabouje/WinToast.git
|
url = https://github.com/mohabouje/WinToast.git
|
||||||
|
@ -41,3 +38,6 @@
|
||||||
[submodule "lib/lua/src"]
|
[submodule "lib/lua/src"]
|
||||||
path = lib/lua/src
|
path = lib/lua/src
|
||||||
url = https://github.com/lua/lua
|
url = https://github.com/lua/lua
|
||||||
|
[submodule "lib/crashpad"]
|
||||||
|
path = lib/crashpad
|
||||||
|
url = https://github.com/getsentry/crashpad
|
||||||
|
|
20
.patches/qt5-on-newer-gcc.patch
Normal file
20
.patches/qt5-on-newer-gcc.patch
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
This patch ensures Qt 5.15 in particular can build with modern compilers
|
||||||
|
|
||||||
|
See https://bugreports.qt.io/browse/QTBUG-91909 and https://codereview.qt-project.org/c/qt/qtbase/+/339417
|
||||||
|
---
|
||||||
|
|
||||||
|
diff --git a/src/concurrent/qtconcurrentthreadengine.h b/src/concurrent/qtconcurrentthreadengine.h
|
||||||
|
index cbd8ad04..4cd5b85 100644
|
||||||
|
--- a/src/concurrent/qtconcurrentthreadengine.h
|
||||||
|
+++ b/src/concurrent/qtconcurrentthreadengine.h
|
||||||
|
@@ -256,8 +256,8 @@
|
||||||
|
class ThreadEngineStarter<void> : public ThreadEngineStarterBase<void>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
- ThreadEngineStarter<void>(ThreadEngine<void> *_threadEngine)
|
||||||
|
- :ThreadEngineStarterBase<void>(_threadEngine) {}
|
||||||
|
+ ThreadEngineStarter(ThreadEngine<void> *_threadEngine)
|
||||||
|
+ : ThreadEngineStarterBase<void>(_threadEngine) {}
|
||||||
|
|
||||||
|
void startBlocking()
|
||||||
|
{
|
|
@ -3,6 +3,7 @@
|
||||||
## Unversioned
|
## Unversioned
|
||||||
|
|
||||||
- Major: Added live emote updates for BTTV (#4147)
|
- Major: Added live emote updates for BTTV (#4147)
|
||||||
|
- Minor: Added option to highlight your own messages in Highlights page under Users tab. (#3833)
|
||||||
- Minor: Change the highlight order to prioritize Message highlights over User highlights. (#4303)
|
- Minor: Change the highlight order to prioritize Message highlights over User highlights. (#4303)
|
||||||
- Minor: Added ability to negate search options by prefixing it with an exclamation mark (e.g. `!badge:mod` to search for messages where the author does not have the moderator badge). (#4207)
|
- Minor: Added ability to negate search options by prefixing it with an exclamation mark (e.g. `!badge:mod` to search for messages where the author does not have the moderator badge). (#4207)
|
||||||
- Minor: Search window input will automatically use currently selected text if present. (#4178)
|
- Minor: Search window input will automatically use currently selected text if present. (#4178)
|
||||||
|
@ -11,6 +12,9 @@
|
||||||
- Minor: Added link to streamlink docs for easier user setup. (#4217)
|
- Minor: Added link to streamlink docs for easier user setup. (#4217)
|
||||||
- Minor: Added setting to turn off rendering of reply context. (#4224)
|
- Minor: Added setting to turn off rendering of reply context. (#4224)
|
||||||
- Minor: Added setting to select which channels to log. (#4302)
|
- Minor: Added setting to select which channels to log. (#4302)
|
||||||
|
- Minor: Added support for HTTP and Socks5 proxies through environment variables. (#4321)
|
||||||
|
- Minor: Remove sending part of the multipart emoji workaround (#4361)
|
||||||
|
- Minor: Added crashpad to capture crashes on Windows locally. See PR for build/crash analysis instructions. (#4351)
|
||||||
- Bugfix: Fixed crash that would occur when performing certain actions after removing all tabs. (#4271)
|
- Bugfix: Fixed crash that would occur when performing certain actions after removing all tabs. (#4271)
|
||||||
- Bugfix: Fixed highlight sounds not reloading on change properly. (#4194)
|
- Bugfix: Fixed highlight sounds not reloading on change properly. (#4194)
|
||||||
- Bugfix: Fixed CTRL + C not working in reply thread popups. (#4209)
|
- Bugfix: Fixed CTRL + C not working in reply thread popups. (#4209)
|
||||||
|
@ -27,6 +31,7 @@
|
||||||
- Bugfix: Fixed the split "Search" menu action not opening the correct search window. (#4305)
|
- Bugfix: Fixed the split "Search" menu action not opening the correct search window. (#4305)
|
||||||
- Bugfix: Fixed an issue on Windows when opening links in incognito mode that contained forward slashes in hash (#4307)
|
- Bugfix: Fixed an issue on Windows when opening links in incognito mode that contained forward slashes in hash (#4307)
|
||||||
- Bugfix: Fixed an issue where beta versions wouldn't update to stable versions correctly. (#4329)
|
- Bugfix: Fixed an issue where beta versions wouldn't update to stable versions correctly. (#4329)
|
||||||
|
- Bugfix: Avoided crash that could occur when receiving channel point reward information. (#4360)
|
||||||
- Dev: Changed sound backend from Qt to miniaudio. (#4334)
|
- Dev: Changed sound backend from Qt to miniaudio. (#4334)
|
||||||
- Dev: Remove protocol from QApplication's Organization Domain (so changed from `https://www.chatterino.com` to `chatterino.com`). (#4256)
|
- Dev: Remove protocol from QApplication's Organization Domain (so changed from `https://www.chatterino.com` to `chatterino.com`). (#4256)
|
||||||
- Dev: Ignore `WM_SHOWWINDOW` hide events, causing fewer attempted rescales. (#4198)
|
- Dev: Ignore `WM_SHOWWINDOW` hide events, causing fewer attempted rescales. (#4198)
|
||||||
|
@ -44,6 +49,10 @@
|
||||||
- Dev: Added CMake Install Support on Windows. (#4300)
|
- Dev: Added CMake Install Support on Windows. (#4300)
|
||||||
- Dev: Changed conan generator to [`CMakeDeps`](https://docs.conan.io/en/latest/reference/conanfile/tools/cmake/cmakedeps.html) and [`CMakeToolchain`](https://docs.conan.io/en/latest/reference/conanfile/tools/cmake/cmaketoolchain.html). See PR for migration notes. (#4335)
|
- Dev: Changed conan generator to [`CMakeDeps`](https://docs.conan.io/en/latest/reference/conanfile/tools/cmake/cmakedeps.html) and [`CMakeToolchain`](https://docs.conan.io/en/latest/reference/conanfile/tools/cmake/cmaketoolchain.html). See PR for migration notes. (#4335)
|
||||||
- Dev: Add scripting capabilities with Lua (#4341)
|
- Dev: Add scripting capabilities with Lua (#4341)
|
||||||
|
- Dev: Refactored 7TV EventAPI implementation. (#4342)
|
||||||
|
- Dev: Disabled ImageExpirationPool in tests. (#4363)
|
||||||
|
- Dev: Don't rely on undocumented registry keys to find the default browser on Windows. (#4362)
|
||||||
|
- Dev: Use `QEnterEvent` for `QWidget::enterEvent` on Qt 6. (#4365)
|
||||||
|
|
||||||
## 2.4.0
|
## 2.4.0
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@ option(USE_SYSTEM_PAJLADA_SETTINGS "Use system pajlada settings library" OFF)
|
||||||
option(USE_SYSTEM_LIBCOMMUNI "Use system communi library" OFF)
|
option(USE_SYSTEM_LIBCOMMUNI "Use system communi library" OFF)
|
||||||
option(USE_SYSTEM_QTKEYCHAIN "Use system QtKeychain library" OFF)
|
option(USE_SYSTEM_QTKEYCHAIN "Use system QtKeychain library" OFF)
|
||||||
option(BUILD_WITH_QTKEYCHAIN "Build Chatterino with support for your system key chain" ON)
|
option(BUILD_WITH_QTKEYCHAIN "Build Chatterino with support for your system key chain" ON)
|
||||||
|
option(BUILD_WITH_CRASHPAD "Build chatterino with crashpad" OFF)
|
||||||
option(USE_PRECOMPILED_HEADERS "Use precompiled headers" ON)
|
option(USE_PRECOMPILED_HEADERS "Use precompiled headers" ON)
|
||||||
option(BUILD_WITH_QT6 "Use Qt6 instead of default Qt5" OFF)
|
option(BUILD_WITH_QT6 "Use Qt6 instead of default Qt5" OFF)
|
||||||
option(CHATTERINO_GENERATE_COVERAGE "Generate coverage files" OFF)
|
option(CHATTERINO_GENERATE_COVERAGE "Generate coverage files" OFF)
|
||||||
|
@ -149,11 +150,16 @@ else()
|
||||||
add_subdirectory("${CMAKE_SOURCE_DIR}/lib/settings" EXCLUDE_FROM_ALL)
|
add_subdirectory("${CMAKE_SOURCE_DIR}/lib/settings" EXCLUDE_FROM_ALL)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
||||||
if (CHATTERINO_PLUGINS)
|
if (CHATTERINO_PLUGINS)
|
||||||
set(LUA_INCLUDE_DIRS "${CMAKE_SOURCE_DIR}/lib/lua/src")
|
set(LUA_INCLUDE_DIRS "${CMAKE_SOURCE_DIR}/lib/lua/src")
|
||||||
add_subdirectory(lib/lua)
|
add_subdirectory(lib/lua)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if (BUILD_WITH_CRASHPAD)
|
||||||
|
add_subdirectory("${CMAKE_SOURCE_DIR}/lib/crashpad" EXCLUDE_FROM_ALL)
|
||||||
|
endif()
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 20)
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit a6748f4f51273d86312e3d27ebe5277c9b1ff870
|
Subproject commit c3dc841af4dbf44669e65b82cb68a575864326bd
|
1
lib/crashpad
Submodule
1
lib/crashpad
Submodule
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 918fd319d679306c8c95ee92376c6fa6ef3407a0
|
|
@ -1 +0,0 @@
|
||||||
Subproject commit a4626c12e9ae6f02fc1ca7a4e399bd8307424103
|
|
202
resources/licenses/crashpad.txt
Normal file
202
resources/licenses/crashpad.txt
Normal file
|
@ -0,0 +1,202 @@
|
||||||
|
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
|
@ -22,8 +22,8 @@
|
||||||
#include "providers/chatterino/ChatterinoBadges.hpp"
|
#include "providers/chatterino/ChatterinoBadges.hpp"
|
||||||
#include "providers/ffz/FfzBadges.hpp"
|
#include "providers/ffz/FfzBadges.hpp"
|
||||||
#include "providers/irc/Irc2.hpp"
|
#include "providers/irc/Irc2.hpp"
|
||||||
#include "providers/seventv/eventapi/SeventvEventAPIDispatch.hpp"
|
#include "providers/seventv/eventapi/Dispatch.hpp"
|
||||||
#include "providers/seventv/eventapi/SeventvEventAPISubscription.hpp"
|
#include "providers/seventv/eventapi/Subscription.hpp"
|
||||||
#include "providers/seventv/SeventvBadges.hpp"
|
#include "providers/seventv/SeventvBadges.hpp"
|
||||||
#include "providers/seventv/SeventvEventAPI.hpp"
|
#include "providers/seventv/SeventvEventAPI.hpp"
|
||||||
#include "providers/twitch/ChannelPointReward.hpp"
|
#include "providers/twitch/ChannelPointReward.hpp"
|
||||||
|
|
|
@ -200,10 +200,14 @@ set(SOURCE_FILES
|
||||||
messages/search/SubtierPredicate.cpp
|
messages/search/SubtierPredicate.cpp
|
||||||
messages/search/SubtierPredicate.hpp
|
messages/search/SubtierPredicate.hpp
|
||||||
|
|
||||||
|
providers/Crashpad.cpp
|
||||||
|
providers/Crashpad.hpp
|
||||||
providers/IvrApi.cpp
|
providers/IvrApi.cpp
|
||||||
providers/IvrApi.hpp
|
providers/IvrApi.hpp
|
||||||
providers/LinkResolver.cpp
|
providers/LinkResolver.cpp
|
||||||
providers/LinkResolver.hpp
|
providers/LinkResolver.hpp
|
||||||
|
providers/NetworkConfigurationProvider.cpp
|
||||||
|
providers/NetworkConfigurationProvider.hpp
|
||||||
providers/RecentMessagesApi.cpp
|
providers/RecentMessagesApi.cpp
|
||||||
providers/RecentMessagesApi.hpp
|
providers/RecentMessagesApi.hpp
|
||||||
|
|
||||||
|
@ -259,14 +263,14 @@ set(SOURCE_FILES
|
||||||
providers/seventv/SeventvEventAPI.cpp
|
providers/seventv/SeventvEventAPI.cpp
|
||||||
providers/seventv/SeventvEventAPI.hpp
|
providers/seventv/SeventvEventAPI.hpp
|
||||||
|
|
||||||
providers/seventv/eventapi/SeventvEventAPIClient.cpp
|
providers/seventv/eventapi/Client.cpp
|
||||||
providers/seventv/eventapi/SeventvEventAPIClient.hpp
|
providers/seventv/eventapi/Client.hpp
|
||||||
providers/seventv/eventapi/SeventvEventAPIDispatch.cpp
|
providers/seventv/eventapi/Dispatch.cpp
|
||||||
providers/seventv/eventapi/SeventvEventAPIDispatch.hpp
|
providers/seventv/eventapi/Dispatch.hpp
|
||||||
providers/seventv/eventapi/SeventvEventAPIMessage.cpp
|
providers/seventv/eventapi/Message.cpp
|
||||||
providers/seventv/eventapi/SeventvEventAPIMessage.hpp
|
providers/seventv/eventapi/Message.hpp
|
||||||
providers/seventv/eventapi/SeventvEventAPISubscription.cpp
|
providers/seventv/eventapi/Subscription.cpp
|
||||||
providers/seventv/eventapi/SeventvEventAPISubscription.hpp
|
providers/seventv/eventapi/Subscription.hpp
|
||||||
|
|
||||||
providers/twitch/ChannelPointReward.cpp
|
providers/twitch/ChannelPointReward.cpp
|
||||||
providers/twitch/ChannelPointReward.hpp
|
providers/twitch/ChannelPointReward.hpp
|
||||||
|
@ -651,6 +655,22 @@ else()
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# Set the output of TARGET to be
|
||||||
|
# - CMAKE_BIN_DIR/lib for libraries
|
||||||
|
# - CMAKE_BIN_DIR/bin for BINARIES
|
||||||
|
# an additional argument specifies the subdirectory.
|
||||||
|
function(set_target_directory_hierarchy TARGET)
|
||||||
|
set_target_properties(${TARGET}
|
||||||
|
PROPERTIES
|
||||||
|
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib/${ARGV1}"
|
||||||
|
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib/${ARGV1}"
|
||||||
|
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin/${ARGV1}"
|
||||||
|
RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_BINARY_DIR}/bin/${ARGV1}"
|
||||||
|
RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_BINARY_DIR}/bin/${ARGV1}"
|
||||||
|
RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_BINARY_DIR}/bin/${ARGV1}"
|
||||||
|
)
|
||||||
|
endfunction()
|
||||||
|
|
||||||
if (BUILD_APP)
|
if (BUILD_APP)
|
||||||
if (APPLE)
|
if (APPLE)
|
||||||
add_executable(${EXECUTABLE_PROJECT} ${MACOS_BUNDLE_ICON_FILE} main.cpp)
|
add_executable(${EXECUTABLE_PROJECT} ${MACOS_BUNDLE_ICON_FILE} main.cpp)
|
||||||
|
@ -663,15 +683,7 @@ if (BUILD_APP)
|
||||||
|
|
||||||
target_link_libraries(${EXECUTABLE_PROJECT} PUBLIC ${LIBRARY_PROJECT})
|
target_link_libraries(${EXECUTABLE_PROJECT} PUBLIC ${LIBRARY_PROJECT})
|
||||||
|
|
||||||
set_target_properties(${EXECUTABLE_PROJECT}
|
set_target_directory_hierarchy(${EXECUTABLE_PROJECT})
|
||||||
PROPERTIES
|
|
||||||
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib"
|
|
||||||
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib"
|
|
||||||
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin"
|
|
||||||
RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_BINARY_DIR}/bin"
|
|
||||||
RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_BINARY_DIR}/bin"
|
|
||||||
RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_BINARY_DIR}/bin"
|
|
||||||
)
|
|
||||||
|
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
if (NOT WINDEPLOYQT_PATH)
|
if (NOT WINDEPLOYQT_PATH)
|
||||||
|
@ -851,8 +863,29 @@ if (LIBRT)
|
||||||
)
|
)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
|
if (BUILD_WITH_CRASHPAD)
|
||||||
|
target_compile_definitions(${LIBRARY_PROJECT} PUBLIC CHATTERINO_WITH_CRASHPAD)
|
||||||
|
target_link_libraries(${LIBRARY_PROJECT} PUBLIC crashpad::client)
|
||||||
|
set_target_directory_hierarchy(crashpad_handler crashpad)
|
||||||
|
endif()
|
||||||
|
|
||||||
# Configure compiler warnings
|
# Configure compiler warnings
|
||||||
if (MSVC)
|
if (MSVC)
|
||||||
|
# Change flags for RelWithDebInfo
|
||||||
|
|
||||||
|
# Default: "/debug /INCREMENTAL"
|
||||||
|
# Changes:
|
||||||
|
# - Disable incremental linking to reduce padding
|
||||||
|
# - Enable all optimizations - by default when /DEBUG is specified,
|
||||||
|
# these optimizations will be disabled. We need /DEBUG to generate a PDB.
|
||||||
|
# See https://gitlab.kitware.com/cmake/cmake/-/issues/20812 for more details.
|
||||||
|
set(CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO "/DEBUG /INCREMENTAL:NO /OPT:REF,ICF,LBR")
|
||||||
|
|
||||||
|
# Use the function inlining level from 'Release' mode (2).
|
||||||
|
string(REPLACE "/Ob1" "/Ob2" CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}")
|
||||||
|
|
||||||
|
# Configure warnings
|
||||||
|
|
||||||
# Someone adds /W3 before we add /W4.
|
# Someone adds /W3 before we add /W4.
|
||||||
# This makes sure, only /W4 is specified.
|
# This makes sure, only /W4 is specified.
|
||||||
string(REPLACE "/W3" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
|
string(REPLACE "/W3" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
|
||||||
|
@ -881,6 +914,7 @@ if (MSVC)
|
||||||
/wd4100
|
/wd4100
|
||||||
/wd4267
|
/wd4267
|
||||||
)
|
)
|
||||||
|
# Disable min/max macros from Windows.h
|
||||||
target_compile_definitions(${LIBRARY_PROJECT} PUBLIC NOMINMAX)
|
target_compile_definitions(${LIBRARY_PROJECT} PUBLIC NOMINMAX)
|
||||||
else ()
|
else ()
|
||||||
target_compile_options(${LIBRARY_PROJECT} PUBLIC
|
target_compile_options(${LIBRARY_PROJECT} PUBLIC
|
||||||
|
|
|
@ -162,7 +162,7 @@ namespace {
|
||||||
// true.
|
// true.
|
||||||
void initSignalHandler()
|
void initSignalHandler()
|
||||||
{
|
{
|
||||||
#ifdef NDEBUG
|
#if defined(NDEBUG) && !defined(CHATTERINO_WITH_CRASHPAD)
|
||||||
signalsInitTime = std::chrono::steady_clock::now();
|
signalsInitTime = std::chrono::steady_clock::now();
|
||||||
|
|
||||||
signal(SIGSEGV, handleSignal);
|
signal(SIGSEGV, handleSignal);
|
||||||
|
|
|
@ -44,6 +44,17 @@ namespace {
|
||||||
return defaultValue;
|
return defaultValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boost::optional<QString> readOptionalStringEnv(const char *envName)
|
||||||
|
{
|
||||||
|
auto envString = std::getenv(envName);
|
||||||
|
if (envString != nullptr)
|
||||||
|
{
|
||||||
|
return QString(envString);
|
||||||
|
}
|
||||||
|
|
||||||
|
return boost::none;
|
||||||
|
}
|
||||||
|
|
||||||
uint16_t readPortEnv(const char *envName, uint16_t defaultValue)
|
uint16_t readPortEnv(const char *envName, uint16_t defaultValue)
|
||||||
{
|
{
|
||||||
auto envString = std::getenv(envName);
|
auto envString = std::getenv(envName);
|
||||||
|
@ -89,6 +100,7 @@ Env::Env()
|
||||||
readStringEnv("CHATTERINO2_TWITCH_SERVER_HOST", "irc.chat.twitch.tv"))
|
readStringEnv("CHATTERINO2_TWITCH_SERVER_HOST", "irc.chat.twitch.tv"))
|
||||||
, twitchServerPort(readPortEnv("CHATTERINO2_TWITCH_SERVER_PORT", 443))
|
, twitchServerPort(readPortEnv("CHATTERINO2_TWITCH_SERVER_PORT", 443))
|
||||||
, twitchServerSecure(readBoolEnv("CHATTERINO2_TWITCH_SERVER_SECURE", true))
|
, twitchServerSecure(readBoolEnv("CHATTERINO2_TWITCH_SERVER_SECURE", true))
|
||||||
|
, proxyUrl(readOptionalStringEnv("CHATTERINO2_PROXY_URL"))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <boost/optional.hpp>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
|
@ -16,6 +17,7 @@ public:
|
||||||
const QString twitchServerHost;
|
const QString twitchServerHost;
|
||||||
const uint16_t twitchServerPort;
|
const uint16_t twitchServerPort;
|
||||||
const bool twitchServerSecure;
|
const bool twitchServerSecure;
|
||||||
|
const boost::optional<QString> proxyUrl;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace chatterino
|
} // namespace chatterino
|
||||||
|
|
|
@ -29,6 +29,7 @@ Q_LOGGING_CATEGORY(chatterinoMain, "chatterino.main", logThreshold);
|
||||||
Q_LOGGING_CATEGORY(chatterinoMessage, "chatterino.message", logThreshold);
|
Q_LOGGING_CATEGORY(chatterinoMessage, "chatterino.message", logThreshold);
|
||||||
Q_LOGGING_CATEGORY(chatterinoNativeMessage, "chatterino.nativemessage",
|
Q_LOGGING_CATEGORY(chatterinoNativeMessage, "chatterino.nativemessage",
|
||||||
logThreshold);
|
logThreshold);
|
||||||
|
Q_LOGGING_CATEGORY(chatterinoNetwork, "chatterino.network", logThreshold);
|
||||||
Q_LOGGING_CATEGORY(chatterinoNotification, "chatterino.notification",
|
Q_LOGGING_CATEGORY(chatterinoNotification, "chatterino.notification",
|
||||||
logThreshold);
|
logThreshold);
|
||||||
Q_LOGGING_CATEGORY(chatterinoNuulsuploader, "chatterino.nuulsuploader",
|
Q_LOGGING_CATEGORY(chatterinoNuulsuploader, "chatterino.nuulsuploader",
|
||||||
|
|
|
@ -23,6 +23,7 @@ Q_DECLARE_LOGGING_CATEGORY(chatterinoLua);
|
||||||
Q_DECLARE_LOGGING_CATEGORY(chatterinoMain);
|
Q_DECLARE_LOGGING_CATEGORY(chatterinoMain);
|
||||||
Q_DECLARE_LOGGING_CATEGORY(chatterinoMessage);
|
Q_DECLARE_LOGGING_CATEGORY(chatterinoMessage);
|
||||||
Q_DECLARE_LOGGING_CATEGORY(chatterinoNativeMessage);
|
Q_DECLARE_LOGGING_CATEGORY(chatterinoNativeMessage);
|
||||||
|
Q_DECLARE_LOGGING_CATEGORY(chatterinoNetwork);
|
||||||
Q_DECLARE_LOGGING_CATEGORY(chatterinoNotification);
|
Q_DECLARE_LOGGING_CATEGORY(chatterinoNotification);
|
||||||
Q_DECLARE_LOGGING_CATEGORY(chatterinoNuulsuploader);
|
Q_DECLARE_LOGGING_CATEGORY(chatterinoNuulsuploader);
|
||||||
Q_DECLARE_LOGGING_CATEGORY(chatterinoPubSub);
|
Q_DECLARE_LOGGING_CATEGORY(chatterinoPubSub);
|
||||||
|
|
|
@ -100,6 +100,9 @@ QStringList Version::buildTags() const
|
||||||
#ifdef _MSC_FULL_VER
|
#ifdef _MSC_FULL_VER
|
||||||
tags.append("MSVC " + QString::number(_MSC_FULL_VER, 10));
|
tags.append("MSVC " + QString::number(_MSC_FULL_VER, 10));
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef CHATTERINO_WITH_CRASHPAD
|
||||||
|
tags.append("Crashpad");
|
||||||
|
#endif
|
||||||
|
|
||||||
return tags;
|
return tags;
|
||||||
}
|
}
|
||||||
|
|
|
@ -85,12 +85,6 @@ void sendWhisperMessage(const QString &text)
|
||||||
auto app = getApp();
|
auto app = getApp();
|
||||||
QString toSend = text.simplified();
|
QString toSend = text.simplified();
|
||||||
|
|
||||||
// This is to make sure that combined emoji go through properly, see
|
|
||||||
// https://github.com/Chatterino/chatterino2/issues/3384 and
|
|
||||||
// https://mm2pl.github.io/emoji_rfc.pdf for more details
|
|
||||||
// Constants used here are defined in TwitchChannel.hpp
|
|
||||||
toSend.replace(ZERO_WIDTH_JOINER, ESCAPE_TAG);
|
|
||||||
|
|
||||||
app->twitch->sendMessage("jtv", toSend);
|
app->twitch->sendMessage("jtv", toSend);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -210,6 +210,35 @@ void rebuildUserHighlights(Settings &settings,
|
||||||
{
|
{
|
||||||
auto userHighlights = settings.highlightedUsers.readOnly();
|
auto userHighlights = settings.highlightedUsers.readOnly();
|
||||||
|
|
||||||
|
if (settings.enableSelfMessageHighlight)
|
||||||
|
{
|
||||||
|
bool showInMentions = settings.showSelfMessageHighlightInMentions;
|
||||||
|
|
||||||
|
checks.emplace_back(HighlightCheck{
|
||||||
|
[showInMentions](
|
||||||
|
const auto &args, const auto &badges, const auto &senderName,
|
||||||
|
const auto &originalMessage, const auto &flags,
|
||||||
|
const auto self) -> boost::optional<HighlightResult> {
|
||||||
|
(void)args; //unused
|
||||||
|
(void)badges; //unused
|
||||||
|
(void)senderName; //unused
|
||||||
|
(void)flags; //unused
|
||||||
|
(void)originalMessage; //unused
|
||||||
|
|
||||||
|
if (!self)
|
||||||
|
{
|
||||||
|
return boost::none;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Highlight color is provided by the ColorProvider and will be updated accordingly
|
||||||
|
auto highlightColor = ColorProvider::instance().color(
|
||||||
|
ColorType::SelfMessageHighlight);
|
||||||
|
|
||||||
|
return HighlightResult{false, false, (QUrl) nullptr,
|
||||||
|
highlightColor, showInMentions};
|
||||||
|
}});
|
||||||
|
}
|
||||||
|
|
||||||
for (const auto &highlight : *userHighlights)
|
for (const auto &highlight : *userHighlights)
|
||||||
{
|
{
|
||||||
checks.emplace_back(HighlightCheck{
|
checks.emplace_back(HighlightCheck{
|
||||||
|
@ -391,6 +420,11 @@ void HighlightController::initialize(Settings &settings, Paths & /*paths*/)
|
||||||
this->rebuildListener_.addSetting(settings.enableSubHighlight);
|
this->rebuildListener_.addSetting(settings.enableSubHighlight);
|
||||||
this->rebuildListener_.addSetting(settings.enableSubHighlightSound);
|
this->rebuildListener_.addSetting(settings.enableSubHighlightSound);
|
||||||
this->rebuildListener_.addSetting(settings.enableSubHighlightTaskbar);
|
this->rebuildListener_.addSetting(settings.enableSubHighlightTaskbar);
|
||||||
|
this->rebuildListener_.addSetting(settings.enableSelfMessageHighlight);
|
||||||
|
this->rebuildListener_.addSetting(
|
||||||
|
settings.showSelfMessageHighlightInMentions);
|
||||||
|
// We do not need to rebuild the listener for the selfMessagesHighlightColor
|
||||||
|
// The color is dynamically fetched any time the self message highlight is triggered
|
||||||
this->rebuildListener_.addSetting(settings.subHighlightSoundUrl);
|
this->rebuildListener_.addSetting(settings.subHighlightSoundUrl);
|
||||||
|
|
||||||
this->rebuildListener_.addSetting(settings.enableThreadHighlight);
|
this->rebuildListener_.addSetting(settings.enableThreadHighlight);
|
||||||
|
|
|
@ -36,6 +36,10 @@ public:
|
||||||
ThreadMessageRow = 6,
|
ThreadMessageRow = 6,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum UserHighlightRowIndexes {
|
||||||
|
SelfMessageRow = 0,
|
||||||
|
};
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// turn a vector item into a model row
|
// turn a vector item into a model row
|
||||||
virtual HighlightPhrase getItemFromRow(
|
virtual HighlightPhrase getItemFromRow(
|
||||||
|
|
|
@ -10,6 +10,8 @@ namespace {
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
QColor HighlightPhrase::FALLBACK_HIGHLIGHT_COLOR = QColor(127, 63, 73, 127);
|
QColor HighlightPhrase::FALLBACK_HIGHLIGHT_COLOR = QColor(127, 63, 73, 127);
|
||||||
|
QColor HighlightPhrase::FALLBACK_SELF_MESSAGE_HIGHLIGHT_COLOR =
|
||||||
|
QColor(0, 118, 221, 115);
|
||||||
QColor HighlightPhrase::FALLBACK_REDEEMED_HIGHLIGHT_COLOR =
|
QColor HighlightPhrase::FALLBACK_REDEEMED_HIGHLIGHT_COLOR =
|
||||||
QColor(28, 126, 141, 60);
|
QColor(28, 126, 141, 60);
|
||||||
QColor HighlightPhrase::FALLBACK_FIRST_MESSAGE_HIGHLIGHT_COLOR =
|
QColor HighlightPhrase::FALLBACK_FIRST_MESSAGE_HIGHLIGHT_COLOR =
|
||||||
|
|
|
@ -79,6 +79,8 @@ public:
|
||||||
* Qt>=5.13.
|
* Qt>=5.13.
|
||||||
*/
|
*/
|
||||||
static QColor FALLBACK_HIGHLIGHT_COLOR;
|
static QColor FALLBACK_HIGHLIGHT_COLOR;
|
||||||
|
// Used for automatic self messages highlighing
|
||||||
|
static QColor FALLBACK_SELF_MESSAGE_HIGHLIGHT_COLOR;
|
||||||
static QColor FALLBACK_REDEEMED_HIGHLIGHT_COLOR;
|
static QColor FALLBACK_REDEEMED_HIGHLIGHT_COLOR;
|
||||||
static QColor FALLBACK_SUB_COLOR;
|
static QColor FALLBACK_SUB_COLOR;
|
||||||
static QColor FALLBACK_FIRST_MESSAGE_HIGHLIGHT_COLOR;
|
static QColor FALLBACK_FIRST_MESSAGE_HIGHLIGHT_COLOR;
|
||||||
|
|
|
@ -3,7 +3,9 @@
|
||||||
#include "Application.hpp"
|
#include "Application.hpp"
|
||||||
#include "controllers/highlights/HighlightModel.hpp"
|
#include "controllers/highlights/HighlightModel.hpp"
|
||||||
#include "controllers/highlights/HighlightPhrase.hpp"
|
#include "controllers/highlights/HighlightPhrase.hpp"
|
||||||
|
#include "providers/colors/ColorProvider.hpp"
|
||||||
#include "singletons/Settings.hpp"
|
#include "singletons/Settings.hpp"
|
||||||
|
#include "singletons/WindowManager.hpp"
|
||||||
#include "util/StandardItemHelper.hpp"
|
#include "util/StandardItemHelper.hpp"
|
||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
|
@ -37,6 +39,86 @@ HighlightPhrase UserHighlightModel::getItemFromRow(
|
||||||
highlightColor};
|
highlightColor};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void UserHighlightModel::afterInit()
|
||||||
|
{
|
||||||
|
// User highlight settings for your own messages
|
||||||
|
std::vector<QStandardItem *> messagesRow = this->createRow();
|
||||||
|
setBoolItem(messagesRow[Column::Pattern],
|
||||||
|
getSettings()->enableSelfMessageHighlight.getValue(), true,
|
||||||
|
false);
|
||||||
|
messagesRow[Column::Pattern]->setData("Your messages (automatic)",
|
||||||
|
Qt::DisplayRole);
|
||||||
|
setBoolItem(messagesRow[Column::ShowInMentions],
|
||||||
|
getSettings()->showSelfMessageHighlightInMentions.getValue(),
|
||||||
|
true, false);
|
||||||
|
messagesRow[Column::FlashTaskbar]->setFlags({});
|
||||||
|
messagesRow[Column::PlaySound]->setFlags({});
|
||||||
|
messagesRow[Column::UseRegex]->setFlags({});
|
||||||
|
messagesRow[Column::CaseSensitive]->setFlags({});
|
||||||
|
messagesRow[Column::SoundPath]->setFlags({});
|
||||||
|
|
||||||
|
auto selfColor =
|
||||||
|
ColorProvider::instance().color(ColorType::SelfMessageHighlight);
|
||||||
|
setColorItem(messagesRow[Column::Color], *selfColor, false);
|
||||||
|
|
||||||
|
this->insertCustomRow(
|
||||||
|
messagesRow, HighlightModel::UserHighlightRowIndexes::SelfMessageRow);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UserHighlightModel::customRowSetData(
|
||||||
|
const std::vector<QStandardItem *> &row, int column, const QVariant &value,
|
||||||
|
int role, int rowIndex)
|
||||||
|
{
|
||||||
|
switch (column)
|
||||||
|
{
|
||||||
|
case Column::Pattern: {
|
||||||
|
if (role == Qt::CheckStateRole)
|
||||||
|
{
|
||||||
|
if (rowIndex ==
|
||||||
|
HighlightModel::UserHighlightRowIndexes::SelfMessageRow)
|
||||||
|
{
|
||||||
|
getSettings()->enableSelfMessageHighlight.setValue(
|
||||||
|
value.toBool());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Column::ShowInMentions: {
|
||||||
|
if (role == Qt::CheckStateRole)
|
||||||
|
{
|
||||||
|
if (rowIndex ==
|
||||||
|
HighlightModel::UserHighlightRowIndexes::SelfMessageRow)
|
||||||
|
{
|
||||||
|
getSettings()->showSelfMessageHighlightInMentions.setValue(
|
||||||
|
value.toBool());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Column::Color: {
|
||||||
|
// Custom color
|
||||||
|
if (role == Qt::DecorationRole)
|
||||||
|
{
|
||||||
|
auto colorName = value.value<QColor>().name(QColor::HexArgb);
|
||||||
|
if (rowIndex ==
|
||||||
|
HighlightModel::UserHighlightRowIndexes::SelfMessageRow)
|
||||||
|
{
|
||||||
|
// Update the setting with the new value
|
||||||
|
getSettings()->selfMessageHighlightColor.setValue(
|
||||||
|
colorName);
|
||||||
|
// Update the color provider with the new color to be used for future
|
||||||
|
const_cast<ColorProvider &>(ColorProvider::instance())
|
||||||
|
.updateColor(ColorType::SelfMessageHighlight,
|
||||||
|
QColor(colorName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
getApp()->windows->forceLayoutChannelViews();
|
||||||
|
}
|
||||||
|
|
||||||
// row into vector item
|
// row into vector item
|
||||||
void UserHighlightModel::getRowFromItem(const HighlightPhrase &item,
|
void UserHighlightModel::getRowFromItem(const HighlightPhrase &item,
|
||||||
std::vector<QStandardItem *> &row)
|
std::vector<QStandardItem *> &row)
|
||||||
|
|
|
@ -22,6 +22,12 @@ protected:
|
||||||
|
|
||||||
virtual void getRowFromItem(const HighlightPhrase &item,
|
virtual void getRowFromItem(const HighlightPhrase &item,
|
||||||
std::vector<QStandardItem *> &row) override;
|
std::vector<QStandardItem *> &row) override;
|
||||||
|
|
||||||
|
virtual void afterInit() override;
|
||||||
|
|
||||||
|
virtual void customRowSetData(const std::vector<QStandardItem *> &row,
|
||||||
|
int column, const QVariant &value, int role,
|
||||||
|
int rowIndex) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace chatterino
|
} // namespace chatterino
|
||||||
|
|
10
src/main.cpp
10
src/main.cpp
|
@ -1,15 +1,17 @@
|
||||||
#include "BrowserExtension.hpp"
|
#include "BrowserExtension.hpp"
|
||||||
#include "common/Args.hpp"
|
#include "common/Args.hpp"
|
||||||
|
#include "common/Env.hpp"
|
||||||
#include "common/Modes.hpp"
|
#include "common/Modes.hpp"
|
||||||
#include "common/QLogging.hpp"
|
#include "common/QLogging.hpp"
|
||||||
#include "common/Version.hpp"
|
#include "common/Version.hpp"
|
||||||
|
#include "providers/Crashpad.hpp"
|
||||||
#include "providers/IvrApi.hpp"
|
#include "providers/IvrApi.hpp"
|
||||||
|
#include "providers/NetworkConfigurationProvider.hpp"
|
||||||
#include "providers/twitch/api/Helix.hpp"
|
#include "providers/twitch/api/Helix.hpp"
|
||||||
#include "RunGui.hpp"
|
#include "RunGui.hpp"
|
||||||
#include "singletons/Paths.hpp"
|
#include "singletons/Paths.hpp"
|
||||||
#include "singletons/Settings.hpp"
|
#include "singletons/Settings.hpp"
|
||||||
#include "util/AttachToConsole.hpp"
|
#include "util/AttachToConsole.hpp"
|
||||||
#include "util/IncognitoBrowser.hpp"
|
|
||||||
|
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QCommandLineParser>
|
#include <QCommandLineParser>
|
||||||
|
@ -57,6 +59,10 @@ int main(int argc, char **argv)
|
||||||
|
|
||||||
initArgs(a);
|
initArgs(a);
|
||||||
|
|
||||||
|
#ifdef CHATTERINO_WITH_CRASHPAD
|
||||||
|
const auto crashpadHandler = installCrashHandler();
|
||||||
|
#endif
|
||||||
|
|
||||||
// run in gui mode or browser extension host mode
|
// run in gui mode or browser extension host mode
|
||||||
if (getArgs().shouldRunBrowserExtensionHost)
|
if (getArgs().shouldRunBrowserExtensionHost)
|
||||||
{
|
{
|
||||||
|
@ -81,6 +87,8 @@ int main(int argc, char **argv)
|
||||||
attachToConsole();
|
attachToConsole();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NetworkConfigurationProvider::applyFromEnv(Env::get());
|
||||||
|
|
||||||
IvrApi::initialize();
|
IvrApi::initialize();
|
||||||
Helix::initialize();
|
Helix::initialize();
|
||||||
|
|
||||||
|
|
|
@ -272,7 +272,9 @@ namespace detail {
|
||||||
// IMAGE2
|
// IMAGE2
|
||||||
Image::~Image()
|
Image::~Image()
|
||||||
{
|
{
|
||||||
|
#ifndef DISABLE_IMAGE_EXPIRATION_POOL
|
||||||
ImageExpirationPool::instance().removeImagePtr(this);
|
ImageExpirationPool::instance().removeImagePtr(this);
|
||||||
|
#endif
|
||||||
|
|
||||||
if (this->empty_ && !this->frames_)
|
if (this->empty_ && !this->frames_)
|
||||||
{
|
{
|
||||||
|
@ -425,7 +427,9 @@ void Image::load() const
|
||||||
Image *this2 = const_cast<Image *>(this);
|
Image *this2 = const_cast<Image *>(this);
|
||||||
this2->shouldLoad_ = false;
|
this2->shouldLoad_ = false;
|
||||||
this2->actuallyLoad();
|
this2->actuallyLoad();
|
||||||
|
#ifndef DISABLE_IMAGE_EXPIRATION_POOL
|
||||||
ImageExpirationPool::instance().addImagePtr(this2->shared_from_this());
|
ImageExpirationPool::instance().addImagePtr(this2->shared_from_this());
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -551,6 +555,8 @@ void Image::expireFrames()
|
||||||
this->shouldLoad_ = true; // Mark as needing load again
|
this->shouldLoad_ = true; // Mark as needing load again
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef DISABLE_IMAGE_EXPIRATION_POOL
|
||||||
|
|
||||||
ImageExpirationPool::ImageExpirationPool()
|
ImageExpirationPool::ImageExpirationPool()
|
||||||
{
|
{
|
||||||
QObject::connect(&this->freeTimer_, &QTimer::timeout, [this] {
|
QObject::connect(&this->freeTimer_, &QTimer::timeout, [this] {
|
||||||
|
@ -643,4 +649,6 @@ void ImageExpirationPool::freeOld()
|
||||||
# endif
|
# endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
} // namespace chatterino
|
} // namespace chatterino
|
||||||
|
|
|
@ -19,6 +19,14 @@
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
|
#ifdef CHATTERINO_TEST
|
||||||
|
// When running tests, the ImageExpirationPool destructor can be called before
|
||||||
|
// all images are deleted, leading to a use-after-free of its mutex. This
|
||||||
|
// happens despite the lifetime of the ImageExpirationPool being (apparently)
|
||||||
|
// static. Therefore, just disable it during testing.
|
||||||
|
# define DISABLE_IMAGE_EXPIRATION_POOL
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
namespace detail {
|
namespace detail {
|
||||||
template <typename Image>
|
template <typename Image>
|
||||||
|
@ -105,6 +113,8 @@ private:
|
||||||
// forward-declarable function that calls Image::getEmpty() under the hood.
|
// forward-declarable function that calls Image::getEmpty() under the hood.
|
||||||
ImagePtr getEmptyImagePtr();
|
ImagePtr getEmptyImagePtr();
|
||||||
|
|
||||||
|
#ifndef DISABLE_IMAGE_EXPIRATION_POOL
|
||||||
|
|
||||||
class ImageExpirationPool
|
class ImageExpirationPool
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
|
@ -131,4 +141,6 @@ private:
|
||||||
std::mutex mutex_;
|
std::mutex mutex_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
} // namespace chatterino
|
} // namespace chatterino
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
|
|
||||||
#include <cinttypes>
|
#include <cinttypes>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <unordered_map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
|
|
95
src/providers/Crashpad.cpp
Normal file
95
src/providers/Crashpad.cpp
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
#ifdef CHATTERINO_WITH_CRASHPAD
|
||||||
|
# include "providers/Crashpad.hpp"
|
||||||
|
|
||||||
|
# include "common/QLogging.hpp"
|
||||||
|
# include "singletons/Paths.hpp"
|
||||||
|
|
||||||
|
# include <QApplication>
|
||||||
|
# include <QDir>
|
||||||
|
# include <QString>
|
||||||
|
|
||||||
|
# include <memory>
|
||||||
|
# include <string>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
/// The name of the crashpad handler executable.
|
||||||
|
/// This varies across platforms
|
||||||
|
# if defined(Q_OS_UNIX)
|
||||||
|
const QString CRASHPAD_EXECUTABLE_NAME = QStringLiteral("crashpad_handler");
|
||||||
|
# elif defined(Q_OS_WINDOWS)
|
||||||
|
const QString CRASHPAD_EXECUTABLE_NAME = QStringLiteral("crashpad_handler.exe");
|
||||||
|
# else
|
||||||
|
# error Unsupported platform
|
||||||
|
# endif
|
||||||
|
|
||||||
|
/// Converts a QString into the platform string representation.
|
||||||
|
# if defined(Q_OS_UNIX)
|
||||||
|
std::string nativeString(const QString &s)
|
||||||
|
{
|
||||||
|
return s.toStdString();
|
||||||
|
}
|
||||||
|
# elif defined(Q_OS_WINDOWS)
|
||||||
|
std::wstring nativeString(const QString &s)
|
||||||
|
{
|
||||||
|
return s.toStdWString();
|
||||||
|
}
|
||||||
|
# else
|
||||||
|
# error Unsupported platform
|
||||||
|
# endif
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace chatterino {
|
||||||
|
|
||||||
|
std::unique_ptr<crashpad::CrashpadClient> installCrashHandler()
|
||||||
|
{
|
||||||
|
// Currently, the following directory layout is assumed:
|
||||||
|
// [applicationDirPath]
|
||||||
|
// │
|
||||||
|
// ├─chatterino
|
||||||
|
// │
|
||||||
|
// ╰─[crashpad]
|
||||||
|
// │
|
||||||
|
// ╰─crashpad_handler
|
||||||
|
// TODO: The location of the binary might vary across platforms
|
||||||
|
auto crashpadBinDir = QDir(QApplication::applicationDirPath());
|
||||||
|
|
||||||
|
if (!crashpadBinDir.cd("crashpad"))
|
||||||
|
{
|
||||||
|
qCDebug(chatterinoApp) << "Cannot find crashpad directory";
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
if (!crashpadBinDir.exists(CRASHPAD_EXECUTABLE_NAME))
|
||||||
|
{
|
||||||
|
qCDebug(chatterinoApp) << "Cannot find crashpad handler executable";
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto handlerPath = base::FilePath(nativeString(
|
||||||
|
crashpadBinDir.absoluteFilePath(CRASHPAD_EXECUTABLE_NAME)));
|
||||||
|
|
||||||
|
// Argument passed in --database
|
||||||
|
// > Crash reports are written to this database, and if uploads are enabled,
|
||||||
|
// uploaded from this database to a crash report collection server.
|
||||||
|
const auto databaseDir =
|
||||||
|
base::FilePath(nativeString(getPaths()->crashdumpDirectory));
|
||||||
|
|
||||||
|
auto client = std::make_unique<crashpad::CrashpadClient>();
|
||||||
|
|
||||||
|
// See https://chromium.googlesource.com/crashpad/crashpad/+/HEAD/handler/crashpad_handler.md
|
||||||
|
// for documentation on available options.
|
||||||
|
if (!client->StartHandler(handlerPath, databaseDir, {}, {}, {}, {}, true,
|
||||||
|
false))
|
||||||
|
{
|
||||||
|
qCDebug(chatterinoApp) << "Failed to start crashpad handler";
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
qCDebug(chatterinoApp) << "Started crashpad handler";
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace chatterino
|
||||||
|
|
||||||
|
#endif
|
14
src/providers/Crashpad.hpp
Normal file
14
src/providers/Crashpad.hpp
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef CHATTERINO_WITH_CRASHPAD
|
||||||
|
# include <client/crashpad_client.h>
|
||||||
|
|
||||||
|
# include <memory>
|
||||||
|
|
||||||
|
namespace chatterino {
|
||||||
|
|
||||||
|
std::unique_ptr<crashpad::CrashpadClient> installCrashHandler();
|
||||||
|
|
||||||
|
} // namespace chatterino
|
||||||
|
|
||||||
|
#endif
|
76
src/providers/NetworkConfigurationProvider.cpp
Normal file
76
src/providers/NetworkConfigurationProvider.cpp
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
#include "providers/NetworkConfigurationProvider.hpp"
|
||||||
|
|
||||||
|
#include "common/Env.hpp"
|
||||||
|
#include "common/QLogging.hpp"
|
||||||
|
|
||||||
|
#include <QNetworkProxy>
|
||||||
|
#include <QSslConfiguration>
|
||||||
|
#include <QUrl>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
/**
|
||||||
|
* Creates a QNetworkProxy from a given URL.
|
||||||
|
*
|
||||||
|
* Creates an HTTP proxy by default, a Socks5 will be created if the scheme is 'socks5'.
|
||||||
|
*/
|
||||||
|
QNetworkProxy createProxyFromUrl(const QUrl &url)
|
||||||
|
{
|
||||||
|
QNetworkProxy proxy;
|
||||||
|
proxy.setHostName(url.host(QUrl::FullyEncoded));
|
||||||
|
proxy.setUser(url.userName(QUrl::FullyEncoded));
|
||||||
|
proxy.setPassword(url.password(QUrl::FullyEncoded));
|
||||||
|
proxy.setPort(url.port(1080));
|
||||||
|
|
||||||
|
if (url.scheme().compare(QStringLiteral("socks5"), Qt::CaseInsensitive) ==
|
||||||
|
0)
|
||||||
|
{
|
||||||
|
proxy.setType(QNetworkProxy::Socks5Proxy);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
proxy.setType(QNetworkProxy::HttpProxy);
|
||||||
|
if (!proxy.user().isEmpty() || !proxy.password().isEmpty())
|
||||||
|
{
|
||||||
|
// for some reason, Qt doesn't set the Proxy-Authorization header
|
||||||
|
const auto auth = proxy.user() + ":" + proxy.password();
|
||||||
|
const auto base64 = auth.toUtf8().toBase64();
|
||||||
|
proxy.setRawHeader("Proxy-Authorization",
|
||||||
|
QByteArray("Basic ").append(base64));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return proxy;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempts to apply the proxy specified by `url` as the application proxy.
|
||||||
|
*/
|
||||||
|
void applyProxy(const QString &url)
|
||||||
|
{
|
||||||
|
auto proxyUrl = QUrl(url);
|
||||||
|
if (!proxyUrl.isValid() || proxyUrl.isEmpty())
|
||||||
|
{
|
||||||
|
qCDebug(chatterinoNetwork)
|
||||||
|
<< "Invalid or empty proxy url: " << proxyUrl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto proxy = createProxyFromUrl(proxyUrl);
|
||||||
|
|
||||||
|
QNetworkProxy::setApplicationProxy(proxy);
|
||||||
|
qCDebug(chatterinoNetwork) << "Set application proxy to" << proxy;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace chatterino {
|
||||||
|
|
||||||
|
void NetworkConfigurationProvider::applyFromEnv(const Env &env)
|
||||||
|
{
|
||||||
|
if (env.proxyUrl)
|
||||||
|
{
|
||||||
|
applyProxy(env.proxyUrl.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace chatterino
|
59
src/providers/NetworkConfigurationProvider.hpp
Normal file
59
src/providers/NetworkConfigurationProvider.hpp
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "common/QLogging.hpp"
|
||||||
|
|
||||||
|
#include <QNetworkProxy>
|
||||||
|
#include <websocketpp/error.hpp>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace chatterino {
|
||||||
|
|
||||||
|
class Env;
|
||||||
|
|
||||||
|
/** This class manipulates the global network configuration (e.g. proxies). */
|
||||||
|
class NetworkConfigurationProvider
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/** This class should never be instantiated. */
|
||||||
|
NetworkConfigurationProvider() = delete;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies the configuration requested from the environment variables.
|
||||||
|
*
|
||||||
|
* Currently a proxy is applied if configured.
|
||||||
|
*/
|
||||||
|
static void applyFromEnv(const Env &env);
|
||||||
|
|
||||||
|
static void applyToWebSocket(const auto &connection)
|
||||||
|
{
|
||||||
|
const auto applicationProxy = QNetworkProxy::applicationProxy();
|
||||||
|
if (applicationProxy.type() != QNetworkProxy::HttpProxy)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
std::string url = "http://";
|
||||||
|
url += applicationProxy.hostName().toStdString();
|
||||||
|
url += ":";
|
||||||
|
url += std::to_string(applicationProxy.port());
|
||||||
|
websocketpp::lib::error_code ec;
|
||||||
|
connection->set_proxy(url, ec);
|
||||||
|
if (ec)
|
||||||
|
{
|
||||||
|
qCDebug(chatterinoNetwork)
|
||||||
|
<< "Couldn't set websocket proxy:" << ec.value();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
connection->set_proxy_basic_auth(
|
||||||
|
applicationProxy.user().toStdString(),
|
||||||
|
applicationProxy.password().toStdString(), ec);
|
||||||
|
if (ec)
|
||||||
|
{
|
||||||
|
qCDebug(chatterinoNetwork)
|
||||||
|
<< "Couldn't set websocket proxy auth:" << ec.value();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace chatterino
|
|
@ -84,6 +84,9 @@ namespace {
|
||||||
for (const auto jsonMessage : jsonMessages)
|
for (const auto jsonMessage : jsonMessages)
|
||||||
{
|
{
|
||||||
auto content = jsonMessage.toString();
|
auto content = jsonMessage.toString();
|
||||||
|
|
||||||
|
// For explanation of why this exists, see src/providers/twitch/TwitchChannel.hpp,
|
||||||
|
// where these constants are defined
|
||||||
content.replace(COMBINED_FIXER, ZERO_WIDTH_JOINER);
|
content.replace(COMBINED_FIXER, ZERO_WIDTH_JOINER);
|
||||||
|
|
||||||
auto message =
|
auto message =
|
||||||
|
|
|
@ -81,6 +81,20 @@ void ColorProvider::initTypeColorMap()
|
||||||
HighlightPhrase::FALLBACK_HIGHLIGHT_COLOR)});
|
HighlightPhrase::FALLBACK_HIGHLIGHT_COLOR)});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
customColor = getSettings()->selfMessageHighlightColor;
|
||||||
|
if (QColor(customColor).isValid())
|
||||||
|
{
|
||||||
|
this->typeColorMap_.insert({ColorType::SelfMessageHighlight,
|
||||||
|
std::make_shared<QColor>(customColor)});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this->typeColorMap_.insert(
|
||||||
|
{ColorType::SelfMessageHighlight,
|
||||||
|
std::make_shared<QColor>(
|
||||||
|
HighlightPhrase::FALLBACK_SELF_MESSAGE_HIGHLIGHT_COLOR)});
|
||||||
|
}
|
||||||
|
|
||||||
customColor = getSettings()->subHighlightColor;
|
customColor = getSettings()->subHighlightColor;
|
||||||
if (QColor(customColor).isValid())
|
if (QColor(customColor).isValid())
|
||||||
{
|
{
|
||||||
|
|
|
@ -16,6 +16,8 @@ enum class ColorType {
|
||||||
FirstMessageHighlight,
|
FirstMessageHighlight,
|
||||||
ElevatedMessageHighlight,
|
ElevatedMessageHighlight,
|
||||||
ThreadMessageHighlight,
|
ThreadMessageHighlight,
|
||||||
|
// Used in automatic highlights of your own messages
|
||||||
|
SelfMessageHighlight,
|
||||||
};
|
};
|
||||||
|
|
||||||
class ColorProvider
|
class ColorProvider
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
|
|
||||||
#include <rapidjson/rapidjson.h>
|
#include <rapidjson/rapidjson.h>
|
||||||
|
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
class QAbstractTableModel;
|
class QAbstractTableModel;
|
||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include "common/Version.hpp"
|
#include "common/Version.hpp"
|
||||||
#include "providers/liveupdates/BasicPubSubClient.hpp"
|
#include "providers/liveupdates/BasicPubSubClient.hpp"
|
||||||
#include "providers/liveupdates/BasicPubSubWebsocket.hpp"
|
#include "providers/liveupdates/BasicPubSubWebsocket.hpp"
|
||||||
|
#include "providers/NetworkConfigurationProvider.hpp"
|
||||||
#include "providers/twitch/PubSubHelpers.hpp"
|
#include "providers/twitch/PubSubHelpers.hpp"
|
||||||
#include "util/DebugCount.hpp"
|
#include "util/DebugCount.hpp"
|
||||||
#include "util/ExponentialBackoff.hpp"
|
#include "util/ExponentialBackoff.hpp"
|
||||||
|
@ -336,6 +337,8 @@ private:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NetworkConfigurationProvider::applyToWebSocket(con);
|
||||||
|
|
||||||
this->websocketClient_.connect(con);
|
this->websocketClient_.connect(con);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
#include "messages/Image.hpp"
|
#include "messages/Image.hpp"
|
||||||
#include "messages/ImageSet.hpp"
|
#include "messages/ImageSet.hpp"
|
||||||
#include "messages/MessageBuilder.hpp"
|
#include "messages/MessageBuilder.hpp"
|
||||||
#include "providers/seventv/eventapi/SeventvEventAPIDispatch.hpp"
|
#include "providers/seventv/eventapi/Dispatch.hpp"
|
||||||
#include "providers/twitch/TwitchChannel.hpp"
|
#include "providers/twitch/TwitchChannel.hpp"
|
||||||
#include "singletons/Settings.hpp"
|
#include "singletons/Settings.hpp"
|
||||||
|
|
||||||
|
@ -30,6 +30,7 @@
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
using namespace chatterino;
|
using namespace chatterino;
|
||||||
|
using namespace seventv::eventapi;
|
||||||
|
|
||||||
// These declarations won't throw an exception.
|
// These declarations won't throw an exception.
|
||||||
const QString CHANNEL_HAS_NO_EMOTES("This channel has no 7TV channel emotes.");
|
const QString CHANNEL_HAS_NO_EMOTES("This channel has no 7TV channel emotes.");
|
||||||
|
@ -224,7 +225,7 @@ EmoteMap parseEmotes(const QJsonArray &emoteSetEmotes, bool isGlobal)
|
||||||
}
|
}
|
||||||
|
|
||||||
EmotePtr createUpdatedEmote(const EmotePtr &oldEmote,
|
EmotePtr createUpdatedEmote(const EmotePtr &oldEmote,
|
||||||
const SeventvEventAPIEmoteUpdateDispatch &dispatch)
|
const EmoteUpdateDispatch &dispatch)
|
||||||
{
|
{
|
||||||
bool toNonAliased = oldEmote->baseName.has_value() &&
|
bool toNonAliased = oldEmote->baseName.has_value() &&
|
||||||
dispatch.emoteName == oldEmote->baseName->string;
|
dispatch.emoteName == oldEmote->baseName->string;
|
||||||
|
@ -245,6 +246,8 @@ EmotePtr createUpdatedEmote(const EmotePtr &oldEmote,
|
||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
|
|
||||||
|
using namespace seventv::eventapi;
|
||||||
|
|
||||||
SeventvEmotes::SeventvEmotes()
|
SeventvEmotes::SeventvEmotes()
|
||||||
: global_(std::make_shared<EmoteMap>())
|
: global_(std::make_shared<EmoteMap>())
|
||||||
{
|
{
|
||||||
|
@ -401,7 +404,7 @@ void SeventvEmotes::loadChannelEmotes(
|
||||||
|
|
||||||
boost::optional<EmotePtr> SeventvEmotes::addEmote(
|
boost::optional<EmotePtr> SeventvEmotes::addEmote(
|
||||||
Atomic<std::shared_ptr<const EmoteMap>> &map,
|
Atomic<std::shared_ptr<const EmoteMap>> &map,
|
||||||
const SeventvEventAPIEmoteAddDispatch &dispatch)
|
const EmoteAddDispatch &dispatch)
|
||||||
{
|
{
|
||||||
// Check for visibility first, so we don't copy the map.
|
// Check for visibility first, so we don't copy the map.
|
||||||
auto emoteData = dispatch.emoteJson["data"].toObject();
|
auto emoteData = dispatch.emoteJson["data"].toObject();
|
||||||
|
@ -429,7 +432,7 @@ boost::optional<EmotePtr> SeventvEmotes::addEmote(
|
||||||
|
|
||||||
boost::optional<EmotePtr> SeventvEmotes::updateEmote(
|
boost::optional<EmotePtr> SeventvEmotes::updateEmote(
|
||||||
Atomic<std::shared_ptr<const EmoteMap>> &map,
|
Atomic<std::shared_ptr<const EmoteMap>> &map,
|
||||||
const SeventvEventAPIEmoteUpdateDispatch &dispatch)
|
const EmoteUpdateDispatch &dispatch)
|
||||||
{
|
{
|
||||||
auto oldMap = map.get();
|
auto oldMap = map.get();
|
||||||
auto oldEmote = oldMap->findEmote(dispatch.emoteName, dispatch.emoteID);
|
auto oldEmote = oldMap->findEmote(dispatch.emoteName, dispatch.emoteID);
|
||||||
|
@ -451,7 +454,7 @@ boost::optional<EmotePtr> SeventvEmotes::updateEmote(
|
||||||
|
|
||||||
boost::optional<EmotePtr> SeventvEmotes::removeEmote(
|
boost::optional<EmotePtr> SeventvEmotes::removeEmote(
|
||||||
Atomic<std::shared_ptr<const EmoteMap>> &map,
|
Atomic<std::shared_ptr<const EmoteMap>> &map,
|
||||||
const SeventvEventAPIEmoteRemoveDispatch &dispatch)
|
const EmoteRemoveDispatch &dispatch)
|
||||||
{
|
{
|
||||||
// This copies the map.
|
// This copies the map.
|
||||||
EmoteMap updatedMap = *map.get();
|
EmoteMap updatedMap = *map.get();
|
||||||
|
|
|
@ -10,9 +10,12 @@
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
|
|
||||||
class Channel;
|
class Channel;
|
||||||
struct SeventvEventAPIEmoteAddDispatch;
|
|
||||||
struct SeventvEventAPIEmoteUpdateDispatch;
|
namespace seventv::eventapi {
|
||||||
struct SeventvEventAPIEmoteRemoveDispatch;
|
struct EmoteAddDispatch;
|
||||||
|
struct EmoteUpdateDispatch;
|
||||||
|
struct EmoteRemoveDispatch;
|
||||||
|
} // namespace seventv::eventapi
|
||||||
|
|
||||||
// https://github.com/SevenTV/API/blob/a84e884b5590dbb5d91a5c6b3548afabb228f385/data/model/emote-set.model.go#L29-L36
|
// https://github.com/SevenTV/API/blob/a84e884b5590dbb5d91a5c6b3548afabb228f385/data/model/emote-set.model.go#L29-L36
|
||||||
enum class SeventvActiveEmoteFlag : int64_t {
|
enum class SeventvActiveEmoteFlag : int64_t {
|
||||||
|
@ -86,7 +89,7 @@ public:
|
||||||
*/
|
*/
|
||||||
static boost::optional<EmotePtr> addEmote(
|
static boost::optional<EmotePtr> addEmote(
|
||||||
Atomic<std::shared_ptr<const EmoteMap>> &map,
|
Atomic<std::shared_ptr<const EmoteMap>> &map,
|
||||||
const SeventvEventAPIEmoteAddDispatch &dispatch);
|
const seventv::eventapi::EmoteAddDispatch &dispatch);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates an emote in this `map`.
|
* Updates an emote in this `map`.
|
||||||
|
@ -97,7 +100,7 @@ public:
|
||||||
*/
|
*/
|
||||||
static boost::optional<EmotePtr> updateEmote(
|
static boost::optional<EmotePtr> updateEmote(
|
||||||
Atomic<std::shared_ptr<const EmoteMap>> &map,
|
Atomic<std::shared_ptr<const EmoteMap>> &map,
|
||||||
const SeventvEventAPIEmoteUpdateDispatch &dispatch);
|
const seventv::eventapi::EmoteUpdateDispatch &dispatch);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes an emote from this `map`.
|
* Removes an emote from this `map`.
|
||||||
|
@ -108,7 +111,7 @@ public:
|
||||||
*/
|
*/
|
||||||
static boost::optional<EmotePtr> removeEmote(
|
static boost::optional<EmotePtr> removeEmote(
|
||||||
Atomic<std::shared_ptr<const EmoteMap>> &map,
|
Atomic<std::shared_ptr<const EmoteMap>> &map,
|
||||||
const SeventvEventAPIEmoteRemoveDispatch &dispatch);
|
const seventv::eventapi::EmoteRemoveDispatch &dispatch);
|
||||||
|
|
||||||
/** Fetches an emote-set by its id */
|
/** Fetches an emote-set by its id */
|
||||||
static void getEmoteSet(
|
static void getEmoteSet(
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
#include "providers/seventv/SeventvEventAPI.hpp"
|
#include "providers/seventv/SeventvEventAPI.hpp"
|
||||||
|
|
||||||
#include "providers/seventv/eventapi/SeventvEventAPIClient.hpp"
|
#include "providers/seventv/eventapi/Client.hpp"
|
||||||
#include "providers/seventv/eventapi/SeventvEventAPIDispatch.hpp"
|
#include "providers/seventv/eventapi/Dispatch.hpp"
|
||||||
#include "providers/seventv/eventapi/SeventvEventAPIMessage.hpp"
|
#include "providers/seventv/eventapi/Message.hpp"
|
||||||
|
|
||||||
#include <QJsonArray>
|
#include <QJsonArray>
|
||||||
|
|
||||||
|
@ -10,6 +10,8 @@
|
||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
|
|
||||||
|
using namespace seventv::eventapi;
|
||||||
|
|
||||||
SeventvEventAPI::SeventvEventAPI(
|
SeventvEventAPI::SeventvEventAPI(
|
||||||
QString host, std::chrono::milliseconds defaultHeartbeatInterval)
|
QString host, std::chrono::milliseconds defaultHeartbeatInterval)
|
||||||
: BasicPubSubManager(std::move(host))
|
: BasicPubSubManager(std::move(host))
|
||||||
|
@ -22,13 +24,14 @@ void SeventvEventAPI::subscribeUser(const QString &userID,
|
||||||
{
|
{
|
||||||
if (!userID.isEmpty() && this->subscribedUsers_.insert(userID).second)
|
if (!userID.isEmpty() && this->subscribedUsers_.insert(userID).second)
|
||||||
{
|
{
|
||||||
this->subscribe({userID, SeventvEventAPISubscriptionType::UpdateUser});
|
this->subscribe(
|
||||||
|
{ObjectIDCondition{userID}, SubscriptionType::UpdateUser});
|
||||||
}
|
}
|
||||||
if (!emoteSetID.isEmpty() &&
|
if (!emoteSetID.isEmpty() &&
|
||||||
this->subscribedEmoteSets_.insert(emoteSetID).second)
|
this->subscribedEmoteSets_.insert(emoteSetID).second)
|
||||||
{
|
{
|
||||||
this->subscribe(
|
this->subscribe(
|
||||||
{emoteSetID, SeventvEventAPISubscriptionType::UpdateEmoteSet});
|
{ObjectIDCondition{emoteSetID}, SubscriptionType::UpdateEmoteSet});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,7 +40,7 @@ void SeventvEventAPI::unsubscribeEmoteSet(const QString &id)
|
||||||
if (this->subscribedEmoteSets_.erase(id) > 0)
|
if (this->subscribedEmoteSets_.erase(id) > 0)
|
||||||
{
|
{
|
||||||
this->unsubscribe(
|
this->unsubscribe(
|
||||||
{id, SeventvEventAPISubscriptionType::UpdateEmoteSet});
|
{ObjectIDCondition{id}, SubscriptionType::UpdateEmoteSet});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,27 +48,27 @@ void SeventvEventAPI::unsubscribeUser(const QString &id)
|
||||||
{
|
{
|
||||||
if (this->subscribedUsers_.erase(id) > 0)
|
if (this->subscribedUsers_.erase(id) > 0)
|
||||||
{
|
{
|
||||||
this->unsubscribe({id, SeventvEventAPISubscriptionType::UpdateUser});
|
this->unsubscribe(
|
||||||
|
{ObjectIDCondition{id}, SubscriptionType::UpdateUser});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<BasicPubSubClient<SeventvEventAPISubscription>>
|
std::shared_ptr<BasicPubSubClient<Subscription>> SeventvEventAPI::createClient(
|
||||||
SeventvEventAPI::createClient(liveupdates::WebsocketClient &client,
|
liveupdates::WebsocketClient &client, websocketpp::connection_hdl hdl)
|
||||||
websocketpp::connection_hdl hdl)
|
|
||||||
{
|
{
|
||||||
auto shared = std::make_shared<SeventvEventAPIClient>(
|
auto shared =
|
||||||
client, hdl, this->heartbeatInterval_);
|
std::make_shared<Client>(client, hdl, this->heartbeatInterval_);
|
||||||
return std::static_pointer_cast<
|
return std::static_pointer_cast<BasicPubSubClient<Subscription>>(
|
||||||
BasicPubSubClient<SeventvEventAPISubscription>>(std::move(shared));
|
std::move(shared));
|
||||||
}
|
}
|
||||||
|
|
||||||
void SeventvEventAPI::onMessage(
|
void SeventvEventAPI::onMessage(
|
||||||
websocketpp::connection_hdl hdl,
|
websocketpp::connection_hdl hdl,
|
||||||
BasicPubSubManager<SeventvEventAPISubscription>::WebsocketMessagePtr msg)
|
BasicPubSubManager<Subscription>::WebsocketMessagePtr msg)
|
||||||
{
|
{
|
||||||
const auto &payload = QString::fromStdString(msg->get_payload());
|
const auto &payload = QString::fromStdString(msg->get_payload());
|
||||||
|
|
||||||
auto pMessage = parseSeventvEventAPIBaseMessage(payload);
|
auto pMessage = parseBaseMessage(payload);
|
||||||
|
|
||||||
if (!pMessage)
|
if (!pMessage)
|
||||||
{
|
{
|
||||||
|
@ -76,11 +79,10 @@ void SeventvEventAPI::onMessage(
|
||||||
auto message = *pMessage;
|
auto message = *pMessage;
|
||||||
switch (message.op)
|
switch (message.op)
|
||||||
{
|
{
|
||||||
case SeventvEventAPIOpcode::Hello: {
|
case Opcode::Hello: {
|
||||||
if (auto client = this->findClient(hdl))
|
if (auto client = this->findClient(hdl))
|
||||||
{
|
{
|
||||||
if (auto *stvClient =
|
if (auto *stvClient = dynamic_cast<Client *>(client.get()))
|
||||||
dynamic_cast<SeventvEventAPIClient *>(client.get()))
|
|
||||||
{
|
{
|
||||||
stvClient->setHeartbeatInterval(
|
stvClient->setHeartbeatInterval(
|
||||||
message.data["heartbeat_interval"].toInt());
|
message.data["heartbeat_interval"].toInt());
|
||||||
|
@ -88,19 +90,18 @@ void SeventvEventAPI::onMessage(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case SeventvEventAPIOpcode::Heartbeat: {
|
case Opcode::Heartbeat: {
|
||||||
if (auto client = this->findClient(hdl))
|
if (auto client = this->findClient(hdl))
|
||||||
{
|
{
|
||||||
if (auto *stvClient =
|
if (auto *stvClient = dynamic_cast<Client *>(client.get()))
|
||||||
dynamic_cast<SeventvEventAPIClient *>(client.get()))
|
|
||||||
{
|
{
|
||||||
stvClient->handleHeartbeat();
|
stvClient->handleHeartbeat();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case SeventvEventAPIOpcode::Dispatch: {
|
case Opcode::Dispatch: {
|
||||||
auto dispatch = message.toInner<SeventvEventAPIDispatch>();
|
auto dispatch = message.toInner<Dispatch>();
|
||||||
if (!dispatch)
|
if (!dispatch)
|
||||||
{
|
{
|
||||||
qCDebug(chatterinoSeventvEventAPI)
|
qCDebug(chatterinoSeventvEventAPI)
|
||||||
|
@ -110,17 +111,20 @@ void SeventvEventAPI::onMessage(
|
||||||
this->handleDispatch(*dispatch);
|
this->handleDispatch(*dispatch);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case SeventvEventAPIOpcode::Reconnect: {
|
case Opcode::Reconnect: {
|
||||||
if (auto client = this->findClient(hdl))
|
if (auto client = this->findClient(hdl))
|
||||||
{
|
{
|
||||||
if (auto *stvClient =
|
if (auto *stvClient = dynamic_cast<Client *>(client.get()))
|
||||||
dynamic_cast<SeventvEventAPIClient *>(client.get()))
|
|
||||||
{
|
{
|
||||||
stvClient->close("Reconnecting");
|
stvClient->close("Reconnecting");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case Opcode::Ack: {
|
||||||
|
// unhandled
|
||||||
|
}
|
||||||
|
break;
|
||||||
default: {
|
default: {
|
||||||
qCDebug(chatterinoSeventvEventAPI) << "Unhandled op:" << payload;
|
qCDebug(chatterinoSeventvEventAPI) << "Unhandled op:" << payload;
|
||||||
}
|
}
|
||||||
|
@ -128,11 +132,29 @@ void SeventvEventAPI::onMessage(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SeventvEventAPI::handleDispatch(const SeventvEventAPIDispatch &dispatch)
|
void SeventvEventAPI::handleDispatch(const Dispatch &dispatch)
|
||||||
{
|
{
|
||||||
switch (dispatch.type)
|
switch (dispatch.type)
|
||||||
{
|
{
|
||||||
case SeventvEventAPISubscriptionType::UpdateEmoteSet: {
|
case SubscriptionType::UpdateEmoteSet: {
|
||||||
|
this->onEmoteSetUpdate(dispatch);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SubscriptionType::UpdateUser: {
|
||||||
|
this->onUserUpdate(dispatch);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default: {
|
||||||
|
qCDebug(chatterinoSeventvEventAPI)
|
||||||
|
<< "Unknown subscription type:" << (int)dispatch.type
|
||||||
|
<< "body:" << dispatch.body;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SeventvEventAPI::onEmoteSetUpdate(const Dispatch &dispatch)
|
||||||
|
{
|
||||||
// dispatchBody: {
|
// dispatchBody: {
|
||||||
// pushed: Array<{ key, value }>,
|
// pushed: Array<{ key, value }>,
|
||||||
// pulled: Array<{ key, old_value }>,
|
// pulled: Array<{ key, old_value }>,
|
||||||
|
@ -146,8 +168,7 @@ void SeventvEventAPI::handleDispatch(const SeventvEventAPIDispatch &dispatch)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
SeventvEventAPIEmoteAddDispatch added(
|
const EmoteAddDispatch added(dispatch, pushed["value"].toObject());
|
||||||
dispatch, pushed["value"].toObject());
|
|
||||||
|
|
||||||
if (added.validate())
|
if (added.validate())
|
||||||
{
|
{
|
||||||
|
@ -167,8 +188,8 @@ void SeventvEventAPI::handleDispatch(const SeventvEventAPIDispatch &dispatch)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
SeventvEventAPIEmoteUpdateDispatch update(
|
const EmoteUpdateDispatch update(dispatch,
|
||||||
dispatch, updated["old_value"].toObject(),
|
updated["old_value"].toObject(),
|
||||||
updated["value"].toObject());
|
updated["value"].toObject());
|
||||||
|
|
||||||
if (update.validate())
|
if (update.validate())
|
||||||
|
@ -189,8 +210,8 @@ void SeventvEventAPI::handleDispatch(const SeventvEventAPIDispatch &dispatch)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
SeventvEventAPIEmoteRemoveDispatch removed(
|
const EmoteRemoveDispatch removed(dispatch,
|
||||||
dispatch, pulled["old_value"].toObject());
|
pulled["old_value"].toObject());
|
||||||
|
|
||||||
if (removed.validate())
|
if (removed.validate())
|
||||||
{
|
{
|
||||||
|
@ -203,8 +224,9 @@ void SeventvEventAPI::handleDispatch(const SeventvEventAPIDispatch &dispatch)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
case SeventvEventAPISubscriptionType::UpdateUser: {
|
void SeventvEventAPI::onUserUpdate(const Dispatch &dispatch)
|
||||||
|
{
|
||||||
// dispatchBody: {
|
// dispatchBody: {
|
||||||
// updated: Array<{ key, value: Array<{key, value}> }>
|
// updated: Array<{ key, value: Array<{key, value}> }>
|
||||||
// }
|
// }
|
||||||
|
@ -223,7 +245,7 @@ void SeventvEventAPI::handleDispatch(const SeventvEventAPIDispatch &dispatch)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
SeventvEventAPIUserConnectionUpdateDispatch update(
|
const UserConnectionUpdateDispatch update(
|
||||||
dispatch, value, (size_t)updated["index"].toInt());
|
dispatch, value, (size_t)updated["index"].toInt());
|
||||||
|
|
||||||
if (update.validate())
|
if (update.validate())
|
||||||
|
@ -238,14 +260,5 @@ void SeventvEventAPI::handleDispatch(const SeventvEventAPIDispatch &dispatch)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
default: {
|
|
||||||
qCDebug(chatterinoSeventvEventAPI)
|
|
||||||
<< "Unknown subscription type:" << (int)dispatch.type
|
|
||||||
<< "body:" << dispatch.body;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace chatterino
|
} // namespace chatterino
|
||||||
|
|
|
@ -8,14 +8,17 @@
|
||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
|
|
||||||
struct SeventvEventAPISubscription;
|
namespace seventv::eventapi {
|
||||||
struct SeventvEventAPIDispatch;
|
struct Subscription;
|
||||||
struct SeventvEventAPIEmoteAddDispatch;
|
struct Dispatch;
|
||||||
struct SeventvEventAPIEmoteUpdateDispatch;
|
struct EmoteAddDispatch;
|
||||||
struct SeventvEventAPIEmoteRemoveDispatch;
|
struct EmoteUpdateDispatch;
|
||||||
struct SeventvEventAPIUserConnectionUpdateDispatch;
|
struct EmoteRemoveDispatch;
|
||||||
|
struct UserConnectionUpdateDispatch;
|
||||||
|
} // namespace seventv::eventapi
|
||||||
|
|
||||||
class SeventvEventAPI : public BasicPubSubManager<SeventvEventAPISubscription>
|
class SeventvEventAPI
|
||||||
|
: public BasicPubSubManager<seventv::eventapi::Subscription>
|
||||||
{
|
{
|
||||||
template <typename T>
|
template <typename T>
|
||||||
using Signal =
|
using Signal =
|
||||||
|
@ -27,10 +30,10 @@ public:
|
||||||
std::chrono::milliseconds(25000));
|
std::chrono::milliseconds(25000));
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
Signal<SeventvEventAPIEmoteAddDispatch> emoteAdded;
|
Signal<seventv::eventapi::EmoteAddDispatch> emoteAdded;
|
||||||
Signal<SeventvEventAPIEmoteUpdateDispatch> emoteUpdated;
|
Signal<seventv::eventapi::EmoteUpdateDispatch> emoteUpdated;
|
||||||
Signal<SeventvEventAPIEmoteRemoveDispatch> emoteRemoved;
|
Signal<seventv::eventapi::EmoteRemoveDispatch> emoteRemoved;
|
||||||
Signal<SeventvEventAPIUserConnectionUpdateDispatch> userUpdated;
|
Signal<seventv::eventapi::UserConnectionUpdateDispatch> userUpdated;
|
||||||
} signals_; // NOLINT(readability-identifier-naming)
|
} signals_; // NOLINT(readability-identifier-naming)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -48,18 +51,23 @@ public:
|
||||||
void unsubscribeEmoteSet(const QString &id);
|
void unsubscribeEmoteSet(const QString &id);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::shared_ptr<BasicPubSubClient<SeventvEventAPISubscription>>
|
std::shared_ptr<BasicPubSubClient<seventv::eventapi::Subscription>>
|
||||||
createClient(liveupdates::WebsocketClient &client,
|
createClient(liveupdates::WebsocketClient &client,
|
||||||
websocketpp::connection_hdl hdl) override;
|
websocketpp::connection_hdl hdl) override;
|
||||||
void onMessage(
|
void onMessage(
|
||||||
websocketpp::connection_hdl hdl,
|
websocketpp::connection_hdl hdl,
|
||||||
BasicPubSubManager<SeventvEventAPISubscription>::WebsocketMessagePtr
|
BasicPubSubManager<seventv::eventapi::Subscription>::WebsocketMessagePtr
|
||||||
msg) override;
|
msg) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void handleDispatch(const SeventvEventAPIDispatch &dispatch);
|
void handleDispatch(const seventv::eventapi::Dispatch &dispatch);
|
||||||
|
|
||||||
|
void onEmoteSetUpdate(const seventv::eventapi::Dispatch &dispatch);
|
||||||
|
void onUserUpdate(const seventv::eventapi::Dispatch &dispatch);
|
||||||
|
|
||||||
|
/** emote-set ids */
|
||||||
std::unordered_set<QString> subscribedEmoteSets_;
|
std::unordered_set<QString> subscribedEmoteSets_;
|
||||||
|
/** user ids */
|
||||||
std::unordered_set<QString> subscribedUsers_;
|
std::unordered_set<QString> subscribedUsers_;
|
||||||
std::chrono::milliseconds heartbeatInterval_;
|
std::chrono::milliseconds heartbeatInterval_;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,44 +1,42 @@
|
||||||
#include "providers/seventv/eventapi/SeventvEventAPIClient.hpp"
|
#include "providers/seventv/eventapi/Client.hpp"
|
||||||
|
|
||||||
#include "providers/seventv/eventapi/SeventvEventAPISubscription.hpp"
|
#include "providers/seventv/eventapi/Subscription.hpp"
|
||||||
#include "providers/twitch/PubSubHelpers.hpp"
|
#include "providers/twitch/PubSubHelpers.hpp"
|
||||||
|
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino::seventv::eventapi {
|
||||||
|
|
||||||
SeventvEventAPIClient::SeventvEventAPIClient(
|
Client::Client(liveupdates::WebsocketClient &websocketClient,
|
||||||
liveupdates::WebsocketClient &websocketClient,
|
|
||||||
liveupdates::WebsocketHandle handle,
|
liveupdates::WebsocketHandle handle,
|
||||||
std::chrono::milliseconds heartbeatInterval)
|
std::chrono::milliseconds heartbeatInterval)
|
||||||
: BasicPubSubClient<SeventvEventAPISubscription>(websocketClient,
|
: BasicPubSubClient<Subscription>(websocketClient, std::move(handle))
|
||||||
std::move(handle))
|
|
||||||
, lastHeartbeat_(std::chrono::steady_clock::now())
|
, lastHeartbeat_(std::chrono::steady_clock::now())
|
||||||
, heartbeatInterval_(heartbeatInterval)
|
, heartbeatInterval_(heartbeatInterval)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void SeventvEventAPIClient::onConnectionEstablished()
|
void Client::onConnectionEstablished()
|
||||||
{
|
{
|
||||||
this->lastHeartbeat_.store(std::chrono::steady_clock::now(),
|
this->lastHeartbeat_.store(std::chrono::steady_clock::now(),
|
||||||
std::memory_order_release);
|
std::memory_order_release);
|
||||||
this->checkHeartbeat();
|
this->checkHeartbeat();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SeventvEventAPIClient::setHeartbeatInterval(int intervalMs)
|
void Client::setHeartbeatInterval(int intervalMs)
|
||||||
{
|
{
|
||||||
qCDebug(chatterinoSeventvEventAPI)
|
qCDebug(chatterinoSeventvEventAPI)
|
||||||
<< "Setting expected heartbeat interval to" << intervalMs << "ms";
|
<< "Setting expected heartbeat interval to" << intervalMs << "ms";
|
||||||
this->heartbeatInterval_ = std::chrono::milliseconds(intervalMs);
|
this->heartbeatInterval_ = std::chrono::milliseconds(intervalMs);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SeventvEventAPIClient::handleHeartbeat()
|
void Client::handleHeartbeat()
|
||||||
{
|
{
|
||||||
this->lastHeartbeat_.store(std::chrono::steady_clock::now(),
|
this->lastHeartbeat_.store(std::chrono::steady_clock::now(),
|
||||||
std::memory_order_release);
|
std::memory_order_release);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SeventvEventAPIClient::checkHeartbeat()
|
void Client::checkHeartbeat()
|
||||||
{
|
{
|
||||||
// Following the heartbeat docs, a connection is dead
|
// Following the heartbeat docs, a connection is dead
|
||||||
// after three missed heartbeats.
|
// after three missed heartbeats.
|
||||||
|
@ -54,8 +52,7 @@ void SeventvEventAPIClient::checkHeartbeat()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto self = std::dynamic_pointer_cast<SeventvEventAPIClient>(
|
auto self = std::dynamic_pointer_cast<Client>(this->shared_from_this());
|
||||||
this->shared_from_this());
|
|
||||||
|
|
||||||
runAfter(this->websocketClient_.get_io_service(), this->heartbeatInterval_,
|
runAfter(this->websocketClient_.get_io_service(), this->heartbeatInterval_,
|
||||||
[self](auto) {
|
[self](auto) {
|
||||||
|
@ -67,4 +64,4 @@ void SeventvEventAPIClient::checkHeartbeat()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace chatterino
|
} // namespace chatterino::seventv::eventapi
|
|
@ -2,16 +2,20 @@
|
||||||
|
|
||||||
#include "providers/liveupdates/BasicPubSubClient.hpp"
|
#include "providers/liveupdates/BasicPubSubClient.hpp"
|
||||||
// this needs to be included for the specialization
|
// this needs to be included for the specialization
|
||||||
// of std::hash for SeventvEventAPISubscription
|
// of std::hash for Subscription
|
||||||
#include "providers/seventv/eventapi/SeventvEventAPISubscription.hpp"
|
#include "providers/seventv/eventapi/Subscription.hpp"
|
||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
|
class SeventvEventAPI;
|
||||||
|
|
||||||
class SeventvEventAPIClient
|
} // namespace chatterino
|
||||||
: public BasicPubSubClient<SeventvEventAPISubscription>
|
|
||||||
|
namespace chatterino::seventv::eventapi {
|
||||||
|
|
||||||
|
class Client : public BasicPubSubClient<Subscription>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
SeventvEventAPIClient(liveupdates::WebsocketClient &websocketClient,
|
Client(liveupdates::WebsocketClient &websocketClient,
|
||||||
liveupdates::WebsocketHandle handle,
|
liveupdates::WebsocketHandle handle,
|
||||||
std::chrono::milliseconds heartbeatInterval);
|
std::chrono::milliseconds heartbeatInterval);
|
||||||
|
|
||||||
|
@ -29,7 +33,7 @@ private:
|
||||||
// This will be set once on the welcome message.
|
// This will be set once on the welcome message.
|
||||||
std::chrono::milliseconds heartbeatInterval_;
|
std::chrono::milliseconds heartbeatInterval_;
|
||||||
|
|
||||||
friend class SeventvEventAPI;
|
friend SeventvEventAPI;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace chatterino
|
} // namespace chatterino::seventv::eventapi
|
|
@ -1,21 +1,20 @@
|
||||||
#include "providers/seventv/eventapi/SeventvEventAPIDispatch.hpp"
|
#include "providers/seventv/eventapi/Dispatch.hpp"
|
||||||
|
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino::seventv::eventapi {
|
||||||
|
|
||||||
SeventvEventAPIDispatch::SeventvEventAPIDispatch(QJsonObject obj)
|
Dispatch::Dispatch(QJsonObject obj)
|
||||||
: type(magic_enum::enum_cast<SeventvEventAPISubscriptionType>(
|
: type(magic_enum::enum_cast<SubscriptionType>(
|
||||||
obj["type"].toString().toStdString())
|
obj["type"].toString().toStdString())
|
||||||
.value_or(SeventvEventAPISubscriptionType::INVALID))
|
.value_or(SubscriptionType::INVALID))
|
||||||
, body(obj["body"].toObject())
|
, body(obj["body"].toObject())
|
||||||
, id(this->body["id"].toString())
|
, id(this->body["id"].toString())
|
||||||
, actorName(this->body["actor"].toObject()["display_name"].toString())
|
, actorName(this->body["actor"].toObject()["display_name"].toString())
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
SeventvEventAPIEmoteAddDispatch::SeventvEventAPIEmoteAddDispatch(
|
EmoteAddDispatch::EmoteAddDispatch(const Dispatch &dispatch, QJsonObject emote)
|
||||||
const SeventvEventAPIDispatch &dispatch, QJsonObject emote)
|
|
||||||
: emoteSetID(dispatch.id)
|
: emoteSetID(dispatch.id)
|
||||||
, actorName(dispatch.actorName)
|
, actorName(dispatch.actorName)
|
||||||
, emoteJson(std::move(emote))
|
, emoteJson(std::move(emote))
|
||||||
|
@ -23,7 +22,7 @@ SeventvEventAPIEmoteAddDispatch::SeventvEventAPIEmoteAddDispatch(
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SeventvEventAPIEmoteAddDispatch::validate() const
|
bool EmoteAddDispatch::validate() const
|
||||||
{
|
{
|
||||||
bool validValues =
|
bool validValues =
|
||||||
!this->emoteSetID.isEmpty() && !this->emoteJson.isEmpty();
|
!this->emoteSetID.isEmpty() && !this->emoteJson.isEmpty();
|
||||||
|
@ -43,8 +42,8 @@ bool SeventvEventAPIEmoteAddDispatch::validate() const
|
||||||
emoteData.contains("owner");
|
emoteData.contains("owner");
|
||||||
}
|
}
|
||||||
|
|
||||||
SeventvEventAPIEmoteRemoveDispatch::SeventvEventAPIEmoteRemoveDispatch(
|
EmoteRemoveDispatch::EmoteRemoveDispatch(const Dispatch &dispatch,
|
||||||
const SeventvEventAPIDispatch &dispatch, QJsonObject emote)
|
QJsonObject emote)
|
||||||
: emoteSetID(dispatch.id)
|
: emoteSetID(dispatch.id)
|
||||||
, actorName(dispatch.actorName)
|
, actorName(dispatch.actorName)
|
||||||
, emoteName(emote["name"].toString())
|
, emoteName(emote["name"].toString())
|
||||||
|
@ -52,14 +51,14 @@ SeventvEventAPIEmoteRemoveDispatch::SeventvEventAPIEmoteRemoveDispatch(
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SeventvEventAPIEmoteRemoveDispatch::validate() const
|
bool EmoteRemoveDispatch::validate() const
|
||||||
{
|
{
|
||||||
return !this->emoteSetID.isEmpty() && !this->emoteName.isEmpty() &&
|
return !this->emoteSetID.isEmpty() && !this->emoteName.isEmpty() &&
|
||||||
!this->emoteID.isEmpty();
|
!this->emoteID.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
SeventvEventAPIEmoteUpdateDispatch::SeventvEventAPIEmoteUpdateDispatch(
|
EmoteUpdateDispatch::EmoteUpdateDispatch(const Dispatch &dispatch,
|
||||||
const SeventvEventAPIDispatch &dispatch, QJsonObject oldValue,
|
QJsonObject oldValue,
|
||||||
QJsonObject value)
|
QJsonObject value)
|
||||||
: emoteSetID(dispatch.id)
|
: emoteSetID(dispatch.id)
|
||||||
, actorName(dispatch.actorName)
|
, actorName(dispatch.actorName)
|
||||||
|
@ -69,17 +68,15 @@ SeventvEventAPIEmoteUpdateDispatch::SeventvEventAPIEmoteUpdateDispatch(
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SeventvEventAPIEmoteUpdateDispatch::validate() const
|
bool EmoteUpdateDispatch::validate() const
|
||||||
{
|
{
|
||||||
return !this->emoteSetID.isEmpty() && !this->emoteID.isEmpty() &&
|
return !this->emoteSetID.isEmpty() && !this->emoteID.isEmpty() &&
|
||||||
!this->oldEmoteName.isEmpty() && !this->emoteName.isEmpty() &&
|
!this->oldEmoteName.isEmpty() && !this->emoteName.isEmpty() &&
|
||||||
this->oldEmoteName != this->emoteName;
|
this->oldEmoteName != this->emoteName;
|
||||||
}
|
}
|
||||||
|
|
||||||
SeventvEventAPIUserConnectionUpdateDispatch::
|
UserConnectionUpdateDispatch::UserConnectionUpdateDispatch(
|
||||||
SeventvEventAPIUserConnectionUpdateDispatch(
|
const Dispatch &dispatch, const QJsonObject &update, size_t connectionIndex)
|
||||||
const SeventvEventAPIDispatch &dispatch, const QJsonObject &update,
|
|
||||||
size_t connectionIndex)
|
|
||||||
: userID(dispatch.id)
|
: userID(dispatch.id)
|
||||||
, actorName(dispatch.actorName)
|
, actorName(dispatch.actorName)
|
||||||
, oldEmoteSetID(update["old_value"].toObject()["id"].toString())
|
, oldEmoteSetID(update["old_value"].toObject()["id"].toString())
|
||||||
|
@ -88,10 +85,10 @@ SeventvEventAPIUserConnectionUpdateDispatch::
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SeventvEventAPIUserConnectionUpdateDispatch::validate() const
|
bool UserConnectionUpdateDispatch::validate() const
|
||||||
{
|
{
|
||||||
return !this->userID.isEmpty() && !this->oldEmoteSetID.isEmpty() &&
|
return !this->userID.isEmpty() && !this->oldEmoteSetID.isEmpty() &&
|
||||||
!this->emoteSetID.isEmpty();
|
!this->emoteSetID.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace chatterino
|
} // namespace chatterino::seventv::eventapi
|
70
src/providers/seventv/eventapi/Dispatch.hpp
Normal file
70
src/providers/seventv/eventapi/Dispatch.hpp
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "providers/seventv/eventapi/Subscription.hpp"
|
||||||
|
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
namespace chatterino::seventv::eventapi {
|
||||||
|
|
||||||
|
// https://github.com/SevenTV/EventAPI/tree/ca4ff15cc42b89560fa661a76c5849047763d334#message-payload
|
||||||
|
struct Dispatch {
|
||||||
|
SubscriptionType type;
|
||||||
|
QJsonObject body;
|
||||||
|
QString id;
|
||||||
|
// it's okay for this to be empty
|
||||||
|
QString actorName;
|
||||||
|
|
||||||
|
Dispatch(QJsonObject obj);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct EmoteAddDispatch {
|
||||||
|
QString emoteSetID;
|
||||||
|
QString actorName;
|
||||||
|
QJsonObject emoteJson;
|
||||||
|
QString emoteID;
|
||||||
|
|
||||||
|
EmoteAddDispatch(const Dispatch &dispatch, QJsonObject emote);
|
||||||
|
|
||||||
|
bool validate() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct EmoteRemoveDispatch {
|
||||||
|
QString emoteSetID;
|
||||||
|
QString actorName;
|
||||||
|
QString emoteName;
|
||||||
|
QString emoteID;
|
||||||
|
|
||||||
|
EmoteRemoveDispatch(const Dispatch &dispatch, QJsonObject emote);
|
||||||
|
|
||||||
|
bool validate() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct EmoteUpdateDispatch {
|
||||||
|
QString emoteSetID;
|
||||||
|
QString actorName;
|
||||||
|
QString emoteID;
|
||||||
|
QString oldEmoteName;
|
||||||
|
QString emoteName;
|
||||||
|
|
||||||
|
EmoteUpdateDispatch(const Dispatch &dispatch, QJsonObject oldValue,
|
||||||
|
QJsonObject value);
|
||||||
|
|
||||||
|
bool validate() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct UserConnectionUpdateDispatch {
|
||||||
|
QString userID;
|
||||||
|
QString actorName;
|
||||||
|
QString oldEmoteSetID;
|
||||||
|
QString emoteSetID;
|
||||||
|
size_t connectionIndex;
|
||||||
|
|
||||||
|
UserConnectionUpdateDispatch(const Dispatch &dispatch,
|
||||||
|
const QJsonObject &update,
|
||||||
|
size_t connectionIndex);
|
||||||
|
|
||||||
|
bool validate() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace chatterino::seventv::eventapi
|
11
src/providers/seventv/eventapi/Message.cpp
Normal file
11
src/providers/seventv/eventapi/Message.cpp
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
#include "providers/seventv/eventapi/Message.hpp"
|
||||||
|
|
||||||
|
namespace chatterino::seventv::eventapi {
|
||||||
|
|
||||||
|
Message::Message(QJsonObject _json)
|
||||||
|
: data(_json["d"].toObject())
|
||||||
|
, op(Opcode(_json["op"].toInt()))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace chatterino::seventv::eventapi
|
|
@ -1,6 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "providers/seventv/eventapi/SeventvEventAPISubscription.hpp"
|
#include "providers/seventv/eventapi/Subscription.hpp"
|
||||||
|
|
||||||
#include <boost/optional.hpp>
|
#include <boost/optional.hpp>
|
||||||
#include <magic_enum.hpp>
|
#include <magic_enum.hpp>
|
||||||
|
@ -8,27 +8,26 @@
|
||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino::seventv::eventapi {
|
||||||
|
|
||||||
struct SeventvEventAPIMessage {
|
struct Message {
|
||||||
QJsonObject data;
|
QJsonObject data;
|
||||||
|
|
||||||
SeventvEventAPIOpcode op;
|
Opcode op;
|
||||||
|
|
||||||
SeventvEventAPIMessage(QJsonObject _json);
|
Message(QJsonObject _json);
|
||||||
|
|
||||||
template <class InnerClass>
|
template <class InnerClass>
|
||||||
boost::optional<InnerClass> toInner();
|
boost::optional<InnerClass> toInner();
|
||||||
};
|
};
|
||||||
|
|
||||||
template <class InnerClass>
|
template <class InnerClass>
|
||||||
boost::optional<InnerClass> SeventvEventAPIMessage::toInner()
|
boost::optional<InnerClass> Message::toInner()
|
||||||
{
|
{
|
||||||
return InnerClass{this->data};
|
return InnerClass{this->data};
|
||||||
}
|
}
|
||||||
|
|
||||||
static boost::optional<SeventvEventAPIMessage> parseSeventvEventAPIBaseMessage(
|
static boost::optional<Message> parseBaseMessage(const QString &blob)
|
||||||
const QString &blob)
|
|
||||||
{
|
{
|
||||||
QJsonDocument jsonDoc(QJsonDocument::fromJson(blob.toUtf8()));
|
QJsonDocument jsonDoc(QJsonDocument::fromJson(blob.toUtf8()));
|
||||||
|
|
||||||
|
@ -37,7 +36,7 @@ static boost::optional<SeventvEventAPIMessage> parseSeventvEventAPIBaseMessage(
|
||||||
return boost::none;
|
return boost::none;
|
||||||
}
|
}
|
||||||
|
|
||||||
return SeventvEventAPIMessage(jsonDoc.object());
|
return Message(jsonDoc.object());
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace chatterino
|
} // namespace chatterino::seventv::eventapi
|
|
@ -1,72 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "providers/seventv/eventapi/SeventvEventAPISubscription.hpp"
|
|
||||||
|
|
||||||
#include <QJsonObject>
|
|
||||||
#include <QString>
|
|
||||||
|
|
||||||
namespace chatterino {
|
|
||||||
|
|
||||||
// https://github.com/SevenTV/EventAPI/tree/ca4ff15cc42b89560fa661a76c5849047763d334#message-payload
|
|
||||||
struct SeventvEventAPIDispatch {
|
|
||||||
SeventvEventAPISubscriptionType type;
|
|
||||||
QJsonObject body;
|
|
||||||
QString id;
|
|
||||||
// it's okay for this to be empty
|
|
||||||
QString actorName;
|
|
||||||
|
|
||||||
SeventvEventAPIDispatch(QJsonObject obj);
|
|
||||||
};
|
|
||||||
|
|
||||||
struct SeventvEventAPIEmoteAddDispatch {
|
|
||||||
QString emoteSetID;
|
|
||||||
QString actorName;
|
|
||||||
QJsonObject emoteJson;
|
|
||||||
QString emoteID;
|
|
||||||
|
|
||||||
SeventvEventAPIEmoteAddDispatch(const SeventvEventAPIDispatch &dispatch,
|
|
||||||
QJsonObject emote);
|
|
||||||
|
|
||||||
bool validate() const;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct SeventvEventAPIEmoteRemoveDispatch {
|
|
||||||
QString emoteSetID;
|
|
||||||
QString actorName;
|
|
||||||
QString emoteName;
|
|
||||||
QString emoteID;
|
|
||||||
|
|
||||||
SeventvEventAPIEmoteRemoveDispatch(const SeventvEventAPIDispatch &dispatch,
|
|
||||||
QJsonObject emote);
|
|
||||||
|
|
||||||
bool validate() const;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct SeventvEventAPIEmoteUpdateDispatch {
|
|
||||||
QString emoteSetID;
|
|
||||||
QString actorName;
|
|
||||||
QString emoteID;
|
|
||||||
QString oldEmoteName;
|
|
||||||
QString emoteName;
|
|
||||||
|
|
||||||
SeventvEventAPIEmoteUpdateDispatch(const SeventvEventAPIDispatch &dispatch,
|
|
||||||
QJsonObject oldValue, QJsonObject value);
|
|
||||||
|
|
||||||
bool validate() const;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct SeventvEventAPIUserConnectionUpdateDispatch {
|
|
||||||
QString userID;
|
|
||||||
QString actorName;
|
|
||||||
QString oldEmoteSetID;
|
|
||||||
QString emoteSetID;
|
|
||||||
size_t connectionIndex;
|
|
||||||
|
|
||||||
SeventvEventAPIUserConnectionUpdateDispatch(
|
|
||||||
const SeventvEventAPIDispatch &dispatch, const QJsonObject &update,
|
|
||||||
size_t connectionIndex);
|
|
||||||
|
|
||||||
bool validate() const;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace chatterino
|
|
|
@ -1,11 +0,0 @@
|
||||||
#include "providers/seventv/eventapi/SeventvEventAPIMessage.hpp"
|
|
||||||
|
|
||||||
namespace chatterino {
|
|
||||||
|
|
||||||
SeventvEventAPIMessage::SeventvEventAPIMessage(QJsonObject _json)
|
|
||||||
: data(_json["d"].toObject())
|
|
||||||
, op(SeventvEventAPIOpcode(_json["op"].toInt()))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace chatterino
|
|
|
@ -1,80 +0,0 @@
|
||||||
#include "providers/seventv/eventapi/SeventvEventAPISubscription.hpp"
|
|
||||||
|
|
||||||
#include <QDebug>
|
|
||||||
#include <QJsonDocument>
|
|
||||||
#include <QJsonObject>
|
|
||||||
|
|
||||||
#include <tuple>
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
using namespace chatterino;
|
|
||||||
|
|
||||||
const char *typeToString(SeventvEventAPISubscriptionType type)
|
|
||||||
{
|
|
||||||
switch (type)
|
|
||||||
{
|
|
||||||
case SeventvEventAPISubscriptionType::UpdateEmoteSet:
|
|
||||||
return "emote_set.update";
|
|
||||||
case SeventvEventAPISubscriptionType::UpdateUser:
|
|
||||||
return "user.update";
|
|
||||||
default:
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QJsonObject createDataJson(const char *typeName, const QString &condition)
|
|
||||||
{
|
|
||||||
QJsonObject data;
|
|
||||||
data["type"] = typeName;
|
|
||||||
{
|
|
||||||
QJsonObject conditionObj;
|
|
||||||
conditionObj["object_id"] = condition;
|
|
||||||
data["condition"] = conditionObj;
|
|
||||||
}
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
namespace chatterino {
|
|
||||||
|
|
||||||
bool SeventvEventAPISubscription::operator==(
|
|
||||||
const SeventvEventAPISubscription &rhs) const
|
|
||||||
{
|
|
||||||
return std::tie(this->condition, this->type) ==
|
|
||||||
std::tie(rhs.condition, rhs.type);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SeventvEventAPISubscription::operator!=(
|
|
||||||
const SeventvEventAPISubscription &rhs) const
|
|
||||||
{
|
|
||||||
return !(rhs == *this);
|
|
||||||
}
|
|
||||||
|
|
||||||
QByteArray SeventvEventAPISubscription::encodeSubscribe() const
|
|
||||||
{
|
|
||||||
const auto *typeName = typeToString(this->type);
|
|
||||||
QJsonObject root;
|
|
||||||
root["op"] = (int)SeventvEventAPIOpcode::Subscribe;
|
|
||||||
root["d"] = createDataJson(typeName, this->condition);
|
|
||||||
return QJsonDocument(root).toJson();
|
|
||||||
}
|
|
||||||
|
|
||||||
QByteArray SeventvEventAPISubscription::encodeUnsubscribe() const
|
|
||||||
{
|
|
||||||
const auto *typeName = typeToString(this->type);
|
|
||||||
QJsonObject root;
|
|
||||||
root["op"] = (int)SeventvEventAPIOpcode::Unsubscribe;
|
|
||||||
root["d"] = createDataJson(typeName, this->condition);
|
|
||||||
return QJsonDocument(root).toJson();
|
|
||||||
}
|
|
||||||
|
|
||||||
QDebug &operator<<(QDebug &dbg, const SeventvEventAPISubscription &subscription)
|
|
||||||
{
|
|
||||||
dbg << "SeventvEventAPISubscription{ condition:" << subscription.condition
|
|
||||||
<< "type:" << (int)subscription.type << '}';
|
|
||||||
return dbg;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace chatterino
|
|
|
@ -1,76 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <magic_enum.hpp>
|
|
||||||
#include <QByteArray>
|
|
||||||
#include <QHash>
|
|
||||||
#include <QString>
|
|
||||||
|
|
||||||
namespace chatterino {
|
|
||||||
|
|
||||||
// https://github.com/SevenTV/EventAPI/tree/ca4ff15cc42b89560fa661a76c5849047763d334#subscription-types
|
|
||||||
enum class SeventvEventAPISubscriptionType {
|
|
||||||
UpdateEmoteSet,
|
|
||||||
UpdateUser,
|
|
||||||
|
|
||||||
INVALID,
|
|
||||||
};
|
|
||||||
|
|
||||||
// https://github.com/SevenTV/EventAPI/tree/ca4ff15cc42b89560fa661a76c5849047763d334#opcodes
|
|
||||||
enum class SeventvEventAPIOpcode {
|
|
||||||
Dispatch = 0,
|
|
||||||
Hello = 1,
|
|
||||||
Heartbeat = 2,
|
|
||||||
Reconnect = 4,
|
|
||||||
Ack = 5,
|
|
||||||
Error = 6,
|
|
||||||
EndOfStream = 7,
|
|
||||||
Identify = 33,
|
|
||||||
Resume = 34,
|
|
||||||
Subscribe = 35,
|
|
||||||
Unsubscribe = 36,
|
|
||||||
Signal = 37,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct SeventvEventAPISubscription {
|
|
||||||
bool operator==(const SeventvEventAPISubscription &rhs) const;
|
|
||||||
bool operator!=(const SeventvEventAPISubscription &rhs) const;
|
|
||||||
QString condition;
|
|
||||||
SeventvEventAPISubscriptionType type;
|
|
||||||
|
|
||||||
QByteArray encodeSubscribe() const;
|
|
||||||
QByteArray encodeUnsubscribe() const;
|
|
||||||
|
|
||||||
friend QDebug &operator<<(QDebug &dbg,
|
|
||||||
const SeventvEventAPISubscription &subscription);
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace chatterino
|
|
||||||
|
|
||||||
template <>
|
|
||||||
constexpr magic_enum::customize::customize_t magic_enum::customize::enum_name<
|
|
||||||
chatterino::SeventvEventAPISubscriptionType>(
|
|
||||||
chatterino::SeventvEventAPISubscriptionType value) noexcept
|
|
||||||
{
|
|
||||||
switch (value)
|
|
||||||
{
|
|
||||||
case chatterino::SeventvEventAPISubscriptionType::UpdateEmoteSet:
|
|
||||||
return "emote_set.update";
|
|
||||||
case chatterino::SeventvEventAPISubscriptionType::UpdateUser:
|
|
||||||
return "user.update";
|
|
||||||
|
|
||||||
default:
|
|
||||||
return default_tag;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace std {
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct hash<chatterino::SeventvEventAPISubscription> {
|
|
||||||
size_t operator()(const chatterino::SeventvEventAPISubscription &sub) const
|
|
||||||
{
|
|
||||||
return (size_t)qHash(sub.condition, qHash((int)sub.type));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace std
|
|
105
src/providers/seventv/eventapi/Subscription.cpp
Normal file
105
src/providers/seventv/eventapi/Subscription.cpp
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
#include "providers/seventv/eventapi/Subscription.hpp"
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QJsonObject>
|
||||||
|
|
||||||
|
#include <tuple>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
using namespace chatterino::seventv::eventapi;
|
||||||
|
|
||||||
|
const char *typeToString(SubscriptionType type)
|
||||||
|
{
|
||||||
|
return magic_enum::enum_name(type).data();
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject createDataJson(const char *typeName, const Condition &condition)
|
||||||
|
{
|
||||||
|
QJsonObject data;
|
||||||
|
data["type"] = typeName;
|
||||||
|
data["condition"] = std::visit(
|
||||||
|
[](const auto &c) {
|
||||||
|
return c.encode();
|
||||||
|
},
|
||||||
|
condition);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace chatterino::seventv::eventapi {
|
||||||
|
|
||||||
|
bool Subscription::operator==(const Subscription &rhs) const
|
||||||
|
{
|
||||||
|
return std::tie(this->condition, this->type) ==
|
||||||
|
std::tie(rhs.condition, rhs.type);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Subscription::operator!=(const Subscription &rhs) const
|
||||||
|
{
|
||||||
|
return !(rhs == *this);
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray Subscription::encodeSubscribe() const
|
||||||
|
{
|
||||||
|
const auto *typeName = typeToString(this->type);
|
||||||
|
QJsonObject root;
|
||||||
|
root["op"] = (int)Opcode::Subscribe;
|
||||||
|
root["d"] = createDataJson(typeName, this->condition);
|
||||||
|
return QJsonDocument(root).toJson();
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray Subscription::encodeUnsubscribe() const
|
||||||
|
{
|
||||||
|
const auto *typeName = typeToString(this->type);
|
||||||
|
QJsonObject root;
|
||||||
|
root["op"] = (int)Opcode::Unsubscribe;
|
||||||
|
root["d"] = createDataJson(typeName, this->condition);
|
||||||
|
return QJsonDocument(root).toJson();
|
||||||
|
}
|
||||||
|
|
||||||
|
QDebug &operator<<(QDebug &dbg, const Subscription &subscription)
|
||||||
|
{
|
||||||
|
std::visit(
|
||||||
|
[&](const auto &cond) {
|
||||||
|
dbg << "Subscription{ condition:" << cond
|
||||||
|
<< "type:" << magic_enum::enum_name(subscription.type).data()
|
||||||
|
<< '}';
|
||||||
|
},
|
||||||
|
subscription.condition);
|
||||||
|
return dbg;
|
||||||
|
}
|
||||||
|
|
||||||
|
ObjectIDCondition::ObjectIDCondition(QString objectID)
|
||||||
|
: objectID(std::move(objectID))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject ObjectIDCondition::encode() const
|
||||||
|
{
|
||||||
|
QJsonObject obj;
|
||||||
|
obj["object_id"] = this->objectID;
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ObjectIDCondition::operator==(const ObjectIDCondition &rhs) const
|
||||||
|
{
|
||||||
|
return this->objectID == rhs.objectID;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ObjectIDCondition::operator!=(const ObjectIDCondition &rhs) const
|
||||||
|
{
|
||||||
|
return !(*this == rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
QDebug &operator<<(QDebug &dbg, const ObjectIDCondition &condition)
|
||||||
|
{
|
||||||
|
dbg << "{ objectID:" << condition.objectID << "}";
|
||||||
|
return dbg;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace chatterino::seventv::eventapi
|
106
src/providers/seventv/eventapi/Subscription.hpp
Normal file
106
src/providers/seventv/eventapi/Subscription.hpp
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <magic_enum.hpp>
|
||||||
|
#include <QByteArray>
|
||||||
|
#include <QHash>
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
#include <variant>
|
||||||
|
|
||||||
|
namespace chatterino::seventv::eventapi {
|
||||||
|
|
||||||
|
// https://github.com/SevenTV/EventAPI/tree/ca4ff15cc42b89560fa661a76c5849047763d334#subscription-types
|
||||||
|
enum class SubscriptionType {
|
||||||
|
UpdateEmoteSet,
|
||||||
|
UpdateUser,
|
||||||
|
|
||||||
|
INVALID,
|
||||||
|
};
|
||||||
|
|
||||||
|
// https://github.com/SevenTV/EventAPI/tree/ca4ff15cc42b89560fa661a76c5849047763d334#opcodes
|
||||||
|
enum class Opcode {
|
||||||
|
Dispatch = 0,
|
||||||
|
Hello = 1,
|
||||||
|
Heartbeat = 2,
|
||||||
|
Reconnect = 4,
|
||||||
|
Ack = 5,
|
||||||
|
Error = 6,
|
||||||
|
EndOfStream = 7,
|
||||||
|
Identify = 33,
|
||||||
|
Resume = 34,
|
||||||
|
Subscribe = 35,
|
||||||
|
Unsubscribe = 36,
|
||||||
|
Signal = 37,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ObjectIDCondition {
|
||||||
|
ObjectIDCondition(QString objectID);
|
||||||
|
|
||||||
|
QString objectID;
|
||||||
|
|
||||||
|
QJsonObject encode() const;
|
||||||
|
|
||||||
|
friend QDebug &operator<<(QDebug &dbg, const ObjectIDCondition &condition);
|
||||||
|
bool operator==(const ObjectIDCondition &rhs) const;
|
||||||
|
bool operator!=(const ObjectIDCondition &rhs) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
using Condition = std::variant<ObjectIDCondition>;
|
||||||
|
|
||||||
|
struct Subscription {
|
||||||
|
bool operator==(const Subscription &rhs) const;
|
||||||
|
bool operator!=(const Subscription &rhs) const;
|
||||||
|
Condition condition;
|
||||||
|
SubscriptionType type;
|
||||||
|
|
||||||
|
QByteArray encodeSubscribe() const;
|
||||||
|
QByteArray encodeUnsubscribe() const;
|
||||||
|
|
||||||
|
friend QDebug &operator<<(QDebug &dbg, const Subscription &subscription);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace chatterino::seventv::eventapi
|
||||||
|
|
||||||
|
template <>
|
||||||
|
constexpr magic_enum::customize::customize_t magic_enum::customize::enum_name<
|
||||||
|
chatterino::seventv::eventapi::SubscriptionType>(
|
||||||
|
chatterino::seventv::eventapi::SubscriptionType value) noexcept
|
||||||
|
{
|
||||||
|
using chatterino::seventv::eventapi::SubscriptionType;
|
||||||
|
switch (value)
|
||||||
|
{
|
||||||
|
case SubscriptionType::UpdateEmoteSet:
|
||||||
|
return "emote_set.update";
|
||||||
|
case SubscriptionType::UpdateUser:
|
||||||
|
return "user.update";
|
||||||
|
|
||||||
|
default:
|
||||||
|
return default_tag;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace std {
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct hash<chatterino::seventv::eventapi::ObjectIDCondition> {
|
||||||
|
size_t operator()(
|
||||||
|
const chatterino::seventv::eventapi::ObjectIDCondition &c) const
|
||||||
|
{
|
||||||
|
return (size_t)qHash(c.objectID);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct hash<chatterino::seventv::eventapi::Subscription> {
|
||||||
|
size_t operator()(
|
||||||
|
const chatterino::seventv::eventapi::Subscription &sub) const
|
||||||
|
{
|
||||||
|
const size_t conditionHash =
|
||||||
|
std::hash<chatterino::seventv::eventapi::Condition>{}(
|
||||||
|
sub.condition);
|
||||||
|
return (size_t)qHash(conditionHash, qHash((int)sub.type));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace std
|
|
@ -311,10 +311,11 @@ std::vector<MessagePtr> IrcMessageHandler::parsePrivMessage(
|
||||||
void IrcMessageHandler::handlePrivMessage(Communi::IrcPrivateMessage *message,
|
void IrcMessageHandler::handlePrivMessage(Communi::IrcPrivateMessage *message,
|
||||||
TwitchIrcServer &server)
|
TwitchIrcServer &server)
|
||||||
{
|
{
|
||||||
// This is to make sure that combined emoji go through properly, see
|
// This is for compatibility with older Chatterino versions. Twitch didn't use
|
||||||
// https://github.com/Chatterino/chatterino2/issues/3384 and
|
// to allow ZERO WIDTH JOINER unicode character, so Chatterino used ESCAPE_TAG
|
||||||
|
// instead.
|
||||||
|
// See https://github.com/Chatterino/chatterino2/issues/3384 and
|
||||||
// https://mm2pl.github.io/emoji_rfc.pdf for more details
|
// https://mm2pl.github.io/emoji_rfc.pdf for more details
|
||||||
// Constants used here are defined in TwitchChannel.hpp
|
|
||||||
|
|
||||||
this->addMessage(
|
this->addMessage(
|
||||||
message, message->target(),
|
message, message->target(),
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#include "providers/twitch/PubSubManager.hpp"
|
#include "providers/twitch/PubSubManager.hpp"
|
||||||
|
|
||||||
#include "common/QLogging.hpp"
|
#include "common/QLogging.hpp"
|
||||||
|
#include "providers/NetworkConfigurationProvider.hpp"
|
||||||
#include "providers/twitch/PubSubActions.hpp"
|
#include "providers/twitch/PubSubActions.hpp"
|
||||||
#include "providers/twitch/PubSubClient.hpp"
|
#include "providers/twitch/PubSubClient.hpp"
|
||||||
#include "providers/twitch/PubSubHelpers.hpp"
|
#include "providers/twitch/PubSubHelpers.hpp"
|
||||||
|
@ -514,6 +515,8 @@ void PubSub::addClient()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NetworkConfigurationProvider::applyToWebSocket(con);
|
||||||
|
|
||||||
this->websocketClient.connect(con);
|
this->websocketClient.connect(con);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
#include "providers/bttv/BttvLiveUpdates.hpp"
|
#include "providers/bttv/BttvLiveUpdates.hpp"
|
||||||
#include "providers/bttv/liveupdates/BttvLiveUpdateMessages.hpp"
|
#include "providers/bttv/liveupdates/BttvLiveUpdateMessages.hpp"
|
||||||
#include "providers/RecentMessagesApi.hpp"
|
#include "providers/RecentMessagesApi.hpp"
|
||||||
#include "providers/seventv/eventapi/SeventvEventAPIDispatch.hpp"
|
#include "providers/seventv/eventapi/Dispatch.hpp"
|
||||||
#include "providers/seventv/SeventvEmotes.hpp"
|
#include "providers/seventv/SeventvEmotes.hpp"
|
||||||
#include "providers/seventv/SeventvEventAPI.hpp"
|
#include "providers/seventv/SeventvEventAPI.hpp"
|
||||||
#include "providers/twitch/api/Helix.hpp"
|
#include "providers/twitch/api/Helix.hpp"
|
||||||
|
@ -121,16 +121,6 @@ TwitchChannel::TwitchChannel(const QString &name)
|
||||||
this->loadRecentMessagesReconnect();
|
this->loadRecentMessagesReconnect();
|
||||||
});
|
});
|
||||||
|
|
||||||
this->destroyed.connect([this]() {
|
|
||||||
getApp()->twitch->dropSeventvChannel(this->seventvUserID_,
|
|
||||||
this->seventvEmoteSetID_);
|
|
||||||
|
|
||||||
if (getApp()->twitch->bttvLiveUpdates)
|
|
||||||
{
|
|
||||||
getApp()->twitch->bttvLiveUpdates->partChannel(this->roomId());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this->messageRemovedFromStart.connect([this](MessagePtr &msg) {
|
this->messageRemovedFromStart.connect([this](MessagePtr &msg) {
|
||||||
if (msg->replyThread)
|
if (msg->replyThread)
|
||||||
{
|
{
|
||||||
|
@ -169,6 +159,17 @@ TwitchChannel::TwitchChannel(const QString &name)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TwitchChannel::~TwitchChannel()
|
||||||
|
{
|
||||||
|
getApp()->twitch->dropSeventvChannel(this->seventvUserID_,
|
||||||
|
this->seventvEmoteSetID_);
|
||||||
|
|
||||||
|
if (getApp()->twitch->bttvLiveUpdates)
|
||||||
|
{
|
||||||
|
getApp()->twitch->bttvLiveUpdates->partChannel(this->roomId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void TwitchChannel::initialize()
|
void TwitchChannel::initialize()
|
||||||
{
|
{
|
||||||
this->fetchDisplayName();
|
this->fetchDisplayName();
|
||||||
|
@ -303,8 +304,22 @@ void TwitchChannel::addChannelPointReward(const ChannelPointReward &reward)
|
||||||
<< "[TwitchChannel" << this->getName()
|
<< "[TwitchChannel" << this->getName()
|
||||||
<< "] Channel point reward added:" << reward.id << ","
|
<< "] Channel point reward added:" << reward.id << ","
|
||||||
<< reward.title << "," << reward.isUserInputRequired;
|
<< reward.title << "," << reward.isUserInputRequired;
|
||||||
|
|
||||||
|
// TODO: There's an underlying bug here. This bug should be fixed.
|
||||||
|
// This only attempts to prevent a crash when invoking the signal.
|
||||||
|
try
|
||||||
|
{
|
||||||
this->channelPointRewardAdded.invoke(reward);
|
this->channelPointRewardAdded.invoke(reward);
|
||||||
}
|
}
|
||||||
|
catch (const std::bad_function_call &)
|
||||||
|
{
|
||||||
|
qCWarning(chatterinoTwitch).nospace()
|
||||||
|
<< "[TwitchChannel " << this->getName()
|
||||||
|
<< "] Caught std::bad_function_call when adding channel point "
|
||||||
|
"reward ChannelPointReward{ id: "
|
||||||
|
<< reward.id << ", title: " << reward.title << " }.";
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TwitchChannel::isChannelPointRewardKnown(const QString &rewardId)
|
bool TwitchChannel::isChannelPointRewardKnown(const QString &rewardId)
|
||||||
|
@ -355,10 +370,6 @@ QString TwitchChannel::prepareMessage(const QString &message) const
|
||||||
auto app = getApp();
|
auto app = getApp();
|
||||||
QString parsedMessage = app->emotes->emojis.replaceShortCodes(message);
|
QString parsedMessage = app->emotes->emojis.replaceShortCodes(message);
|
||||||
|
|
||||||
// This is to make sure that combined emoji go through properly, see
|
|
||||||
// https://github.com/Chatterino/chatterino2/issues/3384 and
|
|
||||||
// https://mm2pl.github.io/emoji_rfc.pdf for more details
|
|
||||||
parsedMessage.replace(ZERO_WIDTH_JOINER, ESCAPE_TAG);
|
|
||||||
parsedMessage = parsedMessage.simplified();
|
parsedMessage = parsedMessage.simplified();
|
||||||
|
|
||||||
if (parsedMessage.isEmpty())
|
if (parsedMessage.isEmpty())
|
||||||
|
@ -691,7 +702,7 @@ void TwitchChannel::removeBttvEmote(
|
||||||
}
|
}
|
||||||
|
|
||||||
void TwitchChannel::addSeventvEmote(
|
void TwitchChannel::addSeventvEmote(
|
||||||
const SeventvEventAPIEmoteAddDispatch &dispatch)
|
const seventv::eventapi::EmoteAddDispatch &dispatch)
|
||||||
{
|
{
|
||||||
if (!SeventvEmotes::addEmote(this->seventvEmotes_, dispatch))
|
if (!SeventvEmotes::addEmote(this->seventvEmotes_, dispatch))
|
||||||
{
|
{
|
||||||
|
@ -703,7 +714,7 @@ void TwitchChannel::addSeventvEmote(
|
||||||
}
|
}
|
||||||
|
|
||||||
void TwitchChannel::updateSeventvEmote(
|
void TwitchChannel::updateSeventvEmote(
|
||||||
const SeventvEventAPIEmoteUpdateDispatch &dispatch)
|
const seventv::eventapi::EmoteUpdateDispatch &dispatch)
|
||||||
{
|
{
|
||||||
if (!SeventvEmotes::updateEmote(this->seventvEmotes_, dispatch))
|
if (!SeventvEmotes::updateEmote(this->seventvEmotes_, dispatch))
|
||||||
{
|
{
|
||||||
|
@ -717,7 +728,7 @@ void TwitchChannel::updateSeventvEmote(
|
||||||
}
|
}
|
||||||
|
|
||||||
void TwitchChannel::removeSeventvEmote(
|
void TwitchChannel::removeSeventvEmote(
|
||||||
const SeventvEventAPIEmoteRemoveDispatch &dispatch)
|
const seventv::eventapi::EmoteRemoveDispatch &dispatch)
|
||||||
{
|
{
|
||||||
auto removed = SeventvEmotes::removeEmote(this->seventvEmotes_, dispatch);
|
auto removed = SeventvEmotes::removeEmote(this->seventvEmotes_, dispatch);
|
||||||
if (!removed)
|
if (!removed)
|
||||||
|
@ -730,7 +741,7 @@ void TwitchChannel::removeSeventvEmote(
|
||||||
}
|
}
|
||||||
|
|
||||||
void TwitchChannel::updateSeventvUser(
|
void TwitchChannel::updateSeventvUser(
|
||||||
const SeventvEventAPIUserConnectionUpdateDispatch &dispatch)
|
const seventv::eventapi::UserConnectionUpdateDispatch &dispatch)
|
||||||
{
|
{
|
||||||
if (dispatch.connectionIndex != this->seventvUserTwitchConnectionIndex_)
|
if (dispatch.connectionIndex != this->seventvUserTwitchConnectionIndex_)
|
||||||
{
|
{
|
||||||
|
|
|
@ -22,8 +22,10 @@
|
||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
|
|
||||||
// This is to make sure that combined emoji go through properly, see
|
// This is for compatibility with older Chatterino versions. Twitch didn't use
|
||||||
// https://github.com/Chatterino/chatterino2/issues/3384 and
|
// to allow ZERO WIDTH JOINER unicode character, so Chatterino used ESCAPE_TAG
|
||||||
|
// instead.
|
||||||
|
// See https://github.com/Chatterino/chatterino2/issues/3384 and
|
||||||
// https://mm2pl.github.io/emoji_rfc.pdf for more details
|
// https://mm2pl.github.io/emoji_rfc.pdf for more details
|
||||||
const QString ZERO_WIDTH_JOINER = QString(QChar(0x200D));
|
const QString ZERO_WIDTH_JOINER = QString(QChar(0x200D));
|
||||||
|
|
||||||
|
@ -49,11 +51,15 @@ class FfzEmotes;
|
||||||
class BttvEmotes;
|
class BttvEmotes;
|
||||||
struct BttvLiveUpdateEmoteUpdateAddMessage;
|
struct BttvLiveUpdateEmoteUpdateAddMessage;
|
||||||
struct BttvLiveUpdateEmoteRemoveMessage;
|
struct BttvLiveUpdateEmoteRemoveMessage;
|
||||||
|
|
||||||
class SeventvEmotes;
|
class SeventvEmotes;
|
||||||
struct SeventvEventAPIEmoteAddDispatch;
|
namespace seventv::eventapi {
|
||||||
struct SeventvEventAPIEmoteUpdateDispatch;
|
struct EmoteAddDispatch;
|
||||||
struct SeventvEventAPIEmoteRemoveDispatch;
|
struct EmoteUpdateDispatch;
|
||||||
struct SeventvEventAPIUserConnectionUpdateDispatch;
|
struct EmoteRemoveDispatch;
|
||||||
|
struct UserConnectionUpdateDispatch;
|
||||||
|
} // namespace seventv::eventapi
|
||||||
|
|
||||||
struct ChannelPointReward;
|
struct ChannelPointReward;
|
||||||
class MessageThread;
|
class MessageThread;
|
||||||
struct CheerEmoteSet;
|
struct CheerEmoteSet;
|
||||||
|
@ -98,6 +104,7 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
explicit TwitchChannel(const QString &channelName);
|
explicit TwitchChannel(const QString &channelName);
|
||||||
|
~TwitchChannel() override;
|
||||||
|
|
||||||
void initialize();
|
void initialize();
|
||||||
|
|
||||||
|
@ -149,14 +156,16 @@ public:
|
||||||
void removeBttvEmote(const BttvLiveUpdateEmoteRemoveMessage &message);
|
void removeBttvEmote(const BttvLiveUpdateEmoteRemoveMessage &message);
|
||||||
|
|
||||||
/** Adds a 7TV channel emote to this channel. */
|
/** Adds a 7TV channel emote to this channel. */
|
||||||
void addSeventvEmote(const SeventvEventAPIEmoteAddDispatch &dispatch);
|
void addSeventvEmote(const seventv::eventapi::EmoteAddDispatch &dispatch);
|
||||||
/** Updates a 7TV channel emote's name in this channel */
|
/** Updates a 7TV channel emote's name in this channel */
|
||||||
void updateSeventvEmote(const SeventvEventAPIEmoteUpdateDispatch &dispatch);
|
void updateSeventvEmote(
|
||||||
|
const seventv::eventapi::EmoteUpdateDispatch &dispatch);
|
||||||
/** Removes a 7TV channel emote from this channel */
|
/** Removes a 7TV channel emote from this channel */
|
||||||
void removeSeventvEmote(const SeventvEventAPIEmoteRemoveDispatch &dispatch);
|
void removeSeventvEmote(
|
||||||
|
const seventv::eventapi::EmoteRemoveDispatch &dispatch);
|
||||||
/** Updates the current 7TV user. Currently, only the emote-set is updated. */
|
/** Updates the current 7TV user. Currently, only the emote-set is updated. */
|
||||||
void updateSeventvUser(
|
void updateSeventvUser(
|
||||||
const SeventvEventAPIUserConnectionUpdateDispatch &dispatch);
|
const seventv::eventapi::UserConnectionUpdateDispatch &dispatch);
|
||||||
|
|
||||||
// Update the channel's 7TV information (the channel's 7TV user ID and emote set ID)
|
// Update the channel's 7TV information (the channel's 7TV user ID and emote set ID)
|
||||||
void updateSeventvData(const QString &newUserID,
|
void updateSeventvData(const QString &newUserID,
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
#include "messages/Message.hpp"
|
#include "messages/Message.hpp"
|
||||||
#include "messages/MessageBuilder.hpp"
|
#include "messages/MessageBuilder.hpp"
|
||||||
#include "providers/bttv/BttvLiveUpdates.hpp"
|
#include "providers/bttv/BttvLiveUpdates.hpp"
|
||||||
#include "providers/seventv/eventapi/SeventvEventAPISubscription.hpp"
|
#include "providers/seventv/eventapi/Subscription.hpp"
|
||||||
#include "providers/seventv/SeventvEventAPI.hpp"
|
#include "providers/seventv/SeventvEventAPI.hpp"
|
||||||
#include "providers/twitch/api/Helix.hpp"
|
#include "providers/twitch/api/Helix.hpp"
|
||||||
#include "providers/twitch/ChannelPointReward.hpp"
|
#include "providers/twitch/ChannelPointReward.hpp"
|
||||||
|
|
|
@ -9,6 +9,8 @@
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
|
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
|
|
||||||
struct Emote;
|
struct Emote;
|
||||||
|
|
|
@ -142,6 +142,7 @@ void Paths::initSubDirectories()
|
||||||
this->miscDirectory = makePath("Misc");
|
this->miscDirectory = makePath("Misc");
|
||||||
this->twitchProfileAvatars = makePath("ProfileAvatars");
|
this->twitchProfileAvatars = makePath("ProfileAvatars");
|
||||||
this->pluginsDirectory = makePath("Plugins");
|
this->pluginsDirectory = makePath("Plugins");
|
||||||
|
this->crashdumpDirectory = makePath("Crashes");
|
||||||
//QDir().mkdir(this->twitchProfileAvatars + "/twitch");
|
//QDir().mkdir(this->twitchProfileAvatars + "/twitch");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,9 @@ public:
|
||||||
// Directory for miscellaneous files. Same as <appDataDirectory>/Misc
|
// Directory for miscellaneous files. Same as <appDataDirectory>/Misc
|
||||||
QString miscDirectory;
|
QString miscDirectory;
|
||||||
|
|
||||||
|
// Directory for crashdumps. Same as <appDataDirectory>/Crashes
|
||||||
|
QString crashdumpDirectory;
|
||||||
|
|
||||||
// Hash of QCoreApplication::applicationFilePath()
|
// Hash of QCoreApplication::applicationFilePath()
|
||||||
QString applicationFilePathHash;
|
QString applicationFilePathHash;
|
||||||
|
|
||||||
|
|
|
@ -288,6 +288,13 @@ public:
|
||||||
QStringSetting selfHighlightColor = {"/highlighting/selfHighlightColor",
|
QStringSetting selfHighlightColor = {"/highlighting/selfHighlightColor",
|
||||||
""};
|
""};
|
||||||
|
|
||||||
|
BoolSetting enableSelfMessageHighlight = {
|
||||||
|
"/highlighting/selfMessageHighlight/enabled", false};
|
||||||
|
BoolSetting showSelfMessageHighlightInMentions = {
|
||||||
|
"/highlighting/selfMessageHighlight/showInMentions", false};
|
||||||
|
QStringSetting selfMessageHighlightColor = {
|
||||||
|
"/highlighting/selfMessageHighlight/color", ""};
|
||||||
|
|
||||||
BoolSetting enableWhisperHighlight = {
|
BoolSetting enableWhisperHighlight = {
|
||||||
"/highlighting/whisperHighlight/whispersHighlighted", true};
|
"/highlighting/whisperHighlight/whispersHighlighted", true};
|
||||||
BoolSetting enableWhisperHighlightSound = {
|
BoolSetting enableWhisperHighlightSound = {
|
||||||
|
|
|
@ -1,15 +1,17 @@
|
||||||
#include "util/IncognitoBrowser.hpp"
|
#include "util/IncognitoBrowser.hpp"
|
||||||
|
#ifdef USEWINSDK
|
||||||
|
# include "util/WindowsHelper.hpp"
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <QProcess>
|
#include <QProcess>
|
||||||
#include <QRegularExpression>
|
#include <QRegularExpression>
|
||||||
#include <QSettings>
|
|
||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
using namespace chatterino;
|
using namespace chatterino;
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
#ifdef USEWINSDK
|
||||||
QString injectPrivateSwitch(QString command)
|
QString injectPrivateSwitch(QString command)
|
||||||
{
|
{
|
||||||
// list of command line switches to turn on private browsing in browsers
|
// list of command line switches to turn on private browsing in browsers
|
||||||
|
@ -47,23 +49,27 @@ QString injectPrivateSwitch(QString command)
|
||||||
|
|
||||||
QString getCommand()
|
QString getCommand()
|
||||||
{
|
{
|
||||||
// get default browser prog id
|
// get default browser start command, by protocol if possible, falling back to extension if not
|
||||||
auto browserId = QSettings("HKEY_CURRENT_"
|
QString command =
|
||||||
"USER\\Software\\Microsoft\\Windows\\Shell\\"
|
getAssociatedCommand(AssociationQueryType::Protocol, L"http");
|
||||||
"Associations\\UrlAssociatio"
|
|
||||||
"ns\\http\\UserChoice",
|
|
||||||
QSettings::NativeFormat)
|
|
||||||
.value("Progid")
|
|
||||||
.toString();
|
|
||||||
|
|
||||||
// get default browser start command
|
|
||||||
auto command =
|
|
||||||
QSettings("HKEY_CLASSES_ROOT\\" + browserId + "\\shell\\open\\command",
|
|
||||||
QSettings::NativeFormat)
|
|
||||||
.value("Default")
|
|
||||||
.toString();
|
|
||||||
if (command.isNull())
|
if (command.isNull())
|
||||||
{
|
{
|
||||||
|
// failed to fetch default browser by protocol, try by file extension instead
|
||||||
|
command =
|
||||||
|
getAssociatedCommand(AssociationQueryType::FileExtension, L".html");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (command.isNull())
|
||||||
|
{
|
||||||
|
// also try the equivalent .htm extension
|
||||||
|
command =
|
||||||
|
getAssociatedCommand(AssociationQueryType::FileExtension, L".htm");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (command.isNull())
|
||||||
|
{
|
||||||
|
// failed to find browser command
|
||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,7 +90,7 @@ namespace chatterino {
|
||||||
|
|
||||||
bool supportsIncognitoLinks()
|
bool supportsIncognitoLinks()
|
||||||
{
|
{
|
||||||
#ifdef Q_OS_WIN
|
#ifdef USEWINSDK
|
||||||
return !getCommand().isNull();
|
return !getCommand().isNull();
|
||||||
#else
|
#else
|
||||||
return false;
|
return false;
|
||||||
|
@ -93,7 +99,7 @@ bool supportsIncognitoLinks()
|
||||||
|
|
||||||
bool openLinkIncognito(const QString &link)
|
bool openLinkIncognito(const QString &link)
|
||||||
{
|
{
|
||||||
#ifdef Q_OS_WIN
|
#ifdef USEWINSDK
|
||||||
auto command = getCommand();
|
auto command = getCommand();
|
||||||
|
|
||||||
// TODO: split command into program path and incognito argument
|
// TODO: split command into program path and incognito argument
|
||||||
|
|
|
@ -6,6 +6,9 @@
|
||||||
|
|
||||||
#ifdef USEWINSDK
|
#ifdef USEWINSDK
|
||||||
|
|
||||||
|
# include <Shlwapi.h>
|
||||||
|
# include <VersionHelpers.h>
|
||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
|
|
||||||
typedef enum MONITOR_DPI_TYPE {
|
typedef enum MONITOR_DPI_TYPE {
|
||||||
|
@ -17,6 +20,8 @@ typedef enum MONITOR_DPI_TYPE {
|
||||||
|
|
||||||
typedef HRESULT(CALLBACK *GetDpiForMonitor_)(HMONITOR, MONITOR_DPI_TYPE, UINT *,
|
typedef HRESULT(CALLBACK *GetDpiForMonitor_)(HMONITOR, MONITOR_DPI_TYPE, UINT *,
|
||||||
UINT *);
|
UINT *);
|
||||||
|
typedef HRESULT(CALLBACK *AssocQueryString_)(ASSOCF, ASSOCSTR, LPCWSTR, LPCWSTR,
|
||||||
|
LPWSTR, DWORD *);
|
||||||
|
|
||||||
boost::optional<UINT> getWindowDpi(HWND hwnd)
|
boost::optional<UINT> getWindowDpi(HWND hwnd)
|
||||||
{
|
{
|
||||||
|
@ -83,6 +88,67 @@ void setRegisteredForStartup(bool isRegistered)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString getAssociatedCommand(AssociationQueryType queryType, LPCWSTR query)
|
||||||
|
{
|
||||||
|
static HINSTANCE shlwapi = LoadLibrary(L"shlwapi");
|
||||||
|
if (shlwapi == nullptr)
|
||||||
|
{
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
static auto assocQueryString =
|
||||||
|
AssocQueryString_(GetProcAddress(shlwapi, "AssocQueryStringW"));
|
||||||
|
if (assocQueryString == nullptr)
|
||||||
|
{
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// always error out instead of returning a truncated string when the
|
||||||
|
// buffer is too small - avoids race condition when the user changes their
|
||||||
|
// default browser between calls to AssocQueryString
|
||||||
|
ASSOCF flags = ASSOCF_NOTRUNCATE;
|
||||||
|
|
||||||
|
if (queryType == AssociationQueryType::Protocol)
|
||||||
|
{
|
||||||
|
// ASSOCF_IS_PROTOCOL was introduced in Windows 8
|
||||||
|
if (IsWindows8OrGreater())
|
||||||
|
{
|
||||||
|
flags |= ASSOCF_IS_PROTOCOL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD resultSize = 0;
|
||||||
|
if (FAILED(assocQueryString(flags, ASSOCSTR_COMMAND, query, nullptr,
|
||||||
|
nullptr, &resultSize)))
|
||||||
|
{
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resultSize <= 1)
|
||||||
|
{
|
||||||
|
// resultSize includes the null terminator. if resultSize is 1, the
|
||||||
|
// returned value would be the empty string.
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString result;
|
||||||
|
auto buf = new wchar_t[resultSize];
|
||||||
|
if (SUCCEEDED(assocQueryString(flags, ASSOCSTR_COMMAND, query, nullptr, buf,
|
||||||
|
&resultSize)))
|
||||||
|
{
|
||||||
|
// QString::fromWCharArray expects the length in characters *not
|
||||||
|
// including* the null terminator, but AssocQueryStringW calculates
|
||||||
|
// length including the null terminator
|
||||||
|
result = QString::fromWCharArray(buf, resultSize - 1);
|
||||||
|
}
|
||||||
|
delete[] buf;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace chatterino
|
} // namespace chatterino
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -7,12 +7,16 @@
|
||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
|
|
||||||
|
enum class AssociationQueryType { Protocol, FileExtension };
|
||||||
|
|
||||||
boost::optional<UINT> getWindowDpi(HWND hwnd);
|
boost::optional<UINT> getWindowDpi(HWND hwnd);
|
||||||
void flushClipboard();
|
void flushClipboard();
|
||||||
|
|
||||||
bool isRegisteredForStartup();
|
bool isRegisteredForStartup();
|
||||||
void setRegisteredForStartup(bool isRegistered);
|
void setRegisteredForStartup(bool isRegistered);
|
||||||
|
|
||||||
|
QString getAssociatedCommand(AssociationQueryType queryType, LPCWSTR query);
|
||||||
|
|
||||||
} // namespace chatterino
|
} // namespace chatterino
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -217,7 +217,11 @@ void Button::fancyPaint(QPainter &painter)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Button::enterEvent(QEvent *)
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||||
|
void Button::enterEvent(QEnterEvent * /*event*/)
|
||||||
|
#else
|
||||||
|
void Button::enterEvent(QEvent * /*event*/)
|
||||||
|
#endif
|
||||||
{
|
{
|
||||||
this->mouseOver_ = true;
|
this->mouseOver_ = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,7 +57,11 @@ signals:
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void paintEvent(QPaintEvent *) override;
|
virtual void paintEvent(QPaintEvent *) override;
|
||||||
virtual void enterEvent(QEvent *) override;
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||||
|
void enterEvent(QEnterEvent * /*event*/) override;
|
||||||
|
#else
|
||||||
|
void enterEvent(QEvent * /*event*/) override;
|
||||||
|
#endif
|
||||||
virtual void leaveEvent(QEvent *) override;
|
virtual void leaveEvent(QEvent *) override;
|
||||||
virtual void mousePressEvent(QMouseEvent *event) override;
|
virtual void mousePressEvent(QMouseEvent *event) override;
|
||||||
virtual void mouseReleaseEvent(QMouseEvent *event) override;
|
virtual void mouseReleaseEvent(QMouseEvent *event) override;
|
||||||
|
|
|
@ -1440,7 +1440,11 @@ void ChannelView::wheelEvent(QWheelEvent *event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChannelView::enterEvent(QEvent *)
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||||
|
void ChannelView::enterEvent(QEnterEvent * /*event*/)
|
||||||
|
#else
|
||||||
|
void ChannelView::enterEvent(QEvent * /*event*/)
|
||||||
|
#endif
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -167,7 +167,11 @@ protected:
|
||||||
void paintEvent(QPaintEvent *) override;
|
void paintEvent(QPaintEvent *) override;
|
||||||
void wheelEvent(QWheelEvent *event) override;
|
void wheelEvent(QWheelEvent *event) override;
|
||||||
|
|
||||||
void enterEvent(QEvent *) override;
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||||
|
void enterEvent(QEnterEvent * /*event*/) override;
|
||||||
|
#else
|
||||||
|
void enterEvent(QEvent * /*event*/) override;
|
||||||
|
#endif
|
||||||
void leaveEvent(QEvent *) override;
|
void leaveEvent(QEvent *) override;
|
||||||
|
|
||||||
void mouseMoveEvent(QMouseEvent *event) override;
|
void mouseMoveEvent(QMouseEvent *event) override;
|
||||||
|
|
|
@ -674,7 +674,11 @@ void NotebookTab::mouseDoubleClickEvent(QMouseEvent *event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||||
|
void NotebookTab::enterEvent(QEnterEvent *event)
|
||||||
|
#else
|
||||||
void NotebookTab::enterEvent(QEvent *event)
|
void NotebookTab::enterEvent(QEvent *event)
|
||||||
|
#endif
|
||||||
{
|
{
|
||||||
this->mouseOver_ = true;
|
this->mouseOver_ = true;
|
||||||
|
|
||||||
|
|
|
@ -61,7 +61,11 @@ protected:
|
||||||
virtual void mousePressEvent(QMouseEvent *event) override;
|
virtual void mousePressEvent(QMouseEvent *event) override;
|
||||||
virtual void mouseReleaseEvent(QMouseEvent *event) override;
|
virtual void mouseReleaseEvent(QMouseEvent *event) override;
|
||||||
virtual void mouseDoubleClickEvent(QMouseEvent *event) override;
|
virtual void mouseDoubleClickEvent(QMouseEvent *event) override;
|
||||||
virtual void enterEvent(QEvent *) override;
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||||
|
void enterEvent(QEnterEvent *event) override;
|
||||||
|
#else
|
||||||
|
void enterEvent(QEvent *event) override;
|
||||||
|
#endif
|
||||||
virtual void leaveEvent(QEvent *) override;
|
virtual void leaveEvent(QEvent *) override;
|
||||||
|
|
||||||
virtual void dragEnterEvent(QDragEnterEvent *event) override;
|
virtual void dragEnterEvent(QDragEnterEvent *event) override;
|
||||||
|
|
|
@ -119,6 +119,11 @@ AboutPage::AboutPage()
|
||||||
addLicense(form.getElement(), "Fluent icons",
|
addLicense(form.getElement(), "Fluent icons",
|
||||||
"https://github.com/microsoft/fluentui-system-icons",
|
"https://github.com/microsoft/fluentui-system-icons",
|
||||||
":/licenses/fluenticons.txt");
|
":/licenses/fluenticons.txt");
|
||||||
|
#endif
|
||||||
|
#ifdef CHATTERINO_WITH_CRASHPAD
|
||||||
|
addLicense(form.getElement(), "sentry-crashpad",
|
||||||
|
"https://github.com/getsentry/crashpad",
|
||||||
|
":/licenses/crashpad.txt");
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -822,7 +822,11 @@ void Split::resizeEvent(QResizeEvent *event)
|
||||||
this->overlay_->setGeometry(this->rect());
|
this->overlay_->setGeometry(this->rect());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Split::enterEvent(QEvent *event)
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||||
|
void Split::enterEvent(QEnterEvent * /*event*/)
|
||||||
|
#else
|
||||||
|
void Split::enterEvent(QEvent * /*event*/)
|
||||||
|
#endif
|
||||||
{
|
{
|
||||||
this->isMouseOver_ = true;
|
this->isMouseOver_ = true;
|
||||||
|
|
||||||
|
|
|
@ -105,7 +105,11 @@ protected:
|
||||||
void keyPressEvent(QKeyEvent *event) override;
|
void keyPressEvent(QKeyEvent *event) override;
|
||||||
void keyReleaseEvent(QKeyEvent *event) override;
|
void keyReleaseEvent(QKeyEvent *event) override;
|
||||||
void resizeEvent(QResizeEvent *event) override;
|
void resizeEvent(QResizeEvent *event) override;
|
||||||
void enterEvent(QEvent *event) override;
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||||
|
void enterEvent(QEnterEvent * /*event*/) override;
|
||||||
|
#else
|
||||||
|
void enterEvent(QEvent * /*event*/) override;
|
||||||
|
#endif
|
||||||
void leaveEvent(QEvent *event) override;
|
void leaveEvent(QEvent *event) override;
|
||||||
|
|
||||||
void dragEnterEvent(QDragEnterEvent *event) override;
|
void dragEnterEvent(QDragEnterEvent *event) override;
|
||||||
|
|
|
@ -945,7 +945,11 @@ void SplitHeader::mouseDoubleClickEvent(QMouseEvent *event)
|
||||||
this->doubleClicked_ = true;
|
this->doubleClicked_ = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||||
|
void SplitHeader::enterEvent(QEnterEvent *event)
|
||||||
|
#else
|
||||||
void SplitHeader::enterEvent(QEvent *event)
|
void SplitHeader::enterEvent(QEvent *event)
|
||||||
|
#endif
|
||||||
{
|
{
|
||||||
if (!this->tooltipText_.isEmpty())
|
if (!this->tooltipText_.isEmpty())
|
||||||
{
|
{
|
||||||
|
|
|
@ -42,7 +42,11 @@ protected:
|
||||||
void mousePressEvent(QMouseEvent *event) override;
|
void mousePressEvent(QMouseEvent *event) override;
|
||||||
void mouseReleaseEvent(QMouseEvent *event) override;
|
void mouseReleaseEvent(QMouseEvent *event) override;
|
||||||
void mouseMoveEvent(QMouseEvent *event) override;
|
void mouseMoveEvent(QMouseEvent *event) override;
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||||
|
void enterEvent(QEnterEvent *event) override;
|
||||||
|
#else
|
||||||
void enterEvent(QEvent *event) override;
|
void enterEvent(QEvent *event) override;
|
||||||
|
#endif
|
||||||
void leaveEvent(QEvent *event) override;
|
void leaveEvent(QEvent *event) override;
|
||||||
void mouseDoubleClickEvent(QMouseEvent *event) override;
|
void mouseDoubleClickEvent(QMouseEvent *event) override;
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,15 @@
|
||||||
#include "providers/seventv/SeventvEventAPI.hpp"
|
#include "providers/seventv/SeventvEventAPI.hpp"
|
||||||
|
|
||||||
#include "providers/seventv/eventapi/SeventvEventAPIClient.hpp"
|
#include "providers/seventv/eventapi/Client.hpp"
|
||||||
#include "providers/seventv/eventapi/SeventvEventAPIDispatch.hpp"
|
#include "providers/seventv/eventapi/Dispatch.hpp"
|
||||||
#include "providers/seventv/eventapi/SeventvEventAPIMessage.hpp"
|
#include "providers/seventv/eventapi/Message.hpp"
|
||||||
|
|
||||||
#include <boost/optional.hpp>
|
#include <boost/optional.hpp>
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
using namespace chatterino;
|
using namespace chatterino;
|
||||||
|
using namespace chatterino::seventv::eventapi;
|
||||||
using namespace std::chrono_literals;
|
using namespace std::chrono_literals;
|
||||||
|
|
||||||
const QString EMOTE_SET_A = "60b39e943e203cc169dfc106";
|
const QString EMOTE_SET_A = "60b39e943e203cc169dfc106";
|
||||||
|
@ -21,10 +22,10 @@ TEST(SeventvEventAPI, AllEvents)
|
||||||
auto *eventAPI = new SeventvEventAPI(host, std::chrono::milliseconds(1000));
|
auto *eventAPI = new SeventvEventAPI(host, std::chrono::milliseconds(1000));
|
||||||
eventAPI->start();
|
eventAPI->start();
|
||||||
|
|
||||||
boost::optional<SeventvEventAPIEmoteAddDispatch> addDispatch;
|
boost::optional<EmoteAddDispatch> addDispatch;
|
||||||
boost::optional<SeventvEventAPIEmoteUpdateDispatch> updateDispatch;
|
boost::optional<EmoteUpdateDispatch> updateDispatch;
|
||||||
boost::optional<SeventvEventAPIEmoteRemoveDispatch> removeDispatch;
|
boost::optional<EmoteRemoveDispatch> removeDispatch;
|
||||||
boost::optional<SeventvEventAPIUserConnectionUpdateDispatch> userDispatch;
|
boost::optional<UserConnectionUpdateDispatch> userDispatch;
|
||||||
|
|
||||||
eventAPI->signals_.emoteAdded.connect([&](const auto &d) {
|
eventAPI->signals_.emoteAdded.connect([&](const auto &d) {
|
||||||
addDispatch = d;
|
addDispatch = d;
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
{
|
{
|
||||||
"$schema": "https://raw.githubusercontent.com/microsoft/vcpkg/master/scripts/vcpkg.schema.json",
|
"$schema": "https://raw.githubusercontent.com/microsoft/vcpkg-tool/main/docs/vcpkg.schema.json",
|
||||||
"name": "chatterino",
|
"name": "chatterino",
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"builtin-baseline": "5ba2b95aea2a39aa89444949c7a047af38c401c1",
|
"builtin-baseline": "43f56137beabcd470ac2650cdf3954761f65b70e",
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
"benchmark",
|
"benchmark",
|
||||||
"boost-asio",
|
"boost-asio",
|
||||||
|
@ -14,7 +14,6 @@
|
||||||
"boost-variant",
|
"boost-variant",
|
||||||
"gtest",
|
"gtest",
|
||||||
"openssl",
|
"openssl",
|
||||||
"qt5-multimedia",
|
|
||||||
"qt5-tools"
|
"qt5-tools"
|
||||||
],
|
],
|
||||||
"overrides": [
|
"overrides": [
|
||||||
|
|
Loading…
Reference in a new issue