feat: added a new job blockChain-block-number

This commit is contained in:
SwiftHawk 2024-05-10 14:51:16 +05:00
parent 5f7d4fe72e
commit 9d336c0c4d
5 changed files with 4414 additions and 1961 deletions

View File

@ -1,27 +1,27 @@
{
"env": {
"browser": false,
"es6": true,
"node": true
},
"parser": "@typescript-eslint/parser",
"parserOptions": {
"project": "tsconfig.json",
"sourceType": "module",
"ecmaVersion": 2020
},
"plugins": ["@typescript-eslint", "jest"],
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:jest/recommended",
"prettier"
],
"rules": {
// The following rule is enabled only to supplement the inline suppression
// examples, and because it is not a recommended rule, you should either
// disable it, or understand what it enforces.
// https://typescript-eslint.io/rules/explicit-function-return-type/
"@typescript-eslint/explicit-function-return-type": "warn"
}
"env": {
"browser": false,
"es6": true,
"node": true
},
"parser": "@typescript-eslint/parser",
"parserOptions": {
"project": "tsconfig.json",
"sourceType": "module",
"ecmaVersion": 2020
},
"plugins": ["@typescript-eslint", "jest"],
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:jest/recommended",
"prettier"
],
"rules": {
// The following rule is enabled only to supplement the inline suppression
// examples, and because it is not a recommended rule, you should either
// disable it, or understand what it enforces.
// https://typescript-eslint.io/rules/explicit-function-return-type/
// "@typescript-eslint/explicit-function-return-type": "warn"
}
}

View File

@ -1,58 +1,58 @@
{
"name": "data-vending-machine-skeleton",
"version": "0.1.0",
"description": "nostr data vending machine skeleton",
"type": "module",
"engines": {
"node": "~19"
},
"keywords": [
"nostr",
"ai",
"bitcoin"
],
"devDependencies": {
"@types/jest": "~29.5",
"@types/node": "~18",
"@typescript-eslint/eslint-plugin": "~5.59",
"@typescript-eslint/parser": "~5.59",
"eslint": "~8.38",
"eslint-config-prettier": "~8.8",
"eslint-plugin-jest": "~27.2",
"jest": "~29.5",
"prettier": "~2.8",
"rimraf": "~5.0",
"ts-api-utils": "~0.0.44",
"ts-jest": "~29.1",
"typescript": "~5.0"
},
"scripts": {
"start": "node build/src/main.js",
"clean": "rimraf coverage build tmp",
"prebuild": "npm run lint",
"build": "tsc -p tsconfig.json",
"build:watch": "tsc -w -p tsconfig.json",
"build:release": "npm run clean && tsc -p tsconfig.release.json",
"lint": "eslint . --ext .ts --ext .mts",
"test": "jest --coverage",
"prettier": "prettier --config .prettierrc --write .",
"test:watch": "jest --watch"
},
"author": "pablof7z",
"license": "MIT",
"dependencies": {
"@nostr-dev-kit/ndk": "^0.7.5",
"axios": "^1.4.0",
"debug": "^4.3.4",
"file-type": "^18.5.0",
"form-data": "^4.0.0",
"form-data-encoder": "^3.0.0",
"formdata-node": "^5.0.1",
"light-bolt11-decoder": "^3.0.0",
"lnbits": "^1.1.5",
"tslib": "~2.5"
},
"volta": {
"node": "18.12.1"
}
"name": "data-vending-machine-skeleton",
"version": "0.1.0",
"description": "nostr data vending machine skeleton",
"type": "module",
"engines": {
"node": "~19"
},
"keywords": [
"nostr",
"ai",
"bitcoin"
],
"devDependencies": {
"@types/jest": "~29.5",
"@types/node": "~18",
"@typescript-eslint/eslint-plugin": "~5.59",
"@typescript-eslint/parser": "~5.59",
"eslint": "~8.38",
"eslint-config-prettier": "~8.8",
"eslint-plugin-jest": "~27.2",
"jest": "~29.5",
"prettier": "~2.8",
"rimraf": "~5.0",
"ts-api-utils": "~0.0.44",
"ts-jest": "~29.1",
"typescript": "~5.0"
},
"scripts": {
"start": "node build/src/main.js",
"clean": "rimraf coverage build tmp",
"_prebuild": "npm run lint",
"build": "tsc -p tsconfig.json",
"build:watch": "tsc -w -p tsconfig.json",
"build:release": "npm run clean && tsc -p tsconfig.release.json",
"lint": "eslint . --ext .ts --ext .mts",
"test": "jest --coverage",
"prettier": "prettier --config .prettierrc --write .",
"test:watch": "jest --watch"
},
"author": "pablof7z",
"license": "MIT",
"dependencies": {
"@nostr-dev-kit/ndk": "^0.8.1",
"axios": "^1.4.0",
"debug": "^4.3.4",
"file-type": "^18.5.0",
"form-data": "^4.0.0",
"form-data-encoder": "^3.0.0",
"formdata-node": "^5.0.1",
"light-bolt11-decoder": "^3.0.0",
"lnbits": "^1.1.5",
"tslib": "~2.5"
},
"volta": {
"node": "18.12.1"
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,45 @@
import { NDKEvent } from '@nostr-dev-kit/ndk';
import { log } from '../main.js';
import axios from 'axios';
export async function blockChainBlockNumberJob(
event: NDKEvent,
): Promise<string> {
log('New blockChain-block-number job', event.rawEvent());
const input = event.tagValue('i');
const blockChainUrl = `https://blockchain.info/blocks/${input}?format=json`;
console.log('blockChainUrl :>> ', blockChainUrl);
const output = await axios
.get(blockChainUrl)
.then((res) => {
const closestObject = findClosestObject(res.data, input);
return closestObject.block_index.toString();
})
.catch((err) => {
console.log('err in blockChain request :>> ', err);
return Promise.reject(err);
});
return output;
}
// todo: define type for array
// Function to find the object with the closest timestamp
function findClosestObject(array, timestamp) {
let closestObject = null;
let minDifference = Infinity;
array.forEach((obj) => {
const difference = Math.abs(obj.time - timestamp);
if (difference < minDifference) {
minDifference = difference;
closestObject = obj;
}
});
return closestObject;
}

View File

@ -8,59 +8,91 @@ import { inProgress } from './jobs/reactions/in-progress.js';
import { publishStatus } from './jobs/reactions/status.js';
import { priceJob } from './jobs/price.js';
import { getConfig } from './config/index.js';
import { decode } from "light-bolt11-decoder";
import { decode } from 'light-bolt11-decoder';
import { checkInvoiceStatus } from './utils/lnbits.js';
import { blockChainBlockNumberJob } from './job-types/blockChain-block-number.js';
export const log = debug('fool-me-once-dvm');
export const configFile = process.argv[2] || `${process.env.HOME}/.fool-me-once.json`;
export const configFile =
process.argv[2] || `${process.env.HOME}/.fool-me-once.json`;
log('configFile', {configFile});
log('configFile', { configFile });
export const ndk = new NDK({
explicitRelayUrls: [
'wss://relay.f7z.io'
'wss://relay.damus.io',
'wss://relay.primal.net',
'wss://relayable.org',
],
signer: getSigner(),
})
});
await ndk.connect(2000);
log('connected')
log('connected');
const subs = ndk.subscribe({
kinds: [68001],
since: Math.floor(Date.now() / 1000),
"#j": ["summarize", "explain"],
}, { closeOnEose: false })
const subs = ndk.subscribe(
{
kinds: [68001 as number],
since: Math.floor(Date.now() / 1000),
'#j': ['summarize', 'explain'],
},
{ closeOnEose: false },
);
const speechToTextSub = ndk.subscribe({
kinds: [68001],
since: Math.floor(Date.now() / 1000),
"#j": ["speech-to-text"],
}, { closeOnEose: false })
const speechToTextSub = ndk.subscribe(
{
kinds: [68001 as number],
since: Math.floor(Date.now() / 1000),
'#j': ['speech-to-text'],
},
{ closeOnEose: false },
);
subs.on("event", (e) => processJobEvent(e, 'summarize'));
speechToTextSub.on("event", (e) => processJobEvent(e, 'speech-to-text'));
const blockChainBlockNumberSub = ndk.subscribe(
{
kinds: [68001 as number],
since: Math.floor(Date.now() / 1000),
'#j': ['blockChain-block-number'],
},
{ closeOnEose: false },
);
type JobType = "summarize" | "explain" | "speech-to-text";
subs.on('event', (e) => processJobEvent(e, 'summarize'));
speechToTextSub.on('event', (e) => processJobEvent(e, 'speech-to-text'));
blockChainBlockNumberSub.on('event', (e) =>
processJobEvent(e, 'blockChain-block-number'),
);
type JobType =
| 'summarize'
| 'explain'
| 'speech-to-text'
| 'blockChain-block-number';
async function processJobEvent(event: NDKEvent, type: JobType): Promise<void> {
const config = getConfig();
let jobAmount = await priceJob(event);
let jobAmount =
type === 'blockChain-block-number' ? 0 : await priceJob(event);
let output: any;
let payReqEvent: NDKEvent;
let paidAmount = 0;
const waitForPaymentBeforeProcessing = (): boolean => {
const processWithoutPaymentLimit = config.processWithoutPaymentLimit ?? 500;
log('waitForPaymentBeforeProcessing', {jobAmount, processWithoutPaymentLimit});
return jobAmount && jobAmount > (processWithoutPaymentLimit * 1000);
}
const processWithoutPaymentLimit =
config.processWithoutPaymentLimit ?? 500;
log('waitForPaymentBeforeProcessing', {
jobAmount,
processWithoutPaymentLimit,
});
return jobAmount && jobAmount > processWithoutPaymentLimit * 1000;
};
const waitForPaymentBeforePublishingResult = (): boolean => {
const serveResultsWithoutPaymentLimit = config.serveResultsWithoutPaymentLimit ?? 1000;
log('waitForPaymentBeforeProcessing', {jobAmount});
return jobAmount && jobAmount > (serveResultsWithoutPaymentLimit * 1000);
}
const serveResultsWithoutPaymentLimit =
config.serveResultsWithoutPaymentLimit ?? 1000;
log('waitForPaymentBeforeProcessing', { jobAmount });
return jobAmount && jobAmount > serveResultsWithoutPaymentLimit * 1000;
};
const missingAmount = (): number => jobAmount - paidAmount;
@ -70,14 +102,17 @@ async function processJobEvent(event: NDKEvent, type: JobType): Promise<void> {
if (config.undercut) {
startUndercutting();
}
}
};
const startUndercutting = async (): Promise<void> => {
const undercutSub = ndk.subscribe({
kinds: [68002, 68003],
...event.filter()
}, { closeOnEose: false, groupable: false });
undercutSub.on("event", async (e) => {
const undercutSub = ndk.subscribe(
{
kinds: [68002 as number, 68003 as number],
...event.filter(),
},
{ closeOnEose: false, groupable: false },
);
undercutSub.on('event', async (e) => {
if (e.pubkey === payReqEvent.pubkey) return;
// check if this is a payment request
@ -97,14 +132,17 @@ async function processJobEvent(event: NDKEvent, type: JobType): Promise<void> {
payReqEvent = await requirePayment(event, jobAmount, true);
}, 5000);
});
}
};
const waitForPaymentViaZap = (payReqEvent, resolve, reject) => {
const zapmon = ndk.subscribe({
kinds: [68002, 9735],
...payReqEvent.filter()
}, { closeOnEose: false });
zapmon.on("event", (e) => {
const zapmon = ndk.subscribe(
{
kinds: [68002, 9735],
...payReqEvent.filter(),
},
{ closeOnEose: false },
);
zapmon.on('event', (e) => {
log(`received a ${e.kind} for the payment request`, e.rawEvent());
// TODO: validate amount, zapper, etc
@ -115,11 +153,11 @@ async function processJobEvent(event: NDKEvent, type: JobType): Promise<void> {
// zapmon.close();
resolve();
} else if (e.kind === 68003) {
zapmon.close();
zapmon.stop();
reject();
}
});
}
};
const waitForPaymentViaLNInvoice = (payReqEvent, resolve) => {
const amountTag = payReqEvent.getMatchingTags('amount')[0];
@ -129,8 +167,8 @@ async function processJobEvent(event: NDKEvent, type: JobType): Promise<void> {
const invoice = decode(bolt11);
const pr = invoice.payment_hash;
log({invoice});
log({pr});
log({ invoice });
log({ pr });
const checkInterval = setInterval(() => {
checkInvoiceStatus(invoice).then((status) => {
@ -140,40 +178,44 @@ async function processJobEvent(event: NDKEvent, type: JobType): Promise<void> {
clearInterval(checkInterval);
resolve();
}
})
});
}, 2000);
}
};
const waitForPayment = async (payReqEvent: NDKEvent): Promise<void> => {
log('waitForPayment');
const promise = new Promise<void>((resolve, reject) => {
waitForPaymentViaZap(payReqEvent, resolve, reject)
waitForPaymentViaLNInvoice(payReqEvent, resolve)
waitForPaymentViaZap(payReqEvent, resolve, reject);
waitForPaymentViaLNInvoice(payReqEvent, resolve);
});
return promise;
}
};
const startProcessing = async () => {
log('startProcessing');
inProgress(event);
await inProgress(event);
switch (type) {
case "summarize": {
case 'summarize': {
output = await onNewSummarizationJob(event);
break;
}
case "speech-to-text": {
case 'speech-to-text': {
output = await speechToTextJob(event);
break;
}
case 'blockChain-block-number': {
output = await blockChainBlockNumberJob(event);
break;
}
}
}
};
const publishResult = (): void => {
log('publishResult');
complete(event, missingAmount(), { output });
}
};
await validateJobRequest(event);
@ -184,13 +226,15 @@ async function processJobEvent(event: NDKEvent, type: JobType): Promise<void> {
await startProcessing();
await publishStatus(event, 'finished');
await publishStatus(event, 'finished').catch((err) =>
console.log('err :>> ', err),
);
if (jobAmount > paidAmount && waitForPaymentBeforePublishingResult()) {
await reqPayment();
await waitForPayment(payReqEvent);
log(`done with wait for publish`)
log(`done with wait for publish`);
}
await publishResult();
}
}