현 상태를 친구에게 공개
This commit is contained in:
107
core/friend.go
107
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))
|
||||
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user