Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(plugins): add c2.WebSocket #6076

Open
wants to merge 15 commits into
base: master
Choose a base branch
from

Conversation

Nerixyz
Copy link
Contributor

@Nerixyz Nerixyz commented Mar 14, 2025

Adds a small WebSocket client. Connections must use TLS.

Example

Go in a Twitch channel and run /mirror-chat. All chat messages will now appear twice.

---@type c2.WebSocket|nil
local ws = nil
c2.register_command("/mirror-chat", function(ctx)
    if ws then
        ws:close()
    end
    local proto = "wss"
    if #ctx.words >= 2 then
        proto = ctx.words[2]
    end
    ws = c2.WebSocket.new(proto .. "://irc-ws.chat.twitch.tv")
    local chan = ctx.channel
    ws.on_text = function(text)
        -- Don't care about multiple messages in one
        text = text:sub(1, -3)
        if #text > 4 and text:sub(1, 4) == 'PING' then
            ws:send_text("PONG" .. text:sub(5))
            return
        end

        local sender_end = text:find(' ', 1, true)
        if sender_end == nil then
            return
        end
        local sender = text:sub(2, sender_end - 1)

        local command_end = text:find(' ', sender_end + 1, true)
        if command_end == nil then
            return
        end
        local command = text:sub(sender_end + 1, command_end - 1)
        if command ~= "PRIVMSG" then
            return
        end

        local name_end = sender:find('!', 1, true)
        if name_end == nil then
            return
        end
        local sender_name = sender:sub(1, name_end - 1)

        local msg_start = text:find(':', command_end + 1, true)
        if msg_start == nil then
            return
        end
        chan:add_system_message(sender_name .. ": " .. text:sub(msg_start + 1))
    end
    ws:send_text("NICK justinfan123456")
    ws:send_text("JOIN #" .. ctx.channel:get_name())
    chan:add_system_message("Mirroring messages")
end)

Requests: #4931, #4999 (comment)

@Mm2PL
Copy link
Collaborator

Mm2PL commented Mar 14, 2025

How did you decide on the TLS requirement? It seems weird to not be able to connect to another local program.

@Nerixyz
Copy link
Contributor Author

Nerixyz commented Mar 14, 2025

How did you decide on the TLS requirement? It seems weird to not be able to connect to another local program.

Because of Boost.Beast and that you need to statically declare the types of streams. It's not super hard to add non-TLS streams. I could do it later.

Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clang-tidy made some suggestions

There were too many comments to post at once. Showing the first 25 out of 27. Check the log or trigger a new build to see more.

@Nerixyz
Copy link
Contributor Author

Nerixyz commented Mar 14, 2025

I could do it later.

Added.

@Nerixyz Nerixyz marked this pull request as ready for review March 15, 2025 11:41
@@ -3,16 +3,16 @@ freebsd_instance:

task:
install_script:
- pkg install -y boost-libs git libnotify qt6-base qt6-svg qt6-5compat qt6-imageformats qtkeychain-qt6 cmake pkgconf
- pkg install -y llvm19-lite boost-libs git libnotify qt6-base qt6-svg qt6-5compat qt6-imageformats qtkeychain-qt6 cmake pkgconf
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is because of https://redirect.github.com/ThePhD/sol2/issues/1581. Builds with Clang 18 fail. 17 and 19 both work.

@@ -1,6 +1,6 @@
/** @noSelfInFile */

declare module c2 {
declare namespace c2 {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I understand the docs correctly, then we have to use namespace here instead of module.

@@ -74,6 +77,7 @@ class PluginController
static void loadChatterinoLib(lua_State *l);
bool tryLoadFromDir(const QDir &pluginDir);
std::map<QString, std::unique_ptr<Plugin>> plugins_;
WebSocketPool webSocketPool_;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is currently scoped to the controller but could technically be global to the app.

@@ -89,7 +89,7 @@ QByteArray sol_lua_get(sol::types<QByteArray>, lua_State *L, int index,
sol::stack::record &tracking)
{
auto str = sol::stack::get<std::string_view>(L, index, tracking);
return QByteArray::fromRawData(str.data(), str.length());
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fromRawData creates a view over the data (it doesn't copy) and is suspect to dangling references. We should use QByteArrayView for this.

@@ -9,50 +9,48 @@ namespace chatterino {
// Taken from
// https://stackoverflow.com/questions/21646467/how-to-execute-a-functor-or-a-lambda-in-a-given-thread-in-qt-gcd-style
// Qt 5/4 - preferred, has least allocations
template <typename F>
static void postToThread(F &&fun, QObject *obj = QCoreApplication::instance())
static void postToThread(auto &&f, QObject *obj = QCoreApplication::instance())
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This now accepts non-copyable functions (e.g. lambdas with std::unique_ptr captures).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants