Add relays to database and start tracking them
This commit is contained in:
parent
4a47c25f1e
commit
78f638e633
|
@ -8,6 +8,7 @@ interface DittoDB {
|
||||||
events: EventRow;
|
events: EventRow;
|
||||||
tags: TagRow;
|
tags: TagRow;
|
||||||
users: UserRow;
|
users: UserRow;
|
||||||
|
relays: RelayRow;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface EventRow {
|
interface EventRow {
|
||||||
|
@ -34,6 +35,10 @@ interface UserRow {
|
||||||
inserted_at: Date;
|
inserted_at: Date;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface RelayRow {
|
||||||
|
url: string;
|
||||||
|
}
|
||||||
|
|
||||||
const db = new Kysely<DittoDB>({
|
const db = new Kysely<DittoDB>({
|
||||||
dialect: new DenoSqliteDialect({
|
dialect: new DenoSqliteDialect({
|
||||||
database: new Sqlite(Conf.dbPath),
|
database: new Sqlite(Conf.dbPath),
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
import { Kysely } from '@/deps.ts';
|
||||||
|
|
||||||
|
export async function up(db: Kysely<any>): Promise<void> {
|
||||||
|
await db.schema
|
||||||
|
.createTable('relays')
|
||||||
|
.addColumn('url', 'text', (col) => col.primaryKey())
|
||||||
|
.execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(db: Kysely<any>): Promise<void> {
|
||||||
|
await db.schema.dropTable('relays').execute();
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
import { db } from '@/db.ts';
|
||||||
|
|
||||||
|
/** Inserts relays into the database, skipping duplicates. */
|
||||||
|
function addRelays(relays: `wss://${string}`[]) {
|
||||||
|
const values = relays.map((url) => ({ url }));
|
||||||
|
|
||||||
|
return db.insertInto('relays')
|
||||||
|
.values(values)
|
||||||
|
.onConflict((oc) => oc.column('url').doNothing())
|
||||||
|
.execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get a list of all known good relays. */
|
||||||
|
async function getAllRelays(): Promise<string[]> {
|
||||||
|
const rows = await db.selectFrom('relays').select('relays.url').execute();
|
||||||
|
return rows.map((row) => row.url);
|
||||||
|
}
|
||||||
|
|
||||||
|
export { addRelays, getAllRelays };
|
|
@ -1,20 +1,21 @@
|
||||||
import { Conf } from '@/config.ts';
|
|
||||||
import { insertEvent, isLocallyFollowed } from '@/db/events.ts';
|
import { insertEvent, isLocallyFollowed } from '@/db/events.ts';
|
||||||
|
import { addRelays, getAllRelays } from '@/db/relays.ts';
|
||||||
import { findUser } from '@/db/users.ts';
|
import { findUser } from '@/db/users.ts';
|
||||||
import { RelayPool } from '@/deps.ts';
|
import { RelayPool } from '@/deps.ts';
|
||||||
import { trends } from '@/trends.ts';
|
import { trends } from '@/trends.ts';
|
||||||
import { nostrDate, nostrNow } from '@/utils.ts';
|
import { isRelay, nostrDate, nostrNow } from '@/utils.ts';
|
||||||
|
|
||||||
import type { SignedEvent } from '@/event.ts';
|
import type { SignedEvent } from '@/event.ts';
|
||||||
|
|
||||||
const relay = new RelayPool([Conf.relay]);
|
const relays = await getAllRelays();
|
||||||
|
const pool = new RelayPool(relays);
|
||||||
|
|
||||||
// This file watches all events on your Ditto relay and triggers
|
// This file watches all events on your Ditto relay and triggers
|
||||||
// side-effects based on them. This can be used for things like
|
// side-effects based on them. This can be used for things like
|
||||||
// notifications, trending hashtag tracking, etc.
|
// notifications, trending hashtag tracking, etc.
|
||||||
relay.subscribe(
|
pool.subscribe(
|
||||||
[{ kinds: [1], since: nostrNow() }],
|
[{ kinds: [1], since: nostrNow() }],
|
||||||
[Conf.relay],
|
relays,
|
||||||
handleEvent,
|
handleEvent,
|
||||||
undefined,
|
undefined,
|
||||||
undefined,
|
undefined,
|
||||||
|
@ -25,6 +26,7 @@ async function handleEvent(event: SignedEvent): Promise<void> {
|
||||||
console.info('firehose event:', event.id);
|
console.info('firehose event:', event.id);
|
||||||
|
|
||||||
trackHashtags(event);
|
trackHashtags(event);
|
||||||
|
trackRelays(event);
|
||||||
|
|
||||||
if (await findUser({ pubkey: event.pubkey }) || await isLocallyFollowed(event.pubkey)) {
|
if (await findUser({ pubkey: event.pubkey }) || await isLocallyFollowed(event.pubkey)) {
|
||||||
insertEvent(event).catch(console.warn);
|
insertEvent(event).catch(console.warn);
|
||||||
|
@ -49,3 +51,19 @@ function trackHashtags(event: SignedEvent): void {
|
||||||
// do nothing
|
// do nothing
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Tracks nown relays in the database. */
|
||||||
|
function trackRelays(event: SignedEvent) {
|
||||||
|
const relays = new Set<`wss://${string}`>();
|
||||||
|
|
||||||
|
event.tags.forEach((tag) => {
|
||||||
|
if (['p', 'e', 'a'].includes(tag[0]) && isRelay(tag[2])) {
|
||||||
|
relays.add(tag[2]);
|
||||||
|
}
|
||||||
|
if (event.kind === 10002 && tag[0] === 'r' && isRelay(tag[1])) {
|
||||||
|
relays.add(tag[1]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return addRelays([...relays]);
|
||||||
|
}
|
||||||
|
|
|
@ -20,24 +20,6 @@ const jsonSchema = z.string().transform((value, ctx) => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
/** Alias for `safeParse`, but instead of returning a success object it returns the value (or undefined on fail). */
|
|
||||||
function parseValue<T>(schema: z.ZodType<T>, value: unknown): T | undefined {
|
|
||||||
const result = schema.safeParse(value);
|
|
||||||
return result.success ? result.data : undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
const parseRelay = (relay: string | URL) => parseValue(relaySchema, relay);
|
|
||||||
|
|
||||||
const relaySchema = z.custom<URL>((relay) => {
|
|
||||||
if (typeof relay !== 'string') return false;
|
|
||||||
try {
|
|
||||||
const { protocol } = new URL(relay);
|
|
||||||
return protocol === 'wss:' || protocol === 'ws:';
|
|
||||||
} catch (_e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const emojiTagSchema = z.tuple([z.literal('emoji'), z.string(), z.string().url()]);
|
const emojiTagSchema = z.tuple([z.literal('emoji'), z.string(), z.string().url()]);
|
||||||
|
|
||||||
/** https://developer.mozilla.org/en-US/docs/Glossary/Base64#the_unicode_problem */
|
/** https://developer.mozilla.org/en-US/docs/Glossary/Base64#the_unicode_problem */
|
||||||
|
@ -60,13 +42,4 @@ const hashtagSchema = z.string().regex(/^\w{1,30}$/);
|
||||||
*/
|
*/
|
||||||
const safeUrlSchema = z.string().max(2048).url();
|
const safeUrlSchema = z.string().max(2048).url();
|
||||||
|
|
||||||
export {
|
export { decode64Schema, emojiTagSchema, filteredArray, hashtagSchema, jsonSchema, safeUrlSchema };
|
||||||
decode64Schema,
|
|
||||||
emojiTagSchema,
|
|
||||||
filteredArray,
|
|
||||||
hashtagSchema,
|
|
||||||
jsonSchema,
|
|
||||||
parseRelay,
|
|
||||||
relaySchema,
|
|
||||||
safeUrlSchema,
|
|
||||||
};
|
|
||||||
|
|
|
@ -142,6 +142,12 @@ function activityJson<T, P extends string>(c: Context<any, P>, object: T) {
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Schema to parse a relay URL. */
|
||||||
|
const relaySchema = z.string().max(255).startsWith('wss://').url();
|
||||||
|
|
||||||
|
/** Check whether the value is a valid relay URL. */
|
||||||
|
const isRelay = (relay: string): relay is `wss://${string}` => relaySchema.safeParse(relay).success;
|
||||||
|
|
||||||
export {
|
export {
|
||||||
activityJson,
|
activityJson,
|
||||||
bech32ToPubkey,
|
bech32ToPubkey,
|
||||||
|
@ -149,6 +155,7 @@ export {
|
||||||
eventAge,
|
eventAge,
|
||||||
eventDateComparator,
|
eventDateComparator,
|
||||||
findTag,
|
findTag,
|
||||||
|
isRelay,
|
||||||
lookupAccount,
|
lookupAccount,
|
||||||
type Nip05,
|
type Nip05,
|
||||||
nostrDate,
|
nostrDate,
|
||||||
|
|
Loading…
Reference in New Issue