From 946b50261e32a24a9f4df7580525cb0ae66183b7 Mon Sep 17 00:00:00 2001 From: Yury Date: Thu, 23 May 2024 10:45:56 +0300 Subject: [PATCH 1/3] chore: fixed code format --- .prettierrc | 16 +- package-lock.json | 213 +----------- package.json | 114 ++++--- src/config/index.ts | 46 +-- src/job-types/blockChain-block-number.ts | 56 +-- src/job-types/speech-to-text.ts | 220 ++++++------ src/job-types/summarization.ts | 12 +- src/jobs/price.ts | 22 +- src/jobs/reactions/in-progress.ts | 26 +- src/jobs/reactions/status.ts | 36 +- src/local-signer.ts | 10 +- src/main.ts | 418 +++++++++++------------ src/utils/fetch-file-from-input.ts | 40 +-- src/utils/lnbits.ts | 46 +-- src/validations/expiration.ts | 16 +- src/validations/index.ts | 64 ++-- src/validations/no-recent-results.ts | 27 +- src/validations/requester.ts | 12 +- 18 files changed, 604 insertions(+), 790 deletions(-) diff --git a/.prettierrc b/.prettierrc index 207629b..293d90e 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,13 +1,7 @@ { - "singleQuote": true, - "trailingComma": "all", - "tabWidth": 4, - "overrides": [ - { - "files": ["*.ts", "*.mts"], - "options": { - "parser": "typescript" - } - } - ] + "trailingComma": "none", + "tabWidth": 2, + "semi": false, + "singleQuote": true, + "endOfLine": "auto" } diff --git a/package-lock.json b/package-lock.json index c45c9ef..4c76a63 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "0.1.0", "license": "MIT", "dependencies": { - "@nostr-dev-kit/ndk": "^0.7.5", + "@nostr-dev-kit/ndk": "^0.8.1", "axios": "^1.4.0", "debug": "^4.3.4", "file-type": "^18.5.0", @@ -29,7 +29,7 @@ "eslint-config-prettier": "~8.8", "eslint-plugin-jest": "~27.2", "jest": "~29.5", - "prettier": "~2.8", + "prettier": "3.2.5", "rimraf": "~5.0", "ts-api-utils": "~0.0.44", "ts-jest": "~29.1", @@ -1644,9 +1644,9 @@ } }, "node_modules/@nostr-dev-kit/ndk": { - "version": "0.7.7", - "resolved": "https://registry.npmjs.org/@nostr-dev-kit/ndk/-/ndk-0.7.7.tgz", - "integrity": "sha512-IRTW16q40zzuSBkpYpDUcZJRrbw26JTeicfZN6O/2Gw7D2w6Pe42VqFwpbcP9xOnFPEGP2eNV6SwXQ3y0tjBtw==", + "version": "0.8.23", + "resolved": "https://registry.npmjs.org/@nostr-dev-kit/ndk/-/ndk-0.8.23.tgz", + "integrity": "sha512-wX/9Cl02gCR0Kz25C/1xxGO47K13Ve1x8IISbkF/M3RTTXftYBvzB7bL8qwLaFaeqb02cMU0YVL+oKDrYzH/Ng==", "dependencies": { "@noble/hashes": "^1.3.1", "@noble/secp256k1": "^2.0.0", @@ -1663,10 +1663,9 @@ "eventemitter3": "^5.0.1", "light-bolt11-decoder": "^3.0.0", "node-fetch": "^3.3.1", - "nostr-tools": "^1.11.2", + "nostr-tools": "^1.14.0", "tsd": "^0.28.1", - "utf8-buffer": "^1.0.0", - "websocket-polyfill": "^0.0.3" + "utf8-buffer": "^1.0.0" } }, "node_modules/@pkgjs/parseargs": { @@ -2552,18 +2551,6 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, - "node_modules/bufferutil": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.8.tgz", - "integrity": "sha512-4T53u4PdgsXqKaIctwF8ifXlRTTmEPJ8iEPWFdGZvcf7sbwYo6FKFEX9eNNAnzFZ7EzJAQ3CJeOtCRA4rDp7Pw==", - "hasInstallScript": true, - "dependencies": { - "node-gyp-build": "^4.3.0" - }, - "engines": { - "node": ">=6.14.2" - } - }, "node_modules/call-bind": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", @@ -2792,18 +2779,6 @@ "node": ">= 8" } }, - "node_modules/d": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.2.tgz", - "integrity": "sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==", - "dependencies": { - "es5-ext": "^0.10.64", - "type": "^2.7.2" - }, - "engines": { - "node": ">=0.12" - } - }, "node_modules/data-uri-to-buffer": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", @@ -3177,43 +3152,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/es5-ext": { - "version": "0.10.64", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.64.tgz", - "integrity": "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==", - "hasInstallScript": true, - "dependencies": { - "es6-iterator": "^2.0.3", - "es6-symbol": "^3.1.3", - "esniff": "^2.0.1", - "next-tick": "^1.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/es6-iterator": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", - "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", - "dependencies": { - "d": "1", - "es5-ext": "^0.10.35", - "es6-symbol": "^3.1.1" - } - }, - "node_modules/es6-symbol": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.4.tgz", - "integrity": "sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==", - "dependencies": { - "d": "^1.0.2", - "ext": "^1.7.0" - }, - "engines": { - "node": ">=0.12" - } - }, "node_modules/esbuild": { "version": "0.17.19", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.19.tgz", @@ -3561,20 +3499,6 @@ "node": ">=14.17" } }, - "node_modules/esniff": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz", - "integrity": "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==", - "dependencies": { - "d": "^1.0.1", - "es5-ext": "^0.10.62", - "event-emitter": "^0.3.5", - "type": "^2.7.2" - }, - "engines": { - "node": ">=0.10" - } - }, "node_modules/espree": { "version": "9.6.1", "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", @@ -3658,15 +3582,6 @@ "node": ">=0.10.0" } }, - "node_modules/event-emitter": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", - "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==", - "dependencies": { - "d": "1", - "es5-ext": "~0.10.14" - } - }, "node_modules/eventemitter3": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", @@ -3720,14 +3635,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/ext": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", - "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==", - "dependencies": { - "type": "^2.7.2" - } - }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -4702,11 +4609,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" - }, "node_modules/is-unicode-supported": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", @@ -5884,11 +5786,6 @@ "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==" }, - "node_modules/next-tick": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", - "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==" - }, "node_modules/nice-try": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", @@ -5929,16 +5826,6 @@ "url": "https://opencollective.com/node-fetch" } }, - "node_modules/node-gyp-build": { - "version": "4.8.1", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.1.tgz", - "integrity": "sha512-OSs33Z9yWr148JZcbZd5WiAXhh/n9z8TxQcdMhIOlpN9AhWpLfvVFO73+m77bBABQMaY9XSvIa+qk0jlI7Gcaw==", - "bin": { - "node-gyp-build": "bin.js", - "node-gyp-build-optional": "optional.js", - "node-gyp-build-test": "build-test.js" - } - }, "node_modules/node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -6578,15 +6465,15 @@ } }, "node_modules/prettier": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", - "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz", + "integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==", "dev": true, "bin": { - "prettier": "bin-prettier.js" + "prettier": "bin/prettier.cjs" }, "engines": { - "node": ">=10.13.0" + "node": ">=14" }, "funding": { "url": "https://github.com/prettier/prettier?sponsor=1" @@ -7659,11 +7546,6 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.3.tgz", "integrity": "sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==" }, - "node_modules/tstl": { - "version": "2.5.16", - "resolved": "https://registry.npmjs.org/tstl/-/tstl-2.5.16.tgz", - "integrity": "sha512-+O2ybLVLKcBwKm4HymCEwZIT0PpwS3gCYnxfSDEjJEKADvIFruaQjd3m7CAKNU1c7N3X3WjVz87re7TA2A5FUw==" - }, "node_modules/tsutils": { "version": "3.21.0", "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", @@ -7683,11 +7565,6 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" }, - "node_modules/type": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz", - "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==" - }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -7788,14 +7665,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "dependencies": { - "is-typedarray": "^1.0.0" - } - }, "node_modules/typescript": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz", @@ -7866,18 +7735,6 @@ "punycode": "^2.1.0" } }, - "node_modules/utf-8-validate": { - "version": "5.0.10", - "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.10.tgz", - "integrity": "sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==", - "hasInstallScript": true, - "dependencies": { - "node-gyp-build": "^4.3.0" - }, - "engines": { - "node": ">=6.14.2" - } - }, "node_modules/utf8-buffer": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/utf8-buffer/-/utf8-buffer-1.0.0.tgz", @@ -7931,44 +7788,6 @@ "node": ">= 14" } }, - "node_modules/websocket": { - "version": "1.0.34", - "resolved": "https://registry.npmjs.org/websocket/-/websocket-1.0.34.tgz", - "integrity": "sha512-PRDso2sGwF6kM75QykIesBijKSVceR6jL2G8NGYyq2XrItNC2P5/qL5XeR056GhA+Ly7JMFvJb9I312mJfmqnQ==", - "dependencies": { - "bufferutil": "^4.0.1", - "debug": "^2.2.0", - "es5-ext": "^0.10.50", - "typedarray-to-buffer": "^3.1.5", - "utf-8-validate": "^5.0.2", - "yaeti": "^0.0.6" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/websocket-polyfill": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/websocket-polyfill/-/websocket-polyfill-0.0.3.tgz", - "integrity": "sha512-pF3kR8Uaoau78MpUmFfzbIRxXj9PeQrCuPepGE6JIsfsJ/o/iXr07Q2iQNzKSSblQJ0FiGWlS64N4pVSm+O3Dg==", - "dependencies": { - "tstl": "^2.0.7", - "websocket": "^1.0.28" - } - }, - "node_modules/websocket/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/websocket/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -8086,14 +7905,6 @@ "node": ">=10" } }, - "node_modules/yaeti": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz", - "integrity": "sha512-MvQa//+KcZCUkBTIC9blM+CU9J2GzuTytsOUwf2lidtvkx/6gnEp1QvJv34t9vdjhFmha/mUiNDbN0D0mJWdug==", - "engines": { - "node": ">=0.10.32" - } - }, "node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", diff --git a/package.json b/package.json index dd0c117..0fb21bb 100644 --- a/package.json +++ b/package.json @@ -1,58 +1,60 @@ { - "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" - } + "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": "3.2.5", + "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", + "formatter:check": "prettier --check \"src/**/*.{ts,tsx,js,jsx,html,css,sass,less,yml,md,graphql}\"", + "formatter:fix": "prettier --write \"src/**/*.{ts,tsx,js,jsx,html,css,sass,less,yml,md,graphql}\"" + }, + "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" + } } diff --git a/src/config/index.ts b/src/config/index.ts index be30bdd..3faec73 100644 --- a/src/config/index.ts +++ b/src/config/index.ts @@ -1,37 +1,37 @@ -import { NDKPrivateKeySigner } from '@nostr-dev-kit/ndk'; -import fs from 'fs'; -import { log, configFile } from '../main.js'; +import { NDKPrivateKeySigner } from '@nostr-dev-kit/ndk' +import fs from 'fs' +import { log, configFile } from '../main.js' type IConfig = { - key: string; - discount?: number; - undercut?: number; - processWithoutPaymentLimit?: number; - serveResultsWithoutPaymentLimit?: number; + key: string + discount?: number + undercut?: number + processWithoutPaymentLimit?: number + serveResultsWithoutPaymentLimit?: number } export function getConfig(): IConfig { - let config: IConfig; + let config: IConfig - if (fs.existsSync(configFile)) { - config = JSON.parse(fs.readFileSync(configFile, 'utf8')); + if (fs.existsSync(configFile)) { + config = JSON.parse(fs.readFileSync(configFile, 'utf8')) + } + + if (!config) { + log('Generating new config') + config = { + key: NDKPrivateKeySigner.generate().privateKey } - if (!config) { - log('Generating new config') - config = { - key: NDKPrivateKeySigner.generate().privateKey, - }; + const signer = NDKPrivateKeySigner.generate() + config.key = signer.privateKey - const signer = NDKPrivateKeySigner.generate(); - config.key = signer.privateKey; + saveConfig(config) + } - saveConfig(config); - } - - return config; + return config } export function saveConfig(config: IConfig) { - fs.writeFileSync(configFile, JSON.stringify(config)); + fs.writeFileSync(configFile, JSON.stringify(config)) } diff --git a/src/job-types/blockChain-block-number.ts b/src/job-types/blockChain-block-number.ts index 379499c..555d3ee 100644 --- a/src/job-types/blockChain-block-number.ts +++ b/src/job-types/blockChain-block-number.ts @@ -1,43 +1,43 @@ -import { NDKEvent } from '@nostr-dev-kit/ndk'; -import { log } from '../main.js'; -import axios from 'axios'; +import { NDKEvent } from '@nostr-dev-kit/ndk' +import { log } from '../main.js' +import axios from 'axios' export async function blockChainBlockNumberJob( - event: NDKEvent, + event: NDKEvent ): Promise { - log('New blockChain-block-number job', event.rawEvent()); + log('New blockChain-block-number job', event.rawEvent()) - const input = event.tagValue('i'); + const input = event.tagValue('i') - const blockChainUrl = `https://blockchain.info/blocks/${input}?format=json`; + const blockChainUrl = `https://blockchain.info/blocks/${input}?format=json` - 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); - }); + 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; + 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; + let closestObject = null + let minDifference = Infinity - array.forEach((obj) => { - const difference = Math.abs(obj.time - timestamp); - if (difference < minDifference) { - minDifference = difference; - closestObject = obj; - } - }); + array.forEach((obj) => { + const difference = Math.abs(obj.time - timestamp) + if (difference < minDifference) { + minDifference = difference + closestObject = obj + } + }) - return closestObject; + return closestObject } diff --git a/src/job-types/speech-to-text.ts b/src/job-types/speech-to-text.ts index 6d16c5c..7c353b3 100644 --- a/src/job-types/speech-to-text.ts +++ b/src/job-types/speech-to-text.ts @@ -1,142 +1,144 @@ -import { NDKEvent, type NostrEvent } from '@nostr-dev-kit/ndk'; -import axios from 'axios'; -import FormData from 'form-data'; -import { log } from '../main.js'; -import { fetchFileFromInput } from '../utils/fetch-file-from-input.js'; -import { ndk } from '../main.js'; +import { NDKEvent, type NostrEvent } from '@nostr-dev-kit/ndk' +import axios from 'axios' +import FormData from 'form-data' +import { log } from '../main.js' +import { fetchFileFromInput } from '../utils/fetch-file-from-input.js' +import { ndk } from '../main.js' // import { fileTypeFromFile, type FileTypeResult } from 'file-type'; -import { exec } from 'child_process'; -import fs from 'fs'; -import { createInvoice } from '../utils/lnbits.js'; +import { exec } from 'child_process' +import fs from 'fs' +import { createInvoice } from '../utils/lnbits.js' export async function speechToTextJob(event: NDKEvent): Promise { - log("New speech-to-text job", event.rawEvent()); + log('New speech-to-text job', event.rawEvent()) - // fetch the input file - const file = await fetchFile(event); - log(`file: ${file}`); + // fetch the input file + const file = await fetchFile(event) + log(`file: ${file}`) - if (!file) { - return undefined; - } + if (!file) { + return undefined + } - // check if file exists - // if (!fs.existsSync(file)) { - // log(`file does not exist: ${file}`); - // return undefined; - // } + // check if file exists + // if (!fs.existsSync(file)) { + // log(`file does not exist: ${file}`); + // return undefined; + // } - // let fileType: FileTypeResult; + // let fileType: FileTypeResult; - // try { - // fileType = await fileTypeFromFile(file); - // log(`fileType: ${fileType}`); - // } catch (error) { - // log(error); - // } + // try { + // fileType = await fileTypeFromFile(file); + // log(`fileType: ${fileType}`); + // } catch (error) { + // log(error); + // } - const whisperCommand = (resolve: any, file: string) => { - setTimeout(() => { + const whisperCommand = (resolve: any, file: string) => { + setTimeout(() => { + if (!fs.existsSync(file)) { + log(`file does not exist: ${file}`) + return undefined + } + const formData = new FormData() + const a = fs.createReadStream(file) + console.log({ a }) - if (!fs.existsSync(file)) { - log(`file does not exist: ${file}`); - return undefined; - } - const formData = new FormData(); - const a = fs.createReadStream(file); - console.log({a}); + formData.append('file', a) + formData.append('model', 'whisper-1') - formData.append('file', a); - formData.append('model', 'whisper-1'); + axios + .post('http://127.0.0.1:10111/v1/audio/transcriptions', formData, { + headers: { + Authorization: 'Bearer fsdfdsf', + ...formData.getHeaders() + } + }) + .then((response) => { + const { text } = response.data + console.log({ text }) + resolve(text) + // Handle response + }) + .catch((error) => { + console.log({ error }) + // Handle error + }) + }, 1000) + } - axios.post('http://127.0.0.1:10111/v1/audio/transcriptions', formData, { - headers: { - 'Authorization': 'Bearer fsdfdsf', - ...formData.getHeaders(), - }, - }).then(response => { - const { text } = response.data; - console.log({text}); - resolve(text); - // Handle response - }) - .catch(error => { - console.log({error}); - // Handle error - }); - }, 1000); - } + // const input = event.tagValue('i'); + const range = event.getMatchingTags('param').find((tag) => tag[1] === 'range') + return new Promise((resolve) => whisperCommand(resolve, file)) - // const input = event.tagValue('i'); - const range = event.getMatchingTags('param').find((tag) => tag[1] === 'range'); - return new Promise((resolve) => whisperCommand(resolve, file)); - - if (range) { - return new Promise((resolve) => { - const startTime = range[2]; - const endTime = range[3]; - const randomName = Math.random().toString(36).substring(7)+'.mp3'; - const command = `ffmpeg -ss ${startTime} -to ${endTime} -i ${file} -vn -acodec copy ${randomName}`; - console.log({startTime, endTime, randomName, command}); - exec(command, (error, stderr, stdout) => { - console.log('ffmpeg', {error, stderr, stdout}); + if (range) { + return new Promise((resolve) => { + const startTime = range[2] + const endTime = range[3] + const randomName = Math.random().toString(36).substring(7) + '.mp3' + const command = `ffmpeg -ss ${startTime} -to ${endTime} -i ${file} -vn -acodec copy ${randomName}` + console.log({ startTime, endTime, randomName, command }) + exec(command, (error, stderr, stdout) => { + console.log('ffmpeg', { error, stderr, stdout }) - whisperCommand(resolve, randomName); - }); - }); - } else { - return new Promise((resolve) => whisperCommand(resolve, file)); - } + whisperCommand(resolve, randomName) + }) + }) + } else { + return new Promise((resolve) => whisperCommand(resolve, file)) + } } interface ICompleteParams { - output: string; + output: string } -export async function addAmount(event: NDKEvent, amount: number, includeInvoice = true): Promise { - const tag = ['amount', amount.toString()]; +export async function addAmount( + event: NDKEvent, + amount: number, + includeInvoice = true +): Promise { + const tag = ['amount', amount.toString()] - if (includeInvoice) { - const invoice = await createInvoice(amount / 1000); - tag.push(invoice.payment_request); - } + if (includeInvoice) { + const invoice = await createInvoice(amount / 1000) + tag.push(invoice.payment_request) + } - event.tags.push(tag); + event.tags.push(tag) } export async function complete( - jobRequest: NDKEvent, - amount: number, - completeParams: ICompleteParams, - includeInvoice?: boolean + jobRequest: NDKEvent, + amount: number, + completeParams: ICompleteParams, + includeInvoice?: boolean ): Promise { - const jobResult = new NDKEvent(ndk, { - kind: 68002, - content: completeParams.output, - tags: [ - [ 'status', 'success' ] - ] - } as NostrEvent); + const jobResult = new NDKEvent(ndk, { + kind: 68002, + content: completeParams.output, + tags: [['status', 'success']] + } as NostrEvent) - if (amount > 0) { - await addAmount(jobResult, amount, includeInvoice); - } - jobResult.tag(jobRequest); + if (amount > 0) { + await addAmount(jobResult, amount, includeInvoice) + } + jobResult.tag(jobRequest) + await jobResult.sign() + log(jobResult.rawEvent()) - await jobResult.sign(); - log(jobResult.rawEvent()); - - await jobResult.publish(); - return jobResult; + await jobResult.publish() + return jobResult } export async function fetchFile(event: NDKEvent): Promise { - const inputTags = event.getMatchingTags("i"); + const inputTags = event.getMatchingTags('i') - if (inputTags.length !== 1) { - throw new Error(`Incorrect number of inputs: ${inputTags.length}`); - } + if (inputTags.length !== 1) { + throw new Error(`Incorrect number of inputs: ${inputTags.length}`) + } - return fetchFileFromInput(inputTags[0]); -} \ No newline at end of file + return fetchFileFromInput(inputTags[0]) +} diff --git a/src/job-types/summarization.ts b/src/job-types/summarization.ts index 0fe8a9e..e347f11 100644 --- a/src/job-types/summarization.ts +++ b/src/job-types/summarization.ts @@ -1,11 +1,11 @@ -import { NDKEvent } from '@nostr-dev-kit/ndk'; -import { validateJobRequest } from '../validations/index.js'; -import { inProgress } from '../jobs/reactions/in-progress.js'; +import { NDKEvent } from '@nostr-dev-kit/ndk' +import { validateJobRequest } from '../validations/index.js' +import { inProgress } from '../jobs/reactions/in-progress.js' export async function onNewSummarizationJob(event: NDKEvent): Promise { - console.log("New summarization job"); + console.log('New summarization job') - await validateJobRequest(event); + await validateJobRequest(event) - inProgress(event); + inProgress(event) } diff --git a/src/jobs/price.ts b/src/jobs/price.ts index 3e4e922..b21b105 100644 --- a/src/jobs/price.ts +++ b/src/jobs/price.ts @@ -1,17 +1,17 @@ -import { NDKEvent } from '@nostr-dev-kit/ndk'; -import { log } from '../main.js'; -import { getConfig } from '../config/index.js'; +import { NDKEvent } from '@nostr-dev-kit/ndk' +import { log } from '../main.js' +import { getConfig } from '../config/index.js' export async function priceJob(event: NDKEvent): Promise { - const config = getConfig(); - const bidTag = event.tagValue('bid'); - let bidAmount = bidTag ? parseInt(bidTag) : 1001 * 1000; + const config = getConfig() + const bidTag = event.tagValue('bid') + let bidAmount = bidTag ? parseInt(bidTag) : 1001 * 1000 - if (config.discount) { - bidAmount = bidAmount * config.discount; - } + if (config.discount) { + bidAmount = bidAmount * config.discount + } - log(`bid amount: ${bidAmount} (${config.discount??'no'} discount)`); + log(`bid amount: ${bidAmount} (${config.discount ?? 'no'} discount)`) - return bidAmount; + return bidAmount } diff --git a/src/jobs/reactions/in-progress.ts b/src/jobs/reactions/in-progress.ts index 5c551f9..c8a69e7 100644 --- a/src/jobs/reactions/in-progress.ts +++ b/src/jobs/reactions/in-progress.ts @@ -1,19 +1,17 @@ -import { NDKEvent } from '@nostr-dev-kit/ndk'; -import { log, ndk } from '../../main.js'; +import { NDKEvent } from '@nostr-dev-kit/ndk' +import { log, ndk } from '../../main.js' export async function inProgress(event: NDKEvent): Promise { - log("marking job as in progress"); + log('marking job as in progress') - const reactEvent = new NDKEvent(ndk, { - kind: 68003, - content: "👍", - tags: [ - [ "status", "started" ], - ] - }) + const reactEvent = new NDKEvent(ndk, { + kind: 68003, + content: '👍', + tags: [['status', 'started']] + }) - reactEvent.tag(event, "job"); - await reactEvent.sign(); - await reactEvent.publish(); - return reactEvent; + reactEvent.tag(event, 'job') + await reactEvent.sign() + await reactEvent.publish() + return reactEvent } diff --git a/src/jobs/reactions/status.ts b/src/jobs/reactions/status.ts index 739e2a2..340bbd7 100644 --- a/src/jobs/reactions/status.ts +++ b/src/jobs/reactions/status.ts @@ -1,25 +1,23 @@ -import { NDKEvent, NDKTag } from '@nostr-dev-kit/ndk'; -import { log, ndk } from '../../main.js'; +import { NDKEvent, NDKTag } from '@nostr-dev-kit/ndk' +import { log, ndk } from '../../main.js' export async function publishStatus( - event: NDKEvent, - status: string, - extraTags: NDKTag[] = []): Promise { - log("marking job as finished"); + event: NDKEvent, + status: string, + extraTags: NDKTag[] = [] +): Promise { + log('marking job as finished') - const reactEvent = new NDKEvent(ndk, { - kind: 68003, - content: "👍", - tags: [ - [ "status", status ], - ...extraTags - ] - }) + const reactEvent = new NDKEvent(ndk, { + kind: 68003, + content: '👍', + tags: [['status', status], ...extraTags] + }) - console.log({extraTags}) + console.log({ extraTags }) - reactEvent.tag(event, "job"); - await reactEvent.sign(); - await reactEvent.publish(); - return reactEvent; + reactEvent.tag(event, 'job') + await reactEvent.sign() + await reactEvent.publish() + return reactEvent } diff --git a/src/local-signer.ts b/src/local-signer.ts index 0c5ae49..c167b21 100644 --- a/src/local-signer.ts +++ b/src/local-signer.ts @@ -1,8 +1,8 @@ -import { NDKPrivateKeySigner } from '@nostr-dev-kit/ndk'; -import { getConfig } from './config/index.js'; +import { NDKPrivateKeySigner } from '@nostr-dev-kit/ndk' +import { getConfig } from './config/index.js' export default function getSigner(): NDKPrivateKeySigner { - const config = getConfig(); + const config = getConfig() - return new NDKPrivateKeySigner(config.key!); -} \ No newline at end of file + return new NDKPrivateKeySigner(config.key!) +} diff --git a/src/main.ts b/src/main.ts index 9425873..cdf88e9 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,240 +1,238 @@ -import NDK, { NDKEvent } from '@nostr-dev-kit/ndk'; -import debug from 'debug'; -import { onNewSummarizationJob } from './job-types/summarization.js'; -import { complete, speechToTextJob } from './job-types/speech-to-text.js'; -import getSigner from './local-signer.js'; -import { requirePayment, validateJobRequest } from './validations/index.js'; -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 { checkInvoiceStatus } from './utils/lnbits.js'; -import { blockChainBlockNumberJob } from './job-types/blockChain-block-number.js'; +import NDK, { NDKEvent } from '@nostr-dev-kit/ndk' +import debug from 'debug' +import { onNewSummarizationJob } from './job-types/summarization.js' +import { complete, speechToTextJob } from './job-types/speech-to-text.js' +import getSigner from './local-signer.js' +import { requirePayment, validateJobRequest } from './validations/index.js' +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 { checkInvoiceStatus } from './utils/lnbits.js' +import { blockChainBlockNumberJob } from './job-types/blockChain-block-number.js' -export const log = debug('fool-me-once-dvm'); +export const log = debug('fool-me-once-dvm') export const configFile = - process.argv[2] || `${process.env.HOME}/.fool-me-once.json`; + process.argv[2] || `${process.env.HOME}/.fool-me-once.json` -log('configFile', { configFile }); +log('configFile', { configFile }) export const ndk = new NDK({ - explicitRelayUrls: [ - 'wss://relay.damus.io', - 'wss://relay.primal.net', - 'wss://relayable.org', - ], - signer: getSigner(), -}); -await ndk.connect(2000); -log('connected'); + explicitRelayUrls: [ + 'wss://relay.damus.io', + 'wss://relay.primal.net', + 'wss://relayable.org' + ], + signer: getSigner() +}) +await ndk.connect(2000) +log('connected') const subs = ndk.subscribe( - { - kinds: [68001 as number], - since: Math.floor(Date.now() / 1000), - '#j': ['summarize', 'explain'], - }, - { closeOnEose: false }, -); + { + kinds: [68001 as number], + since: Math.floor(Date.now() / 1000), + '#j': ['summarize', 'explain'] + }, + { closeOnEose: false } +) const speechToTextSub = ndk.subscribe( - { - kinds: [68001 as number], - since: Math.floor(Date.now() / 1000), - '#j': ['speech-to-text'], - }, - { closeOnEose: false }, -); + { + kinds: [68001 as number], + since: Math.floor(Date.now() / 1000), + '#j': ['speech-to-text'] + }, + { closeOnEose: false } +) const blockChainBlockNumberSub = ndk.subscribe( - { - kinds: [68001 as number], - since: Math.floor(Date.now() / 1000), - '#j': ['blockChain-block-number'], - }, - { closeOnEose: false }, -); + { + kinds: [68001 as number], + since: Math.floor(Date.now() / 1000), + '#j': ['blockChain-block-number'] + }, + { closeOnEose: false } +) -subs.on('event', (e) => processJobEvent(e, 'summarize')); -speechToTextSub.on('event', (e) => processJobEvent(e, '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'), -); + processJobEvent(e, 'blockChain-block-number') +) type JobType = - | 'summarize' - | 'explain' - | 'speech-to-text' - | 'blockChain-block-number'; + | 'summarize' + | 'explain' + | 'speech-to-text' + | 'blockChain-block-number' async function processJobEvent(event: NDKEvent, type: JobType): Promise { - const config = getConfig(); - let jobAmount = - type === 'blockChain-block-number' ? 0 : await priceJob(event); - let output: any; - let payReqEvent: NDKEvent; - let paidAmount = 0; + const config = getConfig() + 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 waitForPaymentBeforeProcessing = (): boolean => { + 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 waitForPaymentBeforePublishingResult = (): boolean => { + const serveResultsWithoutPaymentLimit = + config.serveResultsWithoutPaymentLimit ?? 1000 + log('waitForPaymentBeforeProcessing', { jobAmount }) + return jobAmount && jobAmount > serveResultsWithoutPaymentLimit * 1000 + } - const missingAmount = (): number => jobAmount - paidAmount; + const missingAmount = (): number => jobAmount - paidAmount - const reqPayment = async (): Promise => { - payReqEvent = await requirePayment(event, missingAmount(), true); + const reqPayment = async (): Promise => { + payReqEvent = await requirePayment(event, missingAmount(), true) - if (config.undercut) { - startUndercutting(); - } - }; - - const startUndercutting = async (): Promise => { - 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 - const amountValue = e.tagValue('amount'); - if (!amountValue) return; - - log(`found someone else's bid`, amountValue); - - // check if it's more-or-less than the current bid - const amount = parseInt(amountValue); - if (amount > jobAmount) return; - - // if so, undercut - jobAmount = Math.round(amount * config.undercut); - log(`undercutting to ${jobAmount}`); - setTimeout(async () => { - 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) => { - log(`received a ${e.kind} for the payment request`, e.rawEvent()); - - // TODO: validate amount, zapper, etc - if (e.kind === 9735) { - // TODO: This needs to check the actual zap - paidAmount = jobAmount; - - // zapmon.close(); - resolve(); - } else if (e.kind === 68003) { - zapmon.stop(); - reject(); - } - }); - }; - - const waitForPaymentViaLNInvoice = (payReqEvent, resolve) => { - const amountTag = payReqEvent.getMatchingTags('amount')[0]; - const bolt11 = amountTag[2]; - - if (!bolt11) return; - - const invoice = decode(bolt11); - const pr = invoice.payment_hash; - log({ invoice }); - log({ pr }); - - const checkInterval = setInterval(() => { - checkInvoiceStatus(invoice).then((status) => { - if (status.paid) { - log('invoice paid'); - paidAmount = jobAmount; - clearInterval(checkInterval); - resolve(); - } - }); - }, 2000); - }; - - const waitForPayment = async (payReqEvent: NDKEvent): Promise => { - log('waitForPayment'); - const promise = new Promise((resolve, reject) => { - waitForPaymentViaZap(payReqEvent, resolve, reject); - waitForPaymentViaLNInvoice(payReqEvent, resolve); - }); - - return promise; - }; - - const startProcessing = async () => { - log('startProcessing'); - await inProgress(event); - - switch (type) { - case 'summarize': { - output = await onNewSummarizationJob(event); - break; - } - 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); - - if (jobAmount > paidAmount && waitForPaymentBeforeProcessing()) { - await reqPayment(); - await waitForPayment(payReqEvent); + if (config.undercut) { + startUndercutting() } + } - await startProcessing(); + const startUndercutting = async (): Promise => { + 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 - await publishStatus(event, 'finished').catch((err) => - console.log('err :>> ', err), - ); + // check if this is a payment request + const amountValue = e.tagValue('amount') + if (!amountValue) return - if (jobAmount > paidAmount && waitForPaymentBeforePublishingResult()) { - await reqPayment(); - await waitForPayment(payReqEvent); - log(`done with wait for publish`); + log(`found someone else's bid`, amountValue) + + // check if it's more-or-less than the current bid + const amount = parseInt(amountValue) + if (amount > jobAmount) return + + // if so, undercut + jobAmount = Math.round(amount * config.undercut) + log(`undercutting to ${jobAmount}`) + setTimeout(async () => { + 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) => { + log(`received a ${e.kind} for the payment request`, e.rawEvent()) + + // TODO: validate amount, zapper, etc + if (e.kind === 9735) { + // TODO: This needs to check the actual zap + paidAmount = jobAmount + + // zapmon.close(); + resolve() + } else if (e.kind === 68003) { + zapmon.stop() + reject() + } + }) + } + + const waitForPaymentViaLNInvoice = (payReqEvent, resolve) => { + const amountTag = payReqEvent.getMatchingTags('amount')[0] + const bolt11 = amountTag[2] + + if (!bolt11) return + + const invoice = decode(bolt11) + const pr = invoice.payment_hash + log({ invoice }) + log({ pr }) + + const checkInterval = setInterval(() => { + checkInvoiceStatus(invoice).then((status) => { + if (status.paid) { + log('invoice paid') + paidAmount = jobAmount + clearInterval(checkInterval) + resolve() + } + }) + }, 2000) + } + + const waitForPayment = async (payReqEvent: NDKEvent): Promise => { + log('waitForPayment') + const promise = new Promise((resolve, reject) => { + waitForPaymentViaZap(payReqEvent, resolve, reject) + waitForPaymentViaLNInvoice(payReqEvent, resolve) + }) + + return promise + } + + const startProcessing = async () => { + log('startProcessing') + await inProgress(event) + + switch (type) { + case 'summarize': { + output = await onNewSummarizationJob(event) + break + } + case 'speech-to-text': { + output = await speechToTextJob(event) + break + } + case 'blockChain-block-number': { + output = await blockChainBlockNumberJob(event) + break + } } + } - await publishResult(); + const publishResult = (): void => { + log('publishResult') + complete(event, missingAmount(), { output }) + } + + await validateJobRequest(event) + + if (jobAmount > paidAmount && waitForPaymentBeforeProcessing()) { + await reqPayment() + await waitForPayment(payReqEvent) + } + + await startProcessing() + + 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`) + } + + await publishResult() } diff --git a/src/utils/fetch-file-from-input.ts b/src/utils/fetch-file-from-input.ts index 8836730..12c7029 100644 --- a/src/utils/fetch-file-from-input.ts +++ b/src/utils/fetch-file-from-input.ts @@ -1,28 +1,28 @@ -import { NDKTag } from '@nostr-dev-kit/ndk'; -import axios from 'axios'; -import fs from 'fs'; +import { NDKTag } from '@nostr-dev-kit/ndk' +import axios from 'axios' +import fs from 'fs' export async function fetchFileFromInput(input: NDKTag): Promise { - switch (input[2]) { - case 'url': { - const url = input[1]; - const fileExtension = url.split('.').pop(); + switch (input[2]) { + case 'url': { + const url = input[1] + const fileExtension = url.split('.').pop() - // download the file - // save it to the local filesystem - // return the path to the file - const response = await axios.get(url, { - responseType: 'stream' - }); + // download the file + // save it to the local filesystem + // return the path to the file + const response = await axios.get(url, { + responseType: 'stream' + }) - const randomName = Math.random().toString(36).substring(7); - const path = `./${randomName}.${fileExtension}`; + const randomName = Math.random().toString(36).substring(7) + const path = `./${randomName}.${fileExtension}` - await response.data.pipe(fs.createWriteStream(path)); + await response.data.pipe(fs.createWriteStream(path)) - return path; - } + return path } + } - return undefined; -} \ No newline at end of file + return undefined +} diff --git a/src/utils/lnbits.ts b/src/utils/lnbits.ts index 62f6855..dc1286f 100644 --- a/src/utils/lnbits.ts +++ b/src/utils/lnbits.ts @@ -1,38 +1,38 @@ -import _LNBits from "lnbits"; +import _LNBits from 'lnbits' -let LNBits: any; +let LNBits: any if (_LNBits.default) { - LNBits = _LNBits.default; + LNBits = _LNBits.default } else { - LNBits = _LNBits; + LNBits = _LNBits } -function getWallet (): any { - return LNBits({ - adminKey: "", - invoiceReadKey: 'bfbcb5b116c04179bc618ab78265a939', - endpoint: 'https://legend.lnbits.com' - }); +function getWallet(): any { + return LNBits({ + adminKey: '', + invoiceReadKey: 'bfbcb5b116c04179bc618ab78265a939', + endpoint: 'https://legend.lnbits.com' + }) } export async function createInvoice(amount: number): Promise { - const { wallet } = getWallet(); + const { wallet } = getWallet() - const newInvoice = await wallet.createInvoice({ - amount: amount, - memo: 'data vending machine', - out: false, - }); + const newInvoice = await wallet.createInvoice({ + amount: amount, + memo: 'data vending machine', + out: false + }) - return newInvoice; + return newInvoice } export async function checkInvoiceStatus(invoice): Promise { - const { wallet } = getWallet(); - const invoiceStatus = await wallet.checkInvoice({ - payment_hash: invoice.payment_hash, - }); + const { wallet } = getWallet() + const invoiceStatus = await wallet.checkInvoice({ + payment_hash: invoice.payment_hash + }) - return invoiceStatus; -} \ No newline at end of file + return invoiceStatus +} diff --git a/src/validations/expiration.ts b/src/validations/expiration.ts index 284ad12..3be80d1 100644 --- a/src/validations/expiration.ts +++ b/src/validations/expiration.ts @@ -1,12 +1,12 @@ -import { NDKEvent } from '@nostr-dev-kit/ndk'; +import { NDKEvent } from '@nostr-dev-kit/ndk' export default async function validateExpiration(event: NDKEvent) { - const expTag = event.tagValue('exp'); - const timeNow = Math.floor(Date.now() / 1000); + const expTag = event.tagValue('exp') + const timeNow = Math.floor(Date.now() / 1000) - if (expTag && parseInt(expTag) < timeNow - 10) { - throw new Error('Job expired'); - } + if (expTag && parseInt(expTag) < timeNow - 10) { + throw new Error('Job expired') + } - return -} \ No newline at end of file + return +} diff --git a/src/validations/index.ts b/src/validations/index.ts index f7d6718..2b4074a 100644 --- a/src/validations/index.ts +++ b/src/validations/index.ts @@ -1,40 +1,42 @@ -import { NDKEvent, type NostrEvent } from '@nostr-dev-kit/ndk'; -import validateExpiration from './expiration.js'; -import validateRequester from './requester.js'; -import validateNoRecentResults from './no-recent-results.js'; -import { ndk } from '../main.js'; -import { addAmount } from '../job-types/speech-to-text.js'; +import { NDKEvent, type NostrEvent } from '@nostr-dev-kit/ndk' +import validateExpiration from './expiration.js' +import validateRequester from './requester.js' +import validateNoRecentResults from './no-recent-results.js' +import { ndk } from '../main.js' +import { addAmount } from '../job-types/speech-to-text.js' export async function validateJobRequest(event: NDKEvent): Promise { - await validateExpiration(event); - await validateRequester(event); - await validateNoRecentResults(event); + await validateExpiration(event) + await validateRequester(event) + await validateNoRecentResults(event) } -export async function requirePayment(event: NDKEvent, amount?: number, publish?: boolean): Promise { - if (!amount) { - const bidTag = event.tagValue('bid'); - amount = bidTag ? parseInt(bidTag) : undefined; - } +export async function requirePayment( + event: NDKEvent, + amount?: number, + publish?: boolean +): Promise { + if (!amount) { + const bidTag = event.tagValue('bid') + amount = bidTag ? parseInt(bidTag) : undefined + } - if (!amount) { - throw new Error("No amount specified"); - } + if (!amount) { + throw new Error('No amount specified') + } - const payReq = new NDKEvent(ndk, { - kind: 68003, - content: "`Please pay for this job`", - tags: [ - ["status", "payment-required"], - ] - } as NostrEvent); - await addAmount(payReq, amount); - payReq.tag(event, "job"); + const payReq = new NDKEvent(ndk, { + kind: 68003, + content: '`Please pay for this job`', + tags: [['status', 'payment-required']] + } as NostrEvent) + await addAmount(payReq, amount) + payReq.tag(event, 'job') - await payReq.sign(); - console.log(payReq.rawEvent()); + await payReq.sign() + console.log(payReq.rawEvent()) - if (publish !== false) await payReq.publish(); + if (publish !== false) await payReq.publish() - return payReq; -} \ No newline at end of file + return payReq +} diff --git a/src/validations/no-recent-results.ts b/src/validations/no-recent-results.ts index fca88eb..0a2df5c 100644 --- a/src/validations/no-recent-results.ts +++ b/src/validations/no-recent-results.ts @@ -1,13 +1,18 @@ -import { NDKEvent } from '@nostr-dev-kit/ndk'; -import { ndk } from '../main.js'; +import { NDKEvent } from '@nostr-dev-kit/ndk' +import { ndk } from '../main.js' -export default async function validateNoRecentResults(event: NDKEvent): Promise { - const results = ndk.fetchEvents({ - kinds: [68002 as number], - "#e": [event.id], - }, { groupable: false }) +export default async function validateNoRecentResults( + event: NDKEvent +): Promise { + const results = ndk.fetchEvents( + { + kinds: [68002 as number], + '#e': [event.id] + }, + { groupable: false } + ) - if (results.length > 0) { - throw new Error(`This job already has ${results.length} results`); - } -} \ No newline at end of file + if (results.length > 0) { + throw new Error(`This job already has ${results.length} results`) + } +} diff --git a/src/validations/requester.ts b/src/validations/requester.ts index aaea3ca..439d427 100644 --- a/src/validations/requester.ts +++ b/src/validations/requester.ts @@ -1,5 +1,9 @@ -import { NDKEvent } from '@nostr-dev-kit/ndk'; +import { NDKEvent } from '@nostr-dev-kit/ndk' -export default async function validateRequester(event: NDKEvent): Promise { - if (event) { /* empty */ } -} \ No newline at end of file +export default async function validateRequester( + event: NDKEvent +): Promise { + if (event) { + /* empty */ + } +} -- 2.34.1 From d67d53b172704aa4f3cc41c1a3f5ed1c9b2ad0ed Mon Sep 17 00:00:00 2001 From: Yury Date: Thu, 23 May 2024 12:13:20 +0300 Subject: [PATCH 2/3] feat(Jobs): added relay-info job --- pnpm-lock.yaml | 12 ++++----- src/job-types/relay-info.ts | 49 +++++++++++++++++++++++++++++++++++++ src/main.ts | 41 +++++++++++++++++++------------ src/types/index.ts | 2 ++ src/types/job.ts | 14 +++++++++++ src/types/relay.ts | 9 +++++++ 6 files changed, 106 insertions(+), 21 deletions(-) create mode 100644 src/job-types/relay-info.ts create mode 100644 src/types/index.ts create mode 100644 src/types/job.ts create mode 100644 src/types/relay.ts diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b9e54e7..12b8c30 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -64,8 +64,8 @@ importers: specifier: ~29.5 version: 29.5.0(@types/node@18.16.19) prettier: - specifier: ~2.8 - version: 2.8.8 + specifier: 3.2.5 + version: 3.2.5 rimraf: specifier: ~5.0 version: 5.0.1 @@ -2171,9 +2171,9 @@ packages: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} - prettier@2.8.8: - resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} - engines: {node: '>=10.13.0'} + prettier@3.2.5: + resolution: {integrity: sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==} + engines: {node: '>=14'} hasBin: true pretty-format@29.6.1: @@ -5246,7 +5246,7 @@ snapshots: prelude-ls@1.2.1: {} - prettier@2.8.8: {} + prettier@3.2.5: {} pretty-format@29.6.1: dependencies: diff --git a/src/job-types/relay-info.ts b/src/job-types/relay-info.ts new file mode 100644 index 0000000..7ce1ee9 --- /dev/null +++ b/src/job-types/relay-info.ts @@ -0,0 +1,49 @@ +import { NDKEvent } from '@nostr-dev-kit/ndk' +import { log } from '../main.js' +import axios from 'axios' +import { RelayInfo } from '../types/index.js' + +export const relayInfoJob = async (event: NDKEvent): Promise => { + log('New relay-info job', event.rawEvent()) + + const input: string = event.tagValue('i') + + let relays: string[] = [] + + try { + const relaysArr = JSON.parse(input) + + // input is a string containing an array of strings representing multiple relay URIs + relays = relaysArr + } catch (err) { + // input is a string containing a string representing single relay URI + + relays.push(input) + } + + const wssPrefix = 'wss://' + const headers = { + headers: { Accept: 'application/nostr+json' } + } + + const requests = relays.map((relay) => { + if (relay.startsWith(wssPrefix)) { + relay = relay.replace(wssPrefix, '') + } + + return axios + .get((relay = `https://${relay}`), headers) + .catch(() => { + return axios + .get(`http://${relay}`, headers) + .catch(() => undefined) + }) + }) + + let responses = await Promise.all(requests) + responses = responses.filter((response) => response !== undefined) + + const data = responses.map((response) => response.data) + + return Promise.resolve(JSON.stringify(data)) +} diff --git a/src/main.ts b/src/main.ts index cdf88e9..de1a7da 100644 --- a/src/main.ts +++ b/src/main.ts @@ -11,6 +11,8 @@ import { getConfig } from './config/index.js' import { decode } from 'light-bolt11-decoder' import { checkInvoiceStatus } from './utils/lnbits.js' import { blockChainBlockNumberJob } from './job-types/blockChain-block-number.js' +import { relayInfoJob } from './job-types/relay-info.js' +import { JobType, JobTypes } from './types/index.js' export const log = debug('fool-me-once-dvm') @@ -34,7 +36,7 @@ const subs = ndk.subscribe( { kinds: [68001 as number], since: Math.floor(Date.now() / 1000), - '#j': ['summarize', 'explain'] + '#j': [JobTypes.Summarize, JobTypes.Explain] }, { closeOnEose: false } ) @@ -43,7 +45,7 @@ const speechToTextSub = ndk.subscribe( { kinds: [68001 as number], since: Math.floor(Date.now() / 1000), - '#j': ['speech-to-text'] + '#j': [JobTypes.SpeechToText] }, { closeOnEose: false } ) @@ -52,26 +54,31 @@ const blockChainBlockNumberSub = ndk.subscribe( { kinds: [68001 as number], since: Math.floor(Date.now() / 1000), - '#j': ['blockChain-block-number'] + '#j': [JobTypes.BlockChainBlockNumber] + }, + { closeOnEose: false } +) +const relayInfoSub = ndk.subscribe( + { + kinds: [68001 as number], + since: Math.floor(Date.now() / 1000), + '#j': [JobTypes.RelayInfo] }, { closeOnEose: false } ) -subs.on('event', (e) => processJobEvent(e, 'summarize')) -speechToTextSub.on('event', (e) => processJobEvent(e, 'speech-to-text')) +subs.on('event', (e) => processJobEvent(e, JobTypes.Summarize)) +speechToTextSub.on('event', (e) => processJobEvent(e, JobTypes.SpeechToText)) blockChainBlockNumberSub.on('event', (e) => - processJobEvent(e, 'blockChain-block-number') + processJobEvent(e, JobTypes.BlockChainBlockNumber) ) +relayInfoSub.on('event', (e) => processJobEvent(e, JobTypes.RelayInfo)) -type JobType = - | 'summarize' - | 'explain' - | 'speech-to-text' - | 'blockChain-block-number' +const freeJobs: string[] = [JobTypes.BlockChainBlockNumber, JobTypes.RelayInfo] async function processJobEvent(event: NDKEvent, type: JobType): Promise { const config = getConfig() - let jobAmount = type === 'blockChain-block-number' ? 0 : await priceJob(event) + let jobAmount = freeJobs.includes(type) ? 0 : await priceJob(event) let output: any let payReqEvent: NDKEvent let paidAmount = 0 @@ -195,18 +202,22 @@ async function processJobEvent(event: NDKEvent, type: JobType): Promise { await inProgress(event) switch (type) { - case 'summarize': { + case JobTypes.Summarize: { output = await onNewSummarizationJob(event) break } - case 'speech-to-text': { + case JobTypes.SpeechToText: { output = await speechToTextJob(event) break } - case 'blockChain-block-number': { + case JobTypes.BlockChainBlockNumber: { output = await blockChainBlockNumberJob(event) break } + case JobTypes.RelayInfo: { + output = await relayInfoJob(event) + break + } } } diff --git a/src/types/index.ts b/src/types/index.ts new file mode 100644 index 0000000..1fc9c26 --- /dev/null +++ b/src/types/index.ts @@ -0,0 +1,2 @@ +export * from './relay.js' +export * from './job.js' diff --git a/src/types/job.ts b/src/types/job.ts new file mode 100644 index 0000000..3a60329 --- /dev/null +++ b/src/types/job.ts @@ -0,0 +1,14 @@ +export type JobType = + | 'summarize' + | 'explain' + | 'speech-to-text' + | 'blockChain-block-number' + | 'relay-info' + +export enum JobTypes { + Summarize = 'summarize', + Explain = 'explain', + SpeechToText = 'speech-to-text', + BlockChainBlockNumber = 'blockChain-block-number', + RelayInfo = 'relay-info' +} diff --git a/src/types/relay.ts b/src/types/relay.ts new file mode 100644 index 0000000..7924fb2 --- /dev/null +++ b/src/types/relay.ts @@ -0,0 +1,9 @@ +export interface RelayInfo { + name: string + description: string + pubkey: string + contact: string + supported_nips: number[] + software: string + version: string +} -- 2.34.1 From eba2472f2fdc7712071b586be519ceb9030692f6 Mon Sep 17 00:00:00 2001 From: Yury Date: Thu, 23 May 2024 14:06:08 +0300 Subject: [PATCH 3/3] feat: improved relay-info job to return object --- src/job-types/relay-info.ts | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/job-types/relay-info.ts b/src/job-types/relay-info.ts index 7ce1ee9..2b44828 100644 --- a/src/job-types/relay-info.ts +++ b/src/job-types/relay-info.ts @@ -21,21 +21,21 @@ export const relayInfoJob = async (event: NDKEvent): Promise => { relays.push(input) } - const wssPrefix = 'wss://' + const prefixes = { wss: 'wss://', https: 'https://', http: 'http://' } const headers = { headers: { Accept: 'application/nostr+json' } } const requests = relays.map((relay) => { - if (relay.startsWith(wssPrefix)) { - relay = relay.replace(wssPrefix, '') + if (relay.startsWith(prefixes.wss)) { + relay = relay.replace(prefixes.wss, '') } return axios - .get((relay = `https://${relay}`), headers) + .get((relay = `${prefixes.https}${relay}`), headers) .catch(() => { return axios - .get(`http://${relay}`, headers) + .get(`${prefixes.http}${relay}`, headers) .catch(() => undefined) }) }) @@ -43,7 +43,15 @@ export const relayInfoJob = async (event: NDKEvent): Promise => { let responses = await Promise.all(requests) responses = responses.filter((response) => response !== undefined) - const data = responses.map((response) => response.data) + const relaysInfo: { [key: string]: RelayInfo } = {} - return Promise.resolve(JSON.stringify(data)) + responses.forEach((response) => { + const relayURI = response.config.url + .replace(prefixes.https, prefixes.wss) + .replace(prefixes.http, prefixes.wss) + + relaysInfo[relayURI] = response.data + }) + + return Promise.resolve(JSON.stringify(relaysInfo)) } -- 2.34.1