mirror of
https://gitlab.com/soapbox-pub/ditto.git
synced 2025-12-06 11:29:46 +00:00
first working version
This commit is contained in:
parent
9298bf6608
commit
fb199123be
1 changed files with 130 additions and 25 deletions
|
|
@ -1,43 +1,148 @@
|
||||||
import { Storages } from '@/storages.ts';
|
import { Storages } from '@/storages.ts';
|
||||||
import { Command } from 'commander';
|
import { NostrFilter } from '@nostrify/nostrify';
|
||||||
|
import { Command, InvalidOptionArgumentError } from 'commander';
|
||||||
|
|
||||||
const store = await Storages.db();
|
const store = await Storages.db();
|
||||||
|
|
||||||
interface ExportFilter {
|
interface ExportFilter {
|
||||||
authors: string[];
|
authors?: string[];
|
||||||
|
ids?: string[];
|
||||||
|
kinds?: number[];
|
||||||
|
limit?: number;
|
||||||
|
search?: string;
|
||||||
|
/**
|
||||||
|
* Array of `key=value` pairs.
|
||||||
|
*/
|
||||||
|
tags?: string[];
|
||||||
|
since?: number;
|
||||||
|
until?: number;
|
||||||
|
/**
|
||||||
|
* shortcut for `--tag d=<value>`
|
||||||
|
*/
|
||||||
|
d?: string;
|
||||||
|
/**
|
||||||
|
* shortcut for `--tag e=<value>`
|
||||||
|
*/
|
||||||
|
e?: string;
|
||||||
|
/**
|
||||||
|
* shortcut for `--tag p=<value>`
|
||||||
|
*/
|
||||||
|
p?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
function safeParseInt(s: string) {
|
||||||
|
const n = parseInt(s);
|
||||||
|
if (isNaN(n)) throw new InvalidOptionArgumentError('Not a number.');
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
function findInvalid(arr: string[], predicate = (v: string) => !/[a-f0-9]{64}/.test(v)) {
|
||||||
|
return arr.find(predicate);
|
||||||
|
}
|
||||||
|
|
||||||
|
function die(code: number, ...args: any[]) {
|
||||||
|
console.error(...args);
|
||||||
|
Deno.exit(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
function tagFilterShortcut(name: 'd' | 'e' | 'p', value: string) {
|
||||||
|
const val = [value];
|
||||||
|
if (findInvalid(val)) die(1, `ERROR: Invalid value supplied for ${name}-tag.`);
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function exportEvents(args: ExportFilter) {
|
||||||
|
const filter: NostrFilter = {};
|
||||||
|
const { authors, ids, kinds, d, e, limit, p, search, since, until, tags } = args;
|
||||||
|
if (since) {
|
||||||
|
filter.since = since;
|
||||||
|
}
|
||||||
|
if (until) {
|
||||||
|
filter.until = until;
|
||||||
|
}
|
||||||
|
if (authors && authors.length) {
|
||||||
|
const invalid = findInvalid(authors);
|
||||||
|
if (invalid) die(1, `ERROR: Invalid pubkey ${invalid} supplied.`);
|
||||||
|
filter.authors = authors;
|
||||||
|
}
|
||||||
|
if (ids) {
|
||||||
|
const invalid = findInvalid(ids);
|
||||||
|
if (invalid) die(1, `ERROR: Invalid event ID ${invalid} supplied.`);
|
||||||
|
}
|
||||||
|
if (kinds && kinds.length) {
|
||||||
|
filter.kinds = kinds;
|
||||||
|
}
|
||||||
|
if (d) {
|
||||||
|
filter['#d'] = tagFilterShortcut('d', d);
|
||||||
|
}
|
||||||
|
if (e) {
|
||||||
|
filter['#e'] = tagFilterShortcut('e', e);
|
||||||
|
}
|
||||||
|
if (p) {
|
||||||
|
filter['#p'] = tagFilterShortcut('e', p);
|
||||||
|
}
|
||||||
|
if (search) {
|
||||||
|
filter.search = search;
|
||||||
|
}
|
||||||
|
if (limit) {
|
||||||
|
filter.limit = limit;
|
||||||
|
}
|
||||||
|
if (tags) {
|
||||||
|
for (const val of tags) {
|
||||||
|
const [name, ...values] = val.split('=');
|
||||||
|
filter[`#${name}`] = [values.join('=')];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let count = 0;
|
||||||
|
for await (const msg of store.req([filter])) {
|
||||||
|
if (msg[0] === 'EOSE') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (msg[0] === 'EVENT') {
|
||||||
|
console.log(JSON.stringify(msg[2]));
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
if (msg[0] === 'CLOSED') {
|
||||||
|
console.error('Database closed unexpectedly');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.warn(`Exported ${count} events`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (import.meta.main) {
|
if (import.meta.main) {
|
||||||
const exporter = new Command()
|
const exporter = new Command()
|
||||||
.name('db:export')
|
.name('db:export')
|
||||||
.description('Export the specified set of events from the Ditto database.')
|
.description('Export the specified set of events from the Ditto database, in JSONL format.')
|
||||||
.version('0.1.0')
|
.version('0.1.0')
|
||||||
.showHelpAfterError();
|
.showHelpAfterError();
|
||||||
|
|
||||||
exporter
|
exporter
|
||||||
// .option('')
|
.option('-a, --authors <authors...>', 'Pubkeys of authors whose events you want to export.', [])
|
||||||
.action(async (args: ExportFilter) => {
|
.option('-i, --ids <ids...>', 'IDs of events you want to export.', [])
|
||||||
console.warn('Exporting events...');
|
.option(
|
||||||
let count = 0;
|
'-k --kinds <kinds...>',
|
||||||
|
'Event kinds you want to export.',
|
||||||
|
(v: string, arr: number[]) => arr.concat([safeParseInt(v)]),
|
||||||
|
[],
|
||||||
|
)
|
||||||
|
.option(
|
||||||
|
'-t --tags <tag pairs...>',
|
||||||
|
'A list of key=value pairs of tags to search for events using. For tag values with spaces etc, simply quote the entire item, like `deno task db:export -t "name=A string with spaces in it"`.',
|
||||||
|
[],
|
||||||
|
)
|
||||||
|
.option('--search <search string>', 'A string to full-text search the db for.')
|
||||||
|
.option('-s --since <number>', 'The oldest time an exported event should be from.', safeParseInt)
|
||||||
|
.option('-u --until <number>', 'The newest time an exported event should be from.', safeParseInt)
|
||||||
|
.option('--limit <number>', 'Maximum number of events to export.', safeParseInt)
|
||||||
|
.option('-d <string>', 'Shortcut for `--tag d=<value>`.')
|
||||||
|
.option('-e <string>', 'Shortcut for `--tag e=<value>`.')
|
||||||
|
.option('-p <string>', 'Shortcut for `--tag p=<value>`.')
|
||||||
|
.action(exportEvents);
|
||||||
|
|
||||||
for await (const msg of store.req([{}])) {
|
await exporter.parseAsync(Deno.args, { from: 'user' });
|
||||||
if (msg[0] === 'EOSE') {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (msg[0] === 'EVENT') {
|
|
||||||
console.log(JSON.stringify(msg[2]));
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
if (msg[0] === 'CLOSED') {
|
|
||||||
console.error('Database closed unexpectedly');
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
console.warn(`Exported ${count} events`);
|
|
||||||
});
|
|
||||||
|
|
||||||
exporter.parse(Deno.args, { from: 'user' });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Deno.exit();
|
Deno.exit();
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue