How to fetch messages until a specific date? - javascript

How would one fetch messages from text channel beginning with the latest/newest message until a specific date. For example until the date two days ago.
The desired result is having a function that will do the job and return an array of messages dating in a range: now -> end date specified as the function's argument.

This would be my approach, feel free to post your own better answers :3
async function fetchMessagesUntil(channel, endDate, lastID) {
let messages = (await channel.messages.fetch({ limit: 100, before: lastID })).array();
if (messages.length == 0) return messages;
for (let i = 0; i < messages.length; i++) {
if (messages[i].createdAt.getTime() < endDate.getTime()) {
return messages.slice(0, i);
}
}
return messages.concat(
await fetchMessagesUntil(channel, endDate, messages[messages.length - 1].id)
);
}
Example usage
let end = new Date();
end.setDate(end.getDate() - 2); // Subtract two days from now
(await fetchMessagesUntil(message.channel, end)).forEach(x => console.log(x.content));

Related

Is there a good way to loop through APIs?

There are too many loops in this API, which is causing it to run slowly.
this data is only for year i have to run same code for Day month week looping same code makes the code slower.
// ```GRAPH CALCULATION FOR YEAR```\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
let query = '';
graph_data = '';
let startOfYear = MOMENT().startOf('year');
let monthsForYear = [];
let year = [];
// Create an array of dates representing the start of each month in the year
for (let index = 0; index <= 11; index++) {
const add1Month = MOMENT(startOfYear)
.add(index, 'month')
.format('YYYY-MM-DDTHH:mm:SS.000+00:00');
monthsForYear.push(add1Month);
}
// Get the actual amount for each month
for (let i = 0; i < monthsForYear.length; i++) {
let j = i + 1;
let d = await PRISMA.orders.findMany({
where: {
created_at: {
gte: monthsForYear[i],
lte:
i === monthsForYear.length - 1 ? endOftheYear_date : monthsForYear[j],
},
},
select: { actual_amount: true },
});
// Calculate the total actual amount for the month
let total = 0;
d.forEach((el) => {
total += el.actual_amount;
});
year.push(total);
}
// Set the graph data to the calculated amounts
graphDataOfYear = year;
You execute asynchronous statements (using await) in a loop, which means they are executed consecutively: The second statement starts only after the first has completed. It may be faster if you execute them in parallel.
The first loop starts all statements in parallel, then all results are awaited together and the total actual amounts calculated in a second loop:
let statements = [];
for (let i = 0; i < monthsForYear.length; i++) {
let j = i + 1;
statements.push(PRISMA.orders.findMany({
where: {
created_at: {
gte: monthsForYear[i],
lte:
i === monthsForYear.length - 1 ? endOftheYear_date : monthsForYear[j],
},
},
select: { actual_amount: true },
}));
}
for (const d of await Promise.all(statements)) {
// Calculate the total actual amount for the month
let total = 0;
d.forEach((el) => {
total += el.actual_amount;
});
year.push(total);
}
The general pattern is:
let statements = [];
for (...) {
statements.push(/* asynchronous statement without await */);
}
for (const d of await Promise.all(statements)) {
/* d is the result of each asynchronous statement in turn */
}
It is very likely even faster if you can let PRISMA do the total actual amount calculation, because then only one number (the total actual amount) must be sent back to your Javascript code, whereas with your current code every order that exists in PRISMA is sent back for one of the months, and then again for one of the weeks, and for one of the days. But I am no PRISMA expert.

How to fetch more than 1000 bans of a discord server using discord.js?

I have a command serverinfo and it shows how many bans there are on the server. If the server has 1200 bans, the bot will show that the server has 1000 bans.
Is it possible to somehow make the bot shows how many bans really are?
In the documentation the following parameters are specified limit?| number | number of users to return (up to maximum 1000) | default 1000
How to change default 1000 to e.g. 1500?
I am using discord.js vers 13.8.0 and node.js version 16.15.1.
For a v13 discord.js solution to your problem, this is what I did.
I edited this to include the while loop.
let completeBanIdList = await (async (a = [], last = 0, limit = 1000) => {
while(limit === 1000){
let bans = await guild.bans.fetch({after: last, limit: limit});
let banlist = bans.map(user => user.user.id);
last = bans.last().user.id;
limit = banlist.length;
for(let i = 0; i < limit; i++){a.push(banlist[i]);}
}
return a;
})();
// below is just console logging to show that it worked properly...
let banIdObj = ((o = {}) => {
for(let i = 0; i < completeBanIdList.length; i++){
o[completeBanIdList[i]] = 1;
}
return o;
})();
console.log(`unique ban id count:`, completeBanIdList.length, `should equal number of id keys in object:`, Object.keys(banIdObj).length);
Although the max limit is 1000, you can use the limit and before options to fetch the previous bans, too. before is a snowflake, it tells Discord to consider only bans before this id.
So, if you have around 1200 bans, you can fetch the first 1000, grab the id of the last item in the returned collection and use its id as the before option in your second fetch:
let firstHalf = await guild.bans.fetch({ limit: 1000 });
let secondHalf = await guild.bans.fetch({ before: firstHalf.last().id, limit: 1000 });
You can also create a new function that fetches more than 1000 bans. There is an example below. This function returns a collection of GuildBans, so you can use the usual collection methods, like filter, each, etc.
async function fetchMoreBans(guild, limit = 2000) {
if (!guild || typeof guild !== 'object')
throw new Error(`Expected a guild, got "${typeof guild}"`);
if (!Number.isInteger(limit))
throw new Error(`Expected an integer, got ${typeof limit}.`);
if (limit <= 1000) return guild.bans.fetch({ limit });
let collection = new Collection();
let lastId = null;
let options = {};
let remaining = limit;
while (remaining > 0) {
options.limit = remaining > 1000 ? 1000 : remaining;
remaining = remaining > 1000 ? remaining - 1000 : 0;
if (lastId) options.before = lastId;
let bans = await guild.bans.fetch(options);
if (!bans.last()) break;
collection = collection.concat(bans);
lastId = bans.last().id;
}
return collection;
}
Example usage:
client.on('messageCreate', async (message) => {
if (message.author.bot) return;
let banList = await fetchMore(message.guild, 1500);
let filteredBanList = banList.filter(({ user }) =>
user.username.toLowerCase().startsWith('e'),
);
console.log(`There are ${banList.size} bans fetched`);
console.log(
`Number of bans where the user's username starts with the letter "e": ${filteredBanList.size}`,
);
});

Is there a way to select only certain fields from Firestore?

I am working on a performance issue of a function, that takes 15sec to response, which makes a request to firebase for all documents that are
"ErrorID" "==" "0"
The problem is that there are many documents and they are kind of very large objects, and I only need TWO FIELDS (Order and Amount) of each document, there are any way to request only those two fields that accomplish the condition?
Something like :
firestore.collection("purchases").where("ErrorID", "==", "0").get(Order, Amount);
The function that im talking about:
const totalEarn = async (req, res, next) => {
const DAY = 86400000;
const WEEK = 604800016;
const MONTH = 2629800000;
try {
let snap = await firestore.collection("purchases").where("ErrorID", "==", "0").get(); // CONDITION
let totalToday = 0;
let totalYesterday = 0;
let totalLastWeek = 0;
let totalLastMonth = 0;
let now = Date.now();
let Yesterday = now - 86400000;
await snap.forEach((doc) => { // THIS FOR EACH TAKES TOO MUCH TIME
let info = doc.data();
let time = info.Order.split("-")[2]; // FIRESTORE FIELD -> ORDER
let amount = info.AmountEur * 1; // FIRESTORE FIELD -> AMOUNT
if (time > now - DAY) {
totalToday = totalToday + amount;
}
if (time < now - DAY && time > Yesterday - DAY) {
totalYesterday = totalYesterday + amount;
}
if (time > now - WEEK) {
totalLastWeek = totalLastWeek + amount;
}
if (time > now - MONTH) {
totalLastMonth = totalLastMonth + amount;
}
});
res.send({
status: true,
data: {
totalToday: totalToday.toFixed(2),
totalYesterday: totalYesterday.toFixed(2),
totalLastWeek: totalLastWeek.toFixed(2),
totalLastMonth: totalLastMonth.toFixed(2),
},
});
} catch (error) {
res.status(410).send({
status: false,
error: "some error occured counting the numbers",
e: error.message,
});
}
};
The document im talking about
If you use Firestore Node.JS client or Firebase Admin SDK, then you can use select() to select fields:
import { Firestore } from "#google-cloud/firestore";
const firestore = new Firestore();
const snap = firestore
.collection("purchases")
.where("ErrorID", "==", "0")
.select("Order", "Amount")
.get();

in JavaScript, why value is defined inside scope. but undefined outside scope?

I'm trying to calculate the amount of sign-ups of each day of the current month, and send that data
to the Statistics page, where I display that data in a chart using Chart.js.
the data initialized well insides the query's scope, however I "lose" it outside of the scope.
EDIT - I extended my code so maybe there's some information relevant to my problem.
Here is my code:
async function getSignUps() {
const query = { createAt: { $gt: date.getFirstDateOfMonth(), $lt: new Date() } };
const projection = { createAt: 1, _id: 0 }; //can be added to find()
var signUps = new Array(date.getDaysInMonth()).fill(0); //create empty array of days in current month
Contractor_Users_Collection.find(query).project(projection).toArray(function (err, result) {
if (err) throw err;
// manipulte data to create array that the index indicates the day of month
// the value indicates the amount of signups per that day of the month
for (let i = 0, d = date.getFirstDateOfMonth(); i < result.length; i++, d.setDate(d.getDate() + 1)) {
nextDate = new Date(d.getDate() + 1);
if (d <= result[i]['createAt'] <= nextDate) {
day = result[i]['createAt'].getDate() - 1;
++signUps[day];
}
}
console.log('*****');
console.log('signUps inside find : ' + signUps);
console.log('*****');
})
console.log('*****');
console.log('signUps outside find : ' + signUps);
console.log('*****');
return signUps;
};
router.get("/statistics",async (req, res) => {
const signUps = await getSignUps();
console.log('*****');
console.log('signUps :' + signUps);
console.log('*****');
res.status(200).render("statistics", { signUps: signUps });
});
Here's the output :
*****
signUps outside find : 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
*****
*****
signUps :0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
*****
*****
signUps inside find : 1,3,3,5,4,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
*****
I found the solution, hope it will help to others...
async function getSignUps() {
const query = { createAt: { $gt: date.getFirstDateOfMonth(), $lt: new Date() } };
const projection = { createAt: 1, _id: 0 }; //can be added to find()
var signUps = new Array(date.getDaysInMonth()).fill(0); //create empty array of days in current month
try {
let result = Contractor_Users_Collection.find(query).project(projection)
result = await result.toArray()
// manipulte data to create array that the index indicates the day of month
// the value indicates the amount of signups per that day of the month
for (let i = 0, d = date.getFirstDateOfMonth(); i < result.length; i++, d.setDate(d.getDate() + 1)) {
nextDate = new Date(d.getDate() + 1);
if (d <= result[i]['createAt'] <= nextDate) {
day = result[i]['createAt'].getDate() - 1;
++signUps[day];
}
}
return signUps;
} catch (error) {
console.error(error)
throw error
}
};

How to aggregate arrays of days weekly in JS?

For example, take the time range from 05/10/2019 to 05/25/2019.
Dates on this interval need to be aggregated like this (2019 is omitted for brevity):
const result = [
[ '05-10', '05-11', '05-12'], // week 1
['05-13', '05-14', '05-15', '05-16', '05-17', '05-18', '05-19'], // week 2
['05-20', '05-21', '05-22', '05-23', '05-24', '05-25' ], // week 3
];
What is the best way to solve this problem with JS?
Is it possible to implement this by setting the beginning of the week on any day?
Will packages moment and moment-range help in this?
You can go through the dates, and if the day is 1 (Monday), create a new Array in your results:
const startDate = new Date('05-10-2019'),
endDate = new Date('05-25-2019'),
result = [];
function _twoDigits(x) {
return String(x).padStart(2, '0');
}
let tmp = startDate;
do {
if (tmp.getDay() === 1 || result.length === 0) {
// Create a week Array
result.push([]);
}
const str = `${_twoDigits(tmp.getMonth() + 1)}-${_twoDigits(tmp.getDate())}`;
// Add this date to the last week Array
result[result.length - 1].push(str);
// Add 24 hours
tmp = new Date(tmp.getTime() + 86400000);
} while (tmp.getTime() <= endDate.getTime());
console.log(result);
Note: MomentJS may help, but it's a big library. If you only need to do 2 or 3 basic things with dates, I would recommend not using it. If you need to do a lot of work with dates, then yes, it's a powerful library that will save you a lot of time.
Here is one possible implementation if you are interested in moment.js code.
But as blex said it's a large lib.
const start = moment('2019-05-10');
const end = moment('2019-05-25');
const array = [[]];
const from_date = moment(start).startOf('isoWeek');
const to_date = moment(end).endOf('isoWeek');
let j = 0;
let added = 0;
for (let currentDate = moment(from_date); currentDate <= to_date; currentDate.add(1, 'day')) {
if (added === 7) {
array.push([]);
j++;
added = 0;
}
if (currentDate.isBetween(start, end, null, '[]')) {
array[j].push(currentDate.format('MM-DD'));
}
else {
array[j].push('');
}
added++;
}
document.getElementById('output').innerText = JSON.stringify(array);
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.24.0/moment.min.js"></script>
<p id="output"></p>

Categories

Resources