feat: unified chat API pt. 2
This commit is contained in:
parent
41d523f205
commit
df9c9977a5
|
@ -390,7 +390,7 @@ func (b *StatusNode) gifService() *gif.Service {
|
|||
|
||||
func (b *StatusNode) ChatService() *chat.Service {
|
||||
if b.chatSrvc == nil {
|
||||
b.chatSrvc = chat.NewService(b.appDB)
|
||||
b.chatSrvc = chat.NewService()
|
||||
}
|
||||
return b.chatSrvc
|
||||
}
|
||||
|
|
|
@ -666,6 +666,10 @@ func (m *Messenger) Start() (*MessengerResponse, error) {
|
|||
return response, nil
|
||||
}
|
||||
|
||||
func (m *Messenger) IdentityPublicKey() *ecdsa.PublicKey {
|
||||
return &m.identity.PublicKey
|
||||
}
|
||||
|
||||
// cleanTopics remove any topic that does not have a Listen flag set
|
||||
func (m *Messenger) cleanTopics() error {
|
||||
if m.mailserversDatabase == nil {
|
||||
|
|
|
@ -6,12 +6,14 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/status-im/status-go/eth-node/crypto"
|
||||
"github.com/status-im/status-go/eth-node/types"
|
||||
"github.com/status-im/status-go/images"
|
||||
"github.com/status-im/status-go/protocol"
|
||||
"github.com/status-im/status-go/protocol/common"
|
||||
"github.com/status-im/status-go/protocol/communities"
|
||||
"github.com/status-im/status-go/protocol/protobuf"
|
||||
"github.com/status-im/status-go/protocol/requests"
|
||||
v1protocol "github.com/status-im/status-go/protocol/v1"
|
||||
)
|
||||
|
||||
|
@ -61,7 +63,7 @@ type Chat struct {
|
|||
InvitationAdmin string `json:"invitationAdmin,omitempty"`
|
||||
ReceivedInvitationAdmin string `json:"receivedInvitationAdmin,omitempty"`
|
||||
Profile string `json:"profile,omitempty"`
|
||||
CommunityID string `json:"communityId,omitempty"`
|
||||
CommunityID string `json:"communityId"`
|
||||
CategoryID string `json:"categoryId"`
|
||||
Position int32 `json:"position,omitempty"`
|
||||
Permissions *protobuf.CommunityPermissions `json:"permissions,omitempty"`
|
||||
|
@ -93,7 +95,7 @@ type API struct {
|
|||
s *Service
|
||||
}
|
||||
|
||||
func (api *API) GetChats(parent context.Context) (map[string]ChannelGroup, error) {
|
||||
func (api *API) GetChats(ctx context.Context) (map[string]ChannelGroup, error) {
|
||||
joinedCommunities, err := api.s.messenger.JoinedCommunities()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -101,10 +103,7 @@ func (api *API) GetChats(parent context.Context) (map[string]ChannelGroup, error
|
|||
|
||||
channels := api.s.messenger.Chats()
|
||||
|
||||
pubKey, err := api.s.accountsDB.GetPublicKey()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pubKey := types.EncodeHex(crypto.FromECDSAPub(api.s.messenger.IdentityPublicKey()))
|
||||
|
||||
result := make(map[string]ChannelGroup)
|
||||
|
||||
|
@ -179,40 +178,13 @@ func (api *API) GetChats(parent context.Context) (map[string]ChannelGroup, error
|
|||
return result, nil
|
||||
}
|
||||
|
||||
func (api *API) GetChat(parent context.Context, communityID types.HexBytes, chatID string) (*Chat, error) {
|
||||
fullChatID := chatID
|
||||
|
||||
pubKey, err := api.s.accountsDB.GetPublicKey()
|
||||
func (api *API) GetChat(ctx context.Context, communityID types.HexBytes, chatID string) (*Chat, error) {
|
||||
pubKey := types.EncodeHex(crypto.FromECDSAPub(api.s.messenger.IdentityPublicKey()))
|
||||
messengerChat, community, err := api.getChatAndCommunity(pubKey, communityID, chatID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if string(communityID.Bytes()) == pubKey { // Obtaining chats from personal
|
||||
communityID = []byte{}
|
||||
}
|
||||
|
||||
if len(communityID) != 0 {
|
||||
fullChatID = string(communityID.Bytes()) + chatID
|
||||
}
|
||||
|
||||
messengerChat := api.s.messenger.Chat(fullChatID)
|
||||
if messengerChat == nil {
|
||||
return nil, ErrChatNotFound
|
||||
}
|
||||
|
||||
var community *communities.Community
|
||||
if messengerChat.CommunityID != "" {
|
||||
communityID, err := hexutil.Decode(messengerChat.CommunityID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
community, err = api.s.messenger.GetCommunityByID(communityID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
pinnedMessages, cursor, err := api.s.messenger.PinnedMessageByChatID(messengerChat.ID, "", -1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -226,6 +198,36 @@ func (api *API) GetChat(parent context.Context, communityID types.HexBytes, chat
|
|||
return result, nil
|
||||
}
|
||||
|
||||
func (api *API) GetMembers(ctx context.Context, communityID types.HexBytes, chatID string) (map[string]Member, error) {
|
||||
pubKey := types.EncodeHex(crypto.FromECDSAPub(api.s.messenger.IdentityPublicKey()))
|
||||
messengerChat, community, err := api.getChatAndCommunity(pubKey, communityID, chatID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return getChatMembers(messengerChat, community, pubKey)
|
||||
}
|
||||
|
||||
func (api *API) JoinChat(ctx context.Context, communityID types.HexBytes, chatID string) (*Chat, error) {
|
||||
if len(communityID) != 0 {
|
||||
return nil, errors.New("joining community chats is not supported (you already joined all the community chats)")
|
||||
}
|
||||
|
||||
response, err := api.s.messenger.CreatePublicChat(&requests.CreatePublicChat{ID: chatID})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
chat := response.Chats()[0]
|
||||
pubKey := types.EncodeHex(crypto.FromECDSAPub(api.s.messenger.IdentityPublicKey()))
|
||||
pinnedMessages, cursor, err := api.s.messenger.PinnedMessageByChatID(chat.ID, "", -1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return toAPIChat(chat, nil, pubKey, pinnedMessages, cursor)
|
||||
}
|
||||
|
||||
func toAPIChat(protocolChat *protocol.Chat, community *communities.Community, pubKey string, pinnedMessages []*common.PinnedMessage, cursor string) (*Chat, error) {
|
||||
chat := &Chat{
|
||||
ID: strings.TrimPrefix(protocolChat.ID, protocolChat.CommunityID),
|
||||
|
@ -242,7 +244,6 @@ func toAPIChat(protocolChat *protocol.Chat, community *communities.Community, pu
|
|||
UnviewedMessagesCount: protocolChat.UnviewedMessagesCount,
|
||||
UnviewedMentionsCount: protocolChat.UnviewedMentionsCount,
|
||||
LastMessage: protocolChat.LastMessage,
|
||||
Members: make(map[string]Member),
|
||||
MembershipUpdates: protocolChat.MembershipUpdates,
|
||||
Alias: protocolChat.Alias,
|
||||
Identicon: protocolChat.Identicon,
|
||||
|
@ -274,48 +275,58 @@ func toAPIChat(protocolChat *protocol.Chat, community *communities.Community, pu
|
|||
return nil, err
|
||||
}
|
||||
|
||||
chat.setChatMembers(protocolChat, community, pubKey)
|
||||
chatMembers, err := getChatMembers(protocolChat, community, pubKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
chat.Members = chatMembers
|
||||
|
||||
if chat.CommunityID == "" {
|
||||
chat.CommunityID = pubKey
|
||||
}
|
||||
|
||||
return chat, nil
|
||||
}
|
||||
|
||||
func (chat *Chat) setChatMembers(sourceChat *protocol.Chat, community *communities.Community, userPubKey string) {
|
||||
func getChatMembers(sourceChat *protocol.Chat, community *communities.Community, userPubKey string) (map[string]Member, error) {
|
||||
result := make(map[string]Member)
|
||||
if sourceChat.ChatType == protocol.ChatTypePrivateGroupChat && len(sourceChat.Members) > 0 {
|
||||
for _, m := range sourceChat.Members {
|
||||
chat.Members[m.ID] = Member{
|
||||
result[m.ID] = Member{
|
||||
Admin: m.Admin,
|
||||
Joined: m.Joined,
|
||||
}
|
||||
}
|
||||
return
|
||||
return result, nil
|
||||
}
|
||||
|
||||
if sourceChat.ChatType == protocol.ChatTypeOneToOne {
|
||||
chat.Members[sourceChat.ID] = Member{
|
||||
result[sourceChat.ID] = Member{
|
||||
Joined: true,
|
||||
}
|
||||
chat.Members[userPubKey] = Member{
|
||||
result[userPubKey] = Member{
|
||||
Joined: true,
|
||||
}
|
||||
return
|
||||
return result, nil
|
||||
}
|
||||
|
||||
if community != nil {
|
||||
for pubKey, m := range community.Description().Members {
|
||||
if pubKey == userPubKey {
|
||||
chat.Members[pubKey] = Member{
|
||||
Roles: m.Roles,
|
||||
Joined: true,
|
||||
}
|
||||
} else {
|
||||
chat.Members[pubKey] = Member{
|
||||
Roles: m.Roles,
|
||||
Joined: community.Joined(),
|
||||
}
|
||||
for member, m := range community.Description().Members {
|
||||
pubKey, err := common.HexToPubkey(member)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result[member] = Member{
|
||||
Roles: m.Roles,
|
||||
Joined: community.Joined(),
|
||||
Admin: community.IsMemberAdmin(pubKey),
|
||||
}
|
||||
|
||||
}
|
||||
return
|
||||
return result, nil
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (chat *Chat) populateCommunityFields(community *communities.Community) error {
|
||||
|
@ -344,3 +355,35 @@ func (chat *Chat) populateCommunityFields(community *communities.Community) erro
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (api *API) getChatAndCommunity(pubKey string, communityID types.HexBytes, chatID string) (*protocol.Chat, *communities.Community, error) {
|
||||
fullChatID := chatID
|
||||
|
||||
if string(communityID.Bytes()) == pubKey { // Obtaining chats from personal
|
||||
communityID = []byte{}
|
||||
}
|
||||
|
||||
if len(communityID) != 0 {
|
||||
fullChatID = string(communityID.Bytes()) + chatID
|
||||
}
|
||||
|
||||
messengerChat := api.s.messenger.Chat(fullChatID)
|
||||
if messengerChat == nil {
|
||||
return nil, nil, ErrChatNotFound
|
||||
}
|
||||
|
||||
var community *communities.Community
|
||||
if messengerChat.CommunityID != "" {
|
||||
communityID, err := hexutil.Decode(messengerChat.CommunityID)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
community, err = api.s.messenger.GetCommunityByID(communityID)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return messengerChat, community, nil
|
||||
}
|
||||
|
|
|
@ -0,0 +1,207 @@
|
|||
package chat
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/status-im/status-go/eth-node/crypto"
|
||||
"github.com/status-im/status-go/eth-node/types"
|
||||
"github.com/status-im/status-go/protocol"
|
||||
"github.com/status-im/status-go/protocol/common"
|
||||
"github.com/status-im/status-go/protocol/requests"
|
||||
)
|
||||
|
||||
type GroupChatResponse struct {
|
||||
Chat *Chat `json:"chat"`
|
||||
Messages []*common.Message `json:"messages"`
|
||||
}
|
||||
|
||||
type GroupChatResponseWithInvitations struct {
|
||||
Chat *Chat `json:"chat"`
|
||||
Messages []*common.Message `json:"messages"`
|
||||
Invitations []*protocol.GroupChatInvitation `json:"invitations"`
|
||||
}
|
||||
|
||||
type CreateOneToOneChatResponse struct {
|
||||
Chat *Chat `json:"chat,omitempty"`
|
||||
Contact *protocol.Contact `json:"contact,omitempty"`
|
||||
}
|
||||
|
||||
type StartGroupChatResponse struct {
|
||||
Chat *Chat `json:"chat,omitempty"`
|
||||
Contacts []*protocol.Contact `json:"contacts"`
|
||||
Messages []*common.Message `json:"messages,omitempty"`
|
||||
}
|
||||
|
||||
func (api *API) CreateOneToOneChat(ctx context.Context, ID types.HexBytes, ensName string) (*CreateOneToOneChatResponse, error) {
|
||||
pubKey := types.EncodeHex(crypto.FromECDSAPub(api.s.messenger.IdentityPublicKey()))
|
||||
response, err := api.s.messenger.CreateOneToOneChat(&requests.CreateOneToOneChat{ID: ID, ENSName: ensName})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
protocolChat := response.Chats()[0]
|
||||
pinnedMessages, cursor, err := api.s.messenger.PinnedMessageByChatID(protocolChat.ID, "", -1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
chat, err := toAPIChat(protocolChat, nil, pubKey, pinnedMessages, cursor)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var contact *protocol.Contact
|
||||
if ensName != "" {
|
||||
contact = response.Contacts[0]
|
||||
}
|
||||
|
||||
return &CreateOneToOneChatResponse{
|
||||
Chat: chat,
|
||||
Contact: contact,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (api *API) CreateGroupChat(ctx context.Context, name string, members []string) (*GroupChatResponse, error) {
|
||||
return api.execAndGetGroupChatResponse(func() (*protocol.MessengerResponse, error) {
|
||||
return api.s.messenger.CreateGroupChatWithMembers(ctx, name, members)
|
||||
})
|
||||
}
|
||||
|
||||
func (api *API) CreateGroupChatFromInvitation(name string, chatID string, adminPK string) (*GroupChatResponse, error) {
|
||||
return api.execAndGetGroupChatResponse(func() (*protocol.MessengerResponse, error) {
|
||||
return api.s.messenger.CreateGroupChatFromInvitation(name, chatID, adminPK)
|
||||
})
|
||||
}
|
||||
|
||||
func (api *API) LeaveGroupChat(ctx context.Context, chatID string, remove bool) (*GroupChatResponse, error) {
|
||||
return api.execAndGetGroupChatResponse(func() (*protocol.MessengerResponse, error) {
|
||||
return api.s.messenger.LeaveGroupChat(ctx, chatID, remove)
|
||||
})
|
||||
}
|
||||
|
||||
func (api *API) AddMembersToGroupChat(ctx context.Context, chatID string, members []string) (*GroupChatResponseWithInvitations, error) {
|
||||
return api.execAndGetGroupChatResponseWithInvitations(func() (*protocol.MessengerResponse, error) {
|
||||
return api.s.messenger.AddMembersToGroupChat(ctx, chatID, members)
|
||||
})
|
||||
}
|
||||
|
||||
func (api *API) RemoveMemberFromGroupChat(ctx context.Context, chatID string, member string) (*GroupChatResponse, error) {
|
||||
return api.execAndGetGroupChatResponse(func() (*protocol.MessengerResponse, error) {
|
||||
return api.s.messenger.RemoveMemberFromGroupChat(ctx, chatID, member)
|
||||
})
|
||||
}
|
||||
|
||||
func (api *API) AddAdminsToGroupChat(ctx context.Context, chatID string, members []string) (*GroupChatResponse, error) {
|
||||
return api.execAndGetGroupChatResponse(func() (*protocol.MessengerResponse, error) {
|
||||
return api.s.messenger.AddAdminsToGroupChat(ctx, chatID, members)
|
||||
})
|
||||
}
|
||||
|
||||
func (api *API) ConfirmJoiningGroup(ctx context.Context, chatID string) (*GroupChatResponse, error) {
|
||||
return api.execAndGetGroupChatResponse(func() (*protocol.MessengerResponse, error) {
|
||||
return api.s.messenger.ConfirmJoiningGroup(ctx, chatID)
|
||||
})
|
||||
}
|
||||
|
||||
func (api *API) ChangeGroupChatName(ctx context.Context, chatID string, name string) (*GroupChatResponse, error) {
|
||||
return api.execAndGetGroupChatResponse(func() (*protocol.MessengerResponse, error) {
|
||||
return api.s.messenger.ChangeGroupChatName(ctx, chatID, name)
|
||||
})
|
||||
}
|
||||
|
||||
func (api *API) SendGroupChatInvitationRequest(ctx context.Context, chatID string, adminPK string, message string) (*GroupChatResponseWithInvitations, error) {
|
||||
return api.execAndGetGroupChatResponseWithInvitations(func() (*protocol.MessengerResponse, error) {
|
||||
return api.s.messenger.SendGroupChatInvitationRequest(ctx, chatID, adminPK, message)
|
||||
})
|
||||
}
|
||||
|
||||
func (api *API) GetGroupChatInvitations() ([]*protocol.GroupChatInvitation, error) {
|
||||
return api.s.messenger.GetGroupChatInvitations()
|
||||
}
|
||||
|
||||
func (api *API) SendGroupChatInvitationRejection(ctx context.Context, invitationRequestID string) ([]*protocol.GroupChatInvitation, error) {
|
||||
response, err := api.s.messenger.SendGroupChatInvitationRejection(ctx, invitationRequestID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return response.Invitations, nil
|
||||
}
|
||||
|
||||
func (api *API) StartGroupChat(ctx context.Context, name string, members []string) (*StartGroupChatResponse, error) {
|
||||
pubKey := types.EncodeHex(crypto.FromECDSAPub(api.s.messenger.IdentityPublicKey()))
|
||||
|
||||
var response *protocol.MessengerResponse
|
||||
var err error
|
||||
if len(members) == 1 {
|
||||
memberPk, err := common.HexToPubkey(members[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response, err = api.s.messenger.CreateOneToOneChat(&requests.CreateOneToOneChat{
|
||||
ID: types.HexBytes(crypto.FromECDSAPub(memberPk)),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
response, err = api.s.messenger.CreateGroupChatWithMembers(ctx, name, members)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
chat, err := toAPIChat(response.Chats()[0], nil, pubKey, nil, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &StartGroupChatResponse{
|
||||
Chat: chat,
|
||||
Contacts: response.Contacts,
|
||||
Messages: response.Messages(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func toGroupChatResponse(pubKey string, response *protocol.MessengerResponse) (*GroupChatResponse, error) {
|
||||
chat, err := toAPIChat(response.Chats()[0], nil, pubKey, nil, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &GroupChatResponse{
|
||||
Chat: chat,
|
||||
Messages: response.Messages(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func toGroupChatResponseWithInvitations(pubKey string, response *protocol.MessengerResponse) (*GroupChatResponseWithInvitations, error) {
|
||||
g, err := toGroupChatResponse(pubKey, response)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &GroupChatResponseWithInvitations{
|
||||
Chat: g.Chat,
|
||||
Messages: g.Messages,
|
||||
Invitations: response.Invitations,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (api *API) execAndGetGroupChatResponse(fn func() (*protocol.MessengerResponse, error)) (*GroupChatResponse, error) {
|
||||
pubKey := types.EncodeHex(crypto.FromECDSAPub(api.s.messenger.IdentityPublicKey()))
|
||||
response, err := fn()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return toGroupChatResponse(pubKey, response)
|
||||
}
|
||||
|
||||
func (api *API) execAndGetGroupChatResponseWithInvitations(fn func() (*protocol.MessengerResponse, error)) (*GroupChatResponseWithInvitations, error) {
|
||||
pubKey := types.EncodeHex(crypto.FromECDSAPub(api.s.messenger.IdentityPublicKey()))
|
||||
|
||||
response, err := fn()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return toGroupChatResponseWithInvitations(pubKey, response)
|
||||
}
|
|
@ -1,24 +1,18 @@
|
|||
package chat
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
gethrpc "github.com/ethereum/go-ethereum/rpc"
|
||||
|
||||
"github.com/status-im/status-go/multiaccounts/accounts"
|
||||
"github.com/status-im/status-go/protocol"
|
||||
)
|
||||
|
||||
func NewService(appDB *sql.DB) *Service {
|
||||
return &Service{
|
||||
accountsDB: accounts.NewDB(appDB),
|
||||
}
|
||||
func NewService() *Service {
|
||||
return &Service{}
|
||||
}
|
||||
|
||||
type Service struct {
|
||||
accountsDB *accounts.Database
|
||||
messenger *protocol.Messenger
|
||||
messenger *protocol.Messenger
|
||||
}
|
||||
|
||||
func (s *Service) Init(messenger *protocol.Messenger) {
|
||||
|
|
Loading…
Reference in New Issue