From ad90f636940e6e5429cec119cf058f19a31dcf2f Mon Sep 17 00:00:00 2001 From: mountain Date: Fri, 22 Sep 2023 18:32:23 +0900 Subject: [PATCH] =?UTF-8?q?=ED=98=84=20=EC=83=81=ED=83=9C=EB=A5=BC=20?= =?UTF-8?q?=EC=B9=9C=EA=B5=AC=EC=97=90=EA=B2=8C=20=EA=B3=B5=EA=B0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/friend.go | 107 ++++++++++++++++++++++---------------------- core/invitation.go | 23 +++++++--- core/social.go | 1 + core/social_test.go | 7 +++ 4 files changed, 77 insertions(+), 61 deletions(-) diff --git a/core/friend.go b/core/friend.go index 4371e2f..6d2230b 100644 --- a/core/friend.go +++ b/core/friend.go @@ -21,7 +21,6 @@ import ( const ( monitoring_center_count = 100 - state_online = "online" state_offline = "offline" ) @@ -42,7 +41,7 @@ type registerListener struct { type monitoringCenter struct { regChan chan registerListener - publishState func(string, string, string) + publishState func(primitive.ObjectID, string) } type friends struct { @@ -60,9 +59,8 @@ type listener struct { type listenerMap struct { listeners map[primitive.ObjectID]*listener - connected bool - online []byte offline []byte + lastState []byte } func init() { @@ -82,28 +80,17 @@ func combineObjectID(l primitive.ObjectID, r primitive.ObjectID) (out primitive. return } -func makeSrcMap(src string, connected bool) *listenerMap { - online, _ := json.Marshal(wshandler.DownstreamMessage{ - Body: bson.M{ - "from": src, - "state": state_online, - }, - Tag: friend_state_tag, - }) - +func makeSrcMap(src string, state []byte) *listenerMap { offline, _ := json.Marshal(wshandler.DownstreamMessage{ - Body: bson.M{ - "from": src, - "state": state_offline, - }, - Tag: friend_state_tag, + Alias: src, + Body: bson.M{}, + Tag: friend_state_tag, }) return &listenerMap{ listeners: make(map[primitive.ObjectID]*listener), - connected: connected, - online: online, offline: offline, + lastState: state, } } @@ -120,8 +107,12 @@ func makeFriends(ctx context.Context, so *Social, conns *connections) (*friends, regChan := make(chan registerListener) moncen = append(moncen, monitoringCenter{ regChan: regChan, - publishState: func(src, alias, state string) { - so.redison.Publish(ctx, subChannel, src+alias+":"+state).Result() + publishState: func(accid primitive.ObjectID, state string) { + 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 { case reg := <-regChan: // 내가 관심있는 애들 등록 - srcmap, online := listeners[reg.src] - if !online { - srcmap = makeSrcMap(reg.alias, false) + srcmap, exists := listeners[reg.src] + if !exists { + srcmap = makeSrcMap(reg.alias, nil) listeners[reg.src] = srcmap } @@ -142,51 +133,54 @@ func makeFriends(ctx context.Context, so *Social, conns *connections) (*friends, // 등록 해제. 모니터링 종료 // listener목록에서 나(reg.l.me)를 제거 delete(srcmap.listeners, reg.l.me) - online = false + exists = false logger.Println("regChan unregistered :", reg.src.Hex(), reg.l.me.Hex()) } else if oldl, ok := srcmap.listeners[reg.l.me]; ok { // 내가 이미 리스너로 등록되어 있다. // 상대방이 나를 차단했을 경우에는 기존 리스너가 nil임 - online = oldl != nil - logger.Println("regChan registered :", reg.src.Hex(), reg.l.me.Hex(), "old", online) + exists = oldl != nil } else { - logger.Println("regChan registered :", reg.src.Hex(), reg.l.me.Hex()) srcmap.listeners[reg.l.me] = reg.l } - if online && srcmap != nil { - logger.Println("regChan send online :", reg.l.me.Hex(), string(srcmap.online)) - reg.l.c.WriteMessage(websocket.TextMessage, srcmap.online) + if exists && srcmap != nil && len(srcmap.lastState) > 0 { + reg.l.c.WriteMessage(websocket.TextMessage, srcmap.lastState) } - if len(srcmap.listeners) == 0 && !srcmap.connected { + if len(srcmap.listeners) == 0 && len(srcmap.lastState) == 0 { delete(listeners, reg.src) } case msg := <-pubsub.Channel(): target, _ := primitive.ObjectIDFromHex(msg.Payload[:24]) - aliasstate := strings.SplitN(msg.Payload[24:], ":", 2) - var sent []byte + state := msg.Payload[24:] if srcmap, ok := listeners[target]; ok { - if aliasstate[1] == state_online { - sent = srcmap.online - srcmap.connected = true - } else if aliasstate[1] == state_offline { - sent = srcmap.offline - srcmap.connected = false + if srcmap == nil { + delete(listeners, target) + break + } + + if len(state) == 0 { + // 접속 종료 + srcmap.lastState = nil if len(srcmap.listeners) == 0 { delete(listeners, target) } - } - if len(sent) > 0 { for _, l := range srcmap.listeners { - logger.Println("state fire :", l.me, string(sent)) - l.c.WriteMessage(websocket.TextMessage, sent) + l.c.WriteMessage(websocket.TextMessage, srcmap.offline) + } + } else { + srcmap.lastState = []byte(state) + for _, l := range srcmap.listeners { + l.c.WriteMessage(websocket.TextMessage, srcmap.lastState) } } - } else if aliasstate[1] == state_online { - listeners[target] = makeSrcMap(aliasstate[0], true) + } else if len(state) > 0 { + 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 } -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) { // 로그 오프 상태를 알림 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) } @@ -252,6 +240,17 @@ func (fs *friends) addFriend(f *friendDoc) error { 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) { fid, _ := primitive.ObjectIDFromHex(ctx.Arguments[0].(string)) diff --git a/core/invitation.go b/core/invitation.go index 3e9858c..6b019b6 100644 --- a/core/invitation.go +++ b/core/invitation.go @@ -234,13 +234,23 @@ func (iv *invitation) Trim(ctx wshandler.ApiCallContext) { } func (iv *invitation) InviteAsFriend(w http.ResponseWriter, r *http.Request) { - // 1. mongodb에 추가 - // 1-1. block이 되어있다면(==이미 도큐먼트가 있다면) 마치 성공인 것처럼 아무것도 안하고 끝 - // 2. mongodb에 추가가 성공하면 publish + // 내 현재 친구 숫자 + 내가 보낸 초대 숫자가 FriendsMax를 넘을 수 없다. + // TODO : 이미 친구면 초대 불가 + var ivdoc invitationDoc 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) return } @@ -248,19 +258,18 @@ func (iv *invitation) InviteAsFriend(w http.ResponseWriter, r *http.Request) { // ivdoc.To가 invdoc.From을 차단했으면 표시 exists, err := iv.mongoClient.Exists(block_collection_name, bson.M{"_id": combineObjectID(ivdoc.To, ivdoc.From)}) if err != nil { - logger.Println("IniviteAsFriend failed:", err) + logger.Println("InviteAsFriend failed:", err) w.WriteHeader(http.StatusInternalServerError) return } - // exists면 차단된 상태 ivdoc.Blocked = exists ivdoc.Timestamp = time.Now().UTC().Unix() _, newid, err := iv.mongoClient.Update(invitation_collection_name, bson.M{ "_id": combineObjectID(ivdoc.From, ivdoc.To), }, bson.M{"$setOnInsert": ivdoc}, options.Update().SetUpsert(true)) if err != nil || newid == nil { - logger.Println("IniviteAsFriend failed:", err) + logger.Println("InviteAsFriend failed:", err) w.WriteHeader(http.StatusInternalServerError) return } diff --git a/core/social.go b/core/social.go index bb0ae08..8eb3b8a 100644 --- a/core/social.go +++ b/core/social.go @@ -21,6 +21,7 @@ type SocialConfig struct { MaingateApiToken string `json:"maingate_api_token"` RedisURL string `json:"social_redis_url"` MongoURL string `json:"social_storage_url"` + FriendsMax int `json:"social_friends_max"` } var config SocialConfig diff --git a/core/social_test.go b/core/social_test.go index adc8130..32df065 100644 --- a/core/social_test.go +++ b/core/social_test.go @@ -5,6 +5,7 @@ import ( "context" "encoding/binary" "fmt" + "strings" "testing" "time" @@ -61,5 +62,11 @@ func TestNameHash(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) }