libaskexperts
Version:
A TypeScript library to create experts based on NIP-174
124 lines (108 loc) • 3.9 kB
text/typescript
import { Expert, Ask, Bid, Question, Answer, QuestionAnswerPair } from "../src";
import { generateSecretKey, getPublicKey } from "nostr-tools";
// Default relays to connect to
const DEFAULT_RELAYS = [
"wss://relay.nostr.band",
"wss://relay.damus.io",
"wss://nos.lol",
];
/**
* Test implementation of an expert using the Expert class
*/
import { createWallet } from "nwc-enclaved-utils";
async function runTestExpert() {
console.log("Starting test expert...");
// Create a wallet for generating invoices
console.log("Creating wallet...");
const { nwcString, lnAddress } = await createWallet();
console.log("Wallet created with lnAddress:", lnAddress);
// Generate expert keypair
const expertPrivateKey = generateSecretKey();
const expertPublicKey = getPublicKey(expertPrivateKey);
console.log(`Expert pubkey: ${expertPublicKey}`);
// Create an expert instance
const expert = new Expert({
nwcString,
expertPrivkey: expertPrivateKey,
askRelays: DEFAULT_RELAYS,
questionRelays: DEFAULT_RELAYS,
hashtags: ["test"], // Listen for asks with the "test" hashtag
onAsk: handleAsk,
onQuestion: handleQuestion,
bidTimeout: 600, // 10 minutes
});
// Start the expert
expert.start();
console.log("Expert started, listening for asks...");
// Keep the process running
process.on("SIGINT", () => {
console.log("Shutting down expert...");
expert[Symbol.dispose]();
process.exit(0);
});
/**
* Handle an ask event
*
* @param ask The ask to handle
* @returns A bid if the expert wants to participate, otherwise undefined
*/
async function handleAsk(ask: Ask): Promise<Bid | undefined> {
console.log(`Handling ask: ${JSON.stringify(ask)}`);
// In a real implementation, you would evaluate the ask and decide whether to bid
// For this test, we'll always bid 10 sats
return {
content:
"I'm a test expert and I can provide a real answer to your question!",
bid_sats: 10,
};
}
/**
* Handle a question event
*
* @param ask The original ask
* @param bid The bid that was accepted
* @param question The question to answer
* @returns An answer to the question
*/
async function handleQuestion(
ask: Ask,
bid: Bid,
question: Question,
history?: QuestionAnswerPair[]
): Promise<Answer> {
console.log(`Handling question: ${JSON.stringify(question)}`);
// Log history if available
if (history && history.length > 0) {
console.log(`Question history (${history.length} previous exchanges):`);
history.forEach((pair, index) => {
console.log(`Exchange ${index + 1}:`);
console.log(`- Question: ${pair.question.content}`);
console.log(`- Answer: ${pair.answer.content}`);
});
}
// In a real implementation, you would generate a thoughtful answer based on the question
// For this test, we'll just return a simple answer
// If this is a first question, offer a followup option
if (!history || history.length === 0) {
return {
content: `This is a test answer to your question: "${question.content}". In a real implementation, this would be a thoughtful response based on the expert's knowledge. You can ask a followup question if needed.`,
followup_sats: 5, // Allow followup questions for 5 sats
};
} else {
// For followup questions, don't offer another followup
return {
content: `This is a followup answer to your question: "${question.content}". I see we've had ${history.length} previous exchanges.`,
};
}
}
}
// If this file is run directly, start the test expert
if (
process.argv[1].endsWith("expert.test.ts") ||
process.argv[1].endsWith("expert.test.js")
) {
runTestExpert().catch((error) => {
console.error("Error running test expert:", error);
process.exit(1);
});
}