Appending messages to chatbox - javascript

So I am currently busy with a chatbox, which stores all the messages in Firebase and immediately appends the message into the messagebox. The problem is that Firebase obtains messages in a certain order, I append to the chatbox in a certain way, and when I refresh it, it is different again.
function init_chatbox() {
$('.empty-chat').show();
ref.child("/chatboxes/{{ chatbox.pk }}/messages/").on("child_added", function (snapshot) {
$('.empty-chat').hide();
var object = snapshot.val();
var key = snapshot.key();
var name = "";
ref.child("/users/" + object['user_id'] + "/name").once('value', function(snapshot) {
name = snapshot.val();
var timestamp = object['timestamp'];
var message = object['message'];
extra_html = ' (remove)(edit)';
$('ul.chat-messages').append('<li class="' + key +'"><p class="author"><span>' + name + '</span><span></span><span class="time" data-livestamp="' + timestamp + '"></span>' + extra_html + '</p><p class="message">' + message + '</p></li>');
});
});
When I have the code like this (with .append), it shows the code like this immediately:
Kevin 2 hours ago (remove)(edit)
some message here
John 10 minutes ago (remove)(edit)
test
John 2 minutes ago (remove)(edit)
msg
Kevin few seconds ago (remove)(edit)
test
And when i refresh the page, the message will be at the top, like the order should be.
Here is the thing, I also tried to .prepend, but it just gives another other, which isn't right either (posts the new message on top, but when refreshing it displays the good order, but then the lastest message on top).
I hope someone can help me with this, have been trying to solve this

You should take a look at orderByChild() in the firebase docs.
Basically you can order the messages by their timestamp. Like this:
ref.child("/chatboxes/{{ chatbox.pk }}/messages/")
.orderByChild("timestamp")
.on("child_added", function (snap) { ... });
Messages with the lowest timestamp will come first.

Related

How to get data from a especif Time period in InfluxDB Node-RED

I'm trying to read data from InfluxDB v1.x but only in a especific time period, Example:
Get the values of temper01 from 2021-11-09/14:00:00 to 2021-11-10/14:00:00.
i know its possible with this code :
"WHERE time >= '2021-11-09T14:00:00Z' AND time <= '2021-11-10T14:00:00Z'" But i wanna know if it is possible to use a variable instead of the direct timestamp.
i've tried this code :
msg.query = "SELECT time , Temper01, Temper02, Temper03 FROM "
+ global.get("ID_device1")
+ " WHERE time >= "
+ msg.payload;
+ " AND time <= '2021-11-18T00:00:00.000Z' " ;
but dosent matter the msg.payload value, it just get data from the very first data stored, and when i try to single quote msg.payload i get the invalid operation: time and *influxql.VarRef are not compatible" error message.
Assuming that msg.paylaod looks something like 2020-11-18T00:00:00.000Z then you have not quoted the time string you are inserting into the query.
It should look like this:
msg.query = "SELECT time , Temper01, Temper02, Temper03 FROM "
+ global.get("ID_device1")
+ " WHERE time >= '"
+ msg.payload;
+ "' AND time <= '2021-11-18T00:00:00.000Z' " ;
The difference is the ' at the end of the string on the 3rd line and again at the start of the string on the last line.
Directly comparing a working version with the version that didn't work would have shown this.

How to create an “unlimited” number of independent timers as individual list items in an unordered list with Javascript or jQuery?

I am trying to write a function which when executed (e.g. user clicks a button or image) creates and displays a new timer as a new list item in an unordered list (jQuery Sortable list). It doesn’t need to be super accurate so SetInterval should work fine. It doesn’t need any stops or resets. I would like the user to be able to create as many new independent (count-up) timers (as list items) in the list as they want, theoretically (although in reality there will likely be less than 10-15 on the go at the same time).
The following code does achieve this (or at least does the first time it is run). Subsequent clicks cause grief as I suspect that the same id is being used more than once for both “minutes” and “seconds” causing a conflict between list items.
function listTimer() {
var sec = 0;
function pad ( val ) { return val > 9 ? val : "0" + val; }
setInterval (function(){
document.getElementById("seconds").innerHTML=pad(++sec%60);
document.getElementById("minutes").innerHTML=pad(parseInt(sec/60,10));
}, 1000);
$(document).ready(function(){
$("#sortable1").append('<li class="ui-state-default">' + '<span id="minutes">' + '' + '</span>' + ':' + '<span id="seconds">' + '' + '</span>' + '</li>');
});
}
To allow multiple timers I then figured that each time the function is executed, the values should increment so they are seen as separate. As such I tried
Var i = 0;
function listTimer() {
var sec = 0;
function pad ( val ) { return val > 9 ? val : "0" + val; }
setInterval (function(){
document.getElementById("seconds"+i).innerHTML=pad(++sec%60);
document.getElementById("minutes"+i).innerHTML=pad(parseInt(sec/60,10));
}, 1000);
$(document).ready(function(){
$("#sortable1").append('<li class="ui-state-default">' + '<span id="minutes"+i>' + '' + '</span>' + ':' + '<span id="seconds"+i>' + '' + '</span>' + '</li>');
i=++;
});
}
The “seconds” + i ( and “minutes” =i ) in the .innerHTML works because if I leave var i=0 and then hard code “seconds0” and “minutes0” (instead of “seconds”+i etc) in the span id, a timer is generated as planned (once). The trick is that the “seconds” + i (and “minutes” =i ) in the span id do not work as I imagined. If I leave it as per the code above (e.g. in both the .innerHTML and span id) no list item is generated. I suspect the problem is in incrementing the span id.
Addressing the “span id=” to increment it (multiple ways) does not seem to have helped.
I have tried declaring and inserting a variable with no luck:
var newSeconds= “seconds” +i;
var newMinutes= “seconds” +i;
$(document).ready(function(){
$("#sortable1").append('<li class="ui-state-default">' + '<span id=newMinutes >' + '' + '</span>' + ':' + '<span id=newSeconds>' + '' + '</span>' + '</li>');
I have tried changing the id of the span just prior to the append with either:
document.getElementById("seconds").setAttribute("id", "seconds" +i);
document.getElementById("minutes").setAttribute("id", "minutes" + i);
or
document.getElementById("seconds").id("seconds" +i);
document.getElementById("minutes").id ("minutes" + i);
or
var newSeconds= “seconds” +i;
var newMinutes= “seconds” +i;
document.getElementById("seconds").setAttribute("id", newSeconds);
document.getElementById("minutes").setAttribute("id", newMinutes);
or by combinations of these e.g putting quotation marks around the newSeconds/newMinutes in both the .id and .setAttribute.
but I can’t seem to make the append method work and create a new independent list timer each time the trigger is clicked. The timers jump all over the place (or not at all) when the function is executed multiple times.
I have tried searching for javascript or jQuery ways of doing this but I can only seem to see previous questions that revolve around a certain number of timers (and hard coding them e.g. timer1, timer2 etc) rather than an "unlimited" number of timers. I have looked at books on Javascript and jQuery but can't seem to nut out the solution.
I am hoping I have given a minimal reproducible example. I am obviously missing fundamental issues but am unconscious incompetent at the moment. Is anyone happy to show me the error of my ways and help me get the function working?
I think that the issue stems from your referring to the timers by their Id attributes - an Id attribute is supposed to appear once per page, so having it appear in each timer will definitely cause some confusion.
I would recommend a different structure as well for organization. Here are my thoughts in pseudocode (leaving the implementation up to you)
const $timerContainerDiv = $("…"); // the place where timers live
var timers = []; // this is an array containing all of your timers
// function to add a new timer to the array
var addTimer = function(int minutes, int seconds, int title) {
// html that defines the actual structure of the timer,
// including elements for hours and minutes, each identifiable
// by a js class, and each one including a data attribute giving its value
// for example:
var $timer = $("<div class='timer' data-minutes='" + minutes + "' data-seconds='" + seconds + "' title='" + title + "'>");
timers.push(timer);
}
// now define a timer function to update all timers once per second
var updateTimers = function() {
// update each timer, decrementing one second
$.each(timers, function(index, val) {
var $timer = $(val);
var minutes = $timer.data("minutes");
var seconds = $timer.data("seconds");
var title = $timer.attr("title");
seconds--;
// need logic for when seconds goes negative to reset to 59 and decrement minutes
// need logic for when timer done, etc
$timer.empty();
$timer.append("<span>" + title + ": " + minutes + ":" + seconds + " remaining</span>");
});
setTimeout(updateTimers,1000); // call itself
}
updateTimers(); // get the whole thing started

How do I define the guild I want to use ".channels" on?

I'm setting up a bot analytics staff channel for my discord bot's support server.
I want the bot to be able to send a message and then edit it every minute. But, when I try to do this, I get an error saying that I didn't define the guild that I want to use .channels on. The exact error is TypeError: Cannot read property 'channels' of undefined.
I've tried defining my guild in many different ways, including making it into a variable and combining it with the function that finds the guild itself. But, none of these possible solutions, some of which coming from some related Stack Overflow posts, have worked for me.
I've also tried looking at some tutorials, but they also haven't worked either: the code always says that I'm not defining my guild correctly.
//Stats For Staff Channel
let serverchannel = bot.guilds.get("498548588318556210").channels.get("528288924917825556");
let servercount = bots.guilds.size
let uptimetotalseconds = (bot.uptime / 1000);
let uptimehours = Math.floor(uptimetotalseconds / 360);
uptimeseconds %= 3600;
let uptimeminutes = Math.floor(uptimetotalseconds / 60);
let uptimeseconds = uptimetotalseconds % 60;
let uptime = "Total Uptime: \n \n Hours: " + uptimehours + "\n Minutes: " + uptimeminutes + "\n Seconds: " + uptimeseconds + "\n Total Uptime In Seconds: " + uptimetotalseconds + "\n Total Uptime In MiliSeconds " + bot.upTime + "\n \n \n \n"
let messagetobesent = "**Bot Status** \n \n" + "Server Count: **" + servercount + " guilds** \n \n" + "Total Uptime: \n Hours: **" + uptimehours + "** \n" + "Minutes: **" + uptimeminutes + "** \n \n **Thats it!These stats are updated every minute and more are being added soon. ** "
serverchannel.send(messagetobesent)
while (true) {
setTimeout(function() {
message.edit(messagetobesent)
}, 60000);
}
The bot should grab the support server and channel that I want the message to be sent to by using their ids. Then, it should send a message stored as a variable in that channel. Finally, every minute it will edit that message with the new value of the variable.
TypeError: Cannot read property 'channels' of undefined.
This error actually means that it couldn't find the guild you're trying to get, Are you sure that bot.guilds.get("498548588318556210") actually returns a guild? Maybe bot is defined by another Discord.js client inside your code that's not in the guild you're trying to get? Or is there actually a guild with that ID? Or is your client actually a member of that guild?

Email multiple rows to one recipient based on category values using Apps Script

What I'm looking for:
I am working on a spreadsheet that contains transaction information for multiple people. Some people have only 1 transaction (1 row of information) while others may have multiple transactions (rows of information). So far, the code I am working on is able send an email containing one transaction per email. However, I am having trouble figuring out how to group multiple transactions into one email. One of the column fields is "Email address" and ideally I could use that to group multiple rows. Pretty much a mail merge that can be grouped by a column in the spread sheet or by the identification of similar values.
What I've tried/what I'm thinking:
I already have the code sort by email address so that the transactions for each person are consecutive on the sheet. I am thinking that I need to loop with an if statement saying something along the lines "if the next value in the 'email address' field is the same as the previous one, add a new line in the body of the email." From what I have researched I might need to do a loop within a loop for the email body but I am not completely sure how to go about that. In the code I have, I have already somewhat formatted the email the way it should be.
Disclaimer:
I'm pretty new to coding so any help is appreciated. I am unsure how to loop in the body of the message and eventually exit once the script hits a different person. I'd like to do it all in GAS.
This is what I've got so far:
var SENT = "Y";
function sendEmails() {
//This section specifies the sheet and some definitions we are working with
var sheet =SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet1');
var lastrow = sheet.getLastRow();
var startrow = 4; // First row of data to process
var numrows = lastrow; // Number of rows to process
sheet.sort(14, true); //Sorts the sheet by email address
//This section formats the columns so they appear correctly in the html email below
var column = sheet.getRange("K4:L");
column.setNumberFormat("MM/dd/yyyy"); // Simple date format
var column = sheet.getRange("M:M");
column.setNumberFormat("#"); // Simple plain text format
var column = sheet.getRange("K4:L");
column.setNumberFormat("#"); // Simple plain text format
var column = sheet.getRange("E:E");
column.setNumberFormat("$0.00"); // Simple currency format
//This section specifies the actual data we will be working with
var datarange = sheet.getRange(startrow, 1, lastrow, 15)// Fetch the range of cells
var data = datarange.getValues();// Fetch values for each row in the Range.
//Defining column data
for (var i = 0; i < data.length; ++i) {
var col = data[i];
var firstname = col[2]; // Column starting at 0 from left to right
var expamount = col[4]; // Column starting at 0 from left to right
var subdate = col[10]; // Column starting at 0 from left to right
var trandate = col[11]; // Column starting at 0 from left to right
var reportname = col[12]; // Column starting at 0 from left to right
var emailaddress = col[13]; // Column starting at 0 from left to right
var emailsent = col[14]; //Column starting at 0 from left to right
var subject = "MinistryCard Expenses Over 90 Days"; // Subject for the email to be sent
var emailintro = // Introduction part of the email
'Hi ' + firstname + ', <br /><br />' +
'This is the introduction to the email.' +
'The related expenses are below: <br /><br/ >'
var emailtrans = //THIS IS WHERE I WOULD LIKE THE MAGIC TO HAPPEN... I THINK
'<strong>Report Name: </strong>' + reportname + ' <br /><strong>Transaction Date: </strong>' + trandate + ' <br /><strong>Transaction Amount: </strong>' + expamount +
' <br /><strong>Approval Date: </strong>' + subdate + '<br /><br />'
var emailend = // The endd of the email
'We apologize for any inconvenience this may cause. ' +
'Also, please contact us if you believe you are recieving this email in error. Thank you. <br /><br /><br />' +
'Enjoy your day, <br /><br />'
//The section below retrieves alias email address to send as
var me = Session.getActiveUser().getEmail();// Log the aliases for this Gmail account and send an email as the first one.
var aliases = GmailApp.getAliases(); // Gets the alias ministrycard#cru.org from account
Logger.log(aliases); // Logs the alias
// The section below sends the actual emails
if (emailaddress != "" && emailsent != SENT){
if (aliases.length > 0){ // Prevents sending duplicates
GmailApp.sendEmail(emailaddress, subject , emailintro + emailtrans + emailend, {'from': aliases[0],'replyto': 'ministrycard#cru.org', htmlBody : emailintro + emailtrans + emailend});
sheet.getRange(startrow + i, 15).setValue(SENT);
SpreadsheetApp.flush(); // Make sure the cell is updated right away in case the script is interrupted
}} else { }}}
There are multiple solutions. Here's mine:
declare a nextEmail variable before the main for loop begins.
var nextEmail = '';
at the beginning of the loop, set nextEmail to the email in the next row:
if (i < data.length - 1) {
nextEmail = data[i+1][13]//looks at next row, 13th column
} else {//catch if i is the maximum row
nextEmail = ''
}
add another conditional that checks that the next email address is not the same before sending the email
if (emailaddress != "" && emailsent != SENT && nextEmail != emailaddress){
You didn't seem to specify what parts of the message you want to change each time and what parts you want to stay the same, but I'm assuming you want to change only emailtrans.
now, instead of declaing a new variable emailtrans each time it loops around, declare emailtrans before the loop as an empty string...
var emailtrans = ''
...and instead append the big string to emailtrans
emailtrans +=
'<strong>Report Name: </strong>' + reportname + ' <br /><strong>Transaction Date: </strong>' + trandate + ' <br /><strong>Transaction Amount: </strong>' + expamount +
' <br /><strong>Approval Date: </strong>' + subdate + '<br /><br />';
and then set emailtrans to an empty string right after you send the email, inside of the if statement.
emailtrans = ''
There are a few things you didn't ask about but I still think should be addressed. You probably would have caught a lot of this if only you tested whatever you've got, even if it's incomplete, to make sure it did what you want before asking for help.
SENT variable should probably be declared within the function (good practice)
numrows is only equal to lastrow if startrow is zero. Set numrows to lastrow - startrow and use numrows instead of lastrow when you declare datarange.
You sort the whole sheet based on column M, but your script only collect data from row 4 onward. Depending on your labels in rows 1-3, the sorting might place some emails in rows 1-3 and your labels in row 4 and onward. Instead, I'm guessing you only want to sort the range containing the emails, so do a .sort() on that range instead of the whole sheet.
Putting in semicolons where you define emailintro, emailtrans, and emailend in multiple lines is probably a good idea.
I could be wrong, but it seems the html tags aren't formatted right. <br /><br /> wouldn't insert a break ... they would just end two breaks?
Declaring a variable inside a for loop is bad practice. Declare it outside the loop beforehand, and set it (thing = value), not declare it (var thing = value), inside the loop.
Hope this helps.

How to write arguments in function?

I have been using functions but I am not able to tackle this.
What I have done is created a function, then made this to use the values provided by the document class or ids and do the work. Once work is done then just give the data back! It worked!
Now I want to make this function happen for two divs, the first function works good. The issue is with the second one. The function is correct, their is some other bug while writing the result.
Here is my code:
function time_untilCom(id) {
var Time2 = Date.parse(document.getElementById("time_" + 2).value);
var curTime2 = new Date();
var timeToWrite2 = "";
var seconds2 = Math.floor((curTime2 - Time2) / (1000));
if (seconds2 > 0 && seconds2 < 60) {// seconds..
timeToWrite2 = seconds2 + " seconds ago";
$('#update_' + 2).html(seconds2);
$('#jstime_' + 2).html(timeToWrite2 + " <b>Time that was captured!</b>");
}
}
If I use it as it is, it works! The issue comes when I try to replace these
("time_" + 2), ("#update_" + 2), ("#jstime" + 2) with ("time_" + id), ("#update_" + id), ("#jstime_" + id).
What i want to happen is that the function would be provided with a common ID that is applied throughout the div and use that ID, to get the value of time, convert it to seconds, do other stuff and then provide me with the result in the corresponding element with the id that was in the argument.
function works great, it do provide me with the result. But the issue is with the id its not being sent I guess. Or if is being sent then not being applied. What might be the issue here? And don't mind the seconds i have that covered too.
I am really very sorry for short code:
Pardon me, I was about to write the code for the function too. But electricity ran out!
Here is the code: onload="time_untilCom('2'), this is the way I am executing this.
And once in the main code, it will be executed like this: onload="time_untilCom(#row.Id) because I am using ASP.NET Web Pages I will be using the server side code to write the ID from Database. And will then user the ID throughtout the div to update the time!
From what I understand, you probably want to replace the second line
var Time2 = Date.parse(document.getElementById("time_" + 2).value);
with
var Time2 = Date.parse(document.getElementById(id).value);
And at the end you can also use
$('#'+id).html(timeToWrite2 + " <b>Time that was captured!</b>");
You are passing "id" as an argument, but you never use it inside the function. My question is: In your example you are using 2 as appendix to id attributes. Is it the 2 (or other numbers respectively) that you want to have as the id parameter of the function?
Then you could just replace each + 2 in your code by + id
function time_untilCom(id) {
var Time2 = Date.parse(document.getElementById("time_" + id).value);
var curTime2 = new Date();
var timeToWrite2 = "";
var seconds2 = Math.floor((curTime2 - Time2) / (1000));
if (seconds2 > 0 && seconds2 < 60) {// seconds..
timeToWrite2 = seconds2 + " seconds ago";
$('#update_' + id).html(seconds2);
$('#jstime_' + id).html(timeToWrite2 + " <b>Time that was captured!</b>");
}
}
EDIT: Please tell us where and how exactly do you call time_untilCom? Did you pass the id there?

Categories

Resources