diff --git a/socketserver/server/commands.go b/socketserver/server/commands.go index dc400717..8ba96fb9 100644 --- a/socketserver/server/commands.go +++ b/socketserver/server/commands.go @@ -96,7 +96,7 @@ func DispatchC2SCommand(conn *websocket.Conn, client *ClientInfo, msg ClientMess } } -func callHandler(handler CommandHandler, conn *websocket.Conn, client *ClientInfo, cmsg ClientMessage) (rmsg ClientMessage, err error) { +func callHandler(handler CommandHandler, conn *websocket.Conn, client *ClientInfo, cmsg ClientMessage) (_ ClientMessage, err error) { defer func() { if r := recover(); r != nil { var ok bool @@ -114,7 +114,7 @@ var DebugHello = "" // C2SHello implements the `hello` C2S Command. // It calls SubscribeGlobal() and SubscribeDefaults() with the client, and fills out ClientInfo.Version and ClientInfo.ClientID. -func C2SHello(conn *websocket.Conn, client *ClientInfo, msg ClientMessage) (rmsg ClientMessage, err error) { +func C2SHello(_ *websocket.Conn, client *ClientInfo, msg ClientMessage) (_ ClientMessage, err error) { ary, ok := msg.Arguments.([]interface{}) if !ok { if DebugHello != "" { @@ -177,13 +177,13 @@ func C2SHello(conn *websocket.Conn, client *ClientInfo, msg ClientMessage) (rmsg }, nil } -func C2SPing(conn *websocket.Conn, client *ClientInfo, msg ClientMessage) (rmsg ClientMessage, err error) { +func C2SPing(*websocket.Conn, *ClientInfo, ClientMessage) (ClientMessage, error) { return ClientMessage{ Arguments: float64(time.Now().UnixNano()/1000) / 1000, }, nil } -func C2SSetUser(conn *websocket.Conn, client *ClientInfo, msg ClientMessage) (rmsg ClientMessage, err error) { +func C2SSetUser(_ *websocket.Conn, client *ClientInfo, msg ClientMessage) (ClientMessage, error) { username, err := msg.ArgumentsAsString() if err != nil { return @@ -206,26 +206,21 @@ func C2SSetUser(conn *websocket.Conn, client *ClientInfo, msg ClientMessage) (rm return ResponseSuccess, nil } -func C2SReady(conn *websocket.Conn, client *ClientInfo, msg ClientMessage) (rmsg ClientMessage, err error) { - // disconnectAt, err := msg.ArgumentsAsInt() - // if err != nil { - // return - // } - +func C2SReady(_ *websocket.Conn, client *ClientInfo, msg ClientMessage) (ClientMessage, error) { client.Mutex.Lock() client.ReadyComplete = true client.Mutex.Unlock() client.MsgChannelKeepalive.Add(1) go func() { - client.Send(ClientMessage{MessageID: msg.MessageID, Command: SuccessCommand}) + client.Send(msg.Reply(SuccessCommand, nil)) SendBacklogForNewClient(client) client.MsgChannelKeepalive.Done() }() return ClientMessage{Command: AsyncResponseCommand}, nil } -func C2SSubscribe(conn *websocket.Conn, client *ClientInfo, msg ClientMessage) (rmsg ClientMessage, err error) { +func C2SSubscribe(_ *websocket.Conn, client *ClientInfo, msg ClientMessage) (ClientMessage, error) { channel, err := msg.ArgumentsAsString() if err != nil { return @@ -252,7 +247,7 @@ func C2SSubscribe(conn *websocket.Conn, client *ClientInfo, msg ClientMessage) ( // C2SUnsubscribe implements the `unsub` C2S Command. // It removes the channel from ClientInfo.CurrentChannels and calls UnsubscribeSingleChat. -func C2SUnsubscribe(conn *websocket.Conn, client *ClientInfo, msg ClientMessage) (rmsg ClientMessage, err error) { +func C2SUnsubscribe(_ *websocket.Conn, client *ClientInfo, msg ClientMessage) (ClientMessage, error) { channel, err := msg.ArgumentsAsString() if err != nil { return @@ -270,9 +265,8 @@ func C2SUnsubscribe(conn *websocket.Conn, client *ClientInfo, msg ClientMessage) } // C2SSurvey implements the survey C2S Command. -// Surveys are discarded.s -func C2SSurvey(conn *websocket.Conn, client *ClientInfo, msg ClientMessage) (rmsg ClientMessage, err error) { - // Discard +func C2SSurvey(*websocket.Conn, *ClientInfo, ClientMessage) (ClientMessage, error) { + // Surveys are not collected. return ResponseSuccess, nil } @@ -290,7 +284,7 @@ var followEventsLock sync.Mutex // C2STrackFollow implements the `track_follow` C2S Command. // It adds the record to `followEvents`, which is submitted to the backend on a timer. -func C2STrackFollow(conn *websocket.Conn, client *ClientInfo, msg ClientMessage) (rmsg ClientMessage, err error) { +func C2STrackFollow(_ *websocket.Conn, client *ClientInfo, msg ClientMessage) (_ ClientMessage, err error) { channel, following, err := msg.ArgumentsAsStringAndBool() if err != nil { return @@ -317,58 +311,8 @@ var ErrNegativeEmoteUsage = errors.New("Emote usage count cannot be negative") // C2SEmoticonUses implements the `emoticon_uses` C2S Command. // msg.Arguments are in the JSON format of [1]map[emoteID]map[ChatroomName]float64. -func C2SEmoticonUses(conn *websocket.Conn, client *ClientInfo, msg ClientMessage) (rmsg ClientMessage, err error) { - // if this panics, will be caught by callHandler - mapRoot := msg.Arguments.([]interface{})[0].(map[string]interface{}) - - // Validate: male suire - for strEmote, val1 := range mapRoot { - _, err = strconv.Atoi(strEmote) - if err != nil { - return - } - mapInner := val1.(map[string]interface{}) - for _, val2 := range mapInner { - var count = int(val2.(float64)) - if count <= 0 { - err = ErrNegativeEmoteUsage - return - } - } - } - - aggregateEmoteUsageLock.Lock() - defer aggregateEmoteUsageLock.Unlock() - - var total int - - for strEmote, val1 := range mapRoot { - var emoteID int - emoteID, err = strconv.Atoi(strEmote) - if err != nil { - return - } - - destMapInner, ok := aggregateEmoteUsage[emoteID] - if !ok { - destMapInner = make(map[string]int) - aggregateEmoteUsage[emoteID] = destMapInner - } - - mapInner := val1.(map[string]interface{}) - for roomName, val2 := range mapInner { - var count = int(val2.(float64)) - if count > 200 { - count = 200 - } - roomName = TwitchChannelPool.Intern(roomName) - destMapInner[roomName] += count - total += count - } - } - - Statistics.EmotesReportedTotal += uint64(total) - +func C2SEmoticonUses(*websocket.Conn, *ClientInfo, ClientMessage) (ClientMessage, error) { + // We do not collect emote usage data return ResponseSuccess, nil } @@ -443,7 +387,7 @@ var bunchGroup singleflight.Group // C2SHandleBunchedCommand handles C2S Commands such as `get_link`. // It makes a request to the backend server for the data, but any other requests coming in while the first is pending also get the responses from the first one. -func C2SHandleBunchedCommand(conn *websocket.Conn, client *ClientInfo, msg ClientMessage) (ClientMessage, error) { +func C2SHandleBunchedCommand(_ *websocket.Conn, client *ClientInfo, msg ClientMessage) (ClientMessage, error) { key := fmt.Sprintf("%s:%s", msg.Command, msg.origArguments) resultCh := bunchGroup.DoChan(key, func() (interface{}, error) { @@ -453,29 +397,21 @@ func C2SHandleBunchedCommand(conn *websocket.Conn, client *ClientInfo, msg Clien client.MsgChannelKeepalive.Add(1) go func() { result := <-resultCh - var reply ClientMessage - reply.MessageID = msg.MessageID - if result.Err != nil { - reply.Command = ErrorCommand - if efb, ok := result.Err.(ErrForwardedFromBackend); ok { - reply.Arguments = efb.JSONError - } else { - reply.Arguments = result.Err.Error() - } + if efb, ok := result.Err.(ErrForwardedFromBackend); ok { + client.Send(msg.Reply(ErrorCommand, efb.JSONError)) + } else if result.Err != nil { + client.Send(msg.Reply(ErrorCommand, result.Err.Error())) } else { - reply.Command = SuccessCommand - reply.origArguments = result.Val.(string) - reply.parseOrigArguments() + client.Send(msg.ReplyJSON(SuccessCommand, result.Val.(string))) } - client.Send(reply) client.MsgChannelKeepalive.Done() }() return ClientMessage{Command: AsyncResponseCommand}, nil } -func C2SHandleRemoteCommand(conn *websocket.Conn, client *ClientInfo, msg ClientMessage) (rmsg ClientMessage, err error) { +func C2SHandleRemoteCommand(conn *websocket.Conn, client *ClientInfo, msg ClientMessage) (ClientMessage, error) { client.MsgChannelKeepalive.Add(1) go doRemoteCommand(conn, msg, client) @@ -491,7 +427,7 @@ func doRemoteCommand(conn *websocket.Conn, msg ClientMessage, client *ClientInfo if err == ErrAuthorizationNeeded { if client.TwitchUsername == "" { // Not logged in - client.Send(ClientMessage{MessageID: msg.MessageID, Command: ErrorCommand, Arguments: AuthorizationNeededError}) + client.Send(msg.Reply(ErrorCommand, AuthorizationNeededError)) client.MsgChannelKeepalive.Done() return } @@ -499,19 +435,17 @@ func doRemoteCommand(conn *websocket.Conn, msg ClientMessage, client *ClientInfo if success { doRemoteCommand(conn, msg, client) } else { - client.Send(ClientMessage{MessageID: msg.MessageID, Command: ErrorCommand, Arguments: AuthorizationFailedErrorString}) + client.Send(msg.Reply(ErrorCommand, AuthorizationFailedErrorString)) client.MsgChannelKeepalive.Done() } }) return // without keepalive.Done() } else if bfe, ok := err.(ErrForwardedFromBackend); ok { - client.Send(ClientMessage{MessageID: msg.MessageID, Command: ErrorCommand, Arguments: bfe.JSONError}) + client.Send(msg.Reply(ErrorCommand, bfe.JSONError)) } else if err != nil { - client.Send(ClientMessage{MessageID: msg.MessageID, Command: ErrorCommand, Arguments: err.Error()}) + client.Send(msg.Reply(ErrorCommand, err.Error())) } else { - msg := ClientMessage{MessageID: msg.MessageID, Command: SuccessCommand, origArguments: resp} - msg.parseOrigArguments() - client.Send(msg) + client.Send(msg.ReplyJSON(SuccessCommand, resp)) } client.MsgChannelKeepalive.Done() } diff --git a/socketserver/server/types.go b/socketserver/server/types.go index 1048434c..52197790 100644 --- a/socketserver/server/types.go +++ b/socketserver/server/types.go @@ -64,6 +64,24 @@ type ClientMessage struct { origArguments string } +func (cm ClientMessage) Reply(cmd string, args interface{}) ClientMessage { + return ClientMessage{ + MessageID: cm.MessageID, + Command: cmd, + Arguments: args, + } +} + +func (cm ClientMessage) ReplyJSON(cmd string, argsJSON string) ClientMessage { + n := ClientMessage{ + MessageID: cm.MessageID, + Command: cmd, + origArguments: argsJSON, + } + n.parseOrigArguments() + return n +} + type AuthInfo struct { // The client's claimed username on Twitch. TwitchUsername string