Gentle nudge NSFW feature

In profile cards, show a prompt to regular users to remind a user to
mark their camera as Explicit, when the camera is blue and is currently
being watched by the current user.

Under the hood, this sends a 'react' message with a msgID of -451 and
the target's username as the react message. Updated chat pages will look
for this reaction and show a nice reminder in chat, if the user's camera
is blue and they have at least 2 watchers. Old page versions at
deployment time will simply ignore these react messages.
This commit is contained in:
Noah 2025-03-17 22:14:09 -07:00
parent b2e7f383b5
commit 3180d2ddf9
2 changed files with 92 additions and 1 deletions

View File

@ -41,6 +41,7 @@ const configuration = {
const FileUploadMaxSize = 1024 * 1024 * 8; // 8 MB
const DebugChannelID = "barertc-debug";
const ReactNudgeNsfwMessageID = -451;
// Webcam sizes: ideal is 640x480 as this is the most friendly for end users, e.g. if everyone
// broadcasted at 720p users on weaker hardware would run into problems sooner.
@ -1307,6 +1308,12 @@ export default {
who = msg.username,
emoji = msg.message;
// Special react cases: nudge NSFW.
if (msgID === ReactNudgeNsfwMessageID) {
this.onNudgeNsfw(msg);
return;
}
if (this.messageReactions[msgID] == undefined) {
this.messageReactions[msgID] = {};
}
@ -2040,6 +2047,13 @@ export default {
username: username,
});
},
isWatching(username) {
// Is the current user watching this target user?
if (this.WebRTC.pc[username] != undefined && this.WebRTC.streams[username] != undefined) {
return true;
}
return false;
},
isWatchingMe(username) {
// Return whether the user is watching your camera
return this.webcam.watching[username] === true;
@ -2893,6 +2907,44 @@ export default {
return false;
},
// Functions to help users 'nudge' each other into marking their cams as Explicit.
sendNudgeNsfw(username) {
// Send a nudge to the username. This is triggered by their profile modal: if the user is
// on blue camera, others on chat can anonymously nudge them into marking their camera as red.
// Nudges are a special kind of emoji reaction (so we could add this feature in frontend only without
// a server side deployment to support it).
this.client.send({
action: 'react',
msgID: ReactNudgeNsfwMessageID,
message: username,
});
},
onNudgeNsfw(msg) {
// Handler for a nudge NSFW react message.
if (msg.message !== this.username) {
return; // Not for us
}
// Sanity check that we are on blue camera.
if (!this.webcam.active || this.webcam.nsfw) {
return;
}
// Only show this if we have at least 2 watchers.
let watchers = Object.keys(this.webcam.watching).length;
if (watchers < 2) {
return;
}
// Show a nice message on chat.
this.ChatServer(
`<strong>Your webcam is <span class="has-text-danger">Hot!</span></strong> <i class="fa fa-fire has-text-danger"></i><br><br>` +
`Somebody who is watching your camera thinks that your webcam should be tagged as <span class="has-text-danger"><i class="fa fa-fire mx-1"></i> Explicit</span>.<br><br>` +
`In case you forgot to do so, please click on the '<i class="fa fa-fire has-text-danger"></i> Explicit' button at the top of the page to turn your camera 'red.' Thank you! <i class="fa fa-heart has-text-danger"></i>`,
);
},
// Show who watches our video.
showViewers() {
// TODO: for now, ChatClient is our bro.
@ -4787,10 +4839,12 @@ export default {
:is-booted="isBooted(profileModal.username)"
:profile-webhook-enabled="isWebhookEnabled('profile')"
:vip-config="config.VIP"
:is-watching="isWatching(profileModal.username)"
@send-dm="openDMs"
@mute-user="muteUser"
@boot-user="bootUser"
@send-command="sendCommand"
@nudge-nsfw="sendNudgeNsfw"
@report="doCustomReport"
@cancel="profileModal.visible = false"></ProfileModal>

View File

@ -10,6 +10,7 @@ export default {
username: String, // the local user
isViewerOp: Boolean, // the viewer is an operator (show buttons)
websiteUrl: String,
isWatching: Boolean, // chat user is currently watching the profile user
isDnd: Boolean,
isMuted: Boolean,
isBooted: Boolean,
@ -45,6 +46,9 @@ export default {
callback() {},
},
// The local user has sent a NSFW nudge already.
sentNsfwNudge: false,
// Error messaging from backend
error: null,
};
@ -113,6 +117,7 @@ export default {
if (!this.profileWebhookEnabled) return;
if (!this.user || !this.user?.username) return;
this.busy = true;
this.sentNsfwNudge = false;
return fetch("/api/profile", {
method: "POST",
mode: "same-origin",
@ -185,6 +190,23 @@ export default {
this.cancel();
});
},
nudgeNsfw() {
// Gentler, public user-facing version to gently remind the user that
// their webcam should probably be marked as Explicit.
if (this.sentNsfwNudge) return;
this.modalConfirm({
title: "Mark a webcam as Explicit",
message: `Should @${this.user.username}'s webcam be marked as Explicit?\n\n` +
`If their webcam is 'blue' and they are behaving sexually on camera, you may send them a gentle reminder ` +
`and ask them to tag their webcam as Explicit.\n\n` +
`They will not know for sure that it was you who did this.`,
icon: "fa fa-fire",
}).then(() => {
this.$emit('nudge-nsfw', this.user.username);
this.sentNsfwNudge = true;
});
},
cutCamera() {
this.modalConfirm({
message: "Make this user stop broadcasting their camera?",
@ -431,7 +453,22 @@ export default {
<!-- Login At -->
<div class="mt-3 is-size-7">
<em>Online since: {{ onlineSince }}</em>
<div class="columns is-multiline is-mobile">
<div class="column is-half">
<em>Online since: {{ onlineSince }}</em>
</div>
<!-- Public user button to 'gently nudge this blue camera as NSFW' -->
<div class="column is-half" v-if="isOnBlueCam && isWatching">
<a href="#" v-if="isOnBlueCam && isWatching"
type="button"
class="has-text-danger"
@click="nudgeNsfw()" title="Should their webcam be marked as Explicit?">
<i class="fa fa-fire mr-1"></i>
{{ sentNsfwNudge ? 'Thank you for helping tag this camera as Explicit!' : 'Should their camera be marked as Explicit?' }}
</a>
</div>
</div>
</div>
<!-- Profile Fields spinner/error -->