node-xmpp --- Create a Persistant and Private Chatroom - node.js

I need to create a Private and Persistent Chat room dynamically via Node that does not automatically delete itself.
I've searched the net and couldn't find much on how to do it. This is the code snippet I use to create the chatroom:
var cl = new xmpp.Client({
jid: jabber_creds.jid,
password: jabber_creds.password,
host: jabber_creds.host,
port: jabber_creds.port
});
cl.on('online', function() {
var room_jid = jabber_creds.room_jid.replace("%s", chatRoomName);
// join room (and request no chat history)
cl.send(new xmpp.Element('presence', { to: room_jid }).
c('x', { xmlns: 'http://jabber.org/protocol/muc' })
);
// create room
cl.send(new xmpp.Element('iq', { to: room_jid, id: 'create', type: 'set' }).
c('query', { xmlns: 'http://jabber.org/protocol/muc#owner' }).
c('x', { xmlns: 'jabber:x:data',type: 'submit' })
);
});

Chat room persistence is handled at the server not the client. Yes the client can request that a server hold onto a chat room but you can't actually persist it from the client. Check with the server documentation that you are using to make sure it supports this.

Try to follow XEP-0045
http://xmpp.org/extensions/xep-0045.html#createroom
Just read The workflow for creating and configuring such rooms is as follows section
You need to do the next:
Send 1st presence to a new room
Server returns you a couple of messages that room was created, but you should unlock it
You should create configuration form and send to the server. In this form you can set 'Persistent' type & 'Only members' type
Server will return you success result if everything is OK

You can reference https://github.com/node-xmpp/node-xmpp/blob/master/examples/create_room.js and send configuration you want by XEP-0045
//create_room.js
'use strict'
var xmpp = require('../index')
,argv = process.argv
if (argv.length < 5) {
console.error('Usage: node create_room.js <my-jid> <my-password> <room-name>')
process.exit(1)
}
var cl = new xmpp.Client({ jid: argv[2], password: argv[3] })
cl.on('online', function(data) {
var userJid = data.jid.user + '#' + data.jid.domain,
roomJid = argv[4] + '#conference.' + data.jid.domain,
pres,
iq
console.log('Connected as ' + userJid + '/' + data.jid.resource)
console.log('Create room - ' + argv[4])
pres = new xmpp.Element(
'presence',
{ from: userJid, to: roomJid + '/' + data.jid.user })
.c('x', {'xmlns':'http://jabber.org/protocol/muc'})
cl.send(pres.tree())
iq = new xmpp.Element(
'iq',
{ to: roomJid, type: 'set' })
.c('query', { xmlns: 'http://jabber.org/protocol/muc#owner' })
.c('x', { xmlns: "jabber:x:data", type: "submit"})
//set room to be hidden by sending configuration. ref: http://xmpp.org/extensions/xep-0045.html
iq.c('field', { 'var': 'FORM_TYPE' })
.c('value').t('http://jabber.org/protocol/muc#roomconfig').up().up()
.c('field', { 'var': 'muc#roomconfig_publicroom'})
.c('value').t('0').up().up()
cl.send(iq.tree())
// exit later for sending configuration done
setTimeout(function() {
cl.end()
}, 100)
})
cl.on('error', function(e) {
console.error(e)
process.exit(1)
})

Related

What is the proper way to set up this Cron Task after a restart?

I am building an IOT application which connects to an MQTT server where users subscribe to a topic for a particular building. I am able to connect, retrieve the information I need, and save it to a database.
The problem is, each individual user is stored in the session, and the connection parameters are submitted in a form, thereby the Subscribe function, which is needed for the topic subscription loses the information it needs for connection on each server restart.
What is the proper way to setup a cron task, which stores the submitted info for each user, along with their session, when the server is restarted(especially when it crashes from a bug)?
var mqtt = require('mqtt');
var Building = require('../models/Building')
var Volume= require('../models/Volume')
/////////////////////////////////////////////////////////////////////////
var today = new Date();
var date = today.getFullYear() + '-' + (today.getMonth() + 1) + '-' + today.getDate();
///////////////////////START MQTT SUBSCRIPTION//////////////////////////////////////////////////////////////////
const Subscribe = async (req, res, name) => {
const options = {
port: port,
username: req.body.username,
password: req.body.password,
clientId: "someclientid"
}
const client = mqtt.connect("someserver", options);
var topic_list = "sometopic"
client.on('connect', function () {
client.subscribe(topic_list, function (err, granted) {
if (err) {
console.error(err);
return;
}
console.log('Subscribed to topic: ' + topic_list);
});
console.log("connected flag " + client.connected);
})
client.on('message', function (topic, message, packet) {
var msgObject = JSON.parse(message.toString())
var data = JSON.stringify(msgObject.data).slice(-9, -5);
console.log(JSON.stringify(msgObject.data))
var v = new DataView(new ArrayBuffer(4))
v.setUint32(0, `0x${data}0000`)
console.log('message is ' + v.getFloat32(0))
var decimal = v.getFloat32(0);
storedata(req, decimal, name);
});
}
/////////////////////////End MQTT SUBSCRIPTION//////////////////////////////////////////////
async function storedata(req, value, name) {
const absvalue = Math.abs(value)
if (absvalue != 0) {
var building = await Building.findOne({
user: req.user,
name: name
});
if (building) {
const oldvolume = await Volume.create({source:name, calculated_at:date, volume:absvalue, user:req.user});
oldvolume.save()
console.log(oldvolume)
}
}
}
module.exports.Subscribe = Subscribe;

Node JS: Twich Bot messages are only displayed if on the own channel

I'm trying to make a bot that reacts to The User Messages. My Problem is, that the Messages the Bot sends appear in the Console, but not in the Twich Chat. In the Twich chat the Messages are only displayed on the Bots Channel.
Im Using tmi.js
My Code:
const tmi = require("tmi.js");
var config = require('./config');
const { channels } = require("./config");
const twitch_client = new tmi.client(config);
twitch_client.connect();
grussFormen = [
'Hi',
'Hallo',
'Guten Tag',
'Sehr geehrte Damen und Herren',
'Sehr geehrter Herr',
'Sehr geehrte Frau',
'Grüß dich',
'Grüß Sie',
'Grüezi',
'Hey',
'Hej',
'Moin',
'Servus',
'Na?',
'Was geht?'
];
verabschiedungen = [
'Mit freundlichen Grüßen',
'Viele Grüße',
'Liebe Grüße',
'Alles Liebe',
'Bis dann',
'Bis später',
'Bis morgen',
'Mach’s gut',
'Tschüss',
'Tschüssi',
'Tschü’',
'Schönen Tag noch',
'Schönen Abend noch',
'Ciao',
'Tschau',
'Tschaui',
'Wir sehen uns!',
]
twitch_client.on('connected', (address, port) =>{
// twitch_client.action('derNiklaas', 'Restarted');
});
twitch_client.on('chat', (channel, user, message, self) =>{
//In lowercase and withour Spaces
// analysingMessage = message.toLowerCase().replace(/\s/g, '');
//In lowercase
analysingMessage = message.toLowerCase();
//Nach Begrüßungs Formeln suchen
for (let item of grussFormen) {
if (analysingMessage.includes(' ' + item.toLowerCase()) || analysingMessage.includes(item.toLowerCase() + ' ') || analysingMessage == item.toLowerCase()){
twitch_client.action(channel, item + ' #' + user.username, (err) =>{
if(err){
console.log(err);
}
});
}
}
//Nach Verabschiedungen suchen
for (let item of verabschiedungen) {
if (analysingMessage.includes(' ' + item.toLowerCase()) || analysingMessage.includes(item.toLowerCase() + ' ') || analysingMessage == item.toLowerCase()){
twitch_client.action(channel, item + ' #' + user.username, (err) =>{
if(err){
console.log(err);
}
});
}
}
});
config.js (The Comments are not in:
module.exports = {
options: {
debug: true
},
connection: {
reconnect: true,
secure: true,
timeout: 180000,
reconnectDecay: 1.4,
reconnectInterval: 1000,
},
identity: {
username: 'isi_ko_bot',
password: 'I Removed my Password here :D'
},
channels: [
'derNiklaas' // A Channel I Whatch
// 'isi_ko_bot' // The Bots Channel
// 'isi_kohd' // My Channel
]
}
Screenshot of the Console:
It seems that you're trying to make it so that the bot react to a specific message, to do this use the following format.
if (message.startsWith("message you want the bot to react to")) {
//what you want the bot to do
}
example:
if (message.startsWith("hello")) {
client.say (channel, "Hello There!")
}

Call socket.io API from an external file

I am trying to call my Socket.io interface from another file that is express routes. Lets say I want to create the following group chat with this and then send a notification to the involved users in the group chat,
routes.js
router.post(
"/save-groupchat",
passport.authenticate("jwt", { session: false }),
(req, res) => {
new GroupChat({
creator: req.user._id,
groupName: req.body.groupName,
groupMembers: userList
})
.save()
.then(newGroup => {
GroupChat.findOne(newGroup)
.populate("groupMembers.user")
.then(groupChat => {
//Notify the user's as well
userList.map((user, key) => {
NotificationCenter.findOneAndUpdate(
{ owner: user.user },
{
$push: {
notifications: {
$each: [
{
type: "group-invite",
title:
"You have been invited to " +
req.body.groupName +
" by " +
req.user.name,
notificationTriggeredBy: req.user._id
}
],
$position: 0
}
}
}
)
.then(() => {
//How do I call the socket here <---------------------------------
console.log("Notified User: " + req.body.userList[key].name);
})
.catch(err => {
console.log(
"Error when inviting: " +
req.body.userList[key].name +
"\n " +
err
);
});
});
res.json(groupChat);
});
});
}
);
./microservice/chat/chat (Socket Interface)
And then my socket interface is like this,
let user_list = {};
io.sockets.on("connection", socket => {
socket.on("send-notification", notification => {
console.log("How do I get here from the other file?");
});
})
...
Here is how I have my server.js file
var http = require("http").Server(app);
var io = require("socket.io")(http);
ChatMicroservice = require("./microservice/chat/chat")(io);
How would I access the socket interface and use the user_list of sockets to send out the notifications?
To call socket.io API from an external file you should either create a singleton class and export it or save socket object to a global variable, and save the connected socket id in the database corresponding to the user. so whenever a socket connection is established to the server, the socket id ie, socket.id is saved in the database corresponding to the user-id.
so your socket interface becomes
global.io = io;
io.sockets.on("connection", socket => {
// add your method to save the socket id (socket.id)
// user-id passed can be accessed socket.handshake.query.userId
socket.on("send-notification", notification => {
console.log("How do I get here from the other file?");
});
})
In your other file call
global.io.to(id).emit(event, value);
If you are planning to scale your application horizontally use socket.io-redis in your Socket Interface
const redis = require('socket.io-redis');
global.io.adapter(redis({ host: redisUrl, port: redisPort }));

Node.js Script doesn't exit

I am noob at Js but trying to make life easier by writing api to transfer files from irc to my server
After some digging I got a module that downloads file (code below based on npm xdcc) but when I run the module it doesn't exit after completing commands
Eg. After spitting out 'Download Complete' I have to press Ctrl+C to exit
var irc = require('xdcc').irc;
var ProgressBar = require('progress');
var progress;
var connectIRC = function (bot, pack) {
var user = 'desu' + Math.random().toString(36).substr(7, 3);
var start = 0;
console.log('Connecting...');
var client = new irc.Client('irc.rizon.net', user, {
channels: ['#nibl'],
userName: user,
realName: user
});
client.on('join', function(channel, nick, message) {
if (nick !== user) return;
console.log('Joined', channel);
client.getXdcc(bot, 'xdcc send #' + pack, '.');
});
client.on('xdcc-connect', function(meta) {
console.log('Connected: ' + meta.ip + ':' + meta.port);
progress = new ProgressBar('Downloading... [:bar] :percent, :etas remaining', {
incomplete: ' ',
total: meta.length,
width: 20
});
});
var last = 0;
client.on('xdcc-data', function(received) {
progress.tick(received - last);
last = received;
});
client.on('xdcc-end', function(received) {
console.log('Download completed');
});
client.on('notice', function(from, to, message) {
if (to == user && from == bot) {
console.log("[notice]", message);
}
});
client.on('error', function(message) {
console.error(message);
});
};
module.exports.connectIRC = connectIRC;
Also when the irc bot doesn't send any notice , the filename is wrapped in double quotes
Eg.
Packet name = Node.mkv
If bot sends a notice then name of file download = Node.mkv
If bot doesn't send any notice then name = "Node.mkv" (including double quotes)
Any help would be appreciated
If you want auto close your Node JS app you have to call the global process object's exit method so :
client.on('xdcc-end', function(received) {
console.log('Download completed');
process.exit()
});

register new xmpp account with node-xmpp ( node.js )

I'm looking at 'XEP-0077 in-band registration' about how to register a new XMPP account. Here is my code. I use node-xmpp to connect my node.js application to an ejabberd server.
var net = require('net');
var xmpp = require('node-xmpp');
var cache = new Object();
net.createServer( function(socket) {
socket.setEncoding('utf8');
socket.addListener('data',function(data) {
data = data.substr(0,data.length-2);
if(cache.admin==undefined && data=='login') {
var ejabberd =new xmpp.Client({jid:"admin#mine",password:'12345',host:'192.168.7.202',port:'5222'});
cache.admin = ejabberd;
cache.admin.addListener('online',function() {
cache.admin.send(new xmpp.Element('presence',{type:'chat'}).c('show').c('status').t('mine status'));
cache.admin.send(new xmpp.Element('iq',{type:'get',id:'reg1'}).c('query',{xmlns:'jabber:iq:register'}));
})
cache.admin.addListener('stanza',function(stanza) {
if(stanza.is('iq')) {
console.log(stanza.children[1]);
}
})
cache.admin.addListener('end',function() {
cache.admin.end();
cache.admin = undefined;
})
}
if(cache.admin!=undefined && data=='logout') {
cache.admin.end();
cache.admin = undefined;
} else if(cache.admin!=undefined && data=='register') {
cache.admin.send(new xmpp.Element('iq',{type:'set',id:'reg1'}).c('query',{xmlns:'jabber:iq:register'}).c('username').t('alow').up().c('password').t('test'));
}
});
}).listen(5000);
If i run this code, I get this error:
{ name: 'error',
parent:
{ name: 'iq',
parent: null,
attrs:
{ from: 'admin#mine',
to: 'admin#mine/20108892991316770090454637',
id: 'reg1',
type: 'error',
xmlns: 'jabber:client',
'xmlns:stream': 'http://etherx.jabber.org/streams' },
children: [ [Object], [Circular] ] },
attrs: { code: '403', type: 'auth' },
children:
[ { name: '**forbidden**',
parent: [Circular],
attrs: [Object],
children: [] } ] }
In 'XEP-0077: In-Band Registration' it says that the forbidden reason means that "The sender does not have sufficient permissions to cancel the registration".
How can I get such permissions?
I have been strugling with something similar, I wanted to register a new user account via in-band registraton from nodejs to an ejabberd server running in ubuntu. Here is what I did and worked for me:
//Dependencies
var xmpp = require('node-xmpp');
//Host configuration
var host = "localhost";
var port = "5222";
var admin = "sebastian#localhost";
var adminPass = "adminPass";
var connection = new xmpp.Client({
jid: admin,
password: adminPass,
host: host,
port: port
});
//user to be registered name & pass
var newUserName = "pepe";
var newUserPass = "pepePass";
//Stream
var iq = "<stream:stream xmlns:stream='http://etherx.jabber.org/streams' xmlns='jabber:component:accept' to='localhost'><iq type='set' id='reg2'><query xmlns='jabber:iq:register'><username>" + newUserName + "</username><password>" + newUserPass + "</password></query></iq></stream>";
//Send
connection.send(iq);
//End
connection.end();
The var iq is kind of messy, I suppose that if you know how to use Strophe.js in a propper way that part could look a little bit nicer and cleaner. I was missing the section of the xml, it seems that if you want to send a stream, you have to provide a valid ejabberd namespace, that was what was failing for me. Hope this helps you sort your problem out.
Which server are you using? Are you sure it has XEP-77 enabled? Test with an existing client. Ensure that the account you're trying to create does not already exist. Ensure that the account has the correct domain name.

Resources