Add "song_changed_live" Webhook trigger and restructure comomon Webhook components.

This commit is contained in:
Buster Neece 2022-11-27 03:49:58 -06:00
parent 5ae4c59976
commit 4170e9f026
No known key found for this signature in database
GPG Key ID: F1D2E64A0005E80E
17 changed files with 234 additions and 248 deletions

View File

@ -8,6 +8,8 @@ release channel, you can take advantage of these new features and fixes.
- On Mastodon and Twitter posts, you can now specify different message bodies for the different web hook trigger types (
i.e. live DJ connect/disconnect or station online/offline).
- Web Hooks can now also be dispatched specifically when a song changes _and_ a DJ/streamer is live.
## Code Quality/Technical Changes
## Bug Fixes

View File

@ -9,6 +9,7 @@ use App\Webhook\Connector;
$allTriggers = [
WebhookTriggers::SongChanged->value,
WebhookTriggers::SongChangedLive->value,
WebhookTriggers::ListenerGained->value,
WebhookTriggers::ListenerLost->value,
WebhookTriggers::LiveConnect->value,
@ -82,15 +83,4 @@ return [
'triggers' => [],
],
],
// The triggers that can be selected for a web hook to trigger.
'triggers' => [
WebhookTriggers::SongChanged->value => __('Any time the currently playing song changes'),
WebhookTriggers::ListenerGained->value => __('Any time the listener count increases'),
WebhookTriggers::ListenerLost->value => __('Any time the listener count decreases'),
WebhookTriggers::LiveConnect->value => __('Any time a live streamer/DJ connects to the stream'),
WebhookTriggers::LiveDisconnect->value => __('Any time a live streamer/DJ disconnects from the stream'),
WebhookTriggers::StationOffline->value => __('When the station broadcast goes offline.'),
WebhookTriggers::StationOnline->value => __('When the station broadcast comes online.'),
],
];

View File

@ -53,8 +53,8 @@
<streaming-log-modal ref="logModal"></streaming-log-modal>
<edit-modal ref="editModal" :create-url="listUrl" :webhook-types="webhookTypes"
:webhook-triggers="webhookTriggers" :now-playing-url="nowPlayingUrl"
@relist="relist"></edit-modal>
:trigger-titles="langTriggerTitles" :trigger-descriptions="langTriggerDescriptions"
:now-playing-url="nowPlayingUrl" @relist="relist"></edit-modal>
</div>
</template>
@ -72,8 +72,7 @@ export default {
props: {
listUrl: String,
nowPlayingUrl: String,
webhookTypes: Object,
webhookTriggers: Object
webhookTypes: Object
},
data() {
return {
@ -84,6 +83,32 @@ export default {
]
};
},
computed: {
langTriggerTitles() {
return {
song_changed: this.$gettext('Song Change'),
song_changed_live: this.$gettext('Song Change (Live Only)'),
listener_gained: this.$gettext('Listener Gained'),
listener_lost: this.$gettext('Listener Lost'),
live_connect: this.$gettext('Live Streamer/DJ Connected'),
live_disconnect: this.$gettext('Live Streamer/DJ Disconnected'),
station_offline: this.$gettext('Station Goes Offline'),
station_online: this.$gettext('Station Goes Online'),
}
},
langTriggerDescriptions() {
return {
song_changed: this.$gettext('Any time the currently playing song changes'),
song_changed_live: this.$gettext('When the song changes and a live streamer/DJ is connected'),
listener_gained: this.$gettext('Any time the listener count increases'),
listener_lost: this.$gettext('Any time the listener count decreases'),
live_connect: this.$gettext('Any time a live streamer/DJ connects to the stream'),
live_disconnect: this.$gettext('Any time a live streamer/DJ disconnects from the stream'),
station_offline: this.$gettext('When the station broadcast goes offline'),
station_online: this.$gettext('When the station broadcast comes online'),
}
}
},
methods: {
langToggleButton(record) {
return (record.is_enabled)
@ -100,7 +125,7 @@ export default {
},
getTriggerNames(triggers) {
return _.map(triggers, (trigger) => {
return _.get(this.webhookTriggers, trigger, '');
return _.get(this.langTriggerTitles, trigger, '');
});
},
relist() {

View File

@ -41,7 +41,8 @@ export default {
props: {
nowPlayingUrl: String,
webhookTypes: Object,
webhookTriggers: Object
triggerTitles: Object,
triggerDescriptions: Object
},
data() {
return {
@ -82,7 +83,9 @@ export default {
let webhookKeys = _.get(this.webhookTypes, [this.type, 'triggers'], []);
return _.map(webhookKeys, (key) => {
return {
text: this.webhookTriggers[key],
html:
'<h6 class="font-weight-bold mb-0">' + this.triggerTitles[key] + '</h6>'
+ '<p class="card-text small">' + this.triggerDescriptions[key] + '</p>',
value: key
};
});
@ -185,6 +188,7 @@ export default {
token_secret: {required},
rate_limit: {},
message: {},
message_song_changed_live: {},
message_live_connect: {},
message_live_disconnect: {},
message_station_offline: {},
@ -197,6 +201,7 @@ export default {
token_secret: '',
rate_limit: 0,
message: this.langTwitterDefaultMessage,
message_song_changed_live: this.langTwitterSongChangedLiveMessage,
message_live_connect: this.langTwitterDjOnMessage,
message_live_disconnect: this.langTwitterDjOffMessage,
message_station_offline: this.langTwitterStationOfflineMessage,
@ -211,6 +216,7 @@ export default {
rate_limit: {},
visibility: {required},
message: {},
message_song_changed_live: {},
message_live_connect: {},
message_live_disconnect: {},
message_station_offline: {},
@ -222,6 +228,7 @@ export default {
rate_limit: 0,
visibility: 'public',
message: this.langTwitterDefaultMessage,
message_song_changed_live: this.langTwitterSongChangedLiveMessage,
message_live_connect: this.langTwitterDjOnMessage,
message_live_disconnect: this.langTwitterDjOffMessage,
message_station_offline: this.langTwitterStationOfflineMessage,
@ -276,6 +283,16 @@ export default {
url: '{{ station.public_player_url }}'
});
},
langTwitterSongChangedLiveMessage() {
let msg = this.$gettext('Now playing on %{ station }: %{ title } by %{ artist } with your host, %{ dj }! Tune in now: %{ url }');
return this.$gettextInterpolate(msg, {
station: '{{ station.name }}',
title: '{{ now_playing.song.title }}',
artist: '{{ now_playing.song.artist }}',
dj: '{{ live.streamer_name }}',
url: '{{ station.public_player_url }}'
});
},
langTwitterDjOnMessage() {
let msg = this.$gettext('%{ dj } is now live on %{ station }! Tune in now: %{ url }');
return this.$gettextInterpolate(msg, {

View File

@ -0,0 +1,75 @@
<template>
<b-wrapped-form-group class="col-md-12" id="form_config_rate_limit" :field="form.config.rate_limit">
<template #label="{lang}">
<translate :key="lang">Only Post Once Every...</translate>
</template>
<template #default="props">
<b-form-select :id="props.id" :options="rateLimitOptions" v-model="props.field.$model">
</b-form-select>
</template>
</b-wrapped-form-group>
</template>
<script>
import BWrappedFormGroup from "~/components/Form/BWrappedFormGroup";
export default {
name: 'CommonRateLimitFields',
components: {BWrappedFormGroup},
props: {
form: Object
},
computed: {
langSeconds() {
return this.$gettext('%{ seconds } seconds');
},
langMinutes() {
return this.$gettext('%{ minutes } minutes');
},
rateLimitOptions() {
return [
{
text: this.$gettext('No Limit'),
value: 0,
},
{
text: this.$gettextInterpolate(this.langSeconds, {seconds: 15}),
value: 15,
},
{
text: this.$gettextInterpolate(this.langSeconds, {seconds: 30}),
value: 30,
},
{
text: this.$gettextInterpolate(this.langSeconds, {seconds: 60}),
value: 60,
},
{
text: this.$gettextInterpolate(this.langMinutes, {minutes: 2}),
value: 120,
},
{
text: this.$gettextInterpolate(this.langMinutes, {minutes: 5}),
value: 300,
},
{
text: this.$gettextInterpolate(this.langMinutes, {minutes: 10}),
value: 600,
},
{
text: this.$gettextInterpolate(this.langMinutes, {minutes: 15}),
value: 900,
},
{
text: this.$gettextInterpolate(this.langMinutes, {minutes: 30}),
value: 1800,
},
{
text: this.$gettextInterpolate(this.langMinutes, {minutes: 60}),
value: 3600,
}
];
},
}
}
</script>

View File

@ -0,0 +1,79 @@
<template>
<div>
<common-formatting-info :now-playing-url="nowPlayingUrl"></common-formatting-info>
<b-form-group>
<b-form-row>
<template v-if="hasTrigger('song_changed')">
<b-wrapped-form-group class="col-md-12" id="form_config_message" :field="form.config.message"
input-type="textarea">
<template #label="{lang}">
<translate :key="lang">Message Body on Song Change</translate>
</template>
</b-wrapped-form-group>
</template>
<template v-if="hasTrigger('song_changed_live')">
<b-wrapped-form-group class="col-md-12" id="form_config_message_song_changed_live"
:field="form.config.message_song_changed_live"
input-type="textarea">
<template #label="{lang}">
<translate :key="lang">Message Body on Song Change with Stramer/DJ Connected</translate>
</template>
</b-wrapped-form-group>
</template>
<template v-if="hasTrigger('live_connect')">
<b-wrapped-form-group class="col-md-12" id="form_config_message_live_connect"
:field="form.config.message_live_connect" input-type="textarea">
<template #label="{lang}">
<translate :key="lang">Message Body on Streamer/DJ Connect</translate>
</template>
</b-wrapped-form-group>
</template>
<template v-if="hasTrigger('live_disconnect')">
<b-wrapped-form-group class="col-md-12" id="form_config_message_live_disconnect"
:field="form.config.message_live_disconnect" input-type="textarea">
<template #label="{lang}">
<translate :key="lang">Message Body on Streamer/DJ Disconnect</translate>
</template>
</b-wrapped-form-group>
</template>
<template v-if="hasTrigger('station_offline')">
<b-wrapped-form-group class="col-md-12" id="form_config_message_station_offline"
:field="form.config.message_station_offline" input-type="textarea">
<template #label="{lang}">
<translate :key="lang">Message Body on Station Offline</translate>
</template>
</b-wrapped-form-group>
</template>
<template v-if="hasTrigger('station_online')">
<b-wrapped-form-group class="col-md-12" id="form_config_message_station_online"
:field="form.config.message_station_online" input-type="textarea">
<template #label="{lang}">
<translate :key="lang">Message Body on Station Online</translate>
</template>
</b-wrapped-form-group>
</template>
</b-form-row>
</b-form-group>
</div>
</template>
<script>
import BWrappedFormGroup from "~/components/Form/BWrappedFormGroup";
import CommonFormattingInfo from "./FormattingInfo";
import _ from 'lodash';
export default {
name: 'CommonSocialPostFields',
components: {CommonFormattingInfo, BWrappedFormGroup},
props: {
form: Object,
nowPlayingUrl: String
},
methods: {
hasTrigger(trigger) {
return _.includes(this.form.triggers.$model, trigger);
}
}
}
</script>

View File

@ -67,7 +67,7 @@
<script>
import BWrappedFormGroup from "~/components/Form/BWrappedFormGroup";
import CommonFormattingInfo from "./CommonFormattingInfo";
import CommonFormattingInfo from "./Common/FormattingInfo";
export default {
name: 'Discord',

View File

@ -35,7 +35,7 @@
<script>
import BWrappedFormGroup from "~/components/Form/BWrappedFormGroup";
import CommonFormattingInfo from "./CommonFormattingInfo";
import CommonFormattingInfo from "./Common/FormattingInfo";
export default {
name: 'Email',

View File

@ -46,20 +46,10 @@
</template>
</b-wrapped-form-group>
<b-wrapped-form-group class="col-md-12" id="form_config_rate_limit" :field="form.config.rate_limit">
<template #label="{lang}">
<translate :key="lang">Only Post Once Every...</translate>
</template>
<template #default="props">
<b-form-select :id="props.id" :options="rateLimitOptions" v-model="props.field.$model">
</b-form-select>
</template>
</b-wrapped-form-group>
<common-rate-limit-fields :form="form"></common-rate-limit-fields>
</b-form-row>
</b-form-group>
<common-formatting-info :now-playing-url="nowPlayingUrl"></common-formatting-info>
<b-form-group>
<b-form-row>
<b-wrapped-form-group class="col-md-12" id="form_config_visibility" :field="form.config.visibility">
@ -72,115 +62,32 @@
</b-form-radio-group>
</template>
</b-wrapped-form-group>
<template v-if="hasTrigger('song_changed')">
<b-wrapped-form-group class="col-md-12" id="form_config_message" :field="form.config.message"
input-type="textarea">
<template #label="{lang}">
<translate :key="lang">Message Body on Song Change</translate>
</template>
</b-wrapped-form-group>
</template>
<template v-if="hasTrigger('live_connect')">
<b-wrapped-form-group class="col-md-12" id="form_config_message_live_connect"
:field="form.config.message_live_connect" input-type="textarea">
<template #label="{lang}">
<translate :key="lang">Message Body on Streamer/DJ Connect</translate>
</template>
</b-wrapped-form-group>
</template>
<template v-if="hasTrigger('live_disconnect')">
<b-wrapped-form-group class="col-md-12" id="form_config_message_live_disconnect"
:field="form.config.message_live_disconnect" input-type="textarea">
<template #label="{lang}">
<translate :key="lang">Message Body on Streamer/DJ Disconnect</translate>
</template>
</b-wrapped-form-group>
</template>
<template v-if="hasTrigger('station_offline')">
<b-wrapped-form-group class="col-md-12" id="form_config_message_station_offline"
:field="form.config.message_station_offline" input-type="textarea">
<template #label="{lang}">
<translate :key="lang">Message Body on Station Offline</translate>
</template>
</b-wrapped-form-group>
</template>
<template v-if="hasTrigger('station_online')">
<b-wrapped-form-group class="col-md-12" id="form_config_message_station_online"
:field="form.config.message_station_online" input-type="textarea">
<template #label="{lang}">
<translate :key="lang">Message Body on Station Online</translate>
</template>
</b-wrapped-form-group>
</template>
</b-form-row>
</b-form-group>
<common-social-post-fields :form="form" :now-playing-url="nowPlayingUrl"></common-social-post-fields>
</div>
</template>
<script>
import BWrappedFormGroup from "~/components/Form/BWrappedFormGroup";
import CommonFormattingInfo from "./CommonFormattingInfo";
import _ from 'lodash';
import RateLimitFields from "./Common/RateLimitFields";
import CommonRateLimitFields from "./Common/RateLimitFields";
import CommonSocialPostFields from "./Common/SocialPostFields";
export default {
name: 'Twitter',
components: {CommonFormattingInfo, BWrappedFormGroup},
name: 'Mastodon',
components: {
CommonRateLimitFields,
CommonSocialPostFields,
RateLimitFields,
BWrappedFormGroup
},
props: {
form: Object,
nowPlayingUrl: String
},
computed: {
langSeconds() {
return this.$gettext('%{ seconds } seconds');
},
langMinutes() {
return this.$gettext('%{ minutes } minutes');
},
rateLimitOptions() {
return [
{
text: this.$gettext('No Limit'),
value: 0,
},
{
text: this.$gettextInterpolate(this.langSeconds, {seconds: 15}),
value: 15,
},
{
text: this.$gettextInterpolate(this.langSeconds, {seconds: 30}),
value: 30,
},
{
text: this.$gettextInterpolate(this.langSeconds, {seconds: 60}),
value: 60,
},
{
text: this.$gettextInterpolate(this.langMinutes, {minutes: 2}),
value: 120,
},
{
text: this.$gettextInterpolate(this.langMinutes, {minutes: 5}),
value: 300,
},
{
text: this.$gettextInterpolate(this.langMinutes, {minutes: 10}),
value: 600,
},
{
text: this.$gettextInterpolate(this.langMinutes, {minutes: 15}),
value: 900,
},
{
text: this.$gettextInterpolate(this.langMinutes, {minutes: 30}),
value: 1800,
},
{
text: this.$gettextInterpolate(this.langMinutes, {minutes: 60}),
value: 3600,
}
];
},
visibilityOptions() {
return [
{
@ -197,11 +104,6 @@ export default {
}
];
}
},
methods: {
hasTrigger(trigger) {
return _.includes(this.form.triggers.$model, trigger);
}
}
}
</script>

View File

@ -67,7 +67,7 @@
<script>
import BWrappedFormGroup from "~/components/Form/BWrappedFormGroup";
import CommonFormattingInfo from "./CommonFormattingInfo";
import CommonFormattingInfo from "./Common/FormattingInfo";
export default {
name: 'Telegram',

View File

@ -55,135 +55,25 @@
</template>
</b-wrapped-form-group>
<b-wrapped-form-group class="col-md-12" id="form_config_rate_limit" :field="form.config.rate_limit">
<template #label="{lang}">
<translate :key="lang">Only Send One Tweet Every...</translate>
</template>
<template #default="props">
<b-form-select :id="props.id" :options="rateLimitOptions" v-model="props.field.$model">
</b-form-select>
</template>
</b-wrapped-form-group>
<common-rate-limit-fields :form="form"></common-rate-limit-fields>
</b-form-row>
</b-form-group>
<common-formatting-info :now-playing-url="nowPlayingUrl"></common-formatting-info>
<b-form-group>
<b-form-row>
<template v-if="hasTrigger('song_changed')">
<b-wrapped-form-group class="col-md-12" id="form_config_message" :field="form.config.message"
input-type="textarea">
<template #label="{lang}">
<translate :key="lang">Message Body on Song Change</translate>
</template>
</b-wrapped-form-group>
</template>
<template v-if="hasTrigger('live_connect')">
<b-wrapped-form-group class="col-md-12" id="form_config_message_live_connect"
:field="form.config.message_live_connect" input-type="textarea">
<template #label="{lang}">
<translate :key="lang">Message Body on Streamer/DJ Connect</translate>
</template>
</b-wrapped-form-group>
</template>
<template v-if="hasTrigger('live_disconnect')">
<b-wrapped-form-group class="col-md-12" id="form_config_message_live_disconnect"
:field="form.config.message_live_disconnect" input-type="textarea">
<template #label="{lang}">
<translate :key="lang">Message Body on Streamer/DJ Disconnect</translate>
</template>
</b-wrapped-form-group>
</template>
<template v-if="hasTrigger('station_offline')">
<b-wrapped-form-group class="col-md-12" id="form_config_message_station_offline"
:field="form.config.message_station_offline" input-type="textarea">
<template #label="{lang}">
<translate :key="lang">Message Body on Station Offline</translate>
</template>
</b-wrapped-form-group>
</template>
<template v-if="hasTrigger('station_online')">
<b-wrapped-form-group class="col-md-12" id="form_config_message_station_online"
:field="form.config.message_station_online" input-type="textarea">
<template #label="{lang}">
<translate :key="lang">Message Body on Station Online</translate>
</template>
</b-wrapped-form-group>
</template>
</b-form-row>
</b-form-group>
<common-social-post-fields :form="form" :now-playing-url="nowPlayingUrl"></common-social-post-fields>
</div>
</template>
<script>
import BWrappedFormGroup from "~/components/Form/BWrappedFormGroup";
import CommonFormattingInfo from "./CommonFormattingInfo";
import _ from "lodash";
import CommonRateLimitFields from "./Common/RateLimitFields";
import CommonSocialPostFields from "./Common/SocialPostFields";
export default {
name: 'Twitter',
components: {CommonFormattingInfo, BWrappedFormGroup},
components: {CommonSocialPostFields, CommonRateLimitFields, BWrappedFormGroup},
props: {
form: Object,
nowPlayingUrl: String
},
computed: {
langSeconds() {
return this.$gettext('%{ seconds } seconds');
},
langMinutes() {
return this.$gettext('%{ minutes } minutes');
},
rateLimitOptions() {
return [
{
text: this.$gettext('No Limit'),
value: 0,
},
{
text: this.$gettextInterpolate(this.langSeconds, {seconds: 15}),
value: 15,
},
{
text: this.$gettextInterpolate(this.langSeconds, {seconds: 30}),
value: 30,
},
{
text: this.$gettextInterpolate(this.langSeconds, {seconds: 60}),
value: 60,
},
{
text: this.$gettextInterpolate(this.langMinutes, {minutes: 2}),
value: 120,
},
{
text: this.$gettextInterpolate(this.langMinutes, {minutes: 5}),
value: 300,
},
{
text: this.$gettextInterpolate(this.langMinutes, {minutes: 10}),
value: 600,
},
{
text: this.$gettextInterpolate(this.langMinutes, {minutes: 15}),
value: 900,
},
{
text: this.$gettextInterpolate(this.langMinutes, {minutes: 30}),
value: 1800,
},
{
text: this.$gettextInterpolate(this.langMinutes, {minutes: 60}),
value: 3600,
}
];
}
},
methods: {
hasTrigger(trigger) {
return _.includes(this.form.triggers.$model, trigger);
}
}
}
</script>

View File

@ -38,7 +38,6 @@ final class WebhooksAction
'listUrl' => $router->fromHere('api:stations:webhooks'),
'nowPlayingUrl' => $router->fromHere('api:nowplaying:index'),
'webhookTypes' => $webhookConfig['webhooks'],
'webhookTriggers' => $webhookConfig['triggers'],
'enableAdvancedFeatures' => $settings->getEnableAdvancedFeatures(),
]
);

View File

@ -7,6 +7,7 @@ namespace App\Entity\Enums;
enum WebhookTriggers: string
{
case SongChanged = 'song_changed';
case SongChangedLive = 'song_changed_live';
case ListenerGained = 'listener_gained';
case ListenerLost = 'listener_lost';
case LiveConnect = 'live_connect';

View File

@ -206,6 +206,10 @@ final class NowPlayingTask implements NowPlayingTaskInterface, EventSubscriberIn
if ($npOld->now_playing?->song?->id !== $np->now_playing?->song?->id) {
$triggers[] = WebhookTriggers::SongChanged->value;
if ($np->live->is_live) {
$triggers[] = WebhookTriggers::SongChangedLive->value;
}
}
}

View File

@ -45,6 +45,7 @@ final class Mastodon extends AbstractConnector
$messages = [
WebhookTriggers::SongChanged->value => $config['message'] ?? '',
WebhookTriggers::SongChangedLive->value => $config['message_song_changed_live'] ?? '',
WebhookTriggers::LiveConnect->value => $config['message_live_connect'] ?? '',
WebhookTriggers::LiveDisconnect->value => $config['message_live_disconnect'] ?? '',
WebhookTriggers::StationOffline->value => $config['message_station_offline'] ?? '',

View File

@ -69,6 +69,7 @@ final class Twitter extends AbstractConnector
// Dispatch webhook
$messages = [
WebhookTriggers::SongChanged->value => $config['message'] ?? '',
WebhookTriggers::SongChangedLive->value => $config['message_song_changed_live'] ?? '',
WebhookTriggers::LiveConnect->value => $config['message_live_connect'] ?? '',
WebhookTriggers::LiveDisconnect->value => $config['message_live_disconnect'] ?? '',
WebhookTriggers::StationOffline->value => $config['message_station_offline'] ?? '',