현 상태를 친구에게 공개

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 (
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))

View File

@ -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
}

View File

@ -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

View File

@ -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)
}