1 package org
.asamk
.signal
.manager
.storage
.recipients
;
3 import org
.whispersystems
.signalservice
.api
.push
.PNI
;
4 import org
.whispersystems
.signalservice
.api
.push
.ServiceId
;
5 import org
.whispersystems
.signalservice
.api
.push
.SignalServiceAddress
;
7 import java
.util
.Optional
;
9 public record RecipientAddress(
10 Optional
<ServiceId
> serviceId
, Optional
<PNI
> pni
, Optional
<String
> number
, Optional
<String
> username
14 * Construct a RecipientAddress.
16 * @param serviceId The ACI or PNI of the user, if available.
17 * @param number The phone number of the user, if available.
19 public RecipientAddress
{
20 if (serviceId
.isPresent() && serviceId
.get().isUnknown()) {
21 serviceId
= Optional
.empty();
23 if (pni
.isPresent() && pni
.get().isUnknown()) {
24 pni
= Optional
.empty();
26 if (serviceId
.isEmpty() && pni
.isPresent()) {
27 serviceId
= Optional
.of(pni
.get());
29 if (serviceId
.isPresent() && serviceId
.get() instanceof PNI sPNI
) {
30 if (pni
.isPresent() && !sPNI
.equals(pni
.get())) {
31 throw new AssertionError("Must not have two different PNIs!");
34 pni
= Optional
.of(sPNI
);
37 if (serviceId
.isEmpty() && number
.isEmpty()) {
38 throw new AssertionError("Must have either a ServiceId or E164 number!");
42 public RecipientAddress(Optional
<ServiceId
> serviceId
, Optional
<String
> number
) {
43 this(serviceId
, Optional
.empty(), number
, Optional
.empty());
46 public RecipientAddress(ServiceId serviceId
, String e164
) {
47 this(Optional
.ofNullable(serviceId
), Optional
.empty(), Optional
.ofNullable(e164
), Optional
.empty());
50 public RecipientAddress(ServiceId serviceId
, PNI pni
, String e164
) {
51 this(Optional
.ofNullable(serviceId
), Optional
.ofNullable(pni
), Optional
.ofNullable(e164
), Optional
.empty());
54 public RecipientAddress(ServiceId serviceId
, PNI pni
, String e164
, String username
) {
55 this(Optional
.ofNullable(serviceId
),
56 Optional
.ofNullable(pni
),
57 Optional
.ofNullable(e164
),
58 Optional
.ofNullable(username
));
61 public RecipientAddress(SignalServiceAddress address
) {
62 this(Optional
.of(address
.getServiceId()), Optional
.empty(), address
.getNumber(), Optional
.empty());
65 public RecipientAddress(org
.asamk
.signal
.manager
.api
.RecipientAddress address
) {
66 this(address
.uuid().map(ServiceId
::from
), Optional
.empty(), address
.number(), address
.username());
69 public RecipientAddress(ServiceId serviceId
) {
70 this(Optional
.of(serviceId
), Optional
.empty());
73 public RecipientAddress
withIdentifiersFrom(RecipientAddress address
) {
74 return new RecipientAddress((
75 this.serviceId
.isEmpty() || this.isServiceIdPNI() || this.serviceId
.equals(address
.pni
)
76 ) && !address
.isServiceIdPNI() ? address
.serviceId
: this.serviceId
,
77 address
.pni
.or(this::pni
),
78 address
.number
.or(this::number
),
79 address
.username
.or(this::username
));
82 public RecipientAddress
removeIdentifiersFrom(RecipientAddress address
) {
83 return new RecipientAddress(address
.serviceId
.equals(this.serviceId
) || address
.pni
.equals(this.serviceId
)
86 address
.pni
.equals(this.pni
) || address
.serviceId
.equals(this.pni
) ? Optional
.empty() : this.pni
,
87 address
.number
.equals(this.number
) ? Optional
.empty() : this.number
,
88 address
.username
.equals(this.username
) ? Optional
.empty() : this.username
);
91 public String
getIdentifier() {
92 if (serviceId
.isPresent()) {
93 return serviceId
.get().toString();
94 } else if (number
.isPresent()) {
97 throw new AssertionError("Given the checks in the constructor, this should not be possible.");
101 public String
getLegacyIdentifier() {
102 if (number
.isPresent()) {
104 } else if (serviceId
.isPresent()) {
105 return serviceId
.get().toString();
107 throw new AssertionError("Given the checks in the constructor, this should not be possible.");
111 public boolean matches(RecipientAddress other
) {
112 return (serviceId
.isPresent() && other
.serviceId
.isPresent() && serviceId
.get().equals(other
.serviceId
.get()))
114 pni
.isPresent() && other
.serviceId
.isPresent() && pni
.get().equals(other
.serviceId
.get())
117 serviceId
.isPresent() && other
.pni
.isPresent() && serviceId
.get().equals(other
.pni
.get())
120 pni
.isPresent() && other
.pni
.isPresent() && pni
.get().equals(other
.pni
.get())
123 number
.isPresent() && other
.number
.isPresent() && number
.get().equals(other
.number
.get())
127 public boolean hasSingleIdentifier() {
128 final var identifiersCount
= serviceId().map(s
-> 1).orElse(0)
129 + number().map(s
-> 1).orElse(0)
130 + username().map(s
-> 1).orElse(0);
131 return identifiersCount
== 1;
134 public boolean hasIdentifiersOf(RecipientAddress address
) {
135 return (address
.serviceId
.isEmpty() || address
.serviceId
.equals(serviceId
) || address
.serviceId
.equals(pni
))
136 && (address
.pni
.isEmpty() || address
.pni
.equals(pni
))
137 && (address
.number
.isEmpty() || address
.number
.equals(number
))
138 && (address
.username
.isEmpty() || address
.username
.equals(username
));
141 public boolean hasAdditionalIdentifiersThan(RecipientAddress address
) {
143 serviceId
.isPresent() && (
144 address
.serviceId
.isEmpty() || (
145 !address
.serviceId
.equals(serviceId
) && !address
.pni
.equals(serviceId
)
149 pni
.isPresent() && !address
.serviceId
.equals(pni
) && (
150 address
.pni
.isEmpty() || !address
.pni
.equals(pni
)
153 number
.isPresent() && (
154 address
.number
.isEmpty() || !address
.number
.equals(number
)
157 username
.isPresent() && (
158 address
.username
.isEmpty() || !address
.username
.equals(username
)
163 public boolean hasOnlyPniAndNumber() {
164 return pni
.isPresent() && serviceId
.equals(pni
) && number
.isPresent();
167 public boolean isServiceIdPNI() {
168 return serviceId
.isPresent() && (pni
.isPresent() && serviceId
.equals(pni
));
171 public SignalServiceAddress
toSignalServiceAddress() {
172 return new SignalServiceAddress(serviceId
.orElse(ServiceId
.UNKNOWN
), number
);
175 public org
.asamk
.signal
.manager
.api
.RecipientAddress
toApiRecipientAddress() {
176 return new org
.asamk
.signal
.manager
.api
.RecipientAddress(serviceId().map(ServiceId
::uuid
),