├── lib ├── twitch-eventsub-ws │ ├── ast │ │ ├── lib │ │ │ ├── tests │ │ │ │ ├── __init__.py │ │ │ │ └── resources │ │ │ │ │ ├── simple.hpp │ │ │ │ │ ├── optional.hpp │ │ │ │ │ ├── vector.hpp │ │ │ │ │ ├── vector-pod.hpp │ │ │ │ │ ├── string.hpp │ │ │ │ │ ├── const.hpp │ │ │ │ │ └── test.hpp │ │ │ ├── templates │ │ │ │ ├── error-failed-to-deserialize.tmpl │ │ │ │ ├── error-missing-field.tmpl │ │ │ │ ├── initializer-variant.tmpl │ │ │ │ ├── initializer-vector.tmpl │ │ │ │ ├── field-optional-vector.tmpl │ │ │ │ ├── initializer-optional-vector.tmpl │ │ │ │ ├── initializer-optional.tmpl │ │ │ │ ├── initializer-basic.tmpl │ │ │ │ ├── enum-definition.tmpl │ │ │ │ ├── struct-definition.tmpl │ │ │ │ ├── field-basic.tmpl │ │ │ │ └── field-vector.tmpl │ │ │ ├── membertype.py │ │ │ ├── __init__.py │ │ │ ├── format.py │ │ │ └── jinja_env.py │ │ ├── .gitignore │ │ ├── requirements-dev.txt │ │ ├── requirements.txt │ │ ├── check-clang.hpp │ │ ├── get-builtin-include-dirs.py │ │ └── check-clang.py │ ├── .gitignore │ ├── src │ │ ├── json.cpp │ │ └── payloads │ │ │ ├── channel-ban-v1.cpp │ │ │ └── channel-moderate-v2.cpp │ ├── .prettierignore │ ├── benchmarks │ │ ├── src │ │ │ └── main.cpp │ │ ├── resources │ │ │ └── bench.qrc │ │ └── CMakeLists.txt │ ├── pyproject.toml │ ├── include │ │ └── twitch-eventsub-ws │ │ │ ├── messages │ │ │ └── metadata.inc │ │ │ ├── payloads │ │ │ ├── session-welcome.inc │ │ │ ├── channel-ban-v1.inc │ │ │ ├── channel-update-v1.inc │ │ │ ├── stream-offline-v1.inc │ │ │ ├── stream-online-v1.inc │ │ │ ├── automod-message-hold-v2.inc │ │ │ ├── automod-message-update-v2.inc │ │ │ ├── channel-chat-user-message-hold-v1.inc │ │ │ ├── channel-suspicious-user-update-v1.inc │ │ │ ├── channel-suspicious-user-message-v1.inc │ │ │ ├── subscription.inc │ │ │ ├── channel-chat-user-message-update-v1.inc │ │ │ ├── suspicious-users.inc │ │ │ └── automod-message.inc │ │ │ ├── detail │ │ │ ├── errors.hpp │ │ │ └── variant.hpp │ │ │ ├── chrono.hpp │ │ │ └── logger.hpp │ ├── TODO.md │ ├── .isort.cfg │ ├── README.md │ └── tests │ │ ├── resources │ │ └── messages │ │ │ └── session-welcome.json │ │ └── CMakeLists.txt ├── README.md └── semver │ └── README.md ├── resources ├── .gitignore ├── icon.ico ├── icon.png ├── split │ ├── up.png │ ├── down.png │ ├── left.png │ ├── move.png │ └── right.png ├── buttons │ ├── ban.png │ ├── mod.png │ ├── vip.png │ ├── unban.png │ ├── unmod.png │ ├── unvip.png │ ├── update.png │ ├── copyDark.png │ ├── trashCan.png │ ├── clearSearch.png │ ├── copyLight.png │ ├── replyDark.png │ ├── updateError.png │ ├── replyThreadDark.png │ ├── streamerModeEnabledDark.png │ ├── streamerModeEnabledLight.png │ ├── moderationDisabledDarkMode18x18.png │ ├── cancelDark.svg │ ├── cancel.svg │ ├── reloadDark.svg │ ├── reloadLight.svg │ ├── emoteDark.svg │ ├── emote.svg │ └── edit.svg ├── chatterino.icns ├── avatars │ ├── anon.png │ ├── fourtf.png │ ├── fraxx.png │ ├── jaxkey.png │ ├── miguvt.png │ ├── mm2pl.png │ ├── nealxm.png │ ├── slch.png │ ├── supa.jpg │ ├── techno.png │ ├── ynot01.png │ ├── zneix.png │ ├── crazysmc.png │ ├── cyclone.png │ ├── iprodigy.png │ ├── jakeryw.png │ ├── kararty.png │ ├── occluder.png │ ├── pajlada.png │ ├── revolter.jpg │ ├── xheaveny.png │ ├── _1xelerate.png │ ├── alazymeme.png │ ├── brian6932.png │ ├── devpoland.png │ ├── droidicus.png │ ├── hicupalot.png │ ├── karlpolice.png │ ├── maliByatzes.png │ ├── matthewde.jpg │ ├── mohad12211.png │ ├── niller2005.png │ ├── obertura777.png │ ├── otaviobia.png │ ├── vincent0955.png │ ├── wissididom.png │ ├── zakary2841.png │ ├── knobby-tires.jpg │ ├── zonianmidian.png │ └── explooosion_code.png ├── sounds │ └── ping2.wav ├── streamerMode.png ├── twitch │ ├── automod.png │ └── sharedChat.png ├── examples │ ├── moving.gif │ └── splitting.gif ├── scrolling │ ├── upScroll.png │ ├── downScroll.png │ └── neutralScroll.png ├── settings │ ├── aboutlogo.png │ ├── keybinds.svg │ ├── accounts.svg │ ├── notifications.svg │ ├── ignore.svg │ ├── commands.svg │ ├── plugins.svg │ └── filters.svg ├── raw │ ├── README.md │ └── buttons │ │ ├── replyDark.svg │ │ └── replyThreadDark.svg ├── com.chatterino.chatterino.desktop └── switcher │ ├── plus.svg │ └── switch.svg ├── .dockerignore ├── .github ├── FUNDING.yml ├── pull_request_template.md ├── ISSUE_TEMPLATE │ ├── feature-suggestion.md │ ├── something-completely-different.md │ └── config.yml └── workflows │ ├── post-clang-tidy-review.yml │ ├── winget.yml │ └── changelog-check.yml ├── .sanitizers ├── ubsan-suppressions ├── asan-suppressions ├── lsan-suppressions └── tsan-suppressions ├── cmake ├── resources │ ├── qt.conf.in │ ├── resources_autogenerated.qrc.in │ ├── ResourcesAutogen.cpp.in │ └── ResourcesAutogen.hpp.in ├── FindLRUCache.cmake ├── FindMagicEnum.cmake ├── FindBoostCertify.cmake ├── FindWebsocketpp.cmake └── FindSol2.cmake ├── .gitattributes ├── src ├── util │ ├── MacOsHelpers.h │ ├── AttachToConsole.hpp │ ├── SelfCheck.hpp │ ├── CustomPlayer.hpp │ ├── Clipboard.hpp │ ├── FuzzyConvert.hpp │ ├── MacOsHelpers.mm │ ├── CombinePath.hpp │ ├── QStringHash.hpp │ ├── SharedPtrElementLess.hpp │ ├── DisplayBadge.hpp │ ├── DistanceBetweenPoints.hpp │ ├── IncognitoBrowser.hpp │ ├── DisplayBadge.cpp │ ├── FunctionEventFilter.cpp │ ├── LoadPixmap.hpp │ ├── AttachToConsole.cpp │ ├── RemoveScrollAreaBackground.hpp │ ├── Clipboard.cpp │ ├── WindowsHelper.hpp │ ├── FunctionEventFilter.hpp │ ├── WeakPtrHelpers.hpp │ ├── XDGDirectory.hpp │ ├── Expected.hpp │ ├── UnixSignalHandler.hpp │ ├── FormatTime.hpp │ ├── SampleData.hpp │ ├── XDGHelper.hpp │ ├── RenameThread.hpp │ ├── FilesystemHelpers.hpp │ ├── ImageUploader.hpp │ ├── AbandonObject.hpp │ ├── CustomPlayer.cpp │ └── LayoutHelper.cpp ├── BrowserExtension.hpp ├── widgets │ ├── dialogs │ │ ├── switcher │ │ │ ├── QuickSwitcherModel.cpp │ │ │ ├── AbstractSwitcherItem.hpp │ │ │ ├── QuickSwitcherModel.hpp │ │ │ └── SwitchSplitItem.hpp │ │ ├── WelcomeDialog.hpp │ │ ├── LastRunCrashDialog.hpp │ │ ├── WelcomeDialog.cpp │ │ ├── SelectChannelFiltersDialog.hpp │ │ ├── font │ │ │ ├── PreviewWidget.hpp │ │ │ ├── FontDialog.hpp │ │ │ └── FontFamilyWidget.hpp │ │ ├── ColorPickerDialog.hpp │ │ ├── EditUserNotesDialog.hpp │ │ └── UpdateDialog.hpp │ ├── splits │ │ ├── SplitCommon.hpp │ │ ├── DraggedSplit.cpp │ │ └── DraggedSplit.hpp │ ├── helper │ │ ├── DebugPopup.hpp │ │ ├── InvisibleSizeGrip.hpp │ │ ├── color │ │ │ ├── Checkerboard.hpp │ │ │ └── ColorItemDelegate.hpp │ │ ├── InvisibleSizeGrip.cpp │ │ ├── TrimRegExpValidator.hpp │ │ ├── IconDelegate.hpp │ │ ├── TrimRegExpValidator.cpp │ │ ├── RegExpItemDelegate.hpp │ │ ├── LiveIndicator.hpp │ │ └── CommonTexts.hpp │ ├── settingspages │ │ ├── AccountsPage.hpp │ │ ├── NotificationPage.hpp │ │ ├── NicknamesPage.hpp │ │ ├── IgnoresPage.hpp │ │ ├── KeyboardSettingsPage.hpp │ │ ├── CommandPage.hpp │ │ ├── AboutPage.hpp │ │ ├── ExternalToolsPage.hpp │ │ ├── FiltersPage.hpp │ │ ├── ModerationPage.hpp │ │ ├── PluginsPage.hpp │ │ ├── DankerinoPage.hpp │ │ ├── ForserinoPage.hpp │ │ └── GeneralPage.hpp │ ├── NotebookEnums.hpp │ ├── ChatterListWidget.hpp │ ├── AccountSwitchWidget.hpp │ ├── listview │ │ ├── GenericListItem.cpp │ │ └── GenericItemDelegate.hpp │ ├── buttons │ │ ├── InitUpdateButton.hpp │ │ └── DimButton.cpp │ ├── AccountSwitchPopup.hpp │ └── FramelessEmbedWindow.hpp ├── controllers │ ├── filters │ │ └── lang │ │ │ └── expressions │ │ │ ├── Expression.cpp │ │ │ ├── ListExpression.hpp │ │ │ ├── UnaryOperation.hpp │ │ │ ├── ValueExpression.hpp │ │ │ └── BinaryOperation.hpp │ ├── plugins │ │ ├── api │ │ │ ├── EventType.hpp │ │ │ ├── JSON.hpp │ │ │ ├── JSONParse.hpp │ │ │ └── JSONStringify.hpp │ │ └── PluginPermission.hpp │ ├── commands │ │ ├── builtin │ │ │ └── twitch │ │ │ │ ├── SendReply.hpp │ │ │ │ ├── SendWhisper.hpp │ │ │ │ ├── AddVIP.hpp │ │ │ │ ├── Shoutout.hpp │ │ │ │ ├── UpdateColor.hpp │ │ │ │ ├── Warn.hpp │ │ │ │ ├── GetVIPs.hpp │ │ │ │ ├── Unban.hpp │ │ │ │ ├── AddModerator.hpp │ │ │ │ ├── RemoveVIP.hpp │ │ │ │ ├── GetModerators.hpp │ │ │ │ ├── RemoveModerator.hpp │ │ │ │ ├── StartCommercial.hpp │ │ │ │ ├── UpdateChannel.hpp │ │ │ │ ├── Chatters.hpp │ │ │ │ ├── ShieldMode.hpp │ │ │ │ ├── Raid.hpp │ │ │ │ ├── DeleteMessages.hpp │ │ │ │ ├── Ban.hpp │ │ │ │ ├── Poll.hpp │ │ │ │ ├── Prediction.hpp │ │ │ │ ├── Block.hpp │ │ │ │ ├── Announce.hpp │ │ │ │ └── ChatSettings.hpp │ │ └── CommandContext.hpp │ ├── hotkeys │ │ ├── HotkeyHelpers.hpp │ │ └── HotkeyCategory.hpp │ ├── sound │ │ └── NullBackend.cpp │ ├── completion │ │ └── strategies │ │ │ ├── ClassicUserStrategy.hpp │ │ │ ├── CommandStrategy.hpp │ │ │ ├── ClassicUserStrategy.cpp │ │ │ ├── SmartEmoteStrategy.hpp │ │ │ └── ClassicEmoteStrategy.hpp │ ├── accounts │ │ ├── Account.hpp │ │ └── AccountController.hpp │ ├── emotes │ │ └── EmoteController.hpp │ ├── logging │ │ └── ChannelLog.cpp │ ├── pings │ │ └── MutedChannelModel.hpp │ ├── ignores │ │ └── IgnoreModel.hpp │ ├── nicknames │ │ └── NicknamesModel.hpp │ └── highlights │ │ └── HighlightCheck.hpp ├── common │ ├── ProviderId.hpp │ ├── TimeoutStackStyle.hpp │ ├── ChatterinoSetting.cpp │ ├── Literals.hpp │ ├── enums │ │ ├── MessageContext.hpp │ │ └── MessageOverflow.hpp │ ├── network │ │ └── NetworkManager.hpp │ ├── LastMessageLineStyle.hpp │ ├── Modes.hpp │ ├── Env.hpp │ ├── Credentials.hpp │ ├── StreamerModeSetting.hpp │ └── ThumbnailPreviewMode.hpp ├── providers │ ├── ffz │ │ ├── FfzUtil.hpp │ │ └── FfzUtil.cpp │ ├── twitch │ │ ├── TwitchHelpers.hpp │ │ ├── PubSubClientOptions.hpp │ │ ├── PubSubMessages.hpp │ │ ├── EmoteValue.hpp │ │ ├── TwitchHelpers.cpp │ │ ├── pubsubmessages │ │ │ ├── Unlisten.hpp │ │ │ ├── ChannelPoints.cpp │ │ │ └── Listen.hpp │ │ ├── TwitchCommon.hpp │ │ ├── eventsub │ │ │ ├── SubscriptionHandle.hpp │ │ │ └── SubscriptionHandle.cpp │ │ ├── TwitchUser.cpp │ │ └── TwitchBadge.hpp │ ├── liveupdates │ │ └── Diag.hpp │ ├── emoji │ │ └── EmojiStyle.hpp │ ├── pronouns │ │ ├── UserPronouns.cpp │ │ └── UserPronouns.hpp │ ├── bttv │ │ ├── BttvBadges.hpp │ │ └── liveupdates │ │ │ └── BttvLiveUpdateClient.hpp │ └── seventv │ │ ├── eventapi │ │ └── Message.cpp │ │ └── SeventvBadges.hpp ├── messages │ ├── MessageSimilarity.hpp │ ├── Link.cpp │ ├── search │ │ ├── SubstringPredicate.cpp │ │ ├── LinkPredicate.cpp │ │ ├── RegexPredicate.cpp │ │ ├── ChannelPredicate.cpp │ │ └── AuthorPredicate.cpp │ └── Link.hpp ├── singletons │ ├── Resources.hpp │ └── Resources.cpp ├── RunGui.hpp ├── ForwardDecl.hpp └── debug │ ├── AssertInGuiThread.hpp │ ├── Benchmark.cpp │ └── Benchmark.hpp ├── images_forserino └── rainbow.png ├── scripts ├── build-docker-images.sh ├── windows-fix-directory-case-sensitivity.sh ├── README.md ├── update-emoji-data.sh ├── get-tlds-update.sh ├── check-format.sh ├── get-vcpkg-package-versions.sh └── check-line-endings.sh ├── images_dankerino └── example_stream_settings.png ├── tests ├── resources │ └── test-resources.qrc ├── snapshots │ ├── IrcMessageHandler │ │ ├── blocked-user.json │ │ ├── reward-blocked-user.json │ │ ├── sub-blocked-user.json │ │ └── shared-chat-raid.json │ ├── ImageUploader │ │ ├── Export │ │ │ ├── i-nuuls.json │ │ │ ├── basic.json │ │ │ ├── i-fourtf.json │ │ │ └── s-ul.json │ │ └── Import │ │ │ ├── i-nuuls.json │ │ │ ├── basic.json │ │ │ ├── i-fourtf.json │ │ │ └── s-ul.json │ ├── PluginMessageCtor │ │ └── empty.json │ └── EventSub │ │ └── channel-ban │ │ ├── ban.json │ │ └── timeout.json ├── docker-compose.yml └── src │ ├── XDGDesktopFile.cpp │ └── IncognitoBrowser.cpp ├── benchmarks └── resources │ └── bench.qrc ├── .prettierrc ├── QtCreatorPackageManager.cmake ├── mocks └── include │ └── mocks │ ├── Channel.hpp │ ├── EmoteController.hpp │ ├── DisabledStreamerMode.hpp │ ├── TwitchUsers.hpp │ ├── LinkResolver.hpp │ └── ChatterinoBadges.hpp ├── .codecov.yml ├── .prettierignore ├── .git-blame-ignore-revs ├── patches └── 0004-clear-shortcut.patch └── .CI └── full-ubuntu-build.sh /lib/twitch-eventsub-ws/ast/lib/tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/twitch-eventsub-ws/.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__/ 2 | -------------------------------------------------------------------------------- /resources/.gitignore: -------------------------------------------------------------------------------- 1 | linuxinstall 2 | /qt.conf 3 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | build* 2 | .mypy_cache 3 | .cache 4 | .docker 5 | -------------------------------------------------------------------------------- /lib/twitch-eventsub-ws/ast/.gitignore: -------------------------------------------------------------------------------- 1 | /venv 2 | /__pycache__ 3 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | custom: "https://streamelements.com/fourtf/tip" 2 | -------------------------------------------------------------------------------- /lib/twitch-eventsub-ws/src/json.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | -------------------------------------------------------------------------------- /.sanitizers/ubsan-suppressions: -------------------------------------------------------------------------------- 1 | enum:NetworkResult.hpp 2 | enum:gtest.h 3 | -------------------------------------------------------------------------------- /cmake/resources/qt.conf.in: -------------------------------------------------------------------------------- 1 | [Platforms] 2 | WindowsArguments = ${WINDOWS_ARGUMENTS} 3 | -------------------------------------------------------------------------------- /resources/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felyp7/Forserino/HEAD/resources/icon.ico -------------------------------------------------------------------------------- /resources/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felyp7/Forserino/HEAD/resources/icon.png -------------------------------------------------------------------------------- /.sanitizers/asan-suppressions: -------------------------------------------------------------------------------- 1 | # Ignore openssl issues 2 | interceptor_via_lib:libcrypto.so.3 3 | -------------------------------------------------------------------------------- /resources/split/up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felyp7/Forserino/HEAD/resources/split/up.png -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Normalize line endings to LF for files that Git detects as text 2 | * text=auto 3 | -------------------------------------------------------------------------------- /resources/buttons/ban.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felyp7/Forserino/HEAD/resources/buttons/ban.png -------------------------------------------------------------------------------- /resources/buttons/mod.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felyp7/Forserino/HEAD/resources/buttons/mod.png -------------------------------------------------------------------------------- /resources/buttons/vip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felyp7/Forserino/HEAD/resources/buttons/vip.png -------------------------------------------------------------------------------- /resources/chatterino.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felyp7/Forserino/HEAD/resources/chatterino.icns -------------------------------------------------------------------------------- /resources/split/down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felyp7/Forserino/HEAD/resources/split/down.png -------------------------------------------------------------------------------- /resources/split/left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felyp7/Forserino/HEAD/resources/split/left.png -------------------------------------------------------------------------------- /resources/split/move.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felyp7/Forserino/HEAD/resources/split/move.png -------------------------------------------------------------------------------- /resources/split/right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felyp7/Forserino/HEAD/resources/split/right.png -------------------------------------------------------------------------------- /src/util/MacOsHelpers.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | void chatterinoSetMacOsActivationPolicyProhibited(); 4 | -------------------------------------------------------------------------------- /.sanitizers/lsan-suppressions: -------------------------------------------------------------------------------- 1 | # Ignore openssl issues 2 | leak:libcrypto.so.3 3 | leak:CRYPTO_zalloc 4 | -------------------------------------------------------------------------------- /images_forserino/rainbow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felyp7/Forserino/HEAD/images_forserino/rainbow.png -------------------------------------------------------------------------------- /lib/twitch-eventsub-ws/ast/lib/templates/error-failed-to-deserialize.tmpl: -------------------------------------------------------------------------------- 1 | return {{field.name}}.error(); 2 | -------------------------------------------------------------------------------- /resources/avatars/anon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felyp7/Forserino/HEAD/resources/avatars/anon.png -------------------------------------------------------------------------------- /resources/avatars/fourtf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felyp7/Forserino/HEAD/resources/avatars/fourtf.png -------------------------------------------------------------------------------- /resources/avatars/fraxx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felyp7/Forserino/HEAD/resources/avatars/fraxx.png -------------------------------------------------------------------------------- /resources/avatars/jaxkey.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felyp7/Forserino/HEAD/resources/avatars/jaxkey.png -------------------------------------------------------------------------------- /resources/avatars/miguvt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felyp7/Forserino/HEAD/resources/avatars/miguvt.png -------------------------------------------------------------------------------- /resources/avatars/mm2pl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felyp7/Forserino/HEAD/resources/avatars/mm2pl.png -------------------------------------------------------------------------------- /resources/avatars/nealxm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felyp7/Forserino/HEAD/resources/avatars/nealxm.png -------------------------------------------------------------------------------- /resources/avatars/slch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felyp7/Forserino/HEAD/resources/avatars/slch.png -------------------------------------------------------------------------------- /resources/avatars/supa.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felyp7/Forserino/HEAD/resources/avatars/supa.jpg -------------------------------------------------------------------------------- /resources/avatars/techno.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felyp7/Forserino/HEAD/resources/avatars/techno.png -------------------------------------------------------------------------------- /resources/avatars/ynot01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felyp7/Forserino/HEAD/resources/avatars/ynot01.png -------------------------------------------------------------------------------- /resources/avatars/zneix.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felyp7/Forserino/HEAD/resources/avatars/zneix.png -------------------------------------------------------------------------------- /resources/buttons/unban.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felyp7/Forserino/HEAD/resources/buttons/unban.png -------------------------------------------------------------------------------- /resources/buttons/unmod.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felyp7/Forserino/HEAD/resources/buttons/unmod.png -------------------------------------------------------------------------------- /resources/buttons/unvip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felyp7/Forserino/HEAD/resources/buttons/unvip.png -------------------------------------------------------------------------------- /resources/buttons/update.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felyp7/Forserino/HEAD/resources/buttons/update.png -------------------------------------------------------------------------------- /resources/sounds/ping2.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felyp7/Forserino/HEAD/resources/sounds/ping2.wav -------------------------------------------------------------------------------- /resources/streamerMode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felyp7/Forserino/HEAD/resources/streamerMode.png -------------------------------------------------------------------------------- /resources/twitch/automod.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felyp7/Forserino/HEAD/resources/twitch/automod.png -------------------------------------------------------------------------------- /lib/twitch-eventsub-ws/.prettierignore: -------------------------------------------------------------------------------- 1 | .pytest_cache/ 2 | venv/ 3 | build/ 4 | 5 | compile_commands.json 6 | -------------------------------------------------------------------------------- /lib/twitch-eventsub-ws/benchmarks/src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | BENCHMARK_MAIN(); 4 | -------------------------------------------------------------------------------- /resources/avatars/crazysmc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felyp7/Forserino/HEAD/resources/avatars/crazysmc.png -------------------------------------------------------------------------------- /resources/avatars/cyclone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felyp7/Forserino/HEAD/resources/avatars/cyclone.png -------------------------------------------------------------------------------- /resources/avatars/iprodigy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felyp7/Forserino/HEAD/resources/avatars/iprodigy.png -------------------------------------------------------------------------------- /resources/avatars/jakeryw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felyp7/Forserino/HEAD/resources/avatars/jakeryw.png -------------------------------------------------------------------------------- /resources/avatars/kararty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felyp7/Forserino/HEAD/resources/avatars/kararty.png -------------------------------------------------------------------------------- /resources/avatars/occluder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felyp7/Forserino/HEAD/resources/avatars/occluder.png -------------------------------------------------------------------------------- /resources/avatars/pajlada.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felyp7/Forserino/HEAD/resources/avatars/pajlada.png -------------------------------------------------------------------------------- /resources/avatars/revolter.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felyp7/Forserino/HEAD/resources/avatars/revolter.jpg -------------------------------------------------------------------------------- /resources/avatars/xheaveny.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felyp7/Forserino/HEAD/resources/avatars/xheaveny.png -------------------------------------------------------------------------------- /resources/buttons/copyDark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felyp7/Forserino/HEAD/resources/buttons/copyDark.png -------------------------------------------------------------------------------- /resources/buttons/trashCan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felyp7/Forserino/HEAD/resources/buttons/trashCan.png -------------------------------------------------------------------------------- /resources/examples/moving.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felyp7/Forserino/HEAD/resources/examples/moving.gif -------------------------------------------------------------------------------- /src/util/AttachToConsole.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace chatterino { 4 | void attachToConsole(); 5 | } 6 | -------------------------------------------------------------------------------- /lib/twitch-eventsub-ws/ast/lib/templates/error-missing-field.tmpl: -------------------------------------------------------------------------------- 1 | EVENTSUB_BAIL_HERE(error::Kind::FieldMissing); 2 | -------------------------------------------------------------------------------- /lib/twitch-eventsub-ws/ast/lib/templates/initializer-variant.tmpl: -------------------------------------------------------------------------------- 1 | .{{field.name}} = std::move({{field.name}}), 2 | -------------------------------------------------------------------------------- /lib/twitch-eventsub-ws/ast/lib/templates/initializer-vector.tmpl: -------------------------------------------------------------------------------- 1 | .{{field.name}} = std::move(v{{field.name}}), 2 | -------------------------------------------------------------------------------- /resources/avatars/_1xelerate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felyp7/Forserino/HEAD/resources/avatars/_1xelerate.png -------------------------------------------------------------------------------- /resources/avatars/alazymeme.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felyp7/Forserino/HEAD/resources/avatars/alazymeme.png -------------------------------------------------------------------------------- /resources/avatars/brian6932.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felyp7/Forserino/HEAD/resources/avatars/brian6932.png -------------------------------------------------------------------------------- /resources/avatars/devpoland.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felyp7/Forserino/HEAD/resources/avatars/devpoland.png -------------------------------------------------------------------------------- /resources/avatars/droidicus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felyp7/Forserino/HEAD/resources/avatars/droidicus.png -------------------------------------------------------------------------------- /resources/avatars/hicupalot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felyp7/Forserino/HEAD/resources/avatars/hicupalot.png -------------------------------------------------------------------------------- /resources/avatars/karlpolice.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felyp7/Forserino/HEAD/resources/avatars/karlpolice.png -------------------------------------------------------------------------------- /resources/avatars/maliByatzes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felyp7/Forserino/HEAD/resources/avatars/maliByatzes.png -------------------------------------------------------------------------------- /resources/avatars/matthewde.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felyp7/Forserino/HEAD/resources/avatars/matthewde.jpg -------------------------------------------------------------------------------- /resources/avatars/mohad12211.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felyp7/Forserino/HEAD/resources/avatars/mohad12211.png -------------------------------------------------------------------------------- /resources/avatars/niller2005.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felyp7/Forserino/HEAD/resources/avatars/niller2005.png -------------------------------------------------------------------------------- /resources/avatars/obertura777.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felyp7/Forserino/HEAD/resources/avatars/obertura777.png -------------------------------------------------------------------------------- /resources/avatars/otaviobia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felyp7/Forserino/HEAD/resources/avatars/otaviobia.png -------------------------------------------------------------------------------- /resources/avatars/vincent0955.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felyp7/Forserino/HEAD/resources/avatars/vincent0955.png -------------------------------------------------------------------------------- /resources/avatars/wissididom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felyp7/Forserino/HEAD/resources/avatars/wissididom.png -------------------------------------------------------------------------------- /resources/avatars/zakary2841.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felyp7/Forserino/HEAD/resources/avatars/zakary2841.png -------------------------------------------------------------------------------- /resources/buttons/clearSearch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felyp7/Forserino/HEAD/resources/buttons/clearSearch.png -------------------------------------------------------------------------------- /resources/buttons/copyLight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felyp7/Forserino/HEAD/resources/buttons/copyLight.png -------------------------------------------------------------------------------- /resources/buttons/replyDark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felyp7/Forserino/HEAD/resources/buttons/replyDark.png -------------------------------------------------------------------------------- /resources/buttons/updateError.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felyp7/Forserino/HEAD/resources/buttons/updateError.png -------------------------------------------------------------------------------- /resources/examples/splitting.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felyp7/Forserino/HEAD/resources/examples/splitting.gif -------------------------------------------------------------------------------- /resources/scrolling/upScroll.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felyp7/Forserino/HEAD/resources/scrolling/upScroll.png -------------------------------------------------------------------------------- /resources/settings/aboutlogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felyp7/Forserino/HEAD/resources/settings/aboutlogo.png -------------------------------------------------------------------------------- /resources/twitch/sharedChat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felyp7/Forserino/HEAD/resources/twitch/sharedChat.png -------------------------------------------------------------------------------- /lib/README.md: -------------------------------------------------------------------------------- 1 | Third party libraries are stored here. 2 | 3 | Fetched via `git submodule update --init --recursive` 4 | -------------------------------------------------------------------------------- /lib/twitch-eventsub-ws/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.black] 2 | line-length = 120 3 | 4 | [tool.ruff] 5 | line-length = 120 6 | -------------------------------------------------------------------------------- /resources/avatars/knobby-tires.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felyp7/Forserino/HEAD/resources/avatars/knobby-tires.jpg -------------------------------------------------------------------------------- /resources/avatars/zonianmidian.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felyp7/Forserino/HEAD/resources/avatars/zonianmidian.png -------------------------------------------------------------------------------- /resources/scrolling/downScroll.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felyp7/Forserino/HEAD/resources/scrolling/downScroll.png -------------------------------------------------------------------------------- /lib/twitch-eventsub-ws/ast/lib/templates/field-optional-vector.tmpl: -------------------------------------------------------------------------------- 1 | assert(false && "OPTIONAL VECTOR FIELD UNIMPLEMENTED"); 2 | -------------------------------------------------------------------------------- /resources/avatars/explooosion_code.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felyp7/Forserino/HEAD/resources/avatars/explooosion_code.png -------------------------------------------------------------------------------- /resources/buttons/replyThreadDark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felyp7/Forserino/HEAD/resources/buttons/replyThreadDark.png -------------------------------------------------------------------------------- /resources/scrolling/neutralScroll.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felyp7/Forserino/HEAD/resources/scrolling/neutralScroll.png -------------------------------------------------------------------------------- /lib/twitch-eventsub-ws/ast/lib/tests/resources/simple.hpp: -------------------------------------------------------------------------------- 1 | struct Simple { 2 | int a; 3 | bool b; 4 | char c; 5 | }; 6 | -------------------------------------------------------------------------------- /scripts/build-docker-images.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | docker build -f .docker/Dockerfile.build -t chatterino-ubuntu-build .docker 4 | -------------------------------------------------------------------------------- /scripts/windows-fix-directory-case-sensitivity.sh: -------------------------------------------------------------------------------- 1 | find . -not -path "*.git*" -exec fsutil.exe file setCaseSensitiveInfo {} disable \; 2 | -------------------------------------------------------------------------------- /images_dankerino/example_stream_settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felyp7/Forserino/HEAD/images_dankerino/example_stream_settings.png -------------------------------------------------------------------------------- /lib/twitch-eventsub-ws/ast/lib/templates/initializer-optional-vector.tmpl: -------------------------------------------------------------------------------- 1 | assert(false && "OPTIONAL VECTOR INITIALIZER UNIMPLEMENTED"); 2 | -------------------------------------------------------------------------------- /resources/buttons/streamerModeEnabledDark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felyp7/Forserino/HEAD/resources/buttons/streamerModeEnabledDark.png -------------------------------------------------------------------------------- /tests/resources/test-resources.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | 001-mimeapps.list 4 | 5 | 6 | -------------------------------------------------------------------------------- /lib/twitch-eventsub-ws/ast/lib/tests/resources/optional.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | struct Optional { 4 | std::optional a; 5 | }; 6 | -------------------------------------------------------------------------------- /resources/buttons/streamerModeEnabledLight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felyp7/Forserino/HEAD/resources/buttons/streamerModeEnabledLight.png -------------------------------------------------------------------------------- /scripts/README.md: -------------------------------------------------------------------------------- 1 | # scripts 2 | 3 | This directory contains scripts that may be useful for a contributor to run while working on Chatterino 4 | -------------------------------------------------------------------------------- /scripts/update-emoji-data.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | wget https://raw.githubusercontent.com/iamcal/emoji-data/master/emoji.json -O ../resources/emoji.json 3 | -------------------------------------------------------------------------------- /src/BrowserExtension.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace chatterino { 4 | 5 | void runBrowserExtensionHost(); 6 | 7 | } // namespace chatterino 8 | -------------------------------------------------------------------------------- /resources/buttons/moderationDisabledDarkMode18x18.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felyp7/Forserino/HEAD/resources/buttons/moderationDisabledDarkMode18x18.png -------------------------------------------------------------------------------- /lib/twitch-eventsub-ws/benchmarks/resources/bench.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | messages.ndjson 4 | 5 | 6 | -------------------------------------------------------------------------------- /lib/twitch-eventsub-ws/ast/lib/tests/resources/vector.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | struct Vector { 4 | std::vector a; 5 | std::vector> b; 6 | }; 7 | -------------------------------------------------------------------------------- /src/widgets/dialogs/switcher/QuickSwitcherModel.cpp: -------------------------------------------------------------------------------- 1 | #include "widgets/dialogs/switcher/QuickSwitcherModel.hpp" 2 | 3 | namespace chatterino { 4 | 5 | } // namespace chatterino 6 | -------------------------------------------------------------------------------- /lib/twitch-eventsub-ws/ast/requirements-dev.txt: -------------------------------------------------------------------------------- 1 | # Test runner - run pytest in this dir to run tests 2 | pytest==8.3.4 3 | 4 | black==24.3.0 5 | flake8==7.1.1 6 | mypy==1.15.0 7 | isort==5.12.0 8 | -------------------------------------------------------------------------------- /src/util/SelfCheck.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace chatterino::selfcheck { 4 | 5 | /// Checks if WEBPs can be loaded 6 | void checkWebp(); 7 | 8 | } // namespace chatterino::selfcheck 9 | -------------------------------------------------------------------------------- /benchmarks/resources/bench.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | recentmessages-nymn.json 4 | seventvemotes-nymn.json 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/controllers/filters/lang/expressions/Expression.cpp: -------------------------------------------------------------------------------- 1 | #include "controllers/filters/lang/expressions/Expression.hpp" 2 | 3 | namespace chatterino::filters { 4 | 5 | } // namespace chatterino::filters 6 | -------------------------------------------------------------------------------- /src/util/CustomPlayer.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace chatterino { 6 | 7 | void openInCustomPlayer(QStringView channel); 8 | 9 | } // namespace chatterino 10 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | trailingComma: es5 2 | endOfLine: auto 3 | overrides: 4 | - files: "*.ts" 5 | options: 6 | tabWidth: 4 7 | - files: "*.md" 8 | options: 9 | proseWrap: preserve 10 | -------------------------------------------------------------------------------- /src/common/ProviderId.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace chatterino { 4 | 5 | enum class ProviderId { // NOLINT(performance-enum-size) 6 | Twitch, 7 | }; 8 | // 9 | } // namespace chatterino 10 | -------------------------------------------------------------------------------- /lib/twitch-eventsub-ws/ast/lib/templates/initializer-optional.tmpl: -------------------------------------------------------------------------------- 1 | {%- if field.trivial -%} 2 | .{{field.name}} = {{field.name}}, 3 | {%- else -%} 4 | .{{field.name}} = std::move({{field.name}}), 5 | {%- endif -%} 6 | -------------------------------------------------------------------------------- /resources/raw/README.md: -------------------------------------------------------------------------------- 1 | # `resources/raw` 2 | 3 | This directory contains raw source files used to generate images in `resources` and isn't included in the final executable. 4 | These files are not optimized/minimized. 5 | -------------------------------------------------------------------------------- /lib/twitch-eventsub-ws/ast/lib/membertype.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | 4 | class MemberType(Enum): 5 | BASIC = 1 6 | VECTOR = 2 7 | OPTIONAL = 3 8 | OPTIONAL_VECTOR = 4 9 | VARIANT = 5 10 | -------------------------------------------------------------------------------- /lib/twitch-eventsub-ws/ast/lib/templates/initializer-basic.tmpl: -------------------------------------------------------------------------------- 1 | {%- if field.trivial -%} 2 | .{{field.name}} = {{field.name}}.value(), 3 | {%- else -%} 4 | .{{field.name}} = std::move({{field.name}}.value()), 5 | {%- endif -%} 6 | -------------------------------------------------------------------------------- /lib/twitch-eventsub-ws/include/twitch-eventsub-ws/messages/metadata.inc: -------------------------------------------------------------------------------- 1 | boost::json::result_for::type tag_invoke( 2 | boost::json::try_value_to_tag, const boost::json::value &jvRoot); 3 | -------------------------------------------------------------------------------- /lib/semver/README.md: -------------------------------------------------------------------------------- 1 | From https://github.com/Neargye/semver 2 | 3 | Downloaded 2023-01-25 from commit hash [eae828abf579836ba2ecc72a8604ad1b6fb10d86](https://github.com/Neargye/semver/commit/eae828abf579836ba2ecc72a8604ad1b6fb10d86) 4 | -------------------------------------------------------------------------------- /lib/twitch-eventsub-ws/ast/lib/tests/resources/vector-pod.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | struct Pod { 4 | int a; 5 | bool b; 6 | char c; 7 | }; 8 | 9 | struct VectorPod { 10 | std::vector a; 11 | }; 12 | -------------------------------------------------------------------------------- /lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/session-welcome.inc: -------------------------------------------------------------------------------- 1 | boost::json::result_for::type tag_invoke( 2 | boost::json::try_value_to_tag, const boost::json::value &jvRoot); 3 | -------------------------------------------------------------------------------- /src/util/Clipboard.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace chatterino { 6 | 7 | void crossPlatformCopy(const QString &text); 8 | 9 | QString getClipboardText(); 10 | 11 | } // namespace chatterino 12 | -------------------------------------------------------------------------------- /src/widgets/splits/SplitCommon.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace chatterino { 4 | 5 | enum class SplitDirection { 6 | Left, 7 | Above, 8 | Right, 9 | Below, 10 | }; 11 | 12 | } // namespace chatterino 13 | -------------------------------------------------------------------------------- /lib/twitch-eventsub-ws/ast/lib/templates/enum-definition.tmpl: -------------------------------------------------------------------------------- 1 | boost::json::result_for<{{enum.full_name}}, boost::json::value>::type tag_invoke( 2 | boost::json::try_value_to_tag<{{enum.full_name}}>, const boost::json::value &jvRoot); 3 | -------------------------------------------------------------------------------- /src/providers/ffz/FfzUtil.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common/Aliases.hpp" 4 | 5 | #include 6 | 7 | namespace chatterino { 8 | 9 | Url parseFfzUrl(const QString &ffzUrl); 10 | 11 | } // namespace chatterino 12 | -------------------------------------------------------------------------------- /lib/twitch-eventsub-ws/ast/lib/templates/struct-definition.tmpl: -------------------------------------------------------------------------------- 1 | boost::json::result_for<{{struct.full_name}}, boost::json::value>::type tag_invoke( 2 | boost::json::try_value_to_tag<{{struct.full_name}}>, const boost::json::value &jvRoot); 3 | -------------------------------------------------------------------------------- /src/providers/twitch/TwitchHelpers.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace chatterino { 6 | 7 | bool trimChannelName(const QString &channelName, QString &outChannelName); 8 | 9 | } // namespace chatterino 10 | -------------------------------------------------------------------------------- /resources/buttons/cancelDark.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/widgets/dialogs/switcher/AbstractSwitcherItem.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "widgets/listview/GenericListItem.hpp" 4 | 5 | namespace chatterino { 6 | 7 | using AbstractSwitcherItem = GenericListItem; 8 | 9 | } // namespace chatterino 10 | -------------------------------------------------------------------------------- /resources/buttons/cancel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/util/FuzzyConvert.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace chatterino { 6 | 7 | int fuzzyToInt(const QString &str, int default_); 8 | float fuzzyToFloat(const QString &str, float default_); 9 | 10 | } // namespace chatterino 11 | -------------------------------------------------------------------------------- /src/widgets/helper/DebugPopup.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "widgets/BasePopup.hpp" 4 | 5 | namespace chatterino { 6 | 7 | class DebugPopup : public BasePopup 8 | { 9 | public: 10 | DebugPopup(); 11 | }; 12 | 13 | } // namespace chatterino 14 | -------------------------------------------------------------------------------- /src/widgets/dialogs/WelcomeDialog.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "widgets/BaseWindow.hpp" 4 | 5 | namespace chatterino { 6 | 7 | class WelcomeDialog : public BaseWindow 8 | { 9 | public: 10 | WelcomeDialog(); 11 | }; 12 | 13 | } // namespace chatterino 14 | -------------------------------------------------------------------------------- /src/util/MacOsHelpers.mm: -------------------------------------------------------------------------------- 1 | #include "util/MacOsHelpers.h" 2 | 3 | #include 4 | 5 | void chatterinoSetMacOsActivationPolicyProhibited() 6 | { 7 | [[NSApplication sharedApplication] setActivationPolicy:NSApplicationActivationPolicyProhibited]; 8 | } 9 | 10 | -------------------------------------------------------------------------------- /src/widgets/dialogs/switcher/QuickSwitcherModel.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "widgets/listview/GenericListModel.hpp" 4 | 5 | #include 6 | 7 | namespace chatterino { 8 | 9 | using QuickSwitcherModel = GenericListModel; 10 | 11 | } // namespace chatterino 12 | -------------------------------------------------------------------------------- /lib/twitch-eventsub-ws/TODO.md: -------------------------------------------------------------------------------- 1 | ## TODO 2 | 3 | - Default of `json_transform` should be `snake_case` since it will be used most 4 | - The websocket connections need to die when user is changed 5 | - Figure out if the websocket connections need to die when the user refreshed their token 6 | -------------------------------------------------------------------------------- /lib/twitch-eventsub-ws/ast/lib/tests/resources/string.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | struct String { 6 | std::string a; 7 | const std::string b; 8 | std::vector c; 9 | std::optional d; 10 | }; 11 | -------------------------------------------------------------------------------- /src/common/TimeoutStackStyle.hpp: -------------------------------------------------------------------------------- 1 | namespace chatterino { 2 | 3 | enum class TimeoutStackStyle : int { 4 | StackHard = 0, 5 | DontStackBeyondUserMessage = 1, 6 | DontStack = 2, 7 | 8 | Default = DontStackBeyondUserMessage, 9 | }; 10 | 11 | } // namespace chatterino 12 | -------------------------------------------------------------------------------- /lib/twitch-eventsub-ws/.isort.cfg: -------------------------------------------------------------------------------- 1 | [settings] 2 | multi_line_output=3 3 | include_trailing_comma=True 4 | force_grid_wrap=0 5 | known_first_party=lib 6 | known_typing=typing 7 | use_parentheses=True 8 | line_length=120 9 | sections=FUTURE,TYPING,STDLIB,FIRSTPARTY,THIRDPARTY,LOCALFOLDER 10 | -------------------------------------------------------------------------------- /resources/buttons/reloadDark.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /resources/com.chatterino.chatterino.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Type=Application 3 | Version=1.0 4 | Name=Chatterino 5 | Comment=Chat client for Twitch 6 | Exec=chatterino 7 | Icon=com.chatterino.chatterino 8 | Terminal=false 9 | Categories=Network;InstantMessaging; 10 | StartupWMClass=chatterino 11 | -------------------------------------------------------------------------------- /resources/buttons/reloadLight.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/controllers/plugins/api/EventType.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifdef CHATTERINO_HAVE_PLUGINS 3 | 4 | namespace chatterino::lua::api { 5 | 6 | /** 7 | * @exposeenum c2.EventType 8 | */ 9 | enum class EventType { 10 | CompletionRequested, 11 | }; 12 | 13 | } // namespace chatterino::lua::api 14 | #endif 15 | -------------------------------------------------------------------------------- /src/messages/MessageSimilarity.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "messages/Message.hpp" 4 | 5 | #include 6 | namespace chatterino { 7 | 8 | template 9 | void setSimilarityFlags(const MessagePtr &message, const T &messages); 10 | 11 | } // namespace chatterino 12 | -------------------------------------------------------------------------------- /src/singletons/Resources.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ResourcesAutogen.hpp" 4 | 5 | namespace chatterino { 6 | 7 | /// This class in thread safe but needs to be initialized from the gui thread 8 | /// first. 9 | Resources2 &getResources(); 10 | void initResources(); 11 | 12 | } // namespace chatterino 13 | -------------------------------------------------------------------------------- /QtCreatorPackageManager.cmake: -------------------------------------------------------------------------------- 1 | # https://www.qt.io/blog/qt-creator-cmake-package-manager-auto-setup 2 | 3 | # set(QT_CREATOR_SKIP_PACKAGE_MANAGER_SETUP ON) # skip both conan and vcpkg auto-setups 4 | # set(QT_CREATOR_SKIP_CONAN_SETUP ON) # skip conan auto-setup 5 | set(QT_CREATOR_SKIP_VCPKG_SETUP ON) # skip vcpkg auto-setup 6 | -------------------------------------------------------------------------------- /lib/twitch-eventsub-ws/ast/requirements.txt: -------------------------------------------------------------------------------- 1 | # parsing of header files 2 | clang==15.0.7 3 | 4 | # ensures formatting of generated files is consistent 5 | clang-format==16.0.6 6 | 7 | # used for generating the code 8 | Jinja2==3.1.6 9 | jinja2-workarounds==0.1.0 10 | 11 | # colors for logging 12 | colorama==0.4.6 13 | -------------------------------------------------------------------------------- /lib/twitch-eventsub-ws/ast/lib/tests/resources/const.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | struct Pod { 5 | }; 6 | 7 | struct Const { 8 | const int a; 9 | const bool b; 10 | const char c; 11 | const Pod d; 12 | const std::vector e; 13 | const std::optional f; 14 | }; 15 | -------------------------------------------------------------------------------- /src/providers/ffz/FfzUtil.cpp: -------------------------------------------------------------------------------- 1 | #include "providers/ffz/FfzUtil.hpp" 2 | 3 | #include 4 | 5 | namespace chatterino { 6 | 7 | Url parseFfzUrl(const QString &ffzUrl) 8 | { 9 | QUrl asURL(ffzUrl); 10 | asURL.setScheme("https"); 11 | return {asURL.toString()}; 12 | } 13 | 14 | } // namespace chatterino 15 | -------------------------------------------------------------------------------- /src/widgets/settingspages/AccountsPage.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "widgets/settingspages/SettingsPage.hpp" 4 | 5 | namespace chatterino { 6 | 7 | class AccountSwitchWidget; 8 | 9 | class AccountsPage : public SettingsPage 10 | { 11 | public: 12 | AccountsPage(); 13 | }; 14 | 15 | } // namespace chatterino 16 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /src/RunGui.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class QApplication; 4 | 5 | namespace chatterino { 6 | 7 | class Args; 8 | class Paths; 9 | class Settings; 10 | class Updates; 11 | 12 | void runGui(QApplication &a, const Paths &paths, Settings &settings, 13 | const Args &args, Updates &updates); 14 | 15 | } // namespace chatterino 16 | -------------------------------------------------------------------------------- /src/common/ChatterinoSetting.cpp: -------------------------------------------------------------------------------- 1 | #include "common/ChatterinoSetting.hpp" 2 | 3 | #include "singletons/Settings.hpp" 4 | 5 | namespace chatterino { 6 | 7 | void _registerSetting(std::weak_ptr setting) 8 | { 9 | _actuallyRegisterSetting(std::move(setting)); 10 | } 11 | 12 | } // namespace chatterino 13 | -------------------------------------------------------------------------------- /src/controllers/commands/builtin/twitch/SendReply.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class QString; 4 | 5 | namespace chatterino { 6 | 7 | struct CommandContext; 8 | 9 | } // namespace chatterino 10 | 11 | namespace chatterino::commands { 12 | 13 | QString sendReply(const CommandContext &ctx); 14 | 15 | } // namespace chatterino::commands 16 | -------------------------------------------------------------------------------- /src/controllers/commands/builtin/twitch/SendWhisper.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class QString; 4 | 5 | namespace chatterino { 6 | 7 | struct CommandContext; 8 | 9 | } // namespace chatterino 10 | 11 | namespace chatterino::commands { 12 | 13 | QString sendWhisper(const CommandContext &ctx); 14 | 15 | } // namespace chatterino::commands 16 | -------------------------------------------------------------------------------- /src/controllers/commands/builtin/twitch/AddVIP.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class QString; 4 | 5 | namespace chatterino { 6 | 7 | struct CommandContext; 8 | 9 | } // namespace chatterino 10 | 11 | namespace chatterino::commands { 12 | 13 | /// /vip 14 | QString addVIP(const CommandContext &ctx); 15 | 16 | } // namespace chatterino::commands 17 | -------------------------------------------------------------------------------- /src/controllers/commands/builtin/twitch/Shoutout.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace chatterino { 6 | 7 | struct CommandContext; 8 | 9 | } // namespace chatterino 10 | 11 | namespace chatterino::commands { 12 | 13 | QString sendShoutout(const CommandContext &ctx); 14 | 15 | } // namespace chatterino::commands 16 | -------------------------------------------------------------------------------- /src/controllers/commands/builtin/twitch/UpdateColor.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class QString; 4 | 5 | namespace chatterino { 6 | 7 | struct CommandContext; 8 | 9 | } // namespace chatterino 10 | 11 | namespace chatterino::commands { 12 | 13 | QString updateUserColor(const CommandContext &ctx); 14 | 15 | } // namespace chatterino::commands 16 | -------------------------------------------------------------------------------- /src/controllers/commands/builtin/twitch/Warn.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class QString; 4 | 5 | namespace chatterino { 6 | 7 | struct CommandContext; 8 | 9 | } // namespace chatterino 10 | 11 | namespace chatterino::commands { 12 | 13 | /// /warn 14 | QString sendWarn(const CommandContext &ctx); 15 | 16 | } // namespace chatterino::commands 17 | -------------------------------------------------------------------------------- /src/providers/twitch/PubSubClientOptions.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace chatterino { 6 | 7 | /** 8 | * @brief Options to change the behaviour of the underlying websocket clients 9 | **/ 10 | struct PubSubClientOptions { 11 | std::chrono::seconds pingInterval_; 12 | }; 13 | 14 | } // namespace chatterino 15 | -------------------------------------------------------------------------------- /src/util/CombinePath.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace chatterino { 7 | 8 | // https://stackoverflow.com/a/13014491 9 | inline QString combinePath(const QString &a, const QString &b) 10 | { 11 | return QDir::cleanPath(a + QDir::separator() + b); 12 | } 13 | 14 | } // namespace chatterino 15 | -------------------------------------------------------------------------------- /src/widgets/dialogs/LastRunCrashDialog.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace chatterino { 6 | 7 | class Args; 8 | class Paths; 9 | 10 | class LastRunCrashDialog : public QDialog 11 | { 12 | public: 13 | explicit LastRunCrashDialog(const Args &args, const Paths &paths); 14 | }; 15 | 16 | } // namespace chatterino 17 | -------------------------------------------------------------------------------- /src/widgets/dialogs/WelcomeDialog.cpp: -------------------------------------------------------------------------------- 1 | #include "widgets/dialogs/WelcomeDialog.hpp" 2 | 3 | namespace chatterino { 4 | 5 | WelcomeDialog::WelcomeDialog() 6 | : BaseWindow({BaseWindow::EnableCustomFrame, BaseWindow::DisableLayoutSave}) 7 | { 8 | this->setWindowTitle("Chatterino quick setup"); 9 | } 10 | 11 | } // namespace chatterino 12 | -------------------------------------------------------------------------------- /mocks/include/mocks/Channel.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common/Channel.hpp" 4 | 5 | namespace chatterino::mock { 6 | 7 | class MockChannel : public Channel 8 | { 9 | public: 10 | MockChannel(const QString &name) 11 | : Channel(name, Channel::Type::Twitch) 12 | { 13 | } 14 | }; 15 | 16 | } // namespace chatterino::mock 17 | -------------------------------------------------------------------------------- /src/controllers/commands/builtin/twitch/GetVIPs.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace chatterino { 6 | 7 | struct CommandContext; 8 | 9 | } // namespace chatterino 10 | 11 | namespace chatterino::commands { 12 | 13 | /// /vips 14 | QString getVIPs(const CommandContext &ctx); 15 | 16 | } // namespace chatterino::commands 17 | -------------------------------------------------------------------------------- /src/controllers/commands/builtin/twitch/Unban.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class QString; 4 | 5 | namespace chatterino { 6 | 7 | struct CommandContext; 8 | 9 | } // namespace chatterino 10 | 11 | namespace chatterino::commands { 12 | 13 | /// /unban 14 | QString unbanUser(const CommandContext &ctx); 15 | 16 | } // namespace chatterino::commands 17 | -------------------------------------------------------------------------------- /src/ForwardDecl.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | // This file contains common forward declarations. 6 | 7 | namespace chatterino { 8 | class Channel; 9 | class ChannelView; 10 | using ChannelPtr = std::shared_ptr; 11 | 12 | struct Message; 13 | using MessagePtr = std::shared_ptr; 14 | } // namespace chatterino 15 | -------------------------------------------------------------------------------- /src/controllers/commands/builtin/twitch/AddModerator.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class QString; 4 | 5 | namespace chatterino { 6 | 7 | struct CommandContext; 8 | 9 | } // namespace chatterino 10 | 11 | namespace chatterino::commands { 12 | 13 | /// /mod 14 | QString addModerator(const CommandContext &ctx); 15 | 16 | } // namespace chatterino::commands 17 | -------------------------------------------------------------------------------- /src/controllers/commands/builtin/twitch/RemoveVIP.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class QString; 4 | 5 | namespace chatterino { 6 | 7 | struct CommandContext; 8 | 9 | } // namespace chatterino 10 | 11 | namespace chatterino::commands { 12 | 13 | /// /unvip 14 | QString removeVIP(const CommandContext &ctx); 15 | 16 | } // namespace chatterino::commands 17 | -------------------------------------------------------------------------------- /src/providers/liveupdates/Diag.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace chatterino::liveupdates { 6 | 7 | struct Diag { 8 | std::atomic connectionsClosed{0}; 9 | std::atomic connectionsOpened{0}; 10 | std::atomic connectionsFailed{0}; 11 | }; 12 | 13 | } // namespace chatterino::liveupdates 14 | -------------------------------------------------------------------------------- /src/controllers/commands/builtin/twitch/GetModerators.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class QString; 4 | 5 | namespace chatterino { 6 | 7 | struct CommandContext; 8 | 9 | } // namespace chatterino 10 | 11 | namespace chatterino::commands { 12 | 13 | /// /mods 14 | QString getModerators(const CommandContext &ctx); 15 | 16 | } // namespace chatterino::commands 17 | -------------------------------------------------------------------------------- /src/util/QStringHash.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace boost { 8 | 9 | template <> 10 | struct hash { 11 | std::size_t operator()(QString const &s) const 12 | { 13 | return qHash(s); 14 | } 15 | }; 16 | 17 | } // namespace boost 18 | -------------------------------------------------------------------------------- /src/controllers/commands/builtin/twitch/RemoveModerator.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class QString; 4 | 5 | namespace chatterino { 6 | 7 | struct CommandContext; 8 | 9 | } // namespace chatterino 10 | 11 | namespace chatterino::commands { 12 | 13 | /// /unmod 14 | QString removeModerator(const CommandContext &ctx); 15 | 16 | } // namespace chatterino::commands 17 | -------------------------------------------------------------------------------- /src/widgets/helper/InvisibleSizeGrip.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace chatterino { 6 | 7 | class InvisibleSizeGrip : public QSizeGrip 8 | { 9 | public: 10 | explicit InvisibleSizeGrip(QWidget *parent = nullptr); 11 | 12 | protected: 13 | void paintEvent(QPaintEvent *event) override; 14 | }; 15 | 16 | } // namespace chatterino 17 | -------------------------------------------------------------------------------- /cmake/resources/resources_autogenerated.qrc.in: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | @RES_RESOURCES_CONTENT@ 8 | 9 | 10 | @RES_QT_CONF_PATH@ 11 | 12 | 13 | -------------------------------------------------------------------------------- /resources/settings/keybinds.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/controllers/commands/builtin/twitch/StartCommercial.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace chatterino { 6 | 7 | struct CommandContext; 8 | 9 | } // namespace chatterino 10 | 11 | namespace chatterino::commands { 12 | 13 | /// /commercial 14 | QString startCommercial(const CommandContext &ctx); 15 | 16 | } // namespace chatterino::commands 17 | -------------------------------------------------------------------------------- /tests/snapshots/IrcMessageHandler/blocked-user.json: -------------------------------------------------------------------------------- 1 | { 2 | "input": "@tmi-sent-ts=1726692855226;subscriber=1;id=ff82c584-5d20-459f-b2d5-dcacbb693559;room-id=11148817;user-id=12345;display-name=blocked;badges=subscriber/24;badge-info=subscriber/27;color=#FF0000;flags=;user-type=;emotes= :blocked!blocked@blocked.tmi.twitch.tv PRIVMSG #pajlada :a", 3 | "output": [ 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/channel-ban-v1.inc: -------------------------------------------------------------------------------- 1 | boost::json::result_for::type tag_invoke( 2 | boost::json::try_value_to_tag, const boost::json::value &jvRoot); 3 | 4 | boost::json::result_for::type tag_invoke( 5 | boost::json::try_value_to_tag, const boost::json::value &jvRoot); 6 | -------------------------------------------------------------------------------- /lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/channel-update-v1.inc: -------------------------------------------------------------------------------- 1 | boost::json::result_for::type tag_invoke( 2 | boost::json::try_value_to_tag, const boost::json::value &jvRoot); 3 | 4 | boost::json::result_for::type tag_invoke( 5 | boost::json::try_value_to_tag, const boost::json::value &jvRoot); 6 | -------------------------------------------------------------------------------- /lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/stream-offline-v1.inc: -------------------------------------------------------------------------------- 1 | boost::json::result_for::type tag_invoke( 2 | boost::json::try_value_to_tag, const boost::json::value &jvRoot); 3 | 4 | boost::json::result_for::type tag_invoke( 5 | boost::json::try_value_to_tag, const boost::json::value &jvRoot); 6 | -------------------------------------------------------------------------------- /lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/stream-online-v1.inc: -------------------------------------------------------------------------------- 1 | boost::json::result_for::type tag_invoke( 2 | boost::json::try_value_to_tag, const boost::json::value &jvRoot); 3 | 4 | boost::json::result_for::type tag_invoke( 5 | boost::json::try_value_to_tag, const boost::json::value &jvRoot); 6 | -------------------------------------------------------------------------------- /lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/automod-message-hold-v2.inc: -------------------------------------------------------------------------------- 1 | boost::json::result_for::type tag_invoke( 2 | boost::json::try_value_to_tag, const boost::json::value &jvRoot); 3 | 4 | boost::json::result_for::type tag_invoke( 5 | boost::json::try_value_to_tag, const boost::json::value &jvRoot); 6 | -------------------------------------------------------------------------------- /src/util/SharedPtrElementLess.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace chatterino { 6 | 7 | template 8 | struct SharedPtrElementLess { 9 | bool operator()(const std::shared_ptr &a, 10 | const std::shared_ptr &b) const 11 | { 12 | return a->operator<(*b); 13 | } 14 | }; 15 | 16 | } // namespace chatterino 17 | -------------------------------------------------------------------------------- /lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/automod-message-update-v2.inc: -------------------------------------------------------------------------------- 1 | boost::json::result_for::type tag_invoke( 2 | boost::json::try_value_to_tag, const boost::json::value &jvRoot); 3 | 4 | boost::json::result_for::type tag_invoke( 5 | boost::json::try_value_to_tag, const boost::json::value &jvRoot); 6 | -------------------------------------------------------------------------------- /resources/switcher/plus.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/common/Literals.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | /// Re-export of Qt's string literals. In new code, prefer using 6 | /// Qt::StringLiterals. 7 | namespace chatterino::literals { 8 | 9 | using Qt::StringLiterals::operator""_s; 10 | using Qt::StringLiterals::operator""_ba; 11 | using Qt::StringLiterals::operator""_L1; 12 | 13 | } // namespace chatterino::literals 14 | -------------------------------------------------------------------------------- /src/controllers/commands/builtin/twitch/UpdateChannel.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class QString; 4 | 5 | namespace chatterino { 6 | 7 | struct CommandContext; 8 | 9 | } // namespace chatterino 10 | 11 | namespace chatterino::commands { 12 | 13 | QString setTitle(const CommandContext &ctx); 14 | QString setGame(const CommandContext &ctx); 15 | 16 | } // namespace chatterino::commands 17 | -------------------------------------------------------------------------------- /src/widgets/helper/color/Checkerboard.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace chatterino { 6 | 7 | void drawCheckerboard(QPainter &painter, QRect rect, int tileSize = 4); 8 | inline void drawCheckerboard(QPainter &painter, QSize size, int tileSize = 4) 9 | { 10 | drawCheckerboard(painter, {{0, 0}, size}, tileSize); 11 | } 12 | 13 | } // namespace chatterino 14 | -------------------------------------------------------------------------------- /lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/channel-chat-user-message-hold-v1.inc: -------------------------------------------------------------------------------- 1 | boost::json::result_for::type tag_invoke( 2 | boost::json::try_value_to_tag, const boost::json::value &jvRoot); 3 | 4 | boost::json::result_for::type tag_invoke( 5 | boost::json::try_value_to_tag, const boost::json::value &jvRoot); 6 | -------------------------------------------------------------------------------- /lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/channel-suspicious-user-update-v1.inc: -------------------------------------------------------------------------------- 1 | boost::json::result_for::type tag_invoke( 2 | boost::json::try_value_to_tag, const boost::json::value &jvRoot); 3 | 4 | boost::json::result_for::type tag_invoke( 5 | boost::json::try_value_to_tag, const boost::json::value &jvRoot); 6 | -------------------------------------------------------------------------------- /src/controllers/commands/builtin/twitch/Chatters.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class QString; 4 | 5 | namespace chatterino { 6 | 7 | struct CommandContext; 8 | 9 | } // namespace chatterino 10 | 11 | namespace chatterino::commands { 12 | 13 | QString chatters(const CommandContext &ctx); 14 | 15 | QString testChatters(const CommandContext &ctx); 16 | 17 | } // namespace chatterino::commands 18 | -------------------------------------------------------------------------------- /src/widgets/settingspages/NotificationPage.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "widgets/settingspages/SettingsPage.hpp" 4 | 5 | class QComboBox; 6 | 7 | namespace chatterino { 8 | 9 | class NotificationPage : public SettingsPage 10 | { 11 | public: 12 | NotificationPage(); 13 | 14 | private: 15 | QComboBox *createToastReactionComboBox(); 16 | }; 17 | 18 | } // namespace chatterino 19 | -------------------------------------------------------------------------------- /lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/channel-suspicious-user-message-v1.inc: -------------------------------------------------------------------------------- 1 | boost::json::result_for::type tag_invoke( 2 | boost::json::try_value_to_tag, const boost::json::value &jvRoot); 3 | 4 | boost::json::result_for::type tag_invoke( 5 | boost::json::try_value_to_tag, const boost::json::value &jvRoot); 6 | -------------------------------------------------------------------------------- /lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/subscription.inc: -------------------------------------------------------------------------------- 1 | boost::json::result_for::type tag_invoke( 2 | boost::json::try_value_to_tag, const boost::json::value &jvRoot); 3 | 4 | boost::json::result_for::type tag_invoke( 5 | boost::json::try_value_to_tag, 6 | const boost::json::value &jvRoot); 7 | -------------------------------------------------------------------------------- /src/controllers/commands/builtin/twitch/ShieldMode.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace chatterino { 6 | 7 | struct CommandContext; 8 | 9 | } // namespace chatterino 10 | 11 | namespace chatterino::commands { 12 | 13 | QString shieldModeOn(const CommandContext &ctx); 14 | QString shieldModeOff(const CommandContext &ctx); 15 | 16 | } // namespace chatterino::commands 17 | -------------------------------------------------------------------------------- /src/util/DisplayBadge.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace chatterino { 6 | 7 | class DisplayBadge 8 | { 9 | public: 10 | DisplayBadge(QString displayName, QString badgeName); 11 | 12 | QString displayName() const; 13 | QString badgeName() const; 14 | 15 | private: 16 | QString displayName_; 17 | QString badgeName_; 18 | }; 19 | 20 | } // namespace chatterino 21 | -------------------------------------------------------------------------------- /src/util/DistanceBetweenPoints.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | namespace chatterino { 8 | 9 | inline qreal distanceBetweenPoints(const QPointF &p1, const QPointF &p2) 10 | { 11 | QPointF tmp = p1 - p2; 12 | 13 | qreal distance = tmp.x() * tmp.x() + tmp.y() * tmp.y(); 14 | 15 | return std::sqrt(distance); 16 | } 17 | 18 | } // namespace chatterino 19 | -------------------------------------------------------------------------------- /lib/twitch-eventsub-ws/ast/lib/__init__.py: -------------------------------------------------------------------------------- 1 | from .generate import generate 2 | from .helpers import get_clang_builtin_include_dirs, init_clang_cindex, temporary_file 3 | from .logging import init_logging 4 | from .parse import parse 5 | 6 | __all__ = [ 7 | "generate", 8 | "get_clang_builtin_include_dirs", 9 | "init_clang_cindex", 10 | "init_logging", 11 | "parse", 12 | "temporary_file", 13 | ] 14 | -------------------------------------------------------------------------------- /src/util/IncognitoBrowser.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace chatterino::incognitobrowser::detail { 6 | 7 | QString getPrivateSwitch(const QString &browserExecutable); 8 | 9 | } // namespace chatterino::incognitobrowser::detail 10 | 11 | namespace chatterino { 12 | 13 | bool supportsIncognitoLinks(); 14 | bool openLinkIncognito(const QString &link); 15 | 16 | } // namespace chatterino 17 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-suggestion.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature suggestion 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **What should be added?** 11 | 12 | 13 | **Why should it be added?** 14 | 15 | -------------------------------------------------------------------------------- /lib/twitch-eventsub-ws/README.md: -------------------------------------------------------------------------------- 1 | # twitch-eventsub-ws 2 | 3 | This library implements some of [Twitch's EventSub](https://dev.twitch.tv/docs/eventsub/) topics for use in [Chatterino 2](https://github.com/Chatterino/chatterino2) 4 | 5 | Topic generation is done in the `ast` directory 6 | 7 | The example can be built like this: 8 | 9 | ```sh 10 | mkdir build 11 | cd build 12 | cmake ../example 13 | cmake --build . 14 | ``` 15 | -------------------------------------------------------------------------------- /src/controllers/commands/builtin/twitch/Raid.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace chatterino { 6 | 7 | struct CommandContext; 8 | 9 | } // namespace chatterino 10 | 11 | namespace chatterino::commands { 12 | 13 | /// /raid 14 | QString startRaid(const CommandContext &ctx); 15 | 16 | /// /unraid 17 | QString cancelRaid(const CommandContext &ctx); 18 | 19 | } // namespace chatterino::commands 20 | -------------------------------------------------------------------------------- /src/common/enums/MessageContext.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace chatterino { 4 | 5 | /** @exposeenum c2.MessageContext */ 6 | 7 | /// Context of the message being added to a channel 8 | enum class MessageContext { 9 | /// This message is the original 10 | Original, 11 | /// This message is a repost of a message that has already been added in a channel 12 | Repost, 13 | }; 14 | 15 | } // namespace chatterino 16 | -------------------------------------------------------------------------------- /tests/docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | http-server: 3 | restart: always 4 | image: ghcr.io/kevinastone/httpbox:master 5 | command: "--port 9051" 6 | ports: 7 | - "9051:9051" 8 | pubsub-server: 9 | restart: always 10 | image: ghcr.io/chatterino/twitch-pubsub-server-test:latest 11 | command: "./server 0.0.0.0:9050 0.0.0.0:9052" 12 | ports: 13 | - "9050:9050" 14 | - "9052:9052" 15 | -------------------------------------------------------------------------------- /resources/settings/accounts.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/widgets/NotebookEnums.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace chatterino { 4 | 5 | enum NotebookTabLocation { Top = 0, Left = 1, Right = 2, Bottom = 3 }; 6 | 7 | // Controls the visibility of tabs in this notebook 8 | enum NotebookTabVisibility : int { 9 | // Show all tabs 10 | AllTabs = 0, 11 | 12 | // Only show tabs containing splits that are live 13 | LiveOnly = 1, 14 | }; 15 | 16 | } // namespace chatterino 17 | -------------------------------------------------------------------------------- /resources/switcher/switch.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/common/network/NetworkManager.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace chatterino { 7 | 8 | class NetworkManager : public QObject 9 | { 10 | Q_OBJECT 11 | 12 | public: 13 | static QThread *workerThread; 14 | static QNetworkAccessManager *accessManager; 15 | 16 | static void init(); 17 | static void deinit(); 18 | }; 19 | 20 | } // namespace chatterino 21 | -------------------------------------------------------------------------------- /src/widgets/settingspages/NicknamesPage.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "widgets/settingspages/SettingsPage.hpp" 4 | 5 | namespace chatterino { 6 | 7 | class EditableModelView; 8 | 9 | class NicknamesPage : public SettingsPage 10 | { 11 | public: 12 | NicknamesPage(); 13 | bool filterElements(const QString &query) override; 14 | 15 | private: 16 | EditableModelView *view_; 17 | }; 18 | 19 | } // namespace chatterino 20 | -------------------------------------------------------------------------------- /lib/twitch-eventsub-ws/ast/check-clang.hpp: -------------------------------------------------------------------------------- 1 | // This is a header used for testing if the library can parse a file. 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | boost::json::value doSomething(QString s, 9 | std::chrono::system_clock::time_point tp); 10 | 11 | static_assert(sizeof(QString) > sizeof(int)); 12 | static_assert(sizeof(boost::json::value) > sizeof(int)); 13 | -------------------------------------------------------------------------------- /src/common/LastMessageLineStyle.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | namespace chatterino { 8 | 9 | /// The values must always be castable to a Qt::BrushStyle 10 | // NOLINTNEXTLINE(performance-enum-size) 11 | enum class LastMessageLineStyle : std::underlying_type_t { 12 | Solid = Qt::SolidPattern, 13 | Dotted = Qt::VerPattern, 14 | }; 15 | 16 | } // namespace chatterino 17 | -------------------------------------------------------------------------------- /src/controllers/commands/builtin/twitch/DeleteMessages.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class QString; 4 | 5 | namespace chatterino { 6 | 7 | struct CommandContext; 8 | 9 | } // namespace chatterino 10 | 11 | namespace chatterino::commands { 12 | 13 | /// /clear 14 | QString deleteAllMessages(const CommandContext &ctx); 15 | 16 | /// /delete 17 | QString deleteOneMessage(const CommandContext &ctx); 18 | 19 | } // namespace chatterino::commands 20 | -------------------------------------------------------------------------------- /src/widgets/settingspages/IgnoresPage.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "widgets/settingspages/SettingsPage.hpp" 4 | 5 | #include 6 | 7 | class QVBoxLayout; 8 | 9 | namespace chatterino { 10 | 11 | class IgnoresPage : public SettingsPage 12 | { 13 | public: 14 | IgnoresPage(); 15 | 16 | void onShow() final; 17 | 18 | private: 19 | QStringListModel userListModel_; 20 | }; 21 | 22 | } // namespace chatterino 23 | -------------------------------------------------------------------------------- /.sanitizers/tsan-suppressions: -------------------------------------------------------------------------------- 1 | race:libdbus-1.so.3 2 | deadlock:libdbus-1.so.3 3 | race:libglib-2.0.so.0 4 | race:libgio-2.0.so.0 5 | 6 | # Not sure about these suppression 7 | # race:qscopedpointer.h 8 | # race:qarraydata.cpp 9 | # race:qarraydata.h 10 | # race:qarraydataops.h 11 | # race:libQt6Core.so.6 12 | # race:libQt6Gui.so.6 13 | # race:libQt6XcbQpa.so.6 14 | # race:libQt6Network.so.6 15 | 16 | # very not sure about this one 17 | # race:qstring.h 18 | -------------------------------------------------------------------------------- /src/debug/AssertInGuiThread.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | namespace chatterino { 9 | 10 | inline bool isGuiThread() 11 | { 12 | return QCoreApplication::instance()->thread() == QThread::currentThread(); 13 | } 14 | 15 | inline void assertInGuiThread() 16 | { 17 | #ifdef _DEBUG 18 | assert(isGuiThread()); 19 | #endif 20 | } 21 | 22 | } // namespace chatterino 23 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/something-completely-different.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Something completely different 3 | about: If you have something completely different 4 | title: '' 5 | assignees: '' 6 | 7 | --- 8 | 9 | 14 | -------------------------------------------------------------------------------- /src/controllers/hotkeys/HotkeyHelpers.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "controllers/hotkeys/ActionNames.hpp" 4 | 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | namespace chatterino { 11 | 12 | std::vector parseHotkeyArguments(QString argumentString); 13 | std::optional findHotkeyActionDefinition( 14 | HotkeyCategory category, const QString &action); 15 | 16 | } // namespace chatterino 17 | -------------------------------------------------------------------------------- /src/controllers/plugins/api/JSON.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef CHATTERINO_HAVE_PLUGINS 4 | # include 5 | 6 | namespace chatterino::lua::api { 7 | 8 | /// Loads the 'json' module as a table. 9 | /// 10 | /// Because `nullptr` is used as a sentinel for "null", this also adds a 11 | /// `__tostring` method on lightuserdata. 12 | sol::object loadJson(sol::state_view lua); 13 | 14 | } // namespace chatterino::lua::api 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /src/widgets/helper/InvisibleSizeGrip.cpp: -------------------------------------------------------------------------------- 1 | #include "widgets/helper/InvisibleSizeGrip.hpp" 2 | 3 | namespace chatterino { 4 | 5 | InvisibleSizeGrip::InvisibleSizeGrip(QWidget *parent) 6 | : QSizeGrip(parent) 7 | { 8 | // required on Windows to prevent this from being ignored when dragging 9 | this->setMouseTracking(true); 10 | } 11 | 12 | void InvisibleSizeGrip::paintEvent(QPaintEvent *event) 13 | { 14 | } 15 | 16 | } // namespace chatterino 17 | -------------------------------------------------------------------------------- /src/widgets/helper/color/ColorItemDelegate.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace chatterino { 6 | 7 | class ColorItemDelegate : public QStyledItemDelegate 8 | { 9 | public: 10 | explicit ColorItemDelegate(QObject *parent = nullptr); 11 | 12 | void paint(QPainter *painter, const QStyleOptionViewItem &option, 13 | const QModelIndex &index) const override; 14 | }; 15 | 16 | } // namespace chatterino 17 | -------------------------------------------------------------------------------- /tests/snapshots/IrcMessageHandler/reward-blocked-user.json: -------------------------------------------------------------------------------- 1 | { 2 | "input": "@tmi-sent-ts=1728127030336;subscriber=1;id=d09d1034-4f73-422c-b239-29d463a47973;room-id=11148817;user-id=12345;display-name=blocked;badges=subscriber/24;badge-info=subscriber/27;color=#FF0000;flags=;user-type=;emotes=25:11-15;custom-reward-id=47cc9d27-f771-47d9-8fb9-893b8524ccb3 :blocked!blocked@blocked.tmi.twitch.tv PRIVMSG #pajlada :test TESTS Kappa", 3 | "output": [ 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /src/providers/twitch/PubSubMessages.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "providers/twitch/pubsubmessages/Base.hpp" // IWYU pragma: export 4 | #include "providers/twitch/pubsubmessages/ChannelPoints.hpp" // IWYU pragma: export 5 | #include "providers/twitch/pubsubmessages/Listen.hpp" // IWYU pragma: export 6 | #include "providers/twitch/pubsubmessages/Message.hpp" // IWYU pragma: export 7 | #include "providers/twitch/pubsubmessages/Unlisten.hpp" // IWYU pragma: export 8 | -------------------------------------------------------------------------------- /resources/buttons/emoteDark.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/twitch-eventsub-ws/ast/lib/format.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import subprocess 3 | 4 | log = logging.getLogger(__name__) 5 | 6 | 7 | def format_code(code: str) -> str: 8 | proc = subprocess.Popen(["clang-format", "-"], stdin=subprocess.PIPE, stdout=subprocess.PIPE) 9 | outs, errs = proc.communicate(input=code.encode(), timeout=2) 10 | if errs is not None: 11 | log.warning(f"Error formatting code: {errs.decode()}") 12 | 13 | return outs.decode() 14 | -------------------------------------------------------------------------------- /lib/twitch-eventsub-ws/src/payloads/channel-ban-v1.cpp: -------------------------------------------------------------------------------- 1 | #include "twitch-eventsub-ws/payloads/channel-ban-v1.hpp" 2 | 3 | namespace chatterino::eventsub::lib::payload::channel_ban::v1 { 4 | 5 | std::chrono::system_clock::duration Event::timeoutDuration() const 6 | { 7 | if (!this->endsAt) 8 | { 9 | return {}; 10 | } 11 | 12 | return *this->endsAt - this->bannedAt; 13 | } 14 | 15 | } // namespace chatterino::eventsub::lib::payload::channel_ban::v1 16 | -------------------------------------------------------------------------------- /resources/buttons/emote.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/controllers/sound/NullBackend.cpp: -------------------------------------------------------------------------------- 1 | #include "controllers/sound/NullBackend.hpp" 2 | 3 | #include "common/QLogging.hpp" 4 | 5 | namespace chatterino { 6 | 7 | NullBackend::NullBackend() 8 | { 9 | qCInfo(chatterinoSound) << "Initializing null sound backend"; 10 | } 11 | 12 | void NullBackend::play(const QUrl &sound) 13 | { 14 | // Do nothing 15 | qCDebug(chatterinoSound) << "null backend asked to play" << sound; 16 | } 17 | 18 | } // namespace chatterino 19 | -------------------------------------------------------------------------------- /lib/twitch-eventsub-ws/include/twitch-eventsub-ws/detail/errors.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "twitch-eventsub-ws/errors.hpp" 4 | 5 | #define EVENTSUB_BAIL_HERE(kind) \ 6 | { \ 7 | static constexpr boost::source_location loc = BOOST_CURRENT_LOCATION; \ 8 | return ::chatterino::eventsub::lib::error::makeCode(kind, &loc); \ 9 | } 10 | -------------------------------------------------------------------------------- /mocks/include/mocks/EmoteController.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "controllers/emotes/EmoteController.hpp" 4 | #include "providers/emoji/Emojis.hpp" 5 | 6 | namespace chatterino::mock { 7 | 8 | class EmoteController : public chatterino::EmoteController 9 | { 10 | public: 11 | EmoteController() 12 | { 13 | this->getEmojis()->load(); 14 | } 15 | 16 | void initialize() override 17 | { 18 | } 19 | }; 20 | 21 | } // namespace chatterino::mock 22 | -------------------------------------------------------------------------------- /resources/buttons/edit.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/util/DisplayBadge.cpp: -------------------------------------------------------------------------------- 1 | #include "util/DisplayBadge.hpp" 2 | 3 | namespace chatterino { 4 | DisplayBadge::DisplayBadge(QString displayName, QString badgeName) 5 | : displayName_(displayName) 6 | , badgeName_(badgeName) 7 | { 8 | } 9 | 10 | QString DisplayBadge::displayName() const 11 | { 12 | return this->displayName_; 13 | } 14 | 15 | QString DisplayBadge::badgeName() const 16 | { 17 | return this->badgeName_; 18 | } 19 | 20 | } // namespace chatterino 21 | -------------------------------------------------------------------------------- /cmake/resources/ResourcesAutogen.cpp.in: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** WARNING! This file is autogenerated by cmake 3 | ** WARNING! All changes made in this file will be lost! 4 | *****************************************************************************/ 5 | #include "ResourcesAutogen.hpp" 6 | 7 | namespace chatterino { 8 | 9 | Resources2::Resources2() 10 | { 11 | @RES_SOURCE_CONTENT@ 12 | } 13 | } // namespace chatterino 14 | -------------------------------------------------------------------------------- /src/widgets/settingspages/KeyboardSettingsPage.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "widgets/settingspages/SettingsPage.hpp" 4 | 5 | namespace chatterino { 6 | 7 | class HotkeyModel; 8 | class EditableModelView; 9 | 10 | class KeyboardSettingsPage : public SettingsPage 11 | { 12 | public: 13 | KeyboardSettingsPage(); 14 | bool filterElements(const QString &query) override; 15 | 16 | private: 17 | EditableModelView *view_; 18 | }; 19 | 20 | } // namespace chatterino 21 | -------------------------------------------------------------------------------- /src/messages/Link.cpp: -------------------------------------------------------------------------------- 1 | #include "messages/Link.hpp" 2 | 3 | namespace chatterino { 4 | 5 | Link::Link() 6 | : type(None) 7 | , value(QString()) 8 | { 9 | } 10 | 11 | Link::Link(Type _type, const QString &_value) 12 | : type(_type) 13 | , value(_value) 14 | { 15 | } 16 | 17 | bool Link::isValid() const 18 | { 19 | return this->type != None; 20 | } 21 | 22 | bool Link::isUrl() const 23 | { 24 | return this->type == Url; 25 | } 26 | 27 | } // namespace chatterino 28 | -------------------------------------------------------------------------------- /src/util/FunctionEventFilter.cpp: -------------------------------------------------------------------------------- 1 | #include "util/FunctionEventFilter.hpp" 2 | 3 | namespace chatterino { 4 | 5 | FunctionEventFilter::FunctionEventFilter( 6 | QObject *parent, std::function function) 7 | : QObject(parent) 8 | , function_(std::move(function)) 9 | { 10 | } 11 | 12 | bool FunctionEventFilter::eventFilter(QObject *watched, QEvent *event) 13 | { 14 | return this->function_(watched, event); 15 | } 16 | 17 | } // namespace chatterino 18 | -------------------------------------------------------------------------------- /src/widgets/ChatterListWidget.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "widgets/BaseWindow.hpp" 4 | 5 | #include 6 | #include 7 | 8 | namespace chatterino { 9 | 10 | class TwitchChannel; 11 | 12 | class ChatterListWidget : public BaseWindow 13 | { 14 | Q_OBJECT 15 | 16 | public: 17 | ChatterListWidget(const TwitchChannel *twitchChannel, QWidget *parent); 18 | 19 | Q_SIGNAL void userClicked(QString userLogin); 20 | }; 21 | 22 | } // namespace chatterino 23 | -------------------------------------------------------------------------------- /lib/twitch-eventsub-ws/include/twitch-eventsub-ws/detail/variant.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace chatterino::eventsub::lib::detail { 6 | 7 | template 8 | consteval std::string_view fieldFor() 9 | { 10 | return T::TAG; 11 | } 12 | 13 | template 14 | consteval std::string_view fieldFor() 15 | requires requires { T::FIELD; } 16 | { 17 | return T::FIELD; 18 | } 19 | 20 | } // namespace chatterino::eventsub::lib::detail 21 | -------------------------------------------------------------------------------- /src/controllers/commands/builtin/twitch/Ban.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace chatterino { 6 | 7 | struct CommandContext; 8 | 9 | } // namespace chatterino 10 | 11 | namespace chatterino::commands { 12 | 13 | /// /ban 14 | QString sendBan(const CommandContext &ctx); 15 | /// /banid 16 | QString sendBanById(const CommandContext &ctx); 17 | 18 | /// /timeout 19 | QString sendTimeout(const CommandContext &ctx); 20 | 21 | } // namespace chatterino::commands 22 | -------------------------------------------------------------------------------- /src/widgets/settingspages/CommandPage.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "widgets/settingspages/SettingsPage.hpp" 4 | 5 | #include 6 | 7 | namespace chatterino { 8 | 9 | class EditableModelView; 10 | 11 | class CommandPage : public SettingsPage 12 | { 13 | public: 14 | CommandPage(); 15 | bool filterElements(const QString &query) override; 16 | 17 | private: 18 | QTimer commandsEditTimer_; 19 | EditableModelView *view; 20 | }; 21 | 22 | } // namespace chatterino 23 | -------------------------------------------------------------------------------- /src/controllers/commands/builtin/twitch/Poll.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class QString; 4 | 5 | namespace chatterino { 6 | 7 | struct CommandContext; 8 | 9 | } // namespace chatterino 10 | 11 | namespace chatterino::commands { 12 | 13 | /// /poll 14 | QString createPoll(const CommandContext &ctx); 15 | 16 | /// /endpoll 17 | QString endPoll(const CommandContext &ctx); 18 | 19 | /// /cancelpoll 20 | QString cancelPoll(const CommandContext &ctx); 21 | 22 | } // namespace chatterino::commands 23 | -------------------------------------------------------------------------------- /src/widgets/dialogs/SelectChannelFiltersDialog.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace chatterino { 6 | 7 | class SelectChannelFiltersDialog : public QDialog 8 | { 9 | public: 10 | SelectChannelFiltersDialog(const QList &previousSelection, 11 | QWidget *parent = nullptr); 12 | 13 | const QList &getSelection() const; 14 | 15 | private: 16 | QList currentSelection_; 17 | }; 18 | 19 | } // namespace chatterino 20 | -------------------------------------------------------------------------------- /src/widgets/dialogs/font/PreviewWidget.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace chatterino { 8 | 9 | class PreviewWidget : public QWidget 10 | { 11 | public: 12 | PreviewWidget(const QFont &startFont, QWidget *parent = nullptr); 13 | 14 | void setFont(const QFont &font); 15 | 16 | void paintEvent(QPaintEvent *event) override; 17 | 18 | private: 19 | QFont font; 20 | }; 21 | 22 | } // namespace chatterino 23 | -------------------------------------------------------------------------------- /cmake/resources/ResourcesAutogen.hpp.in: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** WARNING! This file is autogenerated by cmake 3 | ** WARNING! All changes made in this file will be lost! 4 | *****************************************************************************/ 5 | #include 6 | 7 | namespace chatterino { 8 | 9 | class Resources2 10 | { 11 | public: 12 | Resources2(); 13 | 14 | @RES_HEADER_CONTENT@ 15 | }; 16 | } // namespace chatterino 17 | -------------------------------------------------------------------------------- /resources/settings/notifications.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/common/Modes.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace chatterino { 4 | 5 | class Modes 6 | { 7 | public: 8 | Modes(); 9 | 10 | static const Modes &instance(); 11 | 12 | bool isNightly{}; 13 | bool isPortable{}; 14 | 15 | /// Marked by the line `externally-packaged` 16 | /// 17 | /// The externally packaged mode comes with the following changes: 18 | /// - No shortcuts are created by default 19 | bool isExternallyPackaged{}; 20 | }; 21 | 22 | } // namespace chatterino 23 | -------------------------------------------------------------------------------- /src/util/LoadPixmap.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "common/Aliases.hpp" 3 | 4 | #include 5 | 6 | namespace chatterino { 7 | 8 | /** 9 | * Loads an image from url into a QPixmap. Allows for file:// protocol links. Uses cacheing. 10 | * 11 | * @param callback The callback you will get the pixmap by. It will be invoked concurrently with no guarantees on which thread. 12 | */ 13 | void loadPixmapFromUrl(const Url &url, std::function &&callback); 14 | 15 | } // namespace chatterino 16 | -------------------------------------------------------------------------------- /src/messages/search/SubstringPredicate.cpp: -------------------------------------------------------------------------------- 1 | #include "messages/search/SubstringPredicate.hpp" 2 | 3 | #include "messages/Message.hpp" 4 | 5 | namespace chatterino { 6 | 7 | SubstringPredicate::SubstringPredicate(const QString &search) 8 | : MessagePredicate(false) 9 | , search_(search) 10 | { 11 | } 12 | 13 | bool SubstringPredicate::appliesToImpl(const Message &message) 14 | { 15 | return message.searchText.contains(this->search_, Qt::CaseInsensitive); 16 | } 17 | 18 | } // namespace chatterino 19 | -------------------------------------------------------------------------------- /src/providers/emoji/EmojiStyle.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace chatterino { 6 | 7 | /// The available emoji styles in Chatterino 8 | /// 9 | /// Each enum value has a "bitset value" defined so it can be used in a FlagsEnum to figure out 10 | /// which emojis support which emoji style / set 11 | enum class EmojiStyle : std::uint8_t { 12 | Twitter = 1 << 0, 13 | Facebook = 1 << 1, 14 | Apple = 1 << 2, 15 | Google = 1 << 3, 16 | }; 17 | 18 | } // namespace chatterino 19 | -------------------------------------------------------------------------------- /tests/snapshots/IrcMessageHandler/sub-blocked-user.json: -------------------------------------------------------------------------------- 1 | { 2 | "input": "@badges=subscriber/0,premium/1;color=#0000FF;display-name=blocked;emotes=;id=fe390424-ab89-4c33-bb5a-53c6e5214b9f;login=blocked;mod=0;msg-id=sub;msg-param-months=0;msg-param-sub-plan-name=Dakotaz;msg-param-sub-plan=Prime;room-id=39298218;subscriber=0;system-msg=byebyeheart\\sjust\\ssubscribed\\swith\\sTwitch\\sPrime!;tmi-sent-ts=1528190963670;turbo=0;user-id=12345;user-type= :tmi.twitch.tv USERNOTICE #pajlada", 3 | "output": [ 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /src/common/Env.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | namespace chatterino { 8 | 9 | class Env 10 | { 11 | Env(); 12 | 13 | public: 14 | static const Env &get(); 15 | 16 | const QString recentMessagesApiUrl; 17 | const QString linkResolverUrl; 18 | const QString twitchServerHost; 19 | const uint16_t twitchServerPort; 20 | const bool twitchServerSecure; 21 | const std::optional proxyUrl; 22 | }; 23 | 24 | } // namespace chatterino 25 | -------------------------------------------------------------------------------- /src/controllers/plugins/api/JSONParse.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef CHATTERINO_HAVE_PLUGINS 4 | # include 5 | 6 | namespace chatterino::lua::api { 7 | 8 | /// Parse JSON from a string into a Lua value 9 | /// 10 | /// Exposed in the 'json' package as `parse(string[, options]): any`. 11 | /// It uses the C-Function signature, because we mostly use the raw Lua API in 12 | /// the implementation. 13 | int jsonParse(lua_State *L); 14 | 15 | } // namespace chatterino::lua::api 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /src/widgets/settingspages/AboutPage.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "widgets/settingspages/SettingsPage.hpp" 4 | 5 | class QLabel; 6 | class QFormLayout; 7 | 8 | namespace chatterino { 9 | 10 | class AboutPage : public SettingsPage 11 | { 12 | public: 13 | AboutPage(); 14 | 15 | private: 16 | void addLicense(QFormLayout *form, const QString &name_, 17 | const QString &website, const QString &licenseLink); 18 | 19 | QLabel *logo_{}; 20 | }; 21 | 22 | } // namespace chatterino 23 | -------------------------------------------------------------------------------- /cmake/FindLRUCache.cmake: -------------------------------------------------------------------------------- 1 | include(FindPackageHandleStandardArgs) 2 | 3 | find_path(LRUCache_INCLUDE_DIR lrucache/lrucache.hpp HINTS ${CMAKE_SOURCE_DIR}/lib/lrucache) 4 | 5 | find_package_handle_standard_args(LRUCache DEFAULT_MSG LRUCache_INCLUDE_DIR) 6 | 7 | if (LRUCache_FOUND) 8 | add_library(LRUCache INTERFACE IMPORTED) 9 | set_target_properties(LRUCache PROPERTIES 10 | INTERFACE_INCLUDE_DIRECTORIES "${LRUCache_INCLUDE_DIR}" 11 | ) 12 | endif () 13 | 14 | mark_as_advanced(LRUCache_INCLUDE_DIR) 15 | -------------------------------------------------------------------------------- /resources/settings/ignore.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/controllers/plugins/api/JSONStringify.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef CHATTERINO_HAVE_PLUGINS 4 | # include 5 | 6 | namespace chatterino::lua::api { 7 | 8 | /// Serializes a Lua value to JSON. 9 | /// 10 | /// Exposed in the 'json' package as `stringify(value[, options]): string`. 11 | /// It uses the C-Function signature, because we mostly use the raw Lua API in 12 | /// the implementation. 13 | int jsonStringify(lua_State *L); 14 | 15 | } // namespace chatterino::lua::api 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /src/providers/twitch/EmoteValue.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace chatterino { 6 | 7 | struct EmoteValue { 8 | public: 9 | int getSet() 10 | { 11 | return set_; 12 | } 13 | 14 | int getId() 15 | { 16 | return id_; 17 | } 18 | 19 | const QString &getChannelName() 20 | { 21 | return channelName_; 22 | } 23 | 24 | private: 25 | int set_; 26 | int id_; 27 | QString channelName_; 28 | }; 29 | 30 | } // namespace chatterino 31 | -------------------------------------------------------------------------------- /src/widgets/AccountSwitchWidget.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "pajlada/signals/signalholder.hpp" 4 | 5 | #include 6 | 7 | namespace chatterino { 8 | 9 | class AccountSwitchWidget : public QListWidget 10 | { 11 | Q_OBJECT 12 | 13 | public: 14 | explicit AccountSwitchWidget(QWidget *parent = nullptr); 15 | 16 | void refresh(); 17 | 18 | private: 19 | void refreshSelection(); 20 | 21 | pajlada::Signals::SignalHolder managedConnections_; 22 | }; 23 | 24 | } // namespace chatterino 25 | -------------------------------------------------------------------------------- /src/providers/twitch/TwitchHelpers.cpp: -------------------------------------------------------------------------------- 1 | #include "providers/twitch/TwitchHelpers.hpp" 2 | 3 | #include "common/QLogging.hpp" 4 | 5 | namespace chatterino { 6 | 7 | bool trimChannelName(const QString &channelName, QString &outChannelName) 8 | { 9 | if (channelName.length() < 2) 10 | { 11 | qCDebug(chatterinoTwitch) << "channel name length below 2"; 12 | return false; 13 | } 14 | 15 | outChannelName = channelName.mid(1); 16 | 17 | return true; 18 | } 19 | 20 | } // namespace chatterino 21 | -------------------------------------------------------------------------------- /src/widgets/helper/TrimRegExpValidator.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace chatterino { 7 | 8 | class TrimRegExpValidator : public QRegularExpressionValidator 9 | { 10 | Q_OBJECT 11 | 12 | public: 13 | TrimRegExpValidator(const QRegularExpression &re, 14 | QObject *parent = nullptr); 15 | 16 | QValidator::State validate(QString &input, int &pos) const override; 17 | }; 18 | 19 | } // namespace chatterino 20 | -------------------------------------------------------------------------------- /resources/settings/commands.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/controllers/completion/strategies/ClassicUserStrategy.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "controllers/completion/sources/UserSource.hpp" 4 | #include "controllers/completion/strategies/Strategy.hpp" 5 | 6 | namespace chatterino::completion { 7 | 8 | class ClassicUserStrategy : public Strategy 9 | { 10 | void apply(const std::vector &items, 11 | std::vector &output, 12 | const QString &query) const override; 13 | }; 14 | 15 | } // namespace chatterino::completion 16 | -------------------------------------------------------------------------------- /src/widgets/settingspages/ExternalToolsPage.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "widgets/settingspages/GeneralPageView.hpp" 4 | #include "widgets/settingspages/SettingsPage.hpp" 5 | 6 | namespace chatterino { 7 | 8 | class ExternalToolsPage : public SettingsPage 9 | { 10 | public: 11 | ExternalToolsPage(); 12 | 13 | bool filterElements(const QString &query) override; 14 | 15 | private: 16 | void initLayout(GeneralPageView &layout); 17 | 18 | GeneralPageView *view{}; 19 | }; 20 | 21 | } // namespace chatterino 22 | -------------------------------------------------------------------------------- /lib/twitch-eventsub-ws/ast/lib/jinja_env.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from jinja2 import Environment, FileSystemLoader 4 | from jinja2_workarounds import MultiLineInclude 5 | 6 | from .membertype import MemberType 7 | 8 | template_paths = os.path.join(os.path.dirname(os.path.realpath(__file__)), "templates") 9 | 10 | # print(f"Loading jinja templates from {template_paths}") 11 | 12 | env = Environment( 13 | loader=FileSystemLoader(template_paths), 14 | extensions=[MultiLineInclude], 15 | ) 16 | env.globals["MemberType"] = MemberType 17 | -------------------------------------------------------------------------------- /src/widgets/helper/IconDelegate.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace chatterino { 6 | 7 | /** 8 | * IconDelegate draws the decoration role pixmap scaled down to a square icon 9 | */ 10 | class IconDelegate : public QStyledItemDelegate 11 | { 12 | public: 13 | explicit IconDelegate(QObject *parent = nullptr); 14 | 15 | void paint(QPainter *painter, const QStyleOptionViewItem &option, 16 | const QModelIndex &index) const override; 17 | }; 18 | 19 | } // namespace chatterino 20 | -------------------------------------------------------------------------------- /.github/workflows/post-clang-tidy-review.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Post clang-tidy review comments 3 | 4 | on: 5 | workflow_run: 6 | workflows: ["clang-tidy"] 7 | types: 8 | - completed 9 | 10 | jobs: 11 | post: 12 | runs-on: ubuntu-latest 13 | # Only when a build succeeds 14 | if: ${{ github.event.workflow_run.conclusion == 'success' }} 15 | 16 | steps: 17 | - uses: ZedThree/clang-tidy-review/post@v0.22.2 18 | with: 19 | lgtm_comment_body: "" 20 | num_comments_as_exitcode: false 21 | -------------------------------------------------------------------------------- /src/controllers/commands/CommandContext.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | namespace chatterino { 8 | 9 | class Channel; 10 | using ChannelPtr = std::shared_ptr; 11 | class TwitchChannel; 12 | 13 | struct CommandContext { 14 | QStringList words; 15 | 16 | // Can be null 17 | ChannelPtr channel; 18 | 19 | // Can be null if `channel` is null or if `channel` is not a Twitch channel 20 | TwitchChannel *twitchChannel; 21 | }; 22 | 23 | } // namespace chatterino 24 | -------------------------------------------------------------------------------- /src/controllers/commands/builtin/twitch/Prediction.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class QString; 4 | 5 | namespace chatterino { 6 | 7 | struct CommandContext; 8 | 9 | } // namespace chatterino 10 | 11 | namespace chatterino::commands { 12 | 13 | /// /prediction 14 | QString createPrediction(const CommandContext &ctx); 15 | 16 | /// /lockprediction 17 | QString lockPrediction(const CommandContext &ctx); 18 | 19 | /// /cancelprediction 20 | QString cancelPrediction(const CommandContext &ctx); 21 | 22 | } // namespace chatterino::commands 23 | -------------------------------------------------------------------------------- /cmake/FindMagicEnum.cmake: -------------------------------------------------------------------------------- 1 | include(FindPackageHandleStandardArgs) 2 | 3 | find_path(MagicEnum_INCLUDE_DIR magic_enum/magic_enum.hpp HINTS ${CMAKE_SOURCE_DIR}/lib/magic_enum/include) 4 | 5 | find_package_handle_standard_args(MagicEnum DEFAULT_MSG MagicEnum_INCLUDE_DIR) 6 | 7 | if (MagicEnum_FOUND) 8 | add_library(MagicEnum INTERFACE IMPORTED) 9 | set_target_properties(MagicEnum PROPERTIES 10 | INTERFACE_INCLUDE_DIRECTORIES "${MagicEnum_INCLUDE_DIR}" 11 | ) 12 | endif () 13 | 14 | mark_as_advanced(MagicEnum_INCLUDE_DIR) 15 | -------------------------------------------------------------------------------- /lib/twitch-eventsub-ws/src/payloads/channel-moderate-v2.cpp: -------------------------------------------------------------------------------- 1 | #include "twitch-eventsub-ws/payloads/channel-moderate-v2.hpp" 2 | 3 | namespace chatterino::eventsub::lib::payload::channel_moderate::v2 { 4 | 5 | bool Event::isFromSharedChat() const noexcept 6 | { 7 | return this->sourceBroadcasterUserID && this->sourceBroadcasterUserLogin && 8 | this->sourceBroadcasterUserName && 9 | *this->sourceBroadcasterUserID != this->broadcasterUserID; 10 | } 11 | } // namespace chatterino::eventsub::lib::payload::channel_moderate::v2 12 | -------------------------------------------------------------------------------- /lib/twitch-eventsub-ws/tests/resources/messages/session-welcome.json: -------------------------------------------------------------------------------- 1 | { 2 | "metadata": { 3 | "message_id": "faf21c17-82d3-4479-8daa-295f16b706f0", 4 | "message_type": "session_welcome", 5 | "message_timestamp": "2023-07-19T14:56:51.634234626Z" 6 | }, 7 | "payload": { 8 | "session": { 9 | "id": "AQoQILE98gtqShGmLD7AM6yJThAB", 10 | "status": "connected", 11 | "connected_at": "2023-07-19T14:56:51.616329898Z", 12 | "keepalive_timeout_seconds": 10, 13 | "reconnect_url": null 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/providers/twitch/pubsubmessages/Unlisten.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | namespace chatterino { 8 | 9 | // PubSubUnlistenMessage is an outgoing UNLISTEN message that is sent for the client to unsubscribe from a list of topics 10 | struct PubSubUnlistenMessage { 11 | const std::vector topics; 12 | 13 | const QString nonce; 14 | 15 | PubSubUnlistenMessage(std::vector _topics); 16 | 17 | QByteArray toJson() const; 18 | }; 19 | 20 | } // namespace chatterino 21 | -------------------------------------------------------------------------------- /src/singletons/Resources.cpp: -------------------------------------------------------------------------------- 1 | #include "singletons/Resources.hpp" 2 | 3 | #include "debug/AssertInGuiThread.hpp" 4 | 5 | namespace { 6 | 7 | using namespace chatterino; 8 | 9 | static Resources2 *resources = nullptr; 10 | 11 | } // namespace 12 | 13 | namespace chatterino { 14 | 15 | Resources2 &getResources() 16 | { 17 | assert(resources); 18 | 19 | return *resources; 20 | } 21 | 22 | void initResources() 23 | { 24 | assertInGuiThread(); 25 | 26 | resources = new Resources2; 27 | } 28 | 29 | } // namespace chatterino 30 | -------------------------------------------------------------------------------- /src/util/AttachToConsole.cpp: -------------------------------------------------------------------------------- 1 | #include "util/AttachToConsole.hpp" 2 | 3 | #ifdef USEWINSDK 4 | # include 5 | 6 | # include 7 | # include 8 | #endif 9 | 10 | namespace chatterino { 11 | 12 | void attachToConsole() 13 | { 14 | #ifdef USEWINSDK 15 | if (AttachConsole(ATTACH_PARENT_PROCESS)) 16 | { 17 | std::ignore = freopen_s(nullptr, "CONOUT$", "w", stdout); 18 | std::ignore = freopen_s(nullptr, "CONOUT$", "w", stderr); 19 | } 20 | #endif 21 | } 22 | 23 | } // namespace chatterino 24 | -------------------------------------------------------------------------------- /src/util/RemoveScrollAreaBackground.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace chatterino { 6 | 7 | static void removeScrollAreaBackground(QScrollArea *scrollArea, 8 | QWidget *childWidget) 9 | { 10 | scrollArea->setWidgetResizable(true); 11 | scrollArea->setFrameStyle(0); 12 | 13 | QPalette p; 14 | p.setColor(QPalette::Window, QColor(0, 0, 0, 0)); 15 | scrollArea->setPalette(p); 16 | childWidget->setPalette(p); 17 | } 18 | 19 | } // namespace chatterino 20 | -------------------------------------------------------------------------------- /.github/workflows/winget.yml: -------------------------------------------------------------------------------- 1 | name: Publish to WinGet 2 | on: 3 | release: 4 | types: [released] 5 | jobs: 6 | publish: 7 | runs-on: windows-latest 8 | if: ${{ startsWith(github.event.release.tag_name, 'v') }} && github.repository_owner == 'Chatterino' 9 | steps: 10 | - uses: vedantmgoyal9/winget-releaser@main 11 | with: 12 | identifier: ChatterinoTeam.Chatterino 13 | installers-regex: ^Chatterino.Installer.exe$ 14 | token: ${{ secrets.WINGET_TOKEN }} 15 | fork-user: chatterino-ci 16 | -------------------------------------------------------------------------------- /src/common/enums/MessageOverflow.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace chatterino { 4 | 5 | // MessageOverflow is used for controlling how to guide the user into not 6 | // sending a message that will be discarded by Twitch 7 | enum MessageOverflow { 8 | // Allow overflowing characters to be inserted into the input box, but highlight them in red 9 | Highlight, 10 | 11 | // Prevent more characters from being inserted into the input box 12 | Prevent, 13 | 14 | // Do nothing 15 | Allow, 16 | }; 17 | 18 | } // namespace chatterino 19 | -------------------------------------------------------------------------------- /lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/channel-chat-user-message-update-v1.inc: -------------------------------------------------------------------------------- 1 | boost::json::result_for::type tag_invoke( 2 | boost::json::try_value_to_tag, const boost::json::value &jvRoot); 3 | 4 | boost::json::result_for::type tag_invoke( 5 | boost::json::try_value_to_tag, const boost::json::value &jvRoot); 6 | 7 | boost::json::result_for::type tag_invoke( 8 | boost::json::try_value_to_tag, const boost::json::value &jvRoot); 9 | -------------------------------------------------------------------------------- /mocks/include/mocks/DisabledStreamerMode.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "singletons/StreamerMode.hpp" 4 | 5 | class DisabledStreamerMode : public chatterino::IStreamerMode 6 | { 7 | public: 8 | bool isEnabled() const override 9 | { 10 | return false; 11 | } 12 | 13 | bool shouldHideModActions() const override 14 | { 15 | return false; 16 | } 17 | 18 | bool shouldHideRestrictedUsers() const override 19 | { 20 | return false; 21 | } 22 | 23 | void start() override 24 | { 25 | } 26 | }; 27 | -------------------------------------------------------------------------------- /scripts/get-tlds-update.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | #Download the official list of active TLDs from IANA 4 | #Remove the first line that contains data not needed. 5 | #Put everything that can be into lowercase. 6 | #Output the result to a file. 7 | curl -s 'https://data.iana.org/TLD/tlds-alpha-by-domain.txt' | sed -e '1d' -e 's/\(.*\)/\L\1/' > tlds.txt 8 | 9 | #Get the TLDs in punycode format. 10 | #Convert the punycode to Unicode. 11 | #Append the results to the current file. 12 | sed -n -e '/^xn--/p' tlds.txt | idn2 -d >> tlds.txt 13 | mv tlds.txt ../resources/tlds.txt -------------------------------------------------------------------------------- /lib/twitch-eventsub-ws/ast/lib/tests/resources/test.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | struct Foo { 5 | struct InnerFoo { 6 | int asd; 7 | }; 8 | 9 | int test; 10 | }; 11 | 12 | struct S { 13 | Foo a; 14 | std::vector as; 15 | /** 16 | * 4 17 | * 5 18 | * json_rename=vec_of_ints 19 | **/ 20 | std::vector vecOfInts; 21 | std::optional ab; 22 | /** 23 | * json_dont_fail_on_deserialization=True 24 | **/ 25 | std::optional ac; 26 | int b; 27 | }; 28 | -------------------------------------------------------------------------------- /lib/twitch-eventsub-ws/include/twitch-eventsub-ws/chrono.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | namespace chatterino::eventsub::lib { 8 | 9 | struct AsISO8601 { 10 | }; 11 | 12 | boost::json::result_for::type 14 | tag_invoke( 15 | boost::json::try_value_to_tag, 16 | const boost::json::value &jvRoot, const AsISO8601 &); 17 | 18 | } // namespace chatterino::eventsub::lib 19 | -------------------------------------------------------------------------------- /src/widgets/helper/TrimRegExpValidator.cpp: -------------------------------------------------------------------------------- 1 | #include "widgets/helper/TrimRegExpValidator.hpp" 2 | 3 | namespace chatterino { 4 | 5 | TrimRegExpValidator::TrimRegExpValidator(const QRegularExpression &re, 6 | QObject *parent) 7 | : QRegularExpressionValidator(re, parent) 8 | { 9 | } 10 | 11 | QValidator::State TrimRegExpValidator::validate(QString &input, int &pos) const 12 | { 13 | input = input.trimmed(); 14 | return QRegularExpressionValidator::validate(input, pos); 15 | } 16 | 17 | } // namespace chatterino 18 | -------------------------------------------------------------------------------- /src/controllers/commands/builtin/twitch/Block.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class QString; 4 | 5 | namespace chatterino { 6 | 7 | struct CommandContext; 8 | 9 | } // namespace chatterino 10 | 11 | namespace chatterino::commands { 12 | 13 | /// /block 14 | QString blockUser(const CommandContext &ctx); 15 | 16 | /// /ignore 17 | QString ignoreUser(const CommandContext &ctx); 18 | 19 | /// /unblock 20 | QString unblockUser(const CommandContext &ctx); 21 | 22 | /// /unignore 23 | QString unignoreUser(const CommandContext &ctx); 24 | 25 | } // namespace chatterino::commands 26 | -------------------------------------------------------------------------------- /src/util/Clipboard.cpp: -------------------------------------------------------------------------------- 1 | #include "util/Clipboard.hpp" 2 | 3 | #include 4 | #include 5 | 6 | namespace chatterino { 7 | 8 | void crossPlatformCopy(const QString &text) 9 | { 10 | auto *clipboard = QApplication::clipboard(); 11 | 12 | clipboard->setText(text); 13 | 14 | if (clipboard->supportsSelection()) 15 | { 16 | clipboard->setText(text, QClipboard::Selection); 17 | } 18 | } 19 | 20 | QString getClipboardText() 21 | { 22 | return QApplication::clipboard()->text(); 23 | } 24 | 25 | } // namespace chatterino 26 | -------------------------------------------------------------------------------- /cmake/FindBoostCertify.cmake: -------------------------------------------------------------------------------- 1 | include(FindPackageHandleStandardArgs) 2 | 3 | find_path(BoostCertify_INCLUDE_DIR boost/certify/https_verification.hpp HINTS ${CMAKE_SOURCE_DIR}/lib/certify/include) 4 | 5 | find_package_handle_standard_args(BoostCertify DEFAULT_MSG BoostCertify_INCLUDE_DIR) 6 | 7 | if (BoostCertify_FOUND) 8 | add_library(BoostCertify INTERFACE IMPORTED) 9 | set_target_properties(BoostCertify PROPERTIES 10 | INTERFACE_INCLUDE_DIRECTORIES "${BoostCertify_INCLUDE_DIR}" 11 | ) 12 | endif () 13 | 14 | mark_as_advanced(BoostCertify_INCLUDE_DIR) 15 | -------------------------------------------------------------------------------- /src/debug/Benchmark.cpp: -------------------------------------------------------------------------------- 1 | #include "debug/Benchmark.hpp" 2 | 3 | #include "common/QLogging.hpp" 4 | 5 | namespace chatterino { 6 | 7 | BenchmarkGuard::BenchmarkGuard(const QString &_name) 8 | : name_(_name) 9 | { 10 | timer_.start(); 11 | } 12 | 13 | BenchmarkGuard::~BenchmarkGuard() 14 | { 15 | qCDebug(chatterinoBenchmark) 16 | << this->name_ << float(timer_.nsecsElapsed()) / 1000000.0f << "ms"; 17 | } 18 | 19 | qreal BenchmarkGuard::getElapsedMs() 20 | { 21 | return qreal(timer_.nsecsElapsed()) / 1000000.0; 22 | } 23 | 24 | } // namespace chatterino 25 | -------------------------------------------------------------------------------- /src/widgets/dialogs/ColorPickerDialog.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "widgets/BasePopup.hpp" 4 | 5 | namespace chatterino { 6 | 7 | class ColorPickerDialog : public BasePopup 8 | { 9 | Q_OBJECT 10 | 11 | public: 12 | ColorPickerDialog(QColor color, QWidget *parent); 13 | 14 | QColor color() const; 15 | 16 | Q_SIGNALS: 17 | void colorChanged(QColor color); 18 | void colorConfirmed(QColor color); 19 | 20 | public Q_SLOTS: 21 | void setColor(const QColor &color); 22 | 23 | private: 24 | QColor color_; 25 | }; 26 | 27 | } // namespace chatterino 28 | -------------------------------------------------------------------------------- /cmake/FindWebsocketpp.cmake: -------------------------------------------------------------------------------- 1 | include(FindPackageHandleStandardArgs) 2 | 3 | find_path(Websocketpp_INCLUDE_DIR websocketpp/version.hpp HINTS ${CMAKE_SOURCE_DIR}/lib/websocketpp) 4 | 5 | find_package_handle_standard_args(Websocketpp DEFAULT_MSG Websocketpp_INCLUDE_DIR) 6 | 7 | if (Websocketpp_FOUND) 8 | add_library(websocketpp::websocketpp INTERFACE IMPORTED) 9 | set_target_properties(websocketpp::websocketpp PROPERTIES 10 | INTERFACE_INCLUDE_DIRECTORIES "${Websocketpp_INCLUDE_DIR}" 11 | ) 12 | endif () 13 | 14 | mark_as_advanced(Websocketpp_INCLUDE_DIR) 15 | -------------------------------------------------------------------------------- /resources/raw/buttons/replyDark.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/providers/pronouns/UserPronouns.cpp: -------------------------------------------------------------------------------- 1 | #include "providers/pronouns/UserPronouns.hpp" 2 | 3 | #include 4 | 5 | #include 6 | 7 | namespace chatterino::pronouns { 8 | 9 | UserPronouns::UserPronouns(QString pronouns) 10 | : representation{!pronouns.isEmpty() ? std::move(pronouns) : QString()} 11 | { 12 | } 13 | 14 | bool UserPronouns::isUnspecified() const 15 | { 16 | return this->representation.isEmpty(); 17 | } 18 | 19 | UserPronouns::operator bool() const 20 | { 21 | return !isUnspecified(); 22 | } 23 | 24 | } // namespace chatterino::pronouns 25 | -------------------------------------------------------------------------------- /src/util/WindowsHelper.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef USEWINSDK 4 | 5 | # include 6 | # include 7 | 8 | # include 9 | 10 | namespace chatterino { 11 | 12 | enum class AssociationQueryType { Protocol, FileExtension }; 13 | 14 | std::optional getWindowDpi(HWND hwnd); 15 | void flushClipboard(); 16 | 17 | bool isRegisteredForStartup(); 18 | void setRegisteredForStartup(bool isRegistered); 19 | 20 | QString getAssociatedExecutable(AssociationQueryType queryType, LPCWSTR query); 21 | 22 | } // namespace chatterino 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/suspicious-users.inc: -------------------------------------------------------------------------------- 1 | boost::json::result_for::type tag_invoke( 2 | boost::json::try_value_to_tag, const boost::json::value &jvRoot); 3 | 4 | boost::json::result_for::type tag_invoke( 5 | boost::json::try_value_to_tag, const boost::json::value &jvRoot); 6 | 7 | boost::json::result_for::type 8 | tag_invoke(boost::json::try_value_to_tag, 9 | const boost::json::value &jvRoot); 10 | -------------------------------------------------------------------------------- /scripts/check-format.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -eu 4 | 5 | fail="0" 6 | 7 | clang-format --version 8 | 9 | while read -r file; do 10 | if ! diff -u <(cat "$file") <(clang-format "$file"); then 11 | echo "$file differs!!!!!!!" 12 | fail="1" 13 | fi 14 | done < <(find src/ tests/src benchmarks/src mocks/include -type f \( -iname "*.hpp" -o -iname "*.cpp" \)) 15 | 16 | if [ "$fail" = "1" ]; then 17 | echo "At least one file is poorly formatted - check the output above" 18 | exit 1 19 | fi 20 | 21 | echo "Everything seems to be formatted properly! Good job" 22 | -------------------------------------------------------------------------------- /src/widgets/listview/GenericListItem.cpp: -------------------------------------------------------------------------------- 1 | #include "widgets/listview/GenericListItem.hpp" 2 | 3 | namespace chatterino { 4 | 5 | const QSize GenericListItem::ICON_SIZE(32, 32); 6 | 7 | GenericListItem *GenericListItem::fromVariant(const QVariant &variant) 8 | { 9 | // See https://stackoverflow.com/a/44503822 . 10 | return static_cast(variant.value()); 11 | } 12 | 13 | GenericListItem::GenericListItem() 14 | { 15 | } 16 | 17 | GenericListItem::GenericListItem(const QIcon &icon) 18 | : icon_(icon) 19 | { 20 | } 21 | 22 | } // namespace chatterino 23 | -------------------------------------------------------------------------------- /.github/workflows/changelog-check.yml: -------------------------------------------------------------------------------- 1 | name: Changelog Check 2 | 3 | on: 4 | pull_request: 5 | branches: [dankerino] 6 | types: [opened, synchronize, reopened, ready_for_review, labeled, unlabeled] 7 | 8 | jobs: 9 | check-changelog: 10 | runs-on: ubuntu-latest 11 | steps: 12 | # Gives an error if there's no change in the changelog (except using label) 13 | - name: Changelog check 14 | uses: dangoslen/changelog-enforcer@v3 15 | with: 16 | changeLogPath: "CHANGELOG.md" 17 | skipLabels: "no changelog entry needed, ci, submodules" 18 | -------------------------------------------------------------------------------- /lib/twitch-eventsub-ws/ast/get-builtin-include-dirs.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import logging 4 | 5 | from lib import get_clang_builtin_include_dirs, init_logging 6 | 7 | log = logging.getLogger(__name__) 8 | 9 | 10 | def main(): 11 | init_logging() 12 | 13 | quote_includes, angle_includes = get_clang_builtin_include_dirs() 14 | 15 | print("Quote includes:") 16 | print("\n".join(quote_includes)) 17 | print() 18 | 19 | print("Angle includes:") 20 | print("\n".join(angle_includes)) 21 | print() 22 | 23 | 24 | if __name__ == "__main__": 25 | main() 26 | -------------------------------------------------------------------------------- /resources/settings/plugins.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/controllers/accounts/Account.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common/ProviderId.hpp" 4 | 5 | #include 6 | 7 | namespace chatterino { 8 | 9 | class Account 10 | { 11 | public: 12 | Account(ProviderId providerId); 13 | virtual ~Account() = default; 14 | 15 | virtual QString toString() const = 0; 16 | const QString &getCategory() const; 17 | ProviderId getProviderId() const; 18 | 19 | bool operator<(const Account &other) const; 20 | 21 | private: 22 | ProviderId providerId_; 23 | QString category_; 24 | }; 25 | 26 | } // namespace chatterino 27 | -------------------------------------------------------------------------------- /src/util/FunctionEventFilter.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | namespace chatterino { 9 | 10 | class FunctionEventFilter : public QObject 11 | { 12 | Q_OBJECT 13 | 14 | public: 15 | FunctionEventFilter(QObject *parent, 16 | std::function function); 17 | 18 | protected: 19 | bool eventFilter(QObject *watched, QEvent *event) override; 20 | 21 | private: 22 | std::function function_; 23 | }; 24 | 25 | } // namespace chatterino 26 | -------------------------------------------------------------------------------- /src/util/WeakPtrHelpers.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace chatterino { 6 | 7 | /// Check if the owner of two weak pointers is equal. 8 | /// 9 | /// Like `std::weak_ptr::owner_before`, this compares the control blocks, 10 | /// not the objects themselves. 11 | /// Equivalent to the C++ 26 `std::weak_ptr::owner_equal`. 12 | template 13 | bool weakOwnerEquals(const std::weak_ptr &a, 14 | const std::weak_ptr &b) noexcept 15 | { 16 | return !a.owner_before(b) && !b.owner_before(a); 17 | } 18 | 19 | } // namespace chatterino 20 | -------------------------------------------------------------------------------- /src/util/XDGDirectory.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace chatterino { 6 | 7 | #if defined(Q_OS_UNIX) and !defined(Q_OS_DARWIN) 8 | 9 | enum class XDGDirectoryType { 10 | Config, 11 | Data, 12 | }; 13 | 14 | /// getXDGDirectories returns a list of directories given a directory type 15 | /// 16 | /// This will attempt to read the relevant environment variable (e.g. XDG_CONFIG_HOME and XDG_CONFIG_DIRS) and merge them, with sane defaults 17 | QStringList getXDGDirectories(XDGDirectoryType directory); 18 | 19 | #endif 20 | 21 | } // namespace chatterino 22 | -------------------------------------------------------------------------------- /src/widgets/splits/DraggedSplit.cpp: -------------------------------------------------------------------------------- 1 | #include "widgets/splits/DraggedSplit.hpp" 2 | 3 | #include 4 | 5 | namespace chatterino { 6 | 7 | static bool currentlyDraggingSplit = false; 8 | 9 | bool isDraggingSplit() 10 | { 11 | return currentlyDraggingSplit; 12 | } 13 | 14 | void startDraggingSplit() 15 | { 16 | assert(currentlyDraggingSplit == false); 17 | 18 | currentlyDraggingSplit = true; 19 | } 20 | 21 | void stopDraggingSplit() 22 | { 23 | assert(currentlyDraggingSplit == true); 24 | 25 | currentlyDraggingSplit = false; 26 | } 27 | 28 | } // namespace chatterino 29 | -------------------------------------------------------------------------------- /src/controllers/hotkeys/HotkeyCategory.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace chatterino { 6 | 7 | // HotkeyCategory describes where the hotkeys action takes place. 8 | // Each HotkeyCategory represents a widget that has customizable hotkeys. This 9 | // is needed because more than one widget can have the same or similar action. 10 | enum class HotkeyCategory { 11 | PopupWindow, 12 | Split, 13 | SplitInput, 14 | Window, 15 | }; 16 | 17 | struct HotkeyCategoryData { 18 | QString name; 19 | QString displayName; 20 | }; 21 | 22 | } // namespace chatterino 23 | -------------------------------------------------------------------------------- /src/widgets/buttons/InitUpdateButton.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace pajlada::Signals { 6 | class SignalHolder; 7 | } // namespace pajlada::Signals 8 | 9 | namespace chatterino { 10 | 11 | class PixmapButton; 12 | 13 | /// Initializes the update button 14 | /// 15 | /// The `relayout` function gets called whenever the button visibility changes 16 | void initUpdateButton(PixmapButton &button, 17 | const std::function &relayout, 18 | pajlada::Signals::SignalHolder &signalHolder); 19 | 20 | } // namespace chatterino 21 | -------------------------------------------------------------------------------- /src/widgets/helper/RegExpItemDelegate.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace chatterino { 7 | 8 | class RegExpItemDelegate : public QStyledItemDelegate 9 | { 10 | Q_OBJECT 11 | 12 | public: 13 | RegExpItemDelegate(QObject *parent, QRegularExpression regexp); 14 | 15 | QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, 16 | const QModelIndex &index) const override; 17 | 18 | private: 19 | const QRegularExpression regexp_; 20 | }; 21 | 22 | } // namespace chatterino 23 | -------------------------------------------------------------------------------- /resources/settings/filters.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /scripts/get-vcpkg-package-versions.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | dependencies="$(jq -r -c '.dependencies[] | if type == "string" then . else .name end' vcpkg.json)" 4 | dependencies+=" openssl" 5 | baseline="$(jq -r -c '."builtin-baseline"' vcpkg.json)" 6 | 7 | for dependency_name in $dependencies; do 8 | dependency_url="https://raw.githubusercontent.com/microsoft/vcpkg/${baseline}/ports/${dependency_name}/vcpkg.json" 9 | dependency_version="$(curl -s "$dependency_url" | jq -rc '.version')" 10 | echo "Dependency $dependency_name is at version '$dependency_version' in baseline $baseline" 11 | done 12 | -------------------------------------------------------------------------------- /tests/snapshots/ImageUploader/Export/i-nuuls.json: -------------------------------------------------------------------------------- 1 | { 2 | "input": { 3 | "deletionLink": "", 4 | "formField": "attachment", 5 | "headers": "", 6 | "link": "", 7 | "url": "https://i.nuuls.com/upload" 8 | }, 9 | "output": { 10 | "Body": "MultipartFormData", 11 | "DeletionURL": "", 12 | "FileFormName": "attachment", 13 | "Name": "Chatterino Image Uploader Settings", 14 | "RequestMethod": "POST", 15 | "RequestURL": "https://i.nuuls.com/upload", 16 | "URL": "", 17 | "Version": "1.0.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /tests/snapshots/ImageUploader/Import/i-nuuls.json: -------------------------------------------------------------------------------- 1 | { 2 | "input": { 3 | "Body": "MultipartFormData", 4 | "DeletionURL": "", 5 | "FileFormName": "attachment", 6 | "Name": "Chatterino Image Uploader Settings", 7 | "RequestMethod": "POST", 8 | "RequestURL": "https://i.nuuls.com/upload", 9 | "URL": "", 10 | "Version": "1.0.0" 11 | }, 12 | "output": { 13 | "deletionLink": "", 14 | "formField": "attachment", 15 | "headers": "", 16 | "link": "", 17 | "url": "https://i.nuuls.com/upload" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/twitch-eventsub-ws/include/twitch-eventsub-ws/logger.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace chatterino::eventsub::lib { 6 | 7 | class Logger 8 | { 9 | public: 10 | virtual ~Logger() = default; 11 | virtual void debug(std::string_view msg) = 0; 12 | virtual void warn(std::string_view msg) = 0; 13 | }; 14 | 15 | class NullLogger : public Logger 16 | { 17 | public: 18 | void debug(std::string_view msg) override 19 | { 20 | } 21 | 22 | void warn(std::string_view msg) override 23 | { 24 | } 25 | }; 26 | 27 | } // namespace chatterino::eventsub::lib 28 | -------------------------------------------------------------------------------- /src/providers/twitch/pubsubmessages/ChannelPoints.cpp: -------------------------------------------------------------------------------- 1 | #include "providers/twitch/pubsubmessages/ChannelPoints.hpp" 2 | 3 | #include "util/QMagicEnum.hpp" 4 | 5 | namespace chatterino { 6 | 7 | PubSubCommunityPointsChannelV1Message::PubSubCommunityPointsChannelV1Message( 8 | const QJsonObject &root) 9 | : typeString(root.value("type").toString()) 10 | , data(root.value("data").toObject()) 11 | { 12 | auto oType = qmagicenum::enumCast(this->typeString); 13 | if (oType.has_value()) 14 | { 15 | this->type = oType.value(); 16 | } 17 | } 18 | 19 | } // namespace chatterino 20 | -------------------------------------------------------------------------------- /src/providers/twitch/pubsubmessages/Listen.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | namespace chatterino { 8 | 9 | // PubSubListenMessage is an outgoing LISTEN message that is sent for the client to subscribe to a list of topics 10 | struct PubSubListenMessage { 11 | const std::vector topics; 12 | 13 | const QString nonce; 14 | 15 | QString token; 16 | 17 | PubSubListenMessage(std::vector _topics); 18 | 19 | void setToken(const QString &_token); 20 | 21 | QByteArray toJson() const; 22 | }; 23 | 24 | } // namespace chatterino 25 | -------------------------------------------------------------------------------- /src/providers/bttv/BttvBadges.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "util/BadgeRegistry.hpp" 4 | 5 | #include 6 | 7 | #include 8 | 9 | namespace chatterino { 10 | 11 | struct Emote; 12 | using EmotePtr = std::shared_ptr; 13 | 14 | class BttvBadges : public BadgeRegistry 15 | { 16 | public: 17 | BttvBadges() = default; 18 | 19 | protected: 20 | QString idForBadge(const QJsonObject &badgeJson) const override; 21 | EmotePtr createBadge(const QString &id, 22 | const QJsonObject &badgeJson) const override; 23 | }; 24 | 25 | } // namespace chatterino 26 | -------------------------------------------------------------------------------- /lib/twitch-eventsub-ws/ast/lib/templates/field-basic.tmpl: -------------------------------------------------------------------------------- 1 | const auto *jv{{field.name}} = root.if_contains("{{field.json_name}}"); 2 | if (jv{{field.name}} == nullptr) 3 | { 4 | {% include 'error-missing-field.tmpl' indent content %} 5 | } 6 | {% if field.tag %} 7 | auto {{field.name}} = boost::json::try_value_to<{{field.type_name}}>(*jv{{field.name}}, {{field.tag}}()); 8 | {% else %} 9 | auto {{field.name}} = boost::json::try_value_to<{{field.type_name}}>(*jv{{field.name}}); 10 | {% endif %} 11 | if ({{field.name}}.has_error()) 12 | { 13 | {% include 'error-failed-to-deserialize.tmpl' indent content %} 14 | } 15 | 16 | -------------------------------------------------------------------------------- /mocks/include/mocks/TwitchUsers.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "providers/twitch/TwitchUser.hpp" 4 | #include "providers/twitch/TwitchUsers.hpp" 5 | 6 | namespace chatterino::mock { 7 | 8 | class TwitchUsers : public ITwitchUsers 9 | { 10 | public: 11 | TwitchUsers() = default; 12 | 13 | std::shared_ptr resolveID(const UserId &id) 14 | { 15 | TwitchUser u = { 16 | .id = id.string, 17 | .name = {}, 18 | .displayName = {}, 19 | }; 20 | return std::make_shared(u); 21 | } 22 | }; 23 | 24 | } // namespace chatterino::mock 25 | -------------------------------------------------------------------------------- /src/providers/seventv/eventapi/Message.cpp: -------------------------------------------------------------------------------- 1 | #include "providers/seventv/eventapi/Message.hpp" 2 | 3 | namespace chatterino::seventv::eventapi { 4 | 5 | Message::Message(QJsonObject _json) 6 | : data(_json["d"].toObject()) 7 | , op(Opcode(_json["op"].toInt())) 8 | { 9 | } 10 | 11 | std::optional parseBaseMessage(const QByteArray &blob) 12 | { 13 | QJsonDocument jsonDoc(QJsonDocument::fromJson(blob)); 14 | 15 | if (jsonDoc.isNull()) 16 | { 17 | return std::nullopt; 18 | } 19 | 20 | return Message(jsonDoc.object()); 21 | } 22 | 23 | } // namespace chatterino::seventv::eventapi 24 | -------------------------------------------------------------------------------- /lib/twitch-eventsub-ws/ast/check-clang.py: -------------------------------------------------------------------------------- 1 | import clang.cindex 2 | import sys 3 | from pathlib import Path 4 | 5 | from lib import init_clang_cindex, parse 6 | 7 | init_clang_cindex() 8 | 9 | assert len(sys.argv) == 2 10 | additional_includes = sys.argv[1].split(";") 11 | print("includes:", additional_includes) 12 | 13 | tu = parse(Path(__file__).parent / "check-clang.hpp", additional_includes) 14 | 15 | if len(tu.diagnostics) > 0: 16 | for diag in tu.diagnostics: 17 | print(diag.location) 18 | print(diag.spelling) 19 | print(diag.option) 20 | assert False, "TU had warnings/errors when parsing" 21 | -------------------------------------------------------------------------------- /src/providers/seventv/SeventvBadges.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "util/BadgeRegistry.hpp" 4 | 5 | #include 6 | 7 | #include 8 | 9 | namespace chatterino { 10 | 11 | struct Emote; 12 | using EmotePtr = std::shared_ptr; 13 | 14 | class SeventvBadges : public BadgeRegistry 15 | { 16 | public: 17 | SeventvBadges() = default; 18 | 19 | protected: 20 | QString idForBadge(const QJsonObject &badgeJson) const override; 21 | EmotePtr createBadge(const QString &id, 22 | const QJsonObject &badgeJson) const override; 23 | }; 24 | 25 | } // namespace chatterino 26 | -------------------------------------------------------------------------------- /src/util/Expected.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | class QString; 8 | 9 | namespace chatterino { 10 | 11 | template 12 | using Expected = nonstd::expected_lite::expected; 13 | 14 | template 15 | using ExpectedStr = Expected; 16 | 17 | // convenience function from nonstd/expected.hpp 18 | template 19 | constexpr nonstd::unexpected> makeUnexpected(E &&value) 20 | { 21 | return nonstd::unexpected>(std::forward(value)); 22 | } 23 | 24 | } // namespace chatterino 25 | -------------------------------------------------------------------------------- /resources/raw/buttons/replyThreadDark.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/controllers/completion/strategies/CommandStrategy.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "controllers/completion/sources/CommandSource.hpp" 4 | #include "controllers/completion/strategies/Strategy.hpp" 5 | 6 | namespace chatterino::completion { 7 | 8 | class CommandStrategy : public Strategy 9 | { 10 | public: 11 | CommandStrategy(bool startsWithOnly); 12 | 13 | void apply(const std::vector &items, 14 | std::vector &output, 15 | const QString &query) const override; 16 | 17 | private: 18 | bool startsWithOnly_; 19 | }; 20 | 21 | } // namespace chatterino::completion 22 | -------------------------------------------------------------------------------- /src/util/UnixSignalHandler.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #if defined(Q_OS_UNIX) 6 | # include 7 | # include 8 | 9 | # include 10 | 11 | namespace chatterino { 12 | 13 | class UnixSignalHandler : public QObject 14 | { 15 | Q_OBJECT 16 | 17 | public: 18 | UnixSignalHandler(int signal); 19 | 20 | static void fired(int signal); 21 | 22 | Q_SIGNALS: 23 | void signalFired(); 24 | 25 | private: 26 | void handleSignal(); 27 | 28 | std::array fd{}; 29 | QSocketNotifier *socketNotifier; 30 | }; 31 | 32 | } // namespace chatterino 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /src/widgets/helper/LiveIndicator.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "widgets/BaseWidget.hpp" 4 | 5 | namespace chatterino { 6 | 7 | class LiveIndicator : public BaseWidget 8 | { 9 | public: 10 | LiveIndicator(QWidget *parent = nullptr); 11 | 12 | void setViewers(int viewers); 13 | 14 | protected: 15 | void scaleChangedEvent(float newScale) override; 16 | void paintEvent(QPaintEvent *event) override; 17 | void enterEvent(QEnterEvent *event) override; 18 | void leaveEvent(QEvent *event) override; 19 | 20 | private: 21 | void updateScale(); 22 | 23 | bool hovered = false; 24 | }; 25 | 26 | } // namespace chatterino 27 | -------------------------------------------------------------------------------- /src/widgets/listview/GenericItemDelegate.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace chatterino { 6 | 7 | class SwitcherItemDelegate : public QStyledItemDelegate 8 | { 9 | Q_OBJECT 10 | 11 | public: 12 | SwitcherItemDelegate(QObject *parent = nullptr); 13 | ~SwitcherItemDelegate() override; 14 | 15 | void paint(QPainter *painter, const QStyleOptionViewItem &option, 16 | const QModelIndex &index) const override; 17 | 18 | QSize sizeHint(const QStyleOptionViewItem &option, 19 | const QModelIndex &index) const override; 20 | }; 21 | 22 | } // namespace chatterino 23 | -------------------------------------------------------------------------------- /src/debug/Benchmark.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace chatterino { 7 | 8 | class BenchmarkGuard 9 | { 10 | public: 11 | BenchmarkGuard(const QString &_name); 12 | ~BenchmarkGuard(); 13 | 14 | BenchmarkGuard(const BenchmarkGuard &) = delete; 15 | BenchmarkGuard &operator=(const BenchmarkGuard &) = delete; 16 | 17 | BenchmarkGuard(BenchmarkGuard &&) = delete; 18 | BenchmarkGuard &operator=(BenchmarkGuard &&) = delete; 19 | 20 | qreal getElapsedMs(); 21 | 22 | private: 23 | QElapsedTimer timer_; 24 | QString name_; 25 | }; 26 | 27 | } // namespace chatterino 28 | -------------------------------------------------------------------------------- /src/messages/search/LinkPredicate.cpp: -------------------------------------------------------------------------------- 1 | #include "messages/search/LinkPredicate.hpp" 2 | 3 | #include "common/LinkParser.hpp" 4 | #include "messages/Message.hpp" 5 | 6 | namespace chatterino { 7 | 8 | LinkPredicate::LinkPredicate(bool negate) 9 | : MessagePredicate(negate) 10 | { 11 | } 12 | 13 | bool LinkPredicate::appliesToImpl(const Message &message) 14 | { 15 | for (const auto &word : message.messageText.split(' ', Qt::SkipEmptyParts)) 16 | { 17 | if (linkparser::parse(word).has_value()) 18 | { 19 | return true; 20 | } 21 | } 22 | 23 | return false; 24 | } 25 | 26 | } // namespace chatterino 27 | -------------------------------------------------------------------------------- /src/widgets/AccountSwitchPopup.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "widgets/BaseWindow.hpp" 4 | 5 | #include 6 | 7 | namespace chatterino { 8 | 9 | class AccountSwitchWidget; 10 | 11 | class AccountSwitchPopup : public BaseWindow 12 | { 13 | Q_OBJECT 14 | 15 | public: 16 | AccountSwitchPopup(QWidget *parent = nullptr); 17 | 18 | void refresh(); 19 | 20 | protected: 21 | void paintEvent(QPaintEvent *event) override; 22 | 23 | void themeChangedEvent() override; 24 | 25 | private: 26 | struct { 27 | AccountSwitchWidget *accountSwitchWidget = nullptr; 28 | } ui_; 29 | }; 30 | 31 | } // namespace chatterino 32 | -------------------------------------------------------------------------------- /src/messages/search/RegexPredicate.cpp: -------------------------------------------------------------------------------- 1 | #include "messages/search/RegexPredicate.hpp" 2 | 3 | #include "messages/Message.hpp" 4 | 5 | namespace chatterino { 6 | 7 | RegexPredicate::RegexPredicate(const QString ®ex, bool negate) 8 | : MessagePredicate(negate) 9 | , regex_(regex, QRegularExpression::CaseInsensitiveOption) 10 | { 11 | } 12 | 13 | bool RegexPredicate::appliesToImpl(const Message &message) 14 | { 15 | if (!regex_.isValid()) 16 | { 17 | return false; 18 | } 19 | 20 | QRegularExpressionMatch match = regex_.match(message.messageText); 21 | 22 | return match.hasMatch(); 23 | } 24 | 25 | } // namespace chatterino 26 | -------------------------------------------------------------------------------- /src/providers/twitch/TwitchCommon.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | namespace chatterino { 9 | 10 | [[maybe_unused]] inline const char *const ANONYMOUS_USERNAME = "justinfan64537"; 11 | 12 | inline constexpr int TWITCH_MESSAGE_LIMIT = 500; 13 | 14 | inline QByteArray getDefaultClientID() 15 | { 16 | return QByteArrayLiteral("7ue61iz46fz11y3cugd0l3tawb4taal"); 17 | } 18 | 19 | extern const std::vector TWITCH_USERNAME_COLORS; 20 | 21 | extern const QStringList TWITCH_DEFAULT_COMMANDS; 22 | 23 | extern const QStringList TWITCH_WHISPER_COMMANDS; 24 | 25 | } // namespace chatterino 26 | -------------------------------------------------------------------------------- /src/util/FormatTime.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | namespace chatterino { 9 | 10 | // format: 1h 23m 42s 11 | QString formatTime(int totalSeconds); 12 | QString formatTime(const QString &totalSecondsString); 13 | QString formatTime(std::chrono::seconds totalSeconds); 14 | 15 | /// Formats a duration that's expected to be long (sevreal months or years) like 16 | /// "4 years, 5 days and 8 hours". 17 | /// 18 | /// This includes the components year, month, day, and hour. 19 | QString formatLongFriendlyDuration(const QDateTime &from, const QDateTime &to); 20 | 21 | } // namespace chatterino 22 | -------------------------------------------------------------------------------- /src/widgets/settingspages/FiltersPage.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "widgets/settingspages/SettingsPage.hpp" 4 | 5 | #include 6 | 7 | class QVBoxLayout; 8 | 9 | namespace chatterino { 10 | 11 | class EditableModelView; 12 | 13 | class FiltersPage : public SettingsPage 14 | { 15 | public: 16 | FiltersPage(); 17 | 18 | void onShow() final; 19 | bool filterElements(const QString &query) override; 20 | 21 | private: 22 | void tableCellClicked(const QModelIndex &clicked, EditableModelView *view); 23 | 24 | QStringListModel userListModel_; 25 | EditableModelView *view_; 26 | }; 27 | 28 | } // namespace chatterino 29 | -------------------------------------------------------------------------------- /tests/snapshots/PluginMessageCtor/empty.json: -------------------------------------------------------------------------------- 1 | { 2 | "input": "msg = {}", 3 | "output": { 4 | "badgeInfos": { 5 | }, 6 | "badges": [ 7 | ], 8 | "channelName": "", 9 | "count": 1, 10 | "displayName": "", 11 | "elements": [ 12 | ], 13 | "flags": "", 14 | "frozen": false, 15 | "id": "", 16 | "localizedName": "", 17 | "loginName": "", 18 | "messageText": "", 19 | "searchText": "", 20 | "serverReceivedTime": "", 21 | "timeoutUser": "", 22 | "userID": "", 23 | "usernameColor": "#ff000000" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /tests/src/XDGDesktopFile.cpp: -------------------------------------------------------------------------------- 1 | #include "util/XDGDesktopFile.hpp" 2 | 3 | #include "Test.hpp" 4 | 5 | #include 6 | 7 | #if defined(Q_OS_UNIX) and !defined(Q_OS_DARWIN) 8 | 9 | using namespace chatterino; 10 | 11 | TEST(XDGDesktopFile, String) 12 | { 13 | auto desktopFile = XDGDesktopFile(":/001-mimeapps.list"); 14 | auto entries = desktopFile.getEntries("Default Applications"); 15 | 16 | ASSERT_EQ(entries["thisshould"], ""); 17 | 18 | ASSERT_EQ(entries["lol"], ""); 19 | ASSERT_EQ(entries["x-scheme-handler/http"], QString("firefox.desktop")); 20 | 21 | ASSERT_EQ(desktopFile.getEntries("test").size(), 2); 22 | } 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /src/common/Credentials.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | namespace chatterino { 9 | 10 | class Credentials 11 | { 12 | public: 13 | static Credentials &instance(); 14 | 15 | void get(const QString &provider, const QString &name, QObject *receiver, 16 | std::function &&onLoaded); 17 | void set(const QString &provider, const QString &name, 18 | const QString &credential); 19 | void erase(const QString &provider, const QString &name); 20 | 21 | private: 22 | Credentials() = default; 23 | }; 24 | 25 | } // namespace chatterino 26 | -------------------------------------------------------------------------------- /src/util/SampleData.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace chatterino { 6 | 7 | /// Sample messages coming from IRC 8 | 9 | const QStringList &getSampleCheerMessages(); 10 | const QStringList &getSampleSubMessages(); 11 | const QStringList &getSampleMiscMessages(); 12 | const QStringList &getSampleEmoteTestMessages(); 13 | 14 | /// Channel point reward tests 15 | 16 | const QString &getSampleChannelRewardMessage(); 17 | const QString &getSampleChannelRewardMessage2(); 18 | const QString &getSampleChannelRewardIRCMessage(); 19 | 20 | /// Links 21 | 22 | const QStringList &getSampleLinkMessages(); 23 | 24 | } // namespace chatterino 25 | -------------------------------------------------------------------------------- /src/util/XDGHelper.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "util/XDGDesktopFile.hpp" 4 | 5 | #include 6 | 7 | namespace chatterino { 8 | 9 | #if defined(Q_OS_UNIX) and !defined(Q_OS_DARWIN) 10 | 11 | std::optional getDefaultBrowserDesktopFile(); 12 | 13 | /// Parses the given `execKey` and returns the resulting program name, ignoring all arguments 14 | /// 15 | /// Parsing is done in accordance to https://specifications.freedesktop.org/desktop-entry-spec/latest/ar01s07.html 16 | /// 17 | /// Note: We do *NOT* support field codes 18 | QString parseDesktopExecProgram(const QString &execKey); 19 | 20 | #endif 21 | 22 | } // namespace chatterino 23 | -------------------------------------------------------------------------------- /src/widgets/settingspages/ModerationPage.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "widgets/settingspages/SettingsPage.hpp" 4 | 5 | #include 6 | 7 | class QTabWidget; 8 | class QPushButton; 9 | 10 | namespace chatterino { 11 | 12 | class ModerationPage : public SettingsPage 13 | { 14 | public: 15 | ModerationPage(); 16 | 17 | void selectModerationActions(); 18 | 19 | private: 20 | void addModerationButtonSettings(QTabWidget *); 21 | 22 | QTimer itemsChangedTimer_; 23 | QTabWidget *tabWidget_{}; 24 | 25 | std::vector durationInputs_; 26 | std::vector unitInputs_; 27 | }; 28 | 29 | } // namespace chatterino 30 | -------------------------------------------------------------------------------- /src/widgets/splits/DraggedSplit.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace chatterino { 4 | 5 | // Returns true if the user is currently dragging a split in this Chatterino instance 6 | // We need to keep track of this to ensure splits from other Chatterino instances aren't treated as memory we own 7 | [[nodiscard]] bool isDraggingSplit(); 8 | 9 | // Set that a split is currently being dragged 10 | // Used by the Split::drag function when a drag is initiated 11 | void startDraggingSplit(); 12 | 13 | // Set that a split is no longer being dragged 14 | // Used by the Split::drag function when a drag is finished 15 | void stopDraggingSplit(); 16 | 17 | } // namespace chatterino 18 | -------------------------------------------------------------------------------- /src/controllers/emotes/EmoteController.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace chatterino { 6 | 7 | class TwitchEmotes; 8 | class Emojis; 9 | class GIFTimer; 10 | 11 | class EmoteController 12 | { 13 | public: 14 | EmoteController(); 15 | virtual ~EmoteController(); 16 | 17 | virtual void initialize(); 18 | 19 | TwitchEmotes *getTwitchEmotes() const; 20 | 21 | Emojis *getEmojis() const; 22 | 23 | GIFTimer *getGIFTimer() const; 24 | 25 | private: 26 | std::unique_ptr twitchEmotes_; 27 | std::unique_ptr emojis_; 28 | std::unique_ptr gifTimer_; 29 | }; 30 | 31 | } // namespace chatterino 32 | -------------------------------------------------------------------------------- /src/providers/bttv/liveupdates/BttvLiveUpdateClient.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "providers/bttv/liveupdates/BttvLiveUpdateSubscription.hpp" 4 | #include "providers/liveupdates/BasicPubSubClient.hpp" 5 | 6 | namespace chatterino { 7 | 8 | class BttvLiveUpdates; 9 | 10 | class BttvLiveUpdateClient 11 | : public BasicPubSubClient 12 | { 13 | public: 14 | BttvLiveUpdateClient(BttvLiveUpdates &manager); 15 | 16 | void onMessage(const QByteArray &msg) /* override */; 17 | 18 | void broadcastMe(const QString &channelID, const QString &userID); 19 | 20 | private: 21 | BttvLiveUpdates &manager; 22 | }; 23 | 24 | } // namespace chatterino 25 | -------------------------------------------------------------------------------- /src/providers/pronouns/UserPronouns.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace chatterino::pronouns { 6 | 7 | class UserPronouns 8 | { 9 | public: 10 | UserPronouns() = default; 11 | UserPronouns(QString pronouns); 12 | 13 | QString format() const 14 | { 15 | if (isUnspecified()) 16 | { 17 | return "unspecified"; 18 | } 19 | return this->representation; 20 | } 21 | 22 | bool isUnspecified() const; 23 | 24 | /// True, iff the pronouns are not unspecified. 25 | operator bool() const; 26 | 27 | private: 28 | QString representation; 29 | }; 30 | 31 | } // namespace chatterino::pronouns 32 | -------------------------------------------------------------------------------- /src/widgets/settingspages/PluginsPage.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef CHATTERINO_HAVE_PLUGINS 4 | # include "util/LayoutCreator.hpp" 5 | # include "widgets/settingspages/SettingsPage.hpp" 6 | 7 | # include 8 | # include 9 | # include 10 | # include 11 | 12 | namespace chatterino { 13 | class Plugin; 14 | 15 | class PluginsPage : public SettingsPage 16 | { 17 | public: 18 | PluginsPage(); 19 | 20 | private: 21 | void rebuildContent(); 22 | 23 | LayoutCreator scrollAreaWidget_; 24 | QGroupBox *generalGroup; 25 | QFrame *dataFrame_; 26 | }; 27 | 28 | } // namespace chatterino 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /src/controllers/logging/ChannelLog.cpp: -------------------------------------------------------------------------------- 1 | #include "controllers/logging/ChannelLog.hpp" 2 | 3 | namespace chatterino { 4 | 5 | ChannelLog::ChannelLog(QString channelName) 6 | : channelName_(std::move(channelName)) 7 | { 8 | } 9 | 10 | bool ChannelLog::operator==(const ChannelLog &other) const 11 | { 12 | return this->channelName_ == other.channelName_; 13 | } 14 | 15 | QString ChannelLog::channelName() const 16 | { 17 | return this->channelName_.toLower(); 18 | } 19 | 20 | QString ChannelLog::toString() const 21 | { 22 | return this->channelName_; 23 | } 24 | 25 | ChannelLog ChannelLog::createEmpty() 26 | { 27 | return {""}; 28 | } 29 | 30 | } // namespace chatterino 31 | -------------------------------------------------------------------------------- /tests/snapshots/EventSub/channel-ban/ban.json: -------------------------------------------------------------------------------- 1 | { 2 | "input": { 3 | "banned_at": "2023-05-20T12:30:55.518375571Z", 4 | "broadcaster_user_id": "11148817", 5 | "broadcaster_user_login": "pajlada", 6 | "broadcaster_user_name": "pajlada", 7 | "ends_at": null, 8 | "is_permanent": true, 9 | "moderator_user_id": "29024944", 10 | "moderator_user_login": "CLIModerator", 11 | "moderator_user_name": "CLIModerator", 12 | "reason": "This is a test event", 13 | "user_id": "40389552", 14 | "user_login": "testFromUser", 15 | "user_name": "testFromUser" 16 | }, 17 | "output": [ 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /src/providers/twitch/eventsub/SubscriptionHandle.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "providers/twitch/eventsub/SubscriptionRequest.hpp" 4 | 5 | #include 6 | 7 | namespace chatterino::eventsub { 8 | 9 | struct RawSubscriptionHandle { 10 | const SubscriptionRequest request; 11 | 12 | RawSubscriptionHandle(SubscriptionRequest request_); 13 | 14 | ~RawSubscriptionHandle(); 15 | }; 16 | 17 | /// Keeps a reference count of a specific subscription 18 | /// 19 | /// If no more references exist of a specific subscription, we send an 20 | /// unsubscription request 21 | using SubscriptionHandle = std::unique_ptr; 22 | 23 | } // namespace chatterino::eventsub 24 | -------------------------------------------------------------------------------- /.codecov.yml: -------------------------------------------------------------------------------- 1 | comment: false 2 | ignore: 3 | - "**/usr/local*/**/*" 4 | - "**/usr/include/**/*" 5 | - "**/lib/certify/*" 6 | - "**/lib/expected-lite/*" 7 | - "**/lib/googletest/*" 8 | - "**/lib/libcommuni/*" 9 | - "**/lib/lrucache/*" 10 | - "**/lib/lua/*" 11 | - "**/lib/magic_enum/*" 12 | - "**/lib/miniaudio/*" 13 | - "**/lib/qtkeychain/*" 14 | - "**/lib/rapidjson/*" 15 | - "**/lib/semver/*" 16 | - "**/lib/serialize/*" 17 | - "**/lib/settings/*" 18 | - "**/lib/signals/*" 19 | - "**/lib/sol2/*" 20 | - "**/lib/websocketpp/*" 21 | - "**/lib/WinToast/*" 22 | - "**/build*/" 23 | - "**/ui_*.h" 24 | - "**/moc_*.cpp" 25 | fixes: 26 | - "__w/chatterino2/chatterino2/::" 27 | -------------------------------------------------------------------------------- /lib/twitch-eventsub-ws/ast/lib/templates/field-vector.tmpl: -------------------------------------------------------------------------------- 1 | {% if field.tag -%} 2 | static_assert(false && "JSON tag support is not implemented for vectors"); 3 | {%- endif %} 4 | std::vector<{{field.type_name}}> v{{field.name}}; 5 | const auto *jv{{field.name}} = root.if_contains("{{field.json_name}}"); 6 | if (jv{{field.name}} != nullptr && !jv{{field.name}}->is_null()) { 7 | auto {{field.name}} = boost::json::try_value_to>(*jv{{field.name}}); 8 | if ({{field.name}}.has_error()) 9 | { 10 | {% include 'error-failed-to-deserialize.tmpl' indent content %} 11 | } else { 12 | v{{field.name}} = std::move({{field.name}}.value()); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /tests/snapshots/ImageUploader/Export/basic.json: -------------------------------------------------------------------------------- 1 | { 2 | "input": { 3 | "deletionLink": "{more}", 4 | "formField": "form", 5 | "headers": "My-Header: Foo", 6 | "link": "foo{bar}baz", 7 | "url": "http://example.com" 8 | }, 9 | "output": { 10 | "Body": "MultipartFormData", 11 | "DeletionURL": "{more}", 12 | "FileFormName": "form", 13 | "Headers": { 14 | "My-Header": "Foo" 15 | }, 16 | "Name": "Chatterino Image Uploader Settings", 17 | "RequestMethod": "POST", 18 | "RequestURL": "http://example.com", 19 | "URL": "foo{bar}baz", 20 | "Version": "1.0.0" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /tests/snapshots/ImageUploader/Import/basic.json: -------------------------------------------------------------------------------- 1 | { 2 | "input": { 3 | "Body": "MultipartFormData", 4 | "DeletionURL": "{more}", 5 | "FileFormName": "form", 6 | "Headers": { 7 | "My-Header": "Foo" 8 | }, 9 | "Name": "Chatterino Image Uploader Settings", 10 | "RequestMethod": "POST", 11 | "RequestURL": "http://example.com", 12 | "URL": "foo{bar}baz", 13 | "Version": "1.0.0" 14 | }, 15 | "output": { 16 | "deletionLink": "{more}", 17 | "formField": "form", 18 | "headers": "My-Header: Foo", 19 | "link": "foo{bar}baz", 20 | "url": "http://example.com" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/util/RenameThread.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #ifdef Q_OS_LINUX 7 | # include 8 | #endif 9 | 10 | namespace chatterino { 11 | 12 | #ifdef Q_OS_WIN 13 | namespace windows::detail { 14 | void renameThread(void *hThread, const QString &name); 15 | } // namespace windows::detail 16 | #endif 17 | 18 | template 19 | void renameThread(T &thread, const QString &threadName) 20 | { 21 | #ifdef Q_OS_LINUX 22 | pthread_setname_np(thread.native_handle(), threadName.toLocal8Bit()); 23 | #elif defined(Q_OS_WIN) 24 | windows::detail::renameThread(thread.native_handle(), threadName); 25 | #endif 26 | } 27 | 28 | } // namespace chatterino 29 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # JSON resources should not be prettified... 2 | resources/*.json 3 | benchmarks/resources/*.json 4 | tests/resources/*.json 5 | tests/snapshots/**/*.json 6 | # ...themes should be prettified for readability. 7 | !resources/themes/*.json 8 | 9 | # Ignore submodule files 10 | lib/*/ 11 | !lib/twitch-eventsub-ws 12 | conan-pkgs/*/ 13 | cmake/sanitizers-cmake/ 14 | tools/crash-handler 15 | 16 | # Build folders 17 | *build-*/ 18 | [bB]uild 19 | /_build/ 20 | 21 | # Editors 22 | .vscode 23 | .vs 24 | .idea 25 | dependencies 26 | .cache 27 | .editorconfig 28 | 29 | # vcpkg 30 | vcpkg_installed/ 31 | 32 | # Compile commands generated by CMake 33 | compile_commands.json 34 | 35 | # include files 36 | *.inc 37 | -------------------------------------------------------------------------------- /src/providers/twitch/TwitchUser.cpp: -------------------------------------------------------------------------------- 1 | #include "providers/twitch/TwitchUser.hpp" 2 | 3 | #include "debug/AssertInGuiThread.hpp" 4 | #include "providers/twitch/api/Helix.hpp" 5 | 6 | namespace chatterino { 7 | 8 | void TwitchUser::fromHelixBlock(const HelixBlock &ignore) 9 | { 10 | this->id = ignore.userId; 11 | this->name = ignore.userName; 12 | this->displayName = ignore.displayName; 13 | } 14 | 15 | void TwitchUser::update(const HelixUser &user) const 16 | { 17 | assertInGuiThread(); 18 | assert(this->id == user.id); 19 | this->name = user.login; 20 | this->displayName = user.displayName; 21 | this->profilePictureUrl = user.profileImageUrl; 22 | } 23 | 24 | } // namespace chatterino 25 | -------------------------------------------------------------------------------- /src/util/FilesystemHelpers.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | namespace chatterino { 9 | 10 | inline QString stdPathToQString(const std::filesystem::path &path) 11 | { 12 | #ifdef Q_OS_WIN 13 | return QString::fromStdWString(path.native()); 14 | #else 15 | return QString::fromStdString(path.native()); 16 | #endif 17 | } 18 | 19 | inline std::filesystem::path qStringToStdPath(const QString &path) 20 | { 21 | // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) 22 | const auto *ptr = reinterpret_cast(path.utf16()); 23 | return {ptr, ptr + path.size()}; 24 | } 25 | 26 | } // namespace chatterino 27 | -------------------------------------------------------------------------------- /cmake/FindSol2.cmake: -------------------------------------------------------------------------------- 1 | include(FindPackageHandleStandardArgs) 2 | 3 | find_path(Sol2_INCLUDE_DIR sol/sol.hpp HINTS ${CMAKE_SOURCE_DIR}/lib/sol2/include) 4 | 5 | find_package_handle_standard_args(Sol2 DEFAULT_MSG Sol2_INCLUDE_DIR) 6 | 7 | if (Sol2_FOUND) 8 | add_library(Sol2 INTERFACE IMPORTED) 9 | set_target_properties(Sol2 PROPERTIES 10 | INTERFACE_INCLUDE_DIRECTORIES "${Sol2_INCLUDE_DIR}" 11 | ) 12 | target_compile_definitions(Sol2 INTERFACE 13 | SOL_ALL_SAFETIES_ON=1 14 | SOL_USING_CXX_LUA=1 15 | SOL_NO_NIL=0 16 | ) 17 | target_link_libraries(Sol2 INTERFACE lua) 18 | add_library(sol2::sol2 ALIAS Sol2) 19 | endif () 20 | 21 | mark_as_advanced(Sol2_INCLUDE_DIR) 22 | -------------------------------------------------------------------------------- /src/controllers/filters/lang/expressions/ListExpression.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "controllers/filters/lang/expressions/Expression.hpp" 4 | #include "controllers/filters/lang/Types.hpp" 5 | 6 | namespace chatterino::filters { 7 | 8 | class ListExpression : public Expression 9 | { 10 | public: 11 | ListExpression(ExpressionList &&list); 12 | 13 | QVariant execute(const ContextMap &context) const override; 14 | PossibleType synthesizeType(const TypingContext &context) const override; 15 | QString debug(const TypingContext &context) const override; 16 | QString filterString() const override; 17 | 18 | private: 19 | ExpressionList list_; 20 | }; 21 | 22 | } // namespace chatterino::filters 23 | -------------------------------------------------------------------------------- /src/controllers/pings/MutedChannelModel.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common/SignalVectorModel.hpp" 4 | 5 | #include 6 | 7 | namespace chatterino { 8 | 9 | class MutedChannelModel : public SignalVectorModel 10 | { 11 | explicit MutedChannelModel(QObject *parent); 12 | 13 | protected: 14 | // turn a vector item into a model row 15 | QString getItemFromRow(std::vector &row, 16 | const QString &original) override; 17 | 18 | // turns a row in the model into a vector item 19 | void getRowFromItem(const QString &item, 20 | std::vector &row) override; 21 | }; 22 | 23 | } // namespace chatterino 24 | -------------------------------------------------------------------------------- /tests/snapshots/EventSub/channel-ban/timeout.json: -------------------------------------------------------------------------------- 1 | { 2 | "input": { 3 | "banned_at": "2023-05-20T12:30:55.518375571Z", 4 | "broadcaster_user_id": "11148817", 5 | "broadcaster_user_login": "pajlada", 6 | "broadcaster_user_name": "pajlada", 7 | "ends_at": "2023-05-20T12:40:55.518375571Z", 8 | "is_permanent": false, 9 | "moderator_user_id": "29024944", 10 | "moderator_user_login": "CLIModerator", 11 | "moderator_user_name": "CLIModerator", 12 | "reason": "This is a test event", 13 | "user_id": "40389552", 14 | "user_login": "testFromUser", 15 | "user_name": "testFromUser" 16 | }, 17 | "output": [ 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Issue about the Chatterino Browser Extension 4 | url: https://github.com/Chatterino/chatterino-browser-ext/issues 5 | about: Make a suggestion or report a bug about the Chatterino browser extension. 6 | - name: Suggestions or feature request 7 | url: https://github.com/chatterino/chatterino2/discussions/categories/ideas 8 | about: Got something you think should change or be added? Search for or start a new discussion! 9 | - name: Help 10 | url: https://github.com/chatterino/chatterino2/discussions/categories/q-a 11 | about: Chatterino2 not working as you'd expect? Not sure it's a bug? Check the Q&A section! 12 | -------------------------------------------------------------------------------- /src/controllers/commands/builtin/twitch/Announce.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class QString; 4 | 5 | namespace chatterino { 6 | 7 | struct CommandContext; 8 | 9 | } // namespace chatterino 10 | 11 | namespace chatterino::commands { 12 | 13 | /// /announce 14 | QString sendAnnouncement(const CommandContext &ctx); 15 | 16 | /// /announceblue 17 | QString sendAnnouncementBlue(const CommandContext &ctx); 18 | 19 | /// /announcegreen 20 | QString sendAnnouncementGreen(const CommandContext &ctx); 21 | 22 | /// /announceorange 23 | QString sendAnnouncementOrange(const CommandContext &ctx); 24 | 25 | /// /announcepurple 26 | QString sendAnnouncementPurple(const CommandContext &ctx); 27 | 28 | } // namespace chatterino::commands 29 | -------------------------------------------------------------------------------- /src/messages/search/ChannelPredicate.cpp: -------------------------------------------------------------------------------- 1 | #include "messages/search/ChannelPredicate.hpp" 2 | 3 | #include "messages/Message.hpp" 4 | 5 | namespace chatterino { 6 | 7 | ChannelPredicate::ChannelPredicate(const QString &channels, bool negate) 8 | : MessagePredicate(negate) 9 | , channels_() 10 | { 11 | // Check if any comma-seperated values were passed and transform those 12 | for (const auto &channel : channels.split(',', Qt::SkipEmptyParts)) 13 | { 14 | this->channels_ << channel; 15 | } 16 | } 17 | 18 | bool ChannelPredicate::appliesToImpl(const Message &message) 19 | { 20 | return channels_.contains(message.channelName, Qt::CaseInsensitive); 21 | } 22 | 23 | } // namespace chatterino 24 | -------------------------------------------------------------------------------- /scripts/check-line-endings.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -eu 4 | 5 | fail="0" 6 | 7 | dos2unix --version 8 | 9 | while read -r file; do 10 | num_dos_line_endings=$(dos2unix -id "$file" | awk '/[0-9]+/{print $(NF-1)}') 11 | if [ "$num_dos_line_endings" -gt "0" ]; then 12 | >&2 echo "File '$file' contains $num_dos_line_endings DOS line-endings, it should only be using unix line-endings!" 13 | fail="1" 14 | fi 15 | done < <(find src/ -type f \( -iname "*.hpp" -o -iname "*.cpp" \)) 16 | 17 | if [ "$fail" = "1" ]; then 18 | >&2 echo "At least one file is not using unix line-endings - check the output above" 19 | exit 1 20 | fi 21 | 22 | >&2 echo "Every file seems to be using unix line-endings. Good job!" 23 | -------------------------------------------------------------------------------- /.git-blame-ignore-revs: -------------------------------------------------------------------------------- 1 | # If a commit modifies a ton of files and doesn't really contribute to the 2 | # output of git-blame, please add it here 3 | # 4 | # Don't add commits from the same PR you are creating. We squash PRs into a 5 | # single commit, so references to those commits will be lost 6 | # 7 | # 2018 - changed to 80 max column 8 | f71ff08e686ae76c3dd4084d0f05f27ba9b3fdcb 9 | # 10 | # 2018 - added brace wrapping after if and for 11 | e259b9e39f46f3cb0e4838c988d4f320a03dfaa4 12 | # 13 | # 2019 - Normalize line endings in already existing files 14 | b06eb9df835c25154899fbcf43e9b37addcea1b1 15 | # 16 | # 2025 - Unindented inner namespaces https://github.com/Chatterino/chatterino2/pull/6235 17 | 8acca1c241db169a682c4712154145f5fc92e0d4 18 | -------------------------------------------------------------------------------- /src/common/StreamerModeSetting.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace chatterino { 8 | 9 | enum StreamerModeSetting : std::uint8_t { 10 | Disabled, 11 | Enabled, 12 | DetectStreamingSoftware, 13 | }; 14 | 15 | constexpr std::optional qmagicenumDisplayName( 16 | StreamerModeSetting value) noexcept 17 | { 18 | switch (value) 19 | { 20 | case Disabled: 21 | return "Disabled"; 22 | 23 | case Enabled: 24 | return "Enabled"; 25 | 26 | case DetectStreamingSoftware: 27 | return "Automatic (Detect streaming software)"; 28 | } 29 | } 30 | 31 | } // namespace chatterino 32 | -------------------------------------------------------------------------------- /src/util/ImageUploader.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "util/Expected.hpp" 4 | 5 | class QJsonObject; 6 | class QString; 7 | 8 | namespace chatterino { 9 | 10 | class Settings; 11 | 12 | namespace imageuploader::detail { 13 | 14 | // Exports current image uploader settings to JSON format. 15 | QJsonObject exportSettings(const Settings &s); 16 | 17 | // Imports image uploader settings from JSON into the Settings object. 18 | bool importSettings(const QJsonObject &settingsObj, Settings &s); 19 | 20 | // Validates if the clipboard text contains valid JSON and parses it. 21 | ExpectedStr validateImportJson(const QString &clipboardText); 22 | 23 | } // namespace imageuploader::detail 24 | 25 | } // namespace chatterino 26 | -------------------------------------------------------------------------------- /src/widgets/dialogs/switcher/SwitchSplitItem.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "widgets/dialogs/switcher/AbstractSwitcherItem.hpp" 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | namespace chatterino { 10 | 11 | class SplitContainer; 12 | class Split; 13 | 14 | class SwitchSplitItem : public AbstractSwitcherItem 15 | { 16 | public: 17 | SwitchSplitItem(SplitContainer *container, Split *split = nullptr); 18 | 19 | void action() override; 20 | 21 | void paint(QPainter *painter, const QRect &rect) const override; 22 | QSize sizeHint(const QRect &rect) const override; 23 | 24 | private: 25 | SplitContainer *container_{}; 26 | Split *split_{}; 27 | }; 28 | 29 | } // namespace chatterino 30 | -------------------------------------------------------------------------------- /tests/snapshots/ImageUploader/Export/i-fourtf.json: -------------------------------------------------------------------------------- 1 | { 2 | "input": { 3 | "deletionLink": "", 4 | "formField": "file", 5 | "headers": "Authorization: Basic XXXXXXXXXXXXXXX", 6 | "link": "", 7 | "url": "https://i.yourwebsite.com/upload" 8 | }, 9 | "output": { 10 | "Body": "MultipartFormData", 11 | "DeletionURL": "", 12 | "FileFormName": "file", 13 | "Headers": { 14 | "Authorization": "Basic XXXXXXXXXXXXXXX" 15 | }, 16 | "Name": "Chatterino Image Uploader Settings", 17 | "RequestMethod": "POST", 18 | "RequestURL": "https://i.yourwebsite.com/upload", 19 | "URL": "", 20 | "Version": "1.0.0" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /tests/snapshots/ImageUploader/Import/i-fourtf.json: -------------------------------------------------------------------------------- 1 | { 2 | "input": { 3 | "Body": "MultipartFormData", 4 | "DeletionURL": "", 5 | "FileFormName": "file", 6 | "Headers": { 7 | "Authorization": "Basic XXXXXXXXXXXXXXX" 8 | }, 9 | "Name": "Chatterino Image Uploader Settings", 10 | "RequestMethod": "POST", 11 | "RequestURL": "https://i.yourwebsite.com/upload", 12 | "URL": "", 13 | "Version": "1.0.0" 14 | }, 15 | "output": { 16 | "deletionLink": "", 17 | "formField": "file", 18 | "headers": "Authorization: Basic XXXXXXXXXXXXXXX", 19 | "link": "", 20 | "url": "https://i.yourwebsite.com/upload" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /patches/0004-clear-shortcut.patch: -------------------------------------------------------------------------------- 1 | --- 2 | src/widgets/splits/Split.cpp | 1 + 3 | 1 files changed, 1 insertions(+), 0 deletion(-) 4 | 5 | diff --git a/src/widgets/splits/Split.cpp b/src/widgets/splits/Split.cpp 6 | index d35ca701..6e286fae 100644 7 | --- a/src/widgets/splits/Split.cpp 8 | +++ b/src/widgets/splits/Split.cpp 9 | @@ -124,6 +124,7 @@ Split::Split(QWidget *parent) 10 | // CreateShortcut(this, "ALT+SHIFT+LEFT", &Split::doDecFlexX); 11 | // CreateShortcut(this, "ALT+SHIFT+UP", &Split::doIncFlexY); 12 | // CreateShortcut(this, "ALT+SHIFT+DOWN", &Split::doDecFlexY); 13 | + createShortcut(this, "Ctrl+L", &Split::clear); 14 | 15 | this->input_->ui_.textEdit->installEventFilter(parent); 16 | 17 | -- 18 | 2.20.1 19 | -------------------------------------------------------------------------------- /src/widgets/buttons/DimButton.cpp: -------------------------------------------------------------------------------- 1 | #include "widgets/buttons/DimButton.hpp" 2 | 3 | namespace chatterino { 4 | 5 | DimButton::DimButton(BaseWidget *parent) 6 | : Button(parent) 7 | { 8 | } 9 | 10 | DimButton::Dim DimButton::dim() const noexcept 11 | { 12 | return this->dim_; 13 | } 14 | 15 | void DimButton::setDim(Dim value) 16 | { 17 | this->dim_ = value; 18 | 19 | this->invalidateContent(); 20 | } 21 | 22 | qreal DimButton::currentContentOpacity() const noexcept 23 | { 24 | if (this->dim_ == Dim::None || this->mouseOver()) 25 | { 26 | return 1; 27 | } 28 | if (this->dim_ == Dim::Some) 29 | { 30 | return 0.7; 31 | } 32 | 33 | return 0.15; 34 | } 35 | 36 | } // namespace chatterino 37 | -------------------------------------------------------------------------------- /src/widgets/dialogs/EditUserNotesDialog.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "pajlada/signals/signal.hpp" 4 | #include "widgets/BasePopup.hpp" 5 | 6 | class QTextEdit; 7 | 8 | namespace chatterino { 9 | 10 | class EditUserNotesDialog : public BasePopup 11 | { 12 | Q_OBJECT 13 | 14 | public: 15 | EditUserNotesDialog(QWidget *parent = nullptr); 16 | 17 | void setNotes(const QString &initialNotes); 18 | void updateWindowTitle(const QString &displayUsername); 19 | 20 | pajlada::Signals::Signal onOk; 21 | 22 | protected: 23 | void showEvent(QShowEvent *event) override; 24 | void themeChangedEvent() override; 25 | 26 | private: 27 | QTextEdit *textEdit_{}; 28 | }; 29 | 30 | } // namespace chatterino 31 | -------------------------------------------------------------------------------- /lib/twitch-eventsub-ws/tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(twitch-eventsub-ws-test) 2 | 3 | set(SOURCES 4 | src/parse.cpp 5 | src/string.cpp 6 | src/chrono.cpp 7 | ) 8 | 9 | add_executable(${PROJECT_NAME} ${SOURCES}) 10 | 11 | if(CHATTERINO_SANITIZER_SUPPORT) 12 | add_sanitizers(${PROJECT_NAME}) 13 | endif() 14 | 15 | target_link_libraries(${PROJECT_NAME} PRIVATE twitch-eventsub-ws GTest::gtest_main) 16 | 17 | set_target_properties(${PROJECT_NAME} 18 | PROPERTIES 19 | RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin" 20 | CXX_STANDARD 20 21 | ) 22 | 23 | if(MSVC) 24 | target_compile_options(${PROJECT_NAME} PRIVATE /EHsc /bigobj) 25 | endif() 26 | 27 | include(GoogleTest) 28 | gtest_discover_tests(${PROJECT_NAME}) 29 | -------------------------------------------------------------------------------- /src/controllers/filters/lang/expressions/UnaryOperation.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "controllers/filters/lang/expressions/Expression.hpp" 4 | #include "controllers/filters/lang/Types.hpp" 5 | 6 | namespace chatterino::filters { 7 | 8 | class UnaryOperation : public Expression 9 | { 10 | public: 11 | UnaryOperation(TokenType op, ExpressionPtr right); 12 | 13 | QVariant execute(const ContextMap &context) const override; 14 | PossibleType synthesizeType(const TypingContext &context) const override; 15 | QString debug(const TypingContext &context) const override; 16 | QString filterString() const override; 17 | 18 | private: 19 | TokenType op_; 20 | ExpressionPtr right_; 21 | }; 22 | 23 | } // namespace chatterino::filters 24 | -------------------------------------------------------------------------------- /src/controllers/plugins/PluginPermission.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifdef CHATTERINO_HAVE_PLUGINS 3 | 4 | # include 5 | # include 6 | 7 | # include 8 | 9 | namespace chatterino { 10 | 11 | struct PluginPermission { 12 | explicit PluginPermission(const QJsonObject &obj); 13 | // This is for tests 14 | PluginPermission() = default; 15 | 16 | enum class Type { 17 | FilesystemRead, 18 | FilesystemWrite, 19 | Network, 20 | }; 21 | Type type; 22 | std::vector errors; 23 | 24 | bool isValid() const 25 | { 26 | return this->errors.empty(); 27 | } 28 | 29 | QString toHtml() const; 30 | }; 31 | 32 | } // namespace chatterino 33 | #endif 34 | -------------------------------------------------------------------------------- /mocks/include/mocks/LinkResolver.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "providers/links/LinkResolver.hpp" 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | namespace chatterino::mock { 10 | 11 | class LinkResolver : public ILinkResolver 12 | { 13 | public: 14 | LinkResolver() = default; 15 | ~LinkResolver() override = default; 16 | 17 | MOCK_METHOD(void, resolve, (LinkInfo * info), (override)); 18 | }; 19 | 20 | class EmptyLinkResolver : public ILinkResolver 21 | { 22 | public: 23 | EmptyLinkResolver() = default; 24 | ~EmptyLinkResolver() override = default; 25 | 26 | void resolve(LinkInfo *info) override 27 | { 28 | // 29 | } 30 | }; 31 | 32 | } // namespace chatterino::mock 33 | -------------------------------------------------------------------------------- /src/widgets/settingspages/DankerinoPage.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "widgets/settingspages/SettingsPage.hpp" 4 | 5 | class QLabel; 6 | class QCheckBox; 7 | class QComboBox; 8 | 9 | namespace chatterino { 10 | 11 | class GeneralPageView; 12 | class DescriptionLabel; 13 | struct DropdownArgs; 14 | 15 | class DankerinoPage : public SettingsPage 16 | { 17 | Q_OBJECT 18 | 19 | public: 20 | DankerinoPage(); 21 | 22 | bool filterElements(const QString &query); 23 | 24 | private: 25 | void initLayout(GeneralPageView &layout); 26 | void initExtra(); 27 | 28 | QString getFont(const DropdownArgs &args) const; 29 | 30 | DescriptionLabel *cachePath_{}; 31 | GeneralPageView *view_{}; 32 | }; 33 | 34 | } // namespace chatterino 35 | -------------------------------------------------------------------------------- /src/widgets/settingspages/ForserinoPage.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "widgets/settingspages/SettingsPage.hpp" 4 | 5 | class QLabel; 6 | class QCheckBox; 7 | class QComboBox; 8 | 9 | namespace chatterino { 10 | 11 | class GeneralPageView; 12 | class DescriptionLabel; 13 | struct DropdownArgs; 14 | 15 | class ForserinoPage : public SettingsPage 16 | { 17 | Q_OBJECT 18 | 19 | public: 20 | ForserinoPage(); 21 | 22 | bool filterElements(const QString &query); 23 | 24 | private: 25 | void initLayout(GeneralPageView &layout); 26 | void initExtra(); 27 | 28 | QString getFont(const DropdownArgs &args) const; 29 | 30 | DescriptionLabel *cachePath_{}; 31 | GeneralPageView *view_{}; 32 | }; 33 | 34 | } // namespace chatterino 35 | -------------------------------------------------------------------------------- /lib/twitch-eventsub-ws/include/twitch-eventsub-ws/payloads/automod-message.inc: -------------------------------------------------------------------------------- 1 | boost::json::result_for::type tag_invoke( 2 | boost::json::try_value_to_tag, const boost::json::value &jvRoot); 3 | 4 | boost::json::result_for::type tag_invoke( 5 | boost::json::try_value_to_tag, 6 | const boost::json::value &jvRoot); 7 | 8 | boost::json::result_for::type tag_invoke( 9 | boost::json::try_value_to_tag, const boost::json::value &jvRoot); 10 | 11 | boost::json::result_for::type tag_invoke( 12 | boost::json::try_value_to_tag, 13 | const boost::json::value &jvRoot); 14 | -------------------------------------------------------------------------------- /src/providers/twitch/TwitchBadge.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "messages/MessageElement.hpp" 4 | 5 | #include 6 | 7 | namespace chatterino { 8 | 9 | class Badge 10 | { 11 | public: 12 | Badge(QString key, QString value); 13 | 14 | bool operator==(const Badge &other) const; 15 | 16 | // Class members are fetched from both "badges" and "badge-info" tags 17 | // E.g.: "badges": "subscriber/18", "badge-info": "subscriber/22" 18 | QString key_; // subscriber 19 | QString value_; // 18 20 | //QString info_; // 22 (should be parsed separetly into an std::unordered_map) 21 | MessageElementFlag flag_{ 22 | MessageElementFlag::BadgeVanity}; // badge slot it takes up 23 | }; 24 | 25 | } // namespace chatterino 26 | -------------------------------------------------------------------------------- /src/widgets/settingspages/GeneralPage.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "widgets/settingspages/SettingsPage.hpp" 4 | 5 | class QLabel; 6 | class QCheckBox; 7 | class QComboBox; 8 | 9 | namespace chatterino { 10 | 11 | class GeneralPageView; 12 | class DescriptionLabel; 13 | struct DropdownArgs; 14 | 15 | class GeneralPage : public SettingsPage 16 | { 17 | Q_OBJECT 18 | 19 | public: 20 | GeneralPage(); 21 | 22 | bool filterElements(const QString &query) override; 23 | 24 | private: 25 | void initLayout(GeneralPageView &layout); 26 | void initExtra(); 27 | 28 | QString getFont(const DropdownArgs &args) const; 29 | 30 | DescriptionLabel *cachePath_{}; 31 | GeneralPageView *view_{}; 32 | }; 33 | 34 | } // namespace chatterino 35 | -------------------------------------------------------------------------------- /src/controllers/accounts/AccountController.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common/SignalVector.hpp" 4 | #include "providers/twitch/TwitchAccountManager.hpp" 5 | 6 | #include 7 | 8 | namespace chatterino { 9 | 10 | class Account; 11 | class Settings; 12 | class Paths; 13 | 14 | class AccountModel; 15 | 16 | class AccountController final 17 | { 18 | public: 19 | AccountController(); 20 | 21 | AccountModel *createModel(QObject *parent); 22 | 23 | /** 24 | * Load current user & send off a signal to subscribers about any potential changes 25 | */ 26 | void load(); 27 | 28 | TwitchAccountManager twitch; 29 | 30 | private: 31 | SignalVector> accounts_; 32 | }; 33 | 34 | } // namespace chatterino 35 | -------------------------------------------------------------------------------- /src/controllers/completion/strategies/ClassicUserStrategy.cpp: -------------------------------------------------------------------------------- 1 | #include "controllers/completion/strategies/ClassicUserStrategy.hpp" 2 | 3 | namespace chatterino::completion { 4 | 5 | void ClassicUserStrategy::apply(const std::vector &items, 6 | std::vector &output, 7 | const QString &query) const 8 | { 9 | QString lowerQuery = query.toLower(); 10 | if (lowerQuery.startsWith('@')) 11 | { 12 | lowerQuery = lowerQuery.mid(1); 13 | } 14 | 15 | for (const auto &item : items) 16 | { 17 | if (item.first.startsWith(lowerQuery)) 18 | { 19 | output.push_back(item); 20 | } 21 | } 22 | } 23 | } // namespace chatterino::completion 24 | -------------------------------------------------------------------------------- /src/controllers/ignores/IgnoreModel.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common/SignalVectorModel.hpp" 4 | 5 | #include 6 | 7 | namespace chatterino { 8 | 9 | class IgnorePhrase; 10 | 11 | class IgnoreModel : public SignalVectorModel 12 | { 13 | public: 14 | explicit IgnoreModel(QObject *parent); 15 | 16 | protected: 17 | // turn a vector item into a model row 18 | IgnorePhrase getItemFromRow(std::vector &row, 19 | const IgnorePhrase &original) override; 20 | 21 | // turns a row in the model into a vector item 22 | void getRowFromItem(const IgnorePhrase &item, 23 | std::vector &row) override; 24 | }; 25 | 26 | } // namespace chatterino 27 | -------------------------------------------------------------------------------- /src/messages/Link.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace chatterino { 6 | 7 | struct Link { 8 | public: 9 | enum Type { 10 | None, 11 | Url, 12 | UserInfo, 13 | UserWhisper, 14 | InsertText, 15 | UserAction, 16 | AutoModAllow, 17 | AutoModDeny, 18 | OpenAccountsPage, 19 | JumpToChannel, 20 | Reconnect, 21 | CopyToClipboard, 22 | ReplyToMessage, 23 | ViewThread, 24 | JumpToMessage, 25 | }; 26 | 27 | Link(); 28 | Link(Type getType, const QString &getValue); 29 | 30 | Type type; 31 | QString value; 32 | 33 | bool isValid() const; 34 | bool isUrl() const; 35 | }; 36 | 37 | } // namespace chatterino 38 | -------------------------------------------------------------------------------- /tests/src/IncognitoBrowser.cpp: -------------------------------------------------------------------------------- 1 | #include "util/IncognitoBrowser.hpp" 2 | 3 | #include "Test.hpp" 4 | 5 | using namespace chatterino; 6 | 7 | TEST(IncognitoBrowser, getPrivateSwitch) 8 | { 9 | using namespace chatterino::incognitobrowser::detail; 10 | 11 | ASSERT_EQ(getPrivateSwitch("firefox.exe"), "-private-window"); 12 | ASSERT_EQ(getPrivateSwitch("firefox"), "-private-window"); 13 | ASSERT_EQ(getPrivateSwitch("firefox-forsen-version"), "-private-window"); 14 | 15 | ASSERT_EQ(getPrivateSwitch("chrome.exe"), "-incognito"); 16 | ASSERT_EQ(getPrivateSwitch("google-chrome-stable"), "-incognito"); 17 | 18 | ASSERT_EQ(getPrivateSwitch("opera.exe"), "-newprivatetab"); 19 | 20 | ASSERT_EQ(getPrivateSwitch("unsupportedBrowser.exe"), ""); 21 | } 22 | -------------------------------------------------------------------------------- /src/common/ThumbnailPreviewMode.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace chatterino { 8 | 9 | enum class ThumbnailPreviewMode : std::uint8_t { 10 | DontShow, 11 | 12 | AlwaysShow, 13 | 14 | ShowOnShift, 15 | }; 16 | 17 | constexpr std::optional qmagicenumDisplayName( 18 | ThumbnailPreviewMode value) noexcept 19 | { 20 | switch (value) 21 | { 22 | case ThumbnailPreviewMode::DontShow: 23 | return "Don't show"; 24 | case ThumbnailPreviewMode::AlwaysShow: 25 | return "Always show"; 26 | case ThumbnailPreviewMode::ShowOnShift: 27 | return "Hold shift"; 28 | } 29 | } 30 | 31 | } // namespace chatterino 32 | -------------------------------------------------------------------------------- /src/util/AbandonObject.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace chatterino { 6 | 7 | /// Guard to call `deleteLater` on a QObject when destroyed. 8 | class AbandonObject 9 | { 10 | public: 11 | AbandonObject(QObject *obj) 12 | : obj_(obj) 13 | { 14 | } 15 | 16 | ~AbandonObject() 17 | { 18 | if (this->obj_) 19 | { 20 | this->obj_->deleteLater(); 21 | } 22 | } 23 | 24 | AbandonObject(const AbandonObject &) = delete; 25 | AbandonObject(AbandonObject &&) = delete; 26 | AbandonObject &operator=(const AbandonObject &) = delete; 27 | AbandonObject &operator=(AbandonObject &&) = delete; 28 | 29 | private: 30 | QObject *obj_; 31 | }; 32 | 33 | } // namespace chatterino 34 | -------------------------------------------------------------------------------- /src/widgets/dialogs/UpdateDialog.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "singletons/Updates.hpp" 4 | #include "widgets/BaseWindow.hpp" 5 | 6 | #include 7 | 8 | class QPushButton; 9 | 10 | namespace chatterino { 11 | 12 | class Label; 13 | 14 | class UpdateDialog : public BaseWindow 15 | { 16 | public: 17 | enum Button { Dismiss, Install }; 18 | 19 | UpdateDialog(); 20 | 21 | pajlada::Signals::Signal