This code ends up spamming messages for one second every time it runs. Does anyone know to prevent that? the variable count is the amount of messages sent in the last hour, and my goal is if more then 15 were sent in the past hour then to send a message. Here is the code I tried:
var count = await keyv.get('messagecount')
const sendtochan = await rotate.guild.channels.cache.get('760626595332358205');
cron.schedule('0 0 * * * *', async () => {
if (count > 14 && !rotate.author.bot) {
keyv.set('messagecount', 0)
const sendtochan = await rotate.guild.channels.cache.get('760626595332358205');
sendtochan.send(`This channel's current topic is: **${await keyv.get('topic')}**`);
return
}
if (count < 14 && !rotate.author.bot) {
keyv.set('messagecount', 0)
return
}
});
Related
The fluent-ffmpeg package has a function that will create screenshots at even intervals in a video for a given count. The problem with this is that it often spawns a ENAMETOOLONG error on Windows.
To get around this, I copied the relevant parts of the source code that I needed and instead of running it all as one long command, split it up into n many commands (where n is the amount of screenshots).
This seems to work at first. It (sometimes) gets the first screenshot, but then it throws the same ENAMETOOLONG error. How can I get around this?
Here is what I have attmpted:
const count = 50 // just an example
const dest = path.join(os.tmpdir(), `screenshots_${Date.now()}`)
const interval = 100 / (1 + count)
let timemarks = Array(count)
.fill(0).map((_, i) => (interval * (i + 1)) + '%')
.map(mark => {
if (('' + mark).match(/^([\d.]+)%$/)) {
// #ts-ignore
return videoDuration * parseFloat(mark) / 100
} else {
return mark
}
})
.map(mark => {
if (typeof mark === 'number') {
return mark
}
if (mark.indexOf(':') === -1 && mark.indexOf('.') >= 0) {
return Number(mark)
}
const parts = mark.split(':')
let secs = Number(parts.pop())
if (parts.length) {
secs += Number(parts.pop()) * 60
}
if (parts.length) {
secs += Number(parts.pop()) * 3600
}
return secs
})
.sort((a, b) => a - b)
// my first attempt:
for (const mark of timemarks) {
this.video
.screenshot({
count: 1,
folder: dest,
timemarks: [mark],
})
}
// second attempt:
let i = 0
for (const mark of timemarks) {
const filename = `frame_${i.toString().padStart(count.toString().length, '0')}.jpg`
this.video
.setStartTime(mark)
.output(path.join(dest, filename))
.frames(1)
.run()
i++
}
For context, this.video is just ffmpeg(videoFilePath).
My first attempt, as I previously stated, just throws the same error and doesn't really get anywhere. Sometimes I get the first screenshot, most times I do not.
The second attempt doesn't even do anything. It seems to take it's time doing something, but I don't actually get any screenshots. If they saved anywhere, I don't know where.
Is anyone able to help me with this?
I want to write a system that automates max. 8192 different values consistently 40 times per second. The kind of automation, I want to do is e.g. letting some values form a sinus from 0 to 255, others ramp up from 50 to 80, others switch between 255 and 0 etc.
I need to know how long each execution takes and if the execution takes longer than allowed it should "drop a frame".
my first approach would be
// simplified code
const fps = 40
const executionStats : number[] = []
executionStats.length = fps * 10
const startFrame = new Date()
type SingleValue = {current: number, effectType: string, effectStarted: number}
const values : SingleValue[] = [...] // up to 8192 entries
// this is calculating the target value based on some lookups in other objects
// and based on when the effect started and in what tick we are in right now
const runSingleEffect = (value: SingleValue, tick: number) : number => {...}
const handler = async (tick: number) => {
values.forEach(value => {value.current = runSingleEffect(value, tick)})
}
setInterval(async () => {
const start = new Date()
await handler((start - startFrame) / fps);
const end = new Date()
executionStats.push(end - start)
executionStats.unshift()
}, 1000 / fps)
is there any better approach than using a simple setInterval? (obviously I could use a ring buffer for the execution stats) but is there a better way to make sure that only one handler runs at a time? Is there maybe a library for that kind of stuff?
I would use setTimeout with continuous resubscription. You would recalculate the next tick on every "loop" end.
function defer (ms) {
const frameBudgetMs = 1000 / fps;
setTimeout(async () => {
const start = Date.now();
try {
await handler((start - startFrame) / fps);
} finally {
const end = Date.now();
const ms = end - start;
executionStats.push(ms);
executionStats.unshift();
// a) In case we are over our frame, start next defer immediately.
let nextTickMs = Math.max(0, frameBudgetMs - ms);
defer(nextTickMs);
// b) In case we want to execute at specific frame interval, but just dropping the frames
let nextTickMs = frameBudget - ms % frameBudget;
defer(nextTickMs);
}
}, ms)
}
// start our loop
defer(0);
As you can see, you are completely flexible in resubscription scenarios, and no parallel runs are possible in such a "loop".
I am using node-cron to send operational emails (currently searching for new emails that need to be sent every minute).
My function (operationalEmails) looks for mongodb records that have a sent flag = false, sends the email, then changes the sent flag = true.
If an iteration of the cron has a large payload of records to send, it could take more than a minute.
How do I ensure the last iteration of the cron is complete before starting a new one?
//Run every Min
cron.schedule("* * * * *", () => {
operationalEmails();
});
you would need to create a simple lock
const running = false;
function operationalEmails() {
if (running) {
return
}
running = true;
// do stuff
running = false;
}
//Run every Min
cron.schedule("* * * * *", () => {
operationalEmails();
});
I have a function (called rankCheck), which takes three parameters:
Guild object (aka a Discord server)
UserId
Callback Function
The function will fetch the last 500 messages from every text channel in the provided guild. It will then will then only keep any messages that start with "!rank" and were sent by the provided UserId. Finally, it will count the remaining messages and pass the integer to the callback function.
async function rankChecks(guild, userId = *REMOVED FOR PRIVACY*, callback){
sumOfRankChecks = 0;
guild.channels.cache.each(channel => { //for each text channel, get # of rank checks for userId in last 500 msgs.
if (channel.type === "text"){
fetchMessages(channel, 500).then(msgs => {
let filteredMsgs = msgs.filter(msg => msg.content.startsWith("!rank") && msg.member.user.id == userId);
sumOfRankChecks = sumOfRankChecks + filteredMsgs.length;
});
}
});
callback(sumOfRankChecks);
}
Since discord only allows fetching 100 messages at once, I use this function (fetchMessages) to bypass this limit, by sending multiple requests, and then combining the results into one.
async function fetchMessages(channel, limit) {
const sum_messages = [];
let last_id;
while (true) {
const options = { limit: 100 };
if (last_id) {
options.before = last_id;
}
const messages = await channel.messages.fetch(options);
sum_messages.push(...messages.array());
last_id = messages.last().id;
if (messages.size != 100 || sum_messages >= limit) {
break;
}
}
return sum_messages;
}
When I call the rankCheck function, the return value is always 0
rankChecks(msg.guild, *REMOVED FOR PRIVACY*, function(int){
console.log(int);
});
Output:
0
However when I add a console.log into my rankCheck function:
async function rankChecks(guild, userId = *REMOVED FOR PRIVACY*, callback){
sumOfRankChecks = 0;
guild.channels.cache.each(channel => { //for each text channel, get # of rank checks for userId in last 500 msgs.
if (channel.type === "text"){
fetchMessages(channel, 500).then(msgs => {
let filteredMsgs = msgs.filter(msg => msg.content.startsWith("!rank") && msg.member.user.id == userId);
sumOfRankChecks = sumOfRankChecks + filteredMsgs.length;
console.log(sumOfRankChecks) //NEW CONSOLE.LOG!!!!!!!!!!!!!!!
});
}
});
callback(sumOfRankChecks);
}
Output:
3
5
This is the output I was expecting. Since I have 2 text channels in my server, I got 2 logs. If you had 3 channels, you would get 3 logs, etc. 3 messages from channel #1, and 2 messages from channel #2, therefore in total, there are 5 messages.
5 should be the integer that is passed into the callback function, but 0 is passed instead. Why is this?
Your callback function is being called before you even change sumOfRankChecks. Collection#each (and Map#forEach() and the gang) cannot wait for Promises to resolve because of the way they're built. Your code also wouldn't wait anyway, because you're not using await.
Despite what one might think is happening, guild.channels.each() is called, and callback() is called immediately after. This is the source of your confusion.
For more about async vs sync, you can check out the explanation in my answer here. You must use a for loop and await, or refactor your code so that async/await syntax is not necessary.
NOTE: The Discord.js documentation hyperlinked is for recently released v12. If your Discord.js isn't up to date, switch to the correct version at the top of the page for accurate info.
I have divided a day on 6 parts and each part of a day has its own interval. My function must run in this intervals.
for example 12:00pm - 15:00pm interval is 10 min, so function must be called every 10 min, but 15:00pm - 20:00pm interval is 2 min so from 15:01pm interval must be changed from 10min to 2min.
Need a suggestion how to build this kind of timer.
I can get intervals from mongoDB or local json file.
I guess I have to check what time is it and get an interval (from mongoDB or json file) for that time, then pass it to setInterval() or Cron job scheduler.
Tryed this way but every time im passing new interval last intervals are still working: If interval is 2 min and im changing it to 5 min, function is called twice: every 2 min and every 5 min in both setInterval() and Cron
const test = (min) => {
console.log(min);
var intervale = setInterval(() => {
console.log('firing', min, new Date());
}, min);
}
const test = (min) => {
cron.schedule(`0 */${min} * * * *`, () => {
console.log('firing cron', new Date())
});
}
thank you
Can you do something like this? This uses the EventEmitter model and fires an event based on the custom Interval.
const eventemitter_cls = require("events").EventEmitter;
let eventemitter = new eventemitter_cls();
intervalName = "randomintervals"
let sumOfTwoNumbers = () => {
console.log(`hello world ${(new Date()).getMinutes()} ${(new Date()).getSeconds()}` );
// After you are done with your function, again you need to emit the event based on the time interval
setTimeout(() => {
eventemitter.emit(intervalName)
}, getTimeInterval());
}
let getTimeInterval = () => {
let currentTime = new Date();
// put your complicated interval schedule here
if(currentTime.getHours() >= 10 && currentTime.getHours() < 11){
return 5000;
}
else if (currentTime.getHours() > 12 && currentTime.getHours() < 15) {
return 2000;
}
else{
return 1000;
}
}
// give your function name here. the function will be called whenever someone emits intervalName
eventemitter.on(intervalName, sumOfTwoNumbers);
eventemitter.emit(intervalName);
I think It's still firing because there is no clearInterval telling it to stop.