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
|
||||
|
||||
./appimagetool-x86_64.AppImage appdir
|
||||
|
||||
# TODO: Create appimage in a unique directory instead maybe idk?
|
||||
rm -rf appdir
|
||||
|
|
|
@ -1,6 +1,37 @@
|
|||
#!/bin/sh
|
||||
|
||||
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
|
||||
echo "ERROR: No chatterino binary file found. This script must be run in the build folder, and chatterino must be built first."
|
||||
exit 1
|
||||
|
@ -8,33 +39,53 @@ fi
|
|||
|
||||
chatterino_version=$(git describe 2>/dev/null | cut -c 2-) || true
|
||||
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"
|
||||
echo "Falling back to setting the version to '$chatterino_version'"
|
||||
else
|
||||
echo "Found Chatterino version $chatterino_version via git"
|
||||
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 package/DEBIAN -p
|
||||
packaging_dir="$(realpath ./package)"
|
||||
mkdir -p "$packaging_dir/DEBIAN"
|
||||
|
||||
echo "Making control file"
|
||||
cat >> "$packaging_dir/DEBIAN/control" << EOF
|
||||
Package: chatterino
|
||||
Section: net
|
||||
Priority: optional
|
||||
Version: $chatterino_version
|
||||
Architecture: amd64
|
||||
Maintainer: Mm2PL <mm2pl@kotmisia.pl>
|
||||
Description: Testing out chatterino as a Ubuntu package
|
||||
Depends: libc6, libqt5concurrent5, libqt5core5a, libqt5dbus5, libqt5gui5, libqt5multimedia5, libqt5network5, libqt5svg5, libqt5widgets5, libssl1.1, libstdc++6
|
||||
Depends: $dependencies
|
||||
Section: net
|
||||
Priority: optional
|
||||
Homepage: https://github.com/Chatterino/chatterino2
|
||||
Description: Ubuntu package built for $ubuntu_release
|
||||
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"
|
||||
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:
|
||||
build:
|
||||
name: "Build ${{ matrix.os }}, Qt ${{ matrix.qt-version }} (PCH:${{ matrix.pch }}, LTO:${{ matrix.force-lto }})"
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [windows-latest, ubuntu-20.04, macos-latest]
|
||||
os: [windows-latest, macos-latest]
|
||||
qt-version: [5.15.2, 5.12.12]
|
||||
pch: [true]
|
||||
force-lto: [false]
|
||||
plugins: [false]
|
||||
skip_artifact: ["no"]
|
||||
crashpad: [true]
|
||||
include:
|
||||
# Ubuntu 20.04, Qt 5.12
|
||||
- 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
|
||||
pch: false
|
||||
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
|
||||
qt-version: 5.15.2
|
||||
pch: true
|
||||
|
@ -45,7 +67,6 @@ jobs:
|
|||
pch: true
|
||||
force-lto: false
|
||||
plugins: true
|
||||
|
||||
fail-fast: false
|
||||
|
||||
steps:
|
||||
|
@ -59,6 +80,18 @@ jobs:
|
|||
if: matrix.plugins == true
|
||||
run: |
|
||||
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
|
||||
|
||||
- name: Set environment variables for windows-latest
|
||||
|
@ -69,7 +102,7 @@ jobs:
|
|||
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: true
|
||||
submodules: recursive
|
||||
fetch-depth: 0 # allows for tags access
|
||||
|
||||
- name: Install Qt
|
||||
|
@ -85,14 +118,14 @@ jobs:
|
|||
if: startsWith(matrix.os, 'windows')
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
key: ${{ runner.os }}-conan-user-${{ hashFiles('**/conanfile.txt') }}
|
||||
key: ${{ runner.os }}-${{ matrix.crashpad }}-conan-user-${{ hashFiles('**/conanfile.txt') }}
|
||||
path: ~/.conan/
|
||||
|
||||
- name: Cache conan packages part 2
|
||||
if: startsWith(matrix.os, 'windows')
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
key: ${{ runner.os }}-conan-root-${{ hashFiles('**/conanfile.txt') }}
|
||||
key: ${{ runner.os }}-${{ matrix.crashpad }}-conan-root-${{ hashFiles('**/conanfile.txt') }}
|
||||
path: C:/.conan/
|
||||
|
||||
- name: Add Conan to path
|
||||
|
@ -106,7 +139,7 @@ jobs:
|
|||
|
||||
- name: Enable Developer Command Prompt
|
||||
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)
|
||||
if: startsWith(matrix.os, 'windows')
|
||||
|
@ -119,34 +152,50 @@ jobs:
|
|||
run: |
|
||||
mkdir 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 `
|
||||
-G"NMake Makefiles" `
|
||||
-DCMAKE_BUILD_TYPE=Release `
|
||||
-DCMAKE_BUILD_TYPE=RelWithDebInfo `
|
||||
-DCMAKE_TOOLCHAIN_FILE="conan_toolchain.cmake" `
|
||||
-DBUILD_WITH_CRASHPAD="$Env:C2_ENABLE_CRASHPAD" `
|
||||
-DCHATTERINO_LTO="$Env:C2_ENABLE_LTO" `
|
||||
-DCHATTERINO_PLUGINS="$Env:C2_PLUGINS" `
|
||||
..
|
||||
set cl=/MP
|
||||
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/
|
||||
cp bin/chatterino.exe Chatterino2/
|
||||
echo nightly > Chatterino2/modes
|
||||
7z a chatterino-windows-x86-64.zip Chatterino2/
|
||||
|
||||
- name: Upload artifact (Windows)
|
||||
if: startsWith(matrix.os, 'windows') && matrix.plugins == false
|
||||
- name: Upload artifact (Windows - binary)
|
||||
if: startsWith(matrix.os, 'windows') && matrix.skip_artifact != 'yes'
|
||||
uses: actions/upload-artifact@v3
|
||||
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
|
||||
|
||||
- name: Upload artifact (Windows, plugins)
|
||||
if: startsWith(matrix.os, 'windows') && matrix.plugins == true
|
||||
- name: Upload artifact (Windows - symbols)
|
||||
if: startsWith(matrix.os, 'windows') && matrix.skip_artifact != 'yes'
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: chatterino-windows-x86-64-${{ matrix.qt-version }}-plugins.zip
|
||||
path: build/chatterino-windows-x86-64.zip
|
||||
name: chatterino-windows-x86-64-${{ matrix.qt-version }}-symbols.pdb.7z
|
||||
path: build/bin/chatterino.pdb.7z
|
||||
|
||||
- name: Clean Conan pkgs
|
||||
if: startsWith(matrix.os, 'windows')
|
||||
|
@ -170,7 +219,6 @@ jobs:
|
|||
libboost-filesystem-dev \
|
||||
libpulse-dev \
|
||||
libxkbcommon-x11-0 \
|
||||
libgstreamer-plugins-base1.0-0 \
|
||||
build-essential \
|
||||
libgl1-mesa-dev \
|
||||
libxcb-icccm4 \
|
||||
|
@ -179,12 +227,18 @@ jobs:
|
|||
libxcb-render-util0 \
|
||||
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)
|
||||
if: startsWith(matrix.os, 'ubuntu')
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
cmake \
|
||||
CXXFLAGS=-fno-sized-deallocation cmake \
|
||||
-DCMAKE_INSTALL_PREFIX=appdir/usr/ \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DPAJLADA_SETTINGS_USE_BOOST_FILESYSTEM=On \
|
||||
|
@ -215,24 +269,24 @@ jobs:
|
|||
clang-tidy-review-metadata.json
|
||||
|
||||
- name: Package - AppImage (Ubuntu)
|
||||
if: startsWith(matrix.os, 'ubuntu')
|
||||
if: startsWith(matrix.os, 'ubuntu') && matrix.skip_artifact != 'yes'
|
||||
run: |
|
||||
cd build
|
||||
sh ./../.CI/CreateAppImage.sh
|
||||
shell: bash
|
||||
|
||||
- name: Package - .deb (Ubuntu)
|
||||
if: startsWith(matrix.os, 'ubuntu')
|
||||
if: startsWith(matrix.os, 'ubuntu') && matrix.skip_artifact != 'yes'
|
||||
run: |
|
||||
cd build
|
||||
sh ./../.CI/CreateUbuntuDeb.sh
|
||||
shell: bash
|
||||
|
||||
- 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
|
||||
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
|
||||
|
||||
- name: Upload artifact - AppImage (Ubuntu, plugins)
|
||||
|
@ -243,17 +297,10 @@ jobs:
|
|||
path: build/Chatterino-x86_64.AppImage
|
||||
|
||||
- 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
|
||||
with:
|
||||
name: Chatterino-${{ matrix.qt-version }}.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
|
||||
name: Chatterino-${{ matrix.os }}-Qt-${{ matrix.qt-version }}-${{ artifact_descr }}.deb
|
||||
path: build/Chatterino-x86_64.deb
|
||||
|
||||
# MACOS
|
||||
|
@ -290,17 +337,10 @@ jobs:
|
|||
shell: bash
|
||||
|
||||
- name: Upload artifact (MacOS)
|
||||
if: startsWith(matrix.os, 'macos') && matrix.plugins == false
|
||||
if: startsWith(matrix.os, 'macos')
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: chatterino-osx-${{ matrix.qt-version }}.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
|
||||
name: chatterino-osx-${{ matrix.qt-version }}-${{ artifact_descr }}.dmg
|
||||
path: build/chatterino-osx.dmg
|
||||
create-release:
|
||||
needs: build
|
||||
|
@ -320,7 +360,12 @@ jobs:
|
|||
|
||||
- uses: actions/download-artifact@v3
|
||||
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/
|
||||
|
||||
- uses: actions/download-artifact@v3
|
||||
|
|
2
.github/workflows/test.yml
vendored
2
.github/workflows/test.yml
vendored
|
@ -6,7 +6,7 @@ on:
|
|||
workflow_dispatch:
|
||||
|
||||
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:
|
||||
group: test-${{ github.ref }}
|
||||
|
|
6
.gitmodules
vendored
6
.gitmodules
vendored
|
@ -2,9 +2,6 @@
|
|||
path = lib/libcommuni
|
||||
url = https://github.com/Chatterino/libcommuni
|
||||
branch = chatterino-cmake
|
||||
[submodule "lib/qBreakpad"]
|
||||
path = lib/qBreakpad
|
||||
url = https://github.com/jiakuan/qBreakpad.git
|
||||
[submodule "lib/WinToast"]
|
||||
path = lib/WinToast
|
||||
url = https://github.com/mohabouje/WinToast.git
|
||||
|
@ -41,3 +38,6 @@
|
|||
[submodule "lib/lua/src"]
|
||||
path = lib/lua/src
|
||||
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
|
||||
|
||||
- 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: 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)
|
||||
|
@ -11,6 +12,9 @@
|
|||
- 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 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 highlight sounds not reloading on change properly. (#4194)
|
||||
- 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 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: Avoided crash that could occur when receiving channel point reward information. (#4360)
|
||||
- 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: Ignore `WM_SHOWWINDOW` hide events, causing fewer attempted rescales. (#4198)
|
||||
|
@ -44,6 +49,10 @@
|
|||
- 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: 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
|
||||
|
||||
|
|
|
@ -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_QTKEYCHAIN "Use system QtKeychain library" OFF)
|
||||
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(BUILD_WITH_QT6 "Use Qt6 instead of default Qt5" OFF)
|
||||
option(CHATTERINO_GENERATE_COVERAGE "Generate coverage files" OFF)
|
||||
|
@ -149,11 +150,16 @@ else()
|
|||
add_subdirectory("${CMAKE_SOURCE_DIR}/lib/settings" EXCLUDE_FROM_ALL)
|
||||
endif()
|
||||
|
||||
|
||||
if (CHATTERINO_PLUGINS)
|
||||
set(LUA_INCLUDE_DIRS "${CMAKE_SOURCE_DIR}/lib/lua/src")
|
||||
add_subdirectory(lib/lua)
|
||||
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_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/ffz/FfzBadges.hpp"
|
||||
#include "providers/irc/Irc2.hpp"
|
||||
#include "providers/seventv/eventapi/SeventvEventAPIDispatch.hpp"
|
||||
#include "providers/seventv/eventapi/SeventvEventAPISubscription.hpp"
|
||||
#include "providers/seventv/eventapi/Dispatch.hpp"
|
||||
#include "providers/seventv/eventapi/Subscription.hpp"
|
||||
#include "providers/seventv/SeventvBadges.hpp"
|
||||
#include "providers/seventv/SeventvEventAPI.hpp"
|
||||
#include "providers/twitch/ChannelPointReward.hpp"
|
||||
|
|
|
@ -200,10 +200,14 @@ set(SOURCE_FILES
|
|||
messages/search/SubtierPredicate.cpp
|
||||
messages/search/SubtierPredicate.hpp
|
||||
|
||||
providers/Crashpad.cpp
|
||||
providers/Crashpad.hpp
|
||||
providers/IvrApi.cpp
|
||||
providers/IvrApi.hpp
|
||||
providers/LinkResolver.cpp
|
||||
providers/LinkResolver.hpp
|
||||
providers/NetworkConfigurationProvider.cpp
|
||||
providers/NetworkConfigurationProvider.hpp
|
||||
providers/RecentMessagesApi.cpp
|
||||
providers/RecentMessagesApi.hpp
|
||||
|
||||
|
@ -259,14 +263,14 @@ set(SOURCE_FILES
|
|||
providers/seventv/SeventvEventAPI.cpp
|
||||
providers/seventv/SeventvEventAPI.hpp
|
||||
|
||||
providers/seventv/eventapi/SeventvEventAPIClient.cpp
|
||||
providers/seventv/eventapi/SeventvEventAPIClient.hpp
|
||||
providers/seventv/eventapi/SeventvEventAPIDispatch.cpp
|
||||
providers/seventv/eventapi/SeventvEventAPIDispatch.hpp
|
||||
providers/seventv/eventapi/SeventvEventAPIMessage.cpp
|
||||
providers/seventv/eventapi/SeventvEventAPIMessage.hpp
|
||||
providers/seventv/eventapi/SeventvEventAPISubscription.cpp
|
||||
providers/seventv/eventapi/SeventvEventAPISubscription.hpp
|
||||
providers/seventv/eventapi/Client.cpp
|
||||
providers/seventv/eventapi/Client.hpp
|
||||
providers/seventv/eventapi/Dispatch.cpp
|
||||
providers/seventv/eventapi/Dispatch.hpp
|
||||
providers/seventv/eventapi/Message.cpp
|
||||
providers/seventv/eventapi/Message.hpp
|
||||
providers/seventv/eventapi/Subscription.cpp
|
||||
providers/seventv/eventapi/Subscription.hpp
|
||||
|
||||
providers/twitch/ChannelPointReward.cpp
|
||||
providers/twitch/ChannelPointReward.hpp
|
||||
|
@ -651,6 +655,22 @@ else()
|
|||
)
|
||||
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 (APPLE)
|
||||
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})
|
||||
|
||||
set_target_properties(${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"
|
||||
)
|
||||
set_target_directory_hierarchy(${EXECUTABLE_PROJECT})
|
||||
|
||||
if (WIN32)
|
||||
if (NOT WINDEPLOYQT_PATH)
|
||||
|
@ -851,8 +863,29 @@ if (LIBRT)
|
|||
)
|
||||
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
|
||||
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.
|
||||
# This makes sure, only /W4 is specified.
|
||||
string(REPLACE "/W3" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
|
||||
|
@ -881,6 +914,7 @@ if (MSVC)
|
|||
/wd4100
|
||||
/wd4267
|
||||
)
|
||||
# Disable min/max macros from Windows.h
|
||||
target_compile_definitions(${LIBRARY_PROJECT} PUBLIC NOMINMAX)
|
||||
else ()
|
||||
target_compile_options(${LIBRARY_PROJECT} PUBLIC
|
||||
|
|
|
@ -162,7 +162,7 @@ namespace {
|
|||
// true.
|
||||
void initSignalHandler()
|
||||
{
|
||||
#ifdef NDEBUG
|
||||
#if defined(NDEBUG) && !defined(CHATTERINO_WITH_CRASHPAD)
|
||||
signalsInitTime = std::chrono::steady_clock::now();
|
||||
|
||||
signal(SIGSEGV, handleSignal);
|
||||
|
|
|
@ -44,6 +44,17 @@ namespace {
|
|||
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)
|
||||
{
|
||||
auto envString = std::getenv(envName);
|
||||
|
@ -89,6 +100,7 @@ Env::Env()
|
|||
readStringEnv("CHATTERINO2_TWITCH_SERVER_HOST", "irc.chat.twitch.tv"))
|
||||
, twitchServerPort(readPortEnv("CHATTERINO2_TWITCH_SERVER_PORT", 443))
|
||||
, twitchServerSecure(readBoolEnv("CHATTERINO2_TWITCH_SERVER_SECURE", true))
|
||||
, proxyUrl(readOptionalStringEnv("CHATTERINO2_PROXY_URL"))
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <boost/optional.hpp>
|
||||
#include <QString>
|
||||
|
||||
namespace chatterino {
|
||||
|
@ -16,6 +17,7 @@ public:
|
|||
const QString twitchServerHost;
|
||||
const uint16_t twitchServerPort;
|
||||
const bool twitchServerSecure;
|
||||
const boost::optional<QString> proxyUrl;
|
||||
};
|
||||
|
||||
} // namespace chatterino
|
||||
|
|
|
@ -29,6 +29,7 @@ Q_LOGGING_CATEGORY(chatterinoMain, "chatterino.main", logThreshold);
|
|||
Q_LOGGING_CATEGORY(chatterinoMessage, "chatterino.message", logThreshold);
|
||||
Q_LOGGING_CATEGORY(chatterinoNativeMessage, "chatterino.nativemessage",
|
||||
logThreshold);
|
||||
Q_LOGGING_CATEGORY(chatterinoNetwork, "chatterino.network", logThreshold);
|
||||
Q_LOGGING_CATEGORY(chatterinoNotification, "chatterino.notification",
|
||||
logThreshold);
|
||||
Q_LOGGING_CATEGORY(chatterinoNuulsuploader, "chatterino.nuulsuploader",
|
||||
|
|
|
@ -23,6 +23,7 @@ Q_DECLARE_LOGGING_CATEGORY(chatterinoLua);
|
|||
Q_DECLARE_LOGGING_CATEGORY(chatterinoMain);
|
||||
Q_DECLARE_LOGGING_CATEGORY(chatterinoMessage);
|
||||
Q_DECLARE_LOGGING_CATEGORY(chatterinoNativeMessage);
|
||||
Q_DECLARE_LOGGING_CATEGORY(chatterinoNetwork);
|
||||
Q_DECLARE_LOGGING_CATEGORY(chatterinoNotification);
|
||||
Q_DECLARE_LOGGING_CATEGORY(chatterinoNuulsuploader);
|
||||
Q_DECLARE_LOGGING_CATEGORY(chatterinoPubSub);
|
||||
|
|
|
@ -100,6 +100,9 @@ QStringList Version::buildTags() const
|
|||
#ifdef _MSC_FULL_VER
|
||||
tags.append("MSVC " + QString::number(_MSC_FULL_VER, 10));
|
||||
#endif
|
||||
#ifdef CHATTERINO_WITH_CRASHPAD
|
||||
tags.append("Crashpad");
|
||||
#endif
|
||||
|
||||
return tags;
|
||||
}
|
||||
|
|
|
@ -85,12 +85,6 @@ void sendWhisperMessage(const QString &text)
|
|||
auto app = getApp();
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -210,6 +210,35 @@ void rebuildUserHighlights(Settings &settings,
|
|||
{
|
||||
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)
|
||||
{
|
||||
checks.emplace_back(HighlightCheck{
|
||||
|
@ -391,6 +420,11 @@ void HighlightController::initialize(Settings &settings, Paths & /*paths*/)
|
|||
this->rebuildListener_.addSetting(settings.enableSubHighlight);
|
||||
this->rebuildListener_.addSetting(settings.enableSubHighlightSound);
|
||||
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.enableThreadHighlight);
|
||||
|
|
|
@ -36,6 +36,10 @@ public:
|
|||
ThreadMessageRow = 6,
|
||||
};
|
||||
|
||||
enum UserHighlightRowIndexes {
|
||||
SelfMessageRow = 0,
|
||||
};
|
||||
|
||||
protected:
|
||||
// turn a vector item into a model row
|
||||
virtual HighlightPhrase getItemFromRow(
|
||||
|
|
|
@ -10,6 +10,8 @@ namespace {
|
|||
} // namespace
|
||||
|
||||
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(28, 126, 141, 60);
|
||||
QColor HighlightPhrase::FALLBACK_FIRST_MESSAGE_HIGHLIGHT_COLOR =
|
||||
|
|
|
@ -79,6 +79,8 @@ public:
|
|||
* Qt>=5.13.
|
||||
*/
|
||||
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_SUB_COLOR;
|
||||
static QColor FALLBACK_FIRST_MESSAGE_HIGHLIGHT_COLOR;
|
||||
|
|
|
@ -3,7 +3,9 @@
|
|||
#include "Application.hpp"
|
||||
#include "controllers/highlights/HighlightModel.hpp"
|
||||
#include "controllers/highlights/HighlightPhrase.hpp"
|
||||
#include "providers/colors/ColorProvider.hpp"
|
||||
#include "singletons/Settings.hpp"
|
||||
#include "singletons/WindowManager.hpp"
|
||||
#include "util/StandardItemHelper.hpp"
|
||||
|
||||
namespace chatterino {
|
||||
|
@ -37,6 +39,86 @@ HighlightPhrase UserHighlightModel::getItemFromRow(
|
|||
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
|
||||
void UserHighlightModel::getRowFromItem(const HighlightPhrase &item,
|
||||
std::vector<QStandardItem *> &row)
|
||||
|
|
|
@ -22,6 +22,12 @@ protected:
|
|||
|
||||
virtual void getRowFromItem(const HighlightPhrase &item,
|
||||
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
|
||||
|
|
10
src/main.cpp
10
src/main.cpp
|
@ -1,15 +1,17 @@
|
|||
#include "BrowserExtension.hpp"
|
||||
#include "common/Args.hpp"
|
||||
#include "common/Env.hpp"
|
||||
#include "common/Modes.hpp"
|
||||
#include "common/QLogging.hpp"
|
||||
#include "common/Version.hpp"
|
||||
#include "providers/Crashpad.hpp"
|
||||
#include "providers/IvrApi.hpp"
|
||||
#include "providers/NetworkConfigurationProvider.hpp"
|
||||
#include "providers/twitch/api/Helix.hpp"
|
||||
#include "RunGui.hpp"
|
||||
#include "singletons/Paths.hpp"
|
||||
#include "singletons/Settings.hpp"
|
||||
#include "util/AttachToConsole.hpp"
|
||||
#include "util/IncognitoBrowser.hpp"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QCommandLineParser>
|
||||
|
@ -57,6 +59,10 @@ int main(int argc, char **argv)
|
|||
|
||||
initArgs(a);
|
||||
|
||||
#ifdef CHATTERINO_WITH_CRASHPAD
|
||||
const auto crashpadHandler = installCrashHandler();
|
||||
#endif
|
||||
|
||||
// run in gui mode or browser extension host mode
|
||||
if (getArgs().shouldRunBrowserExtensionHost)
|
||||
{
|
||||
|
@ -81,6 +87,8 @@ int main(int argc, char **argv)
|
|||
attachToConsole();
|
||||
}
|
||||
|
||||
NetworkConfigurationProvider::applyFromEnv(Env::get());
|
||||
|
||||
IvrApi::initialize();
|
||||
Helix::initialize();
|
||||
|
||||
|
|
|
@ -272,7 +272,9 @@ namespace detail {
|
|||
// IMAGE2
|
||||
Image::~Image()
|
||||
{
|
||||
#ifndef DISABLE_IMAGE_EXPIRATION_POOL
|
||||
ImageExpirationPool::instance().removeImagePtr(this);
|
||||
#endif
|
||||
|
||||
if (this->empty_ && !this->frames_)
|
||||
{
|
||||
|
@ -425,7 +427,9 @@ void Image::load() const
|
|||
Image *this2 = const_cast<Image *>(this);
|
||||
this2->shouldLoad_ = false;
|
||||
this2->actuallyLoad();
|
||||
#ifndef DISABLE_IMAGE_EXPIRATION_POOL
|
||||
ImageExpirationPool::instance().addImagePtr(this2->shared_from_this());
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -551,6 +555,8 @@ void Image::expireFrames()
|
|||
this->shouldLoad_ = true; // Mark as needing load again
|
||||
}
|
||||
|
||||
#ifndef DISABLE_IMAGE_EXPIRATION_POOL
|
||||
|
||||
ImageExpirationPool::ImageExpirationPool()
|
||||
{
|
||||
QObject::connect(&this->freeTimer_, &QTimer::timeout, [this] {
|
||||
|
@ -643,4 +649,6 @@ void ImageExpirationPool::freeOld()
|
|||
# endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace chatterino
|
||||
|
|
|
@ -19,6 +19,14 @@
|
|||
#include <memory>
|
||||
#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 detail {
|
||||
template <typename Image>
|
||||
|
@ -105,6 +113,8 @@ private:
|
|||
// forward-declarable function that calls Image::getEmpty() under the hood.
|
||||
ImagePtr getEmptyImagePtr();
|
||||
|
||||
#ifndef DISABLE_IMAGE_EXPIRATION_POOL
|
||||
|
||||
class ImageExpirationPool
|
||||
{
|
||||
private:
|
||||
|
@ -131,4 +141,6 @@ private:
|
|||
std::mutex mutex_;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace chatterino
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
#include <cinttypes>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
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)
|
||||
{
|
||||
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);
|
||||
|
||||
auto message =
|
||||
|
|
|
@ -81,6 +81,20 @@ void ColorProvider::initTypeColorMap()
|
|||
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;
|
||||
if (QColor(customColor).isValid())
|
||||
{
|
||||
|
|
|
@ -16,6 +16,8 @@ enum class ColorType {
|
|||
FirstMessageHighlight,
|
||||
ElevatedMessageHighlight,
|
||||
ThreadMessageHighlight,
|
||||
// Used in automatic highlights of your own messages
|
||||
SelfMessageHighlight,
|
||||
};
|
||||
|
||||
class ColorProvider
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
|
||||
#include <rapidjson/rapidjson.h>
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
class QAbstractTableModel;
|
||||
|
||||
namespace chatterino {
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "common/Version.hpp"
|
||||
#include "providers/liveupdates/BasicPubSubClient.hpp"
|
||||
#include "providers/liveupdates/BasicPubSubWebsocket.hpp"
|
||||
#include "providers/NetworkConfigurationProvider.hpp"
|
||||
#include "providers/twitch/PubSubHelpers.hpp"
|
||||
#include "util/DebugCount.hpp"
|
||||
#include "util/ExponentialBackoff.hpp"
|
||||
|
@ -336,6 +337,8 @@ private:
|
|||
return;
|
||||
}
|
||||
|
||||
NetworkConfigurationProvider::applyToWebSocket(con);
|
||||
|
||||
this->websocketClient_.connect(con);
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
#include "messages/Image.hpp"
|
||||
#include "messages/ImageSet.hpp"
|
||||
#include "messages/MessageBuilder.hpp"
|
||||
#include "providers/seventv/eventapi/SeventvEventAPIDispatch.hpp"
|
||||
#include "providers/seventv/eventapi/Dispatch.hpp"
|
||||
#include "providers/twitch/TwitchChannel.hpp"
|
||||
#include "singletons/Settings.hpp"
|
||||
|
||||
|
@ -30,6 +30,7 @@
|
|||
namespace {
|
||||
|
||||
using namespace chatterino;
|
||||
using namespace seventv::eventapi;
|
||||
|
||||
// These declarations won't throw an exception.
|
||||
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,
|
||||
const SeventvEventAPIEmoteUpdateDispatch &dispatch)
|
||||
const EmoteUpdateDispatch &dispatch)
|
||||
{
|
||||
bool toNonAliased = oldEmote->baseName.has_value() &&
|
||||
dispatch.emoteName == oldEmote->baseName->string;
|
||||
|
@ -245,6 +246,8 @@ EmotePtr createUpdatedEmote(const EmotePtr &oldEmote,
|
|||
|
||||
namespace chatterino {
|
||||
|
||||
using namespace seventv::eventapi;
|
||||
|
||||
SeventvEmotes::SeventvEmotes()
|
||||
: global_(std::make_shared<EmoteMap>())
|
||||
{
|
||||
|
@ -401,7 +404,7 @@ void SeventvEmotes::loadChannelEmotes(
|
|||
|
||||
boost::optional<EmotePtr> SeventvEmotes::addEmote(
|
||||
Atomic<std::shared_ptr<const EmoteMap>> &map,
|
||||
const SeventvEventAPIEmoteAddDispatch &dispatch)
|
||||
const EmoteAddDispatch &dispatch)
|
||||
{
|
||||
// Check for visibility first, so we don't copy the map.
|
||||
auto emoteData = dispatch.emoteJson["data"].toObject();
|
||||
|
@ -429,7 +432,7 @@ boost::optional<EmotePtr> SeventvEmotes::addEmote(
|
|||
|
||||
boost::optional<EmotePtr> SeventvEmotes::updateEmote(
|
||||
Atomic<std::shared_ptr<const EmoteMap>> &map,
|
||||
const SeventvEventAPIEmoteUpdateDispatch &dispatch)
|
||||
const EmoteUpdateDispatch &dispatch)
|
||||
{
|
||||
auto oldMap = map.get();
|
||||
auto oldEmote = oldMap->findEmote(dispatch.emoteName, dispatch.emoteID);
|
||||
|
@ -451,7 +454,7 @@ boost::optional<EmotePtr> SeventvEmotes::updateEmote(
|
|||
|
||||
boost::optional<EmotePtr> SeventvEmotes::removeEmote(
|
||||
Atomic<std::shared_ptr<const EmoteMap>> &map,
|
||||
const SeventvEventAPIEmoteRemoveDispatch &dispatch)
|
||||
const EmoteRemoveDispatch &dispatch)
|
||||
{
|
||||
// This copies the map.
|
||||
EmoteMap updatedMap = *map.get();
|
||||
|
|
|
@ -10,9 +10,12 @@
|
|||
namespace chatterino {
|
||||
|
||||
class Channel;
|
||||
struct SeventvEventAPIEmoteAddDispatch;
|
||||
struct SeventvEventAPIEmoteUpdateDispatch;
|
||||
struct SeventvEventAPIEmoteRemoveDispatch;
|
||||
|
||||
namespace seventv::eventapi {
|
||||
struct EmoteAddDispatch;
|
||||
struct EmoteUpdateDispatch;
|
||||
struct EmoteRemoveDispatch;
|
||||
} // namespace seventv::eventapi
|
||||
|
||||
// https://github.com/SevenTV/API/blob/a84e884b5590dbb5d91a5c6b3548afabb228f385/data/model/emote-set.model.go#L29-L36
|
||||
enum class SeventvActiveEmoteFlag : int64_t {
|
||||
|
@ -86,7 +89,7 @@ public:
|
|||
*/
|
||||
static boost::optional<EmotePtr> addEmote(
|
||||
Atomic<std::shared_ptr<const EmoteMap>> &map,
|
||||
const SeventvEventAPIEmoteAddDispatch &dispatch);
|
||||
const seventv::eventapi::EmoteAddDispatch &dispatch);
|
||||
|
||||
/**
|
||||
* Updates an emote in this `map`.
|
||||
|
@ -97,7 +100,7 @@ public:
|
|||
*/
|
||||
static boost::optional<EmotePtr> updateEmote(
|
||||
Atomic<std::shared_ptr<const EmoteMap>> &map,
|
||||
const SeventvEventAPIEmoteUpdateDispatch &dispatch);
|
||||
const seventv::eventapi::EmoteUpdateDispatch &dispatch);
|
||||
|
||||
/**
|
||||
* Removes an emote from this `map`.
|
||||
|
@ -108,7 +111,7 @@ public:
|
|||
*/
|
||||
static boost::optional<EmotePtr> removeEmote(
|
||||
Atomic<std::shared_ptr<const EmoteMap>> &map,
|
||||
const SeventvEventAPIEmoteRemoveDispatch &dispatch);
|
||||
const seventv::eventapi::EmoteRemoveDispatch &dispatch);
|
||||
|
||||
/** Fetches an emote-set by its id */
|
||||
static void getEmoteSet(
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
#include "providers/seventv/SeventvEventAPI.hpp"
|
||||
|
||||
#include "providers/seventv/eventapi/SeventvEventAPIClient.hpp"
|
||||
#include "providers/seventv/eventapi/SeventvEventAPIDispatch.hpp"
|
||||
#include "providers/seventv/eventapi/SeventvEventAPIMessage.hpp"
|
||||
#include "providers/seventv/eventapi/Client.hpp"
|
||||
#include "providers/seventv/eventapi/Dispatch.hpp"
|
||||
#include "providers/seventv/eventapi/Message.hpp"
|
||||
|
||||
#include <QJsonArray>
|
||||
|
||||
|
@ -10,6 +10,8 @@
|
|||
|
||||
namespace chatterino {
|
||||
|
||||
using namespace seventv::eventapi;
|
||||
|
||||
SeventvEventAPI::SeventvEventAPI(
|
||||
QString host, std::chrono::milliseconds defaultHeartbeatInterval)
|
||||
: BasicPubSubManager(std::move(host))
|
||||
|
@ -22,13 +24,14 @@ void SeventvEventAPI::subscribeUser(const QString &userID,
|
|||
{
|
||||
if (!userID.isEmpty() && this->subscribedUsers_.insert(userID).second)
|
||||
{
|
||||
this->subscribe({userID, SeventvEventAPISubscriptionType::UpdateUser});
|
||||
this->subscribe(
|
||||
{ObjectIDCondition{userID}, SubscriptionType::UpdateUser});
|
||||
}
|
||||
if (!emoteSetID.isEmpty() &&
|
||||
this->subscribedEmoteSets_.insert(emoteSetID).second)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
this->unsubscribe({id, SeventvEventAPISubscriptionType::UpdateUser});
|
||||
this->unsubscribe(
|
||||
{ObjectIDCondition{id}, SubscriptionType::UpdateUser});
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<BasicPubSubClient<SeventvEventAPISubscription>>
|
||||
SeventvEventAPI::createClient(liveupdates::WebsocketClient &client,
|
||||
websocketpp::connection_hdl hdl)
|
||||
std::shared_ptr<BasicPubSubClient<Subscription>> SeventvEventAPI::createClient(
|
||||
liveupdates::WebsocketClient &client, websocketpp::connection_hdl hdl)
|
||||
{
|
||||
auto shared = std::make_shared<SeventvEventAPIClient>(
|
||||
client, hdl, this->heartbeatInterval_);
|
||||
return std::static_pointer_cast<
|
||||
BasicPubSubClient<SeventvEventAPISubscription>>(std::move(shared));
|
||||
auto shared =
|
||||
std::make_shared<Client>(client, hdl, this->heartbeatInterval_);
|
||||
return std::static_pointer_cast<BasicPubSubClient<Subscription>>(
|
||||
std::move(shared));
|
||||
}
|
||||
|
||||
void SeventvEventAPI::onMessage(
|
||||
websocketpp::connection_hdl hdl,
|
||||
BasicPubSubManager<SeventvEventAPISubscription>::WebsocketMessagePtr msg)
|
||||
BasicPubSubManager<Subscription>::WebsocketMessagePtr msg)
|
||||
{
|
||||
const auto &payload = QString::fromStdString(msg->get_payload());
|
||||
|
||||
auto pMessage = parseSeventvEventAPIBaseMessage(payload);
|
||||
auto pMessage = parseBaseMessage(payload);
|
||||
|
||||
if (!pMessage)
|
||||
{
|
||||
|
@ -76,11 +79,10 @@ void SeventvEventAPI::onMessage(
|
|||
auto message = *pMessage;
|
||||
switch (message.op)
|
||||
{
|
||||
case SeventvEventAPIOpcode::Hello: {
|
||||
case Opcode::Hello: {
|
||||
if (auto client = this->findClient(hdl))
|
||||
{
|
||||
if (auto *stvClient =
|
||||
dynamic_cast<SeventvEventAPIClient *>(client.get()))
|
||||
if (auto *stvClient = dynamic_cast<Client *>(client.get()))
|
||||
{
|
||||
stvClient->setHeartbeatInterval(
|
||||
message.data["heartbeat_interval"].toInt());
|
||||
|
@ -88,19 +90,18 @@ void SeventvEventAPI::onMessage(
|
|||
}
|
||||
}
|
||||
break;
|
||||
case SeventvEventAPIOpcode::Heartbeat: {
|
||||
case Opcode::Heartbeat: {
|
||||
if (auto client = this->findClient(hdl))
|
||||
{
|
||||
if (auto *stvClient =
|
||||
dynamic_cast<SeventvEventAPIClient *>(client.get()))
|
||||
if (auto *stvClient = dynamic_cast<Client *>(client.get()))
|
||||
{
|
||||
stvClient->handleHeartbeat();
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SeventvEventAPIOpcode::Dispatch: {
|
||||
auto dispatch = message.toInner<SeventvEventAPIDispatch>();
|
||||
case Opcode::Dispatch: {
|
||||
auto dispatch = message.toInner<Dispatch>();
|
||||
if (!dispatch)
|
||||
{
|
||||
qCDebug(chatterinoSeventvEventAPI)
|
||||
|
@ -110,17 +111,20 @@ void SeventvEventAPI::onMessage(
|
|||
this->handleDispatch(*dispatch);
|
||||
}
|
||||
break;
|
||||
case SeventvEventAPIOpcode::Reconnect: {
|
||||
case Opcode::Reconnect: {
|
||||
if (auto client = this->findClient(hdl))
|
||||
{
|
||||
if (auto *stvClient =
|
||||
dynamic_cast<SeventvEventAPIClient *>(client.get()))
|
||||
if (auto *stvClient = dynamic_cast<Client *>(client.get()))
|
||||
{
|
||||
stvClient->close("Reconnecting");
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Opcode::Ack: {
|
||||
// unhandled
|
||||
}
|
||||
break;
|
||||
default: {
|
||||
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)
|
||||
{
|
||||
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: {
|
||||
// pushed: Array<{ key, value }>,
|
||||
// pulled: Array<{ key, old_value }>,
|
||||
|
@ -146,8 +168,7 @@ void SeventvEventAPI::handleDispatch(const SeventvEventAPIDispatch &dispatch)
|
|||
continue;
|
||||
}
|
||||
|
||||
SeventvEventAPIEmoteAddDispatch added(
|
||||
dispatch, pushed["value"].toObject());
|
||||
const EmoteAddDispatch added(dispatch, pushed["value"].toObject());
|
||||
|
||||
if (added.validate())
|
||||
{
|
||||
|
@ -167,8 +188,8 @@ void SeventvEventAPI::handleDispatch(const SeventvEventAPIDispatch &dispatch)
|
|||
continue;
|
||||
}
|
||||
|
||||
SeventvEventAPIEmoteUpdateDispatch update(
|
||||
dispatch, updated["old_value"].toObject(),
|
||||
const EmoteUpdateDispatch update(dispatch,
|
||||
updated["old_value"].toObject(),
|
||||
updated["value"].toObject());
|
||||
|
||||
if (update.validate())
|
||||
|
@ -189,8 +210,8 @@ void SeventvEventAPI::handleDispatch(const SeventvEventAPIDispatch &dispatch)
|
|||
continue;
|
||||
}
|
||||
|
||||
SeventvEventAPIEmoteRemoveDispatch removed(
|
||||
dispatch, pulled["old_value"].toObject());
|
||||
const EmoteRemoveDispatch removed(dispatch,
|
||||
pulled["old_value"].toObject());
|
||||
|
||||
if (removed.validate())
|
||||
{
|
||||
|
@ -203,8 +224,9 @@ void SeventvEventAPI::handleDispatch(const SeventvEventAPIDispatch &dispatch)
|
|||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SeventvEventAPISubscriptionType::UpdateUser: {
|
||||
|
||||
void SeventvEventAPI::onUserUpdate(const Dispatch &dispatch)
|
||||
{
|
||||
// dispatchBody: {
|
||||
// updated: Array<{ key, value: Array<{key, value}> }>
|
||||
// }
|
||||
|
@ -223,7 +245,7 @@ void SeventvEventAPI::handleDispatch(const SeventvEventAPIDispatch &dispatch)
|
|||
continue;
|
||||
}
|
||||
|
||||
SeventvEventAPIUserConnectionUpdateDispatch update(
|
||||
const UserConnectionUpdateDispatch update(
|
||||
dispatch, value, (size_t)updated["index"].toInt());
|
||||
|
||||
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
|
||||
|
|
|
@ -8,14 +8,17 @@
|
|||
|
||||
namespace chatterino {
|
||||
|
||||
struct SeventvEventAPISubscription;
|
||||
struct SeventvEventAPIDispatch;
|
||||
struct SeventvEventAPIEmoteAddDispatch;
|
||||
struct SeventvEventAPIEmoteUpdateDispatch;
|
||||
struct SeventvEventAPIEmoteRemoveDispatch;
|
||||
struct SeventvEventAPIUserConnectionUpdateDispatch;
|
||||
namespace seventv::eventapi {
|
||||
struct Subscription;
|
||||
struct Dispatch;
|
||||
struct EmoteAddDispatch;
|
||||
struct EmoteUpdateDispatch;
|
||||
struct EmoteRemoveDispatch;
|
||||
struct UserConnectionUpdateDispatch;
|
||||
} // namespace seventv::eventapi
|
||||
|
||||
class SeventvEventAPI : public BasicPubSubManager<SeventvEventAPISubscription>
|
||||
class SeventvEventAPI
|
||||
: public BasicPubSubManager<seventv::eventapi::Subscription>
|
||||
{
|
||||
template <typename T>
|
||||
using Signal =
|
||||
|
@ -27,10 +30,10 @@ public:
|
|||
std::chrono::milliseconds(25000));
|
||||
|
||||
struct {
|
||||
Signal<SeventvEventAPIEmoteAddDispatch> emoteAdded;
|
||||
Signal<SeventvEventAPIEmoteUpdateDispatch> emoteUpdated;
|
||||
Signal<SeventvEventAPIEmoteRemoveDispatch> emoteRemoved;
|
||||
Signal<SeventvEventAPIUserConnectionUpdateDispatch> userUpdated;
|
||||
Signal<seventv::eventapi::EmoteAddDispatch> emoteAdded;
|
||||
Signal<seventv::eventapi::EmoteUpdateDispatch> emoteUpdated;
|
||||
Signal<seventv::eventapi::EmoteRemoveDispatch> emoteRemoved;
|
||||
Signal<seventv::eventapi::UserConnectionUpdateDispatch> userUpdated;
|
||||
} signals_; // NOLINT(readability-identifier-naming)
|
||||
|
||||
/**
|
||||
|
@ -48,18 +51,23 @@ public:
|
|||
void unsubscribeEmoteSet(const QString &id);
|
||||
|
||||
protected:
|
||||
std::shared_ptr<BasicPubSubClient<SeventvEventAPISubscription>>
|
||||
std::shared_ptr<BasicPubSubClient<seventv::eventapi::Subscription>>
|
||||
createClient(liveupdates::WebsocketClient &client,
|
||||
websocketpp::connection_hdl hdl) override;
|
||||
void onMessage(
|
||||
websocketpp::connection_hdl hdl,
|
||||
BasicPubSubManager<SeventvEventAPISubscription>::WebsocketMessagePtr
|
||||
BasicPubSubManager<seventv::eventapi::Subscription>::WebsocketMessagePtr
|
||||
msg) override;
|
||||
|
||||
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_;
|
||||
/** user ids */
|
||||
std::unordered_set<QString> subscribedUsers_;
|
||||
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 <utility>
|
||||
|
||||
namespace chatterino {
|
||||
namespace chatterino::seventv::eventapi {
|
||||
|
||||
SeventvEventAPIClient::SeventvEventAPIClient(
|
||||
liveupdates::WebsocketClient &websocketClient,
|
||||
Client::Client(liveupdates::WebsocketClient &websocketClient,
|
||||
liveupdates::WebsocketHandle handle,
|
||||
std::chrono::milliseconds heartbeatInterval)
|
||||
: BasicPubSubClient<SeventvEventAPISubscription>(websocketClient,
|
||||
std::move(handle))
|
||||
: BasicPubSubClient<Subscription>(websocketClient, std::move(handle))
|
||||
, lastHeartbeat_(std::chrono::steady_clock::now())
|
||||
, heartbeatInterval_(heartbeatInterval)
|
||||
{
|
||||
}
|
||||
|
||||
void SeventvEventAPIClient::onConnectionEstablished()
|
||||
void Client::onConnectionEstablished()
|
||||
{
|
||||
this->lastHeartbeat_.store(std::chrono::steady_clock::now(),
|
||||
std::memory_order_release);
|
||||
this->checkHeartbeat();
|
||||
}
|
||||
|
||||
void SeventvEventAPIClient::setHeartbeatInterval(int intervalMs)
|
||||
void Client::setHeartbeatInterval(int intervalMs)
|
||||
{
|
||||
qCDebug(chatterinoSeventvEventAPI)
|
||||
<< "Setting expected heartbeat interval to" << intervalMs << "ms";
|
||||
this->heartbeatInterval_ = std::chrono::milliseconds(intervalMs);
|
||||
}
|
||||
|
||||
void SeventvEventAPIClient::handleHeartbeat()
|
||||
void Client::handleHeartbeat()
|
||||
{
|
||||
this->lastHeartbeat_.store(std::chrono::steady_clock::now(),
|
||||
std::memory_order_release);
|
||||
}
|
||||
|
||||
void SeventvEventAPIClient::checkHeartbeat()
|
||||
void Client::checkHeartbeat()
|
||||
{
|
||||
// Following the heartbeat docs, a connection is dead
|
||||
// after three missed heartbeats.
|
||||
|
@ -54,8 +52,7 @@ void SeventvEventAPIClient::checkHeartbeat()
|
|||
return;
|
||||
}
|
||||
|
||||
auto self = std::dynamic_pointer_cast<SeventvEventAPIClient>(
|
||||
this->shared_from_this());
|
||||
auto self = std::dynamic_pointer_cast<Client>(this->shared_from_this());
|
||||
|
||||
runAfter(this->websocketClient_.get_io_service(), this->heartbeatInterval_,
|
||||
[self](auto) {
|
||||
|
@ -67,4 +64,4 @@ void SeventvEventAPIClient::checkHeartbeat()
|
|||
});
|
||||
}
|
||||
|
||||
} // namespace chatterino
|
||||
} // namespace chatterino::seventv::eventapi
|
|
@ -2,16 +2,20 @@
|
|||
|
||||
#include "providers/liveupdates/BasicPubSubClient.hpp"
|
||||
// this needs to be included for the specialization
|
||||
// of std::hash for SeventvEventAPISubscription
|
||||
#include "providers/seventv/eventapi/SeventvEventAPISubscription.hpp"
|
||||
// of std::hash for Subscription
|
||||
#include "providers/seventv/eventapi/Subscription.hpp"
|
||||
|
||||
namespace chatterino {
|
||||
class SeventvEventAPI;
|
||||
|
||||
class SeventvEventAPIClient
|
||||
: public BasicPubSubClient<SeventvEventAPISubscription>
|
||||
} // namespace chatterino
|
||||
|
||||
namespace chatterino::seventv::eventapi {
|
||||
|
||||
class Client : public BasicPubSubClient<Subscription>
|
||||
{
|
||||
public:
|
||||
SeventvEventAPIClient(liveupdates::WebsocketClient &websocketClient,
|
||||
Client(liveupdates::WebsocketClient &websocketClient,
|
||||
liveupdates::WebsocketHandle handle,
|
||||
std::chrono::milliseconds heartbeatInterval);
|
||||
|
||||
|
@ -29,7 +33,7 @@ private:
|
|||
// This will be set once on the welcome message.
|
||||
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>
|
||||
|
||||
namespace chatterino {
|
||||
namespace chatterino::seventv::eventapi {
|
||||
|
||||
SeventvEventAPIDispatch::SeventvEventAPIDispatch(QJsonObject obj)
|
||||
: type(magic_enum::enum_cast<SeventvEventAPISubscriptionType>(
|
||||
Dispatch::Dispatch(QJsonObject obj)
|
||||
: type(magic_enum::enum_cast<SubscriptionType>(
|
||||
obj["type"].toString().toStdString())
|
||||
.value_or(SeventvEventAPISubscriptionType::INVALID))
|
||||
.value_or(SubscriptionType::INVALID))
|
||||
, body(obj["body"].toObject())
|
||||
, id(this->body["id"].toString())
|
||||
, actorName(this->body["actor"].toObject()["display_name"].toString())
|
||||
{
|
||||
}
|
||||
|
||||
SeventvEventAPIEmoteAddDispatch::SeventvEventAPIEmoteAddDispatch(
|
||||
const SeventvEventAPIDispatch &dispatch, QJsonObject emote)
|
||||
EmoteAddDispatch::EmoteAddDispatch(const Dispatch &dispatch, QJsonObject emote)
|
||||
: emoteSetID(dispatch.id)
|
||||
, actorName(dispatch.actorName)
|
||||
, emoteJson(std::move(emote))
|
||||
|
@ -23,7 +22,7 @@ SeventvEventAPIEmoteAddDispatch::SeventvEventAPIEmoteAddDispatch(
|
|||
{
|
||||
}
|
||||
|
||||
bool SeventvEventAPIEmoteAddDispatch::validate() const
|
||||
bool EmoteAddDispatch::validate() const
|
||||
{
|
||||
bool validValues =
|
||||
!this->emoteSetID.isEmpty() && !this->emoteJson.isEmpty();
|
||||
|
@ -43,8 +42,8 @@ bool SeventvEventAPIEmoteAddDispatch::validate() const
|
|||
emoteData.contains("owner");
|
||||
}
|
||||
|
||||
SeventvEventAPIEmoteRemoveDispatch::SeventvEventAPIEmoteRemoveDispatch(
|
||||
const SeventvEventAPIDispatch &dispatch, QJsonObject emote)
|
||||
EmoteRemoveDispatch::EmoteRemoveDispatch(const Dispatch &dispatch,
|
||||
QJsonObject emote)
|
||||
: emoteSetID(dispatch.id)
|
||||
, actorName(dispatch.actorName)
|
||||
, 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() &&
|
||||
!this->emoteID.isEmpty();
|
||||
}
|
||||
|
||||
SeventvEventAPIEmoteUpdateDispatch::SeventvEventAPIEmoteUpdateDispatch(
|
||||
const SeventvEventAPIDispatch &dispatch, QJsonObject oldValue,
|
||||
EmoteUpdateDispatch::EmoteUpdateDispatch(const Dispatch &dispatch,
|
||||
QJsonObject oldValue,
|
||||
QJsonObject value)
|
||||
: emoteSetID(dispatch.id)
|
||||
, actorName(dispatch.actorName)
|
||||
|
@ -69,17 +68,15 @@ SeventvEventAPIEmoteUpdateDispatch::SeventvEventAPIEmoteUpdateDispatch(
|
|||
{
|
||||
}
|
||||
|
||||
bool SeventvEventAPIEmoteUpdateDispatch::validate() const
|
||||
bool EmoteUpdateDispatch::validate() const
|
||||
{
|
||||
return !this->emoteSetID.isEmpty() && !this->emoteID.isEmpty() &&
|
||||
!this->oldEmoteName.isEmpty() && !this->emoteName.isEmpty() &&
|
||||
this->oldEmoteName != this->emoteName;
|
||||
}
|
||||
|
||||
SeventvEventAPIUserConnectionUpdateDispatch::
|
||||
SeventvEventAPIUserConnectionUpdateDispatch(
|
||||
const SeventvEventAPIDispatch &dispatch, const QJsonObject &update,
|
||||
size_t connectionIndex)
|
||||
UserConnectionUpdateDispatch::UserConnectionUpdateDispatch(
|
||||
const Dispatch &dispatch, const QJsonObject &update, size_t connectionIndex)
|
||||
: userID(dispatch.id)
|
||||
, actorName(dispatch.actorName)
|
||||
, 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() &&
|
||||
!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
|
||||
|
||||
#include "providers/seventv/eventapi/SeventvEventAPISubscription.hpp"
|
||||
#include "providers/seventv/eventapi/Subscription.hpp"
|
||||
|
||||
#include <boost/optional.hpp>
|
||||
#include <magic_enum.hpp>
|
||||
|
@ -8,27 +8,26 @@
|
|||
#include <QJsonObject>
|
||||
#include <QString>
|
||||
|
||||
namespace chatterino {
|
||||
namespace chatterino::seventv::eventapi {
|
||||
|
||||
struct SeventvEventAPIMessage {
|
||||
struct Message {
|
||||
QJsonObject data;
|
||||
|
||||
SeventvEventAPIOpcode op;
|
||||
Opcode op;
|
||||
|
||||
SeventvEventAPIMessage(QJsonObject _json);
|
||||
Message(QJsonObject _json);
|
||||
|
||||
template <class InnerClass>
|
||||
boost::optional<InnerClass> toInner();
|
||||
};
|
||||
|
||||
template <class InnerClass>
|
||||
boost::optional<InnerClass> SeventvEventAPIMessage::toInner()
|
||||
boost::optional<InnerClass> Message::toInner()
|
||||
{
|
||||
return InnerClass{this->data};
|
||||
}
|
||||
|
||||
static boost::optional<SeventvEventAPIMessage> parseSeventvEventAPIBaseMessage(
|
||||
const QString &blob)
|
||||
static boost::optional<Message> parseBaseMessage(const QString &blob)
|
||||
{
|
||||
QJsonDocument jsonDoc(QJsonDocument::fromJson(blob.toUtf8()));
|
||||
|
||||
|
@ -37,7 +36,7 @@ static boost::optional<SeventvEventAPIMessage> parseSeventvEventAPIBaseMessage(
|
|||
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,
|
||||
TwitchIrcServer &server)
|
||||
{
|
||||
// This is to make sure that combined emoji go through properly, see
|
||||
// https://github.com/Chatterino/chatterino2/issues/3384 and
|
||||
// This is for compatibility with older Chatterino versions. Twitch didn't use
|
||||
// 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
|
||||
// Constants used here are defined in TwitchChannel.hpp
|
||||
|
||||
this->addMessage(
|
||||
message, message->target(),
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "providers/twitch/PubSubManager.hpp"
|
||||
|
||||
#include "common/QLogging.hpp"
|
||||
#include "providers/NetworkConfigurationProvider.hpp"
|
||||
#include "providers/twitch/PubSubActions.hpp"
|
||||
#include "providers/twitch/PubSubClient.hpp"
|
||||
#include "providers/twitch/PubSubHelpers.hpp"
|
||||
|
@ -514,6 +515,8 @@ void PubSub::addClient()
|
|||
return;
|
||||
}
|
||||
|
||||
NetworkConfigurationProvider::applyToWebSocket(con);
|
||||
|
||||
this->websocketClient.connect(con);
|
||||
}
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
#include "providers/bttv/BttvLiveUpdates.hpp"
|
||||
#include "providers/bttv/liveupdates/BttvLiveUpdateMessages.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/SeventvEventAPI.hpp"
|
||||
#include "providers/twitch/api/Helix.hpp"
|
||||
|
@ -121,16 +121,6 @@ TwitchChannel::TwitchChannel(const QString &name)
|
|||
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) {
|
||||
if (msg->replyThread)
|
||||
{
|
||||
|
@ -169,6 +159,17 @@ TwitchChannel::TwitchChannel(const QString &name)
|
|||
#endif
|
||||
}
|
||||
|
||||
TwitchChannel::~TwitchChannel()
|
||||
{
|
||||
getApp()->twitch->dropSeventvChannel(this->seventvUserID_,
|
||||
this->seventvEmoteSetID_);
|
||||
|
||||
if (getApp()->twitch->bttvLiveUpdates)
|
||||
{
|
||||
getApp()->twitch->bttvLiveUpdates->partChannel(this->roomId());
|
||||
}
|
||||
}
|
||||
|
||||
void TwitchChannel::initialize()
|
||||
{
|
||||
this->fetchDisplayName();
|
||||
|
@ -303,8 +304,22 @@ void TwitchChannel::addChannelPointReward(const ChannelPointReward &reward)
|
|||
<< "[TwitchChannel" << this->getName()
|
||||
<< "] Channel point reward added:" << reward.id << ","
|
||||
<< 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);
|
||||
}
|
||||
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)
|
||||
|
@ -355,10 +370,6 @@ QString TwitchChannel::prepareMessage(const QString &message) const
|
|||
auto app = getApp();
|
||||
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();
|
||||
|
||||
if (parsedMessage.isEmpty())
|
||||
|
@ -691,7 +702,7 @@ void TwitchChannel::removeBttvEmote(
|
|||
}
|
||||
|
||||
void TwitchChannel::addSeventvEmote(
|
||||
const SeventvEventAPIEmoteAddDispatch &dispatch)
|
||||
const seventv::eventapi::EmoteAddDispatch &dispatch)
|
||||
{
|
||||
if (!SeventvEmotes::addEmote(this->seventvEmotes_, dispatch))
|
||||
{
|
||||
|
@ -703,7 +714,7 @@ void TwitchChannel::addSeventvEmote(
|
|||
}
|
||||
|
||||
void TwitchChannel::updateSeventvEmote(
|
||||
const SeventvEventAPIEmoteUpdateDispatch &dispatch)
|
||||
const seventv::eventapi::EmoteUpdateDispatch &dispatch)
|
||||
{
|
||||
if (!SeventvEmotes::updateEmote(this->seventvEmotes_, dispatch))
|
||||
{
|
||||
|
@ -717,7 +728,7 @@ void TwitchChannel::updateSeventvEmote(
|
|||
}
|
||||
|
||||
void TwitchChannel::removeSeventvEmote(
|
||||
const SeventvEventAPIEmoteRemoveDispatch &dispatch)
|
||||
const seventv::eventapi::EmoteRemoveDispatch &dispatch)
|
||||
{
|
||||
auto removed = SeventvEmotes::removeEmote(this->seventvEmotes_, dispatch);
|
||||
if (!removed)
|
||||
|
@ -730,7 +741,7 @@ void TwitchChannel::removeSeventvEmote(
|
|||
}
|
||||
|
||||
void TwitchChannel::updateSeventvUser(
|
||||
const SeventvEventAPIUserConnectionUpdateDispatch &dispatch)
|
||||
const seventv::eventapi::UserConnectionUpdateDispatch &dispatch)
|
||||
{
|
||||
if (dispatch.connectionIndex != this->seventvUserTwitchConnectionIndex_)
|
||||
{
|
||||
|
|
|
@ -22,8 +22,10 @@
|
|||
|
||||
namespace chatterino {
|
||||
|
||||
// This is to make sure that combined emoji go through properly, see
|
||||
// https://github.com/Chatterino/chatterino2/issues/3384 and
|
||||
// This is for compatibility with older Chatterino versions. Twitch didn't use
|
||||
// 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
|
||||
const QString ZERO_WIDTH_JOINER = QString(QChar(0x200D));
|
||||
|
||||
|
@ -49,11 +51,15 @@ class FfzEmotes;
|
|||
class BttvEmotes;
|
||||
struct BttvLiveUpdateEmoteUpdateAddMessage;
|
||||
struct BttvLiveUpdateEmoteRemoveMessage;
|
||||
|
||||
class SeventvEmotes;
|
||||
struct SeventvEventAPIEmoteAddDispatch;
|
||||
struct SeventvEventAPIEmoteUpdateDispatch;
|
||||
struct SeventvEventAPIEmoteRemoveDispatch;
|
||||
struct SeventvEventAPIUserConnectionUpdateDispatch;
|
||||
namespace seventv::eventapi {
|
||||
struct EmoteAddDispatch;
|
||||
struct EmoteUpdateDispatch;
|
||||
struct EmoteRemoveDispatch;
|
||||
struct UserConnectionUpdateDispatch;
|
||||
} // namespace seventv::eventapi
|
||||
|
||||
struct ChannelPointReward;
|
||||
class MessageThread;
|
||||
struct CheerEmoteSet;
|
||||
|
@ -98,6 +104,7 @@ public:
|
|||
};
|
||||
|
||||
explicit TwitchChannel(const QString &channelName);
|
||||
~TwitchChannel() override;
|
||||
|
||||
void initialize();
|
||||
|
||||
|
@ -149,14 +156,16 @@ public:
|
|||
void removeBttvEmote(const BttvLiveUpdateEmoteRemoveMessage &message);
|
||||
|
||||
/** 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 */
|
||||
void updateSeventvEmote(const SeventvEventAPIEmoteUpdateDispatch &dispatch);
|
||||
void updateSeventvEmote(
|
||||
const seventv::eventapi::EmoteUpdateDispatch &dispatch);
|
||||
/** 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. */
|
||||
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)
|
||||
void updateSeventvData(const QString &newUserID,
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
#include "messages/Message.hpp"
|
||||
#include "messages/MessageBuilder.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/twitch/api/Helix.hpp"
|
||||
#include "providers/twitch/ChannelPointReward.hpp"
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
#include <QString>
|
||||
#include <QVariant>
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
struct Emote;
|
||||
|
|
|
@ -142,6 +142,7 @@ void Paths::initSubDirectories()
|
|||
this->miscDirectory = makePath("Misc");
|
||||
this->twitchProfileAvatars = makePath("ProfileAvatars");
|
||||
this->pluginsDirectory = makePath("Plugins");
|
||||
this->crashdumpDirectory = makePath("Crashes");
|
||||
//QDir().mkdir(this->twitchProfileAvatars + "/twitch");
|
||||
}
|
||||
|
||||
|
|
|
@ -25,6 +25,9 @@ public:
|
|||
// Directory for miscellaneous files. Same as <appDataDirectory>/Misc
|
||||
QString miscDirectory;
|
||||
|
||||
// Directory for crashdumps. Same as <appDataDirectory>/Crashes
|
||||
QString crashdumpDirectory;
|
||||
|
||||
// Hash of QCoreApplication::applicationFilePath()
|
||||
QString applicationFilePathHash;
|
||||
|
||||
|
|
|
@ -288,6 +288,13 @@ public:
|
|||
QStringSetting selfHighlightColor = {"/highlighting/selfHighlightColor",
|
||||
""};
|
||||
|
||||
BoolSetting enableSelfMessageHighlight = {
|
||||
"/highlighting/selfMessageHighlight/enabled", false};
|
||||
BoolSetting showSelfMessageHighlightInMentions = {
|
||||
"/highlighting/selfMessageHighlight/showInMentions", false};
|
||||
QStringSetting selfMessageHighlightColor = {
|
||||
"/highlighting/selfMessageHighlight/color", ""};
|
||||
|
||||
BoolSetting enableWhisperHighlight = {
|
||||
"/highlighting/whisperHighlight/whispersHighlighted", true};
|
||||
BoolSetting enableWhisperHighlightSound = {
|
||||
|
|
|
@ -1,15 +1,17 @@
|
|||
#include "util/IncognitoBrowser.hpp"
|
||||
#ifdef USEWINSDK
|
||||
# include "util/WindowsHelper.hpp"
|
||||
#endif
|
||||
|
||||
#include <QProcess>
|
||||
#include <QRegularExpression>
|
||||
#include <QSettings>
|
||||
#include <QVariant>
|
||||
|
||||
namespace {
|
||||
|
||||
using namespace chatterino;
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#ifdef USEWINSDK
|
||||
QString injectPrivateSwitch(QString command)
|
||||
{
|
||||
// list of command line switches to turn on private browsing in browsers
|
||||
|
@ -47,23 +49,27 @@ QString injectPrivateSwitch(QString command)
|
|||
|
||||
QString getCommand()
|
||||
{
|
||||
// get default browser prog id
|
||||
auto browserId = QSettings("HKEY_CURRENT_"
|
||||
"USER\\Software\\Microsoft\\Windows\\Shell\\"
|
||||
"Associations\\UrlAssociatio"
|
||||
"ns\\http\\UserChoice",
|
||||
QSettings::NativeFormat)
|
||||
.value("Progid")
|
||||
.toString();
|
||||
// get default browser start command, by protocol if possible, falling back to extension if not
|
||||
QString command =
|
||||
getAssociatedCommand(AssociationQueryType::Protocol, L"http");
|
||||
|
||||
// get default browser start command
|
||||
auto command =
|
||||
QSettings("HKEY_CLASSES_ROOT\\" + browserId + "\\shell\\open\\command",
|
||||
QSettings::NativeFormat)
|
||||
.value("Default")
|
||||
.toString();
|
||||
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();
|
||||
}
|
||||
|
||||
|
@ -84,7 +90,7 @@ namespace chatterino {
|
|||
|
||||
bool supportsIncognitoLinks()
|
||||
{
|
||||
#ifdef Q_OS_WIN
|
||||
#ifdef USEWINSDK
|
||||
return !getCommand().isNull();
|
||||
#else
|
||||
return false;
|
||||
|
@ -93,7 +99,7 @@ bool supportsIncognitoLinks()
|
|||
|
||||
bool openLinkIncognito(const QString &link)
|
||||
{
|
||||
#ifdef Q_OS_WIN
|
||||
#ifdef USEWINSDK
|
||||
auto command = getCommand();
|
||||
|
||||
// TODO: split command into program path and incognito argument
|
||||
|
|
|
@ -6,6 +6,9 @@
|
|||
|
||||
#ifdef USEWINSDK
|
||||
|
||||
# include <Shlwapi.h>
|
||||
# include <VersionHelpers.h>
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
typedef enum MONITOR_DPI_TYPE {
|
||||
|
@ -17,6 +20,8 @@ typedef enum MONITOR_DPI_TYPE {
|
|||
|
||||
typedef HRESULT(CALLBACK *GetDpiForMonitor_)(HMONITOR, MONITOR_DPI_TYPE, UINT *,
|
||||
UINT *);
|
||||
typedef HRESULT(CALLBACK *AssocQueryString_)(ASSOCF, ASSOCSTR, LPCWSTR, LPCWSTR,
|
||||
LPWSTR, DWORD *);
|
||||
|
||||
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
|
||||
|
||||
#endif
|
||||
|
|
|
@ -7,12 +7,16 @@
|
|||
|
||||
namespace chatterino {
|
||||
|
||||
enum class AssociationQueryType { Protocol, FileExtension };
|
||||
|
||||
boost::optional<UINT> getWindowDpi(HWND hwnd);
|
||||
void flushClipboard();
|
||||
|
||||
bool isRegisteredForStartup();
|
||||
void setRegisteredForStartup(bool isRegistered);
|
||||
|
||||
QString getAssociatedCommand(AssociationQueryType queryType, LPCWSTR query);
|
||||
|
||||
} // namespace chatterino
|
||||
|
||||
#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;
|
||||
}
|
||||
|
|
|
@ -57,7 +57,11 @@ signals:
|
|||
|
||||
protected:
|
||||
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 mousePressEvent(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 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 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)
|
||||
#endif
|
||||
{
|
||||
this->mouseOver_ = true;
|
||||
|
||||
|
|
|
@ -61,7 +61,11 @@ protected:
|
|||
virtual void mousePressEvent(QMouseEvent *event) override;
|
||||
virtual void mouseReleaseEvent(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 dragEnterEvent(QDragEnterEvent *event) override;
|
||||
|
|
|
@ -119,6 +119,11 @@ AboutPage::AboutPage()
|
|||
addLicense(form.getElement(), "Fluent icons",
|
||||
"https://github.com/microsoft/fluentui-system-icons",
|
||||
":/licenses/fluenticons.txt");
|
||||
#endif
|
||||
#ifdef CHATTERINO_WITH_CRASHPAD
|
||||
addLicense(form.getElement(), "sentry-crashpad",
|
||||
"https://github.com/getsentry/crashpad",
|
||||
":/licenses/crashpad.txt");
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
@ -822,7 +822,11 @@ void Split::resizeEvent(QResizeEvent *event)
|
|||
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;
|
||||
|
||||
|
|
|
@ -105,7 +105,11 @@ protected:
|
|||
void keyPressEvent(QKeyEvent *event) override;
|
||||
void keyReleaseEvent(QKeyEvent *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 dragEnterEvent(QDragEnterEvent *event) override;
|
||||
|
|
|
@ -945,7 +945,11 @@ void SplitHeader::mouseDoubleClickEvent(QMouseEvent *event)
|
|||
this->doubleClicked_ = true;
|
||||
}
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
void SplitHeader::enterEvent(QEnterEvent *event)
|
||||
#else
|
||||
void SplitHeader::enterEvent(QEvent *event)
|
||||
#endif
|
||||
{
|
||||
if (!this->tooltipText_.isEmpty())
|
||||
{
|
||||
|
|
|
@ -42,7 +42,11 @@ protected:
|
|||
void mousePressEvent(QMouseEvent *event) override;
|
||||
void mouseReleaseEvent(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;
|
||||
#endif
|
||||
void leaveEvent(QEvent *event) override;
|
||||
void mouseDoubleClickEvent(QMouseEvent *event) override;
|
||||
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
#include "providers/seventv/SeventvEventAPI.hpp"
|
||||
|
||||
#include "providers/seventv/eventapi/SeventvEventAPIClient.hpp"
|
||||
#include "providers/seventv/eventapi/SeventvEventAPIDispatch.hpp"
|
||||
#include "providers/seventv/eventapi/SeventvEventAPIMessage.hpp"
|
||||
#include "providers/seventv/eventapi/Client.hpp"
|
||||
#include "providers/seventv/eventapi/Dispatch.hpp"
|
||||
#include "providers/seventv/eventapi/Message.hpp"
|
||||
|
||||
#include <boost/optional.hpp>
|
||||
#include <gtest/gtest.h>
|
||||
#include <QString>
|
||||
|
||||
using namespace chatterino;
|
||||
using namespace chatterino::seventv::eventapi;
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
const QString EMOTE_SET_A = "60b39e943e203cc169dfc106";
|
||||
|
@ -21,10 +22,10 @@ TEST(SeventvEventAPI, AllEvents)
|
|||
auto *eventAPI = new SeventvEventAPI(host, std::chrono::milliseconds(1000));
|
||||
eventAPI->start();
|
||||
|
||||
boost::optional<SeventvEventAPIEmoteAddDispatch> addDispatch;
|
||||
boost::optional<SeventvEventAPIEmoteUpdateDispatch> updateDispatch;
|
||||
boost::optional<SeventvEventAPIEmoteRemoveDispatch> removeDispatch;
|
||||
boost::optional<SeventvEventAPIUserConnectionUpdateDispatch> userDispatch;
|
||||
boost::optional<EmoteAddDispatch> addDispatch;
|
||||
boost::optional<EmoteUpdateDispatch> updateDispatch;
|
||||
boost::optional<EmoteRemoveDispatch> removeDispatch;
|
||||
boost::optional<UserConnectionUpdateDispatch> userDispatch;
|
||||
|
||||
eventAPI->signals_.emoteAdded.connect([&](const auto &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",
|
||||
"version": "2.0.0",
|
||||
"builtin-baseline": "5ba2b95aea2a39aa89444949c7a047af38c401c1",
|
||||
"builtin-baseline": "43f56137beabcd470ac2650cdf3954761f65b70e",
|
||||
"dependencies": [
|
||||
"benchmark",
|
||||
"boost-asio",
|
||||
|
@ -14,7 +14,6 @@
|
|||
"boost-variant",
|
||||
"gtest",
|
||||
"openssl",
|
||||
"qt5-multimedia",
|
||||
"qt5-tools"
|
||||
],
|
||||
"overrides": [
|
||||
|
|
Loading…
Reference in a new issue