Zippytal-Node/zoneAudioChannelshandler.go
2022-08-28 22:17:30 +02:00

1053 lines
33 KiB
Go

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"
AUDIO_CHANNNEL_NAME_EDITED = "audio_channel_name_edited"
AUDIO_CHANNNEL_TYPE_EDITED = "audio_channel_type_edited"
AUDIO_CHANNEL_MEMBER_REMOVED = "audio_channel_member_removed"
AUDIO_CHANNEL_MEMBER_ADDED = "audio_channel_member_added"
ADDED_IN_AUDIO_CHANNEL = "added_in_audio_channel"
REMOVED_FROM_AUDIO_CHANNEL = "removed_from_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 mbr, ok := member.(string); ok && mbr != owner {
m = append(m, mbr)
}
}
baseConfig := &AudioChannelConfig{
ID: channelName,
Owner: owner,
ChannelType: channelType,
Members: append(m,owner),
}
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_AUDIO_CHANNEL, "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(AUDIO_CHANNNEL_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, "audioChannelConfig.json"))
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)
}
zach.sendDataChannelMessage(AUDIO_CHANNNEL_TYPE_EDITED, "node", member, map[string]interface{}{
"channelId": channelId,
"channelType": channelType,
})
}
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)
}
zach.sendDataChannelMessage(AUDIO_CHANNNEL_TYPE_EDITED, "node", member, map[string]interface{}{
"channelId": channelId,
"channelType": channelType,
})
}
}
_ = 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
}
_ = atomicallyExecute(zach.AudioChannelsFlag, func() (err error) {
zach.AudioChannels[channelId].Members = audioChannelConfig.Members
ac := zach.AudioChannels[channelId]
broadcastLoop:
for _, member := range ac.Members {
for _, m := range addedMembers {
if member == m {
done, e := zach.sendDataChannelMessage(ADDED_IN_AUDIO_CHANNEL, "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(AUDIO_CHANNEL_MEMBER_ADDED, "node", member, map[string]interface{}{
"userId": m,
"channelId": ac.ID,
})
select {
case <-done:
case err = <-e:
logger.Println(err)
}
}
}
}
return
})
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
}
_ = atomicallyExecute(zach.AudioChannelsFlag, func() (err error) {
zach.AudioChannels[channelId].Members = audioChannelConfig.Members
ac := zach.AudioChannels[channelId]
broadcastLoop:
for _, member := range ac.Members {
if member == channelMember {
continue broadcastLoop
}
done, e := zach.sendDataChannelMessage(AUDIO_CHANNEL_MEMBER_REMOVED, "node", member, map[string]interface{}{
"userId": channelMember,
"channelId": ac.ID,
})
select {
case <-done:
case err = <-e:
logger.Println(err)
}
}
return
})
zach.sendZoneRequest(REMOVE_KNOWN_AUDIO_CHANNEL, "node", map[string]interface{}{
"userId": channelMember,
"channelId": channelId,
})
done, e := zach.sendDataChannelMessage(REMOVED_FROM_AUDIO_CHANNEL, "node", channelMember, map[string]interface{}{
"channelId": channelId,
"userId": channelMember,
})
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_AUDIO_CHANNEL, "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_AUDIO_CHANNEL, "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 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 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
}