How to call Web3js function from Nodejs file - node.js

I have the following working web3js code, Calling App.createContract() on button click works well on a webpage, however I want to call App.createContract() or similar from another Nodejs controller. Infact what I am thinking of is making an API in Node which could call the web3js function and returns a JSON result back to the caller. Can you please help me how to import my web3js file and call the function from Node Controller? Thanks
import "../stylesheets/app.css";
import { default as Web3} from 'web3';
import { default as contract } from 'truffle-contract';
import { default as CryptoJS} from 'crypto-js';
var accounts;
var account;
var shLogABI;
var shLogContract;
var shLogCode;
var shLogSource;
window.App = {
start: function() {
var self = this;
web3.eth.getAccounts(function(err, accs) {
if (err != null) {
alert("There was an error fetching your accounts.");
return;
}
if (accs.length == 0) {
alert("Couldn't get any accounts! Make sure your Ethereum client is configured correctly.");
return;
}
accounts = accs;
console.log(accounts);
account = accounts[0];
web3.eth.defaultAccount= account;
shLogSource= "pragma solidity ^0.4.6; contract SHLog { struct LogData{ string FileName; uint UploadTimeStamp; string AttestationDate; } mapping(uint => LogData) Trail; uint8 TrailCount=0; function AddNewLog(string FileName, uint UploadTimeStamp, string AttestationDate) { LogData memory newLog; newLog.FileName = FileName; newLog.UploadTimeStamp= UploadTimeStamp; newLog.AttestationDate= AttestationDate; Trail[TrailCount] = newLog; TrailCount++; } function GetTrailCount() returns(uint8){ return TrailCount; } function GetLog(uint8 TrailNo) returns (string,uint,string) { return (Trail[TrailNo].FileName, Trail[TrailNo].UploadTimeStamp, Trail[TrailNo].AttestationDate); } }";
web3.eth.compile.solidity(shLogSource, function(error, shLogCompiled){
shLogABI = JSON.parse(' [ { "constant": false, "inputs": [ { "name": "TrailNo", "type": "uint8" } ], "name": "GetLog", "outputs": [ { "name": "", "type": "string" }, { "name": "", "type": "uint256" }, { "name": "", "type": "string" } ], "payable": false, "type": "function" }, { "constant": false, "inputs": [ { "name": "FileName", "type": "string" }, { "name": "UploadTimeStamp", "type": "uint256" }, { "name": "AttestationDate", "type": "string" } ], "name": "AddNewLog", "outputs": [], "payable": false, "type": "function" }, { "constant": false, "inputs": [], "name": "GetTrailCount", "outputs": [ { "name": "", "type": "uint8" } ], "payable": false, "type": "function" } ]');
shLogContract = web3.eth.contract(shLogABI);
});
});
},
createContract: function()
{
shLogContract.new("", {from:account, gas: 3000000}, function (error, deployedContract){
if(deployedContract.address)
{
document.getElementById("contractAddress").value=deployedContract.address;
document.getElementById("fileName").value = '';
document.getElementById("uploadTimeStamp").value = '';
document.getElementById("attestationDate").value = '';
}
})
},
addNewLog: function()
{
var contractAddress = document.getElementById("contractAddress").value;
var deployedshLog = shLogContract.at(contractAddress);
var fileName = document.getElementById("fileName").value;
var uploadTimeStamp = document.getElementById("uploadTimeStamp").value;
var attestationDate = document.getElementById("attestationDate").value;
deployedshLog.AddNewLog(fileName, uploadTimeStamp, attestationDate, function(error){
console.log(error);
})
},
getLog: function()
{
try{
var contractAddress = document.getElementById("contractAddress").value;
var deployedshLog = shLogContract.at(contractAddress);
deployedshLog.GetTrailCount.call(function (error, trailCount){
deployedshLog.GetLog.call(trailCount-1, function(error, returnValues){
document.getElementById("fileName").value= returnValues[0];
document.getElementById("uploadTimeStamp").value = returnValues[1];
document.getElementById("attestationDate").value=returnValues[2];
})
})
}
catch (error) {
document.getElementById("fileName").value= error.message;
document.getElementById("uploadTimeStamp").value = error.message;
document.getElementById("attestationDate").value= error.message;
}
}
};
window.addEventListener('load', function() {
if (typeof web3 !== 'undefined') {
console.warn("Using web3 detected from external source. If using MetaMask, see the following link. Feel free to delete this warning. :) http://truffleframework.com/tutorials/truffle-and-metamask")
window.web3 = new Web3(web3.currentProvider);
} else {
console.warn("No web3 detected. Falling back to http://localhost:8545. You should remove this fallback when you deploy live, as it's inherently insecure. Consider switching to Metamask for development. More info here: http://truffleframework.com/tutorials/truffle-and-metamask");
// fallback - use your fallback strategy (local node / hosted node + in-dapp id mgmt / fail)
window.web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));
}
App.start();
});

Simple example for web3js 1.0.0.beta*
1) Add web3js to your package.json on a server side:
"web3": "^1.0.0-beta.27"
2) Require and init web3 with some provider:
const Web3 = require('web3');
const web3SocketProvider = new Web3.providers.WebsocketProvider('ws://0.0.0.0:8546');
const web3Obj = new Web3(web3Provider);
Now you can use your web3 object as usual:
async function getAccounts() {
let accounts = await web3Obj.eth.getAccounts();
}

Related

Pancake Sniper Bot

I have a demo sniper bot that work only with BNB.My question is can someone help me to make it work with other tokens?I think that most changes of the code must be done in network.js file.I want the sniper bot to work with different tokens like BUSD,USDT and more.
Full code:https://github.com/zookyy/crypto-sniper
network.js
const chalk = require('chalk');
const ethers = require('ethers');
const msg = require('./msg.js');
const config = require('./config.js');
const cache = require('./cache.js');
const args = require('minimist')(process.argv.slice(2));
require('./init.js');
class Network {
async load() {
try {
// initialize stuff
this.node = new ethers.providers.WebSocketProvider(config.cfg.wallet.wss_node);
// initialize account
this.wallet = new ethers.Wallet(config.cfg.wallet.secret_key);
this.account = this.wallet.connect(this.node);
// get network id for later use
this.network = await this.node.getNetwork();
// pcs stuff for later use
this.factory = new ethers.Contract(
'0xcA143Ce32Fe78f1f7019d7d551a6402fC5350c73',
[
'event PairCreated(address indexed token0, address indexed token1, address pair, uint)',
'function getPair(address tokenA, address tokenB) external view returns (address pair)'
],
this.account // pass connected account to pcs factory
);
this.router = new ethers.Contract(
'0x10ED43C718714eb63d5aA57B78B54704E256024E',
[
'function getAmountsOut(uint amountIn, address[] memory path) public view returns (uint[] memory amounts)',
'function swapExactTokensForTokens(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline) external returns (uint[] memory amounts)',
'function swapExactTokensForTokensSupportingFeeOnTransferTokens(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline) external returns (uint[] memory amounts)',
'function swapExactETHForTokensSupportingFeeOnTransferTokens(uint amountOutMin, address[] calldata path, address to, uint deadline) external payable',
'function swapExactTokensForETHSupportingFeeOnTransferTokens(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline) external',
'function swapETHForExactTokens(uint amountOut, address[] calldata path, address to, uint deadline) external payable returns (uint[] memory amounts)'
],
this.account // pass connected account to pcs router
);
this.contract_in = new ethers.Contract(
config.cfg.contracts.input = '0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56',
[
{ "constant": true, "inputs": [{ "name": "_owner", "type": "address" }], "name": "balanceOf", "outputs": [{ "name": "balance", "type": "uint256" }], "payable": false, "type": "function" },
{ "constant": false, "inputs": [{ "name": "guy", "type": "address" }, { "name": "wad", "type": "uint256" }], "name": "approve", "outputs": [{ "name": "approved", "type": "bool" }], "payable": false, "type": "function" },
{ "constant": true, "inputs": [{ "name": "sender", "type": "address" }, { "name": "guy", "type": "address" }], "name": "allowance", "outputs": [{ "name": "allowed", "type": "uint256" }], "payable": false, "type": "function" },
{ "constant": true, "inputs":[],"name":"symbol","outputs":[{"name":"outname","type":"string"}], "payable": false, "type":"function"},
{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},
{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"}
],
this.account // pass connected account to bnb smart contract
);
this.contract_out = new ethers.Contract(
config.cfg.contracts.output,
[
{ "constant": true, "inputs": [{ "name": "_owner", "type": "address" }], "name": "balanceOf", "outputs": [{ "name": "balance", "type": "uint256" }], "payable": false, "type": "function" },
{ "constant": false, "inputs": [{ "name": "guy", "type": "address" }, { "name": "wad", "type": "uint256" }], "name": "approve", "outputs": [{ "name": "approved", "type": "bool" }], "payable": false, "type": "function" },
{ "constant": true, "inputs": [{ "name": "sender", "type": "address" }, { "name": "guy", "type": "address" }], "name": "allowance", "outputs": [{ "name": "allowed", "type": "uint256" }], "payable": false, "type": "function" },
{ "constant": true, "inputs":[],"name":"symbol","outputs":[{"name":"outname","type":"string"}], "payable": false, "type":"function"},
{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},
{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"}
],
this.account // pass connected account to bnb smart contract
);
// load user balances (for later use)
this.bnb_balance = parseInt(await this.account.getBalance());
this.input_balance = parseInt(this.bnb_balance,(this.isETH(config.cfg.contracts.input) ? this.bnb_balance : await this.contract_in.balanceOf(this.account.address )));
this.output_balance = parseInt((this.isETH(config.cfg.contracts.output) ? this.bnb_balance : await this.contract_out.balanceOf(this.account.address)));
// load some more variables
this.base_nonce = parseInt(await this.node.getTransactionCount(this.account.address));
this.nonce_offset = 0;
this.first_block = -1;
} catch(e) {
msg.error(`[error::network] ${e}`);
process.exit();
}
}
async prepare() {
msg.primary(`[debug::network] Preparing network..`);
// format maxInt.
const maxInt = (ethers.BigNumber.from("2").pow(ethers.BigNumber.from("256").sub(ethers.BigNumber.from("1")))).toString();
// cache & prepare contracts
if(!cache.isAddressCached(config.cfg.contracts.input)) {
cache.createAddress(config.cfg.contracts.input);
cache.setAddressArtifacts(config.cfg.contracts.input, (await this.contract_in.decimals()), await this.contract_in.symbol());
msg.primary(`[debug::network] Approving balance for ${cache.data.addresses[config.cfg.contracts.input].symbol}.`);
// approve output (for later)
const inTx = await this.contract_in.approve(
this.router.address,
maxInt,
{
'gasLimit': config.cfg.transaction.gas_limit,
'gasPrice': config.cfg.transaction.gas_price,
'nonce': (this.getNonce())
}
);
let inReceipt = await inTx.wait();
if(!inReceipt.logs[0].transactionHash) {
msg.error(`[error::network] Could not approve ${cache.data.addresses[config.cfg.contracts.input].symbol}. (cache)`);
process.exit();
}
msg.success(`[debug::network] ${cache.data.addresses[config.cfg.contracts.input].symbol} has been approved. (cache)`);
await cache.save();
} else {
msg.success(`[debug::network] ${cache.data.addresses[config.cfg.contracts.input].symbol} has already been approved. (cache)`);
}
if(!cache.isAddressCached(config.cfg.contracts.output)) {
cache.createAddress(config.cfg.contracts.output);
cache.setAddressArtifacts(config.cfg.contracts.output, (await this.contract_out.decimals()), await this.contract_out.symbol());
msg.primary(`[debug::network] Approving balance for ${cache.data.addresses[config.cfg.contracts.output].symbol}.`);
// approve output (for later)
const outTx = await this.contract_out.approve(
this.router.address,
maxInt,
{
'gasLimit': config.cfg.transaction.gas_limit,
'gasPrice': config.cfg.transaction.gas_price,
'nonce': (this.getNonce())
}
);
let outReceipt = await outTx.wait();
if(!outReceipt.logs[0].transactionHash) {
msg.error(`[error::network] Could not approve ${cache.data.addresses[config.cfg.contracts.output].symbol}. (cache)`);
process.exit();
}
msg.success(`[debug::network] ${cache.data.addresses[config.cfg.contracts.output].symbol} has been approved. (cache)`);
await cache.save();
} else {
msg.success(`[debug::network] ${cache.data.addresses[config.cfg.contracts.output].symbol} has already been approved. (cache)`);
}
// now that the cache is done, restructure variables
config.cfg.transaction.amount_in_formatted = ethers.utils.parseUnits(`${config.cfg.transaction.amount_in}`, cache.data.addresses[config.cfg.contracts.input].decimals);
}
// wrapper function for swapping
async swapFromTokenToToken(amountIn, amountOutMin, contracts) {
try {
return this.router.swapExactETHForTokensSupportingFeeOnTransferTokens(
amountOutMin,
contracts,
this.account.address,
(Date.now() + 1000 * 60 * 10),
{
'value': amountIn,
'gasLimit': config.cfg.transaction.gas_limit,
'gasPrice': config.cfg.transaction.gas_price,
'nonce': (this.getNonce())
}
);
} catch(e) {
console.log(`[error::swap] ${e.error}`);
process.exit();
}
}
async estimateTransaction(amountIn, amountOutMin, contracts) {
try {
let gas = await this.router.estimateGas.swapExactETHForTokensSupportingFeeOnTransferTokens(
amountOutMin,
contracts,
this.account.address,
(Date.now() + 1000 * 60 * 10),
{
'value': amountIn,
'gasLimit': config.cfg.transaction.gas_limit,
'gasPrice': config.cfg.transaction.gas_price
}
);
// check if is using enough gas.
if(gas > parseInt(config.cfg.transaction.gas_limit) || gas > parseInt(config.cfg.transaction.gas_limit)) {
msg.error(`[error::simulate] The transaction requires at least ${gas} gas, your limit is ${config.cfg.transaction.gas_limit}.`);
process.exit();
}
return true;
} catch(e) {
console.log(`[error::estimategas] ${e.error}`);
return this.estimateTransaction(amountIn, amountOutMin, contracts);
}
}
async transactToken(from, to) {
try {
let inputTokenAmount = config.cfg.transaction.amount_in_formatted;
// get output amounts
let amounts = await this.router.getAmountsOut(inputTokenAmount, [from, to]);
// calculate min output with current slippage in bnb
let amountOutMin = amounts[1].sub(amounts[1].div(100).mul(config.cfg.transaction.buy_slippage));
// simulate transaction to verify outputs.
let estimationPassed = await this.estimateTransaction(inputTokenAmount, amountOutMin, [from, to]);
// if simulation passed, notify, else, exit
if(estimationPassed) {
msg.success(`[debug::transact] Estimation passed successfully. proceeding with transaction.`);
} else {
msg.error(`[error::transact] Estimation did not pass checks. exiting..`);
process.exit();
}
let tx = await this.swapFromTokenToToken(
inputTokenAmount,
amountOutMin,
[from, to]
);
msg.success(`[debug::transact] TX has been submitted. Waiting for response..\n`);
let receipt = await tx.wait();
// get current ballance from output contract.
let currentOutBalance = await this.contract_out.balanceOf(this.account.address);
this.amount_bought_unformatted = ethers.utils.formatUnits(`${(currentOutBalance - this.output_balance)}`, cache.data.addresses[config.cfg.contracts.output].decimals);
return receipt;
} catch(err) {
if(err.error && err.error.message){
msg.error(`[error::transact] ${err.error.message}`);
}
else
console.log(err);
return this.transactToken(from, to);
}
return null;
}
isETH(token) {
return (token.toLowerCase() = ('0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c').toLowerCase())
}
async getLiquidity(pair) {
const bnbValue = await this.contract_in.balanceOf(pair);
const formattedbnbValue = await ethers.utils.formatEther(bnbValue);
// testing
if (formattedbnbValue < 1) {
msg.warning("[debug::liquidity] There is not enough liquidity yet.");
return this.getLiquidity(pair);
}
return formattedbnbValue;
}
async getPair(contract_in, contract_out) {
// get pair address
let pairAddress = await this.factory.getPair(contract_in, contract_out);
// no pair found, re-launch
if (!pairAddress || (pairAddress.toString().indexOf('0x0000000000000') > -1)) {
msg.warning("[debug::pair] Could not find pair for specified contracts. retrying..");
return this.getPair(contract_in, contract_out);
}
return pairAddress;
}
getNonce() {
let nonce = (this.base_nonce + this.nonce_offset);
this.nonce_offset ++;
return nonce;
}
}
module.exports = new Network();

How do I modify all occurrences of a property (including nested) in an object before sending a JSON response?

I have an object created by parsing some JSON. The JSON data looks like this:
{
"id": "fan",
"name": "My super awesome fan",
"image": "Icon.png",
"details": {
"parts": [
{
"name": "base",
"image": "base.png"
},
{
"name": "blades",
"image": "blade.png"
}
],
"sale": {
"value": "prime",
"image": "PrimeDay.png"
}
}
}
The values in the image property point to files located under the directory "public" which is served using express.static. For example, the blade.png file is located at:
public
--products
--fan (this is the same as the id property)
--blade.png
Now, when a query is made to fetch the details of this object, I want to modify the image property of the object sent in the response so that the JSON response looks like:
{
"id": "fan",
"name": "My super awesome fan",
"image": "http://localhost:3000/products/fan/icon.png",
"details": {
"parts": [
{
"name": "base",
"image": "http://localhost:3000/products/fan/base.png"
},
{
"name": "blades",
"image": "http://localhost:3000/products/fan/blade.png"
}
],
"sale": {
"value": "prime",
"image": "http://localhost:3000/products/fan/PrimeDay.png"
}
}
}
I went down the path of using an express middleware to create a url to point to the path of the using
const url = req.protocol + '://' + req.get('host')
How do I use this url value to modify the response object?
I don't want to hardcode the full localhost path to the images in the JSON, since the path may change when deployed.
I was able to solve this, partly using Charly's solution. The only reason I could not use it fully was because the fan part of the URL is also dynamic and is dependent on the id property in the JSON data.
Here's my approach:
setImagePaths = (data) => {
const baseUrl = 'http://localhost:3000/products/'
const baseId = data.id;
const patchImagePaths = (obj) => {
Object.keys(obj).forEach(key => {
if (typeof obj[key] === 'object') {
return patchImagePaths(obj[key]);
}
if (key === 'image') {
obj[key] = baseUrl + baseId + '/' + obj[key];
}
});
}
patchImagePaths(data);
}
You could modify your response object with a recursive function to update the image properties:
var myObject = {
"id": "fan",
"name": "My super awesome fan",
"image": "Icon.png",
"details": {
"parts": [
{
"name": "base",
"image": "base.png"
},
{
"name": "blades",
"image": "blade.png"
}
],
"sale": {
"value": "prime",
"image": "PrimeDay.png"
}
}
}
;
const url = "http://localhost:3000/products/fan/";
//You can use here your url function with whatever output it has.
function resetImages (obj) {
Object.keys(obj).forEach(function (key) {
if (Array.isArray(obj[key])) {
let arr = obj[key];
arr.map((i)=>{
return i.image = url+i.image;
});
return arr;
}
if (typeof obj[key] === 'object') {
return resetImages(obj[key]);
}
if(key==="image") {
obj[key]=url+obj[key];
}
});
}
resetImages(myObject);
console.log(JSON.stringify(myObject));
**Output:**
{"id":"fan","name":"My super awesome fan","image":"http://localhost:3000/products/fan/Icon.png","details":{"parts":[{"name":"base","image":"http://localhost:3000/products/fan/base.png"},{"name":"blades","image":"http://localhost:3000/products/fan/blade.png"}],"sale":{"value":"prime","image":"http://localhost:3000/products/fan/PrimeDay.png"}}}

Null value in model.findById when I make a get request [mongodb]

Problem
Hi dev,
I have the problem that when I try to make a get request to the series by id it shows me null.
I have noticed from the Atlas Mongos platform that I created the collection but it does not show me the data, only the structure of the scheme shows me
Function.js
const fs = require('fs');
const fetch = require('node-fetch');
const BASE_URL = " http://localhost:8081/api/v1/"
async function getSeries() {
return new Promise((resolve , reject) =>{
setTimeout(() => {
const res = require('./simple_database/series/1.json' , 'utf8');
resolve(res)
}, 1000);
})
}
module.exports = {
getSeries
}
Router
The route allseries allows me to access all the content. What I want to do is pass that content to the SeriesModel, maybe it is there where I have the problem that the data is not being inserted correctly.
In the route series/:id is where the null value is returning to me
const express = require('express');
const router = express.Router();
const f = require('./function');
const SeriesModel = require('./models/series');
router.get('/allseries', (req, res) => {
f.getSeries().then((series) =>{
res.status(200).json({
series
})
}).then((doc) =>{
SeriesModel.insertMany(doc , function(err , docs){
if(err){
console.error(err)
}else{
console.log(docs);
console.info('%d serie were successfully stored.', docs.length);
}
})
})
});
router.get('/series/:id' , (req , res , next) =>{
const id = req.params.id;
SeriesModel.findById(id)
.exec()
.then((doc) =>{
console.log("From database " , doc);
res.status(200).json(doc)
}).catch((err) =>{
console.error(err);
res.status(500).json({error: err})
})
})
module.exports = router;
Model/series.js
const mongoose = require('mongoose');
const serieSchema = mongoose.Schema({
"_id": {
"$oid": {
"type": "ObjectId"
}
},
"series_id": {
"type": "String"
},
"aggregateRating": {
"reviewCount": {
"type": "Number"
},
"ratingCount": {
"type": "Number"
},
"#type": {
"type": "String"
},
"ratingValue": {
"type": "Number"
}
},
"episodes": {
"1x": {
"07 Ghost": {
"type": [
"Mixed"
]
}
}
},
"metadata": {
"description": {
"type": "String"
},
"url": {
"type": "String"
},
"image": {
"type": "String"
},
"type": {
"type": "String"
},
"id": {
"type": "String"
},
"name": {
"type": "String"
}
},
"1x": {
"07 Ghost": {
"type": [
"Mixed"
]
}
}
});
module.exports = mongoose.model("cr_series" , serieSchema);
It is because findById takes it's parameter in form of object like this
SeriesModel.findById({_id:id})
You need to tell your query to which json object you want to match your incoming object.

How do i get textDetection and LabelDetection with NodeJs?

const results = await visionClient.labelDetection(imageUri).safeSearchDetection(imageUri);
i am trying to get an image response with cloud vision.
Below is an example of code for an HTTPS Cloud Function that will do the OCR (i.e. text detection) of an image stored in Firebase Storage. You would, for example, call it from your app after you have uploaded an image to Firebase Storage (in the gs://myproject.com/imgtoocr/ bucket), by passing the image name in the body of the HTTP Request.
....
const vision = require('#google-cloud/vision');
const client = new vision.ImageAnnotatorClient();
exports.imageOCR = functions.https.onRequest((req, res) => {
cors(req, res, () => {
const imageFilename = req.body.imageFilename;
let result;
return client
.documentTextDetection(
'gs://myproject.com/imgtoocr/' + imageFilename
)
.then(results => {
const blocks = results[0].fullTextAnnotation.pages[0].blocks;
blocks.forEach(blockDetail => {
blockDetail.paragraphs.forEach(paragraph => {
//Here you build the result you want to send back
});
});
return {
result: result
};
})
.then(ocrResult => {
return res.status(200).json(ocrResult);
})
.catch(err => {
console.error('ERROR:', err);
res.status(400).send(err);
});
});
});
You will find more info and examples (in particular for Label Detection) in the following documentation for node.js:
https://cloud.google.com/vision/docs/ocr-tutorial
https://cloud.google.com/vision/docs/detecting-labels
https://cloud.google.com/nodejs/docs/reference/vision/0.19.x/
Solved it this way for version 0.21.0
import * as vision from '#google-cloud/vision';
const visionClient = new vision.ImageAnnotatorClient();
const request = {
"image": {
"source": {
"imageUri": imageUri
}
},
"features": [
{
"type": "FACE_DETECTION"
},
{
"type": "LABEL_DETECTION"
},
{
"type": "SAFE_SEARCH_DETECTION"
},
{
"type": "WEB_DETECTION"
},
{
"type": "CROP_HINTS"
},
{
"type": "IMAGE_PROPERTIES"
},
{
"type": "DOCUMENT_TEXT_DETECTION"
},
{
"type": "TEXT_DETECTION"
},
{
"type": "LOGO_DETECTION"
},
{
"type": "LANDMARK_DETECTION"
},
{
"type": "TYPE_UNSPECIFIED"
},
// Other detection types here...
]
};
return await visionClient.annotateImage(request).then((res) => {
console.log(JSON.stringify(res));
});

API.ai Actions on Google - Failed to parse JSON response string with 'INVALID_ARGUMENT' error: ": Cannot find field."

This error is similar to what I asked here, but this time it's with NodeJs client.
I am trying to find directions to a location. As soon as the intent is triggered on my webhook, I am calculating the directions using GoogleMapAPI. But before it can finish and send a response, I receive the error on my Actions Console. I checked total response time and it is less than 2 seconds which is less than 5 seconds timeout by Google.Where I am wrong???
My API.ai Intent
Using express.js with Action-on-Google Node Client
'use strict';
const express = require('express');
const bodyParser = require('body-parser');
const intentHandler = require('./intent_handler')
const app = express();
app.use(bodyParser.json());
const ApiAiAssistant = require('actions-on-google').ApiAiAssistant;
// Create functions to handle requests here
....
....
const DIRECTION_INTENT = 'action_direction';
function MyAssistant(req, res) {
const assistant = new ApiAiAssistant({request: req, response: res});
assistant.handleRequest(responseHandler(assistant));
}
function responseHandler (assistant) {
// intent contains the name of the intent you defined in the Actions area of API.AI
let intent = assistant.getIntent();
switch (intent) {
case WELCOME_INTENT:
...
break;
case WELCOME_FALLBACK_PERMISSION_INTENT:
...
break;
case DIRECTION_INTENT:
console.log(">>>>>>>DIRECTION_INTENT<<<<<<<");
intentHandler.directionIntent(assistant);
break;
}
}
app.post('/', function (req, res) {
MyAssistant(req, res);
});
app.listen(8080, function () {
console.log('app listening on port 8080!')
});
Handler Code
'use strict';
const speech = require("./speech_template");
const direction = require("./directionModule");
const intent_handler = {
'welcomeIntent': function (assistant) {
.....
},
'welcomeFallbackPermissionIntent': function (assistant) {
.....
},
'directionIntent':function (assistant) {
console.log('direction intent');
direction.getDirectionWithSavedAddress(function (response) {
assistant.ask(response);
});
}
};
module.exports = intent_handler;
Direction Extraction --- ERROR comes on Action Console before this get finished
'use strict';
const striptags = require('striptags');
const speech = require("./speech_template");
let googleMapsClient = require('#google/maps').createClient({
key: global.GOOGLE_DIRECTION_KEY
});
const directionModule = {
'getDirectionWithSavedAddress': function (eventCallback) {
let myAdd = <From Saved Data>;
if (myAdd === undefined) {
console.log("error......");
}
let destination = <From Saved Data>;
this.getDirectionWithAddress(myAdd, destination, function (dir) {
....
if(SUCCESS){
eventCallback(`<speak> ${steps} </speak>`);
}else{
eventCallback(`<speak> ${speech.ERROR_DIRECTIONS} </speak>`);
}
});
},
'getDirectionWithAddress': function (add1, add2, eventCallback) {
let dir = {};
googleMapsClient.directions({
origin: add1,
destination: add2,
mode: "driving",
departure_time: "now"
}, function (err, response) {
if (!err) {
console.log(response.json.routes[0]);
....
....
....
} else {
console.log(`Error --> ${err.toString()}`);
....
}
eventCallback(dir);
});
}
};
module.exports = directionModule;
UPDATE
I am running the code locally via WebStorm and exposing webhook via port forwarding using ngrok.
Update2
BAD REQUEST 400
{
"originalRequest": {
"source": "google",
"version": "2",
"data": {
"isInSandbox": true,
"surface": {
"capabilities": [
{
"name": "actions.capability.AUDIO_OUTPUT"
}
]
},
"inputs": [
{
"rawInputs": [
{
"query": "get me there",
"inputType": "VOICE"
}
],
"arguments": [
{
"rawText": "get me there",
"textValue": "get me there",
"name": "text"
}
],
"intent": "actions.intent.TEXT"
}
],
"user": {
"locale": "en-US",
"userId": "<uID>"
},
"device": {},
"conversation": {
"conversationId": "<cID>",
"type": "ACTIVE",
"conversationToken": "[\"_actions_on_google_\",\"defaultwelcomeintent-followup\"]"
}
}
},
"id": "<ID>",
"timestamp": "2017-09-12T17:08:10.321Z",
"lang": "en",
"result": {
"source": "agent",
"resolvedQuery": "get me there",
"speech": "",
"action": "action_direction",
"actionIncomplete": false,
"parameters": {},
"contexts": [
{
"name": "_actions_on_google_",
"parameters": {},
"lifespan": 99
},
{
"name": "google_assistant_input_type_voice",
"parameters": {},
"lifespan": 0
},
{
"name": "actions_capability_audio_output",
"parameters": {},
"lifespan": 0
},
{
"name": "defaultwelcomeintent-followup",
"parameters": {},
"lifespan": 4
}
],
"metadata": {
"intentId": "<iID>",
"webhookUsed": "true",
"webhookForSlotFillingUsed": "false",
"nluResponseTime": 15,
"intentName": "DirectionIntent"
},
"fulfillment": {
"speech": "",
"messages": [
{
"type": 0,
"speech": ""
}
]
},
"score": 1
},
"status": {
"code": 200,
"errorType": "success"
},
"sessionId": "<sID>"
}
This looks like before my callback is finished, my webhook is sending empty response to Google Actions.
Why is this happening and How to resolve it?????
The problem lies in how your directionIntent() function calls, and handles the result of, your getDirectionWithSavedAddress() function. It expects getDirectionWithSavedAddress() returns a function, when it does not. Instead, getDirectionWithSavedAddress() expects to send its results to a callback.
So after it makes its call to getDirectionWithAddress(), the function ends, returning nothing. This "nothing" is sent to assistant.ask(), which returns that to Google's server. This is an invalid response, so you're getting the error.
Fixing this should be straightforward. You need to call getDirectionWithSavedAddress() with a callback function. Inside this function you should call assistant.ask() with the value sent to the callback.
So directionIntent() might look something like
'directionIntent':function (assistant) {
console.log('direction intent');
direction.getDirectionWithSavedAddress( function( msg ){
assistant.ask( msg );
} );
}
Updated
This line makes no sense:
assistant.handleRequest(responseHandler(assistant));
The assistant.handleRequest() function is supposed to be passed a Map of Intent names to functions to call to handle the event. You're doing this manually in the responseHandler() function and you're not returning a Map. Since you're not returning a Map, it fails when trying to do the handleRequest() and generates the error "Action Error: Request handler can NOT be empty".
You can fix this by just calling responseHandler(assistant) and not dealing with handleRequest() at all. Or you can create the map that handleRequest() is expecting and get rid of responseHandler() completely.

Resources