package localserver import ( "context" "encoding/json" "fmt" "io/fs" "os" "path/filepath" "strconv" "github.com/pion/webrtc/v3" ) const ( JOIN_AUDIO_CHANNEL = "join_audio_channel" LEAVE_AUDIO_CHANNEL = "leave_audio_channel" LIST_AUDIO_CHANNELS = "list_audio_channels" GET_AUDIO_CHANNELS = "get_audio_channels" GET_AUDIO_CHANNEL = "get_audio_channel" CREATE_AUDIO_CHANNEL = "create_audio_channel" DELETE_AUDIO_CHANNEL = "delete_audio_channel" EDIT_AUDIO_CHANNEL_TYPE = "edit_audio_channel_type" EDIT_AUDIO_CHANNEL_NAME = "edit_audio_channel_name" ADD_AUDIO_CHANNEL_MEMBERS = "add_audio_channel_members" REMOVE_AUDIO_CHANNEL_MEMBER = "remove_audio_channel_member" ) const ( USER_JOINED_AUDIO_CHANNEL = "user_joined_audio_channel" USER_LEFT_AUDIO_CHANNEL = "user_left_audio_channel" ) type AudioChannelMember struct { } type AudioChannelConfig struct { CurrentMembersId []string ID string `json:"id"` Owner string `json:"owner"` ChannelType string `json:"channelType"` Members []string `json:"members"` } type ZoneAudioChannelsHandler struct { HostId string ZoneId string ZoneMembersId []string DataChannels map[string]*DataChannel DataChannelsFlag *uint32 AudioChannelsFlag *uint32 AudioChannels map[string]*AudioChannel reqChans []chan<- *ZoneRequest } const LOBBY string = "lobby" func NewZoneAudioChannelsHandler(hostId string, zoneId string, owner string, authorizedMembers []string, dataChannels map[string]*DataChannel, dataChannelFlag *uint32) (zoneAudioChannelsHandler *ZoneAudioChannelsHandler, err error) { var dirs []fs.DirEntry dirs, err = os.ReadDir(filepath.Join("data", "zones", zoneId, "audioChannels")) if err != nil { if os.IsNotExist(err) { logger.Printf("creating audioChannels directory for zone %s...\n", zoneId) mkdirErr := os.MkdirAll(filepath.Join("data", "zones", zoneId, "audioChannels", LOBBY), 0700) if mkdirErr != nil { return nil, mkdirErr } file, ferr := os.Create(filepath.Join("data", "zones", zoneId, "audioChannels", LOBBY, "audioChannelConfig.json")) if ferr != nil { return nil, ferr } baseConfig := AudioChannelConfig{ ID: LOBBY, Owner: owner, ChannelType: "public", CurrentMembersId: make([]string, 0), Members: make([]string, 0), } bs, jsonErr := json.Marshal(baseConfig) if jsonErr != nil { return nil, jsonErr } if _, writeErr := file.WriteString(string(bs)); writeErr != nil { return nil, writeErr } _ = file.Close() dirs, err = os.ReadDir(filepath.Join("data", "zones", zoneId, "audioChannels")) if err != nil { return nil, err } } else { return } } audioChannels := make(map[string]*AudioChannel) for _, audioChannel := range dirs { var bs []byte bs, err = os.ReadFile(filepath.Join("data", "zones", zoneId, "audioChannels", audioChannel.Name(), "audioChannelConfig.json")) if err != nil { return nil, err } logger.Println(string(bs)) var acc AudioChannelConfig if err = json.Unmarshal(bs, &acc); err != nil { return nil, err } logger.Println("audioChannels data :", acc.ID, acc.ChannelType, acc.Owner, acc.Members) ac := NewAudioChannel(acc.ID, acc.Owner, acc.ChannelType, acc.Members, make([]string, 0), make(map[string]*AudioChannelMember)) audioChannels[acc.ID] = ac } audioChannelFlag := uint32(0) zoneAudioChannelsHandler = &ZoneAudioChannelsHandler{ HostId: hostId, ZoneId: zoneId, ZoneMembersId: authorizedMembers, DataChannels: dataChannels, DataChannelsFlag: dataChannelFlag, AudioChannels: audioChannels, AudioChannelsFlag: &audioChannelFlag, } return } type SendDCMessageFunc = func(reqType string, from string, to string, payload map[string]interface{}) (<-chan struct{}, <-chan error) func (zach *ZoneAudioChannelsHandler) sendZoneRequest(reqType string, from string, payload map[string]interface{}) { go func() { for _, rc := range zach.reqChans { rc <- &ZoneRequest{ ReqType: reqType, From: from, Payload: payload, } } }() } func (zach *ZoneAudioChannelsHandler) sendDataChannelMessage(reqType string, from string, to string, payload map[string]interface{}) (<-chan struct{}, <-chan error) { done, errCh := make(chan struct{}), make(chan error) go func() { if err := atomicallyExecute(zach.DataChannelsFlag, func() (err error) { if _, ok := zach.DataChannels[to]; ok { bs, jsonErr := json.Marshal(&ZoneResponse{ Type: reqType, From: zach.HostId, To: to, Payload: payload, }) if jsonErr != nil { return jsonErr } err = zach.DataChannels[to].DataChannel.SendText(string(bs)) } return }); err != nil { errCh <- err return } done <- struct{}{} }() return done, errCh } func (zach *ZoneAudioChannelsHandler) Init(ctx context.Context, authorizedMembers []string) (err error) { for _, member := range authorizedMembers { if serr := zach.SetAllPublicAudioChannelForUser(member); serr != nil { logger.Println(serr) } } return } func (zach *ZoneAudioChannelsHandler) Subscribe(ctx context.Context, publisher <-chan *ZoneRequest) (reqChan chan *ZoneRequest, done chan struct{}, errCh chan error) { reqChan, done, errCh = make(chan *ZoneRequest), make(chan struct{}), make(chan error) zach.reqChans = append(zach.reqChans, reqChan) go func() { for { select { case <-ctx.Done(): done <- struct{}{} return case req := <-publisher: if err := zach.handleZoneRequest(ctx, req); err != nil { errCh <- err } } } }() return } func (zach *ZoneAudioChannelsHandler) signalCandidate(from string, to string, candidate *webrtc.ICECandidate) (err error) { d, e := zach.sendDataChannelMessage(string(AUDIO_CHANNEL_WEBRTC_CANDIDATE), from, to, map[string]interface{}{ "from": from, "to": to, "candidate": candidate.ToJSON().Candidate, "sdpMid": *candidate.ToJSON().SDPMid, "sdpMlineIndex": strconv.Itoa(int(*candidate.ToJSON().SDPMLineIndex)), }) select { case <-d: case err = <-e: } return } func (zach *ZoneAudioChannelsHandler) GetAudioChannels(userId string, channelsId ...interface{}) (err error) { audioChannels := make([]*AudioChannel, 0, len(channelsId)) for _, id := range channelsId { if _, ok := id.(string); !ok { err = fmt.Errorf("id of wrong type") return } logger.Println("audioChannel from get audioChannels", id.(string)) _ = atomicallyExecute(zach.AudioChannelsFlag, func() (err error) { if _, ok := zach.AudioChannels[id.(string)]; ok { logger.Println(zach.AudioChannels[id.(string)]) audioChannels = append(audioChannels, zach.AudioChannels[id.(string)]) } return }) } answer := &ZoneResponse{ Type: "get_audio_channels_response", From: "", To: "", Payload: map[string]interface{}{ "audioChannels": audioChannels, }, } bs, jsonErr := json.Marshal(answer) if jsonErr != nil { return jsonErr } err = atomicallyExecute(zach.DataChannelsFlag, func() (err error) { if _, ok := zach.DataChannels[userId]; ok { err = zach.DataChannels[userId].DataChannel.SendText(string(bs)) if err != nil { return } } return }) return } func (zach *ZoneAudioChannelsHandler) ListAudioChannels() (audioChannels []*AudioChannel, err error) { audioChannels = make([]*AudioChannel, 0) err = atomicallyExecute(zach.AudioChannelsFlag, func() (err error) { for _, audioChannel := range zach.AudioChannels { audioChannels = append(audioChannels, audioChannel) } return }) return } func (zach *ZoneAudioChannelsHandler) AddNewAudioChannel(channelName string, owner string, channelType string, members []interface{}) (err error) { if _, ok := zach.AudioChannels[channelName]; ok { err = fmt.Errorf("an audio channel with this name already exist") return } mkdirErr := os.MkdirAll(filepath.Join("data", "zones", zach.ZoneId, "audioChannels", channelName), 0700) if mkdirErr != nil { return mkdirErr } file, ferr := os.Create(filepath.Join("data", "zones", zach.ZoneId, "audioChannels", channelName, "audioChannelConfig.json")) if ferr != nil { return ferr } m := make([]string, 0, len(members)) for _, member := range members { if _, ok := member.(string); ok { m = append(m, member.(string)) } } baseConfig := &AudioChannelConfig{ ID: channelName, Owner: owner, ChannelType: channelType, Members: m, } bs, jsonErr := json.Marshal(baseConfig) if jsonErr != nil { return jsonErr } if _, writeErr := file.WriteString(string(bs)); writeErr != nil { return writeErr } err = file.Close() if err != nil { return err } var acc AudioChannelConfig if err = json.Unmarshal(bs, &acc); err != nil { return err } ac := NewAudioChannel(acc.ID, acc.Owner, acc.ChannelType, acc.Members, acc.CurrentMembersId, make(map[string]*AudioChannelMember)) _ = atomicallyExecute(zach.AudioChannelsFlag, func() (err error) { zach.AudioChannels[ac.ID] = ac return }) newAudioChannelForMembers := func(members []string) (err error) { for _, member := range members { zach.sendZoneRequest(ADD_KNOWN_AUDIO_CHANNEL, "node", map[string]interface{}{ "userId": member, "channelId": channelName, }) done, e := zach.sendDataChannelMessage("get_audio_channels_response", "node", member, map[string]interface{}{ "audioChannels": []*AudioChannel{zach.AudioChannels[channelName]}, }) select { case <-done: case err = <-e: return } } return } switch ac.ChannelType { case BROADCAST: fallthrough case PUBLIC: err = atomicallyExecute(zach.AudioChannelsFlag, func() (err error) { err = newAudioChannelForMembers(zach.ZoneMembersId) return }) case PRIVATE: err = newAudioChannelForMembers(ac.Members) } return } func (zach *ZoneAudioChannelsHandler) DeleteAudioChannel(channelId string) (err error) { if err = atomicallyExecute(zach.AudioChannelsFlag, func() (err error) { defer delete(zach.AudioChannels, channelId) if _, ok := zach.AudioChannels[channelId]; !ok { err = fmt.Errorf("no corresponding audio channel") return } removeKnownAudioChannels := func(members []string) (err error) { for _, member := range members { zach.sendZoneRequest(REMOVE_KNOWN_AUDIO_CHANNEL, "node", map[string]interface{}{ "userId": member, "channelId": channelId, }) done, e := zach.sendDataChannelMessage(REMOVED_FROM_CHAT, "node", member, map[string]interface{}{ "channelId": channelId, "userId": member, }) select { case <-done: continue case err = <-e: return } } return } switch zach.AudioChannels[channelId].ChannelType { case BROADCAST: fallthrough case PUBLIC: err = removeKnownAudioChannels(zach.ZoneMembersId) case PRIVATE: err = removeKnownAudioChannels(zach.AudioChannels[channelId].Members) } return }); err != nil { return } if err = os.RemoveAll(filepath.Join("data", "zones", zach.ZoneId, "audioChannels", channelId)); err != nil { return } return } func (zach *ZoneAudioChannelsHandler) EditAudioChannelName(channelId string, newAudioChannelId string) (err error) { if _, ok := zach.AudioChannels[channelId]; !ok { err = fmt.Errorf("no coresponding audioChannel") return } bs, err := os.ReadFile(filepath.Join("data", "zones", zach.ZoneId, "audioChannels", channelId, "audioChannelConfig.json")) if err != nil { return } var audioChannelConfig AudioChannelConfig if err = json.Unmarshal(bs, &audioChannelConfig); err != nil { return } audioChannelConfig.ID = newAudioChannelId bs, err = json.Marshal(&audioChannelConfig) if err = os.Rename(filepath.Join("data", "zones", zach.ZoneId, "audioChannels", channelId), filepath.Join("data", "zones", zach.ZoneId, "audioChannels", newAudioChannelId)); err != nil { return } f, err := os.OpenFile(filepath.Join("data", "zones", zach.ZoneId, "audioChannels", newAudioChannelId, "audioChannelConfig.json"), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0755) defer func() { if e := f.Close(); e != nil { logger.Println(e) } }() if err != nil { return err } if _, err = f.Write(bs); err != nil { return err } audioChannel := NewAudioChannel(audioChannelConfig.ID, audioChannelConfig.Owner, audioChannelConfig.ChannelType, audioChannelConfig.Members, make([]string, 0), make(map[string]*AudioChannelMember)) _ = atomicallyExecute(zach.AudioChannelsFlag, func() (err error) { defer delete(zach.AudioChannels, channelId) zach.AudioChannels[newAudioChannelId] = audioChannel updateKnownAudioChannels := func(members []string) (err error) { for _, member := range members { zach.sendZoneRequest(ADD_KNOWN_AUDIO_CHANNEL, "node", map[string]interface{}{ "userId": member, "channelId": newAudioChannelId, }) zach.sendZoneRequest(REMOVE_KNOWN_AUDIO_CHANNEL, "node", map[string]interface{}{ "userId": member, "channelId": channelId, }) done, e := zach.sendDataChannelMessage(CHAT_NAME_EDITED, "node", member, map[string]interface{}{ "formerAudioChannelId": channelId, "newAudioChannelId": newAudioChannelId, }) select { case <-done: case channelErr := <-e: logger.Println(channelErr) } } return } switch audioChannel.ChannelType { case BROADCAST: fallthrough case PUBLIC: err = updateKnownAudioChannels(zach.ZoneMembersId) case PRIVATE: err = updateKnownAudioChannels(audioChannel.Members) } return }) return } func (zach *ZoneAudioChannelsHandler) EditAudioChannelType(channelId string, channelType string) (err error) { if _, ok := zach.AudioChannels[channelId]; !ok { err = fmt.Errorf("no coresponding audioChannel") return } bs, err := os.ReadFile(filepath.Join("data", "zones", zach.ZoneId, "audioChannels", channelId)) if err != nil { return } var audioChannelConfig AudioChannelConfig if err = json.Unmarshal(bs, &audioChannelConfig); err != nil { return } audioChannelConfig.ChannelType = channelType bs, err = json.Marshal(&audioChannelConfig) f, err := os.OpenFile(filepath.Join("data", "zones", zach.ZoneId, "audioChannels", channelId, "audioChannelConfig.json"), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0755) defer func() { _ = f.Close() }() if err != nil { return err } if _, err = f.Write(bs); err != nil { return err } audioChannel := NewAudioChannel(audioChannelConfig.ID, audioChannelConfig.Owner, audioChannelConfig.ChannelType, audioChannelConfig.Members, make([]string, 0), make(map[string]*AudioChannelMember)) switch channelType { case BROADCAST: fallthrough case PUBLIC: var members = []string{} _ = atomicallyExecute(zach.AudioChannelsFlag, func() (err error) { members = append(members, zach.ZoneMembersId...) return }) for _, member := range zach.ZoneMembersId { if pubErr := zach.SetAudioChannelPublicForUser(channelId, member); pubErr != nil { logger.Println(pubErr) } } case PRIVATE: var members = []string{} _ = atomicallyExecute(zach.AudioChannelsFlag, func() (err error) { members = append(members, zach.ZoneMembersId...) return }) for _, member := range zach.ZoneMembersId { if pubErr := zach.SetAudioChannelPrivateForUser(channelId, member); pubErr != nil { logger.Println(pubErr) } } } _ = atomicallyExecute(zach.AudioChannelsFlag, func() (err error) { zach.AudioChannels[channelId] = audioChannel return }) return } func (zach *ZoneAudioChannelsHandler) AddAudioChannelsMembers(channelId string, members []interface{}) (err error) { if _, ok := zach.AudioChannels[channelId]; !ok { err = fmt.Errorf("no coresponding audioChannel") return } bs, err := os.ReadFile(filepath.Join("data", "zones", zach.ZoneId, "audioChannels", channelId, "audioChannelConfig.json")) if err != nil { return } var audioChannelConfig AudioChannelConfig if err = json.Unmarshal(bs, &audioChannelConfig); err != nil { return } logger.Printf("%s - %s - %s -%v\n", audioChannelConfig.ID, audioChannelConfig.ChannelType, audioChannelConfig.Owner, members) addedMembers := make([]string, 0) memberLoop: for _, audioChannelMember := range members { logger.Println("entering broadcast loop") if _, ok := audioChannelMember.(string); !ok { continue } for _, member := range audioChannelConfig.Members { if member == audioChannelMember { continue memberLoop } } audioChannelConfig.Members = append(audioChannelConfig.Members, audioChannelMember.(string)) addedMembers = append(addedMembers, audioChannelMember.(string)) logger.Println("sending zone request", ADD_KNOWN_AUDIO_CHANNEL) zach.sendZoneRequest(ADD_KNOWN_AUDIO_CHANNEL, "node", map[string]interface{}{ "userId": audioChannelMember, "channelId": channelId, }) logger.Println("--------------done") } bs, err = json.Marshal(&audioChannelConfig) f, err := os.OpenFile(filepath.Join("data", "zones", zach.ZoneId, "audioChannels", channelId, "audioChannelConfig.json"), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0755) defer func() { _ = f.Close() }() if err != nil { return err } if _, err = f.Write(bs); err != nil { return err } ac := NewAudioChannel(audioChannelConfig.ID, audioChannelConfig.Owner, audioChannelConfig.ChannelType, audioChannelConfig.Members, make([]string, 0), make(map[string]*AudioChannelMember)) _ = atomicallyExecute(zach.AudioChannelsFlag, func() (err error) { zach.AudioChannels[channelId] = ac return }) broadcastLoop: for _, member := range ac.Members { for _, m := range addedMembers { if member == m { done, e := zach.sendDataChannelMessage(ADDED_IN_CHAT, "node", member, map[string]interface{}{ "audioChannel": ac, }) select { case <-done: case err = <-e: logger.Println(err) } continue broadcastLoop } if _, ok := zach.DataChannels[member]; ok { done, e := zach.sendDataChannelMessage(CHAT_MEMBER_ADDED, "node", member, map[string]interface{}{ "userId": m, "channelId": ac.ID, }) select { case <-done: case err = <-e: logger.Println(err) } } } } return } func (zach *ZoneAudioChannelsHandler) RemoveAudioChannelMember(channelId string, channelMember string) (err error) { if _, ok := zach.AudioChannels[channelId]; !ok { err = fmt.Errorf("no coresponding audioChannel") return } bs, err := os.ReadFile(filepath.Join("data", "zones", zach.ZoneId, "audioChannels", channelId, "audioChannelConfig.json")) if err != nil { return } var audioChannelConfig AudioChannelConfig if err = json.Unmarshal(bs, &audioChannelConfig); err != nil { logger.Println(string(bs)) logger.Println("json error right here") return } if channelMember == audioChannelConfig.Owner { err = fmt.Errorf("you cannot remove the owner from the audioChannel") return } var index int var contain bool for i, member := range audioChannelConfig.Members { if member == channelMember { index = i contain = true break } } if !contain { err = fmt.Errorf("member %s not in the channel %s", channelMember, channelId) return } audioChannelConfig.Members = append(audioChannelConfig.Members[:index], audioChannelConfig.Members[index+1:]...) bs, err = json.Marshal(&audioChannelConfig) if err != nil { logger.Println("json error there") return } f, err := os.OpenFile(filepath.Join("data", "zones", zach.ZoneId, "audioChannels", channelId, "audioChannelConfig.json"), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0755) defer func() { _ = f.Close() }() if err != nil { return } logger.Println(string(bs)) if _, err = f.Write(bs); err != nil { return } var ac *AudioChannel _ = atomicallyExecute(zach.AudioChannelsFlag, func() (err error) { ac := NewAudioChannel(audioChannelConfig.ID, audioChannelConfig.Owner, audioChannelConfig.ChannelType, audioChannelConfig.Members, make([]string, 0), make(map[string]*AudioChannelMember)) zach.AudioChannels[channelId] = ac return }) zach.sendZoneRequest(REMOVE_KNOWN_AUDIO_CHANNEL, "node", map[string]interface{}{ "userId": channelMember, "channelId": channelId, }) done, e := zach.sendDataChannelMessage(REMOVED_FROM_CHAT, "node", channelMember, map[string]interface{}{ "channelId": channelId, "userId": channelMember, }) select { case <-done: case err = <-e: logger.Println(err) } broadcastLoop: for _, member := range ac.Members { if member == channelMember { continue broadcastLoop } done, e := zach.sendDataChannelMessage(CHAT_MEMBER_REMOVED, "node", member, map[string]interface{}{ "userId": channelMember, "channelId": ac.ID, }) select { case <-done: case err = <-e: logger.Println(err) } } return } func (zach *ZoneAudioChannelsHandler) SetAudioChannelPrivateForUser(channelId string, member string) (err error) { err = atomicallyExecute(zach.AudioChannelsFlag, func() (err error) { if audioChannel, ok := zach.AudioChannels[channelId]; ok { var contain bool for _, m := range audioChannel.Members { if m == member { contain = true break } } if !contain { zach.sendZoneRequest(REMOVE_KNOWN_AUDIO_CHANNEL, "node", map[string]interface{}{ "userId": member, "channelId": channelId, }) done, e := zach.sendDataChannelMessage(REMOVED_FROM_CHAT, "node", member, map[string]interface{}{ "channelId": channelId, "userId": member, }) select { case <-done: case err = <-e: } } } return }) return } func (zach *ZoneAudioChannelsHandler) SetAudioChannelPublicForUser(channelId string, member string) (err error) { err = atomicallyExecute(zach.AudioChannelsFlag, func() (err error) { if audioChannel, ok := zach.AudioChannels[channelId]; ok { var contain bool for _, m := range audioChannel.Members { if m == member { contain = true break } } if !contain { zach.sendZoneRequest(ADD_KNOWN_AUDIO_CHANNEL, "node", map[string]interface{}{ "userId": member, "channelId": channelId, }) done, e := zach.sendDataChannelMessage(ADDED_IN_CHAT, "node", member, map[string]interface{}{ "audioChannel": audioChannel, }) select { case <-done: case err = <-e: } } } return }) return } func (zach *ZoneAudioChannelsHandler) SetAllPublicAudioChannelForUser(userId string) (err error) { audioChannels, err := zach.ListAudioChannels() if err != nil { return } for _, audioChannel := range audioChannels { if audioChannel.ChannelType == PUBLIC || audioChannel.ChannelType == BROADCAST { if audioChannel.ID == LOBBY { if err = zach.AddAudioChannelsMembers(audioChannel.ID, []interface{}{userId}); err != nil { logger.Println(err) } continue } if err = zach.SetAudioChannelPublicForUser(audioChannel.ID, userId); err != nil { continue } } } return } func (zach *ZoneAudioChannelsHandler) RemoveUserFromAllAudioChannels(userId string) (err error) { audioChannels, err := zach.ListAudioChannels() if err != nil { return } for _, audioChannel := range audioChannels { if err = zach.RemoveAudioChannelMember(audioChannel.ID, userId); err != nil { continue } } return } func (zach *ZoneAudioChannelsHandler) RemoveAllUserAudioChannels(userId string) (err error) { audioChannels, err := zach.ListAudioChannels() if err != nil { return } for _, audioChannel := range audioChannels { if audioChannel.Owner == userId { if derr := zach.DeleteAudioChannel(audioChannel.ID); derr != nil { logger.Println("**************", derr) } } } return } func (zach *ZoneAudioChannelsHandler) JoinAudioChannel(channelId string, userId string, sdp string) (err error) { err = atomicallyExecute(zach.AudioChannelsFlag, func() (err error) { if _, ok := zach.AudioChannels[channelId]; !ok { err = fmt.Errorf("no audio channel with corresponding id") return } audioChannel := zach.AudioChannels[channelId] audioChannel.CurrentMembersId = append(audioChannel.CurrentMembersId, userId) audioChannel.CurrentMembers[userId] = &AudioChannelMember{} signalMembers := func(members []string) { for _, u := range audioChannel.Members { done, e := zach.sendDataChannelMessage(USER_JOINED_AUDIO_CHANNEL, "node", u, map[string]interface{}{ "userId": userId, "channelId": channelId, }) select { case <-done: case err := <-e: logger.Println(err) } } } if audioChannel.ChannelType == PRIVATE { signalMembers(audioChannel.Members) } else { signalMembers(zach.ZoneMembersId) } d, e := audioChannel.HandleOffer(context.Background(), channelId, userId, sdp, zach.HostId, zach.sendDataChannelMessage, zach.signalCandidate) select { case <-d: case err = <-e: } return }) return } func (zach *ZoneAudioChannelsHandler) LeaveAudioChannel(channelId string, userId string) (err error) { err = atomicallyExecute(zach.AudioChannelsFlag, func() (err error) { if _, ok := zach.AudioChannels[channelId]; !ok { err = fmt.Errorf("no audio channel with corresponding id") return } audioChannel := zach.AudioChannels[channelId] var index int var contain bool for i, v := range audioChannel.CurrentMembersId { if v == userId { index = i contain = true } } if !contain { err = fmt.Errorf("this channel does not contain the provided user Id") return } defer audioChannel.HandleLeavingMember(userId) if len(audioChannel.CurrentMembersId) <= 1 { audioChannel.CurrentMembersId = make([]string, 0) } else { audioChannel.CurrentMembersId = append(audioChannel.CurrentMembersId[:index], audioChannel.CurrentMembersId[index+1:]...) } delete(audioChannel.CurrentMembers, userId) signalMembers := func(members []string) { for _, u := range audioChannel.Members { done, e := zach.sendDataChannelMessage(USER_LEFT_AUDIO_CHANNEL, "node", u, map[string]interface{}{ "userId": userId, "channelId": channelId, }) select { case <-done: case err := <-e: logger.Println(err) } } } if audioChannel.ChannelType == PRIVATE { signalMembers(audioChannel.Members) } else { signalMembers(zach.ZoneMembersId) } return }) return } func (zach *ZoneAudioChannelsHandler) handleZoneRequest(ctx context.Context, req *ZoneRequest) (err error) { switch req.ReqType { case LEAVE_ZONE: logger.Println("*-----------------handling leaving zone---------------*") if err = verifyFieldsString(req.Payload, "userId"); err != nil { return } for _, ac := range zach.AudioChannels { var contain bool var id string for _, member := range ac.CurrentMembersId { if member == req.Payload["userId"].(string) { id = member contain = true logger.Printf("*------------------id is %s--------------*\n", id) break } } if contain { err = zach.LeaveAudioChannel(ac.ID, id) break } } case string(REMOVED_ZONE_AUTHORIZED_MEMBER): if err = verifyFieldsString(req.Payload, "userId"); err != nil { return } var index int for i, m := range zach.ZoneMembersId { if m == req.Payload["userId"].(string) { index = i break } } zach.ZoneMembersId = append(zach.ZoneMembersId[:index], zach.ZoneMembersId[index+1:]...) if err = zach.RemoveAllUserAudioChannels(req.Payload["userId"].(string)); err != nil { logger.Println("****______________", err) } err = zach.RemoveUserFromAllAudioChannels(req.Payload["userId"].(string)) case string(NEW_AUTHORIZED_ZONE_MEMBER): if err = verifyFieldsString(req.Payload, "userId"); err != nil { return } zach.ZoneMembersId = append(zach.ZoneMembersId, req.Payload["userId"].(string)) err = zach.SetAllPublicAudioChannelForUser(req.Payload["userId"].(string)) case JOIN_AUDIO_CHANNEL: if err = verifyFieldsString(req.Payload, "channelId", "userId", "sdp"); err != nil { return } err = zach.JoinAudioChannel(req.Payload["channelId"].(string), req.Payload["userId"].(string), req.Payload["sdp"].(string)) case LEAVE_AUDIO_CHANNEL: if err = verifyFieldsString(req.Payload, "channelId", "userId"); err != nil { return } err = zach.LeaveAudioChannel(req.Payload["channelId"].(string), req.Payload["userId"].(string)) case ADD_AUDIO_CHANNEL_MEMBERS: if err = verifyFieldsString(req.Payload, "channelId"); err != nil { return } if err = verifyFieldsSliceInterface(req.Payload, "members"); err != nil { return } err = zach.AddAudioChannelsMembers(req.Payload["channelId"].(string), req.Payload["members"].([]interface{})) case REMOVE_AUDIO_CHANNEL_MEMBER: if err = verifyFieldsString(req.Payload, "channelId", "userId"); err != nil { return } err = zach.RemoveAudioChannelMember(req.Payload["channelId"].(string), req.Payload["userId"].(string)) if err != nil { logger.Println("an error occured in add known audioChannel", err) return } case EDIT_AUDIO_CHANNEL_NAME: if err = verifyFieldsString(req.Payload, "channelId", "newAudioChannelId"); err != nil { return } if err = zach.EditAudioChannelName(req.Payload["channelId"].(string), req.Payload["newAudioChannelId"].(string)); err != nil { return } case EDIT_AUDIO_CHANNEL_TYPE: if err = verifyFieldsString(req.Payload, "channelId", "channelType"); err != nil { return } if err = zach.EditAudioChannelType(req.Payload["channelId"].(string), req.Payload["channelType"].(string)); err != nil { return } case DELETE_AUDIO_CHANNEL: if err = verifyFieldsString(req.Payload, "channelId"); err != nil { return } err = zach.DeleteAudioChannel(req.Payload["channelId"].(string)) case CREATE_AUDIO_CHANNEL: if err = verifyFieldsString(req.Payload, "channelId", "owner", "channelType"); err != nil { return } if err = verifyFieldsSliceInterface(req.Payload, "members"); err != nil { return } err = zach.AddNewAudioChannel(req.Payload["channelId"].(string), req.Payload["owner"].(string), req.Payload["channelType"].(string), req.Payload["members"].([]interface{})) case GET_AUDIO_CHANNELS: if err = verifyFieldsSliceInterface(req.Payload, "channelsId"); err != nil { return } err = zach.GetAudioChannels(req.From, req.Payload["channelsId"].([]interface{})...) case string(AUDIO_CHANNEL_WEBRTC_COUNTER_OFFER): logger.Println("handling audio channel counter offer") if err = verifyFieldsString(req.Payload, "channelId", "userId"); err != nil { return } err = atomicallyExecute(zach.AudioChannelsFlag, func() (err error) { if _, ok := zach.AudioChannels[req.Payload["channelId"].(string)]; !ok { err = fmt.Errorf("no channel corresponging the one requested") return } err = zach.AudioChannels[req.Payload["channelId"].(string)].HandleCounterOffer(ctx, req.Payload["userId"].(string), zach.sendDataChannelMessage) return }) case string(AUDIO_CHANNEL_WEBRTC_RENNEGOTIATION_OFFER): if err = verifyFieldsString(req.Payload, "channelId", "userId", "sdp"); err != nil { return } err = atomicallyExecute(zach.AudioChannelsFlag, func() (err error) { if _, ok := zach.AudioChannels[req.Payload["channelId"].(string)]; !ok { err = fmt.Errorf("no channel corresponging the one requested") return } err = zach.AudioChannels[req.Payload["channelId"].(string)].HandleRennegotiationOffer(req.Payload["userId"].(string), req.Payload["sdp"].(string), zach.sendDataChannelMessage) return }) case string(AUDIO_CHANNEL_WEBRTC_RENNEGOTIATION_ANSWER): if err = verifyFieldsString(req.Payload, "channelId", "userId", "sdp"); err != nil { return } err = atomicallyExecute(zach.AudioChannelsFlag, func() (err error) { if _, ok := zach.AudioChannels[req.Payload["channelId"].(string)]; !ok { err = fmt.Errorf("no channel corresponging the one requested") return } err = zach.AudioChannels[req.Payload["channelId"].(string)].HandleRennegotiationAnswer(req.Payload["userId"].(string), req.Payload["sdp"].(string)) return }) case string(AUDIO_CHANNEL_WEBRTC_CANDIDATE): logger.Println("handling audio channel webrtc candidate") logger.Println(req.Payload) if err = verifyFieldsString(req.Payload, FROM, "candidate", "sdpMlineIndex", "sdpMid", "channelId", "userId"); err != nil { return } logger.Println(req.Payload) i, convErr := strconv.Atoi(req.Payload["sdpMlineIndex"].(string)) if convErr != nil { return convErr } sdpMlineIndex := uint16(i) sdpMid := req.Payload["sdpMid"].(string) logger.Println(sdpMid, sdpMlineIndex) err = atomicallyExecute(zach.AudioChannelsFlag, func() (err error) { if _, ok := zach.AudioChannels[req.Payload["channelId"].(string)]; !ok { err = fmt.Errorf("no channel corresponging the one requested") return } err = zach.AudioChannels[req.Payload["channelId"].(string)].AddCandidate(&webrtc.ICECandidateInit{ Candidate: req.Payload["candidate"].(string), SDPMid: &sdpMid, SDPMLineIndex: &sdpMlineIndex, }, req.Payload["userId"].(string)) return }) return default: logger.Println("audio channel handler still in process of implementation") } return }