mirror of
https://github.com/Chatterino/chatterino2.git
synced 2024-11-21 22:24:07 +01:00
Move shit around, add g_load()
This commit is contained in:
parent
cf7b73f251
commit
3013e73e94
4 changed files with 203 additions and 110 deletions
|
@ -136,6 +136,8 @@ set(SOURCE_FILES
|
|||
controllers/pings/MutedChannelModel.cpp
|
||||
controllers/pings/MutedChannelModel.hpp
|
||||
|
||||
controllers/plugins/ApiChatterino.cpp
|
||||
controllers/plugins/ApiChatterino.hpp
|
||||
controllers/plugins/Plugin.cpp
|
||||
controllers/plugins/Plugin.hpp
|
||||
controllers/plugins/PluginController.hpp
|
||||
|
|
143
src/controllers/plugins/ApiChatterino.cpp
Normal file
143
src/controllers/plugins/ApiChatterino.cpp
Normal file
|
@ -0,0 +1,143 @@
|
|||
#ifdef CHATTERINO_HAVE_PLUGINS
|
||||
# include "ApiChatterino.hpp"
|
||||
|
||||
# include "Application.hpp"
|
||||
# include "common/QLogging.hpp"
|
||||
# include "controllers/commands/CommandController.hpp"
|
||||
# include "controllers/plugins/LuaUtilities.hpp"
|
||||
# include "controllers/plugins/PluginController.hpp"
|
||||
# include "messages/MessageBuilder.hpp"
|
||||
# include "providers/twitch/TwitchIrcServer.hpp"
|
||||
|
||||
// lua stuff
|
||||
# include "lauxlib.h"
|
||||
# include "lua.h"
|
||||
# include "lualib.h"
|
||||
namespace chatterino::lua::api {
|
||||
|
||||
int c2_register_command(lua_State *L)
|
||||
{
|
||||
auto *pl = getApp()->plugins->getPluginByStatePtr(L);
|
||||
if (pl == nullptr)
|
||||
{
|
||||
luaL_error(L, "internal error: no plugin"); // NOLINT
|
||||
return 0;
|
||||
}
|
||||
|
||||
QString name;
|
||||
if (!lua::peek(L, &name, 1))
|
||||
{
|
||||
// NOLINTNEXTLINE
|
||||
luaL_error(L, "cannot get string (1st arg of register_command)");
|
||||
return 0;
|
||||
}
|
||||
if (lua_isnoneornil(L, 2))
|
||||
{
|
||||
// NOLINTNEXTLINE
|
||||
luaL_error(L, "missing argument for register_command: function "
|
||||
"\"pointer\"");
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto callbackSavedName = QString("c2commandcb-%1").arg(name);
|
||||
lua_setfield(L, LUA_REGISTRYINDEX, callbackSavedName.toStdString().c_str());
|
||||
auto ok = pl->registerCommand(name, callbackSavedName);
|
||||
|
||||
// delete both name and callback
|
||||
lua_pop(L, 2);
|
||||
|
||||
lua::push(L, ok);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int c2_send_msg(lua_State *L)
|
||||
{
|
||||
QString text;
|
||||
QString channel;
|
||||
lua::pop(L, &text);
|
||||
lua::pop(L, &channel);
|
||||
|
||||
const auto chn = getApp()->twitch->getChannelOrEmpty(channel);
|
||||
if (chn->isEmpty())
|
||||
{
|
||||
qCDebug(chatterinoLua) << "send_msg: no channel" << channel;
|
||||
lua::push(L, false);
|
||||
return 1;
|
||||
}
|
||||
QString message = text;
|
||||
message = message.replace('\n', ' ');
|
||||
QString outText = getApp()->commands->execCommand(message, chn, false);
|
||||
chn->sendMessage(outText);
|
||||
lua::push(L, true);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int c2_system_msg(lua_State *L)
|
||||
{
|
||||
if (lua_gettop(L) != 2)
|
||||
{
|
||||
qCDebug(chatterinoLua) << "system_msg: need 2 args";
|
||||
luaL_error(L, "need exactly 2 arguments"); // NOLINT
|
||||
lua::push(L, false);
|
||||
return 1;
|
||||
}
|
||||
QString channel;
|
||||
QString text;
|
||||
lua::pop(L, &text);
|
||||
lua::pop(L, &channel);
|
||||
const auto chn = getApp()->twitch->getChannelOrEmpty(channel);
|
||||
if (chn->isEmpty())
|
||||
{
|
||||
qCDebug(chatterinoLua) << "system_msg: no channel" << channel;
|
||||
lua::push(L, false);
|
||||
return 1;
|
||||
}
|
||||
qCDebug(chatterinoLua) << "system_msg: OK!";
|
||||
chn->addMessage(makeSystemMessage(text));
|
||||
lua::push(L, true);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const QChar REPLACEMENT_CHARACTER = QChar(0xFFFD);
|
||||
int g_load(lua_State *L)
|
||||
{
|
||||
auto countArgs = lua_gettop(L);
|
||||
QString str;
|
||||
if (lua::peek(L, &str, 1))
|
||||
{
|
||||
if (str.contains(REPLACEMENT_CHARACTER))
|
||||
{
|
||||
// NOLINTNEXTLINE
|
||||
luaL_error(L, "invalid utf-8 in load() is not allowed");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// NOLINTNEXTLINE
|
||||
luaL_error(L, "using reader function in load() is not allowed");
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (int i = 0; i < countArgs; i++)
|
||||
{
|
||||
lua_seti(L, LUA_REGISTRYINDEX, i);
|
||||
}
|
||||
|
||||
// fetch load and call it
|
||||
lua_getfield(L, LUA_REGISTRYINDEX, "real_load");
|
||||
|
||||
for (int i = 0; i < countArgs; i++)
|
||||
{
|
||||
lua_geti(L, LUA_REGISTRYINDEX, i);
|
||||
lua_pushnil(L);
|
||||
lua_seti(L, LUA_REGISTRYINDEX, i);
|
||||
}
|
||||
|
||||
lua_call(L, countArgs, LUA_MULTRET);
|
||||
qCDebug(chatterinoLua) << "FDM " << lua_gettop(L);
|
||||
|
||||
return lua_gettop(L);
|
||||
}
|
||||
} // namespace chatterino::lua::api
|
||||
#endif
|
16
src/controllers/plugins/ApiChatterino.hpp
Normal file
16
src/controllers/plugins/ApiChatterino.hpp
Normal file
|
@ -0,0 +1,16 @@
|
|||
#pragma once
|
||||
#ifdef CHATTERINO_HAVE_PLUGINS
|
||||
|
||||
struct lua_State;
|
||||
namespace chatterino::lua::api {
|
||||
// names in this namespace reflect what's visible inside Lua and follow the lua naming scheme
|
||||
|
||||
int c2_register_command(lua_State *L); // NOLINT(readability-identifier-naming)
|
||||
int c2_send_msg(lua_State *L); // NOLINT(readability-identifier-naming)
|
||||
int c2_system_msg(lua_State *L); // NOLINT(readability-identifier-naming)
|
||||
|
||||
int g_load(lua_State *L); // NOLINT(readability-identifier-naming)
|
||||
|
||||
} // namespace chatterino::lua::api
|
||||
|
||||
#endif
|
|
@ -1,13 +1,11 @@
|
|||
#include "PluginController.hpp"
|
||||
#ifdef CHATTERINO_HAVE_PLUGINS
|
||||
|
||||
#ifdef CHATTERINO_HAVE_PLUGINS
|
||||
# include "Application.hpp"
|
||||
# include "common/QLogging.hpp"
|
||||
# include "controllers/commands/CommandContext.hpp"
|
||||
# include "controllers/plugins/ApiChatterino.hpp"
|
||||
# include "controllers/plugins/LuaUtilities.hpp"
|
||||
# include "lauxlib.h"
|
||||
# include "lua.h"
|
||||
# include "lualib.h"
|
||||
# include "messages/MessageBuilder.hpp"
|
||||
# include "providers/twitch/TwitchIrcServer.hpp"
|
||||
# include "singletons/Paths.hpp"
|
||||
|
@ -17,6 +15,11 @@
|
|||
# include "widgets/splits/Split.hpp"
|
||||
# include "widgets/Window.hpp"
|
||||
|
||||
// lua stuff
|
||||
# include "lauxlib.h"
|
||||
# include "lua.h"
|
||||
# include "lualib.h"
|
||||
|
||||
# include <QJsonDocument>
|
||||
|
||||
# include <memory>
|
||||
|
@ -135,6 +138,41 @@ void PluginController::openLibrariesFor(lua_State *L,
|
|||
luaL_requiref(L, reg.name, reg.func, int(true));
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE
|
||||
static const luaL_Reg C2LIB[] = {
|
||||
{"system_msg", lua::api::c2_system_msg},
|
||||
{"register_command", lua::api::c2_register_command},
|
||||
{"send_msg", lua::api::c2_send_msg},
|
||||
{nullptr, nullptr},
|
||||
};
|
||||
lua_pushglobaltable(L);
|
||||
auto global = lua_gettop(L);
|
||||
|
||||
// count of elements in C2LIB - 1 (to account for terminator)
|
||||
lua::pushEmptyTable(L, 3);
|
||||
|
||||
luaL_setfuncs(L, C2LIB, 0);
|
||||
lua_setfield(L, global, "c2");
|
||||
|
||||
// ban functions
|
||||
// Note: this might not be fully secure? some kind of metatable fuckery might come up?
|
||||
|
||||
lua_pushglobaltable(L);
|
||||
auto gtable = lua_gettop(L);
|
||||
lua_getfield(L, gtable, "load");
|
||||
|
||||
// possibly randomize this name at runtime to prevent some attacks?
|
||||
lua_setfield(L, LUA_REGISTRYINDEX, "real_load");
|
||||
|
||||
// NOLINTNEXTLINE
|
||||
static const luaL_Reg replacementFuncs[] = {
|
||||
{"load", lua::api::g_load},
|
||||
{nullptr, nullptr},
|
||||
};
|
||||
luaL_setfuncs(L, replacementFuncs, 0);
|
||||
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
void PluginController::load(const QFileInfo &index, const QDir &pluginDir,
|
||||
|
@ -143,7 +181,6 @@ void PluginController::load(const QFileInfo &index, const QDir &pluginDir,
|
|||
qCDebug(chatterinoLua) << "Running lua file" << index;
|
||||
lua_State *l = luaL_newstate();
|
||||
PluginController::openLibrariesFor(l, meta);
|
||||
PluginController::loadChatterinoLib(l);
|
||||
|
||||
auto pluginName = pluginDir.dirName();
|
||||
auto plugin = std::make_unique<Plugin>(pluginName, l, meta, pluginDir);
|
||||
|
@ -254,111 +291,6 @@ QString PluginController::tryExecPluginCommand(const QString &commandName,
|
|||
return "";
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
int luaC2SystemMsg(lua_State *L)
|
||||
{
|
||||
if (lua_gettop(L) != 2)
|
||||
{
|
||||
qCDebug(chatterinoLua) << "system_msg: need 2 args";
|
||||
luaL_error(L, "need exactly 2 arguments"); // NOLINT
|
||||
lua::push(L, false);
|
||||
return 1;
|
||||
}
|
||||
QString channel;
|
||||
QString text;
|
||||
lua::pop(L, &text);
|
||||
lua::pop(L, &channel);
|
||||
const auto chn = getApp()->twitch->getChannelOrEmpty(channel);
|
||||
if (chn->isEmpty())
|
||||
{
|
||||
qCDebug(chatterinoLua) << "system_msg: no channel" << channel;
|
||||
lua::push(L, false);
|
||||
return 1;
|
||||
}
|
||||
qCDebug(chatterinoLua) << "system_msg: OK!";
|
||||
chn->addMessage(makeSystemMessage(text));
|
||||
lua::push(L, true);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int luaC2RegisterCommand(lua_State *L)
|
||||
{
|
||||
auto *pl = getApp()->plugins->getPluginByStatePtr(L);
|
||||
if (pl == nullptr)
|
||||
{
|
||||
luaL_error(L, "internal error: no plugin"); // NOLINT
|
||||
return 0;
|
||||
}
|
||||
|
||||
QString name;
|
||||
if (!lua::peek(L, &name, 1))
|
||||
{
|
||||
// NOLINTNEXTLINE
|
||||
luaL_error(L, "cannot get string (1st arg of register_command)");
|
||||
return 0;
|
||||
}
|
||||
if (lua_isnoneornil(L, 2))
|
||||
{
|
||||
// NOLINTNEXTLINE
|
||||
luaL_error(L, "missing argument for register_command: function "
|
||||
"\"pointer\"");
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto callbackSavedName = QString("c2commandcb-%1").arg(name);
|
||||
lua_setfield(L, LUA_REGISTRYINDEX, callbackSavedName.toStdString().c_str());
|
||||
auto ok = pl->registerCommand(name, callbackSavedName);
|
||||
|
||||
// delete both name and callback
|
||||
lua_pop(L, 2);
|
||||
|
||||
lua::push(L, ok);
|
||||
return 1;
|
||||
}
|
||||
int luaC2SendMsg(lua_State *L)
|
||||
{
|
||||
QString text;
|
||||
QString channel;
|
||||
lua::pop(L, &text);
|
||||
lua::pop(L, &channel);
|
||||
|
||||
const auto chn = getApp()->twitch->getChannelOrEmpty(channel);
|
||||
if (chn->isEmpty())
|
||||
{
|
||||
qCDebug(chatterinoLua) << "send_msg: no channel" << channel;
|
||||
lua::push(L, false);
|
||||
return 1;
|
||||
}
|
||||
QString message = text;
|
||||
message = message.replace('\n', ' ');
|
||||
QString outText = getApp()->commands->execCommand(message, chn, false);
|
||||
chn->sendMessage(outText);
|
||||
lua::push(L, true);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE
|
||||
static const luaL_Reg C2LIB[] = {
|
||||
{"system_msg", luaC2SystemMsg},
|
||||
{"register_command", luaC2RegisterCommand},
|
||||
{"send_msg", luaC2SendMsg},
|
||||
{nullptr, nullptr},
|
||||
};
|
||||
}
|
||||
|
||||
void PluginController::loadChatterinoLib(lua_State *L)
|
||||
{
|
||||
lua_pushglobaltable(L);
|
||||
auto global = lua_gettop(L);
|
||||
|
||||
// count of elements in C2LIB - 1 (to account for terminator)
|
||||
lua::pushEmptyTable(L, 3);
|
||||
|
||||
luaL_setfuncs(L, C2LIB, 0);
|
||||
lua_setfield(L, global, "c2");
|
||||
}
|
||||
|
||||
bool PluginController::isEnabled(const QString &codename)
|
||||
{
|
||||
if (!getSettings()->enableAnyPlugins)
|
||||
|
|
Loading…
Reference in a new issue