Closed joaokamun closed 5 years ago
I also want this!
For now (maybe it is a wrong way, but it works) I just have created a class that extends BeyondCode\LaravelWebSockets\WebSockets\WebSocketHandler
, and a custom Router, and put my own logic in certain hooks...
This would be really cool but possibly also tricky looking at the docs it has a few "specialties" that requires some work to make this work correctly.
For example: https://pusher.com/docs/webhooks#delay
Just spit-ballin' here, but it might be a good one to write events to an Redis buffer/list and let another process/cron handle the actual sending to prevent adding to much work to the websocket server process causing it to block more than needed (which also would be an easy-ish way to get batched hook working easily).
@stayallive So basically the main feature to be develop here could be just an option to save events somewhere (like redis), and each individual project cares about what to do with the stored events?
@joaokamun well, I would implement a php artisan websockets:webhooks
command or something like that in this package so you could just add to a cron or daemon running that command which handles all the webhook sending.
I also want this! For now (maybe it is a wrong way, but it works) I just have created a class that extends
BeyondCode\LaravelWebSockets\WebSockets\WebSocketHandler
, and a custom Router, and put my own logic in certain hooks...
@enzonotario could you please share an example of what you've done? Thanks!
Update: Managed to get it done by extending the websockets.router
singleton in the IoC. Thanks for the idea, works nice and smooth. However, a nice webhooks implementation that allows 2 way communication between Laravel and the frontend would be amazing. CC @mpociot @freekmurze
@stefandanaita sorry for the delay... I just have registered a singleton in my AppServiceProvider
:
public function register()
{
$this->app->singleton('websockets.router', function () {
return new Router();
});
}
Router.php
<?php
namespace App\WebSockets\Server;
use App\WebSockets\WebSockets\WebSocketHandler;
use BeyondCode\LaravelWebSockets\HttpApi\Controllers\FetchChannelController;
use BeyondCode\LaravelWebSockets\HttpApi\Controllers\FetchChannelsController;
use BeyondCode\LaravelWebSockets\HttpApi\Controllers\FetchUsersController;
use BeyondCode\LaravelWebSockets\HttpApi\Controllers\TriggerEventController;
class Router extends \BeyondCode\LaravelWebSockets\Server\Router
{
public function echo()
{
$this->get('/app/{appKey}', WebSocketHandler::class);
$this->post('/apps/{appId}/events', TriggerEventController::class);
$this->get('/apps/{appId}/channels', FetchChannelsController::class);
$this->get('/apps/{appId}/channels/{channelName}', FetchChannelController::class);
$this->get('/apps/{appId}/channels/{channelName}/users', FetchUsersController::class);
}
}
WebSocketHandler.php
<?php
namespace App\WebSockets\WebSockets;
use Ratchet\ConnectionInterface;
use Ratchet\RFC6455\Messaging\MessageInterface;
class WebSocketHandler extends \BeyondCode\LaravelWebSockets\WebSockets\WebSocketHandler
{
public function onMessage(ConnectionInterface $connection, MessageInterface $message)
{
// My custom code...
parent::onMessage($connection, $message);
}
}
but sure, this is a weird way. Extending as you said in #21 I think that is a better way!
@stefandanaita You sir are BRILLIANT!!!!!!!!!! This was a lifesaver!!! Thank you so much. Quick question though, do you know i can know which client disconnect from the app when the connection is closed? That's the whole point of extending this package. I need to tell the chat app he is offline after a non graceful log out. Thanks.
@stefandanaita You sir are BRILLIANT!!!!!!!!!! This was a lifesaver!!! Thank you so much. Quick question though, do you know i can know which client disconnect from the app when the connection is closed? That's the whole point of extending this package. I need to tell the chat app he is offline after a non graceful log out. Thanks.
@nicolasvahidzein Did you figured out how to identify user once we extend the WebSocketHandler?
Hi, In my custom onClose function how can I get user's id or username if I use presence channels?
@noobshooter27 The connection can make use of the channel manager and you can get the connection from there.
2.x will contain extendable hooks: https://github.com/beyondcode/laravel-websockets/pull/465
Hi @rennokki , may I know how can I get the private channel name from the $connection
in my custom onClose
function?
Tried this $this->channelManager->getChannels($connection->app->id)
, but it seem like don't have the channel name inside.
Hi @rennokki , may I know how can I get the private channel name from the
$connection
in my customonClose
function? Tried this$this->channelManager->getChannels($connection->app->id)
, but it seem like don't have the channel name inside.
@hengjingyoong
try dd($this->channelManager);
you'll find everything there
@hengjingyoong Have you called parent::onClose($connection, $exception)
before anything else in your custom method?
@rennokki, I just used the original WebSocketHandler to test it out.
Thanks @noobshooter27 , finally I managed to find out what is the channel name from the closing connection.
What I did was defined a custom function in BeyondCode\LaravelWebSockets\WebSockets\Channels\Channel
public function checkConnection($socketId)
{
if ($this->subscribedConnections[$socketId] ?? null) {
return $this->channelName;
}
return false;
}
and then in BeyondCode\LaravelWebSockets\WebSockets\WebSocketHandler
, get the channel name before it closed.
public function onClose(ConnectionInterface $connection)
{
$allChannels = $this->channelManager->getChannels($connection->app->id);
foreach($allChannels as $channel){
if ($channelName = $channel->checkConnection($connection->socketId)) {
break;
}
}
dd($channelName);
$this->channelManager->removeFromAllChannels($connection);
DashboardLogger::disconnection($connection);
StatisticsLogger::disconnection($connection);
}
But I'm not sure if that is secure to work in this way.
ps: my working scenario is attach the user_id to the private channel name, that's why I need to get back the user_id from channel name, and notify my backend which user is offline. This is to handle the non graceful log out
mentioned by @nicolasvahidzein
@hengjingyoong It's highly recommended to not change any vendor/*
files.
@hengjingyoong It's highly recommended to not change any
vendor/*
files.
Do you have any though on getting the channel name without adding my own function in BeyondCode\LaravelWebSockets\WebSockets\Channels\Channel
?
Finally I figure out how to get the closing channel name.
Here is it.
<?php
namespace App\WebSocket;
use Ratchet\ConnectionInterface;
use BeyondCode\LaravelWebSockets\WebSockets\WebSocketHandler as BaseWebSocketHandler;
class CustomWebSocketHandler extends BaseWebSocketHandler
{
public function onClose(ConnectionInterface $connection)
{
$allChannels = $this->channelManager->getChannels($connection->app->id);
foreach($allChannels as $channelName => $channel){
if($channel->getSubscribedConnections()[$connection->socketId] ?? null) {
$closingChannel = $channel->getChannelName();
break;
}
}
dd($closingChannel);
parent::onClose($connection);
}
}
ps: I was missed out the getSubscribedConnections
function in BeyondCode\LaravelWebSockets\WebSockets\Channels\Channel
lol
Thank you @hengjingyoong! I made just had to do some minor changes because it was throwing an error, I will leave it here in case someone else needs it
<?php
namespace App\WebSocket;
use Ratchet\ConnectionInterface;
use BeyondCode\LaravelWebSockets\WebSockets\WebSocketHandler as BaseWebSocketHandler;
class CustomWebSocketHandler extends BaseWebSocketHandler
{
public function onClose(ConnectionInterface $connection)
{
$allChannels = $this->channelManager->getChannels($connection->app->id);
$closingChannel = null;
foreach($allChannels as $channelName => $channel){
if($channel->getSubscribedConnections()[$connection->socketId] ?? null) {
$closingChannel = $channelName;
break;
}
}
dd($closingChannel);
parent::onClose($connection);
}
}
Thank you @hengjingyoong! I made just had to do some minor changes because it was throwing an error, I will leave it here in case someone else needs it
<?php namespace App\WebSocket; use Ratchet\ConnectionInterface; use BeyondCode\LaravelWebSockets\WebSockets\WebSocketHandler as BaseWebSocketHandler; class CustomWebSocketHandler extends BaseWebSocketHandler { public function onClose(ConnectionInterface $connection) { $allChannels = $this->channelManager->getChannels($connection->app->id); $closingChannel = null; foreach($allChannels as $channelName => $channel){ if($channel->getSubscribedConnections()[$connection->socketId] ?? null) { $closingChannel = $channelName; break; } } dd($closingChannel); parent::onClose($connection); } }
Hi thank you for your code but I need users data inside of $allChannels how can i get it? (In my case I use presence channel)
Another approach that is simple is using the custom ArrayChannelManager as indicated in the websockets config, and then make a call like:
public function removeFromAllChannels(ConnectionInterface $connection)
{
// THIS ONE LINE
$response = Http::get(config('app.url') . '/socket-disconnected/' . $connection->socketId . '/{key-for-security}');
if (!isset($connection->app)) {
return;
}
....
}
In web.php:
use App\Http\Controllers\WebsocketController;
Route::get('/socket-disconnected/{socket_id}/{security_key}', [WebsocketController::class, 'socketDisconnected']);
Dispatch the desired job in WebsocketController:
class WebsocketController extends Controller
{
public function socketDisconnected(Request $request, string $socket_id, string $security_key)
{
logger("Socket id received: " . $socket_id);
if($security_key == config('websockets.security_key')) {
ProcessWebsocketDisconnects::dispatch($socket_id);
}
return response('OK', 200)->header('Content-Type', 'text/plain');
}
}
Thank you @garrettboone I will have to try this soon. Sounds awesome.
The only missing feature for me fully migrate from pusher is the event webhooks, I could not find anything about it. Is something planned to have? I can contribute to that, just want to know if you guys have something in mind or some direction to follow.
Regards,