check
This commit is contained in:
@@ -48,7 +48,10 @@ async function invalidateCachePattern (pattern) {
|
||||
for await (const key of redis.scanIterator({ MATCH: pattern, COUNT: 100 })) {
|
||||
keys.push(key)
|
||||
}
|
||||
if (keys.length) await redis.del(keys)
|
||||
// این کلاینتِ redis برای del چندکلیدی آرگومانهای جدا میخواد، نه یه آرایه —
|
||||
// پاسدادنِ مستقیمِ آرایه با خطای "arguments[1] must be of type string|Buffer"
|
||||
// silently fail میشد و کشِ باطلنشده میموند
|
||||
if (keys.length) await redis.del(...keys)
|
||||
} catch (err) {
|
||||
console.error(`[cache] pattern invalidate error for ${pattern}:`, err.message)
|
||||
}
|
||||
|
||||
@@ -2,15 +2,17 @@ const { resolveCommentModerationStatus } = require('./contentModeration')
|
||||
|
||||
const STATUS_FALLBACKS = {
|
||||
temp_accept: ['temp_accept', 'accepted', 'pending'],
|
||||
rejected: ['rejected', 'pending'],
|
||||
rejected: ['rejected', 'pending']
|
||||
}
|
||||
|
||||
/**
|
||||
* Save comment with moderation status; fall back if server schema lacks newer enum values.
|
||||
* verifyBadge: تیکِ وریفایِ کاربرِ سازنده (creator) — اگه طلایی/سبز/آبی نباشه،
|
||||
* status روی pending میمونه تا از پنل ادمین دستی تایید بشه
|
||||
*/
|
||||
async function saveCommentWithModerationStatus(Model, data, textField = 'comment') {
|
||||
async function saveCommentWithModerationStatus (Model, data, textField = 'comment', verifyBadge) {
|
||||
const text = data[textField] ?? data.text ?? ''
|
||||
const preferredStatus = resolveCommentModerationStatus(text)
|
||||
const preferredStatus = resolveCommentModerationStatus(text, verifyBadge)
|
||||
const candidates = STATUS_FALLBACKS[preferredStatus] ?? [preferredStatus]
|
||||
|
||||
let lastError = null
|
||||
@@ -34,5 +36,5 @@ async function saveCommentWithModerationStatus(Model, data, textField = 'comment
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
saveCommentWithModerationStatus,
|
||||
saveCommentWithModerationStatus
|
||||
}
|
||||
|
||||
@@ -1,63 +1,76 @@
|
||||
/**
|
||||
* Moderation helpers for comments and posts (Persian profanity + explicit cues).
|
||||
*/
|
||||
const { isVerifiedBadge } = require('./verifyBadge')
|
||||
|
||||
const PROFANITY_PATTERNS = [
|
||||
/\b(کس|کیر|کون|جنده|حروم|لاشی|بی\s*شعور|گه|گایید|گائید)\b/i,
|
||||
/\b(fuck|shit|bitch|asshole)\b/i,
|
||||
];
|
||||
/\b(fuck|shit|bitch|asshole)\b/i
|
||||
]
|
||||
|
||||
const EXPLICIT_CONTENT_PATTERNS = [
|
||||
/\b(برهنگ|برهنگی|پورن|porn|nude|naked|sex)\b/i,
|
||||
/\b(سکس|جنسی\s*صریح)\b/i,
|
||||
];
|
||||
/\b(سکس|جنسی\s*صریح)\b/i
|
||||
]
|
||||
|
||||
function normalizeText(text) {
|
||||
if (!text || typeof text !== 'string') return '';
|
||||
function normalizeText (text) {
|
||||
if (!text || typeof text !== 'string') return ''
|
||||
return text
|
||||
.replace(/[\u200c\u200f]/g, '')
|
||||
.replace(/\s+/g, ' ')
|
||||
.trim();
|
||||
.trim()
|
||||
}
|
||||
|
||||
function matchesAny(text, patterns) {
|
||||
const normalized = normalizeText(text);
|
||||
if (!normalized) return false;
|
||||
return patterns.some((pattern) => pattern.test(normalized));
|
||||
function matchesAny (text, patterns) {
|
||||
const normalized = normalizeText(text)
|
||||
if (!normalized) return false
|
||||
return patterns.some((pattern) => pattern.test(normalized))
|
||||
}
|
||||
|
||||
function containsProfanity(text) {
|
||||
return matchesAny(text, PROFANITY_PATTERNS);
|
||||
function containsProfanity (text) {
|
||||
return matchesAny(text, PROFANITY_PATTERNS)
|
||||
}
|
||||
|
||||
function containsExplicitContent(text) {
|
||||
return matchesAny(text, EXPLICIT_CONTENT_PATTERNS);
|
||||
function containsExplicitContent (text) {
|
||||
return matchesAny(text, EXPLICIT_CONTENT_PATTERNS)
|
||||
}
|
||||
|
||||
/** Comment / vitrine / academy comment status */
|
||||
function resolveCommentModerationStatus(text) {
|
||||
/**
|
||||
* Comment / vitrine / academy comment status.
|
||||
* کاربرِ بدون تیک وریفای (طلایی/سبز/آبی) نباید مستقیم توی اپ/وب نمایش داده بشه —
|
||||
* باید توی پنل ادمین دستی تایید بشه، پس status روش pending میمونه
|
||||
*/
|
||||
function resolveCommentModerationStatus (text, verifyBadge) {
|
||||
if (containsProfanity(text) || containsExplicitContent(text)) {
|
||||
return 'rejected';
|
||||
return 'rejected'
|
||||
}
|
||||
return 'temp_accept';
|
||||
if (!isVerifiedBadge(verifyBadge)) {
|
||||
return 'pending'
|
||||
}
|
||||
return 'temp_accept'
|
||||
}
|
||||
|
||||
/**
|
||||
* Post moderation:
|
||||
* - reject: profanity / explicit text in caption
|
||||
* - temp_accept: visible in app, awaits manual full approval
|
||||
* - pending: creator ندارد تیک وریفای طلایی/سبز/آبی — نیاز به تاییدِ دستیِ پنل داره
|
||||
* - temp_accept: creator وریفایشدهست — مستقیم توی اپ نمایش داده میشه
|
||||
*/
|
||||
function resolvePostModerationStatus({ caption, filePaths = [] }) {
|
||||
function resolvePostModerationStatus ({ caption, filePaths = [], verifyBadge }) {
|
||||
if (containsProfanity(caption) || containsExplicitContent(caption)) {
|
||||
return { status: 'reject', reason: 'caption_violation' };
|
||||
return { status: 'reject', reason: 'caption_violation' }
|
||||
}
|
||||
|
||||
return { status: 'temp_accept', reason: 'awaiting_review' };
|
||||
if (!isVerifiedBadge(verifyBadge)) {
|
||||
return { status: 'pending', reason: 'awaiting_admin_review' }
|
||||
}
|
||||
|
||||
return { status: 'temp_accept', reason: 'awaiting_review' }
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
containsProfanity,
|
||||
containsExplicitContent,
|
||||
resolveCommentModerationStatus,
|
||||
resolvePostModerationStatus,
|
||||
};
|
||||
resolvePostModerationStatus
|
||||
}
|
||||
|
||||
@@ -257,19 +257,26 @@ async function buildUserExploreSignals(userId) {
|
||||
}
|
||||
|
||||
const user = await UserModel.findById(userId)
|
||||
.select('expertise sub_expertise')
|
||||
.select('expertise sub_expertise algorithm_reset_at')
|
||||
.lean()
|
||||
|
||||
// بعد از «ریست الگوریتم»، لایک/کامنت/آفرِ قبل از این تاریخ نباید توی ترجیحات
|
||||
// دوبارهساختهشده اثر بذاره — وگرنه با اینکه کشِ رتبهبندی پاک شده، سیگنالهای
|
||||
// شخصیسازیِ قدیمی از نو محاسبه میشن و کاربر عملاً هیچ تغییری حس نمیکنه
|
||||
const resetAt = user?.algorithm_reset_at || null
|
||||
const createdAtFilter = resetAt ? { createdAt: { $gte: resetAt } } : {}
|
||||
|
||||
const [likes, comments, offers, interactions] = await Promise.all([
|
||||
LikeModel.find({ userId }).select('postId createdAt').lean(),
|
||||
LikeModel.find({ userId, ...createdAtFilter }).select('postId createdAt').lean(),
|
||||
CommentModel.find({
|
||||
creator: userId,
|
||||
comment_for: 'post',
|
||||
post: { $ne: null }
|
||||
post: { $ne: null },
|
||||
...createdAtFilter
|
||||
})
|
||||
.select('post user createdAt')
|
||||
.lean(),
|
||||
OfferModel.find({ sender: userId }).select('receiver createdAt').lean(),
|
||||
OfferModel.find({ sender: userId, ...createdAtFilter }).select('receiver createdAt').lean(),
|
||||
ExploreInteractionModel.find({ viewerId: userId })
|
||||
.sort({ createdAt: -1 })
|
||||
.limit(1200)
|
||||
|
||||
@@ -1,4 +1,12 @@
|
||||
function computeVerifyBadge({ is_verified, license_verified, gold_badge }) {
|
||||
const VERIFIED_BADGES = ['gold', 'green', 'blue']
|
||||
|
||||
// کاربر با تیک طلایی/سبز/آبی «وریفایشده» حساب میشه — محتواش نیازی به تاییدِ
|
||||
// دستیِ پنل نداره و مستقیم توی اپ/وب نمایش داده میشه
|
||||
function isVerifiedBadge (badge) {
|
||||
return VERIFIED_BADGES.includes(badge)
|
||||
}
|
||||
|
||||
function computeVerifyBadge ({ is_verified, license_verified, gold_badge }) {
|
||||
if (gold_badge) return 'gold'
|
||||
if (license_verified) return 'green'
|
||||
if (is_verified === 'verified') return 'blue'
|
||||
@@ -6,7 +14,7 @@ function computeVerifyBadge({ is_verified, license_verified, gold_badge }) {
|
||||
return 'none'
|
||||
}
|
||||
|
||||
function syncVerifyBadge(userDoc) {
|
||||
function syncVerifyBadge (userDoc) {
|
||||
userDoc.verify_badge = computeVerifyBadge({
|
||||
is_verified: userDoc.is_verified,
|
||||
license_verified: userDoc.license_verified,
|
||||
@@ -15,4 +23,4 @@ function syncVerifyBadge(userDoc) {
|
||||
return userDoc.verify_badge
|
||||
}
|
||||
|
||||
module.exports = { computeVerifyBadge, syncVerifyBadge }
|
||||
module.exports = { computeVerifyBadge, syncVerifyBadge, isVerifiedBadge, VERIFIED_BADGES }
|
||||
|
||||
Reference in New Issue
Block a user