Rules drop posters at the header pass: the CelebMovie family (rule set version 3)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
093d0a71ad
commit
3d24db5565
9 files changed
+116
-9
No files matched your search
@@ -48,7 +48,7 @@ export interface ProbeCandidate {
|
||||
|
||||
/** What a rule dropped in a batch (REBUILD.md, rules and recovery). */
|
||||
export interface RuleCount {
|
||||
rule: 'crosspost' | 'obfuscated' | 'executable' | 'garbage' | 'per-article' | 'incomplete'
|
||||
rule: 'crosspost' | 'obfuscated' | 'poster' | 'executable' | 'garbage' | 'per-article' | 'incomplete'
|
||||
key: string
|
||||
articles: number
|
||||
files: number
|
||||
@@ -215,7 +215,7 @@ export class RangeIndexer {
|
||||
uploadsOf(all).forEach((list, upload) => {
|
||||
const verdict = this.rules.judge(
|
||||
key,
|
||||
list.map((f) => ({ name: f.segments[0].file.name, totalParts: f.segments[0].totalParts, groups: f.segments[0].file.groups, firstMessageId: f.segments[0].messageId }))
|
||||
list.map((f) => ({ name: f.segments[0].file.name, totalParts: f.segments[0].totalParts, groups: f.segments[0].file.groups, firstMessageId: f.segments[0].messageId, poster: f.segments[0].file.poster }))
|
||||
)
|
||||
if (verdict.rule === null) {
|
||||
this.batch.releases.push(chunkOf(key, list, upload))
|
||||
|
||||
+1
-1
@@ -1,2 +1,2 @@
|
||||
export { uploadTool, signatureGroup, signatureKey, explored } from './upload_tools.js'
|
||||
export { RuleSet, DEFAULT_RULES, isRandomFile, ownerOf, type Rules, type Verdict } from './rule_set.js'
|
||||
export { RuleSet, DEFAULT_RULES, isRandomFile, ownerOf, posterWords, type Rules, type Verdict } from './rule_set.js'
|
||||
+22
-3
@@ -24,6 +24,12 @@ export interface Rules {
|
||||
* 2026-10-09). Genuine software comes in archives.
|
||||
*/
|
||||
dropExecutables: boolean
|
||||
/**
|
||||
* Posters whose uploads are dropped whatever they are: fragments of the poster lowercased without punctuation
|
||||
* ("@__Celeb_Movie__@" → "celebmovie"). The CelebMovie family's scraped celebrity pictures, the same files again and
|
||||
* again under other titles (119k releases on 2026-10-16; LOG.md 2026-10-16 (65)).
|
||||
*/
|
||||
droppedPosters: string[]
|
||||
}
|
||||
|
||||
export const DEFAULT_RULES: Omit<Rules, 'priorities' | 'droppedSignatures'> = {
|
||||
@@ -32,9 +38,15 @@ export const DEFAULT_RULES: Omit<Rules, 'priorities' | 'droppedSignatures'> = {
|
||||
perArticleMaxParts: 3,
|
||||
samplePercent: 1,
|
||||
dropExecutables: true,
|
||||
droppedPosters: ['celebmovie', 'celebwow', 'celebfx', 'iluvcelebs', 'nudecelebs', 'celebsextapes'],
|
||||
}
|
||||
|
||||
export type Verdict = { rule: null } | { rule: 'executable' | 'garbage' | 'per-article'; key: string; sample: boolean }
|
||||
export type Verdict = { rule: null } | { rule: 'poster' | 'executable' | 'garbage' | 'per-article'; key: string; sample: boolean }
|
||||
|
||||
/** A poster as droppedPosters matches it: lowercased, letters and digits only. */
|
||||
export function posterWords(poster: string): string {
|
||||
return poster.replace(/[^A-Za-z0-9]/g, '').toLowerCase()
|
||||
}
|
||||
|
||||
/** Files that come along with a release's content: they don't make it one of executables or not. */
|
||||
const SIDECAR = /\.(par2|nfo|sfv|txt|jpe?g|png|nzb)$/i
|
||||
@@ -57,18 +69,25 @@ export function ownerOf(groups: string[], priorities: Record<string, number>): s
|
||||
export class RuleSet {
|
||||
private signatures: Set<string>
|
||||
private tools: Set<string>
|
||||
private posters: string[]
|
||||
constructor(readonly rules: Rules) {
|
||||
this.posters = rules.droppedPosters ?? []
|
||||
this.signatures = new Set(rules.droppedSignatures)
|
||||
this.tools = new Set(rules.droppedTools)
|
||||
}
|
||||
|
||||
/**
|
||||
* A release's verdict from its files: dropped when its content is bare
|
||||
* A release's verdict from its files: dropped when its poster is; when its content is bare
|
||||
* executables; else kept as a whole when any file has a readable name; else dropped as garbage when its signature or tool is
|
||||
* dropped, as per-article random subjects when every file has few parts.
|
||||
*/
|
||||
judge(releaseKey: string, files: { name: string; totalParts: number; groups: string[]; firstMessageId: string }[]): Verdict {
|
||||
judge(releaseKey: string, files: { name: string; totalParts: number; groups: string[]; firstMessageId: string; poster: string }[]): Verdict {
|
||||
const sample = explored(releaseKey, this.rules.samplePercent)
|
||||
if (this.posters.length > 0 && files.length > 0) {
|
||||
const words = posterWords(files[0].poster)
|
||||
const dropped = this.posters.find((p) => words.includes(p))
|
||||
if (dropped) return { rule: 'poster', key: dropped, sample }
|
||||
}
|
||||
if (this.rules.dropExecutables) {
|
||||
const content = files.filter((f) => !SIDECAR.test(f.name))
|
||||
if (content.length > 0 && content.every((f) => EXECUTABLE.test(f.name))) {
|
||||
|
||||
Reference in new issue
Block a user