I am trying to implement a simple extraction function that connects to the Poloniex.returnTicker endpoint and, extract the 'last' key for any currency pairs starting with 'BTC'. There are a number of keys starting with 'BTC' but, my extraction function is stopping after returning exactly one key. I am mapping the extracted data in an object in the following manner:
extracted = {
BTC: {
STR: {
lastPrice: price
},
ETH: {
lastPrice: price
}, //...
}, //...
}
I wrote the following function for extraction:
module.exports = {
extractData: (tickerdata, marker) => {
if(!!!marker){
marker = 'BTC';
}
return new Promise((res, rej) => {
let currentBuffer = {};
Object.keys(tickerdata)
.filter((key) => {
return key.startsWith(marker);
})
.forEach((filtKey) => {
let c = filtKey.split('_'),
src = c[0],
dst = c[1];
currentBuffer[src] = {};
Object.keys(tickerdata[filtKey])
.forEach((entry) => {
if(!!!(dst in currentBuffer[src])){
currentBuffer[src][dst] = {};
}
if(entry == 'last'){
currentBuffer[src][dst]['lastPrice'] = tickerdata[filtKey][entry];
}
});
});
res(currentBuffer);
});
},//... rest of the module exports
For reference, each call to return ticker endpoint returns data in the following format: Poloniex API. Look at returnTicker endpoint for example.
I am guessing that there is a logical error in the extraction function but, I am not sure where.
UPDATE
What about this code?
module.exports = {
extractData: (tickerdata, marker) =>
Object.keys(tickerdata)
.map(x => x.split('_'))
.filter(x => x && x[0] == (marker || 'BTC') && x[1])
.map(x => x[0] + '_' + x[1])
.map(x => [x, Object.keys(tickerdata[x])
.map(y => [y, tickerdata[x][y].lastPrice])]);
and usage:
const extracted = {
ETH_STR: {
BTC: {
lastPrice: 1
}
},
BTC_ETH: {
STR: {
lastPrice: 1
}
},
BTC_STR: {
STR: {
lastPrice: 1
},
ETH: {
lastPrice: 2
}, //...
}, //...
};
const result = extractData(extracted, 'BTC');
console.log(JSON.stringify(result));
with result
[["BTC_ETH",[["STR",1]]],["BTC_STR",[["STR",1],["ETH",2]]]]
I have tinkered around with my code and, finally was able to make it work. Besides adding more code for extracting data from the API response object, tickerdata, I have changed the promise to return instead of resolve. This ensured that the flow remains in proper sequence. I used a then clause on the promise to retrieve the extracted values (named buff). Here is the full working code:
`module.exports = {
extractData: (markers) => {
// extracts the data for all markers which, are a comma separated string of base currencies
return new Promise((res, rej) => {
markers = (!!!(markers) ? 'USDT,BTC' : markers)
polinst.returnTicker()
.then((tickerdata) => {
let buff = {};
markers.split(',').forEach((marker) => {
Object.keys(tickerdata)
.filter((key) => {
return key.startsWith(marker);
})
.forEach((fkey) => {
let c = fkey.split('_'),
src = c[0],
dst = c[1];
buff[src] = (!!!(buff[src]) ? {} : buff[src]);
buff[src][dst] = (!!!(buff[src][dst]) ? {} : buff[src][dst]);
Object.keys(tickerdata[fkey])
.forEach((k) => {
if(k == 'last'){
buff[src][dst]['lastPrice'] = tickerdata[fkey][k];
}
else if(k == 'quoteVolume'){
buff[src][dst]['volume'] = tickerdata[fkey][k];
}
else if(k == 'lowestAsk'){
buff[src][dst]['ask'] = tickerdata[fkey][k];
}
else if(k == 'highestBid'){
buff[src][dst]['highestBid'] = tickerdata[fkey][k];
}
});
});
});
return buff;
}).then((d)=>{
res(d);
}).catch((err)=>{
console.log(err);
});
});
},
};`
Related
I am making a Type racing minigame with my discord bot, the code works... but I want to change the messages it sends to embeds, im new to Commando and it wont let me use the discord.js functions im used to using
I need to change all the bots responses to embeds, and make it so when it sends a new embed it just edits the old one so it isnt spamming messages. Here is my code:
const Commando = require('discord.js-commando')
const { words } = require('../../util/fast-type-words')
const example = {
channelId: {
message: 'message object',
stage: 'string',
counter: 'number',
currentWord: 'string',
remainingWords: ['words here'],
points: {
userId: 'points',
},
},
}
const games = {}
const stages = {
STARTING: (counter) => {
return `A new "fast type" game is starting in ${counter}s!`
},
IN_GAME: (word) => {
let spacedWord = ''
for (const character of [...word]) {
spacedWord += character
spacedWord += ' '
}
return `The new word is **${spacedWord}**!`
},
ENDING: (points) => {
const sorted = Object.keys(points).sort((a, b) => {
return points[b] - points[a]
})
let results = ''
for (const key of sorted) {
const amount = points[key]
results += `<#${key}> had ${amount} point${amount === 1 ? '' : 's'}\n`
}
return `The game is now over Here's how everyone did:\n\n${results}------------------`
},
}
const selectWord = (game) => {
game.currentWord =
game.remainingWords[Math.floor(Math.random() * game.remainingWords.length)]
const index = game.remainingWords.indexOf(game.currentWord)
game.remainingWords.splice(index, 1)
}
const gameLoop = () => {
for (const key in games) {
const game = games[key]
const { message, stage } = game
if (stage === 'STARTING') {
let string = stages[stage](game.counter)
message.edit(string)
if (game.counter <= 0) {
game.stage = 'IN_GAME'
game.counter = 15
selectWord(game)
string = stages[game.stage](game.currentWord)
message.edit(string)
}
} else if (stage === 'IN_GAME') {
if (game.counter <= 0) {
game.stage = 'ENDING'
const string = stages[game.stage](game.points)
message.edit(string)
// Delete the game
delete games[key]
continue
}
}
--game.counter
}
setTimeout(gameLoop, 1000)
}
module.exports = class FastTypeGame extends Commando.Command {
constructor(client) {
super(client, {
name: 'fasttype',
group: 'games',
memberName: 'fasttype',
description: 'Starts a fast type game',
userPermissions: ['ADMINISTRATOR'],
})
client.on('message', (message) => {
const { channel, content, member } = message
const { id } = channel
const game = games[id]
if (game && game.currentWord && !member.user.bot) {
message.delete()
if (
game.stage === 'IN_GAME' &&
content.toLowerCase() === game.currentWord.toLowerCase()
) {
game.currentWord = null
const seconds = 2
const { points } = game
points[member.id] = points[member.id] || 0
message
.reply(`You won! +1 point (${++points[member.id]} total)`)
.then((newMessage) => {
newMessage.delete({
timeout: 1000 * seconds,
})
})
setTimeout(() => {
if (game.stage === 'IN_GAME') {
selectWord(game)
const string = stages[game.stage](game.currentWord)
game.message.edit(string)
}
}, 1000 * seconds)
}
}
})
gameLoop()
}
async run(message) {
const { channel } = message
message.delete()
channel.send('Preparing game...').then((message) => {
games[channel.id] = {
message,
stage: 'STARTING',
counter: 5,
remainingWords: [...words],
points: {
'719805930547445772': 4,
'723819104045105172': 1,
},
}
})
}
}
First change embeded content is not related to discord.js-commando to change the content of sended embeded message you need to get Message Object then using edit() method to pass the new embed content to it:
-Bonus: You can also edit text message into embed message.
Docs for edit method: https://discord.js.org/#/docs/main/stable/class/Message?scrollTo=edit
Example code:
let youCurrentMessage = await channel.send(embedContent);
yourCurrentMessage.edit(newEmbedContent);
yourCurrentMessage.edit(newEmbedContent2);
// If you edit message in other command , session.You need message id
let yourCurrentMessage = await msg.channel.messages.fetch(editMessageId);
yourCurrentMessage.edit(newEmbedContent);
const names = cookie.currentMon.name;
dexData.pokemons.find((pokemon) => {
if (pokemon.name === names) {
pokemon.amount += 1;
} else {
const dexobj = {
pokemonID: poke.num,
name: names,
amount: 1,
};
dexData.pokemons.push(dexobj);
dexData.totalPokemon += 1;
};
});
dexData.save().catch((error) => {
console.log(error);
});
I have this code, and when the else statement is run it pushes the dexobj multiple times. If theres 2 previous docs, it will push 2 times. It basically doubles what is currently in the database. How can i search through the array of pokemon like this without the multiple pushes?
changed it to
const names = cookie.currentMon.name;
const pokemon = dexData.pokemons.find(x => x.name === names)
if (pokemon) {
pokemon.amount += 1;
} else {
const dexobj = {
pokemonID: poke.num,
name: names,
amount: 1,
};
dexData.pokemons.push(dexobj);
dexData.totalPokemon += 1;
};
dexData.save().catch((error) => {
console.log(error);
});
and it works now
How's it going?
I got the example order book code in python (https://support.kraken.com/hc/en-us/articles/360027677512-Example-order-book-code-Python-) and translate it to javascript to run in node. But the book is wrong, it doesn't remove all old prices level. I'm sending my code below. I'd like help to solve this issue.
const websocket = require('ws');
const ws = new websocket('wss://ws.kraken.com');
const api_book = {'bid':[], 'ask':[]};
const api_depth = 10;
const api_output_book = () => {
bid = api_book['bid'].sort((x, y) => parseFloat(y[0])-parseFloat(x[0]));
ask = api_book['ask'].sort((x, y) => parseFloat(x[0])-parseFloat(y[0]));
console.log ('Bid\t\t\t\t\tAsk');
for (let x=0;x<api_depth;x++) {
console.log(`${bid[x][0]} (${bid[x][1]})\t\t\t${ask[x][0]} (${ask[x][1]})`);
}
}
const api_update_book = (side, data) => {
data.forEach((e) => {
let index = api_book[side].findIndex(o => o[0] == e[0]);
if (parseFloat(e[1]) > 0){
if(index < 0){
api_book[side].push([e[0],e[1]]);
} else {
api_book[side][index] = [e[0],e[1]];
}
} else {
api_book[side].splice(index,1);
}
});
if(side=='bid'){
api_book['bid'].sort((x, y) => parseFloat(y[0])-parseFloat(x[0]));
} else if(side=='ask'){
api_book['ask'].sort((x, y) => parseFloat(x[0])-parseFloat(y[0]));
}
}
ws.on('open', open = () => {
ws.send('{"event":"subscribe", "subscription":{"name":"book", "depth":'+api_depth+'}, "pair":["XBT/USD"]}');
console.log('Kraken websocket connected!');
});
ws.on('message', incoming = (data) => {
try {
data = JSON.parse(data.toString('utf8'));
if (data[1]) {
if (data[1]['as']) {
api_update_book('ask', data[1]['as'])
api_update_book('bid', data[1]['bs'])
} else if (data[1]['a'] || data[1]['b']) {
if (data[1]['a']) {
api_update_book('ask', data[1]['a']);
}
if (data[1]['b']) {
api_update_book('bid', data[1]['b']);
}
}
api_output_book();
}
} catch (error) {
console.log(error);
}
});
So I have also been playing around with Kraken's order book and came up with this solution using Angular. I also added a few console logs into the mix so that you can take it and run it in the browser. Hope this helps!
// variables
private ws = new WebSocket('wss://ws.kraken.com')
public asks = [];
public bids = [];
// Web Socket open connection
this.ws.onopen = () => {
this.ws.send(JSON.stringify(this.message));
console.log('Trade WS with Kraken connected')
}
// Fires when new data is received from web socket
this.ws.onmessage = (event) => {
var data = JSON.parse(event.data);
if (!data.event) {
if (data[1]['as']) {
this.asks = data[1]['as'];
this.bids = data[1]['bs'];
console.log('Initialised Book');
console.log(this.asks, this.bids);
} else if (data[1]['a'] || data[1]['b']) {
if (data[1]['a']) {
this.update_book(this.asks, 'ask', data[1]['a']);
}
if (data[1]['b']) {
this.update_book(this.bids, 'bid', data[1]['b']);
}
}
}
}
// Updating Orderbook
update_book (arr, side, data) {
if (data.length > 1) { // If 2 sets of data are received then the first will be deleted and the second will be added
let index = arr.findIndex(o => o[0] == data[0][0]); // Get position of first data
arr.splice(index, 1); // Delete data
arr.push([ data[1][0], data[1][1] ]); // Insert new data
console.log('Delete and Insert');
} else {
let index = arr.findIndex(o => o[0] == data[0][0]);
console.error(index);
if (index > -1) { // If the index matches a price in the list then it is an update message
arr[index] = [data[0][0], data[0][1]]; // Update matching position in the book
console.log('Updated ' + index);
} else { // If the index is -1 then it is a new price that came in
arr.push([data[0][0], data[0][1]]); // Insert new price
this.sort_book(arr, side); // Sort the book with the new price
arr.splice(10, 1); // Delete the 11th entry
console.log('Insert Only');
}
}
this.sort_book(arr, side); // Sort the order book
}
// Sort Orderbook
sort_book (arr, side) {
if (side == 'bid') {
arr.sort((x, y) => parseFloat(y[0]) - parseFloat(x[0]));
} else if (side == 'ask') {
arr.sort((x, y) => parseFloat(x[0]) - parseFloat(y[0]));
}
}
I would also recommend just having a look at this resource:
How to maintain a valid orderbook
I am using Cloud Functions for Firebase together with my Firebase Realtime Database in order to do some data management for my app.
One of my functions though seems to get terminated since it takes about 100-150 seconds to complete. This happens with error : ESOCKETTIMEDOUT.
Is there a way to prevent this?
Here is my function:
function getTopCarsForUserWithPreferences(userId, genres) {
const pathToCars = admin.database().ref('cars');
pathTocars.orderByChild("IsTop").equalTo(true).once("value").then(function(snapshot) {
return writeSuggestedCars(userId, genres, snapshot);
}).catch(reason => {
console.log(reason)
})
}
function writeSuggestedCars(userId, genres, snapshot) {
const carsToWrite = {};
var snapCount = 0
snapshot.forEach(function(carSnapshot) {
snapCount += 1
const carDict = carSnapshot.val();
const carGenres = carDict.taCarGenre;
const genre_one = genres[0];
const genre_two = genres[1];
if (carGenres[genre_one] === true ||carGenres[genre_two] == true) {
carsToWrite[carSnapshot.key] = carDict
}
if (snapshot.numChildren() - 1 == snapCount) {
const pathToSuggest = admin.database().ref('carsSuggested').child(userId);
pathToSuggest.set(carsToWrite).then(snap => {
}).catch(reason => {
console.log(reason)
});
}
});
}
The getTopCarsForUserWithPreferences gets called when a user adds preferences. Also the cars table has about 50k entries.
Well you need to return everytime you use a async task.
Edit: you return 'writeSuggestedCars' but I think it never returns a value. I do not have a compiler, but I thought it was return Promise.resolved(). Can you insert it where I putted 'HERE'?
Maybe this will work:
function getTopCarsForUserWithPreferences(userId, genres) {
const pathToCars = admin.database().ref('cars');
return pathTocars.orderByChild("IsTop").equalTo(true).once("value").then(function(snapshot) {
return writeSuggestedCars(userId, genres, snapshot);
}).catch(reason => {
console.log(reason)
})
}
function writeSuggestedCars(userId, genres, snapshot) {
const carsToWrite = {};
var snapCount = 0
snapshot.forEach(function(carSnapshot) {
snapCount += 1
const carDict = carSnapshot.val();
const carGenres = carDict.taCarGenre;
const genre_one = genres[0];
const genre_two = genres[1];
if (carGenres[genre_one] === true ||carGenres[genre_two] == true) {
carsToWrite[carSnapshot.key] = carDict
}
if (snapshot.numChildren() - 1 == snapCount) {
const pathToSuggest = admin.database().ref('carsSuggested').child(userId);
return pathToSuggest.set(carsToWrite).then(snap => {
// 'HERE' I think return promise/Promise.resolve() will work
}).catch(reason => {
console.log(reason)
});
}
});
}
I am trying to implement a simple search scheme to search for values in one observable from another observable. The buildLookup function below builds a lookup table using values from an observable:
// Build lookup table from an observable.
// Returns a promise
function buildLookup(obs, keyName, valName) {
const map = new Map();
obs.subscribe((obj) => map.set(obj[keyName], obj[valName]));
// use concat to force wait until `obs` is complete
return obs.concat(Observable.from([map])).toPromise();
}
Then I have another function this uses the result of this function (a promise):
// Lookup in a previously built lookup table.
function lookup(source, prom, keyName, fieldName) {
return source.map((obj) => {
const prom2 = prom.then((map) => {
return lodash.assign({}, obj, { [fieldName]: map.get(String(obj[keyName])) });
});
return Observable.fromPromise(prom2);
})
.flatMap((x) => x);
}
For some reason, this implementation does not work, and every other lookup seems to fail. Could you someone guide me on:
what is wrong with this code, and
whether there is a better way to implement something like this?
Thanks a bunch in advance for your help!
I am attaching my test code below:
"use strict";
const lodash = require("lodash");
const rxjs = require("rxjs");
const chai = require("chai");
const Observable = rxjs.Observable;
const assert = chai.assert;
const assign = lodash.assign;
describe("search", () => {
it("simple search", (done) => {
let nextId = 1, nextId2 = 1;
const sourceObs = Observable.interval(5).take(5).map((i) => {
const id = nextId++;
return { id: `${id}` };
});
const searchableObs = Observable.interval(5).take(5).map((i) => {
const id = nextId2++;
return Observable.from([
{ id: `${id}`, code: "square", val: id * id },
]);
}).flatMap((x) => x);
const results = [];
const verifyNext = (x) => {
assert.isDefined(x);
results.push(x);
};
const verifyErr = (err) => done(err);
const verifyComplete = () => {
assert.equal(results.length, 5);
try {
results.forEach((r) => {
console.log(r);
// assert.equal(r.val, r.id*r.id); <== *** fails ***
});
} catch (err) {
done(err);
}
done();
};
// main
const lookupTbl = buildLookup(searchableObs, "id", "val"); // promise that returns a map
lookup(sourceObs, lookupTbl, "id", "val")
.subscribe(verifyNext, verifyErr, verifyComplete)
;
});
});
// output
// { id: '1', val: 1 }
// { id: '2', val: undefined }
// { id: '3', val: 9 }
// { id: '4', val: undefined }
// { id: '5', val: 25 }
So, a bunch of things to address here.
The main issue is that you are doing side-effects in your sourceObs and searchableObs observables, and it is not published, so the side-effects happen multiple times because you subscribe multiple times, giving you a wrong map entirely. For instance, I get maps like:
{"1" => 1, "4" => 16, "7" => 49, "12" => 144}
But you are doing something so trivial that you should really not use mutable variables.
To solve this, here is how you can create the proper observables:
const sourceObs = Rx.Observable.range(1, 5).map(i => ({ id: `${i}` }));
const searchableObs = Rx.Observable.range(1, 5).map(i =>
({ id: `${i}`, code: "square", val: i * i })
);
There is no reason to use a variable since range returns the numbers 1, 2, ...
And your use of o.map(_ => Rx.Observable.from(...)).concatMap(e => e) is really just the same as o...
While I'm here, this is a simplified version of your correct but clumsy functions:
// so buildLookup just needs to return a map once it's finished populating it
function buildLookup(obs, keyName, valName) {
// following your style here, though this could be done using `scan`
const map = new Map();
obs.subscribe((obj) => map.set(obj[keyName], obj[valName]));
// instead of your promise, I just wait for `obs` to complete and return `map` as an observable element
return obs.ignoreElements().concat(Rx.Observable.of(map));
}
// and lookup just needs to wait for the map, and then populate fields in the object
function lookup(source, prom, keyName, fieldName) {
return prom
.concatMap(map => source.map(obj => ({ obj: obj, map: map })))
.map(({ obj, map }) => lodash.assign({}, obj, { [fieldName]: map.get(String(obj[keyName])) }))
;
}
This should work for you.