Expanded linting + fix warnings (#1396)

* Expand the linters and types of warnings to improve consistency and safety

* Fail lint workflow if there are errors

* golint has been replaced by revive

* Hand-pick some of the default exclude list

* Ignore error when trying to delete preview gif

* Ignore linter warning opening playlist path

* Rename user field Id -> ID

* A bunch of renames to address linter warnings

* Rename ChatClient -> Client per linter suggestion best practice

* Rename ChatServer -> Server per linter suggestion best practice

* More linter warning fixes

* Add missing comments to all exported functions and properties
This commit is contained in:
Gabe Kangas
2021-09-12 00:18:15 -07:00
committed by GitHub
parent 70e9f4945f
commit c6c6f0233d
57 changed files with 331 additions and 186 deletions

View File

@@ -12,6 +12,7 @@ import (
var getStatus func() models.Status
// Start begins the chat server.
func Start(getStatusFunc func() models.Status) error {
setupPersistence()
@@ -26,11 +27,11 @@ func Start(getStatusFunc func() models.Status) error {
}
// GetClientsForUser will return chat connections that are owned by a specific user.
func GetClientsForUser(userID string) ([]*ChatClient, error) {
clients := map[string][]*ChatClient{}
func GetClientsForUser(userID string) ([]*Client, error) {
clients := map[string][]*Client{}
for _, client := range _server.clients {
clients[client.User.Id] = append(clients[client.User.Id], client)
clients[client.User.ID] = append(clients[client.User.ID], client)
}
if _, exists := clients[userID]; !exists {
@@ -40,8 +41,9 @@ func GetClientsForUser(userID string) ([]*ChatClient, error) {
return clients[userID], nil
}
func GetClients() []*ChatClient {
clients := []*ChatClient{}
// GetClients will return all the current chat clients connected.
func GetClients() []*Client {
clients := []*Client{}
// Convert the keyed map to a slice.
for _, client := range _server.clients {
@@ -55,6 +57,7 @@ func GetClients() []*ChatClient {
return clients
}
// SendSystemMessage will send a message string as a system message to all clients.
func SendSystemMessage(text string, ephemeral bool) error {
message := events.SystemMessageEvent{
MessageEvent: events.MessageEvent{
@@ -69,12 +72,13 @@ func SendSystemMessage(text string, ephemeral bool) error {
}
if !ephemeral {
saveEvent(message.Id, "system", message.Body, message.GetMessageType(), nil, message.Timestamp)
saveEvent(message.ID, "system", message.Body, message.GetMessageType(), nil, message.Timestamp)
}
return nil
}
// SendSystemAction will send a system action string as an action event to all clients.
func SendSystemAction(text string, ephemeral bool) error {
message := events.ActionEvent{
MessageEvent: events.MessageEvent{
@@ -90,20 +94,23 @@ func SendSystemAction(text string, ephemeral bool) error {
}
if !ephemeral {
saveEvent(message.Id, "action", message.Body, message.GetMessageType(), nil, message.Timestamp)
saveEvent(message.ID, "action", message.Body, message.GetMessageType(), nil, message.Timestamp)
}
return nil
}
// SendAllWelcomeMessage will send the chat message to all connected clients.
func SendAllWelcomeMessage() {
_server.sendAllWelcomeMessage()
}
// Broadcast will send all connected clients the outbound object provided.
func Broadcast(event events.OutboundEvent) error {
return _server.Broadcast(event.GetBroadcastPayload())
}
// HandleClientConnection handles a single inbound websocket connection.
func HandleClientConnection(w http.ResponseWriter, r *http.Request) {
_server.HandleClientConnection(w, r)
}

View File

@@ -15,12 +15,13 @@ import (
"github.com/owncast/owncast/geoip"
)
type ChatClient struct {
// Client represents a single chat client.
type Client struct {
id uint
accessToken string
conn *websocket.Conn
User *user.User `json:"user"`
server *ChatServer
server *Server
ipAddress string `json:"-"`
// Buffered channel of outbound messages.
send chan []byte
@@ -35,7 +36,7 @@ type ChatClient struct {
type chatClientEvent struct {
data []byte
client *ChatClient
client *Client
}
const (
@@ -64,7 +65,7 @@ var (
space = []byte{' '}
)
func (c *ChatClient) sendConnectedClientInfo() {
func (c *Client) sendConnectedClientInfo() {
payload := events.EventPayload{
"type": events.ConnectedUserInfo,
"user": c.User,
@@ -73,7 +74,7 @@ func (c *ChatClient) sendConnectedClientInfo() {
c.sendPayload(payload)
}
func (c *ChatClient) readPump() {
func (c *Client) readPump() {
// Allow 3 messages every two seconds.
limit := rate.Every(2 * time.Second / 3)
c.rateLimiter = rate.NewLimiter(limit, 1)
@@ -122,11 +123,11 @@ func (c *ChatClient) readPump() {
}
}
func (c *ChatClient) writePump() {
func (c *Client) writePump() {
ticker := time.NewTicker(pingPeriod)
defer func() {
ticker.Stop()
c.conn.Close()
_ = c.conn.Close()
}()
for {
@@ -167,14 +168,14 @@ func (c *ChatClient) writePump() {
}
}
func (c *ChatClient) handleEvent(data []byte) {
func (c *Client) handleEvent(data []byte) {
c.server.inbound <- chatClientEvent{data: data, client: c}
}
func (c *ChatClient) close() {
func (c *Client) close() {
log.Traceln("client closed:", c.User.DisplayName, c.id, c.ipAddress)
c.conn.Close()
_ = c.conn.Close()
c.server.unregister <- c.id
if c.send != nil {
close(c.send)
@@ -182,18 +183,18 @@ func (c *ChatClient) close() {
}
}
func (c *ChatClient) passesRateLimit() bool {
func (c *Client) passesRateLimit() bool {
return c.rateLimiter.Allow() && !c.inTimeout
}
func (c *ChatClient) startChatRejectionTimeout() {
func (c *Client) startChatRejectionTimeout() {
if c.timeoutTimer != nil {
return
}
c.inTimeout = true
c.timeoutTimer = time.NewTimer(10 * time.Second)
go func(c *ChatClient) {
go func(c *Client) {
for range c.timeoutTimer.C {
c.inTimeout = false
c.timeoutTimer = nil
@@ -203,7 +204,7 @@ func (c *ChatClient) startChatRejectionTimeout() {
c.sendAction("You are temporarily blocked from sending chat messages due to perceived flooding.")
}
func (c *ChatClient) sendPayload(payload events.EventPayload) {
func (c *Client) sendPayload(payload events.EventPayload) {
var data []byte
data, err := json.Marshal(payload)
if err != nil {
@@ -216,7 +217,7 @@ func (c *ChatClient) sendPayload(payload events.EventPayload) {
}
}
func (c *ChatClient) sendAction(message string) {
func (c *Client) sendAction(message string) {
clientMessage := events.ActionEvent{
MessageEvent: events.MessageEvent{
Body: message,

View File

@@ -1,3 +1,4 @@
// nolint:goimports
// +build !freebsd
package chat

View File

@@ -13,7 +13,7 @@ import (
log "github.com/sirupsen/logrus"
)
func (s *ChatServer) userNameChanged(eventData chatClientEvent) {
func (s *Server) userNameChanged(eventData chatClientEvent) {
var receivedEvent events.NameChangeEvent
if err := json.Unmarshal(eventData.data, &receivedEvent); err != nil {
log.Errorln("error unmarshalling to NameChangeEvent", err)
@@ -43,7 +43,7 @@ func (s *ChatServer) userNameChanged(eventData chatClientEvent) {
oldName := savedUser.DisplayName
// Save the new name
user.ChangeUsername(eventData.client.User.Id, receivedEvent.NewName)
user.ChangeUsername(eventData.client.User.ID, receivedEvent.NewName)
// Update the connected clients associated user with the new name
now := time.Now()
@@ -69,7 +69,7 @@ func (s *ChatServer) userNameChanged(eventData chatClientEvent) {
webhooks.SendChatEventUsernameChanged(receivedEvent)
}
func (s *ChatServer) userMessageSent(eventData chatClientEvent) {
func (s *Server) userMessageSent(eventData chatClientEvent) {
var event events.UserMessageEvent
if err := json.Unmarshal(eventData.data, &event); err != nil {
log.Errorln("error unmarshalling to UserMessageEvent", err)

View File

@@ -1,20 +1,22 @@
package events
// ActionEvent represents an action that took place, not a chat message.
type ActionEvent struct {
Event
MessageEvent
}
// ActionEvent will return the object to send to all chat users.
// GetBroadcastPayload will return the object to send to all chat users.
func (e *ActionEvent) GetBroadcastPayload() EventPayload {
return EventPayload{
"id": e.Id,
"id": e.ID,
"timestamp": e.Timestamp,
"body": e.Body,
"type": e.GetMessageType(),
}
}
// GetMessageType will return the type of message.
func (e *ActionEvent) GetMessageType() EventType {
return ChatActionSent
}

View File

@@ -20,6 +20,7 @@ import (
// EventPayload is a generic key/value map for sending out to chat clients.
type EventPayload map[string]interface{}
// OutboundEvent represents an event that is sent out to all listeners of the chat server.
type OutboundEvent interface {
GetBroadcastPayload() EventPayload
GetMessageType() EventType
@@ -28,10 +29,11 @@ type OutboundEvent interface {
// Event is any kind of event. A type is required to be specified.
type Event struct {
Type EventType `json:"type,omitempty"`
Id string `json:"id"`
ID string `json:"id"`
Timestamp time.Time `json:"timestamp"`
}
// UserEvent is an event with an associated user.
type UserEvent struct {
User *user.User `json:"user"`
HiddenAt *time.Time `json:"hiddenAt,omitempty"`
@@ -44,6 +46,7 @@ type MessageEvent struct {
RawBody string `json:"-"`
}
// SystemActionEvent is an event that represents an action that took place, not a chat message.
type SystemActionEvent struct {
Event
MessageEvent
@@ -51,13 +54,13 @@ type SystemActionEvent struct {
// SetDefaults will set default properties of all inbound events.
func (e *Event) SetDefaults() {
e.Id = shortid.MustGenerate()
e.ID = shortid.MustGenerate()
e.Timestamp = time.Now()
}
// SetDefaults will set default properties of all inbound events.
func (e *UserMessageEvent) SetDefaults() {
e.Id = shortid.MustGenerate()
e.ID = shortid.MustGenerate()
e.Timestamp = time.Now()
e.RenderAndSanitizeMessageBody()
}
@@ -92,6 +95,7 @@ func RenderAndSanitize(raw string) string {
return strings.TrimSpace(safe)
}
// RenderMarkdown will return HTML rendered from the string body of a chat message.
func RenderMarkdown(raw string) string {
markdown := goldmark.New(
goldmark.WithRendererOptions(

View File

@@ -27,8 +27,11 @@ const (
// ConnectedUserInfo is a private event to a user letting them know their user details.
ConnectedUserInfo EventType = "CONNECTED_USER_INFO"
// ChatActionSent is a generic chat action that can be used for anything that doesn't need specific handling or formatting.
ChatActionSent EventType = "CHAT_ACTION"
ErrorNeedsRegistration EventType = "ERROR_NEEDS_REGISTRATION"
ChatActionSent EventType = "CHAT_ACTION"
// ErrorNeedsRegistration is an error returned when the client needs to perform registration.
ErrorNeedsRegistration EventType = "ERROR_NEEDS_REGISTRATION"
// ErrorMaxConnectionsExceeded is an error returned when the server determined it should not handle more connections.
ErrorMaxConnectionsExceeded EventType = "ERROR_MAX_CONNECTIONS_EXCEEDED"
ErrorUserDisabled EventType = "ERROR_USER_DISABLED"
// ErrorUserDisabled is an error returned when the connecting user has been previously banned/disabled.
ErrorUserDisabled EventType = "ERROR_USER_DISABLED"
)

View File

@@ -7,7 +7,7 @@ type NameChangeEvent struct {
NewName string `json:"newName"`
}
// NameChangeEventBroadcast is fired when a user changes their chat display name.
// NameChangeBroadcast represents a user changing their chat display name.
type NameChangeBroadcast struct {
Event
UserEvent
@@ -17,7 +17,7 @@ type NameChangeBroadcast struct {
// GetBroadcastPayload will return the object to send to all chat users.
func (e *NameChangeBroadcast) GetBroadcastPayload() EventPayload {
return EventPayload{
"id": e.Id,
"id": e.ID,
"timestamp": e.Timestamp,
"user": e.User,
"oldName": e.Oldname,

View File

@@ -8,10 +8,10 @@ type SystemMessageEvent struct {
MessageEvent
}
// SystemMessageEvent will return the object to send to all chat users.
// GetBroadcastPayload will return the object to send to all chat users.
func (e *SystemMessageEvent) GetBroadcastPayload() EventPayload {
return EventPayload{
"id": e.Id,
"id": e.ID,
"timestamp": e.Timestamp,
"body": e.Body,
"type": SystemMessageSent,
@@ -21,6 +21,7 @@ func (e *SystemMessageEvent) GetBroadcastPayload() EventPayload {
}
}
// GetMessageType will return the event type for this message.
func (e *SystemMessageEvent) GetMessageType() EventType {
return SystemMessageSent
}

View File

@@ -10,7 +10,7 @@ type UserDisabledEvent struct {
func (e *UserDisabledEvent) GetBroadcastPayload() EventPayload {
return EventPayload{
"type": ErrorUserDisabled,
"id": e.Id,
"id": e.ID,
"timestamp": e.Timestamp,
"user": e.User,
}

View File

@@ -10,7 +10,7 @@ type UserJoinedEvent struct {
func (e *UserJoinedEvent) GetBroadcastPayload() EventPayload {
return EventPayload{
"type": UserJoined,
"id": e.Id,
"id": e.ID,
"timestamp": e.Timestamp,
"user": e.User,
}

View File

@@ -10,7 +10,7 @@ type UserMessageEvent struct {
// GetBroadcastPayload will return the object to send to all chat users.
func (e *UserMessageEvent) GetBroadcastPayload() EventPayload {
return EventPayload{
"id": e.Id,
"id": e.ID,
"timestamp": e.Timestamp,
"body": e.Body,
"user": e.User,
@@ -19,6 +19,7 @@ func (e *UserMessageEvent) GetBroadcastPayload() EventPayload {
}
}
// GetMessageType will return the event type for this message.
func (e *UserMessageEvent) GetMessageType() EventType {
return MessageSent
}

View File

@@ -6,6 +6,7 @@ import (
log "github.com/sirupsen/logrus"
)
// SetMessagesVisibility will set the visibility of multiple messages by ID.
func SetMessagesVisibility(messageIDs []string, visibility bool) error {
// Save new message visibility
if err := saveMessageVisibility(messageIDs, visibility); err != nil {
@@ -17,7 +18,7 @@ func SetMessagesVisibility(messageIDs []string, visibility bool) error {
// Note: Our client expects a single message at a time, so we can't just
// send an array of messages in a single update.
for _, id := range messageIDs {
message, err := getMessageById(id)
message, err := getMessageByID(id)
if err != nil {
log.Errorln(err)
continue

View File

@@ -5,7 +5,6 @@ import (
"strings"
"time"
_ "github.com/mattn/go-sqlite3"
"github.com/owncast/owncast/core/chat/events"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/core/user"
@@ -33,11 +32,12 @@ func setupPersistence() {
}()
}
// SaveUserMessage will save a single chat event to the messages database.
func SaveUserMessage(event events.UserMessageEvent) {
saveEvent(event.Id, event.User.Id, event.Body, event.Type, event.HiddenAt, event.Timestamp)
saveEvent(event.ID, event.User.ID, event.Body, event.Type, event.HiddenAt, event.Timestamp)
}
func saveEvent(id string, userId string, body string, eventType string, hidden *time.Time, timestamp time.Time) {
func saveEvent(id string, userID string, body string, eventType string, hidden *time.Time, timestamp time.Time) {
defer func() {
_historyCache = nil
}()
@@ -61,7 +61,7 @@ func saveEvent(id string, userId string, body string, eventType string, hidden *
defer stmt.Close()
if _, err = stmt.Exec(id, userId, body, eventType, hidden, timestamp); err != nil {
if _, err = stmt.Exec(id, userID, body, eventType, hidden, timestamp); err != nil {
log.Errorln("error saving", eventType, err)
return
}
@@ -82,7 +82,7 @@ func getChat(query string) []events.UserMessageEvent {
for rows.Next() {
var id string
var userId string
var userID string
var body string
var messageType models.EventType
var hiddenAt *time.Time
@@ -96,7 +96,7 @@ func getChat(query string) []events.UserMessageEvent {
var userNameChangedAt *time.Time
// Convert a database row into a chat event
err = rows.Scan(&id, &userId, &body, &messageType, &hiddenAt, &timestamp, &userDisplayName, &userDisplayColor, &userCreatedAt, &userDisabledAt, &previousUsernames, &userNameChangedAt)
err = rows.Scan(&id, &userID, &body, &messageType, &hiddenAt, &timestamp, &userDisplayName, &userDisplayColor, &userCreatedAt, &userDisabledAt, &previousUsernames, &userNameChangedAt)
if err != nil {
log.Errorln("There is a problem converting query to chat objects. Please report this:", query)
break
@@ -120,7 +120,7 @@ func getChat(query string) []events.UserMessageEvent {
}
user := user.User{
Id: userId,
ID: userID,
AccessToken: "",
DisplayName: *userDisplayName,
DisplayColor: *userDisplayColor,
@@ -133,7 +133,7 @@ func getChat(query string) []events.UserMessageEvent {
message := events.UserMessageEvent{
Event: events.Event{
Type: messageType,
Id: id,
ID: id,
Timestamp: timestamp,
},
UserEvent: events.UserEvent{
@@ -154,6 +154,7 @@ func getChat(query string) []events.UserMessageEvent {
var _historyCache *[]events.UserMessageEvent
// GetChatModerationHistory will return all the chat messages suitable for moderation purposes.
func GetChatModerationHistory() []events.UserMessageEvent {
if _historyCache != nil {
return *_historyCache
@@ -168,6 +169,7 @@ func GetChatModerationHistory() []events.UserMessageEvent {
return result
}
// GetChatHistory will return all the chat messages suitable for returning as user-facing chat history.
func GetChatHistory() []events.UserMessageEvent {
// Get all visible messages
var query = fmt.Sprintf("SELECT messages.id, user_id, body, eventType, hidden_at, timestamp, display_name, display_color, created_at, disabled_at, previous_names, namechanged_at FROM messages, users WHERE messages.user_id = users.id AND hidden_at IS NULL AND disabled_at IS NULL ORDER BY timestamp DESC LIMIT %d", maxBacklogNumber)
@@ -181,9 +183,9 @@ func GetChatHistory() []events.UserMessageEvent {
return m
}
// SetMessageVisibilityForUserId will bulk change the visibility of messages for a user
// SetMessageVisibilityForUserID will bulk change the visibility of messages for a user
// and then send out visibility changed events to chat clients.
func SetMessageVisibilityForUserId(userID string, visible bool) error {
func SetMessageVisibilityForUserID(userID string, visible bool) error {
defer func() {
_historyCache = nil
}()
@@ -198,7 +200,7 @@ func SetMessageVisibilityForUserId(userID string, visible bool) error {
}
for _, message := range messages {
ids = append(ids, message.Id)
ids = append(ids, message.ID)
}
// Tell the clients to hide/show these messages.
@@ -250,29 +252,29 @@ func saveMessageVisibility(messageIDs []string, visible bool) error {
return nil
}
func getMessageById(messageID string) (*events.UserMessageEvent, error) {
func getMessageByID(messageID string) (*events.UserMessageEvent, error) {
var query = "SELECT * FROM messages WHERE id = ?"
row := _datastore.DB.QueryRow(query, messageID)
var id string
var userId string
var userID string
var body string
var eventType models.EventType
var hiddenAt *time.Time
var timestamp time.Time
err := row.Scan(&id, &userId, &body, &eventType, &hiddenAt, &timestamp)
err := row.Scan(&id, &userID, &body, &eventType, &hiddenAt, &timestamp)
if err != nil {
log.Errorln(err)
return nil, err
}
user := user.GetUserById(userId)
user := user.GetUserByID(userID)
return &events.UserMessageEvent{
events.Event{
Type: eventType,
Id: id,
ID: id,
Timestamp: timestamp,
},
events.UserEvent{

View File

@@ -18,12 +18,13 @@ import (
"github.com/owncast/owncast/utils"
)
var _server *ChatServer
var _server *Server
type ChatServer struct {
// Server represents an instance of the chat server.
type Server struct {
mu sync.RWMutex
seq uint
clients map[uint]*ChatClient
clients map[uint]*Client
maxSocketConnectionLimit int64
// send outbound message payload to all clients
@@ -36,12 +37,13 @@ type ChatServer struct {
unregister chan uint // the ChatClient id
}
func NewChat() *ChatServer {
// NewChat will return a new instance of the chat server.
func NewChat() *Server {
maximumConcurrentConnectionLimit := getMaximumConcurrentConnectionLimit()
setSystemConcurrentConnectionLimit(maximumConcurrentConnectionLimit)
server := &ChatServer{
clients: map[uint]*ChatClient{},
server := &Server{
clients: map[uint]*Client{},
outbound: make(chan []byte),
inbound: make(chan chatClientEvent),
unregister: make(chan uint),
@@ -51,13 +53,14 @@ func NewChat() *ChatServer {
return server
}
func (s *ChatServer) Run() {
// Run will start the chat server.
func (s *Server) Run() {
for {
select {
case clientId := <-s.unregister:
if _, ok := s.clients[clientId]; ok {
case clientID := <-s.unregister:
if _, ok := s.clients[clientID]; ok {
s.mu.Lock()
delete(s.clients, clientId)
delete(s.clients, clientID)
s.mu.Unlock()
}
@@ -68,8 +71,8 @@ func (s *ChatServer) Run() {
}
// Addclient registers new connection as a User.
func (s *ChatServer) Addclient(conn *websocket.Conn, user *user.User, accessToken string, userAgent string, ipAddress string) *ChatClient {
client := &ChatClient{
func (s *Server) Addclient(conn *websocket.Conn, user *user.User, accessToken string, userAgent string, ipAddress string) *Client {
client := &Client{
server: s,
conn: conn,
User: user,
@@ -101,14 +104,14 @@ func (s *ChatServer) Addclient(conn *websocket.Conn, user *user.User, accessToke
}
// Asynchronously, optionally, fetch GeoIP data.
go func(client *ChatClient) {
go func(client *Client) {
client.Geo = geoip.GetGeoFromIP(ipAddress)
}(client)
return client
}
func (s *ChatServer) sendUserJoinedMessage(c *ChatClient) {
func (s *Server) sendUserJoinedMessage(c *Client) {
userJoinedEvent := events.UserJoinedEvent{}
userJoinedEvent.SetDefaults()
userJoinedEvent.User = c.User
@@ -121,7 +124,8 @@ func (s *ChatServer) sendUserJoinedMessage(c *ChatClient) {
webhooks.SendChatEventUserJoined(userJoinedEvent)
}
func (s *ChatServer) ClientClosed(c *ChatClient) {
// ClientClosed is fired when a client disconnects or connection is dropped.
func (s *Server) ClientClosed(c *Client) {
s.mu.Lock()
defer s.mu.Unlock()
c.close()
@@ -132,7 +136,8 @@ func (s *ChatServer) ClientClosed(c *ChatClient) {
}
}
func (s *ChatServer) HandleClientConnection(w http.ResponseWriter, r *http.Request) {
// HandleClientConnection is fired when a single client connects to the websocket.
func (s *Server) HandleClientConnection(w http.ResponseWriter, r *http.Request) {
if data.GetChatDisabled() {
_, _ = w.Write([]byte(events.ChatDisabled))
return
@@ -155,7 +160,7 @@ func (s *ChatServer) HandleClientConnection(w http.ResponseWriter, r *http.Reque
if accessToken == "" {
log.Errorln("Access token is required")
// Return HTTP status code
conn.Close()
_ = conn.Close()
return
}
@@ -166,17 +171,17 @@ func (s *ChatServer) HandleClientConnection(w http.ResponseWriter, r *http.Reque
"type": events.ErrorNeedsRegistration,
})
// Send error that registration is required
conn.Close()
_ = conn.Close()
return
}
// User is disabled therefore we should disconnect.
if user.DisabledAt != nil {
log.Traceln("Disabled user", user.Id, user.DisplayName, "rejected")
log.Traceln("Disabled user", user.ID, user.DisplayName, "rejected")
_ = conn.WriteJSON(events.EventPayload{
"type": events.ErrorUserDisabled,
})
conn.Close()
_ = conn.Close()
return
}
@@ -187,7 +192,7 @@ func (s *ChatServer) HandleClientConnection(w http.ResponseWriter, r *http.Reque
}
// Broadcast sends message to all connected clients.
func (s *ChatServer) Broadcast(payload events.EventPayload) error {
func (s *Server) Broadcast(payload events.EventPayload) error {
data, err := json.Marshal(payload)
if err != nil {
return err
@@ -212,7 +217,8 @@ func (s *ChatServer) Broadcast(payload events.EventPayload) error {
return nil
}
func (s *ChatServer) Send(payload events.EventPayload, client *ChatClient) {
// Send will send a single payload to a single connected client.
func (s *Server) Send(payload events.EventPayload, client *Client) {
data, err := json.Marshal(payload)
if err != nil {
log.Errorln(err)
@@ -223,7 +229,7 @@ func (s *ChatServer) Send(payload events.EventPayload, client *ChatClient) {
}
// DisconnectUser will forcefully disconnect all clients belonging to a user by ID.
func (s *ChatServer) DisconnectUser(userID string) {
func (s *Server) DisconnectUser(userID string) {
s.mu.Lock()
clients, err := GetClientsForUser(userID)
s.mu.Unlock()
@@ -234,9 +240,9 @@ func (s *ChatServer) DisconnectUser(userID string) {
}
for _, client := range clients {
log.Traceln("Disconnecting client", client.User.Id, "owned by", client.User.DisplayName)
log.Traceln("Disconnecting client", client.User.ID, "owned by", client.User.DisplayName)
go func(client *ChatClient) {
go func(client *Client) {
event := events.UserDisabledEvent{}
event.SetDefaults()
@@ -257,7 +263,7 @@ func (s *ChatServer) DisconnectUser(userID string) {
}
}
func (s *ChatServer) eventReceived(event chatClientEvent) {
func (s *Server) eventReceived(event chatClientEvent) {
var typecheck map[string]interface{}
if err := json.Unmarshal(event.data, &typecheck); err != nil {
log.Debugln(err)
@@ -277,7 +283,7 @@ func (s *ChatServer) eventReceived(event chatClientEvent) {
}
}
func (s *ChatServer) sendWelcomeMessageToClient(c *ChatClient) {
func (s *Server) sendWelcomeMessageToClient(c *Client) {
// Add an artificial delay so people notice this message come in.
time.Sleep(7 * time.Second)
@@ -288,7 +294,7 @@ func (s *ChatServer) sendWelcomeMessageToClient(c *ChatClient) {
}
}
func (s *ChatServer) sendAllWelcomeMessage() {
func (s *Server) sendAllWelcomeMessage() {
welcomeMessage := utils.RenderSimpleMarkdown(data.GetServerWelcomeMessage())
if welcomeMessage != "" {
@@ -303,7 +309,7 @@ func (s *ChatServer) sendAllWelcomeMessage() {
}
}
func (s *ChatServer) sendSystemMessageToClient(c *ChatClient, message string) {
func (s *Server) sendSystemMessageToClient(c *Client, message string) {
clientMessage := events.SystemMessageEvent{
Event: events.Event{},
MessageEvent: events.MessageEvent{
@@ -314,7 +320,7 @@ func (s *ChatServer) sendSystemMessageToClient(c *ChatClient, message string) {
s.Send(clientMessage.GetBroadcastPayload(), c)
}
func (s *ChatServer) sendActionToClient(c *ChatClient, message string) {
func (s *Server) sendActionToClient(c *Client, message string) {
clientMessage := events.ActionEvent{
MessageEvent: events.MessageEvent{
Body: message,

View File

@@ -112,7 +112,7 @@ func transitionToOfflineVideoStreamContent() {
}
// Delete the preview Gif
os.Remove(path.Join(config.WebRoot, "preview.gif"))
_ = os.Remove(path.Join(config.WebRoot, "preview.gif"))
}
func resetDirectories() {

View File

@@ -516,6 +516,7 @@ func SetVideoCodec(codec string) error {
return _datastore.SetString(videoCodecKey, codec)
}
// GetVideoCodec returns the codec to use for transcoding video.
func GetVideoCodec() string {
codec, err := _datastore.GetString(videoCodecKey)
if codec == "" || err != nil {

View File

@@ -6,6 +6,7 @@ import (
log "github.com/sirupsen/logrus"
)
// CreateMessagesTable will create the chat messages table if needed.
func CreateMessagesTable(db *sql.DB) {
createTableSQL := `CREATE TABLE IF NOT EXISTS messages (
"id" string NOT NULL,

View File

@@ -64,7 +64,7 @@ func migrateToSchema1(db *sql.DB) {
return
}
var lastUsed *time.Time = nil
var lastUsed *time.Time
if lastUsedString != nil {
lastUsedTime, _ := time.Parse(time.RFC3339, *lastUsedString)
lastUsed = &lastUsedTime

View File

@@ -144,6 +144,7 @@ func (ds *Datastore) Reset() {
PopulateDefaults()
}
// GetDatastore returns the shared instance of the owncast datastore.
func GetDatastore() *Datastore {
return _datastore
}

View File

@@ -169,7 +169,7 @@ func GetWebhooks() ([]models.Webhook, error) { //nolint
return webhooks, err
}
var lastUsed *time.Time = nil
var lastUsed *time.Time
if lastUsedString != nil {
lastUsedTime, _ := time.Parse(time.RFC3339, *lastUsedString)
lastUsed = &lastUsedTime

View File

@@ -60,6 +60,7 @@ func Start(setStreamAsConnected func(*io.PipeReader), setBroadcaster func(models
}
}
// HandleConn is fired when an inbound RTMP connection takes place.
func HandleConn(c *rtmp.Conn, nc net.Conn) {
c.LogTagEvent = func(isRead bool, t flvio.Tag) {
if t.Type == flvio.TAG_AMF0 {
@@ -70,13 +71,13 @@ func HandleConn(c *rtmp.Conn, nc net.Conn) {
if _hasInboundRTMPConnection {
log.Errorln("stream already running; can not overtake an existing stream")
nc.Close()
_ = nc.Close()
return
}
if !secretMatch(data.GetStreamKey(), c.URL.Path) {
log.Errorln("invalid streaming key; rejecting incoming stream")
nc.Close()
_ = nc.Close()
return
}
@@ -129,8 +130,8 @@ func handleDisconnect(conn net.Conn) {
}
log.Infoln("Inbound stream disconnected.")
conn.Close()
_pipe.Close()
_ = conn.Close()
_ = _pipe.Close()
_hasInboundRTMPConnection = false
}

View File

@@ -61,8 +61,8 @@ func RemoveChatClient(clientID string) {
l.Unlock()
}
// SetViewerIdActive sets a client as active and connected.
func SetViewerIdActive(id string) {
// SetViewerIDActive sets a client as active and connected.
func SetViewerIDActive(id string) {
l.Lock()
defer l.Unlock()
@@ -81,10 +81,10 @@ func pruneViewerCount() {
l.Lock()
defer l.Unlock()
for viewerId := range _stats.Viewers {
viewerLastSeenTime := _stats.Viewers[viewerId]
for viewerID := range _stats.Viewers {
viewerLastSeenTime := _stats.Viewers[viewerID]
if time.Since(viewerLastSeenTime) < _activeViewerPurgeTimeout {
viewers[viewerId] = viewerLastSeenTime
viewers[viewerID] = viewerLastSeenTime
}
}

View File

@@ -29,6 +29,7 @@ func GetStatus() models.Status {
}
}
// GetCurrentBroadcast will return the currently active broadcast.
func GetCurrentBroadcast() *models.CurrentBroadcast {
return _currentBroadcast
}
@@ -38,6 +39,7 @@ func setBroadcaster(broadcaster models.Broadcaster) {
_broadcaster = &broadcaster
}
// GetBroadcaster will return the details of the currently active broadcaster.
func GetBroadcaster() *models.Broadcaster {
return _broadcaster
}

View File

@@ -11,6 +11,7 @@ import (
"github.com/owncast/owncast/utils"
)
// LocalStorage represents an instance of the local storage provider for HLS video.
type LocalStorage struct {
}

View File

@@ -25,7 +25,7 @@ import (
// then keep a reference to it here.
var _queuedPlaylistUpdates = make(map[string]string)
// S3Storage is the s3 implementation of the ChunkStorageProvider.
// S3Storage is the s3 implementation of a storage provider.
type S3Storage struct {
sess *session.Session
host string
@@ -124,7 +124,7 @@ func (s *S3Storage) MasterPlaylistWritten(localFilePath string) {
// Save saves the file to the s3 bucket.
func (s *S3Storage) Save(filePath string, retryCount int) (string, error) {
file, err := os.Open(filePath)
file, err := os.Open(filePath) // nolint
if err != nil {
return "", err
}
@@ -153,10 +153,10 @@ func (s *S3Storage) Save(filePath string, retryCount int) (string, error) {
if retryCount < 4 {
log.Traceln("Retrying...")
return s.Save(filePath, retryCount+1)
} else {
log.Warnln("Giving up on", filePath, err)
return "", fmt.Errorf("Giving up on %s", filePath)
}
log.Warnln("Giving up on", filePath, err)
return "", fmt.Errorf("Giving up on %s", filePath)
}
return response.Location, nil
@@ -185,7 +185,7 @@ func (s *S3Storage) connectAWS() *session.Session {
// rewriteRemotePlaylist will take a local playlist and rewrite it to have absolute URLs to remote locations.
func (s *S3Storage) rewriteRemotePlaylist(filePath string) error {
f, err := os.Open(filePath)
f, err := os.Open(filePath) // nolint
if err != nil {
panic(err)
}

View File

@@ -110,7 +110,7 @@ func SetStreamAsDisconnected() {
log.Warnln(err)
}
if utils.DoesFileExists(playlistFilePath) {
f, err := os.OpenFile(playlistFilePath, os.O_CREATE|os.O_RDWR, os.ModePerm)
f, err := os.OpenFile(playlistFilePath, os.O_CREATE|os.O_RDWR, os.ModePerm) //nolint
if err != nil {
log.Errorln(err)
}

View File

@@ -29,35 +29,43 @@ var supportedCodecs = map[string]string{
(&NvencCodec{}).Name(): "NVIDIA nvenc",
}
// Libx264Codec represents an instance of the Libx264 Codec.
type Libx264Codec struct {
}
// Name returns the codec name.
func (c *Libx264Codec) Name() string {
return "libx264"
}
// DisplayName returns the human readable name of the codec.
func (c *Libx264Codec) DisplayName() string {
return "x264"
}
// GlobalFlags are the global flags used with this codec in the transcoder.
func (c *Libx264Codec) GlobalFlags() string {
return ""
}
// PixelFormat is the pixel format required for this codec.
func (c *Libx264Codec) PixelFormat() string {
return "yuv420p" //nolint:goconst
}
// ExtraArguments are the extra arguments used with this codec in the transcoder.
func (c *Libx264Codec) ExtraArguments() string {
return strings.Join([]string{
"-tune", "zerolatency", // Option used for good for fast encoding and low-latency streaming (always includes iframes in each segment)
}, " ")
}
// ExtraFilters are the extra filters required for this codec in the transcoder.
func (c *Libx264Codec) ExtraFilters() string {
return ""
}
// VariantFlags returns a string representing a single variant processed by this codec.
func (c *Libx264Codec) VariantFlags(v *HLSVariant) string {
bufferSize := int(float64(v.videoBitrate) * 1.2) // How often it checks the bitrate of encoded segments to see if it's too high/low.
@@ -68,6 +76,7 @@ func (c *Libx264Codec) VariantFlags(v *HLSVariant) string {
}, " ")
}
// GetPresetForLevel returns the string preset for this codec given an integer level.
func (c *Libx264Codec) GetPresetForLevel(l int) string {
presetMapping := []string{
"ultrafast",
@@ -84,39 +93,48 @@ func (c *Libx264Codec) GetPresetForLevel(l int) string {
return presetMapping[l]
}
// OmxCodec represents an instance of the Omx codec.
type OmxCodec struct {
}
// Name returns the codec name.
func (c *OmxCodec) Name() string {
return "h264_omx"
}
// DisplayName returns the human readable name of the codec.
func (c *OmxCodec) DisplayName() string {
return "OpenMAX (omx)"
}
// GlobalFlags are the global flags used with this codec in the transcoder.
func (c *OmxCodec) GlobalFlags() string {
return ""
}
// PixelFormat is the pixel format required for this codec.
func (c *OmxCodec) PixelFormat() string {
return "yuv420p"
}
// ExtraArguments are the extra arguments used with this codec in the transcoder.
func (c *OmxCodec) ExtraArguments() string {
return strings.Join([]string{
"-tune", "zerolatency", // Option used for good for fast encoding and low-latency streaming (always includes iframes in each segment)
}, " ")
}
// ExtraFilters are the extra filters required for this codec in the transcoder.
func (c *OmxCodec) ExtraFilters() string {
return ""
}
// VariantFlags returns a string representing a single variant processed by this codec.
func (c *OmxCodec) VariantFlags(v *HLSVariant) string {
return ""
}
// GetPresetForLevel returns the string preset for this codec given an integer level.
func (c *OmxCodec) GetPresetForLevel(l int) string {
presetMapping := []string{
"ultrafast",
@@ -133,17 +151,21 @@ func (c *OmxCodec) GetPresetForLevel(l int) string {
return presetMapping[l]
}
// VaapiCodec represents an instance of the Vaapi codec.
type VaapiCodec struct {
}
// Name returns the codec name.
func (c *VaapiCodec) Name() string {
return "h264_vaapi"
}
// DisplayName returns the human readable name of the codec.
func (c *VaapiCodec) DisplayName() string {
return "VA-API"
}
// GlobalFlags are the global flags used with this codec in the transcoder.
func (c *VaapiCodec) GlobalFlags() string {
flags := []string{
"-vaapi_device", "/dev/dri/renderD128",
@@ -152,22 +174,27 @@ func (c *VaapiCodec) GlobalFlags() string {
return strings.Join(flags, " ")
}
// PixelFormat is the pixel format required for this codec.
func (c *VaapiCodec) PixelFormat() string {
return "vaapi_vld"
}
// ExtraFilters are the extra filters required for this codec in the transcoder.
func (c *VaapiCodec) ExtraFilters() string {
return "format=nv12,hwupload"
}
// ExtraArguments are the extra arguments used with this codec in the transcoder.
func (c *VaapiCodec) ExtraArguments() string {
return ""
}
// VariantFlags returns a string representing a single variant processed by this codec.
func (c *VaapiCodec) VariantFlags(v *HLSVariant) string {
return ""
}
// GetPresetForLevel returns the string preset for this codec given an integer level.
func (c *VaapiCodec) GetPresetForLevel(l int) string {
presetMapping := []string{
"ultrafast",
@@ -184,17 +211,21 @@ func (c *VaapiCodec) GetPresetForLevel(l int) string {
return presetMapping[l]
}
// NvencCodec represents an instance of the Nvenc Codec.
type NvencCodec struct {
}
// Name returns the codec name.
func (c *NvencCodec) Name() string {
return "h264_nvenc"
}
// DisplayName returns the human readable name of the codec.
func (c *NvencCodec) DisplayName() string {
return "nvidia nvenc"
}
// GlobalFlags are the global flags used with this codec in the transcoder.
func (c *NvencCodec) GlobalFlags() string {
flags := []string{
"-hwaccel cuda",
@@ -203,23 +234,28 @@ func (c *NvencCodec) GlobalFlags() string {
return strings.Join(flags, " ")
}
// PixelFormat is the pixel format required for this codec.
func (c *NvencCodec) PixelFormat() string {
return "yuv420p"
}
// ExtraArguments are the extra arguments used with this codec in the transcoder.
func (c *NvencCodec) ExtraArguments() string {
return ""
}
// ExtraFilters are the extra filters required for this codec in the transcoder.
func (c *NvencCodec) ExtraFilters() string {
return ""
}
// VariantFlags returns a string representing a single variant processed by this codec.
func (c *NvencCodec) VariantFlags(v *HLSVariant) string {
tuning := "ll" // low latency
return fmt.Sprintf("-tune:v:%d %s", v.index, tuning)
}
// GetPresetForLevel returns the string preset for this codec given an integer level.
func (c *NvencCodec) GetPresetForLevel(l int) string {
presetMapping := []string{
"p1",
@@ -236,37 +272,46 @@ func (c *NvencCodec) GetPresetForLevel(l int) string {
return presetMapping[l]
}
// QuicksyncCodec represents an instance of the Intel Quicksync Codec.
type QuicksyncCodec struct {
}
// Name returns the codec name.
func (c *QuicksyncCodec) Name() string {
return "h264_qsv"
}
// DisplayName returns the human readable name of the codec.
func (c *QuicksyncCodec) DisplayName() string {
return "Intel QuickSync"
}
// GlobalFlags are the global flags used with this codec in the transcoder.
func (c *QuicksyncCodec) GlobalFlags() string {
return ""
}
// PixelFormat is the pixel format required for this codec.
func (c *QuicksyncCodec) PixelFormat() string {
return "nv12"
}
// ExtraArguments are the extra arguments used with this codec in the transcoder.
func (c *QuicksyncCodec) ExtraArguments() string {
return ""
}
// ExtraFilters are the extra filters required for this codec in the transcoder.
func (c *QuicksyncCodec) ExtraFilters() string {
return ""
}
// VariantFlags returns a string representing a single variant processed by this codec.
func (c *QuicksyncCodec) VariantFlags(v *HLSVariant) string {
return ""
}
// GetPresetForLevel returns the string preset for this codec given an integer level.
func (c *QuicksyncCodec) GetPresetForLevel(l int) string {
presetMapping := []string{
"ultrafast",
@@ -283,36 +328,45 @@ func (c *QuicksyncCodec) GetPresetForLevel(l int) string {
return presetMapping[l]
}
// Video4Linux represents an instance of the V4L Codec.
type Video4Linux struct{}
// Name returns the codec name.
func (c *Video4Linux) Name() string {
return "h264_v4l2m2m"
}
// DisplayName returns the human readable name of the codec.
func (c *Video4Linux) DisplayName() string {
return "Video4Linux"
}
// GlobalFlags are the global flags used with this codec in the transcoder.
func (c *Video4Linux) GlobalFlags() string {
return ""
}
// PixelFormat is the pixel format required for this codec.
func (c *Video4Linux) PixelFormat() string {
return "nv21"
}
// ExtraArguments are the extra arguments used with this codec in the transcoder.
func (c *Video4Linux) ExtraArguments() string {
return ""
}
// ExtraFilters are the extra filters required for this codec in the transcoder.
func (c *Video4Linux) ExtraFilters() string {
return ""
}
// VariantFlags returns a string representing a single variant processed by this codec.
func (c *Video4Linux) VariantFlags(v *HLSVariant) string {
return ""
}
// GetPresetForLevel returns the string preset for this codec given an integer level.
func (c *Video4Linux) GetPresetForLevel(l int) string {
presetMapping := []string{
"ultrafast",

View File

@@ -18,6 +18,7 @@ import (
var _timer *time.Ticker
// StopThumbnailGenerator will stop the periodic generating of a thumbnail from video.
func StopThumbnailGenerator() {
if _timer != nil {
_timer.Stop()
@@ -98,11 +99,11 @@ func fireThumbnailGenerator(segmentPath string, variantIndex int) error {
ffmpegCmd := strings.Join(thumbnailCmdFlags, " ")
if _, err := exec.Command("sh", "-c", ffmpegCmd).Output(); err != nil {
return err
} else {
// rename temp file
if err := os.Rename(outputFileTemp, outputFile); err != nil {
log.Errorln(err)
}
}
// rename temp file
if err := os.Rename(outputFileTemp, outputFile); err != nil {
log.Errorln(err)
}
// If YP support is enabled also create an animated GIF preview

View File

@@ -76,6 +76,7 @@ func (v *VideoSize) getString() string {
return ""
}
// Stop will stop the transcoder and kill all processing.
func (t *Transcoder) Stop() {
log.Traceln("Transcoder STOP requested.")
err := _commandExec.Process.Kill()
@@ -410,15 +411,17 @@ func (t *Transcoder) SetAppendToStream(append bool) {
t.appendToStream = append
}
// SetIdentifer enables appending a unique identifier to segment file name.
// SetIdentifier enables appending a unique identifier to segment file name.
func (t *Transcoder) SetIdentifier(output string) {
t.segmentIdentifier = output
}
// SetInternalHTTPPort will set the port to be used for internal communication.
func (t *Transcoder) SetInternalHTTPPort(port string) {
t.internalListenerPort = port
}
// SetCodec will set the codec to be used for the transocder.
func (t *Transcoder) SetCodec(codecName string) {
t.codec = getCodec(codecName)
}

View File

@@ -99,24 +99,24 @@ func createVariantDirectories() {
if len(data.GetStreamOutputVariants()) != 0 {
for index := range data.GetStreamOutputVariants() {
if err := os.MkdirAll(path.Join(config.PrivateHLSStoragePath, strconv.Itoa(index)), 0777); err != nil {
if err := os.MkdirAll(path.Join(config.PrivateHLSStoragePath, strconv.Itoa(index)), 0750); err != nil {
log.Fatalln(err)
}
dir := path.Join(config.PublicHLSStoragePath, strconv.Itoa(index))
log.Traceln("Creating", dir)
if err := os.MkdirAll(dir, 0777); err != nil {
if err := os.MkdirAll(dir, 0750); err != nil {
log.Fatalln(err)
}
}
} else {
dir := path.Join(config.PrivateHLSStoragePath, strconv.Itoa(0))
log.Traceln("Creating", dir)
if err := os.MkdirAll(dir, 0777); err != nil {
if err := os.MkdirAll(dir, 0750); err != nil {
log.Fatalln(err)
}
dir = path.Join(config.PublicHLSStoragePath, strconv.Itoa(0))
log.Traceln("Creating", dir)
if err := os.MkdirAll(dir, 0777); err != nil {
if err := os.MkdirAll(dir, 0750); err != nil {
log.Fatalln(err)
}
}

View File

@@ -14,7 +14,7 @@ import (
// ExternalAPIUser represents a single 3rd party integration that uses an access token.
// This struct mostly matches the User struct so they can be used interchangeably.
type ExternalAPIUser struct {
Id string `json:"id"`
ID string `json:"id"`
AccessToken string `json:"accessToken"`
DisplayName string `json:"displayName"`
DisplayColor int `json:"displayColor"`
@@ -40,7 +40,7 @@ var validAccessTokenScopes = []string{
ScopeHasAdminAccess,
}
// InsertToken will add a new token to the database.
// InsertExternalAPIUser will add a new API user to the database.
func InsertExternalAPIUser(token string, name string, color int, scopes []string) error {
log.Traceln("Adding new API user:", name)
@@ -206,7 +206,7 @@ func makeExternalAPIUserFromRow(row *sql.Row) (*ExternalAPIUser, error) {
}
integration := ExternalAPIUser{
Id: id,
ID: id,
AccessToken: accessToken,
DisplayName: displayName,
DisplayColor: displayColor,
@@ -237,7 +237,7 @@ func makeExternalAPIUsersFromRows(rows *sql.Rows) ([]ExternalAPIUser, error) {
}
integration := ExternalAPIUser{
Id: id,
ID: id,
AccessToken: accessToken,
DisplayName: displayName,
DisplayColor: displayColor,

View File

@@ -16,8 +16,9 @@ import (
var _datastore *data.Datastore
// User represents a single chat user.
type User struct {
Id string `json:"id"`
ID string `json:"id"`
AccessToken string `json:"-"`
DisplayName string `json:"displayName"`
DisplayColor int `json:"displayColor"`
@@ -27,14 +28,17 @@ type User struct {
NameChangedAt *time.Time `json:"nameChangedAt,omitempty"`
}
// IsEnabled will return if this single user is enabled.
func (u *User) IsEnabled() bool {
return u.DisabledAt == nil
}
// SetupUsers will perform the initial initialization of the user package.
func SetupUsers() {
_datastore = data.GetDatastore()
}
// CreateAnonymousUser will create a new anonymous user with the provided display name.
func CreateAnonymousUser(username string) (*User, error) {
id := shortid.MustGenerate()
accessToken, err := utils.GenerateAccessToken()
@@ -51,7 +55,7 @@ func CreateAnonymousUser(username string) (*User, error) {
displayColor := utils.GenerateRandomDisplayColor()
user := &User{
Id: id,
ID: id,
AccessToken: accessToken,
DisplayName: displayName,
DisplayColor: displayColor,
@@ -65,7 +69,8 @@ func CreateAnonymousUser(username string) (*User, error) {
return user, nil
}
func ChangeUsername(userId string, username string) {
// ChangeUsername will change the user associated to userID from one display name to another.
func ChangeUsername(userID string, username string) {
_datastore.DbLock.Lock()
defer _datastore.DbLock.Unlock()
@@ -87,13 +92,13 @@ func ChangeUsername(userId string, username string) {
}
defer stmt.Close()
_, err = stmt.Exec(username, fmt.Sprintf(",%s", username), time.Now(), userId)
_, err = stmt.Exec(username, fmt.Sprintf(",%s", username), time.Now(), userID)
if err != nil {
log.Errorln(err)
}
if err := tx.Commit(); err != nil {
log.Errorln("error changing display name of user", userId, err)
log.Errorln("error changing display name of user", userID, err)
}
}
@@ -116,7 +121,7 @@ func create(user *User) error {
}
defer stmt.Close()
_, err = stmt.Exec(user.Id, user.AccessToken, user.DisplayName, user.DisplayColor, user.DisplayName, user.CreatedAt)
_, err = stmt.Exec(user.ID, user.AccessToken, user.DisplayName, user.DisplayColor, user.DisplayName, user.CreatedAt)
if err != nil {
log.Errorln("error creating new user", err)
}
@@ -124,6 +129,7 @@ func create(user *User) error {
return tx.Commit()
}
// SetEnabled will will set the enabled flag on a single user assigned to userID.
func SetEnabled(userID string, enabled bool) error {
_datastore.DbLock.Lock()
defer _datastore.DbLock.Unlock()
@@ -166,8 +172,8 @@ func GetUserByToken(token string) *User {
return getUserFromRow(row)
}
// GetUserById will return a user by a user ID.
func GetUserById(id string) *User {
// GetUserByID will return a user by a user ID.
func GetUserByID(id string) *User {
_datastore.DbLock.Lock()
defer _datastore.DbLock.Unlock()
@@ -218,7 +224,7 @@ func getUsersFromRows(rows *sql.Rows) []*User {
}
user := &User{
Id: id,
ID: id,
DisplayName: displayName,
DisplayColor: displayColor,
CreatedAt: createdAt,
@@ -250,7 +256,7 @@ func getUserFromRow(row *sql.Row) *User {
}
return &User{
Id: id,
ID: id,
DisplayName: displayName,
DisplayColor: displayColor,
CreatedAt: createdAt,

View File

@@ -5,6 +5,7 @@ import (
"github.com/owncast/owncast/models"
)
// SendChatEvent will send a chat event to webhook destinations.
func SendChatEvent(chatEvent *events.UserMessageEvent) {
webhookEvent := WebhookEvent{
Type: chatEvent.GetMessageType(),
@@ -12,7 +13,7 @@ func SendChatEvent(chatEvent *events.UserMessageEvent) {
User: chatEvent.User,
Body: chatEvent.Body,
RawBody: chatEvent.RawBody,
ID: chatEvent.Id,
ID: chatEvent.ID,
Visible: chatEvent.HiddenAt == nil,
Timestamp: &chatEvent.Timestamp,
},
@@ -21,6 +22,7 @@ func SendChatEvent(chatEvent *events.UserMessageEvent) {
SendEventToWebhooks(webhookEvent)
}
// SendChatEventUsernameChanged will send a username changed event to webhook destinations.
func SendChatEventUsernameChanged(event events.NameChangeEvent) {
webhookEvent := WebhookEvent{
Type: models.UserNameChanged,

View File

@@ -8,6 +8,7 @@ import (
"github.com/teris-io/shortid"
)
// SendStreamStatusEvent will send all webhook destinations the current stream status.
func SendStreamStatusEvent(eventType models.EventType) {
SendEventToWebhooks(WebhookEvent{
Type: eventType,

View File

@@ -14,11 +14,13 @@ import (
"github.com/owncast/owncast/models"
)
// WebhookEvent represents an event sent as a webhook.
type WebhookEvent struct {
Type models.EventType `json:"type"` // messageSent | userJoined | userNameChange
EventData interface{} `json:"eventData,omitempty"`
}
// WebhookChatMessage represents a single chat message sent as a webhook payload.
type WebhookChatMessage struct {
User *user.User `json:"user,omitempty"`
Body string `json:"body,omitempty"`
@@ -28,6 +30,7 @@ type WebhookChatMessage struct {
Timestamp *time.Time `json:"timestamp,omitempty"`
}
// SendEventToWebhooks will send a single webhook event to all webhook destinations.
func SendEventToWebhooks(payload WebhookEvent) {
webhooks := data.GetWebhooksForEvent(payload.Type)