223 lines
6.8 KiB
Go
223 lines
6.8 KiB
Go
package core
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
"reflect"
|
|
|
|
"repositories.action2quare.com/ayo/gocommon"
|
|
"repositories.action2quare.com/ayo/gocommon/logger"
|
|
"repositories.action2quare.com/ayo/gocommon/wshandler"
|
|
)
|
|
|
|
func init() {
|
|
groupTypeContainer()["chat"] = reflect.TypeOf(&groupChat{})
|
|
}
|
|
|
|
type channelID = string
|
|
type chatConfig struct {
|
|
DefaultCapacity int64 `json:"default_capacity"`
|
|
Channels map[string]map[string]any `json:"channels"`
|
|
}
|
|
|
|
type groupChat struct {
|
|
chatConfig
|
|
rh *gocommon.RedisonHandler
|
|
incSizeScript string
|
|
decSizeScript string
|
|
enterRoom func(channelID, accountID)
|
|
leaveRoom func(channelID, accountID)
|
|
sendUpstreamMessage func(msg *wshandler.UpstreamMessage)
|
|
}
|
|
|
|
var increaseSizeScript = `
|
|
local cap = redis.call("HGET", KEYS[1], "capacity")
|
|
local newseq = redis.call("HINCRBY", KEYS[1], "seq", 1)
|
|
|
|
local newsize = redis.call("HINCRBY", KEYS[1], "size", 1)
|
|
if tonumber(cap) < newsize then
|
|
redis.call("HINCRBY", KEYS[1], "size", -1)
|
|
return {err = "channel is full"}
|
|
end
|
|
|
|
redis.call("HSET", "_m_"..KEYS[1], KEYS[2], ARGV[1])
|
|
return {newsize, newseq}
|
|
`
|
|
var decreaseSizeScript = `
|
|
local newseq = redis.call("HINCRBY", KEYS[1], "seq", 1)
|
|
local newsize = redis.call("HINCRBY", KEYS[1], "size", -1)
|
|
redis.call("HDEL", "_m_"..KEYS[1], KEYS[2])
|
|
|
|
return {newsize, newseq}
|
|
`
|
|
|
|
func (gc *groupChat) Initialize(sub *subTavern, cfg configDocument) error {
|
|
incScript, err := sub.redisClient.ScriptLoad(sub.redisClient.Context(), increaseSizeScript).Result()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
decScript, err := sub.redisClient.ScriptLoad(sub.redisClient.Context(), decreaseSizeScript).Result()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// newsize, err := sub.redisClient.EvalSha(sub.redisClient.Context(), incScript, []string{"myhash", "alias"}, "accid").Result()
|
|
// if err != nil {
|
|
// return err
|
|
// }
|
|
// logger.Println(newsize.([]any))
|
|
// newsize, err = sub.redisClient.EvalSha(sub.redisClient.Context(), decScript, []string{"myhash", "alias"}).Result()
|
|
// if err != nil {
|
|
// return err
|
|
// }
|
|
// logger.Println(newsize.([]any))
|
|
|
|
rem, _ := json.Marshal(cfg)
|
|
if err = json.Unmarshal(rem, &gc.chatConfig); err != nil {
|
|
return err
|
|
}
|
|
|
|
gc.enterRoom = func(chanid channelID, accid accountID) {
|
|
sub.wsh.EnterRoom(sub.region, string(chanid), accid)
|
|
}
|
|
gc.leaveRoom = func(chanid channelID, accid accountID) {
|
|
sub.wsh.LeaveRoom(sub.region, string(chanid), accid)
|
|
}
|
|
gc.sendUpstreamMessage = func(msg *wshandler.UpstreamMessage) {
|
|
sub.wsh.SendUpstreamMessage(sub.region, msg)
|
|
}
|
|
|
|
gc.rh = gocommon.NewRedisonHandler(sub.redisClient.Context(), sub.redisClient)
|
|
gc.incSizeScript = incScript
|
|
gc.decSizeScript = decScript
|
|
|
|
sub.apiFuncs.registApiFunction("CreateChattingChannel", gc.CreateChattingChannel)
|
|
sub.apiFuncs.registApiFunction("FetchChattingChannels", gc.FetchChattingChannels)
|
|
|
|
for name, cfg := range gc.chatConfig.Channels {
|
|
if _, ok := cfg["capacity"]; !ok {
|
|
cfg["capacity"] = gc.chatConfig.DefaultCapacity
|
|
}
|
|
cfg["key"] = name
|
|
|
|
_, err := gc.rh.HMSet(gc.rh.Context(), name, cfg).Result()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (gc *groupChat) ClientMessageReceived(sender *wshandler.Sender, mt wshandler.WebSocketMessageType, message any) {
|
|
if mt == wshandler.Disconnected {
|
|
rooms := message.([]string)
|
|
for _, chanid := range rooms {
|
|
sizeseq, err := gc.rh.EvalSha(gc.rh.Context(), gc.decSizeScript, []string{chanid, sender.Alias}).Result()
|
|
if err == nil {
|
|
gc.sendUpstreamMessage(&wshandler.UpstreamMessage{
|
|
Target: "#" + chanid,
|
|
Body: map[string]any{"size": sizeseq.([]any)[0], "seq": sizeseq.([]any)[1]},
|
|
Tag: []string{"ChattingChannelProperties"},
|
|
})
|
|
}
|
|
}
|
|
} else if mt == wshandler.BinaryMessage {
|
|
commandline := message.([]any)
|
|
cmd := commandline[0].(string)
|
|
args := commandline[1:]
|
|
switch cmd {
|
|
case "EnterChattingChannel":
|
|
chanid := args[0].(string)
|
|
sizeseq, err := gc.rh.EvalSha(gc.rh.Context(), gc.incSizeScript, []string{chanid, sender.Alias}, sender.Accid.Hex()).Result()
|
|
if err == nil {
|
|
gc.enterRoom(chanid, sender.Accid)
|
|
gc.sendUpstreamMessage(&wshandler.UpstreamMessage{
|
|
Target: "#" + chanid,
|
|
Body: map[string]any{"size": sizeseq.([]any)[0], "seq": sizeseq.([]any)[1]},
|
|
Tag: []string{"ChattingChannelProperties"},
|
|
})
|
|
} else {
|
|
// 입장 실패 알림
|
|
gc.sendUpstreamMessage(&wshandler.UpstreamMessage{
|
|
Target: "@" + sender.Accid.Hex(),
|
|
Body: map[string]string{"id": chanid, "err": err.Error()},
|
|
Tag: []string{"EnterChattingChannelFailed"},
|
|
})
|
|
}
|
|
|
|
case "LeaveChattingChannel":
|
|
chanid := args[0].(string)
|
|
gc.leaveRoom(chanid, sender.Accid)
|
|
sizeseq, err := gc.rh.EvalSha(gc.rh.Context(), gc.decSizeScript, []string{chanid, sender.Alias}).Result()
|
|
if err == nil {
|
|
gc.sendUpstreamMessage(&wshandler.UpstreamMessage{
|
|
Target: "#" + chanid,
|
|
Body: map[string]any{"size": sizeseq.([]any)[0], "seq": sizeseq.([]any)[1]},
|
|
Tag: []string{"ChattingChannelProperties"},
|
|
})
|
|
}
|
|
|
|
case "TextMessage":
|
|
chanid := args[0].(string)
|
|
msg := args[1].(string)
|
|
gc.sendUpstreamMessage(&wshandler.UpstreamMessage{
|
|
Target: "#" + chanid,
|
|
Body: map[string]any{"sender": sender.Alias, "msg": msg},
|
|
Tag: []string{"TextMessage"},
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
func (gc *groupChat) CreateChattingChannel(w http.ResponseWriter, r *http.Request) {
|
|
chanstr, _ := gocommon.ReadStringFormValue(r.Form, "name")
|
|
if len(chanstr) == 0 {
|
|
logger.Println("CreateChattingChannel failed. name is missing")
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
capacity, _ := gocommon.ReadIntegerFormValue(r.Form, "capacity")
|
|
if capacity == 0 {
|
|
capacity = gc.chatConfig.DefaultCapacity
|
|
}
|
|
|
|
_, err := gc.rh.HSetNX(gc.rh.Context(), chanstr, "capacity", capacity).Result()
|
|
if err != nil {
|
|
logger.Println("CreateChattingChannel failed. HSetNX returns err :", err)
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
return
|
|
}
|
|
}
|
|
|
|
func (gc *groupChat) FetchChattingChannels(w http.ResponseWriter, r *http.Request) {
|
|
prefix, _ := gocommon.ReadStringFormValue(r.Form, "prefix")
|
|
if len(prefix) == 0 {
|
|
logger.Println("FetchChattingChannel failed. prefix is missing")
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
keys, err := gc.rh.Keys(gc.rh.Context(), prefix+"*").Result()
|
|
if err != nil {
|
|
logger.Println("FetchChattingChannel failed. Keys return err :", err)
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
var channels []map[string]string
|
|
for _, key := range keys {
|
|
onechan, err := gc.rh.HGetAll(gc.rh.Context(), key).Result()
|
|
if err != nil {
|
|
logger.Println("FetchChattingChannel failed. HGetAll return err :", err)
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
return
|
|
}
|
|
channels = append(channels, onechan)
|
|
}
|
|
|
|
enc := json.NewEncoder(w)
|
|
enc.Encode(channels)
|
|
}
|