Fix mentions extractions with punctuations

Also don't consume spaces in regex, resulting in broken mention
extractions
This commit is contained in:
Chocobozzz
2026-07-13 14:42:23 +02:00
parent 6bc19037a1
commit 09fabc2a3c
3 changed files with 19 additions and 36 deletions
@@ -1,7 +1,7 @@
/* oxlint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
import { expect } from 'chai'
import { extractMentions } from '@peertube/peertube-server/core/helpers/mentions.js'
import { extractLocalMentions } from '@peertube/peertube-server/core/helpers/mentions.js'
describe('Comment model', function () {
it('Should correctly extract mentions', async function () {
@@ -10,8 +10,13 @@ describe('Comment model', function () {
const isLocal = true
const result = extractMentions(text, isLocal).sort((a, b) => a.localeCompare(b))
const result = extractLocalMentions(text, isLocal)
expect(result).to.deep.equal([ 'another', 'chocobozzz', 'end', 'flo', 'florian', 'jean' ])
expect(result).to.deep.equal([ 'florian', 'jean', 'flo', 'another', 'chocobozzz', 'end' ])
})
it('Should correctly extract mentions with adjacent punctuations and newlines', async function () {
expect(extractLocalMentions('thanks @bob!', true)).to.deep.equal([ 'bob' ])
expect(extractLocalMentions('@bob\n', true)).to.deep.equal([ 'bob' ])
})
})
+8 -30
View File
@@ -3,38 +3,16 @@ import { WEBSERVER } from '@server/initializers/constants.js'
import { actorNameAlphabet } from './custom-validators/activitypub/actor.js'
import { regexpCapture } from './regexp.js'
export function extractMentions (text: string, isLocal: boolean) {
let result: string[] = []
export function extractLocalMentions (text: string, fromLocalEntity: boolean) {
const setHostOptionalChar = fromLocalEntity
? '?'
: ''
const localMention = `@(${actorNameAlphabet}+)`
const remoteMention = `${localMention}@${WEBSERVER.HOST}`
// FIXME: Use RegExp.escape() when NodeJS 24 is the minimum version required by peertube
const mentionRegex = new RegExp(`(?<=^|\\s)@(${actorNameAlphabet}+)(?:@${WEBSERVER.HOST})${setHostOptionalChar}(?=[\\s.,!?)]|$)`, 'gmu')
const mentionRegex = isLocal
? '(?:(?:' + remoteMention + ')|(?:' + localMention + '))' // Include local mentions?
: '(?:' + remoteMention + ')'
const firstMentionRegex = new RegExp(`^${mentionRegex} `, 'g')
const endMentionRegex = new RegExp(` ${mentionRegex}$`, 'g')
const remoteMentionsRegex = new RegExp(' ' + remoteMention + ' ', 'g')
result = result.concat(
regexpCapture(text, firstMentionRegex)
.map(([ , username1, username2 ]) => username1 || username2),
regexpCapture(text, endMentionRegex)
.map(([ , username1, username2 ]) => username1 || username2),
regexpCapture(text, remoteMentionsRegex)
const results = regexpCapture(text, mentionRegex)
.map(([ , username ]) => username)
)
// Include local mentions
if (isLocal) {
const localMentionsRegex = new RegExp(' ' + localMention + ' ', 'g')
result = result.concat(
regexpCapture(text, localMentionsRegex)
.map(([ , username ]) => username)
)
}
return uniqify(result)
return uniqify(results)
}
+2 -2
View File
@@ -9,7 +9,7 @@ import {
} from '@peertube/peertube-models'
import { afterCommitIfTransaction, retryTransactionWrapper } from '@server/helpers/database-utils.js'
import { logger } from '@server/helpers/logger.js'
import { extractMentions } from '@server/helpers/mentions.js'
import { extractLocalMentions } from '@server/helpers/mentions.js'
import { sequelizeTypescript } from '@server/initializers/database.js'
import { getLocalApproveReplyActivityPubUrl } from '@server/lib/activitypub/url.js'
import { getServerActor } from '@server/models/application/application.js'
@@ -698,7 +698,7 @@ export class VideoCommentModel extends SequelizeModel<VideoCommentModel> {
}
extractMentions () {
return extractMentions(this.text, this.isLocal())
return extractLocalMentions(this.text, this.isLocal())
}
toFormattedJSON (this: MCommentFormattable) {