diff --git a/commands/rules_signatures.ts b/commands/rules_signatures.ts new file mode 100644 index 0000000..6ab748f --- /dev/null +++ b/commands/rules_signatures.ts @@ -0,0 +1,52 @@ +import { BaseCommand, args, flags } from '@adonisjs/core/ace' +import type { CommandOptions } from '@adonisjs/core/types/ace' +import { readFile } from 'node:fs/promises' +import RuleSets from '#services/rule_sets' +import { signatureKey } from '#lib/rules/index' + +/** + * Sets the dropped upload signatures (REBUILD.md, rules and recovery) from a + * file of "toolgroup" lines, e.g. voyager 1's learned ones: + * + * psql … -At -F $'\t' -c "SELECT tool, group_name FROM upload_signatures WHERE dropped" + * + * Publishes a new rule set version (ranges record the version they ran with). + */ +export default class RulesSignatures extends BaseCommand { + static commandName = 'rules:signatures' + static description = 'Publishes a rule set with the dropped upload signatures from a file' + static options: CommandOptions = { startApp: true } + + @args.string({ description: 'File of "toolgroup" lines (more columns and # lines are ignored), e.g. database/data/voyager1_signatures.tsv' }) + declare file: string + + @flags.string({ description: 'Note stored with the rule set version' }) + declare note?: string + + @flags.boolean({ description: 'Add to the current signatures instead of replacing them' }) + declare add: boolean + + async run() { + const lines = (await readFile(this.file, 'utf8')).split('\n').map((l) => l.trim()).filter((l) => l && !l.startsWith('#')) + const signatures = new Set() + for (const line of lines) { + const [tool, group] = line.split('\t') + if (!tool || !group) throw new Error(`Not "toolgroup": ${line}`) + signatures.add(signatureKey(tool, group)) + } + const { version: _v, priorities: _p, ...current } = await RuleSets.current() + const before = new Set(current.droppedSignatures) + const next = this.add ? new Set([...before, ...signatures]) : signatures + if (next.size === before.size && [...next].every((s) => before.has(s))) { + this.logger.info(`Unchanged: ${before.size} dropped signatures already.`) + return + } + const version = await RuleSets.publish( + { ...current, droppedSignatures: [...next].sort() }, + this.note ?? `${next.size} dropped signatures from ${this.file}` + ) + const added = [...next].filter((s) => !before.has(s)).length + const removed = [...before].filter((s) => !next.has(s)).length + this.logger.info(`Rule set ${version}: ${next.size} dropped signatures (${added} added, ${removed} removed).`) + } +} diff --git a/database/data/voyager1_signatures.tsv b/database/data/voyager1_signatures.tsv new file mode 100644 index 0000000..3a21fc0 --- /dev/null +++ b/database/data/voyager1_signatures.tsv @@ -0,0 +1,30 @@ +# Voyager 1 upload_signatures WHERE dropped, 2026-10-09: tool, group, probed, named +other alt.binaries.ath 10988 24 +ngPost alt.binaries.ath 3843 0 +hex32 alt.binaries.test 1890 2 +ngPost alt.binaries.a51 1516 0 +nyuu alt.binaries.a51 700 0 +nyuu alt.binaries.mom 656 0 +hex32 alt.binaries.boneless 453 1 +nyuu alt.binaries.misc 356 0 +nyuu alt.binaries.games.encrypted 353 0 +other alt.binaries.etc 317 1 +other alt.binaries.misc 310 0 +nyuu alt.binaries.warez 292 0 +other alt.binaries.usenet2day 255 0 +other alt.binaries.ijsklontje 238 0 +ngPost alt.binaries.bloaf 237 0 +nyuu alt.binaries.nl 196 0 +other alt.binaries.archive.encrypted 189 0 +nyuu alt.binaries.frogs 172 0 +other alt.binaries.encrypted 160 0 +yuun alt.binaries.nl 152 0 +ngPost alt.binaries.nl 149 0 +other alt.binaries.bloaf 134 0 +nyuu alt.binaries.erotica 119 0 +other alt.binaries.warez 119 0 +other alt.binaries.ftn 107 0 +nyuu alt.binaries.ijsklontje 106 0 +random alt.binaries.newznzb.delta 103 0 +ngPost alt.binaries.mom 102 0 +random alt.binaries.newznzb.charlie 101 0