2020-01-20 20:56:06 +00:00
|
|
|
package ext
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2023-05-22 21:38:02 +00:00
|
|
|
"crypto/ecdsa"
|
2020-01-20 20:56:06 +00:00
|
|
|
"encoding/hex"
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"math/big"
|
|
|
|
"time"
|
|
|
|
|
2022-01-17 03:42:11 +00:00
|
|
|
"github.com/status-im/status-go/services/browsers"
|
2022-09-14 10:46:11 +00:00
|
|
|
"github.com/status-im/status-go/services/wallet"
|
2023-06-21 11:20:43 +00:00
|
|
|
"github.com/status-im/status-go/services/wallet/bigint"
|
2022-01-17 03:42:11 +00:00
|
|
|
|
2021-08-26 20:25:43 +00:00
|
|
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
2020-01-20 20:56:06 +00:00
|
|
|
"github.com/ethereum/go-ethereum/log"
|
|
|
|
"github.com/ethereum/go-ethereum/rlp"
|
|
|
|
|
2022-09-14 10:46:11 +00:00
|
|
|
ethcommon "github.com/ethereum/go-ethereum/common"
|
2020-07-14 14:07:19 +00:00
|
|
|
"github.com/status-im/status-go/eth-node/crypto"
|
2020-01-20 20:56:06 +00:00
|
|
|
"github.com/status-im/status-go/eth-node/types"
|
2023-07-03 10:46:09 +00:00
|
|
|
"github.com/status-im/status-go/images"
|
2020-01-20 20:56:06 +00:00
|
|
|
"github.com/status-im/status-go/mailserver"
|
2022-03-23 18:47:00 +00:00
|
|
|
"github.com/status-im/status-go/multiaccounts/settings"
|
2020-01-20 20:56:06 +00:00
|
|
|
"github.com/status-im/status-go/protocol"
|
2020-09-01 13:27:01 +00:00
|
|
|
"github.com/status-im/status-go/protocol/common"
|
2020-11-18 09:16:51 +00:00
|
|
|
"github.com/status-im/status-go/protocol/communities"
|
2023-07-07 13:03:37 +00:00
|
|
|
"github.com/status-im/status-go/protocol/communities/token"
|
feat: introduce messenger APIs to extract discord channels
As part of the new Discord <-> Status Community Import functionality,
we're adding an API that extracts all discord categories and channels
from a previously exported discord export file.
These APIs can be used in clients to show the user what categories and
channels will be imported later on.
There are two APIs:
1. `Messenger.ExtractDiscordCategoriesAndChannels(filesToimport
[]string) (*MessengerResponse, map[string]*discord.ImportError)`
This takes a list of exported discord export (JSON) files (typically one per
channel), reads them, and extracts the categories and channels into
dedicated data structures (`[]DiscordChannel` and `[]DiscordCategory`)
It also returns the oldest message timestamp found in all extracted
channels.
The API is synchronous and returns the extracted data as
a `*MessengerResponse`. This allows to make the API available
status-go's RPC interface.
The error case is a `map[string]*discord.ImportError` where each key
is a file path of a JSON file that we tried to extract data from, and
the value a `discord.ImportError` which holds an error message and an
error code, allowing for distinguishing between "critical" errors and
"non-critical" errors.
2. `Messenger.RequestExtractDiscordCategoriesAndChannels(filesToImport
[]string)`
This is the asynchronous counterpart to
`ExtractDiscordCategoriesAndChannels`. The reason this API has been
added is because discord servers can have a lot of message and
channel data, which causes `ExtractDiscordCategoriesAndChannels` to
block the thread for too long, making apps potentially feel like they
are stuck.
This API runs inside a go routine, eventually calls
`ExtractDiscordCategoriesAndChannels`, and then emits a newly
introduced `DiscordCategoriesAndChannelsExtractedSignal` that clients
can react to.
Failure of extraction has to be determined by the
`discord.ImportErrors` emitted by the signal.
**A note about exported discord history files**
We expect users to export their discord histories via the
[DiscordChatExporter](https://github.com/Tyrrrz/DiscordChatExporter/wiki/GUI%2C-CLI-and-Formats-explained#exportguild)
tool. The tool allows to export the data in different formats, such as
JSON, HTML and CSV.
We expect users to have their data exported as JSON.
Closes: https://github.com/status-im/status-desktop/issues/6690
2022-07-13 09:33:53 +00:00
|
|
|
"github.com/status-im/status-go/protocol/discord"
|
2020-01-20 20:56:06 +00:00
|
|
|
"github.com/status-im/status-go/protocol/encryption/multidevice"
|
2020-07-27 12:27:48 +00:00
|
|
|
"github.com/status-im/status-go/protocol/protobuf"
|
2020-07-22 07:41:40 +00:00
|
|
|
"github.com/status-im/status-go/protocol/pushnotificationclient"
|
2021-01-11 10:32:51 +00:00
|
|
|
"github.com/status-im/status-go/protocol/requests"
|
2020-01-20 20:56:06 +00:00
|
|
|
"github.com/status-im/status-go/protocol/transport"
|
2020-10-27 17:35:28 +00:00
|
|
|
"github.com/status-im/status-go/protocol/urls"
|
2022-07-05 19:49:44 +00:00
|
|
|
"github.com/status-im/status-go/protocol/verification"
|
2020-01-20 20:56:06 +00:00
|
|
|
"github.com/status-im/status-go/services/ext/mailservers"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
// defaultRequestTimeout is the default request timeout in seconds
|
|
|
|
defaultRequestTimeout = 10
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
// ErrInvalidMailServerPeer is returned when it fails to parse enode from params.
|
|
|
|
ErrInvalidMailServerPeer = errors.New("invalid mailServerPeer value")
|
|
|
|
// ErrInvalidSymKeyID is returned when it fails to get a symmetric key.
|
|
|
|
ErrInvalidSymKeyID = errors.New("invalid symKeyID value")
|
|
|
|
// ErrInvalidPublicKey is returned when public key can't be extracted
|
|
|
|
// from MailServer's nodeID.
|
|
|
|
ErrInvalidPublicKey = errors.New("can't extract public key")
|
|
|
|
// ErrPFSNotEnabled is returned when an endpoint PFS only is called but
|
|
|
|
// PFS is disabled
|
|
|
|
ErrPFSNotEnabled = errors.New("pfs not enabled")
|
|
|
|
)
|
|
|
|
|
|
|
|
// -----
|
|
|
|
// PAYLOADS
|
|
|
|
// -----
|
|
|
|
|
|
|
|
// MessagesRequest is a RequestMessages() request payload.
|
|
|
|
type MessagesRequest struct {
|
|
|
|
// MailServerPeer is MailServer's enode address.
|
|
|
|
MailServerPeer string `json:"mailServerPeer"`
|
|
|
|
|
|
|
|
// From is a lower bound of time range (optional).
|
|
|
|
// Default is 24 hours back from now.
|
|
|
|
From uint32 `json:"from"`
|
|
|
|
|
|
|
|
// To is a upper bound of time range (optional).
|
|
|
|
// Default is now.
|
|
|
|
To uint32 `json:"to"`
|
|
|
|
|
|
|
|
// Limit determines the number of messages sent by the mail server
|
|
|
|
// for the current paginated request
|
|
|
|
Limit uint32 `json:"limit"`
|
|
|
|
|
|
|
|
// Cursor is used as starting point for paginated requests
|
|
|
|
Cursor string `json:"cursor"`
|
|
|
|
|
2021-07-21 19:02:50 +00:00
|
|
|
// StoreCursor is used as starting point for WAKUV2 paginatedRequests
|
|
|
|
StoreCursor *StoreRequestCursor `json:"storeCursor"`
|
|
|
|
|
2020-01-20 20:56:06 +00:00
|
|
|
// Topic is a regular Whisper topic.
|
|
|
|
// DEPRECATED
|
|
|
|
Topic types.TopicType `json:"topic"`
|
|
|
|
|
|
|
|
// Topics is a list of Whisper topics.
|
|
|
|
Topics []types.TopicType `json:"topics"`
|
|
|
|
|
|
|
|
// SymKeyID is an ID of a symmetric key to authenticate to MailServer.
|
|
|
|
// It's derived from MailServer password.
|
|
|
|
SymKeyID string `json:"symKeyID"`
|
|
|
|
|
|
|
|
// Timeout is the time to live of the request specified in seconds.
|
|
|
|
// Default is 10 seconds
|
|
|
|
Timeout time.Duration `json:"timeout"`
|
|
|
|
|
|
|
|
// Force ensures that requests will bypass enforced delay.
|
|
|
|
Force bool `json:"force"`
|
|
|
|
}
|
|
|
|
|
2021-06-16 20:19:45 +00:00
|
|
|
type StoreRequestCursor struct {
|
|
|
|
Digest []byte `json:"digest"`
|
|
|
|
ReceivedTime float64 `json:"receivedTime"`
|
|
|
|
}
|
|
|
|
|
2020-01-20 20:56:06 +00:00
|
|
|
func (r *MessagesRequest) SetDefaults(now time.Time) {
|
|
|
|
// set From and To defaults
|
|
|
|
if r.To == 0 {
|
|
|
|
r.To = uint32(now.UTC().Unix())
|
|
|
|
}
|
|
|
|
|
|
|
|
if r.From == 0 {
|
|
|
|
oneDay := uint32(86400) // -24 hours
|
|
|
|
if r.To < oneDay {
|
|
|
|
r.From = 0
|
|
|
|
} else {
|
|
|
|
r.From = r.To - oneDay
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if r.Timeout == 0 {
|
|
|
|
r.Timeout = defaultRequestTimeout
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// MessagesResponse is a response for requestMessages2 method.
|
|
|
|
type MessagesResponse struct {
|
|
|
|
// Cursor from the response can be used to retrieve more messages
|
|
|
|
// for the previous request.
|
|
|
|
Cursor string `json:"cursor"`
|
|
|
|
|
|
|
|
// Error indicates that something wrong happened when sending messages
|
|
|
|
// to the requester.
|
|
|
|
Error error `json:"error"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// -----
|
|
|
|
// PUBLIC API
|
|
|
|
// -----
|
|
|
|
|
|
|
|
// PublicAPI extends whisper public API.
|
|
|
|
type PublicAPI struct {
|
|
|
|
service *Service
|
|
|
|
eventSub mailservers.EnvelopeEventSubscriber
|
|
|
|
log log.Logger
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewPublicAPI returns instance of the public API.
|
|
|
|
func NewPublicAPI(s *Service, eventSub mailservers.EnvelopeEventSubscriber) *PublicAPI {
|
|
|
|
return &PublicAPI{
|
|
|
|
service: s,
|
|
|
|
eventSub: eventSub,
|
|
|
|
log: log.New("package", "status-go/services/sshext.PublicAPI"),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// RetryConfig specifies configuration for retries with timeout and max amount of retries.
|
|
|
|
type RetryConfig struct {
|
|
|
|
BaseTimeout time.Duration
|
|
|
|
// StepTimeout defines duration increase per each retry.
|
|
|
|
StepTimeout time.Duration
|
|
|
|
MaxRetries int
|
|
|
|
}
|
|
|
|
|
|
|
|
func WaitForExpiredOrCompleted(requestID types.Hash, events chan types.EnvelopeEvent, timeout time.Duration) (*types.MailServerResponse, error) {
|
|
|
|
expired := fmt.Errorf("request %x expired", requestID)
|
|
|
|
after := time.NewTimer(timeout)
|
|
|
|
defer after.Stop()
|
|
|
|
for {
|
|
|
|
var ev types.EnvelopeEvent
|
|
|
|
select {
|
|
|
|
case ev = <-events:
|
|
|
|
case <-after.C:
|
|
|
|
return nil, expired
|
|
|
|
}
|
|
|
|
if ev.Hash != requestID {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
switch ev.Event {
|
|
|
|
case types.EventMailServerRequestCompleted:
|
|
|
|
data, ok := ev.Data.(*types.MailServerResponse)
|
|
|
|
if ok {
|
|
|
|
return data, nil
|
|
|
|
}
|
|
|
|
return nil, errors.New("invalid event data type")
|
|
|
|
case types.EventMailServerRequestExpired:
|
|
|
|
return nil, expired
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type Author struct {
|
|
|
|
PublicKey types.HexBytes `json:"publicKey"`
|
|
|
|
Alias string `json:"alias"`
|
|
|
|
Identicon string `json:"identicon"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type Metadata struct {
|
|
|
|
DedupID []byte `json:"dedupId"`
|
|
|
|
EncryptionID types.HexBytes `json:"encryptionId"`
|
|
|
|
MessageID types.HexBytes `json:"messageId"`
|
|
|
|
Author Author `json:"author"`
|
|
|
|
}
|
|
|
|
|
2020-04-14 11:49:03 +00:00
|
|
|
func (api *PublicAPI) LeaveGroupChat(ctx Context, chatID string, remove bool) (*protocol.MessengerResponse, error) {
|
|
|
|
return api.service.messenger.LeaveGroupChat(ctx, chatID, remove)
|
2020-01-20 20:56:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (api *PublicAPI) CreateGroupChatWithMembers(ctx Context, name string, members []string) (*protocol.MessengerResponse, error) {
|
|
|
|
return api.service.messenger.CreateGroupChatWithMembers(ctx, name, members)
|
|
|
|
}
|
|
|
|
|
2020-08-07 13:49:37 +00:00
|
|
|
func (api *PublicAPI) CreateGroupChatFromInvitation(name string, chatID string, adminPK string) (*protocol.MessengerResponse, error) {
|
|
|
|
return api.service.messenger.CreateGroupChatFromInvitation(name, chatID, adminPK)
|
|
|
|
}
|
|
|
|
|
2020-01-20 20:56:06 +00:00
|
|
|
func (api *PublicAPI) AddMembersToGroupChat(ctx Context, chatID string, members []string) (*protocol.MessengerResponse, error) {
|
|
|
|
return api.service.messenger.AddMembersToGroupChat(ctx, chatID, members)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (api *PublicAPI) RemoveMemberFromGroupChat(ctx Context, chatID string, member string) (*protocol.MessengerResponse, error) {
|
2022-12-10 04:26:51 +00:00
|
|
|
return api.service.messenger.RemoveMembersFromGroupChat(ctx, chatID, []string{member})
|
|
|
|
}
|
|
|
|
|
|
|
|
func (api *PublicAPI) RemoveMembersFromGroupChat(ctx Context, chatID string, members []string) (*protocol.MessengerResponse, error) {
|
|
|
|
return api.service.messenger.RemoveMembersFromGroupChat(ctx, chatID, members)
|
2020-01-20 20:56:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (api *PublicAPI) AddAdminsToGroupChat(ctx Context, chatID string, members []string) (*protocol.MessengerResponse, error) {
|
|
|
|
return api.service.messenger.AddAdminsToGroupChat(ctx, chatID, members)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (api *PublicAPI) ConfirmJoiningGroup(ctx context.Context, chatID string) (*protocol.MessengerResponse, error) {
|
|
|
|
return api.service.messenger.ConfirmJoiningGroup(ctx, chatID)
|
|
|
|
}
|
|
|
|
|
2020-04-14 11:48:32 +00:00
|
|
|
func (api *PublicAPI) ChangeGroupChatName(ctx Context, chatID string, name string) (*protocol.MessengerResponse, error) {
|
|
|
|
return api.service.messenger.ChangeGroupChatName(ctx, chatID, name)
|
|
|
|
}
|
|
|
|
|
2020-08-07 13:49:37 +00:00
|
|
|
func (api *PublicAPI) SendGroupChatInvitationRequest(ctx Context, chatID string, adminPK string, message string) (*protocol.MessengerResponse, error) {
|
|
|
|
return api.service.messenger.SendGroupChatInvitationRequest(ctx, chatID, adminPK, message)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (api *PublicAPI) GetGroupChatInvitations() ([]*protocol.GroupChatInvitation, error) {
|
|
|
|
return api.service.messenger.GetGroupChatInvitations()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (api *PublicAPI) SendGroupChatInvitationRejection(ctx Context, invitationRequestID string) (*protocol.MessengerResponse, error) {
|
|
|
|
return api.service.messenger.SendGroupChatInvitationRejection(ctx, invitationRequestID)
|
|
|
|
}
|
|
|
|
|
2020-01-20 20:56:06 +00:00
|
|
|
func (api *PublicAPI) LoadFilters(parent context.Context, chats []*transport.Filter) ([]*transport.Filter, error) {
|
|
|
|
return api.service.messenger.LoadFilters(chats)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (api *PublicAPI) SaveChat(parent context.Context, chat *protocol.Chat) error {
|
|
|
|
return api.service.messenger.SaveChat(chat)
|
|
|
|
}
|
|
|
|
|
2021-06-30 12:44:49 +00:00
|
|
|
func (api *PublicAPI) SaveMessages(parent context.Context, messages []*common.Message) error {
|
|
|
|
return api.service.messenger.SaveMessages(messages)
|
|
|
|
}
|
|
|
|
|
2021-01-11 10:32:51 +00:00
|
|
|
func (api *PublicAPI) CreateOneToOneChat(parent context.Context, request *requests.CreateOneToOneChat) (*protocol.MessengerResponse, error) {
|
|
|
|
return api.service.messenger.CreateOneToOneChat(request)
|
|
|
|
}
|
|
|
|
|
2021-03-25 15:15:22 +00:00
|
|
|
func (api *PublicAPI) CreatePublicChat(parent context.Context, request *requests.CreatePublicChat) (*protocol.MessengerResponse, error) {
|
|
|
|
return api.service.messenger.CreatePublicChat(request)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (api *PublicAPI) CreateProfileChat(parent context.Context, request *requests.CreateProfileChat) (*protocol.MessengerResponse, error) {
|
|
|
|
return api.service.messenger.CreateProfileChat(request)
|
|
|
|
}
|
|
|
|
|
2020-01-20 20:56:06 +00:00
|
|
|
func (api *PublicAPI) Chats(parent context.Context) []*protocol.Chat {
|
|
|
|
return api.service.messenger.Chats()
|
|
|
|
}
|
|
|
|
|
2021-09-07 14:05:36 +00:00
|
|
|
func (api *PublicAPI) ChatsPreview(parent context.Context) []*protocol.ChatPreview {
|
|
|
|
return api.service.messenger.ChatsPreview()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (api *PublicAPI) Chat(parent context.Context, chatID string) *protocol.Chat {
|
|
|
|
return api.service.messenger.Chat(chatID)
|
2021-08-16 11:04:35 +00:00
|
|
|
}
|
|
|
|
|
2021-04-07 12:57:14 +00:00
|
|
|
func (api *PublicAPI) ActiveChats(parent context.Context) []*protocol.Chat {
|
|
|
|
return api.service.messenger.ActiveChats()
|
|
|
|
}
|
|
|
|
|
2020-01-20 20:56:06 +00:00
|
|
|
func (api *PublicAPI) DeleteChat(parent context.Context, chatID string) error {
|
|
|
|
return api.service.messenger.DeleteChat(chatID)
|
|
|
|
}
|
|
|
|
|
2023-06-02 11:25:32 +00:00
|
|
|
func (api *PublicAPI) MuteCommunityCategory(request *requests.MuteCategory) error {
|
|
|
|
return api.service.messenger.SetMutePropertyOnChatsByCategory(request, true)
|
2022-07-06 16:16:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (api *PublicAPI) UnmuteCommunityCategory(communityID string, categoryID string) error {
|
2023-06-02 11:25:32 +00:00
|
|
|
return api.service.messenger.SetMutePropertyOnChatsByCategory(&requests.MuteCategory{CommunityID: communityID, CategoryID: categoryID, MutedType: protocol.Unmuted}, false)
|
2022-07-06 16:16:19 +00:00
|
|
|
}
|
|
|
|
|
2023-07-19 12:14:42 +00:00
|
|
|
func (api *PublicAPI) MuteCommunityChats(request *requests.MuteCommunity) (time.Time, error) {
|
|
|
|
return api.service.messenger.MuteAllCommunityChats(request)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (api *PublicAPI) UnMuteCommunityChats(communityID string) (time.Time, error) {
|
|
|
|
return api.service.messenger.UnMuteAllCommunityChats(communityID)
|
|
|
|
}
|
|
|
|
|
2023-04-16 15:06:00 +00:00
|
|
|
func (api *PublicAPI) MuteChatV2(parent context.Context, request *requests.MuteChat) (time.Time, error) {
|
|
|
|
return api.service.messenger.MuteChatV2(request)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (api *PublicAPI) MuteChat(parent context.Context, chatID string) (time.Time, error) {
|
|
|
|
return api.service.messenger.MuteChat(&requests.MuteChat{ChatID: chatID, MutedType: 0})
|
2020-06-26 07:46:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (api *PublicAPI) UnmuteChat(parent context.Context, chatID string) error {
|
|
|
|
return api.service.messenger.UnmuteChat(chatID)
|
|
|
|
}
|
|
|
|
|
2023-10-22 09:41:20 +00:00
|
|
|
func (api *PublicAPI) BlockContact(ctx context.Context, contactID string) (*protocol.MessengerResponse, error) {
|
2021-11-05 15:11:10 +00:00
|
|
|
api.log.Info("blocking contact", "contact", contactID)
|
2023-10-22 09:41:20 +00:00
|
|
|
return api.service.messenger.BlockContact(ctx, contactID, false)
|
2020-01-20 20:56:06 +00:00
|
|
|
}
|
|
|
|
|
2022-04-08 20:14:37 +00:00
|
|
|
// This function is the same as the one above, but used only on the desktop side, since at the end it doesn't set
|
|
|
|
// `Added` flag to `false`, but only `Blocked` to `true`
|
2023-10-22 09:41:20 +00:00
|
|
|
func (api *PublicAPI) BlockContactDesktop(ctx context.Context, contactID string) (*protocol.MessengerResponse, error) {
|
2022-04-08 20:14:37 +00:00
|
|
|
api.log.Info("blocking contact", "contact", contactID)
|
2023-10-22 09:41:20 +00:00
|
|
|
return api.service.messenger.BlockContactDesktop(ctx, contactID)
|
2022-04-08 20:14:37 +00:00
|
|
|
}
|
|
|
|
|
2023-06-01 17:26:12 +00:00
|
|
|
func (api *PublicAPI) UnblockContact(parent context.Context, contactID string) (*protocol.MessengerResponse, error) {
|
2021-10-25 07:18:28 +00:00
|
|
|
return api.service.messenger.UnblockContact(contactID)
|
|
|
|
}
|
|
|
|
|
2020-01-20 20:56:06 +00:00
|
|
|
func (api *PublicAPI) Contacts(parent context.Context) []*protocol.Contact {
|
|
|
|
return api.service.messenger.Contacts()
|
|
|
|
}
|
|
|
|
|
2020-06-04 13:32:47 +00:00
|
|
|
func (api *PublicAPI) GetContactByID(parent context.Context, id string) *protocol.Contact {
|
|
|
|
return api.service.messenger.GetContactByID(id)
|
|
|
|
}
|
|
|
|
|
2023-04-05 20:25:44 +00:00
|
|
|
func (api *PublicAPI) RequestContactInfoFromMailserver(pubkey string) (*protocol.Contact, error) {
|
|
|
|
return api.service.messenger.RequestContactInfoFromMailserver(pubkey, true)
|
|
|
|
}
|
|
|
|
|
2020-01-20 20:56:06 +00:00
|
|
|
func (api *PublicAPI) RemoveFilters(parent context.Context, chats []*transport.Filter) error {
|
|
|
|
return api.service.messenger.RemoveFilters(chats)
|
|
|
|
}
|
|
|
|
|
|
|
|
// EnableInstallation enables an installation for multi-device sync.
|
|
|
|
func (api *PublicAPI) EnableInstallation(installationID string) error {
|
|
|
|
return api.service.messenger.EnableInstallation(installationID)
|
|
|
|
}
|
|
|
|
|
|
|
|
// DisableInstallation disables an installation for multi-device sync.
|
|
|
|
func (api *PublicAPI) DisableInstallation(installationID string) error {
|
|
|
|
return api.service.messenger.DisableInstallation(installationID)
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetOurInstallations returns all the installations available given an identity
|
|
|
|
func (api *PublicAPI) GetOurInstallations() []*multidevice.Installation {
|
|
|
|
return api.service.messenger.Installations()
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetInstallationMetadata sets the metadata for our own installation
|
|
|
|
func (api *PublicAPI) SetInstallationMetadata(installationID string, data *multidevice.InstallationMetadata) error {
|
|
|
|
return api.service.messenger.SetInstallationMetadata(installationID, data)
|
|
|
|
}
|
|
|
|
|
2023-03-20 12:51:17 +00:00
|
|
|
// SetInstallationName sets the only the name in metadata for a given installation
|
|
|
|
func (api *PublicAPI) SetInstallationName(installationID string, name string) error {
|
|
|
|
return api.service.messenger.SetInstallationName(installationID, name)
|
|
|
|
}
|
|
|
|
|
2020-12-17 14:36:09 +00:00
|
|
|
// Communities returns a list of communities that are stored
|
2020-11-18 09:16:51 +00:00
|
|
|
func (api *PublicAPI) Communities(parent context.Context) ([]*communities.Community, error) {
|
|
|
|
return api.service.messenger.Communities()
|
|
|
|
}
|
|
|
|
|
2020-12-17 14:36:09 +00:00
|
|
|
// JoinedCommunities returns a list of communities that the user has joined
|
2020-11-18 09:16:51 +00:00
|
|
|
func (api *PublicAPI) JoinedCommunities(parent context.Context) ([]*communities.Community, error) {
|
|
|
|
return api.service.messenger.JoinedCommunities()
|
|
|
|
}
|
|
|
|
|
2022-06-24 13:40:12 +00:00
|
|
|
// CommunityTags return the list of possible community tags
|
|
|
|
func (api *PublicAPI) CommunityTags(parent context.Context) map[string]string {
|
|
|
|
return requests.TagsEmojies
|
|
|
|
}
|
|
|
|
|
2022-06-02 12:17:52 +00:00
|
|
|
// CuratedCommunities returns the list of curated communities stored in the smart contract. If a community is
|
|
|
|
// already known by the node, its description will be returned and and will asynchronously retrieve the
|
|
|
|
// description for the communities it does not know
|
|
|
|
func (api *PublicAPI) CuratedCommunities(parent context.Context) (*communities.KnownCommunitiesResponse, error) {
|
|
|
|
return api.service.messenger.CuratedCommunities()
|
|
|
|
}
|
|
|
|
|
2022-09-20 19:57:39 +00:00
|
|
|
// SpectateCommunity spectates community with the given ID
|
|
|
|
// Meaning user is only a spectator, not a member
|
|
|
|
func (api *PublicAPI) SpectateCommunity(parent context.Context, communityID types.HexBytes) (*protocol.MessengerResponse, error) {
|
|
|
|
return api.service.messenger.SpectateCommunity(communityID)
|
|
|
|
}
|
|
|
|
|
2020-12-17 14:36:09 +00:00
|
|
|
// JoinCommunity joins a community with the given ID
|
2021-01-11 10:32:51 +00:00
|
|
|
func (api *PublicAPI) JoinCommunity(parent context.Context, communityID types.HexBytes) (*protocol.MessengerResponse, error) {
|
2023-05-29 17:57:05 +00:00
|
|
|
return api.service.messenger.JoinCommunity(parent, communityID, false)
|
2020-11-18 09:16:51 +00:00
|
|
|
}
|
|
|
|
|
2020-12-17 14:36:09 +00:00
|
|
|
// LeaveCommunity leaves a commuity with the given ID
|
2023-10-22 09:41:20 +00:00
|
|
|
func (api *PublicAPI) LeaveCommunity(ctx context.Context, communityID types.HexBytes) (*protocol.MessengerResponse, error) {
|
2020-11-18 09:16:51 +00:00
|
|
|
return api.service.messenger.LeaveCommunity(communityID)
|
|
|
|
}
|
|
|
|
|
2020-12-17 14:36:09 +00:00
|
|
|
// CreateCommunity creates a new community with the provided description
|
2021-01-11 10:32:51 +00:00
|
|
|
func (api *PublicAPI) CreateCommunity(request *requests.CreateCommunity) (*protocol.MessengerResponse, error) {
|
2022-07-19 09:31:52 +00:00
|
|
|
return api.service.messenger.CreateCommunity(request, true)
|
2021-05-18 19:32:15 +00:00
|
|
|
}
|
2020-11-18 09:16:51 +00:00
|
|
|
|
2021-05-18 19:32:15 +00:00
|
|
|
// EditCommunity edits an existing community with the provided description
|
|
|
|
func (api *PublicAPI) EditCommunity(request *requests.EditCommunity) (*protocol.MessengerResponse, error) {
|
|
|
|
return api.service.messenger.EditCommunity(request)
|
2020-11-18 09:16:51 +00:00
|
|
|
}
|
|
|
|
|
2023-07-21 09:41:26 +00:00
|
|
|
// RemovePrivateKey removes the private key of the community with given ID
|
|
|
|
func (api *PublicAPI) RemovePrivateKey(id types.HexBytes) (*protocol.MessengerResponse, error) {
|
|
|
|
return api.service.messenger.RemovePrivateKey(id)
|
|
|
|
}
|
|
|
|
|
2023-10-12 19:21:49 +00:00
|
|
|
// Sets the community shard for a community and updates all active filters for the community
|
|
|
|
func (api *PublicAPI) SetCommunityShard(request *requests.SetCommunityShard) (*protocol.MessengerResponse, error) {
|
|
|
|
return api.service.messenger.SetCommunityShard(request)
|
|
|
|
}
|
|
|
|
|
2020-12-17 14:36:09 +00:00
|
|
|
// ExportCommunity exports the private key of the community with given ID
|
2021-01-11 10:32:51 +00:00
|
|
|
func (api *PublicAPI) ExportCommunity(id types.HexBytes) (types.HexBytes, error) {
|
2020-11-18 09:16:51 +00:00
|
|
|
key, err := api.service.messenger.ExportCommunity(id)
|
|
|
|
if err != nil {
|
2021-01-11 10:32:51 +00:00
|
|
|
return nil, err
|
2020-11-18 09:16:51 +00:00
|
|
|
}
|
2021-01-11 10:32:51 +00:00
|
|
|
return crypto.FromECDSA(key), nil
|
2020-11-18 09:16:51 +00:00
|
|
|
}
|
|
|
|
|
2020-12-17 14:36:09 +00:00
|
|
|
// ImportCommunity imports a community with the given private key in hex
|
2021-07-22 17:41:49 +00:00
|
|
|
func (api *PublicAPI) ImportCommunity(ctx context.Context, hexPrivateKey string) (*protocol.MessengerResponse, error) {
|
2020-11-18 09:16:51 +00:00
|
|
|
// Strip the 0x from the beginning
|
|
|
|
privateKey, err := crypto.HexToECDSA(hexPrivateKey[2:])
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2021-07-22 17:41:49 +00:00
|
|
|
return api.service.messenger.ImportCommunity(ctx, privateKey)
|
2023-08-02 17:11:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// GetCommunityPublicKeyFromPrivateKey gets the community's public key from its private key
|
|
|
|
func (api *PublicAPI) GetCommunityPublicKeyFromPrivateKey(ctx context.Context, hexPrivateKey string) string {
|
|
|
|
publicKey := api.service.messenger.GetCommunityIDFromKey(hexPrivateKey)
|
|
|
|
return publicKey
|
2023-08-14 08:59:02 +00:00
|
|
|
}
|
2020-11-18 09:16:51 +00:00
|
|
|
|
2023-08-14 08:59:02 +00:00
|
|
|
// Get community members contact list for provided wallet addresses
|
|
|
|
func (api *PublicAPI) GetCommunityMembersForWalletAddresses(communityID types.HexBytes, chainID uint64) (map[string]*protocol.Contact, error) {
|
|
|
|
return api.service.messenger.GetCommunityMembersForWalletAddresses(communityID, chainID)
|
2020-11-18 09:16:51 +00:00
|
|
|
}
|
|
|
|
|
2023-06-01 20:02:34 +00:00
|
|
|
// Speeds up importing messages from archives
|
|
|
|
func (api *PublicAPI) SpeedupArchivesImport(ctx context.Context) {
|
|
|
|
api.service.messenger.SpeedupArchivesImport()
|
|
|
|
}
|
|
|
|
|
|
|
|
// Slows down importing messages from archives
|
|
|
|
func (api *PublicAPI) SlowdownArchivesImport(ctx context.Context) {
|
|
|
|
api.service.messenger.SlowdownArchivesImport()
|
|
|
|
}
|
|
|
|
|
2020-12-17 14:36:09 +00:00
|
|
|
// CreateCommunityChat creates a community chat in the given community
|
2021-01-11 10:32:51 +00:00
|
|
|
func (api *PublicAPI) CreateCommunityChat(communityID types.HexBytes, c *protobuf.CommunityChat) (*protocol.MessengerResponse, error) {
|
|
|
|
return api.service.messenger.CreateCommunityChat(communityID, c)
|
2020-11-18 09:16:51 +00:00
|
|
|
}
|
|
|
|
|
2021-07-30 17:05:44 +00:00
|
|
|
// EditCommunityChat edits a community chat in the given community
|
2021-06-01 12:13:17 +00:00
|
|
|
func (api *PublicAPI) EditCommunityChat(communityID types.HexBytes, chatID string, c *protobuf.CommunityChat) (*protocol.MessengerResponse, error) {
|
|
|
|
return api.service.messenger.EditCommunityChat(communityID, chatID, c)
|
|
|
|
}
|
|
|
|
|
2021-07-30 17:05:44 +00:00
|
|
|
// DeleteCommunityChat deletes a community chat in the given community
|
|
|
|
func (api *PublicAPI) DeleteCommunityChat(communityID types.HexBytes, chatID string) (*protocol.MessengerResponse, error) {
|
|
|
|
return api.service.messenger.DeleteCommunityChat(communityID, chatID)
|
|
|
|
}
|
|
|
|
|
2021-01-11 10:32:51 +00:00
|
|
|
// ShareCommunity share the community with a set of users
|
|
|
|
func (api *PublicAPI) ShareCommunity(request *requests.ShareCommunity) (*protocol.MessengerResponse, error) {
|
|
|
|
return api.service.messenger.ShareCommunity(request)
|
2020-11-18 09:16:51 +00:00
|
|
|
}
|
|
|
|
|
2022-02-24 16:08:44 +00:00
|
|
|
// ShareImageMessage share the selected chat image with a set of users
|
|
|
|
func (api *PublicAPI) ShareImageMessage(request *requests.ShareImageMessage) (*protocol.MessengerResponse, error) {
|
|
|
|
return api.service.messenger.ShareImageMessage(request)
|
|
|
|
}
|
|
|
|
|
2020-12-21 15:10:52 +00:00
|
|
|
// RemoveUserFromCommunity removes the user with pk from the community with ID
|
2021-01-11 10:32:51 +00:00
|
|
|
func (api *PublicAPI) RemoveUserFromCommunity(communityID types.HexBytes, userPublicKey string) (*protocol.MessengerResponse, error) {
|
|
|
|
return api.service.messenger.RemoveUserFromCommunity(communityID, userPublicKey)
|
|
|
|
}
|
|
|
|
|
2021-06-30 13:29:43 +00:00
|
|
|
// SetCommunityMuted sets the community's muted value
|
2023-06-17 08:19:05 +00:00
|
|
|
func (api *PublicAPI) SetCommunityMuted(request *requests.MuteCommunity) error {
|
|
|
|
return api.service.messenger.SetMuted(request)
|
2021-06-30 13:29:43 +00:00
|
|
|
}
|
|
|
|
|
2021-03-19 09:15:45 +00:00
|
|
|
// BanUserFromCommunity removes the user with pk from the community with ID
|
2023-10-22 09:41:20 +00:00
|
|
|
func (api *PublicAPI) BanUserFromCommunity(ctx context.Context, request *requests.BanUserFromCommunity) (*protocol.MessengerResponse, error) {
|
|
|
|
return api.service.messenger.BanUserFromCommunity(ctx, request)
|
2021-03-19 09:15:45 +00:00
|
|
|
}
|
|
|
|
|
2022-06-23 07:12:15 +00:00
|
|
|
// UnbanUserFromCommunity removes the user's pk from the community ban list
|
|
|
|
func (api *PublicAPI) UnbanUserFromCommunity(request *requests.UnbanUserFromCommunity) (*protocol.MessengerResponse, error) {
|
|
|
|
return api.service.messenger.UnbanUserFromCommunity(request)
|
|
|
|
}
|
|
|
|
|
2022-12-02 11:34:02 +00:00
|
|
|
func (api *PublicAPI) AddRoleToMember(request *requests.AddRoleToMember) (*protocol.MessengerResponse, error) {
|
|
|
|
return api.service.messenger.AddRoleToMember(request)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (api *PublicAPI) RemoveRoleFromMember(request *requests.RemoveRoleFromMember) (*protocol.MessengerResponse, error) {
|
|
|
|
return api.service.messenger.RemoveRoleFromMember(request)
|
|
|
|
}
|
|
|
|
|
2023-03-02 16:27:48 +00:00
|
|
|
func (api *PublicAPI) CreateCommunityTokenPermission(request *requests.CreateCommunityTokenPermission) (*protocol.MessengerResponse, error) {
|
|
|
|
return api.service.messenger.CreateCommunityTokenPermission(request)
|
|
|
|
}
|
|
|
|
|
2023-08-18 15:29:44 +00:00
|
|
|
// ReevaluateCommunityMembersPermissions reevaluates community members permissions
|
|
|
|
func (api *PublicAPI) ReevaluateCommunityMembersPermissions(request *requests.ReevaluateCommunityMembersPermissions) (*protocol.MessengerResponse, error) {
|
|
|
|
return api.service.messenger.ReevaluateCommunityMembersPermissions(request)
|
|
|
|
}
|
|
|
|
|
2023-03-02 16:27:48 +00:00
|
|
|
func (api *PublicAPI) DeleteCommunityTokenPermission(request *requests.DeleteCommunityTokenPermission) (*protocol.MessengerResponse, error) {
|
|
|
|
return api.service.messenger.DeleteCommunityTokenPermission(request)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (api *PublicAPI) EditCommunityTokenPermission(request *requests.EditCommunityTokenPermission) (*protocol.MessengerResponse, error) {
|
|
|
|
return api.service.messenger.EditCommunityTokenPermission(request)
|
|
|
|
}
|
|
|
|
|
2021-01-11 10:32:51 +00:00
|
|
|
// MyPendingRequestsToJoin returns the pending requests for the logged in user
|
|
|
|
func (api *PublicAPI) MyPendingRequestsToJoin() ([]*communities.RequestToJoin, error) {
|
|
|
|
return api.service.messenger.MyPendingRequestsToJoin()
|
|
|
|
}
|
|
|
|
|
2022-10-28 08:41:20 +00:00
|
|
|
// MyCanceledRequestsToJoin returns the pending requests for the logged in user
|
|
|
|
func (api *PublicAPI) MyCanceledRequestsToJoin() ([]*communities.RequestToJoin, error) {
|
|
|
|
return api.service.messenger.MyCanceledRequestsToJoin()
|
|
|
|
}
|
|
|
|
|
2021-01-11 10:32:51 +00:00
|
|
|
// PendingRequestsToJoinForCommunity returns the pending requests to join for a given community
|
|
|
|
func (api *PublicAPI) PendingRequestsToJoinForCommunity(id types.HexBytes) ([]*communities.RequestToJoin, error) {
|
|
|
|
return api.service.messenger.PendingRequestsToJoinForCommunity(id)
|
|
|
|
}
|
|
|
|
|
2023-08-18 19:52:13 +00:00
|
|
|
// AllPendingRequestsToJoinForCommunity returns the all the pending requests to join, including accepted and rejected ones, for a given community
|
|
|
|
func (api *PublicAPI) AllPendingRequestsToJoinForCommunity(id types.HexBytes) ([]*communities.RequestToJoin, error) {
|
|
|
|
return api.service.messenger.AllPendingRequestsToJoinForCommunity(id)
|
|
|
|
}
|
|
|
|
|
2022-08-04 07:44:35 +00:00
|
|
|
// DeclinedRequestsToJoinForCommunity returns the declined requests to join for a given community
|
|
|
|
func (api *PublicAPI) DeclinedRequestsToJoinForCommunity(id types.HexBytes) ([]*communities.RequestToJoin, error) {
|
|
|
|
return api.service.messenger.DeclinedRequestsToJoinForCommunity(id)
|
|
|
|
}
|
|
|
|
|
2022-10-28 08:41:20 +00:00
|
|
|
// CanceledRequestsToJoinForCommunity returns the declined requests to join for a given community
|
|
|
|
func (api *PublicAPI) CanceledRequestsToJoinForCommunity(id types.HexBytes) ([]*communities.RequestToJoin, error) {
|
|
|
|
return api.service.messenger.CanceledRequestsToJoinForCommunity(id)
|
|
|
|
}
|
|
|
|
|
|
|
|
// CancelRequestToJoinCommunity accepts a pending request to join a community
|
2023-10-22 09:41:20 +00:00
|
|
|
func (api *PublicAPI) CancelRequestToJoinCommunity(ctx context.Context, request *requests.CancelRequestToJoinCommunity) (*protocol.MessengerResponse, error) {
|
|
|
|
return api.service.messenger.CancelRequestToJoinCommunity(ctx, request)
|
2022-10-28 08:41:20 +00:00
|
|
|
}
|
|
|
|
|
2021-01-11 10:32:51 +00:00
|
|
|
// AcceptRequestToJoinCommunity accepts a pending request to join a community
|
|
|
|
func (api *PublicAPI) AcceptRequestToJoinCommunity(request *requests.AcceptRequestToJoinCommunity) (*protocol.MessengerResponse, error) {
|
|
|
|
return api.service.messenger.AcceptRequestToJoinCommunity(request)
|
|
|
|
}
|
|
|
|
|
|
|
|
// DeclineRequestToJoinCommunity accepts a pending request to join a community
|
2022-10-25 22:06:20 +00:00
|
|
|
func (api *PublicAPI) DeclineRequestToJoinCommunity(request *requests.DeclineRequestToJoinCommunity) (*protocol.MessengerResponse, error) {
|
2021-01-11 10:32:51 +00:00
|
|
|
return api.service.messenger.DeclineRequestToJoinCommunity(request)
|
|
|
|
}
|
|
|
|
|
|
|
|
// RequestToJoinCommunity requests to join a particular community
|
|
|
|
func (api *PublicAPI) RequestToJoinCommunity(request *requests.RequestToJoinCommunity) (*protocol.MessengerResponse, error) {
|
|
|
|
return api.service.messenger.RequestToJoinCommunity(request)
|
2020-12-21 15:10:52 +00:00
|
|
|
}
|
|
|
|
|
2023-07-10 14:11:37 +00:00
|
|
|
// EditSharedAddressesForCommunity edits the addresses that are shared with the owner of the community
|
|
|
|
func (api *PublicAPI) EditSharedAddressesForCommunity(request *requests.EditSharedAddresses) (*protocol.MessengerResponse, error) {
|
|
|
|
return api.service.messenger.EditSharedAddressesForCommunity(request)
|
|
|
|
}
|
|
|
|
|
2023-08-09 15:08:20 +00:00
|
|
|
// GetRevealedAccounts gets the revealed addresses for a member in a community
|
|
|
|
func (api *PublicAPI) GetRevealedAccounts(communityID types.HexBytes, memberPk string) ([]*protobuf.RevealedAccount, error) {
|
|
|
|
return api.service.messenger.GetRevealedAccounts(communityID, memberPk)
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetRevealedAccountsForAllMembers gets the revealed addresses for all the members of a community
|
|
|
|
func (api *PublicAPI) GetRevealedAccountsForAllMembers(communityID types.HexBytes) (map[string][]*protobuf.RevealedAccount, error) {
|
|
|
|
return api.service.messenger.GetRevealedAccountsForAllMembers(communityID)
|
|
|
|
}
|
|
|
|
|
2023-04-21 09:18:47 +00:00
|
|
|
// CheckAndClearPendingRequestToJoinCommunity to delete pending request to join a community which are older than 7 days
|
2023-10-22 09:41:20 +00:00
|
|
|
func (api *PublicAPI) CheckAndDeletePendingRequestToJoinCommunity(ctx context.Context) (*protocol.MessengerResponse, error) {
|
|
|
|
return api.service.messenger.CheckAndDeletePendingRequestToJoinCommunity(ctx, true)
|
2023-04-21 09:18:47 +00:00
|
|
|
}
|
|
|
|
|
2021-05-23 13:34:17 +00:00
|
|
|
// CreateCommunityCategory creates a category within a particular community
|
|
|
|
func (api *PublicAPI) CreateCommunityCategory(request *requests.CreateCommunityCategory) (*protocol.MessengerResponse, error) {
|
|
|
|
return api.service.messenger.CreateCommunityCategory(request)
|
|
|
|
}
|
|
|
|
|
|
|
|
// ReorderCommunityCategories is used to change the order of the categories of a community
|
|
|
|
func (api *PublicAPI) ReorderCommunityCategories(request *requests.ReorderCommunityCategories) (*protocol.MessengerResponse, error) {
|
|
|
|
return api.service.messenger.ReorderCommunityCategories(request)
|
|
|
|
}
|
|
|
|
|
|
|
|
// ReorderCommunityChat allows changing the order of the chat or switching its category
|
|
|
|
func (api *PublicAPI) ReorderCommunityChat(request *requests.ReorderCommunityChat) (*protocol.MessengerResponse, error) {
|
|
|
|
return api.service.messenger.ReorderCommunityChat(request)
|
|
|
|
}
|
|
|
|
|
|
|
|
// EditCommunityCategory modifies a category within a particular community
|
|
|
|
func (api *PublicAPI) EditCommunityCategory(request *requests.EditCommunityCategory) (*protocol.MessengerResponse, error) {
|
|
|
|
return api.service.messenger.EditCommunityCategory(request)
|
|
|
|
}
|
|
|
|
|
|
|
|
// DeleteCommunityCategory deletes a category within a particular community and removes this category from any chat that has it
|
|
|
|
func (api *PublicAPI) DeleteCommunityCategory(request *requests.DeleteCommunityCategory) (*protocol.MessengerResponse, error) {
|
|
|
|
return api.service.messenger.DeleteCommunityCategory(request)
|
|
|
|
}
|
|
|
|
|
2020-01-20 20:56:06 +00:00
|
|
|
type ApplicationMessagesResponse struct {
|
2020-09-01 13:27:01 +00:00
|
|
|
Messages []*common.Message `json:"messages"`
|
|
|
|
Cursor string `json:"cursor"`
|
2020-01-20 20:56:06 +00:00
|
|
|
}
|
|
|
|
|
2021-08-31 08:49:39 +00:00
|
|
|
type MarkMessagSeenResponse struct {
|
|
|
|
Count uint64 `json:"count"`
|
|
|
|
CountWithMentions uint64 `json:"countWithMentions"`
|
|
|
|
}
|
|
|
|
|
2021-05-14 21:22:50 +00:00
|
|
|
type ApplicationPinnedMessagesResponse struct {
|
2021-06-08 15:23:32 +00:00
|
|
|
PinnedMessages []*common.PinnedMessage `json:"pinnedMessages"`
|
|
|
|
Cursor string `json:"cursor"`
|
2021-05-14 21:22:50 +00:00
|
|
|
}
|
|
|
|
|
2021-07-22 17:41:49 +00:00
|
|
|
type ApplicationStatusUpdatesResponse struct {
|
|
|
|
StatusUpdates []protocol.UserStatus `json:"statusUpdates"`
|
|
|
|
}
|
|
|
|
|
2023-01-05 16:11:57 +00:00
|
|
|
type ApplicationSwitcherCardsResponse struct {
|
|
|
|
SwitcherCards []protocol.SwitcherCard `json:"switcherCards"`
|
|
|
|
}
|
|
|
|
|
2020-01-20 20:56:06 +00:00
|
|
|
func (api *PublicAPI) ChatMessages(chatID, cursor string, limit int) (*ApplicationMessagesResponse, error) {
|
|
|
|
messages, cursor, err := api.service.messenger.MessageByChatID(chatID, cursor, limit)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return &ApplicationMessagesResponse{
|
|
|
|
Messages: messages,
|
|
|
|
Cursor: cursor,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2021-11-05 13:48:20 +00:00
|
|
|
func (api *PublicAPI) MessageByMessageID(messageID string) (*common.Message, error) {
|
|
|
|
return api.service.messenger.MessageByID(messageID)
|
|
|
|
}
|
|
|
|
|
2022-12-11 19:08:51 +00:00
|
|
|
func (api *PublicAPI) FirstUnseenMessageID(chatID string) (string, error) {
|
|
|
|
return api.service.messenger.FirstUnseenMessageID(chatID)
|
|
|
|
}
|
|
|
|
|
2021-08-19 19:47:03 +00:00
|
|
|
func (api *PublicAPI) AllMessagesFromChatWhichMatchTerm(chatID, searchTerm string, caseSensitive bool) (*ApplicationMessagesResponse, error) {
|
2021-08-04 19:31:44 +00:00
|
|
|
messages, err := api.service.messenger.AllMessageByChatIDWhichMatchTerm(chatID, searchTerm, caseSensitive)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return &ApplicationMessagesResponse{
|
|
|
|
Messages: messages,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2021-08-19 19:47:03 +00:00
|
|
|
func (api *PublicAPI) AllMessagesFromChatsAndCommunitiesWhichMatchTerm(communityIds []string, chatIds []string, searchTerm string, caseSensitive bool) (*ApplicationMessagesResponse, error) {
|
|
|
|
messages, err := api.service.messenger.AllMessagesFromChatsAndCommunitiesWhichMatchTerm(communityIds, chatIds, searchTerm, caseSensitive)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return &ApplicationMessagesResponse{
|
|
|
|
Messages: messages,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2021-05-14 21:22:50 +00:00
|
|
|
func (api *PublicAPI) ChatPinnedMessages(chatID, cursor string, limit int) (*ApplicationPinnedMessagesResponse, error) {
|
2021-06-08 15:23:32 +00:00
|
|
|
pinnedMessages, cursor, err := api.service.messenger.PinnedMessageByChatID(chatID, cursor, limit)
|
2021-05-14 21:22:50 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return &ApplicationPinnedMessagesResponse{
|
2021-06-08 15:23:32 +00:00
|
|
|
PinnedMessages: pinnedMessages,
|
|
|
|
Cursor: cursor,
|
2021-05-14 21:22:50 +00:00
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2021-07-22 17:41:49 +00:00
|
|
|
func (api *PublicAPI) StatusUpdates() (*ApplicationStatusUpdatesResponse, error) {
|
|
|
|
statusUpdates, err := api.service.messenger.StatusUpdates()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return &ApplicationStatusUpdatesResponse{
|
|
|
|
StatusUpdates: statusUpdates,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2023-01-05 16:11:57 +00:00
|
|
|
func (api *PublicAPI) UpsertSwitcherCard(request *requests.UpsertSwitcherCard) error {
|
|
|
|
return api.service.messenger.UpsertSwitcherCard(request)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (api *PublicAPI) DeleteSwitcherCard(id string) error {
|
|
|
|
return api.service.messenger.DeleteSwitcherCard(id)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (api *PublicAPI) SwitcherCards() (*ApplicationSwitcherCardsResponse, error) {
|
|
|
|
switcherCards, err := api.service.messenger.SwitcherCards()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return &ApplicationSwitcherCardsResponse{
|
|
|
|
SwitcherCards: switcherCards,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2021-01-14 22:15:13 +00:00
|
|
|
func (api *PublicAPI) StartMessenger() (*protocol.MessengerResponse, error) {
|
2020-01-29 19:40:06 +00:00
|
|
|
return api.service.StartMessenger()
|
|
|
|
}
|
|
|
|
|
2021-07-22 17:41:49 +00:00
|
|
|
func (api *PublicAPI) SetUserStatus(ctx context.Context, status int, customText string) error {
|
|
|
|
return api.service.messenger.SetUserStatus(ctx, status, customText)
|
|
|
|
}
|
|
|
|
|
2020-01-20 20:56:06 +00:00
|
|
|
func (api *PublicAPI) DeleteMessage(id string) error {
|
|
|
|
return api.service.messenger.DeleteMessage(id)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (api *PublicAPI) DeleteMessagesByChatID(id string) error {
|
|
|
|
return api.service.messenger.DeleteMessagesByChatID(id)
|
|
|
|
}
|
|
|
|
|
2021-08-31 08:49:39 +00:00
|
|
|
func (api *PublicAPI) MarkMessagesSeen(chatID string, ids []string) (*MarkMessagSeenResponse, error) {
|
|
|
|
count, withMentions, err := api.service.messenger.MarkMessagesSeen(chatID, ids)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
response := &MarkMessagSeenResponse{Count: count, CountWithMentions: withMentions}
|
|
|
|
return response, nil
|
2020-01-20 20:56:06 +00:00
|
|
|
}
|
|
|
|
|
2023-10-22 09:41:20 +00:00
|
|
|
func (api *PublicAPI) MarkAllRead(ctx context.Context, chatID string) error {
|
|
|
|
return api.service.messenger.MarkAllRead(ctx, chatID)
|
2020-02-26 12:31:48 +00:00
|
|
|
}
|
|
|
|
|
2023-10-22 09:41:20 +00:00
|
|
|
func (api *PublicAPI) MarkAllReadInCommunity(ctx context.Context, communityID string) ([]string, error) {
|
|
|
|
return api.service.messenger.MarkAllReadInCommunity(ctx, communityID)
|
2021-09-20 06:33:36 +00:00
|
|
|
}
|
|
|
|
|
2022-01-18 16:31:34 +00:00
|
|
|
func (api *PublicAPI) SendContactRequest(ctx context.Context, request *requests.SendContactRequest) (*protocol.MessengerResponse, error) {
|
|
|
|
return api.service.messenger.SendContactRequest(ctx, request)
|
|
|
|
}
|
|
|
|
|
2023-02-21 18:08:11 +00:00
|
|
|
func (api *PublicAPI) AddContact(ctx context.Context, request *requests.AddContact) (*protocol.MessengerResponse, error) {
|
|
|
|
return api.service.messenger.AddContact(ctx, request)
|
|
|
|
}
|
|
|
|
|
2022-01-18 16:31:34 +00:00
|
|
|
func (api *PublicAPI) AcceptContactRequest(ctx context.Context, request *requests.AcceptContactRequest) (*protocol.MessengerResponse, error) {
|
|
|
|
return api.service.messenger.AcceptContactRequest(ctx, request)
|
|
|
|
}
|
|
|
|
|
2023-02-21 18:08:11 +00:00
|
|
|
func (api *PublicAPI) DeclineContactRequest(ctx context.Context, request *requests.DeclineContactRequest) (*protocol.MessengerResponse, error) {
|
|
|
|
return api.service.messenger.DeclineContactRequest(ctx, request)
|
|
|
|
}
|
|
|
|
|
2022-06-17 16:20:43 +00:00
|
|
|
func (api *PublicAPI) AcceptLatestContactRequestForContact(ctx context.Context, request *requests.AcceptLatestContactRequestForContact) (*protocol.MessengerResponse, error) {
|
|
|
|
return api.service.messenger.AcceptLatestContactRequestForContact(ctx, request)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (api *PublicAPI) DismissLatestContactRequestForContact(ctx context.Context, request *requests.DismissLatestContactRequestForContact) (*protocol.MessengerResponse, error) {
|
|
|
|
return api.service.messenger.DismissLatestContactRequestForContact(ctx, request)
|
|
|
|
}
|
|
|
|
|
2022-01-18 16:31:34 +00:00
|
|
|
func (api *PublicAPI) RetractContactRequest(ctx context.Context, request *requests.RetractContactRequest) (*protocol.MessengerResponse, error) {
|
|
|
|
return api.service.messenger.RetractContactRequest(request)
|
|
|
|
}
|
|
|
|
|
2020-12-22 10:49:25 +00:00
|
|
|
func (api *PublicAPI) RemoveContact(ctx context.Context, pubKey string) (*protocol.MessengerResponse, error) {
|
|
|
|
return api.service.messenger.RemoveContact(ctx, pubKey)
|
|
|
|
}
|
|
|
|
|
2021-10-25 10:25:37 +00:00
|
|
|
func (api *PublicAPI) SetContactLocalNickname(ctx context.Context, request *requests.SetContactLocalNickname) (*protocol.MessengerResponse, error) {
|
|
|
|
return api.service.messenger.SetContactLocalNickname(request)
|
2021-10-25 07:18:28 +00:00
|
|
|
}
|
|
|
|
|
2021-03-25 15:15:22 +00:00
|
|
|
func (api *PublicAPI) ClearHistory(request *requests.ClearHistory) (*protocol.MessengerResponse, error) {
|
|
|
|
return api.service.messenger.ClearHistory(request)
|
2020-12-22 10:49:25 +00:00
|
|
|
}
|
|
|
|
|
2021-03-25 15:15:22 +00:00
|
|
|
func (api *PublicAPI) DeactivateChat(request *requests.DeactivateChat) (*protocol.MessengerResponse, error) {
|
|
|
|
return api.service.messenger.DeactivateChat(request)
|
2020-12-22 10:49:25 +00:00
|
|
|
}
|
|
|
|
|
2020-01-20 20:56:06 +00:00
|
|
|
func (api *PublicAPI) UpdateMessageOutgoingStatus(id, newOutgoingStatus string) error {
|
|
|
|
return api.service.messenger.UpdateMessageOutgoingStatus(id, newOutgoingStatus)
|
|
|
|
}
|
|
|
|
|
2020-09-01 13:27:01 +00:00
|
|
|
func (api *PublicAPI) SendChatMessage(ctx context.Context, message *common.Message) (*protocol.MessengerResponse, error) {
|
2020-01-20 20:56:06 +00:00
|
|
|
return api.service.messenger.SendChatMessage(ctx, message)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (api *PublicAPI) ReSendChatMessage(ctx context.Context, messageID string) error {
|
|
|
|
return api.service.messenger.ReSendChatMessage(ctx, messageID)
|
|
|
|
}
|
|
|
|
|
2020-12-01 09:43:46 +00:00
|
|
|
func (api *PublicAPI) SendChatMessages(ctx context.Context, messages []*common.Message) (*protocol.MessengerResponse, error) {
|
|
|
|
return api.service.messenger.SendChatMessages(ctx, messages)
|
|
|
|
}
|
|
|
|
|
2022-10-14 08:50:36 +00:00
|
|
|
func (api *PublicAPI) SendOneToOneMessage(request *requests.SendOneToOneMessage) (*protocol.MessengerResponse, error) {
|
|
|
|
return api.service.messenger.SendOneToOneMessage(request)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (api *PublicAPI) SendGroupChatMessage(request *requests.SendGroupChatMessage) (*protocol.MessengerResponse, error) {
|
|
|
|
return api.service.messenger.SendGroupChatMessage(request)
|
|
|
|
}
|
|
|
|
|
2021-06-08 06:07:45 +00:00
|
|
|
func (api *PublicAPI) EditMessage(ctx context.Context, request *requests.EditMessage) (*protocol.MessengerResponse, error) {
|
|
|
|
return api.service.messenger.EditMessage(ctx, request)
|
|
|
|
}
|
|
|
|
|
2021-07-26 21:06:32 +00:00
|
|
|
func (api *PublicAPI) DeleteMessageAndSend(ctx context.Context, messageID string) (*protocol.MessengerResponse, error) {
|
|
|
|
return api.service.messenger.DeleteMessageAndSend(ctx, messageID)
|
|
|
|
}
|
|
|
|
|
2022-09-28 11:42:17 +00:00
|
|
|
func (api *PublicAPI) DeleteMessageForMeAndSync(ctx context.Context, chatID string, messageID string) (*protocol.MessengerResponse, error) {
|
|
|
|
return api.service.messenger.DeleteMessageForMeAndSync(ctx, chatID, messageID)
|
|
|
|
}
|
|
|
|
|
2021-05-14 21:22:50 +00:00
|
|
|
func (api *PublicAPI) SendPinMessage(ctx context.Context, message *common.PinMessage) (*protocol.MessengerResponse, error) {
|
|
|
|
return api.service.messenger.SendPinMessage(ctx, message)
|
|
|
|
}
|
|
|
|
|
2020-01-20 20:56:06 +00:00
|
|
|
func (api *PublicAPI) RequestTransaction(ctx context.Context, chatID, value, contract, address string) (*protocol.MessengerResponse, error) {
|
|
|
|
return api.service.messenger.RequestTransaction(ctx, chatID, value, contract, address)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (api *PublicAPI) RequestAddressForTransaction(ctx context.Context, chatID, from, value, contract string) (*protocol.MessengerResponse, error) {
|
|
|
|
return api.service.messenger.RequestAddressForTransaction(ctx, chatID, from, value, contract)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (api *PublicAPI) DeclineRequestAddressForTransaction(ctx context.Context, messageID string) (*protocol.MessengerResponse, error) {
|
|
|
|
return api.service.messenger.DeclineRequestAddressForTransaction(ctx, messageID)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (api *PublicAPI) DeclineRequestTransaction(ctx context.Context, messageID string) (*protocol.MessengerResponse, error) {
|
|
|
|
return api.service.messenger.DeclineRequestTransaction(ctx, messageID)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (api *PublicAPI) AcceptRequestAddressForTransaction(ctx context.Context, messageID, address string) (*protocol.MessengerResponse, error) {
|
|
|
|
return api.service.messenger.AcceptRequestAddressForTransaction(ctx, messageID, address)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (api *PublicAPI) SendTransaction(ctx context.Context, chatID, value, contract, transactionHash string, signature types.HexBytes) (*protocol.MessengerResponse, error) {
|
|
|
|
return api.service.messenger.SendTransaction(ctx, chatID, value, contract, transactionHash, signature)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (api *PublicAPI) AcceptRequestTransaction(ctx context.Context, transactionHash, messageID string, signature types.HexBytes) (*protocol.MessengerResponse, error) {
|
|
|
|
return api.service.messenger.AcceptRequestTransaction(ctx, transactionHash, messageID, signature)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (api *PublicAPI) SendContactUpdates(ctx context.Context, name, picture string) error {
|
|
|
|
return api.service.messenger.SendContactUpdates(ctx, name, picture)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (api *PublicAPI) SendContactUpdate(ctx context.Context, contactID, name, picture string) (*protocol.MessengerResponse, error) {
|
|
|
|
return api.service.messenger.SendContactUpdate(ctx, contactID, name, picture)
|
|
|
|
}
|
|
|
|
|
2022-02-17 15:13:10 +00:00
|
|
|
func (api *PublicAPI) SetDisplayName(ctx context.Context, displayName string) error {
|
2023-03-30 10:18:13 +00:00
|
|
|
return api.service.messenger.SetDisplayName(displayName)
|
2022-02-17 15:13:10 +00:00
|
|
|
}
|
|
|
|
|
2022-07-05 19:49:44 +00:00
|
|
|
func (api *PublicAPI) MarkAsTrusted(ctx context.Context, contactID string) error {
|
|
|
|
return api.service.messenger.MarkAsTrusted(ctx, contactID)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (api *PublicAPI) MarkAsUntrustworthy(ctx context.Context, contactID string) error {
|
|
|
|
return api.service.messenger.MarkAsUntrustworthy(ctx, contactID)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (api *PublicAPI) RemoveTrustStatus(ctx context.Context, contactID string) error {
|
|
|
|
return api.service.messenger.RemoveTrustStatus(ctx, contactID)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (api *PublicAPI) GetTrustStatus(ctx context.Context, contactID string) (verification.TrustStatus, error) {
|
|
|
|
return api.service.messenger.GetTrustStatus(contactID)
|
|
|
|
}
|
|
|
|
|
2022-10-28 14:08:18 +00:00
|
|
|
func (api *PublicAPI) GetLatestVerificationRequestFrom(ctx context.Context, contactID string) (*verification.Request, error) {
|
|
|
|
return api.service.messenger.GetLatestVerificationRequestFrom(contactID)
|
|
|
|
}
|
|
|
|
|
2022-08-31 14:41:58 +00:00
|
|
|
func (api *PublicAPI) SendContactVerificationRequest(ctx context.Context, contactID string, challenge string) (*protocol.MessengerResponse, error) {
|
2022-07-05 19:49:44 +00:00
|
|
|
return api.service.messenger.SendContactVerificationRequest(ctx, contactID, challenge)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (api *PublicAPI) GetReceivedVerificationRequests(ctx context.Context) ([]*verification.Request, error) {
|
|
|
|
return api.service.messenger.GetReceivedVerificationRequests(ctx)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (api *PublicAPI) GetVerificationRequestSentTo(ctx context.Context, contactID string) (*verification.Request, error) {
|
|
|
|
return api.service.messenger.GetVerificationRequestSentTo(ctx, contactID)
|
|
|
|
}
|
|
|
|
|
2022-12-14 08:27:02 +00:00
|
|
|
func (api *PublicAPI) CancelVerificationRequest(ctx context.Context, id string) (*protocol.MessengerResponse, error) {
|
|
|
|
return api.service.messenger.CancelVerificationRequest(ctx, id)
|
2022-07-05 19:49:44 +00:00
|
|
|
}
|
|
|
|
|
2022-12-14 08:27:02 +00:00
|
|
|
func (api *PublicAPI) AcceptContactVerificationRequest(ctx context.Context, id string, response string) (*protocol.MessengerResponse, error) {
|
|
|
|
return api.service.messenger.AcceptContactVerificationRequest(ctx, id, response)
|
2022-07-05 19:49:44 +00:00
|
|
|
}
|
|
|
|
|
2022-12-14 08:27:02 +00:00
|
|
|
func (api *PublicAPI) DeclineContactVerificationRequest(ctx context.Context, id string) (*protocol.MessengerResponse, error) {
|
|
|
|
return api.service.messenger.DeclineContactVerificationRequest(ctx, id)
|
2022-07-05 19:49:44 +00:00
|
|
|
}
|
|
|
|
|
2022-10-28 11:37:46 +00:00
|
|
|
func (api *PublicAPI) VerifiedTrusted(ctx context.Context, request *requests.VerifiedTrusted) (*protocol.MessengerResponse, error) {
|
|
|
|
return api.service.messenger.VerifiedTrusted(ctx, request)
|
2022-07-05 19:49:44 +00:00
|
|
|
}
|
|
|
|
|
2022-10-28 11:37:46 +00:00
|
|
|
func (api *PublicAPI) VerifiedUntrustworthy(ctx context.Context, request *requests.VerifiedUntrustworthy) (*protocol.MessengerResponse, error) {
|
|
|
|
return api.service.messenger.VerifiedUntrustworthy(ctx, request)
|
2022-07-05 19:49:44 +00:00
|
|
|
}
|
|
|
|
|
2020-01-20 20:56:06 +00:00
|
|
|
func (api *PublicAPI) SendPairInstallation(ctx context.Context) (*protocol.MessengerResponse, error) {
|
2023-02-28 12:32:45 +00:00
|
|
|
return api.service.messenger.SendPairInstallation(ctx, nil)
|
2020-01-20 20:56:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (api *PublicAPI) SyncDevices(ctx context.Context, name, picture string) error {
|
2023-01-06 12:21:14 +00:00
|
|
|
return api.service.messenger.SyncDevices(ctx, name, picture, nil)
|
2020-01-20 20:56:06 +00:00
|
|
|
}
|
|
|
|
|
2022-06-08 18:39:53 +00:00
|
|
|
func (api *PublicAPI) AddBookmark(ctx context.Context, bookmark browsers.Bookmark) error {
|
|
|
|
return api.service.messenger.AddBookmark(ctx, bookmark)
|
|
|
|
}
|
|
|
|
|
2022-08-24 14:05:35 +00:00
|
|
|
func (api *PublicAPI) AddBrowser(ctx context.Context, browser browsers.Browser) error {
|
|
|
|
return api.service.messenger.AddBrowser(ctx, browser)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (api *PublicAPI) GetBrowsers(ctx context.Context) (browsers []*browsers.Browser, err error) {
|
|
|
|
return api.service.messenger.GetBrowsers(ctx)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (api *PublicAPI) DeleteBrowser(ctx context.Context, id string) error {
|
|
|
|
return api.service.messenger.DeleteBrowser(ctx, id)
|
|
|
|
}
|
|
|
|
|
2022-06-08 18:39:53 +00:00
|
|
|
func (api *PublicAPI) RemoveBookmark(ctx context.Context, url string) error {
|
|
|
|
return api.service.messenger.RemoveBookmark(ctx, url)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (api *PublicAPI) UpdateBookmark(ctx context.Context, oldURL string, bookmark browsers.Bookmark) error {
|
|
|
|
return api.service.messenger.UpdateBookmark(ctx, oldURL, bookmark)
|
2022-01-17 03:42:11 +00:00
|
|
|
}
|
|
|
|
|
2020-03-20 08:32:13 +00:00
|
|
|
func (api *PublicAPI) SignMessageWithChatKey(ctx context.Context, message string) (types.HexBytes, error) {
|
|
|
|
return api.service.messenger.SignMessage(message)
|
|
|
|
}
|
|
|
|
|
2023-08-29 13:17:37 +00:00
|
|
|
func (api *PublicAPI) CreateCommunityTokenDeploymentSignature(ctx context.Context, chainID uint64, addressFrom string, communityID string) (types.HexBytes, error) {
|
|
|
|
return api.service.messenger.CreateCommunityTokenDeploymentSignature(ctx, chainID, addressFrom, communityID)
|
|
|
|
}
|
|
|
|
|
2022-07-22 08:10:47 +00:00
|
|
|
// wallet connect session apis
|
|
|
|
func (api *PublicAPI) AddWalletConnectSession(ctx context.Context, request *requests.AddWalletConnectSession) error {
|
|
|
|
return api.service.messenger.AddWalletConnectSession(request)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (api *PublicAPI) GetWalletConnectSession(ctx context.Context) ([]protocol.WalletConnectSession, error) {
|
|
|
|
return api.service.messenger.GetWalletConnectSession()
|
|
|
|
}
|
2020-07-14 14:07:19 +00:00
|
|
|
|
2022-07-22 08:10:47 +00:00
|
|
|
func (api *PublicAPI) DestroyWalletConnectSession(ctx context.Context, PeerID string) error {
|
|
|
|
return api.service.messenger.DestroyWalletConnectSession(PeerID)
|
|
|
|
}
|
|
|
|
|
2022-09-14 10:46:11 +00:00
|
|
|
// Saved Addresses APIs
|
|
|
|
func (api *PublicAPI) UpsertSavedAddress(ctx context.Context, sa wallet.SavedAddress) error {
|
|
|
|
return api.service.messenger.UpsertSavedAddress(ctx, sa)
|
|
|
|
}
|
|
|
|
|
2023-03-06 13:26:46 +00:00
|
|
|
func (api *PublicAPI) DeleteSavedAddress(ctx context.Context, address ethcommon.Address, ens string, isTest bool) error {
|
|
|
|
return api.service.messenger.DeleteSavedAddress(ctx, address, ens, isTest)
|
2022-09-14 10:46:11 +00:00
|
|
|
}
|
|
|
|
|
2022-07-22 08:10:47 +00:00
|
|
|
// PushNotifications server endpoints
|
2020-07-22 07:41:40 +00:00
|
|
|
func (api *PublicAPI) StartPushNotificationsServer() error {
|
2022-03-23 18:47:00 +00:00
|
|
|
err := api.service.accountsDB.SaveSettingField(settings.PushNotificationsServerEnabled, true)
|
2020-07-17 11:41:49 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-07-22 07:41:40 +00:00
|
|
|
return api.service.messenger.StartPushNotificationsServer()
|
2020-07-14 14:07:19 +00:00
|
|
|
}
|
|
|
|
|
2020-07-22 07:41:40 +00:00
|
|
|
func (api *PublicAPI) StopPushNotificationsServer() error {
|
2022-03-23 18:47:00 +00:00
|
|
|
err := api.service.accountsDB.SaveSettingField(settings.PushNotificationsServerEnabled, false)
|
2020-07-17 11:41:49 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-07-22 07:41:40 +00:00
|
|
|
return api.service.messenger.StopPushNotificationsServer()
|
2020-07-14 14:07:19 +00:00
|
|
|
}
|
|
|
|
|
2020-07-22 07:41:40 +00:00
|
|
|
// PushNotification client endpoints
|
2020-07-14 14:07:19 +00:00
|
|
|
|
2020-07-30 08:24:30 +00:00
|
|
|
func (api *PublicAPI) RegisterForPushNotifications(ctx context.Context, deviceToken string, apnTopic string, tokenType protobuf.PushNotificationRegistration_TokenType) error {
|
|
|
|
return api.service.messenger.RegisterForPushNotifications(ctx, deviceToken, apnTopic, tokenType)
|
2020-07-14 14:07:19 +00:00
|
|
|
}
|
|
|
|
|
2020-07-30 13:37:32 +00:00
|
|
|
func (api *PublicAPI) UnregisterFromPushNotifications(ctx context.Context) error {
|
2020-07-15 12:43:15 +00:00
|
|
|
return api.service.messenger.UnregisterFromPushNotifications(ctx)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (api *PublicAPI) DisableSendingNotifications(ctx context.Context) error {
|
2022-03-23 18:47:00 +00:00
|
|
|
err := api.service.accountsDB.SaveSettingField(settings.SendPushNotifications, false)
|
2020-07-15 12:43:15 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return api.service.messenger.DisableSendingPushNotifications()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (api *PublicAPI) EnableSendingNotifications(ctx context.Context) error {
|
2022-03-23 18:47:00 +00:00
|
|
|
err := api.service.accountsDB.SaveSettingField(settings.SendPushNotifications, true)
|
2020-07-15 12:43:15 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return api.service.messenger.EnableSendingPushNotifications()
|
|
|
|
}
|
|
|
|
|
2020-07-17 11:41:49 +00:00
|
|
|
func (api *PublicAPI) EnablePushNotificationsFromContactsOnly(ctx context.Context) error {
|
2022-03-23 18:47:00 +00:00
|
|
|
err := api.service.accountsDB.SaveSettingField(settings.PushNotificationsFromContactsOnly, true)
|
2020-07-17 11:41:49 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return api.service.messenger.EnablePushNotificationsFromContactsOnly()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (api *PublicAPI) DisablePushNotificationsFromContactsOnly(ctx context.Context) error {
|
2022-03-23 18:47:00 +00:00
|
|
|
err := api.service.accountsDB.SaveSettingField(settings.PushNotificationsFromContactsOnly, false)
|
2020-07-17 11:41:49 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return api.service.messenger.DisablePushNotificationsFromContactsOnly()
|
|
|
|
}
|
|
|
|
|
2020-09-03 07:30:03 +00:00
|
|
|
func (api *PublicAPI) EnablePushNotificationsBlockMentions(ctx context.Context) error {
|
2022-03-23 18:47:00 +00:00
|
|
|
err := api.service.accountsDB.SaveSettingField(settings.PushNotificationsBlockMentions, true)
|
2020-09-03 07:30:03 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return api.service.messenger.EnablePushNotificationsBlockMentions()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (api *PublicAPI) DisablePushNotificationsBlockMentions(ctx context.Context) error {
|
2022-03-23 18:47:00 +00:00
|
|
|
err := api.service.accountsDB.SaveSettingField(settings.PushNotificationsBlockMentions, false)
|
2020-09-03 07:30:03 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return api.service.messenger.DisablePushNotificationsBlockMentions()
|
|
|
|
}
|
|
|
|
|
2020-07-22 07:41:40 +00:00
|
|
|
func (api *PublicAPI) AddPushNotificationsServer(ctx context.Context, publicKeyBytes types.HexBytes) error {
|
2020-07-14 14:07:19 +00:00
|
|
|
publicKey, err := crypto.UnmarshalPubkey(publicKeyBytes)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-08-20 09:05:39 +00:00
|
|
|
// this is coming from a user, so it has to be a custom server
|
|
|
|
return api.service.messenger.AddPushNotificationsServer(ctx, publicKey, pushnotificationclient.ServerTypeCustom)
|
2020-07-14 14:07:19 +00:00
|
|
|
}
|
|
|
|
|
2020-07-17 11:41:49 +00:00
|
|
|
func (api *PublicAPI) RemovePushNotificationServer(ctx context.Context, publicKeyBytes types.HexBytes) error {
|
|
|
|
publicKey, err := crypto.UnmarshalPubkey(publicKeyBytes)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return api.service.messenger.RemovePushNotificationServer(ctx, publicKey)
|
|
|
|
}
|
|
|
|
|
2020-08-20 09:05:39 +00:00
|
|
|
func (api *PublicAPI) GetPushNotificationsServers() ([]*pushnotificationclient.PushNotificationServer, error) {
|
|
|
|
return api.service.messenger.GetPushNotificationsServers()
|
2020-07-16 08:36:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (api *PublicAPI) RegisteredForPushNotifications() (bool, error) {
|
|
|
|
return api.service.messenger.RegisteredForPushNotifications()
|
|
|
|
}
|
|
|
|
|
2020-07-27 12:27:48 +00:00
|
|
|
// Emoji
|
|
|
|
|
|
|
|
func (api *PublicAPI) SendEmojiReaction(ctx context.Context, chatID, messageID string, emojiID protobuf.EmojiReaction_Type) (*protocol.MessengerResponse, error) {
|
|
|
|
return api.service.messenger.SendEmojiReaction(ctx, chatID, messageID, emojiID)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (api *PublicAPI) SendEmojiReactionRetraction(ctx context.Context, emojiReactionID string) (*protocol.MessengerResponse, error) {
|
|
|
|
return api.service.messenger.SendEmojiReactionRetraction(ctx, emojiReactionID)
|
|
|
|
}
|
|
|
|
|
2020-07-27 15:57:01 +00:00
|
|
|
func (api *PublicAPI) EmojiReactionsByChatID(chatID string, cursor string, limit int) ([]*protocol.EmojiReaction, error) {
|
|
|
|
return api.service.messenger.EmojiReactionsByChatID(chatID, cursor, limit)
|
|
|
|
}
|
|
|
|
|
2021-11-05 13:48:20 +00:00
|
|
|
func (api *PublicAPI) EmojiReactionsByChatIDMessageID(chatID string, messageID string) ([]*protocol.EmojiReaction, error) {
|
|
|
|
return api.service.messenger.EmojiReactionsByChatIDMessageID(chatID, messageID)
|
|
|
|
}
|
|
|
|
|
2020-10-27 17:35:28 +00:00
|
|
|
func (api *PublicAPI) GetLinkPreviewWhitelist() []urls.Site {
|
|
|
|
return urls.LinkPreviewWhitelist()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (api *PublicAPI) GetLinkPreviewData(link string) (previewData urls.LinkPreviewData, err error) {
|
|
|
|
return urls.GetLinkPreviewData(link)
|
|
|
|
}
|
|
|
|
|
URL unfurling (initial implementation) (#3471)
This is the initial implementation for the new URL unfurling requirements. The
most important one is that only the message sender will pay the privacy cost for
unfurling and extracting metadata from websites. Once the message is sent, the
unfurled data will be stored at the protocol level and receivers will just
profit and happily decode the metadata to render it.
Further development of this URL unfurling capability will be mostly guided by
issues created on clients. For the moment in status-mobile:
https://github.com/status-im/status-mobile/labels/url-preview
- https://github.com/status-im/status-mobile/issues/15918
- https://github.com/status-im/status-mobile/issues/15917
- https://github.com/status-im/status-mobile/issues/15910
- https://github.com/status-im/status-mobile/issues/15909
- https://github.com/status-im/status-mobile/issues/15908
- https://github.com/status-im/status-mobile/issues/15906
- https://github.com/status-im/status-mobile/issues/15905
### Terminology
In the code, I've tried to stick to the word "unfurl URL" to really mean the
process of extracting metadata from a website, sort of lower level. I use "link
preview" to mean a higher level structure which is enriched by unfurled data.
"link preview" is also how designers refer to it.
### User flows
1. Carol needs to see link previews while typing in the chat input field. Notice
from the diagram nothing is persisted and that status-go endpoints are
essentially stateless.
```
#+begin_src plantuml :results verbatim
Client->>Server: Call wakuext_getTextURLs
Server-->>Client: Normalized URLs
Client->>Client: Render cached unfurled URLs
Client->>Server: Unfurl non-cached URLs.\nCall wakuext_unfurlURLs
Server->>Website: Fetch metadata
Website-->>Server: Metadata (thumbnail URL, title, etc)
Server->>Website: Fetch thumbnail
Server->>Website: Fetch favicon
Website-->>Server: Favicon bytes
Website-->>Server: Thumbnail bytes
Server->>Server: Decode & process images
Server-->>Client: Unfurled data (thumbnail data URI, etc)
#+end_src
```
```
,------. ,------. ,-------.
|Client| |Server| |Website|
`--+---' `--+---' `---+---'
| Call wakuext_getTextURLs | |
| ---------------------------------------> |
| | |
| Normalized URLs | |
| <- - - - - - - - - - - - - - - - - - - - |
| | |
|----. | |
| | Render cached unfurled URLs | |
|<---' | |
| | |
| Unfurl non-cached URLs. | |
| Call wakuext_unfurlURLs | |
| ---------------------------------------> |
| | |
| | Fetch metadata |
| | ------------------------------------>
| | |
| | Metadata (thumbnail URL, title, etc)|
| | <- - - - - - - - - - - - - - - - - -
| | |
| | Fetch thumbnail |
| | ------------------------------------>
| | |
| | Fetch favicon |
| | ------------------------------------>
| | |
| | Favicon bytes |
| | <- - - - - - - - - - - - - - - - - -
| | |
| | Thumbnail bytes |
| | <- - - - - - - - - - - - - - - - - -
| | |
| |----. |
| | | Decode & process images |
| |<---' |
| | |
| Unfurled data (thumbnail data URI, etc)| |
| <- - - - - - - - - - - - - - - - - - - - |
,--+---. ,--+---. ,---+---.
|Client| |Server| |Website|
`------' `------' `-------'
```
2. Carol sends the text message with link previews in the RPC request
wakuext_sendChatMessages. status-go assumes the link previews are good
because it can't and shouldn't attempt to re-unfurl them.
```
#+begin_src plantuml :results verbatim
Client->>Server: Call wakuext_sendChatMessages
Server->>Server: Transform link previews to\nbe proto-marshalled
Server->DB: Write link previews serialized as JSON
Server-->>Client: Updated message response
#+end_src
```
```
,------. ,------. ,--.
|Client| |Server| |DB|
`--+---' `--+---' `+-'
| Call wakuext_sendChatMessages| |
| -----------------------------> |
| | |
| |----. |
| | | Transform link previews to |
| |<---' be proto-marshalled |
| | |
| | |
| | Write link previews serialized as JSON|
| | -------------------------------------->
| | |
| Updated message response | |
| <- - - - - - - - - - - - - - - |
,--+---. ,--+---. ,+-.
|Client| |Server| |DB|
`------' `------' `--'
```
3. The message was sent over waku and persisted locally in Carol's device. She
should now see the link previews in the chat history. There can be many link
previews shared by other chat members, therefore it is important to serve the
assets via the media server to avoid overloading the ReactNative bridge with
lots of big JSON payloads containing base64 encoded data URIs (maybe this
concern is meaningless for desktop). When a client is rendering messages with
link previews, they will have the field linkPreviews, and the thumbnail URL
will point to the local media server.
```
#+begin_src plantuml :results verbatim
Client->>Server: GET /link-preview/thumbnail (media server)
Server->>DB: Read from user_messages.unfurled_links
Server->Server: Unmarshal JSON
Server-->>Client: HTTP Content-Type: image/jpeg/etc
#+end_src
```
```
,------. ,------. ,--.
|Client| |Server| |DB|
`--+---' `--+---' `+-'
| GET /link-preview/thumbnail (media server)| |
| ------------------------------------------> |
| | |
| | Read from user_messages.unfurled_links|
| | -------------------------------------->
| | |
| |----. |
| | | Unmarshal JSON |
| |<---' |
| | |
| HTTP Content-Type: image/jpeg/etc | |
| <- - - - - - - - - - - - - - - - - - - - - |
,--+---. ,--+---. ,+-.
|Client| |Server| |DB|
`------' `------' `--'
```
### Some limitations of the current implementation
The following points will become separate issues in status-go that I'll work on
over the next couple weeks. In no order of importance:
- Improve how multiple links are fetched; retries on failure and testing how
unfurling behaves around the timeout limits (deterministically, not by making
real HTTP calls as I did). https://github.com/status-im/status-go/issues/3498
- Unfurl favicons and store them in the protobuf too.
- For this PR, I added unfurling support only for websites with OpenGraph
https://ogp.me/ meta tags. Other unfurlers will be implemented on demand. The
next one will probably be for oEmbed https://oembed.com/, the protocol
supported by YouTube, for example.
- Resize and/or compress thumbnails (and favicons). Often times, thumbnails are
huge for the purposes of link previews. There is already support for
compressing JPEGs in status-go, but I prefer to work with compression in a
separate PR because I'd like to also solve the problem for PNGs (probably
convert them to JPEGs, plus compress them). This would be a safe choice for
thumbnails, favicons not so much because transparency is desirable.
- Editing messages is not yet supported.
- I haven't coded any artificial limit on the number of previews or on the size
of the thumbnail payload. This will be done in a separate issue. I have heard
the ideal solution may be to split messages into smaller chunks of ~125 KiB
because of libp2p, but that might be too complicated at this stage of the
product (?).
- Link preview deletion.
- For the moment, OpenGraph metadata is extracted by requesting data for the
English language (and fallback to whatever is available). In the future, we'll
want to unfurl by respecting the user's local device language. Some websites,
like GoDaddy, are already localized based on the device's IP, but many aren't.
- The website's description text should be limited by a certain number of
characters, especially because it's outside our control. Exactly how much has
not been decided yet, so it'll be done separately.
- URL normalization can be tricky, so I implemented only the basics to help with
caching. For example, the url https://status.im and HTTPS://status.im are
considered identical. Also, a URL is considered valid for unfurling if its TLD
exists according to publicsuffix.EffectiveTLDPlusOne. This was essential,
otherwise the default Go url.Parse approach would consider many invalid URLs
valid, and thus the server would waste resources trying to unfurl the
unfurleable.
### Other requirements
- If the message is edited, the link previews should reflect the edited text,
not the original one. This has been aligned with the design team as well.
- If the website's thumbnail or the favicon can't be fetched, just ignore them.
The only mandatory piece of metadata is the website's title and URL.
- Link previews in clients should be generated in near real-time, that is, as
the user types, previews are updated. In mobile this performs very well, and
it's what other clients like WhatsApp, Telegram, and Facebook do.
### Decisions
- While the user typing in the input field, the client is constantly (debounced)
asking status-go to parse the text and extract normalized URLs and then the
client checks if they're already in its in-memory cache. If they are, no RPC
call is made. I chose this approach to achieve the best possible performance
in mobile and avoid the whole RPC overhead, since the chat experience is
already not smooth enough. The mobile client uses URLs as cache keys in a
hashmap, i.e. if the key is present, it means the preview is readily available
(naive, but good enough for now). This decision also gave me more flexibility
to find the best UX at this stage of the feature.
- Due to the requirement that users should be able to see independent loading
indicators for each link preview, when status-go can't unfurl a URL, it
doesn't return it in the response.
- As an initial implementation, I added the BLOB column unfurled_links to the
user_messages table. The preview data is then serialized as JSON before being
stored in this column. I felt that creating a separate table and the related
code for this initial PR would be inconvenient. Is that reasonable to you?
Once things stabilize I can create a proper table if we want to avoid this
kind of solution with serialized columns.
2023-05-18 18:43:06 +00:00
|
|
|
// GetTextURLs parses text and returns a deduplicated and (somewhat) normalized
|
|
|
|
// slice of URLs. The returned URLs can be used as cache keys by clients.
|
|
|
|
func (api *PublicAPI) GetTextURLs(text string) []string {
|
2023-08-21 19:31:32 +00:00
|
|
|
return protocol.GetURLs(text)
|
URL unfurling (initial implementation) (#3471)
This is the initial implementation for the new URL unfurling requirements. The
most important one is that only the message sender will pay the privacy cost for
unfurling and extracting metadata from websites. Once the message is sent, the
unfurled data will be stored at the protocol level and receivers will just
profit and happily decode the metadata to render it.
Further development of this URL unfurling capability will be mostly guided by
issues created on clients. For the moment in status-mobile:
https://github.com/status-im/status-mobile/labels/url-preview
- https://github.com/status-im/status-mobile/issues/15918
- https://github.com/status-im/status-mobile/issues/15917
- https://github.com/status-im/status-mobile/issues/15910
- https://github.com/status-im/status-mobile/issues/15909
- https://github.com/status-im/status-mobile/issues/15908
- https://github.com/status-im/status-mobile/issues/15906
- https://github.com/status-im/status-mobile/issues/15905
### Terminology
In the code, I've tried to stick to the word "unfurl URL" to really mean the
process of extracting metadata from a website, sort of lower level. I use "link
preview" to mean a higher level structure which is enriched by unfurled data.
"link preview" is also how designers refer to it.
### User flows
1. Carol needs to see link previews while typing in the chat input field. Notice
from the diagram nothing is persisted and that status-go endpoints are
essentially stateless.
```
#+begin_src plantuml :results verbatim
Client->>Server: Call wakuext_getTextURLs
Server-->>Client: Normalized URLs
Client->>Client: Render cached unfurled URLs
Client->>Server: Unfurl non-cached URLs.\nCall wakuext_unfurlURLs
Server->>Website: Fetch metadata
Website-->>Server: Metadata (thumbnail URL, title, etc)
Server->>Website: Fetch thumbnail
Server->>Website: Fetch favicon
Website-->>Server: Favicon bytes
Website-->>Server: Thumbnail bytes
Server->>Server: Decode & process images
Server-->>Client: Unfurled data (thumbnail data URI, etc)
#+end_src
```
```
,------. ,------. ,-------.
|Client| |Server| |Website|
`--+---' `--+---' `---+---'
| Call wakuext_getTextURLs | |
| ---------------------------------------> |
| | |
| Normalized URLs | |
| <- - - - - - - - - - - - - - - - - - - - |
| | |
|----. | |
| | Render cached unfurled URLs | |
|<---' | |
| | |
| Unfurl non-cached URLs. | |
| Call wakuext_unfurlURLs | |
| ---------------------------------------> |
| | |
| | Fetch metadata |
| | ------------------------------------>
| | |
| | Metadata (thumbnail URL, title, etc)|
| | <- - - - - - - - - - - - - - - - - -
| | |
| | Fetch thumbnail |
| | ------------------------------------>
| | |
| | Fetch favicon |
| | ------------------------------------>
| | |
| | Favicon bytes |
| | <- - - - - - - - - - - - - - - - - -
| | |
| | Thumbnail bytes |
| | <- - - - - - - - - - - - - - - - - -
| | |
| |----. |
| | | Decode & process images |
| |<---' |
| | |
| Unfurled data (thumbnail data URI, etc)| |
| <- - - - - - - - - - - - - - - - - - - - |
,--+---. ,--+---. ,---+---.
|Client| |Server| |Website|
`------' `------' `-------'
```
2. Carol sends the text message with link previews in the RPC request
wakuext_sendChatMessages. status-go assumes the link previews are good
because it can't and shouldn't attempt to re-unfurl them.
```
#+begin_src plantuml :results verbatim
Client->>Server: Call wakuext_sendChatMessages
Server->>Server: Transform link previews to\nbe proto-marshalled
Server->DB: Write link previews serialized as JSON
Server-->>Client: Updated message response
#+end_src
```
```
,------. ,------. ,--.
|Client| |Server| |DB|
`--+---' `--+---' `+-'
| Call wakuext_sendChatMessages| |
| -----------------------------> |
| | |
| |----. |
| | | Transform link previews to |
| |<---' be proto-marshalled |
| | |
| | |
| | Write link previews serialized as JSON|
| | -------------------------------------->
| | |
| Updated message response | |
| <- - - - - - - - - - - - - - - |
,--+---. ,--+---. ,+-.
|Client| |Server| |DB|
`------' `------' `--'
```
3. The message was sent over waku and persisted locally in Carol's device. She
should now see the link previews in the chat history. There can be many link
previews shared by other chat members, therefore it is important to serve the
assets via the media server to avoid overloading the ReactNative bridge with
lots of big JSON payloads containing base64 encoded data URIs (maybe this
concern is meaningless for desktop). When a client is rendering messages with
link previews, they will have the field linkPreviews, and the thumbnail URL
will point to the local media server.
```
#+begin_src plantuml :results verbatim
Client->>Server: GET /link-preview/thumbnail (media server)
Server->>DB: Read from user_messages.unfurled_links
Server->Server: Unmarshal JSON
Server-->>Client: HTTP Content-Type: image/jpeg/etc
#+end_src
```
```
,------. ,------. ,--.
|Client| |Server| |DB|
`--+---' `--+---' `+-'
| GET /link-preview/thumbnail (media server)| |
| ------------------------------------------> |
| | |
| | Read from user_messages.unfurled_links|
| | -------------------------------------->
| | |
| |----. |
| | | Unmarshal JSON |
| |<---' |
| | |
| HTTP Content-Type: image/jpeg/etc | |
| <- - - - - - - - - - - - - - - - - - - - - |
,--+---. ,--+---. ,+-.
|Client| |Server| |DB|
`------' `------' `--'
```
### Some limitations of the current implementation
The following points will become separate issues in status-go that I'll work on
over the next couple weeks. In no order of importance:
- Improve how multiple links are fetched; retries on failure and testing how
unfurling behaves around the timeout limits (deterministically, not by making
real HTTP calls as I did). https://github.com/status-im/status-go/issues/3498
- Unfurl favicons and store them in the protobuf too.
- For this PR, I added unfurling support only for websites with OpenGraph
https://ogp.me/ meta tags. Other unfurlers will be implemented on demand. The
next one will probably be for oEmbed https://oembed.com/, the protocol
supported by YouTube, for example.
- Resize and/or compress thumbnails (and favicons). Often times, thumbnails are
huge for the purposes of link previews. There is already support for
compressing JPEGs in status-go, but I prefer to work with compression in a
separate PR because I'd like to also solve the problem for PNGs (probably
convert them to JPEGs, plus compress them). This would be a safe choice for
thumbnails, favicons not so much because transparency is desirable.
- Editing messages is not yet supported.
- I haven't coded any artificial limit on the number of previews or on the size
of the thumbnail payload. This will be done in a separate issue. I have heard
the ideal solution may be to split messages into smaller chunks of ~125 KiB
because of libp2p, but that might be too complicated at this stage of the
product (?).
- Link preview deletion.
- For the moment, OpenGraph metadata is extracted by requesting data for the
English language (and fallback to whatever is available). In the future, we'll
want to unfurl by respecting the user's local device language. Some websites,
like GoDaddy, are already localized based on the device's IP, but many aren't.
- The website's description text should be limited by a certain number of
characters, especially because it's outside our control. Exactly how much has
not been decided yet, so it'll be done separately.
- URL normalization can be tricky, so I implemented only the basics to help with
caching. For example, the url https://status.im and HTTPS://status.im are
considered identical. Also, a URL is considered valid for unfurling if its TLD
exists according to publicsuffix.EffectiveTLDPlusOne. This was essential,
otherwise the default Go url.Parse approach would consider many invalid URLs
valid, and thus the server would waste resources trying to unfurl the
unfurleable.
### Other requirements
- If the message is edited, the link previews should reflect the edited text,
not the original one. This has been aligned with the design team as well.
- If the website's thumbnail or the favicon can't be fetched, just ignore them.
The only mandatory piece of metadata is the website's title and URL.
- Link previews in clients should be generated in near real-time, that is, as
the user types, previews are updated. In mobile this performs very well, and
it's what other clients like WhatsApp, Telegram, and Facebook do.
### Decisions
- While the user typing in the input field, the client is constantly (debounced)
asking status-go to parse the text and extract normalized URLs and then the
client checks if they're already in its in-memory cache. If they are, no RPC
call is made. I chose this approach to achieve the best possible performance
in mobile and avoid the whole RPC overhead, since the chat experience is
already not smooth enough. The mobile client uses URLs as cache keys in a
hashmap, i.e. if the key is present, it means the preview is readily available
(naive, but good enough for now). This decision also gave me more flexibility
to find the best UX at this stage of the feature.
- Due to the requirement that users should be able to see independent loading
indicators for each link preview, when status-go can't unfurl a URL, it
doesn't return it in the response.
- As an initial implementation, I added the BLOB column unfurled_links to the
user_messages table. The preview data is then serialized as JSON before being
stored in this column. I felt that creating a separate table and the related
code for this initial PR would be inconvenient. Is that reasonable to you?
Once things stabilize I can create a proper table if we want to avoid this
kind of solution with serialized columns.
2023-05-18 18:43:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// UnfurlURLs uses a best-effort approach to unfurl each URL. Failed URLs will
|
|
|
|
// be removed from the response.
|
|
|
|
//
|
|
|
|
// This endpoint expects the client to send URLs normalized by GetTextURLs.
|
2023-10-13 12:25:34 +00:00
|
|
|
func (api *PublicAPI) UnfurlURLs(urls []string) (protocol.UnfurlURLsResponse, error) {
|
2023-08-21 19:31:32 +00:00
|
|
|
return api.service.messenger.UnfurlURLs(nil, urls)
|
URL unfurling (initial implementation) (#3471)
This is the initial implementation for the new URL unfurling requirements. The
most important one is that only the message sender will pay the privacy cost for
unfurling and extracting metadata from websites. Once the message is sent, the
unfurled data will be stored at the protocol level and receivers will just
profit and happily decode the metadata to render it.
Further development of this URL unfurling capability will be mostly guided by
issues created on clients. For the moment in status-mobile:
https://github.com/status-im/status-mobile/labels/url-preview
- https://github.com/status-im/status-mobile/issues/15918
- https://github.com/status-im/status-mobile/issues/15917
- https://github.com/status-im/status-mobile/issues/15910
- https://github.com/status-im/status-mobile/issues/15909
- https://github.com/status-im/status-mobile/issues/15908
- https://github.com/status-im/status-mobile/issues/15906
- https://github.com/status-im/status-mobile/issues/15905
### Terminology
In the code, I've tried to stick to the word "unfurl URL" to really mean the
process of extracting metadata from a website, sort of lower level. I use "link
preview" to mean a higher level structure which is enriched by unfurled data.
"link preview" is also how designers refer to it.
### User flows
1. Carol needs to see link previews while typing in the chat input field. Notice
from the diagram nothing is persisted and that status-go endpoints are
essentially stateless.
```
#+begin_src plantuml :results verbatim
Client->>Server: Call wakuext_getTextURLs
Server-->>Client: Normalized URLs
Client->>Client: Render cached unfurled URLs
Client->>Server: Unfurl non-cached URLs.\nCall wakuext_unfurlURLs
Server->>Website: Fetch metadata
Website-->>Server: Metadata (thumbnail URL, title, etc)
Server->>Website: Fetch thumbnail
Server->>Website: Fetch favicon
Website-->>Server: Favicon bytes
Website-->>Server: Thumbnail bytes
Server->>Server: Decode & process images
Server-->>Client: Unfurled data (thumbnail data URI, etc)
#+end_src
```
```
,------. ,------. ,-------.
|Client| |Server| |Website|
`--+---' `--+---' `---+---'
| Call wakuext_getTextURLs | |
| ---------------------------------------> |
| | |
| Normalized URLs | |
| <- - - - - - - - - - - - - - - - - - - - |
| | |
|----. | |
| | Render cached unfurled URLs | |
|<---' | |
| | |
| Unfurl non-cached URLs. | |
| Call wakuext_unfurlURLs | |
| ---------------------------------------> |
| | |
| | Fetch metadata |
| | ------------------------------------>
| | |
| | Metadata (thumbnail URL, title, etc)|
| | <- - - - - - - - - - - - - - - - - -
| | |
| | Fetch thumbnail |
| | ------------------------------------>
| | |
| | Fetch favicon |
| | ------------------------------------>
| | |
| | Favicon bytes |
| | <- - - - - - - - - - - - - - - - - -
| | |
| | Thumbnail bytes |
| | <- - - - - - - - - - - - - - - - - -
| | |
| |----. |
| | | Decode & process images |
| |<---' |
| | |
| Unfurled data (thumbnail data URI, etc)| |
| <- - - - - - - - - - - - - - - - - - - - |
,--+---. ,--+---. ,---+---.
|Client| |Server| |Website|
`------' `------' `-------'
```
2. Carol sends the text message with link previews in the RPC request
wakuext_sendChatMessages. status-go assumes the link previews are good
because it can't and shouldn't attempt to re-unfurl them.
```
#+begin_src plantuml :results verbatim
Client->>Server: Call wakuext_sendChatMessages
Server->>Server: Transform link previews to\nbe proto-marshalled
Server->DB: Write link previews serialized as JSON
Server-->>Client: Updated message response
#+end_src
```
```
,------. ,------. ,--.
|Client| |Server| |DB|
`--+---' `--+---' `+-'
| Call wakuext_sendChatMessages| |
| -----------------------------> |
| | |
| |----. |
| | | Transform link previews to |
| |<---' be proto-marshalled |
| | |
| | |
| | Write link previews serialized as JSON|
| | -------------------------------------->
| | |
| Updated message response | |
| <- - - - - - - - - - - - - - - |
,--+---. ,--+---. ,+-.
|Client| |Server| |DB|
`------' `------' `--'
```
3. The message was sent over waku and persisted locally in Carol's device. She
should now see the link previews in the chat history. There can be many link
previews shared by other chat members, therefore it is important to serve the
assets via the media server to avoid overloading the ReactNative bridge with
lots of big JSON payloads containing base64 encoded data URIs (maybe this
concern is meaningless for desktop). When a client is rendering messages with
link previews, they will have the field linkPreviews, and the thumbnail URL
will point to the local media server.
```
#+begin_src plantuml :results verbatim
Client->>Server: GET /link-preview/thumbnail (media server)
Server->>DB: Read from user_messages.unfurled_links
Server->Server: Unmarshal JSON
Server-->>Client: HTTP Content-Type: image/jpeg/etc
#+end_src
```
```
,------. ,------. ,--.
|Client| |Server| |DB|
`--+---' `--+---' `+-'
| GET /link-preview/thumbnail (media server)| |
| ------------------------------------------> |
| | |
| | Read from user_messages.unfurled_links|
| | -------------------------------------->
| | |
| |----. |
| | | Unmarshal JSON |
| |<---' |
| | |
| HTTP Content-Type: image/jpeg/etc | |
| <- - - - - - - - - - - - - - - - - - - - - |
,--+---. ,--+---. ,+-.
|Client| |Server| |DB|
`------' `------' `--'
```
### Some limitations of the current implementation
The following points will become separate issues in status-go that I'll work on
over the next couple weeks. In no order of importance:
- Improve how multiple links are fetched; retries on failure and testing how
unfurling behaves around the timeout limits (deterministically, not by making
real HTTP calls as I did). https://github.com/status-im/status-go/issues/3498
- Unfurl favicons and store them in the protobuf too.
- For this PR, I added unfurling support only for websites with OpenGraph
https://ogp.me/ meta tags. Other unfurlers will be implemented on demand. The
next one will probably be for oEmbed https://oembed.com/, the protocol
supported by YouTube, for example.
- Resize and/or compress thumbnails (and favicons). Often times, thumbnails are
huge for the purposes of link previews. There is already support for
compressing JPEGs in status-go, but I prefer to work with compression in a
separate PR because I'd like to also solve the problem for PNGs (probably
convert them to JPEGs, plus compress them). This would be a safe choice for
thumbnails, favicons not so much because transparency is desirable.
- Editing messages is not yet supported.
- I haven't coded any artificial limit on the number of previews or on the size
of the thumbnail payload. This will be done in a separate issue. I have heard
the ideal solution may be to split messages into smaller chunks of ~125 KiB
because of libp2p, but that might be too complicated at this stage of the
product (?).
- Link preview deletion.
- For the moment, OpenGraph metadata is extracted by requesting data for the
English language (and fallback to whatever is available). In the future, we'll
want to unfurl by respecting the user's local device language. Some websites,
like GoDaddy, are already localized based on the device's IP, but many aren't.
- The website's description text should be limited by a certain number of
characters, especially because it's outside our control. Exactly how much has
not been decided yet, so it'll be done separately.
- URL normalization can be tricky, so I implemented only the basics to help with
caching. For example, the url https://status.im and HTTPS://status.im are
considered identical. Also, a URL is considered valid for unfurling if its TLD
exists according to publicsuffix.EffectiveTLDPlusOne. This was essential,
otherwise the default Go url.Parse approach would consider many invalid URLs
valid, and thus the server would waste resources trying to unfurl the
unfurleable.
### Other requirements
- If the message is edited, the link previews should reflect the edited text,
not the original one. This has been aligned with the design team as well.
- If the website's thumbnail or the favicon can't be fetched, just ignore them.
The only mandatory piece of metadata is the website's title and URL.
- Link previews in clients should be generated in near real-time, that is, as
the user types, previews are updated. In mobile this performs very well, and
it's what other clients like WhatsApp, Telegram, and Facebook do.
### Decisions
- While the user typing in the input field, the client is constantly (debounced)
asking status-go to parse the text and extract normalized URLs and then the
client checks if they're already in its in-memory cache. If they are, no RPC
call is made. I chose this approach to achieve the best possible performance
in mobile and avoid the whole RPC overhead, since the chat experience is
already not smooth enough. The mobile client uses URLs as cache keys in a
hashmap, i.e. if the key is present, it means the preview is readily available
(naive, but good enough for now). This decision also gave me more flexibility
to find the best UX at this stage of the feature.
- Due to the requirement that users should be able to see independent loading
indicators for each link preview, when status-go can't unfurl a URL, it
doesn't return it in the response.
- As an initial implementation, I added the BLOB column unfurled_links to the
user_messages table. The preview data is then serialized as JSON before being
stored in this column. I felt that creating a separate table and the related
code for this initial PR would be inconvenient. Is that reasonable to you?
Once things stabilize I can create a proper table if we want to avoid this
kind of solution with serialized columns.
2023-05-18 18:43:06 +00:00
|
|
|
}
|
|
|
|
|
2021-01-11 10:32:51 +00:00
|
|
|
func (api *PublicAPI) EnsVerified(pk, ensName string) error {
|
|
|
|
return api.service.messenger.ENSVerified(pk, ensName)
|
|
|
|
}
|
|
|
|
|
2023-10-12 19:21:49 +00:00
|
|
|
// DEPRECATED
|
2021-09-17 09:02:04 +00:00
|
|
|
func (api *PublicAPI) RequestCommunityInfoFromMailserver(communityID string) (*communities.Community, error) {
|
2023-10-12 19:21:49 +00:00
|
|
|
return api.service.messenger.RequestCommunityInfoFromMailserver(communityID, nil, true)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (api *PublicAPI) RequestCommunityInfoFromMailserverWithShard(communityID string, shard *common.Shard) (*communities.Community, error) {
|
|
|
|
return api.service.messenger.RequestCommunityInfoFromMailserver(communityID, shard, true)
|
2021-04-19 12:09:46 +00:00
|
|
|
}
|
|
|
|
|
2023-10-12 19:21:49 +00:00
|
|
|
// DEPRECATED
|
2022-02-14 21:49:14 +00:00
|
|
|
func (api *PublicAPI) RequestCommunityInfoFromMailserverAsync(communityID string) error {
|
2023-10-12 19:21:49 +00:00
|
|
|
return api.service.messenger.RequestCommunityInfoFromMailserverAsync(communityID, nil)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (api *PublicAPI) RequestCommunityInfoFromMailserverAsyncWithShard(communityID string, shard *common.Shard) error {
|
|
|
|
return api.service.messenger.RequestCommunityInfoFromMailserverAsync(communityID, shard)
|
2022-02-14 21:49:14 +00:00
|
|
|
}
|
|
|
|
|
2023-03-03 14:31:48 +00:00
|
|
|
func (api *PublicAPI) ActivityCenterNotifications(request protocol.ActivityCenterNotificationsRequest) (*protocol.ActivityCenterPaginationResponse, error) {
|
|
|
|
return api.service.messenger.ActivityCenterNotifications(request)
|
2021-04-07 12:57:14 +00:00
|
|
|
}
|
|
|
|
|
2023-03-03 14:31:48 +00:00
|
|
|
func (api *PublicAPI) ActivityCenterNotificationsCount(request protocol.ActivityCenterCountRequest) (*protocol.ActivityCenterCountResponse, error) {
|
|
|
|
return api.service.messenger.ActivityCenterNotificationsCount(request)
|
2023-01-20 12:45:32 +00:00
|
|
|
}
|
|
|
|
|
2023-02-17 10:08:08 +00:00
|
|
|
func (api *PublicAPI) HasUnseenActivityCenterNotifications() (bool, error) {
|
|
|
|
return api.service.messenger.HasUnseenActivityCenterNotifications()
|
2021-04-07 12:57:14 +00:00
|
|
|
}
|
|
|
|
|
2023-02-17 10:08:08 +00:00
|
|
|
func (api *PublicAPI) GetActivityCenterState() (*protocol.ActivityCenterState, error) {
|
|
|
|
return api.service.messenger.GetActivityCenterState()
|
2021-06-11 16:47:53 +00:00
|
|
|
}
|
|
|
|
|
2023-02-17 10:08:08 +00:00
|
|
|
func (api *PublicAPI) MarkAsSeenActivityCenterNotifications() (*protocol.MessengerResponse, error) {
|
|
|
|
return api.service.messenger.MarkAsSeenActivityCenterNotifications()
|
2021-09-24 10:57:15 +00:00
|
|
|
}
|
|
|
|
|
2023-02-17 10:08:08 +00:00
|
|
|
func (api *PublicAPI) MarkAllActivityCenterNotificationsRead(ctx context.Context) (*protocol.MessengerResponse, error) {
|
|
|
|
return api.service.messenger.MarkAllActivityCenterNotificationsRead(ctx)
|
2021-04-07 12:57:14 +00:00
|
|
|
}
|
|
|
|
|
2023-02-17 10:08:08 +00:00
|
|
|
func (api *PublicAPI) MarkActivityCenterNotificationsRead(ctx context.Context, ids []types.HexBytes) (*protocol.MessengerResponse, error) {
|
2023-06-10 02:00:17 +00:00
|
|
|
return api.service.messenger.MarkActivityCenterNotificationsRead(ctx, ids, 0, true)
|
2021-04-07 12:57:14 +00:00
|
|
|
}
|
|
|
|
|
2023-10-22 09:41:20 +00:00
|
|
|
func (api *PublicAPI) MarkActivityCenterNotificationsUnread(ctx context.Context, ids []types.HexBytes) (*protocol.MessengerResponse, error) {
|
|
|
|
m := api.service.messenger
|
|
|
|
updatedAt := m.GetCurrentTimeInMillis()
|
|
|
|
return m.MarkActivityCenterNotificationsUnread(ctx, ids, updatedAt, true)
|
2023-02-17 10:08:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (api *PublicAPI) AcceptActivityCenterNotifications(ctx context.Context, ids []types.HexBytes) (*protocol.MessengerResponse, error) {
|
2023-10-22 09:41:20 +00:00
|
|
|
m := api.service.messenger
|
|
|
|
updatedAt := m.GetCurrentTimeInMillis()
|
|
|
|
return api.service.messenger.AcceptActivityCenterNotifications(ctx, ids, updatedAt, true)
|
2021-04-07 12:57:14 +00:00
|
|
|
}
|
|
|
|
|
2021-12-02 14:23:02 +00:00
|
|
|
func (api *PublicAPI) DismissActivityCenterNotifications(ctx context.Context, ids []types.HexBytes) error {
|
2023-06-10 02:00:17 +00:00
|
|
|
_, err := api.service.messenger.DismissActivityCenterNotifications(ctx, ids, 0, true)
|
2021-12-02 14:23:02 +00:00
|
|
|
return err
|
2021-04-07 12:57:14 +00:00
|
|
|
}
|
|
|
|
|
2023-02-24 23:47:04 +00:00
|
|
|
func (api *PublicAPI) DeleteActivityCenterNotifications(ctx context.Context, ids []types.HexBytes) error {
|
2023-10-22 09:41:20 +00:00
|
|
|
m := api.service.messenger
|
|
|
|
updatedAt := m.GetCurrentTimeInMillis()
|
|
|
|
_, err := m.MarkActivityCenterNotificationsDeleted(ctx, ids, updatedAt, true)
|
|
|
|
return err
|
2023-02-24 23:47:04 +00:00
|
|
|
}
|
|
|
|
|
2023-06-07 13:24:49 +00:00
|
|
|
func (api *PublicAPI) RequestAllHistoricMessages(forceFetchingBackup bool) (*protocol.MessengerResponse, error) {
|
|
|
|
return api.service.messenger.RequestAllHistoricMessages(forceFetchingBackup)
|
2021-03-25 15:15:22 +00:00
|
|
|
}
|
|
|
|
|
2023-06-07 13:24:49 +00:00
|
|
|
func (api *PublicAPI) RequestAllHistoricMessagesWithRetries(forceFetchingBackup bool) (*protocol.MessengerResponse, error) {
|
|
|
|
return api.service.messenger.RequestAllHistoricMessagesWithRetries(forceFetchingBackup)
|
2022-01-31 10:33:56 +00:00
|
|
|
}
|
|
|
|
|
2022-01-12 16:02:01 +00:00
|
|
|
func (api *PublicAPI) DisconnectActiveMailserver() {
|
|
|
|
api.service.messenger.DisconnectActiveMailserver()
|
|
|
|
}
|
|
|
|
|
2020-01-20 20:56:06 +00:00
|
|
|
// Echo is a method for testing purposes.
|
|
|
|
func (api *PublicAPI) Echo(ctx context.Context, message string) (string, error) {
|
|
|
|
return message, nil
|
|
|
|
}
|
|
|
|
|
2021-03-25 15:15:22 +00:00
|
|
|
func (api *PublicAPI) FillGaps(chatID string, messageIDs []string) error {
|
|
|
|
return api.service.messenger.FillGaps(chatID, messageIDs)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (api *PublicAPI) SyncChatFromSyncedFrom(chatID string) (uint32, error) {
|
|
|
|
return api.service.messenger.SyncChatFromSyncedFrom(chatID)
|
|
|
|
}
|
|
|
|
|
2021-08-26 20:25:43 +00:00
|
|
|
// BloomFilter returns the current bloom filter bytes
|
|
|
|
func (api *PublicAPI) BloomFilter() string {
|
|
|
|
return hexutil.Encode(api.service.messenger.BloomFilter())
|
|
|
|
}
|
|
|
|
|
2021-11-22 13:40:14 +00:00
|
|
|
func (api *PublicAPI) StartDiscV5() error {
|
|
|
|
return api.service.messenger.StartDiscV5()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (api *PublicAPI) StopDiscV5() error {
|
|
|
|
return api.service.messenger.StopDiscV5()
|
|
|
|
}
|
|
|
|
|
2022-03-08 15:25:00 +00:00
|
|
|
func (api *PublicAPI) GetCommunitiesSettings() ([]communities.CommunitySettings, error) {
|
|
|
|
return api.service.messenger.GetCommunitiesSettings()
|
|
|
|
}
|
|
|
|
|
2022-03-21 14:18:36 +00:00
|
|
|
func (api *PublicAPI) EnableCommunityHistoryArchiveProtocol() error {
|
|
|
|
return api.service.messenger.EnableCommunityHistoryArchiveProtocol()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (api *PublicAPI) DisableCommunityHistoryArchiveProtocol() error {
|
|
|
|
return api.service.messenger.DisableCommunityHistoryArchiveProtocol()
|
|
|
|
}
|
|
|
|
|
2023-05-22 21:38:02 +00:00
|
|
|
func (api *PublicAPI) SubscribeToPubsubTopic(topic string, optPublicKey string) error {
|
|
|
|
var publicKey *ecdsa.PublicKey
|
|
|
|
if optPublicKey != "" {
|
|
|
|
keyBytes, err := hexutil.Decode(optPublicKey)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
publicKey, err = crypto.UnmarshalPubkey(keyBytes)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return api.service.messenger.SubscribeToPubsubTopic(topic, publicKey)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (api *PublicAPI) StorePubsubTopicKey(topic string, privKey string) error {
|
|
|
|
keyBytes, err := hexutil.Decode(privKey)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
p, err := crypto.ToECDSA(keyBytes)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return api.service.messenger.StorePubsubTopicKey(topic, p)
|
|
|
|
}
|
|
|
|
|
2021-09-10 17:06:06 +00:00
|
|
|
func (api *PublicAPI) AddStorePeer(address string) (string, error) {
|
2023-02-22 21:58:17 +00:00
|
|
|
peerID, err := api.service.messenger.AddStorePeer(address)
|
|
|
|
return string(peerID), err
|
2021-08-30 14:57:28 +00:00
|
|
|
}
|
|
|
|
|
2021-09-10 17:06:06 +00:00
|
|
|
func (api *PublicAPI) AddRelayPeer(address string) (string, error) {
|
2023-02-22 21:58:17 +00:00
|
|
|
peerID, err := api.service.messenger.AddRelayPeer(address)
|
|
|
|
return string(peerID), err
|
2021-08-30 14:57:28 +00:00
|
|
|
}
|
|
|
|
|
2021-09-10 17:06:06 +00:00
|
|
|
func (api *PublicAPI) DialPeer(address string) error {
|
|
|
|
return api.service.messenger.DialPeer(address)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (api *PublicAPI) DialPeerByID(peerID string) error {
|
|
|
|
return api.service.messenger.DialPeerByID(peerID)
|
|
|
|
}
|
|
|
|
|
2021-08-30 14:57:28 +00:00
|
|
|
func (api *PublicAPI) DropPeer(peerID string) error {
|
|
|
|
return api.service.messenger.DropPeer(peerID)
|
|
|
|
}
|
|
|
|
|
2022-11-24 15:00:44 +00:00
|
|
|
func (api *PublicAPI) Peers() map[string]types.WakuV2Peer {
|
2021-08-30 14:57:28 +00:00
|
|
|
return api.service.messenger.Peers()
|
|
|
|
}
|
|
|
|
|
2022-11-24 21:27:46 +00:00
|
|
|
func (api *PublicAPI) ListenAddresses() ([]string, error) {
|
|
|
|
return api.service.messenger.ListenAddresses()
|
|
|
|
}
|
|
|
|
|
2022-03-23 18:47:00 +00:00
|
|
|
func (api *PublicAPI) ChangeIdentityImageShowTo(showTo settings.ProfilePicturesShowToType) error {
|
|
|
|
err := api.service.accountsDB.SaveSettingField(settings.ProfilePicturesShowTo, showTo)
|
2021-10-07 15:02:01 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return api.service.messenger.PublishIdentityImage()
|
|
|
|
}
|
|
|
|
|
2021-10-11 15:39:52 +00:00
|
|
|
func (api *PublicAPI) BackupData() (uint64, error) {
|
|
|
|
return api.service.messenger.BackupData(context.Background())
|
|
|
|
}
|
|
|
|
|
2022-02-08 16:23:04 +00:00
|
|
|
func (api *PublicAPI) ImageServerURL() string {
|
|
|
|
return api.service.messenger.ImageServerURL()
|
|
|
|
}
|
|
|
|
|
2022-01-31 10:33:56 +00:00
|
|
|
func (api *PublicAPI) ToggleUseMailservers(value bool) error {
|
|
|
|
return api.service.messenger.ToggleUseMailservers(value)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (api *PublicAPI) SetPinnedMailservers(pinnedMailservers map[string]string) error {
|
|
|
|
return api.service.messenger.SetPinnedMailservers(pinnedMailservers)
|
|
|
|
}
|
|
|
|
|
feat: introduce messenger APIs to extract discord channels
As part of the new Discord <-> Status Community Import functionality,
we're adding an API that extracts all discord categories and channels
from a previously exported discord export file.
These APIs can be used in clients to show the user what categories and
channels will be imported later on.
There are two APIs:
1. `Messenger.ExtractDiscordCategoriesAndChannels(filesToimport
[]string) (*MessengerResponse, map[string]*discord.ImportError)`
This takes a list of exported discord export (JSON) files (typically one per
channel), reads them, and extracts the categories and channels into
dedicated data structures (`[]DiscordChannel` and `[]DiscordCategory`)
It also returns the oldest message timestamp found in all extracted
channels.
The API is synchronous and returns the extracted data as
a `*MessengerResponse`. This allows to make the API available
status-go's RPC interface.
The error case is a `map[string]*discord.ImportError` where each key
is a file path of a JSON file that we tried to extract data from, and
the value a `discord.ImportError` which holds an error message and an
error code, allowing for distinguishing between "critical" errors and
"non-critical" errors.
2. `Messenger.RequestExtractDiscordCategoriesAndChannels(filesToImport
[]string)`
This is the asynchronous counterpart to
`ExtractDiscordCategoriesAndChannels`. The reason this API has been
added is because discord servers can have a lot of message and
channel data, which causes `ExtractDiscordCategoriesAndChannels` to
block the thread for too long, making apps potentially feel like they
are stuck.
This API runs inside a go routine, eventually calls
`ExtractDiscordCategoriesAndChannels`, and then emits a newly
introduced `DiscordCategoriesAndChannelsExtractedSignal` that clients
can react to.
Failure of extraction has to be determined by the
`discord.ImportErrors` emitted by the signal.
**A note about exported discord history files**
We expect users to export their discord histories via the
[DiscordChatExporter](https://github.com/Tyrrrz/DiscordChatExporter/wiki/GUI%2C-CLI-and-Formats-explained#exportguild)
tool. The tool allows to export the data in different formats, such as
JSON, HTML and CSV.
We expect users to have their data exported as JSON.
Closes: https://github.com/status-im/status-desktop/issues/6690
2022-07-13 09:33:53 +00:00
|
|
|
func (api *PublicAPI) RequestExtractDiscordChannelsAndCategories(filesToImport []string) {
|
|
|
|
api.service.messenger.RequestExtractDiscordChannelsAndCategories(filesToImport)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (api *PublicAPI) ExtractDiscordChannelsAndCategories(filesToImport []string) (*protocol.MessengerResponse, map[string]*discord.ImportError) {
|
|
|
|
return api.service.messenger.ExtractDiscordChannelsAndCategories(filesToImport)
|
|
|
|
}
|
|
|
|
|
2023-10-25 16:32:21 +00:00
|
|
|
func (api *PublicAPI) RequestImportDiscordChannel(request *requests.ImportDiscordChannel) {
|
|
|
|
api.service.messenger.RequestImportDiscordChannel(request)
|
|
|
|
}
|
|
|
|
|
2022-09-29 11:50:23 +00:00
|
|
|
func (api *PublicAPI) RequestImportDiscordCommunity(request *requests.ImportDiscordCommunity) {
|
|
|
|
api.service.messenger.RequestImportDiscordCommunity(request)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (api *PublicAPI) RequestCancelDiscordCommunityImport(id string) {
|
|
|
|
api.service.messenger.MarkDiscordCommunityImportAsCancelled(id)
|
|
|
|
}
|
|
|
|
|
2023-10-25 16:32:21 +00:00
|
|
|
func (api *PublicAPI) RequestCancelDiscordChannelImport(discordChannelID string) {
|
|
|
|
api.service.messenger.MarkDiscordChannelImportAsCancelled(discordChannelID)
|
|
|
|
}
|
|
|
|
|
2023-03-14 15:34:35 +00:00
|
|
|
func (api *PublicAPI) BuildContact(request *requests.BuildContact) (*protocol.Contact, error) {
|
|
|
|
return api.service.messenger.BuildContact(request)
|
2023-01-20 18:51:36 +00:00
|
|
|
}
|
|
|
|
|
2023-07-07 13:03:37 +00:00
|
|
|
func (api *PublicAPI) GetCommunityTokens(communityID string) ([]*token.CommunityToken, error) {
|
2023-03-02 17:33:30 +00:00
|
|
|
return api.service.messenger.GetCommunityTokens(communityID)
|
2023-01-27 13:27:24 +00:00
|
|
|
}
|
|
|
|
|
2023-07-07 13:03:37 +00:00
|
|
|
func (api *PublicAPI) GetAllCommunityTokens() ([]*token.CommunityToken, error) {
|
2023-04-26 08:48:10 +00:00
|
|
|
return api.service.messenger.GetAllCommunityTokens()
|
|
|
|
}
|
|
|
|
|
2023-07-07 13:03:37 +00:00
|
|
|
func (api *PublicAPI) SaveCommunityToken(token *token.CommunityToken, croppedImage *images.CroppedImage) (*token.CommunityToken, error) {
|
!refactor: introduce `SaveCommunityToken()` and change `AddCommunityToken()`
**This is a breaking change!**
Prior to this commit we had `AddCommunityToken(token *communities,
croppedImage CroppedImage)` that we used to
1. add a `CommunityToken` to the user's database and
2. to create a `CommunityTokenMetadata` from it which is then added to
the community's `CommunityDescription` and published to its members
However, I've then discovered that we need to separate these two things,
such that we can deploy a community token, then add it to the database
only for tracking purposes, **then** add it to the community description
(and propagate to members) once we know that the deploy tx indeed went
through.
To implement this, this commit introduces a new API
`SaveCommunityToken(token *communities.CommunityToken, croppedImage
CroppedImage)` which adds the token to the database only and doesn't
touch the community description.
The `AddCommunityToken` API is then changed that it's exclusively used
for adding an already saved `CommunityToken` to the community
description so it can be published to members. Hence, the signature is
now `AddCommunityToken(communityID string, chainID int, address
string)`, which makes this a breaking change.
Clients that used `AddCommunityToken()` before now need to ensure that
they first call `SaveCommunityToken()` as `AddCommunityToken()` will
fail otherwise.
2023-07-25 11:35:17 +00:00
|
|
|
return api.service.messenger.SaveCommunityToken(token, croppedImage)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (api *PublicAPI) AddCommunityToken(communityID string, chainID int, address string) error {
|
|
|
|
return api.service.messenger.AddCommunityToken(communityID, chainID, address)
|
2023-01-27 13:27:24 +00:00
|
|
|
}
|
|
|
|
|
2023-07-07 13:03:37 +00:00
|
|
|
func (api *PublicAPI) UpdateCommunityTokenState(chainID int, contractAddress string, deployState token.DeployState) error {
|
2023-06-02 08:07:00 +00:00
|
|
|
return api.service.messenger.UpdateCommunityTokenState(chainID, contractAddress, deployState)
|
|
|
|
}
|
|
|
|
|
2023-07-18 08:33:45 +00:00
|
|
|
func (api *PublicAPI) UpdateCommunityTokenAddress(chainID int, oldContractAddress string, newContractAddress string) error {
|
|
|
|
return api.service.messenger.UpdateCommunityTokenAddress(chainID, oldContractAddress, newContractAddress)
|
|
|
|
}
|
|
|
|
|
2023-06-21 11:20:43 +00:00
|
|
|
func (api *PublicAPI) UpdateCommunityTokenSupply(chainID int, contractAddress string, supply *bigint.BigInt) error {
|
2023-06-02 08:07:00 +00:00
|
|
|
return api.service.messenger.UpdateCommunityTokenSupply(chainID, contractAddress, supply)
|
2023-01-27 13:27:24 +00:00
|
|
|
}
|
|
|
|
|
2023-07-24 13:04:11 +00:00
|
|
|
func (api *PublicAPI) RemoveCommunityToken(chainID int, contractAddress string) error {
|
|
|
|
return api.service.messenger.RemoveCommunityToken(chainID, contractAddress)
|
|
|
|
}
|
|
|
|
|
2023-03-08 17:47:09 +00:00
|
|
|
func (api *PublicAPI) ToggleCollapsedCommunityCategory(request *requests.ToggleCollapsedCommunityCategory) error {
|
|
|
|
return api.service.messenger.ToggleCollapsedCommunityCategory(request)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (api *PublicAPI) CollapsedCommunityCategories() ([]protocol.CollapsedCommunityCategory, error) {
|
|
|
|
return api.service.messenger.CollapsedCommunityCategories()
|
|
|
|
}
|
|
|
|
|
2023-04-25 12:00:17 +00:00
|
|
|
func (api *PublicAPI) CheckPermissionsToJoinCommunity(request *requests.CheckPermissionToJoinCommunity) (*communities.CheckPermissionToJoinResponse, error) {
|
|
|
|
return api.service.messenger.CheckPermissionsToJoinCommunity(request)
|
|
|
|
}
|
|
|
|
|
2023-06-12 15:17:37 +00:00
|
|
|
func (api *PublicAPI) CheckCommunityChannelPermissions(request *requests.CheckCommunityChannelPermissions) (*communities.CheckChannelPermissionsResponse, error) {
|
|
|
|
return api.service.messenger.CheckCommunityChannelPermissions(request)
|
|
|
|
}
|
|
|
|
|
2023-06-13 12:50:15 +00:00
|
|
|
func (api *PublicAPI) CheckAllCommunityChannelsPermissions(request *requests.CheckAllCommunityChannelsPermissions) (*communities.CheckAllChannelsPermissionsResponse, error) {
|
|
|
|
return api.service.messenger.CheckAllCommunityChannelsPermissions(request)
|
2023-07-04 13:48:52 +00:00
|
|
|
}
|
|
|
|
|
2023-08-01 13:08:57 +00:00
|
|
|
func (api *PublicAPI) CollectCommunityMetrics(request *requests.CommunityMetricsRequest) (*protocol.CommunityMetricsResponse, error) {
|
|
|
|
return api.service.messenger.CollectCommunityMetrics(request)
|
|
|
|
}
|
|
|
|
|
2023-07-04 13:48:52 +00:00
|
|
|
func (api *PublicAPI) ShareCommunityURLWithChatKey(communityID types.HexBytes) (string, error) {
|
|
|
|
return api.service.messenger.ShareCommunityURLWithChatKey(communityID)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (api *PublicAPI) ShareCommunityURLWithData(communityID types.HexBytes) (string, error) {
|
|
|
|
return api.service.messenger.ShareCommunityURLWithData(communityID)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (api *PublicAPI) ShareCommunityChannelURLWithChatKey(request *requests.CommunityChannelShareURL) (string, error) {
|
|
|
|
return api.service.messenger.ShareCommunityChannelURLWithChatKey(request)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (api *PublicAPI) ShareCommunityChannelURLWithData(request *requests.CommunityChannelShareURL) (string, error) {
|
|
|
|
return api.service.messenger.ShareCommunityChannelURLWithData(request)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (api *PublicAPI) ShareUserURLWithENS(pubKey string) (string, error) {
|
|
|
|
return api.service.messenger.ShareUserURLWithENS(pubKey)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (api *PublicAPI) ShareUserURLWithChatKey(pubKey string) (string, error) {
|
|
|
|
return api.service.messenger.ShareUserURLWithChatKey(pubKey)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (api *PublicAPI) ShareUserURLWithData(pubKey string) (string, error) {
|
|
|
|
return api.service.messenger.ShareUserURLWithData(pubKey)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (api *PublicAPI) ParseSharedURL(url string) (*protocol.URLDataResponse, error) {
|
|
|
|
return api.service.messenger.ParseSharedURL(url)
|
2023-06-13 12:50:15 +00:00
|
|
|
}
|
|
|
|
|
2022-10-14 08:50:36 +00:00
|
|
|
func (api *PublicAPI) Messenger() *protocol.Messenger {
|
|
|
|
return api.service.messenger
|
|
|
|
}
|
|
|
|
|
2023-05-15 07:21:41 +00:00
|
|
|
// ChatMentionReplaceWithPublicKey checks if the text contains mentions and replace mention with user public key.
|
|
|
|
// e.g. abc @alice -> abc 0x123
|
|
|
|
func (api *PublicAPI) ChatMentionReplaceWithPublicKey(chatID, text string) (string, error) {
|
|
|
|
return api.service.messenger.GetMentionsManager().ReplaceWithPublicKey(chatID, text)
|
|
|
|
}
|
|
|
|
|
|
|
|
// ChatMentionOnChangeText
|
|
|
|
// chatID: chat id
|
|
|
|
// text: the full text user input in the chat input field
|
|
|
|
// as performance consideration, we don't need to call this function each time after user input a character,
|
|
|
|
// say user input "abc", we don't need to call this function 3 times, instead,
|
|
|
|
// we can call this function 2 times as following:
|
|
|
|
// 1. user input "a", call this function with text "a"
|
|
|
|
// 2. user input "c", call this function with text "abc"
|
|
|
|
// whatever, we should ensure ChatMentionOnChangeText know(invoked) the latest full text.
|
|
|
|
// ChatMentionOnChangeText will maintain state of fulltext and diff between previous/latest full text internally.
|
2023-04-27 03:06:40 +00:00
|
|
|
func (api *PublicAPI) ChatMentionOnChangeText(chatID, text string) (*protocol.ChatMentionContext, error) {
|
|
|
|
return api.service.messenger.GetMentionsManager().OnChangeText(chatID, text)
|
2023-04-07 08:47:38 +00:00
|
|
|
}
|
|
|
|
|
2023-05-15 07:21:41 +00:00
|
|
|
// ChatMentionSelectMention select mention from mention suggestion list
|
|
|
|
func (api *PublicAPI) ChatMentionSelectMention(chatID, text, primaryName, publicKey string) (*protocol.ChatMentionContext, error) {
|
|
|
|
return api.service.messenger.GetMentionsManager().SelectMention(chatID, text, primaryName, publicKey)
|
2023-04-07 08:47:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (api *PublicAPI) ChatMentionClearMentions(chatID string) {
|
|
|
|
api.service.messenger.GetMentionsManager().ClearMentions(chatID)
|
|
|
|
}
|
|
|
|
|
2023-05-15 07:21:41 +00:00
|
|
|
// ChatMentionToInputField checks if the text contains mentions and replace mention with readable username.
|
|
|
|
// generally, this function is invoked before user editing a sent message.
|
2023-04-07 08:47:38 +00:00
|
|
|
func (api *PublicAPI) ChatMentionToInputField(chatID, text string) (*protocol.ChatMentionContext, error) {
|
|
|
|
return api.service.messenger.GetMentionsManager().ToInputField(chatID, text)
|
|
|
|
}
|
|
|
|
|
2023-06-22 06:54:58 +00:00
|
|
|
func (api *PublicAPI) GetCheckChannelPermissionResponses(parent context.Context, communityID types.HexBytes) (*communities.CheckAllChannelsPermissionsResponse, error) {
|
|
|
|
return api.service.messenger.GetCommunityCheckChannelPermissionResponses(communityID)
|
|
|
|
}
|
|
|
|
|
2023-08-03 00:24:11 +00:00
|
|
|
// CreateClosedCommunity used only for test purposes
|
|
|
|
func (api *PublicAPI) CreateClosedCommunity() (*protocol.MessengerResponse, error) {
|
|
|
|
return api.service.messenger.CreateClosedCommunity()
|
|
|
|
}
|
|
|
|
|
|
|
|
// CreateOpenCommunity used only for test purposes
|
|
|
|
func (api *PublicAPI) CreateOpenCommunity() (*protocol.MessengerResponse, error) {
|
|
|
|
return api.service.messenger.CreateOpenCommunity()
|
|
|
|
}
|
|
|
|
|
|
|
|
// CreateTokenGatedCommunity used only for test purposes
|
|
|
|
func (api *PublicAPI) CreateTokenGatedCommunity() (*protocol.MessengerResponse, error) {
|
|
|
|
return api.service.messenger.CreateTokenGatedCommunity()
|
|
|
|
}
|
|
|
|
|
2023-10-24 10:43:18 +00:00
|
|
|
// Set profile showcase preference for current user
|
|
|
|
func (api *PublicAPI) SetProfileShowcasePreferences(preferences protocol.ProfileShowcasePreferences) error {
|
|
|
|
return api.service.messenger.SetProfileShowcasePreferences(preferences)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get all profile showcase preferences for current user
|
|
|
|
func (api *PublicAPI) GetProfileShowcasePreferences() (*protocol.ProfileShowcasePreferences, error) {
|
|
|
|
return api.service.messenger.GetProfileShowcasePreferences()
|
|
|
|
}
|
|
|
|
|
2020-01-20 20:56:06 +00:00
|
|
|
// -----
|
|
|
|
// HELPER
|
|
|
|
// -----
|
|
|
|
|
|
|
|
// MakeMessagesRequestPayload makes a specific payload for MailServer
|
|
|
|
// to request historic messages.
|
|
|
|
// DEPRECATED
|
|
|
|
func MakeMessagesRequestPayload(r MessagesRequest) ([]byte, error) {
|
|
|
|
cursor, err := hex.DecodeString(r.Cursor)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("invalid cursor: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(cursor) > 0 && len(cursor) != mailserver.CursorLength {
|
|
|
|
return nil, fmt.Errorf("invalid cursor size: expected %d but got %d", mailserver.CursorLength, len(cursor))
|
|
|
|
}
|
|
|
|
|
|
|
|
payload := mailserver.MessagesRequestPayload{
|
2020-11-23 07:18:53 +00:00
|
|
|
Lower: r.From,
|
|
|
|
Upper: r.To,
|
|
|
|
// We need to pass bloom filter for
|
|
|
|
// backward compatibility
|
2020-01-20 20:56:06 +00:00
|
|
|
Bloom: createBloomFilter(r),
|
2020-11-23 07:18:53 +00:00
|
|
|
Topics: topicsToByteArray(r.Topics),
|
2020-01-20 20:56:06 +00:00
|
|
|
Limit: r.Limit,
|
|
|
|
Cursor: cursor,
|
|
|
|
// Client must tell the MailServer if it supports batch responses.
|
|
|
|
// This can be removed in the future.
|
|
|
|
Batch: true,
|
|
|
|
}
|
|
|
|
|
|
|
|
return rlp.EncodeToBytes(payload)
|
|
|
|
}
|
|
|
|
|
2020-11-23 07:18:53 +00:00
|
|
|
func topicsToByteArray(topics []types.TopicType) [][]byte {
|
|
|
|
|
|
|
|
var response [][]byte
|
|
|
|
for idx := range topics {
|
|
|
|
response = append(response, topics[idx][:])
|
|
|
|
}
|
|
|
|
|
|
|
|
return response
|
|
|
|
}
|
|
|
|
|
2020-01-20 20:56:06 +00:00
|
|
|
func createBloomFilter(r MessagesRequest) []byte {
|
|
|
|
if len(r.Topics) > 0 {
|
|
|
|
return topicsToBloom(r.Topics...)
|
|
|
|
}
|
|
|
|
return types.TopicToBloom(r.Topic)
|
|
|
|
}
|
|
|
|
|
|
|
|
func topicsToBloom(topics ...types.TopicType) []byte {
|
|
|
|
i := new(big.Int)
|
|
|
|
for _, topic := range topics {
|
|
|
|
bloom := types.TopicToBloom(topic)
|
|
|
|
i.Or(i, new(big.Int).SetBytes(bloom[:]))
|
|
|
|
}
|
|
|
|
|
|
|
|
combined := make([]byte, types.BloomFilterSize)
|
|
|
|
data := i.Bytes()
|
|
|
|
copy(combined[types.BloomFilterSize-len(data):], data[:])
|
|
|
|
|
|
|
|
return combined
|
|
|
|
}
|
|
|
|
|
|
|
|
// TopicsToBloom squashes all topics into a single bloom filter.
|
|
|
|
func TopicsToBloom(topics ...types.TopicType) []byte {
|
|
|
|
return topicsToBloom(topics...)
|
|
|
|
}
|