Zippytal-Node/zoneChatsHandler.go

1619 lines
47 KiB
Go

package localserver
import (
"context"
"encoding/json"
"fmt"
"io/fs"
"os"
"path/filepath"
"strings"
"time"
"github.com/dgraph-io/badger/v3"
"github.com/pion/webrtc/v3"
)
const (
LIST_LATEST_CHATS = "list_latest_chats"
READ_LATEST_MESSAGE = "read_latest_message"
LIST_LATEST_FILES = "list_latest_files"
LIST_CHATS = "list_chats"
GET_CHATS = "get_chats"
GET_CHAT = "get_chat"
CREATE_CHAT = "create_chat"
DELETE_CHAT = "delete_chat"
EDIT_CHAT_TYPE = "edit_chat_type"
EDIT_CHAT_NAME = "edit_chat_name"
ADD_CHAT_MEMBERS = "add_chat_members"
REMOVE_CHAT_MEMBER = "remove_chat_member"
ADD_CHAT_MESSAGE = "add_chat_message"
ZONE_UPLOAD_CHAT_FILE = "zone_upload_chat_file"
ZONE_DOWNLOAD_CHAT_FILE = "zone_download_chat_file"
DELETE_CHAT_FILE = "delete_chat_file"
DELETE_CHAT_MESSAGE = "delete_chat_message"
EDIT_CHAT_MESSAGE = "edit_chat_message"
ZONE_UPLOAD_CHAT_FILE_DONE = "zone_upload_chat_file_done"
)
const (
CHAT_NAME_EDITED = "chat_name_edited"
CHAT_TYPE_EDITED = "chat_type_edited"
CHAT_MESSAGE_LIST = "chat_messages_list"
CHAT_FILES_LIST = "chat_files_list"
CHAT_MEMBER_ADDED = "chat_member_added"
CHAT_MEMBER_REMOVED = "chat_member_removed"
REMOVED_FROM_CHAT = "removed_from_chat"
ADDED_IN_CHAT = "added_in_chat"
GET_CHATS_RESPONSE = "get_chats_response"
NEW_CHAT_MESSAGE = "new_chat_message"
CHAT_MESSAGE_DELETED = "chat_message_deleted"
CHAT_MESSAGE_EDITED = "chat_message_edited"
CHAT_FILE_DELETED = "chat_file_deleted"
NEW_CHAT_FILE = "new_chat_file"
)
const (
GENERAL = "general"
PUBLIC = "public"
PRIVATE = "private"
BROADCAST = "broadcast"
)
type ChatConfig struct {
ChatId string `json:"chatId"`
ChatType string `json:"chatType"`
Owner string `json:"owner"`
Members []string `json:"members"`
}
type Chat struct {
ChatId string `json:"chatId"`
ChatType string `json:"chatType"`
Owner string `json:"owner"`
Members []string `json:"members"`
LastReadIndex uint `json:"lastReadIndex"`
Unread uint `json:"unread"`
DB *ZoneChatDBHandler `json:"-"`
Tracking *ZoneChatTrackingDB `json:"-"`
}
type ZoneChatsHandler[T ZippytalFSInstance] struct {
ZoneName string
ZoneId string
HostId string
ChatFSInstance T
ZoneMembersId []string
DataChannels map[string]*DataChannel
ChatDataChannels map[string]*DataChannel
ChatDataChannelsFlag *uint32
Flag *uint32
ChatFSInstanceFlag *uint32
ChatFlag *uint32
Chats map[string]*Chat
reqChans []chan<- *ZoneRequest
init bool
}
func NewZoneChatsHandler(hostId, zoneId, zoneName, owner string, authorizedMembers []string, dataChannels map[string]*DataChannel, flag *uint32) (zoneChatsHandler *ZoneChatsHandler[*ChatFSInstance], err error) {
var dirs []fs.DirEntry
dirs, err = os.ReadDir(filepath.Join(dataPath, "data", "zones", zoneId, "chats"))
if err != nil {
if os.IsNotExist(err) {
logger.Printf("creating chat directory for zone %s...\n", zoneId)
mkdirErr := os.MkdirAll(filepath.Join(dataPath, "data", "zones", zoneId, "chats", GENERAL), 0700)
if mkdirErr != nil {
return nil, mkdirErr
}
file, ferr := os.Create(filepath.Join(dataPath, "data", "zones", zoneId, "chats", GENERAL, "chatConfig.json"))
if ferr != nil {
return nil, ferr
}
baseConfig := ChatConfig{
ChatId: GENERAL,
Owner: owner,
ChatType: "public",
Members: []string{},
}
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(dataPath, "data", "zones", zoneId, "chats"))
if err != nil {
return nil, err
}
} else {
return
}
}
chats := make(map[string]*Chat)
for _, chat := range dirs {
if strings.HasPrefix(chat.Name(), ".") {
continue
}
zoneChatDBHandler, err := NewZoneChatDBHandler(zoneId, chat.Name())
if err != nil {
return nil, err
}
var bs []byte
bs, err = os.ReadFile(filepath.Join(dataPath, "data", "zones", zoneId, "chats", chat.Name(), "chatConfig.json"))
if err != nil {
return nil, err
}
logger.Println(string(bs))
var c Chat
if err = json.Unmarshal(bs, &c); err != nil {
return nil, err
}
zoneChatTracking, err := NewZoneChatTracking(zoneId, c.ChatId, c.Members...)
if err != nil {
return nil, err
}
logger.Println("chats data :", c.ChatId, c.ChatType, c.Owner, c.Members)
c.DB = zoneChatDBHandler
c.Tracking = zoneChatTracking
chats[c.ChatId] = &c
}
chatFlag := uint32(0)
chatFSFlag := uint32(0)
chatDCFlag := uint32(0)
zoneChatsHandler = &ZoneChatsHandler[*ChatFSInstance]{
HostId: hostId,
ZoneName: zoneName,
ChatFSInstance: NewChatFSInstance(zoneId, owner, authorizedMembers),
ChatFSInstanceFlag: &chatFSFlag,
ZoneId: zoneId,
ZoneMembersId: authorizedMembers,
DataChannels: dataChannels,
ChatDataChannels: make(map[string]*DataChannel),
ChatDataChannelsFlag: &chatDCFlag,
Flag: flag,
Chats: chats,
ChatFlag: &chatFlag,
init: false,
}
return
}
func (zch *ZoneChatsHandler[T]) sendZoneRequest(reqType string, from string, payload map[string]interface{}) {
go func() {
for _, rc := range zch.reqChans {
rc <- &ZoneRequest{
ReqType: reqType,
From: from,
Payload: payload,
}
}
}()
}
func (zch *ZoneChatsHandler[T]) 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(zch.ChatDataChannelsFlag, func() (err error) {
if _, ok := zch.ChatDataChannels[to]; ok {
bs, jsonErr := json.Marshal(&ZoneResponse{
Type: reqType,
From: from,
To: to,
Payload: payload,
})
if jsonErr != nil {
return jsonErr
}
err = zch.ChatDataChannels[to].DataChannel.SendText(string(bs))
} else {
err = fmt.Errorf("no corresponding dataChannel")
}
return
}); err != nil {
errCh <- err
return
}
done <- struct{}{}
}()
return done, errCh
}
func (zch *ZoneChatsHandler[T]) Init(ctx context.Context, authorizedMembers []string) (err error) {
for _, member := range authorizedMembers {
if serr := zch.SetAllPublicChatForUser(member); serr != nil {
logger.Println(serr)
}
}
zch.init = true
return
}
func (zch *ZoneChatsHandler[T]) 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)
zch.reqChans = append(zch.reqChans, reqChan)
go func() {
for {
select {
case <-ctx.Done():
done <- struct{}{}
return
case req := <-publisher:
if err := zch.handleZoneRequest(ctx, req); err != nil {
errCh <- err
}
}
}
}()
return
}
// func (zch *ZoneChatsHandler[T]) signalCandidate(from string, to string, candidate *webrtc.ICECandidate) (err error) {
// d, e := zch.sendDataChannelMessage(string(ZONE_CHAT_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 <-e:
// }
// return
// }
func (zch *ZoneChatsHandler[T]) GetChats(userId string, chatsId ...interface{}) (err error) {
chats := make([]*Chat, 0, len(chatsId))
for _, id := range chatsId {
if _, ok := id.(string); !ok {
err = fmt.Errorf("id of wrong type")
return
}
_ = atomicallyExecute(zch.ChatFlag, func() (err error) {
if _, ok := zch.Chats[id.(string)]; ok {
logger.Println(zch.Chats[id.(string)])
chats = append(chats, zch.Chats[id.(string)])
}
return
})
}
for _, chat := range chats {
index, err := chat.Tracking.GetUserLastIndex(userId)
if err != nil {
return err
}
unread, err := chat.DB.calculateNewChatCount(uint64(index))
if err != nil {
return err
}
chat.LastReadIndex = index
chat.Unread = unread
}
done, e := zch.sendDataChannelMessage(GET_CHATS_RESPONSE, "node", userId, map[string]interface{}{
"chats": chats,
})
select {
case <-done:
case err = <-e:
}
return
}
func (zch *ZoneChatsHandler[T]) ListChats() (chats []*Chat, err error) {
chats = make([]*Chat, 0)
_ = atomicallyExecute(zch.ChatFlag, func() (err error) {
for _, chat := range zch.Chats {
chats = append(chats, chat)
}
return
})
return
}
func (zch *ZoneChatsHandler[T]) AddNewChat(chatName string, owner string, chatType string, members []interface{}) (err error) {
if chatName == "" {
err = fmt.Errorf("not a valid chat name provided")
return
}
if _, ok := zch.Chats[chatName]; ok {
err = fmt.Errorf("a chat with this name already exist")
return
}
mkdirErr := os.MkdirAll(filepath.Join(dataPath, "data", "zones", zch.ZoneId, "chats", chatName), 0700)
if mkdirErr != nil {
return mkdirErr
}
mkdirErr = os.Mkdir(filepath.Join(dataPath, "data", "zones", zch.ZoneId, "chats", chatName, "__tracking__"), 0700)
if mkdirErr != nil {
return mkdirErr
}
file, ferr := os.Create(filepath.Join(dataPath, "data", "zones", zch.ZoneId, "chats", chatName, "chatConfig.json"))
defer func() {
_ = file.Close()
}()
if ferr != nil {
return ferr
}
m := make([]string, 0, len(members))
if chatType == PRIVATE {
for _, member := range members {
if mbr, ok := member.(string); ok && mbr != owner {
m = append(m, mbr)
}
}
m = append(m, owner)
} else {
m = zch.ZoneMembersId
}
baseConfig := &ChatConfig{
ChatId: chatName,
Owner: owner,
ChatType: chatType,
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
}
zoneChatDBHandler, err := NewZoneChatDBHandler(zch.ZoneId, chatName)
if err != nil {
return err
}
zoneChatTrackingDB, err := NewZoneChatTracking(zch.ZoneId, chatName)
if err != nil {
return err
}
if err = zoneChatTrackingDB.Initialize(1, baseConfig.Members...); err != nil {
return err
}
var c Chat
if err = json.Unmarshal(bs, &c); err != nil {
return err
}
c.DB = zoneChatDBHandler
c.Tracking = zoneChatTrackingDB
_ = atomicallyExecute(zch.ChatFlag, func() (err error) {
zch.Chats[c.ChatId] = &c
return
})
newChatForMembers := func(members []string) (err error) {
for _, member := range members {
zch.sendZoneRequest(ADD_KNOWN_CHAT, "node", map[string]interface{}{
"userId": member,
"chatId": chatName,
})
done, e := zch.sendDataChannelMessage(GET_CHATS_RESPONSE, "node", member, map[string]interface{}{
"chats": []*Chat{zch.Chats[chatName]},
})
select {
case <-done:
case <-e:
}
}
bs, err := json.Marshal(map[string]any{
"zoneId": zch.ZoneId,
"chatId": chatName,
})
if err != nil {
return err
}
zch.sendZoneRequest(CREATE_NOTIFICATION, "node", map[string]interface{}{
"type": "added_in_chat",
"title": "Added in zone text channel 💬",
"body": fmt.Sprintf("Added in channel %s in zone %s", c.ChatId, zch.ZoneName),
"isPushed": true,
"payload": string(bs),
"recipients": members,
})
return
}
switch c.ChatType {
case BROADCAST:
fallthrough
case PUBLIC:
err = atomicallyExecute(zch.ChatFlag, func() (err error) {
err = newChatForMembers(zch.ZoneMembersId)
return
})
case PRIVATE:
err = atomicallyExecute(zch.ChatFlag, func() (err error) {
err = newChatForMembers(c.Members)
return
})
}
return
}
func (zch *ZoneChatsHandler[T]) DeleteChat(chatId string) (err error) {
if err = atomicallyExecute(zch.ChatFlag, func() (err error) {
defer delete(zch.Chats, chatId)
if _, ok := zch.Chats[chatId]; !ok {
err = fmt.Errorf("no corresponding chat")
return
}
removeKnownChats := func(members []string) (err error) {
for _, member := range members {
zch.sendZoneRequest(REMOVE_KNOWN_CHAT, "node", map[string]interface{}{
"userId": member,
"chatId": chatId,
})
done, e := zch.sendDataChannelMessage(REMOVED_FROM_CHAT, "node", member, map[string]interface{}{
"chatId": chatId,
"userId": member,
})
select {
case <-done:
case <-e:
}
}
bs, err := json.Marshal(map[string]any{
"zoneId": zch.ZoneId,
"chatId": chatId,
})
if err != nil {
return err
}
zch.sendZoneRequest(CREATE_NOTIFICATION, "node", map[string]any{
"type": "removed_from_chat",
"title": "Removed from zone text channel ⛔️",
"body": fmt.Sprintf("You have no longer access to the channel %s in zone %s", chatId, zch.ZoneName),
"isPushed": true,
"payload": string(bs),
"recipients": members,
})
return
}
switch zch.Chats[chatId].ChatType {
case BROADCAST:
fallthrough
case PUBLIC:
err = removeKnownChats(zch.ZoneMembersId)
case PRIVATE:
err = removeKnownChats(zch.Chats[chatId].Members)
}
return
}); err != nil {
return
}
if err = os.RemoveAll(filepath.Join(dataPath, "data", "zones", zch.ZoneId, "chats", chatId)); err != nil {
return
}
return
}
func (zch *ZoneChatsHandler[T]) EditChatName(chatId string, newChatId string) (err error) {
if _, ok := zch.Chats[chatId]; !ok {
err = fmt.Errorf("no coresponding chat")
return
}
bs, err := os.ReadFile(filepath.Join(dataPath, "data", "zones", zch.ZoneId, "chats", chatId, "chatConfig.json"))
if err != nil {
return
}
var chatConfig ChatConfig
if err = json.Unmarshal(bs, &chatConfig); err != nil {
return
}
chatConfig.ChatId = newChatId
bs, err = json.Marshal(&chatConfig)
if err = os.Rename(filepath.Join(dataPath, "data", "zones", zch.ZoneId, "chats", chatId), filepath.Join(dataPath, "data", "zones", zch.ZoneId, "chats", newChatId)); err != nil {
return
}
f, err := os.OpenFile(filepath.Join(dataPath, "data", "zones", zch.ZoneId, "chats", newChatId, "chatConfig.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
}
chat := &Chat{
Owner: chatConfig.Owner,
ChatId: chatConfig.ChatId,
ChatType: chatConfig.ChatType,
Members: chatConfig.Members,
DB: zch.Chats[chatId].DB,
Tracking: zch.Chats[chatId].Tracking,
}
chat.DB.updateDbCallbackFolder(newChatId)
chat.Tracking.updateDBCallbackFolder(newChatId)
_ = atomicallyExecute(zch.ChatFlag, func() (err error) {
defer delete(zch.Chats, chatId)
zch.Chats[newChatId] = chat
updateKnownChats := func(members []string) (err error) {
for _, member := range members {
zch.sendZoneRequest(ADD_KNOWN_CHAT, "node", map[string]interface{}{
"userId": member,
"chatId": newChatId,
})
zch.sendZoneRequest(REMOVE_KNOWN_CHAT, "node", map[string]interface{}{
"userId": member,
"chatId": chatId,
})
done, e := zch.sendDataChannelMessage(CHAT_NAME_EDITED, "node", member, map[string]interface{}{
"formerChatId": chatId,
"newChatId": newChatId,
})
select {
case <-done:
case channelErr := <-e:
logger.Println(channelErr)
}
}
return
}
switch chat.ChatType {
case BROADCAST:
fallthrough
case PUBLIC:
err = updateKnownChats(zch.ZoneMembersId)
case PRIVATE:
err = updateKnownChats(chat.Members)
}
return
})
return
}
func (zch *ZoneChatsHandler[T]) EditChatType(chatId string, chatType string) (err error) {
if _, ok := zch.Chats[chatId]; !ok {
err = fmt.Errorf("no coresponding chat")
return
}
bs, err := os.ReadFile(filepath.Join(dataPath, "data", "zones", zch.ZoneId, "chats", chatId, "chatConfig.json"))
if err != nil {
return
}
var chatConfig ChatConfig
if err = json.Unmarshal(bs, &chatConfig); err != nil {
return
}
chatConfig.ChatType = chatType
if chatType == PUBLIC {
chatConfig.Members = zch.ZoneMembersId
}
bs, err = json.Marshal(&chatConfig)
f, err := os.OpenFile(filepath.Join(dataPath, "data", "zones", zch.ZoneId, "chats", chatId, "chatConfig.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
}
chat := &Chat{
Owner: chatConfig.Owner,
ChatId: chatConfig.ChatId,
ChatType: chatType,
Members: chatConfig.Members,
DB: zch.Chats[chatId].DB,
Tracking: zch.Chats[chatId].Tracking,
}
switch chatType {
case BROADCAST:
fallthrough
case PUBLIC:
for _, member := range zch.ZoneMembersId {
if pubErr := zch.AddChatMembers(chatId, []any{member}); pubErr != nil {
logger.Println(pubErr)
}
zch.sendDataChannelMessage(CHAT_TYPE_EDITED, "node", member, map[string]interface{}{
"chatId": chatId,
"chatType": chatType,
})
}
case PRIVATE:
}
_ = atomicallyExecute(zch.ChatFlag, func() (err error) {
zch.Chats[chatId] = chat
return
})
return
}
func (zch *ZoneChatsHandler[T]) AddChatMembers(chatId string, chatMembers []any) (err error) {
if _, ok := zch.Chats[chatId]; !ok {
err = fmt.Errorf("no coresponding chat")
return
}
chatMembersStr, err := ToStringSlice(chatMembers)
if err != nil {
return
}
bs, err := os.ReadFile(filepath.Join(dataPath, "data", "zones", zch.ZoneId, "chats", chatId, "chatConfig.json"))
if err != nil {
return
}
var chatConfig ChatConfig
if err = json.Unmarshal(bs, &chatConfig); err != nil {
return
}
logger.Printf("%s - %s - %s -%v\n", chatConfig.ChatId, chatConfig.ChatType, chatConfig.Owner, chatMembers)
addedMembers := make([]string, 0)
memberLoop:
for _, chatMember := range chatMembersStr {
for _, member := range chatConfig.Members {
if member == chatMember {
zch.sendZoneRequest(ADD_KNOWN_CHAT, "node", map[string]interface{}{
"userId": chatMember,
"chatId": chatId,
})
continue memberLoop
}
}
chatConfig.Members = append(chatConfig.Members, chatMember)
addedMembers = append(addedMembers, chatMember)
zch.sendZoneRequest(ADD_KNOWN_CHAT, "node", map[string]interface{}{
"userId": chatMember,
"chatId": chatId,
})
}
bs, err = json.Marshal(&chatConfig)
f, err := os.OpenFile(filepath.Join(dataPath, "data", "zones", zch.ZoneId, "chats", chatId, "chatConfig.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
}
chat := &Chat{
Owner: chatConfig.Owner,
ChatId: chatConfig.ChatId,
ChatType: chatConfig.ChatType,
Members: chatConfig.Members,
DB: zch.Chats[chatId].DB,
Tracking: zch.Chats[chatId].Tracking,
}
_ = atomicallyExecute(zch.ChatFlag, func() (err error) {
lastIndex := chat.DB.PreviousId
zch.Chats[chatId] = chat
var chmb []string
if chat.ChatType == PRIVATE {
chmb = chat.Members
} else {
chmb = zch.ZoneMembersId
}
broadcastLoop:
for _, member := range chmb {
for _, m := range addedMembers {
if err = chat.Tracking.SetUserLastIndex(member, lastIndex); err != nil {
return
}
if member == m {
if m != chat.Owner {
go func() {
bs, err = json.Marshal(map[string]any{
"zoneId": zch.ZoneId,
"chatId": chatId,
"zoneHost": NodeID,
})
if err != nil {
return
}
zch.sendZoneRequest(CREATE_NOTIFICATION, "node", map[string]interface{}{
"type": "added_in_chat",
"title": "Added in zone text channel 💬",
"body": fmt.Sprintf("Added in channel %s in zone %s", chatId, zch.ZoneName),
"isPushed": true,
"payload": string(bs),
"recipients": []string{m},
})
}()
}
done, e := zch.sendDataChannelMessage(ADDED_IN_CHAT, "node", member, map[string]interface{}{
"chat": chat,
})
select {
case <-done:
case err = <-e:
logger.Println(err)
}
continue broadcastLoop
}
if _, ok := zch.DataChannels[member]; ok {
done, e := zch.sendDataChannelMessage(CHAT_MEMBER_ADDED, "node", member, map[string]interface{}{
"userId": m,
"chatId": chat.ChatId,
})
select {
case <-done:
case err = <-e:
logger.Println(err)
}
}
}
}
return
})
return
}
func (zch *ZoneChatsHandler[T]) RemoveChatMember(chatId string, chatMember string) (err error) {
if _, ok := zch.Chats[chatId]; !ok {
err = fmt.Errorf("no coresponding chat")
return
}
bs, err := os.ReadFile(filepath.Join(dataPath, "data", "zones", zch.ZoneId, "chats", chatId, "chatConfig.json"))
if err != nil {
return
}
var chatConfig ChatConfig
if err = json.Unmarshal(bs, &chatConfig); err != nil {
logger.Println(string(bs))
logger.Println("json error right here")
return
}
if chatMember == chatConfig.Owner {
err = fmt.Errorf("you cannot remove the owner from the chat")
return
}
var index int
var contain bool
for i, member := range chatConfig.Members {
if member == chatMember {
index = i
contain = true
break
}
}
if !contain {
err = fmt.Errorf("member %s not in the channel %s", chatMember, chatId)
return
}
chatConfig.Members = append(chatConfig.Members[:index], chatConfig.Members[index+1:]...)
bs, err = json.Marshal(&chatConfig)
if err != nil {
logger.Println("json error there")
return
}
f, err := os.OpenFile(filepath.Join(dataPath, "data", "zones", zch.ZoneId, "chats", chatId, "chatConfig.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 chat *Chat
_ = atomicallyExecute(zch.ChatFlag, func() (err error) {
chat = &Chat{
Owner: chatConfig.Owner,
ChatId: chatConfig.ChatId,
ChatType: chatConfig.ChatType,
Members: chatConfig.Members,
DB: zch.Chats[chatId].DB,
Tracking: zch.Chats[chatId].Tracking,
}
zch.Chats[chatId] = chat
return
})
if err = chat.Tracking.DeleteUserTracking(chatMember); err != nil {
return
}
zch.sendZoneRequest(REMOVE_KNOWN_CHAT, "node", map[string]interface{}{
"userId": chatMember,
"chatId": chatId,
})
if chatMember != chat.Owner {
bs, err = json.Marshal(map[string]any{
"zoneId": zch.ZoneId,
"chatId": chatId,
"zoneHost": NodeID,
})
if err != nil {
return
}
zch.sendZoneRequest(CREATE_NOTIFICATION, "node", map[string]interface{}{
"type": "removed_from_chat",
"title": "Removed from zone text channel ⛔️",
"body": fmt.Sprintf("You have no longer access to the channel %s in zone %s", chatId, zch.ZoneName),
"isPushed": true,
"payload": string(bs),
"recipients": []string{chatMember},
})
}
done, e := zch.sendDataChannelMessage(REMOVED_FROM_CHAT, "node", chatMember, map[string]interface{}{
"chatId": chatId,
"userId": chatMember,
})
select {
case <-done:
case err = <-e:
logger.Println(err)
}
var chmb []string
if chat.ChatType == PRIVATE {
chmb = chat.Members
} else {
chmb = zch.ZoneMembersId
}
broadcastLoop:
for _, member := range chmb {
if member == chatMember {
continue broadcastLoop
}
done, e := zch.sendDataChannelMessage(CHAT_MEMBER_REMOVED, "node", member, map[string]interface{}{
"userId": chatMember,
"chatId": chat.ChatId,
})
select {
case <-done:
case err = <-e:
logger.Println(err)
}
}
return
}
func (zch *ZoneChatsHandler[T]) ReadLastMessage(userId, chatId string) (err error) {
err = atomicallyExecute(zch.ChatFlag, func() (err error) {
if chat, ok := zch.Chats[chatId]; ok {
err = chat.Tracking.SetUserLastIndex(userId, chat.DB.PreviousId)
}
return
})
return
}
func (zch *ZoneChatsHandler[T]) ListLatestChatMessages(userId, chatId string, lastIndex, limit float64) (err error) {
var list []*ChatMessage
var i int
var done bool
if err = atomicallyExecute(zch.ChatFlag, func() (err error) {
if chat, ok := zch.Chats[chatId]; ok {
list, i, done, err = zch.Chats[chatId].DB.ListChatMessages(int(lastIndex), int(limit))
if err != nil {
return
}
err = chat.Tracking.SetUserLastIndex(userId, chat.DB.PreviousId)
}
return
}); err != nil {
return
}
success, e := zch.sendDataChannelMessage(CHAT_MESSAGE_LIST, "node", userId, map[string]interface{}{
"done": done || i <= 0,
"lastIndex": i - 1,
"chatId": chatId,
"chatMessages": list,
})
select {
case <-success:
fmt.Println("done getting latest messages")
case err = <-e:
}
return
}
func (zch *ZoneChatsHandler[T]) ListLatestChatFiles(userId, chatId string, lastIndex, limit float64) (err error) {
var list []*ChatFile
var i int
if err = atomicallyExecute(zch.ChatFlag, func() (err error) {
if _, ok := zch.Chats[chatId]; ok {
list, i, err = zch.Chats[chatId].DB.ListChatFiles(int(lastIndex), int(limit))
}
return
}); err != nil {
return
}
done, e := zch.sendDataChannelMessage(CHAT_FILES_LIST, "node", userId, map[string]interface{}{
"done": i <= 0,
"lastIndex": i,
"chatId": chatId,
"chatMessages": list,
})
select {
case <-done:
case err = <-e:
}
return
}
func (zch *ZoneChatsHandler[T]) AddChatMessage(userId, chatId, content string, isResponse bool, chatResponseId uint64, file *ChatFile) (err error) {
chatMessage := &ChatMessage{
Content: content,
From: userId,
IsResponse: isResponse,
ResponseOf: nil,
File: file,
Tags: make([]string, 0),
Date: time.Now().Format("Mon, 02 Jan 2006 15:04:05 MST"),
}
var chatType string
var chatMembers []string
if err = atomicallyExecute(zch.ChatFlag, func() (err error) {
if chat, ok := zch.Chats[chatId]; ok {
chatType = chat.ChatType
chatMembers = chat.Members
if isResponse {
parentMessage, getErr := chat.DB.GetChatMessage(chatResponseId)
if err != nil {
if getErr != badger.ErrKeyNotFound {
return getErr
}
}
chatMessage.ResponseOf = parentMessage
}
if err = zch.Chats[chatId].DB.AddNewChatMessage(chatMessage); err != nil {
return
}
chatMessage.ID = zch.Chats[chatId].DB.PreviousId
} else {
err = fmt.Errorf("no corresponding chats")
}
return
}); err != nil {
return
}
notifyActivity := func(done <-chan struct{}, e <-chan error, member string) {
select {
case <-done:
case <-e:
_ = atomicallyExecute(zch.ChatFlag, func() (err error) {
if chat, ok := zch.Chats[chatId]; ok {
li, err := chat.Tracking.GetUserLastIndex(member)
if err != nil {
return err
}
count, err := chat.DB.calculateNewChatCount(uint64(li))
if err != nil {
return err
}
if count == 1 {
bs, err := json.Marshal(map[string]any{
"zoneId": zch.ZoneId,
"chatId": chatId,
"zoneHost": NodeID,
})
if err != nil {
return err
}
zch.sendZoneRequest(CREATE_NOTIFICATION, "node", map[string]interface{}{
"type": "new_chat_activity",
"title": "Unread messages 👀",
"body": fmt.Sprintf("New messages in channel %s of %s", chatId, zch.ZoneName),
"isPushed": true,
"payload": string(bs),
"recipients": []string{member},
})
}
}
return
})
}
}
switch chatType {
case BROADCAST:
fallthrough
case PUBLIC:
for _, v := range zch.ZoneMembersId {
done, e := zch.sendDataChannelMessage(NEW_CHAT_MESSAGE, "node", v, map[string]interface{}{
"chatMessage": chatMessage,
"chatId": chatId,
})
go notifyActivity(done, e, v)
}
case PRIVATE:
for _, v := range chatMembers {
done, e := zch.sendDataChannelMessage(NEW_CHAT_MESSAGE, "node", v, map[string]interface{}{
"chatMessage": chatMessage,
"chatId": chatId,
})
go notifyActivity(done, e, v)
}
}
return
}
func (zch *ZoneChatsHandler[T]) SetAllPublicChatForUser(userId string) (err error) {
for _, chat := range zch.Chats {
if chat.ChatType == PUBLIC || chat.ChatType == BROADCAST {
if err = zch.AddChatMembers(chat.ChatId, []interface{}{userId}); err != nil {
logger.Println(err)
}
continue
}
}
return
}
func (zch *ZoneChatsHandler[T]) RemoveUserFromAllChats(userId string) (err error) {
chats, err := zch.ListChats()
if err != nil {
logger.Println("an error occured -----------------*")
return
}
for _, chat := range chats {
if err = zch.RemoveChatMember(chat.ChatId, userId); err != nil {
logger.Println("-------------------*", err)
continue
}
}
return
}
func (zch *ZoneChatsHandler[T]) RemoveAllUserChat(userId string) (err error) {
chats, err := zch.ListChats()
if err != nil {
return
}
for _, chat := range chats {
logger.Println("$$$$$$$$$$ chat owner :", chat.Owner, userId)
if chat.Owner == userId {
if derr := zch.DeleteChat(chat.ChatId); derr != nil {
logger.Println("**************", derr)
}
logger.Println("deleted chat", chat.ChatId)
}
}
return
}
// func (zch *ZoneChatsHandler[T]) ConnectToChatFSInstance(channelId string, userId string, sdp string) (err error) {
// err = atomicallyExecute(zch.ChatFSInstanceFlag, func() (err error) {
// d, e := zch.ChatFSInstance.HandleOffer(context.Background(), channelId, userId, sdp, zch.HostId, zch.sendDataChannelMessage, zch.signalCandidate)
// select {
// case <-d:
// case <-e:
// }
// return
// })
// return
// }
// func (zch *ZoneChatsHandler[T]) LeaveChatFSInstance(
// userId string) (err error) {
// zch.ChatFSInstance.HandleLeavingMember(userId)
// return
// }
func (zch *ZoneChatsHandler[T]) DeleteChatMessage(key uint64, chatId string) (err error) {
err = atomicallyExecute(zch.ChatFlag, func() (err error) {
if _, ok := zch.Chats[chatId]; !ok {
err = fmt.Errorf("no file corresponding to id %s", chatId)
return
}
if err = zch.Chats[chatId].DB.DeleteChatMessage(key); err != nil {
return
}
if zch.Chats[chatId].ChatType == PRIVATE {
for _, member := range zch.Chats[chatId].Members {
d, e := zch.sendDataChannelMessage(CHAT_MESSAGE_DELETED, "node", member, map[string]interface{}{
"chatId": chatId,
"messageId": key,
})
select {
case <-d:
case tempErr := <-e:
logger.Println(tempErr)
}
}
} else {
for _, member := range zch.ZoneMembersId {
d, e := zch.sendDataChannelMessage(CHAT_MESSAGE_DELETED, "node", member, map[string]interface{}{
"chatId": chatId,
"messageId": key,
})
select {
case <-d:
case tempErr := <-e:
logger.Println(tempErr)
}
}
}
previousId := zch.Chats[chatId].DB.PreviousId
if previousId == key {
if err = zch.Chats[chatId].DB.revertPreviousId(); err != nil {
return
}
previousId = zch.Chats[chatId].DB.PreviousId
if previousId == 1 {
previousId = 0
}
if err = zch.Chats[chatId].Tracking.RevertTrackingLastIndex(previousId); err != nil {
return
}
}
return
})
return
}
func (zch *ZoneChatsHandler[T]) UpdateChatMessage(key uint64, chatId, newContent string) (err error) {
err = atomicallyExecute(zch.ChatFlag, func() (err error) {
if _, ok := zch.Chats[chatId]; !ok {
err = fmt.Errorf("no chat corresponding to id %s", chatId)
return
}
if err = zch.Chats[chatId].DB.ModifyChatMessage(key, newContent); err != nil {
return
}
if zch.Chats[chatId].ChatType == PRIVATE {
for _, member := range zch.Chats[chatId].Members {
d, e := zch.sendDataChannelMessage(CHAT_MESSAGE_EDITED, "node", member, map[string]interface{}{
"chatId": chatId,
"messageId": key,
"newContent": newContent,
})
select {
case <-d:
case tempErr := <-e:
logger.Println(tempErr)
}
}
} else {
for _, member := range zch.ZoneMembersId {
d, e := zch.sendDataChannelMessage(CHAT_MESSAGE_EDITED, "node", member, map[string]interface{}{
"chatId": chatId,
"messageId": key,
"newContent": newContent,
})
select {
case <-d:
case tempErr := <-e:
logger.Println(tempErr)
}
}
}
return
})
return
}
func (zch *ZoneChatsHandler[T]) DeleteChatFile(key uint64, fileName, chatId string) (err error) {
err = atomicallyExecute(zch.ChatFlag, func() (err error) {
if _, ok := zch.Chats[chatId]; !ok {
err = fmt.Errorf("no file corresponding to id %s", chatId)
return
}
if err = zch.Chats[chatId].DB.DeleteChatFile(fileName, key); err != nil {
return
}
if zch.Chats[chatId].ChatType == PRIVATE {
for _, member := range zch.Chats[chatId].Members {
d, e := zch.sendDataChannelMessage(CHAT_FILE_DELETED, "node", member, map[string]interface{}{
"chatId": chatId,
"fileId": key,
"fileName": fileName,
})
select {
case <-d:
case tempErr := <-e:
logger.Println(tempErr)
}
}
} else {
for _, member := range zch.ZoneMembersId {
d, e := zch.sendDataChannelMessage(CHAT_FILE_DELETED, "node", member, map[string]interface{}{
"chatId": chatId,
"fileId": key,
"fileName": fileName,
})
select {
case <-d:
case tempErr := <-e:
logger.Println(tempErr)
}
}
}
return
})
return
}
func (zch *ZoneChatsHandler[T]) chatFileUpload(chatId string, filename string, userId string, dc *DataChannel) {
var writePipe chan<- []byte
var done bool
uploadDone := func() {
if writePipe != nil {
close(writePipe)
writePipe = nil
}
done = true
}
dc.DataChannel.OnError(func(err error) {
logger.Println(err)
logger.Println("abort...")
if !done {
uploadDone()
}
if fufErr := zch.ChatFSInstance.FileUploadFailed(chatId, filename, userId); fufErr != nil {
logger.Println(fufErr)
}
})
dc.DataChannel.OnClose(func() {
if done {
logger.Println("closing gracefully...")
} else {
logger.Println("abort...")
uploadDone()
if fufErr := zch.ChatFSInstance.FileUploadFailed(chatId, filename, userId); fufErr != nil {
logger.Println(fufErr)
}
}
})
dc.DataChannel.OnMessage(func(msg webrtc.DataChannelMessage) {
if msg.IsString {
if string(msg.Data) == "init_upload" {
logger.Println("init upload....")
var initErr error
if writePipe, initErr = zch.ChatFSInstance.SetupFileUpload(chatId, filename, userId, dc.DataChannel); initErr != nil {
_ = dc.DataChannel.SendText("abort")
_ = dc.DataChannel.Close()
return
}
logger.Println("upload ready !")
_ = dc.DataChannel.SendText("upload_ready")
} else if string(msg.Data) == "upload_done" {
uploadDone()
}
} else {
writePipe <- msg.Data
}
})
dc.DataChannel.OnOpen(func() {
_ = dc.DataChannel.SendText("channel_ready")
})
}
func (zch *ZoneChatsHandler[T]) chatFileDownload(chatId string, filename string, userId string, dc *DataChannel) {
var done bool
dc.DataChannel.OnError(func(err error) {
if !done {
logger.Println("abort...")
if fdf := zch.ChatFSInstance.FileDownloadFailed(chatId, filename, userId); fdf != nil {
logger.Println(fdf)
}
}
})
dc.DataChannel.OnClose(func() {
if !done {
logger.Println("abort...")
if fdf := zch.ChatFSInstance.FileDownloadFailed(chatId, filename, userId); fdf != nil {
logger.Println(fdf)
}
}
})
dc.DataChannel.OnMessage(func(msg webrtc.DataChannelMessage) {
if msg.IsString {
if string(msg.Data) == "init_download" {
logger.Println("init download....")
var initErr error
if initErr = zch.ChatFSInstance.SetupFileDownload(chatId, filename, userId, dc.DataChannel); initErr != nil {
_ = dc.DataChannel.SendText("abort")
_ = dc.DataChannel.Close()
return
}
logger.Println("download started !")
} else if string(msg.Data) == "download_done" {
done = true
}
}
})
dc.DataChannel.OnOpen(func() {
_ = dc.DataChannel.SendText("channel_ready")
})
}
func (zch *ZoneChatsHandler[T]) handleDataChannel(ctx context.Context, dc *DataChannel) (catched bool) {
var label string
_ = atomicallyExecute(dc.l, func() (err error) {
label = dc.DataChannel.Label()
return
})
if strings.Contains(label, "chat_upload") {
command := strings.Split(label, "|")
if len(command) == 4 {
catched = true
go zch.chatFileUpload(command[1], command[2], command[3], dc)
}
logger.Println(command)
} else if strings.Contains(label, "chat_download") {
command := strings.Split(label, "|")
catched = true
go zch.chatFileDownload(command[1], command[2], command[3], dc)
logger.Println(command)
} else if strings.Contains(label, "chat_data") {
command := strings.Split(label, "|")
catched = true
_ = atomicallyExecute(zch.ChatDataChannelsFlag, func() (err error) {
zch.ChatDataChannels[command[1]] = dc
return
})
dc.DataChannel.OnClose(func() {
fmt.Println("closing gratefully chat dc...")
_ = atomicallyExecute(zch.ChatDataChannelsFlag, func() (err error) {
delete(zch.ChatDataChannels, command[1])
return
})
})
dc.DataChannel.OnError(func(err error) {
fmt.Println("error in chat dc...")
_ = atomicallyExecute(zch.ChatDataChannelsFlag, func() (err error) {
delete(zch.ChatDataChannels, command[1])
return
})
})
}
return
}
func (zch *ZoneChatsHandler[T]) handleZoneRequest(ctx context.Context, req *ZoneRequest) (err error) {
logger.Println("got request in zone chat handler", req)
switch req.ReqType {
case LEAVE_ZONE:
if err = verifyFieldsString(req.Payload, "userId"); err != nil {
return
}
// err = zch.LeaveChatFSInstance(req.Payload["userId"].(string))
case string(REMOVED_ZONE_AUTHORIZED_MEMBER):
if err = verifyFieldsString(req.Payload, "userId"); err != nil {
return
}
var index int
var found bool
for i, m := range zch.ZoneMembersId {
if m == req.Payload["userId"].(string) {
index = i
found = true
break
}
}
if !found {
err = fmt.Errorf("no such user in zone")
return
}
zch.ZoneMembersId = append(zch.ZoneMembersId[:index], zch.ZoneMembersId[index+1:]...)
if err = zch.RemoveAllUserChat(req.Payload["userId"].(string)); err != nil {
logger.Println("****______________", err)
}
err = zch.RemoveUserFromAllChats(req.Payload["userId"].(string))
case string(NEW_AUTHORIZED_ZONE_MEMBER):
if err = verifyFieldsString(req.Payload, "userId"); err != nil {
return
}
var contain bool
for _, m := range zch.ZoneMembersId {
if m == req.Payload["userId"].(string) {
contain = true
break
}
}
if !contain {
zch.ZoneMembersId = append(zch.ZoneMembersId, req.Payload["userId"].(string))
}
err = zch.SetAllPublicChatForUser(req.Payload["userId"].(string))
case ADD_CHAT_MEMBERS:
if err = verifyFieldsString(req.Payload, "chatId"); err != nil {
return
}
if err = verifyFieldsSliceInterface(req.Payload, "members"); err != nil {
return
}
err = zch.AddChatMembers(req.Payload["chatId"].(string), req.Payload["members"].([]interface{}))
case REMOVE_CHAT_MEMBER:
if err = verifyFieldsString(req.Payload, "chatId", "userId"); err != nil {
return
}
err = zch.RemoveChatMember(req.Payload["chatId"].(string), req.Payload["userId"].(string))
if err != nil {
logger.Println("an error occured in add known chat", err)
return
}
case EDIT_CHAT_NAME:
if err = verifyFieldsString(req.Payload, "chatId", "newChatId"); err != nil {
return
}
if err = zch.EditChatName(req.Payload["chatId"].(string), req.Payload["newChatId"].(string)); err != nil {
return
}
case EDIT_CHAT_TYPE:
if err = verifyFieldsString(req.Payload, "chatId", "chatType"); err != nil {
return
}
if err = zch.EditChatType(req.Payload["chatId"].(string), req.Payload["chatType"].(string)); err != nil {
return
}
case DELETE_CHAT:
if err = verifyFieldsString(req.Payload, "chatId"); err != nil {
return
}
err = zch.DeleteChat(req.Payload["chatId"].(string))
case CREATE_CHAT:
if err = verifyFieldsString(req.Payload, "chatId", "owner", "chatType"); err != nil {
return
}
if err = verifyFieldsSliceInterface(req.Payload, "members"); err != nil {
return
}
err = zch.AddNewChat(req.Payload["chatId"].(string), req.Payload["owner"].(string), req.Payload["chatType"].(string), req.Payload["members"].([]interface{}))
case GET_CHATS:
if err = verifyFieldsSliceInterface(req.Payload, "chatsId"); err != nil {
return
}
err = zch.GetChats(req.From, req.Payload["chatsId"].([]interface{})...)
case LIST_LATEST_CHATS:
if err = verifyFieldsString(req.Payload, "chatId"); err != nil {
return
}
if err = verifyFieldsFloat64(req.Payload, "lastIndex", "limit"); err != nil {
return
}
err = zch.ListLatestChatMessages(req.From, req.Payload["chatId"].(string), req.Payload["lastIndex"].(float64), req.Payload["limit"].(float64))
return
case LIST_LATEST_FILES:
if err = verifyFieldsString(req.Payload, "chatId"); err != nil {
return
}
if err = verifyFieldsFloat64(req.Payload, "lastIndex", "limit"); err != nil {
return
}
err = zch.ListLatestChatFiles(req.From, req.Payload["chatId"].(string), req.Payload["lastIndex"].(float64), req.Payload["limit"].(float64))
return
case READ_LATEST_MESSAGE:
if err = verifyFieldsString(req.Payload, "chatId"); err != nil {
return
}
err = zch.ReadLastMessage(req.From, req.Payload["chatId"].(string))
case ADD_CHAT_MESSAGE:
logger.Println("got request in zone chat handler", req)
if err = verifyFieldsString(req.Payload, "chatId", "content"); err != nil {
return
}
if err = verifyFieldsBool(req.Payload, "isResponse"); err != nil {
return
}
var parentChatId uint64
if req.Payload["isResponse"].(bool) {
if err = verifyFieldsFloat64(req.Payload, "parentChatId"); err != nil {
return
}
parentChatId = uint64(req.Payload["parentChatId"].(float64))
}
var file *ChatFile = nil
if _, ok := req.Payload["file"]; ok {
bs, jsonErr := json.Marshal(req.Payload["file"])
if jsonErr != nil {
return jsonErr
}
var f ChatFile
if err = json.Unmarshal(bs, &f); err != nil {
err = fmt.Errorf("the file payload dont match ChatFile struct pattern : %v", err)
return
}
file = &f
}
err = zch.AddChatMessage(req.From, req.Payload["chatId"].(string), req.Payload["content"].(string), req.Payload["isResponse"].(bool), parentChatId, file)
case DELETE_CHAT_MESSAGE:
if err = verifyFieldsString(req.Payload, "chatId"); err != nil {
return
}
if err = verifyFieldsFloat64(req.Payload, "messageId"); err != nil {
return
}
err = zch.DeleteChatMessage(uint64(req.Payload["messageId"].(float64)), req.Payload["chatId"].(string))
case EDIT_CHAT_MESSAGE:
if err = verifyFieldsString(req.Payload, "chatId", "newContent"); err != nil {
return
}
if err = verifyFieldsFloat64(req.Payload, "messageId"); err != nil {
return
}
err = zch.UpdateChatMessage(uint64(req.Payload["messageId"].(float64)), req.Payload["chatId"].(string), req.Payload["newContent"].(string))
case DELETE_CHAT_FILE:
if err = verifyFieldsString(req.Payload, "chatId", "fileName"); err != nil {
return
}
if err = verifyFieldsFloat64(req.Payload, "fileId"); err != nil {
return
}
err = zch.DeleteChatFile(uint64(req.Payload["fileId"].(float64)), req.Payload["fileName"].(string), req.Payload["chatId"].(string))
// case ZONE_UPLOAD_CHAT_FILE:
// if err = verifyFieldsString(req.Payload, "chatId", "userId", "fileName"); err != nil {
// return
// }
// err = zch.ChatFSInstance.SetupFileUpload(req.Payload["chatId"].(string), req.Payload["fileName"].(string), req.Payload["userId"].(string))
// case ZONE_DOWNLOAD_CHAT_FILE:
// if err = verifyFieldsString(req.Payload, "chatId", "userId", "fileName"); err != nil {
// return
// }
// err = zch.ChatFSInstance.SetupFileDownload(req.Payload["chatId"].(string), req.Payload["fileName"].(string), req.Payload["userId"].(string))
// case ZONE_UPLOAD_CHAT_FILE_DONE:
// if err = verifyFieldsBool(req.Payload, "failed"); err != nil {
// return
// }
// if err = verifyFieldsString(req.Payload, "path", "userId", "fileName", "type"); err != nil {
// return
// }
// if err = verifyFieldsFloat64(req.Payload, "size"); err != nil {
// return
// }
// if _, ok := req.Payload["chatFile"]; !ok {
// err = fmt.Errorf("no field chatFile in request payload")
// return
// }
// if req.Payload["failed"].(bool) {
// logger.Println(zch.ChatFSInstance.FileUploadFailed(req.Payload["path"].(string), req.Payload["fileName"].(string), req.Payload["userId"].(string)))
// } else {
// defer func() {
// logger.Println(zch.ChatFSInstance.FileUploadDone(req.Payload["path"].(string), req.Payload["fileName"].(string), req.Payload["userId"].(string)))
// }()
// }
// case string(ZONE_CHAT_WEBRTC_OFFER):
// if err = verifyFieldsString(req.Payload, "channelId", "userId", "sdp"); err != nil {
// return
// }
// err = zch.ConnectToChatFSInstance(req.Payload["channelId"].(string), req.Payload["userId"].(string), req.Payload["sdp"].(string))
// case string(ZONE_CHAT_WEBRTC_COUNTER_OFFER):
// logger.Println("handling fs instance counter offer")
// if err = verifyFieldsString(req.Payload, "channelId", "userId"); err != nil {
// return
// }
// err = atomicallyExecute(zch.ChatFSInstanceFlag, func() (err error) {
// err = zch.ChatFSInstance.HandleCounterOffer(context.Background(), req.Payload["userId"].(string), zch.sendDataChannelMessage)
// return
// })
// case string(ZONE_CHAT_WEBRTC_RENNEGOTIATION_OFFER):
// if err = verifyFieldsString(req.Payload, "channelId", "userId", "sdp"); err != nil {
// return
// }
// err = atomicallyExecute(zch.ChatFSInstanceFlag, func() (err error) {
// err = zch.ChatFSInstance.HandleRennegotiationOffer(req.Payload["userId"].(string), req.Payload["sdp"].(string), zch.sendDataChannelMessage)
// return
// })
// case string(ZONE_CHAT_WEBRTC_RENNEGOTIATION_ANSWER):
// if err = verifyFieldsString(req.Payload, "channelId", "userId", "sdp"); err != nil {
// return
// }
// err = atomicallyExecute(zch.ChatFSInstanceFlag, func() (err error) {
// err = zch.ChatFSInstance.HandleRennegotiationAnswer(req.Payload["userId"].(string), req.Payload["sdp"].(string))
// return
// })
// case string(ZONE_CHAT_WEBRTC_CANDIDATE):
// logger.Println("handling fs instance 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(zch.ChatFSInstanceFlag, func() (err error) {
// err = zch.ChatFSInstance.AddCandidate(&webrtc.ICECandidateInit{
// Candidate: req.Payload["candidate"].(string),
// SDPMid: &sdpMid,
// SDPMLineIndex: &SDPMLineIndex,
// }, req.Payload["userId"].(string))
// return
// })
}
return
}