Hello everyone. I'm working on my twitch bot.
My goal is to be able to create racing tournaments on my channel.
For that i create a bot twich to help me record participants and their results from commands in chat.
In this case i'm using node-red-contrib-excelsheets to register results on local files.
First i get all twitch display-name (pseudo) and register them in my excel sheet. (this part is workin well)
Then with a command like this one : !€b2 (with b referring to the race performed and which will give us the correct column to write in. And 2 being the place where the spectator finished his race.)
My bot is supposed to check the excel file to find the pseudo of my viewer who write the command. Then in the same ligne write in the right collumn his place. And currently this is not working. So maybe i push it too far in my function. Can someone take a look pls ?
There is no Node-red erreur while i execute this function. It is just not writing in my excel or corrupt the excel file.
My second issue is the switch node. i want to use this expression to let message in this type going throw but right now it is not working... : /(!€[a-z][1-8])/
Thanks in advance.
So this is the code of the function :
let message = msg.payload.message;
// Vérification si le pseudo est vide
if (pseudo.trim() === "") {
// Continuer la vérification sans effectuer d'action spécifique
} else {
function assignPlace(message) {
let place = -1; // Valeur par défaut si aucun chiffre n'est trouvé dans le message
// Recherche des chiffres de 1 à 8 dans le message
for (let i = 1; i <= 8; i++) {
if (message.includes(i.toString())) {
place = i; // Si le chiffre est trouvé, la valeur est définie sur ce chiffre
break; // Sortir de la boucle une fois le chiffre trouvé
}
}
return place;
}
// Utilisation de la fonction pour assigner une place en fonction du message
let place = assignPlace(message);
// Nouvelle fonction pour assigner une colonne en fonction de la lettre
function assignColonne(lettre) {
let colonne = lettre.charCodeAt(0) - 95; // Convertit la lettre en code ASCII et soustrait 95 pour obtenir le numéro de colonne
return colonne;
}
// Utilisation de la fonction pour assigner une colonne en fonction de la lettre
let colonne = assignColonne(message.toLowerCase()); // Convertit la lettre en minuscule avant de l'assigner à la colonne
// Vérifier si msg.payload est un tableau
if (!Array.isArray(msg.payload)) {
// Si ce n'est pas le cas, le convertir en un tableau contenant l'élément actuel de msg.payload
msg.payload = [
{
header: {
col1: "Pseudos"
// ... ajoutez des en-têtes de colonnes vides jusqu'à la colonne correspondant à la lettre
// colX: "Lettre" par exemple
},
items: [],
sheetName: "InscriptionPlay"
}
];
}
// Recherche du pseudo dans la feuille de calcul et ajout de la place sur la même ligne
let foundPseudo = false;
for (let i = 0; i < msg.payload.length; i++) {
if (msg.payload[i].items[0] && msg.payload[i].items[0].col1 === pseudo) {
msg.payload[i].items[0]["col" + colonne] = place; // Assigne la place à la colonne correspondant à la lettre
foundPseudo = true;
break; // Sortir de la boucle après avoir trouvé le pseudo
}
}
// Si le pseudo n'a pas été trouvé, l'ajouter à la feuille de calcul
if (!foundPseudo) {
msg.payload.push({
header: {
col1: "Pseudos"
// ... ajoutez des en-têtes de colonnes vides jusqu'à la colonne correspondant à la lettre
// colX: "Lettre" par exemple
},
items: [
{
col1: pseudo,
// ... ajoutez des colonnes vides jusqu'à la colonne correspondant à la lettre "x"
["col" + colonne]: place
}
],
sheetName: "InscriptionPlay"
});
}
msg.filepath = "thecorrectfilepath.xlsx";
return msg;
}
type or paste code here
Sorry i share the wrong code. You will find the right one here :
let pseudo = msg.payload.userstate["display-name"];
let message = msg.payload.message;
// Vérification si le pseudo est vide
if (pseudo.trim() === "") {
// Continuer la vérification sans effectuer d'action spécifique
} else {
function assignPlace(message) {
let place = -1; // Valeur par défaut si aucun chiffre n'est trouvé dans le message
// Recherche des chiffres de 1 à 8 dans le message
for (let i = 1; i <= 8; i++) {
if (message.includes(i.toString())) {
place = i; // Si le chiffre est trouvé, la valeur est définie sur ce chiffre
break; // Sortir de la boucle une fois le chiffre trouvé
}
}
return place;
}
// Utilisation de la fonction pour assigner une place en fonction du message
let place = assignPlace(message);
// Nouvelle fonction pour assigner une colonne en fonction de la lettre
function assignColonne(lettre) {
let colonne = lettre.charCodeAt(0) - 95; // Convertit la lettre en code ASCII et soustrait 95 pour obtenir le numéro de colonne
return colonne;
}
// Utilisation de la fonction pour assigner une colonne en fonction de la lettre
let colonne = assignColonne(message.toLowerCase()); // Convertit la lettre en minuscule avant de l'assigner à la colonne
// Vérifier si msg.payload est un tableau
if (!Array.isArray(msg.payload)) {
// Si ce n'est pas le cas, le convertir en un tableau contenant l'élément actuel de msg.payload
msg.payload = [
{
header: {
col1: "Pseudos"
// ... ajoutez des en-têtes de colonnes vides jusqu'à la colonne correspondant à la lettre
// colX: "Lettre" par exemple
},
items: [],
sheetName: "InscriptionPlay"
}
];
}
// Recherche du pseudo dans la feuille de calcul et ajout de la place sur la même ligne
let foundPseudo = false;
for (let i = 0; i < msg.payload.length; i++) {
if (msg.payload[i].items[0] && msg.payload[i].items[0].col1 === pseudo) {
msg.payload[i].items[0]["col" + colonne] = place; // Assigne la place à la colonne correspondant à la lettre
foundPseudo = true;
break; // Sortir de la boucle après avoir trouvé le pseudo
}
}
// Si le pseudo n'a pas été trouvé, l'ajouter à la feuille de calcul
if (!foundPseudo) {
msg.payload.push({
header: {
col1: "Pseudos"
// ... ajoutez des en-têtes de colonnes vides jusqu'à la colonne correspondant à la lettre
// colX: "Lettre" par exemple
},
items: [
{
col1: pseudo,
// ... ajoutez des colonnes vides jusqu'à la colonne correspondant à la lettre "x"
["col" + colonne]: place
}
],
sheetName: "InscriptionPlay"
});
}
msg.filepath = "thecorrectfilepath.xlsx";
return msg;
}
My choices are due to the fact that I am a beginner in programming and I know nothing about databases.
So I suspect that there is probably some excess in my code. This is one reason why I need an outside opinion.
So I tried to make it "simple" by sending the information to an Excel table.
I chose to send the information to Excel because it is a tool that I know. And to "simply" add values I think it's the simplest for me, if I then want to format my tournament results.
This is what my approach will be, and then you can build on it.
I should note: I know nothing about twitch - so you will have a much easier time then I (and likely a few of us)
So...
Note : This is just an idea on how to simply the approach,
and will help you explore JS at the same time.
You can do a lot of this without using function nodes, but I think the function approach will help you understand Javascript (which is the basis of all the other nodes)
Node RED has a context mechanism, which you can store values/tables etc.
Create the storage area and call it tournament
This creates on object in global context. Do this only once for each tournament
/* Load our global state */
const tournament = global.get("tournament");
/* Player Name, Race Name, Place */
const playerName = msg.payload.userstate["display-name"]
const someRaceName = msg.payload.userstate["race-name"]
const place = msg.payload.userstate["place"]
/* Add Player */
if (!tournament.players.find((P) => P === playerName)) {
tournament.players.push(playerName)
}
/* Add Race */
if (!tournament.races.find((R) => R.name === someRaceName)) {
tournament.races.push({
"name": someRaceName,
"players": []
})
}
/* Get Race */
const race = tournament.races.find((R) => R.name === someRaceName))
/* Only allow players to add their result for each race once! */
if (!race.players.find((P) => P.player === playerName)) {
race.players.push({
"player": playerName,
"place": place
})
}
once this has filled with data - you can use it to calculate the winners of the tournament so on and so forth.
you can view this tournament object in the context tab in the Editor, and use it for other things - such as creating the final excel sheet after the tournament if you wanted it as a file
Using context will be the database if you will for your bot.
Command to return the current rankings in the tournament.
Command for each player to be able to check their last race results (or a chosen race they want to lookup) - and total points
etc etc
You know better what is off interest here , but using context will be much easier.
But please don't let me interfere with it - its just the way I will go myself
Hello everyone. Please check this update.
I've been deeper on Marcus'point of view so i am now using the context to do my things. It is simpler but i just have 1 little problem.
/* Load our global state */
const tournament = global.get("tournament");
/* pseudo */
const pseudo = msg.payload.userstate["display-name"]
/* Add Player */
if (!tournament.players.find((P) => P === pseudo)) {
tournament.players.push(pseudo)
}
return msg;
So now i have all my players register in my tournament
But my command to add all results and races is not working... someRaceName, place and pseudo are not added to the context.
The message send by the player is like that : "!a2"
where "a" is the name of the race and "2" the result of the player. (the name of the race can be a letter between a and z and the result can be a number between 1 and 8.
I'm using a regular expression but i don't know if it is good or not or if it's something else that is wrong in my code.
/* Load our global state */
const tournament = global.get("tournament");
/* Player Name, Race Name, Place */
const pseudo = msg.payload.userstate["display-name"]
const message = msg.payload.message;
const regexRaceName = /^![a-z].*$/;
const regexPlace = /^![a-z][1-8]$/;
/* Vérifier si le message contient une seule lettre pour someRaceName et un chiffre entre 1 et 8 pour place */
if (regexRaceName.test(message[1]) && regexPlace.test(message[2])) {
const someRaceName = message[1];
const place = parseInt(message[2]); // Convertir le chiffre en entier
/* Add Player */
if (!tournament.players.find((P) => P === pseudo)) {
tournament.players.push(pseudo)
}
/* Add Race */
if (!tournament.races.find((R) => R.name === someRaceName)) {
tournament.races.push({
"name": someRaceName,
"players": []
})
}
/* Get Race */
const race = tournament.races.find((R) => R.name === someRaceName)
/* Only allow players to add their result for each race once! */
if (!race.players.find((P) => P.player === pseudo)) {
race.players.push({
"player": pseudo,
"place": place
})
}
}
return msg;
I will continue my investigations. I will update this topic if i found something.
If someone have an idea about what's wrong i really thank you in advance.
have a nice day!
yes I have already experienced the context which resets with each new pseudo and I have already fixed the problem.
It is set up on a separate node with a separate command. and it works. I have several registrants who arrive in my context like intended.
yes i found it too. That was like always very "obvious".
the first regex was good.
Now that's the correct code :
if (regexRaceName.test(message) && regexPlace.test(message)) {
const someRaceName = message[1];
const place = parseInt(message[2]);
the problem was that i called the full regex to check if it feet only with the second or 3rd character. But of course the code was searching the full 3 character to fit with just 1 so it doesn't work
but now it's working.
Once again thank you to be there
You really help !