Add channel logging feature
This commit is contained in:
parent
8004edb7b8
commit
1e702b0e1e
|
@ -50,6 +50,8 @@ type Config struct {
|
||||||
VIP VIP
|
VIP VIP
|
||||||
|
|
||||||
MessageFilters []*MessageFilter
|
MessageFilters []*MessageFilter
|
||||||
|
|
||||||
|
Logging Logging
|
||||||
}
|
}
|
||||||
|
|
||||||
type TurnConfig struct {
|
type TurnConfig struct {
|
||||||
|
@ -99,6 +101,14 @@ type WebhookURL struct {
|
||||||
URL string
|
URL string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Logging configs to monitor channels or usernames.
|
||||||
|
type Logging struct {
|
||||||
|
Enabled bool
|
||||||
|
Directory string
|
||||||
|
Channels []string
|
||||||
|
Usernames []string
|
||||||
|
}
|
||||||
|
|
||||||
// Current loaded configuration.
|
// Current loaded configuration.
|
||||||
var Current = DefaultConfig()
|
var Current = DefaultConfig()
|
||||||
|
|
||||||
|
@ -175,6 +185,11 @@ func DefaultConfig() Config {
|
||||||
ChatServerResponse: "Watch your language.",
|
ChatServerResponse: "Watch your language.",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
Logging: Logging{
|
||||||
|
Directory: "./logs",
|
||||||
|
Channels: []string{"lobby", "offtopic"},
|
||||||
|
Usernames: []string{},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
c.JWT.Strict = true
|
c.JWT.Strict = true
|
||||||
return c
|
return c
|
||||||
|
|
|
@ -220,12 +220,26 @@ func (s *Server) OnMessage(sub *Subscriber, msg messages.Message) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Log this conversation?
|
||||||
|
if IsLoggingUsername(sub) {
|
||||||
|
// The sender of this message is being logged.
|
||||||
|
LogMessage(sub, rcpt.Username, sub.Username, msg)
|
||||||
|
} else if IsLoggingUsername(rcpt) {
|
||||||
|
// The recipient of this message is being logged.
|
||||||
|
LogMessage(rcpt, sub.Username, sub.Username, msg)
|
||||||
|
}
|
||||||
|
|
||||||
if err := s.SendTo(msg.Channel, message); err != nil {
|
if err := s.SendTo(msg.Channel, message); err != nil {
|
||||||
sub.ChatServer("Your message could not be delivered: %s", err)
|
sub.ChatServer("Your message could not be delivered: %s", err)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Are we logging this public channel?
|
||||||
|
if IsLoggingChannel(msg.Channel) {
|
||||||
|
LogChannel(s, msg.Channel, sub.Username, msg)
|
||||||
|
}
|
||||||
|
|
||||||
// Broadcast a chat message to the room.
|
// Broadcast a chat message to the room.
|
||||||
s.Broadcast(message)
|
s.Broadcast(message)
|
||||||
}
|
}
|
||||||
|
|
144
pkg/logging.go
Normal file
144
pkg/logging.go
Normal file
|
@ -0,0 +1,144 @@
|
||||||
|
package barertc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.kirsle.net/apps/barertc/pkg/config"
|
||||||
|
"git.kirsle.net/apps/barertc/pkg/log"
|
||||||
|
"git.kirsle.net/apps/barertc/pkg/messages"
|
||||||
|
)
|
||||||
|
|
||||||
|
// IsLoggingUsername checks whether the app is currently configured to log a user's DMs.
|
||||||
|
func IsLoggingUsername(sub *Subscriber) bool {
|
||||||
|
if !config.Current.Logging.Enabled {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Has a cached setting and writer.
|
||||||
|
if sub.log {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the server config.
|
||||||
|
for _, username := range config.Current.Logging.Usernames {
|
||||||
|
if username == sub.Username {
|
||||||
|
sub.log = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sub.log
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsLoggingChannel checks whether the app is currently logging a public channel.
|
||||||
|
func IsLoggingChannel(channel string) bool {
|
||||||
|
if !config.Current.Logging.Enabled {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, value := range config.Current.Logging.Channels {
|
||||||
|
if value == channel {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// LogMessage appends to a user's conversation log.
|
||||||
|
func LogMessage(sub *Subscriber, otherUsername, senderUsername string, msg messages.Message) {
|
||||||
|
if !sub.log {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create or get the filehandle.
|
||||||
|
fh, err := initLogFile(sub, "@"+sub.Username, otherUsername)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("LogMessage(%s): %s", sub.Username, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fh.Write(
|
||||||
|
[]byte(fmt.Sprintf(
|
||||||
|
"%s [%s] %s\n",
|
||||||
|
time.Now().Format(time.RFC3339),
|
||||||
|
senderUsername,
|
||||||
|
msg.Message,
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LogChannel appends to a channel's conversation log.
|
||||||
|
func LogChannel(s *Server, channel string, username string, msg messages.Message) {
|
||||||
|
fh, err := initLogFile(s, channel)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("LogChannel(%s): %s", channel, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fh.Write(
|
||||||
|
[]byte(fmt.Sprintf(
|
||||||
|
"%s [%s] %s\n",
|
||||||
|
time.Now().Format(time.RFC3339),
|
||||||
|
username,
|
||||||
|
msg.Message,
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize a logging directory.
|
||||||
|
func initLogFile(sub LogCacheable, components ...string) (io.Writer, error) {
|
||||||
|
// Initialize the logfh cache?
|
||||||
|
var logfh = sub.GetLogFilehandleCache()
|
||||||
|
|
||||||
|
var (
|
||||||
|
suffix = components[len(components)-1]
|
||||||
|
middle = components[:len(components)-1]
|
||||||
|
paths = append([]string{
|
||||||
|
config.Current.Logging.Directory,
|
||||||
|
}, middle...,
|
||||||
|
)
|
||||||
|
filename = strings.Join(
|
||||||
|
append(paths, suffix+".txt"),
|
||||||
|
"/",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Already have this conversation log open?
|
||||||
|
if fh, ok := logfh[suffix]; ok {
|
||||||
|
return fh, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Warn("Initialize log directory: path=%+v suffix=%s", paths, suffix)
|
||||||
|
if err := os.MkdirAll(strings.Join(paths, "/"), 0755); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
fh, err := os.OpenFile(filename, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
logfh[suffix] = fh
|
||||||
|
return logfh[suffix], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Interface for objects that hold log filehandle caches.
|
||||||
|
type LogCacheable interface {
|
||||||
|
GetLogFilehandleCache() map[string]io.Writer
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implementations of LogCacheable.
|
||||||
|
func (sub *Subscriber) GetLogFilehandleCache() map[string]io.Writer {
|
||||||
|
if sub.logfh == nil {
|
||||||
|
sub.logfh = map[string]io.Writer{}
|
||||||
|
}
|
||||||
|
return sub.logfh
|
||||||
|
}
|
||||||
|
func (s *Server) GetLogFilehandleCache() map[string]io.Writer {
|
||||||
|
if s.logfh == nil {
|
||||||
|
s.logfh = map[string]io.Writer{}
|
||||||
|
}
|
||||||
|
return s.logfh
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
package barertc
|
package barertc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
@ -16,6 +17,9 @@ type Server struct {
|
||||||
// Connected WebSocket subscribers.
|
// Connected WebSocket subscribers.
|
||||||
subscribersMu sync.RWMutex
|
subscribersMu sync.RWMutex
|
||||||
subscribers map[*Subscriber]struct{}
|
subscribers map[*Subscriber]struct{}
|
||||||
|
|
||||||
|
// Cached filehandles for channel logging.
|
||||||
|
logfh map[string]io.Writer
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewServer initializes the Server.
|
// NewServer initializes the Server.
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -44,6 +45,10 @@ type Subscriber struct {
|
||||||
// Record which message IDs belong to this user.
|
// Record which message IDs belong to this user.
|
||||||
midMu sync.Mutex
|
midMu sync.Mutex
|
||||||
messageIDs map[int64]struct{}
|
messageIDs map[int64]struct{}
|
||||||
|
|
||||||
|
// Logging.
|
||||||
|
log bool
|
||||||
|
logfh map[string]io.Writer
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadLoop spawns a goroutine that reads from the websocket connection.
|
// ReadLoop spawns a goroutine that reads from the websocket connection.
|
||||||
|
|
Loading…
Reference in New Issue
Block a user