mirror of
https://github.com/AsamK/signal-cli
synced 2025-08-29 18:40:39 +00:00
Add MultiAccountManager
This commit is contained in:
parent
6261934dda
commit
4a1af0786c
18 changed files with 221 additions and 131 deletions
|
@ -403,6 +403,8 @@ public class ManagerImpl implements Manager {
|
|||
|
||||
@Override
|
||||
public void submitRateLimitRecaptchaChallenge(String challenge, String captcha) throws IOException {
|
||||
captcha = captcha == null ? null : captcha.replace("signalcaptcha://", "");
|
||||
|
||||
dependencies.getAccountManager().submitRateLimitRecaptchaChallenge(challenge, captcha);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
package org.asamk.signal.manager;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public interface MultiAccountManager extends AutoCloseable {
|
||||
|
||||
List<String> getAccountNumbers();
|
||||
|
||||
void addOnManagerAddedHandler(Consumer<Manager> handler);
|
||||
|
||||
void addOnManagerRemovedHandler(Consumer<Manager> handler);
|
||||
|
||||
Manager getManager(String phoneNumber);
|
||||
|
||||
ProvisioningManager getNewProvisioningManager();
|
||||
|
||||
RegistrationManager getNewRegistrationManager(String username) throws IOException;
|
||||
|
||||
@Override
|
||||
void close();
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
package org.asamk.signal.manager;
|
||||
|
||||
import org.asamk.signal.manager.config.ServiceEnvironment;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class MultiAccountManagerImpl implements MultiAccountManager {
|
||||
|
||||
private final static Logger logger = LoggerFactory.getLogger(MultiAccountManagerImpl.class);
|
||||
|
||||
private final Set<Consumer<Manager>> onManagerAddedHandlers = new HashSet<>();
|
||||
private final Set<Consumer<Manager>> onManagerRemovedHandlers = new HashSet<>();
|
||||
private final Set<Manager> managers = new HashSet<>();
|
||||
private final File dataPath;
|
||||
private final ServiceEnvironment serviceEnvironment;
|
||||
private final String userAgent;
|
||||
|
||||
public MultiAccountManagerImpl(
|
||||
final Collection<Manager> managers,
|
||||
final File dataPath,
|
||||
final ServiceEnvironment serviceEnvironment,
|
||||
final String userAgent
|
||||
) {
|
||||
this.managers.addAll(managers);
|
||||
this.dataPath = dataPath;
|
||||
this.serviceEnvironment = serviceEnvironment;
|
||||
this.userAgent = userAgent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getAccountNumbers() {
|
||||
synchronized (managers) {
|
||||
return managers.stream().map(Manager::getSelfNumber).collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
|
||||
void addManager(final Manager m) {
|
||||
synchronized (managers) {
|
||||
if (managers.contains(m)) {
|
||||
return;
|
||||
}
|
||||
managers.add(m);
|
||||
}
|
||||
synchronized (onManagerAddedHandlers) {
|
||||
for (final var handler : onManagerAddedHandlers) {
|
||||
handler.accept(m);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addOnManagerAddedHandler(final Consumer<Manager> handler) {
|
||||
synchronized (onManagerAddedHandlers) {
|
||||
onManagerAddedHandlers.add(handler);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addOnManagerRemovedHandler(final Consumer<Manager> handler) {
|
||||
synchronized (onManagerRemovedHandlers) {
|
||||
onManagerRemovedHandlers.add(handler);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Manager getManager(final String account) {
|
||||
synchronized (managers) {
|
||||
return managers.stream().filter(m -> m.getSelfNumber().equals(account)).findFirst().orElse(null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProvisioningManager getNewProvisioningManager() {
|
||||
return ProvisioningManager.init(dataPath, serviceEnvironment, userAgent, this::addManager);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RegistrationManager getNewRegistrationManager(String account) throws IOException {
|
||||
return RegistrationManager.init(account, dataPath, serviceEnvironment, userAgent, this::addManager);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
synchronized (managers) {
|
||||
for (var m : managers) {
|
||||
try {
|
||||
m.close();
|
||||
} catch (IOException e) {
|
||||
logger.warn("Cleanup failed", e);
|
||||
}
|
||||
}
|
||||
managers.clear();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -38,6 +38,7 @@ import java.io.File;
|
|||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class ProvisioningManager {
|
||||
|
||||
|
@ -46,16 +47,23 @@ public class ProvisioningManager {
|
|||
private final PathConfig pathConfig;
|
||||
private final ServiceEnvironmentConfig serviceEnvironmentConfig;
|
||||
private final String userAgent;
|
||||
private final Consumer<Manager> newManagerListener;
|
||||
|
||||
private final SignalServiceAccountManager accountManager;
|
||||
private final IdentityKeyPair tempIdentityKey;
|
||||
private final int registrationId;
|
||||
private final String password;
|
||||
|
||||
ProvisioningManager(PathConfig pathConfig, ServiceEnvironmentConfig serviceEnvironmentConfig, String userAgent) {
|
||||
ProvisioningManager(
|
||||
PathConfig pathConfig,
|
||||
ServiceEnvironmentConfig serviceEnvironmentConfig,
|
||||
String userAgent,
|
||||
final Consumer<Manager> newManagerListener
|
||||
) {
|
||||
this.pathConfig = pathConfig;
|
||||
this.serviceEnvironmentConfig = serviceEnvironmentConfig;
|
||||
this.userAgent = userAgent;
|
||||
this.newManagerListener = newManagerListener;
|
||||
|
||||
tempIdentityKey = KeyUtils.generateIdentityKeyPair();
|
||||
registrationId = KeyHelper.generateRegistrationId(false);
|
||||
|
@ -75,12 +83,21 @@ public class ProvisioningManager {
|
|||
|
||||
public static ProvisioningManager init(
|
||||
File settingsPath, ServiceEnvironment serviceEnvironment, String userAgent
|
||||
) {
|
||||
return init(settingsPath, serviceEnvironment, userAgent, null);
|
||||
}
|
||||
|
||||
public static ProvisioningManager init(
|
||||
File settingsPath,
|
||||
ServiceEnvironment serviceEnvironment,
|
||||
String userAgent,
|
||||
Consumer<Manager> newManagerListener
|
||||
) {
|
||||
var pathConfig = PathConfig.createDefault(settingsPath);
|
||||
|
||||
final var serviceConfiguration = ServiceConfig.getServiceEnvironmentConfig(serviceEnvironment, userAgent);
|
||||
|
||||
return new ProvisioningManager(pathConfig, serviceConfiguration, userAgent);
|
||||
return new ProvisioningManager(pathConfig, serviceConfiguration, userAgent, newManagerListener);
|
||||
}
|
||||
|
||||
public URI getDeviceLinkUri() throws TimeoutException, IOException {
|
||||
|
@ -89,7 +106,7 @@ public class ProvisioningManager {
|
|||
return new DeviceLinkInfo(deviceUuid, tempIdentityKey.getPublicKey().getPublicKey()).createDeviceLinkUri();
|
||||
}
|
||||
|
||||
public Manager finishDeviceLink(String deviceName) throws IOException, TimeoutException, UserAlreadyExists {
|
||||
public String finishDeviceLink(String deviceName) throws IOException, TimeoutException, UserAlreadyExists {
|
||||
var ret = accountManager.getNewDeviceRegistration(tempIdentityKey);
|
||||
var number = ret.getNumber();
|
||||
|
||||
|
@ -145,11 +162,11 @@ public class ProvisioningManager {
|
|||
"Failed to request sync messages from linked device, data can be requested again with `sendSyncRequest`.");
|
||||
}
|
||||
|
||||
final var result = m;
|
||||
account = null;
|
||||
m = null;
|
||||
|
||||
return result;
|
||||
if (newManagerListener != null) {
|
||||
newManagerListener.accept(m);
|
||||
m = null;
|
||||
}
|
||||
return number;
|
||||
} finally {
|
||||
if (m != null) {
|
||||
m.close();
|
||||
|
|
|
@ -49,6 +49,7 @@ import org.whispersystems.signalservice.internal.util.DynamicCredentialsProvider
|
|||
import java.io.Closeable;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class RegistrationManager implements Closeable {
|
||||
|
||||
|
@ -58,6 +59,7 @@ public class RegistrationManager implements Closeable {
|
|||
private final PathConfig pathConfig;
|
||||
private final ServiceEnvironmentConfig serviceEnvironmentConfig;
|
||||
private final String userAgent;
|
||||
private final Consumer<Manager> newManagerListener;
|
||||
|
||||
private final SignalServiceAccountManager accountManager;
|
||||
private final PinHelper pinHelper;
|
||||
|
@ -66,12 +68,14 @@ public class RegistrationManager implements Closeable {
|
|||
SignalAccount account,
|
||||
PathConfig pathConfig,
|
||||
ServiceEnvironmentConfig serviceEnvironmentConfig,
|
||||
String userAgent
|
||||
String userAgent,
|
||||
Consumer<Manager> newManagerListener
|
||||
) {
|
||||
this.account = account;
|
||||
this.pathConfig = pathConfig;
|
||||
this.serviceEnvironmentConfig = serviceEnvironmentConfig;
|
||||
this.userAgent = userAgent;
|
||||
this.newManagerListener = newManagerListener;
|
||||
|
||||
GroupsV2Operations groupsV2Operations;
|
||||
try {
|
||||
|
@ -96,6 +100,16 @@ public class RegistrationManager implements Closeable {
|
|||
|
||||
public static RegistrationManager init(
|
||||
String number, File settingsPath, ServiceEnvironment serviceEnvironment, String userAgent
|
||||
) throws IOException {
|
||||
return init(number, settingsPath, serviceEnvironment, userAgent, null);
|
||||
}
|
||||
|
||||
public static RegistrationManager init(
|
||||
String number,
|
||||
File settingsPath,
|
||||
ServiceEnvironment serviceEnvironment,
|
||||
String userAgent,
|
||||
Consumer<Manager> newManagerListener
|
||||
) throws IOException {
|
||||
var pathConfig = PathConfig.createDefault(settingsPath);
|
||||
|
||||
|
@ -112,15 +126,16 @@ public class RegistrationManager implements Closeable {
|
|||
profileKey,
|
||||
TrustNewIdentity.ON_FIRST_USE);
|
||||
|
||||
return new RegistrationManager(account, pathConfig, serviceConfiguration, userAgent);
|
||||
return new RegistrationManager(account, pathConfig, serviceConfiguration, userAgent, newManagerListener);
|
||||
}
|
||||
|
||||
var account = SignalAccount.load(pathConfig.dataPath(), number, true, TrustNewIdentity.ON_FIRST_USE);
|
||||
|
||||
return new RegistrationManager(account, pathConfig, serviceConfiguration, userAgent);
|
||||
return new RegistrationManager(account, pathConfig, serviceConfiguration, userAgent, newManagerListener);
|
||||
}
|
||||
|
||||
public void register(boolean voiceVerification, String captcha) throws IOException, CaptchaRequiredException {
|
||||
captcha = captcha == null ? null : captcha.replace("signalcaptcha://", "");
|
||||
final ServiceResponse<RequestVerificationCodeResponse> response;
|
||||
if (voiceVerification) {
|
||||
response = accountManager.requestVoiceVerificationCode(Utils.getDefaultLocale(),
|
||||
|
@ -140,7 +155,7 @@ public class RegistrationManager implements Closeable {
|
|||
}
|
||||
}
|
||||
|
||||
public Manager verifyAccount(
|
||||
public void verifyAccount(
|
||||
String verificationCode, String pin
|
||||
) throws IOException, PinLockedException, IncorrectPinException {
|
||||
verificationCode = verificationCode.replace("-", "");
|
||||
|
@ -196,10 +211,10 @@ public class RegistrationManager implements Closeable {
|
|||
logger.warn("Failed to set default profile: {}", e.getMessage());
|
||||
}
|
||||
|
||||
final var result = m;
|
||||
m = null;
|
||||
|
||||
return result;
|
||||
if (newManagerListener != null) {
|
||||
newManagerListener.accept(m);
|
||||
m = null;
|
||||
}
|
||||
} finally {
|
||||
if (m != null) {
|
||||
m.close();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue