From dcf72965bd126afbe6e943f32c9ee36716c96993 Mon Sep 17 00:00:00 2001 From: Kasra Date: Wed, 15 Feb 2023 03:24:40 +0330 Subject: [PATCH] new --- index.js | 48 ++--- .../opensea-scraper/src/functions/rankings.js | 68 +++++-- package-lock.json | 174 ++++++++++++++++++ package.json | 4 +- src/v1/controllers/opensea/index.js | 2 +- src/v1/controllers/opensea/rankings/index.js | 2 + .../opensea/rankings/top.controller.js | 33 ++++ .../opensea/rankings/trending.controller.js | 34 ++++ .../opensea/trending.controller.js | 0 src/v1/libs/index.js | 1 + src/v1/libs/redis.lib.js | 3 + src/v1/routes/index.js | 4 +- src/v1/routes/opensea/index.js | 2 +- src/v1/routes/opensea/rankings/index.js | 7 + src/v1/routes/opensea/rankings/top.route.js | 8 + .../routes/opensea/rankings/trending.route.js | 8 + src/v1/routes/opensea/trending.route.js | 8 - src/v1/services/index.js | 2 +- src/v1/services/opensea/index.js | 1 + src/v1/services/opensea/rankings/index.js | 2 + .../services/opensea/rankings/top.service.js | 39 ++++ .../opensea/rankings/trending.service.js | 39 ++++ src/v1/services/trending.service.js | 0 src/v1/tasks/index.js | 0 src/v1/tasks/trending.task.js | 0 25 files changed, 433 insertions(+), 56 deletions(-) create mode 100644 src/v1/controllers/opensea/rankings/index.js create mode 100644 src/v1/controllers/opensea/rankings/top.controller.js create mode 100644 src/v1/controllers/opensea/rankings/trending.controller.js delete mode 100644 src/v1/controllers/opensea/trending.controller.js create mode 100644 src/v1/libs/index.js create mode 100644 src/v1/libs/redis.lib.js create mode 100644 src/v1/routes/opensea/rankings/index.js create mode 100644 src/v1/routes/opensea/rankings/top.route.js create mode 100644 src/v1/routes/opensea/rankings/trending.route.js delete mode 100644 src/v1/routes/opensea/trending.route.js create mode 100644 src/v1/services/opensea/index.js create mode 100644 src/v1/services/opensea/rankings/index.js create mode 100644 src/v1/services/opensea/rankings/top.service.js create mode 100644 src/v1/services/opensea/rankings/trending.service.js delete mode 100644 src/v1/services/trending.service.js delete mode 100644 src/v1/tasks/index.js delete mode 100644 src/v1/tasks/trending.task.js diff --git a/index.js b/index.js index a49f6fc..db4fadb 100644 --- a/index.js +++ b/index.js @@ -4,43 +4,35 @@ const cors = require('cors') const logger = require('morgan') const workerFarm = require('worker-farm') require('dotenv').config() -/* -const { Builder, Browser, By, Key, until } = require('selenium-webdriver') -;(async function example() { - let driver = await new Builder().forBrowser(Browser.FIREFOX).build() - try { - await driver.get('https://www.google.com/ncr') - await driver.findElement(By.name('q')).sendKeys('webdriver', Key.RETURN) - await driver.wait(until.titleIs('webdriver - Google Search'), 1000) - } finally { - await driver.quit() - } -})() -*/ -const options = { - debug: false, - logs: false, - sort: true, - additionalWait: 0, - browserInstance: undefined -} -const OpenseaScraper = require('./local_modules/opensea-scraper') -const q = async () => { - const type = '24h' // possible values: "24h", "7d", "30d", "total" - const chain = 'ethereum' - const ranking = await OpenseaScraper.rankings(type, chain, options) - console.log(ranking) +app.use(cors()) +app.use(logger('dev')) + +const services = { + topCollections: workerFarm( + require.resolve('./src/v1/services/opensea/rankings/top.service') + ), + trendingCollections: workerFarm( + require.resolve('./src/v1/services/opensea/rankings/trending.service') + ) } -q().then(console.log).catch(console.log) +services.topCollections('hello', (err) => { + if (err) console.log(err) +}) + +services.trendingCollections('hello', (err) => { + if (err) console.log(err) +}) + +app.use('/v1', require('./src/v1/routes')) app.use('*', (req, res) => { res.status(404).send({ statusCode: 404, data: null, error: { - message: 'صفحه یافت نشد' + message: '404 Not Found' } }) }) diff --git a/local_modules/opensea-scraper/src/functions/rankings.js b/local_modules/opensea-scraper/src/functions/rankings.js index 965e4b2..aeb0564 100644 --- a/local_modules/opensea-scraper/src/functions/rankings.js +++ b/local_modules/opensea-scraper/src/functions/rankings.js @@ -25,7 +25,8 @@ const { const rankings = async ( type = 'total', chain = undefined, - optionsGiven = {} + optionsGiven = {}, + trending = false ) => { const optionsDefault = { debug: false, @@ -51,7 +52,7 @@ const rankings = async ( const page = await browser.newPage() - const url = getUrl(type, chain) + const url = getUrl(type, chain, trending) logs && console.log('...opening url: ' + url) await page.goto(url) @@ -74,12 +75,12 @@ const rankings = async ( }) // extract relevant info - const top100 = _parseNextDataVarible(__NEXT_DATA__) + const top100 = _parseNextDataVarible(__NEXT_DATA__, trending) logs && console.log(`🥳 DONE. Total ${top100.length} Collections fetched: `) return top100 } -function _parseNextDataVarible(__NEXT_DATA__) { +function _parseNextDataVarible(__NEXT_DATA__, trending) { const extractFloorPrice = (windowCollectionStats, extractionMethod) => { try { if (extractionMethod === 'multichain') { @@ -96,8 +97,37 @@ function _parseNextDataVarible(__NEXT_DATA__) { return null } } + const extractTotalSupply = (windowCollectionStats) => { + try { + return Number(windowCollectionStats.totalSupply) + } catch (err) { + return null + } + } + + if (trending) { + const extractCollection = (node) => { + return { + name: node.name, + slug: node.slug, + logo: node.logo, + isVerified: node.isVerified, + floorPrice: extractFloorPrice(node.windowCollectionStats), + floorPriceMultichain: extractFloorPrice( + node.windowCollectionStats, + 'multichain' + ), + totalSupply: extractTotalSupply(node.windowCollectionStats) + // statsV2: node.statsV2, // 🚧 comment back in if you need additional stats + // windowCollectionStats: node.windowCollectionStats, // 🚧 comment back in if you need additional stats + } + } + + return __NEXT_DATA__.props.relayCache[0][1].json.data.trendingCollections.edges.map( + (obj) => extractCollection(obj.node) + ) + } const extractCollection = (node) => { - console.log(node) return { name: node.name, slug: node.slug, @@ -107,29 +137,39 @@ function _parseNextDataVarible(__NEXT_DATA__) { floorPriceMultichain: extractFloorPrice( node.windowCollectionStats, 'multichain' - ) + ), + totalSupply: extractTotalSupply(node.windowCollectionStats) // statsV2: node.statsV2, // 🚧 comment back in if you need additional stats // windowCollectionStats: node.windowCollectionStats, // 🚧 comment back in if you need additional stats } } - - return __NEXT_DATA__.props.relayCache[0][1].json.data.trendingCollections.edges.map( + return __NEXT_DATA__.props.relayCache[0][1].json.data.rankings.edges.map( (obj) => extractCollection(obj.node) ) } -function getUrl(type, chain) { +function getUrl(type, chain, trending) { chainExtraQueryParameter = chain ? `&chain=${chain}` : '' + if (trending) { + if (type === '24h') { + return `https://opensea.io/rankings/trending?sortBy=one_day_volume${chainExtraQueryParameter}` + } else if (type === '7d') { + return `https://opensea.io/rankings/trending?sortBy=seven_day_volume${chainExtraQueryParameter}` + } else if (type === '30d') { + return `https://opensea.io/rankings/trending?sortBy=thirty_day_volume${chainExtraQueryParameter}` + } else if (type === 'total') { + return `https://opensea.io/rankings/trending?sortBy=total_volume${chainExtraQueryParameter}` + } + } if (type === '24h') { - return `https://opensea.io/rankings/trending?sortBy=one_day_volume${chainExtraQueryParameter}` + return `https://opensea.io/rankings?sortBy=one_day_volume${chainExtraQueryParameter}` } else if (type === '7d') { - return `https://opensea.io/rankings/trending?sortBy=seven_day_volume${chainExtraQueryParameter}` + return `https://opensea.io/rankings?sortBy=seven_day_volume${chainExtraQueryParameter}` } else if (type === '30d') { - return `https://opensea.io/rankings/trending?sortBy=thirty_day_volume${chainExtraQueryParameter}` + return `https://opensea.io/rankings?sortBy=thirty_day_volume${chainExtraQueryParameter}` } else if (type === 'total') { - return `https://opensea.io/rankings/trending?sortBy=total_volume${chainExtraQueryParameter}` + return `https://opensea.io/rankings?sortBy=total_volume${chainExtraQueryParameter}` } - throw new Error( `Invalid type provided. Expected: 24h,7d,30d,total. Got: ${type}` ) diff --git a/package-lock.json b/package-lock.json index 12b1925..fb247bc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,11 +12,17 @@ "cors": "^2.8.5", "dotenv": "^16.0.3", "express": "^4.18.2", + "ioredis": "^5.3.1", "morgan": "^1.10.0", "selenium-webdriver": "^4.8.0", "worker-farm": "^1.7.0" } }, + "node_modules/@ioredis/commands": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.2.0.tgz", + "integrity": "sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==" + }, "node_modules/accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -102,6 +108,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/cluster-key-slot": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", + "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -183,6 +197,14 @@ "ms": "2.0.0" } }, + "node_modules/denque": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", + "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", + "engines": { + "node": ">=0.10" + } + }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -447,6 +469,50 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, + "node_modules/ioredis": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.3.1.tgz", + "integrity": "sha512-C+IBcMysM6v52pTLItYMeV4Hz7uriGtoJdz7SSBDX6u+zwSYGirLdQh3L7t/OItWITcw3gTFMjJReYUwS4zihg==", + "dependencies": { + "@ioredis/commands": "^1.1.1", + "cluster-key-slot": "^1.1.0", + "debug": "^4.3.4", + "denque": "^2.1.0", + "lodash.defaults": "^4.2.0", + "lodash.isarguments": "^3.1.0", + "redis-errors": "^1.2.0", + "redis-parser": "^3.0.0", + "standard-as-callback": "^2.1.0" + }, + "engines": { + "node": ">=12.22.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/ioredis" + } + }, + "node_modules/ioredis/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/ioredis/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, "node_modules/ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -479,6 +545,16 @@ "immediate": "~3.0.5" } }, + "node_modules/lodash.defaults": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", + "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==" + }, + "node_modules/lodash.isarguments": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", + "integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==" + }, "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -721,6 +797,25 @@ "util-deprecate": "~1.0.1" } }, + "node_modules/redis-errors": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", + "integrity": "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==", + "engines": { + "node": ">=4" + } + }, + "node_modules/redis-parser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", + "integrity": "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==", + "dependencies": { + "redis-errors": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -823,6 +918,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/standard-as-callback": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz", + "integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==" + }, "node_modules/statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", @@ -934,6 +1034,11 @@ } }, "dependencies": { + "@ioredis/commands": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.2.0.tgz", + "integrity": "sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==" + }, "accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -1003,6 +1108,11 @@ "get-intrinsic": "^1.0.2" } }, + "cluster-key-slot": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", + "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==" + }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -1060,6 +1170,11 @@ "ms": "2.0.0" } }, + "denque": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", + "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==" + }, "depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -1257,6 +1372,37 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, + "ioredis": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.3.1.tgz", + "integrity": "sha512-C+IBcMysM6v52pTLItYMeV4Hz7uriGtoJdz7SSBDX6u+zwSYGirLdQh3L7t/OItWITcw3gTFMjJReYUwS4zihg==", + "requires": { + "@ioredis/commands": "^1.1.1", + "cluster-key-slot": "^1.1.0", + "debug": "^4.3.4", + "denque": "^2.1.0", + "lodash.defaults": "^4.2.0", + "lodash.isarguments": "^3.1.0", + "redis-errors": "^1.2.0", + "redis-parser": "^3.0.0", + "standard-as-callback": "^2.1.0" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, "ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -1286,6 +1432,16 @@ "immediate": "~3.0.5" } }, + "lodash.defaults": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", + "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==" + }, + "lodash.isarguments": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", + "integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==" + }, "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -1467,6 +1623,19 @@ "util-deprecate": "~1.0.1" } }, + "redis-errors": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", + "integrity": "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==" + }, + "redis-parser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", + "integrity": "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==", + "requires": { + "redis-errors": "^1.0.0" + } + }, "rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -1553,6 +1722,11 @@ "object-inspect": "^1.9.0" } }, + "standard-as-callback": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz", + "integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==" + }, "statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", diff --git a/package.json b/package.json index bc2063a..78dc6be 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,8 @@ "description": "", "main": "index.js", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "dev": "nodemon index.js", + "start": "node index.js" }, "author": "", "license": "ISC", @@ -12,6 +13,7 @@ "cors": "^2.8.5", "dotenv": "^16.0.3", "express": "^4.18.2", + "ioredis": "^5.3.1", "morgan": "^1.10.0", "selenium-webdriver": "^4.8.0", "worker-farm": "^1.7.0" diff --git a/src/v1/controllers/opensea/index.js b/src/v1/controllers/opensea/index.js index 353ee49..c48edea 100644 --- a/src/v1/controllers/opensea/index.js +++ b/src/v1/controllers/opensea/index.js @@ -1 +1 @@ -module.exports.trending = require('./trending.controller') +module.exports.rankings = require('./rankings') diff --git a/src/v1/controllers/opensea/rankings/index.js b/src/v1/controllers/opensea/rankings/index.js new file mode 100644 index 0000000..e8e3aa8 --- /dev/null +++ b/src/v1/controllers/opensea/rankings/index.js @@ -0,0 +1,2 @@ +module.exports.top = require('./top.controller') +module.exports.trending = require('./trending.controller') diff --git a/src/v1/controllers/opensea/rankings/top.controller.js b/src/v1/controllers/opensea/rankings/top.controller.js new file mode 100644 index 0000000..c9f3fff --- /dev/null +++ b/src/v1/controllers/opensea/rankings/top.controller.js @@ -0,0 +1,33 @@ +const { opensea } = require('../../../services') +const { redis } = require('../../../libs') + +const findAll = async (req, res) => { + try { + const redis_key = `opensea_rankings_top_7d_cache` + const cache = await redis.get(redis_key) + if (cache) { + return res.status(200).send({ + statusCode: 200, + data: JSON.parse(cache), + error: null + }) + } else { + const topCollections = await opensea.rankings.top.saveTopCollections() + return res.status(200).send({ + statusCode: 200, + data: topCollections, + error: null + }) + } + } catch (e) { + return res.status(400).status({ + statusCode: 400, + data: null, + error: { + message: e?.message || String(e) + } + }) + } +} + +module.exports = { findAll } diff --git a/src/v1/controllers/opensea/rankings/trending.controller.js b/src/v1/controllers/opensea/rankings/trending.controller.js new file mode 100644 index 0000000..146ffb7 --- /dev/null +++ b/src/v1/controllers/opensea/rankings/trending.controller.js @@ -0,0 +1,34 @@ +const { opensea } = require('../../../services') +const { redis } = require('../../../libs') + +const findAll = async (req, res) => { + try { + const redis_key = `opensea_rankings_trending_24h_cache` + const cache = await redis.get(redis_key) + if (cache) { + return res.status(200).send({ + statusCode: 200, + data: JSON.parse(cache), + error: null + }) + } else { + const trendingCollections = + await opensea.rankings.trending.saveTrendingCollections() + return res.status(200).send({ + statusCode: 200, + data: trendingCollections, + error: null + }) + } + } catch (e) { + return res.status(400).status({ + statusCode: 400, + data: null, + error: { + message: e?.message || String(e) + } + }) + } +} + +module.exports = { findAll } diff --git a/src/v1/controllers/opensea/trending.controller.js b/src/v1/controllers/opensea/trending.controller.js deleted file mode 100644 index e69de29..0000000 diff --git a/src/v1/libs/index.js b/src/v1/libs/index.js new file mode 100644 index 0000000..9b53daa --- /dev/null +++ b/src/v1/libs/index.js @@ -0,0 +1 @@ +module.exports.redis = require('./redis.lib') diff --git a/src/v1/libs/redis.lib.js b/src/v1/libs/redis.lib.js new file mode 100644 index 0000000..edfae1c --- /dev/null +++ b/src/v1/libs/redis.lib.js @@ -0,0 +1,3 @@ +const Redis = require('ioredis') + +module.exports = new Redis() diff --git a/src/v1/routes/index.js b/src/v1/routes/index.js index eec29dc..758ccb2 100644 --- a/src/v1/routes/index.js +++ b/src/v1/routes/index.js @@ -1,5 +1,5 @@ -const express = require('express') -const router = express.Router() +const { Router } = require('express') +const router = Router() router.use('/opensea', require('./opensea')) diff --git a/src/v1/routes/opensea/index.js b/src/v1/routes/opensea/index.js index 3aebec7..5c2ba6f 100644 --- a/src/v1/routes/opensea/index.js +++ b/src/v1/routes/opensea/index.js @@ -1,6 +1,6 @@ const { Router } = require('express') const router = Router() -router.use('/trending', require('./trending.route')) +router.use('/rankings', require('./rankings')) module.exports = router diff --git a/src/v1/routes/opensea/rankings/index.js b/src/v1/routes/opensea/rankings/index.js new file mode 100644 index 0000000..7dd9856 --- /dev/null +++ b/src/v1/routes/opensea/rankings/index.js @@ -0,0 +1,7 @@ +const { Router } = require('express') +const router = Router() + +router.use('/top', require('./top.route')) +router.use('/trending', require('./trending.route')) + +module.exports = router diff --git a/src/v1/routes/opensea/rankings/top.route.js b/src/v1/routes/opensea/rankings/top.route.js new file mode 100644 index 0000000..6ffe438 --- /dev/null +++ b/src/v1/routes/opensea/rankings/top.route.js @@ -0,0 +1,8 @@ +const { Router } = require('express') +const router = Router() + +const { opensea } = require('../../../controllers') + +router.get('/ticker/7d', opensea.rankings.top.findAll) + +module.exports = router diff --git a/src/v1/routes/opensea/rankings/trending.route.js b/src/v1/routes/opensea/rankings/trending.route.js new file mode 100644 index 0000000..dbb178c --- /dev/null +++ b/src/v1/routes/opensea/rankings/trending.route.js @@ -0,0 +1,8 @@ +const { Router } = require('express') +const router = Router() + +const { opensea } = require('../../../controllers') + +router.get('/', opensea.rankings.trending.findAll) + +module.exports = router diff --git a/src/v1/routes/opensea/trending.route.js b/src/v1/routes/opensea/trending.route.js deleted file mode 100644 index 20cdf7b..0000000 --- a/src/v1/routes/opensea/trending.route.js +++ /dev/null @@ -1,8 +0,0 @@ -const { Router } = require('express') -const router = Router() - -const { opensea } = require('../../controllers') - -router.use('/', require('./trending.route')) - -module.exports = router diff --git a/src/v1/services/index.js b/src/v1/services/index.js index 6bf998d..fba2410 100644 --- a/src/v1/services/index.js +++ b/src/v1/services/index.js @@ -1 +1 @@ -module.exports.trendings = require('./trending.service') +module.exports.opensea = require('./opensea') diff --git a/src/v1/services/opensea/index.js b/src/v1/services/opensea/index.js new file mode 100644 index 0000000..c48edea --- /dev/null +++ b/src/v1/services/opensea/index.js @@ -0,0 +1 @@ +module.exports.rankings = require('./rankings') diff --git a/src/v1/services/opensea/rankings/index.js b/src/v1/services/opensea/rankings/index.js new file mode 100644 index 0000000..b52d89b --- /dev/null +++ b/src/v1/services/opensea/rankings/index.js @@ -0,0 +1,2 @@ +module.exports.top = require('./top.service') +module.exports.trending = require('./trending.service') diff --git a/src/v1/services/opensea/rankings/top.service.js b/src/v1/services/opensea/rankings/top.service.js new file mode 100644 index 0000000..d711c0a --- /dev/null +++ b/src/v1/services/opensea/rankings/top.service.js @@ -0,0 +1,39 @@ +const OpenseaScraper = require('../../../../../local_modules/opensea-scraper') +const { redis } = require('../../../libs') + +const options = { + debug: false, + logs: false, + sort: true, + additionalWait: 0, + browserInstance: undefined +} +const saveTopCollections = async () => { + try { + const type = '7d' // possible values: "24h", "7d", "30d", "total" + const chain = 'ethereum' + const rankings = await OpenseaScraper.rankings(type, chain, options, false) + const redisKey = `opensea_rankings_top_${type}_cache` + await redis.set( + redisKey, + JSON.stringify(rankings), + 'EX', + Date.now() + 1000 * 15 + ) + return rankings + } catch (e) { + console.log(e) + } +} + +const start = () => { + saveTopCollections() + .then(() => { + setTimeout(start, 15000) + }) + .catch(() => setTimeout(start, 15000)) +} + +start() + +module.exports = { saveTopCollections } diff --git a/src/v1/services/opensea/rankings/trending.service.js b/src/v1/services/opensea/rankings/trending.service.js new file mode 100644 index 0000000..d171936 --- /dev/null +++ b/src/v1/services/opensea/rankings/trending.service.js @@ -0,0 +1,39 @@ +const OpenseaScraper = require('../../../../../local_modules/opensea-scraper') +const { redis } = require('../../../libs') + +const options = { + debug: false, + logs: false, + sort: true, + additionalWait: 0, + browserInstance: undefined +} +const saveTrendingCollections = async () => { + try { + const type = '24h' // possible values: "24h", "7d", "30d", "total" + const chain = 'ethereum' + const trending = await OpenseaScraper.rankings(type, chain, options, true) + const redisKey = `opensea_rankings_trending_${type}_cache` + await redis.set( + redisKey, + JSON.stringify(trending), + 'EX', + Date.now() + 1000 * 15 + ) + return trending + } catch (e) { + console.log(e) + } +} + +const start = () => { + saveTrendingCollections() + .then(() => { + setTimeout(start, 15000) + }) + .catch(() => setTimeout(start, 15000)) +} + +start() + +module.exports = { saveTrendingCollections } diff --git a/src/v1/services/trending.service.js b/src/v1/services/trending.service.js deleted file mode 100644 index e69de29..0000000 diff --git a/src/v1/tasks/index.js b/src/v1/tasks/index.js deleted file mode 100644 index e69de29..0000000 diff --git a/src/v1/tasks/trending.task.js b/src/v1/tasks/trending.task.js deleted file mode 100644 index e69de29..0000000