Добавлены call-pushes
This commit is contained in:
4
.env
4
.env
@@ -14,6 +14,10 @@ SDU_SERVERS=http://10.211.55.2:7777
|
||||
|
||||
#Firebase Credentials
|
||||
FIREBASE_CREDENTIALS_PATH=serviceAccount.json
|
||||
#Apple APNS
|
||||
APNS_KEY_PATH=voip.p12
|
||||
APNS_P12_PASSWORD=rosetta1
|
||||
IOS_BUNDLE_ID=com.rosetta.dev
|
||||
|
||||
#Каждые сколько дней будет очищаться буфер (максимальная дистанция синхронизации сообщений)
|
||||
BUFFER_CLEANUP_DAYS=7
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -34,6 +34,9 @@ target
|
||||
.settings
|
||||
.project
|
||||
.classpath
|
||||
serviceAccount.json
|
||||
build/*.p12
|
||||
*.p12
|
||||
|
||||
build/.env
|
||||
build/.env*
|
||||
|
||||
6
pom.xml
6
pom.xml
@@ -46,6 +46,12 @@
|
||||
<artifactId>jakarta.persistence-api</artifactId>
|
||||
<version>3.1.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.eatthepath</groupId>
|
||||
<artifactId>pushy</artifactId>
|
||||
<version>0.15.4</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
package im.rosetta.executors;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.UUID;
|
||||
|
||||
import im.rosetta.Failures;
|
||||
import im.rosetta.client.ClientManager;
|
||||
import im.rosetta.client.tags.ECIAuthentificate;
|
||||
import im.rosetta.packet.Packet26SignalPeer;
|
||||
import im.rosetta.packet.runtime.NetworkSignalType;
|
||||
import im.rosetta.service.dispatch.push.PushNotifyDispatcher;
|
||||
import im.rosetta.service.dispatch.runtime.PushType;
|
||||
import im.rosetta.service.services.ForwardUnitService;
|
||||
import io.g365sfu.Room;
|
||||
import io.orprotocol.ProtocolException;
|
||||
@@ -18,6 +23,7 @@ public class Executor26SignalPeer extends PacketExecutor<Packet26SignalPeer> {
|
||||
|
||||
private ClientManager clientManager;
|
||||
private ForwardUnitService fus;
|
||||
private PushNotifyDispatcher pushNotifyDispatcher = new PushNotifyDispatcher();
|
||||
|
||||
public Executor26SignalPeer(ClientManager clientManager, ForwardUnitService fus) {
|
||||
this.clientManager = clientManager;
|
||||
@@ -26,6 +32,8 @@ public class Executor26SignalPeer extends PacketExecutor<Packet26SignalPeer> {
|
||||
|
||||
@Override
|
||||
public void onPacketReceived(Packet26SignalPeer packet, Client client) throws Exception, ProtocolException {
|
||||
String src = packet.getSrc();
|
||||
String dst = packet.getDst();
|
||||
ECIAuthentificate eciAuthentificate = client.getTag(ECIAuthentificate.class);
|
||||
if (eciAuthentificate == null || !eciAuthentificate.hasAuthorized()) {
|
||||
/**
|
||||
@@ -35,6 +43,14 @@ public class Executor26SignalPeer extends PacketExecutor<Packet26SignalPeer> {
|
||||
client.disconnect(Failures.HANDSHAKE_NOT_COMPLETED);
|
||||
return;
|
||||
}
|
||||
if(!src.equals(eciAuthentificate.getPublicKey())) {
|
||||
/**
|
||||
* Если src в пакете не совпадает с авторизованным PK клиента, то это может означать, что клиент пытается
|
||||
* отправить сигнал от другого пользователя, отключаем его от сервера.
|
||||
*/
|
||||
client.disconnect(Failures.DATA_MISSMATCH);
|
||||
return;
|
||||
}
|
||||
NetworkSignalType type = packet.getSignalType();
|
||||
if(type == NetworkSignalType.CALL) {
|
||||
/**
|
||||
@@ -50,6 +66,14 @@ public class Executor26SignalPeer extends PacketExecutor<Packet26SignalPeer> {
|
||||
this.clientManager.sendPacketToAuthorizedPK(packet.getSrc(), responsePacket);
|
||||
return;
|
||||
}
|
||||
/**
|
||||
* Получатель сигнала не занят, отправляем ему пуш уведомление о входящем звонке и сигнал CALL для инициализации звонка
|
||||
*/
|
||||
pushNotifyDispatcher.sendPush(dst, new HashMap<>(){{
|
||||
put("type", PushType.CALL);
|
||||
put("dialog", src);
|
||||
put("callId", UUID.randomUUID().toString());
|
||||
}});
|
||||
}
|
||||
if(type == NetworkSignalType.CREATE_ROOM){
|
||||
/**
|
||||
@@ -66,14 +90,8 @@ public class Executor26SignalPeer extends PacketExecutor<Packet26SignalPeer> {
|
||||
this.clientManager.sendPacketToAuthorizedPK(packet.getDst(), packet);
|
||||
return;
|
||||
}
|
||||
/**
|
||||
* TODO: Проверка на существование получателя
|
||||
*/
|
||||
|
||||
this.clientManager.sendPacketToAuthorizedPK(packet.getDst(), packet);
|
||||
/**
|
||||
* TODO: Высокоприоритетный пуш для сигналов звонков, чтобы мобильные устройства могли показать
|
||||
* интерфейс входящего звонка, даже если приложение находится в фоне
|
||||
*/
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -82,8 +82,7 @@ public class FCM extends Pusher {
|
||||
break;
|
||||
case PushType.CALL:
|
||||
/**
|
||||
* Звонок для андроид используем high priority, чтобы уведомление доставлялось даже если устройство в режиме Doze,
|
||||
* для iOS используем VoIP уведомление, которое доставляется даже если приложение убито
|
||||
* Это только для Android, для iOS используется VoIP APNs с отдельным сертификатом
|
||||
*/
|
||||
androidConfig.setPriority(AndroidConfig.Priority.HIGH);
|
||||
messageBuilder.setAndroidConfig(androidConfig.build());
|
||||
|
||||
@@ -1,15 +1,94 @@
|
||||
package im.rosetta.service.dispatch.push.dispatchers;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.HashMap;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
import com.eatthepath.pushy.apns.ApnsClient;
|
||||
import com.eatthepath.pushy.apns.ApnsClientBuilder;
|
||||
import com.eatthepath.pushy.apns.PushNotificationResponse;
|
||||
import com.eatthepath.pushy.apns.util.SimpleApnsPushNotification;
|
||||
import com.eatthepath.pushy.apns.util.TokenUtil;
|
||||
|
||||
import im.rosetta.service.dispatch.push.Pusher;
|
||||
import im.rosetta.service.dispatch.runtime.PushType;
|
||||
|
||||
public class VoIPApns extends Pusher {
|
||||
|
||||
private ApnsClient client;
|
||||
private String topic;
|
||||
|
||||
public VoIPApns(){
|
||||
this.initializeApns();
|
||||
}
|
||||
|
||||
private void initializeApns() {
|
||||
try {
|
||||
String p12Path = System.getenv("APNS_KEY_PATH");
|
||||
String p12Password = System.getenv("APNS_P12_PASSWORD");
|
||||
String bundleId = System.getenv("IOS_BUNDLE_ID");
|
||||
|
||||
if (p12Path == null || bundleId == null) {
|
||||
throw new IllegalStateException("APNS_P12_PATH and IOS_BUNDLE_ID must be set");
|
||||
}
|
||||
|
||||
this.topic = bundleId + ".voip";
|
||||
|
||||
this.client = new ApnsClientBuilder()
|
||||
.setApnsServer(ApnsClientBuilder.PRODUCTION_APNS_HOST)
|
||||
.setClientCredentials(new File(p12Path), p12Password)
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to init VoIP APNs client", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendPush(String token, HashMap<String, String> data) {
|
||||
// TODO Auto-generated method stub
|
||||
throw new UnsupportedOperationException("Unimplemented method 'sendPush'");
|
||||
if(data.get("type") != PushType.CALL) {
|
||||
/**
|
||||
* Для VoIP APNs отправляем уведомления только о входящих звонках
|
||||
*/
|
||||
return;
|
||||
}
|
||||
try {
|
||||
String normalizedToken = TokenUtil.sanitizeTokenString(token);
|
||||
|
||||
String payload = """
|
||||
{
|
||||
"aps": { "content-available": 1 },
|
||||
"type": "CALL",
|
||||
"callId": "%s",
|
||||
"from": "%s"
|
||||
}
|
||||
""".formatted(
|
||||
escape(data.getOrDefault("callId", "")),
|
||||
escape(data.getOrDefault("dialog", ""))
|
||||
);
|
||||
|
||||
SimpleApnsPushNotification push = new SimpleApnsPushNotification(
|
||||
normalizedToken,
|
||||
topic,
|
||||
payload,
|
||||
null, // invalidation time
|
||||
com.eatthepath.pushy.apns.DeliveryPriority.IMMEDIATE,
|
||||
com.eatthepath.pushy.apns.PushType.VOIP // apns-push-type: voip
|
||||
);
|
||||
|
||||
PushNotificationResponse<SimpleApnsPushNotification> response = client.sendNotification(push).get();
|
||||
|
||||
if (!response.isAccepted()) {
|
||||
System.err.println("VoIP push rejected: " + response.getRejectionReason());
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
} catch (ExecutionException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private String escape(String v) {
|
||||
return v == null ? "" : v.replace("\\", "\\\\").replace("\"", "\\\"");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -150,7 +150,6 @@ public class Server extends WebSocketServer {
|
||||
}
|
||||
} catch (Exception e) {
|
||||
client.disconnect(ServerFailures.BAD_PACKET);
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user