summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGabe Kangas <gabek@real-ity.com>2022-01-18 19:29:00 -0800
committerGabe Kangas <gabek@real-ity.com>2022-01-18 19:29:00 -0800
commit627169fc73319481b60872830d120954e0a28896 (patch)
tree8e2788ed6022e00511f2fe83ccecb50789475f70
parenta621e920e7e10f4b6e8054f7135abe6d650b129e (diff)
Add support for established user mode. #1587
-rw-r--r--config/defaults.go10
-rw-r--r--controllers/admin/chat.go22
-rw-r--r--controllers/admin/serverConfig.go46
-rw-r--r--core/chat/server.go11
-rw-r--r--core/data/config.go94
-rw-r--r--router/router.go3
-rw-r--r--test/automated/api/chatmoderation.test.js37
-rw-r--r--test/automated/api/package-lock.json2
8 files changed, 160 insertions, 65 deletions
diff --git a/config/defaults.go b/config/defaults.go
index 6b5bd1d90..964756af1 100644
--- a/config/defaults.go
+++ b/config/defaults.go
@@ -1,6 +1,10 @@
package config
-import "github.com/owncast/owncast/models"
+import (
+ "time"
+
+ "github.com/owncast/owncast/models"
+)
// Defaults will hold default configuration values.
type Defaults struct {
@@ -27,6 +31,8 @@ type Defaults struct {
FederationUsername string
FederationGoLiveMessage string
+
+ ChatEstablishedUserModeTimeDuration time.Duration
}
// GetDefaults will return default configuration values.
@@ -54,6 +60,8 @@ func GetDefaults() Defaults {
RTMPServerPort: 1935,
StreamKey: "abc123",
+ ChatEstablishedUserModeTimeDuration: time.Minute * 15,
+
StreamVariants: []models.StreamOutputVariant{
{
IsAudioPassthrough: true,
diff --git a/controllers/admin/chat.go b/controllers/admin/chat.go
index 91bdba132..59d9d197a 100644
--- a/controllers/admin/chat.go
+++ b/controllers/admin/chat.go
@@ -12,6 +12,7 @@ import (
"github.com/owncast/owncast/controllers"
"github.com/owncast/owncast/core/chat"
"github.com/owncast/owncast/core/chat/events"
+ "github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/core/user"
"github.com/owncast/owncast/utils"
log "github.com/sirupsen/logrus"
@@ -267,3 +268,24 @@ func SendChatAction(integration user.ExternalAPIUser, w http.ResponseWriter, r *
controllers.WriteSimpleResponse(w, true, "sent")
}
+
+// SetEnableEstablishedChatUserMode sets the requirement for a chat user
+// to be "established" for some time before taking part in chat.
+func SetEnableEstablishedChatUserMode(w http.ResponseWriter, r *http.Request) {
+ if !requirePOST(w, r) {
+ return
+ }
+
+ configValue, success := getValueFromRequest(w, r)
+ if !success {
+ controllers.WriteSimpleResponse(w, false, "unable to update chat established user only mode")
+ return
+ }
+
+ if err := data.SetChatEstablishedUsersOnlyMode(configValue.Value.(bool)); err != nil {
+ controllers.WriteSimpleResponse(w, false, err.Error())
+ return
+ }
+
+ controllers.WriteSimpleResponse(w, true, "chat established users only mode updated")
+}
diff --git a/controllers/admin/serverConfig.go b/controllers/admin/serverConfig.go
index 046dec6b6..0b22d8ddd 100644
--- a/controllers/admin/serverConfig.go
+++ b/controllers/admin/serverConfig.go
@@ -46,12 +46,13 @@ func GetServerConfig(w http.ResponseWriter, r *http.Request) {
NSFW: data.GetNSFW(),
CustomStyles: data.GetCustomStyles(),
},
- FFmpegPath: ffmpeg,
- StreamKey: data.GetStreamKey(),
- WebServerPort: config.WebServerPort,
- WebServerIP: config.WebServerIP,
- RTMPServerPort: data.GetRTMPPortNumber(),
- ChatDisabled: data.GetChatDisabled(),
+ FFmpegPath: ffmpeg,
+ StreamKey: data.GetStreamKey(),
+ WebServerPort: config.WebServerPort,
+ WebServerIP: config.WebServerIP,
+ RTMPServerPort: data.GetRTMPPortNumber(),
+ ChatDisabled: data.GetChatDisabled(),
+ ChatEstablishedUserMode: data.GetChatEstbalishedUsersOnlyMode(),
VideoSettings: videoSettings{
VideoQualityVariants: videoQualityVariants,
LatencyLevel: data.GetStreamLatencyLevel().Level,
@@ -85,22 +86,23 @@ func GetServerConfig(w http.ResponseWriter, r *http.Request) {
}
type serverConfigAdminResponse struct {
- InstanceDetails webConfigResponse `json:"instanceDetails"`
- FFmpegPath string `json:"ffmpegPath"`
- StreamKey string `json:"streamKey"`
- WebServerPort int `json:"webServerPort"`
- WebServerIP string `json:"webServerIP"`
- RTMPServerPort int `json:"rtmpServerPort"`
- S3 models.S3 `json:"s3"`
- VideoSettings videoSettings `json:"videoSettings"`
- YP yp `json:"yp"`
- ChatDisabled bool `json:"chatDisabled"`
- ExternalActions []models.ExternalAction `json:"externalActions"`
- SupportedCodecs []string `json:"supportedCodecs"`
- VideoCodec string `json:"videoCodec"`
- ForbiddenUsernames []string `json:"forbiddenUsernames"`
- Federation federationConfigResponse `json:"federation"`
- SuggestedUsernames []string `json:"suggestedUsernames"`
+ InstanceDetails webConfigResponse `json:"instanceDetails"`
+ FFmpegPath string `json:"ffmpegPath"`
+ StreamKey string `json:"streamKey"`
+ WebServerPort int `json:"webServerPort"`
+ WebServerIP string `json:"webServerIP"`
+ RTMPServerPort int `json:"rtmpServerPort"`
+ S3 models.S3 `json:"s3"`
+ VideoSettings videoSettings `json:"videoSettings"`
+ YP yp `json:"yp"`
+ ChatDisabled bool `json:"chatDisabled"`
+ ChatEstablishedUserMode bool `json:"chatEstablishedUserMode"`
+ ExternalActions []models.ExternalAction `json:"externalActions"`
+ SupportedCodecs []string `json:"supportedCodecs"`
+ VideoCodec string `json:"videoCodec"`
+ ForbiddenUsernames []string `json:"forbiddenUsernames"`
+ Federation federationConfigResponse `json:"federation"`
+ SuggestedUsernames []string `json:"suggestedUsernames"`
}
type videoSettings struct {
diff --git a/core/chat/server.go b/core/chat/server.go
index 08c0317c9..8fd939faf 100644
--- a/core/chat/server.go
+++ b/core/chat/server.go
@@ -11,6 +11,7 @@ import (
"github.com/gorilla/websocket"
+ "github.com/owncast/owncast/config"
"github.com/owncast/owncast/core/chat/events"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/core/user"
@@ -324,6 +325,16 @@ func SendActionToUser(userID string, text string) error {
}
func (s *Server) eventReceived(event chatClientEvent) {
+ c := event.client
+ u := c.User
+
+ // If established chat user only mode is enabled and the user is not old
+ // enough then reject this event and send them an informative message.
+ if u != nil && data.GetChatEstbalishedUsersOnlyMode() && time.Since(event.client.User.CreatedAt) < config.GetDefaults().ChatEstablishedUserModeTimeDuration && !u.IsModerator() {
+ s.sendActionToClient(c, "You have not been an established chat participant long enough to take part in chat. Please enjoy the stream and try again later.")
+ return
+ }
+
var typecheck map[string]interface{}
if err := json.Unmarshal(event.data, &typecheck); err != nil {
log.Debugln(err)
diff --git a/core/data/config.go b/core/data/config.go
index cc5083743..c7c4865b9 100644
--- a/core/data/config.go
+++ b/core/data/config.go
@@ -14,45 +14,46 @@ import (
)
const (
- extraContentKey = "extra_page_content"
- streamTitleKey = "stream_title"
- streamKeyKey = "stream_key"
- logoPathKey = "logo_path"
- serverSummaryKey = "server_summary"
- serverWelcomeMessageKey = "server_welcome_message"
- serverNameKey = "server_name"
- serverURLKey = "server_url"
- httpPortNumberKey = "http_port_number"
- httpListenAddressKey = "http_listen_address"
- rtmpPortNumberKey = "rtmp_port_number"
- serverMetadataTagsKey = "server_metadata_tags"
- directoryEnabledKey = "directory_enabled"
- directoryRegistrationKeyKey = "directory_registration_key"
- socialHandlesKey = "social_handles"
- peakViewersSessionKey = "peak_viewers_session"
- peakViewersOverallKey = "peak_viewers_overall"
- lastDisconnectTimeKey = "last_disconnect_time"
- ffmpegPathKey = "ffmpeg_path"
- nsfwKey = "nsfw"
- s3StorageEnabledKey = "s3_storage_enabled"
- s3StorageConfigKey = "s3_storage_config"
- videoLatencyLevel = "video_latency_level"
- videoStreamOutputVariantsKey = "video_stream_output_variants"
- chatDisabledKey = "chat_disabled"
- externalActionsKey = "external_actions"
- customStylesKey = "custom_styles"
- videoCodecKey = "video_codec"
- blockedUsernamesKey = "blocked_usernames"
- publicKeyKey = "public_key"
- privateKeyKey = "private_key"
- serverInitDateKey = "server_init_date"
- federationEnabledKey = "federation_enabled"
- federationUsernameKey = "federation_username"
- federationPrivateKey = "federation_private"
- federationGoLiveMessageKey = "federation_go_live_message"
- federationShowEngagementKey = "federation_show_engagement"
- federationBlockedDomainsKey = "federation_blocked_domains"
- suggestedUsernamesKey = "suggested_usernames"
+ extraContentKey = "extra_page_content"
+ streamTitleKey = "stream_title"
+ streamKeyKey = "stream_key"
+ logoPathKey = "logo_path"
+ serverSummaryKey = "server_summary"
+ serverWelcomeMessageKey = "server_welcome_message"
+ serverNameKey = "server_name"
+ serverURLKey = "server_url"
+ httpPortNumberKey = "http_port_number"
+ httpListenAddressKey = "http_listen_address"
+ rtmpPortNumberKey = "rtmp_port_number"
+ serverMetadataTagsKey = "server_metadata_tags"
+ directoryEnabledKey = "directory_enabled"
+ directoryRegistrationKeyKey = "directory_registration_key"
+ socialHandlesKey = "social_handles"
+ peakViewersSessionKey = "peak_viewers_session"
+ peakViewersOverallKey = "peak_viewers_overall"
+ lastDisconnectTimeKey = "last_disconnect_time"
+ ffmpegPathKey = "ffmpeg_path"
+ nsfwKey = "nsfw"
+ s3StorageEnabledKey = "s3_storage_enabled"
+ s3StorageConfigKey = "s3_storage_config"
+ videoLatencyLevel = "video_latency_level"
+ videoStreamOutputVariantsKey = "video_stream_output_variants"
+ chatDisabledKey = "chat_disabled"
+ chatEstablishedUsersOnlyModeKey = "chat_established_users_only_mode"
+ externalActionsKey = "external_actions"
+ customStylesKey = "custom_styles"
+ videoCodecKey = "video_codec"
+ blockedUsernamesKey = "blocked_usernames"
+ publicKeyKey = "public_key"
+ privateKeyKey = "private_key"
+ serverInitDateKey = "server_init_date"
+ federationEnabledKey = "federation_enabled"
+ federationUsernameKey = "federation_username"
+ federationPrivateKey = "federation_private"
+ federationGoLiveMessageKey = "federation_go_live_message"
+ federationShowEngagementKey = "federation_show_engagement"
+ federationBlockedDomainsKey = "federation_blocked_domains"
+ suggestedUsernamesKey = "suggested_usernames"
)
// GetExtraPageBodyContent will return the user-supplied body content.
@@ -487,6 +488,21 @@ func GetChatDisabled() bool {
return false
}
+// SetChatEstablishedUsersOnlyMode sets the state of established user only mode.
+func SetChatEstablishedUsersOnlyMode(enabled bool) error {
+ return _datastore.SetBool(chatEstablishedUsersOnlyModeKey, enabled)
+}
+
+// GetChatEstbalishedUsersOnlyMode returns the state of established user only mode.
+func GetChatEstbalishedUsersOnlyMode() bool {
+ enabled, err := _datastore.GetBool(chatEstablishedUsersOnlyModeKey)
+ if err == nil {
+ return enabled
+ }
+
+ return false
+}
+
// GetExternalActions will return the registered external actions.
func GetExternalActions() []models.ExternalAction {
configEntry, err := _datastore.Get(externalActionsKey)
diff --git a/router/router.go b/router/router.go
index 3928675d1..001afacae 100644
--- a/router/router.go
+++ b/router/router.go
@@ -161,6 +161,9 @@ func Start() error {
// Disable chat
http.HandleFunc("/api/admin/config/chat/disable", middleware.RequireAdminAuth(admin.SetChatDisabled))
+ // Enable/disable chat established user mode
+ http.HandleFunc("/api/admin/config/chat/establishedusermode", middleware.RequireAdminAuth(admin.SetEnableEstablishedChatUserMode))
+
// Set chat usernames that are not allowed
http.HandleFunc("/api/admin/config/chat/forbiddenusernames", middleware.RequireAdminAuth(admin.SetForbiddenUsernameList))
diff --git a/test/automated/api/chatmoderation.test.js b/test/automated/api/chatmoderation.test.js
index 1f01e1c99..b8f19f9f4 100644
--- a/test/automated/api/chatmoderation.test.js
+++ b/test/automated/api/chatmoderation.test.js
@@ -12,6 +12,11 @@ const testVisibilityMessage = {
type: 'CHAT',
};
+const establishedUserFailedChatMessage = {
+ body: 'this message should fail to send ' + Math.floor(Math.random() * 100),
+ type: 'CHAT',
+};
+
test('can send a chat message', async (done) => {
const registration = await registerChat();
const accessToken = registration.accessToken;
@@ -63,9 +68,39 @@ test('verify message has become hidden', async (done) => {
.auth('admin', 'abc123');
const message = res.body.filter((obj) => {
- return obj.body === `${testVisibilityMessage.body}`;
+ return obj.body === testVisibilityMessage.body;
});
expect(message.length).toBe(1);
expect(message[0].hiddenAt).toBeTruthy();
done();
});
+
+test('can enable established chat user mode', async (done) => {
+ await request
+ .post('/api/admin/config/chat/establishedusermode')
+ .auth('admin', 'abc123')
+ .send({ value: true })
+ .expect(200);
+ done();
+});
+
+test('can send a message after established user mode is enabled', async (done) => {
+ const registration = await registerChat();
+ const accessToken = registration.accessToken;
+
+ sendChatMessage(establishedUserFailedChatMessage, accessToken, done);
+});
+
+test('verify message is not in the chat feed', async (done) => {
+ const res = await request
+ .get('/api/admin/chat/messages')
+ .expect(200)
+ .auth('admin', 'abc123');
+
+ const message = res.body.filter((obj) => {
+ return obj.body === establishedUserFailedChatMessage.body;
+ });
+
+ expect(message.length).toBe(0);
+ done();
+});
diff --git a/test/automated/api/package-lock.json b/test/automated/api/package-lock.json
index 4e795f10d..b57527dcf 100644
--- a/test/automated/api/package-lock.json
+++ b/test/automated/api/package-lock.json
@@ -692,7 +692,6 @@
"jest-resolve": "^26.6.2",
"jest-util": "^26.6.2",
"jest-worker": "^26.6.2",
- "node-notifier": "^8.0.0",
"slash": "^3.0.0",
"source-map": "^0.6.0",
"string-length": "^4.0.1",
@@ -3145,7 +3144,6 @@
"@types/node": "*",
"anymatch": "^3.0.3",
"fb-watchman": "^2.0.0",
- "fsevents": "^2.1.2",
"graceful-fs": "^4.2.4",
"jest-regex-util": "^26.0.0",
"jest-serializer": "^26.6.2",