Skip to content

Commit b26598d

Browse files
added facade methods and individual client communication
1 parent 4945c42 commit b26598d

File tree

9 files changed

+151
-27
lines changed

9 files changed

+151
-27
lines changed

README.md

Lines changed: 47 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ BROADCAST_DRIVER=socketio
6969
**Example**
7070

7171
```python
72+
# broadcast to given channels
73+
7274
from masonite.controllers import Controller
7375
from masonite.broadcasting import Broadcast
7476

@@ -81,13 +83,54 @@ class YourController(Controller):
8183
broadcast.channel(["channel-name"], "event-name", broadcast_data)
8284
```
8385

86+
```python
87+
# broadcast to selected users/clients
88+
89+
from masonite.controllers import Controller
90+
from masonite.broadcasting import Broadcast
91+
from socketio_driver.facades import Communicator
92+
93+
class YourController(Controller):
94+
95+
def your_function(self, broadcast: Broadcast):
96+
broadcast_data = {
97+
"message": "Hello World"
98+
}
99+
clients = Communicator.clients()
100+
ids = [client.socketID for client in clients]
101+
broadcast.channel(ids, "event-name", broadcast_data)
102+
103+
104+
def or_another_function(self, broadcast: Broadcast):
105+
broadcast_data = {
106+
"message": "Hello World"
107+
}
108+
clients = Communicator.clients()
109+
broadcast.driver("socketio").user(clients[0]).send("event-name", broadcast_data)
110+
```
111+
84112
**Helpers**
85113

86-
List all connected clients:
87-
114+
Facade helpers...
115+
88116
```python
89117
from socketio_driver.facades import Communicator
118+
```
119+
120+
```python
121+
# List all clients:
122+
Communicator.clients()
123+
```
124+
```python
125+
# Get client by sessionID
126+
Communicator.client(id='client-session-id')
127+
```
128+
```python
129+
# Delete all clients
130+
Communicator.delete_all_clients()
131+
```
132+
```python
133+
# Delete client by SocketClient instance
134+
Communicator.delete(client)
90135

91-
communicator.clients() # get list of connected clients
92-
communicator.client(id='client-id') # get client by id, id is basically a socket.io session id
93136
```

src/socketio_driver/communicator.py

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import redis
33
import msgpack
44
from .models.socket_client import SocketClient
5+
from masonite.auth.Sign import Sign
56

67

78
class Communicator:
@@ -117,18 +118,32 @@ def clients(self):
117118
return [self.client(key) for key in client_keys]
118119

119120
def client(self, id):
120-
client_data = self._client.hmget(id, "userID", "address", "sessionId", "connected")
121+
client_data = self._client.hmget(
122+
id, "userID", "address", "sessionID", "socketID", "connected"
123+
)
121124
if client_data is None:
122125
return None
123126

124-
userID, address, sessionID, connected = client_data
127+
userID, address, sessionID, socketID, connected = client_data
125128

126129
userID = userID.decode("utf-8") if userID is not None else None
127130
address = address.decode("utf-8") if address is not None else None
128131
sessionID = sessionID.decode("utf-8") if sessionID is not None else None
132+
socketID = socketID.decode("utf-8") if socketID is not None else None
129133
connected = connected.decode("utf-8") if connected is not None else False
130-
131-
return SocketClient(userID, address, sessionID, connected == "true")
132-
133-
def authenticate(self):
134-
return True
134+
return SocketClient(userID, address, sessionID, socketID, connected == "true")
135+
136+
def delete_all_clients(self):
137+
keys = self._client.keys(pattern="mbroadcast:users:*")
138+
for key in keys:
139+
self._client.delete(key)
140+
141+
def delete(self, client: SocketClient):
142+
if client is None:
143+
return False
144+
self._client.delete(f"mbroadcast:users:{client.sessionID}")
145+
146+
def authenticate(self, channel, socket_id, user_id):
147+
string_to_sign = "%s:%s:%s" % (socket_id, channel, user_id)
148+
auth = Sign().sign(string_to_sign)
149+
return {"auth": auth}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
from masonite.controllers import Controller
2+
from masonite.broadcasting import Broadcast
3+
from masonite.request import Request
4+
from masonite.response import Response
5+
6+
7+
class BroadcastController(Controller):
8+
def authorize(self, request: Request, broadcast: Broadcast, response: Response):
9+
if not request.user():
10+
return response.json({"message": "unauthorized"}, 204)
11+
12+
return broadcast.driver("socketio").authorize(
13+
request.input("channel_name"), request.input("socket_id"), request.user().id
14+
)

src/socketio_driver/drivers/socket_driver.py

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
1+
from ..models.socket_client import SocketClient
2+
3+
14
class SocketDriver:
25
def __init__(self, application) -> None:
36
self.application = application
47
self.connection = None
8+
self.client: SocketClient = None
9+
self.channels: list = []
510

611
def set_options(self, options):
712
self.options = options
@@ -16,9 +21,32 @@ def get_connection(self):
1621

1722
return self.connection
1823

24+
def user(self, client: SocketClient):
25+
self.client = client
26+
return self
27+
28+
def channels(self, channels):
29+
self.channels = channels
30+
return self
31+
32+
def send(self, event, value):
33+
connection = self.get_connection()
34+
if self.client is not None:
35+
self.channel(self.client.socketID, event, value)
36+
elif len(self.channels) > 0:
37+
for channel in self.channels:
38+
self.channel(channel, event, value)
39+
else:
40+
connection.Emit(event, value)
41+
42+
self.client = None
43+
self.channels = []
44+
1945
def channel(self, channel, event, value):
2046
connection = self.get_connection()
2147
return connection.To(channel).Emit(event, value)
2248

23-
def authorize(self, channel, socket_id):
24-
return self.get_connection().authenticate(channel=channel, socket_id=socket_id)
49+
def authorize(self, channel, socket_id, user_id):
50+
return self.get_connection().authenticate(
51+
channel=channel, socket_id=socket_id, user_id=user_id
52+
)

src/socketio_driver/facades/communicator.pyi

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,9 @@ class Communicator:
77
def client(id: str) -> SocketClient | None:
88
"""Returns a client by id."""
99
...
10+
def delete(id: str) -> bool:
11+
"""Deletes a client by id."""
12+
...
13+
def delete_all_clients() -> None:
14+
"""Deletes all clients."""
15+
...

src/socketio_driver/models/socket_client.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,14 @@ class SocketClient:
66
userID: str
77
address: str
88
sessionID: str
9+
socketID: str
910
connected: bool
11+
12+
def to_json(self):
13+
return {
14+
"userID": self.userID,
15+
"address": self.address,
16+
"sessionID": self.sessionID,
17+
"socketID": self.socketID,
18+
"connected": self.connected,
19+
}

tests/integrations/app/controllers/WelcomeController.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,19 @@ class WelcomeController(Controller):
99
"""WelcomeController Controller Class."""
1010

1111
def show(self, view: View, broadcast: Broadcast):
12-
users = Communicator.clients()
13-
print(users)
1412
return view.render("welcome")
1513

14+
def users(self):
15+
# Communicator.delete_all_clients()
16+
users = Communicator.clients()
17+
18+
return {"users": [user.to_json() for user in users]}
19+
1620
def broadcast(self, view: View, broadcast: Broadcast):
1721
broadcast_data = {"message": "Hello World"}
18-
broadcast.channel(["default"], "welcome", broadcast_data)
22+
users = Communicator.clients()
23+
for user in users:
24+
broadcast.driver("socketio").user(user).send("message", user.to_json())
25+
26+
broadcast.channel(["mbroadcast"], "message", broadcast_data)
1927
return {"hello": "world!"}

tests/integrations/routes/web.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,6 @@
22

33
ROUTES = [
44
Route.get("/", "WelcomeController@show"),
5+
Route.get("/users", "WelcomeController@users"),
56
Route.get("/broadcast", "WelcomeController@broadcast"),
67
]

tests/integrations/templates/welcome.html

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -39,27 +39,26 @@
3939

4040
const broadcast = new MasoniteBroadcastClient({
4141
url: 'http://localhost:3000/',
42+
// broadcastUrl: 'http://localhost:8000/pusher/auth',
4243
})
4344

44-
broadcast.listen("welcome", (data) => {
45-
console.log(data);
46-
});
47-
48-
broadcast.listen("chat", (data) => {
49-
console.log("Chat Data", data);
45+
broadcast.onClientConnected(function (user) {
46+
console.log(user);
5047
})
5148

52-
broadcast.listen("PublicEvent", (data) => {
53-
console.log(data);
54-
});
49+
const subscription = broadcast.subscribe(channel)
5550

56-
broadcast.listen("PrivateEvent", (data) => {
51+
subscription.listen("message", (data) => {
52+
console.log(data);
53+
}).listen("PublicEvent", (data) => {
54+
console.log(data);
55+
}).listen("PrivateEvent", (data) => {
5756
console.log(data);
5857
});
5958

6059
const TestButton = document.getElementById("test");
6160
TestButton.addEventListener("click", () => {
62-
broadcast.speak("welcome", {
61+
subscription.emit("message", {
6362
message: "Hello World",
6463
});
6564
});

0 commit comments

Comments
 (0)