Skip to content

feat: show images in album notifications - #226

Merged
highesttt merged 2 commits into
mainfrom
highest/plat-38153
Jul 27, 2026
Merged

feat: show images in album notifications#226
highesttt merged 2 commits into
mainfrom
highest/plat-38153

Conversation

@highesttt

Copy link
Copy Markdown
Collaborator

Summary

  • show the available album preview images in Matrix
  • download album media from the OBS paths provided by LINE
  • keep invalid LINE post links removed
  • skip unavailable previews while preserving successful images

Testing

  • go test -short -vet=off ./... -count=1
  • go vet ./pkg/line ./pkg/connector/...
  • staticcheck across the repository, excluding generated pkg/ltsm

@linear-code

linear-code Bot commented Jul 27, 2026

Copy link
Copy Markdown

PLAT-38153

@indent-zero

indent-zero Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor
PR Summary

Renders LINE album post notifications with inline preview images instead of a plaintext "Open in LINE" link. Album previews are fetched from LINE's dedicated album/a/{OID}/f482x482 endpoint, authenticated with a ChannelService.issueChannelToken-issued channel token plus the notification's chatId/albumIdV2, and appended as m.image parts alongside the existing m.notice. Downloads run in a bounded worker pool (limit 4) with fail-fast cancellation. As a side effect the postEndUrl deep link is no longer rendered in the notice body for any post-notification variant (notes, unknown types, or albums).

  • pkg/line/client.go: added DownloadAlbumPreview(ctx, oid, chatID, albumID) that hits album/a/{oid}/f482x482 with X-Line-ChannelToken, X-Line-Mid, optional X-Line-Album, and X-Line-Access headers; refactored DownloadOBSWithSIDOptions through a shared downloadOBSWithServiceAndSIDOptions and added DownloadOBSResource for non-talk OBS services; introduced Client.channelTokenCache guarded by a per-client mutex.
  • pkg/line/methods.go: added AcquireChannelAccessToken(channelID) (cached, dedup'd ChannelService.issueChannelToken RPC) and parseChannelTokenExpiration supporting numeric (TTL / unix seconds / unix milliseconds), string RFC3339, and null expiration formats with a 5-minute fallback.
  • pkg/connector/handlers/handler.go: added DownloadOBSResource and DownloadAlbumPreview test hooks with internal wrappers.
  • pkg/connector/handlers/post_notification.go: ConvertPostNotification now takes ctx, portal, intent; parses previewMedias JSON (filtering to svc=album/sid=a/mediaType=I and de-duplicating by OID) with a top-level mediaOid fallback; parses chatId + albumIdV2/albumId from postEndUrl; runs downloads through a bounded worker pool with sync.Once + context.WithCancel cancellation; expired objects (ErrOBSObjectNotFound) are logged and skipped, other download errors bubble up wrapped in bridgev2.ErrIgnoringRemoteEvent (retryable), auth failures go through tryRecoverClient; missing chatId returns a notice-only conversion without attempting downloads. Removes the postEndUrl plain/HTML link rendering.
  • pkg/connector/handle_message.go: forwards ctx, portal, intent to ConvertPostNotification.
  • Extensive new tests: album image bridging with dedup, top-level mediaOid fallback on malformed previewMedias, expired-image skip, retryable transient failure, worker-pool concurrency + peak-4-workers verification, fail-fast cancellation of queued jobs, chatId-missing short-circuit, parseAlbumPreviewContext cases, and OBS request-shape verification (album/a/{oid}/f482x482 path + channel-token header set, X-Line-Application absent).

Issues

2 potential issues found:

  • Post notifications no longer include the postEndUrl deep link for any service type (notes, unknown, and albums whose previews fail to download), even though the commit only advertises "show images in album notifications" — please confirm the "Open in LINE" link removal for non-album/degraded cases is intentional. → Autofix
  • Album image PartIDs are album-image-<1-based position in filtered previewMedias>, so if LINE reorders previewMedias or a previously-expired preview becomes downloadable on backfill retry, surviving images shift IDs and can be duplicated as new Matrix parts — consider keying on the OID (e.g. album-image-<oid>) for stable idempotency. → Autofix
1 issue already resolved
  • Album previews are downloaded and uploaded strictly serially, so a 9-image album serializes ~18 OBS round-trips (preflight + object) plus 9 Matrix uploads before the message finishes bridging; consider a small bounded worker pool if album latency becomes noticeable. (fixed by commit c6f1c77)

CI Checks

All CI checks passed at commit 4314232.


⚡ Autofix All Issues

@coderabbitai

coderabbitai Bot commented Jul 27, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Changes

Post notification media conversion

Layer / File(s) Summary
Channel token and OBS access
pkg/line/client.go, pkg/line/methods.go, pkg/line/obs_test.go
LINE channel tokens are cached, OBS URLs accept service, SID, and OID segments, and album preview requests use dedicated headers and error classification.
Album preview conversion
pkg/connector/handlers/post_notification.go
Post notifications produce plain-text notices and optionally parse, download, filter, upload, and append album preview images concurrently.
Handler wiring and validation
pkg/connector/handlers/handler.go, pkg/connector/handle_message.go, pkg/connector/handlers/post_notification_test.go, pkg/connector/handle_message_test.go
Conversion dependencies are injectable, dispatch passes Matrix context, and tests cover media ordering, fallback, cancellation, partial failures, and plain-text output.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant LINE
  participant Handler
  participant OBS
  participant Matrix
  LINE->>Handler: deliver post notification
  Handler->>OBS: download album preview resource
  OBS-->>Handler: return image bytes
  Handler->>Matrix: upload image
  Matrix-->>Handler: return MXC URI
  Handler-->>LINE: produce notice and image parts
Loading

Possibly related PRs

  • beeper/line#225: Changes the same post-notification dispatch and conversion flow.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title matches the main change: adding album notification images.
Description check ✅ Passed The description accurately summarizes album preview images, OBS downloads, link cleanup, and skipped unavailable previews.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch highest/plat-38153

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
pkg/connector/handlers/post_notification.go (1)

93-113: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Cap the number of previews downloaded per notification.

previewMedias comes straight from remote metadata with no length limit, so a single notification can trigger an unbounded serial sequence of OBS downloads plus Matrix uploads on the message-handling path. A small ceiling (LINE shows at most a handful of previews) bounds latency and memory.

♻️ Suggested cap
+	const maxAlbumPreviews = 9
+	if len(previewMedias) > maxAlbumPreviews {
+		previewMedias = previewMedias[:maxAlbumPreviews]
+	}
 	client := h.NewClient()
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/connector/handlers/post_notification.go` around lines 93 - 113, Cap the
preview processing in the loop over previewMedias to a small fixed maximum
before invoking downloadOBSResource or subsequent uploads. Preserve the existing
handling for expired and other download errors, and ensure notifications with
more previews stop processing once the cap is reached.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@pkg/connector/handlers/post_notification.go`:
- Around line 93-113: Cap the preview processing in the loop over previewMedias
to a small fixed maximum before invoking downloadOBSResource or subsequent
uploads. Preserve the existing handling for expired and other download errors,
and ensure notifications with more previews stop processing once the cap is
reached.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e9e5e909-f3de-44ba-a16e-0f96e26d24da

📥 Commits

Reviewing files that changed from the base of the PR and between 46b92d1 and 4314232.

📒 Files selected for processing (7)
  • pkg/connector/handle_message.go
  • pkg/connector/handle_message_test.go
  • pkg/connector/handlers/handler.go
  • pkg/connector/handlers/post_notification.go
  • pkg/connector/handlers/post_notification_test.go
  • pkg/line/client.go
  • pkg/line/obs_test.go
📜 Review details
⏰ Context from checks skipped due to timeout. (4)
  • GitHub Check: Lint with 1.25
  • GitHub Check: build-docker
  • GitHub Check: Lint with 1.25
  • GitHub Check: build-docker
🧰 Additional context used
📓 Path-based instructions (2)
**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*.go: Use go fmt for code formatting across all Go files
Use goimports with -local "github.com/highesttt/matrix-line-messenger" flag to group project-local imports correctly
Use zerolog for logging throughout the codebase
Do not use Msgf in logging; use Msg with structured fields instead
Use Stringer interface where applicable in Go code

Files:

  • pkg/connector/handle_message_test.go
  • pkg/line/obs_test.go
  • pkg/connector/handlers/handler.go
  • pkg/line/client.go
  • pkg/connector/handle_message.go
  • pkg/connector/handlers/post_notification_test.go
  • pkg/connector/handlers/post_notification.go
**/!(ltsm)/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

**/!(ltsm)/**/*.go: Run staticcheck on all Go files excluding pkg/ltsm package (transpiled WASM code)
Run go vet on all Go files excluding pkg/ltsm package (transpiled WASM code)

Files:

  • pkg/connector/handle_message_test.go
  • pkg/line/obs_test.go
  • pkg/connector/handlers/handler.go
  • pkg/line/client.go
  • pkg/connector/handle_message.go
  • pkg/connector/handlers/post_notification_test.go
  • pkg/connector/handlers/post_notification.go
🔇 Additional comments (11)
pkg/line/client.go (2)

719-727: LGTM!


704-718: 🎯 Functional Correctness

Confirm empty-SID callers. The new non-empty sid check also applies to DownloadOBSWithSIDOptions; any existing caller that still passes an empty SID will now fail.

pkg/line/obs_test.go (1)

91-133: LGTM!

pkg/connector/handlers/post_notification.go (4)

4-33: LGTM!


115-146: LGTM!


148-194: LGTM!


196-208: 🎯 Functional Correctness

WebP previews are already supported http.DetectContentType returns image/webp in the Go toolchain targeted here, so this branch won’t silently drop WebP images.

			> Likely an incorrect or invalid review comment.
pkg/connector/handlers/handler.go (1)

32-44: LGTM!

pkg/connector/handle_message.go (1)

380-380: LGTM!

pkg/connector/handlers/post_notification_test.go (1)

19-27: LGTM!

Also applies to: 86-154, 156-189, 191-232, 234-265, 282-283

pkg/connector/handle_message_test.go (1)

125-125: LGTM!

} else {
body.WriteString("\n\nOpen LINE for full details.")
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The previous implementation appended Open in LINE: <postEndUrl> (plain + HTML anchor) for every notification, and Open LINE for full details. when the URL was missing. Both branches are gone now, so GB notes, unknown service types, and albums that produce zero image parts (all previews expired/unsupported, or intent/portal missing) drop back to a bare notice with no way to reach the original post. The commit message only mentions adding album images, so this collateral UX change looks worth calling out explicitly. If it is intentional, consider updating the commit message; otherwise, keeping the link for non-AB types (and as a fallback when no image parts get appended) would preserve the previous behavior.

}

converted.Parts = append(converted.Parts, &bridgev2.ConvertedMessagePart{
ID: networkid.PartID(fmt.Sprintf("album-image-%d", index+1)),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

index here is the position in the post-filter/dedupe previewMedias slice, not the count of successfully-uploaded images (the test intentionally expects album-image-2 when the first entry was expired). Two consequences:

  1. If a re-process of the same message runs after an expired preview becomes downloadable, the surviving image that was album-image-2 last time will be album-image-1 this time, and bridgev2 will treat them as different parts (potential duplicate upload).
  2. If LINE ever returns previewMedias in a different order between backfill and live paths, the same OID gets a different PartID.

Using the OID (already unique per media after the dedupe map) would make part identity stable across retries and orderings — something like networkid.PartID("album-image-" + media.OID).

Comment thread pkg/connector/handlers/post_notification.go Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@pkg/line/client.go`:
- Around line 726-777: Add a finite timeout to the OBS client used by
DownloadAlbumPreview so stalled requests cannot block workers indefinitely when
the caller context has no deadline. Update the OBSClient initialization in
NewClient, or derive a bounded context before the OBS request, while preserving
caller cancellation and existing download behavior.

In `@pkg/line/methods.go`:
- Around line 781-783: Update the RFC3339 parsing branch in the expiration-time
method to apply the same near-expiry validation as the numeric paths: compare
parsedTime against now.Add(obsTokenBuffer), and return the existing fallback
when it is too early; otherwise preserve the parsed expiration adjusted by
obsTokenBuffer.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 9ac34a2d-bda7-47a6-b6c7-886de592e1c1

📥 Commits

Reviewing files that changed from the base of the PR and between 4314232 and c6f1c77.

📒 Files selected for processing (6)
  • pkg/connector/handlers/handler.go
  • pkg/connector/handlers/post_notification.go
  • pkg/connector/handlers/post_notification_test.go
  • pkg/line/client.go
  • pkg/line/methods.go
  • pkg/line/obs_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • pkg/connector/handlers/post_notification.go
📜 Review details
⏰ Context from checks skipped due to timeout. (4)
  • GitHub Check: Lint with 1.25
  • GitHub Check: build-docker
  • GitHub Check: build-docker
  • GitHub Check: Lint with 1.25
🧰 Additional context used
📓 Path-based instructions (2)
**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*.go: Use go fmt for code formatting across all Go files
Use goimports with -local "github.com/highesttt/matrix-line-messenger" flag to group project-local imports correctly
Use zerolog for logging throughout the codebase
Do not use Msgf in logging; use Msg with structured fields instead
Use Stringer interface where applicable in Go code

Files:

  • pkg/line/methods.go
  • pkg/line/obs_test.go
  • pkg/connector/handlers/handler.go
  • pkg/connector/handlers/post_notification_test.go
  • pkg/line/client.go
**/!(ltsm)/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

**/!(ltsm)/**/*.go: Run staticcheck on all Go files excluding pkg/ltsm package (transpiled WASM code)
Run go vet on all Go files excluding pkg/ltsm package (transpiled WASM code)

Files:

  • pkg/line/methods.go
  • pkg/line/obs_test.go
  • pkg/connector/handlers/handler.go
  • pkg/connector/handlers/post_notification_test.go
  • pkg/line/client.go
🔇 Additional comments (7)
pkg/line/client.go (2)

33-34: LGTM!

Also applies to: 49-63


716-724: 🗄️ Data Integrity & Integration

No changes needed for DownloadOBSResource — it delegates to downloadOBSWithServiceAndSIDOptions, which validates service/sid/oid and escapes the OBS path components before building the request.

pkg/line/methods.go (1)

719-767: LGTM!

pkg/connector/handlers/handler.go (1)

32-47: LGTM!

pkg/connector/handlers/post_notification_test.go (2)

23-177: LGTM!

Also applies to: 307-537


179-250: 🎯 Functional Correctness

No issue: for range <int> is valid here. The module targets Go 1.25.0, so both for range albumPreviewWorkerLimit and for range 2 compile as written.

			> Likely an incorrect or invalid review comment.
pkg/line/obs_test.go (1)

91-133: LGTM!

Also applies to: 424-473

Comment thread pkg/line/client.go
Comment on lines +726 to +777
// DownloadAlbumPreview retrieves the thumbnail referenced by an album post
// notification. Album thumbnails use a dedicated TID and are authorized by the
// notification's chatId through LINE's Chrome home channel token.
func (c *Client) DownloadAlbumPreview(ctx context.Context, oid, chatID, albumID string) ([]byte, error) {
if oid == "" || chatID == "" {
return nil, errors.New("album preview OID and chat ID are required")
}
channelToken, err := c.AcquireChannelAccessToken(albumPreviewChannelID)
if err != nil {
return nil, fmt.Errorf("failed to acquire album preview channel token: %w", err)
}

requestURL := fmt.Sprintf(
"%s/r/album/a/%s/%s",
OBSBaseURL,
url.PathEscape(oid),
albumPreviewTID,
)
req, err := http.NewRequestWithContext(ctx, http.MethodGet, requestURL, nil)
if err != nil {
return nil, fmt.Errorf("failed to create album preview request: %w", err)
}
req.Header.Set("User-Agent", UserAgent)
req.Header.Set("X-Line-ChannelToken", channelToken)
req.Header.Set("X-Line-Mid", chatID)
if albumID != "" {
req.Header.Set("X-Line-Album", albumID)
}
if c.AccessToken != "" {
req.Header.Set("X-Line-Access", c.AccessToken)
}

resp, err := c.obsHTTPClient().Do(req)
if err != nil {
return nil, fmt.Errorf("OBS download request failed: %w", err)
}
body, readErr := io.ReadAll(resp.Body)
resp.Body.Close()
if readErr != nil {
return nil, fmt.Errorf("failed to read OBS response body: %w", readErr)
}
switch resp.StatusCode {
case http.StatusOK:
return body, nil
case http.StatusAccepted:
return nil, ErrOBSEncodingIncomplete
case http.StatusNotFound:
return nil, ErrOBSObjectNotFound
default:
return nil, fmt.Errorf("OBS download failed (%d): %s", resp.StatusCode, string(body))
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Confirm whether the context passed into DownloadAlbumPreview/ConvertPostNotification carries a deadline upstream.
rg -n -A5 -B5 'ConvertPostNotification\(' pkg/connector/handle_message.go
rg -n 'context.WithTimeout|context.WithDeadline' pkg/connector

Repository: beeper/line

Length of output: 1148


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Map the relevant files and inspect the exact call path / client construction.
git ls-files 'pkg/line/client.go' 'pkg/connector/handle_message.go' 'pkg/connector/*.go' | sort

echo '--- client.go outline ---'
ast-grep outline pkg/line/client.go --view expanded

echo '--- handle_message.go outline ---'
ast-grep outline pkg/connector/handle_message.go --view expanded

echo '--- relevant snippets ---'
rg -n -A30 -B10 'func \(c \*Client\) NewClient|func \(c \*Client\) obsHTTPClient|type Client struct|func \(c \*Client\) DownloadAlbumPreview|func \(.*ConvertPostNotification|album-preview|worker pool|concurrently|WithTimeout|WithDeadline' pkg/line/client.go pkg/connector/handle_message.go pkg/connector/*.go

Repository: beeper/line

Length of output: 23837


🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n -A20 -B10 'OBSClient|HTTPClient|rpcClientTimeout|Timeout:|obsHTTPClient\(\)|DownloadAlbumPreview\(' pkg/line/client.go pkg/connector/handle_message.go pkg/connector/*.go

# Narrow search for album preview execution details and context handling.
rg -n -A20 -B10 'DownloadAlbumPreview|album preview|album-preview|preview' pkg/connector pkg/line

Repository: beeper/line

Length of output: 50368


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the client constructor and OBS HTTP client selection.
ast-grep outline pkg/line/client.go --view expanded | sed -n '1,260p'

echo '--- constructor/client fields ---'
rg -n -A30 -B10 'func NewClient|type Client struct|OBSClient|HTTPClient|rpcClientTimeout|obsHTTPClient\(\)' pkg/line/client.go

echo '--- album-preview flow ---'
rg -n -A40 -B10 'album preview|album-preview|DownloadAlbumPreview|ConvertPostNotification|worker|pool|concurr|errOBSEncodingIncomplete|ErrOBSObjectNotFound' pkg/connector pkg/line

Repository: beeper/line

Length of output: 50368


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '1,220p' pkg/line/client.go | nl -ba | sed -n '1,220p'

echo '--- later client.go region ---'
sed -n '220,420p' pkg/line/client.go | nl -ba | sed -n '220,420p'

Repository: beeper/line

Length of output: 189


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Focus on the post-notification conversion path and any album-preview concurrency.
rg -n -A40 -B20 'ConvertPostNotification|album preview|album-preview|DownloadAlbumPreview|errgroup|WaitGroup|worker pool|concurrently|TestConvertPostNotificationProcessesAlbumPreviewsConcurrentlyInOrder' pkg/connector pkg/line

Repository: beeper/line

Length of output: 50367


🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n -A25 -B10 'func \(lc \*LineClient\) newClient|func newLineAPIClient|return &line.Client|OBSClient|HTTPClient: .*Timeout|NewClient\(' pkg/connector/client.go pkg/connector/connector.go pkg/connector/handlers/post_notification.go pkg/connector/handle_message.go

Repository: beeper/line

Length of output: 9280


🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n -A20 -B10 'func newLineAPIClient|newLineAPIClient\(' pkg/connector pkg/line

Repository: beeper/line

Length of output: 13529


Add a timeout to the OBS client used for album previews. The album-preview path runs 4 concurrent workers, and newLineAPIClient() builds OBSClient with no timeout, so a stalled OBS download can block a worker indefinitely when the caller context has no deadline. Set a timeout on OBSClient in NewClient or bound the context before calling DownloadAlbumPreview.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/line/client.go` around lines 726 - 777, Add a finite timeout to the OBS
client used by DownloadAlbumPreview so stalled requests cannot block workers
indefinitely when the caller context has no deadline. Update the OBSClient
initialization in NewClient, or derive a bounded context before the OBS request,
while preserving caller cancellation and existing download behavior.

Comment thread pkg/line/methods.go
Comment on lines +781 to +783
if parsedTime, parseErr := time.Parse(time.RFC3339, stringValue); parseErr == nil {
return parsedTime.Add(-obsTokenBuffer)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

RFC3339 branch skips the near-expiry fallback check applied elsewhere.

The numeric paths (lines 792-804) reject/replace an expiresAt that is too close to now (expiresAt.Before(now.Add(obsTokenBuffer))fallback), but the RFC3339 string branch returns parsedTime.Add(-obsTokenBuffer) unconditionally. If the server ever returns an RFC3339 expiration at or near "now", the cached token would be treated as immediately (or nearly) expired, silently defeating the cache for that response shape instead of falling back to defaultChannelTokenLifetime like the numeric path does.

🛠️ Proposed fix to align RFC3339 handling with the numeric path
 		if parsedTime, parseErr := time.Parse(time.RFC3339, stringValue); parseErr == nil {
-			return parsedTime.Add(-obsTokenBuffer)
+			expiresAt := parsedTime.Add(-obsTokenBuffer)
+			if expiresAt.Before(now.Add(obsTokenBuffer)) {
+				return fallback
+			}
+			return expiresAt
 		}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if parsedTime, parseErr := time.Parse(time.RFC3339, stringValue); parseErr == nil {
return parsedTime.Add(-obsTokenBuffer)
}
if parsedTime, parseErr := time.Parse(time.RFC3339, stringValue); parseErr == nil {
expiresAt := parsedTime.Add(-obsTokenBuffer)
if expiresAt.Before(now.Add(obsTokenBuffer)) {
return fallback
}
return expiresAt
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/line/methods.go` around lines 781 - 783, Update the RFC3339 parsing
branch in the expiration-time method to apply the same near-expiry validation as
the numeric paths: compare parsedTime against now.Add(obsTokenBuffer), and
return the existing fallback when it is too early; otherwise preserve the parsed
expiration adjusted by obsTokenBuffer.

@highesttt
highesttt merged commit 67e653a into main Jul 27, 2026
10 checks passed
@highesttt
highesttt deleted the highest/plat-38153 branch July 27, 2026 22:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant