Skip to content
Snippets Groups Projects
Unverified Commit bd3f6384 authored by Sumner Evans's avatar Sumner Evans
Browse files

headers: update to make it look more like the browser

parent 89f1b826
No related branches found
No related tags found
No related merge requests found
Pipeline #16503 passed
......@@ -3,11 +3,11 @@
* Matrix → LinkedIn
* [ ] Message content
* [x] Text
* [ ] Media
* [ ] Files
* [ ] Images
* [ ] Videos
* [ ] GIFs
* [x] Media
* [x] Files
* [x] Images
* [x] Videos (sent as files)
* [x] GIFs
* [ ] Voice Messages
* [ ] ~~Stickers~~ (unsupported)
* [ ] ~~Formatting~~ (LinkedIn does not support rich formatting)
......
......@@ -72,7 +72,7 @@ var fileCaps = event.FileFeatureMap{
},
event.MsgVideo: {
MimeTypes: map[string]event.CapabilitySupportLevel{
"video/mp4": event.CapLevelFullySupported,
"video/mp4": event.CapLevelPartialSupport,
},
Caption: event.CapLevelFullySupported,
MaxCaptionLength: MaxCaptionLength,
......
......@@ -46,7 +46,10 @@ type Client struct {
func NewClient(ctx context.Context, userEntityURN types.URN, jar *stringcookiejar.Jar, handlers Handlers) *Client {
return &Client{
userEntityURN: userEntityURN,
userEntityURN: userEntityURN,
jar: jar,
realtimeSessionID: uuid.New(),
handlers: handlers,
http: &http.Client{
Jar: jar,
......@@ -56,11 +59,6 @@ func NewClient(ctx context.Context, userEntityURN types.URN, jar *stringcookieja
return http.ErrUseLastResponse
},
},
jar: jar,
realtimeSessionID: uuid.New(),
handlers: handlers,
}
}
......
......@@ -92,7 +92,7 @@ func (c *Client) UploadMedia(ctx context.Context, mediaUploadType MediaUploadTyp
resp, err := c.newAuthedRequest(http.MethodPost, linkedInVoyagerMediaUploadMetadataURL).
WithParam("action", "upload").
WithCSRF().
WithRealtimeHeaders().
WithXLIHeaders().
WithHeader("accept", contentTypeJSONLinkedInNormalized).
WithContentType(contentTypeJSONPlaintextUTF8).
WithJSONPayload(UploadMediaMetadataPayload{
......
......@@ -84,7 +84,7 @@ func (c *Client) SendMessage(ctx context.Context, conversationURN types.URN, bod
WithParam("action", "createMessage").
WithCSRF().
WithContentType(contentTypePlaintextUTF8).
WithRealtimeHeaders().
WithXLIHeaders().
Do(ctx)
if err != nil {
return nil, err
......@@ -120,7 +120,7 @@ func (c *Client) EditMessage(ctx context.Context, messageURN types.URN, p SendMe
WithCSRF().
WithJSONPayload(GraphQLPatchBody{Patch: Patch{Set: EditMessagePayload{Body: p}}}).
WithHeader("accept", contentTypeJSON).
WithRealtimeHeaders().
WithXLIHeaders().
Do(ctx)
if err != nil {
return err
......@@ -134,7 +134,7 @@ func (c *Client) RecallMessage(ctx context.Context, messageURN types.URN) error
resp, err := c.newAuthedRequest(http.MethodPost, linkedInVoyagerMessagingDashMessengerMessagesURL).
WithParam("action", "recall").
WithCSRF().
WithRealtimeHeaders().
WithXLIHeaders().
WithJSONPayload(map[string]any{"messageUrn": messageURN}).
Do(ctx)
if err != nil {
......
......@@ -178,6 +178,12 @@ func (c *Client) runHeartbeatsLoop(ctx context.Context) {
log.Debug().Stringer("realtime_session_id", c.realtimeSessionID).Msg("Sending heartbeat")
_, err := c.newAuthedRequest(http.MethodPost, linkedInRealtimeHeartbeatURL).
WithHeader("accept", "*/*").
WithContentType(contentTypePlaintextUTF8).
WithCSRF().
WithHeader("origin", "https://www.linkedin.com").
WithHeader("Priority", "u=1, i").
WithXLIHeaders().
WithJSONPayload(map[string]any{
"isFirstHeartbeat": !isFirst,
"isLastHeartbeat": false,
......@@ -188,8 +194,6 @@ func (c *Client) runHeartbeatsLoop(ctx context.Context) {
"actorUrn": userURN,
"contextUrns": []string{userURN},
}).
WithCSRF().
WithRealtimeHeaders().
Do(ctx)
if err != nil {
log.Err(err).Msg("Failed to send heartbeat")
......@@ -222,7 +226,7 @@ func (c *Client) realtimeConnectLoop(ctx context.Context) {
var err error
c.realtimeResp, err = c.newAuthedRequest(http.MethodGet, linkedInRealtimeConnectURL).
WithCSRF().
WithRealtimeHeaders().
WithRealtimeConnectHeaders().
WithHeader("Accept", contentTypeTextEventStream).
Do(ctx)
if err != nil {
......
......@@ -31,7 +31,21 @@ type authedRequest struct {
func (c *Client) newAuthedRequest(method, urlStr string) *authedRequest {
ar := authedRequest{header: http.Header{}, method: method, client: c}
ar.url, ar.parseErr = url.Parse(urlStr)
ar.params = ar.url.Query()
if ar.parseErr != nil {
ar.params = ar.url.Query()
} else {
ar.params = url.Values{}
}
// Add default headers for every request
ar.header.Add("User-Agent", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36")
ar.header.Add("Accept-Language", "en-US,en;q=0.9")
ar.header.Add("sec-ch-prefers-color-scheme", "light")
ar.header.Add("sec-ch-ua", `"Chromium";v="131", "Not_A Brand";v="24"`)
ar.header.Add("sec-ch-ua-mobile", "?0")
ar.header.Add("sec-ch-ua-platform", `"Linux"`)
return &ar
}
......@@ -63,22 +77,34 @@ func (a *authedRequest) WithContentType(contentType string) *authedRequest {
return a.WithHeader("content-type", contentType)
}
func (a *authedRequest) WithRealtimeHeaders() *authedRequest {
func (a *authedRequest) WithXLIHeaders() *authedRequest {
return a.
WithHeader("Referer", linkedInMessagingBaseURL+"/").
WithHeader("X-LI-Accept", contentTypeJSONLinkedInNormalized).
WithHeader("X-LI-Page-Instance", "urn:li:page:messaging_index;"+a.client.clientPageInstanceID).
WithHeader("X-LI-Query-Accept", contentTypeGraphQL).
WithHeader("X-LI-Query-Map", realtimeQueryMap).
WithHeader("X-LI-Realtime-Session", a.client.realtimeSessionID.String()).
WithHeader("X-LI-Recipe-Accept", contentTypeJSONLinkedInNormalized).
WithHeader("X-LI-Recipe-Map", realtimeRecipeMap).
WithHeader("X-LI-Track", a.client.xLITrack).
WithHeader("X-RestLI-Protocol-Version", "2.0.0")
}
func (a *authedRequest) WithRealtimeConnectHeaders() *authedRequest {
return a.
WithHeader("referer", linkedInMessagingBaseURL+"/").
WithHeader("x-li-accept", contentTypeJSONLinkedInNormalized).
WithHeader("x-li-page-instance", "urn:li:page:messaging_index;"+a.client.clientPageInstanceID).
WithHeader("x-li-query-accept", contentTypeGraphQL).
WithHeader("x-li-query-map", realtimeQueryMap).
WithHeader("x-li-realtime-session", a.client.realtimeSessionID.String()).
WithHeader("x-li-recipe-accept", contentTypeJSONLinkedInNormalized).
WithHeader("x-li-recipe-map", realtimeRecipeMap).
WithHeader("x-li-track", a.client.xLITrack).
WithHeader("x-restli-protocol-version", "2.0.0")
WithHeader("Priority", "u=1, i").
WithHeader("Sec-Fetch-Dest", "empty").
WithHeader("Sec-Fetch-Mode", "cors").
WithHeader("Sec-Fetch-Site", "same-origin").
WithXLIHeaders()
}
func (a *authedRequest) WithWebpageHeaders() *authedRequest {
return a.
WithHeader("Priority", "u=0, i").
WithHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7").
WithHeader("Cache-Control", "max-age=0").
WithHeader("Sec-Fetch-Dest", "document").
WithHeader("Sec-Fetch-Mode", "navigate").
WithHeader("Sec-Fetch-Site", "none").
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment