현 상태를 친구에게 공개

This commit is contained in:
2023-09-22 18:32:23 +09:00
parent 343f69d662
commit ad90f63694
4 changed files with 77 additions and 61 deletions

View File

@ -21,7 +21,6 @@ import (
const ( const (
monitoring_center_count = 100 monitoring_center_count = 100
state_online = "online"
state_offline = "offline" state_offline = "offline"
) )
@ -42,7 +41,7 @@ type registerListener struct {
type monitoringCenter struct { type monitoringCenter struct {
regChan chan registerListener regChan chan registerListener
publishState func(string, string, string) publishState func(primitive.ObjectID, string)
} }
type friends struct { type friends struct {
@ -60,9 +59,8 @@ type listener struct {
type listenerMap struct { type listenerMap struct {
listeners map[primitive.ObjectID]*listener listeners map[primitive.ObjectID]*listener
connected bool
online []byte
offline []byte offline []byte
lastState []byte
} }
func init() { func init() {
@ -82,28 +80,17 @@ func combineObjectID(l primitive.ObjectID, r primitive.ObjectID) (out primitive.
return return
} }
func makeSrcMap(src string, connected bool) *listenerMap { func makeSrcMap(src string, state []byte) *listenerMap {
online, _ := json.Marshal(wshandler.DownstreamMessage{
Body: bson.M{
"from": src,
"state": state_online,
},
Tag: friend_state_tag,
})
offline, _ := json.Marshal(wshandler.DownstreamMessage{ offline, _ := json.Marshal(wshandler.DownstreamMessage{
Body: bson.M{ Alias: src,
"from": src, Body: bson.M{},
"state": state_offline, Tag: friend_state_tag,
},
Tag: friend_state_tag,
}) })
return &listenerMap{ return &listenerMap{
listeners: make(map[primitive.ObjectID]*listener), listeners: make(map[primitive.ObjectID]*listener),
connected: connected,
online: online,
offline: offline, offline: offline,
lastState: state,
} }
} }
@ -120,8 +107,12 @@ func makeFriends(ctx context.Context, so *Social, conns *connections) (*friends,
regChan := make(chan registerListener) regChan := make(chan registerListener)
moncen = append(moncen, monitoringCenter{ moncen = append(moncen, monitoringCenter{
regChan: regChan, regChan: regChan,
publishState: func(src, alias, state string) { publishState: func(accid primitive.ObjectID, state string) {
so.redison.Publish(ctx, subChannel, src+alias+":"+state).Result() if len(state) == 0 {
so.redison.Publish(ctx, subChannel, accid.Hex()).Result()
} else {
so.redison.Publish(ctx, subChannel, accid.Hex()+state).Result()
}
}, },
}) })
@ -132,9 +123,9 @@ func makeFriends(ctx context.Context, so *Social, conns *connections) (*friends,
select { select {
case reg := <-regChan: case reg := <-regChan:
// 내가 관심있는 애들 등록 // 내가 관심있는 애들 등록
srcmap, online := listeners[reg.src] srcmap, exists := listeners[reg.src]
if !online { if !exists {
srcmap = makeSrcMap(reg.alias, false) srcmap = makeSrcMap(reg.alias, nil)
listeners[reg.src] = srcmap listeners[reg.src] = srcmap
} }
@ -142,51 +133,54 @@ func makeFriends(ctx context.Context, so *Social, conns *connections) (*friends,
// 등록 해제. 모니터링 종료 // 등록 해제. 모니터링 종료
// listener목록에서 나(reg.l.me)를 제거 // listener목록에서 나(reg.l.me)를 제거
delete(srcmap.listeners, reg.l.me) delete(srcmap.listeners, reg.l.me)
online = false exists = false
logger.Println("regChan unregistered :", reg.src.Hex(), reg.l.me.Hex()) logger.Println("regChan unregistered :", reg.src.Hex(), reg.l.me.Hex())
} else if oldl, ok := srcmap.listeners[reg.l.me]; ok { } else if oldl, ok := srcmap.listeners[reg.l.me]; ok {
// 내가 이미 리스너로 등록되어 있다. // 내가 이미 리스너로 등록되어 있다.
// 상대방이 나를 차단했을 경우에는 기존 리스너가 nil임 // 상대방이 나를 차단했을 경우에는 기존 리스너가 nil임
online = oldl != nil exists = oldl != nil
logger.Println("regChan registered :", reg.src.Hex(), reg.l.me.Hex(), "old", online)
} else { } else {
logger.Println("regChan registered :", reg.src.Hex(), reg.l.me.Hex())
srcmap.listeners[reg.l.me] = reg.l srcmap.listeners[reg.l.me] = reg.l
} }
if online && srcmap != nil { if exists && srcmap != nil && len(srcmap.lastState) > 0 {
logger.Println("regChan send online :", reg.l.me.Hex(), string(srcmap.online)) reg.l.c.WriteMessage(websocket.TextMessage, srcmap.lastState)
reg.l.c.WriteMessage(websocket.TextMessage, srcmap.online)
} }
if len(srcmap.listeners) == 0 && !srcmap.connected { if len(srcmap.listeners) == 0 && len(srcmap.lastState) == 0 {
delete(listeners, reg.src) delete(listeners, reg.src)
} }
case msg := <-pubsub.Channel(): case msg := <-pubsub.Channel():
target, _ := primitive.ObjectIDFromHex(msg.Payload[:24]) target, _ := primitive.ObjectIDFromHex(msg.Payload[:24])
aliasstate := strings.SplitN(msg.Payload[24:], ":", 2) state := msg.Payload[24:]
var sent []byte
if srcmap, ok := listeners[target]; ok { if srcmap, ok := listeners[target]; ok {
if aliasstate[1] == state_online { if srcmap == nil {
sent = srcmap.online delete(listeners, target)
srcmap.connected = true break
} else if aliasstate[1] == state_offline { }
sent = srcmap.offline
srcmap.connected = false if len(state) == 0 {
// 접속 종료
srcmap.lastState = nil
if len(srcmap.listeners) == 0 { if len(srcmap.listeners) == 0 {
delete(listeners, target) delete(listeners, target)
} }
}
if len(sent) > 0 {
for _, l := range srcmap.listeners { for _, l := range srcmap.listeners {
logger.Println("state fire :", l.me, string(sent)) l.c.WriteMessage(websocket.TextMessage, srcmap.offline)
l.c.WriteMessage(websocket.TextMessage, sent) }
} else {
srcmap.lastState = []byte(state)
for _, l := range srcmap.listeners {
l.c.WriteMessage(websocket.TextMessage, srcmap.lastState)
} }
} }
} else if aliasstate[1] == state_online { } else if len(state) > 0 {
listeners[target] = makeSrcMap(aliasstate[0], true) var dnstream wshandler.DownstreamMessage
if err := json.Unmarshal([]byte(state), &dnstream); err == nil {
listeners[target] = makeSrcMap(dnstream.Alias, []byte(state))
}
} }
} }
} }
@ -202,16 +196,10 @@ func makeFriends(ctx context.Context, so *Social, conns *connections) (*friends,
}, nil }, nil
} }
func (fs *friends) ClientConnected(conn *websocket.Conn, callby *wshandler.Sender) {
// 내 로그인 상태를 알림
meidx := callby.Accid[11] % monitoring_center_count
fs.moncen[meidx].publishState(callby.Accid.Hex(), callby.Alias, state_online)
}
func (fs *friends) ClientDisconnected(conn *websocket.Conn, callby *wshandler.Sender) { func (fs *friends) ClientDisconnected(conn *websocket.Conn, callby *wshandler.Sender) {
// 로그 오프 상태를 알림 // 로그 오프 상태를 알림
meidx := callby.Accid[11] % monitoring_center_count meidx := callby.Accid[11] % monitoring_center_count
fs.moncen[meidx].publishState(callby.Accid.Hex(), callby.Alias, state_offline) fs.moncen[meidx].publishState(callby.Accid, "")
fs.stopMonitoringFriends(callby.Accid) fs.stopMonitoringFriends(callby.Accid)
} }
@ -252,6 +240,17 @@ func (fs *friends) addFriend(f *friendDoc) error {
return nil return nil
} }
func (fs *friends) BroadcastMyState(ctx wshandler.ApiCallContext) {
stateobj := ctx.Arguments[0].(string)
meidx := ctx.CallBy.Accid[11] % monitoring_center_count
bt, _ := json.Marshal(wshandler.DownstreamMessage{
Alias: ctx.CallBy.Alias,
Body: "${state}",
Tag: friend_state_tag,
})
fs.moncen[meidx].publishState(ctx.CallBy.Accid, strings.Replace(string(bt), `"${state}"`, stateobj, 1))
}
func (fs *friends) DeleteFriend(ctx wshandler.ApiCallContext) { func (fs *friends) DeleteFriend(ctx wshandler.ApiCallContext) {
fid, _ := primitive.ObjectIDFromHex(ctx.Arguments[0].(string)) fid, _ := primitive.ObjectIDFromHex(ctx.Arguments[0].(string))

View File

@ -234,13 +234,23 @@ func (iv *invitation) Trim(ctx wshandler.ApiCallContext) {
} }
func (iv *invitation) InviteAsFriend(w http.ResponseWriter, r *http.Request) { func (iv *invitation) InviteAsFriend(w http.ResponseWriter, r *http.Request) {
// 1. mongodb에 추가 // 내 현재 친구 숫자 + 내가 보낸 초대 숫자가 FriendsMax를 넘을 수 없다.
// 1-1. block이 되어있다면(==이미 도큐먼트가 있다면) 마치 성공인 것처럼 아무것도 안하고 끝 // TODO : 이미 친구면 초대 불가
// 2. mongodb에 추가가 성공하면 publish
var ivdoc invitationDoc var ivdoc invitationDoc
if err := gocommon.MakeDecoder(r).Decode(&ivdoc); err != nil { if err := gocommon.MakeDecoder(r).Decode(&ivdoc); err != nil {
logger.Println("IniviteAsFriend failed:", err) logger.Println("InviteAsFriend failed:", err)
w.WriteHeader(http.StatusBadRequest)
return
}
if exists, err := iv.mongoClient.Exists(friends_collection_name, bson.M{"_id": combineObjectID(ivdoc.From, ivdoc.To)}); err != nil {
logger.Println("InviteAsFriend failed:", err)
w.WriteHeader(http.StatusBadRequest)
return
} else if exists {
// 이미 친구
w.WriteHeader(http.StatusBadRequest) w.WriteHeader(http.StatusBadRequest)
return return
} }
@ -248,19 +258,18 @@ func (iv *invitation) InviteAsFriend(w http.ResponseWriter, r *http.Request) {
// ivdoc.To가 invdoc.From을 차단했으면 표시 // ivdoc.To가 invdoc.From을 차단했으면 표시
exists, err := iv.mongoClient.Exists(block_collection_name, bson.M{"_id": combineObjectID(ivdoc.To, ivdoc.From)}) exists, err := iv.mongoClient.Exists(block_collection_name, bson.M{"_id": combineObjectID(ivdoc.To, ivdoc.From)})
if err != nil { if err != nil {
logger.Println("IniviteAsFriend failed:", err) logger.Println("InviteAsFriend failed:", err)
w.WriteHeader(http.StatusInternalServerError) w.WriteHeader(http.StatusInternalServerError)
return return
} }
// exists면 차단된 상태
ivdoc.Blocked = exists ivdoc.Blocked = exists
ivdoc.Timestamp = time.Now().UTC().Unix() ivdoc.Timestamp = time.Now().UTC().Unix()
_, newid, err := iv.mongoClient.Update(invitation_collection_name, bson.M{ _, newid, err := iv.mongoClient.Update(invitation_collection_name, bson.M{
"_id": combineObjectID(ivdoc.From, ivdoc.To), "_id": combineObjectID(ivdoc.From, ivdoc.To),
}, bson.M{"$setOnInsert": ivdoc}, options.Update().SetUpsert(true)) }, bson.M{"$setOnInsert": ivdoc}, options.Update().SetUpsert(true))
if err != nil || newid == nil { if err != nil || newid == nil {
logger.Println("IniviteAsFriend failed:", err) logger.Println("InviteAsFriend failed:", err)
w.WriteHeader(http.StatusInternalServerError) w.WriteHeader(http.StatusInternalServerError)
return return
} }

View File

@ -21,6 +21,7 @@ type SocialConfig struct {
MaingateApiToken string `json:"maingate_api_token"` MaingateApiToken string `json:"maingate_api_token"`
RedisURL string `json:"social_redis_url"` RedisURL string `json:"social_redis_url"`
MongoURL string `json:"social_storage_url"` MongoURL string `json:"social_storage_url"`
FriendsMax int `json:"social_friends_max"`
} }
var config SocialConfig var config SocialConfig

View File

@ -5,6 +5,7 @@ import (
"context" "context"
"encoding/binary" "encoding/binary"
"fmt" "fmt"
"strings"
"testing" "testing"
"time" "time"
@ -61,5 +62,11 @@ func TestNameHash(t *testing.T) {
} }
func TestReJSON(t *testing.T) { func TestReJSON(t *testing.T) {
a := "1:2"
b := "1"
as := strings.SplitN(a, ":", 2)
bs := strings.SplitN(b, ":", 2)
fmt.Println(as, bs)
} }