Skip to content

Commit b058929

Browse files
authored
Merge pull request #587 from steveseguin/beta
Beta
2 parents 8d1b139 + 2814ffb commit b058929

21 files changed

Lines changed: 1138 additions & 30225 deletions

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,8 @@ Much more than just an overlay - Social Stream Ninja is a complete chat ecosyste
209209
- nextcloud (requires domain added)
210210
- favorited (studio pop out chat)
211211
- xeenon (not pop out ; dashboard)
212+
- streamelements (overlay page)
213+
- patreon
212214

213215
There are additional sites supported, but not listed; refer to the sources folder for a more complete listing.
214216

actions/EventFlowEditor.js

Lines changed: 230 additions & 15 deletions
Large diffs are not rendered by default.

actions/EventFlowSystem.js

Lines changed: 132 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -554,6 +554,21 @@ class EventFlowSystem {
554554
return false;
555555
}
556556

557+
case 'messageLength':
558+
const msgLength = messageText ? messageText.length : 0;
559+
const targetLength = config.length || 100;
560+
switch (config.comparison) {
561+
case 'gt': return msgLength > targetLength;
562+
case 'lt': return msgLength < targetLength;
563+
case 'eq': return msgLength === targetLength;
564+
default: return msgLength > targetLength; // Default to greater than
565+
}
566+
567+
case 'containsLink':
568+
// Simple URL detection - matches http://, https://, or www.
569+
const urlRegex = /https?:\/\/[^\s]+|www\.[^\s]+/i;
570+
return messageText ? urlRegex.test(messageText) : false;
571+
557572
case 'fromSource':
558573
if (config.source === '*') {
559574
match = true; // Match any source
@@ -690,14 +705,70 @@ class EventFlowSystem {
690705
result.modified = true;
691706
break;
692707

708+
case 'addPrefix':
709+
if (config.prefix && message.chatmessage) {
710+
// Replace placeholders
711+
let prefix = config.prefix;
712+
prefix = prefix.replace(/\{username\}/g, message.chatname || '');
713+
prefix = prefix.replace(/\{source\}/g, message.type || '');
714+
prefix = prefix.replace(/\{chatname\}/g, message.chatname || '');
715+
716+
result.message = {
717+
...message,
718+
chatmessage: prefix + message.chatmessage
719+
};
720+
result.modified = true;
721+
}
722+
break;
723+
724+
case 'addSuffix':
725+
if (config.suffix && message.chatmessage) {
726+
// Replace placeholders
727+
let suffix = config.suffix;
728+
suffix = suffix.replace(/\{username\}/g, message.chatname || '');
729+
suffix = suffix.replace(/\{source\}/g, message.type || '');
730+
suffix = suffix.replace(/\{chatname\}/g, message.chatname || '');
731+
732+
result.message = {
733+
...message,
734+
chatmessage: message.chatmessage + suffix
735+
};
736+
result.modified = true;
737+
}
738+
break;
739+
740+
case 'findReplace':
741+
if (config.find && message.chatmessage) {
742+
try {
743+
// Create regex with proper escaping for literal search
744+
const flags = config.caseSensitive ? 'g' : 'gi';
745+
const escapedFind = config.find.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
746+
const regex = new RegExp(escapedFind, flags);
747+
748+
result.message = {
749+
...message,
750+
chatmessage: message.chatmessage.replace(regex, config.replace || '')
751+
};
752+
753+
// Only mark as modified if something actually changed
754+
if (result.message.chatmessage !== message.chatmessage) {
755+
result.modified = true;
756+
}
757+
} catch (e) {
758+
console.error('[ExecuteAction - findReplace] Error:', e);
759+
}
760+
}
761+
break;
762+
693763
case 'relay':
694764
console.log('[RELAY DEBUG - Action] Starting relay action execution');
765+
console.log('[RELAY DEBUG - Action] Config:', config);
766+
console.log('[RELAY DEBUG - Action] Message source:', message.type);
695767
console.log('[RELAY DEBUG - Action] sendMessageToTabs available?', !!this.sendMessageToTabs);
696-
console.log('[RELAY DEBUG - Action] sendMessageToTabs type:', typeof this.sendMessageToTabs);
697768

698769
if (this.sendMessageToTabs && message && !message.reflection) {
699770
// Replace all possible template variables
700-
let processedTemplate = config.template;
771+
let processedTemplate = config.template || '[{source}] {username}: {message}';
701772

702773
// Replace all occurrences of template variables
703774
processedTemplate = processedTemplate.replace(/\{source\}/g, message.type || '');
@@ -711,28 +782,49 @@ class EventFlowSystem {
711782
response: processedTemplate
712783
};
713784

714-
if (message.tid) {
715-
relayMessage.tid = message.tid;
785+
// Handle destination based on config
786+
if (config.destination === 'reply') {
787+
// Reply to sender only - use the original message's tab ID
788+
if (message.tid !== undefined && message.tid !== null) {
789+
relayMessage.tid = message.tid;
790+
console.log('[RELAY DEBUG - Action] Mode: Reply to sender, tid:', message.tid);
791+
} else {
792+
console.warn('[RELAY DEBUG - Action] Reply mode selected but no tid available in message');
793+
// Can't reply without a tid, so skip this action
794+
break;
795+
}
796+
} else if (config.destination) {
797+
// Send to specific destination
798+
relayMessage.destination = config.destination;
799+
console.log('[RELAY DEBUG - Action] Mode: Send to specific destination:', config.destination);
800+
} else {
801+
// Send to all platforms (default behavior)
802+
console.log('[RELAY DEBUG - Action] Mode: Send to all platforms');
716803
}
717804

718805
console.log('[RELAY DEBUG - Action] Relay message prepared:', relayMessage);
719-
console.log('[RELAY DEBUG - Action] Config:', config);
720-
console.log('[RELAY DEBUG - Action] Calling sendMessageToTabs with params:');
721-
console.log(' - message:', relayMessage);
722-
console.log(' - reverse:', false);
723-
console.log(' - metadata:', null);
724-
console.log(' - relayMode:', true);
725-
console.log(' - antispam:', false);
726-
console.log(' - timeout:', config.timeout || 5100);
727-
728-
let result = false;
729-
relayMessage.response = this.sanitizeRelay(relayMessage.response, false).trim();
730-
if (relayMessage.response) {
731-
if (!this.checkExactDuplicateAlreadyRelayed(relayMessage.response, false, relayMessage.tid, false)){
732-
result = this.sendMessageToTabs(relayMessage, true, message, true, false, 1000);
733-
}
734-
}
735-
806+
807+
let result = false;
808+
relayMessage.response = this.sanitizeRelay(relayMessage.response, false).trim();
809+
if (relayMessage.response) {
810+
if (!this.checkExactDuplicateAlreadyRelayed(relayMessage.response, false, relayMessage.tid, false)){
811+
// Adjust parameters based on destination mode
812+
const reverse = config.destination !== 'reply'; // Don't reverse if replying to sender
813+
const relayMode = config.destination !== 'reply'; // Don't use relay mode for replies
814+
const timeout = config.timeout || 1000;
815+
816+
console.log('[RELAY DEBUG - Action] Calling sendMessageToTabs with:');
817+
console.log(' - message:', relayMessage);
818+
console.log(' - reverse:', reverse);
819+
console.log(' - metadata:', message);
820+
console.log(' - relayMode:', relayMode);
821+
console.log(' - antispam:', false);
822+
console.log(' - timeout:', timeout);
823+
824+
result = this.sendMessageToTabs(relayMessage, reverse, message, relayMode, false, timeout);
825+
}
826+
}
827+
736828
console.log('[RELAY DEBUG - Action] sendMessageToTabs returned:', result);
737829
} else {
738830
console.error('[RELAY DEBUG - Action] CRITICAL: sendMessageToTabs is not available!');
@@ -796,6 +888,25 @@ class EventFlowSystem {
796888
}
797889
break;
798890

891+
case 'addPoints':
892+
if (this.pointsSystem && config.amount > 0) {
893+
const addResult = await this.pointsSystem.addPoints(
894+
message.chatname,
895+
message.type,
896+
config.amount
897+
);
898+
899+
if (addResult.success) {
900+
console.log(`[ExecuteAction - addPoints] Added ${config.amount} points to ${message.chatname}. New total: ${addResult.points}`);
901+
// You might want to add the new points total to the message for other actions to use
902+
result.message = { ...message, pointsTotal: addResult.points };
903+
result.modified = true;
904+
} else {
905+
console.log(`[ExecuteAction - addPoints] Failed to add points for ${message.chatname}. Reason: ${addResult.message || 'Unknown error'}`);
906+
}
907+
}
908+
break;
909+
799910
case 'spendPoints':
800911
if (this.pointsSystem && config.amount > 0) {
801912
const spendResult = await this.pointsSystem.spendPoints( // Capture the result

actions/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
<div class="editor-header">
1212
<div class="editor-title">Event Flow Editor</div>
1313
<div style="font-size:66%;max-width:50%;">The Flow Editor lets you create custom and complex triggers and action workflows. Have fun with your stream!<br>
14-
<span style="margin-top: 5px; display: inline-block;">⚡ For media playback and OBS actions, add the <a href="../actions.html" target="_blank" style="color: #4a9eff;">Actions Overlay</a> to your stream</span></div>
14+
<span style="margin-top: 5px; display: inline-block;">⚡ For media playback and OBS actions, add the Actions Overlay to your stream (available via settings menu)</span></div>
1515
<div class="editor-actions">
1616
<button id="open-test-panel" class="btn">🧪 Test Flow</button>
1717
</div>

background.html

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -720,8 +720,9 @@ <h3>
720720
<div class="editor-header">
721721
<div class="editor-title">Event Flow Editor</div>
722722
<div style="font-size:66%;max-width:50%;">The Flow Editor lets you create custom and complex triggers and action workflows. Have fun with your stream!<br>
723-
<span style="margin-top: 5px; display: inline-block;">⚡ For media playback and OBS actions, add the <a href="./actions.html" target="_blank" style="color: #4a9eff;">Actions Overlay</a> to your stream</span></div>
723+
<span style="margin-top: 5px; display: inline-block;">⚡ For media playback and OBS actions, add the Actions Overlay to your stream (available via settings menu)</span></div>
724724
<div class="editor-actions">
725+
<button id="back-to-dashboard" class="btn">← Back to Dashboard</button>
725726
<button id="open-test-panel" class="btn">🧪 Test Flow</button>
726727
</div>
727728
</div>

background.js

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6241,10 +6241,6 @@ function processHype(data) { // data here should be a chat message
62416241

62426242
// Handle viewer count updates separately
62436243
if (data.event === 'viewer_update' && data.meta) {
6244-
// Apply pump the numbers if enabled for individual updates
6245-
if (settings.pumpTheNumbers) {
6246-
data.meta = Math.round(parseInt(data.meta) * 1.75) || 0;
6247-
}
62486244
updateViewerCount(data); // This updates viewers and sends combined data via its own path
62496245
return; // Return here so it doesn't process as a chatter
62506246
}
@@ -7787,8 +7783,36 @@ async function isValidTab(tab, data, reverse, published, now, overrideTimeout, r
77877783
}
77887784
}
77897785

7790-
// Check destination and timeout conditions
7791-
if (data.destination && !tab.url.includes(data.destination)) return false;
7786+
// Check destination - match against tab's source type instead of URL
7787+
if (data.destination) {
7788+
// Ensure tab.id exists before trying to get source type
7789+
if (!tab.id) {
7790+
console.log('[RELAY DEBUG - isValidTab] No tab.id available, cannot check source type');
7791+
// Fall back to URL matching if we have a URL
7792+
if (tab.url && !tab.url.includes(data.destination)) {
7793+
return false;
7794+
}
7795+
return true; // If no tab.id and no URL, allow it through
7796+
}
7797+
7798+
const sourceType = await getSourceType(tab.id);
7799+
console.log('[RELAY DEBUG - isValidTab] Tab source type:', sourceType, 'Expected destination:', data.destination);
7800+
7801+
// If we couldn't get the source type, fall back to URL matching for custom destinations
7802+
if (!sourceType) {
7803+
// For custom destinations like channel names, still use URL matching
7804+
if (!tab.url.includes(data.destination)) {
7805+
console.log('[RELAY DEBUG - isValidTab] No source type, URL check failed');
7806+
return false;
7807+
}
7808+
} else {
7809+
// For platform destinations, match exact source type
7810+
if (sourceType.toLowerCase() !== data.destination.toLowerCase()) {
7811+
console.log('[RELAY DEBUG - isValidTab] Source type mismatch');
7812+
return false;
7813+
}
7814+
}
7815+
}
77927816
if (reverse && !overrideTimeout && tab.id) {
77937817
if (tab.id in messageTimeout && now - messageTimeout[tab.id] < overrideTimeout) {
77947818
return false;

dashboard.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,13 @@ function setupReturnButton() {
300300
showEditorView();
301301
});
302302
}
303+
304+
const backToDashboardButton = document.getElementById('back-to-dashboard');
305+
if (backToDashboardButton) {
306+
backToDashboardButton.addEventListener('click', function() {
307+
showDashboardView();
308+
});
309+
}
303310
}
304311
// Main initialization function
305312
function initDashboard() {

dock.html

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3854,6 +3854,7 @@ <h3>Status</h3>
38543854
var autoshowdonos = false;
38553855
var autoshowmembers = false;
38563856
var autoyoutubememberchat = false;
3857+
var autoshowcontentimages = false;
38573858

38583859
var autoshowqueued = false;
38593860
if (urlParams.has("autoshowqueued")) {
@@ -3874,6 +3875,10 @@ <h3>Status</h3>
38743875
if (urlParams.has("autoshowdonos")) {
38753876
autoshowdonos = true;
38763877
}
3878+
3879+
if (urlParams.has("autoshowcontentimages")) {
3880+
autoshowcontentimages = true;
3881+
}
38773882

38783883
if (urlParams.has("autoshowmembers")) {
38793884
autoshowmembers = true;
@@ -8654,7 +8659,7 @@ <h2 id="c">What message do you want to send ${chatName}?</h2>
86548659
} else if (autoshow && !bot) {
86558660
if (doNotAutoshowFiltered && node.classList.contains("hide")) {
86568661
// pass auto show, since filtered out
8657-
} else if (!(autoshowdonos || autoshowmembers) || (autoshowdonos && data.hasDonation) || (autoshowmembers && data.membership)) {
8662+
} else if (!(autoshowdonos || autoshowmembers) || (autoshowdonos && data.hasDonation) || (autoshowmembers && data.membership) || (autoshowcontentimages && data.contentimg)) {
86588663
// dono or not to dono.
86598664
if (autoTimeoutEnabled) {
86608665
autoShowQueue.push(node);

events.html

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,7 @@ <h1>Stream Events & Donations Dashboard</h1>
347347

348348
// Add parameters for display options
349349
var donationsOnly = urlParams.has("donationsonly");
350+
var minValue = urlParams.has("minvalue") ? parseInt(urlParams.get("minvalue")) : 5;
350351
var giftSubsOnly = urlParams.has("giftedsubsonly");
351352
var showUSD = urlParams.has("currency") ? true : false;
352353
var highlightValue = urlParams.has("highlightvalue") ? true : false;
@@ -377,6 +378,11 @@ <h1>Stream Events & Donations Dashboard</h1>
377378

378379
function addMessageToOverlay(data) {
379380
// Only display events from enabled sources
381+
382+
if (data.meta){
383+
console.log(data);
384+
}
385+
380386
if (sources && !sources.includes(data.type)) {
381387
return;
382388
}
@@ -451,6 +457,10 @@ <h1>Stream Events & Donations Dashboard</h1>
451457
messageDiv.classList.add('medium-value');
452458
}
453459
}
460+
461+
if (minValue && (minValue < usdValue)){
462+
return;
463+
}
454464
if (!showUSD){
455465
usdValue = 0;
456466
}

0 commit comments

Comments
 (0)