As you would gather there is a lot of code 
For the shopping the real key is populating the slot list file for words it will accept then sentences includes the following
[Shopping]
add ($shopping/shopping) [and] [($shopping/shopping)] to [(my|the)] shopping [list]
(remove|delete|cancel):delete ($shopping/shopping) [and] [($shopping/shopping)] from [(my|the)] shopping [list]
clear [(my|the)] shopping [list]
(list|whats in|what is in):list [(my|the)] shopping [list]
to generate the list of words I do the following:
- Setup a rule in my email to send the confirmation emails from Coles to a folder called Coles.
- In node-red use the email node to watch that folder
- Split the emails into individual lines
- Use a switch node to throw away unwanted lines
- I feed that through a function node with the following code to create a list in the flow context. It looks for the start of the items based on the content “ORDER SUMMARY” and the end when it finds “Estimated Total”
if (msg.payload.includes(")"))
{
return;
}
if (msg.payload.includes(“ORDER SUMMARY”))
{
flow.set(“done”, false);
return;
}
if (msg.payload.includes(“Estimated total”))
{
flow.set(“done”, true);
return;
}
if (flow.get(“done”) === true)
{
return;
}
var prevordered = flow.get(“previousOrders”) || [];
if (prevordered.includes(msg.payload))
{
return;
}
prevordered.push(msg.payload);
flow.set(“previousOrders”, prevordered);
return msg;
the output is then sent through a filter node that waits for 5 seconds of no more messages and then triggers the list of words to be created and sent to a nodered instance on the Rhasspy server via mqtt.
The code to create the word list is:
var words = [];
flow.get(“previousOrders”).forEach(item =>
{
var newitem = item.replace(/[^a-zA-Z0-9 ]/g, “”).replace(/[A-Z0-9]/g, function(x){return " " + x}).replace(/ */g," “).trim().toLowerCase();
newitem.split(” ").forEach(word =>
{
[“kg”, “g”, “cm”, “m”, “s”, “mg”, “ml”, “l”, “pm”].forEach(unit =>
{
if (word.endsWith(unit) && !isNaN(word.replace(unit, “”)))
{
word = word.replace(unit, “”);
}
})
if (isNaN(word) && (word.length > 2) && !words.includes(word))
{
words.push(word);
}
});
});
flow.set(“shoppingWords”, words.sort());
return {topic: “slotlist/shopping/shopping”, payload: words.sort().join(";;")};
On the rhasspy server I have a node subscribing to mqqt messages slotlist/# which receives any lists and if the list has changed it saves it into the slots folder structure and uses the api to trigger a re-training. Every now and then I have to manually apply unidentified words but given I populated the list with a couple of years worth of orders initially, most of the things we buy are already there.
Then in main nodered instance if it sees the shoppinglist intent it processes accordingly.
Similar logic is also used for all my slot lists including the lists of all my home automation devices, music playlist, favourites, album and artist lists which are extracted from Sonos using socos and a bash script on my home automation machine.
I hope this helps. Please let me know if you need more specific details in any area