Add favicons to external link previews (#4788)
This commit is contained in:
parent
43bc6e4b83
commit
8c0e24dc26
Binary file not shown.
After Width: | Height: | Size: 2.7 KiB |
|
@ -11,13 +11,35 @@ import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
"regexp"
|
||||||
"time"
|
"time"
|
||||||
|
"unicode/utf8"
|
||||||
|
|
||||||
"golang.org/x/image/webp"
|
"golang.org/x/image/webp"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
htmlCommentRegex = regexp.MustCompile(`(?i)<!--([\\s\\S]*?)-->`)
|
||||||
|
svgRegex = regexp.MustCompile(`(?i)^\s*(?:<\?xml[^>]*>\s*)?(?:<!doctype svg[^>]*>\s*)?<svg[^>]*>[^*]*<\/svg>\s*$`)
|
||||||
|
)
|
||||||
|
|
||||||
|
// IsSVG returns true if the given buffer is a valid SVG image.
|
||||||
|
func IsSVG(buf []byte) bool {
|
||||||
|
var isBinary bool
|
||||||
|
if len(buf) < 24 {
|
||||||
|
isBinary = false
|
||||||
|
}
|
||||||
|
for i := 0; i < 14; i++ {
|
||||||
|
charCode, _ := utf8.DecodeRuneInString(string(buf[i]))
|
||||||
|
if charCode == 65533 || charCode <= 8 {
|
||||||
|
isBinary = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return !isBinary && svgRegex.Match(htmlCommentRegex.ReplaceAll(buf, []byte{}))
|
||||||
|
}
|
||||||
|
|
||||||
func Decode(fileName string) (image.Image, error) {
|
func Decode(fileName string) (image.Image, error) {
|
||||||
file, err := os.Open(fileName)
|
file, err := os.Open(fileName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -109,6 +131,8 @@ func GetType(buf []byte) ImageType {
|
||||||
return GIF
|
return GIF
|
||||||
case IsWebp(buf):
|
case IsWebp(buf):
|
||||||
return WEBP
|
return WEBP
|
||||||
|
case IsIco(buf):
|
||||||
|
return ICO
|
||||||
default:
|
default:
|
||||||
return UNKNOWN
|
return UNKNOWN
|
||||||
}
|
}
|
||||||
|
@ -124,6 +148,10 @@ func GetMimeType(buf []byte) (string, error) {
|
||||||
return "gif", nil
|
return "gif", nil
|
||||||
case IsWebp(buf):
|
case IsWebp(buf):
|
||||||
return "webp", nil
|
return "webp", nil
|
||||||
|
case IsIco(buf):
|
||||||
|
return "ico", nil
|
||||||
|
case IsSVG(buf):
|
||||||
|
return "svg", nil
|
||||||
default:
|
default:
|
||||||
return "", errors.New("image format not supported")
|
return "", errors.New("image format not supported")
|
||||||
}
|
}
|
||||||
|
@ -153,6 +181,12 @@ func IsWebp(buf []byte) bool {
|
||||||
buf[10] == 0x42 && buf[11] == 0x50
|
buf[10] == 0x42 && buf[11] == 0x50
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func IsIco(buf []byte) bool {
|
||||||
|
return len(buf) > 4 &&
|
||||||
|
buf[0] == 0 && buf[1] == 0 && buf[2] == 1 || buf[2] == 2 &&
|
||||||
|
buf[4] > 0
|
||||||
|
}
|
||||||
|
|
||||||
func GetImageDimensions(imgBytes []byte) (int, int, error) {
|
func GetImageDimensions(imgBytes []byte) (int, int, error) {
|
||||||
// Decode image bytes
|
// Decode image bytes
|
||||||
img, _, err := image.Decode(bytes.NewReader(imgBytes))
|
img, _, err := image.Decode(bytes.NewReader(imgBytes))
|
||||||
|
|
|
@ -110,3 +110,30 @@ func TestGetPayloadFromURI(t *testing.T) {
|
||||||
payload,
|
payload,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestIsSvg(t *testing.T) {
|
||||||
|
GoodSVG := []byte(`<svg width="300" height="130" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect width="200" height="100" x="10" y="10" rx="20" ry="20" fill="blue" />
|
||||||
|
Sorry, your browser does not support inline SVG.
|
||||||
|
</svg>`)
|
||||||
|
|
||||||
|
BadSVG := []byte(`<head>
|
||||||
|
<link rel="stylesheet" href="styles.css">
|
||||||
|
</head>`)
|
||||||
|
|
||||||
|
require.Equal(t, IsSVG(BadSVG), false)
|
||||||
|
require.Equal(t, IsSVG(GoodSVG), true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIsIco(t *testing.T) {
|
||||||
|
GoodICO, err := Asset("_assets/tests/wikipedia.ico")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
GoodPNG, err := Asset("_assets/tests/qr/defaultQR.png")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
require.Equal(t, IsIco(GoodICO), true)
|
||||||
|
require.Equal(t, IsIco(GoodPNG), false)
|
||||||
|
require.Equal(t, IsPng(GoodPNG), true)
|
||||||
|
require.Equal(t, IsPng(GoodICO), false)
|
||||||
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ const (
|
||||||
PNG
|
PNG
|
||||||
GIF
|
GIF
|
||||||
WEBP
|
WEBP
|
||||||
|
ICO
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -15,8 +15,8 @@ type MakeMediaServerURLType func(msgID string, previewURL string, imageID MediaS
|
||||||
type MakeMediaServerURLMessageWrapperType func(previewURL string, imageID MediaServerImageID) string
|
type MakeMediaServerURLMessageWrapperType func(previewURL string, imageID MediaServerImageID) string
|
||||||
|
|
||||||
type LinkPreviewThumbnail struct {
|
type LinkPreviewThumbnail struct {
|
||||||
Width int `json:"width"`
|
Width int `json:"width,omitempty"`
|
||||||
Height int `json:"height"`
|
Height int `json:"height,omitempty"`
|
||||||
// Non-empty when the thumbnail is available via the media server, i.e. after
|
// Non-empty when the thumbnail is available via the media server, i.e. after
|
||||||
// the chat message is sent.
|
// the chat message is sent.
|
||||||
URL string `json:"url,omitempty"`
|
URL string `json:"url,omitempty"`
|
||||||
|
@ -31,6 +31,7 @@ type LinkPreview struct {
|
||||||
Hostname string `json:"hostname"`
|
Hostname string `json:"hostname"`
|
||||||
Title string `json:"title,omitempty"`
|
Title string `json:"title,omitempty"`
|
||||||
Description string `json:"description,omitempty"`
|
Description string `json:"description,omitempty"`
|
||||||
|
Favicon LinkPreviewThumbnail `json:"favicon,omitempty"`
|
||||||
Thumbnail LinkPreviewThumbnail `json:"thumbnail,omitempty"`
|
Thumbnail LinkPreviewThumbnail `json:"thumbnail,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -288,12 +289,19 @@ func (m *Message) ConvertLinkPreviewsToProto() ([]*protobuf.UnfurledLink, error)
|
||||||
return nil, fmt.Errorf("invalid link preview, url='%s': %w", preview.URL, err)
|
return nil, fmt.Errorf("invalid link preview, url='%s': %w", preview.URL, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var payload []byte
|
var thumbnailPayload []byte
|
||||||
|
var faviconPayload []byte
|
||||||
var err error
|
var err error
|
||||||
if preview.Thumbnail.DataURI != "" {
|
if preview.Thumbnail.DataURI != "" {
|
||||||
payload, err = images.GetPayloadFromURI(preview.Thumbnail.DataURI)
|
thumbnailPayload, err = images.GetPayloadFromURI(preview.Thumbnail.DataURI)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not get data URI payload, url='%s': %w", preview.URL, err)
|
return nil, fmt.Errorf("could not get data URI payload for link preview thumbnail, url='%s': %w", preview.URL, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if preview.Favicon.DataURI != "" {
|
||||||
|
faviconPayload, err = images.GetPayloadFromURI(preview.Favicon.DataURI)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could not get data URI payload for link preview favicon, url='%s': %w", preview.URL, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -304,7 +312,8 @@ func (m *Message) ConvertLinkPreviewsToProto() ([]*protobuf.UnfurledLink, error)
|
||||||
Description: preview.Description,
|
Description: preview.Description,
|
||||||
ThumbnailWidth: uint32(preview.Thumbnail.Width),
|
ThumbnailWidth: uint32(preview.Thumbnail.Width),
|
||||||
ThumbnailHeight: uint32(preview.Thumbnail.Height),
|
ThumbnailHeight: uint32(preview.Thumbnail.Height),
|
||||||
ThumbnailPayload: payload,
|
ThumbnailPayload: thumbnailPayload,
|
||||||
|
FaviconPayload: faviconPayload,
|
||||||
}
|
}
|
||||||
unfurledLinks = append(unfurledLinks, ul)
|
unfurledLinks = append(unfurledLinks, ul)
|
||||||
}
|
}
|
||||||
|
@ -312,7 +321,8 @@ func (m *Message) ConvertLinkPreviewsToProto() ([]*protobuf.UnfurledLink, error)
|
||||||
return unfurledLinks, nil
|
return unfurledLinks, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Message) ConvertFromProtoToLinkPreviews(makeMediaServerURL func(msgID string, previewURL string) string) []LinkPreview {
|
func (m *Message) ConvertFromProtoToLinkPreviews(makeThumbnailMediaServerURL func(msgID string, previewURL string) string,
|
||||||
|
makeFaviconMediaServerURL func(msgID string, previewURL string) string) []LinkPreview {
|
||||||
var links []*protobuf.UnfurledLink
|
var links []*protobuf.UnfurledLink
|
||||||
|
|
||||||
if links = m.GetUnfurledLinks(); links == nil {
|
if links = m.GetUnfurledLinks(); links == nil {
|
||||||
|
@ -340,13 +350,20 @@ func (m *Message) ConvertFromProtoToLinkPreviews(makeMediaServerURL func(msgID s
|
||||||
}
|
}
|
||||||
mediaURL := ""
|
mediaURL := ""
|
||||||
if len(link.ThumbnailPayload) > 0 {
|
if len(link.ThumbnailPayload) > 0 {
|
||||||
mediaURL = makeMediaServerURL(m.ID, link.Url)
|
mediaURL = makeThumbnailMediaServerURL(m.ID, link.Url)
|
||||||
}
|
}
|
||||||
if link.GetThumbnailPayload() != nil {
|
if link.GetThumbnailPayload() != nil {
|
||||||
lp.Thumbnail.Width = int(link.ThumbnailWidth)
|
lp.Thumbnail.Width = int(link.ThumbnailWidth)
|
||||||
lp.Thumbnail.Height = int(link.ThumbnailHeight)
|
lp.Thumbnail.Height = int(link.ThumbnailHeight)
|
||||||
lp.Thumbnail.URL = mediaURL
|
lp.Thumbnail.URL = mediaURL
|
||||||
}
|
}
|
||||||
|
faviconMediaURL := ""
|
||||||
|
if len(link.FaviconPayload) > 0 {
|
||||||
|
faviconMediaURL = makeFaviconMediaServerURL(m.ID, link.Url)
|
||||||
|
}
|
||||||
|
if link.GetFaviconPayload() != nil {
|
||||||
|
lp.Favicon.URL = faviconMediaURL
|
||||||
|
}
|
||||||
previews = append(previews, lp)
|
previews = append(previews, lp)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -146,6 +146,9 @@ func TestConvertLinkPreviewsToProto(t *testing.T) {
|
||||||
URL: "http://localhost:9999",
|
URL: "http://localhost:9999",
|
||||||
DataURI: "data:image/png;base64,iVBORw0KGgoAAAANSUg=",
|
DataURI: "data:image/png;base64,iVBORw0KGgoAAAANSUg=",
|
||||||
},
|
},
|
||||||
|
Favicon: LinkPreviewThumbnail{
|
||||||
|
DataURI: "data:image/png;base64,iVBORw0KGgoAAAANSUg=",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -166,6 +169,10 @@ func TestConvertLinkPreviewsToProto(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, expectedPayload, l.ThumbnailPayload)
|
require.Equal(t, expectedPayload, l.ThumbnailPayload)
|
||||||
|
|
||||||
|
expectedFaviconPayload, err := base64.StdEncoding.DecodeString("iVBORw0KGgoAAAANSUg=")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, expectedFaviconPayload, l.FaviconPayload)
|
||||||
|
|
||||||
// Test any invalid link preview causes an early return.
|
// Test any invalid link preview causes an early return.
|
||||||
invalidPreview := validPreview
|
invalidPreview := validPreview
|
||||||
invalidPreview.Title = ""
|
invalidPreview.Title = ""
|
||||||
|
@ -178,7 +185,7 @@ func TestConvertLinkPreviewsToProto(t *testing.T) {
|
||||||
invalidPreview.Thumbnail.DataURI = "data:hello/png,iVBOR"
|
invalidPreview.Thumbnail.DataURI = "data:hello/png,iVBOR"
|
||||||
msg.LinkPreviews = []LinkPreview{invalidPreview}
|
msg.LinkPreviews = []LinkPreview{invalidPreview}
|
||||||
_, err = msg.ConvertLinkPreviewsToProto()
|
_, err = msg.ConvertLinkPreviewsToProto()
|
||||||
require.ErrorContains(t, err, "could not get data URI payload, url='https://github.com': wrong uri format")
|
require.ErrorContains(t, err, "could not get data URI payload for link preview thumbnail, url='https://github.com': wrong uri format")
|
||||||
|
|
||||||
// Test thumbnail is optional.
|
// Test thumbnail is optional.
|
||||||
somePreview := validPreview
|
somePreview := validPreview
|
||||||
|
@ -196,6 +203,8 @@ func TestConvertFromProtoToLinkPreviews(t *testing.T) {
|
||||||
|
|
||||||
thumbnailPayload, err := base64.StdEncoding.DecodeString("iVBORw0KGgoAAAANSUg=")
|
thumbnailPayload, err := base64.StdEncoding.DecodeString("iVBORw0KGgoAAAANSUg=")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
FaviconPayload, err := base64.StdEncoding.DecodeString("iVBORw0KGgoAAAANSUg=")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
l := &protobuf.UnfurledLink{
|
l := &protobuf.UnfurledLink{
|
||||||
Description: "GitHub is where people build software.",
|
Description: "GitHub is where people build software.",
|
||||||
|
@ -203,6 +212,7 @@ func TestConvertFromProtoToLinkPreviews(t *testing.T) {
|
||||||
Type: protobuf.UnfurledLink_LINK,
|
Type: protobuf.UnfurledLink_LINK,
|
||||||
Url: "https://github.com",
|
Url: "https://github.com",
|
||||||
ThumbnailPayload: thumbnailPayload,
|
ThumbnailPayload: thumbnailPayload,
|
||||||
|
FaviconPayload: FaviconPayload,
|
||||||
ThumbnailWidth: 100,
|
ThumbnailWidth: 100,
|
||||||
ThumbnailHeight: 200,
|
ThumbnailHeight: 200,
|
||||||
}
|
}
|
||||||
|
@ -217,7 +227,7 @@ func TestConvertFromProtoToLinkPreviews(t *testing.T) {
|
||||||
return "https://localhost:6666/" + msgID + "-" + linkURL
|
return "https://localhost:6666/" + msgID + "-" + linkURL
|
||||||
}
|
}
|
||||||
|
|
||||||
previews := msg.ConvertFromProtoToLinkPreviews(urlMaker)
|
previews := msg.ConvertFromProtoToLinkPreviews(urlMaker, urlMaker)
|
||||||
require.Len(t, previews, 1)
|
require.Len(t, previews, 1)
|
||||||
p := previews[0]
|
p := previews[0]
|
||||||
require.Equal(t, l.Type, p.Type)
|
require.Equal(t, l.Type, p.Type)
|
||||||
|
@ -230,11 +240,12 @@ func TestConvertFromProtoToLinkPreviews(t *testing.T) {
|
||||||
// fetched from the media server.
|
// fetched from the media server.
|
||||||
require.Equal(t, "", p.Thumbnail.DataURI)
|
require.Equal(t, "", p.Thumbnail.DataURI)
|
||||||
require.Equal(t, "https://localhost:6666/42-https://github.com", p.Thumbnail.URL)
|
require.Equal(t, "https://localhost:6666/42-https://github.com", p.Thumbnail.URL)
|
||||||
|
require.Equal(t, "https://localhost:6666/42-https://github.com", p.Favicon.URL)
|
||||||
|
|
||||||
// Test when the URL is not parseable by url.Parse.
|
// Test when the URL is not parseable by url.Parse.
|
||||||
l.Url = "postgres://user:abc{DEf1=ghi@example.com:5432/db?sslmode=require"
|
l.Url = "postgres://user:abc{DEf1=ghi@example.com:5432/db?sslmode=require"
|
||||||
msg.ChatMessage.UnfurledLinks = []*protobuf.UnfurledLink{l}
|
msg.ChatMessage.UnfurledLinks = []*protobuf.UnfurledLink{l}
|
||||||
previews = msg.ConvertFromProtoToLinkPreviews(urlMaker)
|
previews = msg.ConvertFromProtoToLinkPreviews(urlMaker, urlMaker)
|
||||||
require.Len(t, previews, 1)
|
require.Len(t, previews, 1)
|
||||||
p = previews[0]
|
p = previews[0]
|
||||||
require.Equal(t, l.Url, p.Hostname)
|
require.Equal(t, l.Url, p.Hostname)
|
||||||
|
@ -246,7 +257,7 @@ func TestConvertFromProtoToLinkPreviews(t *testing.T) {
|
||||||
Url: "https://github.com",
|
Url: "https://github.com",
|
||||||
}
|
}
|
||||||
msg.ChatMessage.UnfurledLinks = []*protobuf.UnfurledLink{l}
|
msg.ChatMessage.UnfurledLinks = []*protobuf.UnfurledLink{l}
|
||||||
previews = msg.ConvertFromProtoToLinkPreviews(urlMaker)
|
previews = msg.ConvertFromProtoToLinkPreviews(urlMaker, urlMaker)
|
||||||
require.Len(t, previews, 1)
|
require.Len(t, previews, 1)
|
||||||
p = previews[0]
|
p = previews[0]
|
||||||
require.Equal(t, 0, p.Thumbnail.Height)
|
require.Equal(t, 0, p.Thumbnail.Height)
|
||||||
|
|
|
@ -6,9 +6,11 @@ import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
neturl "net/url"
|
neturl "net/url"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/keighl/metabolize"
|
"github.com/keighl/metabolize"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
|
"golang.org/x/net/html"
|
||||||
|
|
||||||
"github.com/status-im/status-go/images"
|
"github.com/status-im/status-go/images"
|
||||||
"github.com/status-im/status-go/protocol/common"
|
"github.com/status-im/status-go/protocol/common"
|
||||||
|
@ -38,6 +40,43 @@ func NewOpenGraphUnfurler(URL *neturl.URL, logger *zap.Logger, httpClient *http.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetFavicon(bodyBytes []byte) string {
|
||||||
|
htmlTokens := html.NewTokenizer(bytes.NewBuffer(bodyBytes))
|
||||||
|
loop:
|
||||||
|
for {
|
||||||
|
tt := htmlTokens.Next()
|
||||||
|
switch tt {
|
||||||
|
case html.ErrorToken:
|
||||||
|
break loop
|
||||||
|
case html.StartTagToken:
|
||||||
|
t := htmlTokens.Token()
|
||||||
|
if t.Data != "link" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
isIcon := false
|
||||||
|
href := ""
|
||||||
|
for _, attr := range t.Attr {
|
||||||
|
k := attr.Key
|
||||||
|
v := attr.Val
|
||||||
|
if k == "rel" && (v == "icon" || v == "shortcut icon") {
|
||||||
|
isIcon = true
|
||||||
|
} else if k == "href" &&
|
||||||
|
(strings.Contains(v, ".ico") ||
|
||||||
|
strings.Contains(v, ".png") ||
|
||||||
|
strings.Contains(v, ".svg")) {
|
||||||
|
href = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if isIcon && href != "" {
|
||||||
|
return href
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
func (u *OpenGraphUnfurler) Unfurl() (*common.LinkPreview, error) {
|
func (u *OpenGraphUnfurler) Unfurl() (*common.LinkPreview, error) {
|
||||||
preview := newDefaultLinkPreview(u.url)
|
preview := newDefaultLinkPreview(u.url)
|
||||||
preview.Type = protobuf.UnfurledLink_LINK
|
preview.Type = protobuf.UnfurledLink_LINK
|
||||||
|
@ -58,6 +97,13 @@ func (u *OpenGraphUnfurler) Unfurl() (*common.LinkPreview, error) {
|
||||||
return preview, fmt.Errorf("failed to parse OpenGraph data")
|
return preview, fmt.Errorf("failed to parse OpenGraph data")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
faviconPath := GetFavicon(bodyBytes)
|
||||||
|
t, err := fetchImage(u.logger, u.httpClient, faviconPath, false)
|
||||||
|
if err != nil {
|
||||||
|
u.logger.Info("failed to fetch favicon", zap.String("url", u.url.String()), zap.Error(err))
|
||||||
|
} else {
|
||||||
|
preview.Favicon.DataURI = t.DataURI
|
||||||
|
}
|
||||||
// There are URLs like https://wikipedia.org/ that don't have an OpenGraph
|
// There are URLs like https://wikipedia.org/ that don't have an OpenGraph
|
||||||
// title tag, but article pages do. In the future, we can fallback to the
|
// title tag, but article pages do. In the future, we can fallback to the
|
||||||
// website's title by using the <title> tag.
|
// website's title by using the <title> tag.
|
||||||
|
@ -66,7 +112,7 @@ func (u *OpenGraphUnfurler) Unfurl() (*common.LinkPreview, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if ogMetadata.ThumbnailURL != "" {
|
if ogMetadata.ThumbnailURL != "" {
|
||||||
t, err := fetchThumbnail(u.logger, u.httpClient, ogMetadata.ThumbnailURL)
|
t, err := fetchImage(u.logger, u.httpClient, ogMetadata.ThumbnailURL, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Given we want to fetch thumbnails on a best-effort basis, if an error
|
// Given we want to fetch thumbnails on a best-effort basis, if an error
|
||||||
// happens we simply log it.
|
// happens we simply log it.
|
||||||
|
@ -78,24 +124,25 @@ func (u *OpenGraphUnfurler) Unfurl() (*common.LinkPreview, error) {
|
||||||
|
|
||||||
preview.Title = ogMetadata.Title
|
preview.Title = ogMetadata.Title
|
||||||
preview.Description = ogMetadata.Description
|
preview.Description = ogMetadata.Description
|
||||||
|
|
||||||
return preview, nil
|
return preview, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func fetchThumbnail(logger *zap.Logger, httpClient *http.Client, url string) (common.LinkPreviewThumbnail, error) {
|
func fetchImage(logger *zap.Logger, httpClient *http.Client, url string, getDimensions bool) (common.LinkPreviewThumbnail, error) {
|
||||||
var thumbnail common.LinkPreviewThumbnail
|
var thumbnail common.LinkPreviewThumbnail
|
||||||
|
|
||||||
imgBytes, err := fetchBody(logger, httpClient, url, nil)
|
imgBytes, err := fetchBody(logger, httpClient, url, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return thumbnail, fmt.Errorf("could not fetch thumbnail url='%s': %w", url, err)
|
return thumbnail, fmt.Errorf("could not fetch thumbnail url='%s': %w", url, err)
|
||||||
}
|
}
|
||||||
|
if getDimensions {
|
||||||
width, height, err := images.GetImageDimensions(imgBytes)
|
width, height, err := images.GetImageDimensions(imgBytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return thumbnail, fmt.Errorf("could not get image dimensions url='%s': %w", url, err)
|
return thumbnail, fmt.Errorf("could not get image dimensions url='%s': %w", url, err)
|
||||||
|
}
|
||||||
|
thumbnail.Width = width
|
||||||
|
thumbnail.Height = height
|
||||||
}
|
}
|
||||||
thumbnail.Width = width
|
|
||||||
thumbnail.Height = height
|
|
||||||
|
|
||||||
dataURI, err := images.GetPayloadDataURI(imgBytes)
|
dataURI, err := images.GetPayloadDataURI(imgBytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return thumbnail, fmt.Errorf("could not build data URI url='%s': %w", url, err)
|
return thumbnail, fmt.Errorf("could not build data URI url='%s': %w", url, err)
|
||||||
|
|
|
@ -4342,8 +4342,7 @@ func (m *Messenger) prepareMessage(msg *common.Message, s *server.MediaServer) e
|
||||||
if msg.ContentType == protobuf.ChatMessage_STICKER {
|
if msg.ContentType == protobuf.ChatMessage_STICKER {
|
||||||
msg.StickerLocalURL = s.MakeStickerURL(msg.GetSticker().Hash)
|
msg.StickerLocalURL = s.MakeStickerURL(msg.GetSticker().Hash)
|
||||||
}
|
}
|
||||||
|
msg.LinkPreviews = msg.ConvertFromProtoToLinkPreviews(s.MakeLinkPreviewThumbnailURL, s.MakeLinkPreviewFaviconURL)
|
||||||
msg.LinkPreviews = msg.ConvertFromProtoToLinkPreviews(s.MakeLinkPreviewThumbnailURL)
|
|
||||||
msg.StatusLinkPreviews = msg.ConvertFromProtoToStatusLinkPreviews(s.MakeStatusLinkPreviewThumbnailURL)
|
msg.StatusLinkPreviews = msg.ConvertFromProtoToStatusLinkPreviews(s.MakeStatusLinkPreviewThumbnailURL)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -198,6 +198,63 @@ func (s *MessengerLinkPreviewsTestSuite) readAsset(filename string) []byte {
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *MessengerLinkPreviewsTestSuite) Test_GetFavicon() {
|
||||||
|
goodHTMLPNG := []byte(
|
||||||
|
`
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<link rel="shortcut icon" href="https://www.somehost.com/favicon.png">
|
||||||
|
</head>
|
||||||
|
</html>`)
|
||||||
|
|
||||||
|
goodHTMLSVG := []byte(
|
||||||
|
`
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<link rel="shortcut icon" href="https://www.somehost.com/favicon.svg">
|
||||||
|
</head>
|
||||||
|
</html>`)
|
||||||
|
|
||||||
|
goodHTMLICO := []byte(
|
||||||
|
`
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<link rel="shortcut icon" href="https://www.somehost.com/favicon.ico">
|
||||||
|
</head>
|
||||||
|
</html>`)
|
||||||
|
|
||||||
|
badHTMLNoRelAttr := []byte(
|
||||||
|
`
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<link href="https://www.somehost.com/favicon.png">
|
||||||
|
</head>
|
||||||
|
</html>`)
|
||||||
|
|
||||||
|
GoodHTMLRelAttributeIcon := []byte(
|
||||||
|
`
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<link rel="icon" href="https://www.somehost.com/favicon.png">
|
||||||
|
</head>
|
||||||
|
</html>`)
|
||||||
|
|
||||||
|
faviconPath := GetFavicon(goodHTMLPNG)
|
||||||
|
s.Require().Equal("https://www.somehost.com/favicon.png", faviconPath)
|
||||||
|
|
||||||
|
faviconPath = GetFavicon(goodHTMLSVG)
|
||||||
|
s.Require().Equal("https://www.somehost.com/favicon.svg", faviconPath)
|
||||||
|
|
||||||
|
faviconPath = GetFavicon(goodHTMLICO)
|
||||||
|
s.Require().Equal("https://www.somehost.com/favicon.ico", faviconPath)
|
||||||
|
|
||||||
|
faviconPath = GetFavicon(GoodHTMLRelAttributeIcon)
|
||||||
|
s.Require().Equal("https://www.somehost.com/favicon.png", faviconPath)
|
||||||
|
|
||||||
|
faviconPath = GetFavicon(badHTMLNoRelAttr)
|
||||||
|
s.Require().Equal("", faviconPath)
|
||||||
|
}
|
||||||
|
|
||||||
func (s *MessengerLinkPreviewsTestSuite) Test_UnfurlURLs_YouTube() {
|
func (s *MessengerLinkPreviewsTestSuite) Test_UnfurlURLs_YouTube() {
|
||||||
u := "https://www.youtube.com/watch?v=lE4UXdJSJM4"
|
u := "https://www.youtube.com/watch?v=lE4UXdJSJM4"
|
||||||
thumbnailURL := "https://i.ytimg.com/vi/lE4UXdJSJM4/maxresdefault.jpg"
|
thumbnailURL := "https://i.ytimg.com/vi/lE4UXdJSJM4/maxresdefault.jpg"
|
||||||
|
@ -213,7 +270,7 @@ func (s *MessengerLinkPreviewsTestSuite) Test_UnfurlURLs_YouTube() {
|
||||||
DataURI: "data:image/webp;base64,UklGRiQAAABXRUJQVlA4IBgAAAAwAQCdASoBAAEAAQAaJaQAA3AA/vpMgAA",
|
DataURI: "data:image/webp;base64,UklGRiQAAABXRUJQVlA4IBgAAAAwAQCdASoBAAEAAQAaJaQAA3AA/vpMgAA",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
favicon := "https://www.youtube.com/s/desktop/87423d78/img/favicon.ico"
|
||||||
transport := StubTransport{}
|
transport := StubTransport{}
|
||||||
transport.AddURLMatcher(
|
transport.AddURLMatcher(
|
||||||
u,
|
u,
|
||||||
|
@ -223,9 +280,10 @@ func (s *MessengerLinkPreviewsTestSuite) Test_UnfurlURLs_YouTube() {
|
||||||
<meta property="og:title" content="%s">
|
<meta property="og:title" content="%s">
|
||||||
<meta property="og:description" content="%s">
|
<meta property="og:description" content="%s">
|
||||||
<meta property="og:image" content="%s">
|
<meta property="og:image" content="%s">
|
||||||
|
<link rel="shortcut icon" href="%s">
|
||||||
</head>
|
</head>
|
||||||
</html>
|
</html>
|
||||||
`, expected.Title, expected.Description, thumbnailURL)),
|
`, expected.Title, expected.Description, thumbnailURL, favicon)),
|
||||||
nil,
|
nil,
|
||||||
)
|
)
|
||||||
transport.AddURLMatcher(thumbnailURL, s.readAsset("1.jpg"), nil)
|
transport.AddURLMatcher(thumbnailURL, s.readAsset("1.jpg"), nil)
|
||||||
|
@ -245,6 +303,7 @@ func (s *MessengerLinkPreviewsTestSuite) Test_UnfurlURLs_YouTube() {
|
||||||
s.Require().Equal(expected.Thumbnail.Width, preview.Thumbnail.Width)
|
s.Require().Equal(expected.Thumbnail.Width, preview.Thumbnail.Width)
|
||||||
s.Require().Equal(expected.Thumbnail.Height, preview.Thumbnail.Height)
|
s.Require().Equal(expected.Thumbnail.Height, preview.Thumbnail.Height)
|
||||||
s.Require().Equal(expected.Thumbnail.URL, preview.Thumbnail.URL)
|
s.Require().Equal(expected.Thumbnail.URL, preview.Thumbnail.URL)
|
||||||
|
s.Require().NotNil(preview.Favicon)
|
||||||
s.assertContainsLongString(expected.Thumbnail.DataURI, preview.Thumbnail.DataURI, 100)
|
s.assertContainsLongString(expected.Thumbnail.DataURI, preview.Thumbnail.DataURI, 100)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2291,6 +2291,9 @@ func (s *MessengerSuite) TestSendMessageWithPreviews() {
|
||||||
Width: 100,
|
Width: 100,
|
||||||
Height: 200,
|
Height: 200,
|
||||||
},
|
},
|
||||||
|
Favicon: common.LinkPreviewThumbnail{
|
||||||
|
DataURI: "data:image/png;base64,iVBORw0KGgoAAAANSUg=",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
inputMsg.LinkPreviews = []common.LinkPreview{preview}
|
inputMsg.LinkPreviews = []common.LinkPreview{preview}
|
||||||
|
|
||||||
|
|
|
@ -1225,6 +1225,7 @@ type UnfurledLink struct {
|
||||||
ThumbnailWidth uint32 `protobuf:"varint,5,opt,name=thumbnail_width,json=thumbnailWidth,proto3" json:"thumbnail_width,omitempty"`
|
ThumbnailWidth uint32 `protobuf:"varint,5,opt,name=thumbnail_width,json=thumbnailWidth,proto3" json:"thumbnail_width,omitempty"`
|
||||||
ThumbnailHeight uint32 `protobuf:"varint,6,opt,name=thumbnail_height,json=thumbnailHeight,proto3" json:"thumbnail_height,omitempty"`
|
ThumbnailHeight uint32 `protobuf:"varint,6,opt,name=thumbnail_height,json=thumbnailHeight,proto3" json:"thumbnail_height,omitempty"`
|
||||||
Type UnfurledLink_LinkType `protobuf:"varint,7,opt,name=type,proto3,enum=protobuf.UnfurledLink_LinkType" json:"type,omitempty"`
|
Type UnfurledLink_LinkType `protobuf:"varint,7,opt,name=type,proto3,enum=protobuf.UnfurledLink_LinkType" json:"type,omitempty"`
|
||||||
|
FaviconPayload []byte `protobuf:"bytes,8,opt,name=favicon_payload,json=faviconPayload,proto3" json:"favicon_payload,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *UnfurledLink) Reset() {
|
func (x *UnfurledLink) Reset() {
|
||||||
|
@ -1308,6 +1309,13 @@ func (x *UnfurledLink) GetType() UnfurledLink_LinkType {
|
||||||
return UnfurledLink_LINK
|
return UnfurledLink_LINK
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *UnfurledLink) GetFaviconPayload() []byte {
|
||||||
|
if x != nil {
|
||||||
|
return x.FaviconPayload
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
type UnfurledStatusContactLink struct {
|
type UnfurledStatusContactLink struct {
|
||||||
state protoimpl.MessageState
|
state protoimpl.MessageState
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
|
@ -2137,7 +2145,7 @@ var file_chat_message_proto_rawDesc = []byte{
|
||||||
0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12,
|
0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12,
|
||||||
0x14, 0x0a, 0x05, 0x77, 0x69, 0x64, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05,
|
0x14, 0x0a, 0x05, 0x77, 0x69, 0x64, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05,
|
||||||
0x77, 0x69, 0x64, 0x74, 0x68, 0x12, 0x16, 0x0a, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18,
|
0x77, 0x69, 0x64, 0x74, 0x68, 0x12, 0x16, 0x0a, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18,
|
||||||
0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, 0xaf, 0x02,
|
0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, 0xd8, 0x02,
|
||||||
0x0a, 0x0c, 0x55, 0x6e, 0x66, 0x75, 0x72, 0x6c, 0x65, 0x64, 0x4c, 0x69, 0x6e, 0x6b, 0x12, 0x10,
|
0x0a, 0x0c, 0x55, 0x6e, 0x66, 0x75, 0x72, 0x6c, 0x65, 0x64, 0x4c, 0x69, 0x6e, 0x6b, 0x12, 0x10,
|
||||||
0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c,
|
0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c,
|
||||||
0x12, 0x14, 0x0a, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
|
0x12, 0x14, 0x0a, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||||
|
@ -2154,170 +2162,173 @@ var file_chat_message_proto_rawDesc = []byte{
|
||||||
0x61, 0x69, 0x6c, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x33, 0x0a, 0x04, 0x74, 0x79, 0x70,
|
0x61, 0x69, 0x6c, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x33, 0x0a, 0x04, 0x74, 0x79, 0x70,
|
||||||
0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
|
0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
|
||||||
0x75, 0x66, 0x2e, 0x55, 0x6e, 0x66, 0x75, 0x72, 0x6c, 0x65, 0x64, 0x4c, 0x69, 0x6e, 0x6b, 0x2e,
|
0x75, 0x66, 0x2e, 0x55, 0x6e, 0x66, 0x75, 0x72, 0x6c, 0x65, 0x64, 0x4c, 0x69, 0x6e, 0x6b, 0x2e,
|
||||||
0x4c, 0x69, 0x6e, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0x1f,
|
0x4c, 0x69, 0x6e, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x27,
|
||||||
0x0a, 0x08, 0x4c, 0x69, 0x6e, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x12, 0x08, 0x0a, 0x04, 0x4c, 0x49,
|
0x0a, 0x0f, 0x66, 0x61, 0x76, 0x69, 0x63, 0x6f, 0x6e, 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61,
|
||||||
0x4e, 0x4b, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x49, 0x4d, 0x41, 0x47, 0x45, 0x10, 0x01, 0x22,
|
0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0e, 0x66, 0x61, 0x76, 0x69, 0x63, 0x6f, 0x6e,
|
||||||
0xb4, 0x01, 0x0a, 0x19, 0x55, 0x6e, 0x66, 0x75, 0x72, 0x6c, 0x65, 0x64, 0x53, 0x74, 0x61, 0x74,
|
0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x22, 0x1f, 0x0a, 0x08, 0x4c, 0x69, 0x6e, 0x6b, 0x54,
|
||||||
0x75, 0x73, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x4c, 0x69, 0x6e, 0x6b, 0x12, 0x1d, 0x0a,
|
0x79, 0x70, 0x65, 0x12, 0x08, 0x0a, 0x04, 0x4c, 0x49, 0x4e, 0x4b, 0x10, 0x00, 0x12, 0x09, 0x0a,
|
||||||
0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28,
|
0x05, 0x49, 0x4d, 0x41, 0x47, 0x45, 0x10, 0x01, 0x22, 0xb4, 0x01, 0x0a, 0x19, 0x55, 0x6e, 0x66,
|
||||||
0x0c, 0x52, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x21, 0x0a, 0x0c,
|
0x75, 0x72, 0x6c, 0x65, 0x64, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x43, 0x6f, 0x6e, 0x74, 0x61,
|
||||||
0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01,
|
0x63, 0x74, 0x4c, 0x69, 0x6e, 0x6b, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63,
|
||||||
0x28, 0x09, 0x52, 0x0b, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12,
|
0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x70, 0x75, 0x62, 0x6c,
|
||||||
0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03,
|
0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79,
|
||||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f,
|
0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x69, 0x73,
|
||||||
0x6e, 0x12, 0x33, 0x0a, 0x04, 0x69, 0x63, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32,
|
0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63,
|
||||||
0x1f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x6e, 0x66, 0x75, 0x72,
|
0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64,
|
||||||
0x6c, 0x65, 0x64, 0x4c, 0x69, 0x6e, 0x6b, 0x54, 0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69, 0x6c,
|
0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x33, 0x0a, 0x04, 0x69, 0x63,
|
||||||
0x52, 0x04, 0x69, 0x63, 0x6f, 0x6e, 0x22, 0xae, 0x02, 0x0a, 0x1b, 0x55, 0x6e, 0x66, 0x75, 0x72,
|
0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
||||||
0x6c, 0x65, 0x64, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69,
|
0x62, 0x75, 0x66, 0x2e, 0x55, 0x6e, 0x66, 0x75, 0x72, 0x6c, 0x65, 0x64, 0x4c, 0x69, 0x6e, 0x6b,
|
||||||
0x74, 0x79, 0x4c, 0x69, 0x6e, 0x6b, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e,
|
0x54, 0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69, 0x6c, 0x52, 0x04, 0x69, 0x63, 0x6f, 0x6e, 0x22,
|
||||||
0x69, 0x74, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x63, 0x6f,
|
0xae, 0x02, 0x0a, 0x1b, 0x55, 0x6e, 0x66, 0x75, 0x72, 0x6c, 0x65, 0x64, 0x53, 0x74, 0x61, 0x74,
|
||||||
0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x74, 0x79, 0x49, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x69, 0x73,
|
0x75, 0x73, 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x74, 0x79, 0x4c, 0x69, 0x6e, 0x6b, 0x12,
|
||||||
0x70, 0x6c, 0x61, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
|
0x21, 0x0a, 0x0c, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x74, 0x79, 0x5f, 0x69, 0x64, 0x18,
|
||||||
0x0b, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b,
|
0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x74, 0x79,
|
||||||
0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28,
|
0x49, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x6e, 0x61,
|
||||||
0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x23,
|
0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61,
|
||||||
0x0a, 0x0d, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18,
|
0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70,
|
||||||
0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x43, 0x6f,
|
0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63,
|
||||||
0x75, 0x6e, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x18, 0x05, 0x20, 0x01,
|
0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x6d, 0x65, 0x6d, 0x62, 0x65,
|
||||||
0x28, 0x09, 0x52, 0x05, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x12, 0x33, 0x0a, 0x04, 0x69, 0x63, 0x6f,
|
0x72, 0x73, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c,
|
||||||
0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
|
0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x14, 0x0a, 0x05,
|
||||||
|
0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x63, 0x6f, 0x6c,
|
||||||
|
0x6f, 0x72, 0x12, 0x33, 0x0a, 0x04, 0x69, 0x63, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b,
|
||||||
|
0x32, 0x1f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x6e, 0x66, 0x75,
|
||||||
|
0x72, 0x6c, 0x65, 0x64, 0x4c, 0x69, 0x6e, 0x6b, 0x54, 0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69,
|
||||||
|
0x6c, 0x52, 0x04, 0x69, 0x63, 0x6f, 0x6e, 0x12, 0x37, 0x0a, 0x06, 0x62, 0x61, 0x6e, 0x6e, 0x65,
|
||||||
|
0x72, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
|
||||||
0x75, 0x66, 0x2e, 0x55, 0x6e, 0x66, 0x75, 0x72, 0x6c, 0x65, 0x64, 0x4c, 0x69, 0x6e, 0x6b, 0x54,
|
0x75, 0x66, 0x2e, 0x55, 0x6e, 0x66, 0x75, 0x72, 0x6c, 0x65, 0x64, 0x4c, 0x69, 0x6e, 0x6b, 0x54,
|
||||||
0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69, 0x6c, 0x52, 0x04, 0x69, 0x63, 0x6f, 0x6e, 0x12, 0x37,
|
0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69, 0x6c, 0x52, 0x06, 0x62, 0x61, 0x6e, 0x6e, 0x65, 0x72,
|
||||||
0x0a, 0x06, 0x62, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f,
|
0x22, 0xf4, 0x01, 0x0a, 0x19, 0x55, 0x6e, 0x66, 0x75, 0x72, 0x6c, 0x65, 0x64, 0x53, 0x74, 0x61,
|
||||||
|
0x74, 0x75, 0x73, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4c, 0x69, 0x6e, 0x6b, 0x12, 0x21,
|
||||||
|
0x0a, 0x0c, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x75, 0x75, 0x69, 0x64, 0x18, 0x01,
|
||||||
|
0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x55, 0x75, 0x69,
|
||||||
|
0x64, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x6d, 0x6f, 0x6a, 0x69, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
|
||||||
|
0x52, 0x05, 0x65, 0x6d, 0x6f, 0x6a, 0x69, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x69, 0x73, 0x70, 0x6c,
|
||||||
|
0x61, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64,
|
||||||
|
0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65,
|
||||||
|
0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||||
|
0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05,
|
||||||
|
0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x63, 0x6f, 0x6c,
|
||||||
|
0x6f, 0x72, 0x12, 0x43, 0x0a, 0x09, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x74, 0x79, 0x18,
|
||||||
|
0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
|
||||||
|
0x2e, 0x55, 0x6e, 0x66, 0x75, 0x72, 0x6c, 0x65, 0x64, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x43,
|
||||||
|
0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x74, 0x79, 0x4c, 0x69, 0x6e, 0x6b, 0x52, 0x09, 0x63, 0x6f,
|
||||||
|
0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x74, 0x79, 0x22, 0xfa, 0x01, 0x0a, 0x12, 0x55, 0x6e, 0x66, 0x75,
|
||||||
|
0x72, 0x6c, 0x65, 0x64, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x4c, 0x69, 0x6e, 0x6b, 0x12, 0x10,
|
||||||
|
0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c,
|
||||||
|
0x12, 0x3f, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28,
|
||||||
|
0x0b, 0x32, 0x23, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x6e, 0x66,
|
||||||
|
0x75, 0x72, 0x6c, 0x65, 0x64, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x43, 0x6f, 0x6e, 0x74, 0x61,
|
||||||
|
0x63, 0x74, 0x4c, 0x69, 0x6e, 0x6b, 0x48, 0x00, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63,
|
||||||
|
0x74, 0x12, 0x45, 0x0a, 0x09, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x74, 0x79, 0x18, 0x03,
|
||||||
|
0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,
|
||||||
|
0x55, 0x6e, 0x66, 0x75, 0x72, 0x6c, 0x65, 0x64, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x43, 0x6f,
|
||||||
|
0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x74, 0x79, 0x4c, 0x69, 0x6e, 0x6b, 0x48, 0x00, 0x52, 0x09, 0x63,
|
||||||
|
0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x74, 0x79, 0x12, 0x3f, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x6e,
|
||||||
|
0x6e, 0x65, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x70, 0x72, 0x6f, 0x74,
|
||||||
|
0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x6e, 0x66, 0x75, 0x72, 0x6c, 0x65, 0x64, 0x53, 0x74, 0x61,
|
||||||
|
0x74, 0x75, 0x73, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4c, 0x69, 0x6e, 0x6b, 0x48, 0x00,
|
||||||
|
0x52, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x09, 0x0a, 0x07, 0x70, 0x61, 0x79,
|
||||||
|
0x6c, 0x6f, 0x61, 0x64, 0x22, 0x67, 0x0a, 0x13, 0x55, 0x6e, 0x66, 0x75, 0x72, 0x6c, 0x65, 0x64,
|
||||||
|
0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x4c, 0x69, 0x6e, 0x6b, 0x73, 0x12, 0x50, 0x0a, 0x15, 0x75,
|
||||||
|
0x6e, 0x66, 0x75, 0x72, 0x6c, 0x65, 0x64, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5f, 0x6c,
|
||||||
|
0x69, 0x6e, 0x6b, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x70, 0x72, 0x6f,
|
||||||
|
0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x6e, 0x66, 0x75, 0x72, 0x6c, 0x65, 0x64, 0x53, 0x74,
|
||||||
|
0x61, 0x74, 0x75, 0x73, 0x4c, 0x69, 0x6e, 0x6b, 0x52, 0x13, 0x75, 0x6e, 0x66, 0x75, 0x72, 0x6c,
|
||||||
|
0x65, 0x64, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x4c, 0x69, 0x6e, 0x6b, 0x73, 0x22, 0xad, 0x0b,
|
||||||
|
0x0a, 0x0b, 0x43, 0x68, 0x61, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x14, 0x0a,
|
||||||
|
0x05, 0x63, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x63, 0x6c,
|
||||||
|
0x6f, 0x63, 0x6b, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70,
|
||||||
|
0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d,
|
||||||
|
0x70, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x65, 0x78, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||||
|
0x04, 0x74, 0x65, 0x78, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
|
||||||
|
0x65, 0x5f, 0x74, 0x6f, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x65, 0x73, 0x70,
|
||||||
|
0x6f, 0x6e, 0x73, 0x65, 0x54, 0x6f, 0x12, 0x19, 0x0a, 0x08, 0x65, 0x6e, 0x73, 0x5f, 0x6e, 0x61,
|
||||||
|
0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x65, 0x6e, 0x73, 0x4e, 0x61, 0x6d,
|
||||||
|
0x65, 0x12, 0x17, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01,
|
||||||
|
0x28, 0x09, 0x52, 0x06, 0x63, 0x68, 0x61, 0x74, 0x49, 0x64, 0x12, 0x38, 0x0a, 0x0c, 0x6d, 0x65,
|
||||||
|
0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0e,
|
||||||
|
0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4d, 0x65, 0x73, 0x73,
|
||||||
|
0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0b, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65,
|
||||||
|
0x54, 0x79, 0x70, 0x65, 0x12, 0x44, 0x0a, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x5f,
|
||||||
|
0x74, 0x79, 0x70, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x21, 0x2e, 0x70, 0x72, 0x6f,
|
||||||
|
0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x43, 0x68, 0x61, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67,
|
||||||
|
0x65, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0b, 0x63,
|
||||||
|
0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x34, 0x0a, 0x07, 0x73, 0x74,
|
||||||
|
0x69, 0x63, 0x6b, 0x65, 0x72, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x70, 0x72,
|
||||||
|
0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x4d, 0x65,
|
||||||
|
0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x00, 0x52, 0x07, 0x73, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x72,
|
||||||
|
0x12, 0x2e, 0x0a, 0x05, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32,
|
||||||
|
0x16, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x49, 0x6d, 0x61, 0x67, 0x65,
|
||||||
|
0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x00, 0x52, 0x05, 0x69, 0x6d, 0x61, 0x67, 0x65,
|
||||||
|
0x12, 0x2e, 0x0a, 0x05, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32,
|
||||||
|
0x16, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x41, 0x75, 0x64, 0x69, 0x6f,
|
||||||
|
0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x00, 0x52, 0x05, 0x61, 0x75, 0x64, 0x69, 0x6f,
|
||||||
|
0x12, 0x1e, 0x0a, 0x09, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x74, 0x79, 0x18, 0x0c, 0x20,
|
||||||
|
0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x09, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x74, 0x79,
|
||||||
|
0x12, 0x43, 0x0a, 0x0f, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x72, 0x64, 0x5f, 0x6d, 0x65, 0x73, 0x73,
|
||||||
|
0x61, 0x67, 0x65, 0x18, 0x63, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x70, 0x72, 0x6f, 0x74,
|
||||||
|
0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x72, 0x64, 0x4d, 0x65, 0x73, 0x73,
|
||||||
|
0x61, 0x67, 0x65, 0x48, 0x00, 0x52, 0x0e, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x72, 0x64, 0x4d, 0x65,
|
||||||
|
0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x40, 0x0a, 0x0e, 0x62, 0x72, 0x69, 0x64, 0x67, 0x65, 0x5f,
|
||||||
|
0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x64, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e,
|
||||||
|
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x72, 0x69, 0x64, 0x67, 0x65, 0x4d,
|
||||||
|
0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x00, 0x52, 0x0d, 0x62, 0x72, 0x69, 0x64, 0x67, 0x65,
|
||||||
|
0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x18, 0x0a, 0x05, 0x67, 0x72, 0x61, 0x6e, 0x74,
|
||||||
|
0x18, 0x0d, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x02, 0x18, 0x01, 0x52, 0x05, 0x67, 0x72, 0x61, 0x6e,
|
||||||
|
0x74, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x6e, 0x61, 0x6d,
|
||||||
|
0x65, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79,
|
||||||
|
0x4e, 0x61, 0x6d, 0x65, 0x12, 0x70, 0x0a, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x5f,
|
||||||
|
0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x61, 0x67, 0x61, 0x74,
|
||||||
|
0x65, 0x64, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27,
|
||||||
|
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63,
|
||||||
|
0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x70, 0x61, 0x67, 0x61, 0x74,
|
||||||
|
0x65, 0x64, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x1d, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74,
|
||||||
|
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x70, 0x61, 0x67, 0x61, 0x74, 0x65,
|
||||||
|
0x64, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x3d, 0x0a, 0x0e, 0x75, 0x6e, 0x66, 0x75, 0x72, 0x6c,
|
||||||
|
0x65, 0x64, 0x5f, 0x6c, 0x69, 0x6e, 0x6b, 0x73, 0x18, 0x10, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16,
|
||||||
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x6e, 0x66, 0x75, 0x72, 0x6c,
|
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x6e, 0x66, 0x75, 0x72, 0x6c,
|
||||||
0x65, 0x64, 0x4c, 0x69, 0x6e, 0x6b, 0x54, 0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69, 0x6c, 0x52,
|
0x65, 0x64, 0x4c, 0x69, 0x6e, 0x6b, 0x52, 0x0d, 0x75, 0x6e, 0x66, 0x75, 0x72, 0x6c, 0x65, 0x64,
|
||||||
0x06, 0x62, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x22, 0xf4, 0x01, 0x0a, 0x19, 0x55, 0x6e, 0x66, 0x75,
|
0x4c, 0x69, 0x6e, 0x6b, 0x73, 0x12, 0x25, 0x0a, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x18, 0x11,
|
||||||
0x72, 0x6c, 0x65, 0x64, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65,
|
0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,
|
||||||
0x6c, 0x4c, 0x69, 0x6e, 0x6b, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c,
|
0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x05, 0x73, 0x68, 0x61, 0x72, 0x64, 0x12, 0x51, 0x0a, 0x15,
|
||||||
0x5f, 0x75, 0x75, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x68, 0x61,
|
0x75, 0x6e, 0x66, 0x75, 0x72, 0x6c, 0x65, 0x64, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5f,
|
||||||
0x6e, 0x6e, 0x65, 0x6c, 0x55, 0x75, 0x69, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x6d, 0x6f, 0x6a,
|
0x6c, 0x69, 0x6e, 0x6b, 0x73, 0x18, 0x12, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x70, 0x72,
|
||||||
0x69, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x6d, 0x6f, 0x6a, 0x69, 0x12, 0x21,
|
|
||||||
0x0a, 0x0c, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03,
|
|
||||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d,
|
|
||||||
0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e,
|
|
||||||
0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74,
|
|
||||||
0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x18, 0x05, 0x20, 0x01,
|
|
||||||
0x28, 0x09, 0x52, 0x05, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x12, 0x43, 0x0a, 0x09, 0x63, 0x6f, 0x6d,
|
|
||||||
0x6d, 0x75, 0x6e, 0x69, 0x74, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x70,
|
|
||||||
0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x6e, 0x66, 0x75, 0x72, 0x6c, 0x65, 0x64,
|
|
||||||
0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x74, 0x79, 0x4c,
|
|
||||||
0x69, 0x6e, 0x6b, 0x52, 0x09, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x74, 0x79, 0x22, 0xfa,
|
|
||||||
0x01, 0x0a, 0x12, 0x55, 0x6e, 0x66, 0x75, 0x72, 0x6c, 0x65, 0x64, 0x53, 0x74, 0x61, 0x74, 0x75,
|
|
||||||
0x73, 0x4c, 0x69, 0x6e, 0x6b, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01,
|
|
||||||
0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x3f, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x61,
|
|
||||||
0x63, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
|
||||||
0x62, 0x75, 0x66, 0x2e, 0x55, 0x6e, 0x66, 0x75, 0x72, 0x6c, 0x65, 0x64, 0x53, 0x74, 0x61, 0x74,
|
|
||||||
0x75, 0x73, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x4c, 0x69, 0x6e, 0x6b, 0x48, 0x00, 0x52,
|
|
||||||
0x07, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x12, 0x45, 0x0a, 0x09, 0x63, 0x6f, 0x6d, 0x6d,
|
|
||||||
0x75, 0x6e, 0x69, 0x74, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x70, 0x72,
|
|
||||||
0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x6e, 0x66, 0x75, 0x72, 0x6c, 0x65, 0x64, 0x53,
|
0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x6e, 0x66, 0x75, 0x72, 0x6c, 0x65, 0x64, 0x53,
|
||||||
0x74, 0x61, 0x74, 0x75, 0x73, 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x74, 0x79, 0x4c, 0x69,
|
0x74, 0x61, 0x74, 0x75, 0x73, 0x4c, 0x69, 0x6e, 0x6b, 0x73, 0x52, 0x13, 0x75, 0x6e, 0x66, 0x75,
|
||||||
0x6e, 0x6b, 0x48, 0x00, 0x52, 0x09, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x74, 0x79, 0x12,
|
0x72, 0x6c, 0x65, 0x64, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x4c, 0x69, 0x6e, 0x6b, 0x73, 0x22,
|
||||||
0x3f, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b,
|
0xd0, 0x03, 0x0a, 0x0b, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12,
|
||||||
0x32, 0x23, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x6e, 0x66, 0x75,
|
0x18, 0x0a, 0x14, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x5f, 0x43, 0x4f, 0x4e, 0x54, 0x45,
|
||||||
0x72, 0x6c, 0x65, 0x64, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65,
|
0x4e, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x10, 0x00, 0x12, 0x0e, 0x0a, 0x0a, 0x54, 0x45, 0x58,
|
||||||
0x6c, 0x4c, 0x69, 0x6e, 0x6b, 0x48, 0x00, 0x52, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c,
|
0x54, 0x5f, 0x50, 0x4c, 0x41, 0x49, 0x4e, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x54, 0x49,
|
||||||
0x42, 0x09, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x22, 0x67, 0x0a, 0x13, 0x55,
|
0x43, 0x4b, 0x45, 0x52, 0x10, 0x02, 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53,
|
||||||
0x6e, 0x66, 0x75, 0x72, 0x6c, 0x65, 0x64, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x4c, 0x69, 0x6e,
|
0x10, 0x03, 0x12, 0x09, 0x0a, 0x05, 0x45, 0x4d, 0x4f, 0x4a, 0x49, 0x10, 0x04, 0x12, 0x17, 0x0a,
|
||||||
0x6b, 0x73, 0x12, 0x50, 0x0a, 0x15, 0x75, 0x6e, 0x66, 0x75, 0x72, 0x6c, 0x65, 0x64, 0x5f, 0x73,
|
0x13, 0x54, 0x52, 0x41, 0x4e, 0x53, 0x41, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x43, 0x4f, 0x4d,
|
||||||
0x74, 0x61, 0x74, 0x75, 0x73, 0x5f, 0x6c, 0x69, 0x6e, 0x6b, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28,
|
0x4d, 0x41, 0x4e, 0x44, 0x10, 0x05, 0x12, 0x28, 0x0a, 0x24, 0x53, 0x59, 0x53, 0x54, 0x45, 0x4d,
|
||||||
0x0b, 0x32, 0x1c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x6e, 0x66,
|
0x5f, 0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x5f, 0x43, 0x4f, 0x4e, 0x54, 0x45, 0x4e, 0x54,
|
||||||
0x75, 0x72, 0x6c, 0x65, 0x64, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x4c, 0x69, 0x6e, 0x6b, 0x52,
|
0x5f, 0x50, 0x52, 0x49, 0x56, 0x41, 0x54, 0x45, 0x5f, 0x47, 0x52, 0x4f, 0x55, 0x50, 0x10, 0x06,
|
||||||
0x13, 0x75, 0x6e, 0x66, 0x75, 0x72, 0x6c, 0x65, 0x64, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x4c,
|
0x12, 0x09, 0x0a, 0x05, 0x49, 0x4d, 0x41, 0x47, 0x45, 0x10, 0x07, 0x12, 0x09, 0x0a, 0x05, 0x41,
|
||||||
0x69, 0x6e, 0x6b, 0x73, 0x22, 0xad, 0x0b, 0x0a, 0x0b, 0x43, 0x68, 0x61, 0x74, 0x4d, 0x65, 0x73,
|
0x55, 0x44, 0x49, 0x4f, 0x10, 0x08, 0x12, 0x0d, 0x0a, 0x09, 0x43, 0x4f, 0x4d, 0x4d, 0x55, 0x4e,
|
||||||
0x73, 0x61, 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x01, 0x20,
|
0x49, 0x54, 0x59, 0x10, 0x09, 0x12, 0x16, 0x0a, 0x12, 0x53, 0x59, 0x53, 0x54, 0x45, 0x4d, 0x5f,
|
||||||
0x01, 0x28, 0x04, 0x52, 0x05, 0x63, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69,
|
0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x5f, 0x47, 0x41, 0x50, 0x10, 0x0a, 0x12, 0x13, 0x0a,
|
||||||
0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x74,
|
0x0f, 0x43, 0x4f, 0x4e, 0x54, 0x41, 0x43, 0x54, 0x5f, 0x52, 0x45, 0x51, 0x55, 0x45, 0x53, 0x54,
|
||||||
0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x65, 0x78, 0x74,
|
0x10, 0x0b, 0x12, 0x13, 0x0a, 0x0f, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x52, 0x44, 0x5f, 0x4d, 0x45,
|
||||||
0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x65, 0x78, 0x74, 0x12, 0x1f, 0x0a, 0x0b,
|
0x53, 0x53, 0x41, 0x47, 0x45, 0x10, 0x0c, 0x12, 0x19, 0x0a, 0x15, 0x49, 0x44, 0x45, 0x4e, 0x54,
|
||||||
0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x5f, 0x74, 0x6f, 0x18, 0x04, 0x20, 0x01, 0x28,
|
0x49, 0x54, 0x59, 0x5f, 0x56, 0x45, 0x52, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e,
|
||||||
0x09, 0x52, 0x0a, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x54, 0x6f, 0x12, 0x19, 0x0a,
|
0x10, 0x0d, 0x12, 0x21, 0x0a, 0x1d, 0x53, 0x59, 0x53, 0x54, 0x45, 0x4d, 0x5f, 0x4d, 0x45, 0x53,
|
||||||
0x08, 0x65, 0x6e, 0x73, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52,
|
0x53, 0x41, 0x47, 0x45, 0x5f, 0x50, 0x49, 0x4e, 0x4e, 0x45, 0x44, 0x5f, 0x4d, 0x45, 0x53, 0x53,
|
||||||
0x07, 0x65, 0x6e, 0x73, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x74,
|
0x41, 0x47, 0x45, 0x10, 0x0e, 0x12, 0x24, 0x0a, 0x20, 0x53, 0x59, 0x53, 0x54, 0x45, 0x4d, 0x5f,
|
||||||
0x5f, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x63, 0x68, 0x61, 0x74, 0x49,
|
0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x5f, 0x4d, 0x55, 0x54, 0x55, 0x41, 0x4c, 0x5f, 0x45,
|
||||||
0x64, 0x12, 0x38, 0x0a, 0x0c, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x74, 0x79, 0x70,
|
0x56, 0x45, 0x4e, 0x54, 0x5f, 0x53, 0x45, 0x4e, 0x54, 0x10, 0x0f, 0x12, 0x28, 0x0a, 0x24, 0x53,
|
||||||
0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
|
0x59, 0x53, 0x54, 0x45, 0x4d, 0x5f, 0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x5f, 0x4d, 0x55,
|
||||||
0x75, 0x66, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0b,
|
0x54, 0x55, 0x41, 0x4c, 0x5f, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x5f, 0x41, 0x43, 0x43, 0x45, 0x50,
|
||||||
0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x44, 0x0a, 0x0c, 0x63,
|
0x54, 0x45, 0x44, 0x10, 0x10, 0x12, 0x27, 0x0a, 0x23, 0x53, 0x59, 0x53, 0x54, 0x45, 0x4d, 0x5f,
|
||||||
0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28,
|
0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x5f, 0x4d, 0x55, 0x54, 0x55, 0x41, 0x4c, 0x5f, 0x45,
|
||||||
0x0e, 0x32, 0x21, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x43, 0x68, 0x61,
|
0x56, 0x45, 0x4e, 0x54, 0x5f, 0x52, 0x45, 0x4d, 0x4f, 0x56, 0x45, 0x44, 0x10, 0x11, 0x12, 0x12,
|
||||||
0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74,
|
0x0a, 0x0e, 0x42, 0x52, 0x49, 0x44, 0x47, 0x45, 0x5f, 0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45,
|
||||||
0x54, 0x79, 0x70, 0x65, 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70,
|
0x10, 0x12, 0x42, 0x09, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x42, 0x0d, 0x5a,
|
||||||
0x65, 0x12, 0x34, 0x0a, 0x07, 0x73, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x18, 0x09, 0x20, 0x01,
|
0x0b, 0x2e, 0x2f, 0x3b, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x62, 0x06, 0x70, 0x72,
|
||||||
0x28, 0x0b, 0x32, 0x18, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74,
|
0x6f, 0x74, 0x6f, 0x33,
|
||||||
0x69, 0x63, 0x6b, 0x65, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x00, 0x52, 0x07,
|
|
||||||
0x73, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x12, 0x2e, 0x0a, 0x05, 0x69, 0x6d, 0x61, 0x67, 0x65,
|
|
||||||
0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
|
|
||||||
0x66, 0x2e, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x00,
|
|
||||||
0x52, 0x05, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x12, 0x2e, 0x0a, 0x05, 0x61, 0x75, 0x64, 0x69, 0x6f,
|
|
||||||
0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
|
|
||||||
0x66, 0x2e, 0x41, 0x75, 0x64, 0x69, 0x6f, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x00,
|
|
||||||
0x52, 0x05, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x12, 0x1e, 0x0a, 0x09, 0x63, 0x6f, 0x6d, 0x6d, 0x75,
|
|
||||||
0x6e, 0x69, 0x74, 0x79, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x09, 0x63, 0x6f,
|
|
||||||
0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x74, 0x79, 0x12, 0x43, 0x0a, 0x0f, 0x64, 0x69, 0x73, 0x63, 0x6f,
|
|
||||||
0x72, 0x64, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x63, 0x20, 0x01, 0x28, 0x0b,
|
|
||||||
0x32, 0x18, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x69, 0x73, 0x63,
|
|
||||||
0x6f, 0x72, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x00, 0x52, 0x0e, 0x64, 0x69,
|
|
||||||
0x73, 0x63, 0x6f, 0x72, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x40, 0x0a, 0x0e,
|
|
||||||
0x62, 0x72, 0x69, 0x64, 0x67, 0x65, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x64,
|
|
||||||
0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,
|
|
||||||
0x42, 0x72, 0x69, 0x64, 0x67, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x00, 0x52,
|
|
||||||
0x0d, 0x62, 0x72, 0x69, 0x64, 0x67, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x18,
|
|
||||||
0x0a, 0x05, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x02, 0x18,
|
|
||||||
0x01, 0x52, 0x05, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x69, 0x73, 0x70,
|
|
||||||
0x6c, 0x61, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b,
|
|
||||||
0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x70, 0x0a, 0x20, 0x63,
|
|
||||||
0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x70,
|
|
||||||
0x72, 0x6f, 0x70, 0x61, 0x67, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18,
|
|
||||||
0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
|
|
||||||
0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x50,
|
|
||||||
0x72, 0x6f, 0x70, 0x61, 0x67, 0x61, 0x74, 0x65, 0x64, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x1d,
|
|
||||||
0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x50, 0x72,
|
|
||||||
0x6f, 0x70, 0x61, 0x67, 0x61, 0x74, 0x65, 0x64, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x3d, 0x0a,
|
|
||||||
0x0e, 0x75, 0x6e, 0x66, 0x75, 0x72, 0x6c, 0x65, 0x64, 0x5f, 0x6c, 0x69, 0x6e, 0x6b, 0x73, 0x18,
|
|
||||||
0x10, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
|
|
||||||
0x2e, 0x55, 0x6e, 0x66, 0x75, 0x72, 0x6c, 0x65, 0x64, 0x4c, 0x69, 0x6e, 0x6b, 0x52, 0x0d, 0x75,
|
|
||||||
0x6e, 0x66, 0x75, 0x72, 0x6c, 0x65, 0x64, 0x4c, 0x69, 0x6e, 0x6b, 0x73, 0x12, 0x25, 0x0a, 0x05,
|
|
||||||
0x73, 0x68, 0x61, 0x72, 0x64, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x70, 0x72,
|
|
||||||
0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x68, 0x61, 0x72, 0x64, 0x52, 0x05, 0x73, 0x68,
|
|
||||||
0x61, 0x72, 0x64, 0x12, 0x51, 0x0a, 0x15, 0x75, 0x6e, 0x66, 0x75, 0x72, 0x6c, 0x65, 0x64, 0x5f,
|
|
||||||
0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5f, 0x6c, 0x69, 0x6e, 0x6b, 0x73, 0x18, 0x12, 0x20, 0x01,
|
|
||||||
0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x6e,
|
|
||||||
0x66, 0x75, 0x72, 0x6c, 0x65, 0x64, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x4c, 0x69, 0x6e, 0x6b,
|
|
||||||
0x73, 0x52, 0x13, 0x75, 0x6e, 0x66, 0x75, 0x72, 0x6c, 0x65, 0x64, 0x53, 0x74, 0x61, 0x74, 0x75,
|
|
||||||
0x73, 0x4c, 0x69, 0x6e, 0x6b, 0x73, 0x22, 0xd0, 0x03, 0x0a, 0x0b, 0x43, 0x6f, 0x6e, 0x74, 0x65,
|
|
||||||
0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x18, 0x0a, 0x14, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57,
|
|
||||||
0x4e, 0x5f, 0x43, 0x4f, 0x4e, 0x54, 0x45, 0x4e, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x10, 0x00,
|
|
||||||
0x12, 0x0e, 0x0a, 0x0a, 0x54, 0x45, 0x58, 0x54, 0x5f, 0x50, 0x4c, 0x41, 0x49, 0x4e, 0x10, 0x01,
|
|
||||||
0x12, 0x0b, 0x0a, 0x07, 0x53, 0x54, 0x49, 0x43, 0x4b, 0x45, 0x52, 0x10, 0x02, 0x12, 0x0a, 0x0a,
|
|
||||||
0x06, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x10, 0x03, 0x12, 0x09, 0x0a, 0x05, 0x45, 0x4d, 0x4f,
|
|
||||||
0x4a, 0x49, 0x10, 0x04, 0x12, 0x17, 0x0a, 0x13, 0x54, 0x52, 0x41, 0x4e, 0x53, 0x41, 0x43, 0x54,
|
|
||||||
0x49, 0x4f, 0x4e, 0x5f, 0x43, 0x4f, 0x4d, 0x4d, 0x41, 0x4e, 0x44, 0x10, 0x05, 0x12, 0x28, 0x0a,
|
|
||||||
0x24, 0x53, 0x59, 0x53, 0x54, 0x45, 0x4d, 0x5f, 0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x5f,
|
|
||||||
0x43, 0x4f, 0x4e, 0x54, 0x45, 0x4e, 0x54, 0x5f, 0x50, 0x52, 0x49, 0x56, 0x41, 0x54, 0x45, 0x5f,
|
|
||||||
0x47, 0x52, 0x4f, 0x55, 0x50, 0x10, 0x06, 0x12, 0x09, 0x0a, 0x05, 0x49, 0x4d, 0x41, 0x47, 0x45,
|
|
||||||
0x10, 0x07, 0x12, 0x09, 0x0a, 0x05, 0x41, 0x55, 0x44, 0x49, 0x4f, 0x10, 0x08, 0x12, 0x0d, 0x0a,
|
|
||||||
0x09, 0x43, 0x4f, 0x4d, 0x4d, 0x55, 0x4e, 0x49, 0x54, 0x59, 0x10, 0x09, 0x12, 0x16, 0x0a, 0x12,
|
|
||||||
0x53, 0x59, 0x53, 0x54, 0x45, 0x4d, 0x5f, 0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x5f, 0x47,
|
|
||||||
0x41, 0x50, 0x10, 0x0a, 0x12, 0x13, 0x0a, 0x0f, 0x43, 0x4f, 0x4e, 0x54, 0x41, 0x43, 0x54, 0x5f,
|
|
||||||
0x52, 0x45, 0x51, 0x55, 0x45, 0x53, 0x54, 0x10, 0x0b, 0x12, 0x13, 0x0a, 0x0f, 0x44, 0x49, 0x53,
|
|
||||||
0x43, 0x4f, 0x52, 0x44, 0x5f, 0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x10, 0x0c, 0x12, 0x19,
|
|
||||||
0x0a, 0x15, 0x49, 0x44, 0x45, 0x4e, 0x54, 0x49, 0x54, 0x59, 0x5f, 0x56, 0x45, 0x52, 0x49, 0x46,
|
|
||||||
0x49, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x0d, 0x12, 0x21, 0x0a, 0x1d, 0x53, 0x59, 0x53,
|
|
||||||
0x54, 0x45, 0x4d, 0x5f, 0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x5f, 0x50, 0x49, 0x4e, 0x4e,
|
|
||||||
0x45, 0x44, 0x5f, 0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x10, 0x0e, 0x12, 0x24, 0x0a, 0x20,
|
|
||||||
0x53, 0x59, 0x53, 0x54, 0x45, 0x4d, 0x5f, 0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x5f, 0x4d,
|
|
||||||
0x55, 0x54, 0x55, 0x41, 0x4c, 0x5f, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x5f, 0x53, 0x45, 0x4e, 0x54,
|
|
||||||
0x10, 0x0f, 0x12, 0x28, 0x0a, 0x24, 0x53, 0x59, 0x53, 0x54, 0x45, 0x4d, 0x5f, 0x4d, 0x45, 0x53,
|
|
||||||
0x53, 0x41, 0x47, 0x45, 0x5f, 0x4d, 0x55, 0x54, 0x55, 0x41, 0x4c, 0x5f, 0x45, 0x56, 0x45, 0x4e,
|
|
||||||
0x54, 0x5f, 0x41, 0x43, 0x43, 0x45, 0x50, 0x54, 0x45, 0x44, 0x10, 0x10, 0x12, 0x27, 0x0a, 0x23,
|
|
||||||
0x53, 0x59, 0x53, 0x54, 0x45, 0x4d, 0x5f, 0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x5f, 0x4d,
|
|
||||||
0x55, 0x54, 0x55, 0x41, 0x4c, 0x5f, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x5f, 0x52, 0x45, 0x4d, 0x4f,
|
|
||||||
0x56, 0x45, 0x44, 0x10, 0x11, 0x12, 0x12, 0x0a, 0x0e, 0x42, 0x52, 0x49, 0x44, 0x47, 0x45, 0x5f,
|
|
||||||
0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x10, 0x12, 0x42, 0x09, 0x0a, 0x07, 0x70, 0x61, 0x79,
|
|
||||||
0x6c, 0x6f, 0x61, 0x64, 0x42, 0x0d, 0x5a, 0x0b, 0x2e, 0x2f, 0x3b, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
|
||||||
0x62, 0x75, 0x66, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
|
@ -137,6 +137,7 @@ message UnfurledLink {
|
||||||
uint32 thumbnail_width = 5;
|
uint32 thumbnail_width = 5;
|
||||||
uint32 thumbnail_height = 6;
|
uint32 thumbnail_height = 6;
|
||||||
LinkType type = 7;
|
LinkType type = 7;
|
||||||
|
bytes favicon_payload = 8;
|
||||||
|
|
||||||
enum LinkType {
|
enum LinkType {
|
||||||
LINK = 0;
|
LINK = 0;
|
||||||
|
|
|
@ -34,6 +34,7 @@ const (
|
||||||
discordAuthorsPath = "/discord/authors"
|
discordAuthorsPath = "/discord/authors"
|
||||||
discordAttachmentsPath = basePath + "/discord/attachments"
|
discordAttachmentsPath = basePath + "/discord/attachments"
|
||||||
LinkPreviewThumbnailPath = "/link-preview/thumbnail"
|
LinkPreviewThumbnailPath = "/link-preview/thumbnail"
|
||||||
|
LinkPreviewFaviconPath = "/link-preview/favicon"
|
||||||
StatusLinkPreviewThumbnailPath = "/status-link-preview/thumbnail"
|
StatusLinkPreviewThumbnailPath = "/status-link-preview/thumbnail"
|
||||||
communityTokenImagesPath = "/communityTokenImages"
|
communityTokenImagesPath = "/communityTokenImages"
|
||||||
|
|
||||||
|
|
|
@ -14,19 +14,27 @@ import (
|
||||||
"github.com/status-im/status-go/protocol/protobuf"
|
"github.com/status-im/status-go/protocol/protobuf"
|
||||||
)
|
)
|
||||||
|
|
||||||
func getThumbnailPayload(db *sql.DB, logger *zap.Logger, msgID string, thumbnailURL string) ([]byte, error) {
|
func getUnfurledLinksFromDB(db *sql.DB, msgID string) ([]*protobuf.UnfurledLink, error) {
|
||||||
var payload []byte
|
|
||||||
|
|
||||||
var result []byte
|
var result []byte
|
||||||
err := db.QueryRow(`SELECT unfurled_links FROM user_messages WHERE id = ?`, msgID).Scan(&result)
|
err := db.QueryRow(`SELECT unfurled_links FROM user_messages WHERE id = ?`, msgID).Scan(&result)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return payload, fmt.Errorf("could not find message with message-id '%s': %w", msgID, err)
|
return nil, fmt.Errorf("could not find message with message-id '%s': %w", msgID, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var links []*protobuf.UnfurledLink
|
var links []*protobuf.UnfurledLink
|
||||||
err = json.Unmarshal(result, &links)
|
err = json.Unmarshal(result, &links)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return payload, fmt.Errorf("failed to unmarshal protobuf.UrlPreview: %w", err)
|
return nil, fmt.Errorf("failed to unmarshal protobuf.UrlPreview: %w", err)
|
||||||
|
}
|
||||||
|
return links, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getThumbnailPayload(db *sql.DB, msgID string, thumbnailURL string) ([]byte, error) {
|
||||||
|
var payload []byte
|
||||||
|
|
||||||
|
var links, err = getUnfurledLinksFromDB(db, msgID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, p := range links {
|
for _, p := range links {
|
||||||
|
@ -39,40 +47,82 @@ func getThumbnailPayload(db *sql.DB, logger *zap.Logger, msgID string, thumbnail
|
||||||
return payload, nil
|
return payload, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getFaviconPayload(db *sql.DB, msgID string, faviconURL string) ([]byte, error) {
|
||||||
|
var payload []byte
|
||||||
|
|
||||||
|
var links, err = getUnfurledLinksFromDB(db, msgID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, p := range links {
|
||||||
|
if p.Url == faviconURL {
|
||||||
|
payload = p.FaviconPayload
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return payload, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateAndReturnImageParams(r *http.Request, w http.ResponseWriter, logger *zap.Logger) ImageParams {
|
||||||
|
params := r.URL.Query()
|
||||||
|
parsed := ParseImageParams(logger, params)
|
||||||
|
|
||||||
|
if parsed.MessageID == "" {
|
||||||
|
http.Error(w, "missing query parameter 'message-id'", http.StatusBadRequest)
|
||||||
|
return ImageParams{}
|
||||||
|
}
|
||||||
|
|
||||||
|
if parsed.URL == "" {
|
||||||
|
http.Error(w, "missing query parameter 'url'", http.StatusBadRequest)
|
||||||
|
return ImageParams{}
|
||||||
|
}
|
||||||
|
return parsed
|
||||||
|
}
|
||||||
|
|
||||||
|
func getMimeTypeAndWriteImage(w http.ResponseWriter, logger *zap.Logger, imagePayload []byte) {
|
||||||
|
mimeType, err := images.GetMimeType(imagePayload)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "mime type not supported", http.StatusNotImplemented)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Header().Set("Content-Type", "image/"+mimeType)
|
||||||
|
w.Header().Set("Cache-Control", "no-store")
|
||||||
|
|
||||||
|
_, err = w.Write(imagePayload)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("failed to write response", zap.Error(err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkForFetchImageError(err error, logger *zap.Logger, parsedImageParams ImageParams, w http.ResponseWriter, imageType string) {
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("failed to get "+imageType, zap.String("msgID", parsedImageParams.MessageID))
|
||||||
|
http.Error(w, "failed to get "+imageType, http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func handleLinkPreviewThumbnail(db *sql.DB, logger *zap.Logger) http.HandlerFunc {
|
func handleLinkPreviewThumbnail(db *sql.DB, logger *zap.Logger) http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
params := r.URL.Query()
|
parsed := validateAndReturnImageParams(r, w, logger)
|
||||||
parsed := ParseImageParams(logger, params)
|
if parsed.URL != "" {
|
||||||
|
thumbnail, err := getThumbnailPayload(db, parsed.MessageID, parsed.URL)
|
||||||
if parsed.MessageID == "" {
|
checkForFetchImageError(err, logger, parsed, w, "thumbnail")
|
||||||
http.Error(w, "missing query parameter 'message-id'", http.StatusBadRequest)
|
getMimeTypeAndWriteImage(w, logger, thumbnail)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if parsed.URL == "" {
|
func handleLinkPreviewFavicon(db *sql.DB, logger *zap.Logger) http.HandlerFunc {
|
||||||
http.Error(w, "missing query parameter 'url'", http.StatusBadRequest)
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
parsed := validateAndReturnImageParams(r, w, logger)
|
||||||
}
|
if parsed.URL != "" {
|
||||||
|
favicon, err := getFaviconPayload(db, parsed.MessageID, parsed.URL)
|
||||||
thumbnail, err := getThumbnailPayload(db, logger, parsed.MessageID, parsed.URL)
|
checkForFetchImageError(err, logger, parsed, w, "favicon")
|
||||||
if err != nil {
|
getMimeTypeAndWriteImage(w, logger, favicon)
|
||||||
logger.Error("failed to get thumbnail", zap.String("msgID", parsed.MessageID))
|
|
||||||
http.Error(w, "failed to get thumbnail", http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
mimeType, err := images.GetMimeType(thumbnail)
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, "mime type not supported", http.StatusNotImplemented)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "image/"+mimeType)
|
|
||||||
w.Header().Set("Cache-Control", "no-store")
|
|
||||||
|
|
||||||
_, err = w.Write(thumbnail)
|
|
||||||
if err != nil {
|
|
||||||
logger.Error("failed to write response", zap.Error(err))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,6 +52,7 @@ func NewMediaServer(db *sql.DB, downloader *ipfs.Downloader, multiaccountsDB *mu
|
||||||
imagesPath: handleImage(s.db, s.logger),
|
imagesPath: handleImage(s.db, s.logger),
|
||||||
ipfsPath: handleIPFS(s.downloader, s.logger),
|
ipfsPath: handleIPFS(s.downloader, s.logger),
|
||||||
LinkPreviewThumbnailPath: handleLinkPreviewThumbnail(s.db, s.logger),
|
LinkPreviewThumbnailPath: handleLinkPreviewThumbnail(s.db, s.logger),
|
||||||
|
LinkPreviewFaviconPath: handleLinkPreviewFavicon(s.db, s.logger),
|
||||||
StatusLinkPreviewThumbnailPath: handleStatusLinkPreviewThumbnail(s.db, s.logger),
|
StatusLinkPreviewThumbnailPath: handleStatusLinkPreviewThumbnail(s.db, s.logger),
|
||||||
communityTokenImagesPath: handleCommunityTokenImages(s.db, s.logger),
|
communityTokenImagesPath: handleCommunityTokenImages(s.db, s.logger),
|
||||||
walletCommunityImagesPath: handleWalletCommunityImages(s.walletDB, s.logger),
|
walletCommunityImagesPath: handleWalletCommunityImages(s.walletDB, s.logger),
|
||||||
|
@ -90,6 +91,13 @@ func (s *MediaServer) MakeStatusLinkPreviewThumbnailURL(msgID string, previewURL
|
||||||
return u.String()
|
return u.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *MediaServer) MakeLinkPreviewFaviconURL(msgID string, previewURL string) string {
|
||||||
|
u := s.MakeBaseURL()
|
||||||
|
u.Path = LinkPreviewFaviconPath
|
||||||
|
u.RawQuery = url.Values{"message-id": {msgID}, "url": {previewURL}}.Encode()
|
||||||
|
return u.String()
|
||||||
|
}
|
||||||
|
|
||||||
func (s *MediaServer) MakeDiscordAuthorAvatarURL(authorID string) string {
|
func (s *MediaServer) MakeDiscordAuthorAvatarURL(authorID string) string {
|
||||||
u := s.MakeBaseURL()
|
u := s.MakeBaseURL()
|
||||||
u.Path = discordAuthorsPath
|
u.Path = discordAuthorsPath
|
||||||
|
|
Loading…
Reference in New Issue