status-go/services/ext/api.go

1436 lines
56 KiB
Go
Raw Normal View History

package ext
import (
"context"
"encoding/hex"
"errors"
"fmt"
"math/big"
"time"
2022-01-17 03:42:11 +00:00
"github.com/status-im/status-go/services/browsers"
"github.com/status-im/status-go/services/wallet"
2022-01-17 03:42:11 +00:00
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rlp"
ethcommon "github.com/ethereum/go-ethereum/common"
2020-07-14 14:07:19 +00:00
"github.com/status-im/status-go/eth-node/crypto"
"github.com/status-im/status-go/eth-node/types"
"github.com/status-im/status-go/mailserver"
Sync Settings (#2478) * Sync Settings * Added valueHandlers and Database singleton Some issues remain, need a way to comparing incoming sql.DB to check if the connection is to a different file or not. Maybe make singleton instance per filename * Added functionality to check the sqlite filename * Refactor of Database.SaveSyncSettings to be used as a handler * Implemented inteface for setting sync protobuf factories * Refactored and completed adhoc send setting sync * Tidying up * Immutability refactor * Refactor settings into dedicated package * Breakout structs * Tidy up * Refactor of bulk settings sync * Bug fixes * Addressing feedback * Fix code dropped during rebase * Fix for db closed * Fix for node config related crashes * Provisional fix for type assertion - issue 2 * Adding robust type assertion checks * Partial fix for null literal db storage and json encoding * Fix for passively handling nil sql.DB, and checking if elem has len and if len is 0 * Added test for preferred name behaviour * Adding saved sync settings to MessengerResponse * Completed granular initial sync and clock from network on save * add Settings to isEmpty * Refactor of protobufs, partially done * Added syncSetting receiver handling, some bug fixes * Fix for sticker packs * Implement inactive flag on sync protobuf factory * Refactor of types and structs * Added SettingField.CanSync functionality * Addressing rebase artifact * Refactor of Setting SELECT queries * Refactor of string return queries * VERSION bump and migration index bump * Deactiveate Sync Settings * Deactiveated preferred_name and send_status_updates Co-authored-by: Andrea Maria Piana <andrea.maria.piana@gmail.com>
2022-03-23 18:47:00 +00:00
"github.com/status-im/status-go/multiaccounts/settings"
"github.com/status-im/status-go/protocol"
2020-09-01 13:27:01 +00:00
"github.com/status-im/status-go/protocol/common"
"github.com/status-im/status-go/protocol/communities"
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"
"github.com/status-im/status-go/protocol/encryption/multidevice"
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
"github.com/status-im/status-go/protocol/linkpreview"
"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"
"github.com/status-im/status-go/protocol/transport"
2020-10-27 17:35:28 +00:00
"github.com/status-im/status-go/protocol/urls"
"github.com/status-im/status-go/protocol/verification"
"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"`
// StoreCursor is used as starting point for WAKUV2 paginatedRequests
StoreCursor *StoreRequestCursor `json:"storeCursor"`
// 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"`
}
type StoreRequestCursor struct {
Digest []byte `json:"digest"`
ReceivedTime float64 `json:"receivedTime"`
}
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"`
}
func (api *PublicAPI) LeaveGroupChat(ctx Context, chatID string, remove bool) (*protocol.MessengerResponse, error) {
return api.service.messenger.LeaveGroupChat(ctx, chatID, remove)
}
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)
}
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) {
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)
}
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)
}
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)
}
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)
}
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
}
func (api *PublicAPI) ActiveChats(parent context.Context) []*protocol.Chat {
return api.service.messenger.ActiveChats()
}
func (api *PublicAPI) DeleteChat(parent context.Context, chatID string) error {
return api.service.messenger.DeleteChat(chatID)
}
func (api *PublicAPI) MuteCommunityCategory(request *requests.MuteCategory) error {
return api.service.messenger.SetMutePropertyOnChatsByCategory(request, true)
}
func (api *PublicAPI) UnmuteCommunityCategory(communityID string, categoryID string) error {
return api.service.messenger.SetMutePropertyOnChatsByCategory(&requests.MuteCategory{CommunityID: communityID, CategoryID: categoryID, MutedType: protocol.Unmuted}, false)
}
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)
}
func (api *PublicAPI) BlockContact(parent context.Context, contactID string) (*protocol.MessengerResponse, error) {
2021-11-05 15:11:10 +00:00
api.log.Info("blocking contact", "contact", contactID)
return api.service.messenger.BlockContact(contactID)
}
// 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`
func (api *PublicAPI) BlockContactDesktop(parent context.Context, contactID string) (*protocol.MessengerResponse, error) {
api.log.Info("blocking contact", "contact", contactID)
return api.service.messenger.BlockContactDesktop(contactID)
}
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)
}
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)
}
func (api *PublicAPI) RequestContactInfoFromMailserver(pubkey string) (*protocol.Contact, error) {
return api.service.messenger.RequestContactInfoFromMailserver(pubkey, true)
}
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)
}
// 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
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
func (api *PublicAPI) JoinedCommunities(parent context.Context) ([]*communities.Community, error) {
return api.service.messenger.JoinedCommunities()
}
// CommunityTags return the list of possible community tags
func (api *PublicAPI) CommunityTags(parent context.Context) map[string]string {
return requests.TagsEmojies
}
// 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()
}
// 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) {
return api.service.messenger.JoinCommunity(parent, communityID, false)
}
2020-12-17 14:36:09 +00:00
// LeaveCommunity leaves a commuity with the given ID
2021-01-11 10:32:51 +00:00
func (api *PublicAPI) LeaveCommunity(parent context.Context, communityID types.HexBytes) (*protocol.MessengerResponse, error) {
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) {
return api.service.messenger.CreateCommunity(request, true)
}
// 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-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) {
key, err := api.service.messenger.ExportCommunity(id)
if err != nil {
2021-01-11 10:32:51 +00:00
return nil, err
}
2021-01-11 10:32:51 +00:00
return crypto.FromECDSA(key), nil
}
2020-12-17 14:36:09 +00:00
// ImportCommunity imports a community with the given private key in hex
func (api *PublicAPI) ImportCommunity(ctx context.Context, hexPrivateKey string) (*protocol.MessengerResponse, error) {
// Strip the 0x from the beginning
privateKey, err := crypto.HexToECDSA(hexPrivateKey[2:])
if err != nil {
return nil, err
}
return api.service.messenger.ImportCommunity(ctx, privateKey)
}
// 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)
}
// EditCommunityChat edits a community chat in the given community
func (api *PublicAPI) EditCommunityChat(communityID types.HexBytes, chatID string, c *protobuf.CommunityChat) (*protocol.MessengerResponse, error) {
return api.service.messenger.EditCommunityChat(communityID, chatID, c)
}
// 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
// InviteUsersToCommunity invites the users with pks to the community with ID
func (api *PublicAPI) InviteUsersToCommunity(request *requests.InviteUsersToCommunity) (*protocol.MessengerResponse, error) {
return api.service.messenger.InviteUsersToCommunity(request)
}
// 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)
}
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)
}
// SetCommunityMuted sets the community's muted value
func (api *PublicAPI) SetCommunityMuted(communityID types.HexBytes, muted bool) error {
return api.service.messenger.SetMuted(communityID, muted)
}
// BanUserFromCommunity removes the user with pk from the community with ID
func (api *PublicAPI) BanUserFromCommunity(request *requests.BanUserFromCommunity) (*protocol.MessengerResponse, error) {
return api.service.messenger.BanUserFromCommunity(request)
}
// 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)
}
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)
}
func (api *PublicAPI) CreateCommunityTokenPermission(request *requests.CreateCommunityTokenPermission) (*protocol.MessengerResponse, error) {
return api.service.messenger.CreateCommunityTokenPermission(request)
}
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()
}
// 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)
}
// 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)
}
// 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
func (api *PublicAPI) CancelRequestToJoinCommunity(request *requests.CancelRequestToJoinCommunity) (*protocol.MessengerResponse, error) {
return api.service.messenger.CancelRequestToJoinCommunity(request)
}
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
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
}
// CheckAndClearPendingRequestToJoinCommunity to delete pending request to join a community which are older than 7 days
func (api *PublicAPI) CheckAndDeletePendingRequestToJoinCommunity() (*protocol.MessengerResponse, error) {
return api.service.messenger.CheckAndDeletePendingRequestToJoinCommunity(true)
}
// 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)
}
type ApplicationMessagesResponse struct {
2020-09-01 13:27:01 +00:00
Messages []*common.Message `json:"messages"`
Cursor string `json:"cursor"`
}
type MarkMessagSeenResponse struct {
Count uint64 `json:"count"`
CountWithMentions uint64 `json:"countWithMentions"`
}
type ApplicationPinnedMessagesResponse struct {
PinnedMessages []*common.PinnedMessage `json:"pinnedMessages"`
Cursor string `json:"cursor"`
}
type ApplicationStatusUpdatesResponse struct {
StatusUpdates []protocol.UserStatus `json:"statusUpdates"`
}
2023-01-05 16:11:57 +00:00
type ApplicationSwitcherCardsResponse struct {
SwitcherCards []protocol.SwitcherCard `json:"switcherCards"`
}
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
}
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)
}
func (api *PublicAPI) AllMessagesFromChatWhichMatchTerm(chatID, searchTerm string, caseSensitive bool) (*ApplicationMessagesResponse, error) {
messages, err := api.service.messenger.AllMessageByChatIDWhichMatchTerm(chatID, searchTerm, caseSensitive)
if err != nil {
return nil, err
}
return &ApplicationMessagesResponse{
Messages: messages,
}, nil
}
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
}
func (api *PublicAPI) ChatPinnedMessages(chatID, cursor string, limit int) (*ApplicationPinnedMessagesResponse, error) {
pinnedMessages, cursor, err := api.service.messenger.PinnedMessageByChatID(chatID, cursor, limit)
if err != nil {
return nil, err
}
return &ApplicationPinnedMessagesResponse{
PinnedMessages: pinnedMessages,
Cursor: cursor,
}, nil
}
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
}
func (api *PublicAPI) StartMessenger() (*protocol.MessengerResponse, error) {
return api.service.StartMessenger()
}
func (api *PublicAPI) SetUserStatus(ctx context.Context, status int, customText string) error {
return api.service.messenger.SetUserStatus(ctx, status, customText)
}
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)
}
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-02-26 12:31:48 +00:00
func (api *PublicAPI) MarkAllRead(chatID string) error {
return api.service.messenger.MarkAllRead(chatID)
}
func (api *PublicAPI) MarkAllReadInCommunity(communityID string) ([]string, error) {
return api.service.messenger.MarkAllReadInCommunity(communityID)
}
func (api *PublicAPI) SendContactRequest(ctx context.Context, request *requests.SendContactRequest) (*protocol.MessengerResponse, error) {
return api.service.messenger.SendContactRequest(ctx, request)
}
func (api *PublicAPI) AddContact(ctx context.Context, request *requests.AddContact) (*protocol.MessengerResponse, error) {
return api.service.messenger.AddContact(ctx, request)
}
func (api *PublicAPI) AcceptContactRequest(ctx context.Context, request *requests.AcceptContactRequest) (*protocol.MessengerResponse, error) {
return api.service.messenger.AcceptContactRequest(ctx, request)
}
func (api *PublicAPI) DeclineContactRequest(ctx context.Context, request *requests.DeclineContactRequest) (*protocol.MessengerResponse, error) {
return api.service.messenger.DeclineContactRequest(ctx, request)
}
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)
}
func (api *PublicAPI) RetractContactRequest(ctx context.Context, request *requests.RetractContactRequest) (*protocol.MessengerResponse, error) {
return api.service.messenger.RetractContactRequest(request)
}
func (api *PublicAPI) RemoveContact(ctx context.Context, pubKey string) (*protocol.MessengerResponse, error) {
return api.service.messenger.RemoveContact(ctx, pubKey)
}
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)
}
2021-03-25 15:15:22 +00:00
func (api *PublicAPI) DeactivateChat(request *requests.DeactivateChat) (*protocol.MessengerResponse, error) {
return api.service.messenger.DeactivateChat(request)
}
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) {
return api.service.messenger.SendChatMessage(ctx, message)
}
func (api *PublicAPI) ReSendChatMessage(ctx context.Context, messageID string) error {
return api.service.messenger.ReSendChatMessage(ctx, messageID)
}
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)
}
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)
}
func (api *PublicAPI) SendPinMessage(ctx context.Context, message *common.PinMessage) (*protocol.MessengerResponse, error) {
return api.service.messenger.SendPinMessage(ctx, message)
}
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 {
return api.service.messenger.SetDisplayName(displayName)
2022-02-17 15:13:10 +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)
}
func (api *PublicAPI) SendContactVerificationRequest(ctx context.Context, contactID string, challenge string) (*protocol.MessengerResponse, error) {
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)
}
func (api *PublicAPI) CancelVerificationRequest(ctx context.Context, id string) (*protocol.MessengerResponse, error) {
return api.service.messenger.CancelVerificationRequest(ctx, id)
}
func (api *PublicAPI) AcceptContactVerificationRequest(ctx context.Context, id string, response string) (*protocol.MessengerResponse, error) {
return api.service.messenger.AcceptContactVerificationRequest(ctx, id, response)
}
func (api *PublicAPI) DeclineContactVerificationRequest(ctx context.Context, id string) (*protocol.MessengerResponse, error) {
return api.service.messenger.DeclineContactVerificationRequest(ctx, id)
}
func (api *PublicAPI) VerifiedTrusted(ctx context.Context, request *requests.VerifiedTrusted) (*protocol.MessengerResponse, error) {
return api.service.messenger.VerifiedTrusted(ctx, request)
}
func (api *PublicAPI) VerifiedUntrustworthy(ctx context.Context, request *requests.VerifiedUntrustworthy) (*protocol.MessengerResponse, error) {
return api.service.messenger.VerifiedUntrustworthy(ctx, request)
}
func (api *PublicAPI) SendPairInstallation(ctx context.Context) (*protocol.MessengerResponse, error) {
return api.service.messenger.SendPairInstallation(ctx, nil)
}
func (api *PublicAPI) SyncDevices(ctx context.Context, name, picture string) error {
return api.service.messenger.SyncDevices(ctx, name, picture, nil)
}
func (api *PublicAPI) AddBookmark(ctx context.Context, bookmark browsers.Bookmark) error {
return api.service.messenger.AddBookmark(ctx, bookmark)
}
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)
}
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)
}
// 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
func (api *PublicAPI) DestroyWalletConnectSession(ctx context.Context, PeerID string) error {
return api.service.messenger.DestroyWalletConnectSession(PeerID)
}
// Saved Addresses APIs
func (api *PublicAPI) UpsertSavedAddress(ctx context.Context, sa wallet.SavedAddress) error {
return api.service.messenger.UpsertSavedAddress(ctx, sa)
}
func (api *PublicAPI) DeleteSavedAddress(ctx context.Context, address ethcommon.Address, ens string, isTest bool) error {
return api.service.messenger.DeleteSavedAddress(ctx, address, ens, isTest)
}
// PushNotifications server endpoints
2020-07-22 07:41:40 +00:00
func (api *PublicAPI) StartPushNotificationsServer() error {
Sync Settings (#2478) * Sync Settings * Added valueHandlers and Database singleton Some issues remain, need a way to comparing incoming sql.DB to check if the connection is to a different file or not. Maybe make singleton instance per filename * Added functionality to check the sqlite filename * Refactor of Database.SaveSyncSettings to be used as a handler * Implemented inteface for setting sync protobuf factories * Refactored and completed adhoc send setting sync * Tidying up * Immutability refactor * Refactor settings into dedicated package * Breakout structs * Tidy up * Refactor of bulk settings sync * Bug fixes * Addressing feedback * Fix code dropped during rebase * Fix for db closed * Fix for node config related crashes * Provisional fix for type assertion - issue 2 * Adding robust type assertion checks * Partial fix for null literal db storage and json encoding * Fix for passively handling nil sql.DB, and checking if elem has len and if len is 0 * Added test for preferred name behaviour * Adding saved sync settings to MessengerResponse * Completed granular initial sync and clock from network on save * add Settings to isEmpty * Refactor of protobufs, partially done * Added syncSetting receiver handling, some bug fixes * Fix for sticker packs * Implement inactive flag on sync protobuf factory * Refactor of types and structs * Added SettingField.CanSync functionality * Addressing rebase artifact * Refactor of Setting SELECT queries * Refactor of string return queries * VERSION bump and migration index bump * Deactiveate Sync Settings * Deactiveated preferred_name and send_status_updates Co-authored-by: Andrea Maria Piana <andrea.maria.piana@gmail.com>
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 {
Sync Settings (#2478) * Sync Settings * Added valueHandlers and Database singleton Some issues remain, need a way to comparing incoming sql.DB to check if the connection is to a different file or not. Maybe make singleton instance per filename * Added functionality to check the sqlite filename * Refactor of Database.SaveSyncSettings to be used as a handler * Implemented inteface for setting sync protobuf factories * Refactored and completed adhoc send setting sync * Tidying up * Immutability refactor * Refactor settings into dedicated package * Breakout structs * Tidy up * Refactor of bulk settings sync * Bug fixes * Addressing feedback * Fix code dropped during rebase * Fix for db closed * Fix for node config related crashes * Provisional fix for type assertion - issue 2 * Adding robust type assertion checks * Partial fix for null literal db storage and json encoding * Fix for passively handling nil sql.DB, and checking if elem has len and if len is 0 * Added test for preferred name behaviour * Adding saved sync settings to MessengerResponse * Completed granular initial sync and clock from network on save * add Settings to isEmpty * Refactor of protobufs, partially done * Added syncSetting receiver handling, some bug fixes * Fix for sticker packs * Implement inactive flag on sync protobuf factory * Refactor of types and structs * Added SettingField.CanSync functionality * Addressing rebase artifact * Refactor of Setting SELECT queries * Refactor of string return queries * VERSION bump and migration index bump * Deactiveate Sync Settings * Deactiveated preferred_name and send_status_updates Co-authored-by: Andrea Maria Piana <andrea.maria.piana@gmail.com>
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
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 {
Sync Settings (#2478) * Sync Settings * Added valueHandlers and Database singleton Some issues remain, need a way to comparing incoming sql.DB to check if the connection is to a different file or not. Maybe make singleton instance per filename * Added functionality to check the sqlite filename * Refactor of Database.SaveSyncSettings to be used as a handler * Implemented inteface for setting sync protobuf factories * Refactored and completed adhoc send setting sync * Tidying up * Immutability refactor * Refactor settings into dedicated package * Breakout structs * Tidy up * Refactor of bulk settings sync * Bug fixes * Addressing feedback * Fix code dropped during rebase * Fix for db closed * Fix for node config related crashes * Provisional fix for type assertion - issue 2 * Adding robust type assertion checks * Partial fix for null literal db storage and json encoding * Fix for passively handling nil sql.DB, and checking if elem has len and if len is 0 * Added test for preferred name behaviour * Adding saved sync settings to MessengerResponse * Completed granular initial sync and clock from network on save * add Settings to isEmpty * Refactor of protobufs, partially done * Added syncSetting receiver handling, some bug fixes * Fix for sticker packs * Implement inactive flag on sync protobuf factory * Refactor of types and structs * Added SettingField.CanSync functionality * Addressing rebase artifact * Refactor of Setting SELECT queries * Refactor of string return queries * VERSION bump and migration index bump * Deactiveate Sync Settings * Deactiveated preferred_name and send_status_updates Co-authored-by: Andrea Maria Piana <andrea.maria.piana@gmail.com>
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 {
Sync Settings (#2478) * Sync Settings * Added valueHandlers and Database singleton Some issues remain, need a way to comparing incoming sql.DB to check if the connection is to a different file or not. Maybe make singleton instance per filename * Added functionality to check the sqlite filename * Refactor of Database.SaveSyncSettings to be used as a handler * Implemented inteface for setting sync protobuf factories * Refactored and completed adhoc send setting sync * Tidying up * Immutability refactor * Refactor settings into dedicated package * Breakout structs * Tidy up * Refactor of bulk settings sync * Bug fixes * Addressing feedback * Fix code dropped during rebase * Fix for db closed * Fix for node config related crashes * Provisional fix for type assertion - issue 2 * Adding robust type assertion checks * Partial fix for null literal db storage and json encoding * Fix for passively handling nil sql.DB, and checking if elem has len and if len is 0 * Added test for preferred name behaviour * Adding saved sync settings to MessengerResponse * Completed granular initial sync and clock from network on save * add Settings to isEmpty * Refactor of protobufs, partially done * Added syncSetting receiver handling, some bug fixes * Fix for sticker packs * Implement inactive flag on sync protobuf factory * Refactor of types and structs * Added SettingField.CanSync functionality * Addressing rebase artifact * Refactor of Setting SELECT queries * Refactor of string return queries * VERSION bump and migration index bump * Deactiveate Sync Settings * Deactiveated preferred_name and send_status_updates Co-authored-by: Andrea Maria Piana <andrea.maria.piana@gmail.com>
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 {
Sync Settings (#2478) * Sync Settings * Added valueHandlers and Database singleton Some issues remain, need a way to comparing incoming sql.DB to check if the connection is to a different file or not. Maybe make singleton instance per filename * Added functionality to check the sqlite filename * Refactor of Database.SaveSyncSettings to be used as a handler * Implemented inteface for setting sync protobuf factories * Refactored and completed adhoc send setting sync * Tidying up * Immutability refactor * Refactor settings into dedicated package * Breakout structs * Tidy up * Refactor of bulk settings sync * Bug fixes * Addressing feedback * Fix code dropped during rebase * Fix for db closed * Fix for node config related crashes * Provisional fix for type assertion - issue 2 * Adding robust type assertion checks * Partial fix for null literal db storage and json encoding * Fix for passively handling nil sql.DB, and checking if elem has len and if len is 0 * Added test for preferred name behaviour * Adding saved sync settings to MessengerResponse * Completed granular initial sync and clock from network on save * add Settings to isEmpty * Refactor of protobufs, partially done * Added syncSetting receiver handling, some bug fixes * Fix for sticker packs * Implement inactive flag on sync protobuf factory * Refactor of types and structs * Added SettingField.CanSync functionality * Addressing rebase artifact * Refactor of Setting SELECT queries * Refactor of string return queries * VERSION bump and migration index bump * Deactiveate Sync Settings * Deactiveated preferred_name and send_status_updates Co-authored-by: Andrea Maria Piana <andrea.maria.piana@gmail.com>
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 {
Sync Settings (#2478) * Sync Settings * Added valueHandlers and Database singleton Some issues remain, need a way to comparing incoming sql.DB to check if the connection is to a different file or not. Maybe make singleton instance per filename * Added functionality to check the sqlite filename * Refactor of Database.SaveSyncSettings to be used as a handler * Implemented inteface for setting sync protobuf factories * Refactored and completed adhoc send setting sync * Tidying up * Immutability refactor * Refactor settings into dedicated package * Breakout structs * Tidy up * Refactor of bulk settings sync * Bug fixes * Addressing feedback * Fix code dropped during rebase * Fix for db closed * Fix for node config related crashes * Provisional fix for type assertion - issue 2 * Adding robust type assertion checks * Partial fix for null literal db storage and json encoding * Fix for passively handling nil sql.DB, and checking if elem has len and if len is 0 * Added test for preferred name behaviour * Adding saved sync settings to MessengerResponse * Completed granular initial sync and clock from network on save * add Settings to isEmpty * Refactor of protobufs, partially done * Added syncSetting receiver handling, some bug fixes * Fix for sticker packs * Implement inactive flag on sync protobuf factory * Refactor of types and structs * Added SettingField.CanSync functionality * Addressing rebase artifact * Refactor of Setting SELECT queries * Refactor of string return queries * VERSION bump and migration index bump * Deactiveate Sync Settings * Deactiveated preferred_name and send_status_updates Co-authored-by: Andrea Maria Piana <andrea.maria.piana@gmail.com>
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 {
Sync Settings (#2478) * Sync Settings * Added valueHandlers and Database singleton Some issues remain, need a way to comparing incoming sql.DB to check if the connection is to a different file or not. Maybe make singleton instance per filename * Added functionality to check the sqlite filename * Refactor of Database.SaveSyncSettings to be used as a handler * Implemented inteface for setting sync protobuf factories * Refactored and completed adhoc send setting sync * Tidying up * Immutability refactor * Refactor settings into dedicated package * Breakout structs * Tidy up * Refactor of bulk settings sync * Bug fixes * Addressing feedback * Fix code dropped during rebase * Fix for db closed * Fix for node config related crashes * Provisional fix for type assertion - issue 2 * Adding robust type assertion checks * Partial fix for null literal db storage and json encoding * Fix for passively handling nil sql.DB, and checking if elem has len and if len is 0 * Added test for preferred name behaviour * Adding saved sync settings to MessengerResponse * Completed granular initial sync and clock from network on save * add Settings to isEmpty * Refactor of protobufs, partially done * Added syncSetting receiver handling, some bug fixes * Fix for sticker packs * Implement inactive flag on sync protobuf factory * Refactor of types and structs * Added SettingField.CanSync functionality * Addressing rebase artifact * Refactor of Setting SELECT queries * Refactor of string return queries * VERSION bump and migration index bump * Deactiveate Sync Settings * Deactiveated preferred_name and send_status_updates Co-authored-by: Andrea Maria Piana <andrea.maria.piana@gmail.com>
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 {
Sync Settings (#2478) * Sync Settings * Added valueHandlers and Database singleton Some issues remain, need a way to comparing incoming sql.DB to check if the connection is to a different file or not. Maybe make singleton instance per filename * Added functionality to check the sqlite filename * Refactor of Database.SaveSyncSettings to be used as a handler * Implemented inteface for setting sync protobuf factories * Refactored and completed adhoc send setting sync * Tidying up * Immutability refactor * Refactor settings into dedicated package * Breakout structs * Tidy up * Refactor of bulk settings sync * Bug fixes * Addressing feedback * Fix code dropped during rebase * Fix for db closed * Fix for node config related crashes * Provisional fix for type assertion - issue 2 * Adding robust type assertion checks * Partial fix for null literal db storage and json encoding * Fix for passively handling nil sql.DB, and checking if elem has len and if len is 0 * Added test for preferred name behaviour * Adding saved sync settings to MessengerResponse * Completed granular initial sync and clock from network on save * add Settings to isEmpty * Refactor of protobufs, partially done * Added syncSetting receiver handling, some bug fixes * Fix for sticker packs * Implement inactive flag on sync protobuf factory * Refactor of types and structs * Added SettingField.CanSync functionality * Addressing rebase artifact * Refactor of Setting SELECT queries * Refactor of string return queries * VERSION bump and migration index bump * Deactiveate Sync Settings * Deactiveated preferred_name and send_status_updates Co-authored-by: Andrea Maria Piana <andrea.maria.piana@gmail.com>
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
}
// 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)
}
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()
}
// 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)
}
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 {
return linkpreview.GetURLs(text)
}
// 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.
func (api *PublicAPI) UnfurlURLs(urls []string) ([]common.LinkPreview, error) {
return api.service.messenger.UnfurlURLs(urls)
}
2021-01-11 10:32:51 +00:00
func (api *PublicAPI) EnsVerified(pk, ensName string) error {
return api.service.messenger.ENSVerified(pk, ensName)
}
2021-09-17 09:02:04 +00:00
func (api *PublicAPI) RequestCommunityInfoFromMailserver(communityID string) (*communities.Community, error) {
return api.service.messenger.RequestCommunityInfoFromMailserver(communityID, true)
}
func (api *PublicAPI) RequestCommunityInfoFromMailserverAsync(communityID string) error {
return api.service.messenger.RequestCommunityInfoFromMailserverAsync(communityID)
}
func (api *PublicAPI) ActivityCenterNotifications(request protocol.ActivityCenterNotificationsRequest) (*protocol.ActivityCenterPaginationResponse, error) {
return api.service.messenger.ActivityCenterNotifications(request)
}
func (api *PublicAPI) ActivityCenterNotificationsCount(request protocol.ActivityCenterCountRequest) (*protocol.ActivityCenterCountResponse, error) {
return api.service.messenger.ActivityCenterNotificationsCount(request)
}
func (api *PublicAPI) HasUnseenActivityCenterNotifications() (bool, error) {
return api.service.messenger.HasUnseenActivityCenterNotifications()
}
func (api *PublicAPI) GetActivityCenterState() (*protocol.ActivityCenterState, error) {
return api.service.messenger.GetActivityCenterState()
}
func (api *PublicAPI) MarkAsSeenActivityCenterNotifications() (*protocol.MessengerResponse, error) {
return api.service.messenger.MarkAsSeenActivityCenterNotifications()
}
func (api *PublicAPI) MarkAllActivityCenterNotificationsRead(ctx context.Context) (*protocol.MessengerResponse, error) {
return api.service.messenger.MarkAllActivityCenterNotificationsRead(ctx)
}
func (api *PublicAPI) MarkActivityCenterNotificationsRead(ctx context.Context, ids []types.HexBytes) (*protocol.MessengerResponse, error) {
return api.service.messenger.MarkActivityCenterNotificationsRead(ctx, ids, true)
}
func (api *PublicAPI) MarkActivityCenterNotificationsUnread(ids []types.HexBytes) (*protocol.MessengerResponse, error) {
return api.service.messenger.MarkActivityCenterNotificationsUnread(ids)
}
func (api *PublicAPI) AcceptActivityCenterNotifications(ctx context.Context, ids []types.HexBytes) (*protocol.MessengerResponse, error) {
return api.service.messenger.AcceptActivityCenterNotifications(ctx, ids, true)
}
func (api *PublicAPI) DismissActivityCenterNotifications(ctx context.Context, ids []types.HexBytes) error {
_, err := api.service.messenger.DismissActivityCenterNotifications(ctx, ids, true)
return err
}
func (api *PublicAPI) DeleteActivityCenterNotifications(ctx context.Context, ids []types.HexBytes) error {
return api.service.messenger.DeleteActivityCenterNotifications(ctx, ids, false)
}
2021-03-25 15:15:22 +00:00
func (api *PublicAPI) RequestAllHistoricMessages() (*protocol.MessengerResponse, error) {
return api.service.messenger.RequestAllHistoricMessages()
}
func (api *PublicAPI) RequestAllHistoricMessagesWithRetries() (*protocol.MessengerResponse, error) {
return api.service.messenger.RequestAllHistoricMessagesWithRetries()
}
2022-01-12 16:02:01 +00:00
func (api *PublicAPI) DisconnectActiveMailserver() {
api.service.messenger.DisconnectActiveMailserver()
}
// 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)
}
// BloomFilter returns the current bloom filter bytes
func (api *PublicAPI) BloomFilter() string {
return hexutil.Encode(api.service.messenger.BloomFilter())
}
func (api *PublicAPI) StartDiscV5() error {
return api.service.messenger.StartDiscV5()
}
func (api *PublicAPI) StopDiscV5() error {
return api.service.messenger.StopDiscV5()
}
func (api *PublicAPI) GetCommunitiesSettings() ([]communities.CommunitySettings, error) {
return api.service.messenger.GetCommunitiesSettings()
}
func (api *PublicAPI) EnableCommunityHistoryArchiveProtocol() error {
return api.service.messenger.EnableCommunityHistoryArchiveProtocol()
}
func (api *PublicAPI) DisableCommunityHistoryArchiveProtocol() error {
return api.service.messenger.DisableCommunityHistoryArchiveProtocol()
}
func (api *PublicAPI) AddStorePeer(address string) (string, error) {
peerID, err := api.service.messenger.AddStorePeer(address)
return string(peerID), err
}
func (api *PublicAPI) AddRelayPeer(address string) (string, error) {
peerID, err := api.service.messenger.AddRelayPeer(address)
return string(peerID), err
}
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)
}
func (api *PublicAPI) DropPeer(peerID string) error {
return api.service.messenger.DropPeer(peerID)
}
func (api *PublicAPI) Peers() map[string]types.WakuV2Peer {
return api.service.messenger.Peers()
}
2022-11-24 21:27:46 +00:00
func (api *PublicAPI) ListenAddresses() ([]string, error) {
return api.service.messenger.ListenAddresses()
}
Sync Settings (#2478) * Sync Settings * Added valueHandlers and Database singleton Some issues remain, need a way to comparing incoming sql.DB to check if the connection is to a different file or not. Maybe make singleton instance per filename * Added functionality to check the sqlite filename * Refactor of Database.SaveSyncSettings to be used as a handler * Implemented inteface for setting sync protobuf factories * Refactored and completed adhoc send setting sync * Tidying up * Immutability refactor * Refactor settings into dedicated package * Breakout structs * Tidy up * Refactor of bulk settings sync * Bug fixes * Addressing feedback * Fix code dropped during rebase * Fix for db closed * Fix for node config related crashes * Provisional fix for type assertion - issue 2 * Adding robust type assertion checks * Partial fix for null literal db storage and json encoding * Fix for passively handling nil sql.DB, and checking if elem has len and if len is 0 * Added test for preferred name behaviour * Adding saved sync settings to MessengerResponse * Completed granular initial sync and clock from network on save * add Settings to isEmpty * Refactor of protobufs, partially done * Added syncSetting receiver handling, some bug fixes * Fix for sticker packs * Implement inactive flag on sync protobuf factory * Refactor of types and structs * Added SettingField.CanSync functionality * Addressing rebase artifact * Refactor of Setting SELECT queries * Refactor of string return queries * VERSION bump and migration index bump * Deactiveate Sync Settings * Deactiveated preferred_name and send_status_updates Co-authored-by: Andrea Maria Piana <andrea.maria.piana@gmail.com>
2022-03-23 18:47:00 +00:00
func (api *PublicAPI) ChangeIdentityImageShowTo(showTo settings.ProfilePicturesShowToType) error {
err := api.service.accountsDB.SaveSettingField(settings.ProfilePicturesShowTo, showTo)
if err != nil {
return err
}
return api.service.messenger.PublishIdentityImage()
}
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()
}
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)
}
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-03-14 15:34:35 +00:00
func (api *PublicAPI) BuildContact(request *requests.BuildContact) (*protocol.Contact, error) {
return api.service.messenger.BuildContact(request)
}
func (api *PublicAPI) GetCommunityTokens(communityID string) ([]*communities.CommunityToken, error) {
return api.service.messenger.GetCommunityTokens(communityID)
}
func (api *PublicAPI) GetAllCommunityTokens() ([]*communities.CommunityToken, error) {
return api.service.messenger.GetAllCommunityTokens()
}
func (api *PublicAPI) AddCommunityToken(token *communities.CommunityToken) (*communities.CommunityToken, error) {
return api.service.messenger.AddCommunityToken(token)
}
func (api *PublicAPI) UpdateCommunityTokenState(contractAddress string, deployState communities.DeployState) error {
return api.service.messenger.UpdateCommunityTokenState(contractAddress, deployState)
}
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()
}
func (api *PublicAPI) CheckPermissionsToJoinCommunity(request *requests.CheckPermissionToJoinCommunity) (*communities.CheckPermissionToJoinResponse, error) {
return api.service.messenger.CheckPermissionsToJoinCommunity(request)
}
2022-10-14 08:50:36 +00:00
func (api *PublicAPI) Messenger() *protocol.Messenger {
return api.service.messenger
}
// 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.
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
}
// 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)
}
// 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)
}
// -----
// 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{
Lower: r.From,
Upper: r.To,
// We need to pass bloom filter for
// backward compatibility
Bloom: createBloomFilter(r),
Topics: topicsToByteArray(r.Topics),
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)
}
func topicsToByteArray(topics []types.TopicType) [][]byte {
var response [][]byte
for idx := range topics {
response = append(response, topics[idx][:])
}
return response
}
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...)
}