Skip to content

Commit

Permalink
Face Masks Alertbox Refactor (stream-labs#1008)
Browse files Browse the repository at this point in the history
* Remove profanity filter and alert audio

* Add face mask donation alert box test button

* Remove function to test audio volume

* Remove unnecessary code from face mask service

* Add support for facemaskdonation event

* remove console log
  • Loading branch information
kuhasuki authored Nov 13, 2018
1 parent 8782e03 commit 6ac45ea
Show file tree
Hide file tree
Showing 9 changed files with 52 additions and 459 deletions.
6 changes: 0 additions & 6 deletions app/components/TestWidgets.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,6 @@
@click="tester.test()">
{{ $t(tester.name) }}
</button>
<button
class="button button--trans"
v-if="facemasksActive"
@click="playTestFacemask()">
Mask
</button>
</div>
</transition>
</div>
Expand Down
9 changes: 4 additions & 5 deletions app/components/TestWidgets.vue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,15 @@ export default class TestWidgets extends Vue {
slideOpen = false;

get widgetTesters() {
const allTesters = this.widgetsService.getTesters();
let allTesters = this.widgetsService.getTesters();
if (!this.facemasksActive) {
allTesters = allTesters.filter((tester) => tester.name !== 'Mask');
}
if(!this.testers) return allTesters;
return allTesters.filter((tester) => this.testers.includes(tester.name))
}

get facemasksActive() {
return this.facemasksService.active;
}

playTestFacemask() {
this.facemasksService.playTestAlert();
}
}
2 changes: 1 addition & 1 deletion app/components/pages/Dashboard.vue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ export default class Dashboard extends Vue {
}

async testAudio(volume: number) {
return this.facemasksService.playTestAudio(volume);
return;
}

async navigate(page: TAppPage) {
Expand Down
2 changes: 0 additions & 2 deletions app/services-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,6 @@ import { ProtocolLinksService } from 'services/protocol-links';
import { WebsocketService } from 'services/websocket';
import { ProjectorService } from 'services/projector';
import { FacemasksService } from 'services/facemasks';
import { ProfanityFilterService } from 'util/profanity';
import { I18nService } from 'services/i18n';
import { MediaBackupService } from 'services/media-backup';
import { OutageNotificationsService } from 'services/outage-notifications';
Expand Down Expand Up @@ -173,7 +172,6 @@ export class ServicesManager extends Service {
MediaBackupService,
WebsocketService,
FacemasksService,
ProfanityFilterService,
I18nService,
OutageNotificationsService,
BitGoalService,
Expand Down
179 changes: 26 additions & 153 deletions app/services/facemasks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,8 @@ import fs from 'fs';
import https from 'https';
import electron from 'electron';
import { WebsocketService, TSocketEvent } from 'services/websocket';
import { ProfanityFilterService } from 'util/profanity';
import { TObsValue } from 'components/obs/inputs/ObsInput';
import { StreamingService } from 'services/streaming';
const notificationAudio = require('../../media/sound/facemask4.wav');

interface IFacemasksServiceState {
device: IInputDeviceSelection;
Expand All @@ -43,13 +41,6 @@ interface IFacemask {
is_intro: boolean;
}

interface IProfanitySettings {
profanity_custom_words: string;
profanity_default_words: boolean;
profanity_mode: number;
profanity_names: boolean;
}

interface IFacemaskSettings {
enabled: boolean;
facemasks: IFacemask[];
Expand All @@ -65,6 +56,11 @@ interface IFacemaskAlertMessage {
message: string;
}

interface IFacemaskDonation {
eventId: string;
facemask: string;
}

interface IDownloadProgress {
uuid: string;
progress: number;
Expand All @@ -74,18 +70,16 @@ export class FacemasksService extends PersistentStatefulService<IFacemasksServic
@Inject() userService: UserService;
@Inject() hostsService: HostsService;
@Inject() websocketService: WebsocketService;
@Inject() profanityFilterService: ProfanityFilterService;
@Inject() sourcesService: SourcesService;
@Inject() sourceFiltersService: SourceFiltersService;
@Inject() streamingService: StreamingService;

cdn = `https://${this.hostsService.facemaskCDN}`;
queue: IFacemaskAlertMessage[] = [];
playing = false;
interval: number = null;
facemaskFilter: obs.IFilter = null;
socketConnectionActive = false;

registeredDonations = {};

settings: IFacemaskSettings = {
enabled: false,
facemasks: [],
Expand All @@ -99,17 +93,10 @@ export class FacemasksService extends PersistentStatefulService<IFacemasksServic

downloadProgress: IDownloadProgress[] = [];

profanitySettings: IProfanitySettings = {
profanity_custom_words: '',
profanity_default_words: true,
profanity_mode: 1,
profanity_names: true,
};

static defaultState: IFacemasksServiceState = {
device: { name: null, value: null },
modtimeMap: {},
active: false
active: false,
};

init() {
Expand Down Expand Up @@ -172,159 +159,46 @@ export class FacemasksService extends PersistentStatefulService<IFacemasksServic
this.sourcesService.sourceAdded.subscribe(e => this.onSourceAdded(e));
}

onSocketEvent(event: TSocketEvent) {
if (event.type === 'donation' && event.message[0].facemask) {
this.enqueueAlert(event.message[0]);
}
registerDonationEvent(donation: IFacemaskDonation) {
this.registeredDonations[donation.eventId] = donation.facemask;
}

onSourceAdded(event: ISource) {
if (this.active && event.type === 'dshow_input') {
this.setupFilter();
playDonationEvent(donation: IFacemaskDonation) {
if (this.registeredDonations[donation.eventId] && this.facemaskFilter) {
delete this.registeredDonations[donation.eventId];
this.trigger(donation.facemask);
}
}

playAlerts() {
if (!this.playing && this.queue.length) {
this.playing = true;
this.playQueuedAlert();
this.interval = window.setInterval(() => this.playQueuedAlert(), (this.settings.duration * 1000) + 2000);
onSocketEvent(event: TSocketEvent) {
if (event.type === 'facemaskdonation') {
this.registerDonationEvent({ facemask: event.message[0].facemask, eventId: event.message[0]._id });
}
}

playQueuedAlert() {
if (this.queue.length && this.facemaskFilter) {
const alert = this.queue.shift();
this.trigger(alert.facemask, alert.message, alert.name, alert.formattedAmount);
} else {
this.playing = false;
clearInterval(this.interval);
if (event.type === 'alertPlaying' && event.message.facemask) {
this.playDonationEvent({ facemask: event.message.facemask, eventId: event.message._id });
}
}

playTestAudio(volume: number) {
const alertSound = new Audio(notificationAudio);
alertSound.volume = volume / 100;

alertSound.play();
}

playAudio() {
const alertSound = new Audio(notificationAudio);
alertSound.volume = this.settings.audio_volume / 100;

alertSound.play();
}

enqueueAlert(message: IFacemaskAlertMessage) {
this.queue.push(message);
this.playAlerts();
onSourceAdded(event: ISource) {
if (this.active && event.type === 'dshow_input') {
this.setupFilter();
}
}

trigger(mask: string, message: string, name: string, formattedAmount: string) {
const clean = this.profanitize(message, name);
trigger(mask: string) {
this.updateFilter({
Mask: `${mask}.json`,
alertText: clean['message'],
donorName: `${clean['name']} donated ${formattedAmount}`,
alertActivate: true
});
this.playAudio();
setTimeout(() => {
this.facemaskFilter.update({ alertActivate: false });
}, 1000);
}, 500);
}

updateFilter(settings: Dictionary<TObsValue>) {
if (this.facemaskFilter) this.facemaskFilter.update(settings);
}

playTestAlert() {
if (this.active && this.socketConnectionActive) {
const availableMasks = Object.keys(this.state.modtimeMap).filter(uuid => {
return this.settings.facemasks.some(mask => mask.uuid === uuid) && !this.state.modtimeMap[uuid].intro;
});


if (availableMasks.length) {
const testMask = availableMasks[Math.floor(Math.random() * availableMasks.length)];
this.enqueueAlert({
name: 'Streamlabs',
formattedAmount: '$10.00',
facemask: testMask,
message: 'This is a test Face Mask donation alert'
});
}
}
}

profanitize(message: string, name: string) {
const custom_words_list = this.profanitySettings.profanity_custom_words.trim();
const custom_words = custom_words_list.length ? custom_words_list.replace(/\s\s+/g, ' ').split(' ') : [];
const custom_words_regex = custom_words.length ? this.profanityFilterService.getListRegex(custom_words) : null;

let hasProfanity = false;

try {
if (this.profanitySettings.profanity_names) {
const namePres = this.profanityFilterService.processString(name, {
extraRegex: custom_words_regex,
useDefaultRegex: this.profanitySettings.profanity_default_words,
isName: true,
});

name = namePres[0];
}

// Replace bad word characters with *
if (this.profanitySettings.profanity_mode === 1) {
const pres = this.profanityFilterService.processString(message, {
extraRegex: custom_words_regex,
useDefaultRegex: this.profanitySettings.profanity_default_words,
replace: false,
});
message = pres[0];
hasProfanity = pres[1];
// Replace bad words with happy words
} else if (this.profanitySettings.profanity_mode === 2) {
const pres = this.profanityFilterService.processString(message, {
extraRegex: custom_words_regex,
useDefaultRegex: this.profanitySettings.profanity_default_words,
replace: true,
});
message = pres[0];
hasProfanity = pres[1];
// Clear message if any profanity present
} else if (this.profanitySettings.profanity_mode === 3 || this.profanitySettings.profanity_mode === 4) {
const pres = this.profanityFilterService.processString(message, {
extraRegex: custom_words_regex,
useDefaultRegex: this.profanitySettings.profanity_default_words,
replace: false,
});

if (pres[1]) {
message = '';
hasProfanity = true;
}
}
} catch (ex) {
return { profanity: hasProfanity, message, from: 'Anonymous', name: 'Anonymous' };
}

return { profanity: hasProfanity, message, from: name, name };
}

configureProfanityFilter() {
this.fetchProfanityFilterSettings().then(response => {
this.profanitySettings = {
profanity_custom_words: response.settings.profanity_custom_words,
profanity_default_words: response.settings.profanity_default_words,
profanity_mode: parseInt(response.settings.profanity_mode, 10),
profanity_names: response.settings.profanity_names,
};
});
}

getInputDevicesList() {
const dummy = obs.InputFactory.create('dshow_input', 'fake tmp dshow device', {});
const properties = dummy.properties.get('video_device_id') as obs.IListProperty;
Expand Down Expand Up @@ -363,7 +237,6 @@ export class FacemasksService extends PersistentStatefulService<IFacemasksServic
checkFacemaskSettings(settings:IFacemaskSettings) {
this.settings = settings;
if (settings.enabled) {
this.configureProfanityFilter();
const uuids = settings.facemasks.map((mask: IFacemask) => {
return { uuid: mask.uuid, intro: mask.is_intro };
});
Expand Down Expand Up @@ -497,7 +370,7 @@ export class FacemasksService extends PersistentStatefulService<IFacemasksServic
}
}
} else {
this.SET_ACTIVE(false);
this.facemaskFilter = null;
}
}

Expand Down
21 changes: 14 additions & 7 deletions app/services/websocket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@ import { Subject } from 'rxjs/Subject';
export type TSocketEvent =
IStreamlabelsSocketEvent |
IDonationSocketEvent |
IFacemaskSocketEvent |
IFacemaskDonationSocketEvent |
IFollowSocketEvent |
ISubscriptionSocketEvent
ISubscriptionSocketEvent |
IAlertPlayingSocketEvent

interface IStreamlabelsSocketEvent {
type: 'streamlabels';
Expand All @@ -31,13 +32,11 @@ interface IDonationSocketEvent {
}[];
}

interface IFacemaskSocketEvent {
type: 'facemask';
interface IFacemaskDonationSocketEvent {
type: 'facemaskdonation';
message: {
name: string;
formattedAmount: string;
facemask: string;
message: string;
_id: string;
}[];
}

Expand All @@ -55,6 +54,14 @@ interface ISubscriptionSocketEvent {
}[];
}

interface IAlertPlayingSocketEvent {
type: 'alertPlaying';
message: {
facemask?: string;
_id: string;
}
}

export class WebsocketService extends Service {
@Inject() userService: UserService;
@Inject() hostsService: HostsService;
Expand Down
7 changes: 7 additions & 0 deletions app/services/widgets/widgets-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,13 @@ export const WidgetTesters: IWidgetTester[] = [
},
platforms: ['twitch', 'youtube', 'mixer']
},
{
name: 'Mask',
url(host) {
return `https://${host}/api/v5/slobs/test/streamlabs/facemaskdonation`;
},
platforms: ['twitch', 'youtube', 'mixer']
},
{
name: 'Bits',
url(host, platform) {
Expand Down
Loading

0 comments on commit 6ac45ea

Please sign in to comment.