GET Method failing on Node.js Express backend application - node.js

I am trying to create a backend application using Node.js and Express to get post and modify user data from a .json file. Here is my app.js file:
const express = require("express");
const fs = require("fs");
//setting up the express router
const app = express();
app.use(express.json());
//write the code for routes here
app.post("/add", function(req, resp){
var jsonObject = req.body;
var jsonFile = fs.readFileSync("get.json", "UTF8");
var jsonArray = JSON.parse(jsonFile);
jsonArray.push(jsonObject);
jsonFile = JSON.stringify(jsonArray);
resp.json(jsonFile);
fs.writeFileSync("get.json",jsonFile,"utf-8");
});
app.get('/view/:id?', function(req, resp){
var queryURL = url.parse(req.url, true).query;
var jsonFile = fs.readFileSync("get.json", "UTF8");
var data = JSON.parse(jsonFile);
var id = req.params.id;
// if(typeof queryURL.id === "undefined" || queryURL.id == 0){
// resp.json(data);
// }else{
// resp.json(data[queryURL.id-1]);
// }
if (id >= 0) {
data.array.every(user => {
if (user.id === id) {
resp.json(user);
return false;
} else {
console.log("Next")
}
});
} else{
resp.json(data);
}
});
app.patch("/edit/:id", function(req, res){
let userID = req.params.id;
let userFile = fs.readFileSync("get.json", "UTF-8");
let userArray = JSON.parse(userFile);
let reqUserObject = req.body;
let newUserArray = userArray.map(user => {
if (user.id === userID) {
updatedUser = {...user, ...reqUserObject};
return updatedUser;
} else {
return user;
}
});
userFileData = JSON.stringify(newUserArray);
res.json(userFileData);
fs.writeFileSync("get.json", userFileData, "UTF-8");
});
module.exports = app;
All methods work except for
app.get('/view/:id?', function(req, resp)
This Method has an optional query parameter id, if you pass an id you get a user with an id equal to that id. If you dont pass in an id, you get all the users from get.json.
The json file (get.json) I used is:
[{"id":"1","name":"updated name","age":"22","gender":"Male","email":"userone#gmail.com"},{"id":"2","name":"user two","age":"24","gender":"Female","email":"usertwo#gmail.com"},{"id":"3","name":"user three","age":"23","gender":"Male","email":"userthree#gmail.com"},{"id":"4","name":"user four","age":"21","gender":"Male","email":"userfour#gmail.com"}]
And my test file is:
const request = require("supertest");
const app = require("../app");
const md5 = require("md5");
const fs = require("fs");
//updating a user profile
test("Updating a user", async () => {
await request(app)
.patch("/edit/1")
.send({
name: "updated name",
})
.expect(200);
setTimeout(() => {
const data = JSON.parse(fs.readFileSync("../post.json"));
expect(data.length).toBe(5);
expect(data[0].name).toBe("updated name");
}, 1000);
});
// // posting a data
test("Posting a new data", async () => {
await request(app)
.post("/add")
.send({
id: "5",
name: "user new",
age: "36",
gender: "Female",
email: "usernew#gmail.com",
})
.expect(200);
setTimeout(() => {
const data = JSON.parse(fs.readFileSync("../post.json"));
expect(data.length).toBe(5);
}, 1000);
});
//checking the get route
test("Getting all the user data", async () => {
const response = await request(app).get("/view").expect(200);
expect(response.body.length).toBe(4);
expect(md5(response.body)).toBe("f1d3ff8443297732862df21dc4e57262");
});
//getting profile of a user based on id
test("Getting a single user data", async () => {
const response = await request(app).get("/view?id=2").expect(200);
expect(response.body.length).toBe(1);
expect(md5(response.body)).toBe("93b885adfe0da089cdf634904fd59f71");
});
When I run the tests I get:
rm -rf ./test-report.xml && CI=true ./node_modules/.bin/jest
--testResultsProcessor ./node_modules/jest-junit-reporter --forceExit; t-reporter --forceExit;.bin/jest --testResultsProcessor
./node_modules/jest-juni FAIL test/app.test.js ✓ Updating a user
(45ms) ✓ Posting a new data (17ms) ✕ Getting all the user data
(10ms) ✕ Getting a single user data (4ms)
● Getting all the user data
expected 200 "OK", got 500 "Internal Server Error"
39 | //checking the get route
40 | test("Getting all the user data", async () => {
> 41 | const response = await request(app).get("/view").expect(200);
| ^
42 | expect(response.body.length).toBe(4);
43 | expect(md5(response.body)).toBe("f1d3ff8443297732862df21dc4e57262");
44 | });
at Object.<anonymous>.test (test/app.test.js:41:52)
----
at Test._assertStatus (node_modules/supertest/lib/test.js:252:14)
at node_modules/supertest/lib/test.js:306:17
at Test._assertFunction (node_modules/supertest/lib/test.js:285:13)
at Test.assert (node_modules/supertest/lib/test.js:164:23)
at Server.localAssert (node_modules/supertest/lib/test.js:120:14)
● Getting a single user data
expected 200 "OK", got 500 "Internal Server Error"
46 | //getting profile of a user based on id
47 | test("Getting a single user data", async () => {
> 48 | const response = await request(app).get("/view?id=2").expect(200);
| ^
49 | expect(response.body.length).toBe(1);
50 | expect(md5(response.body)).toBe("93b885adfe0da089cdf634904fd59f71");
51 | });
at Object.<anonymous>.test (test/app.test.js:48:57)
----
at Test._assertStatus (node_modules/supertest/lib/test.js:252:14)
at node_modules/supertest/lib/test.js:306:17
at Test._assertFunction (node_modules/supertest/lib/test.js:285:13)
at Test.assert (node_modules/supertest/lib/test.js:164:23)
at Server.localAssert (node_modules/supertest/lib/test.js:120:14)
Test Suites: 1 failed, 1 total Tests: 2 failed, 2 passed, 4
total Snapshots: 0 total Time: 1.612s Ran all test suites.
Force exiting Jest: Have you considered using --detectOpenHandles to
detect async operations that kept running after all tests finished?
We can see the last 2 tests ("Getting all the user data" and "Getting a single user data") did not pass. These 2 test the app.get('/view/:id?', function(req, resp) method.
How could I fix my app.get('/view/:id?', function(req, resp) method?
Update:
I fixed my app.js according to the suggestion of Yago Biermann, but it is still unable to pass the last 2 tests. My revised app.js:
const express = require("express");
const fs = require("fs");
//setting up the express router
const app = express();
app.use(express.json());
//write the code for routes here
app.post("/add", function(req, resp){
var jsonObject = req.body;
var jsonFile = fs.readFileSync("get.json", "UTF8");
var jsonArray = JSON.parse(jsonFile);
jsonArray.push(jsonObject);
jsonFile = JSON.stringify(jsonArray);
resp.json(jsonFile);
fs.writeFileSync("get.json",jsonFile,"utf-8");
});
app.get('/view', function(req, resp) {
const id = req.query.id;
var jsonFile = fs.readFileSync("get.json", "UTF8");
var data = JSON.parse(jsonFile);
// return the whole data if query parameter wasn't provided
if (!id) return resp.status(200).json(data)
// you should use find instead of every to get the user data
const user = data.find(user => {
if (user.id === id) {
return user;
};
return null;
});
// return the user otherwise return a 404 response
return user ? resp.status(200).json(user) : resp.status(404).json({message:"user not found"})
});
app.patch("/edit/:id", function(req, res){
let userID = req.params.id;
let userFile = fs.readFileSync("get.json", "UTF-8");
let userArray = JSON.parse(userFile);
let reqUserObject = req.body;
let newUserArray = userArray.map(user => {
if (user.id === userID) {
updatedUser = {...user, ...reqUserObject};
return updatedUser;
} else {
return user;
}
});
userFileData = JSON.stringify(newUserArray);
res.json(userFileData);
fs.writeFileSync("get.json", userFileData, "UTF-8");
});
module.exports = app;
And what I get when I run the tests:
rter --forceExit;es/.bin/jest --testResultsProcessor
./node_modules/jest-junit-repo FAIL test/app.test.js ✓ Updating a
user (43ms) ✓ Posting a new data (15ms) ✕ Getting all the user
data (9ms) ✕ Getting a single user data (4ms)
● Getting all the user data
expect(received).toBe(expected) // Object.is equality
Expected: 4
Received: 5
40 | test("Getting all the user data", async () => {
41 | const response = await request(app).get("/view").expect(200);
> 42 | expect(response.body.length).toBe(4);
| ^
43 | expect(md5(response.body)).toBe("f1d3ff8443297732862df21dc4e57262");
44 | });
45 |
at Object.<anonymous>.test (test/app.test.js:42:32)
● Getting a single user data
expect(received).toBe(expected) // Object.is equality
Expected: 1
Received: undefined
47 | test("Getting a single user data", async () => {
48 | const response = await request(app).get("/view?id=2").expect(200);
> 49 | expect(response.body.length).toBe(1);
| ^
50 | expect(md5(response.body)).toBe("93b885adfe0da089cdf634904fd59f71");
51 | });
52 |
at Object.<anonymous>.test (test/app.test.js:49:32)
Test Suites: 1 failed, 1 total Tests: 2 failed, 2 passed, 4
total Snapshots: 0 total Time: 1.71s Ran all test suites.
Force exiting Jest: Have you considered using --detectOpenHandles to
detect async operations that kept running after all tests finished?

The problem seems to be at your tests, in your test "Getting all the user data" you are making a request to /view but you don't have any route to view, at least it's not in your question, note that /view/:id is not the same as /view. Lastly, in your test "Getting a single user data" you are not providing the id for the user as a url parameter, instead you are passing it as a query parameter, so try to do the following:
test("Getting a single user data", async () => {
// pass the id as url
const response = await request(app).get("/view/2").expect(200);
expect(response.params.id).toBe(2);
expect(response.body.length).toBe(1);
expect(md5(response.body)).toBe("93b885adfe0da089cdf634904fd59f71");
});
See the docs about req.params. Hope I helped a bit!
Edit: As you said that the test file can't be changed, do the following in your view route:
// Change to view, now the test on route /view should work
app.get('/view', function(req, resp) {
const id = req.query.id;
var jsonFile = fs.readFileSync("get.json", "UTF8");
var data = JSON.parse(jsonFile);
// return the whole data if query parameter wasn't provided
if (!id) return resp.status(200).json(data)
// you should use find instead of every to get the user data
const user = data.find(user => {
if (user.id === id) {
return user;
};
return null;
});
// return the user otherwise return a 404 response
return user ? resp.status(200).json([user]) : resp.status(404).json({message:"user not found"})
});

Related

WA business api nodejs

I have a problem with my nodejs code and the connection to the official whatsapp business api.
The bot connects the webhook correctly, the messages arrive to the server correctly but the code I have implemented to make it respond is not being effective, I checked the code from top to bottom but I can't find the fault.
I leave you the codes so you have more context:
whatsappController.js:
const fs = require("fs");
const myConsole = new console.Console(fs.createWriteStream("./logs.txt"));
const whatsappService = require("../services/whatsappService")
const VerifyToken = (req, res) => {
try {
var accessToken = "456E7GR****************************";
var token = req.query["hub.verify_token"];
var challenge = req.query["hub.challenge"];
if(challenge != null && token != null && token == accessToken){
res.send(challenge);
}
else{
res.status(400).send();
}
} catch(e) {
res.status(400).send();
}
}
const ReceivedMessage = (req, res) => {
try {
var entry = (req.body["entry"])[0];
var changes = (entry["changes"])[0];
var value = changes["value"];
var messageObject = value["messages"];
if(typeof messageObject != "undefined"){
var messages = messageObject[0];
var text = GetTextUser(messages);
var number = messages["from"];
myConsole.log("Message: " + text + " from: " + number);
whatsappService.SendMessageWhatsApp("The user say: " + text, number);
myConsole.log(messages);
myConsole.log(messageObject);
}
res.send("EVENT_RECEIVED");
}catch(e) {
myConsole.log(e);
res.send("EVENT_RECEIVED");
}
}
function GetTextUser(messages){
var text = "";
var typeMessage = messages["type"];
if(typeMessage == "text"){
text = (messages["text"])["body"];
}
else if(typeMessage == "interactive"){
var interactiveObject = messages["interactive"];
var typeInteractive = interactiveObject["type"];
if(typeInteractive == "button_reply"){
text = (interactiveObject["button_reply"])["title"];
}
else if(typeInteractive == "list_reply"){
text = (interactiveObject["list_reply"])["title"];
}else{
myConsole.log("sin mensaje");
}
}else{
myConsole.log("sin mensaje");
}
return text;
}
module.exports = {
VerifyToken,
ReceivedMessage
}
The second file is whatsappService which I make the connection with the api using the token and I also send the format of the message I want to send when I receive a hello for example...
const https = require("https");
function SendMessageWhatsApp(textResponse, number){
const data = JSON.stringify({
"messaging_product": "whatsapp",
"recipient_type": "individual",
"to": number,
"type": "text",
"text": {
"preview_url": false,
"body": textResponse
}
});
const options = {
host:"graph.facebook.com",
path:"/v15.0/1119744*************/messages",
method:"POST",
body:data,
headers: {
"Content-Type":"application/json",
Authorization:"Bearer EAAWNbICfuWEBAK5ObPbD******************************************************"
}
};
const req = https.request(options, res => {
res.on("data", d=> {
process.stdout.write(d);
});
});
req.on("error", error => {
console.error(error);
});
req.write(data);
req.end();
}
module.exports = {
SendMessageWhatsApp
};
Then I declare the routes for the get (to check token) and post (to receive and reply to messages) methods:
const expres = require("express");
const router = expres.Router();
const whatsappController = require("../controllers/whatsappControllers");
router
.get("/", whatsappController.VerifyToken)
.post("/", whatsappController.ReceivedMessage)
module.exports = router;
Last but not least the index file for the code to run correctly:
const express = require("express");
const apiRoute = require("./routes/routes");
const app = express();
const PORT = process.env.PORT || 3000
app.use(express.json());
app.use("/whatsapp", apiRoute);
app.listen(PORT, () => (console.log("El puerto es: " + PORT)));
I should clarify that I did the tests with Postman and they were all successful, it responds and receives messages correctly, finally I did the tests by uploading the bot to the Azure service and it works without problem until it has to answer/replicate the user's message.
The bot is not responding to the user when he talks to it but everything arrives correctly to the server and it processes it with a 200 response. I attach the evidence that there is no problem in the reception.
Finally I must say that in the meta platform I have everything configured as specified by the same platform, I have already configured the api to answer the messages through the webhooks and everything is correct, I just can't get the bot to answer correctly.
The bot is hosted in the Azure service.
Solved: some numbers have a problema with the api of WAB in my country (Argentina) the phone numbers start in +54 9 11. The problem is the 9 in the phone number, and this have a conflict in the servers of meta, Solution quit number 9 to numbers of this country and the message will send to user.

HTTP POST request test is not getting passed

I wrote some nodejs codes to be evaluated by a particular test file, candidate.test.js but surprisingly the test keeps failing. Below is the error I am getting:
Error: expect(received).toEqual(expected) // deep equality
Expected: 201
Received: 500
I have below the snippet from both app.js and candidate.test.js.
candidate.test.js
const request = require('supertest');
const app = require('../app');
// ...
async function makeRegularGetRequest(requestBody) {
return await request(app)
.post('/restaurant')
.send(requestBody)
.expect('Content-Type', /json/);
}
it('should create a restaurant given valid parameters', async () => {
const requestBody = {
name: '123',
position: 200,
category: 'Pizza',
rating: 5,
};
const createResponse = await makeRegularPostRequest(requestBody);
try {
expect(createResponse.status).toEqual(201);
} catch (error) {
throw error;
}
});
// ...
app.js
// ...
app.post('/restaurant', (request, response) => {
const id = generateRandomString();
const { name, position, category, rating } = request.body;
const restaurant = {
name: name,
position: position,
category: category,
rating: Number(rating),
id: id,
};
restaurants.push(restaurant);
response.send(restaurant).status(201);
});
// ...
I also tried sending only the statusCode to the page using reponse.send(restaurant).status(201).statusCode, but the error still comes out.
I tested this on Postman and it works really fine but it's strange that the test does not pass.
can you console log before and after pushing to restaurants array

Having trouble stubbing an asynchronous function

I am currently working on a full-stack application using the MERN stack. I have been writing unit tests for each route's request handler, and have been finding some difficulty testing error cases, particularly in trying to stub a function to reject a promise
I have the relevant code shown below:
One of my endpoints. Request handling is delegated to userController
const express = require("express");
const { body } = require("express-validator");
const router = express.Router();
const userController = require("../../controllers/user");
router.post(
"/",
body("username")
.isLength({
min: 3,
max: 30,
})
.withMessage(
"Your username must be at least 3 characters and no more than 30!"
),
body("password")
.isLength({ min: 3, max: 50 })
.withMessage(
"Your password must be at least 3 characters and no more than 50!"
),
userController.createNewUser
);
The request handler for the above endpoint. I am trying to test createNewUser. I want to stub createNewUser so that it causes an error to be thrown, so I can test that a 500 status code response is sent.
const bcrypt = require("bcryptjs");
const { validationResult } = require("express-validator");
const User = require("../models/User");
exports.createNewUser = async (req, res, next) => {
const { username, password } = req.body;
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({
success: false,
errors: errors.array(),
});
}
try {
// Create a bcrypt salt
const salt = await bcrypt.genSalt(12);
// Hash the password
const hashedPassword = await bcrypt.hash(password, salt);
// Create a new user
const user = new User({
username,
password: hashedPassword,
});
const response = await user.save();
res.status(200).json(response);
} catch (err) {
res.status(500).json({ msg: err.message });
}
};
The unit test for User endpoints. I am unsure how to test the error case where a 500 status code is returned...
const request = require("supertest");
// const todosController = require("../controllers/todos");
const server = require("../server");
const User = require("../models/TodoItem");
const db = require("./db");
const agent = request.agent(server);
// Setup connection to the database
beforeAll(async () => await db.connect());
afterEach(async () => await db.clear());
afterAll(async () => await db.close());
describe("User endpoints test suite", () => {
describe("POST api/user", () => {
test("It should create a user successfully and return a 200 response code", async () => {
const response = await agent
.post("/api/user")
.set("content-type", "application/json")
.send({ username: "Bob", password: "12345" });
expect(response.body.username).toEqual("Bob");
expect(response.status).toBe(200);
});
});
});
When you are creating unit test, create something small first, you can add complexity and refactor later.
Below are example simple unit and integration tests based on your code.
You can start with user controller.
// File: user.controller.js
const bcrypt = require('bcryptjs');
exports.createNewUser = async (req, res) => {
try {
// Create a bcrypt salt.
const salt = await bcrypt.genSalt(12);
// Just make it simple, show the salt.
res.status(200).json(salt);
} catch (err) {
// Other wise, return the error message.
res.status(500).json({ msg: err.message });
}
};
Based on that try and catch, you can create unit test.
// File: user.controller.spec.js
const bcrypt = require('bcryptjs');
const user = require('./user.controller');
describe('User Controller', () => {
describe('create New User', () => {
const fakeJson = jest.fn();
const fakeStatus = jest.fn().mockReturnThis();
const fakeRes = {
status: fakeStatus,
json: fakeJson,
};
const spy = jest.spyOn(bcrypt, 'genSalt');
afterEach(() => {
jest.clearAllMocks();
});
it('should return salt', async () => {
const testSalt = 'salt';
// Mock the bcrypt.genSalt, always resolved with value testSalt.
spy.mockResolvedValue(testSalt);
// Call the function under test.
await user.createNewUser(undefined, fakeRes);
// Set the expectations.
expect(fakeStatus).toHaveBeenCalledWith(200);
expect(fakeJson).toHaveBeenCalledWith(testSalt);
expect(spy.mock.calls[0][0]).toBe(12);
});
it('should return error message when error', async () => {
const error = new Error('XXX');
// Mock the bcrypt.genSalt, always resolved with value testSalt.
spy.mockRejectedValue(error);
// Call the function under test.
await user.createNewUser(undefined, fakeRes);
// Set the expectations.
expect(fakeStatus).toHaveBeenCalledWith(500);
expect(fakeJson).toHaveBeenCalledWith({ msg: error.message });
expect(spy.mock.calls[0][0]).toBe(12);
});
});
});
When you run it on terminal:
$ npx jest user.controller.spec.js
PASS ./user.controller.spec.js
User Controller
create New User
✓ should return salt (5 ms)
✓ should return error message when error (1 ms)
--------------------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
--------------------|---------|----------|---------|---------|-------------------
All files | 100 | 100 | 100 | 100 |
user.controller.js | 100 | 100 | 100 | 100 |
--------------------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 0.511 s, estimated 1 s
Ran all test suites matching /user.controller.spec.js/i.
Next, if you have sure with your controller, you can create integration test with express.
For example you create app index like this.
// File: index.js
const express = require('express');
const userController = require('./user.controller');
const router = express.Router();
router.post('/user', (req, res, next) => userController.createNewUser(req, res, next));
const app = express();
app.use('/api', router);
module.exports = app;
You can test it using jest for normal & error case like this.
// File: index.spec.js
const request = require('supertest');
const bcrypt = require('bcryptjs');
const server = require('./index');
const userController = require('./user.controller');
const agent = request.agent(server);
describe('App', () => {
describe('POST /', () => {
// Create spy on bcrypt.
const spy = jest.spyOn(bcrypt, 'genSalt');
const error = new Error('XXX');
afterEach(() => {
jest.clearAllMocks();
});
it('should create a salt successfully and return a 200 response code', async () => {
// This test is slow because directly call bcrypt.genSalt.
// To make it faster, mock bcrypt completely, or use spy.mockResolvedValue('SALT');
// Send post request.
const response = await agent.post('/api/user');
// Make sure the response.
expect(response.status).toBe(200);
expect(response.type).toBe('application/json');
expect(spy.mock.results[0].value).toBeDefined();
const spyResult = await spy.mock.results[0].value;
expect(response.body).toBe(spyResult)
});
it('should return 500 and error message when catch error', async () => {
// Makesure spy reject.
spy.mockRejectedValue(error);
// Send post request.
const response = await agent.post('/api/user');
// Make sure the response.
expect(response.status).toBe(500);
expect(response.type).toBe('application/json');
expect(response.body).toBeDefined();
expect(response.body.msg).toBeDefined();
expect(response.body.msg).toBe(error.message);
});
// Or play around with another spy to error alternatives.
it('should return 404 when pass to next', async () => {
// Makesure createNewUser error.
jest.spyOn(userController, 'createNewUser').mockImplementation((req, res, next) => {
// You can setup res here or other implementation to check.
// For example, do next.
next();
});
// Send post request.
const response = await agent.post('/api/user');
// Make sure the response.
expect(response.status).toBe(404);
// Method bcrypt.genSalt should not get called.
expect(spy).not.toHaveBeenCalled();
});
});
});
When you run it from terminal:
$ npx jest index.spec.js
PASS ./index.spec.js
App
POST /
✓ should create a salt successfully and return a 200 response code (40 ms)
✓ should return 500 and error message when catch error (4 ms)
✓ should return 404 when pass to next (5 ms)
--------------------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
--------------------|---------|----------|---------|---------|-------------------
All files | 100 | 100 | 100 | 100 |
index.js | 100 | 100 | 100 | 100 |
user.controller.js | 100 | 100 | 100 | 100 |
--------------------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests: 3 passed, 3 total
Snapshots: 0 total
Time: 0.809 s, estimated 1 s
Ran all test suites matching /index.spec.js/i.
Note: You do not need to use sinon, jest provides mock functions.

Node-Cron: Running multiple schedules a day

Tags:
node-cron, ExpressJs, NodeJs, Replit, Uptimerobot
Situation:
Hey all!
I am trying to get my discord bot to send multiple messages every day on specific times.
I deployed my bot on Replit and use Uptimerobot to ping my app every 10 min to keep the bot live.
In my code I used node-cron shedules for each spicific time it should send a message:
imports
const express = require("express");
const router = express.Router();
const { Client, Intents, Guild } = require("discord.js");
const cron = require("node-cron");
const token = process.env['BOT_TOKEN']
const { promotions } = require("./promotions");
const { testServers } = require("./test-servers");
const { buildMessage } = require("./generateMessage");
Message generator
router.get("/", function(req, res, next) {
const client = new Client({
intents: [Intents.FLAGS.GUILDS],
allowedMentions: { parse: ["users", "roles"] }
});
const composeMessage = guilds => {
let thisGuild;
let discordChannel;
let role;
let holidays;
let start;
let end;
guilds.map((guild, key) => {
guild.channels.cache.map(channel => {
testServers.forEach((promo, index) => {
thisGuild = promo.guild_id;
discordChannel = promo.channel_id;
role = promo.role_id;
holidays = promo.holidays;
start = promo.start;
end = promo.end;
// All relevant promotions
if (discordChannel === channel.id.toString()) {
const notAHoliday = [];
const currentDate = new Date();
holidays.forEach(holiday => {
if (
currentDate >= holiday.start &&
currentDate.setUTCHours(23, 59, 59) <= holiday.end
) {
notAHoliday.push(false);
}
});
if (
notAHoliday.length === 0 &&
(currentDate >= promo.start &&
currentDate.setUTCHours(23, 59, 59) <= promo.end)
) {
const unfilteredMessage = buildMessage(role);
channel.send(unfilteredMessage);
}
}
});
});
});
};
When running the Bot
client.once("ready", () => {
console.log("READY!");
const guilds = client.guilds.cache.map(guild => guild);
cron.schedule("0 55 7 * * Mon,Tue,Wed,Thu,Fri", () => {
console.log("morning");
composeMessage(guilds);
});
cron.schedule("0 31 11 * * Mon,Tue,Wed,Thu,Fri", () => {
console.log("start lunch");
composeMessage(guilds);
});
cron.schedule("0 25 12 * * Mon,Tue,Wed,Thu,Fri", () => {
console.log("end lunch");
composeMessage(guilds);
});
cron.schedule("0 0 16 * * Mon,Tue,Wed,Thu,Fri", () => {
console.log("evening");
composeMessage(guilds);
});
});
client.login(token);
botStatus = "Active";
res.render('index', { status: botStatus, version: "1.0.0" })
});
module.exports = router;
Issue:
The timers work but every time it runs a schedule, I get back a bunch of messages (the longer between schedules, the more messages my bot sends)
I suspect that it has to do with the pinging and the schedules stocking those runs until the shedule runs active and releases it all..
But how should I fix this?
Thanks in advance!

Node.js TypeError: Wit is not a constructor

How to solve "Wit is not a constructor" error coming from Node.js while executing code given by node-wit and wit.ai documentation.
// Setting up our bot
const wit = new Wit(WIT_TOKEN, actions);
I tried all the ways by upgrading and downgrading npm/node versions, but no luck.
Update: Please find the index.js source I used,
Do I need to change anything in this?
module.exports = {
Logger: require('./lib/logger.js').Logger,
logLevels: require('./lib/logger.js').logLevels,
Wit: require('./lib/wit.js').Wit,
}
'use strict';
var express = require('express');
var bodyParser = require('body-parser');
var request = require('request');
const Logger = require('node-wit').Logger;
const levels = require('node-wit').logLevels;
var app = express();
app.use(bodyParser.urlencoded({extended: false}));
app.use(bodyParser.json());
app.listen((process.env.PORT || 3000));
//const Wit = require('node-wit').Wit;
const WIT_TOKEN = process.env.WIT_TOKEN;
const FB_PAGE_TOKEN = process.env.FB_PAGE_TOKEN;
const Wit = require('node-wit').Wit;
// Server frontpage
app.get('/', function (req, res) {
debugger;
res.send('This is TestBot Server');
});
// Messenger API specific code
// See the Send API reference
// https://developers.facebook.com/docs/messenger-platform/send-api-reference
const fbReq = request.defaults({
uri: 'https://graph.facebook.com/me/messages',
method: 'POST',
json: true,
qs: { access_token: FB_PAGE_TOKEN },
headers: {'Content-Type': 'application/json'},
});
const fbMessage = (recipientId, msg, cb) => {
const opts = {
form: {
recipient: {
id: recipientId,
},
message: {
text: msg,
},
},
};
fbReq(opts, (err, resp, data) => {
if (cb) {
cb(err || data.error && data.error.message, data);
}
});
};
// See the Webhook reference
// https://developers.facebook.com/docs/messenger-platform/webhook-reference
const getFirstMessagingEntry = (body) => {
const val = body.object == 'page' &&
body.entry &&
Array.isArray(body.entry) &&
body.entry.length > 0 &&
body.entry[0] &&
body.entry[0].id === FB_PAGE_ID &&
body.entry[0].messaging &&
Array.isArray(body.entry[0].messaging) &&
body.entry[0].messaging.length > 0 &&
body.entry[0].messaging[0]
;
return val || null;
};
// Wit.ai bot specific code
// This will contain all user sessions.
// Each session has an entry:
// sessionId -> {fbid: facebookUserId, context: sessionState}
const sessions = {};
const findOrCreateSession = (fbid) => {
var sessionId;
// Let's see if we already have a session for the user fbid
Object.keys(sessions).forEach(k => {
if (sessions[k].fbid === fbid) {
// Yep, got it!
sessionId = k;
}
});
if (!sessionId) {
// No session found for user fbid, let's create a new one
sessionId = new Date().toISOString();
sessions[sessionId] = {fbid: fbid, context: {}};
}
return sessionId;
};
// Our bot actions
const actions = {
say(sessionId, context, message, cb) {
// Our bot has something to say!
// Let's retrieve the Facebook user whose session belongs to
const recipientId = sessions[sessionId].fbid;
if (recipientId) {
// Yay, we found our recipient!
// Let's forward our bot response to her.
fbMessage(recipientId, message, (err, data) => {
if (err) {
console.log(
'Oops! An error occurred while forwarding the response to',
recipientId,
':',
err
);
}
// Let's give the wheel back to our bot
cb();
});
} else {
console.log('Oops! Couldn\'t find user for session:', sessionId);
// Giving the wheel back to our bot
cb();
}
},
merge(sessionId, context, entities, message, cb) {
cb(context);
},
error(sessionId, context, error) {
console.log(error.message);
},
// You should implement your custom actions here
// See https://wit.ai/docs/quickstart
};
const wit = new Wit(WIT_TOKEN, actions);
// Message handler
app.post('/webhook', (req, res) => {
// Parsing the Messenger API response
// Setting up our bot
//const wit = new Wit(WIT_TOKEN, actions);
const messaging = getFirstMessagingEntry(req.body);
if (messaging && messaging.message && messaging.message.text) {
// Yay! We got a new message!
// We retrieve the Facebook user ID of the sender
const sender = messaging.sender.id;
// We retrieve the user's current session, or create one if it doesn't exist
// This is needed for our bot to figure out the conversation history
const sessionId = findOrCreateSession(sender);
// We retrieve the message content
const msg = messaging.message.text;
const atts = messaging.message.attachments;
if (atts) {
// We received an attachment
// Let's reply with an automatic message
fbMessage(
sender,
'Sorry I can only process text messages for now.'
);
} else if (msg) {
// We received a text message
// Let's forward the message to the Wit.ai Bot Engine
// This will run all actions until our bot has nothing left to do
wit.runActions(
sessionId, // the user's current session
msg, // the user's message
sessions[sessionId].context, // the user's current session state
(error, context) => {
if (error) {
console.log('Oops! Got an error from Wit:', error);
} else {
// Our bot did everything it has to do.
// Now it's waiting for further messages to proceed.
console.log('Waiting for futher messages.');
// Based on the session state, you might want to reset the session.
// This depends heavily on the business logic of your bot.
// Example:
// if (context['done']) {
// delete sessions[sessionId];
// }
// Updating the user's current session state
sessions[sessionId].context = context;
}
}
);
}
}
res.sendStatus(200);
});
There are two typical causes of your issue, either forgetting to require your module or forgetting to npm install it. Check if you:
Forgot to require('node-wit') and obtain the constructor from the returned object:
const Wit = require('node-wit').Wit
Properly required Wit but forgot to npm install node-wit
For everyone who are using messenger.js as your index.js use this:
const Wit = require('./lib/wit');
const log = require('./lib/log');
Please check your node_modules directory for node-wit package.
If node-wit is present then please require it before trying to create its instance.
const {Wit} = require('node-wit');
witHandler = new Wit({
accessToken: accessToken
});

Resources