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: Consolidate font picking into one dialog. (#6531)
|
||||||
- Minor: Added a menu action to sort tabs alphabetically. (#6551)
|
- Minor: Added a menu action to sort tabs alphabetically. (#6551)
|
||||||
- Minor: Fixed "edit hotkey" dialog opening like a normal window. (#6540)
|
- 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 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 `/poll`, `/cancelpoll`, and `/endpoll` commands. (#6583, #6605)
|
||||||
- Minor: Added broadcaster-only `/prediction` command to start a prediction. (#6583)
|
- Minor: Added broadcaster-only `/prediction` command to start a prediction. (#6583)
|
||||||
|
|||||||
Vendored
+10
@@ -376,6 +376,16 @@ declare namespace c2 {
|
|||||||
Original,
|
Original,
|
||||||
Repost,
|
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" {
|
declare module "chatterino.json" {
|
||||||
|
|||||||
@@ -54,6 +54,38 @@ c2.ChannelType = {
|
|||||||
|
|
||||||
-- End src/common/Channel.hpp
|
-- 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/controllers/plugins/api/ChannelRef.hpp
|
||||||
|
|
||||||
-- Begin src/providers/twitch/TwitchChannel.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
|
#### `ChannelType` enum
|
||||||
|
|
||||||
This table describes channel types Chatterino supports. The values behind the
|
This table describes channel types Chatterino supports. The values behind the
|
||||||
|
|||||||
@@ -250,6 +250,8 @@ set(SOURCE_FILES
|
|||||||
controllers/pings/MutedChannelModel.hpp
|
controllers/pings/MutedChannelModel.hpp
|
||||||
|
|
||||||
|
|
||||||
|
controllers/plugins/api/Accounts.cpp
|
||||||
|
controllers/plugins/api/Accounts.hpp
|
||||||
controllers/plugins/api/ChannelRef.cpp
|
controllers/plugins/api/ChannelRef.cpp
|
||||||
controllers/plugins/api/ChannelRef.hpp
|
controllers/plugins/api/ChannelRef.hpp
|
||||||
controllers/plugins/api/EventType.hpp
|
controllers/plugins/api/EventType.hpp
|
||||||
|
|||||||
@@ -82,6 +82,7 @@ sol::table toTable(lua_State *L, const CompletionEvent &ev);
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @includefile common/Channel.hpp
|
* @includefile common/Channel.hpp
|
||||||
|
* @includefile controllers/plugins/api/Accounts.hpp
|
||||||
* @includefile controllers/plugins/api/ChannelRef.hpp
|
* @includefile controllers/plugins/api/ChannelRef.hpp
|
||||||
* @includefile controllers/plugins/api/HTTPResponse.hpp
|
* @includefile controllers/plugins/api/HTTPResponse.hpp
|
||||||
* @includefile controllers/plugins/api/HTTPRequest.hpp
|
* @includefile controllers/plugins/api/HTTPRequest.hpp
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
# include "common/QLogging.hpp"
|
# include "common/QLogging.hpp"
|
||||||
# include "controllers/commands/CommandContext.hpp"
|
# include "controllers/commands/CommandContext.hpp"
|
||||||
# include "controllers/commands/CommandController.hpp"
|
# include "controllers/commands/CommandController.hpp"
|
||||||
|
# include "controllers/plugins/api/Accounts.hpp"
|
||||||
# include "controllers/plugins/api/ChannelRef.hpp"
|
# include "controllers/plugins/api/ChannelRef.hpp"
|
||||||
# include "controllers/plugins/api/HTTPRequest.hpp"
|
# include "controllers/plugins/api/HTTPRequest.hpp"
|
||||||
# include "controllers/plugins/api/HTTPResponse.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::HTTPRequest::createUserType(c2);
|
||||||
lua::api::WebSocket::createUserType(c2, plugin);
|
lua::api::WebSocket::createUserType(c2, plugin);
|
||||||
lua::api::message::createUserType(c2);
|
lua::api::message::createUserType(c2);
|
||||||
|
lua::api::createAccounts(c2);
|
||||||
c2["ChannelType"] = lua::createEnumTable<Channel::Type>(lua);
|
c2["ChannelType"] = lua::createEnumTable<Channel::Type>(lua);
|
||||||
c2["HTTPMethod"] = lua::createEnumTable<NetworkRequestType>(lua);
|
c2["HTTPMethod"] = lua::createEnumTable<NetworkRequestType>(lua);
|
||||||
c2["EventType"] = lua::createEnumTable<lua::api::EventType>(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
|
#ifdef CHATTERINO_HAVE_PLUGINS
|
||||||
# include "Application.hpp"
|
# include "Application.hpp"
|
||||||
# include "common/Channel.hpp"
|
# include "common/Channel.hpp"
|
||||||
# include "common/network/NetworkCommon.hpp"
|
# include "common/network/NetworkCommon.hpp"
|
||||||
|
# include "controllers/accounts/AccountController.hpp"
|
||||||
# include "controllers/commands/Command.hpp" // IWYU pragma: keep
|
# include "controllers/commands/Command.hpp" // IWYU pragma: keep
|
||||||
# include "controllers/commands/CommandController.hpp"
|
# include "controllers/commands/CommandController.hpp"
|
||||||
# include "controllers/plugins/api/ChannelRef.hpp"
|
# include "controllers/plugins/api/ChannelRef.hpp"
|
||||||
@@ -43,6 +45,15 @@ const QString TEST_SETTINGS = R"(
|
|||||||
"enabledPlugins": [
|
"enabledPlugins": [
|
||||||
"test"
|
"test"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"accounts": {
|
||||||
|
"uid117166826": {
|
||||||
|
"username": "testaccount_420",
|
||||||
|
"userID": "117166826",
|
||||||
|
"clientID": "abc",
|
||||||
|
"oauthToken": "def"
|
||||||
|
},
|
||||||
|
"current": "testaccount_420"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
@@ -107,11 +118,18 @@ public:
|
|||||||
return &this->logging;
|
return &this->logging;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AccountController *getAccounts() override
|
||||||
|
{
|
||||||
|
return &this->accounts;
|
||||||
|
}
|
||||||
|
|
||||||
PluginController plugins;
|
PluginController plugins;
|
||||||
mock::Logging logging;
|
mock::Logging logging;
|
||||||
CommandController commands;
|
CommandController commands;
|
||||||
mock::EmoteController emotes;
|
mock::EmoteController emotes;
|
||||||
MockTwitch twitch;
|
MockTwitch twitch;
|
||||||
|
AccountController accounts;
|
||||||
|
mock::Helix helix;
|
||||||
};
|
};
|
||||||
|
|
||||||
QDir luaTestBaseDir(const QString &category)
|
QDir luaTestBaseDir(const QString &category)
|
||||||
@@ -200,6 +218,8 @@ protected:
|
|||||||
|
|
||||||
this->channel = app->twitch.mm2pl;
|
this->channel = app->twitch.mm2pl;
|
||||||
this->rawpl->dataDirectory().mkpath(".");
|
this->rawpl->dataDirectory().mkpath(".");
|
||||||
|
initializeHelix(&this->app->helix);
|
||||||
|
this->app->accounts.load();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TearDown() override
|
void TearDown() override
|
||||||
@@ -1124,4 +1144,18 @@ TEST(PluginMessageConstructionTest, Integrity)
|
|||||||
ASSERT_FALSE(UPDATE_SNAPSHOTS); // make sure fixtures are actually tested
|
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
|
#endif
|
||||||
|
|||||||
Reference in New Issue
Block a user