-
Notifications
You must be signed in to change notification settings - Fork 247
/
linkpreview_unfurler_status.go
172 lines (140 loc) · 5.06 KB
/
linkpreview_unfurler_status.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
package protocol
import (
"fmt"
"go.uber.org/zap"
"github.com/status-im/status-go/api/multiformat"
"github.com/status-im/status-go/images"
"github.com/status-im/status-go/protocol/common"
"github.com/status-im/status-go/protocol/common/shard"
"github.com/status-im/status-go/protocol/communities"
)
type StatusUnfurler struct {
m *Messenger
logger *zap.Logger
url string
}
func NewStatusUnfurler(URL string, messenger *Messenger, logger *zap.Logger) *StatusUnfurler {
return &StatusUnfurler{
m: messenger,
logger: logger.With(zap.String("url", URL)),
url: URL,
}
}
func updateThumbnail(image *images.IdentityImage, thumbnail *common.LinkPreviewThumbnail) error {
if image.IsEmpty() {
return nil
}
width, height, err := images.GetImageDimensions(image.Payload)
if err != nil {
return fmt.Errorf("failed to get image dimensions: %w", err)
}
dataURI, err := image.GetDataURI()
if err != nil {
return fmt.Errorf("failed to get data uri: %w", err)
}
thumbnail.Width = width
thumbnail.Height = height
thumbnail.DataURI = dataURI
return nil
}
func (u *StatusUnfurler) buildContactData(publicKey string) (*common.StatusContactLinkPreview, error) {
// contactID == "0x" + secp251k1 compressed public key as hex-encoded string
contactID, err := multiformat.DeserializeCompressedKey(publicKey)
if err != nil {
return nil, err
}
contact := u.m.GetContactByID(contactID)
// If no contact found locally, fetch it from waku
if contact == nil {
if contact, err = u.m.FetchContact(contactID, true); err != nil {
return nil, fmt.Errorf("failed to request contact info from mailserver for public key '%s': %w", publicKey, err)
}
}
c := &common.StatusContactLinkPreview{
PublicKey: contactID,
DisplayName: contact.DisplayName,
Description: contact.Bio,
}
if image, ok := contact.Images[images.SmallDimName]; ok {
if err = updateThumbnail(&image, &c.Icon); err != nil {
u.logger.Warn("unfurling status link: failed to set contact thumbnail", zap.Error(err))
}
}
return c, nil
}
func (u *StatusUnfurler) buildCommunityData(communityID string, shard *shard.Shard) (*communities.Community, *common.StatusCommunityLinkPreview, error) {
// This automatically checks the database
community, err := u.m.FetchCommunity(&FetchCommunityRequest{
CommunityKey: communityID,
Shard: shard,
TryDatabase: true,
WaitForResponse: true,
})
if err != nil {
return nil, nil, fmt.Errorf("failed to get community info for communityID '%s': %w", communityID, err)
}
if community == nil {
return community, nil, fmt.Errorf("community info fetched, but it is empty")
}
statusCommunityLinkPreviews, err := community.ToStatusLinkPreview()
if err != nil {
return nil, nil, fmt.Errorf("failed to get status community link preview for communityID '%s': %w", communityID, err)
}
return community, statusCommunityLinkPreviews, nil
}
func (u *StatusUnfurler) buildChannelData(channelUUID string, communityID string, communityShard *shard.Shard) (*common.StatusCommunityChannelLinkPreview, error) {
community, communityData, err := u.buildCommunityData(communityID, communityShard)
if err != nil {
return nil, fmt.Errorf("failed to build channel community data: %w", err)
}
channel, ok := community.Chats()[channelUUID]
if !ok {
return nil, fmt.Errorf("channel with channelID '%s' not found in community '%s'", channelUUID, communityID)
}
return &common.StatusCommunityChannelLinkPreview{
ChannelUUID: channelUUID,
Emoji: channel.Identity.Emoji,
DisplayName: channel.Identity.DisplayName,
Description: channel.Identity.Description,
Color: channel.Identity.Color,
Community: communityData,
}, nil
}
func (u *StatusUnfurler) Unfurl() (*common.StatusLinkPreview, error) {
preview := new(common.StatusLinkPreview)
preview.URL = u.url
resp, err := ParseSharedURL(u.url)
if err != nil {
return nil, fmt.Errorf("failed to parse shared url: %w", err)
}
// If a URL has been successfully parsed,
// any further errors should not be returned, only logged.
if resp.Contact != nil {
preview.Contact, err = u.buildContactData(resp.Contact.PublicKey)
if err != nil {
return nil, fmt.Errorf("error when building contact data: %w", err)
}
return preview, nil
}
// NOTE: Currently channel data comes together with community data,
// both `Community` and `Channel` fields will be present.
// So we check for Channel first, then Community.
if resp.Channel != nil {
if resp.Community == nil {
return preview, fmt.Errorf("channel community can't be empty")
}
preview.Channel, err = u.buildChannelData(resp.Channel.ChannelUUID, resp.Community.CommunityID, resp.Shard)
if err != nil {
return nil, fmt.Errorf("error when building channel data: %w", err)
}
return preview, nil
}
if resp.Community != nil {
_, preview.Community, err = u.buildCommunityData(resp.Community.CommunityID, resp.Shard)
if err != nil {
return nil, fmt.Errorf("error when building community data: %w", err)
}
return preview, nil
}
return nil, fmt.Errorf("shared url does not contain contact, community or channel data")
}