Is there a way to multiplex a "here_now" command so I can see the user count in different channels?
First Define pubnub with your publish_key and subscribe_key
var pubnub = PUBNUB.init({
publish_key: 'demo',
subscribe_key: 'demo'
});
To get occupancy for all channels (Occupants and Occupancy Count)
pubnub.here_now({
callback : function(m){console.log(JSON.stringify(m))}
});
To Return a list of channels, associated with a subscribe key, where subscribers exist.
pubnub.where_now({
callback : function(m){console.log(JSON.stringify(m))},
error : function(m){console.log(JSON.stringify(m))}
});
UPDATE
To get occupancy info in selected channels. (Something similar to multiplexing)
var pubnub = PUBNUB.init({
publish_key: 'demo',
subscribe_key: 'demo'
});
var myChannels = ['AAPL', 'SOFIX']; // define your channels here
pubnub.here_now({
callback : function(m){
var result = {};
for (var i=0; i < myChannels.length; i++) {
if (myChannels[i] in m.channels){
result[myChannels[i]] = m.channels[myChannels[i]];
}
}
console.log(JSON.stringify(result));
}
});
References :
here_now : link
where_now : link
list all pubnub channels with active subscribers : link
Related
I have a simple script for listening to Unconfirmed and Confirmed transaction on a Bitcoin cash wallet. I am connecting to a GRPC interface for getting these messages. The script works well and I get the unconfirmed transaction every time a transfer takes place. However, I do not receive any messages for confirmed transaction.
Here's the code I have:
var PROTO_PATH = __dirname + '/bchrpc.proto';
var fs = require('fs');
var bchaddr = require('bchaddrjs');
var axios = require('axios');
var grpc = require('#grpc/grpc-js');
var protoLoader = require('#grpc/proto-loader');
var packageDefinition = protoLoader.loadSync(
PROTO_PATH,
{keepCase: true,
longs: String,
enums: String,
defaults: true,
oneofs: true
});
var pb = grpc.loadPackageDefinition(packageDefinition).pb;
var client = new pb.bchrpc('bchd.imaginary.cash:8335', grpc.credentials.createSsl());
var addr = '<WALLET_ADDRESS>';
console.log(addr);
// Build TransactionFilter & setup live transaction stream
var transactionFilter = pb.TransactionFilter
transactionFilter.all_transactions = false;
transactionFilter.addresses = [addr];
var subscribreTransactionsRequest = pb.SubscribeTransactionsRequest;
subscribreTransactionsRequest.include_mempool = true;
subscribreTransactionsRequest.subscribe = transactionFilter;
var stream = client.SubscribeTransactions(subscribreTransactionsRequest)
function dataHandler(message) {
console.log('New Transaction');
var tx = message
console.log(tx);
const { type } = tx;
// map hashes to strings
switch(type) {
case 'UNCONFIRMED':
console.log('Inputs:');
console.log(tx.unconfirmed_transaction.transaction.inputs);
console.log('----------------\nOutputs');
console.log(tx.unconfirmed_transaction.transaction.outputs);
console.log('---------------------------');
const h1 = tx.unconfirmed_transaction.transaction.hash;
fs.writeFileSync('unconfirmed.txt', `${h1.toString('hex')}\n`, { encoding: 'utf-8', flag: 'a+' });
break;
case 'CONFIRMED':
console.log('Inputs:');
console.log(tx.confirmed_transaction.transaction.inputs);
console.log('----------------\nOutputs');
console.log(tx.confirmed_transaction.transaction.outputs);
console.log('---------------------------');
const h2 = tx.confirmed_transaction.transaction.hash;
fs.writeFileSync('confirmed.txt', `${h2.toString('hex')}\n`, { encoding: 'utf-8', flag: 'a+' });
break;
}
// post
axios.post(URL, tx).then(res => {
console.log(res.status);
}).catch(e => console.error(e));
}
// other functions omitted for brevity
stream.on('data', dataHandler);
stream.on('status', statusHandler);
stream.on('end', endHandler);
stream.on('error', errorHandler);
The script listens for all events on this given address and on data event, invokes dataHandler function which prints the transaction information, writes the transaction hash to a file and finally, sends the transaction information to a remote address. On RST_STREAM error, the script reconnects to the server after 1 second delay. The script uses bchrpc.proto protocol buffer definition from this package here.
Is there anything missing in my code that results in this behavior or, is it because the connecting server is unreliable on message delivery? I also tried the following servers with the same results:
https://bchd.greyh.at:8335
https://bchd.fountainhead.cash:443
Any help on this highly appreciated. Thanks.
I was finally able to figure out the issue after looking at other example scripts. I was missing one option in the subscribreTransactionsRequest that cause the issue and here's the change that solved the issue:
subscribreTransactionsRequest.include_in_block = true;
From the protocol buffer definition:
// When include_in_block is true, transactions are included when they are confirmed. This notification is sent in addition to any requested mempool notifications.
bool include_in_block = 4;
Adding the answer here for future reference.
I'm creating Chat app in Laravel 5.4 using socket.io at client side also sending APNS notification to a target user.
While doing this when user send Message to target user that Message send more than once means It depends upon number of connections connected with node socket.io server which I'm thinking it's mean's My channel ('messages') looping that message to connected number of clients so it's showing more than once at receiver side.
Here My Sender Side (Laravel):
$redis = LRedis::connection();
$data = [
'message' => $msg,
'user' => $auth,
'music'=>$music,
'target_user'=>$target_user
];
$redis->publish('message', json_encode($data));
At socket.js:
var app = require('express')();
var server = require('http').Server(app);
var io = require('socket.io')(server);
var redis = require('redis');
var apn = require('apn');
var request = require('request');
var auth_user_id = null;
var mysql = require('mysql');
var con = mysql.createConnection({
host: "localhost",
user: "root",
password: "",
database: "database"
});
con.connect(function(err) {
if (err) throw err;
console.log("Database Connected!");
});
var options = {
token: {
key: "app-keys/APNsAuthKey_U3QV9H86BR.p8",
keyId: "keyId",
teamId: "teamid",
cert: "app-keys/musicChatAppDevelopment.pem",
},
development: true,
production: false,
};
var apnProvider = new apn.Provider(options);
server.listen(8890);
io.on('connection', (socket) => {
var auth_user_id = socket.handshake.query.auth_user_id;
console.log("client connected");
console.log("Client Socket id--"+socket.id+"-----User Id----"+auth_user_id);
var redisClient = redis.createClient();
redisClient.subscribe('message');
if(auth_user_id>0) {
var sql = "UPDATE users SET socket_id ='"+socket.id+"' WHERE id ="+auth_user_id;
con.query(sql, function (err, result) {
if (err) throw err;
console.log(result.affectedRows + " record(s) updated");
});
}
redisClient.on('message', (channel, data) => {
console.log(channel);
let data_= JSON.parse(data);
let message_ = data_.message;
let deviceToken = data_.target_user['ios_token'];
if(data_.user['id']!=data_.target_user['id']) {
console.log(data_.target_user['ios_token']);
var note = new apn.Notification();
note.expiry = Math.floor(Date.now() / 1000) + 3600; // Expires 1 hour from now.
note.badge = 0;
note.sound = "ping.aiff";
note.alert = {
"title" : data_.user['first_name'],
"body" : message_,
};
note.payload = { "aps" : {
"alert" : {
"title" : data_.user['first_name'],
"body" : message_
}
}
}
note.topic = "com.charpixel.hellodemo";
console.log(note);
let socketId = data_.target_user['socket_id'];
apnProvider.send(note, deviceToken).then( (result) => {
console.log(JSON.stringify(result));
});
console.log("Target User Socket id "+data_.target_user['socket_id']);
socket.broadcast(data_.target_user['socket_id']).emit(channel, data);
io.to(socketId).emit(channel,data);
}
});
socket.on('disconnect', () => {
redisClient.quit();
});
});
At index.blade.php:
var ip = '<?php echo config('app.current_ip'); ?>:8890?auth_user_id='+JSON.parse(user_id)['id'];
console.log("Curent_URL"+ip);
var socket = io.connect(ip);
socket.on('message', function (data) {
});
Also getting apns notification multiple times on my IOS App.
For 1-1 chat you should create separate channel for each chat. Your code sends message on channel 'message' which is subscribed by every client. So there are multiple problems here. For example, if you have 3 clients:
redisClient.on('message') will be triggered for all 3 clients.
As the data remains same (which means device token remains same), no matter for which client on('message) was triggered, the notification will be sent to the same receiver and that's the reason why you are receiving multiple notifications.
The solution is that you should create a unique channel for each chat. See this stackoverflow answer as an example. Its not using redis but you can change it to work with reddis too.
I have been looking for info to know how to connect Redis to Sails.js throw model - controller - view and I think I've got it.
My problem is that I think I don't understand the philosophy of Redis, its keys, etc.
I guess my Redis has not any keys, I mean it's not something like "name : Victor, surname : Garcia" but " Victor:GarcĂa", wihtout a key, so I don't know how to set an attribute in the model of Redis.
So I've tried either no setting attributes or set just an attribute for the whole key (e.g. "Victor:Garcia:33:Seville:Spain") but without a result.
My model looks like this:
module.exports = {
schema: false,
connection: 'redis',
autoPK: false,
autoCreatedAt: false,
autoUpdatedAt: false,
attributes: {
ta : 'string',
// Override toJSON instance method
toJSON: function() {
var obj = this.toObject();
return obj;
}
}
};
I tried this:
Redis.find()
.where({ ta: 'MB:400V:TRAF004:EP:MvMoment' })
.exec(function(err, users) {
// Do stuff here
console.log("User: " + users + " - " + err);
});
With no result, in fact, I get in console:
User: - null
No matter what I set in .where.
I also Tried to do something like this:
Model
module.exports = {
schema: false,
connection: 'redis',
autoPK: false,
autoCreatedAt: false,
autoUpdatedAt: false
};
Without attributes, and I tried in the controller
Redis.find()
.where({ ta: 'MB:400V:TRAF004:EP:MvMoment' })
.exec(function(err, users) {
// Do stuff here
console.log("User: " + users + " - " + err);
});
Taking a look in google I've found this
http://sailsjs.org/#!/documentation/reference/waterline/models/stream.html
And I've tried to do the sample in my application.
Model (Redis.js):
module.exports = {
schema: false,
connection: 'redis',
autoPK: false,
autoCreatedAt: false,
autoUpdatedAt: false
};
Controller:
estStream: function(req,res){
if (req.param('startStream') && req.isSocket){
var getSocket = req.socket;
// Start the stream. Pipe it to sockets.
Redis.stream().pipe(getSocket.emit);
} else {
res.view();
}
View:
<script type="text/javascript">
window.onload = function startListening(){
socket.on('gotUser',function(data){
console.log(data+' has joined the party');
});
};
</script>
<div class="addButton" onClick="socket.get('/monweb/testStream/',{startStream:true})">Stream all the Users !</div>
Bur when I click the button the result by console is as below:
error: Sending 500 ("Server Error") response:
TypeError: Object function (ev) {
if (ev == 'newListener') {
return this.$emit.apply(this, arguments);
}
var args = util.toArray(arguments).slice(1)
, lastArg = args[args.length - 1]
, packet = {
type: 'event'
, name: ev
};
if ('function' == typeof lastArg) {
packet.id = ++this.ackPackets;
packet.ack = lastArg.length ? 'data' : true;
this.acks[packet.id] = lastArg;
args = args.slice(0, args.length - 1);
}
packet.args = args;
return this.packet(packet);
} has no method 'on'
at Stream.pipe (stream.js:65:8)
at module.exports.testStream (/home/victor/gestamp-PRUEBAS/api/controllers/MonWebController.js:1364:24)
at routeTargetFnWrapper (/home/victor/gestamp-PRUEBAS/node_modules/sails/lib/router/bind.js:178:5)
at callbacks (/home/victor/gestamp-PRUEBAS/node_modules/sails/node_modules/express/lib/router/index.js:164:37)
at param (/home/victor/gestamp-PRUEBAS/node_modules/sails/node_modules/express/lib/router/index.js:138:11)
at param (/home/victor/gestamp-PRUEBAS/node_modules/sails/node_modules/express/lib/router/index.js:135:11)
at pass (/home/victor/gestamp-PRUEBAS/node_modules/sails/node_modules/express/lib/router/index.js:145:5)
at nextRoute (/home/victor/gestamp-PRUEBAS/node_modules/sails/node_modules/express/lib/router/index.js:100:7)
at callbacks (/home/victor/gestamp-PRUEBAS/node_modules/sails/node_modules/express/lib/router/index.js:167:11)
at /home/victor/gestamp-PRUEBAS/node_modules/sails/lib/router/bind.js:186:7
at alwaysAllow (/home/victor/gestamp-PRUEBAS/node_modules/sails/lib/hooks/policies/index.js:209:11)
at routeTargetFnWrapper (/home/victor/gestamp-PRUEBAS/node_modules/sails/lib/router/bind.js:178:5)
at callbacks (/home/victor/gestamp-PRUEBAS/node_modules/sails/node_modules/express/lib/router/index.js:164:37)
at param (/home/victor/gestamp-PRUEBAS/node_modules/sails/node_modules/express/lib/router/index.js:138:11)
at param (/home/victor/gestamp-PRUEBAS/node_modules/sails/node_modules/express/lib/router/index.js:135:11)
at pass (/home/victor/gestamp-PRUEBAS/node_modules/sails/node_modules/express/lib/router/index.js:145:5) [TypeError: Object function (ev) {
if (ev == 'newListener') {
return this.$emit.apply(this, arguments);
}
var args = util.toArray(arguments).slice(1)
, lastArg = args[args.length - 1]
, packet = {
type: 'event'
, name: ev
};
if ('function' == typeof lastArg) {
packet.id = ++this.ackPackets;
packet.ack = lastArg.length ? 'data' : true;
this.acks[packet.id] = lastArg;
args = args.slice(0, args.length - 1);
}
packet.args = args;
return this.packet(packet);
} has no method 'on']
events.js:74
throw TypeError('Uncaught, unspecified "error" event.');
^
TypeError: Uncaught, unspecified "error" event.
at TypeError (<anonymous>)
at emit (events.js:74:15)
at ModelStream.end (/home/victor/gestamp-PRUEBAS/node_modules/sails/node_modules/waterline/lib/waterline/utils/stream.js:61:10)
at module.exports.stream (/home/victor/gestamp-PRUEBAS/node_modules/sails/node_modules/waterline/lib/waterline/adapter/stream.js:25:66)
at /home/victor/gestamp-PRUEBAS/node_modules/sails/node_modules/waterline/lib/waterline/query/stream.js:42:20
at process._tickDomainCallback (node.js:492:13)
Sorry, I know I am new with this, I am sure that I am doing something (or a lots of things) wrong, could you help me? All what I want is stream data from Redis to the View. I would like to see in the view every change in Redis.
Thank you all.
There are several questions you are asking so let's try to answer them one by one:
Redis schema format
Redis has support for several data structures out of which sails-redis uses set for indexes and binary string for storing data.
If a part of the data can be used as primary key it can increase your system performance if you use it, if not sails-redis auto generates a primary key based on a sequence.
According to your definitions looks like your schema should look like that:
module.exports = {
connection: 'your redis connection name'
attributes: {
ta : { type: 'string' }
}
}
to start working with it you can use the blueprints api by creating an empty controller.
Retrieving data
To get the data from redis you can use the blueprints api to get the data by the generated id or that you can create a new controller using the find method, this is an example (sorry for calling the model simply Data):
module.exports = {
findByTa: function (req, res) {
Data.find({ where: { ta: req.param('ta') }}).exec(function (err, result) {
res.json(result);
});
}
}
you'll also need to add a route for the new controller function in conf/routes.js
'GET /data/ta/:ta': 'DataController.findByTa',
Streaming data
The stream method is not implemented for sails-redis for now, that's the reason for the error you got.
Notifications on data changes
To get notifications on data changes you need to register the socket for the model to receive new instances and for model instances (records) to receive the update and delete events, this is a sample controller that registers on the model itself and subscribers to all existing instances.
testEvents: function (req, res) {
if (req.isSocket){
Data.watch(req); //register on the model
Data.find({}).exec(function (err, result) {
Data.subscribe(req.socket, result); // subscribe to model instance
});
}
}
A route should be defined for the action:
'GET /data/testEvents': 'DataController.testEvents'
On the client side you should add some code that will call the controller and listen for events:
window.onload = function startListening(){
io.socket.on('data',function(msg){
console.log(msg);
});
io.socket.get('/data/testEvents');
};
How to get list of room in socket.io in latest version (v1.3.7) ?
io.nsps['/namespace'].adapter.rooms
Will return something like this:
{
mIfp3VIpt103xS3RAAAD: { mIfp3VIpt103xS3RAAAD: true },
'9XaViM2Q-A1NGziRAAAE': { '9XaViM2Q-A1NGziRAAAE': true },
science: { '9XaViM2Q-A1NGziRAAAE': true },
'2FopECkAq5Z4AIHMAAAF': { '2FopECkAq5Z4AIHMAAAF': true },
math: { '2FopECkAq5Z4AIHMAAAF': true },
s3JRFhNUz1Tz9apeAAAH: { s3JRFhNUz1Tz9apeAAAH: true }
}
Obviously the only rooms in there was science and math.
I suspect the others are socket id.
How can i only get science and math as a room list?
Socket.io will keep an opened room per connected socket, so that sending a message to a single sockets works:
socket.broadcast.to(id).emit('my message', msg)
You could keep a list of connected sockets id's:
var cached = []
io.on('connection', function(socket) {
if(cached.indexOf(socket.id) === -1)
cached.push(socket.id)
})
Don't forget to remove them on socket disconnection
Then, reduce the list of rooms:
var rooms = io.nsps['/namespace'].adapter.rooms
var realRooms = {}
for(var i in rooms) {
if(~cached.indexOf(i)) {
realRooms[i] = rooms[i]
}
}
There might be other ways of doing this but this is the first I think of.
Since io.nsps['/chatSystem'].adapter.sids will return collection of connected socket, this is how i solve the problem
var realRooms = [];
var rooms = io.nsps['/chatSystem'].adapter.rooms;
var sids = io.nsps['/chatSystem'].adapter.sids;
for ( var room in rooms ) {
if ( !( room in sids ) ) {
realRooms.push( room );
}
}
You can take the list of ROOMS (different to id of each socket/connection) with:
for(R in io.nsps)console.log(R);
this return list of each ROOM in your server/machine.
(nsps is "internal name" of each Namespace)
I want to refresh a Twit stream.
I have a Twitter stream made with the npm module Twit (you can find it here: https://github.com/ttezel/twit ).
Here is my code:
Researches.find().observeChanges({
added: function(){
hashArray = Researches.find().fetch();
hashCount = Researches.find().count();
for(i=0; i<hashCount; i++){
hashArray[i]= hashArray[i].hashtag;
}
}
});
stream = T.stream('statuses/filter', {track: hashArray});
//Launch stream
stream.on('tweet', Meteor.bindEnvironment(function(tweet) {
//Get the hashtag of the tweet
tweetText = tweet.text;
tweetText = tweetText.toLowerCase();
//Get the hashtag of the current tweet
for(i=0; i<hashCount; i++){
var hashCompare = hashArray[i];
hashCompare = hashCompare.toLowerCase();
var isInString = tweetText.search(hashCompare);
if(isInString>=0)
goodHash = hashArray[i];
}
// Get the tweet informations
tweetToInsert = {
user: tweet.user.screen_name,
tweet: tweet.text,
picture: tweet.user.profile_image_url,
date: new Date().getTime(),
hashtag: goodHash
};
matchTweet = Tweets.findOne({tweet:tweetToInsert.tweet});
//Store tweets
if(matchTweet || (lastTweet.user == tweetToInsert.user) || (lastTweet.tweet == tweetToInsert.tweet)){
} else {
console.log(tweetToInsert.tweet);
Tweets.insert(tweetToInsert, function(error) {
if(error)
console.log(error);
});
}
//Store last tweet
lastTweet = {
user: tweetToInsert.user,
tweet: tweetToInsert.tweet
}
//Delete tweet overflow
nbTweet = Tweets.find({hashtag: goodHash}).count();
tweetToDelete = nbTweet-25;
if(nbTweet>25){
for(i=0; i<tweetToDelete;i++){
idDelete = Tweets.findOne({hashtag: goodHash});
Tweets.remove(idDelete._id);
}
}
}));
As you can see, I have an observe on my Researches Collection, with which I made an array with all the hashtag. Then, I made my stream using this array to track every of this hashtag.
Now, here is my problem. When I had a new hashtag to my collection, my array update himself with the new hashtag and is good. The problem is that the stream doesn't update himself.
What I have tried
I have tried to .stop() the stream, accorded to Twit documentation (this works fine), but when I tried to restart him with .start(), it don't work.
Here is the code I've tried:
Researches.find().observeChanges({
added: function(){
hashArray = Researches.find().fetch();
hashCount = Researches.find().count();
for(i=0; i<hashCount; i++){
hashArray[i]= hashArray[i].hashtag;
}
if(stream){
stream.stop();
stream.start();
}
}
});
So, do you know how to refresh/update a Twit stream or delete and created a new one, each time an hashtag is added to the collection.
Thanks
This github issue & comment answers your question: https://github.com/ttezel/twit/issues/90#issuecomment-41247402
Basically you will need to make a second stream and close the first one when you refresh your list.
var Twit = require('twit');
var twit = new Twit(config);
var stream1 = twit.stream('statuses/filter', { track: [ '#yolo' ] });
// ... some time passes ...
// initiate a new streaming connection with our updated track list
var stream2 = twit.stream('statuses/filter', { track: [ '#yolo', '#fun' ] });
stream2.once('connected', function (res) {
console.log('second stream connected')
// stop the first stream ASAP so twitter doesn't block us
stream1.stop();
stream2.on('tweet', function (tweet) {
// handle tweets
});
});