feat: added a new job blockChain-block-number
This commit is contained in:
parent
5f7d4fe72e
commit
9d336c0c4d
@ -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"
|
||||
}
|
||||
}
|
||||
|
112
package.json
112
package.json
@ -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"
|
||||
}
|
||||
}
|
||||
|
6010
pnpm-lock.yaml
6010
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
45
src/job-types/blockChain-block-number.ts
Normal file
45
src/job-types/blockChain-block-number.ts
Normal 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;
|
||||
}
|
156
src/main.ts
156
src/main.ts
@ -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,12 +226,14 @@ 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();
|
||||
|
Loading…
Reference in New Issue
Block a user