Change channel dynamic with laravel,Socket and Redis - laravel-5

!!!
Server - socket How can change channal dynamic
When I broadcast using /fire/1 for example, I only want to send to /room/1.
Currently it sends to /room/1, /room/2, /room/3, etc. Because by default here, everything on the server is subscribed to 'test-channel'. I just can't figure this out.
var server = require('http').Server();
var io = require('socket.io')(server);
var Redis = require('ioredis');
var redis = new Redis();
io.on('connection', function(socket){
console.log('New User Conected here');
redis.subscribe('test-channel');
redis.on('message', function(subscribed ,channel, message) {
console.log(channel);
message = JSON.parse(message);
socket.emit(channel + ':' + message.event, message.data);
});
socket.on('joinRoom', function(room ){
console.log('Join in this Room '+ room);
socket.join(room);
});
});
server.listen(3000);
event | php
public function broadcastOn()
{
return ['test-channel']; // static
}

Add a property to your broadcast event, then pass it to the constructor.
class Message implements ShouldBroadcast{
use SerializesModels;
protected $channel;
public function __construct($channel){
$this->channel = $channel;
}
public function broadcastOn(){
return [$this->channel];
}
}
Then when you fire the event, pass the channel in: event(new Message($channel));

Related

MVC CORE SignalR send messages specific user only

Im using SignalR Core and ASPNET Core 2.2 and try to to send message to single user. for all user it's working fine.
Csharp code
[HubMethodName("SendMessageToUser")]
public Task DirectMessage(string user, string message)
{
//eg.user = abcd#gmail.com
return Clients.User(user).SendAsync("ReceiveMessage", message);
}
JS code
connection.on("ReceiveMessage", function (user, message) {
var msg = message.replace(/&/g, "&").replace(/</g,"<").replace(/>/g, ">");
var encodedMsg = user + " says " + msg;
var li = document.createElement("li");
li.textContent = encodedMsg;
document.getElementById("messagesList").appendChild(li);
});
You can send message to specific user using their connection id so
I need to create a hub like this. The GetConnectionId will return the connect id of that user when they connect to signalR hub
public class ConnectionHub : Hub
{
public async Task Send(string userId)
{
var message = $"Send message to you with user id {userId}";
await Clients.Client(userId).SendAsync("ReceiveMessage", message);
}
public string GetConnectionId()
{
return Context.ConnectionId;
}
}
Then in startup.cs
services.AddSignalR();
app.UseSignalR(routes =>
{
routes.MapHub<ConnectionHub>("/connectionHub");
});
Then the client side code
(function () {
var connection = new signalR.HubConnectionBuilder().withUrl("/connectionHub").build();
connection.start().then(function () {
console.log("connected");
connection.invoke('getConnectionId')
.then(function (connectionId) {
sessionStorage.setItem('conectionId', connectionId);
// Send the connectionId to controller
}).catch(err => console.error(err.toString()));;
});
$("#sendmessage").click(function () {
var connectionId = sessionStorage.getItem('conectionId');
connection.invoke("Send", connectionId);
});
connection.on("ReceiveMessage", function (message) {
console.log(message);
});
})();
First I invoke getConnectionId in the hub to get user connection id then I can send message to specific user using their connection id.
Hope that help

Laravel with Socket/Redis - Private channel routes not working

I am kinda stuck in broadcast routes. i setup a socket server with redis and configured it with Laravel. For public channel ,everything is working fine but when it comes to private or presence channel, it is somehow bypassing laravel broadcast routes. Can't figured out how & why.
i have attached a repo link so you guys can explore it too. Plus some quick bits are also below.
https://github.com/bilahdsid/socket-laravel/tree/socket
TestEvent.php
class TestEvent implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets;
/**
* Create a new event instance.
*
* #return void
*/
public $data;
public function __construct()
{
$this->data = array(
'power'=> '10'
);
}
public function broadcastOn()
{
return new PrivateChannel('test-channel1');
}
public function broadcastWith()
{
return $this->data;
}
}
server.js
var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
var Redis = require('ioredis');
var redis = new Redis();
redis.subscribe('private-test-channel1', function(err, count) {
console.log(err);
});
redis.on('connection',function (socket,channel) {
console.log(socket+''|+channel);
});
redis.on('message', function(channel, message) {
console.log('Message Recieved: ' + message);
message = JSON.parse(message);
io.emit(channel + ':' + message.event, message.data);
});
http.listen(3000, function(){
console.log('Listening on Port 3000');
});
io.on('connection', function(socket){
console.log('a user connected');
});
routes/web-- for firing
Route::get('/', function () {
return view('home');
});
Route::get('fire', function () {
// this fires the event
broadcast(new App\Events\TestEvent());
return "event fired";
});
routes/channel.php -- below line doesn't work-- main issue
Broadcast::channel('private-test-channel', function ($user, $id) {
echo '1111'; exit;
return (int) $user->id === (int) $id;
});
Thanks.
As far as I can see you are defining a channel with the name: test-channel1:
public function broadcastOn()
{
return new PrivateChannel('test-channel1');
}
but in routes/channels.php:
Broadcast::channel('private-test-channel', function ($user, $id) {
Sounds like a typo!

Laravel echo does not receive broadcast message

Here is event
<?php
namespace App\Events;
use Illuminate\Broadcasting\Channel;
use Illuminate\Queue\SerializesModels;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
class FieldWasUpdated implements ShouldBroadcast
{
use InteractsWithSockets, SerializesModels;
public $instance;
public $key;
public $value;
/**
* Create a new event instance.
*
* #return void
*/
public function __construct($instance, $key, $value)
{
$this->instance = $instance;
$this->key = $key;
$this->value = $value;
}
/**
* Get the channels the event should broadcast on.
*
* #return Channel|array
*/
public function broadcastOn()
{
return new Channel("model." . $this->instance->getModelType() . "." . $this->instance->id);
}
//public function broadcastAs() {
// return "FieldWasUpdated"; commented out for testing
//}
}
Here is frontend code
window.Echo = new window.LaravelEcho({
broadcaster: 'socket.io',
host: window.location.hostname + ':3000'
});
// I use different types of `listen` for testing
Echo.channel("model.ANNOUNCEMENT.2")
.listen(".*", function(e) {
console.log(e);
})
.listen("*", function(e) {
console.log(e);
})
.listen("FieldWasUpdated", function(e) {
console.log(e);
})
.listen(".FieldWasUpdated", function(e) {
console.log(e);
})
node.js code
var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
var Redis = require('ioredis');
var redis = new Redis();
http.listen(3000, function(){
console.log('Listening on Port 3000');
});
redis.psubscribe('*', function(err, count) {
console.log(err, count);
});
redis.on('pmessage', function(subscribed, channel, message) {
// different types of emit for testing
message = JSON.parse(message);
const ev = channel + ":" + message.event;
io.emit(ev, message.data);
io.emit(channel, message);
io.emit(message, channel);
io.emit(channel, message.event, message.data);
io.emit(channel, message);
io.emit(channel, message.data, message.event);
});
Here is what I see in chrome development tools when Event is fired inside laravel app
So websocket is working but no one handler is called.
When I uncomment broadcastAs function nothing is changed.
When I rename channel to test-channel in backend and frontend - nothing changes.
After some time found a solution:
Server:
redis.psubscribe('*', function(err, count) {
console.log(err, count);
});
redis.on('pmessage', function(subscribed, channel, message) {
message = JSON.parse(message);
io.emit(message.event, channel, message.data);
});
Client
// both handlers process same event
Echo.channel("model.ANNOUNCEMENT.2")
.listen("FieldWasUpdated", function(e) {
console.log(e);
})
.listen(".App.Events.FieldWasUpdated", function(e) {
console.log(e);
})

How to guard private channels when using socket.io and Laravel Echo?

Here is my server.js file:
var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
var Redis = require('ioredis');
var redis = new Redis();
http.listen(3000, function(){
console.log('Listening on Port 3000');
});
redis.psubscribe('*', function(err, count) {
console.log(err, count);
});
redis.on('pmessage', function(subscribed, channel, message) {
message = JSON.parse(message);
io.emit(message.event, channel, message.data);
});
And a simple event:
class FieldWasUpdated implements ShouldBroadcast
{
use InteractsWithSockets, SerializesModels;
public $instance;
public $key;
public $value;
public function __construct($instance, $key, $value)
{
$this->instance = $instance;
$this->key = $key;
$this->value = $value;
}
public function broadcastOn()
{
return new PrivateChannel("model." . $this->instance->getModelType() . "." . $this->instance->id);
}
}
The client connects to socket.io:
Echo = new Echo({
broadcaster: 'socket.io',
host: window.location.hostname + ':3000'
});
And then listens for events (it is inside a Blade template):
var channel = "model.{{ $instance->getModelType() }}.{{ $instance->id }}";
Echo.private(channel)
.listen("FieldWasUpdated", function(e) {
window.VueBus.$emit("updated", channel, e.key, e.value);
})
.listen("FieldBecameDisabled", function(e) {
window.VueBus.$emit("disabled", channel, e.key);
});
Problem is: authentication is not handled, any user can subscribe to these channels.
Broadcast::channel("model.announcement.*", function($user, $id) {
return false; // this function is not called
})
Here is a sample event from the Chrome developer console (WebSocket):
[
"App\\Events\\FieldWasUpdated",
"private-model.announcement.2",
{
"instance":
{
"type":"ANN_TYPE_MISSED_CALL",
"status":"ANN_STATUS_CANCELLED",
"name":"1233421",
"phone":"+7(222)222-3322",
"email":"sdgsg#mail.com",
"message":"sdgdsgsdgdfdg",
"requirement":null,
"web_type":"ANN_WEB_TYPE_UNKNOWN",
"url":null,
"responsible_id":19,
"recommender_id":18
},
"key":"message",
"value":"sdgdsgsdgdfdg"
}
]
Also there is no /broadcast/auth URL, but BroadcastServiceProvider has a call to Broadcast::routes(); and when the browser loads, no calls to /broadcast/auth occur.
The Laravel documentation goes into detail about this in the Broadcasting chapter: https://laravel.com/docs/5.5/broadcasting#presence-channels. The Authorizing Presence Channels section should be the same for private channels.

How to notify client of successful/failed subscription?

I'm subscribing to 'queue:1.2.3'. How does the client know if they have successfully subscribed? Is there a mechanism for the server to respond to the client?
Below is a simple program that I'm using to test the subscriptions. I'm not sure what to show on the server side.
var sess;
var wsuri = 'ws://test.lan:8000';
window.onload = function() {
// connect to WAMP server
ab.connect(wsuri,
// WAMP session was established
function (session) {
sess = session;
console.log("Connected to " + wsuri);
},
// WAMP session is gone
function (code, reason) {
sess = null;
console.log("Connection lost (" + reason + ")");
}
);
};
function subscribe()
{
sess.subscribe("queue:1.2.3", topicReceived)
}
function topicReceived(topicUri, event)
{
console.log(topicUri + ' ' + event)
}
I feel like this post touches on my question: Calling dispatch in subscribe in Autobahn
Not in WAMPv1. A acknowledgement (with subsription ID) or a subscribe error (with error URI) will be part of WAMPv2 which is currently in the cooking.

Resources