Skip to content

Commit cb9af5d

Browse files
authored
Merge pull request #7 from SEPIA-Framework/dev
v1.1.1 b1
2 parents e8941ed + ffe8442 commit cb9af5d

File tree

6 files changed

+218
-109
lines changed

6 files changed

+218
-109
lines changed

pom.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
<groupId>net.b07z.sepia.websockets</groupId>
55
<artifactId>sepia-websockets</artifactId>
6-
<version>1.1.0</version>
6+
<version>1.1.1</version>
77
<name>SEPIA webSocket server and client</name>
88
<description>WebSockets server and client to offer messages (chat and data) in SEPIA</description>
99

@@ -86,7 +86,7 @@
8686
<dependency>
8787
<groupId>net.b07z.sepia.server.core</groupId>
8888
<artifactId>sepia-core-tools</artifactId>
89-
<version>2.2.0</version>
89+
<version>2.2.1</version>
9090
</dependency>
9191
<dependency>
9292
<groupId>com.j2html</groupId>

src/main/java/net/b07z/sepia/websockets/common/SocketChannel.java

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,11 @@
55
import java.util.Set;
66
import java.util.concurrent.ConcurrentSkipListSet;
77

8+
import org.json.simple.JSONArray;
9+
import org.json.simple.JSONObject;
10+
811
import net.b07z.sepia.server.core.server.ConfigDefaults;
12+
import net.b07z.sepia.server.core.tools.JSON;
913

1014
/**
1115
* This class represents a default private channel on the webSocketServer for registered users.
@@ -15,22 +19,58 @@
1519
*/
1620
public class SocketChannel {
1721

22+
//Some fixed channel IDs (don't allow users to create them!)
23+
public static final String OPEN_WORLD = "openWorld";
24+
public static final String INFO = "info";
25+
public static final String UNKNOWN = "unknown";
26+
public static final List<String> systemChannels = new ArrayList<>();
27+
static {
28+
systemChannels.add(OPEN_WORLD);
29+
systemChannels.add(INFO);
30+
systemChannels.add(UNKNOWN);
31+
}
32+
1833
private String channelId; //name of the channel
1934
private String channelKey; //password to access channel
2035

2136
private String owner; //admin of the channel
2237
private boolean isOpen = false;
2338
private Set<String> members; //users allowed to be in this channel
2439

40+
/**
41+
* Create new channel
42+
* @param id - unique channel ID
43+
* @param key - security key
44+
* @param owner - channel owner (user ID)
45+
*/
2546
public SocketChannel(String id, String key, String owner){
2647
this.channelId = id;
2748
this.channelKey = key;
49+
this.owner = owner;
2850
this.members = new ConcurrentSkipListSet<String>();
2951
if (key.equals("open")){
3052
this.isOpen = true;
3153
this.members.add(ConfigDefaults.defaultAssistantUserId);
3254
}
3355
}
56+
/**
57+
* Import channel data from JSON object previously generated with {@link #getJson()}.
58+
*/
59+
public SocketChannel(JSONObject channelJson){
60+
this.channelId = JSON.getString(channelJson, "channel_id");
61+
this.channelKey = JSON.getString(channelJson, "channel_key");
62+
this.isOpen = JSON.getBoolean(channelJson, "public");
63+
this.owner = JSON.getString(channelJson, "owner");
64+
this.members = new ConcurrentSkipListSet<String>();
65+
JSONArray membersArray = JSON.getJArray(channelJson, "members");
66+
for (Object o : membersArray){
67+
this.members.add(o.toString());
68+
}
69+
if (this.isOpen){
70+
//might already be in members but just to make sure ...
71+
this.members.add(ConfigDefaults.defaultAssistantUserId);
72+
}
73+
}
3474

3575
@Override
3676
public String toString(){
@@ -121,4 +161,22 @@ public boolean isUserMemberOfChannel(SocketUser user){
121161
return members.contains(user.getUserId());
122162
//return getAllMembers().contains(user);
123163
}
164+
165+
/**
166+
* Get channel data as JSON object.
167+
*/
168+
public JSONObject getJson(){
169+
JSONObject channel = new JSONObject();
170+
JSON.put(channel, "channel_id", channelId);
171+
JSON.put(channel, "channel_key", channelKey);
172+
JSON.put(channel, "owner", owner);
173+
JSON.put(channel, "public", isOpen);
174+
JSON.put(channel, "members", members.toArray());
175+
/*
176+
JSON.put(channel, "channel_name", ""); //not yet implemented
177+
JSON.put(channel, "assistants", new JSONArray()); //not yet implemented
178+
JSON.put(channel, "info", new JSONObject()); //not yet implemented
179+
*/
180+
return channel;
181+
}
124182
}

src/main/java/net/b07z/sepia/websockets/common/SocketMessage.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ public static enum DataType{
3333
byebye, //has optional data entry "username"
3434
upgradeClient, //has "role" as data entry
3535
assistAnswer, //has data for assistant communication
36+
assistFollowUp, //mostly same as assistAnswer but given as self-initiated follow-up message (clients could theoretically block this to avoid spam)
3637
directCmd, //has a direct cmd for assistant
3738
remoteAction, //has a remote action like ASR trigger or hotkey submit
3839
errorMessage //combined with TextType.status this message will be displayed as error line in channel (ignores normal status msg settings)
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
package net.b07z.sepia.websockets.endpoints;
2+
3+
import java.util.ArrayList;
4+
import java.util.List;
5+
6+
import org.json.simple.JSONArray;
7+
import org.json.simple.JSONObject;
8+
9+
import net.b07z.sepia.server.core.data.Role;
10+
import net.b07z.sepia.server.core.server.RequestParameters;
11+
import net.b07z.sepia.server.core.server.RequestPostParameters;
12+
import net.b07z.sepia.server.core.server.SparkJavaFw;
13+
import net.b07z.sepia.server.core.tools.JSON;
14+
import net.b07z.sepia.server.core.users.Account;
15+
import net.b07z.sepia.websockets.common.SocketChannel;
16+
import net.b07z.sepia.websockets.common.SocketChannelPool;
17+
import spark.Request;
18+
import spark.Response;
19+
20+
/**
21+
* Create, join and remove channels.
22+
*
23+
* @author Florian Quirin
24+
*/
25+
public class ChannelManager {
26+
27+
/**
28+
* Create a new channel.
29+
*/
30+
public static String createChannel(Request request, Response response){
31+
//get parameters (or throw error)
32+
RequestParameters params = new RequestPostParameters(request);
33+
34+
//authenticate
35+
Account userAccount = new Account();
36+
if (userAccount.authenticate(params)){
37+
//log.info("Authenticated: " + userAccount.getUserID() + ", roles: " + userAccount.getUserRoles()); //debug
38+
if (userAccount.hasRole(Role.developer.name())){
39+
//get parameters
40+
String channelId = params.getString("channelId");
41+
JSONArray initialMembers = params.getJsonArray("members");
42+
String owner = userAccount.getUserID();
43+
boolean isPublic = params.getBoolOrDefault("isPublic", false);
44+
boolean addAssistant = params.getBoolOrDefault("addAssistant", true);
45+
46+
if (channelId == null || channelId.isEmpty()){
47+
JSONObject msgJSON = JSON.make("result", "fail", "error", "missing channelId");
48+
return SparkJavaFw.returnResult(request, response, msgJSON.toJSONString(), 200);
49+
}else{
50+
//filter system channels (user can't create them)
51+
if (SocketChannel.systemChannels.contains(channelId)){
52+
JSONObject msgJSON = JSON.make("result", "fail", "error", "invalid channelId");
53+
return SparkJavaFw.returnResult(request, response, msgJSON.toJSONString(), 200);
54+
}
55+
//filter valid channel names
56+
String cleanChannelId = channelId.replaceAll("[^\\w\\s]","").replaceAll("\\s+", " ");
57+
if (!cleanChannelId.equals(channelId)){
58+
JSONObject msgJSON = JSON.make("result", "fail", "error", "invalid channelId, please use only letters, numbers and '_'.");
59+
return SparkJavaFw.returnResult(request, response, msgJSON.toJSONString(), 200);
60+
}
61+
}
62+
try{
63+
//create channel
64+
String channelKey = SocketChannelPool.createChannel(channelId, owner, isPublic);
65+
if (channelKey != null){
66+
List<String> members = new ArrayList<>();
67+
members.add(owner);
68+
if (initialMembers != null && !initialMembers.isEmpty()){
69+
for (Object o : initialMembers){
70+
members.add((String) o);
71+
}
72+
}
73+
SocketChannel sc = SocketChannelPool.getChannel(channelId);
74+
for (String s : members){
75+
sc.addUser(s, channelKey);
76+
//System.out.println("Member: " + s); //DEBUG
77+
}
78+
if (addAssistant){
79+
sc.addSystemDefaultAssistant(); //Add SEPIA too
80+
//System.out.println("Member: " + SocketConfig.systemAssistantId); //DEBUG
81+
}
82+
JSONObject msgJSON = JSON.make("result", "success", "key", channelKey, "channelId", channelId);
83+
return SparkJavaFw.returnResult(request, response, msgJSON.toJSONString(), 200);
84+
85+
}else{
86+
//error
87+
JSONObject msgJSON = JSON.make("result", "fail", "error", "creation failed! Maybe already exists?");
88+
return SparkJavaFw.returnResult(request, response, msgJSON.toJSONString(), 200);
89+
}
90+
}catch (Exception e){
91+
//error
92+
JSONObject msgJSON = JSON.make("result", "fail", "error", e.getMessage());
93+
return SparkJavaFw.returnResult(request, response, msgJSON.toJSONString(), 200);
94+
}
95+
}else{
96+
//refuse
97+
JSONObject msgJSON = JSON.make("result", "fail", "error", "not authorized, missing required role");
98+
return SparkJavaFw.returnResult(request, response, msgJSON.toJSONString(), 404);
99+
}
100+
}else{
101+
//refuse
102+
JSONObject msgJSON = JSON.make("result", "fail", "error", "not authorized");
103+
return SparkJavaFw.returnResult(request, response, msgJSON.toJSONString(), 404);
104+
}
105+
}
106+
107+
/**
108+
* Join a channel.
109+
*/
110+
public static String joinChannel(Request request, Response response){
111+
//TODO:
112+
// - authenticate
113+
// - get parameters (channelId, key, user)
114+
// - return success
115+
// ...
116+
117+
//get parameters (or throw error)
118+
RequestParameters params = new RequestPostParameters(request);
119+
120+
//authenticate
121+
Account userAccount = new Account();
122+
if (userAccount.authenticate(params)){
123+
124+
JSONObject msgJSON = JSON.make("result", "fail", "error", "not yet implemented");
125+
return SparkJavaFw.returnResult(request, response, msgJSON.toJSONString(), 200);
126+
127+
}else{
128+
//refuse
129+
JSONObject msgJSON = JSON.make("result", "fail", "error", "not authorized");
130+
return SparkJavaFw.returnResult(request, response, msgJSON.toJSONString(), 404);
131+
}
132+
}
133+
}

src/main/java/net/b07z/sepia/websockets/server/SepiaSocketHandler.java

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -157,20 +157,31 @@ public void onMessage(Session userSession, String message) {
157157
}else if (channelId.equals("<auto>")){
158158
//get active channel
159159
if (user != null){
160-
channelId = user.getActiveChannel();
161-
channelAccepted = true;
162-
//check this channel here or assume that all "active channels" really exist and the user is allowed to use it?
160+
//do we have a user that can be in any channel? Then get the receiver active channel
161+
if (user.isOmnipresent()){
162+
SocketUser rec = SocketUserPool.getActiveUserById(msg.receiver);
163+
if (rec != null){
164+
channelId = rec.getActiveChannel(); //TODO: does it sense to broadcast this to all users with this ID not only active?
165+
msg.channelId = channelId; //refresh
166+
channelAccepted = true;
167+
}
168+
}else{
169+
channelId = user.getActiveChannel(); //Note: I wonder what happens when the user is the assistant that is active in all channels?
170+
msg.channelId = channelId; //refresh
171+
//check this channel here or assume that all "active channels" really exist and the user is allowed to use it?
172+
channelAccepted = true;
173+
}
163174
}
164175
}else{
165176
//validate channel - TODO: this procedure has potential to fail when channel operations are not in sync with user, I'm sure, I think, maybe ... ^^
166177
//user must exists if a message should be sent to channel
167178
if (user != null){
168-
sc = SocketChannelPool.getChannel(channelId);
179+
SocketChannel testSc = SocketChannelPool.getChannel(channelId);
169180
//channel exists?
170-
if (sc != null){
181+
if (testSc != null){
171182
//user is active in this channel?
172183
if (!user.getActiveChannel().equals(channelId)){
173-
if (sc.isUserMemberOfChannel(user)){
184+
if (testSc.isUserMemberOfChannel(user)){
174185
//user.setActiveChannel(channelId); //do this here?
175186
channelAccepted = true;
176187
}
@@ -203,7 +214,8 @@ public void onMessage(Session userSession, String message) {
203214
//simply broadcast
204215
if (dataType == null
205216
|| dataType.equals(DataType.openText.name())
206-
|| dataType.equals(DataType.assistAnswer.name())
217+
|| dataType.equals(DataType.assistAnswer.name())
218+
|| dataType.equals(DataType.assistFollowUp.name())
207219
|| dataType.equals(DataType.directCmd.name())
208220
){
209221
preBroadcastAction(user, msg, false, false, false);
@@ -522,7 +534,7 @@ public void broadcastMessage(SocketMessage msg){
522534
//Sends message to all active users of a certain channel - message assumes channelId was checked before
523535
@Override
524536
public void broadcastMessage(SocketMessage msg, String channelId){
525-
//TODO: check channel "<auto>"
537+
//TODO: check channel "<auto>" again? - should have been replaced in channel-check at start ...
526538
SocketChannel sc = SocketChannelPool.getChannel(channelId);
527539
List<SocketUser> activeChannelUsers = sc.getActiveMembers(false); //TODO: this one is tricky, for normal messages it should be "false"
528540
broadcastMessage(msg, activeChannelUsers);

0 commit comments

Comments
 (0)