# SocialStream.Ninja Integration with Streamer.bot

This guide explains how to integrate SocialStream.Ninja with Streamer.bot using WebSockets. This allows Streamer.bot to receive chat messages captured by SocialStream.Ninja and trigger custom actions based on them.

## Integration Features

- Centralized Processing: Receive messages from various platforms supported by SocialStream.Ninja and process them within Streamer.bot.
- Custom Actions: Trigger any Streamer.bot action based on incoming messages (play sounds, show alerts, send messages to Twitch/YouTube, control OBS, etc.).
- Filtering/Routing: Use Streamer.bot's logic (C#, If/Else sub-actions) to handle messages differently based on platform, user, or content.
- Reliable Connection: Uses WebSockets for a persistent, low-latency connection.

## Prerequisites

- Streamer.bot installed on your computer (v0.2.3 or higher recommended; v1.0+ has a redesigned UI)
- SocialStream.Ninja's Standalone app installed and configured
- Basic familiarity with Streamer.bot's interface (Actions, Sub-Actions)

Note: Streamer.bot v1.0 introduced a redesigned UI with dark mode. Menu locations may differ slightly from earlier versions. The WebSocket Server settings are typically found under Settings → Servers or similar. See the official documentation for your version.

## Setting Up Streamer.bot WebSocket Server

v1.0 Users: In Streamer.bot v1.0+, navigate to Settings (gear icon) → Servers → WebSocket Server. Earlier versions use Servers/Clients → WebSocket Server.

1. In Streamer.bot, locate the WebSocket Server settings (see note above)
2. Enable the server by checking Enabled
3. Check Auto Start to start the server automatically when Streamer.bot launches
4. Note the Port (default is 8080)
5. Optionally enable Authentication and set a password if required. If you set a password here, you *must* enter it in SocialStream.Ninja settings.
6. Click Start if the server isn't running. The status should show "Listening".
7. Click Apply or Save (depending on SB version)

## Setting Up the Streamer.bot Action (Required)

To process messages from SocialStream.Ninja, you must create an Action in Streamer.bot. SocialStream.Ninja will trigger this action for each incoming message.

1. In Streamer.bot, navigate to the Actions tab
2. Right-click in the actions pane and select Add
3. Name your action clearly (e.g., "Process SocialStream Message")
4. Very Important: After creating the action, right-click on it and select Copy Action ID. You will paste this ID into the SocialStream.Ninja settings.
5. Add sub-actions to handle the message data. A common first step is to extract the data using C#. Right-click the action → Add Sub-Action → Core → C# → Execute C# Code
6. Paste the following C# code. This code extracts the chat data sent by SocialStream.Ninja from the `chatData` argument:

    using System;
    using Newtonsoft.Json.Linq;

    public class CPHInline
    {
        public bool Execute()
        {
            // --- Get the chatData object sent from SocialStream.Ninja ---
            // Using TryGetArg (best practice since v0.2.3)
            if (!CPH.TryGetArg("chatData", out JObject chatData))
            {
                // Fallback: Try parsing as string (older format)
                if (CPH.TryGetArg("chatData", out string chatDataStr))
                {
                    try {
                        chatData = JObject.Parse(chatDataStr);
                    } catch (Exception ex) {
                        CPH.LogError($"[SocialStream] Failed to parse chatData: {ex.Message}");
                        return false;
                    }
                }
                else
                {
                    CPH.LogWarn("[SocialStream] Action triggered but 'chatData' argument was missing.");
                    return false;
                }
            }

            // --- Extract specific fields from the chatData object ---
            string message = GetStringValue(chatData, "chatmessage", "");
            string username = GetStringValue(chatData, "chatname", "Unknown User");
            string userId = GetStringValue(chatData, "userid", "");
            string avatar = GetStringValue(chatData, "chatimg", "");
            bool isBot = GetBoolValue(chatData, "isBot", false);
            string originalPlatform = GetStringValue(chatData, "originalPlatform", "unknown");
            string source = GetStringValue(chatData, "source", "unknown");

            // Log for debugging (visible in Action Queue logs)
            CPH.LogInfo($"[SocialStream][{originalPlatform}] {username}: {message}");

            // --- Set arguments for subsequent sub-actions ---
            // Access these in other sub-actions as %ss_message%, %ss_username%, etc.
            CPH.SetArgument("ss_message", message);
            CPH.SetArgument("ss_username", username);
            CPH.SetArgument("ss_userId", userId);
            CPH.SetArgument("ss_avatar", avatar);
            CPH.SetArgument("ss_isBot", isBot);
            CPH.SetArgument("ss_platform", originalPlatform);
            CPH.SetArgument("ss_source", source);

            return true;
        }

        // Helper: Safely get string value from JObject
        private string GetStringValue(JObject obj, string key, string defaultValue)
        {
            if (obj != null && obj.TryGetValue(key, StringComparison.OrdinalIgnoreCase, out JToken token))
            {
                return token.ToString();
            }
            return defaultValue;
        }

        // Helper: Safely get bool value from JObject
        private bool GetBoolValue(JObject obj, string key, bool defaultValue)
        {
            if (obj != null && obj.TryGetValue(key, StringComparison.OrdinalIgnoreCase, out JToken token))
            {
                if (token.Type == JTokenType.Boolean)
                    return token.Value<bool>();
                if (bool.TryParse(token.ToString(), out bool result))
                    return result;
            }
            return defaultValue;
        }
    }

6. Click Compile. If it shows "Compiled successfully!", click OK.
7. Add more sub-actions *after* the C# code sub-action to use the data (e.g., send messages, play sounds). Use the arguments set in the C# code (like `%ss_message%`, `%ss_username%`, `%ss_platform%`). For example:
   - Right-click → Add Sub-Action → Twitch → Chat → Send Message
   - Set Message to: `[%ss_platform%] %ss_username%: %ss_message%`
   - Check "Use Arguments" (or ensure variables are enabled)
8. Click Save to save the entire action.

## Configuring SocialStream.Ninja

1. In SocialStream.Ninja's menu, go to Global settings and tools → Mechanics (or similar path).
2. Find the Streamer.bot integration section.
3. Toggle the integration **ON**.
4. Set the **WebSocket URL** to match Streamer.bot (e.g., `ws://127.0.0.1:8080`). Use `127.0.0.1` if SSN is on the same PC as Streamer.bot.
5. If you set a password in Streamer.bot's WebSocket Server settings, enter it in the **Password** field here.
6. **Action ID (Required):** Paste the Action ID you copied from Streamer.bot into this field. This is required for the integration to function.
7. Save your settings in SocialStream.Ninja.

## SocialStream.Ninja Data Structure Reference

When SocialStream.Ninja sends data to Streamer.bot, it passes a detailed JSON object with the following fields that you can access in your Streamer.bot actions. The example C# code above extracts these fields and makes them available as arguments.

### Primary Data Fields

These are the main fields available in the chatData object:

Field Name | Type | Description | Argument Name | Example Usage
--- | --- | --- | --- | ---
chatmessage | String | The actual chat message content. May contain HTML tags if textonly is false. | ss_message | %ss_message%
chatname | String | The display name of the user who sent the message. | ss_username | %ss_username%
userid | String | The unique ID of the user from the original platform. | ss_userId | %ss_userId%
chatimg | String | URL to the user's avatar/profile image. | ss_avatar | %ss_avatar%
isBot | Boolean | Flag indicating if the message was from a bot. | ss_isBot | %ss_isBot%
originalPlatform | String | The platform the message originated from (e.g., "youtube", "twitch", "kick", etc.). | ss_platform | %ss_platform%
source | String | Identifies the source as "SocialStream.Ninja". | ss_source | %ss_source%

### Additional Available Fields

These additional fields may be present depending on the message type and platform:

Field Name | Type | Description | Access in C#
--- | --- | --- | ---
contentimg | String | URL to content image, if present in the message. | GetStringValue(chatData, "contentimg", "")
subtitle | String | Additional text like user status or other metadata. | GetStringValue(chatData, "subtitle", "")
membership | String | Membership information, if applicable. | GetStringValue(chatData, "membership", "")
hasDonation | String/Boolean | Donation information, if present. | GetStringValue(chatData, "hasDonation", "")
type | String | Source type (e.g., "twitch", "youtube", "kick"). | GetStringValue(chatData, "type", "")
id | String | Unique message ID from the source platform. | GetStringValue(chatData, "id", "")
nameColor | String | HEX color code for the username (e.g., "#FF0000"). | GetStringValue(chatData, "nameColor", "#FFFFFF")
chatbadges | String/Array | Chat badges from the source platform. | GetStringValue(chatData, "chatbadges", "")
textonly | Boolean | If true, indicates that HTML has been stripped from the message. | GetBoolValue(chatData, "textonly", false)
tid | String | Transaction ID used for duplicate detection. | GetStringValue(chatData, "tid", "")

### Accessing Additional Fields in C#

To access fields not already extracted in the example C# code, add lines like these inside your Execute() method (after parsing chatData):

    // Extract additional fields as needed
    string contentImage = GetStringValue(chatData, "contentimg", "");
    string subtitle = GetStringValue(chatData, "subtitle", "");
    string nameColor = GetStringValue(chatData, "nameColor", "#FFFFFF");
    string donation = GetStringValue(chatData, "hasDonation", "");
    string membership = GetStringValue(chatData, "membership", "");

    // Make them available as arguments for subsequent sub-actions
    CPH.SetArgument("ss_contentImage", contentImage);
    CPH.SetArgument("ss_subtitle", subtitle);
    CPH.SetArgument("ss_nameColor", nameColor);
    CPH.SetArgument("ss_donation", donation);
    CPH.SetArgument("ss_membership", membership);

### Example: Working with Platform-Specific Messages

You can add conditional logic based on the platform or other message properties. Add this inside your Execute() method:

    // Example: Process messages differently based on platform
    if (originalPlatform.Equals("youtube", StringComparison.OrdinalIgnoreCase))
    {
        // YouTube-specific processing
        CPH.PlaySound("sounds/youtube_alert.mp3");

        // Check for YouTube memberships
        string membership = GetStringValue(chatData, "membership", "");
        if (!string.IsNullOrEmpty(membership))
        {
            CPH.SetArgument("ss_hasMembership", true);
            CPH.SetArgument("ss_membershipInfo", membership);
            CPH.LogInfo($"[YouTube Membership] {username}: {membership}");
        }
    }
    else if (originalPlatform.Equals("twitch", StringComparison.OrdinalIgnoreCase))
    {
        // Twitch-specific processing
        string badges = GetStringValue(chatData, "chatbadges", "");
        bool isSubscriber = badges.Contains("subscriber");
        CPH.SetArgument("ss_isSubscriber", isSubscriber);
    }
    else if (originalPlatform.Equals("kick", StringComparison.OrdinalIgnoreCase))
    {
        // Kick-specific processing
        CPH.LogInfo($"[Kick] Message from {username}");
    }

### Example: Creating Custom Text Formats

You can format messages for display in various ways. Add this inside your Execute() method:

    // Standard format with platform
    string formattedMessage = $"[{originalPlatform.ToUpper()}] {username}: {message}";
    CPH.SetArgument("ss_formattedMessage", formattedMessage);

    // Get name color for styled display
    string nameColor = GetStringValue(chatData, "nameColor", "#FFFFFF");

    // Format for OBS Text Source (GDI+ or Browser Source)
    string obsText = $"{originalPlatform} | {username} says: {message}";
    CPH.SetArgument("ss_obsText", obsText);

    // Check for donations/Super Chats
    string donation = GetStringValue(chatData, "hasDonation", "");
    if (!string.IsNullOrEmpty(donation))
    {
        CPH.SetArgument("ss_isDonation", true);
        CPH.SetArgument("ss_donationAmount", donation);
        // You could trigger a different action for donations
        // CPH.RunAction("Donation Alert", false);
    }

## Troubleshooting

- Cannot connect to WebSocket:
  - Verify the WebSocket Server is Enabled and Running (Status: Listening) in Streamer.bot.
  - In v1.0+, check Settings → Servers → WebSocket Server. In older versions, check Servers/Clients → WebSocket Server.
  - Ensure the Port in SocialStream.Ninja matches the Port in Streamer.bot (default: 8080).
  - Check firewall rules on your PC aren't blocking the connection (especially if not using `127.0.0.1`).
  - Verify the Password matches exactly if authentication is enabled in Streamer.bot (or that it's blank in both if disabled).
  - Check the SocialStream.Ninja console logs (usually F12 in the app/browser) for connection errors.
- Messages not triggering Action in Streamer.bot:
  - Verify the Action ID in SocialStream.Ninja settings exactly matches the ID copied from Streamer.bot (right-click action → Copy Action ID).
  - Check the Streamer.bot Action Queues tab to see if the action is being triggered but failing. Look for errors in the logs.
  - Ensure the C# code in your action compiles successfully (click Compile in the C# sub-action editor).
  - Add a simple `Core → Misc → Log Message` sub-action as the first sub-action to confirm the action is being triggered at all.
  - Check the SocialStream.Ninja console logs for errors when sending the DoAction request.
- C# Code won't compile:
  - Ensure you have `using Newtonsoft.Json.Linq;` at the top of your code.
  - Streamer.bot includes Newtonsoft.Json by default - you don't need to add a reference manually.
  - Check for typos in method names (`CPH.TryGetArg`, `CPH.SetArgument`, `CPH.LogInfo`).
  - Make sure all braces `{}` are properly matched.
- Arguments not working in Sub-Actions:
  - Ensure the C# sub-action runs before the sub-actions that use the arguments.
  - Double-check argument names: `CPH.SetArgument("ss_message", ...)` becomes `%ss_message%` in sub-actions.
  - Arguments are case-sensitive - ensure exact match between what you set and what you use.

### Useful Resources

- Streamer.bot Documentation
- C# Code Actions Reference
- TryGetArg Method
- SetArgument Method
- WebSocket Client Actions

## Important Note on Direct Chat Display

Limitation: Sending messages from an external application (like SocialStream.Ninja) via WebSocket to directly appear within Streamer.bot's own "Chat" tab or integrated overlays (using the `ChatMessage` request type) is currently **not a reliably supported feature** of the Streamer.bot WebSocket server.

While the WebSocket specification includes a `ChatMessage` request, its behaviour for *injecting* messages *into* Streamer.bot's display seems inconsistent or non-functional for this purpose. Its primary role is typically to report messages *from* Streamer.bot *to* connected clients.

Therefore, **you MUST use the Action ID method** described above to send message data to Streamer.bot. You can then use sub-actions within Streamer.bot (like sending a message to your Twitch/YouTube channel) if you want the message content to appear in chat overlays that monitor those platforms.

Please do not report issues related to messages not appearing directly in Streamer.bot's chat tab when *not* using an Action ID, as this relies on functionality that may need to be specifically added or modified by the Streamer.bot developers.

For help on using Streamer.bot, please contact the Streamer.bot's support, as I do have on experience with using it. Also, Streamer.bot's code does not appear to be open-sourced, so if you wish to have additional Social Stream Ninja supported within Streamer.bot, you'll need to make requests to them.

-steve