diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c5b2d99227..e880622c6e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Unversioned +- Major: Added the ability to autocomplete Fossabot & Streamelements commands. (#3209) - Minor: Remove TwitchEmotes.com attribution and the open/copy options when right-clicking a Twitch Emote. (#2214, #3136) - Minor: Strip leading @ and trailing , from username in /user and /usercard commands. (#3143) - Minor: Display a system message when reloading subscription emotes to match BTTV/FFZ behavior (#3135) diff --git a/chatterino.pro b/chatterino.pro index 2586ac790b6..cfbd005f5c0 100644 --- a/chatterino.pro +++ b/chatterino.pro @@ -191,6 +191,7 @@ SOURCES += \ src/messages/SharedMessageBuilder.cpp \ src/providers/bttv/BttvEmotes.cpp \ src/providers/bttv/LoadBttvChannelEmote.cpp \ + src/providers/chatterino/ChatCommands.cpp \ src/providers/chatterino/ChatterinoBadges.cpp \ src/providers/colors/ColorProvider.cpp \ src/providers/emoji/Emojis.cpp \ @@ -431,6 +432,7 @@ HEADERS += \ src/PrecompiledHeader.hpp \ src/providers/bttv/BttvEmotes.hpp \ src/providers/bttv/LoadBttvChannelEmote.hpp \ + src/providers/chatterino/ChatCommands.hpp \ src/providers/chatterino/ChatterinoBadges.hpp \ src/providers/colors/ColorProvider.hpp \ src/providers/emoji/Emojis.hpp \ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 78015123f46..0e5d35426e6 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -169,6 +169,8 @@ set(SOURCE_FILES providers/chatterino/ChatterinoBadges.cpp providers/chatterino/ChatterinoBadges.hpp + providers/chatterino/ChatCommands.hpp + providers/chatterino/ChatCommands.cpp providers/colors/ColorProvider.cpp providers/colors/ColorProvider.hpp diff --git a/src/common/CompletionModel.cpp b/src/common/CompletionModel.cpp index ac96b85b4d0..9d36eb416ef 100644 --- a/src/common/CompletionModel.cpp +++ b/src/common/CompletionModel.cpp @@ -121,6 +121,14 @@ void CompletionModel::refresh(const QString &prefix, bool isFirstWord) } } + if (auto cmds = tc->chatCommands()) + { + for (auto &&cmd : *cmds) + { + addString(cmd.prefix, TaggedString::Type::Command); + } + } + // Bttv Global for (auto &emote : *getApp()->twitch2->getBttvEmotes().emotes()) { diff --git a/src/providers/chatterino/ChatCommands.cpp b/src/providers/chatterino/ChatCommands.cpp new file mode 100644 index 00000000000..e8b46204160 --- /dev/null +++ b/src/providers/chatterino/ChatCommands.cpp @@ -0,0 +1,47 @@ +#include "ChatCommands.hpp" + +#include +#include +#include +#include "common/NetworkRequest.hpp" +#include "common/Outcome.hpp" +#include "common/QLogging.hpp" + +namespace chatterino { + +void loadChatCommands( + const QString &channelId, + std::function &&)> cb) +{ + NetworkRequest("https://api.chatterino.com/chat-commands/twitch/" + + channelId) + .timeout(20000) + .onSuccess([cb = std::move(cb), channelId](auto result) -> Outcome { + auto json = result.parseJsonArray(); + auto out = std::vector(); + + for (auto &&cmdValue : json) + { + auto cmd = cmdValue.toObject(); + + ExternalChatCommand ex; + ex.prefix = cmd.value("prefix").toString(); + ex.description = cmd.value("description").toString(); + ex.source = cmd.value("source").toString(); + + out.push_back(std::move(ex)); + } + + cb(std::move(out)); + + return Success; + }) + .onError([channelId](NetworkResult result) { + qCWarning(chatterinoFfzemotes) + << "Error loading chat commands" << channelId << ", error" + << result.status(); + }) + .execute(); +} + +} // namespace chatterino diff --git a/src/providers/chatterino/ChatCommands.hpp b/src/providers/chatterino/ChatCommands.hpp new file mode 100644 index 00000000000..7c0e80ff872 --- /dev/null +++ b/src/providers/chatterino/ChatCommands.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include +#include + +namespace chatterino { + +class Channel; + +struct ExternalChatCommand { + QString prefix; + QString description; + QString source; +}; + +void loadChatCommands( + const QString &channelId, + std::function &&)> cb); + +} // namespace chatterino diff --git a/src/providers/twitch/TwitchChannel.cpp b/src/providers/twitch/TwitchChannel.cpp index db35de7b259..30e91c24d9f 100644 --- a/src/providers/twitch/TwitchChannel.cpp +++ b/src/providers/twitch/TwitchChannel.cpp @@ -180,6 +180,7 @@ TwitchChannel::TwitchChannel(const QString &name) this->refreshCheerEmotes(); this->refreshFFZChannelEmotes(false); this->refreshBTTVChannelEmotes(false); + this->refreshChatCommands(); }); // timers @@ -321,6 +322,29 @@ boost::optional TwitchChannel::channelPointReward( return it->second; } +std::shared_ptr> + TwitchChannel::chatCommands() const +{ + return this->chatCommands_; +} + +void TwitchChannel::refreshChatCommands() +{ + if (this->roomID_.access()->isEmpty()) + { + qCDebug(chatterinoTwitch) << "[TwitchChannel" << this->getName() + << "] Refreshing chat commands (Missing ID)"; + return; + } + + loadChatCommands(*this->roomID_.access(), + [this](std::vector &&cmds) { + this->chatCommands_ = + std::make_shared>( + std::move(cmds)); + }); +} + void TwitchChannel::sendMessage(const QString &message) { auto app = getApp(); diff --git a/src/providers/twitch/TwitchChannel.hpp b/src/providers/twitch/TwitchChannel.hpp index 84d4adaf214..74b2fdec577 100644 --- a/src/providers/twitch/TwitchChannel.hpp +++ b/src/providers/twitch/TwitchChannel.hpp @@ -7,6 +7,7 @@ #include "common/ChatterSet.hpp" #include "common/Outcome.hpp" #include "common/UniqueAccess.hpp" +#include "providers/chatterino/ChatCommands.hpp" #include "providers/twitch/ChannelPointReward.hpp" #include "providers/twitch/TwitchEmotes.hpp" #include "providers/twitch/api/Helix.hpp" @@ -117,6 +118,11 @@ class TwitchChannel : public Channel, boost::optional channelPointReward( const QString &rewardId) const; + // Chat commands + std::shared_ptr> chatCommands() + const; + void refreshChatCommands(); + private: struct NameOptions { QString displayName; @@ -169,6 +175,7 @@ class TwitchChannel : public Channel, badgeSets_; // "subscribers": { "0": ... "3": ... "6": ... UniqueAccess> cheerEmoteSets_; UniqueAccess> channelPointRewards_; + std::shared_ptr> chatCommands_; bool mod_ = false; bool vip_ = false;