Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions packages/render-helper/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# @ecency/render-helper

## 2.5.32

### Patch Changes

- fix(render-helper): strip the Lumen "Posted via" byline (#1728)

## 2.5.31

### Patch Changes
Expand Down
7 changes: 5 additions & 2 deletions packages/render-helper/dist/browser/index.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion packages/render-helper/dist/browser/index.js.map

Large diffs are not rendered by default.

7 changes: 5 additions & 2 deletions packages/render-helper/dist/node/index.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -2033,11 +2033,14 @@ function traverse(node, forApp, depth = 0, state = { firstImageFound: false }, p

// src/methods/clean-reply.method.ts
function cleanReply(s) {
const pre = s ? s.replace(/(^|\n)-{3,}[ \t]*\n+(?=[^\n]*originally posted through \[scrobble\.life)/i, "$1") : s;
const pre = s ? s.replace(
/(^|\n)-{3,}[ \t]*\n+(?=[^\n]*(?:originally posted through \[scrobble\.life|\*posted via lumen))/i,
"$1"
) : s;
return (pre ? pre.split("\n").filter((item) => item.toLowerCase().includes("posted using [partiko") === false).filter((item) => item.toLowerCase().includes("posted using [dapplr") === false).filter((item) => item.toLowerCase().includes("posted using [leofinance") === false).filter((item) => item.toLowerCase().includes("posted via [neoxian") === false).filter((item) => item.toLowerCase().includes("posted using [neoxian") === false).filter((item) => item.toLowerCase().includes("posted via [first context") === false).filter((item) => item.toLowerCase().includes("posted with [stemgeeks") === false).filter((item) => item.toLowerCase().includes("posted using [bilpcoin") === false).filter((item) => item.toLowerCase().includes("posted using [inleo") === false).filter((item) => item.toLowerCase().includes("posted using [sportstalksocial]") === false).filter((item) => item.toLowerCase().includes("<center><sub>[posted using aeneas.blog") === false).filter((item) => item.toLowerCase().includes("<center><sub>posted via [proofofbrain.io") === false).filter((item) => item.toLowerCase().includes("<center>posted on [hypnochain") === false).filter((item) => item.toLowerCase().includes("<center><sub>posted via [weedcash.network") === false).filter((item) => item.toLowerCase().includes("<center>posted on [naturalmedicine.io") === false).filter((item) => item.toLowerCase().includes("<center><sub>posted via [musicforlife.io") === false).filter((item) => item.toLowerCase().includes("if the truvvl embed is unsupported by your current frontend, click this link to view this story") === false).filter((item) => item.toLowerCase().includes("<center><em>posted from truvvl") === false).filter((item) => item.toLowerCase().includes('view this post <a href="https://travelfeed.io/') === false).filter((item) => item.toLowerCase().includes("read this post on travelfeed.io for the best experience") === false).filter((item) => item.toLowerCase().includes('posted via <a href="https://www.dporn.co/"') === false).filter((item) => item.toLowerCase().includes("\u25B6\uFE0F [watch on 3speak](https://3speak") === false).filter((item) => item.toLowerCase().includes("<sup><sub>posted via [inji.com]") === false).filter((item) => item.toLowerCase().includes("view this post on [liketu]") === false).filter((item) => {
const l = item.toLowerCase();
return !(l.includes("posted from liketu speak") && l.includes("auto-transcrib"));
}).filter((item) => item.toLowerCase().includes("originally posted through [scrobble.life") === false).filter((item) => item.toLowerCase().includes("[via Inbox]") === false).filter((item) => item.toLowerCase().includes("<sub>[via apps from](") === false).join("\n") : "").replace('Posted via <a href="https://d.buzz" data-link="promote-link">D.Buzz</a>', "").replace('<div class="pull-right"><a href="/@hive.engage">![](https://i.imgur.com/XsrNmcl.png)</a></div>', "").replace('<div><a href="https://engage.hivechain.app">![](https://i.imgur.com/XsrNmcl.png)</a></div>', "").replace(`<div class="text-center"><img src="https://cdn.steemitimages.com/DQmNp6YwAm2qwquALZw8PdcovDorwaBSFuxQ38TrYziGT6b/A-20.png"><a href="https://bit.ly/actifit-app"><img src="https://cdn.steemitimages.com/DQmQqfpSmcQtfrHAtzfBtVccXwUL9vKNgZJ2j93m8WNjizw/l5.png"></a><a href="https://bit.ly/actifit-ios"><img src="https://cdn.steemitimages.com/DQmbWy8KzKT1UvCvznUTaFPw6wBUcyLtBT5XL9wdbB7Hfmn/l6.png"></a></div>`, "");
}).filter((item) => item.toLowerCase().includes("originally posted through [scrobble.life") === false).filter((item) => item.toLowerCase().includes("*posted via lumen") === false).filter((item) => item.toLowerCase().includes("[via Inbox]") === false).filter((item) => item.toLowerCase().includes("<sub>[via apps from](") === false).join("\n") : "").replace('Posted via <a href="https://d.buzz" data-link="promote-link">D.Buzz</a>', "").replace('<div class="pull-right"><a href="/@hive.engage">![](https://i.imgur.com/XsrNmcl.png)</a></div>', "").replace('<div><a href="https://engage.hivechain.app">![](https://i.imgur.com/XsrNmcl.png)</a></div>', "").replace(`<div class="text-center"><img src="https://cdn.steemitimages.com/DQmNp6YwAm2qwquALZw8PdcovDorwaBSFuxQ38TrYziGT6b/A-20.png"><a href="https://bit.ly/actifit-app"><img src="https://cdn.steemitimages.com/DQmQqfpSmcQtfrHAtzfBtVccXwUL9vKNgZJ2j93m8WNjizw/l5.png"></a><a href="https://bit.ly/actifit-ios"><img src="https://cdn.steemitimages.com/DQmbWy8KzKT1UvCvznUTaFPw6wBUcyLtBT5XL9wdbB7Hfmn/l6.png"></a></div>`, "");
}

// ../../node_modules/.pnpm/remarkable@2.0.1/node_modules/remarkable/dist/esm/index.js
Expand Down
2 changes: 1 addition & 1 deletion packages/render-helper/dist/node/index.cjs.map

Large diffs are not rendered by default.

7 changes: 5 additions & 2 deletions packages/render-helper/dist/node/index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2006,11 +2006,14 @@ function traverse(node, forApp, depth = 0, state = { firstImageFound: false }, p

// src/methods/clean-reply.method.ts
function cleanReply(s) {
const pre = s ? s.replace(/(^|\n)-{3,}[ \t]*\n+(?=[^\n]*originally posted through \[scrobble\.life)/i, "$1") : s;
const pre = s ? s.replace(
/(^|\n)-{3,}[ \t]*\n+(?=[^\n]*(?:originally posted through \[scrobble\.life|\*posted via lumen))/i,
"$1"
) : s;
return (pre ? pre.split("\n").filter((item) => item.toLowerCase().includes("posted using [partiko") === false).filter((item) => item.toLowerCase().includes("posted using [dapplr") === false).filter((item) => item.toLowerCase().includes("posted using [leofinance") === false).filter((item) => item.toLowerCase().includes("posted via [neoxian") === false).filter((item) => item.toLowerCase().includes("posted using [neoxian") === false).filter((item) => item.toLowerCase().includes("posted via [first context") === false).filter((item) => item.toLowerCase().includes("posted with [stemgeeks") === false).filter((item) => item.toLowerCase().includes("posted using [bilpcoin") === false).filter((item) => item.toLowerCase().includes("posted using [inleo") === false).filter((item) => item.toLowerCase().includes("posted using [sportstalksocial]") === false).filter((item) => item.toLowerCase().includes("<center><sub>[posted using aeneas.blog") === false).filter((item) => item.toLowerCase().includes("<center><sub>posted via [proofofbrain.io") === false).filter((item) => item.toLowerCase().includes("<center>posted on [hypnochain") === false).filter((item) => item.toLowerCase().includes("<center><sub>posted via [weedcash.network") === false).filter((item) => item.toLowerCase().includes("<center>posted on [naturalmedicine.io") === false).filter((item) => item.toLowerCase().includes("<center><sub>posted via [musicforlife.io") === false).filter((item) => item.toLowerCase().includes("if the truvvl embed is unsupported by your current frontend, click this link to view this story") === false).filter((item) => item.toLowerCase().includes("<center><em>posted from truvvl") === false).filter((item) => item.toLowerCase().includes('view this post <a href="https://travelfeed.io/') === false).filter((item) => item.toLowerCase().includes("read this post on travelfeed.io for the best experience") === false).filter((item) => item.toLowerCase().includes('posted via <a href="https://www.dporn.co/"') === false).filter((item) => item.toLowerCase().includes("\u25B6\uFE0F [watch on 3speak](https://3speak") === false).filter((item) => item.toLowerCase().includes("<sup><sub>posted via [inji.com]") === false).filter((item) => item.toLowerCase().includes("view this post on [liketu]") === false).filter((item) => {
const l = item.toLowerCase();
return !(l.includes("posted from liketu speak") && l.includes("auto-transcrib"));
}).filter((item) => item.toLowerCase().includes("originally posted through [scrobble.life") === false).filter((item) => item.toLowerCase().includes("[via Inbox]") === false).filter((item) => item.toLowerCase().includes("<sub>[via apps from](") === false).join("\n") : "").replace('Posted via <a href="https://d.buzz" data-link="promote-link">D.Buzz</a>', "").replace('<div class="pull-right"><a href="/@hive.engage">![](https://i.imgur.com/XsrNmcl.png)</a></div>', "").replace('<div><a href="https://engage.hivechain.app">![](https://i.imgur.com/XsrNmcl.png)</a></div>', "").replace(`<div class="text-center"><img src="https://cdn.steemitimages.com/DQmNp6YwAm2qwquALZw8PdcovDorwaBSFuxQ38TrYziGT6b/A-20.png"><a href="https://bit.ly/actifit-app"><img src="https://cdn.steemitimages.com/DQmQqfpSmcQtfrHAtzfBtVccXwUL9vKNgZJ2j93m8WNjizw/l5.png"></a><a href="https://bit.ly/actifit-ios"><img src="https://cdn.steemitimages.com/DQmbWy8KzKT1UvCvznUTaFPw6wBUcyLtBT5XL9wdbB7Hfmn/l6.png"></a></div>`, "");
}).filter((item) => item.toLowerCase().includes("originally posted through [scrobble.life") === false).filter((item) => item.toLowerCase().includes("*posted via lumen") === false).filter((item) => item.toLowerCase().includes("[via Inbox]") === false).filter((item) => item.toLowerCase().includes("<sub>[via apps from](") === false).join("\n") : "").replace('Posted via <a href="https://d.buzz" data-link="promote-link">D.Buzz</a>', "").replace('<div class="pull-right"><a href="/@hive.engage">![](https://i.imgur.com/XsrNmcl.png)</a></div>', "").replace('<div><a href="https://engage.hivechain.app">![](https://i.imgur.com/XsrNmcl.png)</a></div>', "").replace(`<div class="text-center"><img src="https://cdn.steemitimages.com/DQmNp6YwAm2qwquALZw8PdcovDorwaBSFuxQ38TrYziGT6b/A-20.png"><a href="https://bit.ly/actifit-app"><img src="https://cdn.steemitimages.com/DQmQqfpSmcQtfrHAtzfBtVccXwUL9vKNgZJ2j93m8WNjizw/l5.png"></a><a href="https://bit.ly/actifit-ios"><img src="https://cdn.steemitimages.com/DQmbWy8KzKT1UvCvznUTaFPw6wBUcyLtBT5XL9wdbB7Hfmn/l6.png"></a></div>`, "");
}

// ../../node_modules/.pnpm/remarkable@2.0.1/node_modules/remarkable/dist/esm/index.js
Expand Down
2 changes: 1 addition & 1 deletion packages/render-helper/dist/node/index.mjs.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion packages/render-helper/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@ecency/render-helper",
"private": false,
"version": "2.5.31",
"version": "2.5.32",
"description": "Markdown+Html Render helper",
"repository": {
"type": "git",
Expand Down
34 changes: 34 additions & 0 deletions packages/render-helper/src/methods/clean-reply.method.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,40 @@ describe('cleanReply() method - Reply Cleaning', () => {
expect(result).not.toContain('scrobble.life')
})

it('should remove the Lumen post footer and its separator', () => {
const input = 'My thoughts on the release\n\n---\n*Posted via Lumen*'
const result = cleanReply(input)

expect(result.trim()).toBe('My thoughts on the release')
expect(result).not.toContain('Lumen')
expect(result).not.toContain('---')
})

it('should remove the Lumen lite-account footer with the "by <user>" suffix', () => {
const input = 'This is a comment from a Lumen lite account\n\n---\n*Posted via Lumen by testeraccount*'
const result = cleanReply(input)

expect(result.trim()).toBe('This is a comment from a Lumen lite account')
expect(result).not.toContain('Posted via')
expect(result).not.toContain('---')
})

it('should preserve unrelated horizontal rules when removing the Lumen footer', () => {
const input = 'Intro\n\n---\n\nMore thoughts\n\n---\n*Posted via Lumen by ethuser*'
const result = cleanReply(input)

expect(result).toContain('Intro\n\n---\n\nMore thoughts')
expect(result).not.toContain('Posted via')
expect(result.trim().endsWith('More thoughts')).toBe(true)
})

it('should preserve replies that merely mention Lumen', () => {
const input = 'I posted via Lumen for the first time today and it worked'
const result = cleanReply(input)

expect(result).toBe(input)
})

it('should preserve replies that merely mention scrobble.life', () => {
const input = 'I started tracking what I watch on scrobble.life recently'
const result = cleanReply(input)
Expand Down
15 changes: 11 additions & 4 deletions packages/render-helper/src/methods/clean-reply.method.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
export function cleanReply(s: string): string {
// scrobble.life's footer arrives as "---\n_Originally posted through [scrobble.life/...";
// drop the separator too (lookahead keeps unrelated horizontal rules), otherwise the
// line filter below leaves a dangling <hr> at the end of the post.
const pre = s ? s.replace(/(^|\n)-{3,}[ \t]*\n+(?=[^\n]*originally posted through \[scrobble\.life)/i, '$1') : s
// scrobble.life ("---\n_Originally posted through [scrobble.life/...") and Lumen
// ("---\n*Posted via Lumen*" or "---\n*Posted via Lumen by <user>*") both put a separator
// right before their byline; drop it too (lookahead keeps unrelated horizontal rules),
// otherwise the line filters below leave a dangling <hr> at the end of the post.
const pre = s
? s.replace(
/(^|\n)-{3,}[ \t]*\n+(?=[^\n]*(?:originally posted through \[scrobble\.life|\*posted via lumen))/i,
'$1'
)
: s
return (pre ? pre.split('\n')
.filter(item => item.toLowerCase().includes('posted using [partiko') === false)
.filter(item => item.toLowerCase().includes('posted using [dapplr') === false)
Expand Down Expand Up @@ -35,6 +41,7 @@ export function cleanReply(s: string): string {
return !(l.includes('posted from liketu speak') && l.includes('auto-transcrib'));
})
.filter(item => item.toLowerCase().includes('originally posted through [scrobble.life') === false)
.filter(item => item.toLowerCase().includes('*posted via lumen') === false)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

1. Lumen filter strips inline prose 📎 Requirement gap ≡ Correctness

The new Lumen cleanup filter removes every line containing *posted via lumen, including ordinary
user-authored prose such as I *posted via Lumen yesterday*, instead of only footer lines beginning
with that marker. Because cleanReply processes complete post bodies and summaries, this can
silently delete legitimate content and violate the requirement to preserve non-footer mentions.
Agent Prompt
## Issue description
Update the Lumen cleanup filter so it removes only actual footer lines whose content begins with `*posted via lumen`, while preserving ordinary prose that quotes, discusses, or otherwise contains the italicized phrase. Ensure the separator lookahead uses the same footer-specific matching rule so a preceding horizontal rule is not removed for ordinary content.

## Issue Context
Compliance ID 1 requires plain and proxied-user Lumen footers to be removed while ordinary mentions remain intact. `cleanReply` processes complete post bodies and post summaries, so an unrestricted substring match can cause user content to disappear. Add coverage for prose containing the italicized phrase and for a horizontal rule before such prose, while preserving removal of the actual footer and its optional `by <user>` suffix.

## Fix Focus Areas
- packages/render-helper/src/methods/clean-reply.method.ts[8-10]
- packages/render-helper/src/methods/clean-reply.method.ts[44-44]
- packages/render-helper/src/methods/clean-reply.method.spec.ts[280-285]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Restrict the Lumen filter to the actual footer

When ordinary prose contains an italicized phrase such as I *posted via Lumen* yesterday, this substring predicate removes the entire line. Because cleanReply processes all rendered bodies rather than only entries tagged lumen/1.0, legitimate non-footer content is lost; anchor the match to the complete terminal byline, including its closing * and optional by ... suffix.

Useful? React with 👍 / 👎.

.filter(item => item.toLowerCase().includes('[via Inbox]') === false)
.filter(item => item.toLowerCase().includes('<sub>[via apps from](') === false)
.join('\n') : '')
Expand Down
Loading