Parse smilies in Node.js and Socket.io chat app - node.js

I've created chat application based on this project but what I want to do now is to have parser for smilies. For example, If I write something like:
What's up? :)
That can be parsed as "What's up? [image here]". Also, when someone visits room, all messages from that room needs to be parsed too. What's the easiest way to do this?
P.S. App doesn't have database.

var smileyMap={
"smile.png":[":)",":-)"],
"sad.png":[":(",":-("]
};
var insertSmiley=function(basePath,smileys){
var replacements=[];
Object.keys(smileys).forEach(function(file){
var _file="<img src=\""+basePath+file+"\"\>";
smileys[file].forEach(function(chars){
var reg=new RegExp(chars.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1"),"g");
replacements.push([reg,_file]);
});
});
return function(text){
return replacements.reduce(function(line,replacement){
return line.replace(replacement[0],replacement[1]);
},text);
}
}("http://example.com/images/",smileyMap);
"http://example.com/images/" Has to be the root Path of the smiley Images.
smileyMap has to be an Object with the filename as the key, and the smileys to be replaced as an Array.
insertSmiley("Hello World :-)")
Results in:
Hello World <img src="http://example.com/images/smile.png">

You can use simple string interpolation based on Regex or whatever you want.
Let's imagine that we have map of smiles:
const SMILES_MAP = {
':)': 'http://link-to-smiley.png',
':0': 'http://link-to-another-smiley.png'
};
Also we have a string of our message called message, for instance. Let's build a function that replace all smiles with images:
function buildMessage(message) {
let smiles = Object.keys(SMILES_MAP);
smiles.forEach(smile => message = message.replace(smile, SMILES_MAP[smile]));
return message;
}
Call function with message arguments and get result:
buildMessage("Hello there :)"); // Returns "Hello there http://link-to-smiley.png"

Related

Node.js // I want a function to wait for a variable until it runs

I am working on a Discord Bot that allows a user to store data in a map. Currently I have gotten up to the point of being able to send the data I want correctly but It was hardcoded to send that exact data set and that's not what I want.
Hard Coded data sent
I want the user to be able to type a command and have the bot then process which data set it'll need to process. Processing Logic below.
function arrayproccesor() {
const cmd = require("./bot-logic");
let msg = "";
for (let [x, y] of a.get(cmd)) {
msg += x + ":" + y + "\n";
}
module.exports = msg;
}
as you can see above I tried to import a variable from the command logic, which I had hoped would fetch me the map index that I wanted. Command Logic below.
command(client, "udft", (message) => {
const embed = new Discord.MessageEmbed()
.setColor("#fabe07")
.setImage(
"https://i.pinimg.com/originals/d8/28/f1/d828f1f1a67ac7d03195a9964fb16f94.jpg"
)
.setTimestamp()
.setURL("https://undefeated.com/account/login")
.addFields({
name: "ACCOUNTS:",
value: msg,
inline: true,
})
.addFields({
name: "STORE:",
value: "Undefeated \n \n **LINK:**\n",
inline: true,
});
message.channel.send(embed);
});
I tried to define the cmd variable once the user typed the command !udft and export said variable to the arrayprocessor function, but it didnt quite work that way, I think that the function processed nothing therefore making it send [object Object]
object object
Full Code: Array Proccesor Command Logic
Also I know I could probably put the array and its function in the same file as the command logic but I want to further deal with exporting variables from file to file.
Anyways, Any and all help is appreciated and I'm sorry if I made this longer than it need to be !
Async/Await is a useful way to handle Promises in a hoisted manner.
Please read the docs and implement "async / await".
Here's the link: Click

Replacing text with data from pg-promise

I would like to replace some text in a string with values from a database using pg-promise. As I have not used Promises before, I'm struggling with how to deal with it in the best way.
What I have tried so far doesn't work as I try to combine synchronous and asynchronous programming:
var uid = ...;
"Some string".replace(/\#\{([\w]*?)\}/gmi, function(m, c) {
var r = "";
db.one("SELECT a FROM ... WHERE x = $1 AND y = $2", [c, uid])
.then(function(data) {
r = data.a;
});
return r;
});
r is, unsurprisingly, an empty string. Is there a way to rewrite this block to "wait" for the values from the database?
What I try to do is, to replace placeholders in a message that is send to the user. So the above is part of a function called prepareMessage and I send the message to the user using socket.io so it looks something like this:
io.to(socket.id).emit('message', { text: prepareMessage(msg) });
After some reading and more thinking, I came up with a solution which I'd like to add if someone else has a similar problem.
(In addition to the question above, I had the additional complication that my message is an array of strings and that the order was to be kept.)
The key was to use tasks to send all queries to the DB as one package and wait for all results to return. This led to the following code:
// Sample data
var messages = ["String 1 with no placeholder.",
"String 2, #{placeholder1}, String 2.2.",
"String 3 with some more #{placeholder2}."];
// Collect all matches in array
var matches = [];
messages.forEach(function(text, index) {
const regex = /\#\{([\w]*?)\}/gmi;
var m;
do {
matches.push(regex.exec(text))
} while(m);
});
// Request data from the database
db.task(function(t) {
return t.batch(matches.map(function(m) {
return t.oneOrNone("SELECT ... FROM ... WHERE id = $1", [m[1]])
}));
})
.then(function(r) {
// Replace all occurrences of placeholders
r.forEach(function(p) {
messages = messages.map(function(t) { return t.replace("#{"+p.id+"}", p.replacement); });
});
// Send message to user
io.emit('text', messages)M
})
.catch(function(e) {
// ... error handling ...
});

Passing an async object to dustjs template

I have a hard time understanding how to pass an object fetched from a database to a dust.js template.
Let's say I have a template:
{#person}
{name} - {title}
{/person}
I try to setup a context something like this:
var ctx = {
person: return chunk.map(function(chunk) {
database.person(12345, function(data) {
dust.nextTick(function() {
chunk.end(data); // What to really do here?
});
});
});
}
Where database.person fetches the object from a database and passes it to a callback.
And then I would run the render function:
res.render('person', ctx);
The correct form was:
var ctx = {
person: function(chunk, context, bodies) {
return chunk.map(function(chunk) {
database.person(1234, function(data) {
return chunk.render(bodies.block, context.push(data)).end();
});
});
}
}
Instead of write I had to call render as in Alan's answer.
The whole db call had to be enclosed in the return chunk.map call to work. I also had to chain an end command to send the results back to the stream.
Why these calls are needed is told in the dust.js guide (http://akdubya.github.io/dustjs/#guide):
chunk.map tells Dust to manufacture a new chunk, reserving a slot in
the output stream before continuing on to render the rest of the
template. You must (eventually) call chunk.end() on a mapped chunk to
weave its content back into the stream.
This is something that isn't addressed in the LinkedIn guide pages.
I'm assuming your db call will return something along the lines of a json object such as the "data" below.
The async code looks ok. chunk.write will just write out whatever you give it so you need to pass your data in on the context so it can be accessed in the template. Use chunk.render instead of write. A non-async example would be :
{
"person": function(chunk, context, bodies) {
var data = {name:"Smith", title:"Mr"};
return chunk.render(bodies.block, context.push(data));
}
}
Running that through linkedin dust tests seems to get the right answer.
Hope that helps,
Alan

How to emit an event and pass a function as a parameter with socket.io

I am using socket.io and I am trying to emit an event from my server and pass an object with a function as a parameter. Here is my code:
socket.emit('customEvent', {
name : "Test".
myFunc : function() {
//some logic here
}
});
and then in the client (my app in the browser) I am able to access 'name' property but when I try to access 'myFunc' but I get 'undefined' for it. Here is my code
socket.on('customEvent', function(data){
data.myFunc();
});
What is the correct approach for this (if it is possible at all)?
The data is transmitted as JSON, so it can't contain functions. Maybe you're looking for what's called 'acknowledgments' in socket.io's documentation?
// server
socket.on('customEvent', function (data, fn) {
fn(data.x)
})
// client
socket.emit('customEvent', { x: 1 }, function(){
// ...
})
You can serialize your function, maybe it's a dangerous way for some functions. But socket.io transfers only strings as pure strings or JSON. You can try to eval string function when receive it from server side.
NOTE: Code not tested below:
function hello() {
return "Hello Cruel World";
}
socket.emit('send-function', { myFunc : hello.toString() });
...
on server side:
socket.on('send-function', data) {
console.log(eval(data.myFunc));
}
Try on this code and give us a feedback if it works.

How to use chrome.storage in a chrome extension using a variable's value as the key name?

I'm trying to use chrome's local storage / sync storage (chrome.storage) for an extension, to store data entries, for many different entries. I can't seem to figure out the correct syntax for it. I only want to simply store the information as strings. I have searched and can't find anything yet that works.
This is what works for me at the moment using the normal localStorage technique:
var imageName = "Red Cat 5";
var myDescription = "A nice kitty";
localStorage.setItem (imageName, myDescription);
console.log(localStorage[imageName]);
This works and lets me set the key from an existing variable.
How can I do it using chrome.storage.local.set?
I have been trying this without any success:
var imageName = "Red Cat 5";
var myDescription = "A nice kitty";
chrome.storage.local.set({imageName: myDescription}, function()
{console.log('success?');});
chrome.storage.local.set({imageName: myDescription}, function()
{chrome.storage.local.get(imageName, function(r){console.log(r.imageName);});});
Any help is much appreciated. Thanks!
----- UPDATE BELOW -----
Thanks for the explanation with the code. I hope it helps anyone else. There seems to be little information available on doing this! Your answer helped me come up with this:
var nameOne = "al";
var nameTwo = "bob";
var nameThree = "carl";
var nameFour = "dan";
var dataObj = {};
dataObj[nameOne] = nameTwo;
dataObj[nameThree] = nameFour;
storage.set(dataObj);
storage.get(dataObj, function(result)
{
console.log(result[nameOne]);
console.log(result[nameThree]);
});
Use a named object, not an anonymous object, and set a member variable using square brackets:
var dataObj = {};
dataObj[imageName] = myDescription;
chrome.storage.local.set(dataObj, function() { /*...*/ });
It's not the most elegant looking code, but it's the only way to do it.
In ES6, a slightly shorter approach is to use an object literal with a dynamic property name:
chrome.storage.local.set({
[imageName]: myDescription
}, function() { /*...*/ });
the set method accepts object items, and an optional callback, the get accepts optional string or array or object keys and if you passed the first argument, you got to pass the second argument as well.
Example:
// To set
chrome.storage.local.set({'testKey':'Test Value'});
// To get
chrome.storage.local.get('testKey', function(data){
console.log(data);
// logs out "Object {testKey: "Test Value"}"
})

Resources