1
0
Fork 0
mirror of https://github.com/FrankerFaceZ/FrankerFaceZ.git synced 2025-06-28 15:27:43 +00:00
FrankerFaceZ/socketserver/server/publisher.go

278 lines
8.1 KiB
Go
Raw Normal View History

2015-10-25 03:21:50 -07:00
package server
import (
2015-11-08 22:34:06 -08:00
"errors"
"fmt"
"net/http"
"strconv"
"strings"
2015-10-25 03:21:50 -07:00
"sync"
"time"
)
2015-11-08 22:34:06 -08:00
type PushCommandCacheInfo struct {
Caching BacklogCacheType
Target MessageTargetType
2015-10-25 03:21:50 -07:00
}
// S2CCommandsCacheInfo details what the behavior is of each command that can be sent to /cached_pub.
var S2CCommandsCacheInfo = map[Command]PushCommandCacheInfo{
2015-11-08 22:34:06 -08:00
/// Channel data
// follow_sets: extra emote sets included in the chat
// follow_buttons: extra follow buttons below the stream
"follow_sets": {CacheTypePersistent, MsgTargetTypeChat},
"follow_buttons": {CacheTypePersistent, MsgTargetTypeChat},
2016-01-27 18:04:28 -08:00
"srl_race": {CacheTypeLastOnly, MsgTargetTypeMultichat},
2015-11-08 22:34:06 -08:00
/// Chatter/viewer counts
"chatters": {CacheTypeLastOnly, MsgTargetTypeChat},
"viewers": {CacheTypeLastOnly, MsgTargetTypeChat},
2015-11-08 22:34:06 -08:00
}
2016-04-28 14:39:20 -07:00
var PersistentCachingCommands = []Command{"follow_sets", "follow_buttons"}
2016-04-28 15:33:49 -07:00
var HourlyCachingCommands = []Command{"chatters", "viewers"} /* srl_race */
2016-04-28 14:39:20 -07:00
2015-11-08 22:34:06 -08:00
type BacklogCacheType int
const (
2016-01-17 18:01:21 -08:00
// CacheTypeInvalid is the sentinel value.
2015-11-08 22:34:06 -08:00
CacheTypeInvalid BacklogCacheType = iota
2016-01-17 18:01:21 -08:00
// CacheTypeNever is a message that cannot be cached.
2015-11-08 22:34:06 -08:00
CacheTypeNever
2016-01-17 18:01:21 -08:00
// CacheTypeLastOnly means to save only the last copy of this message,
// and always send it when the backlog is requested.
2015-11-08 22:34:06 -08:00
CacheTypeLastOnly
2016-01-17 18:01:21 -08:00
// CacheTypePersistent means to save the last copy of this message,
// and always send it when the backlog is requested, but do not clean it periodically.
2015-11-08 22:34:06 -08:00
CacheTypePersistent
)
type MessageTargetType int
const (
2016-01-17 18:01:21 -08:00
// MsgTargetTypeInvalid is the sentinel value.
2015-11-08 22:34:06 -08:00
MsgTargetTypeInvalid MessageTargetType = iota
2016-01-17 18:01:21 -08:00
// MsgTargetTypeChat is a message is targeted to all users in a particular chat.
2015-11-08 22:34:06 -08:00
MsgTargetTypeChat
2016-01-17 18:01:21 -08:00
// MsgTargetTypeMultichat is a message is targeted to all users in multiple chats.
2015-11-08 22:34:06 -08:00
MsgTargetTypeMultichat
2016-01-17 18:01:21 -08:00
// MsgTargetTypeGlobal is a message sent to all FFZ users.
2015-11-08 22:34:06 -08:00
MsgTargetTypeGlobal
)
// note: see types.go for methods on these
2016-01-17 18:01:21 -08:00
// ErrorUnrecognizedCacheType is returned by BacklogCacheType.UnmarshalJSON()
2015-11-08 22:34:06 -08:00
var ErrorUnrecognizedCacheType = errors.New("Invalid value for cachetype")
2016-01-17 18:01:21 -08:00
// ErrorUnrecognizedTargetType is returned by MessageTargetType.UnmarshalJSON()
2015-11-08 22:34:06 -08:00
var ErrorUnrecognizedTargetType = errors.New("Invalid value for message target")
type LastSavedMessage struct {
Timestamp time.Time
Data string
}
2015-10-25 03:21:50 -07:00
2015-11-08 22:34:06 -08:00
// map is command -> channel -> data
2016-04-28 15:33:49 -07:00
// CachedLastMessages is of CacheTypeLastOnly.
// Not actually cleaned up by reaper goroutine every ~hour.
2016-01-03 09:31:22 -08:00
var CachedLastMessages = make(map[Command]map[string]LastSavedMessage)
2015-11-08 22:34:06 -08:00
var CachedLSMLock sync.RWMutex
2016-01-17 18:01:21 -08:00
// PersistentLastMessages is of CacheTypePersistent. Never cleaned.
2016-04-28 15:33:49 -07:00
var PersistentLastMessages = CachedLastMessages
var PersistentLSMLock = CachedLSMLock
2015-11-08 22:34:06 -08:00
2015-11-16 12:50:00 -08:00
// DumpBacklogData drops all /cached_pub data.
func DumpBacklogData() {
2015-11-08 22:34:06 -08:00
CachedLSMLock.Lock()
CachedLastMessages = make(map[Command]map[string]LastSavedMessage)
CachedLSMLock.Unlock()
2016-04-28 15:33:49 -07:00
//PersistentLSMLock.Lock()
//PersistentLastMessages = make(map[Command]map[string]LastSavedMessage)
//PersistentLSMLock.Unlock()
2015-11-08 22:34:06 -08:00
}
2015-11-16 12:50:00 -08:00
// SendBacklogForNewClient sends any backlog data relevant to a new client.
// This should be done when the client sends a `ready` message.
// This will only send data for CacheTypePersistent and CacheTypeLastOnly because those do not involve timestamps.
2015-11-08 22:34:06 -08:00
func SendBacklogForNewClient(client *ClientInfo) {
client.Mutex.Lock() // reading CurrentChannels
2015-11-23 23:34:57 -08:00
curChannels := make([]string, len(client.CurrentChannels))
copy(curChannels, client.CurrentChannels)
client.Mutex.Unlock()
2015-11-08 22:34:06 -08:00
PersistentLSMLock.RLock()
2016-04-28 14:39:20 -07:00
for _, cmd := range GetCommandsOfType(CacheTypePersistent) {
chanMap := PersistentLastMessages[cmd]
2015-11-08 22:34:06 -08:00
if chanMap == nil {
continue
}
2015-11-23 23:34:57 -08:00
for _, channel := range curChannels {
2015-11-08 22:34:06 -08:00
msg, ok := chanMap[channel]
if ok {
msg := ClientMessage{MessageID: -1, Command: cmd, origArguments: msg.Data}
msg.parseOrigArguments()
client.MessageChannel <- msg
}
2015-10-25 03:21:50 -07:00
}
}
2015-11-08 22:34:06 -08:00
PersistentLSMLock.RUnlock()
CachedLSMLock.RLock()
2016-04-28 14:39:20 -07:00
for _, cmd := range GetCommandsOfType(CacheTypeLastOnly) {
2015-11-08 22:34:06 -08:00
chanMap := CachedLastMessages[cmd]
if chanMap == nil {
continue
}
2015-11-23 23:34:57 -08:00
for _, channel := range curChannels {
2015-11-08 22:34:06 -08:00
msg, ok := chanMap[channel]
if ok {
msg := ClientMessage{MessageID: -1, Command: cmd, origArguments: msg.Data}
msg.parseOrigArguments()
client.MessageChannel <- msg
}
}
}
CachedLSMLock.RUnlock()
2015-10-25 03:21:50 -07:00
}
2016-04-28 14:39:20 -07:00
func SendBacklogForChannel(client *ClientInfo, channel string) {
PersistentLSMLock.RLock()
for _, cmd := range GetCommandsOfType(CacheTypePersistent) {
chanMap := PersistentLastMessages[cmd]
if chanMap == nil {
continue
}
if msg, ok := chanMap[channel]; ok {
msg := ClientMessage{MessageID: -1, Command: cmd, origArguments: msg.Data}
msg.parseOrigArguments()
client.MessageChannel <- msg
}
}
PersistentLSMLock.RUnlock()
CachedLSMLock.RLock()
for _, cmd := range GetCommandsOfType(CacheTypeLastOnly) {
chanMap := CachedLastMessages[cmd]
if chanMap == nil {
continue
}
if msg, ok := chanMap[channel]; ok {
msg := ClientMessage{MessageID: -1, Command: cmd, origArguments: msg.Data}
msg.parseOrigArguments()
client.MessageChannel <- msg
}
}
CachedLSMLock.RUnlock()
}
2015-11-16 12:50:00 -08:00
type timestampArray interface {
2015-11-08 22:34:06 -08:00
Len() int
GetTime(int) time.Time
}
2015-11-08 22:34:06 -08:00
func SaveLastMessage(which map[Command]map[string]LastSavedMessage, locker sync.Locker, cmd Command, channel string, timestamp time.Time, data string, deleting bool) {
locker.Lock()
defer locker.Unlock()
2016-04-28 15:33:49 -07:00
chanMap, ok := CachedLastMessages[cmd]
2015-11-08 22:34:06 -08:00
if !ok {
if deleting {
return
}
chanMap = make(map[string]LastSavedMessage)
2016-04-28 15:33:49 -07:00
CachedLastMessages[cmd] = chanMap
2015-11-08 22:34:06 -08:00
}
if deleting {
delete(chanMap, channel)
} else {
2016-01-17 18:01:21 -08:00
chanMap[channel] = LastSavedMessage{Timestamp: timestamp, Data: data}
2015-11-08 22:34:06 -08:00
}
}
2016-04-28 14:39:20 -07:00
func GetCommandsOfType(match BacklogCacheType) []Command {
if match == CacheTypePersistent {
return PersistentCachingCommands
} else if match == CacheTypeLastOnly {
return HourlyCachingCommands
} else {
panic("unknown caching type")
2015-10-25 03:21:50 -07:00
}
}
2015-11-16 12:50:00 -08:00
func HTTPBackendDropBacklog(w http.ResponseWriter, r *http.Request) {
2015-11-08 22:34:06 -08:00
r.ParseForm()
formData, err := UnsealRequest(r.Form)
if err != nil {
w.WriteHeader(403)
fmt.Fprintf(w, "Error: %v", err)
return
}
confirm := formData.Get("confirm")
if confirm == "1" {
2015-11-16 12:50:00 -08:00
DumpBacklogData()
2015-10-29 10:29:16 -07:00
}
2015-10-25 03:21:50 -07:00
}
2016-01-17 18:01:21 -08:00
// HTTPBackendCachedPublish handles the /cached_pub route.
// It publishes a message to clients, and then updates the in-server cache for the message.
2015-11-08 22:34:06 -08:00
// notes:
// `scope` is implicit in the command
2015-11-16 13:25:25 -08:00
func HTTPBackendCachedPublish(w http.ResponseWriter, r *http.Request) {
2015-11-08 22:34:06 -08:00
r.ParseForm()
formData, err := UnsealRequest(r.Form)
if err != nil {
w.WriteHeader(403)
fmt.Fprintf(w, "Error: %v", err)
return
}
cmd := CommandPool.InternCommand(formData.Get("cmd"))
2015-11-08 22:34:06 -08:00
json := formData.Get("args")
channel := formData.Get("channel")
deleteMode := formData.Get("delete") != ""
timeStr := formData.Get("time")
timeNum, err := strconv.ParseInt(timeStr, 10, 64)
2015-11-08 22:34:06 -08:00
if err != nil {
w.WriteHeader(422)
fmt.Fprintf(w, "error parsing time: %v", err)
return
2015-11-08 22:34:06 -08:00
}
timestamp := time.Unix(timeNum, 0)
2015-10-25 03:21:50 -07:00
cacheinfo, ok := S2CCommandsCacheInfo[cmd]
2015-11-08 22:34:06 -08:00
if !ok {
w.WriteHeader(422)
fmt.Fprintf(w, "Caching semantics unknown for command '%s'. Post to /addcachedcommand first.", cmd)
return
}
2015-11-08 22:34:06 -08:00
var count int
msg := ClientMessage{MessageID: -1, Command: cmd, origArguments: json}
msg.parseOrigArguments()
if cacheinfo.Caching == CacheTypeLastOnly && cacheinfo.Target == MsgTargetTypeChat {
SaveLastMessage(CachedLastMessages, &CachedLSMLock, cmd, channel, timestamp, json, deleteMode)
count = PublishToChannel(channel, msg)
} else if cacheinfo.Caching == CacheTypePersistent && cacheinfo.Target == MsgTargetTypeChat {
SaveLastMessage(PersistentLastMessages, &PersistentLSMLock, cmd, channel, timestamp, json, deleteMode)
count = PublishToChannel(channel, msg)
2015-11-19 16:38:07 -08:00
} else if cacheinfo.Caching == CacheTypeLastOnly && cacheinfo.Target == MsgTargetTypeMultichat {
channels := strings.Split(channel, ",")
2016-01-27 19:40:17 -08:00
var dummyLock sync.Mutex
CachedLSMLock.Lock()
2015-11-19 16:38:07 -08:00
for _, channel := range channels {
2016-01-27 19:40:17 -08:00
SaveLastMessage(CachedLastMessages, &dummyLock, cmd, channel, timestamp, json, deleteMode)
2015-11-19 16:38:07 -08:00
}
2016-01-27 19:40:17 -08:00
CachedLSMLock.Unlock()
2015-11-19 16:38:07 -08:00
count = PublishToMultiple(channels, msg)
2015-10-25 03:21:50 -07:00
}
2015-11-08 22:34:06 -08:00
w.Write([]byte(strconv.Itoa(count)))
}