930 lines
26 KiB
Go
930 lines
26 KiB
Go
package localserver
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/dgraph-io/badger/v3"
|
|
"github.com/pion/webrtc/v3"
|
|
)
|
|
|
|
type NodeChatConfig struct {
|
|
ID string `json:"id"`
|
|
Name string `json:"name"`
|
|
Initiator string `json:"initiator"`
|
|
Target string `json:"target"`
|
|
InitiatorHost string `json:"initiatorHost"`
|
|
TargetHost string `json:"targetHost"`
|
|
}
|
|
|
|
type NodeChatChannel struct {
|
|
ID string `json:"id"`
|
|
Name string `json:"name"`
|
|
Initiator string `json:"initiator"`
|
|
Target string `json:"target"`
|
|
InitiatorHost string `json:"initiatorHost"`
|
|
TargetHost string `json:"targetHost"`
|
|
LastReadIndex uint `json:"lastReadIndex"`
|
|
Unread uint `json:"unread"`
|
|
DB *NodeChatDBHandler `json:"-"`
|
|
Tracking *NodeChatTrackingDB `json:"-"`
|
|
}
|
|
|
|
type NodeChatChannelsHandler[T ZippytalFSInstance] struct {
|
|
ChatID string
|
|
ChatFSInstance T
|
|
DataChannels map[string]*DataChannel
|
|
ChatDataChannels map[string]*DataChannel
|
|
ChatDataChannelsFlag *uint32
|
|
Flag *uint32
|
|
ChatFSInstanceFlag *uint32
|
|
ChatFlag *uint32
|
|
Chats map[string]*NodeChatChannel
|
|
reqChans []chan<- *ChatRequest
|
|
init bool
|
|
}
|
|
|
|
func NewNodeChatChannelsHandler(chatID, chatName, initiator, target, initiatorHostId, targetHostId string, dataChannels map[string]*DataChannel, flag *uint32) (nodeChatsHandler *NodeChatChannelsHandler[*NodeChatFSInstance], err error) {
|
|
|
|
_, err = os.ReadDir(filepath.Join(dataPath, "data", "chats", chatID))
|
|
if err != nil {
|
|
if os.IsNotExist(err) {
|
|
// logger.Printf("creating chat directory for chat %s...\n", chatID)
|
|
mkdirErr := os.MkdirAll(filepath.Join(dataPath, "data", "chats", chatID), 0700)
|
|
if mkdirErr != nil {
|
|
return nil, mkdirErr
|
|
}
|
|
file, ferr := os.Create(filepath.Join(dataPath, "data", "chats", chatID, "chatConfig.json"))
|
|
if ferr != nil {
|
|
return nil, ferr
|
|
}
|
|
baseConfig := NodeChatConfig{
|
|
ID: chatID,
|
|
Name: chatName,
|
|
Initiator: initiator,
|
|
InitiatorHost: initiatorHostId,
|
|
Target: target,
|
|
TargetHost: targetHostId,
|
|
}
|
|
bs, jsonErr := json.Marshal(baseConfig)
|
|
if jsonErr != nil {
|
|
return nil, jsonErr
|
|
}
|
|
if _, writeErr := file.WriteString(string(bs)); writeErr != nil {
|
|
return nil, writeErr
|
|
}
|
|
_ = file.Close()
|
|
_, err = os.ReadDir(filepath.Join(dataPath, "data", "chats"))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
} else {
|
|
return
|
|
}
|
|
}
|
|
chats := make(map[string]*NodeChatChannel)
|
|
|
|
nodeChatDBHandler, err := NewNodeChatDBHandler(chatID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var bs []byte
|
|
bs, err = os.ReadFile(filepath.Join(dataPath, "data", "chats", chatID, "chatConfig.json"))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
// logger.Println(string(bs))
|
|
var c NodeChatChannel
|
|
if err = json.Unmarshal(bs, &c); err != nil {
|
|
return nil, err
|
|
}
|
|
nodeChatTracking, err := NewNodeChatTracking(chatID, initiator, target)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
// logger.Println("chats data :", c.ID, c.Initiator, c.InitiatorHost, c.Target)
|
|
c.DB = nodeChatDBHandler
|
|
c.Tracking = nodeChatTracking
|
|
chats[c.ID] = &c
|
|
|
|
chatFlag := uint32(0)
|
|
chatFSFlag := uint32(0)
|
|
chatDCFlag := uint32(0)
|
|
nodeChatsHandler = &NodeChatChannelsHandler[*NodeChatFSInstance]{
|
|
ChatID: chatID,
|
|
ChatFSInstance: NewNodeChatFSInstance(chatID),
|
|
ChatFSInstanceFlag: &chatFSFlag,
|
|
DataChannels: dataChannels,
|
|
ChatDataChannels: make(map[string]*DataChannel),
|
|
ChatDataChannelsFlag: &chatDCFlag,
|
|
Flag: flag,
|
|
Chats: chats,
|
|
ChatFlag: &chatFlag,
|
|
init: false,
|
|
}
|
|
return
|
|
}
|
|
|
|
func (zch *NodeChatChannelsHandler[T]) sendChatRequest(reqType string, from string, payload map[string]interface{}) {
|
|
go func() {
|
|
for _, rc := range zch.reqChans {
|
|
rc <- &ChatRequest{
|
|
ReqType: reqType,
|
|
From: from,
|
|
Payload: payload,
|
|
}
|
|
}
|
|
}()
|
|
}
|
|
|
|
func (zch *NodeChatChannelsHandler[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 *NodeChatChannelsHandler[T]) Init(ctx context.Context, initiator, target, initiatorNodeID, targetNodeID string) (err error) {
|
|
// for _, member := range authorizedMembers {
|
|
// if serr := zch.SetAllPublicChatForUser(member); serr != nil {
|
|
// // logger.Println(serr)
|
|
// }
|
|
// }
|
|
zch.init = true
|
|
return
|
|
}
|
|
|
|
func (zch *NodeChatChannelsHandler[T]) Subscribe(ctx context.Context, publisher <-chan *ChatRequest) (reqChan chan *ChatRequest, done chan struct{}, errCh chan error) {
|
|
reqChan, done, errCh = make(chan *ChatRequest), make(chan struct{}), make(chan error)
|
|
fmt.Println("subscribing...")
|
|
fmt.Println(zch)
|
|
zch.reqChans = append(zch.reqChans, reqChan)
|
|
go func() {
|
|
for {
|
|
select {
|
|
case <-ctx.Done():
|
|
done <- struct{}{}
|
|
return
|
|
case req := <-publisher:
|
|
if err := zch.handleChatRequest(ctx, req); err != nil {
|
|
errCh <- err
|
|
}
|
|
}
|
|
}
|
|
}()
|
|
fmt.Println(zch.reqChans)
|
|
return
|
|
}
|
|
|
|
func (zch *NodeChatChannelsHandler[T]) GetChats(userId string) (err error) {
|
|
chats := make([]*NodeChatChannel, 0)
|
|
for _, chat := range zch.Chats {
|
|
if chat.Initiator == userId || chat.Target == userId {
|
|
chats = append(chats, chat)
|
|
}
|
|
}
|
|
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 *NodeChatChannelsHandler[T]) ListChats() (chats []*NodeChatChannel, err error) {
|
|
chats = make([]*NodeChatChannel, 0)
|
|
_ = atomicallyExecute(zch.ChatFlag, func() (err error) {
|
|
for _, chat := range zch.Chats {
|
|
chats = append(chats, chat)
|
|
}
|
|
return
|
|
})
|
|
return
|
|
}
|
|
|
|
func (zch *NodeChatChannelsHandler[T]) AddNewChat(chatID, initiator, target, initiatorHostId, targetHostId string) (err error) {
|
|
if chatID == "" {
|
|
err = fmt.Errorf("not a valid chat name provided")
|
|
return
|
|
}
|
|
if _, ok := zch.Chats[chatID]; ok {
|
|
err = fmt.Errorf("a chat with this name already exist")
|
|
return
|
|
}
|
|
mkdirErr := os.MkdirAll(filepath.Join(dataPath, "data", "chats", chatID), 0700)
|
|
if mkdirErr != nil {
|
|
return mkdirErr
|
|
}
|
|
mkdirErr = os.Mkdir(filepath.Join(dataPath, "data", "chats", chatID, "__tracking__"), 0700)
|
|
if mkdirErr != nil {
|
|
return mkdirErr
|
|
}
|
|
file, ferr := os.Create(filepath.Join(dataPath, "data", "chats", chatID, "chatConfig.json"))
|
|
defer func() {
|
|
_ = file.Close()
|
|
}()
|
|
if ferr != nil {
|
|
return ferr
|
|
}
|
|
baseConfig := &NodeChatConfig{
|
|
ID: chatID,
|
|
}
|
|
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
|
|
}
|
|
nodeChatDBHandler, err := NewNodeChatDBHandler(chatID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
nodeChatTrackingDB, err := NewNodeChatTracking(chatID, initiator, target)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err = nodeChatTrackingDB.Initialize(1, initiator, target); err != nil {
|
|
return err
|
|
}
|
|
var c NodeChatChannel
|
|
if err = json.Unmarshal(bs, &c); err != nil {
|
|
return err
|
|
}
|
|
c.DB = nodeChatDBHandler
|
|
c.Tracking = nodeChatTrackingDB
|
|
_ = atomicallyExecute(zch.ChatFlag, func() (err error) {
|
|
zch.Chats[c.ID] = &c
|
|
return
|
|
})
|
|
return
|
|
}
|
|
|
|
func (zch *NodeChatChannelsHandler[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
|
|
}
|
|
return
|
|
}); err != nil {
|
|
return
|
|
}
|
|
if err = os.RemoveAll(filepath.Join(dataPath, "data", "chats", chatID)); err != nil {
|
|
return
|
|
}
|
|
return
|
|
}
|
|
|
|
func (zch *NodeChatChannelsHandler[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 *NodeChatChannelsHandler[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:
|
|
}
|
|
go func() {
|
|
_ = zch.signalUnreadCount(context.Background(), userId, 0)
|
|
}()
|
|
return
|
|
}
|
|
|
|
func (zch *NodeChatChannelsHandler[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 *NodeChatChannelsHandler[T]) AddChatMessage(userId, chatID, content string, isResponse bool, chatResponseId uint64, file *ChatFile) (err error) {
|
|
if _, ok := zch.Chats[chatID]; !ok {
|
|
err = fmt.Errorf("no such chat")
|
|
return
|
|
}
|
|
chat := zch.Chats[chatID]
|
|
dateTime := time.Now().Format(time.RFC3339)
|
|
chatMessage := &ChatMessage{
|
|
Content: content,
|
|
From: userId,
|
|
IsResponse: isResponse,
|
|
ResponseOf: nil,
|
|
File: file,
|
|
Tags: make([]string, 0),
|
|
Date: dateTime,
|
|
}
|
|
if err = atomicallyExecute(zch.ChatFlag, func() (err error) {
|
|
if chat, ok := zch.Chats[chatID]; ok {
|
|
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
|
|
_ = chat.Tracking.SetUserLastIndex(userId, chatMessage.ID)
|
|
} else {
|
|
err = fmt.Errorf("no corresponding chats")
|
|
}
|
|
return
|
|
}); err != nil {
|
|
return
|
|
}
|
|
|
|
notifyActivity := func(done <-chan struct{}, e <-chan error, destination string) {
|
|
select {
|
|
case <-done:
|
|
fmt.Println(destination, "is connected to this chat")
|
|
case <-e:
|
|
_ = atomicallyExecute(zch.ChatFlag, func() (err error) {
|
|
if chat, ok := zch.Chats[chatID]; ok {
|
|
li, err := chat.Tracking.GetUserLastIndex(destination)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
count, err := chat.DB.calculateNewChatCount(uint64(li))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
fmt.Println("********_**_*")
|
|
fmt.Println(count)
|
|
bs, err := json.Marshal(map[string]any{
|
|
"chatId": chatID,
|
|
"chatHost": NodeID,
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
fmt.Println("noooo errrrrooooorr")
|
|
fmt.Println(destination)
|
|
fmt.Println(userId)
|
|
zch.sendChatRequest(CREATE_NOTIFICATION, "node", map[string]interface{}{
|
|
"type": "new_chat_message",
|
|
"title": fmt.Sprintf("%d Unread messages 👀", count),
|
|
"body": fmt.Sprintf("%d new messages from %s", count, userId),
|
|
"isPushed": true,
|
|
"payload": string(bs),
|
|
"recipients": []string{destination},
|
|
})
|
|
go func() {
|
|
if e := zch.signalUnreadCount(context.Background(), destination, count); e != nil {
|
|
fmt.Println(e)
|
|
}
|
|
}()
|
|
fmt.Println("done without error 1")
|
|
fmt.Println("done without error 2")
|
|
|
|
fmt.Println("sending notification")
|
|
}
|
|
return
|
|
})
|
|
}
|
|
}
|
|
d1, e1 := zch.sendDataChannelMessage(NEW_CHAT_MESSAGE, "node", chat.Target, map[string]interface{}{
|
|
"chatMessage": chatMessage,
|
|
"chatId": chatID,
|
|
})
|
|
d2, e2 := zch.sendDataChannelMessage(NEW_CHAT_MESSAGE, "node", chat.Initiator, map[string]interface{}{
|
|
"chatMessage": chatMessage,
|
|
"chatId": chatID,
|
|
})
|
|
go func() {
|
|
if e := zch.signalLastInteractionTime(context.Background(), dateTime); e != nil {
|
|
fmt.Println(e)
|
|
}
|
|
}()
|
|
if userId == chat.Target {
|
|
go notifyActivity(d2, e2, chat.Initiator)
|
|
} else {
|
|
go notifyActivity(d1, e1, chat.Target)
|
|
}
|
|
return
|
|
}
|
|
|
|
func (zch *NodeChatChannelsHandler[T]) RemoveAllUserChat(userId string) (err error) {
|
|
chats, err := zch.ListChats()
|
|
if err != nil {
|
|
return
|
|
}
|
|
for _, chat := range chats {
|
|
if chat.Initiator == userId || chat.Target == userId {
|
|
if derr := zch.DeleteChat(chat.ID); derr != nil {
|
|
// logger.Println("**************", derr)
|
|
}
|
|
// logger.Println("deleted chat", chat.ID)
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func (zch *NodeChatChannelsHandler[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
|
|
}
|
|
chat := zch.Chats[chatID]
|
|
if err = chat.DB.DeleteChatMessage(key); err != nil {
|
|
return
|
|
}
|
|
for _, member := range [2]string{chat.Initiator, chat.Target} {
|
|
d, e := zch.sendDataChannelMessage(CHAT_MESSAGE_DELETED, "node", member, map[string]interface{}{
|
|
"chatId": chatID,
|
|
"messageId": key,
|
|
})
|
|
select {
|
|
case <-d:
|
|
case <-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 *NodeChatChannelsHandler[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
|
|
}
|
|
chat := zch.Chats[chatID]
|
|
if err = chat.DB.ModifyChatMessage(key, newContent); err != nil {
|
|
return
|
|
}
|
|
for _, member := range [2]string{chat.Target, chat.Initiator} {
|
|
d, e := zch.sendDataChannelMessage(CHAT_MESSAGE_EDITED, "node", member, map[string]interface{}{
|
|
"chatId": chatID,
|
|
"messageId": key,
|
|
"newContent": newContent,
|
|
})
|
|
select {
|
|
case <-d:
|
|
case <-e:
|
|
// logger.Println(tempErr)
|
|
}
|
|
}
|
|
|
|
return
|
|
})
|
|
return
|
|
}
|
|
|
|
func (zch *NodeChatChannelsHandler[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
|
|
}
|
|
chat := zch.Chats[chatID]
|
|
if err = chat.DB.DeleteChatFile(fileName, key); err != nil {
|
|
return
|
|
}
|
|
for _, member := range [2]string{chat.Target, chat.Initiator} {
|
|
d, e := zch.sendDataChannelMessage(CHAT_FILE_DELETED, "node", member, map[string]interface{}{
|
|
"chatId": chatID,
|
|
"fileId": key,
|
|
"fileName": fileName,
|
|
})
|
|
select {
|
|
case <-d:
|
|
case <-e:
|
|
// logger.Println(tempErr)
|
|
}
|
|
}
|
|
|
|
return
|
|
})
|
|
return
|
|
}
|
|
|
|
func (zch *NodeChatChannelsHandler[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 *NodeChatChannelsHandler[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 *NodeChatChannelsHandler[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.OnOpen(func() {
|
|
bs, err := json.Marshal(map[string]any{
|
|
"type": "chat_init",
|
|
"from": NodeID,
|
|
"to": command[1],
|
|
"payload": map[string]any{
|
|
"chatId": zch.ChatID,
|
|
},
|
|
})
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
}
|
|
_ = dc.DataChannel.SendText(string(bs))
|
|
})
|
|
dc.DataChannel.OnMessage(func(msg webrtc.DataChannelMessage) {
|
|
fmt.Println("received message there")
|
|
fmt.Println(msg.Data)
|
|
})
|
|
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 *NodeChatChannelsHandler[T]) signalUnreadCount(ctx context.Context, userId string, count uint) (err error) {
|
|
fmt.Println("this function has been called")
|
|
em := NewEncryptionManager()
|
|
sig := em.SignRequestHMAC(NodeID)
|
|
body, err := json.Marshal(map[string]interface{}{
|
|
"type": UPDATE_UNREAD_COUNT,
|
|
"mac": sig,
|
|
"from": NodeID,
|
|
"peerType": "node",
|
|
"payload": map[string]string{
|
|
"chatId": zch.ChatID,
|
|
"userId": userId,
|
|
"count": fmt.Sprintf("%d", count),
|
|
},
|
|
})
|
|
if err != nil {
|
|
return
|
|
}
|
|
_, err = http.Post("https://dev.zippytal.com/req", "application/json", bytes.NewReader(body))
|
|
if err != nil {
|
|
// logger.Println("error come from there inn chat manager")
|
|
return
|
|
}
|
|
return
|
|
}
|
|
|
|
func (zch *NodeChatChannelsHandler[T]) signalLastInteractionTime(ctx context.Context, dateTime string) (err error) {
|
|
fmt.Println("this function has been called too")
|
|
em := NewEncryptionManager()
|
|
sig := em.SignRequestHMAC(NodeID)
|
|
body, err := json.Marshal(map[string]interface{}{
|
|
"type": UPDATE_LAST_INTERACTION_TIME,
|
|
"mac": sig,
|
|
"from": NodeID,
|
|
"peerType": "node",
|
|
"payload": map[string]string{
|
|
"chatId": zch.ChatID,
|
|
"lastInteractionTime": dateTime,
|
|
},
|
|
})
|
|
if err != nil {
|
|
return
|
|
}
|
|
res, err := http.Post("https://dev.zippytal.com/req", "application/json", bytes.NewReader(body))
|
|
if err != nil {
|
|
// logger.Println("error come from there inn chat manager")
|
|
return
|
|
}
|
|
_, err = io.ReadAll(res.Body)
|
|
if err != nil {
|
|
return
|
|
}
|
|
return
|
|
}
|
|
|
|
func (zch *NodeChatChannelsHandler[T]) handleChatRequest(ctx context.Context, req *ChatRequest) (err error) {
|
|
// logger.Println("got request in zone chat handler", req)
|
|
|
|
fmt.Println(req)
|
|
switch req.ReqType {
|
|
case LEAVE_CHAT:
|
|
if err = VerifyFieldsString(req.Payload, "userId"); err != nil {
|
|
return
|
|
}
|
|
// err = zch.LeaveChatFSInstance(req.Payload["userId"].(string))
|
|
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["initiator"].(string), req.Payload["target"].(string), req.Payload["initiatorHostId"].(string), req.Payload["targetHostId"].(string))
|
|
case GET_CHATS:
|
|
err = zch.GetChats(req.From)
|
|
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))
|
|
}
|
|
return
|
|
}
|