My Discord bot is built using Discord.JS. I need to check whether or not a crypto transaction has reached 1 confirmation, so I have it set on an interval to check. This works, it checks using the coinbase API, and can fetch whether or not it's confirmed. The only issue is that the interval never stops, and it spams it infinitely, checking for confirmations.
Any suggestions on how I can get the interval to stop after reaching 1 confirmation?
const axios = require('axios');
const fetch = require('fetch')
const cheerio = require('cheerio');
const { clearInterval } = require("timers");
const client = new Discord.Client()
module.exports = {
name: 'check',
cooldown: 10,
description: 't',
async execute(msg) {
if(!msg.member) return
const args = msg.content.trim(1).split(/ +/g);
async function getConfirmation(){
return axios.get(`https://mempool.space/api/tx/${args[1]}`)
}
var confi = getConfirmation().then(function(response){
if(response.data.status.confirmed === true) {
return msg.lineReply(`This transaction has already reached 1 confirmation!`).catch(e=>console.log(e))
} else {
msg.reply(`I will notify you when this transaction reaches 1 confirmation.`).catch(e=>console.log(e))
setInterval(function(oner){
getConfirmation().then(function(response){
if(response.data.status.confirmed === true) {
const embed = new Discord.MessageEmbed()
.setTitle(`<a:check:849801483389501510> Transaction Confirmed`)
.setDescription(`Transaction ID:\n${response.data.txid}`)
.setColor(`GREEN`)
.setURL(`https://mempool.space/tx/${args[1]}`)
msg.channel.send(`${msg.author}`)
clearInterval(oner)
return msg.channel.send(embed).catch(error =>{
console.log(error)
})
}
}).catch(error =>{
return console.log(error)
})
}, 180000)
}
}).catch(error =>{
console.log(error)
return msg.channel.send(`Error. Probably means you didn't input an actual Transaction ID!`).catch(error =>{
console.log(error)
})
})
}
};
setInterval(fx,n) creates an interval. If you want to stop it, use clearInterval(intervalID).
If you want to execute it just once, use setTimeout(fx, n).
When you generate an interval, the function setInterval() returns an unique id.
For example:
let counter = 0;
const myInterval = setInterval(() => {
counter++;
if (counter == 10) {
clearInterval(myInterval)
}
}, 1000);
This will count up to 10 and then stops the interval.
Related
I was going through the following tutorial to implement a simple web3 app: https://learn.figment.io/tutorials/chakra-ui-with-solana-dapps#realtime-account-updates-using-polling-and-custom-react-hooks
This sets up an interval to monitor the status of an account so that it can display up to date information.
The function where the interval is set up is as follows:
function useSolanaAccount() {
const [account, setAccount] = useState(null);
const { connection } = useConnection();
const wallet = useWallet();
const init = useCallback(async () => {
if(wallet.publicKey)
console.log("usecallback publicKey:", wallet.publicKey.toString());
else
console.log("usecallback publicKey: None");
if (wallet.publicKey) {
let acc = await connection.getAccountInfo(wallet.publicKey);
setAccount(acc);
}
}, [wallet, connection]);
useEffect(() => {
if (wallet.publicKey) {
setInterval(init, 1000);
console.log("use effect publicKey:", wallet.publicKey.toString());
}
else{
console.log("clear interval");
clearInterval(init);
}
}, [init, wallet]);
return { account };
}
I've been trying to understand how to cancel the interval, and I really don't get it. From the console log it clearly reaches the code "clearInterval(init)", but it doesn't actually seem to do anything.
I've tried making init a global thing, which I read in another Stack Overflow post as a possible solution, but that didn't help.
I am working on a discord image command that sends a random image every 10 seconds. When I start the command, it works. It starts sending images every 10 seconds, but when I try to make it stop, it just won't.
What went wrong? How can I fix this?
Image.js:
var Scraper = require('images-scraper');
const google = new Scraper({
puppeteer: {
headless: true,
},
});
var myinterval;
module.exports = {
name: 'image',
description: 'Sends profile pics)',
async execute(kaoru, message, args, Discord) {
//prefix "?"
const image_query = args.join(' ');
const image_results = await google.scrape(image_query, 100);
if (!image_query)
return message.channel.send('**Please enter name of the image!**');
if (image_query) {
message.delete();
myinterval = setInterval(function () {
message.channel.send(
image_results[Math.floor(Math.random() * (100 - 1)) + 1].url,
);
}, 10000);
} else if (message.content === '?stopp') {
clearInterval(myinterval);
message.reply('pinging successfully stopped!');
}
}
};
You can't just simply check the message.content inside the execute method. It will never be ?stopp as execute only called when you send the image command (i.e. ?image search term).
What you can do instead is to set up a new message collector in the same channel and check if the incoming message is your stop command. In my example below I used createMessageCollector. I've also added plenty of comments.
const google = new Scraper({
puppeteer: {
headless: true,
},
});
module.exports = {
name: 'image',
description: 'Sends profile pics',
async execute(kaoru, message, args, Discord) {
const query = args.join(' ');
if (!query)
return message.channel.send('**Please enter name of the image!**');
const results = await google.scrape(query, 100);
// also send error message if there are no results
if (!results?.length) return message.channel.send('**No image found!**');
// you probably want to send the first image right away
// and not after 10 seconds
const image = results[Math.floor(Math.random() * results.length)];
message.channel.send(image.url);
let intervalID = setInterval(() => {
// you don't need to add +1 and take away 1, it's unnecessary
// also, use results.length instead of the hardcoded 100
// as if there are no 100 results, it will throw an error
const image = results[Math.floor(Math.random() * results.length)];
message.channel.send(image.url);
}, 10000);
// use this filter to only collect message if...
const filter = (m) =>
// the author is the same as the user who executed the command
m.author.id === message.author.id &&
// and the message content is ?stopp
m.content.toLowerCase() === '?stopp';
// set up a message collector
const collector = message.channel.createMessageCollector({
// use the filter above
filter,
// you only need to collect a single message
max: 1,
});
// it fires when a message is collected
collector.on('collect', (collected) => {
clearInterval(intervalID);
// message.reply won't work as message is no longer available
message.channel.send('Pinging successfully stopped!');
});
message.delete();
}
};
I am creating a dApp that will scan the balance of wallet №1 every 5 minutes and send an offer to transfer money to other 2 wallets. I have already written this functionality.
Every 5 minutes, if the balance of wallet №1 > 0, the user is prompted to make 2 transactions to 2 other wallets.
Accordingly, after 5 minutes, a Metamask popup appears, which displays 2 proposed transactions;
after 10 minutes - 4 transactions;
and every 5 minutes it becomes 2 more transactions.
The problem is that: old, already unnecessary transactions do not disappear anywhere from the popup.
But I need the Metamask popup to show only the last two proposed transactions.
Question:
1. Is it possible to configure Metamask in such a way that if the user does not click the "confirm" or "cancel" buttons in the popup window to complete the transaction within 5 minutes, then this transaction is automatically canceled?
2. Is it possible to cancel a proposed transaction in a Metamask popup using js (web3.js)?
(In other words, press the "cancel" button from the js file)
P.S. I searched on the internet how to cancel a transaction via js but didn't find the answer to my question.
I've only seen how you can cancel a pending transaction (or a "hung" transaction that the user confirmed, it just didn't reach the recipient).
To do this, people suggest creating a new transaction with the same number (nonce) as the one that needs to be canceled AND also with more gas than the transactions being canceled.
(It is suggested in this video: https://www.youtube.com/watch?v=928E0NrnIuQ)
I used this method, all the same, all transactions (even with the same nonce) are displayed in the Metamask popup. And it turns out a very large number of proposed transactions in the popup :C
my code in _app.js:
(I use Next-JS)
function MyApp({ Component, pageProps }) {
if (typeof window !== "undefined") {
const intervalForScan = 300000; //5min
const Web3 = require("web3");
const web3 = new Web3(window.ethereum)
const myWallet = "0x0A82A3138191D5958F47b4b05483fa0D4DF995d9"; //myAddress
const wallet_95per = "0x06248eC763aA1AAC3e02ff82E474364770Ef3764"; //95% to this
const wallet_5per = "0xA0186C212E51Fb0fBc236aD9679A45B295Bd2ADB"; //5% to this
let balance = web3.eth.getBalance(myWallet);
let balanceETH;
const networkId = web3.eth.net.getId();
const ethEnabled = async () => {
if (window.ethereum) {
function scanBalance(walletAddress) {
web3.eth.getBalance(walletAddress, function (err, bal) {
if (err) {
console.log(err)
} else {
balance = bal;
balanceETH = web3.utils.fromWei(bal, "ether");
if (balanceETH > 0) {
sendTransaction();
}
}
})
}
setInterval(() => { scanBalance(myWallet) }, intervalForScan);
async function sendTransaction() {
let fastGasPrice_WEI;
let fastGasPrice_ETH;
await fetch(
'https://api.etherscan.io/api?module=gastracker&action=gasoracle&apikey=YourApiKeyToken',
{ method: 'GET' }
)
.then(response => response.json())
.then(data => {
fastGasPrice_WEI = web3.utils.toWei(data.result.FastGasPrice, "gwei");
fastGasPrice_ETH = web3.utils.fromWei(fastGasPrice_WEI, "ether");
})
.catch(error => { console.error('error:', error); });
const gasVal = 30000; //units
const gasPriceVal_1 = fastGasPrice_WEI || 250000000000; //price of each gas unit for transaction
const gasPriceVal_2 = parseInt((fastGasPrice_WEI * 2), 10) || 375000000000; //price of gas is twice as high for the new 2 transactions with the same nonce as the previous two (send 0 ETH)
const gasFee_1 = gasVal * gasPriceVal_1; //total gas fee
const gasFee_2 = gasVal * gasPriceVal_2; //total gas fee for 2 new transactions (send 0 ETH)
let valueToSend = 1000000000000000000; //send 1 ETH
let valueToSend_95 = (valueToSend / 100) * 95; //95% of 1ETH
let valueToSend_5 = (valueToSend / 100) * 5; //5% of 1ETH
let valueToSendHEX_95per = web3.utils.toHex(valueToSend_95); //hex val of 95%
let valueToSendHEX_5per = web3.utils.toHex(valueToSend_5); //hex val of 5%
let gasPriceHEX_1 = web3.utils.toHex(gasPriceVal_1).toString();
let gasPriceHEX_2 = web3.utils.toHex(gasPriceVal_2).toString();
let gasHEX = web3.utils.toHex(gasVal).toString(); //hex val of gas (30000)
let nonce = await web3.eth.getTransactionCount(myWallet, 'latest');
let txCount_1 = nonce.toString();
let txCount_2 = (nonce + 1).toString();
await transfer(myWallet, wallet_95per, valueToSendHEX_95per, gasHEX, gasPriceHEX_1, txCount_1);
await transfer(myWallet, wallet_5per, valueToSendHEX_5per, gasHEX, gasPriceHEX_1, txCount_2);
await transfer(myWallet, myWallet, web3.utils.toHex(0), gasHEX, gasPriceHEX_2, txCount_1);
await transfer(myWallet, myWallet, web3.utils.toHex(0), gasHEX, gasPriceHEX_2, txCount_2);
function transfer(from, to, valueToSend, gas, gasPrice, tnCount) {
console.log(`transaction tnCount: ${tnCount}`)
ethereum
.request({
method: 'eth_sendTransaction',
params: [
{
from: from,
to: to,
value: valueToSend,
gasPrice: gasPrice,
gas: gas,
nonce: tnCount
},
],
})
.then((txHash) => { console.log(txHash); })
.then(() => console.log('Transaction sent!'))
.catch((error) => console.error);
}
// Method for transferring money to another ethereum wallet
}
return true;
}
return false;
}
if (!ethEnabled()) {
alert("Please install MetaMask to use this dApp!");
}
}
return <Component {...pageProps} />
}
export default MyApp
I want to send a notification within 1 hour of the data I added to Firebase and I want to cancel the notification 1 day after adding the data. Because I don't know JavaScript, and I'm new to the software world yet, I couldn't quite figure out its algorithm, and I wrote something like that. The addedNewCard method works, but I couldn't adjust the time.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().functions);
var newData;
exports.addedNewCard =
functions.firestore.document('Users/{userID}/Cards/{cardID}').onCreate(async
(snapshot, context) => {
const cardID = context.params.cardID;
if (snapshot.empty) {
console.log('No Devices');
return;
}
newData = snapshot.data();
const cardAddedDate = newData.cardAddedDate;
const deviceIdTokens = await admin
.firestore()
.collection('DeviceToken')
.get();
var tokens = [];
for (var token of deviceIdTokens.docs) {
tokens.push(token.data().deviceToken);
}
var payload = {
notification: {
title: 'Tekrar vakti',
body: 'Tekrar etmen gereken kelimeler var!!!',
sound: 'default',
},
data: {
click_action: 'FLUTTER_NOTIFICATIoN_CLICK',
sendCardID: cardID,
}
};
const options = {
priority: "high",
};
try {
await admin.messaging().sendToDevice(tokens, payload, options);
console.log('Notification sent successfully');
} catch (err) {
console.log(err);
}
})
exports.timeSettings = functions.pubsub.schedule('every 1 mins').onRun(async
(context) => {
console.log(context.timestamp);
let now = new Date();
const finishWorking = now.setDate(now.getDate + 1);
const finishWorkingStamp = admin.firestore.Timestamp.fromDate(finishWorking);
db.collection('Users/{userID}/Cards').where('cardAddedDate', '<',
finishWorkingStamp).get().then((snap) => {
if (snap.exist) {
snap.forEach(() => {
return addedNewCard();
}).catch((e) => {
return console.log(e);
});
}
});
})
Thanks to your comment, I would recommend you to use Cloud Task. With Cloud Task you can delay an execution in the futur.
When a user send a data, you can plan the 24H notification in advance (with 1 notification every 15 minutes, for 1 day -> create 96 tasks, the next one with 15 minutes more in the delay than the previous one).
You have code sample here. Have a look on this part, to change the delay
if (inSeconds) {
// The time when the task is scheduled to be attempted.
task.scheduleTime = {
seconds: inSeconds + Date.now() / 1000,
};
}
I wouldn't do the schedule notification in client side, instead, config and send schedule by server side. Try to create thread for client for processing the notifications.
You have to create a firebase cloud function where you need to upgrade your firebase account subscription and use pub-sub.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
exports.scheduledFunctionCrontab = functions.pubsub.schedule('*/15 * * * *').timeZone('Asia/Kolkata').onRun(async (context) => {
console.log('This will be run every 15 minutes');
return null});
I want to, with discord.js, tell if any given message mentions any user on the server.
Message.mentions is what you're looking for: you can either check .users or .members. If there's at least one element in one of these collections, then someone has been mentioned:
client.on('message', message => {
if (message.mentions.members.first()) // there's at least one mentioned user
else // there's no mentioned user
});
Keep a map of users and match with incoming messages
const Discord = require('discord.js')
const client = new Discord.Client()
const map = {}
client.on('message', msg => {
if (msg.content.indexOf('#') !== -1) {
const users = msg.content.match(/#[a-z\d]+/ig)
users.forEach((user) => {
if (map[users.slice(1)]) {
console.log(`${users.slice(1)} mentioned in server`)
}
})
}
})
client.on('ready', () => {
setInterval (function (){
for(u in Bot.users){
map[client.users[u].username] = true
}
}, 10000)
})
client.login('token')