I am trying to make my own cryptocurrency payment forwarder, (like https://apirone.com/), but I am facing an issue, this is my code:
const CryptoAccount = require('send-crypto');
const WebSocket = require('ws');
const qrcode = require('qrcode-terminal');
const clipboardy = require('clipboardy');
const init = async function() {
const socket = new WebSocket('wss://ws.blockchain.info/inv');
const wallets = [];
socket.on('open', stream => {
console.log('WebSocket opened');
setInterval(() => socket.send(JSON.stringify({"op": "ping"})), 10000);
});
socket.on('error', err => {
console.error(err.message);
});
socket.on('message', stream => {
try {
const response = JSON.parse(stream.toString('utf-8'));
if (JSON.stringify(response).includes('pong')) {
console.log('pong');
return;
}
console.log('SOCKET RESPONSE', response);
let outAddr = response.x.out[0].addr;
wallets.find(w => w.addr == outAddr).callback(response);
} catch (error) {
console.error(error);
}
})
const generateWallet = function() {
const privateKey = CryptoAccount.newPrivateKey();
const account = new CryptoAccount(privateKey);
return account;
}
const genWalletAndWatch = async function(currency, callback) {
if (currency !== 'BTC') throw new Error('Only BTC is supported currently.');
const wallet = generateWallet();
wallets.push({addr: (await wallet.address('BTC')), callback});
socket.send(JSON.stringify({"op":"addr_sub", "addr": (await wallet.address(currency))}));
return (await wallet.address(currency));
}
socket.onopen = async function(e) {
const wallet = await genWalletAndWatch('BTC', (response) => {
console.log('ANSWER', JSON.stringify(response));
});
console.log(wallet);
qrcode.generate('bitcoin:'+wallet, {small: true});
clipboardy.writeSync(wallet);
}
};
init();
This is a test code so there is no any express server or forward for the moment, I am only trying to create a btc address then to detect when a payment is done, but my problem is: The payment is never detected by the WebSocket, i can create a wallet without problem, I can ping the blockchain.info WebSocket api successfully, but the "addr_sub" never works, I've tried to send BTC etc, nothing is working.
I'm trying to test the following code:
adapter.js
async function adapt(message) {
let parser = JSON.parse(message.content.toString());
let apiResult = await api(parser.id);
let result = apiResult.data.data;
return adapptedMessage = {"id": result.id}
}
This is my api call.
server.js
const axios = require('axios');
const url = process.env.URL;
function getApi(id) {
return axios.get(url + id).catch(function (error) {
if (error.response) {
// Request made and server responded
console.log(error.response.data);
console.log(error.response.status);
console.log(error.response.headers);
} else if (error.request) {
// The request was made but no response was received
console.log(error.request);
} else {
// Something happened in setting up the request that triggered an Error
console.log('Error', error.message);
}});
}
module.exports = getApi
This is how I tried to test.
test.js
jest.mock('./server');
const axios = require('axios');
const adapt = require('./adapter');
describe("Adapter Test", () => {
test("adapt", async () => {
var result = await adapt(getMessage());
const mockResp = {"data":{"data": {"id":10}}};
axios.get = jest.fn(() => mockResp);
assert
expect(result).toStrictEqual(getOfferMessage());
});
})
function getMessage() {
return {"content":"{\"id\":10}"};
}
This is my first test in js, and I don't know how to mock the api call.
All I get is "undefined".
Could you help me?
Thanks
You should pass a factory function to jest.mock when mocking your server module
const mockResp = {"data":{"data": {"id":10}}};
jest.mock('./server', () => () => mockResp);
const adapt = require('./adapter');
describe("Adapter Test", () => {
test("adapt", async () => {
const result = await adapt(getMessage());
expect(result).toStrictEqual({ id: 10 });
});
})
function getMessage() {
return {"content":"{\"id\":10}"};
}
I am trying to create a bot in Slack called "Chochon", the problem is that when I receive the event "app_mention", chochon responds more than once several times in a row, instead of sending 1 message and stopping until they mention it again.
This is my code, chochon function:
socketModeClient.on('app_mention', async ({ event }) => {
try {
console.log(event);
let userBox = await Chochon.users.info({ user: event.user });
let userProfile = userBox.user.profile;
console.log(cli.green(`Event received : [ ${event.type} ] from [ ${userProfile.display_name} ]`));
// Respond to the event
Chochon.chat.postMessage({
channel: event.channel,
text: `Hello <#${event.user}>, I'm Chochon!, I'm a bot that can help you to manage your team.`
});
} catch (error) {
console.error(error);
}
});
The slack client:
Full code:
// Dependencies :
const dotenv = require('dotenv').config();
const path = require('path');
const cli = require('cli-color');
// Web client [CLI]
const { WebClient } = require('#slack/web-api');
const Chochon = new WebClient(process.env.SLACK_BOT_TOKEN.trim());
// Socket IO
const { SocketModeClient } = require('#slack/socket-mode');
const appToken = process.env.SLACK_APP_TOKEN;
const socketModeClient = new SocketModeClient({ appToken });
socketModeClient.start();
// Internal functions
//const eventManager = require(path.resolve(__dirname, './utils/events/manager'));
socketModeClient.on('app_mention', async ({ event }) => {
try {
console.log(event);
let userBox = await Chochon.users.info({ user: event.user });
let userProfile = userBox.user.profile;
console.log(cli.green(`Event received : [ ${event.type} ] from [ ${userProfile.display_name} ]`));
// Respond to the event
Chochon.chat.postMessage({
channel: event.channel,
text: `Hello <#${event.user}>, I'm Chochon!, I'm a bot that can help you to manage your team.`
});
} catch (error) {
console.error(error);
}
});
socketModeClient.on('slash_commands', async ({ body, ack }) => {
if (body.command === "/ping") {
console.log(cli.green(`Event received : [ ${body.command} ]`));
await ack({"text": "I got it, pong!"});
}
});
I tried something like this in the forked child process:
WriterPlugin.js (child process)
function sendCursor(){
try {
let cursor = await getQueryCursor(reqBody, db);
cursor.on('data', (data) => {
process.send(data);
})
} catch (error) {
process.send({
message: error.toString(),
status: 400
})
}
}
controller.js (parent process)
const childProcess = fork(fileToExec);
childProcess.send(objectToProcess);
childProcess.on('message', (data) => {
reply.send(data);
})
This one printed just last data of cursor and i faced with a fastifyError:
"code":"FST_ERR_REP_ALREADY_SENT","statusCode":500},"msg":"Reply already sent"}
How can i properly handle the cursor.stream() from a forked child process using fastify?
You need to push the data into a stream to accomplish this task:
const { fork } = require('child_process')
const { Readable } = require('stream')
const fastify = require('fastify')()
fastify.get('/', (request, reply) => {
const stream = new Readable({
read (size) {}
})
const childProcess = fork('./external-file.js')
childProcess.on('message', (data) => {
stream.push(JSON.stringify(data)) // it must be a string
})
childProcess.on('close', (data) => {
stream.push(null)
})
reply.send(stream)
})
fastify.listen(8080)
Where external-file.js is:
let iteration = 0
const timer = setInterval(() => {
const data = { iteration: iteration++ }
process.send(data)
if (iteration === 3) {
clearInterval(timer)
}
}, 1000)
I am at a complete loss as to why this is not working, I am new to using Hooks in react however want to try.
This app basically connects sockets.io to the server using sockets-auth server emits the time every 1s <- this shows me that the connection is definitely live. It then uses sockets.emit to receive an array of data, this functions. The part that doesn't is a simple button press fails to achieve anything (see both 'Save Changes' buttons at bottom of React element). No error, no response, no acknowledgment at all. I have tried it inline, in its own function. The exact same server routes are functional simultaneously running a separate app with class components.
Help would be much appreciated. Anyway here is some code..
Client-Side (React.js)
import React, { Component,useState, useEffect } from "react";
import io from "socket.io-client";
import './Secret.css'
const Secret = () => {
const [time, setTime] = useState('');
const [userlist, setUserlist] = useState([]);
const socketUrl = 'http://localhost:3001';
let socket = io(socketUrl, {
autoConnect: false,
});
useEffect(() => {
const requestOptions = {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ title: 'React POST Request Example' })
};
fetch('/api/connectsocket', requestOptions)
.then(async response => {
const data = await response.json();
if (!response.ok) {
const error = (data && data.message) || response.status;
return Promise.reject(error);
}
console.log(data)
connectSocket(data)
})
.catch(error => {
console.error('There was an error!', error);
});
}, [socketUrl]);
const connectSocket= (data)=>{
let dataUser = data
console.log(dataUser.email)
let error = null;
socket.on('connect', () => {
console.log('Connected');
socket.emit('authentication', {
token: dataUser.email,
i: dataUser.i
});
});
socket.on('unauthorized', (reason) => {
console.log('Unauthorized:', reason);
error = reason.message;
socket.disconnect();
});
socket.on('disconnect', (reason) => {
console.log(`Disconnected: ${error || reason}`);
error = null;
});
socket.on("admin connected", data => {
socket.emit('admin userlist', { get:'x',for:'who' }, (error) => {
if(error) {
alert(error);
}
});
console.log(data)
});
socket.on("admin user list", data => {
setUserlist(data)
console.log(data)
});
socket.on("FromAPI", data => {
setTime(data)
console.log(data)
});
socket.open();
}
const sendMessage = (x)=>{
console.log(x)
socket.emit('hello', 'JSON.stringify(x)');
}
const doSomething = ()=>{
socket.emit('chatmessage', {msg:"Hello"});
}
return (
<div>
<div className="outerContainer">
<div className="container">
{time}
<button onClick={sendMessage}>Save Changes</button>
<button onClick={doSomething}>Save Changes</button>
{userlist.map(user => (
<tr key={user.id} value={user.id}>
<td>{user.email}</td>
<td>{user.email}</td>
<td>{user.email}</td>
</tr>
))}
</div>
</div>
</div>
);
}
export default Secret
Server-side (Node.js)
const http = require('http');
const cors = require('cors');
const express = require('express');
const bodyParser = require('body-parser');
const cookieParser = require('cookie-parser');
const io = require('socket.io')();
const socketAuth = require('socketio-auth');
//custom modules
const router = require('./modules/router');
const db = require('./modules/db.js');
const prettyDate = require('./modules/prettyDate.js');
const sortArray = require('./modules/sortArray.js');
const app = express();
const server = http.createServer(app);
io.attach(server);
app.use(cors()); ///delete for production
app.use(bodyParser.urlencoded({ extended: true }));
app.use(fileUpload({
createParentPath: true
}));
app.use(bodyParser.json());
app.use(cookieParser());
app.use(router);
app.use(express.static('public'))
db.i12345.sessions.persistence.setAutocompactionInterval(400)
////Sessions Store; not important if you want to run the code without
function setUser(id,user,i){
console.log('setuser')
let institution = i
db[institution].sessions.find({ user:user }, function (err, docs) {
if(docs.length){
db[institution].sessions.update({user:user }, { $set: { id: id } }, { multi: true }, function (err, numReplaced) {
});
} else {
var d = new Date();
var n = d.getTime();
var doc = { user: user
, id: id
, t:n
};
db[institution].sessions.insert(doc, function (err, newDoc) {
});
}
});
}
///user verification; could easily mock; if wanting to actually run code
async function verifyUser (token,i) {
var institution =i
return new Promise((resolve, reject) => {
// setTimeout to mock a cache or database call
setTimeout(() => {
var user = new Promise((resolve,reject) =>{
db[institution].users.find({email:token}, function (err, docs) {
if(docs.length){
resolve(docs);
} else {
reject(false)
}
});
});
if (!user) {
return reject('USER_NOT_FOUND');
}
return resolve(user);
}, 200);
});
}
//// Sockets auth implementation
socketAuth(io, {
authenticate: async (socket, data, callback) => {
const { token, i } = data;
console.log(data)
try {
const user = await verifyUser(token, i);
socket.user = user;
socket.i = i
console.log(i)
return callback(null, true);
} catch (e) {
console.log(`Socket ${socket.id} unauthorized.`);
return callback({ message: 'UNAUTHORIZED' });
}
},
postAuthenticate: async (socket) => {
console.log(`Socket ${socket.id} authenticated.`);
console.log(socket.user)
setUser(socket.id,socket.user, socket.i)
socket.emit('empty list', {queryList:true});
io.emit('admin connected', {admin:true});
socket.conn.on('packet', async (packet) => {
if (socket.auth && packet.type === 'ping') {
}
});
socket.on('chatmessage', (msg) => {
io.emit('chatmessage', msg);
console.log(msg);
});
socket.on('hello', (msg) => {
io.emit('chatmessage', msg);
console.log(msg);
});
socket.on('get tasks', (get) => {
let i = socket.i
let user = socket.user
getTasksChunk(get,i,user)
});
socket.on('admin userlist', (get) => {
let i = socket.i
let user = socket.user
adminGetUserList(get,i,user)
});
socket.on('admin roles', (data) => {
let i = socket.i
let user = socket.user
console.log(data)
console.log('reaced')
});
interval = setInterval(() => getApiAndEmit(socket), 1000);
},
disconnect: async (socket) => {
console.log(`Socket ${socket.id} disconnected.`);
if (socket.user) {
}
},
})
function getTasksChunk(get,i, user){
console.log(get,i,user);
let institution = i
db[institution].tasks24.find({}, async function (err, docs) {
if(docs.length){
for(i=0;i<docs.length;i++){
docs[i].location = `Ward: ${docs[i].location.Ward} Bed: ${docs[i].location.Bed}`
docs[i].patient = docs[i].patient.join(' | ')
docs[i].timestamp = prettyDate(new Date(+docs[i].timestamp))
}
console.log(docs)
let sorted = await sortArray(docs,'timestamp')
let chunk = sorted.slice(0,10)
io.emit('tasks in', chunk);
} else {
}
});
}
const getApiAndEmit = socket => {
const response = new Date();
// Emitting a new message. Will be consumed by the client
socket.emit("FromAPI", response);
};
///////ADMIN????????
function adminGetUserList(get,i,user){
var institution = i
console.log('hello')
db[institution].users.find({}, async function (err, docs) {
if(docs.length){
console.log(docs)
let sorted = await sortArray(docs,'timestamp')
io.emit('admin user list', sorted);
} else {
}
});
}
server.listen(process.env.PORT || 3001, () => console.log(`Server has started.`));
You must store your socket instance in a ref otherwise you would loose the connected socket instance when your component re-renders.
In short you need the socket reference to be the same across renders
const socketUrl = 'http://localhost:3001';
let socket = useRef(null);
useEffect(() => {
socket.current = io(socketUrl, {
autoConnect: false,
});
...
}, [socketUrl]);
Now note that whereever you are using socket you would use socket.current.
Ex:
socket,.current.on('hello', (msg) => {
io.emit('chatmessage', msg);
console.log(msg);
});