feat(plugins): minimal account API (#6554)
Co-authored-by: Mm2PL <mm2pl+gh@kotmisia.pl> Reviewed-by: Mm2PL <mm2pl+gh@kotmisia.pl>
This commit is contained in:
@@ -12,6 +12,7 @@
|
||||
- Minor: Consolidate font picking into one dialog. (#6531)
|
||||
- Minor: Added a menu action to sort tabs alphabetically. (#6551)
|
||||
- Minor: Fixed "edit hotkey" dialog opening like a normal window. (#6540)
|
||||
- Minor: Added an API to get the current Twitch account in plugins. (#6554)
|
||||
- Minor: Added a setting to show the stream title in live messages. (#6572)
|
||||
- Minor: Added broadcaster-only `/poll`, `/cancelpoll`, and `/endpoll` commands. (#6583, #6605)
|
||||
- Minor: Added broadcaster-only `/prediction` command to start a prediction. (#6583)
|
||||
|
||||
Vendored
+10
@@ -376,6 +376,16 @@ declare namespace c2 {
|
||||
Original,
|
||||
Repost,
|
||||
}
|
||||
|
||||
class TwitchAccount implements IWeakResource {
|
||||
is_valid(): boolean;
|
||||
user_login(): string;
|
||||
user_id(): string;
|
||||
color(): string;
|
||||
is_anon(): boolean;
|
||||
}
|
||||
|
||||
function current_account(): TwitchAccount;
|
||||
}
|
||||
|
||||
declare module "chatterino.json" {
|
||||
|
||||
@@ -54,6 +54,38 @@ c2.ChannelType = {
|
||||
|
||||
-- End src/common/Channel.hpp
|
||||
|
||||
-- Begin src/controllers/plugins/api/Accounts.hpp
|
||||
|
||||
|
||||
---@class c2.TwitchAccount
|
||||
c2.TwitchAccount = {}
|
||||
|
||||
--- Returns true if the account this object points to is valid.
|
||||
--- If the object expired, returns false
|
||||
---
|
||||
---@return boolean success
|
||||
function c2.TwitchAccount:is_valid() end
|
||||
|
||||
---@return string user_login The (login) name of the account
|
||||
function c2.TwitchAccount:login() end
|
||||
|
||||
---@return string user_id The Twitch user ID of the account
|
||||
function c2.TwitchAccount:id() end
|
||||
|
||||
---@return string? color Color in chat of this account. `nil` if not yet known
|
||||
function c2.TwitchAccount:color() end
|
||||
|
||||
---@return boolean is_anon `true` if this account is an anonymous account (no associated Twitch user)
|
||||
function c2.TwitchAccount:is_anon() end
|
||||
|
||||
---@return string str
|
||||
function c2.TwitchAccount:__tostring() end
|
||||
|
||||
---Gets the currently logged in Twitch account. This account might be an anonymous one (see `is_anon`).
|
||||
---@return c2.TwitchAccount account
|
||||
function c2.current_account() end
|
||||
-- End src/controllers/plugins/api/Accounts.hpp
|
||||
|
||||
-- Begin src/controllers/plugins/api/ChannelRef.hpp
|
||||
|
||||
-- Begin src/providers/twitch/TwitchChannel.hpp
|
||||
|
||||
@@ -219,6 +219,51 @@ c2.register_callback(
|
||||
)
|
||||
```
|
||||
|
||||
#### `current_account()`
|
||||
|
||||
Returns a `TwitchAccount` representing the current account.
|
||||
|
||||
#### `TwitchAccount`
|
||||
|
||||
Represents a Twitch account. There can only be one selected at a time but
|
||||
unselected accounts stay valid. An account can become invalid if removed by the
|
||||
user in the settings. Using an invalid account produces an error.
|
||||
|
||||
```lua
|
||||
local acc = c2.current_account()
|
||||
|
||||
print(acc:is_valid()) -- true unless user removed account
|
||||
```
|
||||
|
||||
##### `TwitchAccount:login()`
|
||||
|
||||
Returns the login name of the account. This string may only contain lowercase ASCII.
|
||||
|
||||
```lua
|
||||
print(acc:login()) -- "mm2pl"
|
||||
```
|
||||
|
||||
##### `TwitchAccount:id()`
|
||||
|
||||
Returns the Twitch user ID of the account. This uniquely and persistently identifies the account.
|
||||
|
||||
```lua
|
||||
print(acc:id()) -- "117691339"
|
||||
```
|
||||
|
||||
##### `TwitchAccount:color()`
|
||||
|
||||
Returns the color in chat of this account. If the user has not sent any
|
||||
messages this will be `nil`.
|
||||
|
||||
```lua
|
||||
print(acc:color()) -- "#ffdaa520"
|
||||
```
|
||||
|
||||
##### `TwitchAccount:is_anon()`
|
||||
|
||||
Returns `true` if this account is an anonymous account (no associated Twitch user).
|
||||
|
||||
#### `ChannelType` enum
|
||||
|
||||
This table describes channel types Chatterino supports. The values behind the
|
||||
|
||||
@@ -250,6 +250,8 @@ set(SOURCE_FILES
|
||||
controllers/pings/MutedChannelModel.hpp
|
||||
|
||||
|
||||
controllers/plugins/api/Accounts.cpp
|
||||
controllers/plugins/api/Accounts.hpp
|
||||
controllers/plugins/api/ChannelRef.cpp
|
||||
controllers/plugins/api/ChannelRef.hpp
|
||||
controllers/plugins/api/EventType.hpp
|
||||
|
||||
@@ -82,6 +82,7 @@ sol::table toTable(lua_State *L, const CompletionEvent &ev);
|
||||
|
||||
/**
|
||||
* @includefile common/Channel.hpp
|
||||
* @includefile controllers/plugins/api/Accounts.hpp
|
||||
* @includefile controllers/plugins/api/ChannelRef.hpp
|
||||
* @includefile controllers/plugins/api/HTTPResponse.hpp
|
||||
* @includefile controllers/plugins/api/HTTPRequest.hpp
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
# include "common/QLogging.hpp"
|
||||
# include "controllers/commands/CommandContext.hpp"
|
||||
# include "controllers/commands/CommandController.hpp"
|
||||
# include "controllers/plugins/api/Accounts.hpp"
|
||||
# include "controllers/plugins/api/ChannelRef.hpp"
|
||||
# include "controllers/plugins/api/HTTPRequest.hpp"
|
||||
# include "controllers/plugins/api/HTTPResponse.hpp"
|
||||
@@ -232,6 +233,7 @@ void PluginController::initSol(sol::state_view &lua, Plugin *plugin)
|
||||
lua::api::HTTPRequest::createUserType(c2);
|
||||
lua::api::WebSocket::createUserType(c2, plugin);
|
||||
lua::api::message::createUserType(c2);
|
||||
lua::api::createAccounts(c2);
|
||||
c2["ChannelType"] = lua::createEnumTable<Channel::Type>(lua);
|
||||
c2["HTTPMethod"] = lua::createEnumTable<NetworkRequestType>(lua);
|
||||
c2["EventType"] = lua::createEnumTable<lua::api::EventType>(lua);
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
#include "controllers/plugins/api/Accounts.hpp"
|
||||
|
||||
#include "Application.hpp"
|
||||
#include "controllers/accounts/AccountController.hpp"
|
||||
#include "controllers/plugins/SolTypes.hpp" // IWYU pragma: keep
|
||||
#include "providers/twitch/TwitchAccount.hpp"
|
||||
#include "util/WeakPtrHelpers.hpp"
|
||||
|
||||
#include <sol/sol.hpp>
|
||||
|
||||
#include <optional>
|
||||
|
||||
namespace {
|
||||
|
||||
using namespace chatterino;
|
||||
|
||||
class WeakTwitchAccount
|
||||
{
|
||||
public:
|
||||
WeakTwitchAccount(std::weak_ptr<TwitchAccount> account)
|
||||
: weak(std::move(account))
|
||||
{
|
||||
}
|
||||
|
||||
QString login() const
|
||||
{
|
||||
return this->shared()->getUserName();
|
||||
}
|
||||
|
||||
QString id() const
|
||||
{
|
||||
return this->shared()->getUserId();
|
||||
}
|
||||
|
||||
std::optional<QString> color() const
|
||||
{
|
||||
auto c = this->shared()->color();
|
||||
if (c.isValid())
|
||||
{
|
||||
return c.name(QColor::HexArgb);
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
bool isAnon() const
|
||||
{
|
||||
return this->shared()->isAnon();
|
||||
}
|
||||
|
||||
bool isValid() const
|
||||
{
|
||||
return !this->weak.expired();
|
||||
}
|
||||
|
||||
bool operator==(const WeakTwitchAccount &other) const
|
||||
{
|
||||
return weakOwnerEquals(this->weak, other.weak);
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<TwitchAccount> shared() const
|
||||
{
|
||||
auto shared = this->weak.lock();
|
||||
if (!shared)
|
||||
{
|
||||
throw std::runtime_error("Expired account");
|
||||
}
|
||||
return shared;
|
||||
}
|
||||
|
||||
std::weak_ptr<TwitchAccount> weak;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace chatterino::lua::api {
|
||||
|
||||
void createAccounts(sol::table &c2)
|
||||
{
|
||||
c2.new_usertype<WeakTwitchAccount>(
|
||||
// clang-format off
|
||||
"TwitchAccount", sol::no_constructor,
|
||||
sol::meta_function::to_string, &WeakTwitchAccount::login,
|
||||
"is_valid", &WeakTwitchAccount::isValid,
|
||||
"login", &WeakTwitchAccount::login,
|
||||
"id", &WeakTwitchAccount::id,
|
||||
"color", &WeakTwitchAccount::color,
|
||||
"is_anon", &WeakTwitchAccount::isAnon
|
||||
// clang-format on
|
||||
);
|
||||
|
||||
c2.set_function("current_account", [] {
|
||||
return WeakTwitchAccount(getApp()->getAccounts()->twitch.getCurrent());
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace chatterino::lua::api
|
||||
@@ -0,0 +1,42 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef CHATTERINO_HAVE_PLUGINS
|
||||
|
||||
# include <sol/forward.hpp>
|
||||
|
||||
namespace chatterino::lua::api {
|
||||
|
||||
/* @lua-fragment
|
||||
---@class c2.TwitchAccount
|
||||
c2.TwitchAccount = {}
|
||||
|
||||
--- Returns true if the account this object points to is valid.
|
||||
--- If the object expired, returns false
|
||||
---
|
||||
---@return boolean success
|
||||
function c2.TwitchAccount:is_valid() end
|
||||
|
||||
---@return string user_login The (login) name of the account
|
||||
function c2.TwitchAccount:login() end
|
||||
|
||||
---@return string user_id The Twitch user ID of the account
|
||||
function c2.TwitchAccount:id() end
|
||||
|
||||
---@return string? color Color in chat of this account. `nil` if not yet known
|
||||
function c2.TwitchAccount:color() end
|
||||
|
||||
---@return boolean is_anon `true` if this account is an anonymous account (no associated Twitch user)
|
||||
function c2.TwitchAccount:is_anon() end
|
||||
|
||||
---@return string str
|
||||
function c2.TwitchAccount:__tostring() end
|
||||
|
||||
---Gets the currently logged in Twitch account. This account might be an anonymous one (see `is_anon`).
|
||||
---@return c2.TwitchAccount account
|
||||
function c2.current_account() end
|
||||
*/
|
||||
void createAccounts(sol::table &c2);
|
||||
|
||||
} // namespace chatterino::lua::api
|
||||
|
||||
#endif
|
||||
@@ -1,7 +1,9 @@
|
||||
#include "mocks/Helix.hpp"
|
||||
#ifdef CHATTERINO_HAVE_PLUGINS
|
||||
# include "Application.hpp"
|
||||
# include "common/Channel.hpp"
|
||||
# include "common/network/NetworkCommon.hpp"
|
||||
# include "controllers/accounts/AccountController.hpp"
|
||||
# include "controllers/commands/Command.hpp" // IWYU pragma: keep
|
||||
# include "controllers/commands/CommandController.hpp"
|
||||
# include "controllers/plugins/api/ChannelRef.hpp"
|
||||
@@ -43,6 +45,15 @@ const QString TEST_SETTINGS = R"(
|
||||
"enabledPlugins": [
|
||||
"test"
|
||||
]
|
||||
},
|
||||
"accounts": {
|
||||
"uid117166826": {
|
||||
"username": "testaccount_420",
|
||||
"userID": "117166826",
|
||||
"clientID": "abc",
|
||||
"oauthToken": "def"
|
||||
},
|
||||
"current": "testaccount_420"
|
||||
}
|
||||
}
|
||||
)";
|
||||
@@ -107,11 +118,18 @@ public:
|
||||
return &this->logging;
|
||||
}
|
||||
|
||||
AccountController *getAccounts() override
|
||||
{
|
||||
return &this->accounts;
|
||||
}
|
||||
|
||||
PluginController plugins;
|
||||
mock::Logging logging;
|
||||
CommandController commands;
|
||||
mock::EmoteController emotes;
|
||||
MockTwitch twitch;
|
||||
AccountController accounts;
|
||||
mock::Helix helix;
|
||||
};
|
||||
|
||||
QDir luaTestBaseDir(const QString &category)
|
||||
@@ -200,6 +218,8 @@ protected:
|
||||
|
||||
this->channel = app->twitch.mm2pl;
|
||||
this->rawpl->dataDirectory().mkpath(".");
|
||||
initializeHelix(&this->app->helix);
|
||||
this->app->accounts.load();
|
||||
}
|
||||
|
||||
void TearDown() override
|
||||
@@ -1124,4 +1144,18 @@ TEST(PluginMessageConstructionTest, Integrity)
|
||||
ASSERT_FALSE(UPDATE_SNAPSHOTS); // make sure fixtures are actually tested
|
||||
}
|
||||
|
||||
TEST_F(PluginTest, testAccounts)
|
||||
{
|
||||
configure();
|
||||
|
||||
auto res = lua->script(R"lua(
|
||||
local current = c2.current_account()
|
||||
assert(current:login() == "testaccount_420")
|
||||
assert(current:id() == "117166826")
|
||||
assert(current:color() == nil) -- unset
|
||||
assert(not current:is_anon())
|
||||
)lua");
|
||||
ASSERT_TRUE(res.valid()) << res.get<sol::error>().what();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user