Event not emitting inside derived class of event emitter in Node Js - node.js

tinyscrapper.js
I am emitting scrapeStarted event in scrap function
const EventEmitter = require("events");
const axios = require("axios");
const cheerio = require("cheerio");
const { exit } = require("process");
class TinyScraper extends EventEmitter {
constructor(url, timeout) {
super();
this.scrap(url,timeout) }
async scrap(url, timeout) {
this.emit("scrapeStarted");
let results=null;
try {
setTimeout(
() => {
console.log("timeout");
if (!results) {
this.emit("timeout");
exit(1)
}
},
timeout
);
const { data } = await axios.get(url);
// Load HTML we fetched in the previous line
const $ = cheerio.load(data);
const title = $('meta[property="og:title"]').attr("content");
const image = $('meta[property="og:image"]').attr("content");
const desc = $('meta[property="og:description"]').attr("content");
results={ title: title,
image: `strong text`image,
description:desc}
this.emit("scrapeSuccess", results);
} catch (err) {
this.emit("error", err);
}
}
}
module.exports = TinyScraper;
I am listening scrapeStarted event in index.js but the event does not listen while it is called in TinyScrapper class. An exciting thing is an error, timeout and scrapeSuccess event is working fine
Index.js
// index.js
const TinyScraper = require('./tiny-scraper');
const scraper = new TinyScraper('http://localhost:8000/url1',20000);
scraper.on('scrapeSuccess', (data) => {
console.log('JSON Data received scrapping:', data);
});
scraper.on('scrapeStarted', (data) => {
console.log('Started Scraping:', data);
});
scraper.on('error', () => {
console.log('The URL is not valid.');
});
scraper.on('timeout', () => {
console.log('Scraping timed out');
});

Your scraper starts scraping before you've registered event listeners. You'll need to register event listeners and then call scrap yourself.
class TinyScraper extends EventEmitter {
constructor(url, timeout) {
this.url = url;
this.timeout = timeout
super();
}
async scrape() {
const {url, timeout} = this;
this.emit("scrapeStarted");
// ... rest of method
}
}
And then your index.js needs to be refactored to
const TinyScraper = require('./tiny-scraper');
const scraper = new TinyScraper('http://localhost:8000/url1',20000);
scraper.on('scrapeSuccess', (data) => {
console.log('JSON Data received scrapping:', data);
});
// other .on handlers
scraper.scrape();

Related

Create a crypto payment forwarder

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.

Mock api request Jest NodeJs

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}"};
}

SlackBot repeat the event without control

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!"});
}
});

How can i properly send a stream from forked child process in Node.js?

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)

Using Sockets.io with React.js + Hooks; sockets.emit Not Working

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);
});

Resources