I am working on creating a telegram bot, I want to make an anti-spam system, that is, when a person presses a button too many times, the bot will freeze for him for a certain number of seconds, it is possible to write a message about blocking. I just started learning JavaScript.
I use node-telegram-bot-api.
import {
bot
} from '../token.js';
import {
keyboardMain
} from '../keyboards/keyboardsMain.js';
export function commands() {
bot.on('message', msg => {
const text = msg.text;
const chatId = msg.chat.id;
if (text === '/start') {
return bot.sendMessage(chatId, 'hello', keyboardMain);
}
return bot.sendMessage(chatId, 'error');
});
}
You can create a user throttler using Javascript Map
/*
* #param {number} waitTime Seconds to wait
*/
function throttler(waitTime) {
const users = new Map()
return (chatId) => {
const now = parseInt(Date.now()/1000)
const hitTime = users.get(chatId)
if (hitTime) {
const diff = now - hitTime
if (diff < waitTime) {
return false
}
users.set(chatId, now)
return true
}
users.set(chatId, now)
return true
}
}
How to use: You'll get the user's chatId from telegram api. You can use that id as an identifier and stop the user for given specific time.
For instance I'm gonna stop the user for 10seconds once the user requests.
// global 10 second throttler
const throttle = throttler(10) // 10 seconds
// in your code
const allowReply = throttle(chatId) // chatId obtained from telegram
if (allowReply) {
// reply to user
} else {
// dont reply
}
Related
help please, I am writing a telegram bot for feedback, at the moment it works in a private chat (i.e. the user writes to the bot - I answer reply the bot via reply and the user receives SMS from the bot), but does not work in the group chat( in a group I can see the user's messages, but I can't reply to the user through reply). it is necessary that any user from the group can reply to the user's message
const bot = new Telegraf(token, {});
let replyText = {
'helloAdmin': '...',
'helloUser': '...',
'replyWrong': '....'
};
let isAdmin = userId => {
return userId === admin;
};
let forwardToAdmin = ctx => {
if (isAdmin(ctx.message.from.id)) {
ctx.reply(replyText.replyWrong);
} else {
ctx.forwardMessage(admin, ctx.from.id, ctx.message.id);
}
};
bot.start(ctx => {
ctx.reply(isAdmin(ctx.message.from.id)
? replyText.helloAdmin
: replyText.helloUser);
});
bot.on('message', ctx => {
if (ctx.message.reply_to_message && ctx.message.reply_to_message.forward_from && isAdmin(ctx.message.from.id)) {
ctx.telegram.sendCopy(ctx.message.reply_to_message.forward_from.id, ctx.message);
} else {
forwardToAdmin(ctx);
}
});
bot.launch();
there is a getChatAdministrators method (https://core.telegram.org/bots/api#getchatadministrators), I guess you can use it to get Admins of the group and check if one of them is replied. In that case forward it back to user
I am working on creating a telegram bot, I want to make an anti-spam system, that is, when a person presses a button too many times, the bot will freeze for him for a certain number of seconds, it is possible to write a message about blocking. People in other matters do not help me.
import {
bot
} from '../token.js';
import {
keyboardMain
} from '../keyboards/keyboardsMain.js';
export function commands() {
bot.on('message', msg => {
const text = msg.text;
const chatId = msg.chat.id;
if (text === '/start') {
return bot.sendMessage(chatId, 'hello', keyboardMain);
}
return bot.sendMessage(chatId, 'error');
});
}
Do you use telegraf as telegram module ? If yes, you can kick users from channels or groups.
here is a little piece that i made for you :)
you can apply it to anything ^_^
var tps = 0;
var allowpassage = false;
var detectspamon = document;
c = function() {
setTimeout(()=>{
console.log("got clicks:",tps);
if (tps==0) {console.log("no clicking detected");}
else if (tps==1) {console.log("success");allowpassage=true;}
else {console.log("too many clicks per second");}
tps=0;
console.log("setting clicks to 0");
},500);
}
detectspamon.onclick = () => {tps++;console.log(tps);c(tps);}
I am trying to make a bot that deletes a channel ('general') after 10 min after calling execcmd() function but if the msg is sent by someone having 'idofsomeuser' then it should cancel the previous timeout and start the timer again. But it's not working.
function execcmd(msg) {
let ID = msg.author.id
let chan = msg.guild.channels.cache.find(
(channel) => channel.name === 'general'
);
x = 'idofsomeuser'
if (ID == x) {
clearTimeout(x);
ID = setTimeout(() => {
chan.delete();
}, 600000); //10min
} else {
x = setTimeout(() => {
chan.delete();
}, 600000);
}
}
execcmd(msg); //msg is received by client and passed here
Create a closure function in which you store the timeout variable. Doing this enables you to store the timeout safely without having to create a global variable to store it in.
Inside the callback function first check if the id of the author matches. If it does, clear the timeout and set it to null. Then check if the timeout is equal to null and start the timer.
So now the timer will start when the function is called for the first time, and is restarted everytime the user with the id you're looking for is found.
function createClearChannel(channelName, authorId) {
let timeout = null;
const timeoutDuration = 1000 * 60 * 10;
return function (msg) {
const isAuthor = authorId === msg.author.id;
const channel = msg.guild.channels.cache.find(
channel => channel.name === channelName
);
if (isAuthor) {
clearTimeout(timeout);
timeout = null;
}
if (timeout === null) {
timeout = setTimeout(channel.delete, timoutDuration);
}
};
}
To set this up, first call the createClearChannel function and pass the arguments below. Store the result in execcmd and call that function when a message enters.
(You could always omit these parameters and simply hardcode the channel name and id of the user, but by doing this you make the function more flexible so it can be used for other cases as well.)
Important: The createClearChannel should only be called ONCE. So don't create a new execcmd when receives each message, but use the same one everytime.
const execcmd = createClearChannel('general', 'idofsomeuser');
execcmd(msg);
So I'm messing around with creating a discord bot that repeatedly pings a user until they respond/say anything in the chat (annoying, right?). The amount of times to ping the user and the time between each ping can also be adjusted if necessary. However, I can't seem to find a way to detect if the pinged user actually says something in the chat, and a way to stop the loop.
The actual pinging part of the code is in this for loop:
const ping = async () => {
for(var i = 1; i <= pingAmount; i++){
//the wait() command
await new Promise(r => setTimeout(r, pingTime * 1000));
//the actual ping
message.channel.send(`hey <#${userID}> let\'s play minecraft`);
}
//sends a message once pinging is finished
message.channel.send("Pinging Complete.");
};
I've tried nesting the following code inside that loop, but I get no results.
client.on('message', message =>{
if(message.author == taggedUser) {
message.channel.send('User has replied. Stopping pings.')
return;
}
});
Any help is appreciated!
full code below:
module.exports = {
name: 'Ping',
description: "Pings specified user until they appear",
execute(message, args, Discord){
//initialize variables
const client = new Discord.Client();
const taggedUser = message.mentions.users.first();
const userID = message.mentions.users.first().id;
//splits the command
const slicedString = message.content.split(' ');
//grabs specific numbers from command as input
const pingAmount = slicedString.slice(4,5);
const pingTime = slicedString.slice(5);
//display confirmation info in chat
message.channel.send(`So, ${message.author.username}, you want to annoy ${taggedUser.username}? Alright then lol`);
message.channel.send(`btw ${taggedUser.username}\'s user ID is ${userID} lmao`);
message.channel.send(`amount of times to ping: ${pingAmount}`);
message.channel.send(`time between pings: ${pingTime} seconds`);
//checks to make sure pingTime isnt too short
if(pingTime < 5){
if(pingTime == 1){
message.channel.send(`1 second is too short!`);
return;
} else {
message.channel.send(`${pingTime} seconds is too short!`);
return;
}
}
//timer and loop using pingAmount and pingTime as inputs
const ping = async () => {
for(var i = 1; i <= pingAmount; i++){
//the wait() command
await new Promise(r => setTimeout(r, pingTime * 1000));
//the actual ping
message.channel.send(`hey <#${userID}> let\'s play minecraft`);
const pingedUsers = [taggedUser];
// doodle message
const msg = {author: {id:1}};
// message event
const onMessage = (message) => {
if (pingedUsers.indexOf(message.author.id) != -1) {
console.log("user replied");
}
}
onMessage(msg); // nothing
pingedUsers.push(msg.author.id); // push the author id
onMessage(msg); // he replied!
}
//sends a message once pinging is finished
message.channel.send("Pinging Complete.");
};
//runs the ping function
ping();
}
}
You should be comparing the author's snowflake (id) in this case.
You can put the pinged users in a list and see if the message author is in that list.
const pingedUsers = [];
// doodle message
const msg = {author: {id:1}};
// message event
const onMessage = (message) => {
if (pingedUsers.indexOf(message.author.id) != -1) {
console.log("user replied");
}
}
onMessage(msg); // nothing
pingedUsers.push(msg.author.id); // push the author id
onMessage(msg); // he replied!
I'm trying to create a simple example of payments over the XRPL using Ripple-lib. The idea is to send several payments to different accounts stored in an array. I've made it kind of work in a different way as it is expected, but when using the 'then' method (as the docs recommend) does not work at all.
I'm a total newbie to Javascript so I don't have a good grasp on the language nor asyncronous coding and promises. When using the 'then' paradigm, the code stops working and no output can be seen in the console. This is the code I'm currently using. In the comments inside the 'SendXRP' function I explain the problem. How can this be re-arranged? Between the two ways, what is the proper one to code it?
'use strict';
const RippleAPI = require('ripple-lib').RippleAPI;
const sender = 'r*********************************';
const secret = 's****************************';
const destinations = ['r*********************************',
'r*********************************',
'r*********************************'];
const amount = 5;
// Instantiate Ripple API
const api = new RippleAPI({
server: "wss://s.altnet.rippletest.net:51233"
});
run();
async function sendXRP(amount, fee, destination, memo) {
// Update amount
amount = (amount - fee).toString();
// Build payment
const payment = {
source: {
address: sender,
maxAmount: {
value: amount,
currency: 'XRP'
}
},
destination: {
address: destination,
amount: {
value: amount,
currency: 'XRP'
}
},
memos: [
{
data: memo
}
]
};
// Build instuctions
const instructions = {
maxLedgerVersionOffset: 5
};
console.log('Sending ' + amount + ' to ' + destination);
// THIS KIND OF WORKS FOR NOW
// Prepare the payment
const preparedTX = await api.preparePayment(sender, payment, instructions);
// Sign the payment
const signedTX = api.sign(preparedTX.txJSON, secret);
// Submit the payment
const result = await api.submit(signedTX['signedTransaction']);
// Return TX hash on successful TX
if ('resultCode' in result && result['resultCode'] == 'tesSUCCESS') {
return signedTX.id;
} else {
return null;
}
// THIS IS MORE SIMILAR TO HOW IT IS DONE IN THE DOCS! NOT WORKING!
// ALSO, HOW DO I RETURN THE RESULT OF API.SIGN TO THE MAIN FUNCTION?
// Prepare the payment
// api.preparePayment(sender, payment, instructions).then(preparedTX => {
// // Sign the payment
// api.sign(preparedTX.txJSON, secret).then(signedTX => {
// // Submit the payment
// api.submit(signedTX['signedTransaction']);
// })
// }).catch(console.error);
}
function run() {
// Connect to Ripple server
api.connect().then(() => {
return api.getFee();
}).then(async fee => {
for (var i in destinations) {
var hash = await sendXRP(amount, Number(fee), destinations[i], 'memotext');
console.log(hash);
}
}).then(() => {
return api.disconnect();
}).catch(console.error);
}
Could it be that some of the transactions failed to send? If it failed, the result variable from sendXRP should have the txresult, but since you returned null if the result code is not tesSUCCESS, it doesn't return the result information.
const result = await api.submit(signedTX['signedTransaction']);
if ('resultCode' in result && result['resultCode'] == 'tesSUCCESS') {
return signedTX.id;
} else {
return null;
}
Before, when I tried submitting transactions consecutively, it would fail and return error code tefPAST_SEQ.
"The sequence number of the transaction is lower than the current sequence number of the account sending the transaction." from https://developers.ripple.com/tef-codes.html
I recommend removing the if('resultCode' in result...) block and check the transaction result. If the transactions failed with tefPAST_SEQ error, my solution to this is set the account sequence in instructions manually or add setTimeOut after each submit.