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"
|
||||
"net/http"
|
||||
"os"
|
||||
"regexp"
|
||||
"time"
|
||||
"unicode/utf8"
|
||||
|
||||
"golang.org/x/image/webp"
|
||||
|
||||
"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) {
|
||||
file, err := os.Open(fileName)
|
||||
if err != nil {
|
||||
|
@ -109,6 +131,8 @@ func GetType(buf []byte) ImageType {
|
|||
return GIF
|
||||
case IsWebp(buf):
|
||||
return WEBP
|
||||
case IsIco(buf):
|
||||
return ICO
|
||||
default:
|
||||
return UNKNOWN
|
||||
}
|
||||
|
@ -124,6 +148,10 @@ func GetMimeType(buf []byte) (string, error) {
|
|||
return "gif", nil
|
||||
case IsWebp(buf):
|
||||
return "webp", nil
|
||||
case IsIco(buf):
|
||||
return "ico", nil
|
||||
case IsSVG(buf):
|
||||
return "svg", nil
|
||||
default:
|
||||
return "", errors.New("image format not supported")
|
||||
}
|
||||
|
@ -153,6 +181,12 @@ func IsWebp(buf []byte) bool {
|
|||
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) {
|
||||
// Decode image bytes
|
||||
img, _, err := image.Decode(bytes.NewReader(imgBytes))
|
||||
|
|
|
@ -110,3 +110,30 @@ func TestGetPayloadFromURI(t *testing.T) {
|
|||
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
|
||||
GIF
|
||||
WEBP
|
||||
ICO
|
||||
)
|
||||
|
||||
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 LinkPreviewThumbnail struct {
|
||||
Width int `json:"width"`
|
||||
Height int `json:"height"`
|
||||
Width int `json:"width,omitempty"`
|
||||
Height int `json:"height,omitempty"`
|
||||
// Non-empty when the thumbnail is available via the media server, i.e. after
|
||||
// the chat message is sent.
|
||||
URL string `json:"url,omitempty"`
|
||||
|
@ -31,6 +31,7 @@ type LinkPreview struct {
|
|||
Hostname string `json:"hostname"`
|
||||
Title string `json:"title,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
Favicon LinkPreviewThumbnail `json:"favicon,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)
|
||||
}
|
||||
|
||||
var payload []byte
|
||||
var thumbnailPayload []byte
|
||||
var faviconPayload []byte
|
||||
var err error
|
||||
if preview.Thumbnail.DataURI != "" {
|
||||
payload, err = images.GetPayloadFromURI(preview.Thumbnail.DataURI)
|
||||
thumbnailPayload, err = images.GetPayloadFromURI(preview.Thumbnail.DataURI)
|
||||
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,
|
||||
ThumbnailWidth: uint32(preview.Thumbnail.Width),
|
||||
ThumbnailHeight: uint32(preview.Thumbnail.Height),
|
||||
ThumbnailPayload: payload,
|
||||
ThumbnailPayload: thumbnailPayload,
|
||||
FaviconPayload: faviconPayload,
|
||||
}
|
||||
unfurledLinks = append(unfurledLinks, ul)
|
||||
}
|
||||
|
@ -312,7 +321,8 @@ func (m *Message) ConvertLinkPreviewsToProto() ([]*protobuf.UnfurledLink, error)
|
|||
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
|
||||
|
||||
if links = m.GetUnfurledLinks(); links == nil {
|
||||
|
@ -340,13 +350,20 @@ func (m *Message) ConvertFromProtoToLinkPreviews(makeMediaServerURL func(msgID s
|
|||
}
|
||||
mediaURL := ""
|
||||
if len(link.ThumbnailPayload) > 0 {
|
||||
mediaURL = makeMediaServerURL(m.ID, link.Url)
|
||||
mediaURL = makeThumbnailMediaServerURL(m.ID, link.Url)
|
||||
}
|
||||
if link.GetThumbnailPayload() != nil {
|
||||
lp.Thumbnail.Width = int(link.ThumbnailWidth)
|
||||
lp.Thumbnail.Height = int(link.ThumbnailHeight)
|
||||
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)
|
||||
}
|
||||
|
||||
|
|
|
@ -146,6 +146,9 @@ func TestConvertLinkPreviewsToProto(t *testing.T) {
|
|||
URL: "http://localhost:9999",
|
||||
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.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.
|
||||
invalidPreview := validPreview
|
||||
invalidPreview.Title = ""
|
||||
|
@ -178,7 +185,7 @@ func TestConvertLinkPreviewsToProto(t *testing.T) {
|
|||
invalidPreview.Thumbnail.DataURI = "data:hello/png,iVBOR"
|
||||
msg.LinkPreviews = []LinkPreview{invalidPreview}
|
||||
_, 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.
|
||||
somePreview := validPreview
|
||||
|
@ -196,6 +203,8 @@ func TestConvertFromProtoToLinkPreviews(t *testing.T) {
|
|||
|
||||
thumbnailPayload, err := base64.StdEncoding.DecodeString("iVBORw0KGgoAAAANSUg=")
|
||||
require.NoError(t, err)
|
||||
FaviconPayload, err := base64.StdEncoding.DecodeString("iVBORw0KGgoAAAANSUg=")
|
||||
require.NoError(t, err)
|
||||
|
||||
l := &protobuf.UnfurledLink{
|
||||
Description: "GitHub is where people build software.",
|
||||
|
@ -203,6 +212,7 @@ func TestConvertFromProtoToLinkPreviews(t *testing.T) {
|
|||
Type: protobuf.UnfurledLink_LINK,
|
||||
Url: "https://github.com",
|
||||
ThumbnailPayload: thumbnailPayload,
|
||||
FaviconPayload: FaviconPayload,
|
||||
ThumbnailWidth: 100,
|
||||
ThumbnailHeight: 200,
|
||||
}
|
||||
|
@ -217,7 +227,7 @@ func TestConvertFromProtoToLinkPreviews(t *testing.T) {
|
|||
return "https://localhost:6666/" + msgID + "-" + linkURL
|
||||
}
|
||||
|
||||
previews := msg.ConvertFromProtoToLinkPreviews(urlMaker)
|
||||
previews := msg.ConvertFromProtoToLinkPreviews(urlMaker, urlMaker)
|
||||
require.Len(t, previews, 1)
|
||||
p := previews[0]
|
||||
require.Equal(t, l.Type, p.Type)
|
||||
|
@ -230,11 +240,12 @@ func TestConvertFromProtoToLinkPreviews(t *testing.T) {
|
|||
// fetched from the media server.
|
||||
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.Favicon.URL)
|
||||
|
||||
// Test when the URL is not parseable by url.Parse.
|
||||
l.Url = "postgres://user:abc{DEf1=ghi@example.com:5432/db?sslmode=require"
|
||||
msg.ChatMessage.UnfurledLinks = []*protobuf.UnfurledLink{l}
|
||||
previews = msg.ConvertFromProtoToLinkPreviews(urlMaker)
|
||||
previews = msg.ConvertFromProtoToLinkPreviews(urlMaker, urlMaker)
|
||||
require.Len(t, previews, 1)
|
||||
p = previews[0]
|
||||
require.Equal(t, l.Url, p.Hostname)
|
||||
|
@ -246,7 +257,7 @@ func TestConvertFromProtoToLinkPreviews(t *testing.T) {
|
|||
Url: "https://github.com",
|
||||
}
|
||||
msg.ChatMessage.UnfurledLinks = []*protobuf.UnfurledLink{l}
|
||||
previews = msg.ConvertFromProtoToLinkPreviews(urlMaker)
|
||||
previews = msg.ConvertFromProtoToLinkPreviews(urlMaker, urlMaker)
|
||||
require.Len(t, previews, 1)
|
||||
p = previews[0]
|
||||
require.Equal(t, 0, p.Thumbnail.Height)
|
||||
|
|
|
@ -6,9 +6,11 @@ import (
|
|||
"io/ioutil"
|
||||
"net/http"
|
||||
neturl "net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/keighl/metabolize"
|
||||
"go.uber.org/zap"
|
||||
"golang.org/x/net/html"
|
||||
|
||||
"github.com/status-im/status-go/images"
|
||||
"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) {
|
||||
preview := newDefaultLinkPreview(u.url)
|
||||
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")
|
||||
}
|
||||
|
||||
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
|
||||
// title tag, but article pages do. In the future, we can fallback to the
|
||||
// website's title by using the <title> tag.
|
||||
|
@ -66,7 +112,7 @@ func (u *OpenGraphUnfurler) Unfurl() (*common.LinkPreview, error) {
|
|||
}
|
||||
|
||||
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 {
|
||||
// Given we want to fetch thumbnails on a best-effort basis, if an error
|
||||
// happens we simply log it.
|
||||
|
@ -78,24 +124,25 @@ func (u *OpenGraphUnfurler) Unfurl() (*common.LinkPreview, error) {
|
|||
|
||||
preview.Title = ogMetadata.Title
|
||||
preview.Description = ogMetadata.Description
|
||||
|
||||
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
|
||||
|
||||
imgBytes, err := fetchBody(logger, httpClient, url, nil)
|
||||
if err != nil {
|
||||
return thumbnail, fmt.Errorf("could not fetch thumbnail url='%s': %w", url, err)
|
||||
}
|
||||
|
||||
width, height, err := images.GetImageDimensions(imgBytes)
|
||||
if err != nil {
|
||||
return thumbnail, fmt.Errorf("could not get image dimensions url='%s': %w", url, err)
|
||||
if getDimensions {
|
||||
width, height, err := images.GetImageDimensions(imgBytes)
|
||||
if err != nil {
|
||||
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)
|
||||
if err != nil {
|
||||
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 {
|
||||
msg.StickerLocalURL = s.MakeStickerURL(msg.GetSticker().Hash)
|
||||
}
|
||||
|
||||
msg.LinkPreviews = msg.ConvertFromProtoToLinkPreviews(s.MakeLinkPreviewThumbnailURL)
|
||||
msg.LinkPreviews = msg.ConvertFromProtoToLinkPreviews(s.MakeLinkPreviewThumbnailURL, s.MakeLinkPreviewFaviconURL)
|
||||
msg.StatusLinkPreviews = msg.ConvertFromProtoToStatusLinkPreviews(s.MakeStatusLinkPreviewThumbnailURL)
|
||||
|
||||
return nil
|
||||
|
|
|
@ -198,6 +198,63 @@ func (s *MessengerLinkPreviewsTestSuite) readAsset(filename string) []byte {
|
|||
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() {
|
||||
u := "https://www.youtube.com/watch?v=lE4UXdJSJM4"
|
||||
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",
|
||||
},
|
||||
}
|
||||
|
||||
favicon := "https://www.youtube.com/s/desktop/87423d78/img/favicon.ico"
|
||||
transport := StubTransport{}
|
||||
transport.AddURLMatcher(
|
||||
u,
|
||||
|
@ -223,9 +280,10 @@ func (s *MessengerLinkPreviewsTestSuite) Test_UnfurlURLs_YouTube() {
|
|||
<meta property="og:title" content="%s">
|
||||
<meta property="og:description" content="%s">
|
||||
<meta property="og:image" content="%s">
|
||||
<link rel="shortcut icon" href="%s">
|
||||
</head>
|
||||
</html>
|
||||
`, expected.Title, expected.Description, thumbnailURL)),
|
||||
`, expected.Title, expected.Description, thumbnailURL, favicon)),
|
||||
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.Height, preview.Thumbnail.Height)
|
||||
s.Require().Equal(expected.Thumbnail.URL, preview.Thumbnail.URL)
|
||||
s.Require().NotNil(preview.Favicon)
|
||||
s.assertContainsLongString(expected.Thumbnail.DataURI, preview.Thumbnail.DataURI, 100)
|
||||
}
|
||||
|
||||
|
|
|
@ -2291,6 +2291,9 @@ func (s *MessengerSuite) TestSendMessageWithPreviews() {
|
|||
Width: 100,
|
||||
Height: 200,
|
||||
},
|
||||
Favicon: common.LinkPreviewThumbnail{
|
||||
DataURI: "data:image/png;base64,iVBORw0KGgoAAAANSUg=",
|
||||
},
|
||||
}
|
||||
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"`
|
||||
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"`
|
||||
FaviconPayload []byte `protobuf:"bytes,8,opt,name=favicon_payload,json=faviconPayload,proto3" json:"favicon_payload,omitempty"`
|
||||
}
|
||||
|
||||
func (x *UnfurledLink) Reset() {
|
||||
|
@ -1308,6 +1309,13 @@ func (x *UnfurledLink) GetType() UnfurledLink_LinkType {
|
|||
return UnfurledLink_LINK
|
||||
}
|
||||
|
||||
func (x *UnfurledLink) GetFaviconPayload() []byte {
|
||||
if x != nil {
|
||||
return x.FaviconPayload
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type UnfurledStatusContactLink struct {
|
||||
state protoimpl.MessageState
|
||||
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,
|
||||
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,
|
||||
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, 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,
|
||||
|
@ -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,
|
||||
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,
|
||||
0x4c, 0x69, 0x6e, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0x1f,
|
||||
0x0a, 0x08, 0x4c, 0x69, 0x6e, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x12, 0x08, 0x0a, 0x04, 0x4c, 0x49,
|
||||
0x4e, 0x4b, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x49, 0x4d, 0x41, 0x47, 0x45, 0x10, 0x01, 0x22,
|
||||
0xb4, 0x01, 0x0a, 0x19, 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, 0x12, 0x1d, 0x0a,
|
||||
0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28,
|
||||
0x0c, 0x52, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x21, 0x0a, 0x0c,
|
||||
0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 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, 0x03,
|
||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f,
|
||||
0x6e, 0x12, 0x33, 0x0a, 0x04, 0x69, 0x63, 0x6f, 0x6e, 0x18, 0x04, 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, 0x22, 0xae, 0x02, 0x0a, 0x1b, 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, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e,
|
||||
0x69, 0x74, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x63, 0x6f,
|
||||
0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x74, 0x79, 0x49, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x69, 0x73,
|
||||
0x70, 0x6c, 0x61, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 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, 0x03, 0x20, 0x01, 0x28,
|
||||
0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x23,
|
||||
0x0a, 0x0d, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18,
|
||||
0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 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,
|
||||
0x4c, 0x69, 0x6e, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x27,
|
||||
0x0a, 0x0f, 0x66, 0x61, 0x76, 0x69, 0x63, 0x6f, 0x6e, 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61,
|
||||
0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0e, 0x66, 0x61, 0x76, 0x69, 0x63, 0x6f, 0x6e,
|
||||
0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x22, 0x1f, 0x0a, 0x08, 0x4c, 0x69, 0x6e, 0x6b, 0x54,
|
||||
0x79, 0x70, 0x65, 0x12, 0x08, 0x0a, 0x04, 0x4c, 0x49, 0x4e, 0x4b, 0x10, 0x00, 0x12, 0x09, 0x0a,
|
||||
0x05, 0x49, 0x4d, 0x41, 0x47, 0x45, 0x10, 0x01, 0x22, 0xb4, 0x01, 0x0a, 0x19, 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, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63,
|
||||
0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x70, 0x75, 0x62, 0x6c,
|
||||
0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79,
|
||||
0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 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, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64,
|
||||
0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x33, 0x0a, 0x04, 0x69, 0x63,
|
||||
0x6f, 0x6e, 0x18, 0x04, 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, 0x22,
|
||||
0xae, 0x02, 0x0a, 0x1b, 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, 0x12,
|
||||
0x21, 0x0a, 0x0c, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x74, 0x79, 0x5f, 0x69, 0x64, 0x18,
|
||||
0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x74, 0x79,
|
||||
0x49, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x6e, 0x61,
|
||||
0x6d, 0x65, 0x18, 0x02, 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, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63,
|
||||
0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x6d, 0x65, 0x6d, 0x62, 0x65,
|
||||
0x72, 0x73, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c,
|
||||
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,
|
||||
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,
|
||||
0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69, 0x6c, 0x52, 0x06, 0x62, 0x61, 0x6e, 0x6e, 0x65, 0x72,
|
||||
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,
|
||||
0x65, 0x64, 0x4c, 0x69, 0x6e, 0x6b, 0x54, 0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69, 0x6c, 0x52,
|
||||
0x06, 0x62, 0x61, 0x6e, 0x6e, 0x65, 0x72, 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,
|
||||
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, 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, 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,
|
||||
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 (
|
||||
|
|
|
@ -137,6 +137,7 @@ message UnfurledLink {
|
|||
uint32 thumbnail_width = 5;
|
||||
uint32 thumbnail_height = 6;
|
||||
LinkType type = 7;
|
||||
bytes favicon_payload = 8;
|
||||
|
||||
enum LinkType {
|
||||
LINK = 0;
|
||||
|
|
|
@ -34,6 +34,7 @@ const (
|
|||
discordAuthorsPath = "/discord/authors"
|
||||
discordAttachmentsPath = basePath + "/discord/attachments"
|
||||
LinkPreviewThumbnailPath = "/link-preview/thumbnail"
|
||||
LinkPreviewFaviconPath = "/link-preview/favicon"
|
||||
StatusLinkPreviewThumbnailPath = "/status-link-preview/thumbnail"
|
||||
communityTokenImagesPath = "/communityTokenImages"
|
||||
|
||||
|
|
|
@ -14,19 +14,27 @@ import (
|
|||
"github.com/status-im/status-go/protocol/protobuf"
|
||||
)
|
||||
|
||||
func getThumbnailPayload(db *sql.DB, logger *zap.Logger, msgID string, thumbnailURL string) ([]byte, error) {
|
||||
var payload []byte
|
||||
|
||||
func getUnfurledLinksFromDB(db *sql.DB, msgID string) ([]*protobuf.UnfurledLink, error) {
|
||||
var result []byte
|
||||
err := db.QueryRow(`SELECT unfurled_links FROM user_messages WHERE id = ?`, msgID).Scan(&result)
|
||||
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
|
||||
err = json.Unmarshal(result, &links)
|
||||
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 {
|
||||
|
@ -39,40 +47,82 @@ func getThumbnailPayload(db *sql.DB, logger *zap.Logger, msgID string, thumbnail
|
|||
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 {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
params := r.URL.Query()
|
||||
parsed := ParseImageParams(logger, params)
|
||||
|
||||
if parsed.MessageID == "" {
|
||||
http.Error(w, "missing query parameter 'message-id'", http.StatusBadRequest)
|
||||
return
|
||||
parsed := validateAndReturnImageParams(r, w, logger)
|
||||
if parsed.URL != "" {
|
||||
thumbnail, err := getThumbnailPayload(db, parsed.MessageID, parsed.URL)
|
||||
checkForFetchImageError(err, logger, parsed, w, "thumbnail")
|
||||
getMimeTypeAndWriteImage(w, logger, thumbnail)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if parsed.URL == "" {
|
||||
http.Error(w, "missing query parameter 'url'", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
thumbnail, err := getThumbnailPayload(db, logger, parsed.MessageID, parsed.URL)
|
||||
if err != nil {
|
||||
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))
|
||||
func handleLinkPreviewFavicon(db *sql.DB, logger *zap.Logger) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
parsed := validateAndReturnImageParams(r, w, logger)
|
||||
if parsed.URL != "" {
|
||||
favicon, err := getFaviconPayload(db, parsed.MessageID, parsed.URL)
|
||||
checkForFetchImageError(err, logger, parsed, w, "favicon")
|
||||
getMimeTypeAndWriteImage(w, logger, favicon)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -52,6 +52,7 @@ func NewMediaServer(db *sql.DB, downloader *ipfs.Downloader, multiaccountsDB *mu
|
|||
imagesPath: handleImage(s.db, s.logger),
|
||||
ipfsPath: handleIPFS(s.downloader, s.logger),
|
||||
LinkPreviewThumbnailPath: handleLinkPreviewThumbnail(s.db, s.logger),
|
||||
LinkPreviewFaviconPath: handleLinkPreviewFavicon(s.db, s.logger),
|
||||
StatusLinkPreviewThumbnailPath: handleStatusLinkPreviewThumbnail(s.db, s.logger),
|
||||
communityTokenImagesPath: handleCommunityTokenImages(s.db, s.logger),
|
||||
walletCommunityImagesPath: handleWalletCommunityImages(s.walletDB, s.logger),
|
||||
|
@ -90,6 +91,13 @@ func (s *MediaServer) MakeStatusLinkPreviewThumbnailURL(msgID string, previewURL
|
|||
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 {
|
||||
u := s.MakeBaseURL()
|
||||
u.Path = discordAuthorsPath
|
||||
|
|
Loading…
Reference in New Issue