Skip to content

Commit d2423e0

Browse files
steveseguinactions-user
authored andcommitted
feat(audio): Play sound for returning or new chatters
Introduces an optional audio notification when a user sends their first message or returns after 8 hours of inactivity. - Adds `beepreturning` setting to enable/disable the feature for enhanced host awareness. - Triggers `audio/join.wav` playback for new or returning chatters based on `data.firsttime` or inactivity duration checks within `background.js`. - Implements a playback cooldown (`RETURNING_BEEP_COOLDOWN_MS`) to prevent notification spam. - Displays a user hint via `messagePopup` to guide users on enabling background audio playback in their browser. - Skips notifications for internal system messages or events (e.g., `reflection`, `replay`, `bot`, `host`) to reduce noise. - Incorporates new general audio assets (`bell.wav`, `chime.wav`, `join.wav`, `leave.wav`) with `join.wav` being used for this specific feature. [auto-enhanced]
1 parent 4e81f00 commit d2423e0

File tree

7 files changed

+93
-24
lines changed

7 files changed

+93
-24
lines changed

audio/bell.wav

129 KB
Binary file not shown.

audio/chime.wav

86.2 KB
Binary file not shown.

audio/join.wav

9 KB
Binary file not shown.

audio/leave.wav

9.97 KB
Binary file not shown.

background.js

Lines changed: 85 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,13 @@ var messageCounter = messageCounterBase;
3131
var lastAntiSpam = 0;
3232
var tabMessageActivityCounter = {};
3333
var lastAutoMessagePerTab = {};
34-
35-
var connectedPeers = {};
36-
var isSSAPP = false;
34+
let returningBeepAudio = null;
35+
let returningBeepLastPlay = 0;
36+
const RETURNING_BEEP_COOLDOWN_MS = 400;
37+
let returningBeepHintShown = false;
38+
39+
var connectedPeers = {};
40+
var isSSAPP = false;
3741

3842
const HANDLE_DB_NAME = "ssn-file-handles";
3943
const HANDLE_STORE_NAME = "handles";
@@ -3815,13 +3819,20 @@ chrome.runtime.onMessage.addListener(async function (request, sender, sendRespon
38153819
if (request.setting === 'sdk' && isExtensionOn && streamID) {
38163820
initTransport(streamID, password);
38173821
}
3818-
} catch(e) { console.warn(e); }
3819-
3820-
sendResponse({ state: isExtensionOn });
3821-
3822-
if (request.target){
3823-
sendTargetP2P(request, request.target);
3824-
}
3822+
} catch(e) { console.warn(e); }
3823+
3824+
sendResponse({ state: isExtensionOn });
3825+
3826+
if (request.setting === "beepreturning" && request.value && !isSSAPP && !returningBeepHintShown) {
3827+
messagePopup({
3828+
alert: "If you don't hear the notification, click the pinned Background tab once to allow audio playback."
3829+
});
3830+
returningBeepHintShown = true;
3831+
}
3832+
3833+
if (request.target){
3834+
sendTargetP2P(request, request.target);
3835+
}
38253836

38263837
if (request.setting == "midi") {
38273838
toggleMidi();
@@ -10807,14 +10818,51 @@ class HostMessageFilter {
1080710818
}
1080810819
}
1080910820
}
10810-
10811-
// Create an instance
10812-
const hostMessageFilter = new HostMessageFilter();
10813-
10814-
10815-
const patterns = {
10816-
botReply: {
10817-
prefixes: ['botReplyMessageEvent', 'botReplyMessageCommand', 'botReplyMessageValue', 'botReplyMessageTimeout', 'botReplyMessageSource', 'botReplyAll'],
10821+
10822+
// Create an instance
10823+
const hostMessageFilter = new HostMessageFilter();
10824+
10825+
function getReturningBeepPlayer() {
10826+
if (typeof Audio === "undefined") {
10827+
return null;
10828+
}
10829+
if (!returningBeepAudio) {
10830+
try {
10831+
returningBeepAudio = new Audio("./audio/join.wav");
10832+
returningBeepAudio.preload = "auto";
10833+
returningBeepAudio.volume = 0.5;
10834+
} catch (e) {
10835+
console.warn("Unable to initialize returning chatter beep audio", e);
10836+
return null;
10837+
}
10838+
}
10839+
return returningBeepAudio;
10840+
}
10841+
10842+
async function playReturningBeep() {
10843+
const player = getReturningBeepPlayer();
10844+
if (!player) {
10845+
return;
10846+
}
10847+
10848+
const now = Date.now();
10849+
if (now - returningBeepLastPlay < RETURNING_BEEP_COOLDOWN_MS) {
10850+
return;
10851+
}
10852+
returningBeepLastPlay = now;
10853+
10854+
try {
10855+
player.currentTime = 0;
10856+
await player.play();
10857+
} catch (err) {
10858+
console.warn("Returning chatter beep failed to play", err?.message || err);
10859+
}
10860+
}
10861+
10862+
10863+
const patterns = {
10864+
botReply: {
10865+
prefixes: ['botReplyMessageEvent', 'botReplyMessageCommand', 'botReplyMessageValue', 'botReplyMessageTimeout', 'botReplyMessageSource', 'botReplyAll'],
1081810866
type: 'botReply'
1081910867
},
1082010868
chatCommand: {
@@ -11142,11 +11190,25 @@ async function applyBotActions(data, tab = false) {
1114211190
console.error("Error checking first timer:", e);
1114311191
}
1114411192
}
11145-
11146-
if (settings.joke && data.chatmessage && data.chatmessage.toLowerCase() === "!joke") {
11147-
////console.log".");
11148-
//if (Date.now() - messageTimeout > 5100) {
11149-
var score = parseInt(Math.random() * 378);
11193+
11194+
const returningBeepEnabled = !!(settings.beepreturning?.setting ?? settings.beepreturning);
11195+
const hasChatFields = typeof data.chatname === "string" && data.chatname.trim() !== "" && typeof data.chatmessage === "string" && data.chatmessage.trim() !== "";
11196+
const skipReturningBeep = data.reflection || data.replay || data.history || data.reload || data.bot || data.host;
11197+
11198+
if (returningBeepEnabled && hasChatFields && !skipReturningBeep) {
11199+
const nowSeconds = normalizeTimestampToSeconds(data.timestamp) || Math.round(Date.now() / 1000);
11200+
const lastActivitySeconds = normalizeTimestampToSeconds(data.lastactivity);
11201+
const inactiveForEightHours = Number.isFinite(lastActivitySeconds) && nowSeconds && (nowSeconds - lastActivitySeconds >= 8 * 60 * 60);
11202+
11203+
if (data.firsttime || inactiveForEightHours) {
11204+
playReturningBeep();
11205+
}
11206+
}
11207+
11208+
if (settings.joke && data.chatmessage && data.chatmessage.toLowerCase() === "!joke") {
11209+
////console.log".");
11210+
//if (Date.now() - messageTimeout > 5100) {
11211+
var score = parseInt(Math.random() * 378);
1115011212
var joke = jokes[score];
1115111213

1115211214
//messageTimeout = Date.now();

manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "Social Stream Ninja",
33
"description": "Powerful tooling to engage live chat on Youtube, Twitch, Zoom, and more.",
44
"manifest_version": 3,
5-
"version": "3.36.19",
5+
"version": "3.36.20",
66
"homepage_url": "http://socialstream.ninja/",
77
"browser_specific_settings": {
88
"gecko": {

popup.html

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5048,6 +5048,13 @@ <h3>Custom Injection</h3>
50485048
<span class="slider round"></span>
50495049
</label>
50505050
<span data-translate="first-time-chatters"> 🌿 Use the DB to mark new-ish chatters and to include last chat-activity. Required for some triggers.</span>
5051+
</div>
5052+
<div title="Beep when a brand new chatter or someone inactive for 8+ hours speaks (requires first-timer tracking)">
5053+
<label class="switch">
5054+
<input type="checkbox" data-setting="beepreturning" />
5055+
<span class="slider round"></span>
5056+
</label>
5057+
<span>🔔👋 Beep for new or returning chatters (8h+ quiet)</span>
50515058
</div>
50525059
<div title="Override's the database 30-day storage limits">
50535060
<label class="switch">

0 commit comments

Comments
 (0)