From b258f49201b7cbc27c59bde17cb2c030a6a9c99a Mon Sep 17 00:00:00 2001 From: pablof7z Date: Wed, 9 Apr 2025 11:45:30 +0100 Subject: [PATCH] wip --- commands/find-snippets.ts | 47 ++++++++++++++-------- commands/mcp.ts | 3 ++ index.ts | 3 ++ lib/cache/wallets.ts | 85 ++++++++++++++++++++++++++++----------- lib/nostr/snippets.ts | 27 +++++++++++-- lib/utils/log.ts | 4 +- ndk.ts | 11 ++++- package.json | 2 +- 8 files changed, 134 insertions(+), 48 deletions(-) diff --git a/commands/find-snippets.ts b/commands/find-snippets.ts index 33dc64d..9429f61 100644 --- a/commands/find-snippets.ts +++ b/commands/find-snippets.ts @@ -1,25 +1,37 @@ -import { Command } from 'commander'; +import { Command } from "commander"; import { formatPartialMatches, formatSnippets, - getSnippets -} from '../lib/nostr/snippets.js'; + getSnippets, +} from "../lib/nostr/snippets.js"; export function registerFindSnippetsCommand(program: Command): void { program - .command('find-snippets') - .description('Find code snippets with optional filters') - .option('--limit ', 'Maximum number of snippets to return') - .option('--languages ', 'Comma-separated list of languages to filter by') - .option('--tags ', 'Comma-separated list of tags to filter by') - .option('--authors ', 'Comma-separated list of authors to filter by') + .command("find-snippets") + .description("Find code snippets with optional filters") + .option("--limit ", "Maximum number of snippets to return") + .option( + "--languages ", + "Comma-separated list of languages to filter by" + ) + .option("--tags ", "Comma-separated list of tags to filter by") + .option( + "--authors ", + "Comma-separated list of authors to filter by" + ) .action(async (options) => { try { // Parse options - const limit = options.limit ? parseInt(options.limit, 10) : undefined; - const languages = options.languages ? options.languages.split(',') : undefined; - const tags = options.tags ? options.tags.split(',') : undefined; - const authors = options.authors ? options.authors.split(',') : undefined; + const limit = options.limit + ? parseInt(options.limit, 10) + : undefined; + const languages = options.languages + ? options.languages.split(",") + : undefined; + const tags = options.tags ? options.tags.split(",") : undefined; + const authors = options.authors + ? options.authors.split(",") + : undefined; const { snippets, otherSnippets } = await getSnippets({ limit, @@ -29,10 +41,13 @@ export function registerFindSnippetsCommand(program: Command): void { }); if (snippets.length === 0) { - console.log("No code snippets found matching the criteria."); + console.log( + "No code snippets found matching the criteria." + ); } else { const formattedSnippets = formatSnippets(snippets); - const partialMatchesText = formatPartialMatches(otherSnippets); + const partialMatchesText = + formatPartialMatches(otherSnippets); console.log( `Found ${snippets.length} code snippets:\n\n${formattedSnippets}${partialMatchesText}` ); @@ -42,4 +57,4 @@ export function registerFindSnippetsCommand(program: Command): void { process.exit(1); } }); -} \ No newline at end of file +} diff --git a/commands/mcp.ts b/commands/mcp.ts index be8c7d4..063f279 100644 --- a/commands/mcp.ts +++ b/commands/mcp.ts @@ -3,6 +3,7 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js" import type { Command } from "commander"; import { readConfig } from "../config.js"; import { addCreatePubkeyCommand } from "../logic/create-pubkey.js"; +import { addDepositCommand } from "../logic/deposit.js"; import { addFetchSnippetByIdCommand } from "../logic/fetch_snippet_by_id.js"; import { addFindSnippetsCommand } from "../logic/find_snippets.js"; import { addFindUserCommand } from "../logic/find_user.js"; @@ -29,6 +30,7 @@ const commandMap: Record = { "fetch-snippet-by-id": addFetchSnippetByIdCommand, zap: addZapCommand, "wallet-balance": addWalletBalanceCommand, + deposit: addDepositCommand, }; // Global server instance @@ -87,4 +89,5 @@ export function registerMcpCommands(server: McpServer) { addZapCommand(server); addWalletBalanceCommand(server); } + addDepositCommand(server); } diff --git a/index.ts b/index.ts index db9fe93..5a4bd0c 100644 --- a/index.ts +++ b/index.ts @@ -2,9 +2,12 @@ import { initConfig, readConfig, writeConfig } from "./config.js"; import { initNDK, ndk } from "./ndk.js"; import "./db.js"; import { runCli } from "./commands/index.js"; +import { applyMigrations } from "./db.js"; import { log } from "./lib/utils/log.js"; import { runConfigWizard } from "./wizard"; +await applyMigrations(); + log("starting up...: args: " + process.argv.join(" ")); // Load config and ensure defaults diff --git a/lib/cache/wallets.ts b/lib/cache/wallets.ts index afb8ef6..fd7a59d 100644 --- a/lib/cache/wallets.ts +++ b/lib/cache/wallets.ts @@ -1,5 +1,9 @@ +import { + NDKCashuMintList, + NDKPrivateKeySigner, + type NDKSigner, +} from "@nostr-dev-kit/ndk"; import { NDKCashuWallet, NDKNutzapMonitor } from "@nostr-dev-kit/ndk-wallet"; -import { NDKCashuMintList, NDKPrivateKeySigner, type NDKSigner } from "@nostr-dev-kit/ndk"; import { ndk } from "../../ndk.js"; import { log } from "../utils/log.js"; @@ -15,7 +19,12 @@ export const walletsCache: Record = {}; * @param pubkey The public key to get the wallet for * @returns The wallet (from cache or newly loaded) */ -export async function getWallet(pubkey: string, signer: NDKSigner): Promise { +export async function getWallet( + pubkey: string, + signer: NDKSigner +): Promise { + let newWallet = false; + // Return from cache if available if (walletsCache[pubkey]) { console.log(`Returning cached wallet for ${pubkey}`); @@ -23,45 +32,53 @@ export async function getWallet(pubkey: string, signer: NDKSigner): Promise { - log('seen nutzap'); + log("seen nutzap"); }); nutzapMonitor.on("redeemed", (events) => { - log(`Nutzap redeemed for ${pubkey}: ${events.reduce((acc, event) => acc + event.amount, 0)} sats`); + log( + `Nutzap redeemed for ${pubkey}: ${events.reduce((acc, event) => acc + event.amount, 0)} sats` + ); }); nutzapMonitor.start({}); log(`Started nutzap monitor for ${pubkey}`); - + // Set up balance update listener - wallet.on('balance_updated', (newBalance) => { - log(`Balance updated for ${pubkey}: ${newBalance?.amount || 0} sats`); + wallet.on("balance_updated", (newBalance) => { + log( + `Balance updated for ${pubkey}: ${newBalance?.amount || 0} sats` + ); }); } catch (error) { - console.error(`Error starting nutzap monitor for ${pubkey}:`, error); + console.error( + `Error starting nutzap monitor for ${pubkey}:`, + error + ); } - + walletsCache[pubkey] = wallet; - - // No wallet found + + if (!newWallet) { + // Total hack, but we have a race condition getting the balance of the wallet, + // if we're starting the wallet with a get_balance command we'll always return zero + // in the first call, so let's give it some time to catch up. wallet.start() should + // have returned once it knows it has all the proofs + return new Promise((resolve) => { + setTimeout(() => { + resolve(wallet); + }, 1000); + wallet.on("balance_updated", () => resolve(wallet)); + }); + } + + // NKo wallet found return wallet; } catch (error) { console.error(`Error fetching wallet for ${pubkey}:`, error); @@ -105,4 +142,4 @@ export function clearWalletsCache(): void { for (const key of Object.keys(walletsCache)) { delete walletsCache[key]; } -} \ No newline at end of file +} diff --git a/lib/nostr/snippets.ts b/lib/nostr/snippets.ts index 547114f..6fea11f 100644 --- a/lib/nostr/snippets.ts +++ b/lib/nostr/snippets.ts @@ -1,10 +1,14 @@ import type { NDKEvent, NDKFilter } from "@nostr-dev-kit/ndk"; import { db } from "../../db.js"; import { ndk } from "../../ndk.js"; +import { + formatPartialMatches, + formatSnippets, + toSnippet, +} from "../converters/index.js"; import type { CodeSnippet, FindSnippetsParams } from "../types/index.js"; import { log } from "../utils/log.js"; import { SNIPPET_KIND, identifierToPubkeys } from "./utils.js"; -import { formatPartialMatches, formatSnippets, toSnippet } from "../converters/index.js"; /** * Get code snippets from Nostr events of kind 1337 @@ -54,6 +58,19 @@ export async function getSnippets(params: FindSnippetsParams = {}): Promise<{ log(`Fetching snippets with filter: ${JSON.stringify(filter, null, 2)}`); + // ndk.subscribe( + // [filter], + // { closeOnEose: true }, + // { + // onEvent: (event) => { + // log(`Received event: ${event.id}`); + // }, + // onEose: () => { + // log("EOSE received."); + // }, + // } + // ); + // Fetch events const events = await ndk.fetchEvents(filter); @@ -73,8 +90,11 @@ export async function getSnippets(params: FindSnippetsParams = {}): Promise<{ .filter((t): t is string => t !== undefined); // Ensure tag value exists and narrow type // Count how many of the searched tags are present in the event's tags - return params.tags.filter((searchTag) => - aTags.some((eventTag) => eventTag.match(new RegExp(searchTag, "i"))) // Case-insensitive tag matching + return params.tags.filter( + (searchTag) => + aTags.some((eventTag) => + eventTag.match(new RegExp(searchTag, "i")) + ) // Case-insensitive tag matching ).length; } @@ -98,7 +118,6 @@ export async function getSnippets(params: FindSnippetsParams = {}): Promise<{ const snippets = selectedEvents.map(toSnippet); const otherSnippets = notSelectedEvents.map(toSnippet); - // --- BEGIN DATABASE INSERTION --- const allSnippets = [...snippets, ...otherSnippets]; if (allSnippets.length > 0) { diff --git a/lib/utils/log.ts b/lib/utils/log.ts index 2a6411a..dae6ab8 100644 --- a/lib/utils/log.ts +++ b/lib/utils/log.ts @@ -1,8 +1,8 @@ import * as fs from "node:fs"; -import { promises as fsPromises } from "node:fs"; import * as os from "node:os"; import * as path from "node:path"; +const id = Math.floor(Math.random() * 1000000); const timeZero = Date.now(); /** @@ -21,7 +21,7 @@ export function log( const now = new Date(); const timestamp = new Date().toISOString(); const relativeTime = now.getTime() - timeZero; - const logMessage = `[${relativeTime}ms] ${timestamp} - ${message}\n`; + const logMessage = `[${id}] [${relativeTime}ms] ${timestamp} - ${message}\n`; fs.appendFile(logFilePath, logMessage, (err) => { if (err) { console.error("Error writing to log file:", err); diff --git a/ndk.ts b/ndk.ts index a3e8f78..01d34b7 100644 --- a/ndk.ts +++ b/ndk.ts @@ -2,10 +2,12 @@ import NDK, { NDKPrivateKeySigner, type NDKUser, type NDKSigner, + type NDKRelay, } from "@nostr-dev-kit/ndk"; import { NDKNip46Signer } from "@nostr-dev-kit/ndk"; import { type ConfigData, writeConfig } from "./config"; import { updateFollowList } from "./update-follow-list"; +import { log } from "./utils/log"; const DEFAULT_RELAYS = [ "wss://relay.primal.net", @@ -18,6 +20,13 @@ export const ndk = new NDK(); export async function initNDK(config: ConfigData) { ndk.explicitRelayUrls = config.relays || DEFAULT_RELAYS; + ndk.pool.on("relay:connect", (r: NDKRelay) => log(`Connected to ${r.url}`)); + ndk.pool.on("relay:disconnect", (r: NDKRelay) => + log(`Disconnected from ${r.url}`) + ); + ndk.pool.on("relay:connecting", (r: NDKRelay) => + log(`Connecting to ${r.url}`) + ); await ndk.connect(); let signer: NDKSigner; @@ -53,5 +62,5 @@ export async function initNDK(config: ConfigData) { mainUser ??= await signer.user(); - setTimeout(() => updateFollowList(mainUser), 1000); + // setTimeout(() => updateFollowList(mainUser), 1000); } diff --git a/package.json b/package.json index bc21667..6176286 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ }, "dependencies": { "@modelcontextprotocol/sdk": "^1.7.0", - "@nostr-dev-kit/ndk": "2.13.1-rc2", + "@nostr-dev-kit/ndk": "2.13.1-rc7", "@nostr-dev-kit/ndk-wallet": "0.5.3-1", "commander": "^13.1.0", "inquirer": "^12.5.0",