mirror of
https://github.com/Chatterino/chatterino2.git
synced 2024-11-13 19:49:51 +01:00
Merge branch 'master' of github.com:Chatterino/chatterino2 into chore/leak-less-memory-lua
This commit is contained in:
commit
9f5a287861
|
@ -12,7 +12,7 @@ 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)
|
||||
# Get the Ubuntu Release (e.g. 20.04, 22.04 or 24.04)
|
||||
ubuntu_release="$(lsb_release -rs)"
|
||||
|
||||
# The final path where we'll save the .deb package
|
||||
|
@ -22,11 +22,15 @@ deb_path="Chatterino-ubuntu-${ubuntu_release}-x86_64.deb"
|
|||
case "$ubuntu_release" in
|
||||
20.04)
|
||||
# Qt6 static-linked deb, see https://github.com/Chatterino/docker
|
||||
dependencies="libc6, libstdc++6, libblkid1, libbsd0, libc6, libexpat1, libffi7, libfontconfig1, libfreetype6, libglib2.0-0, libglvnd0, libglx0, libgraphite2-3, libharfbuzz0b, libicu66, libjpeg-turbo8, libmount1, libopengl0, libpcre2-16-0, libpcre3, libpng16-16, libselinux1, libssl1.1, libstdc++6, libuuid1, libx11-xcb1, libxau6, libxcb1, libxcb-cursor0, libxcb-glx0, libxcb-icccm4, libxcb-image0, libxcb-keysyms1, libxcb-randr0, libxcb-render0, libxcb-render-util0, libxcb-shape0, libxcb-shm0, libxcb-sync1, libxcb-util1, libxcb-xfixes0, libxcb-xkb1, libxdmcp6, libxkbcommon0, libxkbcommon-x11-0, zlib1g"
|
||||
dependencies="libc6, libstdc++6, libblkid1, libbsd0, libexpat1, libffi7, libfontconfig1, libfreetype6, libglib2.0-0, libglvnd0, libglx0, libgraphite2-3, libharfbuzz0b, libicu66, libjpeg-turbo8, libmount1, libopengl0, libpcre2-16-0, libpcre3, libpng16-16, libselinux1, libssl1.1, libuuid1, libx11-xcb1, libxau6, libxcb1, libxcb-cursor0, libxcb-glx0, libxcb-icccm4, libxcb-image0, libxcb-keysyms1, libxcb-randr0, libxcb-render0, libxcb-render-util0, libxcb-shape0, libxcb-shm0, libxcb-sync1, libxcb-util1, libxcb-xfixes0, libxcb-xkb1, libxdmcp6, libxkbcommon0, libxkbcommon-x11-0, zlib1g"
|
||||
;;
|
||||
22.04)
|
||||
# Qt6 static-linked deb, see https://github.com/Chatterino/docker
|
||||
dependencies="libc6, libstdc++6, libglx0, libopengl0, libpng16-16, libharfbuzz0b, libfreetype6, libfontconfig1, libjpeg-turbo8, libxcb-glx0, libegl1, libx11-6, libxkbcommon0, libx11-xcb1, libxkbcommon-x11-0, libxcb-cursor0, libxcb-icccm4, libxcb-image0, libxcb-keysyms1, libxcb-randr0, libxcb-render-util0, libxcb-shm0, libxcb-sync1, libxcb-xfixes0, libxcb-render0, libxcb-shape0, libxcb-xkb1, libxcb1, libbrotli1, libglib2.0-0, zlib1g, libicu70, libpcre2-16-0, libssl3, libgraphite2-3, libexpat1, libuuid1, libxcb-util1, libxau6, libxdmcp6, libbrotli1, libffi8, libmount1, libselinux1, libpcre3, libicu70, libbsd0, libblkid1, libpcre2-8-0, libmd0"
|
||||
dependencies="libc6, libstdc++6, libglx0, libopengl0, libpng16-16, libharfbuzz0b, libfreetype6, libfontconfig1, libjpeg-turbo8, libxcb-glx0, libegl1, libx11-6, libxkbcommon0, libx11-xcb1, libxkbcommon-x11-0, libxcb-cursor0, libxcb-icccm4, libxcb-image0, libxcb-keysyms1, libxcb-randr0, libxcb-render-util0, libxcb-shm0, libxcb-sync1, libxcb-xfixes0, libxcb-render0, libxcb-shape0, libxcb-xkb1, libxcb1, libbrotli1, libglib2.0-0, zlib1g, libicu70, libpcre2-16-0, libssl3, libgraphite2-3, libexpat1, libuuid1, libxcb-util1, libxau6, libxdmcp6, libffi8, libmount1, libselinux1, libpcre3, libbsd0, libblkid1, libpcre2-8-0, libmd0"
|
||||
;;
|
||||
24.04)
|
||||
# Qt6 static-linked deb, see https://github.com/Chatterino/docker
|
||||
dependencies="libc6, libstdc++6, libglx0, libopengl0, libpng16-16, libharfbuzz0b, libfreetype6, libfontconfig1, libjpeg-turbo8, libxcb-glx0, libegl1, libx11-6, libxkbcommon0, libx11-xcb1, libxkbcommon-x11-0, libxcb-cursor0, libxcb-icccm4, libxcb-image0, libxcb-keysyms1, libxcb-randr0, libxcb-render-util0, libxcb-shm0, libxcb-sync1, libxcb-xfixes0, libxcb-render0, libxcb-shape0, libxcb-xkb1, libxcb1, libbrotli1, libglib2.0-0, zlib1g, libicu74, libpcre2-16-0, libssl3, libgraphite2-3, libexpat1, libuuid1, libxcb-util1, libxau6, libxdmcp6, libffi8, libmount1, libselinux1, libpcre3, libbsd0, libblkid1, libpcre2-8-0, libmd0"
|
||||
;;
|
||||
*)
|
||||
echo "Unsupported Ubuntu release $ubuntu_release"
|
||||
|
|
61
.CI/format-recent-changes.py
Normal file
61
.CI/format-recent-changes.py
Normal file
|
@ -0,0 +1,61 @@
|
|||
from datetime import datetime, timezone
|
||||
import os
|
||||
import subprocess
|
||||
import re
|
||||
|
||||
LINE_REGEX = re.compile(
|
||||
r"""(?x)
|
||||
^(?P<commit>[A-Fa-f0-9]+)\s+
|
||||
\(
|
||||
<(?P<email>[^>]+)>\s+
|
||||
(?P<date>[^\s]+\s[^\s]+\s[^\s]+)\s+
|
||||
(?P<line>\d+)
|
||||
\)\s
|
||||
(?P<content>.*)$
|
||||
"""
|
||||
)
|
||||
VERSION_REGEX = re.compile(r"^#+\s*v?\d")
|
||||
|
||||
# contains lines in the form of
|
||||
# {commit-sha} (<{email}>\s+{date}\s+{line-no}) {line}
|
||||
p = subprocess.run(
|
||||
["git", "blame", "-e", "--date=iso", "../CHANGELOG.md"],
|
||||
cwd=os.path.dirname(os.path.realpath(__file__)),
|
||||
text=True,
|
||||
check=True,
|
||||
capture_output=True,
|
||||
)
|
||||
|
||||
unreleased_lines: list[tuple[datetime, str]] = []
|
||||
for line in p.stdout.splitlines():
|
||||
if not line:
|
||||
continue
|
||||
m = LINE_REGEX.match(line)
|
||||
assert m, f"Failed to match '{line}'"
|
||||
content = m.group("content")
|
||||
|
||||
if not content:
|
||||
continue
|
||||
if content.startswith("#"):
|
||||
if VERSION_REGEX.match(content):
|
||||
break
|
||||
continue # ignore lines with '#'
|
||||
|
||||
d = datetime.fromisoformat(m.group("date"))
|
||||
d = d.astimezone(tz=timezone.utc)
|
||||
content = content.replace("- ", f"- [{d.strftime('%Y-%m-%d')}] ", 1)
|
||||
unreleased_lines.append((d, content))
|
||||
|
||||
unreleased_lines.sort(key=lambda it: it[0], reverse=True)
|
||||
|
||||
if len(unreleased_lines) == 0:
|
||||
print("No changes since last release.")
|
||||
|
||||
for _, line in unreleased_lines[:5]:
|
||||
print(line)
|
||||
|
||||
if len(unreleased_lines) > 5:
|
||||
print("<details><summary>More Changes</summary>\n")
|
||||
for _, line in unreleased_lines[5:]:
|
||||
print(line)
|
||||
print("</details>")
|
|
@ -42,7 +42,7 @@ CheckOptions:
|
|||
- key: readability-identifier-naming.FunctionCase
|
||||
value: camelBack
|
||||
- key: readability-identifier-naming.FunctionIgnoredRegexp
|
||||
value: ^TEST$
|
||||
value: ^(TEST|MOCK_METHOD)$
|
||||
|
||||
- key: readability-identifier-naming.MemberCase
|
||||
value: camelBack
|
||||
|
@ -73,3 +73,6 @@ CheckOptions:
|
|||
|
||||
- key: misc-const-correctness.AnalyzeValues
|
||||
value: false
|
||||
|
||||
- key: cppcoreguidelines-special-member-functions.AllowSoleDefaultDtor
|
||||
value: true
|
||||
|
|
7
.codecov.yml
Normal file
7
.codecov.yml
Normal file
|
@ -0,0 +1,7 @@
|
|||
comment: false
|
||||
ignore:
|
||||
- "/usr/local*/**/*"
|
||||
- "/usr/include/**/*"
|
||||
- "lib/"
|
||||
- "**/ui_*.h"
|
||||
- "**/moc_*.cpp"
|
45
.github/workflows/build.yml
vendored
45
.github/workflows/build.yml
vendored
|
@ -33,7 +33,7 @@ jobs:
|
|||
include:
|
||||
- os: ubuntu-20.04
|
||||
container: ghcr.io/chatterino/chatterino2-build-ubuntu-20.04:latest
|
||||
qt-version: 6.6.1
|
||||
qt-version: 6.7.2
|
||||
force-lto: false
|
||||
plugins: true
|
||||
skip-artifact: false
|
||||
|
@ -42,13 +42,22 @@ jobs:
|
|||
build-deb: true
|
||||
- os: ubuntu-22.04
|
||||
container: ghcr.io/chatterino/chatterino2-build-ubuntu-22.04:latest
|
||||
qt-version: 6.6.1
|
||||
qt-version: 6.7.2
|
||||
force-lto: false
|
||||
plugins: true
|
||||
skip-artifact: false
|
||||
skip-crashpad: false
|
||||
build-appimage: true
|
||||
build-deb: true
|
||||
- os: ubuntu-24.04
|
||||
container: ghcr.io/chatterino/chatterino2-build-ubuntu-24.04:latest
|
||||
qt-version: 6.7.2
|
||||
force-lto: false
|
||||
plugins: true
|
||||
skip-artifact: false
|
||||
skip-crashpad: false
|
||||
build-appimage: false
|
||||
build-deb: true
|
||||
env:
|
||||
C2_ENABLE_LTO: ${{ matrix.force-lto }}
|
||||
C2_PLUGINS: ${{ matrix.plugins }}
|
||||
|
@ -176,7 +185,7 @@ jobs:
|
|||
|
||||
- name: Setup sccache (Windows)
|
||||
# sccache v0.7.4
|
||||
uses: hendrikmuhs/ccache-action@v1.2.13
|
||||
uses: hendrikmuhs/ccache-action@v1.2.14
|
||||
if: startsWith(matrix.os, 'windows')
|
||||
with:
|
||||
variant: sccache
|
||||
|
@ -360,19 +369,25 @@ jobs:
|
|||
- uses: actions/download-artifact@v4
|
||||
name: Linux AppImage
|
||||
with:
|
||||
name: Chatterino-x86_64-Qt-6.6.1.AppImage
|
||||
name: Chatterino-x86_64-Qt-6.7.2.AppImage
|
||||
path: release-artifacts/
|
||||
|
||||
- uses: actions/download-artifact@v4
|
||||
name: Ubuntu 20.04 deb
|
||||
with:
|
||||
name: Chatterino-ubuntu-20.04-Qt-6.6.1.deb
|
||||
name: Chatterino-ubuntu-20.04-Qt-6.7.2.deb
|
||||
path: release-artifacts/
|
||||
|
||||
- uses: actions/download-artifact@v4
|
||||
name: Ubuntu 22.04 deb
|
||||
with:
|
||||
name: Chatterino-ubuntu-22.04-Qt-6.6.1.deb
|
||||
name: Chatterino-ubuntu-22.04-Qt-6.7.2.deb
|
||||
path: release-artifacts/
|
||||
|
||||
- uses: actions/download-artifact@v4
|
||||
name: Ubuntu 24.04 deb
|
||||
with:
|
||||
name: Chatterino-ubuntu-24.04-Qt-6.7.2.deb
|
||||
path: release-artifacts/
|
||||
|
||||
- name: Copy flatpakref
|
||||
|
@ -389,6 +404,22 @@ jobs:
|
|||
working-directory: release-artifacts
|
||||
shell: bash
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.12"
|
||||
|
||||
- name: Format changes
|
||||
id: format-changes
|
||||
run: |
|
||||
delimiter=$(openssl rand -hex 32)
|
||||
{
|
||||
echo "changelog<<$delimiter"
|
||||
python3 ./.CI/format-recent-changes.py
|
||||
echo $delimiter
|
||||
} >> "$GITHUB_OUTPUT"
|
||||
shell: bash
|
||||
|
||||
- name: Create release
|
||||
uses: ncipollo/release-action@v1.14.0
|
||||
with:
|
||||
|
@ -396,7 +427,7 @@ jobs:
|
|||
allowUpdates: true
|
||||
artifactErrorsFailBuild: true
|
||||
artifacts: "release-artifacts/*"
|
||||
body: ${{ github.event.head_commit.message }}
|
||||
body: ${{ steps.format-changes.outputs.changelog }}
|
||||
prerelease: true
|
||||
name: Nightly Release
|
||||
tag: nightly-build
|
||||
|
|
2
.github/workflows/check-formatting.yml
vendored
2
.github/workflows/check-formatting.yml
vendored
|
@ -26,7 +26,7 @@ jobs:
|
|||
run: sudo apt-get -y install dos2unix
|
||||
|
||||
- name: Check formatting
|
||||
uses: DoozyX/clang-format-lint-action@v0.17
|
||||
uses: DoozyX/clang-format-lint-action@v0.18
|
||||
with:
|
||||
source: "./src ./tests/src ./benchmarks/src ./mocks/include"
|
||||
extensions: "hpp,cpp"
|
||||
|
|
2
.github/workflows/clang-tidy.yml
vendored
2
.github/workflows/clang-tidy.yml
vendored
|
@ -45,7 +45,7 @@ jobs:
|
|||
build_dir: build-clang-tidy
|
||||
config_file: ".clang-tidy"
|
||||
split_workflow: true
|
||||
exclude: "lib/*,tools/crash-handler/*"
|
||||
exclude: "lib/*,tools/crash-handler/*,mocks/*"
|
||||
cmake_command: >-
|
||||
./.CI/setup-clang-tidy.sh
|
||||
apt_packages: >-
|
||||
|
|
2
.github/workflows/test-macos.yml
vendored
2
.github/workflows/test-macos.yml
vendored
|
@ -23,7 +23,7 @@ jobs:
|
|||
strategy:
|
||||
matrix:
|
||||
os: [macos-13]
|
||||
qt-version: [5.15.2, 6.5.0]
|
||||
qt-version: [5.15.2, 6.7.1]
|
||||
plugins: [false]
|
||||
fail-fast: false
|
||||
env:
|
||||
|
|
2
.github/workflows/test-windows.yml
vendored
2
.github/workflows/test-windows.yml
vendored
|
@ -69,7 +69,7 @@ jobs:
|
|||
|
||||
- name: Setup sccache
|
||||
# sccache v0.7.4
|
||||
uses: hendrikmuhs/ccache-action@v1.2.13
|
||||
uses: hendrikmuhs/ccache-action@v1.2.14
|
||||
with:
|
||||
variant: sccache
|
||||
# only save on the default (master) branch
|
||||
|
|
22
.github/workflows/test.yml
vendored
22
.github/workflows/test.yml
vendored
|
@ -5,6 +5,10 @@ on:
|
|||
pull_request:
|
||||
workflow_dispatch:
|
||||
merge_group:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- main
|
||||
|
||||
env:
|
||||
TWITCH_PUBSUB_SERVER_TAG: v1.0.7
|
||||
|
@ -24,7 +28,7 @@ jobs:
|
|||
include:
|
||||
- os: "ubuntu-22.04"
|
||||
container: ghcr.io/chatterino/chatterino2-build-ubuntu-22.04:latest
|
||||
qt-version: 6.6.1
|
||||
qt-version: 6.7.1
|
||||
plugins: true
|
||||
fail-fast: false
|
||||
env:
|
||||
|
@ -39,11 +43,11 @@ jobs:
|
|||
- name: Create build directory (Ubuntu)
|
||||
run: mkdir build-test
|
||||
|
||||
- name: Install googlebench
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo apt -y install \
|
||||
libbenchmark-dev
|
||||
sudo DEBIAN_FRONTEND=noninteractive apt -y --no-install-recommends install \
|
||||
libbenchmark-dev gcovr gnupg
|
||||
|
||||
- name: Build (Ubuntu)
|
||||
run: |
|
||||
|
@ -55,6 +59,8 @@ jobs:
|
|||
-DCMAKE_PREFIX_PATH="$Qt6_DIR/lib/cmake" \
|
||||
-DBUILD_WITH_QT6="$C2_BUILD_WITH_QT6" \
|
||||
-DCHATTERINO_STATIC_QT_BUILD=On \
|
||||
-DCHATTERINO_GENERATE_COVERAGE=On \
|
||||
-DCMAKE_BUILD_TYPE=Debug \
|
||||
..
|
||||
cmake --build .
|
||||
working-directory: build-test
|
||||
|
@ -84,3 +90,11 @@ jobs:
|
|||
cd ../build-test
|
||||
ctest --repeat until-pass:4 --output-on-failure --exclude-regex ClassicEmoteNameFiltering
|
||||
working-directory: build-test
|
||||
|
||||
- name: Upload coverage reports to Codecov
|
||||
uses: codecov/codecov-action@v4.5.0
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
plugin: gcov
|
||||
fail_ci_if_error: true
|
||||
verbose: true
|
||||
|
|
44
CHANGELOG.md
44
CHANGELOG.md
|
@ -4,6 +4,8 @@
|
|||
|
||||
- Major: Release plugins alpha. (#5288)
|
||||
- Major: Improve high-DPI support on Windows. (#4868, #5391)
|
||||
- Minor: Removed the Ctrl+Shift+L hotkey for toggling the "live only" tab visibility state. (#5530)
|
||||
- Minor: Moved tab visibility control to a submenu, without any toggle actions. (#5530)
|
||||
- Minor: Add option to customise Moderation buttons with images. (#5369)
|
||||
- Minor: Colored usernames now update on the fly when changing the "Color @usernames" setting. (#5300)
|
||||
- Minor: Added `flags.action` filter variable, allowing you to filter on `/me` messages. (#5397)
|
||||
|
@ -15,8 +17,18 @@
|
|||
- Minor: Added support for Brave & google-chrome-stable browsers. (#5452)
|
||||
- Minor: Added drop indicator line while dragging in tables. (#5256)
|
||||
- Minor: Add channel points indication for new bits power-up redemptions. (#5471)
|
||||
- Minor: Added option to log streams by their ID, allowing for easier "per-stream" log analyzing. (#5507)
|
||||
- Minor: Added `/warn <username> <reason>` command for mods. This prevents the user from chatting until they acknowledge the warning. (#5474)
|
||||
- Minor: Introduce HTTP API for plugins. (#5383)
|
||||
- Minor: Added option to suppress live notifictions on startup. (#5388)
|
||||
- Minor: Improve appearance of reply button. (#5491)
|
||||
- Minor: Introduce HTTP API for plugins. (#5383, #5492, #5494)
|
||||
- Minor: Support more Firefox variants for incognito link opening. (#5503)
|
||||
- Minor: Replying to a message will now display the message being replied to. (#4350, #5519)
|
||||
- Minor: Links can now have prefixes and suffixes such as parentheses. (#5486, #5515)
|
||||
- Minor: Added support for scrolling in splits with touchscreen panning gestures. (#5524)
|
||||
- Minor: Removed experimental IRC support. (#5547)
|
||||
- Minor: Moderators can now see which mods start and cancel raids. (#5563)
|
||||
- Minor: The emote popup now reloads when Twitch emotes are reloaded. (#5580)
|
||||
- Bugfix: Fixed tab move animation occasionally failing to start after closing a tab. (#5426)
|
||||
- Bugfix: If a network request errors with 200 OK, Qt's error code is now reported instead of the HTTP status. (#5378)
|
||||
- Bugfix: Fixed restricted users usernames not being clickable. (#5405)
|
||||
|
@ -24,6 +36,17 @@
|
|||
- Bugfix: Fixed message history occasionally not loading after a sleep. (#5457)
|
||||
- Bugfix: Fixed a crash when tab completing while having an invalid plugin loaded. (#5401)
|
||||
- Bugfix: Fixed windows on Windows not saving correctly when snapping them to the edges. (#5478)
|
||||
- Bugfix: Fixed user info card popups adding duplicate line to log files. (#5499)
|
||||
- Bugfix: Fixed tooltips and input completion popups not working after moving a split. (#5541, #5576)
|
||||
- Bugfix: Fixed rare issue on shutdown where the client would hang. (#5557)
|
||||
- Bugfix: Fixed `/clearmessages` not working with more than one window. (#5489)
|
||||
- Bugfix: Fixed splits staying paused after unfocusing Chatterino in certain configurations. (#5504)
|
||||
- Bugfix: Links with invalid characters in the domain are no longer detected. (#5509)
|
||||
- Bugfix: Fixed janky selection for messages with RTL segments (selection is still wrong, but consistently wrong). (#5525)
|
||||
- Bugfix: Fixed event emotes not showing up in autocomplete and popups. (#5239, #5580)
|
||||
- Bugfix: Fixed tab visibility being controllable in the emote popup. (#5530)
|
||||
- Bugfix: Fixed account switch not being saved if no other settings were changed. (#5558)
|
||||
- Bugfix: Fixed some tooltips not being readable. (#5578)
|
||||
- Dev: Update Windows build from Qt 6.5.0 to Qt 6.7.1. (#5420)
|
||||
- Dev: Update vcpkg build Qt from 6.5.0 to 6.7.0, boost from 1.83.0 to 1.85.0, openssl from 3.1.3 to 3.3.0. (#5422)
|
||||
- Dev: Unsingletonize `ISoundController`. (#5462)
|
||||
|
@ -34,14 +57,33 @@
|
|||
- Dev: Refactor `TwitchIrcServer`, making it abstracted. (#5421, #5435)
|
||||
- Dev: Reduced the amount of scale events. (#5404, #5406)
|
||||
- Dev: Removed unused timegate settings. (#5361)
|
||||
- Dev: Add `Channel::addSystemMessage` helper function, allowing us to avoid the common `channel->addMessage(makeSystemMessage(...));` pattern. (#5500)
|
||||
- Dev: Unsingletonize `Resources2`. (#5460)
|
||||
- Dev: All Lua globals now show in the `c2` global in the LuaLS metadata. (#5385)
|
||||
- Dev: Images are now loaded in worker threads. (#5431)
|
||||
- Dev: Fixed broken `SignalVector::operator[]` implementation. (#5556)
|
||||
- Dev: Qt Creator now auto-configures Conan when loading the project and skips vcpkg. (#5305)
|
||||
- Dev: The MSVC CRT is now bundled with Chatterino as it depends on having a recent version installed. (#5447)
|
||||
- Dev: Refactor/unsingletonize `UserDataController`. (#5459)
|
||||
- Dev: Cleanup `BrowserExtension`. (#5465)
|
||||
- Dev: Deprecate Qt 5.12. (#5396)
|
||||
- Dev: Refactored `MessageFlag` into its own file. (#5549)
|
||||
- Dev: The running Qt version is now shown in the about page if it differs from the compiled version. (#5501)
|
||||
- Dev: `FlagsEnum` is now `constexpr`. (#5510)
|
||||
- Dev: Documented and added tests to RTL handling. (#5473)
|
||||
- Dev: Refactored 7TV/BTTV definitions out of `TwitchIrcServer` into `Application`. (#5532)
|
||||
- Dev: Refactored code that's responsible for deleting old update files. (#5535)
|
||||
- Dev: Cleanly exit on shutdown. (#5537)
|
||||
- Dev: Removed the `getTwitchAbstract` method in `Application`. (#5560)
|
||||
- Dev: Renamed threads created by Chatterino on Linux and Windows. (#5538, #5539, #5544)
|
||||
- Dev: Refactored a few `#define`s into `const(expr)` and cleaned includes. (#5527)
|
||||
- Dev: Added `FlagsEnum::isEmpty`. (#5550)
|
||||
- Dev: Prepared for Qt 6.8 by addressing some deprecations. (#5529)
|
||||
- Dev: Moved some responsibility away from Application into WindowManager. (#5551)
|
||||
- Dev: Fixed benchmarks segfaulting on run. (#5559)
|
||||
- Dev: Refactored `MessageBuilder` to be a single class. (#5548)
|
||||
- Dev: Recent changes are now shown in the nightly release description. (#5553, #5554)
|
||||
- Dev: The timer for `StreamerMode` is now destroyed on the correct thread. (#5571)
|
||||
|
||||
## 2.5.1
|
||||
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
#include "controllers/highlights/HighlightController.hpp"
|
||||
#include "controllers/highlights/HighlightPhrase.hpp"
|
||||
#include "messages/Message.hpp"
|
||||
#include "messages/SharedMessageBuilder.hpp"
|
||||
#include "mocks/EmptyApplication.hpp"
|
||||
#include "singletons/Settings.hpp"
|
||||
#include "messages/MessageBuilder.hpp"
|
||||
#include "mocks/BaseApplication.hpp"
|
||||
#include "mocks/UserData.hpp"
|
||||
#include "util/Helpers.hpp"
|
||||
|
||||
#include <benchmark/benchmark.h>
|
||||
|
@ -16,15 +16,16 @@
|
|||
|
||||
using namespace chatterino;
|
||||
|
||||
class BenchmarkMessageBuilder : public SharedMessageBuilder
|
||||
class BenchmarkMessageBuilder : public MessageBuilder
|
||||
{
|
||||
public:
|
||||
explicit BenchmarkMessageBuilder(
|
||||
Channel *_channel, const Communi::IrcPrivateMessage *_ircMessage,
|
||||
const MessageParseArgs &_args)
|
||||
: SharedMessageBuilder(_channel, _ircMessage, _args)
|
||||
: MessageBuilder(_channel, _ircMessage, _args)
|
||||
{
|
||||
}
|
||||
|
||||
virtual MessagePtr build()
|
||||
{
|
||||
// PARSE
|
||||
|
@ -47,9 +48,14 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
class MockApplication : mock::EmptyApplication
|
||||
class MockApplication : public mock::BaseApplication
|
||||
{
|
||||
public:
|
||||
MockApplication()
|
||||
: highlights(this->settings, &this->accounts)
|
||||
{
|
||||
}
|
||||
|
||||
AccountController *getAccounts() override
|
||||
{
|
||||
return &this->accounts;
|
||||
|
@ -59,16 +65,19 @@ public:
|
|||
return &this->highlights;
|
||||
}
|
||||
|
||||
IUserDataController *getUserData() override
|
||||
{
|
||||
return &this->userData;
|
||||
}
|
||||
|
||||
AccountController accounts;
|
||||
HighlightController highlights;
|
||||
// TODO: Figure this out
|
||||
mock::UserDataController userData;
|
||||
};
|
||||
|
||||
static void BM_HighlightTest(benchmark::State &state)
|
||||
{
|
||||
MockApplication mockApplication;
|
||||
QTemporaryDir settingsDir;
|
||||
Settings settings(settingsDir.path());
|
||||
|
||||
std::string message =
|
||||
R"(@badge-info=subscriber/34;badges=moderator/1,subscriber/24;color=#FF0000;display-name=테스트계정420;emotes=41:6-13,15-22;flags=;id=a3196c7e-be4c-4b49-9c5a-8b8302b50c2a;mod=1;room-id=11148817;subscriber=1;tmi-sent-ts=1590922213730;turbo=0;user-id=117166826;user-type=mod :testaccount_420!testaccount_420@testaccount_420.tmi.twitch.tv PRIVMSG #pajlada :-tags Kreygasm,Kreygasm (no space))";
|
||||
|
|
|
@ -5,8 +5,6 @@
|
|||
#include <QString>
|
||||
#include <QStringList>
|
||||
|
||||
#include <optional>
|
||||
|
||||
using namespace chatterino;
|
||||
|
||||
const QString INPUT = QStringLiteral(
|
||||
|
@ -14,8 +12,9 @@ const QString INPUT = QStringLiteral(
|
|||
"(or 2.4.2 if its out) "
|
||||
"https://github.com/Chatterino/chatterino2/releases/tag/nightly-build "
|
||||
"AlienPls https://www.youtube.com/watch?v=ELBBiBDcWc0 "
|
||||
"127.0.3 aaaa xd 256.256.256.256 AsdQwe xd 127.0.0.1 https://. https://.be "
|
||||
"https://a http://a.b https://a.be ftp://xdd.com "
|
||||
"127.0.3 aaaa xd 256.256.256.256 AsdQwe xd 127.0.0.1 https://. "
|
||||
"*https://.be "
|
||||
"https://a: http://a.b (https://a.be) ftp://xdd.com "
|
||||
"this is a text lol . ://foo.com //aa.de :/foo.de xd.XDDDDDD ");
|
||||
|
||||
static void BM_LinkParsing(benchmark::State &state)
|
||||
|
@ -24,15 +23,15 @@ static void BM_LinkParsing(benchmark::State &state)
|
|||
|
||||
// Make sure the TLDs are loaded
|
||||
{
|
||||
benchmark::DoNotOptimize(LinkParser("xd.com").result());
|
||||
benchmark::DoNotOptimize(linkparser::parse("xd.com"));
|
||||
}
|
||||
|
||||
for (auto _ : state)
|
||||
{
|
||||
for (auto word : words)
|
||||
for (const auto &word : words)
|
||||
{
|
||||
LinkParser parser(word);
|
||||
benchmark::DoNotOptimize(parser.result());
|
||||
auto parsed = linkparser::parse(word);
|
||||
benchmark::DoNotOptimize(parsed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
#include "controllers/accounts/AccountController.hpp"
|
||||
#include "controllers/highlights/HighlightController.hpp"
|
||||
#include "messages/Emote.hpp"
|
||||
#include "mocks/BaseApplication.hpp"
|
||||
#include "mocks/DisabledStreamerMode.hpp"
|
||||
#include "mocks/EmptyApplication.hpp"
|
||||
#include "mocks/LinkResolver.hpp"
|
||||
#include "mocks/TwitchIrcServer.hpp"
|
||||
#include "mocks/UserData.hpp"
|
||||
|
@ -32,9 +32,14 @@ using namespace literals;
|
|||
|
||||
namespace {
|
||||
|
||||
class MockApplication : mock::EmptyApplication
|
||||
class MockApplication : public mock::BaseApplication
|
||||
{
|
||||
public:
|
||||
MockApplication()
|
||||
: highlights(this->settings, &this->accounts)
|
||||
{
|
||||
}
|
||||
|
||||
IEmotes *getEmotes() override
|
||||
{
|
||||
return &this->emotes;
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#include "common/Args.hpp"
|
||||
#include "singletons/Resources.hpp"
|
||||
#include "singletons/Settings.hpp"
|
||||
|
||||
|
@ -16,10 +17,12 @@ int main(int argc, char **argv)
|
|||
|
||||
::benchmark::Initialize(&argc, argv);
|
||||
|
||||
Args args;
|
||||
|
||||
// Ensure settings are initialized before any benchmarks are run
|
||||
QTemporaryDir settingsDir;
|
||||
settingsDir.setAutoRemove(false); // we'll remove it manually
|
||||
chatterino::Settings settings(settingsDir.path());
|
||||
chatterino::Settings settings(args, settingsDir.path());
|
||||
|
||||
QTimer::singleShot(0, [&]() {
|
||||
::benchmark::RunSpecifiedBenchmarks();
|
||||
|
|
|
@ -51,7 +51,7 @@
|
|||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
"enum": ["FilesystemRead", "FilesystemWrite"]
|
||||
"enum": ["FilesystemRead", "FilesystemWrite", "Network"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -158,12 +158,27 @@ function c2.Channel.by_twitch_id(id) end
|
|||
|
||||
-- End src/controllers/plugins/api/ChannelRef.hpp
|
||||
|
||||
-- Begin src/controllers/plugins/api/HTTPRequest.hpp
|
||||
-- Begin src/controllers/plugins/api/HTTPResponse.hpp
|
||||
|
||||
---@class HTTPResponse
|
||||
---@field data string Data received from the server
|
||||
---@field status integer? HTTP Status code returned by the server
|
||||
---@field error string A somewhat human readable description of an error if such happened
|
||||
HTTPResponse = {}
|
||||
|
||||
--- Returns the data. This is not guaranteed to be encoded using any
|
||||
--- particular encoding scheme. It's just the bytes the server returned.
|
||||
---
|
||||
function HTTPResponse:data() end
|
||||
|
||||
--- Returns the status code.
|
||||
---
|
||||
function HTTPResponse:status() end
|
||||
|
||||
--- A somewhat human readable description of an error if such happened
|
||||
---
|
||||
function HTTPResponse:error() end
|
||||
|
||||
-- End src/controllers/plugins/api/HTTPResponse.hpp
|
||||
|
||||
-- Begin src/controllers/plugins/api/HTTPRequest.hpp
|
||||
|
||||
---@alias HTTPCallback fun(result: HTTPResponse): nil
|
||||
---@class HTTPRequest
|
||||
|
@ -213,26 +228,6 @@ function HTTPRequest.create(method, url) end
|
|||
|
||||
-- End src/controllers/plugins/api/HTTPRequest.hpp
|
||||
|
||||
-- Begin src/controllers/plugins/api/HTTPResponse.hpp
|
||||
|
||||
---@class HTTPResponse
|
||||
HTTPResponse = {}
|
||||
|
||||
--- Returns the data. This is not guaranteed to be encoded using any
|
||||
--- particular encoding scheme. It's just the bytes the server returned.
|
||||
---
|
||||
function HTTPResponse:data() end
|
||||
|
||||
--- Returns the status code.
|
||||
---
|
||||
function HTTPResponse:status() end
|
||||
|
||||
--- A somewhat human readable description of an error if such happened
|
||||
---
|
||||
function HTTPResponse:error() end
|
||||
|
||||
-- End src/controllers/plugins/api/HTTPResponse.hpp
|
||||
|
||||
-- Begin src/common/network/NetworkCommon.hpp
|
||||
|
||||
---@alias HTTPMethod integer
|
||||
|
|
|
@ -420,14 +420,6 @@ It returns something like: `"ConnectionRefusedError"`, `"401"`.
|
|||
This function returns the HTTP status code of the request or `nil` if there was
|
||||
an error before a status code could be received.
|
||||
|
||||
```lua
|
||||
{
|
||||
data = "This is the data received from the server as a string",
|
||||
status = 200, -- HTTP status code returned by the server or nil if no response was received because of an error
|
||||
error = "A somewhat human readable description of an error if such happened"
|
||||
}
|
||||
```
|
||||
|
||||
#### `HTTPRequest`
|
||||
|
||||
Allows you to send an HTTP request to a URL. Do not create requests that you
|
||||
|
@ -443,7 +435,7 @@ containing a valid URL (ex. `https://example.com/path/to/api`).
|
|||
```lua
|
||||
local req = c2.HTTPRequest.create(c2.HTTPMethod.Get, "https://example.com")
|
||||
req:on_success(function (res)
|
||||
print(res.data)
|
||||
print(res:data())
|
||||
end)
|
||||
req:execute()
|
||||
```
|
||||
|
@ -496,12 +488,12 @@ request:set_header("Content-Type", "text/plain")
|
|||
request:on_success(function (res)
|
||||
print('Success!')
|
||||
-- Data is in res.data
|
||||
print(res.status)
|
||||
print(res:status())
|
||||
end)
|
||||
request:on_error(function (res)
|
||||
print('Error!')
|
||||
print(res.status)
|
||||
print(res.error)
|
||||
print(res:status())
|
||||
print(res:error())
|
||||
end)
|
||||
request:finally(function ()
|
||||
print('Finally')
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 821c4818ade1aa4da56ac753285c159ce26fd597
|
||||
Subproject commit a78ce469b456c06103b3b30d4bd37e7bb80da30c
|
|
@ -1 +1 @@
|
|||
Subproject commit 182165b584dad130afaf4bcd25b8629799baea38
|
||||
Subproject commit f339d2f73730f8fee4412f5e4938717866ecef48
|
|
@ -1 +1 @@
|
|||
Subproject commit f3e9161ee61b47da71c7858e24efee9b0053357f
|
||||
Subproject commit 7011003eabf6ac95c19f523968a72771fc177fcd
|
76
mocks/include/mocks/BaseApplication.hpp
Normal file
76
mocks/include/mocks/BaseApplication.hpp
Normal file
|
@ -0,0 +1,76 @@
|
|||
#pragma once
|
||||
|
||||
#include "common/Args.hpp"
|
||||
#include "mocks/DisabledStreamerMode.hpp"
|
||||
#include "mocks/EmptyApplication.hpp"
|
||||
#include "providers/bttv/BttvLiveUpdates.hpp"
|
||||
#include "singletons/Fonts.hpp"
|
||||
#include "singletons/Settings.hpp"
|
||||
#include "singletons/Theme.hpp"
|
||||
|
||||
#include <QString>
|
||||
|
||||
namespace chatterino::mock {
|
||||
|
||||
/**
|
||||
* BaseApplication intends to be a mock application with a few more sane defaults, but with less configurability
|
||||
*/
|
||||
class BaseApplication : public EmptyApplication
|
||||
{
|
||||
public:
|
||||
BaseApplication()
|
||||
: settings(this->args, this->settingsDir.path())
|
||||
, updates(this->paths_, this->settings)
|
||||
, theme(this->paths_)
|
||||
, fonts(this->settings)
|
||||
{
|
||||
}
|
||||
|
||||
explicit BaseApplication(const QString &settingsData)
|
||||
: EmptyApplication(settingsData)
|
||||
, settings(this->args, this->settingsDir.path())
|
||||
, updates(this->paths_, this->settings)
|
||||
, theme(this->paths_)
|
||||
, fonts(this->settings)
|
||||
{
|
||||
}
|
||||
|
||||
Updates &getUpdates() override
|
||||
{
|
||||
return this->updates;
|
||||
}
|
||||
|
||||
IStreamerMode *getStreamerMode() override
|
||||
{
|
||||
return &this->streamerMode;
|
||||
}
|
||||
|
||||
Theme *getThemes() override
|
||||
{
|
||||
return &this->theme;
|
||||
}
|
||||
|
||||
Fonts *getFonts() override
|
||||
{
|
||||
return &this->fonts;
|
||||
}
|
||||
|
||||
BttvLiveUpdates *getBttvLiveUpdates() override
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SeventvEventAPI *getSeventvEventAPI() override
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Args args;
|
||||
Settings settings;
|
||||
Updates updates;
|
||||
DisabledStreamerMode streamerMode;
|
||||
Theme theme;
|
||||
Fonts fonts;
|
||||
};
|
||||
|
||||
} // namespace chatterino::mock
|
|
@ -9,4 +9,8 @@ public:
|
|||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void start() override
|
||||
{
|
||||
}
|
||||
};
|
||||
|
|
|
@ -12,12 +12,18 @@ namespace chatterino::mock {
|
|||
class EmptyApplication : public IApplication
|
||||
{
|
||||
public:
|
||||
EmptyApplication()
|
||||
: updates_(this->paths_)
|
||||
EmptyApplication() = default;
|
||||
|
||||
explicit EmptyApplication(const QString &settingsData)
|
||||
{
|
||||
QFile settingsFile(this->settingsDir.filePath("settings.json"));
|
||||
settingsFile.open(QIODevice::WriteOnly | QIODevice::Text);
|
||||
settingsFile.write(settingsData.toUtf8());
|
||||
settingsFile.flush();
|
||||
settingsFile.close();
|
||||
}
|
||||
|
||||
virtual ~EmptyApplication() = default;
|
||||
~EmptyApplication() override = default;
|
||||
|
||||
bool isTest() const override
|
||||
{
|
||||
|
@ -123,13 +129,6 @@ public:
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
IAbstractIrcServer *getTwitchAbstract() override
|
||||
{
|
||||
assert(false && "EmptyApplication::getTwitchAbstract was called "
|
||||
"without being initialized");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PubSub *getTwitchPubSub() override
|
||||
{
|
||||
assert(false && "getTwitchPubSub was called without being initialized");
|
||||
|
@ -209,11 +208,6 @@ public:
|
|||
}
|
||||
#endif
|
||||
|
||||
Updates &getUpdates() override
|
||||
{
|
||||
return this->updates_;
|
||||
}
|
||||
|
||||
BttvEmotes *getBttvEmotes() override
|
||||
{
|
||||
assert(false && "EmptyApplication::getBttvEmotes was called without "
|
||||
|
@ -221,6 +215,13 @@ public:
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
BttvLiveUpdates *getBttvLiveUpdates() override
|
||||
{
|
||||
assert(false && "EmptyApplication::getBttvLiveUpdates was called "
|
||||
"without being initialized");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
FfzEmotes *getFfzEmotes() override
|
||||
{
|
||||
assert(false && "EmptyApplication::getFfzEmotes was called without "
|
||||
|
@ -235,6 +236,13 @@ public:
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
SeventvEventAPI *getSeventvEventAPI() override
|
||||
{
|
||||
assert(false && "EmptyApplication::getSeventvEventAPI was called "
|
||||
"without being initialized");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ILinkResolver *getLinkResolver() override
|
||||
{
|
||||
assert(false && "EmptyApplication::getLinkResolver was called without "
|
||||
|
@ -249,11 +257,16 @@ public:
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
protected:
|
||||
ITwitchUsers *getTwitchUsers() override
|
||||
{
|
||||
assert(false && "EmptyApplication::getTwitchUsers was called without "
|
||||
"being initialized");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QTemporaryDir settingsDir;
|
||||
Paths paths_;
|
||||
Args args_;
|
||||
Updates updates_;
|
||||
};
|
||||
|
||||
} // namespace chatterino::mock
|
||||
|
|
|
@ -410,6 +410,23 @@ public:
|
|||
(FailureCallback<HelixSendMessageError, QString> failureCallback)),
|
||||
(override));
|
||||
|
||||
// get user emotes
|
||||
MOCK_METHOD(
|
||||
void, getUserEmotes,
|
||||
(QString userID, QString broadcasterID,
|
||||
(ResultCallback<std::vector<HelixChannelEmote>, HelixPaginationState>
|
||||
successCallback),
|
||||
FailureCallback<QString> failureCallback, CancellationToken &&token),
|
||||
(override));
|
||||
|
||||
// get followed channel
|
||||
MOCK_METHOD(
|
||||
void, getFollowedChannel,
|
||||
(QString userID, QString broadcasterID,
|
||||
ResultCallback<std::optional<HelixFollowedChannel>> successCallback,
|
||||
FailureCallback<QString> failureCallback),
|
||||
(override));
|
||||
|
||||
MOCK_METHOD(void, update, (QString clientId, QString oauthToken),
|
||||
(override));
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ public:
|
|||
|
||||
MOCK_METHOD(void, addMessage,
|
||||
(const QString &channelName, MessagePtr message,
|
||||
const QString &platformName),
|
||||
const QString &platformName, const QString &streamID),
|
||||
(override));
|
||||
};
|
||||
|
||||
|
@ -27,7 +27,8 @@ public:
|
|||
~EmptyLogging() override = default;
|
||||
|
||||
void addMessage(const QString &channelName, MessagePtr message,
|
||||
const QString &platformName) override
|
||||
const QString &platformName,
|
||||
const QString &streamID) override
|
||||
{
|
||||
//
|
||||
}
|
||||
|
|
|
@ -2,13 +2,11 @@
|
|||
|
||||
#include "mocks/Channel.hpp"
|
||||
#include "providers/bttv/BttvEmotes.hpp"
|
||||
#include "providers/bttv/BttvLiveUpdates.hpp"
|
||||
#include "providers/ffz/FfzEmotes.hpp"
|
||||
#include "providers/seventv/eventapi/Client.hpp"
|
||||
#include "providers/seventv/eventapi/Dispatch.hpp"
|
||||
#include "providers/seventv/eventapi/Message.hpp"
|
||||
#include "providers/seventv/SeventvEmotes.hpp"
|
||||
#include "providers/seventv/SeventvEventAPI.hpp"
|
||||
#include "providers/twitch/TwitchIrcServer.hpp"
|
||||
|
||||
namespace chatterino::mock {
|
||||
|
@ -28,6 +26,38 @@ public:
|
|||
{
|
||||
}
|
||||
|
||||
void connect() override
|
||||
{
|
||||
}
|
||||
|
||||
void sendRawMessage(const QString &rawMessage) override
|
||||
{
|
||||
}
|
||||
|
||||
ChannelPtr getOrAddChannel(const QString &dirtyChannelName) override
|
||||
{
|
||||
assert(false && "unimplemented getOrAddChannel in mock irc server");
|
||||
return {};
|
||||
}
|
||||
|
||||
ChannelPtr getChannelOrEmpty(const QString &dirtyChannelName) override
|
||||
{
|
||||
assert(false && "unimplemented getChannelOrEmpty in mock irc server");
|
||||
return {};
|
||||
}
|
||||
|
||||
void addFakeMessage(const QString &data) override
|
||||
{
|
||||
}
|
||||
|
||||
void addGlobalSystemMessage(const QString &messageText) override
|
||||
{
|
||||
}
|
||||
|
||||
void forEachChannel(std::function<void(ChannelPtr)> func) override
|
||||
{
|
||||
}
|
||||
|
||||
void forEachChannelAndSpecialChannels(
|
||||
std::function<void(ChannelPtr)> func) override
|
||||
{
|
||||
|
@ -46,16 +76,6 @@ public:
|
|||
//
|
||||
}
|
||||
|
||||
std::unique_ptr<BttvLiveUpdates> &getBTTVLiveUpdates() override
|
||||
{
|
||||
return this->bttvLiveUpdates;
|
||||
}
|
||||
|
||||
std::unique_ptr<SeventvEventAPI> &getSeventvEventAPI() override
|
||||
{
|
||||
return this->seventvEventAPI;
|
||||
}
|
||||
|
||||
const IndirectChannel &getWatchingChannel() const override
|
||||
{
|
||||
return this->watchingChannel;
|
||||
|
@ -103,9 +123,6 @@ public:
|
|||
ChannelPtr liveChannel;
|
||||
ChannelPtr automodChannel;
|
||||
QString lastUserThatWhisperedMe{"forsen"};
|
||||
|
||||
std::unique_ptr<BttvLiveUpdates> bttvLiveUpdates;
|
||||
std::unique_ptr<SeventvEventAPI> seventvEventAPI;
|
||||
};
|
||||
|
||||
} // namespace chatterino::mock
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,11 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "common/Singleton.hpp"
|
||||
#include "debug/AssertInGuiThread.hpp"
|
||||
#include "singletons/NativeMessaging.hpp"
|
||||
|
||||
#include <QApplication>
|
||||
|
||||
#include <cassert>
|
||||
#include <memory>
|
||||
|
||||
|
@ -51,19 +47,24 @@ class ImageUploader;
|
|||
class SeventvAPI;
|
||||
class CrashHandler;
|
||||
class BttvEmotes;
|
||||
class BttvLiveUpdates;
|
||||
class FfzEmotes;
|
||||
class SeventvEmotes;
|
||||
class SeventvEventAPI;
|
||||
class ILinkResolver;
|
||||
class IStreamerMode;
|
||||
class IAbstractIrcServer;
|
||||
class ITwitchUsers;
|
||||
|
||||
class IApplication
|
||||
{
|
||||
public:
|
||||
IApplication();
|
||||
virtual ~IApplication() = default;
|
||||
virtual ~IApplication();
|
||||
|
||||
static IApplication *instance;
|
||||
IApplication(const IApplication &) = delete;
|
||||
IApplication(IApplication &&) = delete;
|
||||
IApplication &operator=(const IApplication &) = delete;
|
||||
IApplication &operator=(IApplication &&) = delete;
|
||||
|
||||
virtual bool isTest() const = 0;
|
||||
|
||||
|
@ -81,7 +82,6 @@ public:
|
|||
virtual HighlightController *getHighlights() = 0;
|
||||
virtual NotificationController *getNotifications() = 0;
|
||||
virtual ITwitchIrcServer *getTwitch() = 0;
|
||||
virtual IAbstractIrcServer *getTwitchAbstract() = 0;
|
||||
virtual PubSub *getTwitchPubSub() = 0;
|
||||
virtual ILogging *getChatLogger() = 0;
|
||||
virtual IChatterinoBadges *getChatterinoBadges() = 0;
|
||||
|
@ -98,23 +98,23 @@ public:
|
|||
#endif
|
||||
virtual Updates &getUpdates() = 0;
|
||||
virtual BttvEmotes *getBttvEmotes() = 0;
|
||||
virtual BttvLiveUpdates *getBttvLiveUpdates() = 0;
|
||||
virtual FfzEmotes *getFfzEmotes() = 0;
|
||||
virtual SeventvEmotes *getSeventvEmotes() = 0;
|
||||
virtual SeventvEventAPI *getSeventvEventAPI() = 0;
|
||||
virtual ILinkResolver *getLinkResolver() = 0;
|
||||
virtual IStreamerMode *getStreamerMode() = 0;
|
||||
virtual ITwitchUsers *getTwitchUsers() = 0;
|
||||
};
|
||||
|
||||
class Application : public IApplication
|
||||
{
|
||||
const Paths &paths_;
|
||||
const Args &args_;
|
||||
std::vector<std::unique_ptr<Singleton>> singletons_;
|
||||
int argc_{};
|
||||
char **argv_{};
|
||||
|
||||
public:
|
||||
static Application *instance;
|
||||
|
||||
Application(Settings &_settings, const Paths &paths, const Args &_args,
|
||||
Updates &_updates);
|
||||
~Application() override;
|
||||
|
@ -129,51 +129,48 @@ public:
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* In the interim, before we remove _exit(0); from RunGui.cpp,
|
||||
* this will destroy things we know can be destroyed
|
||||
*/
|
||||
void fakeDtor();
|
||||
|
||||
void initialize(Settings &settings, const Paths &paths);
|
||||
void load();
|
||||
void save();
|
||||
|
||||
int run(QApplication &qtApp);
|
||||
int run();
|
||||
|
||||
friend void test();
|
||||
|
||||
private:
|
||||
Theme *const themes{};
|
||||
std::unique_ptr<Fonts> fonts{};
|
||||
Emotes *const emotes{};
|
||||
AccountController *const accounts{};
|
||||
HotkeyController *const hotkeys{};
|
||||
WindowManager *const windows{};
|
||||
Toasts *const toasts{};
|
||||
ImageUploader *const imageUploader{};
|
||||
SeventvAPI *const seventvAPI{};
|
||||
CrashHandler *const crashHandler{};
|
||||
CommandController *const commands{};
|
||||
NotificationController *const notifications{};
|
||||
HighlightController *const highlights{};
|
||||
std::unique_ptr<Theme> themes;
|
||||
std::unique_ptr<Fonts> fonts;
|
||||
std::unique_ptr<Emotes> emotes;
|
||||
std::unique_ptr<AccountController> accounts;
|
||||
std::unique_ptr<HotkeyController> hotkeys;
|
||||
std::unique_ptr<WindowManager> windows;
|
||||
std::unique_ptr<Toasts> toasts;
|
||||
std::unique_ptr<ImageUploader> imageUploader;
|
||||
std::unique_ptr<SeventvAPI> seventvAPI;
|
||||
std::unique_ptr<CrashHandler> crashHandler;
|
||||
std::unique_ptr<CommandController> commands;
|
||||
std::unique_ptr<NotificationController> notifications;
|
||||
std::unique_ptr<HighlightController> highlights;
|
||||
std::unique_ptr<TwitchIrcServer> twitch;
|
||||
FfzBadges *const ffzBadges{};
|
||||
SeventvBadges *const seventvBadges{};
|
||||
std::unique_ptr<FfzBadges> ffzBadges;
|
||||
std::unique_ptr<SeventvBadges> seventvBadges;
|
||||
std::unique_ptr<UserDataController> userData;
|
||||
std::unique_ptr<ISoundController> sound;
|
||||
TwitchLiveController *const twitchLiveController{};
|
||||
std::unique_ptr<TwitchLiveController> twitchLiveController;
|
||||
std::unique_ptr<PubSub> twitchPubSub;
|
||||
std::unique_ptr<TwitchBadges> twitchBadges;
|
||||
std::unique_ptr<ChatterinoBadges> chatterinoBadges;
|
||||
std::unique_ptr<BttvEmotes> bttvEmotes;
|
||||
std::unique_ptr<BttvLiveUpdates> bttvLiveUpdates;
|
||||
std::unique_ptr<FfzEmotes> ffzEmotes;
|
||||
std::unique_ptr<SeventvEmotes> seventvEmotes;
|
||||
std::unique_ptr<SeventvEventAPI> seventvEventAPI;
|
||||
const std::unique_ptr<Logging> logging;
|
||||
std::unique_ptr<ILinkResolver> linkResolver;
|
||||
std::unique_ptr<IStreamerMode> streamerMode;
|
||||
std::unique_ptr<ITwitchUsers> twitchUsers;
|
||||
#ifdef CHATTERINO_HAVE_PLUGINS
|
||||
PluginController *const plugins{};
|
||||
std::unique_ptr<PluginController> plugins;
|
||||
#endif
|
||||
|
||||
public:
|
||||
|
@ -197,7 +194,6 @@ public:
|
|||
NotificationController *getNotifications() override;
|
||||
HighlightController *getHighlights() override;
|
||||
ITwitchIrcServer *getTwitch() override;
|
||||
IAbstractIrcServer *getTwitchAbstract() override;
|
||||
PubSub *getTwitchPubSub() override;
|
||||
ILogging *getChatLogger() override;
|
||||
FfzBadges *getFfzBadges() override;
|
||||
|
@ -212,51 +208,29 @@ public:
|
|||
#ifdef CHATTERINO_HAVE_PLUGINS
|
||||
PluginController *getPlugins() override;
|
||||
#endif
|
||||
Updates &getUpdates() override
|
||||
{
|
||||
assertInGuiThread();
|
||||
|
||||
return this->updates;
|
||||
}
|
||||
Updates &getUpdates() override;
|
||||
|
||||
BttvEmotes *getBttvEmotes() override;
|
||||
BttvLiveUpdates *getBttvLiveUpdates() override;
|
||||
FfzEmotes *getFfzEmotes() override;
|
||||
SeventvEmotes *getSeventvEmotes() override;
|
||||
SeventvEventAPI *getSeventvEventAPI() override;
|
||||
|
||||
ILinkResolver *getLinkResolver() override;
|
||||
IStreamerMode *getStreamerMode() override;
|
||||
ITwitchUsers *getTwitchUsers() override;
|
||||
|
||||
private:
|
||||
void addSingleton(Singleton *singleton);
|
||||
void initPubSub();
|
||||
void initBttvLiveUpdates();
|
||||
void initSeventvEventAPI();
|
||||
void initNm(const Paths &paths);
|
||||
|
||||
template <typename T,
|
||||
typename = std::enable_if_t<std::is_base_of<Singleton, T>::value>>
|
||||
T &emplace()
|
||||
{
|
||||
auto t = new T;
|
||||
this->singletons_.push_back(std::unique_ptr<T>(t));
|
||||
return *t;
|
||||
}
|
||||
|
||||
template <typename T,
|
||||
typename = std::enable_if_t<std::is_base_of<Singleton, T>::value>>
|
||||
T &emplace(T *t)
|
||||
{
|
||||
this->singletons_.push_back(std::unique_ptr<T>(t));
|
||||
return *t;
|
||||
}
|
||||
|
||||
NativeMessagingServer nmServer{};
|
||||
NativeMessagingServer nmServer;
|
||||
Updates &updates;
|
||||
|
||||
bool initialized{false};
|
||||
};
|
||||
|
||||
Application *getApp();
|
||||
|
||||
// Get an interface version of the Application class - should be preferred when possible for new code
|
||||
IApplication *getIApp();
|
||||
IApplication *getApp();
|
||||
|
||||
} // namespace chatterino
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "BrowserExtension.hpp"
|
||||
|
||||
#include "singletons/NativeMessaging.hpp"
|
||||
#include "util/RenameThread.hpp"
|
||||
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
|
@ -69,6 +70,7 @@ void runLoop()
|
|||
std::this_thread::sleep_for(10s);
|
||||
}
|
||||
});
|
||||
renameThread(thread, "BrowserPingCheck");
|
||||
|
||||
while (true)
|
||||
{
|
||||
|
|
|
@ -276,12 +276,10 @@ set(SOURCE_FILES
|
|||
messages/MessageColor.hpp
|
||||
messages/MessageElement.cpp
|
||||
messages/MessageElement.hpp
|
||||
messages/MessageFlag.hpp
|
||||
messages/MessageThread.cpp
|
||||
messages/MessageThread.hpp
|
||||
|
||||
messages/SharedMessageBuilder.cpp
|
||||
messages/SharedMessageBuilder.hpp
|
||||
|
||||
messages/layouts/MessageLayout.cpp
|
||||
messages/layouts/MessageLayout.hpp
|
||||
messages/layouts/MessageLayoutContainer.cpp
|
||||
|
@ -338,22 +336,8 @@ set(SOURCE_FILES
|
|||
providers/ffz/FfzUtil.cpp
|
||||
providers/ffz/FfzUtil.hpp
|
||||
|
||||
providers/irc/AbstractIrcServer.cpp
|
||||
providers/irc/AbstractIrcServer.hpp
|
||||
providers/irc/Irc2.cpp
|
||||
providers/irc/Irc2.hpp
|
||||
providers/irc/IrcAccount.cpp
|
||||
providers/irc/IrcAccount.hpp
|
||||
providers/irc/IrcChannel2.cpp
|
||||
providers/irc/IrcChannel2.hpp
|
||||
providers/irc/IrcCommands.cpp
|
||||
providers/irc/IrcCommands.hpp
|
||||
providers/irc/IrcConnection2.cpp
|
||||
providers/irc/IrcConnection2.hpp
|
||||
providers/irc/IrcMessageBuilder.cpp
|
||||
providers/irc/IrcMessageBuilder.hpp
|
||||
providers/irc/IrcServer.cpp
|
||||
providers/irc/IrcServer.hpp
|
||||
|
||||
providers/links/LinkInfo.cpp
|
||||
providers/links/LinkInfo.hpp
|
||||
|
@ -418,10 +402,10 @@ set(SOURCE_FILES
|
|||
providers/twitch/TwitchHelpers.hpp
|
||||
providers/twitch/TwitchIrcServer.cpp
|
||||
providers/twitch/TwitchIrcServer.hpp
|
||||
providers/twitch/TwitchMessageBuilder.cpp
|
||||
providers/twitch/TwitchMessageBuilder.hpp
|
||||
providers/twitch/TwitchUser.cpp
|
||||
providers/twitch/TwitchUser.hpp
|
||||
providers/twitch/TwitchUsers.cpp
|
||||
providers/twitch/TwitchUsers.hpp
|
||||
|
||||
providers/twitch/pubsubmessages/AutoMod.cpp
|
||||
providers/twitch/pubsubmessages/AutoMod.hpp
|
||||
|
@ -444,8 +428,6 @@ set(SOURCE_FILES
|
|||
providers/twitch/api/Helix.cpp
|
||||
providers/twitch/api/Helix.hpp
|
||||
|
||||
singletons/Badges.cpp
|
||||
singletons/Badges.hpp
|
||||
singletons/CrashHandler.cpp
|
||||
singletons/CrashHandler.hpp
|
||||
singletons/Emotes.cpp
|
||||
|
@ -487,11 +469,11 @@ set(SOURCE_FILES
|
|||
util/ChannelHelpers.hpp
|
||||
util/Clipboard.cpp
|
||||
util/Clipboard.hpp
|
||||
util/ConcurrentMap.hpp
|
||||
util/DebugCount.cpp
|
||||
util/DebugCount.hpp
|
||||
util/DisplayBadge.cpp
|
||||
util/DisplayBadge.hpp
|
||||
util/Expected.hpp
|
||||
util/FormatTime.cpp
|
||||
util/FormatTime.hpp
|
||||
util/FunctionEventFilter.cpp
|
||||
|
@ -514,11 +496,12 @@ set(SOURCE_FILES
|
|||
util/RapidjsonHelpers.hpp
|
||||
util/RatelimitBucket.cpp
|
||||
util/RatelimitBucket.hpp
|
||||
util/RenameThread.cpp
|
||||
util/RenameThread.hpp
|
||||
util/SampleData.cpp
|
||||
util/SampleData.hpp
|
||||
util/SharedPtrElementLess.hpp
|
||||
util/SplitCommand.cpp
|
||||
util/SplitCommand.hpp
|
||||
util/SignalListener.hpp
|
||||
util/StreamLink.cpp
|
||||
util/StreamLink.hpp
|
||||
util/ThreadGuard.hpp
|
||||
|
@ -578,15 +561,10 @@ set(SOURCE_FILES
|
|||
widgets/dialogs/EditHotkeyDialog.hpp
|
||||
widgets/dialogs/EmotePopup.cpp
|
||||
widgets/dialogs/EmotePopup.hpp
|
||||
widgets/dialogs/IrcConnectionEditor.cpp
|
||||
widgets/dialogs/IrcConnectionEditor.hpp
|
||||
widgets/dialogs/IrcConnectionEditor.ui
|
||||
widgets/dialogs/LastRunCrashDialog.cpp
|
||||
widgets/dialogs/LastRunCrashDialog.hpp
|
||||
widgets/dialogs/LoginDialog.cpp
|
||||
widgets/dialogs/LoginDialog.hpp
|
||||
widgets/dialogs/NotificationPopup.cpp
|
||||
widgets/dialogs/NotificationPopup.hpp
|
||||
widgets/dialogs/QualityPopup.cpp
|
||||
widgets/dialogs/QualityPopup.hpp
|
||||
widgets/dialogs/ReplyThreadPopup.cpp
|
||||
|
@ -645,6 +623,8 @@ set(SOURCE_FILES
|
|||
widgets/helper/IconDelegate.hpp
|
||||
widgets/helper/InvisibleSizeGrip.cpp
|
||||
widgets/helper/InvisibleSizeGrip.hpp
|
||||
widgets/helper/MessageView.cpp
|
||||
widgets/helper/MessageView.hpp
|
||||
widgets/helper/NotebookButton.cpp
|
||||
widgets/helper/NotebookButton.hpp
|
||||
widgets/helper/NotebookTab.cpp
|
||||
|
@ -949,6 +929,10 @@ set_target_properties(${LIBRARY_PROJECT}
|
|||
# this is its own project.
|
||||
set(VERSION_SOURCE_FILES common/Version.cpp common/Version.hpp)
|
||||
add_library(${VERSION_PROJECT} STATIC ${VERSION_SOURCE_FILES})
|
||||
target_compile_definitions(${VERSION_PROJECT} PRIVATE
|
||||
$<$<BOOL:${WIN32}>:USEWINSDK>
|
||||
$<$<BOOL:${BUILD_WITH_CRASHPAD}>:CHATTERINO_WITH_CRASHPAD>
|
||||
)
|
||||
|
||||
# source group for IDEs
|
||||
source_group(TREE ${CMAKE_SOURCE_DIR} FILES ${VERSION_SOURCE_FILES})
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
# include <IrcCommand>
|
||||
# include <IrcConnection>
|
||||
# include <IrcMessage>
|
||||
# include <nonstd/expected.hpp>
|
||||
# include <pajlada/serialize.hpp>
|
||||
# include <pajlada/settings/setting.hpp>
|
||||
# include <pajlada/settings/settinglistener.hpp>
|
||||
|
@ -108,6 +109,7 @@
|
|||
# include <cinttypes>
|
||||
# include <climits>
|
||||
# include <cmath>
|
||||
# include <concepts>
|
||||
# include <cstdint>
|
||||
# include <ctime>
|
||||
# include <functional>
|
||||
|
|
|
@ -41,7 +41,7 @@ namespace {
|
|||
{
|
||||
// borrowed from
|
||||
// https://stackoverflow.com/questions/15035767/is-the-qt-5-dark-fusion-theme-available-for-windows
|
||||
auto dark = qApp->palette();
|
||||
auto dark = QApplication::palette();
|
||||
|
||||
dark.setColor(QPalette::Window, QColor(22, 22, 22));
|
||||
dark.setColor(QPalette::WindowText, Qt::white);
|
||||
|
@ -49,7 +49,7 @@ namespace {
|
|||
dark.setColor(QPalette::Base, QColor("#333"));
|
||||
dark.setColor(QPalette::AlternateBase, QColor("#444"));
|
||||
dark.setColor(QPalette::ToolTipBase, Qt::white);
|
||||
dark.setColor(QPalette::ToolTipText, Qt::white);
|
||||
dark.setColor(QPalette::ToolTipText, Qt::black);
|
||||
dark.setColor(QPalette::Dark, QColor(35, 35, 35));
|
||||
dark.setColor(QPalette::Shadow, QColor(20, 20, 20));
|
||||
dark.setColor(QPalette::Button, QColor(70, 70, 70));
|
||||
|
@ -71,7 +71,7 @@ namespace {
|
|||
dark.setColor(QPalette::Disabled, QPalette::WindowText,
|
||||
QColor(127, 127, 127));
|
||||
|
||||
qApp->setPalette(dark);
|
||||
QApplication::setPalette(dark);
|
||||
}
|
||||
|
||||
void initQt()
|
||||
|
@ -131,7 +131,7 @@ namespace {
|
|||
using namespace std::chrono_literals;
|
||||
|
||||
if (std::chrono::steady_clock::now() - signalsInitTime > 30s &&
|
||||
getIApp()->getCrashHandler()->shouldRecover())
|
||||
getApp()->getCrashHandler()->shouldRecover())
|
||||
{
|
||||
QProcess proc;
|
||||
|
||||
|
@ -241,22 +241,7 @@ void runGui(QApplication &a, const Paths &paths, Settings &settings,
|
|||
}
|
||||
#endif
|
||||
|
||||
auto thread = std::thread([dir = paths.miscDirectory] {
|
||||
{
|
||||
auto path = combinePath(dir, "Update.exe");
|
||||
if (QFile::exists(path))
|
||||
{
|
||||
QFile::remove(path);
|
||||
}
|
||||
}
|
||||
{
|
||||
auto path = combinePath(dir, "update.zip");
|
||||
if (QFile::exists(path))
|
||||
{
|
||||
QFile::remove(path);
|
||||
}
|
||||
}
|
||||
});
|
||||
updates.deleteOldFiles();
|
||||
|
||||
// Clear the cache 1 minute after start.
|
||||
QTimer::singleShot(60 * 1000, [cachePath = paths.cacheDirectory(),
|
||||
|
@ -278,13 +263,10 @@ void runGui(QApplication &a, const Paths &paths, Settings &settings,
|
|||
|
||||
Application app(settings, paths, args, updates);
|
||||
app.initialize(settings, paths);
|
||||
app.run(a);
|
||||
app.run();
|
||||
app.save();
|
||||
|
||||
if (!args.dontSaveSettings)
|
||||
{
|
||||
pajlada::Settings::SettingManager::gSave();
|
||||
}
|
||||
settings.requestSave();
|
||||
|
||||
chatterino::NetworkManager::deinit();
|
||||
|
||||
|
@ -292,9 +274,6 @@ void runGui(QApplication &a, const Paths &paths, Settings &settings,
|
|||
// flushing windows clipboard to keep copied messages
|
||||
flushClipboard();
|
||||
#endif
|
||||
|
||||
app.fakeDtor();
|
||||
|
||||
_exit(0);
|
||||
}
|
||||
|
||||
} // namespace chatterino
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
#pragma once
|
||||
|
||||
#include <boost/container_hash/hash_fwd.hpp>
|
||||
#include <QHash>
|
||||
#include <QString>
|
||||
|
||||
#include <functional>
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
|
||||
#define QStringAlias(name) \
|
||||
namespace chatterino { \
|
||||
struct name { \
|
||||
|
@ -27,12 +29,22 @@
|
|||
return qHash(s.string); \
|
||||
} \
|
||||
}; \
|
||||
} /* namespace std */
|
||||
} /* namespace std */ \
|
||||
namespace boost { \
|
||||
template <> \
|
||||
struct hash<chatterino::name> { \
|
||||
std::size_t operator()(chatterino::name const &s) const \
|
||||
{ \
|
||||
return qHash(s.string); \
|
||||
} \
|
||||
}; \
|
||||
} /* namespace boost */
|
||||
|
||||
QStringAlias(UserName);
|
||||
QStringAlias(UserId);
|
||||
QStringAlias(Url);
|
||||
QStringAlias(Tooltip);
|
||||
QStringAlias(EmoteId);
|
||||
QStringAlias(EmoteSetId);
|
||||
QStringAlias(EmoteName);
|
||||
QStringAlias(EmoteAuthor);
|
||||
|
|
|
@ -3,8 +3,6 @@
|
|||
#include "Application.hpp"
|
||||
#include "messages/Message.hpp"
|
||||
#include "messages/MessageBuilder.hpp"
|
||||
#include "providers/irc/IrcChannel2.hpp"
|
||||
#include "providers/irc/IrcServer.hpp"
|
||||
#include "providers/twitch/IrcMessageHandler.hpp"
|
||||
#include "singletons/Emotes.hpp"
|
||||
#include "singletons/Logging.hpp"
|
||||
|
@ -26,12 +24,16 @@ namespace chatterino {
|
|||
// Channel
|
||||
//
|
||||
Channel::Channel(const QString &name, Type type)
|
||||
: completionModel(*this, nullptr)
|
||||
: completionModel(new TabCompletionModel(*this, nullptr))
|
||||
, lastDate_(QDate::currentDate())
|
||||
, name_(name)
|
||||
, messages_(getSettings()->scrollbackSplitLimit)
|
||||
, type_(type)
|
||||
{
|
||||
if (this->isTwitchChannel())
|
||||
{
|
||||
this->platform_ = "twitch";
|
||||
}
|
||||
}
|
||||
|
||||
Channel::~Channel()
|
||||
|
@ -79,37 +81,25 @@ LimitedQueueSnapshot<MessagePtr> Channel::getMessageSnapshot()
|
|||
return this->messages_.getSnapshot();
|
||||
}
|
||||
|
||||
void Channel::addMessage(MessagePtr message,
|
||||
void Channel::addMessage(MessagePtr message, MessageContext context,
|
||||
std::optional<MessageFlags> overridingFlags)
|
||||
{
|
||||
MessagePtr deleted;
|
||||
|
||||
if (!overridingFlags || !overridingFlags->has(MessageFlag::DoNotLog))
|
||||
if (context == MessageContext::Original)
|
||||
{
|
||||
QString channelPlatform("other");
|
||||
if (this->type_ == Type::Irc)
|
||||
// Only log original messages
|
||||
auto isDoNotLogSet =
|
||||
(overridingFlags && overridingFlags->has(MessageFlag::DoNotLog)) ||
|
||||
message->flags.has(MessageFlag::DoNotLog);
|
||||
|
||||
if (!isDoNotLogSet)
|
||||
{
|
||||
auto *irc = dynamic_cast<IrcChannel *>(this);
|
||||
if (irc != nullptr)
|
||||
{
|
||||
auto *ircServer = irc->server();
|
||||
if (ircServer != nullptr)
|
||||
{
|
||||
channelPlatform = QString("irc-%1").arg(
|
||||
irc->server()->userFriendlyIdentifier());
|
||||
// Only log messages where the `DoNotLog` flag is not set
|
||||
getApp()->getChatLogger()->addMessage(this->name_, message,
|
||||
this->platform_,
|
||||
this->getCurrentStreamID());
|
||||
}
|
||||
else
|
||||
{
|
||||
channelPlatform = "irc-unknown";
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (this->isTwitchChannel())
|
||||
{
|
||||
channelPlatform = "twitch";
|
||||
}
|
||||
getIApp()->getChatLogger()->addMessage(this->name_, message,
|
||||
channelPlatform);
|
||||
}
|
||||
|
||||
if (this->messages_.pushBack(message, deleted))
|
||||
|
@ -120,6 +110,12 @@ void Channel::addMessage(MessagePtr message,
|
|||
this->messageAppended.invoke(message, overridingFlags);
|
||||
}
|
||||
|
||||
void Channel::addSystemMessage(const QString &contents)
|
||||
{
|
||||
auto msg = makeSystemMessage(contents);
|
||||
this->addMessage(msg, MessageContext::Original);
|
||||
}
|
||||
|
||||
void Channel::addOrReplaceTimeout(MessagePtr message)
|
||||
{
|
||||
addOrReplaceChannelTimeout(
|
||||
|
@ -128,7 +124,7 @@ void Channel::addOrReplaceTimeout(MessagePtr message)
|
|||
this->replaceMessage(msg, replacement);
|
||||
},
|
||||
[this](auto msg) {
|
||||
this->addMessage(msg);
|
||||
this->addMessage(msg, MessageContext::Original);
|
||||
},
|
||||
true);
|
||||
|
||||
|
@ -279,6 +275,12 @@ void Channel::deleteMessage(QString messageID)
|
|||
}
|
||||
}
|
||||
|
||||
void Channel::clearMessages()
|
||||
{
|
||||
this->messages_.clear();
|
||||
this->messagesCleared.invoke();
|
||||
}
|
||||
|
||||
MessagePtr Channel::findMessage(QString messageID)
|
||||
{
|
||||
MessagePtr res;
|
||||
|
@ -357,6 +359,11 @@ void Channel::reconnect()
|
|||
{
|
||||
}
|
||||
|
||||
QString Channel::getCurrentStreamID() const
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
std::shared_ptr<Channel> Channel::getEmpty()
|
||||
{
|
||||
static std::shared_ptr<Channel> channel(new Channel("", Type::None));
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
#pragma once
|
||||
|
||||
#include "common/FlagsEnum.hpp"
|
||||
#include "controllers/completion/TabCompletionModel.hpp"
|
||||
#include "messages/LimitedQueue.hpp"
|
||||
#include "messages/MessageFlag.hpp"
|
||||
|
||||
#include <magic_enum/magic_enum.hpp>
|
||||
#include <pajlada/signals/signal.hpp>
|
||||
|
@ -17,8 +17,6 @@ namespace chatterino {
|
|||
|
||||
struct Message;
|
||||
using MessagePtr = std::shared_ptr<const Message>;
|
||||
enum class MessageFlag : int64_t;
|
||||
using MessageFlags = FlagsEnum<MessageFlag>;
|
||||
|
||||
enum class TimeoutStackStyle : int {
|
||||
StackHard = 0,
|
||||
|
@ -28,6 +26,14 @@ enum class TimeoutStackStyle : int {
|
|||
Default = DontStackBeyondUserMessage,
|
||||
};
|
||||
|
||||
/// Context of the message being added to a channel
|
||||
enum class MessageContext {
|
||||
/// This message is the original
|
||||
Original,
|
||||
/// This message is a repost of a message that has already been added in a channel
|
||||
Repost,
|
||||
};
|
||||
|
||||
class Channel : public std::enable_shared_from_this<Channel>
|
||||
{
|
||||
public:
|
||||
|
@ -45,7 +51,6 @@ public:
|
|||
TwitchLive,
|
||||
TwitchAutomod,
|
||||
TwitchEnd,
|
||||
Irc,
|
||||
Misc,
|
||||
};
|
||||
|
||||
|
@ -66,6 +71,7 @@ public:
|
|||
pajlada::Signals::Signal<const std::vector<MessagePtr> &> filledInMessages;
|
||||
pajlada::Signals::NoArgSignal destroyed;
|
||||
pajlada::Signals::NoArgSignal displayNameChanged;
|
||||
pajlada::Signals::NoArgSignal messagesCleared;
|
||||
|
||||
Type getType() const;
|
||||
const QString &getName() const;
|
||||
|
@ -79,10 +85,12 @@ public:
|
|||
// overridingFlags can be filled in with flags that should be used instead
|
||||
// of the message's flags. This is useful in case a flag is specific to a
|
||||
// type of split
|
||||
void addMessage(MessagePtr message,
|
||||
void addMessage(MessagePtr message, MessageContext context,
|
||||
std::optional<MessageFlags> overridingFlags = std::nullopt);
|
||||
void addMessagesAtStart(const std::vector<MessagePtr> &messages_);
|
||||
|
||||
void addSystemMessage(const QString &contents);
|
||||
|
||||
/// Inserts the given messages in order by Message::serverReceivedTime.
|
||||
void fillInMissingMessages(const std::vector<MessagePtr> &messages);
|
||||
|
||||
|
@ -92,6 +100,9 @@ public:
|
|||
void replaceMessage(size_t index, MessagePtr replacement);
|
||||
void deleteMessage(QString messageID);
|
||||
|
||||
/// Removes all messages from this channel and invokes #messagesCleared
|
||||
void clearMessages();
|
||||
|
||||
MessagePtr findMessage(QString messageID);
|
||||
|
||||
bool hasMessages() const;
|
||||
|
@ -109,15 +120,17 @@ public:
|
|||
virtual bool shouldIgnoreHighlights() const;
|
||||
virtual bool canReconnect() const;
|
||||
virtual void reconnect();
|
||||
virtual QString getCurrentStreamID() const;
|
||||
|
||||
static std::shared_ptr<Channel> getEmpty();
|
||||
|
||||
TabCompletionModel completionModel;
|
||||
TabCompletionModel *completionModel;
|
||||
QDate lastDate_;
|
||||
|
||||
protected:
|
||||
virtual void onConnected();
|
||||
virtual void messageRemovedFromStart(const MessagePtr &msg);
|
||||
QString platform_{"other"};
|
||||
|
||||
private:
|
||||
const QString name_;
|
||||
|
@ -173,8 +186,6 @@ constexpr magic_enum::customize::customize_t
|
|||
return "live";
|
||||
case Type::TwitchAutomod:
|
||||
return "automod";
|
||||
case Type::Irc:
|
||||
return "irc";
|
||||
case Type::Misc:
|
||||
return "misc";
|
||||
default:
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
#include "ChannelChatters.hpp"
|
||||
#include "common/ChannelChatters.hpp"
|
||||
|
||||
#include "common/Channel.hpp"
|
||||
#include "messages/Message.hpp"
|
||||
#include "messages/MessageBuilder.hpp"
|
||||
#include "providers/twitch/TwitchMessageBuilder.hpp"
|
||||
|
||||
#include <QColor>
|
||||
|
||||
|
@ -39,11 +38,11 @@ void ChannelChatters::addJoinedUser(const QString &user)
|
|||
auto joinedUsers = this->joinedUsers_.access();
|
||||
joinedUsers->sort();
|
||||
|
||||
MessageBuilder builder;
|
||||
TwitchMessageBuilder::listOfUsersSystemMessage(
|
||||
"Users joined:", *joinedUsers, &this->channel_, &builder);
|
||||
builder->flags.set(MessageFlag::Collapsed);
|
||||
this->channel_.addMessage(builder.release());
|
||||
this->channel_.addMessage(
|
||||
MessageBuilder::makeListOfUsersMessage(
|
||||
"Users joined:", *joinedUsers, &this->channel_,
|
||||
{MessageFlag::Collapsed}),
|
||||
MessageContext::Original);
|
||||
|
||||
joinedUsers->clear();
|
||||
this->joinedUsersMergeQueued_ = false;
|
||||
|
@ -64,11 +63,11 @@ void ChannelChatters::addPartedUser(const QString &user)
|
|||
auto partedUsers = this->partedUsers_.access();
|
||||
partedUsers->sort();
|
||||
|
||||
MessageBuilder builder;
|
||||
TwitchMessageBuilder::listOfUsersSystemMessage(
|
||||
"Users parted:", *partedUsers, &this->channel_, &builder);
|
||||
builder->flags.set(MessageFlag::Collapsed);
|
||||
this->channel_.addMessage(builder.release());
|
||||
this->channel_.addMessage(
|
||||
MessageBuilder::makeListOfUsersMessage(
|
||||
"Users parted:", *partedUsers, &this->channel_,
|
||||
{MessageFlag::Collapsed}),
|
||||
MessageContext::Original);
|
||||
|
||||
partedUsers->clear();
|
||||
this->partedUsersMergeQueued_ = false;
|
||||
|
|
|
@ -2,12 +2,10 @@
|
|||
|
||||
#include "debug/Benchmark.hpp"
|
||||
|
||||
#include <tuple>
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
ChatterSet::ChatterSet()
|
||||
: items(chatterLimit)
|
||||
: items(ChatterSet::CHATTER_LIMIT)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -22,7 +20,7 @@ void ChatterSet::updateOnlineChatters(
|
|||
BenchmarkGuard bench("update online chatters");
|
||||
|
||||
// Create a new lru cache without the users that are not present anymore.
|
||||
cache::lru_cache<QString, QString> tmp(chatterLimit);
|
||||
cache::lru_cache<QString, QString> tmp(ChatterSet::CHATTER_LIMIT);
|
||||
|
||||
for (auto &&chatter : lowerCaseUsernames)
|
||||
{
|
||||
|
@ -32,7 +30,7 @@ void ChatterSet::updateOnlineChatters(
|
|||
|
||||
// Less chatters than the limit => try to preserve as many as possible.
|
||||
}
|
||||
else if (lowerCaseUsernames.size() < chatterLimit)
|
||||
else if (lowerCaseUsernames.size() < ChatterSet::CHATTER_LIMIT)
|
||||
{
|
||||
tmp.put(chatter, chatter);
|
||||
}
|
||||
|
|
|
@ -5,9 +5,6 @@
|
|||
#include <lrucache/lrucache.hpp>
|
||||
#include <QString>
|
||||
|
||||
#include <functional>
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
|
@ -19,7 +16,7 @@ class ChatterSet
|
|||
{
|
||||
public:
|
||||
/// The limit of how many chatters can be saved for a channel.
|
||||
static constexpr size_t chatterLimit = 2000;
|
||||
static constexpr size_t CHATTER_LIMIT = 2000;
|
||||
|
||||
ChatterSet();
|
||||
|
||||
|
|
|
@ -141,4 +141,14 @@ public:
|
|||
using pajlada::Settings::Setting<QString>::operator QString;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct IsChatterinoSettingT : std::false_type {
|
||||
};
|
||||
template <typename T>
|
||||
struct IsChatterinoSettingT<ChatterinoSetting<T>> : std::true_type {
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
concept IsChatterinoSetting = IsChatterinoSettingT<T>::value;
|
||||
|
||||
} // namespace chatterino
|
||||
|
|
|
@ -5,17 +5,17 @@
|
|||
#include <QWidget>
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#define LINK_CHATTERINO_WIKI "https://wiki.chatterino.com"
|
||||
#define LINK_CHATTERINO_DISCORD "https://discord.gg/7Y5AYhAK4z"
|
||||
#define LINK_CHATTERINO_SOURCE "https://github.com/Chatterino/chatterino2"
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
const inline auto TWITCH_PLAYER_URL =
|
||||
QStringLiteral("https://player.twitch.tv/?channel=%1&parent=twitch.tv");
|
||||
constexpr QStringView LINK_CHATTERINO_WIKI = u"https://wiki.chatterino.com";
|
||||
constexpr QStringView LINK_CHATTERINO_DISCORD =
|
||||
u"https://discord.gg/7Y5AYhAK4z";
|
||||
constexpr QStringView LINK_CHATTERINO_SOURCE =
|
||||
u"https://github.com/Chatterino/chatterino2";
|
||||
|
||||
constexpr QStringView TWITCH_PLAYER_URL =
|
||||
u"https://player.twitch.tv/?channel=%1&parent=twitch.tv";
|
||||
|
||||
enum class HighlightState {
|
||||
None,
|
||||
|
@ -23,21 +23,14 @@ enum class HighlightState {
|
|||
NewMessage,
|
||||
};
|
||||
|
||||
const Qt::KeyboardModifiers showSplitOverlayModifiers =
|
||||
constexpr Qt::KeyboardModifiers SHOW_SPLIT_OVERLAY_MODIFIERS =
|
||||
Qt::ControlModifier | Qt::AltModifier;
|
||||
const Qt::KeyboardModifiers showAddSplitRegions =
|
||||
constexpr Qt::KeyboardModifiers SHOW_ADD_SPLIT_REGIONS =
|
||||
Qt::ControlModifier | Qt::AltModifier;
|
||||
const Qt::KeyboardModifiers showResizeHandlesModifiers = Qt::ControlModifier;
|
||||
constexpr Qt::KeyboardModifiers SHOW_RESIZE_HANDLES_MODIFIERS =
|
||||
Qt::ControlModifier;
|
||||
|
||||
#ifndef ATTR_UNUSED
|
||||
# ifdef Q_OS_WIN
|
||||
# define ATTR_UNUSED
|
||||
# else
|
||||
# define ATTR_UNUSED __attribute__((unused))
|
||||
# endif
|
||||
#endif
|
||||
|
||||
static const char *ANONYMOUS_USERNAME_LABEL ATTR_UNUSED = " - anonymous - ";
|
||||
constexpr const char *ANONYMOUS_USERNAME_LABEL = " - anonymous - ";
|
||||
|
||||
template <typename T>
|
||||
std::weak_ptr<T> weakOf(T *element)
|
||||
|
|
|
@ -1,102 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <QMap>
|
||||
#include <QMutex>
|
||||
#include <QMutexLocker>
|
||||
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
template <typename TKey, typename TValue>
|
||||
class ConcurrentMap
|
||||
{
|
||||
public:
|
||||
ConcurrentMap() = default;
|
||||
|
||||
bool tryGet(const TKey &name, TValue &value) const
|
||||
{
|
||||
QMutexLocker lock(&this->mutex);
|
||||
|
||||
auto a = this->data.find(name);
|
||||
if (a == this->data.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
value = a.value();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
TValue getOrAdd(const TKey &name, std::function<TValue()> addLambda)
|
||||
{
|
||||
QMutexLocker lock(&this->mutex);
|
||||
|
||||
auto a = this->data.find(name);
|
||||
if (a == this->data.end())
|
||||
{
|
||||
TValue value = addLambda();
|
||||
this->data.insert(name, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
return a.value();
|
||||
}
|
||||
|
||||
TValue &operator[](const TKey &name)
|
||||
{
|
||||
QMutexLocker lock(&this->mutex);
|
||||
|
||||
return this->data[name];
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
QMutexLocker lock(&this->mutex);
|
||||
|
||||
this->data.clear();
|
||||
}
|
||||
|
||||
void insert(const TKey &name, const TValue &value)
|
||||
{
|
||||
QMutexLocker lock(&this->mutex);
|
||||
|
||||
this->data.insert(name, value);
|
||||
}
|
||||
|
||||
void each(
|
||||
std::function<void(const TKey &name, const TValue &value)> func) const
|
||||
{
|
||||
QMutexLocker lock(&this->mutex);
|
||||
|
||||
QMapIterator<TKey, TValue> it(this->data);
|
||||
|
||||
while (it.hasNext())
|
||||
{
|
||||
it.next();
|
||||
func(it.key(), it.value());
|
||||
}
|
||||
}
|
||||
|
||||
void each(std::function<void(const TKey &name, TValue &value)> func)
|
||||
{
|
||||
QMutexLocker lock(&this->mutex);
|
||||
|
||||
QMutableMapIterator<TKey, TValue> it(this->data);
|
||||
|
||||
while (it.hasNext())
|
||||
{
|
||||
it.next();
|
||||
func(it.key(), it.value());
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
mutable QMutex mutex;
|
||||
QMap<TKey, TValue> data;
|
||||
};
|
||||
|
||||
} // namespace chatterino
|
|
@ -6,12 +6,13 @@
|
|||
#include "singletons/Paths.hpp"
|
||||
#include "singletons/Settings.hpp"
|
||||
#include "util/CombinePath.hpp"
|
||||
#include "util/Overloaded.hpp"
|
||||
#include "util/Variant.hpp"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QSaveFile>
|
||||
#include <QStringBuilder>
|
||||
|
||||
#include <variant>
|
||||
|
||||
|
@ -27,16 +28,16 @@
|
|||
# endif
|
||||
#endif
|
||||
|
||||
#define FORMAT_NAME \
|
||||
([&] { \
|
||||
assert(!provider.contains(":")); \
|
||||
return QString("chatterino:%1:%2").arg(provider).arg(name_); \
|
||||
})()
|
||||
|
||||
namespace {
|
||||
|
||||
using namespace chatterino;
|
||||
|
||||
QString formatName(const QString &provider, const QString &name)
|
||||
{
|
||||
assert(!provider.contains(":"));
|
||||
return u"chatterino:" % provider % u':' % name;
|
||||
}
|
||||
|
||||
bool useKeyring()
|
||||
{
|
||||
#ifdef NO_QTKEYCHAIN
|
||||
|
@ -57,7 +58,7 @@ bool useKeyring()
|
|||
// Insecure storage:
|
||||
QString insecurePath()
|
||||
{
|
||||
return combinePath(getIApp()->getPaths().settingsDirectory,
|
||||
return combinePath(getApp()->getPaths().settingsDirectory,
|
||||
"credentials.json");
|
||||
}
|
||||
|
||||
|
@ -89,7 +90,7 @@ void queueInsecureSave()
|
|||
if (!isQueued)
|
||||
{
|
||||
isQueued = true;
|
||||
QTimer::singleShot(200, qApp, [] {
|
||||
QTimer::singleShot(200, QApplication::instance(), [] {
|
||||
storeInsecure(insecureInstance());
|
||||
isQueued = false;
|
||||
});
|
||||
|
@ -133,8 +134,8 @@ void runNextJob()
|
|||
job->setAutoDelete(true);
|
||||
job->setKey(set.name);
|
||||
job->setTextData(set.credential);
|
||||
QObject::connect(job, &QKeychain::Job::finished, qApp,
|
||||
[](auto) {
|
||||
QObject::connect(job, &QKeychain::Job::finished,
|
||||
QApplication::instance(), [](auto) {
|
||||
runNextJob();
|
||||
});
|
||||
job->start();
|
||||
|
@ -143,8 +144,8 @@ void runNextJob()
|
|||
auto *job = new QKeychain::DeletePasswordJob("chatterino");
|
||||
job->setAutoDelete(true);
|
||||
job->setKey(erase.name);
|
||||
QObject::connect(job, &QKeychain::Job::finished, qApp,
|
||||
[](auto) {
|
||||
QObject::connect(job, &QKeychain::Job::finished,
|
||||
QApplication::instance(), [](auto) {
|
||||
runNextJob();
|
||||
});
|
||||
job->start();
|
||||
|
@ -185,7 +186,7 @@ void Credentials::get(const QString &provider, const QString &name_,
|
|||
{
|
||||
assertInGuiThread();
|
||||
|
||||
auto name = FORMAT_NAME;
|
||||
auto name = formatName(provider, name_);
|
||||
|
||||
if (useKeyring())
|
||||
{
|
||||
|
@ -220,7 +221,7 @@ void Credentials::set(const QString &provider, const QString &name_,
|
|||
/// On linux, we try to use a keychain but show a message to disable it when it fails.
|
||||
/// XXX: add said message
|
||||
|
||||
auto name = FORMAT_NAME;
|
||||
auto name = formatName(provider, name_);
|
||||
|
||||
if (useKeyring())
|
||||
{
|
||||
|
@ -243,7 +244,7 @@ void Credentials::erase(const QString &provider, const QString &name_)
|
|||
{
|
||||
assertInGuiThread();
|
||||
|
||||
auto name = FORMAT_NAME;
|
||||
auto name = formatName(provider, name_);
|
||||
|
||||
if (useKeyring())
|
||||
{
|
||||
|
|
|
@ -1,59 +1,74 @@
|
|||
#pragma once
|
||||
|
||||
#include <initializer_list>
|
||||
#include <concepts>
|
||||
#include <type_traits>
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
template <typename T, typename Q = typename std::underlying_type<T>::type>
|
||||
template <typename T>
|
||||
requires std::is_enum_v<T>
|
||||
class FlagsEnum
|
||||
{
|
||||
public:
|
||||
FlagsEnum()
|
||||
: value_(static_cast<T>(0))
|
||||
using Int = std::underlying_type_t<T>;
|
||||
|
||||
constexpr FlagsEnum() noexcept = default;
|
||||
|
||||
constexpr FlagsEnum(std::convertible_to<T> auto... flags) noexcept
|
||||
: value_(
|
||||
static_cast<T>((static_cast<Int>(static_cast<T>(flags)) | ...)))
|
||||
{
|
||||
}
|
||||
|
||||
FlagsEnum(T value)
|
||||
: value_(value)
|
||||
friend constexpr bool operator==(FlagsEnum lhs, FlagsEnum rhs) noexcept
|
||||
{
|
||||
return lhs.value_ == rhs.value_;
|
||||
}
|
||||
friend constexpr bool operator!=(FlagsEnum lhs, FlagsEnum rhs) noexcept
|
||||
{
|
||||
return lhs.value_ != rhs.value_;
|
||||
}
|
||||
|
||||
FlagsEnum(std::initializer_list<T> flags)
|
||||
friend constexpr bool operator==(FlagsEnum lhs, T rhs) noexcept
|
||||
{
|
||||
for (auto flag : flags)
|
||||
{
|
||||
this->set(flag);
|
||||
return lhs.value_ == rhs;
|
||||
}
|
||||
friend constexpr bool operator!=(FlagsEnum lhs, T rhs) noexcept
|
||||
{
|
||||
return lhs.value_ != rhs;
|
||||
}
|
||||
|
||||
bool operator==(const FlagsEnum<T> &other) const
|
||||
friend constexpr bool operator==(T lhs, FlagsEnum rhs) noexcept
|
||||
{
|
||||
return this->value_ == other.value_;
|
||||
return lhs == rhs.value_;
|
||||
}
|
||||
friend constexpr bool operator!=(T lhs, FlagsEnum rhs) noexcept
|
||||
{
|
||||
return lhs != rhs.value_;
|
||||
}
|
||||
|
||||
bool operator!=(const FlagsEnum<T> &other) const
|
||||
constexpr void set(std::convertible_to<T> auto... flags) noexcept
|
||||
{
|
||||
return this->value_ != other.value_;
|
||||
}
|
||||
|
||||
void set(T flag)
|
||||
{
|
||||
reinterpret_cast<Q &>(this->value_) |= static_cast<Q>(flag);
|
||||
this->value_ =
|
||||
static_cast<T>(static_cast<Int>(this->value_) |
|
||||
(static_cast<Int>(static_cast<T>(flags)) | ...));
|
||||
}
|
||||
|
||||
/** Adds the flags from `flags` in this enum. */
|
||||
void set(FlagsEnum flags)
|
||||
constexpr void set(FlagsEnum flags) noexcept
|
||||
{
|
||||
reinterpret_cast<Q &>(this->value_) |= static_cast<Q>(flags.value_);
|
||||
this->value_ = static_cast<T>(static_cast<Int>(this->value_) |
|
||||
static_cast<Int>(flags.value_));
|
||||
}
|
||||
|
||||
void unset(T flag)
|
||||
constexpr void unset(std::convertible_to<T> auto... flags) noexcept
|
||||
{
|
||||
reinterpret_cast<Q &>(this->value_) &= ~static_cast<Q>(flag);
|
||||
this->value_ =
|
||||
static_cast<T>(static_cast<Int>(this->value_) &
|
||||
~(static_cast<Int>(static_cast<T>(flags)) | ...));
|
||||
}
|
||||
|
||||
void set(T flag, bool value)
|
||||
constexpr void set(T flag, bool value) noexcept
|
||||
{
|
||||
if (value)
|
||||
{
|
||||
|
@ -65,43 +80,65 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
bool has(T flag) const
|
||||
constexpr FlagsEnum operator|(T flag) const noexcept
|
||||
{
|
||||
return static_cast<Q>(this->value_) & static_cast<Q>(flag);
|
||||
return static_cast<T>(static_cast<Int>(this->value_) |
|
||||
static_cast<Int>(flag));
|
||||
}
|
||||
|
||||
FlagsEnum operator|(T flag)
|
||||
constexpr FlagsEnum operator|(FlagsEnum rhs) const noexcept
|
||||
{
|
||||
FlagsEnum xd;
|
||||
xd.value_ = this->value_;
|
||||
xd.set(flag, true);
|
||||
|
||||
return xd;
|
||||
return static_cast<T>(static_cast<Int>(this->value_) |
|
||||
static_cast<Int>(rhs.value_));
|
||||
}
|
||||
|
||||
FlagsEnum operator|(FlagsEnum rhs)
|
||||
constexpr bool has(T flag) const noexcept
|
||||
{
|
||||
return static_cast<T>(static_cast<Q>(this->value_) |
|
||||
static_cast<Q>(rhs.value_));
|
||||
return static_cast<Int>(this->value_) & static_cast<Int>(flag);
|
||||
}
|
||||
|
||||
bool hasAny(FlagsEnum flags) const
|
||||
constexpr bool hasAny(FlagsEnum flags) const noexcept
|
||||
{
|
||||
return static_cast<Q>(this->value_) & static_cast<Q>(flags.value_);
|
||||
return (static_cast<Int>(this->value_) &
|
||||
static_cast<Int>(flags.value_)) != 0;
|
||||
}
|
||||
|
||||
bool hasAll(FlagsEnum<T> flags) const
|
||||
constexpr bool hasAny(std::convertible_to<T> auto... flags) const noexcept
|
||||
{
|
||||
return (static_cast<Q>(this->value_) & static_cast<Q>(flags.value_)) &&
|
||||
static_cast<Q>(flags->value);
|
||||
return this->hasAny(FlagsEnum{flags...});
|
||||
}
|
||||
|
||||
bool hasNone(std::initializer_list<T> flags) const
|
||||
constexpr bool hasAll(FlagsEnum flags) const noexcept
|
||||
{
|
||||
return !this->hasAny(flags);
|
||||
return (static_cast<Int>(this->value_) &
|
||||
static_cast<Int>(flags.value_)) ==
|
||||
static_cast<Int>(flags.value_);
|
||||
}
|
||||
|
||||
T value() const
|
||||
constexpr bool hasAll(std::convertible_to<T> auto... flags) const noexcept
|
||||
{
|
||||
return this->hasAll(FlagsEnum{flags...});
|
||||
}
|
||||
|
||||
constexpr bool hasNone(FlagsEnum flags) const noexcept
|
||||
{
|
||||
return (static_cast<Int>(this->value_) &
|
||||
static_cast<Int>(flags.value_)) == 0;
|
||||
}
|
||||
|
||||
constexpr bool hasNone() const noexcept = delete;
|
||||
constexpr bool hasNone(std::convertible_to<T> auto... flags) const noexcept
|
||||
{
|
||||
return this->hasNone(FlagsEnum{flags...});
|
||||
}
|
||||
|
||||
/// Returns true if the enum has no flag set (i.e. its underlying value is 0)
|
||||
constexpr bool isEmpty() const noexcept
|
||||
{
|
||||
return static_cast<Int>(this->value_) == 0;
|
||||
}
|
||||
|
||||
constexpr T value() const noexcept
|
||||
{
|
||||
return this->value_;
|
||||
}
|
||||
|
|
|
@ -1,62 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <QColor>
|
||||
#include <QMap>
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
// Colors taken from https://modern.ircdocs.horse/formatting.html
|
||||
static QMap<int, QColor> IRC_COLORS = {
|
||||
{0, QColor("white")}, {1, QColor("black")},
|
||||
{2, QColor("blue")}, {3, QColor("green")},
|
||||
{4, QColor("red")}, {5, QColor("brown")},
|
||||
{6, QColor("purple")}, {7, QColor("orange")},
|
||||
{8, QColor("yellow")}, {9, QColor("lightgreen")},
|
||||
{10, QColor("cyan")}, {11, QColor("lightcyan")},
|
||||
{12, QColor("lightblue")}, {13, QColor("pink")},
|
||||
{14, QColor("gray")}, {15, QColor("lightgray")},
|
||||
{16, QColor("#470000")}, {17, QColor("#472100")},
|
||||
{18, QColor("#474700")}, {19, QColor("#324700")},
|
||||
{20, QColor("#004700")}, {21, QColor("#00472c")},
|
||||
{22, QColor("#004747")}, {23, QColor("#002747")},
|
||||
{24, QColor("#000047")}, {25, QColor("#2e0047")},
|
||||
{26, QColor("#470047")}, {27, QColor("#47002a")},
|
||||
{28, QColor("#740000")}, {29, QColor("#743a00")},
|
||||
{30, QColor("#747400")}, {31, QColor("#517400")},
|
||||
{32, QColor("#007400")}, {33, QColor("#007449")},
|
||||
{34, QColor("#007474")}, {35, QColor("#004074")},
|
||||
{36, QColor("#000074")}, {37, QColor("#4b0074")},
|
||||
{38, QColor("#740074")}, {39, QColor("#740045")},
|
||||
{40, QColor("#b50000")}, {41, QColor("#b56300")},
|
||||
{42, QColor("#b5b500")}, {43, QColor("#7db500")},
|
||||
{44, QColor("#00b500")}, {45, QColor("#00b571")},
|
||||
{46, QColor("#00b5b5")}, {47, QColor("#0063b5")},
|
||||
{48, QColor("#0000b5")}, {49, QColor("#7500b5")},
|
||||
{50, QColor("#b500b5")}, {51, QColor("#b5006b")},
|
||||
{52, QColor("#ff0000")}, {53, QColor("#ff8c00")},
|
||||
{54, QColor("#ffff00")}, {55, QColor("#b2ff00")},
|
||||
{56, QColor("#00ff00")}, {57, QColor("#00ffa0")},
|
||||
{58, QColor("#00ffff")}, {59, QColor("#008cff")},
|
||||
{60, QColor("#0000ff")}, {61, QColor("#a500ff")},
|
||||
{62, QColor("#ff00ff")}, {63, QColor("#ff0098")},
|
||||
{64, QColor("#ff5959")}, {65, QColor("#ffb459")},
|
||||
{66, QColor("#ffff71")}, {67, QColor("#cfff60")},
|
||||
{68, QColor("#6fff6f")}, {69, QColor("#65ffc9")},
|
||||
{70, QColor("#6dffff")}, {71, QColor("#59b4ff")},
|
||||
{72, QColor("#5959ff")}, {73, QColor("#c459ff")},
|
||||
{74, QColor("#ff66ff")}, {75, QColor("#ff59bc")},
|
||||
{76, QColor("#ff9c9c")}, {77, QColor("#ffd39c")},
|
||||
{78, QColor("#ffff9c")}, {79, QColor("#e2ff9c")},
|
||||
{80, QColor("#9cff9c")}, {81, QColor("#9cffdb")},
|
||||
{82, QColor("#9cffff")}, {83, QColor("#9cd3ff")},
|
||||
{84, QColor("#9c9cff")}, {85, QColor("#dc9cff")},
|
||||
{86, QColor("#ff9cff")}, {87, QColor("#ff94d3")},
|
||||
{88, QColor("#000000")}, {89, QColor("#131313")},
|
||||
{90, QColor("#282828")}, {91, QColor("#363636")},
|
||||
{92, QColor("#4d4d4d")}, {93, QColor("#656565")},
|
||||
{94, QColor("#818181")}, {95, QColor("#9f9f9f")},
|
||||
{96, QColor("#bcbcbc")}, {97, QColor("#e2e2e2")},
|
||||
{98, QColor("#ffffff")},
|
||||
};
|
||||
|
||||
} // namespace chatterino
|
|
@ -1,17 +1,24 @@
|
|||
#define QT_NO_CAST_FROM_ASCII // avoids unexpected implicit casts
|
||||
#include "common/LinkParser.hpp"
|
||||
|
||||
#include "util/QCompareCaseInsensitive.hpp"
|
||||
|
||||
#include <QFile>
|
||||
#include <QSet>
|
||||
#include <QString>
|
||||
#include <QStringView>
|
||||
#include <QTextStream>
|
||||
|
||||
#include <set>
|
||||
|
||||
namespace {
|
||||
|
||||
QSet<QString> &tlds()
|
||||
using namespace chatterino;
|
||||
|
||||
using TldSet = std::set<QString, QCompareCaseInsensitive>;
|
||||
|
||||
TldSet &tlds()
|
||||
{
|
||||
static QSet<QString> tlds = [] {
|
||||
static TldSet tlds = [] {
|
||||
QFile file(QStringLiteral(":/tlds.txt"));
|
||||
file.open(QFile::ReadOnly);
|
||||
QTextStream stream(&file);
|
||||
|
@ -21,19 +28,12 @@ QSet<QString> &tlds()
|
|||
#else
|
||||
stream.setCodec("UTF-8");
|
||||
#endif
|
||||
int safetyMax = 20000;
|
||||
|
||||
QSet<QString> set;
|
||||
TldSet set;
|
||||
|
||||
while (!stream.atEnd())
|
||||
{
|
||||
auto line = stream.readLine();
|
||||
set.insert(line);
|
||||
|
||||
if (safetyMax-- == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
set.emplace(stream.readLine());
|
||||
}
|
||||
|
||||
return set;
|
||||
|
@ -43,7 +43,7 @@ QSet<QString> &tlds()
|
|||
|
||||
bool isValidTld(QStringView tld)
|
||||
{
|
||||
return tlds().contains(tld.toString().toLower());
|
||||
return tlds().contains(tld);
|
||||
}
|
||||
|
||||
bool isValidIpv4(QStringView host)
|
||||
|
@ -113,19 +113,72 @@ bool startsWithPort(QStringView string)
|
|||
return true;
|
||||
}
|
||||
|
||||
/// @brief Strips ignored characters off @a source
|
||||
///
|
||||
/// As per https://github.github.com/gfm/#autolinks-extension-:
|
||||
///
|
||||
/// '<', '*', '_', '~', and '(' are ignored at the beginning
|
||||
/// '>', '?', '!', '.', ',', ':', '*', '~', and ')' are ignored at the end.
|
||||
///
|
||||
/// A difference to GFM is that '_' isn't a valid suffix.
|
||||
///
|
||||
/// This might remove more than desired (e.g. "(a.com/(foo))" -> "a.com/(foo").
|
||||
/// Parentheses are counted after recognizing a valid IP/host.
|
||||
void strip(QStringView &source)
|
||||
{
|
||||
while (!source.isEmpty())
|
||||
{
|
||||
auto c = source.first();
|
||||
if (c == u'<' || c == u'*' || c == u'_' || c == u'~' || c == u'(')
|
||||
{
|
||||
source = source.mid(1);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
while (!source.isEmpty())
|
||||
{
|
||||
auto c = source.last();
|
||||
if (c == u'>' || c == u'?' || c == u'!' || c == u'.' || c == u',' ||
|
||||
c == u':' || c == u'*' || c == u'~' || c == u')')
|
||||
{
|
||||
source.chop(1);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Checks if @a c is valid in a domain
|
||||
///
|
||||
/// Valid characters are 0-9, A-Z, a-z, '-', '_', and '.' (like in GFM)
|
||||
/// and all non-ASCII characters (unlike in GFM).
|
||||
Q_ALWAYS_INLINE bool isValidDomainChar(char16_t c)
|
||||
{
|
||||
return c >= 0x80 || (u'0' <= c && c <= u'9') || (u'A' <= c && c <= u'Z') ||
|
||||
(u'a' <= c && c <= u'z') || c == u'_' || c == u'-' || c == u'.';
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace chatterino {
|
||||
namespace chatterino::linkparser {
|
||||
|
||||
LinkParser::LinkParser(const QString &unparsedString)
|
||||
std::optional<Parsed> parse(const QString &source) noexcept
|
||||
{
|
||||
ParsedLink result;
|
||||
using SizeType = QString::size_type;
|
||||
|
||||
std::optional<Parsed> result;
|
||||
// This is not implemented with a regex to increase performance.
|
||||
QStringView remaining(unparsedString);
|
||||
QStringView protocol(remaining);
|
||||
|
||||
QStringView link{source};
|
||||
strip(link);
|
||||
|
||||
QStringView remaining = link;
|
||||
QStringView protocol;
|
||||
|
||||
// Check protocol for https?://
|
||||
if (remaining.startsWith(QStringLiteral("http"), Qt::CaseInsensitive) &&
|
||||
if (remaining.startsWith(u"http", Qt::CaseInsensitive) &&
|
||||
remaining.length() >= 4 + 3 + 1) // 'http' + '://' + [any]
|
||||
{
|
||||
// optimistic view assuming there's a protocol (http or https)
|
||||
|
@ -136,11 +189,11 @@ LinkParser::LinkParser(const QString &unparsedString)
|
|||
withProto = withProto.mid(1);
|
||||
}
|
||||
|
||||
if (withProto.startsWith(QStringLiteral("://")))
|
||||
if (withProto.startsWith(u"://"))
|
||||
{
|
||||
// there's really a protocol => consume it
|
||||
remaining = withProto.mid(3);
|
||||
result.protocol = {protocol.begin(), remaining.begin()};
|
||||
protocol = {link.begin(), remaining.begin()};
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -150,18 +203,18 @@ LinkParser::LinkParser(const QString &unparsedString)
|
|||
QStringView host = remaining;
|
||||
QStringView rest;
|
||||
bool lastWasDot = true;
|
||||
int lastDotPos = -1;
|
||||
int nDots = 0;
|
||||
SizeType lastDotPos = -1;
|
||||
SizeType nDots = 0;
|
||||
|
||||
// Extract the host
|
||||
for (int i = 0; i < remaining.size(); i++)
|
||||
for (SizeType i = 0; i < remaining.size(); i++)
|
||||
{
|
||||
char16_t currentChar = remaining[i].unicode();
|
||||
if (currentChar == u'.')
|
||||
{
|
||||
if (lastWasDot) // no double dots ..
|
||||
{
|
||||
return;
|
||||
return result;
|
||||
}
|
||||
lastDotPos = i;
|
||||
lastWasDot = true;
|
||||
|
@ -181,7 +234,7 @@ LinkParser::LinkParser(const QString &unparsedString)
|
|||
|
||||
if (!startsWithPort(remaining))
|
||||
{
|
||||
return;
|
||||
return result;
|
||||
}
|
||||
|
||||
break;
|
||||
|
@ -194,27 +247,68 @@ LinkParser::LinkParser(const QString &unparsedString)
|
|||
rest = remaining.mid(i);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!isValidDomainChar(currentChar))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
if (lastWasDot || lastDotPos <= 0)
|
||||
{
|
||||
return;
|
||||
return result;
|
||||
}
|
||||
|
||||
// check host/tld
|
||||
if ((nDots == 3 && isValidIpv4(host)) ||
|
||||
isValidTld(host.mid(lastDotPos + 1)))
|
||||
{
|
||||
result.host = host;
|
||||
result.rest = rest;
|
||||
result.source = unparsedString;
|
||||
this->result_ = std::move(result);
|
||||
// scan for parentheses (only if there were characters excluded)
|
||||
if (link.end() != source.end() && !rest.empty())
|
||||
{
|
||||
size_t nestingLevel = 0;
|
||||
// position after the last closing brace (i.e. the minimum characters to include)
|
||||
const auto *lastClose = link.end();
|
||||
|
||||
// scan source from rest until the end:
|
||||
// lastClose
|
||||
// v
|
||||
// (example.com/foo/bar/#baz_(qox)),
|
||||
// ▏╌╌rest (before)╌ ▏
|
||||
// ▏╌╌╌╌╌╌╌link (before)╌╌╌╌╌╌╌ ▏
|
||||
// ▏╌╌rest (after)╌╌╌ ▏
|
||||
// ▏╌╌╌╌╌╌╌link (after)╌╌╌╌╌╌╌╌╌ ▏
|
||||
// ▏╌╌╌╌╌╌╌╌╌╌╌╌╌source╌╌╌╌╌╌╌╌╌╌╌╌ ▏
|
||||
// ▏╌╌╌╌╌╌╌search╌╌╌╌╌╌ ▏
|
||||
for (const auto *it = rest.begin(); it < source.end(); it++)
|
||||
{
|
||||
if (it->unicode() == u'(')
|
||||
{
|
||||
nestingLevel++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (nestingLevel != 0 && it->unicode() == u')')
|
||||
{
|
||||
nestingLevel--;
|
||||
if (nestingLevel == 0)
|
||||
{
|
||||
lastClose = it + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
link = QStringView{link.begin(), std::max(link.end(), lastClose)};
|
||||
rest = QStringView{rest.begin(), std::max(rest.end(), lastClose)};
|
||||
}
|
||||
result = Parsed{
|
||||
.protocol = protocol,
|
||||
.host = host,
|
||||
.rest = rest,
|
||||
.link = link,
|
||||
};
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
const std::optional<ParsedLink> &LinkParser::result() const
|
||||
{
|
||||
return this->result_;
|
||||
}
|
||||
|
||||
} // namespace chatterino
|
||||
} // namespace chatterino::linkparser
|
||||
|
|
|
@ -4,43 +4,127 @@
|
|||
|
||||
#include <optional>
|
||||
|
||||
namespace chatterino {
|
||||
namespace chatterino::linkparser {
|
||||
|
||||
struct ParsedLink {
|
||||
/// @brief Represents a parsed link
|
||||
///
|
||||
/// A parsed link is represented as views over the source string for its
|
||||
/// different segments. In this simplified model, a link consists of an optional
|
||||
/// @a protocol, a mandatory @a host and an optional @a rest. These segments are
|
||||
/// always next to eachother in the input string, however together, they don't
|
||||
/// span the whole input as it could contain prefixes or suffixes.
|
||||
///
|
||||
/// Prefixes and suffixes are almost identical to the ones in GitHub Flavored
|
||||
/// Markdown (GFM - https://github.github.com/gfm/#autolinks-extension-).
|
||||
/// The main difference is that '_' isn't a valid suffix.
|
||||
/// Parentheses are counted inside the @a rest: parsing "(a.com/(foo))" would
|
||||
/// result in the link "a.com/(foo)".
|
||||
/// Matching is done case insensitive (e.g. "HTTp://a.com" would be valid).
|
||||
///
|
||||
/// A @a protocol can either be empty, "http://", or "https://".
|
||||
/// A @a host can either be an IPv4 address or a hostname. The hostname must end
|
||||
/// in a valid top level domain. Otherwise, there are no restrictions on it.
|
||||
/// The @a rest can start with an optional port followed by either a '/', '?',
|
||||
/// or '#'.
|
||||
///
|
||||
/// @b Example
|
||||
///
|
||||
/// ```text
|
||||
/// (https://wiki.chatterino.com/Help/#overview)
|
||||
/// ▏▏proto ▕ host ▏ rest ▏▏
|
||||
/// ▏▏ link ▏▏
|
||||
/// ▏ source ▏
|
||||
/// ```
|
||||
struct Parsed {
|
||||
/// The parsed protocol of the link. Can be empty.
|
||||
///
|
||||
/// ```text
|
||||
/// https://www.forsen.tv/commands
|
||||
/// ^------^
|
||||
/// ▏╌╌╌╌╌╌▕
|
||||
///
|
||||
/// www.forsen.tv/commands
|
||||
/// (empty)
|
||||
/// ```
|
||||
QStringView protocol;
|
||||
|
||||
/// The parsed host of the link. Can not be empty.
|
||||
///
|
||||
/// ```text
|
||||
/// https://www.forsen.tv/commands
|
||||
/// ^-----------^
|
||||
/// ▏╌╌╌╌╌╌╌╌╌╌╌▕
|
||||
/// ```
|
||||
QStringView host;
|
||||
|
||||
/// The remainder of the link. Can be empty.
|
||||
///
|
||||
/// ```text
|
||||
/// https://www.forsen.tv/commands
|
||||
/// ^-------^
|
||||
/// ▏╌╌╌╌╌╌╌▕
|
||||
///
|
||||
/// https://www.forsen.tv
|
||||
/// (empty)
|
||||
/// ```
|
||||
QStringView rest;
|
||||
|
||||
/// The original unparsed link.
|
||||
/// The matched link. Can not be empty.
|
||||
///
|
||||
/// ```text
|
||||
/// (https://www.forsen.tv/commands)
|
||||
/// ▏╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌▕
|
||||
/// ```
|
||||
QStringView link;
|
||||
|
||||
/// Checks if the parsed link contains a prefix
|
||||
bool hasPrefix(const QString &source) const noexcept
|
||||
{
|
||||
return this->link.begin() != source.begin();
|
||||
}
|
||||
|
||||
/// The prefix before the parsed link inside @a source. May be empty.
|
||||
///
|
||||
/// ```text
|
||||
/// (https://www.forsen.tv/commands)
|
||||
/// ^
|
||||
///
|
||||
/// https://www.forsen.tv/commands
|
||||
/// ^----------------------------^
|
||||
QString source;
|
||||
/// (empty)
|
||||
/// ```
|
||||
QStringView prefix(const QString &source) const noexcept
|
||||
{
|
||||
return {source.data(), this->link.begin()};
|
||||
}
|
||||
|
||||
/// Checks if the parsed link contains a suffix
|
||||
bool hasSuffix(const QString &source) const noexcept
|
||||
{
|
||||
return this->link.end() != source.end();
|
||||
}
|
||||
|
||||
/// The suffix after the parsed link inside @a source. May be empty.
|
||||
///
|
||||
/// ```text
|
||||
/// (https://www.forsen.tv/commands)
|
||||
/// ^
|
||||
///
|
||||
/// https://www.forsen.tv/commands
|
||||
/// (empty)
|
||||
/// ```
|
||||
QStringView suffix(const QString &source) const noexcept
|
||||
{
|
||||
return {
|
||||
this->link.begin() + this->link.size(),
|
||||
source.data() + source.length(),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
class LinkParser
|
||||
{
|
||||
public:
|
||||
explicit LinkParser(const QString &unparsedString);
|
||||
/// @brief Parses a link from @a source into its segments
|
||||
///
|
||||
/// If no link is contained in @a source, `std::nullopt` will be returned.
|
||||
/// The returned value is valid as long as @a source exists, as it contains
|
||||
/// views into @a source.
|
||||
///
|
||||
/// For the accepted links, see Parsed.
|
||||
std::optional<Parsed> parse(const QString &source) noexcept;
|
||||
|
||||
const std::optional<ParsedLink> &result() const;
|
||||
|
||||
private:
|
||||
std::optional<ParsedLink> result_{};
|
||||
};
|
||||
|
||||
} // namespace chatterino
|
||||
} // namespace chatterino::linkparser
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#include "Modes.hpp"
|
||||
#include "common/Modes.hpp"
|
||||
|
||||
#include "util/CombinePath.hpp"
|
||||
|
||||
|
|
|
@ -127,6 +127,27 @@ public:
|
|||
this->itemsChanged_();
|
||||
}
|
||||
|
||||
bool removeFirstMatching(std::function<bool(const T &)> matcher,
|
||||
void *caller = nullptr)
|
||||
{
|
||||
assertInGuiThread();
|
||||
|
||||
for (int index = 0; index < this->items_.size(); ++index)
|
||||
{
|
||||
T item = this->items_[index];
|
||||
if (matcher(item))
|
||||
{
|
||||
this->items_.erase(this->items_.begin() + index);
|
||||
SignalVectorItemEvent<T> args{item, index, caller};
|
||||
this->itemRemoved.invoke(args);
|
||||
this->itemsChanged_();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::vector<T> &raw() const
|
||||
{
|
||||
assertInGuiThread();
|
||||
|
@ -155,7 +176,7 @@ public:
|
|||
decltype(auto) operator[](size_t index)
|
||||
{
|
||||
assertInGuiThread();
|
||||
return this->items[index];
|
||||
return this->items_[index];
|
||||
}
|
||||
|
||||
auto empty()
|
||||
|
|
|
@ -1,31 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
class Settings;
|
||||
class Paths;
|
||||
|
||||
class Singleton
|
||||
{
|
||||
public:
|
||||
Singleton() = default;
|
||||
virtual ~Singleton() = default;
|
||||
|
||||
Singleton(const Singleton &) = delete;
|
||||
Singleton &operator=(const Singleton &) = delete;
|
||||
|
||||
Singleton(Singleton &&) = delete;
|
||||
Singleton &operator=(Singleton &&) = delete;
|
||||
|
||||
virtual void initialize(Settings &settings, const Paths &paths)
|
||||
{
|
||||
(void)(settings);
|
||||
(void)(paths);
|
||||
}
|
||||
|
||||
virtual void save()
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace chatterino
|
|
@ -1,11 +1,15 @@
|
|||
#include "common/Version.hpp"
|
||||
|
||||
#include "common/Literals.hpp"
|
||||
#include "common/Modes.hpp"
|
||||
|
||||
#include <QFileInfo>
|
||||
#include <QStringBuilder>
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
using namespace literals;
|
||||
|
||||
Version::Version()
|
||||
: version_(CHATTERINO_VERSION)
|
||||
, commitHash_(QStringLiteral(CHATTERINO_GIT_HASH))
|
||||
|
@ -79,11 +83,17 @@ QStringList Version::buildTags() const
|
|||
{
|
||||
QStringList tags;
|
||||
|
||||
tags.append("Qt " QT_VERSION_STR);
|
||||
const auto *runtimeVersion = qVersion();
|
||||
if (runtimeVersion != QLatin1String{QT_VERSION_STR})
|
||||
{
|
||||
tags.append(u"Qt "_s QT_VERSION_STR u" (running on " % runtimeVersion %
|
||||
u")");
|
||||
}
|
||||
else
|
||||
{
|
||||
tags.append(u"Qt "_s QT_VERSION_STR);
|
||||
}
|
||||
|
||||
#ifdef USEWINSDK
|
||||
tags.append("Windows SDK");
|
||||
#endif
|
||||
#ifdef _MSC_FULL_VER
|
||||
tags.append("MSVC " + QString::number(_MSC_FULL_VER, 10));
|
||||
#endif
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
#include <QtGlobal>
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
/**
|
||||
* Valid version formats, in order of latest to oldest
|
||||
|
@ -24,21 +25,7 @@
|
|||
* - 2.4.0-alpha.2
|
||||
* - 2.4.0-alpha
|
||||
**/
|
||||
#define CHATTERINO_VERSION "2.5.1"
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
# define CHATTERINO_OS "win"
|
||||
#elif defined(Q_OS_MACOS)
|
||||
# define CHATTERINO_OS "macos"
|
||||
#elif defined(Q_OS_LINUX)
|
||||
# define CHATTERINO_OS "linux"
|
||||
#elif defined(Q_OS_FREEBSD)
|
||||
# define CHATTERINO_OS "freebsd"
|
||||
#else
|
||||
# define CHATTERINO_OS "unknown"
|
||||
#endif
|
||||
|
||||
namespace chatterino {
|
||||
inline const QString CHATTERINO_VERSION = QStringLiteral("2.5.1");
|
||||
|
||||
class Version
|
||||
{
|
||||
|
|
|
@ -13,6 +13,7 @@ void NetworkManager::init()
|
|||
assert(!NetworkManager::accessManager);
|
||||
|
||||
NetworkManager::workerThread = new QThread;
|
||||
NetworkManager::workerThread->setObjectName("NetworkWorker");
|
||||
NetworkManager::workerThread->start();
|
||||
|
||||
NetworkManager::accessManager = new QNetworkAccessManager;
|
||||
|
|
|
@ -59,7 +59,7 @@ void loadUncached(std::shared_ptr<NetworkData> &&data)
|
|||
|
||||
void loadCached(std::shared_ptr<NetworkData> &&data)
|
||||
{
|
||||
QFile cachedFile(getIApp()->getPaths().cacheDirectory() + "/" +
|
||||
QFile cachedFile(getApp()->getPaths().cacheDirectory() + "/" +
|
||||
data->getHash());
|
||||
|
||||
if (!cachedFile.exists() || !cachedFile.open(QIODevice::ReadOnly))
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "common/QLogging.hpp"
|
||||
#include "common/Version.hpp"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QDebug>
|
||||
#include <QFile>
|
||||
#include <QtConcurrent>
|
||||
|
@ -44,7 +45,7 @@ NetworkRequest NetworkRequest::caller(const QObject *caller) &&
|
|||
if (caller)
|
||||
{
|
||||
// Caller must be in gui thread
|
||||
assert(caller->thread() == qApp->thread());
|
||||
assert(caller->thread() == QApplication::instance()->thread());
|
||||
|
||||
this->data->caller = const_cast<QObject *>(caller);
|
||||
this->data->hasCaller = true;
|
||||
|
|
|
@ -118,7 +118,7 @@ void NetworkTask::logReply()
|
|||
void NetworkTask::writeToCache(const QByteArray &bytes) const
|
||||
{
|
||||
std::ignore = QtConcurrent::run([data = this->data_, bytes] {
|
||||
QFile cachedFile(getIApp()->getPaths().cacheDirectory() + "/" +
|
||||
QFile cachedFile(getApp()->getPaths().cacheDirectory() + "/" +
|
||||
data->getHash());
|
||||
|
||||
if (cachedFile.open(QIODevice::WriteOnly))
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#include "Account.hpp"
|
||||
#include "controllers/accounts/Account.hpp"
|
||||
|
||||
#include <tuple>
|
||||
|
||||
|
|
|
@ -47,7 +47,7 @@ AccountController::AccountController()
|
|||
});
|
||||
}
|
||||
|
||||
void AccountController::initialize(Settings &settings, const Paths &paths)
|
||||
void AccountController::load()
|
||||
{
|
||||
this->twitch.load();
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include "common/SignalVector.hpp"
|
||||
#include "common/Singleton.hpp"
|
||||
#include "providers/twitch/TwitchAccountManager.hpp"
|
||||
|
||||
#include <QObject>
|
||||
|
@ -14,14 +13,17 @@ class Paths;
|
|||
|
||||
class AccountModel;
|
||||
|
||||
class AccountController final : public Singleton
|
||||
class AccountController final
|
||||
{
|
||||
public:
|
||||
AccountController();
|
||||
|
||||
AccountModel *createModel(QObject *parent);
|
||||
|
||||
void initialize(Settings &settings, const Paths &paths) override;
|
||||
/**
|
||||
* Load current user & send off a signal to subscribers about any potential changes
|
||||
*/
|
||||
void load();
|
||||
|
||||
TwitchAccountManager twitch;
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#include "AccountModel.hpp"
|
||||
#include "controllers/accounts/AccountModel.hpp"
|
||||
|
||||
#include "controllers/accounts/Account.hpp"
|
||||
#include "util/StandardItemHelper.hpp"
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#include "Command.hpp"
|
||||
#include "controllers/commands/Command.hpp"
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
|
|
|
@ -121,7 +121,7 @@ const std::unordered_map<QString, VariableReplacer> COMMAND_VARS{
|
|||
(void)(channel); //unused
|
||||
(void)(message); //unused
|
||||
auto uid =
|
||||
getIApp()->getAccounts()->twitch.getCurrent()->getUserId();
|
||||
getApp()->getAccounts()->twitch.getCurrent()->getUserId();
|
||||
return uid.isEmpty() ? altText : uid;
|
||||
},
|
||||
},
|
||||
|
@ -131,7 +131,7 @@ const std::unordered_map<QString, VariableReplacer> COMMAND_VARS{
|
|||
(void)(channel); //unused
|
||||
(void)(message); //unused
|
||||
auto name =
|
||||
getIApp()->getAccounts()->twitch.getCurrent()->getUserName();
|
||||
getApp()->getAccounts()->twitch.getCurrent()->getUserName();
|
||||
return name.isEmpty() ? altText : name;
|
||||
},
|
||||
},
|
||||
|
@ -263,7 +263,7 @@ const std::unordered_map<QString, VariableReplacer> COMMAND_VARS{
|
|||
|
||||
namespace chatterino {
|
||||
|
||||
void CommandController::initialize(Settings &, const Paths &paths)
|
||||
CommandController::CommandController(const Paths &paths)
|
||||
{
|
||||
// Update commands map when the vector of commands has been updated
|
||||
auto addFirstMatchToMap = [this](auto args) {
|
||||
|
@ -459,6 +459,8 @@ void CommandController::initialize(Settings &, const Paths &paths)
|
|||
this->registerCommand("/debug-force-image-unload",
|
||||
&commands::forceImageUnload);
|
||||
|
||||
this->registerCommand("/debug-test", &commands::debugTest);
|
||||
|
||||
this->registerCommand("/shield", &commands::shieldModeOn);
|
||||
this->registerCommand("/shieldoff", &commands::shieldModeOff);
|
||||
|
||||
|
@ -485,7 +487,7 @@ QString CommandController::execCommand(const QString &textNoEmoji,
|
|||
ChannelPtr channel, bool dryRun)
|
||||
{
|
||||
QString text =
|
||||
getIApp()->getEmotes()->getEmojis()->replaceShortCodes(textNoEmoji);
|
||||
getApp()->getEmotes()->getEmojis()->replaceShortCodes(textNoEmoji);
|
||||
QStringList words = text.split(' ', Qt::SkipEmptyParts);
|
||||
|
||||
if (words.length() == 0)
|
||||
|
@ -500,7 +502,7 @@ QString CommandController::execCommand(const QString &textNoEmoji,
|
|||
const auto it = this->userCommands_.find(commandName);
|
||||
if (it != this->userCommands_.end())
|
||||
{
|
||||
text = getIApp()->getEmotes()->getEmojis()->replaceShortCodes(
|
||||
text = getApp()->getEmotes()->getEmojis()->replaceShortCodes(
|
||||
this->execCustomCommand(words, it.value(), dryRun, channel));
|
||||
|
||||
words = text.split(' ', Qt::SkipEmptyParts);
|
||||
|
@ -554,8 +556,7 @@ QString CommandController::execCommand(const QString &textNoEmoji,
|
|||
|
||||
if (!dryRun && channel->getType() == Channel::Type::TwitchWhispers)
|
||||
{
|
||||
channel->addMessage(
|
||||
makeSystemMessage("Use /w <username> <message> to whisper"));
|
||||
channel->addSystemMessage("Use /w <username> <message> to whisper");
|
||||
return "";
|
||||
}
|
||||
|
||||
|
@ -571,7 +572,7 @@ bool CommandController::registerPluginCommand(const QString &commandName)
|
|||
}
|
||||
|
||||
this->commands_[commandName] = [commandName](const CommandContext &ctx) {
|
||||
return getIApp()->getPlugins()->tryExecPluginCommand(commandName, ctx);
|
||||
return getApp()->getPlugins()->tryExecPluginCommand(commandName, ctx);
|
||||
};
|
||||
this->pluginCommands_.append(commandName);
|
||||
return true;
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include "common/SignalVector.hpp"
|
||||
#include "common/Singleton.hpp"
|
||||
#include "util/QStringHash.hpp"
|
||||
|
||||
#include <pajlada/settings.hpp>
|
||||
|
@ -24,7 +23,7 @@ struct Command;
|
|||
class CommandModel;
|
||||
struct CommandContext;
|
||||
|
||||
class CommandController final : public Singleton
|
||||
class CommandController final
|
||||
{
|
||||
public:
|
||||
SignalVector<Command> items;
|
||||
|
@ -33,8 +32,8 @@ public:
|
|||
bool dryRun);
|
||||
QStringList getDefaultChatterinoCommandList();
|
||||
|
||||
void initialize(Settings &, const Paths &paths) override;
|
||||
void save() override;
|
||||
CommandController(const Paths &paths);
|
||||
void save();
|
||||
|
||||
CommandModel *createModel(QObject *parent);
|
||||
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
#include "controllers/accounts/AccountController.hpp"
|
||||
#include "controllers/commands/CommandContext.hpp"
|
||||
#include "controllers/userdata/UserDataController.hpp"
|
||||
#include "messages/MessageBuilder.hpp"
|
||||
#include "providers/twitch/api/Helix.hpp"
|
||||
#include "providers/twitch/TwitchAccount.hpp"
|
||||
#include "providers/twitch/TwitchChannel.hpp"
|
||||
|
@ -39,10 +38,10 @@ QString follow(const CommandContext &ctx)
|
|||
{
|
||||
return "";
|
||||
}
|
||||
ctx.channel->addMessage(makeSystemMessage(
|
||||
ctx.channel->addSystemMessage(
|
||||
"Twitch has removed the ability to follow users through "
|
||||
"third-party applications. For more information, see "
|
||||
"https://github.com/Chatterino/chatterino2/issues/3076"));
|
||||
"https://github.com/Chatterino/chatterino2/issues/3076");
|
||||
return "";
|
||||
}
|
||||
|
||||
|
@ -52,10 +51,10 @@ QString unfollow(const CommandContext &ctx)
|
|||
{
|
||||
return "";
|
||||
}
|
||||
ctx.channel->addMessage(makeSystemMessage(
|
||||
ctx.channel->addSystemMessage(
|
||||
"Twitch has removed the ability to unfollow users through "
|
||||
"third-party applications. For more information, see "
|
||||
"https://github.com/Chatterino/chatterino2/issues/3076"));
|
||||
"https://github.com/Chatterino/chatterino2/issues/3076");
|
||||
return "";
|
||||
}
|
||||
|
||||
|
@ -68,8 +67,8 @@ QString uptime(const CommandContext &ctx)
|
|||
|
||||
if (ctx.twitchChannel == nullptr)
|
||||
{
|
||||
ctx.channel->addMessage(makeSystemMessage(
|
||||
"The /uptime command only works in Twitch Channels."));
|
||||
ctx.channel->addSystemMessage(
|
||||
"The /uptime command only works in Twitch Channels.");
|
||||
return "";
|
||||
}
|
||||
|
||||
|
@ -78,7 +77,7 @@ QString uptime(const CommandContext &ctx)
|
|||
QString messageText =
|
||||
streamStatus->live ? streamStatus->uptime : "Channel is not live.";
|
||||
|
||||
ctx.channel->addMessage(makeSystemMessage(messageText));
|
||||
ctx.channel->addSystemMessage(messageText);
|
||||
|
||||
return "";
|
||||
}
|
||||
|
@ -92,8 +91,7 @@ QString user(const CommandContext &ctx)
|
|||
|
||||
if (ctx.words.size() < 2)
|
||||
{
|
||||
ctx.channel->addMessage(
|
||||
makeSystemMessage("Usage: /user <user> [channel]"));
|
||||
ctx.channel->addSystemMessage("Usage: /user <user> [channel]");
|
||||
return "";
|
||||
}
|
||||
QString userName = ctx.words[1];
|
||||
|
@ -129,11 +127,11 @@ QString requests(const CommandContext &ctx)
|
|||
}
|
||||
else
|
||||
{
|
||||
ctx.channel->addMessage(makeSystemMessage(
|
||||
ctx.channel->addSystemMessage(
|
||||
"Usage: /requests [channel]. You can also use the command "
|
||||
"without arguments in any Twitch channel to open its "
|
||||
"channel points requests queue. Only the broadcaster and "
|
||||
"moderators have permission to view the queue."));
|
||||
"moderators have permission to view the queue.");
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
@ -163,11 +161,11 @@ QString lowtrust(const CommandContext &ctx)
|
|||
}
|
||||
else
|
||||
{
|
||||
ctx.channel->addMessage(makeSystemMessage(
|
||||
ctx.channel->addSystemMessage(
|
||||
"Usage: /lowtrust [channel]. You can also use the command "
|
||||
"without arguments in any Twitch channel to open its "
|
||||
"suspicious user activity feed. Only the broadcaster and "
|
||||
"moderators have permission to view this feed."));
|
||||
"moderators have permission to view this feed.");
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
@ -190,15 +188,15 @@ QString clip(const CommandContext &ctx)
|
|||
if (const auto type = ctx.channel->getType();
|
||||
type != Channel::Type::Twitch && type != Channel::Type::TwitchWatching)
|
||||
{
|
||||
ctx.channel->addMessage(makeSystemMessage(
|
||||
"The /clip command only works in Twitch Channels."));
|
||||
ctx.channel->addSystemMessage(
|
||||
"The /clip command only works in Twitch Channels.");
|
||||
return "";
|
||||
}
|
||||
|
||||
if (ctx.twitchChannel == nullptr)
|
||||
{
|
||||
ctx.channel->addMessage(makeSystemMessage(
|
||||
"The /clip command only works in Twitch Channels."));
|
||||
ctx.channel->addSystemMessage(
|
||||
"The /clip command only works in Twitch Channels.");
|
||||
return "";
|
||||
}
|
||||
|
||||
|
@ -216,25 +214,25 @@ QString marker(const CommandContext &ctx)
|
|||
|
||||
if (ctx.twitchChannel == nullptr)
|
||||
{
|
||||
ctx.channel->addMessage(makeSystemMessage(
|
||||
"The /marker command only works in Twitch channels."));
|
||||
ctx.channel->addSystemMessage(
|
||||
"The /marker command only works in Twitch channels.");
|
||||
return "";
|
||||
}
|
||||
|
||||
// Avoid Helix calls without Client ID and/or OAuth Token
|
||||
if (getIApp()->getAccounts()->twitch.getCurrent()->isAnon())
|
||||
if (getApp()->getAccounts()->twitch.getCurrent()->isAnon())
|
||||
{
|
||||
ctx.channel->addMessage(makeSystemMessage(
|
||||
"You need to be logged in to create stream markers!"));
|
||||
ctx.channel->addSystemMessage(
|
||||
"You need to be logged in to create stream markers!");
|
||||
return "";
|
||||
}
|
||||
|
||||
// Exact same message as in webchat
|
||||
if (!ctx.twitchChannel->isLive())
|
||||
{
|
||||
ctx.channel->addMessage(makeSystemMessage(
|
||||
ctx.channel->addSystemMessage(
|
||||
"You can only add stream markers during live streams. Try "
|
||||
"again when the channel is live streaming."));
|
||||
"again when the channel is live streaming.");
|
||||
return "";
|
||||
}
|
||||
|
||||
|
@ -247,13 +245,13 @@ QString marker(const CommandContext &ctx)
|
|||
ctx.twitchChannel->roomId(), arguments.join(" ").left(140),
|
||||
[channel{ctx.channel},
|
||||
arguments](const HelixStreamMarker &streamMarker) {
|
||||
channel->addMessage(makeSystemMessage(
|
||||
channel->addSystemMessage(
|
||||
QString("Successfully added a stream marker at %1%2")
|
||||
.arg(formatTime(streamMarker.positionSeconds))
|
||||
.arg(streamMarker.description.isEmpty()
|
||||
? ""
|
||||
: QString(": \"%1\"")
|
||||
.arg(streamMarker.description))));
|
||||
.arg(streamMarker.description)));
|
||||
},
|
||||
[channel{ctx.channel}](auto error) {
|
||||
QString errorMessage("Failed to create stream marker - ");
|
||||
|
@ -279,7 +277,7 @@ QString marker(const CommandContext &ctx)
|
|||
break;
|
||||
}
|
||||
|
||||
channel->addMessage(makeSystemMessage(errorMessage));
|
||||
channel->addSystemMessage(errorMessage);
|
||||
});
|
||||
|
||||
return "";
|
||||
|
@ -303,10 +301,10 @@ QString streamlink(const CommandContext &ctx)
|
|||
}
|
||||
else
|
||||
{
|
||||
ctx.channel->addMessage(makeSystemMessage(
|
||||
ctx.channel->addSystemMessage(
|
||||
"/streamlink [channel]. Open specified Twitch channel in "
|
||||
"streamlink. If no channel argument is specified, open the "
|
||||
"current Twitch channel instead."));
|
||||
"current Twitch channel instead.");
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
@ -335,10 +333,10 @@ QString popout(const CommandContext &ctx)
|
|||
}
|
||||
else
|
||||
{
|
||||
ctx.channel->addMessage(makeSystemMessage(
|
||||
ctx.channel->addSystemMessage(
|
||||
"Usage: /popout <channel>. You can also use the command "
|
||||
"without arguments in any Twitch channel to open its "
|
||||
"popout chat."));
|
||||
"popout chat.");
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
@ -369,7 +367,7 @@ QString popup(const CommandContext &ctx)
|
|||
if (target.isEmpty())
|
||||
{
|
||||
auto *currentPage =
|
||||
dynamic_cast<SplitContainer *>(getIApp()
|
||||
dynamic_cast<SplitContainer *>(getApp()
|
||||
->getWindows()
|
||||
->getMainWindow()
|
||||
.getNotebook()
|
||||
|
@ -385,14 +383,13 @@ QString popup(const CommandContext &ctx)
|
|||
}
|
||||
}
|
||||
|
||||
ctx.channel->addMessage(makeSystemMessage(usageMessage));
|
||||
ctx.channel->addSystemMessage(usageMessage);
|
||||
return "";
|
||||
}
|
||||
|
||||
// Open channel passed as argument in a popup
|
||||
auto targetChannel =
|
||||
getIApp()->getTwitchAbstract()->getOrAddChannel(target);
|
||||
getIApp()->getWindows()->openInPopup(targetChannel);
|
||||
auto targetChannel = getApp()->getTwitch()->getOrAddChannel(target);
|
||||
getApp()->getWindows()->openInPopup(targetChannel);
|
||||
|
||||
return "";
|
||||
}
|
||||
|
@ -401,11 +398,11 @@ QString clearmessages(const CommandContext &ctx)
|
|||
{
|
||||
(void)ctx;
|
||||
|
||||
auto *currentPage = dynamic_cast<SplitContainer *>(getIApp()
|
||||
auto *currentPage = getApp()
|
||||
->getWindows()
|
||||
->getMainWindow()
|
||||
.getNotebook()
|
||||
.getSelectedPage());
|
||||
->getLastSelectedWindow()
|
||||
->getNotebook()
|
||||
.getSelectedPage();
|
||||
|
||||
if (auto *split = currentPage->getSelectedSplit())
|
||||
{
|
||||
|
@ -469,8 +466,8 @@ QString openURL(const CommandContext &ctx)
|
|||
const auto &positionalArguments = parser.positionalArguments();
|
||||
if (positionalArguments.isEmpty())
|
||||
{
|
||||
ctx.channel->addMessage(makeSystemMessage(
|
||||
"Usage: /openurl <URL> [--incognito/--no-incognito]"));
|
||||
ctx.channel->addSystemMessage(
|
||||
"Usage: /openurl <URL> [--incognito/--no-incognito]");
|
||||
return "";
|
||||
}
|
||||
auto urlString = parser.positionalArguments().join(' ');
|
||||
|
@ -478,7 +475,7 @@ QString openURL(const CommandContext &ctx)
|
|||
QUrl url = QUrl::fromUserInput(urlString);
|
||||
if (!url.isValid())
|
||||
{
|
||||
ctx.channel->addMessage(makeSystemMessage("Invalid URL specified."));
|
||||
ctx.channel->addSystemMessage("Invalid URL specified.");
|
||||
return "";
|
||||
}
|
||||
|
||||
|
@ -488,9 +485,9 @@ QString openURL(const CommandContext &ctx)
|
|||
|
||||
if (forcePrivateMode && forceNonPrivateMode)
|
||||
{
|
||||
ctx.channel->addMessage(makeSystemMessage(
|
||||
ctx.channel->addSystemMessage(
|
||||
"Error: /openurl may only be called with --incognito or "
|
||||
"--no-incognito, not both at the same time."));
|
||||
"--no-incognito, not both at the same time.");
|
||||
return "";
|
||||
}
|
||||
|
||||
|
@ -518,7 +515,7 @@ QString openURL(const CommandContext &ctx)
|
|||
|
||||
if (!res)
|
||||
{
|
||||
ctx.channel->addMessage(makeSystemMessage("Could not open URL."));
|
||||
ctx.channel->addSystemMessage("Could not open URL.");
|
||||
}
|
||||
|
||||
return "";
|
||||
|
@ -533,8 +530,7 @@ QString sendRawMessage(const CommandContext &ctx)
|
|||
|
||||
if (ctx.channel->isTwitchChannel())
|
||||
{
|
||||
getIApp()->getTwitchAbstract()->sendRawMessage(
|
||||
ctx.words.mid(1).join(" "));
|
||||
getApp()->getTwitch()->sendRawMessage(ctx.words.mid(1).join(" "));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -553,21 +549,21 @@ QString injectFakeMessage(const CommandContext &ctx)
|
|||
|
||||
if (!ctx.channel->isTwitchChannel())
|
||||
{
|
||||
ctx.channel->addMessage(makeSystemMessage(
|
||||
"The /fakemsg command only works in Twitch channels."));
|
||||
ctx.channel->addSystemMessage(
|
||||
"The /fakemsg command only works in Twitch channels.");
|
||||
return "";
|
||||
}
|
||||
|
||||
if (ctx.words.size() < 2)
|
||||
{
|
||||
ctx.channel->addMessage(makeSystemMessage(
|
||||
ctx.channel->addSystemMessage(
|
||||
"Usage: /fakemsg (raw irc text) - injects raw irc text as "
|
||||
"if it was a message received from TMI"));
|
||||
"if it was a message received from TMI");
|
||||
return "";
|
||||
}
|
||||
|
||||
auto ircText = ctx.words.mid(1).join(" ");
|
||||
getIApp()->getTwitchAbstract()->addFakeMessage(ircText);
|
||||
getApp()->getTwitch()->addFakeMessage(ircText);
|
||||
|
||||
return "";
|
||||
}
|
||||
|
@ -583,13 +579,13 @@ QString injectStreamUpdateNoStream(const CommandContext &ctx)
|
|||
}
|
||||
if (ctx.twitchChannel == nullptr)
|
||||
{
|
||||
ctx.channel->addMessage(
|
||||
makeSystemMessage("The /debug-update-to-no-stream command only "
|
||||
"works in Twitch channels"));
|
||||
ctx.channel->addSystemMessage(
|
||||
"The /debug-update-to-no-stream command only "
|
||||
"works in Twitch channels");
|
||||
return "";
|
||||
}
|
||||
|
||||
ctx.twitchChannel->updateStreamStatus(std::nullopt);
|
||||
ctx.twitchChannel->updateStreamStatus(std::nullopt, false);
|
||||
return "";
|
||||
}
|
||||
|
||||
|
@ -602,9 +598,8 @@ QString copyToClipboard(const CommandContext &ctx)
|
|||
|
||||
if (ctx.words.size() < 2)
|
||||
{
|
||||
ctx.channel->addMessage(
|
||||
makeSystemMessage("Usage: /copy <text> - copies provided "
|
||||
"text to clipboard."));
|
||||
ctx.channel->addSystemMessage("Usage: /copy <text> - copies provided "
|
||||
"text to clipboard.");
|
||||
return "";
|
||||
}
|
||||
|
||||
|
@ -621,15 +616,15 @@ QString unstableSetUserClientSideColor(const CommandContext &ctx)
|
|||
|
||||
if (ctx.twitchChannel == nullptr)
|
||||
{
|
||||
ctx.channel->addMessage(
|
||||
makeSystemMessage("The /unstable-set-user-color command only "
|
||||
"works in Twitch channels."));
|
||||
ctx.channel->addSystemMessage(
|
||||
"The /unstable-set-user-color command only "
|
||||
"works in Twitch channels.");
|
||||
return "";
|
||||
}
|
||||
if (ctx.words.size() < 2)
|
||||
{
|
||||
ctx.channel->addMessage(makeSystemMessage(
|
||||
QString("Usage: %1 <TwitchUserID> [color]").arg(ctx.words.at(0))));
|
||||
ctx.channel->addSystemMessage(
|
||||
QString("Usage: %1 <TwitchUserID> [color]").arg(ctx.words.at(0)));
|
||||
return "";
|
||||
}
|
||||
|
||||
|
@ -637,7 +632,7 @@ QString unstableSetUserClientSideColor(const CommandContext &ctx)
|
|||
|
||||
auto color = ctx.words.value(2);
|
||||
|
||||
getIApp()->getUserData()->setUserColor(userID, color);
|
||||
getApp()->getUserData()->setUserColor(userID, color);
|
||||
|
||||
return "";
|
||||
}
|
||||
|
@ -653,9 +648,8 @@ QString openUsercard(const CommandContext &ctx)
|
|||
|
||||
if (ctx.words.size() < 2)
|
||||
{
|
||||
channel->addMessage(
|
||||
makeSystemMessage("Usage: /usercard <username> [channel] or "
|
||||
"/usercard id:<id> [channel]"));
|
||||
channel->addSystemMessage("Usage: /usercard <username> [channel] or "
|
||||
"/usercard id:<id> [channel]");
|
||||
return "";
|
||||
}
|
||||
|
||||
|
@ -668,13 +662,13 @@ QString openUsercard(const CommandContext &ctx)
|
|||
stripChannelName(channelName);
|
||||
|
||||
ChannelPtr channelTemp =
|
||||
getIApp()->getTwitchAbstract()->getChannelOrEmpty(channelName);
|
||||
getApp()->getTwitch()->getChannelOrEmpty(channelName);
|
||||
|
||||
if (channelTemp->isEmpty())
|
||||
{
|
||||
channel->addMessage(makeSystemMessage(
|
||||
channel->addSystemMessage(
|
||||
"A usercard can only be displayed for a channel that is "
|
||||
"currently opened in Chatterino."));
|
||||
"currently opened in Chatterino.");
|
||||
return "";
|
||||
}
|
||||
|
||||
|
@ -683,7 +677,7 @@ QString openUsercard(const CommandContext &ctx)
|
|||
|
||||
// try to link to current split if possible
|
||||
Split *currentSplit = nullptr;
|
||||
auto *currentPage = dynamic_cast<SplitContainer *>(getIApp()
|
||||
auto *currentPage = dynamic_cast<SplitContainer *>(getApp()
|
||||
->getWindows()
|
||||
->getMainWindow()
|
||||
.getNotebook()
|
||||
|
@ -699,7 +693,7 @@ QString openUsercard(const CommandContext &ctx)
|
|||
{
|
||||
// not possible to use current split, try searching for one
|
||||
const auto ¬ebook =
|
||||
getIApp()->getWindows()->getMainWindow().getNotebook();
|
||||
getApp()->getWindows()->getMainWindow().getNotebook();
|
||||
auto count = notebook.getPageCount();
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
|
|
|
@ -22,12 +22,12 @@ QString setLoggingRules(const CommandContext &ctx)
|
|||
{
|
||||
if (ctx.words.size() < 2)
|
||||
{
|
||||
ctx.channel->addMessage(makeSystemMessage(
|
||||
ctx.channel->addSystemMessage(
|
||||
"Usage: /c2-set-logging-rules <rules...>. To enable debug logging "
|
||||
"for all categories from chatterino, use "
|
||||
"'chatterino.*.debug=true'. For the format on the rules, see "
|
||||
"https://doc.qt.io/qt-6/"
|
||||
"qloggingcategory.html#configuring-categories"));
|
||||
"qloggingcategory.html#configuring-categories");
|
||||
return {};
|
||||
}
|
||||
|
||||
|
@ -47,7 +47,7 @@ QString setLoggingRules(const CommandContext &ctx)
|
|||
"https://doc.qt.io/qt-6/qloggingcategory.html#setFilterRules");
|
||||
}
|
||||
|
||||
ctx.channel->addMessage(makeSystemMessage(message));
|
||||
ctx.channel->addSystemMessage(message);
|
||||
return {};
|
||||
}
|
||||
|
||||
|
@ -56,15 +56,13 @@ QString toggleThemeReload(const CommandContext &ctx)
|
|||
if (getTheme()->isAutoReloading())
|
||||
{
|
||||
getTheme()->setAutoReload(false);
|
||||
ctx.channel->addMessage(
|
||||
makeSystemMessage(u"Disabled theme auto reloading."_s));
|
||||
ctx.channel->addSystemMessage(u"Disabled theme auto reloading."_s);
|
||||
return {};
|
||||
}
|
||||
|
||||
getTheme()->setAutoReload(true);
|
||||
ctx.channel->addMessage(
|
||||
makeSystemMessage(u"Auto reloading theme every %1 ms."_s.arg(
|
||||
Theme::AUTO_RELOAD_INTERVAL_MS)));
|
||||
ctx.channel->addSystemMessage(u"Auto reloading theme every %1 ms."_s.arg(
|
||||
Theme::AUTO_RELOAD_INTERVAL_MS));
|
||||
return {};
|
||||
}
|
||||
|
||||
|
@ -92,7 +90,7 @@ QString listEnvironmentVariables(const CommandContext &ctx)
|
|||
builder.emplace<TimestampElement>(QTime::currentTime());
|
||||
builder.emplace<TextElement>(str, MessageElementFlag::Text,
|
||||
MessageColor::System);
|
||||
channel->addMessage(builder.release());
|
||||
channel->addMessage(builder.release(), MessageContext::Original);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
@ -107,7 +105,7 @@ QString listArgs(const CommandContext &ctx)
|
|||
|
||||
QString msg = QApplication::instance()->arguments().join(' ');
|
||||
|
||||
channel->addMessage(makeSystemMessage(msg));
|
||||
channel->addSystemMessage(msg);
|
||||
|
||||
return "";
|
||||
}
|
||||
|
@ -134,4 +132,16 @@ QString forceImageUnload(const CommandContext &ctx)
|
|||
return "";
|
||||
}
|
||||
|
||||
QString debugTest(const CommandContext &ctx)
|
||||
{
|
||||
if (!ctx.channel)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
ctx.channel->addSystemMessage("debug-test called");
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
} // namespace chatterino::commands
|
||||
|
|
|
@ -22,4 +22,6 @@ QString forceImageGarbageCollection(const CommandContext &ctx);
|
|||
|
||||
QString forceImageUnload(const CommandContext &ctx);
|
||||
|
||||
QString debugTest(const CommandContext &ctx);
|
||||
|
||||
} // namespace chatterino::commands
|
||||
|
|
|
@ -1,14 +1,11 @@
|
|||
#include "controllers/commands/builtin/twitch/AddModerator.hpp"
|
||||
|
||||
#include "Application.hpp"
|
||||
#include "common/Channel.hpp"
|
||||
#include "controllers/accounts/AccountController.hpp"
|
||||
#include "controllers/commands/CommandContext.hpp"
|
||||
#include "messages/MessageBuilder.hpp"
|
||||
#include "providers/twitch/api/Helix.hpp"
|
||||
#include "providers/twitch/TwitchAccount.hpp"
|
||||
#include "providers/twitch/TwitchChannel.hpp"
|
||||
#include "providers/twitch/TwitchMessageBuilder.hpp"
|
||||
#include "util/Twitch.hpp"
|
||||
|
||||
namespace chatterino::commands {
|
||||
|
@ -22,23 +19,22 @@ QString addModerator(const CommandContext &ctx)
|
|||
|
||||
if (ctx.twitchChannel == nullptr)
|
||||
{
|
||||
ctx.channel->addMessage(makeSystemMessage(
|
||||
"The /mod command only works in Twitch channels."));
|
||||
ctx.channel->addSystemMessage(
|
||||
"The /mod command only works in Twitch channels.");
|
||||
return "";
|
||||
}
|
||||
if (ctx.words.size() < 2)
|
||||
{
|
||||
ctx.channel->addMessage(makeSystemMessage(
|
||||
ctx.channel->addSystemMessage(
|
||||
"Usage: \"/mod <username>\" - Grant moderator status to a "
|
||||
"user. Use \"/mods\" to list the moderators of this channel."));
|
||||
"user. Use \"/mods\" to list the moderators of this channel.");
|
||||
return "";
|
||||
}
|
||||
|
||||
auto currentUser = getIApp()->getAccounts()->twitch.getCurrent();
|
||||
auto currentUser = getApp()->getAccounts()->twitch.getCurrent();
|
||||
if (currentUser->isAnon())
|
||||
{
|
||||
ctx.channel->addMessage(
|
||||
makeSystemMessage("You must be logged in to mod someone!"));
|
||||
ctx.channel->addSystemMessage("You must be logged in to mod someone!");
|
||||
return "";
|
||||
}
|
||||
|
||||
|
@ -52,10 +48,10 @@ QString addModerator(const CommandContext &ctx)
|
|||
getHelix()->addChannelModerator(
|
||||
twitchChannel->roomId(), targetUser.id,
|
||||
[channel, targetUser] {
|
||||
channel->addMessage(makeSystemMessage(
|
||||
channel->addSystemMessage(
|
||||
QString("You have added %1 as a moderator of this "
|
||||
"channel.")
|
||||
.arg(targetUser.displayName)));
|
||||
.arg(targetUser.displayName));
|
||||
},
|
||||
[channel, targetUser](auto error, auto message) {
|
||||
QString errorMessage =
|
||||
|
@ -116,13 +112,13 @@ QString addModerator(const CommandContext &ctx)
|
|||
}
|
||||
break;
|
||||
}
|
||||
channel->addMessage(makeSystemMessage(errorMessage));
|
||||
channel->addSystemMessage(errorMessage);
|
||||
});
|
||||
},
|
||||
[channel{ctx.channel}, target] {
|
||||
// Equivalent error from IRC
|
||||
channel->addMessage(
|
||||
makeSystemMessage(QString("Invalid username: %1").arg(target)));
|
||||
channel->addSystemMessage(
|
||||
QString("Invalid username: %1").arg(target));
|
||||
});
|
||||
|
||||
return "";
|
||||
|
|
|
@ -1,14 +1,11 @@
|
|||
#include "controllers/commands/builtin/twitch/AddVIP.hpp"
|
||||
|
||||
#include "Application.hpp"
|
||||
#include "common/Channel.hpp"
|
||||
#include "controllers/accounts/AccountController.hpp"
|
||||
#include "controllers/commands/CommandContext.hpp"
|
||||
#include "messages/MessageBuilder.hpp"
|
||||
#include "providers/twitch/api/Helix.hpp"
|
||||
#include "providers/twitch/TwitchAccount.hpp"
|
||||
#include "providers/twitch/TwitchChannel.hpp"
|
||||
#include "providers/twitch/TwitchMessageBuilder.hpp"
|
||||
#include "util/Twitch.hpp"
|
||||
|
||||
namespace chatterino::commands {
|
||||
|
@ -22,23 +19,22 @@ QString addVIP(const CommandContext &ctx)
|
|||
|
||||
if (ctx.twitchChannel == nullptr)
|
||||
{
|
||||
ctx.channel->addMessage(makeSystemMessage(
|
||||
"The /vip command only works in Twitch channels."));
|
||||
ctx.channel->addSystemMessage(
|
||||
"The /vip command only works in Twitch channels.");
|
||||
return "";
|
||||
}
|
||||
if (ctx.words.size() < 2)
|
||||
{
|
||||
ctx.channel->addMessage(makeSystemMessage(
|
||||
ctx.channel->addSystemMessage(
|
||||
"Usage: \"/vip <username>\" - Grant VIP status to a user. Use "
|
||||
"\"/vips\" to list the VIPs of this channel."));
|
||||
"\"/vips\" to list the VIPs of this channel.");
|
||||
return "";
|
||||
}
|
||||
|
||||
auto currentUser = getIApp()->getAccounts()->twitch.getCurrent();
|
||||
auto currentUser = getApp()->getAccounts()->twitch.getCurrent();
|
||||
if (currentUser->isAnon())
|
||||
{
|
||||
ctx.channel->addMessage(
|
||||
makeSystemMessage("You must be logged in to VIP someone!"));
|
||||
ctx.channel->addSystemMessage("You must be logged in to VIP someone!");
|
||||
return "";
|
||||
}
|
||||
|
||||
|
@ -52,9 +48,9 @@ QString addVIP(const CommandContext &ctx)
|
|||
getHelix()->addChannelVIP(
|
||||
twitchChannel->roomId(), targetUser.id,
|
||||
[channel, targetUser] {
|
||||
channel->addMessage(makeSystemMessage(
|
||||
channel->addSystemMessage(
|
||||
QString("You have added %1 as a VIP of this channel.")
|
||||
.arg(targetUser.displayName)));
|
||||
.arg(targetUser.displayName));
|
||||
},
|
||||
[channel, targetUser](auto error, auto message) {
|
||||
QString errorMessage = QString("Failed to add VIP - ");
|
||||
|
@ -97,13 +93,13 @@ QString addVIP(const CommandContext &ctx)
|
|||
}
|
||||
break;
|
||||
}
|
||||
channel->addMessage(makeSystemMessage(errorMessage));
|
||||
channel->addSystemMessage(errorMessage);
|
||||
});
|
||||
},
|
||||
[channel{ctx.channel}, target] {
|
||||
// Equivalent error from IRC
|
||||
channel->addMessage(
|
||||
makeSystemMessage(QString("Invalid username: %1").arg(target)));
|
||||
channel->addSystemMessage(
|
||||
QString("Invalid username: %1").arg(target));
|
||||
});
|
||||
|
||||
return "";
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
#include "controllers/commands/builtin/twitch/Announce.hpp"
|
||||
|
||||
#include "Application.hpp"
|
||||
#include "common/Channel.hpp"
|
||||
#include "controllers/accounts/AccountController.hpp"
|
||||
#include "controllers/commands/CommandContext.hpp"
|
||||
#include "messages/MessageBuilder.hpp"
|
||||
#include "providers/twitch/api/Helix.hpp"
|
||||
#include "providers/twitch/TwitchAccount.hpp"
|
||||
#include "providers/twitch/TwitchChannel.hpp"
|
||||
|
@ -22,8 +20,8 @@ QString sendAnnouncementColor(const CommandContext &ctx,
|
|||
|
||||
if (ctx.twitchChannel == nullptr)
|
||||
{
|
||||
ctx.channel->addMessage(makeSystemMessage(
|
||||
"This command can only be used in Twitch channels."));
|
||||
ctx.channel->addSystemMessage(
|
||||
"This command can only be used in Twitch channels.");
|
||||
return "";
|
||||
}
|
||||
|
||||
|
@ -48,16 +46,16 @@ QString sendAnnouncementColor(const CommandContext &ctx,
|
|||
"message with a %1 highlight.")
|
||||
.arg(colorStr);
|
||||
}
|
||||
ctx.channel->addMessage(makeSystemMessage(usageMsg));
|
||||
ctx.channel->addSystemMessage(usageMsg);
|
||||
return "";
|
||||
}
|
||||
|
||||
auto user = getIApp()->getAccounts()->twitch.getCurrent();
|
||||
auto user = getApp()->getAccounts()->twitch.getCurrent();
|
||||
if (user->isAnon())
|
||||
{
|
||||
ctx.channel->addMessage(makeSystemMessage(
|
||||
ctx.channel->addSystemMessage(
|
||||
QString("You must be logged in to use the /announce%1 command.")
|
||||
.arg(colorStr)));
|
||||
.arg(colorStr));
|
||||
return "";
|
||||
}
|
||||
|
||||
|
@ -93,7 +91,7 @@ QString sendAnnouncementColor(const CommandContext &ctx,
|
|||
break;
|
||||
}
|
||||
|
||||
channel->addMessage(makeSystemMessage(errorMessage));
|
||||
channel->addSystemMessage(errorMessage);
|
||||
});
|
||||
return "";
|
||||
}
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
#include "controllers/accounts/AccountController.hpp"
|
||||
#include "controllers/commands/CommandContext.hpp"
|
||||
#include "controllers/commands/common/ChannelAction.hpp"
|
||||
#include "messages/MessageBuilder.hpp"
|
||||
#include "providers/twitch/api/Helix.hpp"
|
||||
#include "providers/twitch/TwitchAccount.hpp"
|
||||
#include "providers/twitch/TwitchChannel.hpp"
|
||||
|
@ -93,7 +92,7 @@ void banUserByID(const ChannelPtr &channel, const QString &channelID,
|
|||
[channel, displayName](auto error, auto message) {
|
||||
auto errorMessage =
|
||||
formatBanTimeoutError("ban", error, message, displayName);
|
||||
channel->addMessage(makeSystemMessage(errorMessage));
|
||||
channel->addSystemMessage(errorMessage);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -110,7 +109,7 @@ void timeoutUserByID(const ChannelPtr &channel, const QString &channelID,
|
|||
[channel, displayName](auto error, auto message) {
|
||||
auto errorMessage =
|
||||
formatBanTimeoutError("timeout", error, message, displayName);
|
||||
channel->addMessage(makeSystemMessage(errorMessage));
|
||||
channel->addSystemMessage(errorMessage);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -129,7 +128,7 @@ QString sendBan(const CommandContext &ctx)
|
|||
{
|
||||
if (ctx.channel != nullptr)
|
||||
{
|
||||
ctx.channel->addMessage(makeSystemMessage(actions.error()));
|
||||
ctx.channel->addSystemMessage(actions.error());
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -142,11 +141,10 @@ QString sendBan(const CommandContext &ctx)
|
|||
|
||||
assert(!actions.value().empty());
|
||||
|
||||
auto currentUser = getIApp()->getAccounts()->twitch.getCurrent();
|
||||
auto currentUser = getApp()->getAccounts()->twitch.getCurrent();
|
||||
if (currentUser->isAnon())
|
||||
{
|
||||
ctx.channel->addMessage(
|
||||
makeSystemMessage("You must be logged in to ban someone!"));
|
||||
ctx.channel->addSystemMessage("You must be logged in to ban someone!");
|
||||
return "";
|
||||
}
|
||||
|
||||
|
@ -192,16 +190,16 @@ QString sendBan(const CommandContext &ctx)
|
|||
userLoginsToFetch](const auto &users) mutable {
|
||||
if (!actionChannel.hydrateFrom(users))
|
||||
{
|
||||
channel->addMessage(makeSystemMessage(
|
||||
channel->addSystemMessage(
|
||||
QString("Failed to ban, bad channel name: %1")
|
||||
.arg(actionChannel.login)));
|
||||
.arg(actionChannel.login));
|
||||
return;
|
||||
}
|
||||
if (!actionTarget.hydrateFrom(users))
|
||||
{
|
||||
channel->addMessage(makeSystemMessage(
|
||||
channel->addSystemMessage(
|
||||
QString("Failed to ban, bad target name: %1")
|
||||
.arg(actionTarget.login)));
|
||||
.arg(actionTarget.login));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -210,9 +208,9 @@ QString sendBan(const CommandContext &ctx)
|
|||
reason, actionTarget.displayName);
|
||||
},
|
||||
[channel{ctx.channel}, userLoginsToFetch] {
|
||||
channel->addMessage(makeSystemMessage(
|
||||
channel->addSystemMessage(
|
||||
QString("Failed to ban, bad username(s): %1")
|
||||
.arg(userLoginsToFetch.join(", "))));
|
||||
.arg(userLoginsToFetch.join(", ")));
|
||||
});
|
||||
}
|
||||
else
|
||||
|
@ -239,8 +237,8 @@ QString sendBanById(const CommandContext &ctx)
|
|||
}
|
||||
if (twitchChannel == nullptr)
|
||||
{
|
||||
channel->addMessage(makeSystemMessage(
|
||||
QString("The /banid command only works in Twitch channels.")));
|
||||
channel->addSystemMessage(
|
||||
"The /banid command only works in Twitch channels.");
|
||||
return "";
|
||||
}
|
||||
|
||||
|
@ -250,15 +248,14 @@ QString sendBanById(const CommandContext &ctx)
|
|||
"shown to the target user and other moderators.";
|
||||
if (words.size() < 2)
|
||||
{
|
||||
channel->addMessage(makeSystemMessage(usageStr));
|
||||
channel->addSystemMessage(usageStr);
|
||||
return "";
|
||||
}
|
||||
|
||||
auto currentUser = getIApp()->getAccounts()->twitch.getCurrent();
|
||||
auto currentUser = getApp()->getAccounts()->twitch.getCurrent();
|
||||
if (currentUser->isAnon())
|
||||
{
|
||||
channel->addMessage(
|
||||
makeSystemMessage("You must be logged in to ban someone!"));
|
||||
channel->addSystemMessage("You must be logged in to ban someone!");
|
||||
return "";
|
||||
}
|
||||
|
||||
|
@ -282,7 +279,7 @@ QString sendTimeout(const CommandContext &ctx)
|
|||
{
|
||||
if (ctx.channel != nullptr)
|
||||
{
|
||||
ctx.channel->addMessage(makeSystemMessage(actions.error()));
|
||||
ctx.channel->addSystemMessage(actions.error());
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -295,11 +292,11 @@ QString sendTimeout(const CommandContext &ctx)
|
|||
|
||||
assert(!actions.value().empty());
|
||||
|
||||
auto currentUser = getIApp()->getAccounts()->twitch.getCurrent();
|
||||
auto currentUser = getApp()->getAccounts()->twitch.getCurrent();
|
||||
if (currentUser->isAnon())
|
||||
{
|
||||
ctx.channel->addMessage(
|
||||
makeSystemMessage("You must be logged in to timeout someone!"));
|
||||
ctx.channel->addSystemMessage(
|
||||
"You must be logged in to timeout someone!");
|
||||
return "";
|
||||
}
|
||||
|
||||
|
@ -346,16 +343,16 @@ QString sendTimeout(const CommandContext &ctx)
|
|||
userLoginsToFetch](const auto &users) mutable {
|
||||
if (!actionChannel.hydrateFrom(users))
|
||||
{
|
||||
channel->addMessage(makeSystemMessage(
|
||||
channel->addSystemMessage(
|
||||
QString("Failed to timeout, bad channel name: %1")
|
||||
.arg(actionChannel.login)));
|
||||
.arg(actionChannel.login));
|
||||
return;
|
||||
}
|
||||
if (!actionTarget.hydrateFrom(users))
|
||||
{
|
||||
channel->addMessage(makeSystemMessage(
|
||||
channel->addSystemMessage(
|
||||
QString("Failed to timeout, bad target name: %1")
|
||||
.arg(actionTarget.login)));
|
||||
.arg(actionTarget.login));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -364,9 +361,9 @@ QString sendTimeout(const CommandContext &ctx)
|
|||
duration, reason, actionTarget.displayName);
|
||||
},
|
||||
[channel{ctx.channel}, userLoginsToFetch] {
|
||||
channel->addMessage(makeSystemMessage(
|
||||
channel->addSystemMessage(
|
||||
QString("Failed to timeout, bad username(s): %1")
|
||||
.arg(userLoginsToFetch.join(", "))));
|
||||
.arg(userLoginsToFetch.join(", ")));
|
||||
});
|
||||
}
|
||||
else
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
#include "common/Channel.hpp"
|
||||
#include "controllers/accounts/AccountController.hpp"
|
||||
#include "controllers/commands/CommandContext.hpp"
|
||||
#include "messages/MessageBuilder.hpp"
|
||||
#include "providers/twitch/api/Helix.hpp"
|
||||
#include "providers/twitch/TwitchAccount.hpp"
|
||||
#include "util/Twitch.hpp"
|
||||
|
@ -26,23 +25,23 @@ QString blockUser(const CommandContext &ctx)
|
|||
|
||||
if (ctx.twitchChannel == nullptr)
|
||||
{
|
||||
ctx.channel->addMessage(makeSystemMessage(
|
||||
"The /block command only works in Twitch channels."));
|
||||
ctx.channel->addSystemMessage(
|
||||
"The /block command only works in Twitch channels.");
|
||||
return "";
|
||||
}
|
||||
|
||||
if (ctx.words.size() < 2)
|
||||
{
|
||||
ctx.channel->addMessage(makeSystemMessage("Usage: /block <user>"));
|
||||
ctx.channel->addSystemMessage("Usage: /block <user>");
|
||||
return "";
|
||||
}
|
||||
|
||||
auto currentUser = getIApp()->getAccounts()->twitch.getCurrent();
|
||||
auto currentUser = getApp()->getAccounts()->twitch.getCurrent();
|
||||
|
||||
if (currentUser->isAnon())
|
||||
{
|
||||
ctx.channel->addMessage(
|
||||
makeSystemMessage("You must be logged in to block someone!"));
|
||||
ctx.channel->addSystemMessage(
|
||||
"You must be logged in to block someone!");
|
||||
return "";
|
||||
}
|
||||
|
||||
|
@ -53,25 +52,24 @@ QString blockUser(const CommandContext &ctx)
|
|||
target,
|
||||
[currentUser, channel{ctx.channel},
|
||||
target](const HelixUser &targetUser) {
|
||||
getIApp()->getAccounts()->twitch.getCurrent()->blockUser(
|
||||
getApp()->getAccounts()->twitch.getCurrent()->blockUser(
|
||||
targetUser.id, nullptr,
|
||||
[channel, target, targetUser] {
|
||||
channel->addMessage(makeSystemMessage(
|
||||
channel->addSystemMessage(
|
||||
QString("You successfully blocked user %1")
|
||||
.arg(target)));
|
||||
.arg(target));
|
||||
},
|
||||
[channel, target] {
|
||||
channel->addMessage(makeSystemMessage(
|
||||
channel->addSystemMessage(
|
||||
QString("User %1 couldn't be blocked, an unknown "
|
||||
"error occurred!")
|
||||
.arg(target)));
|
||||
.arg(target));
|
||||
});
|
||||
},
|
||||
[channel{ctx.channel}, target] {
|
||||
channel->addMessage(
|
||||
makeSystemMessage(QString("User %1 couldn't be blocked, no "
|
||||
channel->addSystemMessage(QString("User %1 couldn't be blocked, no "
|
||||
"user with that name found!")
|
||||
.arg(target)));
|
||||
.arg(target));
|
||||
});
|
||||
|
||||
return "";
|
||||
|
@ -84,9 +82,9 @@ QString ignoreUser(const CommandContext &ctx)
|
|||
return "";
|
||||
}
|
||||
|
||||
ctx.channel->addMessage(makeSystemMessage(
|
||||
ctx.channel->addSystemMessage(
|
||||
"Ignore command has been renamed to /block, please use it from "
|
||||
"now on as /ignore is going to be removed soon."));
|
||||
"now on as /ignore is going to be removed soon.");
|
||||
|
||||
return blockUser(ctx);
|
||||
}
|
||||
|
@ -100,23 +98,23 @@ QString unblockUser(const CommandContext &ctx)
|
|||
|
||||
if (ctx.twitchChannel == nullptr)
|
||||
{
|
||||
ctx.channel->addMessage(makeSystemMessage(
|
||||
"The /unblock command only works in Twitch channels."));
|
||||
ctx.channel->addSystemMessage(
|
||||
"The /unblock command only works in Twitch channels.");
|
||||
return "";
|
||||
}
|
||||
|
||||
if (ctx.words.size() < 2)
|
||||
{
|
||||
ctx.channel->addMessage(makeSystemMessage("Usage: /unblock <user>"));
|
||||
ctx.channel->addSystemMessage("Usage: /unblock <user>");
|
||||
return "";
|
||||
}
|
||||
|
||||
auto currentUser = getIApp()->getAccounts()->twitch.getCurrent();
|
||||
auto currentUser = getApp()->getAccounts()->twitch.getCurrent();
|
||||
|
||||
if (currentUser->isAnon())
|
||||
{
|
||||
ctx.channel->addMessage(
|
||||
makeSystemMessage("You must be logged in to unblock someone!"));
|
||||
ctx.channel->addSystemMessage(
|
||||
"You must be logged in to unblock someone!");
|
||||
return "";
|
||||
}
|
||||
|
||||
|
@ -126,25 +124,24 @@ QString unblockUser(const CommandContext &ctx)
|
|||
getHelix()->getUserByName(
|
||||
target,
|
||||
[currentUser, channel{ctx.channel}, target](const auto &targetUser) {
|
||||
getIApp()->getAccounts()->twitch.getCurrent()->unblockUser(
|
||||
getApp()->getAccounts()->twitch.getCurrent()->unblockUser(
|
||||
targetUser.id, nullptr,
|
||||
[channel, target, targetUser] {
|
||||
channel->addMessage(makeSystemMessage(
|
||||
channel->addSystemMessage(
|
||||
QString("You successfully unblocked user %1")
|
||||
.arg(target)));
|
||||
.arg(target));
|
||||
},
|
||||
[channel, target] {
|
||||
channel->addMessage(makeSystemMessage(
|
||||
channel->addSystemMessage(
|
||||
QString("User %1 couldn't be unblocked, an unknown "
|
||||
"error occurred!")
|
||||
.arg(target)));
|
||||
.arg(target));
|
||||
});
|
||||
},
|
||||
[channel{ctx.channel}, target] {
|
||||
channel->addMessage(
|
||||
makeSystemMessage(QString("User %1 couldn't be unblocked, "
|
||||
channel->addSystemMessage(QString("User %1 couldn't be unblocked, "
|
||||
"no user with that name found!")
|
||||
.arg(target)));
|
||||
.arg(target));
|
||||
});
|
||||
|
||||
return "";
|
||||
|
@ -157,9 +154,9 @@ QString unignoreUser(const CommandContext &ctx)
|
|||
return "";
|
||||
}
|
||||
|
||||
ctx.channel->addMessage(makeSystemMessage(
|
||||
ctx.channel->addSystemMessage(
|
||||
"Unignore command has been renamed to /unblock, please use it "
|
||||
"from now on as /unignore is going to be removed soon."));
|
||||
"from now on as /unignore is going to be removed soon.");
|
||||
return unblockUser(ctx);
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
#include "Application.hpp"
|
||||
#include "controllers/accounts/AccountController.hpp"
|
||||
#include "controllers/commands/CommandContext.hpp"
|
||||
#include "messages/MessageBuilder.hpp"
|
||||
#include "providers/twitch/api/Helix.hpp"
|
||||
#include "providers/twitch/TwitchAccount.hpp"
|
||||
#include "providers/twitch/TwitchChannel.hpp"
|
||||
|
@ -87,8 +86,8 @@ auto successCallback = [](auto result) {};
|
|||
auto failureCallback = [](ChannelPtr channel, int durationUnitMultiplier = 1) {
|
||||
return [channel, durationUnitMultiplier](const auto &error,
|
||||
const QString &message) {
|
||||
channel->addMessage(makeSystemMessage(
|
||||
formatError(error, message, durationUnitMultiplier)));
|
||||
channel->addSystemMessage(
|
||||
formatError(error, message, durationUnitMultiplier));
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -101,24 +100,24 @@ namespace chatterino::commands {
|
|||
|
||||
QString emoteOnly(const CommandContext &ctx)
|
||||
{
|
||||
auto currentUser = getIApp()->getAccounts()->twitch.getCurrent();
|
||||
auto currentUser = getApp()->getAccounts()->twitch.getCurrent();
|
||||
if (currentUser->isAnon())
|
||||
{
|
||||
ctx.channel->addMessage(makeSystemMessage(P_NOT_LOGGED_IN));
|
||||
ctx.channel->addSystemMessage(P_NOT_LOGGED_IN);
|
||||
return "";
|
||||
}
|
||||
|
||||
if (ctx.twitchChannel == nullptr)
|
||||
{
|
||||
ctx.channel->addMessage(makeSystemMessage(
|
||||
"The /emoteonly command only works in Twitch channels."));
|
||||
ctx.channel->addSystemMessage(
|
||||
"The /emoteonly command only works in Twitch channels.");
|
||||
return "";
|
||||
}
|
||||
|
||||
if (ctx.twitchChannel->accessRoomModes()->emoteOnly)
|
||||
{
|
||||
ctx.channel->addMessage(
|
||||
makeSystemMessage("This room is already in emote-only mode."));
|
||||
ctx.channel->addSystemMessage(
|
||||
"This room is already in emote-only mode.");
|
||||
return "";
|
||||
}
|
||||
|
||||
|
@ -131,23 +130,22 @@ QString emoteOnly(const CommandContext &ctx)
|
|||
|
||||
QString emoteOnlyOff(const CommandContext &ctx)
|
||||
{
|
||||
auto currentUser = getIApp()->getAccounts()->twitch.getCurrent();
|
||||
auto currentUser = getApp()->getAccounts()->twitch.getCurrent();
|
||||
if (currentUser->isAnon())
|
||||
{
|
||||
ctx.channel->addMessage(makeSystemMessage(P_NOT_LOGGED_IN));
|
||||
ctx.channel->addSystemMessage(P_NOT_LOGGED_IN);
|
||||
return "";
|
||||
}
|
||||
if (ctx.twitchChannel == nullptr)
|
||||
{
|
||||
ctx.channel->addMessage(makeSystemMessage(
|
||||
"The /emoteonlyoff command only works in Twitch channels."));
|
||||
ctx.channel->addSystemMessage(
|
||||
"The /emoteonlyoff command only works in Twitch channels.");
|
||||
return "";
|
||||
}
|
||||
|
||||
if (!ctx.twitchChannel->accessRoomModes()->emoteOnly)
|
||||
{
|
||||
ctx.channel->addMessage(
|
||||
makeSystemMessage("This room is not in emote-only mode."));
|
||||
ctx.channel->addSystemMessage("This room is not in emote-only mode.");
|
||||
return "";
|
||||
}
|
||||
|
||||
|
@ -160,24 +158,24 @@ QString emoteOnlyOff(const CommandContext &ctx)
|
|||
|
||||
QString subscribers(const CommandContext &ctx)
|
||||
{
|
||||
auto currentUser = getIApp()->getAccounts()->twitch.getCurrent();
|
||||
auto currentUser = getApp()->getAccounts()->twitch.getCurrent();
|
||||
if (currentUser->isAnon())
|
||||
{
|
||||
ctx.channel->addMessage(makeSystemMessage(P_NOT_LOGGED_IN));
|
||||
ctx.channel->addSystemMessage(P_NOT_LOGGED_IN);
|
||||
return "";
|
||||
}
|
||||
|
||||
if (ctx.twitchChannel == nullptr)
|
||||
{
|
||||
ctx.channel->addMessage(makeSystemMessage(
|
||||
"The /subscribers command only works in Twitch channels."));
|
||||
ctx.channel->addSystemMessage(
|
||||
"The /subscribers command only works in Twitch channels.");
|
||||
return "";
|
||||
}
|
||||
|
||||
if (ctx.twitchChannel->accessRoomModes()->submode)
|
||||
{
|
||||
ctx.channel->addMessage(makeSystemMessage(
|
||||
"This room is already in subscribers-only mode."));
|
||||
ctx.channel->addSystemMessage(
|
||||
"This room is already in subscribers-only mode.");
|
||||
return "";
|
||||
}
|
||||
|
||||
|
@ -190,24 +188,24 @@ QString subscribers(const CommandContext &ctx)
|
|||
|
||||
QString subscribersOff(const CommandContext &ctx)
|
||||
{
|
||||
auto currentUser = getIApp()->getAccounts()->twitch.getCurrent();
|
||||
auto currentUser = getApp()->getAccounts()->twitch.getCurrent();
|
||||
if (currentUser->isAnon())
|
||||
{
|
||||
ctx.channel->addMessage(makeSystemMessage(P_NOT_LOGGED_IN));
|
||||
ctx.channel->addSystemMessage(P_NOT_LOGGED_IN);
|
||||
return "";
|
||||
}
|
||||
|
||||
if (ctx.twitchChannel == nullptr)
|
||||
{
|
||||
ctx.channel->addMessage(makeSystemMessage(
|
||||
"The /subscribersoff command only works in Twitch channels."));
|
||||
ctx.channel->addSystemMessage(
|
||||
"The /subscribersoff command only works in Twitch channels.");
|
||||
return "";
|
||||
}
|
||||
|
||||
if (!ctx.twitchChannel->accessRoomModes()->submode)
|
||||
{
|
||||
ctx.channel->addMessage(
|
||||
makeSystemMessage("This room is not in subscribers-only mode."));
|
||||
ctx.channel->addSystemMessage(
|
||||
"This room is not in subscribers-only mode.");
|
||||
return "";
|
||||
}
|
||||
|
||||
|
@ -220,17 +218,17 @@ QString subscribersOff(const CommandContext &ctx)
|
|||
|
||||
QString slow(const CommandContext &ctx)
|
||||
{
|
||||
auto currentUser = getIApp()->getAccounts()->twitch.getCurrent();
|
||||
auto currentUser = getApp()->getAccounts()->twitch.getCurrent();
|
||||
if (currentUser->isAnon())
|
||||
{
|
||||
ctx.channel->addMessage(makeSystemMessage(P_NOT_LOGGED_IN));
|
||||
ctx.channel->addSystemMessage(P_NOT_LOGGED_IN);
|
||||
return "";
|
||||
}
|
||||
|
||||
if (ctx.twitchChannel == nullptr)
|
||||
{
|
||||
ctx.channel->addMessage(makeSystemMessage(
|
||||
"The /slow command only works in Twitch channels."));
|
||||
ctx.channel->addSystemMessage(
|
||||
"The /slow command only works in Twitch channels.");
|
||||
return "";
|
||||
}
|
||||
|
||||
|
@ -241,20 +239,20 @@ QString slow(const CommandContext &ctx)
|
|||
duration = ctx.words.at(1).toInt(&ok);
|
||||
if (!ok || duration <= 0)
|
||||
{
|
||||
ctx.channel->addMessage(makeSystemMessage(
|
||||
ctx.channel->addSystemMessage(
|
||||
"Usage: \"/slow [duration]\" - Enables slow mode (limit how "
|
||||
"often users may send messages). Duration (optional, "
|
||||
"default=30) must be a positive number of seconds. Use "
|
||||
"\"slowoff\" to disable."));
|
||||
"\"slowoff\" to disable.");
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
if (ctx.twitchChannel->accessRoomModes()->slowMode == duration)
|
||||
{
|
||||
ctx.channel->addMessage(makeSystemMessage(
|
||||
ctx.channel->addSystemMessage(
|
||||
QString("This room is already in %1-second slow mode.")
|
||||
.arg(duration)));
|
||||
.arg(duration));
|
||||
return "";
|
||||
}
|
||||
|
||||
|
@ -267,24 +265,23 @@ QString slow(const CommandContext &ctx)
|
|||
|
||||
QString slowOff(const CommandContext &ctx)
|
||||
{
|
||||
auto currentUser = getIApp()->getAccounts()->twitch.getCurrent();
|
||||
auto currentUser = getApp()->getAccounts()->twitch.getCurrent();
|
||||
if (currentUser->isAnon())
|
||||
{
|
||||
ctx.channel->addMessage(makeSystemMessage(P_NOT_LOGGED_IN));
|
||||
ctx.channel->addSystemMessage(P_NOT_LOGGED_IN);
|
||||
return "";
|
||||
}
|
||||
|
||||
if (ctx.twitchChannel == nullptr)
|
||||
{
|
||||
ctx.channel->addMessage(makeSystemMessage(
|
||||
"The /slowoff command only works in Twitch channels."));
|
||||
ctx.channel->addSystemMessage(
|
||||
"The /slowoff command only works in Twitch channels.");
|
||||
return "";
|
||||
}
|
||||
|
||||
if (ctx.twitchChannel->accessRoomModes()->slowMode <= 0)
|
||||
{
|
||||
ctx.channel->addMessage(
|
||||
makeSystemMessage("This room is not in slow mode."));
|
||||
ctx.channel->addSystemMessage("This room is not in slow mode.");
|
||||
return "";
|
||||
}
|
||||
|
||||
|
@ -297,17 +294,17 @@ QString slowOff(const CommandContext &ctx)
|
|||
|
||||
QString followers(const CommandContext &ctx)
|
||||
{
|
||||
auto currentUser = getIApp()->getAccounts()->twitch.getCurrent();
|
||||
auto currentUser = getApp()->getAccounts()->twitch.getCurrent();
|
||||
if (currentUser->isAnon())
|
||||
{
|
||||
ctx.channel->addMessage(makeSystemMessage(P_NOT_LOGGED_IN));
|
||||
ctx.channel->addSystemMessage(P_NOT_LOGGED_IN);
|
||||
return "";
|
||||
}
|
||||
|
||||
if (ctx.twitchChannel == nullptr)
|
||||
{
|
||||
ctx.channel->addMessage(makeSystemMessage(
|
||||
"The /followers command only works in Twitch channels."));
|
||||
ctx.channel->addSystemMessage(
|
||||
"The /followers command only works in Twitch channels.");
|
||||
return "";
|
||||
}
|
||||
|
||||
|
@ -319,20 +316,20 @@ QString followers(const CommandContext &ctx)
|
|||
// -1 / 60 == 0 => use parsed
|
||||
if (parsed < 0)
|
||||
{
|
||||
ctx.channel->addMessage(makeSystemMessage(
|
||||
ctx.channel->addSystemMessage(
|
||||
"Usage: \"/followers [duration]\" - Enables followers-only "
|
||||
"mode (only users who have followed for 'duration' may chat). "
|
||||
"Examples: \"30m\", \"1 week\", \"5 days 12 hours\". Must be "
|
||||
"less than 3 months."));
|
||||
"less than 3 months.");
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
if (ctx.twitchChannel->accessRoomModes()->followerOnly == duration)
|
||||
{
|
||||
ctx.channel->addMessage(makeSystemMessage(
|
||||
ctx.channel->addSystemMessage(
|
||||
QString("This room is already in %1 followers-only mode.")
|
||||
.arg(formatTime(duration * 60))));
|
||||
.arg(formatTime(duration * 60)));
|
||||
return "";
|
||||
}
|
||||
|
||||
|
@ -345,24 +342,24 @@ QString followers(const CommandContext &ctx)
|
|||
|
||||
QString followersOff(const CommandContext &ctx)
|
||||
{
|
||||
auto currentUser = getIApp()->getAccounts()->twitch.getCurrent();
|
||||
auto currentUser = getApp()->getAccounts()->twitch.getCurrent();
|
||||
if (currentUser->isAnon())
|
||||
{
|
||||
ctx.channel->addMessage(makeSystemMessage(P_NOT_LOGGED_IN));
|
||||
ctx.channel->addSystemMessage(P_NOT_LOGGED_IN);
|
||||
return "";
|
||||
}
|
||||
|
||||
if (ctx.twitchChannel == nullptr)
|
||||
{
|
||||
ctx.channel->addMessage(makeSystemMessage(
|
||||
"The /followersoff command only works in Twitch channels."));
|
||||
ctx.channel->addSystemMessage(
|
||||
"The /followersoff command only works in Twitch channels.");
|
||||
return "";
|
||||
}
|
||||
|
||||
if (ctx.twitchChannel->accessRoomModes()->followerOnly < 0)
|
||||
{
|
||||
ctx.channel->addMessage(
|
||||
makeSystemMessage("This room is not in followers-only mode. "));
|
||||
ctx.channel->addSystemMessage(
|
||||
"This room is not in followers-only mode. ");
|
||||
return "";
|
||||
}
|
||||
|
||||
|
@ -375,24 +372,24 @@ QString followersOff(const CommandContext &ctx)
|
|||
|
||||
QString uniqueChat(const CommandContext &ctx)
|
||||
{
|
||||
auto currentUser = getIApp()->getAccounts()->twitch.getCurrent();
|
||||
auto currentUser = getApp()->getAccounts()->twitch.getCurrent();
|
||||
if (currentUser->isAnon())
|
||||
{
|
||||
ctx.channel->addMessage(makeSystemMessage(P_NOT_LOGGED_IN));
|
||||
ctx.channel->addSystemMessage(P_NOT_LOGGED_IN);
|
||||
return "";
|
||||
}
|
||||
|
||||
if (ctx.twitchChannel == nullptr)
|
||||
{
|
||||
ctx.channel->addMessage(makeSystemMessage(
|
||||
"The /uniquechat command only works in Twitch channels."));
|
||||
ctx.channel->addSystemMessage(
|
||||
"The /uniquechat command only works in Twitch channels.");
|
||||
return "";
|
||||
}
|
||||
|
||||
if (ctx.twitchChannel->accessRoomModes()->r9k)
|
||||
{
|
||||
ctx.channel->addMessage(
|
||||
makeSystemMessage("This room is already in unique-chat mode."));
|
||||
ctx.channel->addSystemMessage(
|
||||
"This room is already in unique-chat mode.");
|
||||
return "";
|
||||
}
|
||||
|
||||
|
@ -405,24 +402,23 @@ QString uniqueChat(const CommandContext &ctx)
|
|||
|
||||
QString uniqueChatOff(const CommandContext &ctx)
|
||||
{
|
||||
auto currentUser = getIApp()->getAccounts()->twitch.getCurrent();
|
||||
auto currentUser = getApp()->getAccounts()->twitch.getCurrent();
|
||||
if (currentUser->isAnon())
|
||||
{
|
||||
ctx.channel->addMessage(makeSystemMessage(P_NOT_LOGGED_IN));
|
||||
ctx.channel->addSystemMessage(P_NOT_LOGGED_IN);
|
||||
return "";
|
||||
}
|
||||
|
||||
if (ctx.twitchChannel == nullptr)
|
||||
{
|
||||
ctx.channel->addMessage(makeSystemMessage(
|
||||
"The /uniquechatoff command only works in Twitch channels."));
|
||||
ctx.channel->addSystemMessage(
|
||||
"The /uniquechatoff command only works in Twitch channels.");
|
||||
return "";
|
||||
}
|
||||
|
||||
if (!ctx.twitchChannel->accessRoomModes()->r9k)
|
||||
{
|
||||
ctx.channel->addMessage(
|
||||
makeSystemMessage("This room is not in unique-chat mode."));
|
||||
ctx.channel->addSystemMessage("This room is not in unique-chat mode.");
|
||||
return "";
|
||||
}
|
||||
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
#include "providers/twitch/api/Helix.hpp"
|
||||
#include "providers/twitch/TwitchAccount.hpp"
|
||||
#include "providers/twitch/TwitchChannel.hpp"
|
||||
#include "providers/twitch/TwitchMessageBuilder.hpp"
|
||||
#include "singletons/Theme.hpp"
|
||||
|
||||
#include <QApplication>
|
||||
|
@ -69,23 +68,22 @@ QString chatters(const CommandContext &ctx)
|
|||
|
||||
if (ctx.twitchChannel == nullptr)
|
||||
{
|
||||
ctx.channel->addMessage(makeSystemMessage(
|
||||
"The /chatters command only works in Twitch Channels."));
|
||||
ctx.channel->addSystemMessage(
|
||||
"The /chatters command only works in Twitch Channels.");
|
||||
return "";
|
||||
}
|
||||
|
||||
// Refresh chatter list via helix api for mods
|
||||
getHelix()->getChatters(
|
||||
ctx.twitchChannel->roomId(),
|
||||
getIApp()->getAccounts()->twitch.getCurrent()->getUserId(), 1,
|
||||
getApp()->getAccounts()->twitch.getCurrent()->getUserId(), 1,
|
||||
[channel{ctx.channel}](auto result) {
|
||||
channel->addMessage(
|
||||
makeSystemMessage(QString("Chatter count: %1.")
|
||||
.arg(localizeNumbers(result.total))));
|
||||
channel->addSystemMessage(QString("Chatter count: %1.")
|
||||
.arg(localizeNumbers(result.total)));
|
||||
},
|
||||
[channel{ctx.channel}](auto error, auto message) {
|
||||
auto errorMessage = formatChattersError(error, message);
|
||||
channel->addMessage(makeSystemMessage(errorMessage));
|
||||
channel->addSystemMessage(errorMessage);
|
||||
});
|
||||
|
||||
return "";
|
||||
|
@ -100,14 +98,14 @@ QString testChatters(const CommandContext &ctx)
|
|||
|
||||
if (ctx.twitchChannel == nullptr)
|
||||
{
|
||||
ctx.channel->addMessage(makeSystemMessage(
|
||||
"The /test-chatters command only works in Twitch Channels."));
|
||||
ctx.channel->addSystemMessage(
|
||||
"The /test-chatters command only works in Twitch Channels.");
|
||||
return "";
|
||||
}
|
||||
|
||||
getHelix()->getChatters(
|
||||
ctx.twitchChannel->roomId(),
|
||||
getIApp()->getAccounts()->twitch.getCurrent()->getUserId(), 5000,
|
||||
getApp()->getAccounts()->twitch.getCurrent()->getUserId(), 5000,
|
||||
[channel{ctx.channel}, twitchChannel{ctx.twitchChannel}](auto result) {
|
||||
QStringList entries;
|
||||
for (const auto &username : result.chatters)
|
||||
|
@ -126,15 +124,13 @@ QString testChatters(const CommandContext &ctx)
|
|||
prefix += QString("(%1):").arg(result.total);
|
||||
}
|
||||
|
||||
MessageBuilder builder;
|
||||
TwitchMessageBuilder::listOfUsersSystemMessage(
|
||||
prefix, entries, twitchChannel, &builder);
|
||||
|
||||
channel->addMessage(builder.release());
|
||||
channel->addMessage(MessageBuilder::makeListOfUsersMessage(
|
||||
prefix, entries, twitchChannel),
|
||||
MessageContext::Original);
|
||||
},
|
||||
[channel{ctx.channel}](auto error, auto message) {
|
||||
auto errorMessage = formatChattersError(error, message);
|
||||
channel->addMessage(makeSystemMessage(errorMessage));
|
||||
channel->addSystemMessage(errorMessage);
|
||||
});
|
||||
|
||||
return "";
|
||||
|
|
|
@ -21,14 +21,14 @@ QString deleteMessages(TwitchChannel *twitchChannel, const QString &messageID)
|
|||
{
|
||||
const auto *commandName = messageID.isEmpty() ? "/clear" : "/delete";
|
||||
|
||||
auto user = getIApp()->getAccounts()->twitch.getCurrent();
|
||||
auto user = getApp()->getAccounts()->twitch.getCurrent();
|
||||
|
||||
// Avoid Helix calls without Client ID and/or OAuth Token
|
||||
if (user->isAnon())
|
||||
{
|
||||
twitchChannel->addMessage(makeSystemMessage(
|
||||
twitchChannel->addSystemMessage(
|
||||
QString("You must be logged in to use the %1 command.")
|
||||
.arg(commandName)));
|
||||
.arg(commandName));
|
||||
return "";
|
||||
}
|
||||
|
||||
|
@ -82,7 +82,7 @@ QString deleteMessages(TwitchChannel *twitchChannel, const QString &messageID)
|
|||
break;
|
||||
}
|
||||
|
||||
twitchChannel->addMessage(makeSystemMessage(errorMessage));
|
||||
twitchChannel->addSystemMessage(errorMessage);
|
||||
});
|
||||
|
||||
return "";
|
||||
|
@ -101,8 +101,8 @@ QString deleteAllMessages(const CommandContext &ctx)
|
|||
|
||||
if (ctx.twitchChannel == nullptr)
|
||||
{
|
||||
ctx.channel->addMessage(makeSystemMessage(
|
||||
"The /clear command only works in Twitch channels."));
|
||||
ctx.channel->addSystemMessage(
|
||||
"The /clear command only works in Twitch channels.");
|
||||
return "";
|
||||
}
|
||||
|
||||
|
@ -120,16 +120,15 @@ QString deleteOneMessage(const CommandContext &ctx)
|
|||
// We use this to ensure the user gets better error messages for missing or malformed arguments
|
||||
if (ctx.twitchChannel == nullptr)
|
||||
{
|
||||
ctx.channel->addMessage(makeSystemMessage(
|
||||
"The /delete command only works in Twitch channels."));
|
||||
ctx.channel->addSystemMessage(
|
||||
"The /delete command only works in Twitch channels.");
|
||||
return "";
|
||||
}
|
||||
|
||||
if (ctx.words.size() < 2)
|
||||
{
|
||||
ctx.channel->addMessage(
|
||||
makeSystemMessage("Usage: /delete <msg-id> - Deletes the "
|
||||
"specified message."));
|
||||
ctx.channel->addSystemMessage("Usage: /delete <msg-id> - Deletes the "
|
||||
"specified message.");
|
||||
return "";
|
||||
}
|
||||
|
||||
|
@ -138,8 +137,8 @@ QString deleteOneMessage(const CommandContext &ctx)
|
|||
if (uuid.isNull())
|
||||
{
|
||||
// The message id must be a valid UUID
|
||||
ctx.channel->addMessage(makeSystemMessage(
|
||||
QString("Invalid msg-id: \"%1\"").arg(messageID)));
|
||||
ctx.channel->addSystemMessage(
|
||||
QString("Invalid msg-id: \"%1\"").arg(messageID));
|
||||
return "";
|
||||
}
|
||||
|
||||
|
@ -149,9 +148,9 @@ QString deleteOneMessage(const CommandContext &ctx)
|
|||
if (msg->loginName == ctx.channel->getName() &&
|
||||
!ctx.channel->isBroadcaster())
|
||||
{
|
||||
ctx.channel->addMessage(makeSystemMessage(
|
||||
ctx.channel->addSystemMessage(
|
||||
"You cannot delete the broadcaster's messages unless "
|
||||
"you are the broadcaster."));
|
||||
"you are the broadcaster.");
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
#include "messages/MessageBuilder.hpp"
|
||||
#include "providers/twitch/api/Helix.hpp"
|
||||
#include "providers/twitch/TwitchChannel.hpp"
|
||||
#include "providers/twitch/TwitchMessageBuilder.hpp"
|
||||
|
||||
namespace {
|
||||
|
||||
|
@ -60,8 +59,8 @@ QString getModerators(const CommandContext &ctx)
|
|||
|
||||
if (ctx.twitchChannel == nullptr)
|
||||
{
|
||||
ctx.channel->addMessage(makeSystemMessage(
|
||||
"The /mods command only works in Twitch Channels."));
|
||||
ctx.channel->addSystemMessage(
|
||||
"The /mods command only works in Twitch Channels.");
|
||||
return "";
|
||||
}
|
||||
|
||||
|
@ -70,22 +69,21 @@ QString getModerators(const CommandContext &ctx)
|
|||
[channel{ctx.channel}, twitchChannel{ctx.twitchChannel}](auto result) {
|
||||
if (result.empty())
|
||||
{
|
||||
channel->addMessage(makeSystemMessage(
|
||||
"This channel does not have any moderators."));
|
||||
channel->addSystemMessage(
|
||||
"This channel does not have any moderators.");
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: sort results?
|
||||
|
||||
MessageBuilder builder;
|
||||
TwitchMessageBuilder::listOfUsersSystemMessage(
|
||||
"The moderators of this channel are", result, twitchChannel,
|
||||
&builder);
|
||||
channel->addMessage(builder.release());
|
||||
channel->addMessage(MessageBuilder::makeListOfUsersMessage(
|
||||
"The moderators of this channel are",
|
||||
result, twitchChannel),
|
||||
MessageContext::Original);
|
||||
},
|
||||
[channel{ctx.channel}](auto error, auto message) {
|
||||
auto errorMessage = formatModsError(error, message);
|
||||
channel->addMessage(makeSystemMessage(errorMessage));
|
||||
channel->addSystemMessage(errorMessage);
|
||||
});
|
||||
|
||||
return "";
|
||||
|
|
|
@ -7,8 +7,6 @@
|
|||
#include "providers/twitch/api/Helix.hpp"
|
||||
#include "providers/twitch/TwitchAccount.hpp"
|
||||
#include "providers/twitch/TwitchChannel.hpp"
|
||||
#include "providers/twitch/TwitchMessageBuilder.hpp"
|
||||
#include "util/Twitch.hpp"
|
||||
|
||||
namespace {
|
||||
|
||||
|
@ -77,19 +75,19 @@ QString getVIPs(const CommandContext &ctx)
|
|||
|
||||
if (ctx.twitchChannel == nullptr)
|
||||
{
|
||||
ctx.channel->addMessage(makeSystemMessage(
|
||||
"The /vips command only works in Twitch channels."));
|
||||
ctx.channel->addSystemMessage(
|
||||
"The /vips command only works in Twitch channels.");
|
||||
return "";
|
||||
}
|
||||
|
||||
auto currentUser = getIApp()->getAccounts()->twitch.getCurrent();
|
||||
auto currentUser = getApp()->getAccounts()->twitch.getCurrent();
|
||||
if (currentUser->isAnon())
|
||||
{
|
||||
ctx.channel->addMessage(makeSystemMessage(
|
||||
ctx.channel->addSystemMessage(
|
||||
"Due to Twitch restrictions, " //
|
||||
"this command can only be used by the broadcaster. "
|
||||
"To see the list of VIPs you must use the "
|
||||
"Twitch website."));
|
||||
"Twitch website.");
|
||||
return "";
|
||||
}
|
||||
|
||||
|
@ -99,23 +97,22 @@ QString getVIPs(const CommandContext &ctx)
|
|||
const std::vector<HelixVip> &vipList) {
|
||||
if (vipList.empty())
|
||||
{
|
||||
channel->addMessage(
|
||||
makeSystemMessage("This channel does not have any VIPs."));
|
||||
channel->addSystemMessage(
|
||||
"This channel does not have any VIPs.");
|
||||
return;
|
||||
}
|
||||
|
||||
auto messagePrefix = QString("The VIPs of this channel are");
|
||||
|
||||
// TODO: sort results?
|
||||
MessageBuilder builder;
|
||||
TwitchMessageBuilder::listOfUsersSystemMessage(
|
||||
messagePrefix, vipList, twitchChannel, &builder);
|
||||
|
||||
channel->addMessage(builder.release());
|
||||
channel->addMessage(MessageBuilder::makeListOfUsersMessage(
|
||||
messagePrefix, vipList, twitchChannel),
|
||||
MessageContext::Original);
|
||||
},
|
||||
[channel{ctx.channel}](auto error, auto message) {
|
||||
auto errorMessage = formatGetVIPsError(error, message);
|
||||
channel->addMessage(makeSystemMessage(errorMessage));
|
||||
channel->addSystemMessage(errorMessage);
|
||||
});
|
||||
|
||||
return "";
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
#include "Application.hpp"
|
||||
#include "controllers/accounts/AccountController.hpp"
|
||||
#include "controllers/commands/CommandContext.hpp"
|
||||
#include "messages/MessageBuilder.hpp"
|
||||
#include "providers/twitch/api/Helix.hpp"
|
||||
#include "providers/twitch/TwitchAccount.hpp"
|
||||
#include "providers/twitch/TwitchChannel.hpp"
|
||||
|
@ -124,24 +123,23 @@ QString startRaid(const CommandContext &ctx)
|
|||
|
||||
if (ctx.twitchChannel == nullptr)
|
||||
{
|
||||
ctx.channel->addMessage(makeSystemMessage(
|
||||
"The /raid command only works in Twitch channels."));
|
||||
ctx.channel->addSystemMessage(
|
||||
"The /raid command only works in Twitch channels.");
|
||||
return "";
|
||||
}
|
||||
|
||||
if (ctx.words.size() < 2)
|
||||
{
|
||||
ctx.channel->addMessage(
|
||||
makeSystemMessage("Usage: \"/raid <username>\" - Raid a user. "
|
||||
"Only the broadcaster can start a raid."));
|
||||
ctx.channel->addSystemMessage(
|
||||
"Usage: \"/raid <username>\" - Raid a user. "
|
||||
"Only the broadcaster can start a raid.");
|
||||
return "";
|
||||
}
|
||||
|
||||
auto currentUser = getIApp()->getAccounts()->twitch.getCurrent();
|
||||
auto currentUser = getApp()->getAccounts()->twitch.getCurrent();
|
||||
if (currentUser->isAnon())
|
||||
{
|
||||
ctx.channel->addMessage(
|
||||
makeSystemMessage("You must be logged in to start a raid!"));
|
||||
ctx.channel->addSystemMessage("You must be logged in to start a raid!");
|
||||
return "";
|
||||
}
|
||||
|
||||
|
@ -154,20 +152,18 @@ QString startRaid(const CommandContext &ctx)
|
|||
channel{ctx.channel}](const HelixUser &targetUser) {
|
||||
getHelix()->startRaid(
|
||||
twitchChannel->roomId(), targetUser.id,
|
||||
[channel, targetUser] {
|
||||
channel->addMessage(
|
||||
makeSystemMessage(QString("You started to raid %1.")
|
||||
.arg(targetUser.displayName)));
|
||||
[] {
|
||||
// do nothing
|
||||
},
|
||||
[channel, targetUser](auto error, auto message) {
|
||||
auto errorMessage = formatStartRaidError(error, message);
|
||||
channel->addMessage(makeSystemMessage(errorMessage));
|
||||
channel->addSystemMessage(errorMessage);
|
||||
});
|
||||
},
|
||||
[channel{ctx.channel}, target] {
|
||||
// Equivalent error from IRC
|
||||
channel->addMessage(
|
||||
makeSystemMessage(QString("Invalid username: %1").arg(target)));
|
||||
channel->addSystemMessage(
|
||||
QString("Invalid username: %1").arg(target));
|
||||
});
|
||||
|
||||
return "";
|
||||
|
@ -182,36 +178,35 @@ QString cancelRaid(const CommandContext &ctx)
|
|||
|
||||
if (ctx.twitchChannel == nullptr)
|
||||
{
|
||||
ctx.channel->addMessage(makeSystemMessage(
|
||||
"The /unraid command only works in Twitch channels."));
|
||||
ctx.channel->addSystemMessage(
|
||||
"The /unraid command only works in Twitch channels.");
|
||||
return "";
|
||||
}
|
||||
|
||||
if (ctx.words.size() != 1)
|
||||
{
|
||||
ctx.channel->addMessage(
|
||||
makeSystemMessage("Usage: \"/unraid\" - Cancel the current raid. "
|
||||
"Only the broadcaster can cancel the raid."));
|
||||
ctx.channel->addSystemMessage(
|
||||
"Usage: \"/unraid\" - Cancel the current raid. "
|
||||
"Only the broadcaster can cancel the raid.");
|
||||
return "";
|
||||
}
|
||||
|
||||
auto currentUser = getIApp()->getAccounts()->twitch.getCurrent();
|
||||
auto currentUser = getApp()->getAccounts()->twitch.getCurrent();
|
||||
if (currentUser->isAnon())
|
||||
{
|
||||
ctx.channel->addMessage(
|
||||
makeSystemMessage("You must be logged in to cancel the raid!"));
|
||||
ctx.channel->addSystemMessage(
|
||||
"You must be logged in to cancel the raid!");
|
||||
return "";
|
||||
}
|
||||
|
||||
getHelix()->cancelRaid(
|
||||
ctx.twitchChannel->roomId(),
|
||||
[channel{ctx.channel}] {
|
||||
channel->addMessage(
|
||||
makeSystemMessage(QString("You cancelled the raid.")));
|
||||
[] {
|
||||
// do nothing
|
||||
},
|
||||
[channel{ctx.channel}](auto error, auto message) {
|
||||
auto errorMessage = formatCancelRaidError(error, message);
|
||||
channel->addMessage(makeSystemMessage(errorMessage));
|
||||
channel->addSystemMessage(errorMessage);
|
||||
});
|
||||
|
||||
return "";
|
||||
|
|
|
@ -1,14 +1,11 @@
|
|||
#include "controllers/commands/builtin/twitch/RemoveModerator.hpp"
|
||||
|
||||
#include "Application.hpp"
|
||||
#include "common/Channel.hpp"
|
||||
#include "controllers/accounts/AccountController.hpp"
|
||||
#include "controllers/commands/CommandContext.hpp"
|
||||
#include "messages/MessageBuilder.hpp"
|
||||
#include "providers/twitch/api/Helix.hpp"
|
||||
#include "providers/twitch/TwitchAccount.hpp"
|
||||
#include "providers/twitch/TwitchChannel.hpp"
|
||||
#include "providers/twitch/TwitchMessageBuilder.hpp"
|
||||
#include "util/Twitch.hpp"
|
||||
|
||||
namespace chatterino::commands {
|
||||
|
@ -22,23 +19,23 @@ QString removeModerator(const CommandContext &ctx)
|
|||
|
||||
if (ctx.twitchChannel == nullptr)
|
||||
{
|
||||
ctx.channel->addMessage(makeSystemMessage(
|
||||
"The /unmod command only works in Twitch channels."));
|
||||
ctx.channel->addSystemMessage(
|
||||
"The /unmod command only works in Twitch channels.");
|
||||
return "";
|
||||
}
|
||||
if (ctx.words.size() < 2)
|
||||
{
|
||||
ctx.channel->addMessage(makeSystemMessage(
|
||||
ctx.channel->addSystemMessage(
|
||||
"Usage: \"/unmod <username>\" - Revoke moderator status from a "
|
||||
"user. Use \"/mods\" to list the moderators of this channel."));
|
||||
"user. Use \"/mods\" to list the moderators of this channel.");
|
||||
return "";
|
||||
}
|
||||
|
||||
auto currentUser = getIApp()->getAccounts()->twitch.getCurrent();
|
||||
auto currentUser = getApp()->getAccounts()->twitch.getCurrent();
|
||||
if (currentUser->isAnon())
|
||||
{
|
||||
ctx.channel->addMessage(
|
||||
makeSystemMessage("You must be logged in to unmod someone!"));
|
||||
ctx.channel->addSystemMessage(
|
||||
"You must be logged in to unmod someone!");
|
||||
return "";
|
||||
}
|
||||
|
||||
|
@ -52,10 +49,10 @@ QString removeModerator(const CommandContext &ctx)
|
|||
getHelix()->removeChannelModerator(
|
||||
twitchChannel->roomId(), targetUser.id,
|
||||
[channel, targetUser] {
|
||||
channel->addMessage(makeSystemMessage(
|
||||
channel->addSystemMessage(
|
||||
QString("You have removed %1 as a moderator of "
|
||||
"this channel.")
|
||||
.arg(targetUser.displayName)));
|
||||
.arg(targetUser.displayName));
|
||||
},
|
||||
[channel, targetUser](auto error, auto message) {
|
||||
QString errorMessage =
|
||||
|
@ -107,13 +104,13 @@ QString removeModerator(const CommandContext &ctx)
|
|||
}
|
||||
break;
|
||||
}
|
||||
channel->addMessage(makeSystemMessage(errorMessage));
|
||||
channel->addSystemMessage(errorMessage);
|
||||
});
|
||||
},
|
||||
[channel{ctx.channel}, target] {
|
||||
// Equivalent error from IRC
|
||||
channel->addMessage(
|
||||
makeSystemMessage(QString("Invalid username: %1").arg(target)));
|
||||
channel->addSystemMessage(
|
||||
QString("Invalid username: %1").arg(target));
|
||||
});
|
||||
|
||||
return "";
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
#include "providers/twitch/api/Helix.hpp"
|
||||
#include "providers/twitch/TwitchAccount.hpp"
|
||||
#include "providers/twitch/TwitchChannel.hpp"
|
||||
#include "providers/twitch/TwitchMessageBuilder.hpp"
|
||||
#include "util/Twitch.hpp"
|
||||
|
||||
namespace chatterino::commands {
|
||||
|
@ -22,23 +21,23 @@ QString removeVIP(const CommandContext &ctx)
|
|||
|
||||
if (ctx.twitchChannel == nullptr)
|
||||
{
|
||||
ctx.channel->addMessage(makeSystemMessage(
|
||||
"The /unvip command only works in Twitch channels."));
|
||||
ctx.channel->addSystemMessage(
|
||||
"The /unvip command only works in Twitch channels.");
|
||||
return "";
|
||||
}
|
||||
if (ctx.words.size() < 2)
|
||||
{
|
||||
ctx.channel->addMessage(makeSystemMessage(
|
||||
ctx.channel->addSystemMessage(
|
||||
"Usage: \"/unvip <username>\" - Revoke VIP status from a user. "
|
||||
"Use \"/vips\" to list the VIPs of this channel."));
|
||||
"Use \"/vips\" to list the VIPs of this channel.");
|
||||
return "";
|
||||
}
|
||||
|
||||
auto currentUser = getIApp()->getAccounts()->twitch.getCurrent();
|
||||
auto currentUser = getApp()->getAccounts()->twitch.getCurrent();
|
||||
if (currentUser->isAnon())
|
||||
{
|
||||
ctx.channel->addMessage(
|
||||
makeSystemMessage("You must be logged in to UnVIP someone!"));
|
||||
ctx.channel->addSystemMessage(
|
||||
"You must be logged in to UnVIP someone!");
|
||||
return "";
|
||||
}
|
||||
|
||||
|
@ -52,9 +51,9 @@ QString removeVIP(const CommandContext &ctx)
|
|||
getHelix()->removeChannelVIP(
|
||||
twitchChannel->roomId(), targetUser.id,
|
||||
[channel, targetUser] {
|
||||
channel->addMessage(makeSystemMessage(
|
||||
channel->addSystemMessage(
|
||||
QString("You have removed %1 as a VIP of this channel.")
|
||||
.arg(targetUser.displayName)));
|
||||
.arg(targetUser.displayName));
|
||||
},
|
||||
[channel, targetUser](auto error, auto message) {
|
||||
QString errorMessage = QString("Failed to remove VIP - ");
|
||||
|
@ -97,13 +96,13 @@ QString removeVIP(const CommandContext &ctx)
|
|||
}
|
||||
break;
|
||||
}
|
||||
channel->addMessage(makeSystemMessage(errorMessage));
|
||||
channel->addSystemMessage(errorMessage);
|
||||
});
|
||||
},
|
||||
[channel{ctx.channel}, target] {
|
||||
// Equivalent error from IRC
|
||||
channel->addMessage(
|
||||
makeSystemMessage(QString("Invalid username: %1").arg(target)));
|
||||
channel->addSystemMessage(
|
||||
QString("Invalid username: %1").arg(target));
|
||||
});
|
||||
|
||||
return "";
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
#include "controllers/commands/builtin/twitch/SendReply.hpp"
|
||||
|
||||
#include "common/Channel.hpp"
|
||||
#include "controllers/commands/CommandContext.hpp"
|
||||
#include "messages/Message.hpp"
|
||||
#include "messages/MessageBuilder.hpp"
|
||||
#include "messages/MessageThread.hpp"
|
||||
#include "providers/twitch/TwitchChannel.hpp"
|
||||
#include "util/Twitch.hpp"
|
||||
|
@ -19,15 +17,14 @@ QString sendReply(const CommandContext &ctx)
|
|||
|
||||
if (ctx.twitchChannel == nullptr)
|
||||
{
|
||||
ctx.channel->addMessage(makeSystemMessage(
|
||||
"The /reply command only works in Twitch channels."));
|
||||
ctx.channel->addSystemMessage(
|
||||
"The /reply command only works in Twitch channels.");
|
||||
return "";
|
||||
}
|
||||
|
||||
if (ctx.words.size() < 3)
|
||||
{
|
||||
ctx.channel->addMessage(
|
||||
makeSystemMessage("Usage: /reply <username> <message>"));
|
||||
ctx.channel->addSystemMessage("Usage: /reply <username> <message>");
|
||||
return "";
|
||||
}
|
||||
|
||||
|
@ -54,8 +51,7 @@ QString sendReply(const CommandContext &ctx)
|
|||
}
|
||||
}
|
||||
|
||||
ctx.channel->addMessage(
|
||||
makeSystemMessage("A message from that user wasn't found."));
|
||||
ctx.channel->addSystemMessage("A message from that user wasn't found.");
|
||||
|
||||
return "";
|
||||
}
|
||||
|
|
|
@ -9,8 +9,6 @@
|
|||
#include "messages/MessageElement.hpp"
|
||||
#include "providers/bttv/BttvEmotes.hpp"
|
||||
#include "providers/ffz/FfzEmotes.hpp"
|
||||
#include "providers/irc/IrcChannel2.hpp"
|
||||
#include "providers/irc/IrcServer.hpp"
|
||||
#include "providers/twitch/api/Helix.hpp"
|
||||
#include "providers/twitch/TwitchAccount.hpp"
|
||||
#include "providers/twitch/TwitchIrcServer.hpp"
|
||||
|
@ -92,7 +90,7 @@ QString formatWhisperError(HelixWhisperError error, const QString &message)
|
|||
|
||||
bool appendWhisperMessageWordsLocally(const QStringList &words)
|
||||
{
|
||||
auto *app = getIApp();
|
||||
auto *app = getApp();
|
||||
|
||||
MessageBuilder b;
|
||||
|
||||
|
@ -102,7 +100,7 @@ bool appendWhisperMessageWordsLocally(const QStringList &words)
|
|||
MessageElementFlag::Text, MessageColor::Text,
|
||||
FontStyle::ChatMediumBold);
|
||||
b.emplace<TextElement>("->", MessageElementFlag::Text,
|
||||
getIApp()->getThemes()->messages.textColors.system);
|
||||
getApp()->getThemes()->messages.textColors.system);
|
||||
b.emplace<TextElement>(words[1] + ":", MessageElementFlag::Text,
|
||||
MessageColor::Text, FontStyle::ChatMediumBold);
|
||||
|
||||
|
@ -115,8 +113,8 @@ bool appendWhisperMessageWordsLocally(const QStringList &words)
|
|||
for (int i = 2; i < words.length(); i++)
|
||||
{
|
||||
{ // Twitch emote
|
||||
auto it = accemotes.emotes.find({words[i]});
|
||||
if (it != accemotes.emotes.end())
|
||||
auto it = accemotes->find({words[i]});
|
||||
if (it != accemotes->end())
|
||||
{
|
||||
b.emplace<EmoteElement>(it->second,
|
||||
MessageElementFlag::TwitchEmote);
|
||||
|
@ -152,10 +150,10 @@ bool appendWhisperMessageWordsLocally(const QStringList &words)
|
|||
void operator()(const QString &string,
|
||||
MessageBuilder &b) const
|
||||
{
|
||||
LinkParser parser(string);
|
||||
if (parser.result())
|
||||
auto link = linkparser::parse(string);
|
||||
if (link)
|
||||
{
|
||||
b.addLink(*parser.result());
|
||||
b.addLink(*link, string);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -177,18 +175,15 @@ bool appendWhisperMessageWordsLocally(const QStringList &words)
|
|||
b->flags.set(MessageFlag::Whisper);
|
||||
auto messagexD = b.release();
|
||||
|
||||
getIApp()->getTwitch()->getWhispersChannel()->addMessage(messagexD);
|
||||
|
||||
auto overrideFlags = std::optional<MessageFlags>(messagexD->flags);
|
||||
overrideFlags->set(MessageFlag::DoNotLog);
|
||||
getApp()->getTwitch()->getWhispersChannel()->addMessage(
|
||||
messagexD, MessageContext::Original);
|
||||
|
||||
if (getSettings()->inlineWhispers &&
|
||||
!(getSettings()->streamerModeSuppressInlineWhispers &&
|
||||
getIApp()->getStreamerMode()->isEnabled()))
|
||||
getApp()->getStreamerMode()->isEnabled()))
|
||||
{
|
||||
app->getTwitchAbstract()->forEachChannel(
|
||||
[&messagexD, overrideFlags](ChannelPtr _channel) {
|
||||
_channel->addMessage(messagexD, overrideFlags);
|
||||
app->getTwitch()->forEachChannel([&messagexD](ChannelPtr _channel) {
|
||||
_channel->addMessage(messagexD, MessageContext::Repost);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -208,16 +203,15 @@ QString sendWhisper(const CommandContext &ctx)
|
|||
|
||||
if (ctx.words.size() < 3)
|
||||
{
|
||||
ctx.channel->addMessage(
|
||||
makeSystemMessage("Usage: /w <username> <message>"));
|
||||
ctx.channel->addSystemMessage("Usage: /w <username> <message>");
|
||||
return "";
|
||||
}
|
||||
|
||||
auto currentUser = getIApp()->getAccounts()->twitch.getCurrent();
|
||||
auto currentUser = getApp()->getAccounts()->twitch.getCurrent();
|
||||
if (currentUser->isAnon())
|
||||
{
|
||||
ctx.channel->addMessage(
|
||||
makeSystemMessage("You must be logged in to send a whisper!"));
|
||||
ctx.channel->addSystemMessage(
|
||||
"You must be logged in to send a whisper!");
|
||||
return "";
|
||||
}
|
||||
auto target = ctx.words.at(1);
|
||||
|
@ -236,27 +230,15 @@ QString sendWhisper(const CommandContext &ctx)
|
|||
},
|
||||
[channel, target, targetUser](auto error, auto message) {
|
||||
auto errorMessage = formatWhisperError(error, message);
|
||||
channel->addMessage(makeSystemMessage(errorMessage));
|
||||
channel->addSystemMessage(errorMessage);
|
||||
});
|
||||
},
|
||||
[channel{ctx.channel}] {
|
||||
channel->addMessage(
|
||||
makeSystemMessage("No user matching that username."));
|
||||
channel->addSystemMessage("No user matching that username.");
|
||||
});
|
||||
return "";
|
||||
}
|
||||
|
||||
// we must be on IRC
|
||||
auto *ircChannel = dynamic_cast<IrcChannel *>(ctx.channel.get());
|
||||
if (ircChannel == nullptr)
|
||||
{
|
||||
// give up
|
||||
return "";
|
||||
}
|
||||
|
||||
auto *server = ircChannel->server();
|
||||
server->sendWhisper(target, message);
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
#include "Application.hpp"
|
||||
#include "controllers/accounts/AccountController.hpp"
|
||||
#include "controllers/commands/CommandContext.hpp"
|
||||
#include "messages/MessageBuilder.hpp"
|
||||
#include "providers/twitch/api/Helix.hpp"
|
||||
#include "providers/twitch/TwitchAccount.hpp"
|
||||
#include "providers/twitch/TwitchChannel.hpp"
|
||||
|
@ -17,20 +16,20 @@ QString toggleShieldMode(const CommandContext &ctx, bool isActivating)
|
|||
|
||||
if (ctx.twitchChannel == nullptr)
|
||||
{
|
||||
ctx.channel->addMessage(makeSystemMessage(
|
||||
ctx.channel->addSystemMessage(
|
||||
QStringLiteral("The %1 command only works in Twitch channels.")
|
||||
.arg(command)));
|
||||
.arg(command));
|
||||
return {};
|
||||
}
|
||||
|
||||
auto user = getIApp()->getAccounts()->twitch.getCurrent();
|
||||
auto user = getApp()->getAccounts()->twitch.getCurrent();
|
||||
|
||||
// Avoid Helix calls without Client ID and/or OAuth Token
|
||||
if (user->isAnon())
|
||||
{
|
||||
ctx.channel->addMessage(makeSystemMessage(
|
||||
ctx.channel->addSystemMessage(
|
||||
QStringLiteral("You must be logged in to use the %1 command.")
|
||||
.arg(command)));
|
||||
.arg(command));
|
||||
return {};
|
||||
}
|
||||
|
||||
|
@ -39,13 +38,11 @@ QString toggleShieldMode(const CommandContext &ctx, bool isActivating)
|
|||
[channel = ctx.channel](const auto &res) {
|
||||
if (!res.isActive)
|
||||
{
|
||||
channel->addMessage(
|
||||
makeSystemMessage("Shield mode was deactivated."));
|
||||
channel->addSystemMessage("Shield mode was deactivated.");
|
||||
return;
|
||||
}
|
||||
|
||||
channel->addMessage(
|
||||
makeSystemMessage("Shield mode was activated."));
|
||||
channel->addSystemMessage("Shield mode was activated.");
|
||||
},
|
||||
[channel = ctx.channel](const auto error, const auto &message) {
|
||||
using Error = HelixUpdateShieldModeError;
|
||||
|
@ -78,7 +75,7 @@ QString toggleShieldMode(const CommandContext &ctx, bool isActivating)
|
|||
}
|
||||
break;
|
||||
}
|
||||
channel->addMessage(makeSystemMessage(errorMessage));
|
||||
channel->addSystemMessage(errorMessage);
|
||||
});
|
||||
|
||||
return {};
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
#include "Application.hpp"
|
||||
#include "controllers/accounts/AccountController.hpp"
|
||||
#include "controllers/commands/CommandContext.hpp"
|
||||
#include "messages/MessageBuilder.hpp"
|
||||
#include "providers/twitch/api/Helix.hpp"
|
||||
#include "providers/twitch/TwitchAccount.hpp"
|
||||
#include "providers/twitch/TwitchChannel.hpp"
|
||||
|
@ -19,24 +18,22 @@ QString sendShoutout(const CommandContext &ctx)
|
|||
|
||||
if (twitchChannel == nullptr)
|
||||
{
|
||||
channel->addMessage(makeSystemMessage(
|
||||
"The /shoutout command only works in Twitch channels."));
|
||||
channel->addSystemMessage(
|
||||
"The /shoutout command only works in Twitch channels.");
|
||||
return "";
|
||||
}
|
||||
|
||||
auto currentUser = getIApp()->getAccounts()->twitch.getCurrent();
|
||||
auto currentUser = getApp()->getAccounts()->twitch.getCurrent();
|
||||
if (currentUser->isAnon())
|
||||
{
|
||||
channel->addMessage(
|
||||
makeSystemMessage("You must be logged in to send shoutout."));
|
||||
channel->addSystemMessage("You must be logged in to send shoutout.");
|
||||
return "";
|
||||
}
|
||||
|
||||
if (words->size() < 2)
|
||||
{
|
||||
channel->addMessage(
|
||||
makeSystemMessage("Usage: \"/shoutout <username>\" - Sends a "
|
||||
"shoutout to the specified twitch user"));
|
||||
channel->addSystemMessage("Usage: \"/shoutout <username>\" - Sends a "
|
||||
"shoutout to the specified twitch user");
|
||||
return "";
|
||||
}
|
||||
|
||||
|
@ -52,8 +49,8 @@ QString sendShoutout(const CommandContext &ctx)
|
|||
twitchChannel->roomId(), targetUser.id,
|
||||
currentUser->getUserId(),
|
||||
[channel, targetUser]() {
|
||||
channel->addMessage(makeSystemMessage(
|
||||
QString("Sent shoutout to %1").arg(targetUser.login)));
|
||||
channel->addSystemMessage(
|
||||
QString("Sent shoutout to %1").arg(targetUser.login));
|
||||
},
|
||||
[channel](auto error, auto message) {
|
||||
QString errorMessage = "Failed to send shoutout - ";
|
||||
|
@ -99,13 +96,13 @@ QString sendShoutout(const CommandContext &ctx)
|
|||
break;
|
||||
}
|
||||
|
||||
channel->addMessage(makeSystemMessage(errorMessage));
|
||||
channel->addSystemMessage(errorMessage);
|
||||
});
|
||||
},
|
||||
[channel, target] {
|
||||
// Equivalent error from IRC
|
||||
channel->addMessage(
|
||||
makeSystemMessage(QString("Invalid username: %1").arg(target)));
|
||||
channel->addSystemMessage(
|
||||
QString("Invalid username: %1").arg(target));
|
||||
});
|
||||
|
||||
return "";
|
||||
|
|
|
@ -1,14 +1,11 @@
|
|||
#include "controllers/commands/builtin/twitch/StartCommercial.hpp"
|
||||
|
||||
#include "Application.hpp"
|
||||
#include "common/Channel.hpp"
|
||||
#include "controllers/accounts/AccountController.hpp"
|
||||
#include "controllers/commands/CommandContext.hpp"
|
||||
#include "messages/MessageBuilder.hpp"
|
||||
#include "providers/twitch/api/Helix.hpp"
|
||||
#include "providers/twitch/TwitchAccount.hpp"
|
||||
#include "providers/twitch/TwitchChannel.hpp"
|
||||
#include "providers/twitch/TwitchMessageBuilder.hpp"
|
||||
|
||||
namespace {
|
||||
|
||||
|
@ -83,8 +80,8 @@ QString startCommercial(const CommandContext &ctx)
|
|||
|
||||
if (ctx.twitchChannel == nullptr)
|
||||
{
|
||||
ctx.channel->addMessage(makeSystemMessage(
|
||||
"The /commercial command only works in Twitch channels."));
|
||||
ctx.channel->addSystemMessage(
|
||||
"The /commercial command only works in Twitch channels.");
|
||||
return "";
|
||||
}
|
||||
|
||||
|
@ -96,17 +93,17 @@ QString startCommercial(const CommandContext &ctx)
|
|||
|
||||
if (ctx.words.size() < 2)
|
||||
{
|
||||
ctx.channel->addMessage(makeSystemMessage(usageStr));
|
||||
ctx.channel->addSystemMessage(usageStr);
|
||||
return "";
|
||||
}
|
||||
|
||||
auto user = getIApp()->getAccounts()->twitch.getCurrent();
|
||||
auto user = getApp()->getAccounts()->twitch.getCurrent();
|
||||
|
||||
// Avoid Helix calls without Client ID and/or OAuth Token
|
||||
if (user->isAnon())
|
||||
{
|
||||
ctx.channel->addMessage(makeSystemMessage(
|
||||
"You must be logged in to use the /commercial command."));
|
||||
ctx.channel->addSystemMessage(
|
||||
"You must be logged in to use the /commercial command.");
|
||||
return "";
|
||||
}
|
||||
|
||||
|
@ -116,18 +113,18 @@ QString startCommercial(const CommandContext &ctx)
|
|||
getHelix()->startCommercial(
|
||||
broadcasterID, length,
|
||||
[channel{ctx.channel}](auto response) {
|
||||
channel->addMessage(makeSystemMessage(
|
||||
channel->addSystemMessage(
|
||||
QString("Starting %1 second long commercial break. "
|
||||
"Keep in mind you are still "
|
||||
"live and not all viewers will receive a "
|
||||
"commercial. "
|
||||
"You may run another commercial in %2 seconds.")
|
||||
.arg(response.length)
|
||||
.arg(response.retryAfter)));
|
||||
.arg(response.retryAfter));
|
||||
},
|
||||
[channel{ctx.channel}](auto error, auto message) {
|
||||
auto errorMessage = formatStartCommercialError(error, message);
|
||||
channel->addMessage(makeSystemMessage(errorMessage));
|
||||
channel->addSystemMessage(errorMessage);
|
||||
});
|
||||
|
||||
return "";
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
#include "controllers/accounts/AccountController.hpp"
|
||||
#include "controllers/commands/CommandContext.hpp"
|
||||
#include "controllers/commands/common/ChannelAction.hpp"
|
||||
#include "messages/MessageBuilder.hpp"
|
||||
#include "providers/twitch/api/Helix.hpp"
|
||||
#include "providers/twitch/TwitchAccount.hpp"
|
||||
|
||||
|
@ -76,7 +75,7 @@ void unbanUserByID(const ChannelPtr &channel, const QString &channelID,
|
|||
break;
|
||||
}
|
||||
|
||||
channel->addMessage(makeSystemMessage(errorMessage));
|
||||
channel->addSystemMessage(errorMessage);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -96,7 +95,7 @@ QString unbanUser(const CommandContext &ctx)
|
|||
{
|
||||
if (ctx.channel != nullptr)
|
||||
{
|
||||
ctx.channel->addMessage(makeSystemMessage(actions.error()));
|
||||
ctx.channel->addSystemMessage(actions.error());
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -109,11 +108,11 @@ QString unbanUser(const CommandContext &ctx)
|
|||
|
||||
assert(!actions.value().empty());
|
||||
|
||||
auto currentUser = getIApp()->getAccounts()->twitch.getCurrent();
|
||||
auto currentUser = getApp()->getAccounts()->twitch.getCurrent();
|
||||
if (currentUser->isAnon())
|
||||
{
|
||||
ctx.channel->addMessage(
|
||||
makeSystemMessage("You must be logged in to unban someone!"));
|
||||
ctx.channel->addSystemMessage(
|
||||
"You must be logged in to unban someone!");
|
||||
return "";
|
||||
}
|
||||
|
||||
|
@ -159,16 +158,16 @@ QString unbanUser(const CommandContext &ctx)
|
|||
userLoginsToFetch](const auto &users) mutable {
|
||||
if (!actionChannel.hydrateFrom(users))
|
||||
{
|
||||
channel->addMessage(makeSystemMessage(
|
||||
channel->addSystemMessage(
|
||||
QString("Failed to timeout, bad channel name: %1")
|
||||
.arg(actionChannel.login)));
|
||||
.arg(actionChannel.login));
|
||||
return;
|
||||
}
|
||||
if (!actionTarget.hydrateFrom(users))
|
||||
{
|
||||
channel->addMessage(makeSystemMessage(
|
||||
channel->addSystemMessage(
|
||||
QString("Failed to timeout, bad target name: %1")
|
||||
.arg(actionTarget.login)));
|
||||
.arg(actionTarget.login));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -177,9 +176,9 @@ QString unbanUser(const CommandContext &ctx)
|
|||
actionTarget.displayName);
|
||||
},
|
||||
[channel{ctx.channel}, userLoginsToFetch] {
|
||||
channel->addMessage(makeSystemMessage(
|
||||
channel->addSystemMessage(
|
||||
QString("Failed to timeout, bad username(s): %1")
|
||||
.arg(userLoginsToFetch.join(", "))));
|
||||
.arg(userLoginsToFetch.join(", ")));
|
||||
});
|
||||
}
|
||||
else
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
#include "controllers/commands/builtin/twitch/UpdateChannel.hpp"
|
||||
|
||||
#include "common/Channel.hpp"
|
||||
#include "common/network/NetworkResult.hpp"
|
||||
#include "controllers/commands/CommandContext.hpp"
|
||||
#include "messages/MessageBuilder.hpp"
|
||||
#include "providers/twitch/api/Helix.hpp"
|
||||
#include "providers/twitch/TwitchChannel.hpp"
|
||||
|
||||
|
@ -70,15 +68,14 @@ QString setTitle(const CommandContext &ctx)
|
|||
|
||||
if (ctx.words.size() < 2)
|
||||
{
|
||||
ctx.channel->addMessage(
|
||||
makeSystemMessage("Usage: /settitle <stream title>"));
|
||||
ctx.channel->addSystemMessage("Usage: /settitle <stream title>");
|
||||
return "";
|
||||
}
|
||||
|
||||
if (ctx.twitchChannel == nullptr)
|
||||
{
|
||||
ctx.channel->addMessage(
|
||||
makeSystemMessage("Unable to set title of non-Twitch channel."));
|
||||
ctx.channel->addSystemMessage(
|
||||
"Unable to set title of non-Twitch channel.");
|
||||
return "";
|
||||
}
|
||||
|
||||
|
@ -89,13 +86,13 @@ QString setTitle(const CommandContext &ctx)
|
|||
[channel{ctx.channel}, title](const auto &result) {
|
||||
(void)result;
|
||||
|
||||
channel->addMessage(
|
||||
makeSystemMessage(QString("Updated title to %1").arg(title)));
|
||||
channel->addSystemMessage(
|
||||
QString("Updated title to %1").arg(title));
|
||||
},
|
||||
[channel{ctx.channel}](auto error, auto message) {
|
||||
auto errorMessage =
|
||||
formatUpdateChannelError("title", error, message);
|
||||
channel->addMessage(makeSystemMessage(errorMessage));
|
||||
channel->addSystemMessage(errorMessage);
|
||||
});
|
||||
|
||||
return "";
|
||||
|
@ -110,15 +107,14 @@ QString setGame(const CommandContext &ctx)
|
|||
|
||||
if (ctx.words.size() < 2)
|
||||
{
|
||||
ctx.channel->addMessage(
|
||||
makeSystemMessage("Usage: /setgame <stream game>"));
|
||||
ctx.channel->addSystemMessage("Usage: /setgame <stream game>");
|
||||
return "";
|
||||
}
|
||||
|
||||
if (ctx.twitchChannel == nullptr)
|
||||
{
|
||||
ctx.channel->addMessage(
|
||||
makeSystemMessage("Unable to set game of non-Twitch channel."));
|
||||
ctx.channel->addSystemMessage(
|
||||
"Unable to set game of non-Twitch channel.");
|
||||
return "";
|
||||
}
|
||||
|
||||
|
@ -130,7 +126,7 @@ QString setGame(const CommandContext &ctx)
|
|||
gameName](const std::vector<HelixGame> &games) {
|
||||
if (games.empty())
|
||||
{
|
||||
channel->addMessage(makeSystemMessage("Game not found."));
|
||||
channel->addSystemMessage("Game not found.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -154,17 +150,17 @@ QString setGame(const CommandContext &ctx)
|
|||
getHelix()->updateChannel(
|
||||
twitchChannel->roomId(), matchedGame.id, "", "",
|
||||
[channel, games, matchedGame](const NetworkResult &) {
|
||||
channel->addMessage(makeSystemMessage(
|
||||
QString("Updated game to %1").arg(matchedGame.name)));
|
||||
channel->addSystemMessage(
|
||||
QString("Updated game to %1").arg(matchedGame.name));
|
||||
},
|
||||
[channel](auto error, auto message) {
|
||||
auto errorMessage =
|
||||
formatUpdateChannelError("game", error, message);
|
||||
channel->addMessage(makeSystemMessage(errorMessage));
|
||||
channel->addSystemMessage(errorMessage);
|
||||
});
|
||||
},
|
||||
[channel{ctx.channel}] {
|
||||
channel->addMessage(makeSystemMessage("Failed to look up game."));
|
||||
channel->addSystemMessage("Failed to look up game.");
|
||||
});
|
||||
|
||||
return "";
|
||||
|
|
|
@ -21,17 +21,17 @@ QString updateUserColor(const CommandContext &ctx)
|
|||
|
||||
if (!ctx.channel->isTwitchChannel())
|
||||
{
|
||||
ctx.channel->addMessage(makeSystemMessage(
|
||||
"The /color command only works in Twitch channels."));
|
||||
ctx.channel->addSystemMessage(
|
||||
"The /color command only works in Twitch channels.");
|
||||
return "";
|
||||
}
|
||||
auto user = getIApp()->getAccounts()->twitch.getCurrent();
|
||||
auto user = getApp()->getAccounts()->twitch.getCurrent();
|
||||
|
||||
// Avoid Helix calls without Client ID and/or OAuth Token
|
||||
if (user->isAnon())
|
||||
{
|
||||
ctx.channel->addMessage(makeSystemMessage(
|
||||
"You must be logged in to use the /color command."));
|
||||
ctx.channel->addSystemMessage(
|
||||
"You must be logged in to use the /color command.");
|
||||
return "";
|
||||
}
|
||||
|
||||
|
@ -39,11 +39,11 @@ QString updateUserColor(const CommandContext &ctx)
|
|||
|
||||
if (colorString.isEmpty())
|
||||
{
|
||||
ctx.channel->addMessage(makeSystemMessage(
|
||||
ctx.channel->addSystemMessage(
|
||||
QString("Usage: /color <color> - Color must be one of Twitch's "
|
||||
"supported colors (%1) or a hex code (#000000) if you "
|
||||
"have Turbo or Prime.")
|
||||
.arg(VALID_HELIX_COLORS.join(", "))));
|
||||
.arg(VALID_HELIX_COLORS.join(", ")));
|
||||
return "";
|
||||
}
|
||||
|
||||
|
@ -54,7 +54,7 @@ QString updateUserColor(const CommandContext &ctx)
|
|||
[colorString, channel{ctx.channel}] {
|
||||
QString successMessage =
|
||||
QString("Your color has been changed to %1.").arg(colorString);
|
||||
channel->addMessage(makeSystemMessage(successMessage));
|
||||
channel->addSystemMessage(successMessage);
|
||||
},
|
||||
[colorString, channel{ctx.channel}](auto error, auto message) {
|
||||
QString errorMessage =
|
||||
|
@ -90,7 +90,7 @@ QString updateUserColor(const CommandContext &ctx)
|
|||
break;
|
||||
}
|
||||
|
||||
channel->addMessage(makeSystemMessage(errorMessage));
|
||||
channel->addSystemMessage(errorMessage);
|
||||
});
|
||||
|
||||
return "";
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
#include "controllers/commands/builtin/twitch/Warn.hpp"
|
||||
|
||||
#include "Application.hpp"
|
||||
#include "common/Channel.hpp"
|
||||
#include "common/QLogging.hpp"
|
||||
#include "controllers/accounts/AccountController.hpp"
|
||||
#include "controllers/commands/CommandContext.hpp"
|
||||
#include "controllers/commands/common/ChannelAction.hpp"
|
||||
#include "messages/MessageBuilder.hpp"
|
||||
#include "providers/twitch/api/Helix.hpp"
|
||||
#include "providers/twitch/TwitchAccount.hpp"
|
||||
#include "providers/twitch/TwitchChannel.hpp"
|
||||
|
||||
namespace {
|
||||
|
||||
|
@ -73,7 +72,7 @@ void warnUserByID(const ChannelPtr &channel, const QString &channelID,
|
|||
break;
|
||||
}
|
||||
|
||||
channel->addMessage(makeSystemMessage(errorMessage));
|
||||
channel->addSystemMessage(errorMessage);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -92,7 +91,7 @@ QString sendWarn(const CommandContext &ctx)
|
|||
{
|
||||
if (ctx.channel != nullptr)
|
||||
{
|
||||
ctx.channel->addMessage(makeSystemMessage(actions.error()));
|
||||
ctx.channel->addSystemMessage(actions.error());
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -105,11 +104,10 @@ QString sendWarn(const CommandContext &ctx)
|
|||
|
||||
assert(!actions.value().empty());
|
||||
|
||||
auto currentUser = getIApp()->getAccounts()->twitch.getCurrent();
|
||||
auto currentUser = getApp()->getAccounts()->twitch.getCurrent();
|
||||
if (currentUser->isAnon())
|
||||
{
|
||||
ctx.channel->addMessage(
|
||||
makeSystemMessage("You must be logged in to warn someone!"));
|
||||
ctx.channel->addSystemMessage("You must be logged in to warn someone!");
|
||||
return "";
|
||||
}
|
||||
|
||||
|
@ -118,8 +116,8 @@ QString sendWarn(const CommandContext &ctx)
|
|||
const auto &reason = action.reason;
|
||||
if (reason.isEmpty())
|
||||
{
|
||||
ctx.channel->addMessage(
|
||||
makeSystemMessage("Failed to warn, you must specify a reason"));
|
||||
ctx.channel->addSystemMessage(
|
||||
"Failed to warn, you must specify a reason");
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -161,16 +159,16 @@ QString sendWarn(const CommandContext &ctx)
|
|||
userLoginsToFetch](const auto &users) mutable {
|
||||
if (!actionChannel.hydrateFrom(users))
|
||||
{
|
||||
channel->addMessage(makeSystemMessage(
|
||||
channel->addSystemMessage(
|
||||
QString("Failed to warn, bad channel name: %1")
|
||||
.arg(actionChannel.login)));
|
||||
.arg(actionChannel.login));
|
||||
return;
|
||||
}
|
||||
if (!actionTarget.hydrateFrom(users))
|
||||
{
|
||||
channel->addMessage(makeSystemMessage(
|
||||
channel->addSystemMessage(
|
||||
QString("Failed to warn, bad target name: %1")
|
||||
.arg(actionTarget.login)));
|
||||
.arg(actionTarget.login));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -179,9 +177,9 @@ QString sendWarn(const CommandContext &ctx)
|
|||
reason, actionTarget.displayName);
|
||||
},
|
||||
[channel{ctx.channel}, userLoginsToFetch] {
|
||||
channel->addMessage(makeSystemMessage(
|
||||
channel->addSystemMessage(
|
||||
QString("Failed to warn, bad username(s): %1")
|
||||
.arg(userLoginsToFetch.join(", "))));
|
||||
.arg(userLoginsToFetch.join(", ")));
|
||||
});
|
||||
}
|
||||
else
|
||||
|
|
|
@ -39,7 +39,7 @@ void TabCompletionModel::updateResults(const QString &query,
|
|||
// Try plugins first
|
||||
bool done{};
|
||||
std::tie(done, results) =
|
||||
getIApp()->getPlugins()->updateCustomCompletions(
|
||||
getApp()->getPlugins()->updateCustomCompletions(
|
||||
query, fullTextContent, cursorPosition, isFirstWord);
|
||||
if (done)
|
||||
{
|
||||
|
|
|
@ -71,20 +71,20 @@ void CommandSource::initializeItems()
|
|||
std::vector<CommandItem> commands;
|
||||
|
||||
#ifdef CHATTERINO_HAVE_PLUGINS
|
||||
for (const auto &command : getIApp()->getCommands()->pluginCommands())
|
||||
for (const auto &command : getApp()->getCommands()->pluginCommands())
|
||||
{
|
||||
addCommand(command, commands);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Custom Chatterino commands
|
||||
for (const auto &command : getIApp()->getCommands()->items)
|
||||
for (const auto &command : getApp()->getCommands()->items)
|
||||
{
|
||||
addCommand(command.name, commands);
|
||||
}
|
||||
|
||||
// Default Chatterino commands
|
||||
auto x = getIApp()->getCommands()->getDefaultChatterinoCommandList();
|
||||
auto x = getApp()->getCommands()->getDefaultChatterinoCommandList();
|
||||
for (const auto &command : x)
|
||||
{
|
||||
addCommand(command, commands);
|
||||
|
|
|
@ -88,33 +88,23 @@ void EmoteSource::addToStringList(QStringList &list, size_t maxCount,
|
|||
|
||||
void EmoteSource::initializeFromChannel(const Channel *channel)
|
||||
{
|
||||
auto *app = getIApp();
|
||||
auto *app = getApp();
|
||||
|
||||
std::vector<EmoteItem> emotes;
|
||||
const auto *tc = dynamic_cast<const TwitchChannel *>(channel);
|
||||
// returns true also for special Twitch channels (/live, /mentions, /whispers, etc.)
|
||||
if (channel->isTwitchChannel())
|
||||
{
|
||||
if (auto user = app->getAccounts()->twitch.getCurrent())
|
||||
{
|
||||
// Twitch Emotes available globally
|
||||
auto emoteData = user->accessEmotes();
|
||||
addEmotes(emotes, emoteData->emotes, "Twitch Emote");
|
||||
|
||||
// Twitch Emotes available locally
|
||||
auto localEmoteData = user->accessLocalEmotes();
|
||||
if ((tc != nullptr) &&
|
||||
localEmoteData->find(tc->roomId()) != localEmoteData->end())
|
||||
{
|
||||
if (const auto *localEmotes = &localEmoteData->at(tc->roomId()))
|
||||
{
|
||||
addEmotes(emotes, *localEmotes, "Local Twitch Emotes");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (tc)
|
||||
{
|
||||
if (auto twitch = tc->localTwitchEmotes())
|
||||
{
|
||||
addEmotes(emotes, *twitch, "Local Twitch Emotes");
|
||||
}
|
||||
|
||||
auto user = getApp()->getAccounts()->twitch.getCurrent();
|
||||
addEmotes(emotes, **user->accessEmotes(), "Twitch Emote");
|
||||
|
||||
// TODO extract "Channel {BetterTTV,7TV,FrankerFaceZ}" text into a #define.
|
||||
if (auto bttv = tc->bttvEmotes())
|
||||
{
|
||||
|
|
|
@ -12,7 +12,7 @@ namespace chatterino::filters {
|
|||
|
||||
ContextMap buildContextMap(const MessagePtr &m, chatterino::Channel *channel)
|
||||
{
|
||||
auto watchingChannel = getIApp()->getTwitch()->getWatchingChannel().get();
|
||||
auto watchingChannel = getApp()->getTwitch()->getWatchingChannel().get();
|
||||
|
||||
/*
|
||||
* Looking to add a new identifier to filters? Here's what to do:
|
||||
|
|
|
@ -72,22 +72,35 @@ QString possibleTypeToString(const PossibleType &possible);
|
|||
|
||||
bool isList(const PossibleType &possibleType);
|
||||
|
||||
inline bool variantIs(const QVariant &a, QMetaType::Type type)
|
||||
inline bool variantIs(const QVariant &a, int type)
|
||||
{
|
||||
return static_cast<QMetaType::Type>(a.type()) == type;
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
return a.typeId() == type;
|
||||
#else
|
||||
return a.type() == type;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline bool variantIsNot(const QVariant &a, QMetaType::Type type)
|
||||
inline bool variantIsNot(const QVariant &a, int type)
|
||||
{
|
||||
return static_cast<QMetaType::Type>(a.type()) != type;
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
return a.typeId() != type;
|
||||
#else
|
||||
return a.type() != type;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline bool convertVariantTypes(QVariant &a, QVariant &b, int type)
|
||||
{
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
QMetaType ty(type);
|
||||
return a.convert(ty) && b.convert(ty);
|
||||
#else
|
||||
return a.convert(type) && b.convert(type);
|
||||
#endif
|
||||
}
|
||||
|
||||
inline bool variantTypesMatch(QVariant &a, QVariant &b, QMetaType::Type type)
|
||||
inline bool variantTypesMatch(QVariant &a, QVariant &b, int type)
|
||||
{
|
||||
return variantIs(a, type) && variantIs(b, type);
|
||||
}
|
||||
|
|
|
@ -56,9 +56,8 @@ QVariant BinaryOperation::execute(const ContextMap &context) const
|
|||
switch (this->op_)
|
||||
{
|
||||
case PLUS:
|
||||
if (static_cast<QMetaType::Type>(left.type()) ==
|
||||
QMetaType::QString &&
|
||||
right.canConvert(QMetaType::QString))
|
||||
if (variantIs(left, QMetaType::QString) &&
|
||||
right.canConvert<QString>())
|
||||
{
|
||||
return left.toString().append(right.toString());
|
||||
}
|
||||
|
@ -143,14 +142,14 @@ QVariant BinaryOperation::execute(const ContextMap &context) const
|
|||
return false;
|
||||
case CONTAINS:
|
||||
if (variantIs(left, QMetaType::QStringList) &&
|
||||
right.canConvert(QMetaType::QString))
|
||||
right.canConvert<QString>())
|
||||
{
|
||||
return left.toStringList().contains(right.toString(),
|
||||
Qt::CaseInsensitive);
|
||||
}
|
||||
|
||||
if (variantIs(left, QMetaType::QVariantMap) &&
|
||||
right.canConvert(QMetaType::QString))
|
||||
right.canConvert<QString>())
|
||||
{
|
||||
return left.toMap().contains(right.toString());
|
||||
}
|
||||
|
@ -160,8 +159,7 @@ QVariant BinaryOperation::execute(const ContextMap &context) const
|
|||
return left.toList().contains(right);
|
||||
}
|
||||
|
||||
if (left.canConvert(QMetaType::QString) &&
|
||||
right.canConvert(QMetaType::QString))
|
||||
if (left.canConvert<QString>() && right.canConvert<QString>())
|
||||
{
|
||||
return left.toString().contains(right.toString(),
|
||||
Qt::CaseInsensitive);
|
||||
|
@ -170,7 +168,7 @@ QVariant BinaryOperation::execute(const ContextMap &context) const
|
|||
return false;
|
||||
case STARTS_WITH:
|
||||
if (variantIs(left, QMetaType::QStringList) &&
|
||||
right.canConvert(QMetaType::QString))
|
||||
right.canConvert<QString>())
|
||||
{
|
||||
auto list = left.toStringList();
|
||||
return !list.isEmpty() &&
|
||||
|
@ -183,8 +181,7 @@ QVariant BinaryOperation::execute(const ContextMap &context) const
|
|||
return left.toList().startsWith(right);
|
||||
}
|
||||
|
||||
if (left.canConvert(QMetaType::QString) &&
|
||||
right.canConvert(QMetaType::QString))
|
||||
if (left.canConvert<QString>() && right.canConvert<QString>())
|
||||
{
|
||||
return left.toString().startsWith(right.toString(),
|
||||
Qt::CaseInsensitive);
|
||||
|
@ -194,7 +191,7 @@ QVariant BinaryOperation::execute(const ContextMap &context) const
|
|||
|
||||
case ENDS_WITH:
|
||||
if (variantIs(left, QMetaType::QStringList) &&
|
||||
right.canConvert(QMetaType::QString))
|
||||
right.canConvert<QString>())
|
||||
{
|
||||
auto list = left.toStringList();
|
||||
return !list.isEmpty() &&
|
||||
|
@ -207,8 +204,7 @@ QVariant BinaryOperation::execute(const ContextMap &context) const
|
|||
return left.toList().endsWith(right);
|
||||
}
|
||||
|
||||
if (left.canConvert(QMetaType::QString) &&
|
||||
right.canConvert(QMetaType::QString))
|
||||
if (left.canConvert<QString>() && right.canConvert<QString>())
|
||||
{
|
||||
return left.toString().endsWith(right.toString(),
|
||||
Qt::CaseInsensitive);
|
||||
|
@ -216,14 +212,18 @@ QVariant BinaryOperation::execute(const ContextMap &context) const
|
|||
|
||||
return false;
|
||||
case MATCH: {
|
||||
if (!left.canConvert(QMetaType::QString))
|
||||
if (!left.canConvert<QString>())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
auto matching = left.toString();
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
switch (static_cast<QMetaType::Type>(right.typeId()))
|
||||
#else
|
||||
switch (static_cast<QMetaType::Type>(right.type()))
|
||||
#endif
|
||||
{
|
||||
case QMetaType::QRegularExpression: {
|
||||
return right.toRegularExpression()
|
||||
|
|
|
@ -53,7 +53,7 @@ void BadgeHighlightModel::getRowFromItem(const HighlightBadge &item,
|
|||
setFilePathItem(row[Column::SoundPath], item.getSoundUrl());
|
||||
setColorItem(row[Column::Color], *item.getColor());
|
||||
|
||||
getIApp()->getTwitchBadges()->getBadgeIcon(
|
||||
getApp()->getTwitchBadges()->getBadgeIcon(
|
||||
item.badgeName(), [item, row](QString /*name*/, const QIconPtr pixmap) {
|
||||
row[Column::Badge]->setData(QVariant(*pixmap), Qt::DecorationRole);
|
||||
});
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
#include "HighlightBadge.hpp"
|
||||
#include "controllers/highlights/HighlightBadge.hpp"
|
||||
|
||||
#include "messages/SharedMessageBuilder.hpp"
|
||||
#include "providers/twitch/TwitchBadge.hpp"
|
||||
#include "singletons/Resources.hpp"
|
||||
#include "util/IrcHelpers.hpp"
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
|
@ -97,7 +96,7 @@ bool HighlightBadge::compare(const QString &id, const Badge &badge) const
|
|||
{
|
||||
if (this->hasVersions_)
|
||||
{
|
||||
auto parts = SharedMessageBuilder::slashKeyValue(id);
|
||||
auto parts = slashKeyValue(id);
|
||||
return parts.first.compare(badge.key_, Qt::CaseInsensitive) == 0 &&
|
||||
parts.second.compare(badge.value_, Qt::CaseInsensitive) == 0;
|
||||
}
|
||||
|
|
|
@ -183,7 +183,7 @@ void rebuildReplyThreadHighlight(Settings &settings,
|
|||
void rebuildMessageHighlights(Settings &settings,
|
||||
std::vector<HighlightCheck> &checks)
|
||||
{
|
||||
auto currentUser = getIApp()->getAccounts()->twitch.getCurrent();
|
||||
auto currentUser = getApp()->getAccounts()->twitch.getCurrent();
|
||||
QString currentUsername = currentUser->getUserName();
|
||||
|
||||
if (settings.enableSelfHighlight && !currentUsername.isEmpty() &&
|
||||
|
@ -442,9 +442,11 @@ std::ostream &operator<<(std::ostream &os, const HighlightResult &result)
|
|||
return os;
|
||||
}
|
||||
|
||||
void HighlightController::initialize(Settings &settings,
|
||||
const Paths & /*paths*/)
|
||||
HighlightController::HighlightController(Settings &settings,
|
||||
AccountController *accounts)
|
||||
{
|
||||
assert(accounts != nullptr);
|
||||
|
||||
this->rebuildListener_.addSetting(settings.enableSelfHighlight);
|
||||
this->rebuildListener_.addSetting(settings.enableSelfHighlightSound);
|
||||
this->rebuildListener_.addSetting(settings.enableSelfHighlightTaskbar);
|
||||
|
@ -507,12 +509,12 @@ void HighlightController::initialize(Settings &settings,
|
|||
this->rebuildChecks(settings);
|
||||
});
|
||||
|
||||
getIApp()->getAccounts()->twitch.currentUserChanged.connect(
|
||||
[this, &settings] {
|
||||
this->bConnections.emplace_back(
|
||||
accounts->twitch.currentUserChanged.connect([this, &settings] {
|
||||
qCDebug(chatterinoHighlights)
|
||||
<< "Rebuild checks because user swapped accounts";
|
||||
this->rebuildChecks(settings);
|
||||
});
|
||||
}));
|
||||
|
||||
this->rebuildChecks(settings);
|
||||
}
|
||||
|
@ -550,7 +552,7 @@ std::pair<bool, HighlightResult> HighlightController::check(
|
|||
// Access for checking
|
||||
const auto checks = this->checks_.accessConst();
|
||||
|
||||
auto currentUser = getIApp()->getAccounts()->twitch.getCurrent();
|
||||
auto currentUser = getApp()->getAccounts()->twitch.getCurrent();
|
||||
auto self = (senderName == currentUser->getUserName());
|
||||
|
||||
for (const auto &check : *checks)
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
#pragma once
|
||||
|
||||
#include "common/FlagsEnum.hpp"
|
||||
#include "common/Singleton.hpp"
|
||||
#include "common/UniqueAccess.hpp"
|
||||
#include "messages/MessageFlag.hpp"
|
||||
#include "singletons/Settings.hpp"
|
||||
|
||||
#include <boost/signals2/connection.hpp>
|
||||
#include <pajlada/settings.hpp>
|
||||
#include <pajlada/settings/settinglistener.hpp>
|
||||
#include <QColor>
|
||||
|
@ -18,8 +19,7 @@ namespace chatterino {
|
|||
|
||||
class Badge;
|
||||
struct MessageParseArgs;
|
||||
enum class MessageFlag : int64_t;
|
||||
using MessageFlags = FlagsEnum<MessageFlag>;
|
||||
class AccountController;
|
||||
|
||||
struct HighlightResult {
|
||||
HighlightResult(bool _alert, bool _playSound,
|
||||
|
@ -83,10 +83,10 @@ struct HighlightCheck {
|
|||
Checker cb;
|
||||
};
|
||||
|
||||
class HighlightController final : public Singleton
|
||||
class HighlightController final
|
||||
{
|
||||
public:
|
||||
void initialize(Settings &settings, const Paths &paths) override;
|
||||
HighlightController(Settings &settings, AccountController *accounts);
|
||||
|
||||
/**
|
||||
* @brief Checks the given message parameters if it matches our internal checks, and returns a result
|
||||
|
@ -108,6 +108,7 @@ private:
|
|||
|
||||
pajlada::SettingListener rebuildListener_;
|
||||
pajlada::Signals::SignalHolder signalHolder_;
|
||||
std::vector<boost::signals2::scoped_connection> bConnections;
|
||||
};
|
||||
|
||||
} // namespace chatterino
|
||||
|
|
|
@ -519,7 +519,7 @@ void HighlightModel::customRowSetData(const std::vector<QStandardItem *> &row,
|
|||
break;
|
||||
}
|
||||
|
||||
getIApp()->getWindows()->forceLayoutChannelViews();
|
||||
getApp()->getWindows()->forceLayoutChannelViews();
|
||||
}
|
||||
|
||||
} // namespace chatterino
|
||||
|
|
|
@ -109,7 +109,7 @@ void UserHighlightModel::customRowSetData(
|
|||
break;
|
||||
}
|
||||
|
||||
getIApp()->getWindows()->forceLayoutChannelViews();
|
||||
getApp()->getWindows()->forceLayoutChannelViews();
|
||||
}
|
||||
|
||||
// row into vector item
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue