slidge-whatsapp 0.3.0__cp311-cp311-manylinux_2_36_aarch64.whl → 0.3.1__cp311-cp311-manylinux_2_36_aarch64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of slidge-whatsapp might be problematic. Click here for more details.
- slidge_whatsapp/event.go +33 -9
- slidge_whatsapp/generated/_whatsapp.cpython-311-aarch64-linux-gnu.h +150 -150
- slidge_whatsapp/generated/_whatsapp.cpython-311-aarch64-linux-gnu.so +0 -0
- slidge_whatsapp/generated/build.py +130 -130
- slidge_whatsapp/generated/whatsapp.c +1478 -1478
- slidge_whatsapp/generated/whatsapp.go +1123 -1123
- slidge_whatsapp/generated/whatsapp.py +863 -863
- slidge_whatsapp/generated/whatsapp_go.h +150 -150
- slidge_whatsapp/go.mod +5 -5
- slidge_whatsapp/go.sum +14 -14
- slidge_whatsapp/session.go +3 -3
- slidge_whatsapp/vendor/github.com/ebitengine/purego/README.md +21 -5
- slidge_whatsapp/vendor/github.com/ebitengine/purego/abi_loong64.h +60 -0
- slidge_whatsapp/vendor/github.com/ebitengine/purego/cgo.go +1 -1
- slidge_whatsapp/vendor/github.com/ebitengine/purego/dlerror.go +1 -1
- slidge_whatsapp/vendor/github.com/ebitengine/purego/dlfcn.go +1 -1
- slidge_whatsapp/vendor/github.com/ebitengine/purego/dlfcn_netbsd.go +15 -0
- slidge_whatsapp/vendor/github.com/ebitengine/purego/dlfcn_nocgo_netbsd.go +9 -0
- slidge_whatsapp/vendor/github.com/ebitengine/purego/dlfcn_stubs.s +1 -1
- slidge_whatsapp/vendor/github.com/ebitengine/purego/func.go +113 -60
- slidge_whatsapp/vendor/github.com/ebitengine/purego/gen.go +6 -0
- slidge_whatsapp/vendor/github.com/ebitengine/purego/go_runtime.go +1 -1
- slidge_whatsapp/vendor/github.com/ebitengine/purego/internal/cgo/dlfcn_cgo_unix.go +2 -2
- slidge_whatsapp/vendor/github.com/ebitengine/purego/internal/cgo/syscall_cgo_unix.go +2 -2
- slidge_whatsapp/vendor/github.com/ebitengine/purego/internal/fakecgo/abi_loong64.h +60 -0
- slidge_whatsapp/vendor/github.com/ebitengine/purego/internal/fakecgo/asm_loong64.s +40 -0
- slidge_whatsapp/vendor/github.com/ebitengine/purego/internal/fakecgo/callbacks.go +1 -1
- slidge_whatsapp/vendor/github.com/ebitengine/purego/internal/fakecgo/doc.go +1 -1
- slidge_whatsapp/vendor/github.com/ebitengine/purego/internal/fakecgo/go_libinit.go +1 -1
- slidge_whatsapp/vendor/github.com/ebitengine/purego/internal/fakecgo/go_linux_loong64.go +92 -0
- slidge_whatsapp/vendor/github.com/ebitengine/purego/internal/fakecgo/go_netbsd.go +106 -0
- slidge_whatsapp/vendor/github.com/ebitengine/purego/internal/fakecgo/go_setenv.go +1 -1
- slidge_whatsapp/vendor/github.com/ebitengine/purego/internal/fakecgo/go_util.go +1 -1
- slidge_whatsapp/vendor/github.com/ebitengine/purego/internal/fakecgo/iscgo.go +1 -1
- slidge_whatsapp/vendor/github.com/ebitengine/purego/internal/fakecgo/libcgo.go +1 -1
- slidge_whatsapp/vendor/github.com/ebitengine/purego/internal/fakecgo/libcgo_darwin.go +4 -0
- slidge_whatsapp/vendor/github.com/ebitengine/purego/internal/fakecgo/libcgo_freebsd.go +4 -0
- slidge_whatsapp/vendor/github.com/ebitengine/purego/internal/fakecgo/libcgo_linux.go +4 -0
- slidge_whatsapp/vendor/github.com/ebitengine/purego/internal/fakecgo/libcgo_netbsd.go +26 -0
- slidge_whatsapp/vendor/github.com/ebitengine/purego/internal/fakecgo/netbsd.go +23 -0
- slidge_whatsapp/vendor/github.com/ebitengine/purego/internal/fakecgo/setenv.go +1 -1
- slidge_whatsapp/vendor/github.com/ebitengine/purego/internal/fakecgo/symbols.go +11 -1
- slidge_whatsapp/vendor/github.com/ebitengine/purego/internal/fakecgo/symbols_darwin.go +1 -0
- slidge_whatsapp/vendor/github.com/ebitengine/purego/internal/fakecgo/symbols_freebsd.go +1 -0
- slidge_whatsapp/vendor/github.com/ebitengine/purego/internal/fakecgo/symbols_linux.go +1 -0
- slidge_whatsapp/vendor/github.com/ebitengine/purego/internal/fakecgo/symbols_netbsd.go +30 -0
- slidge_whatsapp/vendor/github.com/ebitengine/purego/internal/fakecgo/trampolines_loong64.s +71 -0
- slidge_whatsapp/vendor/github.com/ebitengine/purego/internal/fakecgo/trampolines_stubs.s +5 -1
- slidge_whatsapp/vendor/github.com/ebitengine/purego/nocgo.go +1 -1
- slidge_whatsapp/vendor/github.com/ebitengine/purego/struct_amd64.go +8 -4
- slidge_whatsapp/vendor/github.com/ebitengine/purego/struct_arm64.go +16 -6
- slidge_whatsapp/vendor/github.com/ebitengine/purego/struct_loong64.go +190 -0
- slidge_whatsapp/vendor/github.com/ebitengine/purego/struct_other.go +6 -2
- slidge_whatsapp/vendor/github.com/ebitengine/purego/sys_amd64.s +1 -1
- slidge_whatsapp/vendor/github.com/ebitengine/purego/sys_arm64.s +1 -1
- slidge_whatsapp/vendor/github.com/ebitengine/purego/sys_loong64.s +96 -0
- slidge_whatsapp/vendor/github.com/ebitengine/purego/sys_unix_arm64.s +1 -1
- slidge_whatsapp/vendor/github.com/ebitengine/purego/sys_unix_loong64.s +75 -0
- slidge_whatsapp/vendor/github.com/ebitengine/purego/syscall.go +6 -3
- slidge_whatsapp/vendor/github.com/ebitengine/purego/syscall_cgo_linux.go +3 -3
- slidge_whatsapp/vendor/github.com/ebitengine/purego/syscall_sysv.go +13 -10
- slidge_whatsapp/vendor/github.com/ebitengine/purego/syscall_windows.go +1 -1
- slidge_whatsapp/vendor/github.com/ebitengine/purego/zcallback_amd64.s +2002 -2002
- slidge_whatsapp/vendor/github.com/ebitengine/purego/zcallback_arm64.s +4002 -4002
- slidge_whatsapp/vendor/github.com/ebitengine/purego/zcallback_loong64.s +4014 -0
- slidge_whatsapp/vendor/go.mau.fi/util/dbutil/log.go +1 -0
- slidge_whatsapp/vendor/go.mau.fi/util/dbutil/module.go +118 -0
- slidge_whatsapp/vendor/go.mau.fi/util/dbutil/upgradetable.go +0 -34
- slidge_whatsapp/vendor/go.mau.fi/util/exbytes/string.go +20 -0
- slidge_whatsapp/vendor/go.mau.fi/util/exbytes/writer.go +78 -0
- slidge_whatsapp/vendor/go.mau.fi/util/exslices/cast.go +42 -0
- slidge_whatsapp/vendor/go.mau.fi/util/exslices/chunk.go +28 -0
- slidge_whatsapp/vendor/go.mau.fi/util/exslices/deduplicate.go +67 -0
- slidge_whatsapp/vendor/go.mau.fi/util/exslices/diff.go +63 -0
- slidge_whatsapp/vendor/go.mau.fi/util/exsync/event.go +15 -1
- slidge_whatsapp/vendor/go.mau.fi/util/random/string.go +47 -7
- slidge_whatsapp/vendor/go.mau.fi/whatsmeow/appstate/decode.go +1 -0
- slidge_whatsapp/vendor/go.mau.fi/whatsmeow/appstate/encode.go +34 -0
- slidge_whatsapp/vendor/go.mau.fi/whatsmeow/appstate/hash.go +1 -0
- slidge_whatsapp/vendor/go.mau.fi/whatsmeow/appstate.go +3 -0
- slidge_whatsapp/vendor/go.mau.fi/whatsmeow/armadillomessage.go +1 -2
- slidge_whatsapp/vendor/go.mau.fi/whatsmeow/call.go +6 -0
- slidge_whatsapp/vendor/go.mau.fi/whatsmeow/errors.go +1 -0
- slidge_whatsapp/vendor/go.mau.fi/whatsmeow/group.go +63 -42
- slidge_whatsapp/vendor/go.mau.fi/whatsmeow/internals.go +14 -10
- slidge_whatsapp/vendor/go.mau.fi/whatsmeow/message.go +45 -18
- slidge_whatsapp/vendor/go.mau.fi/whatsmeow/msgsecret.go +23 -0
- slidge_whatsapp/vendor/go.mau.fi/whatsmeow/notification.go +5 -1
- slidge_whatsapp/vendor/go.mau.fi/whatsmeow/pair.go +3 -7
- slidge_whatsapp/vendor/go.mau.fi/whatsmeow/proto/waAICommon/WAAICommon.pb.go +7747 -0
- slidge_whatsapp/vendor/go.mau.fi/whatsmeow/proto/{waBotMetadata/WABotMetadata.proto → waAICommon/WAAICommon.proto} +269 -9
- slidge_whatsapp/vendor/go.mau.fi/whatsmeow/proto/waDeviceCapabilities/WAProtobufsDeviceCapabilities.pb.go +128 -14
- slidge_whatsapp/vendor/go.mau.fi/whatsmeow/proto/waDeviceCapabilities/WAProtobufsDeviceCapabilities.proto +10 -0
- slidge_whatsapp/vendor/go.mau.fi/whatsmeow/proto/waE2E/WAWebProtobufsE2E.pb.go +3236 -4732
- slidge_whatsapp/vendor/go.mau.fi/whatsmeow/proto/waE2E/WAWebProtobufsE2E.proto +125 -273
- slidge_whatsapp/vendor/go.mau.fi/whatsmeow/proto/waHistorySync/WAWebProtobufsHistorySync.pb.go +11 -2
- slidge_whatsapp/vendor/go.mau.fi/whatsmeow/proto/waHistorySync/WAWebProtobufsHistorySync.proto +1 -0
- slidge_whatsapp/vendor/go.mau.fi/whatsmeow/proto/waStatusAttributions/WAStatusAttributions.pb.go +220 -81
- slidge_whatsapp/vendor/go.mau.fi/whatsmeow/proto/waStatusAttributions/WAStatusAttributions.proto +13 -0
- slidge_whatsapp/vendor/go.mau.fi/whatsmeow/proto/waSyncAction/WASyncAction.pb.go +705 -449
- slidge_whatsapp/vendor/go.mau.fi/whatsmeow/proto/waSyncAction/WASyncAction.proto +23 -0
- slidge_whatsapp/vendor/go.mau.fi/whatsmeow/proto/waWa6/WAWebProtobufsWa6.pb.go +78 -24
- slidge_whatsapp/vendor/go.mau.fi/whatsmeow/proto/waWa6/WAWebProtobufsWa6.proto +6 -0
- slidge_whatsapp/vendor/go.mau.fi/whatsmeow/proto/waWeb/WAWebProtobufsWeb.pb.go +516 -267
- slidge_whatsapp/vendor/go.mau.fi/whatsmeow/proto/waWeb/WAWebProtobufsWeb.proto +22 -0
- slidge_whatsapp/vendor/go.mau.fi/whatsmeow/receipt.go +2 -0
- slidge_whatsapp/vendor/go.mau.fi/whatsmeow/request.go +4 -0
- slidge_whatsapp/vendor/go.mau.fi/whatsmeow/retry.go +2 -3
- slidge_whatsapp/vendor/go.mau.fi/whatsmeow/send.go +110 -28
- slidge_whatsapp/vendor/go.mau.fi/whatsmeow/store/clientpayload.go +1 -1
- slidge_whatsapp/vendor/go.mau.fi/whatsmeow/store/noop.go +12 -0
- slidge_whatsapp/vendor/go.mau.fi/whatsmeow/store/sqlstore/lidmap.go +82 -4
- slidge_whatsapp/vendor/go.mau.fi/whatsmeow/store/sqlstore/store.go +112 -55
- slidge_whatsapp/vendor/go.mau.fi/whatsmeow/store/sqlstore/upgrades/00-latest-schema.sql +8 -7
- slidge_whatsapp/vendor/go.mau.fi/whatsmeow/store/sqlstore/upgrades/11-redacted-phone-contacts.sql +2 -0
- slidge_whatsapp/vendor/go.mau.fi/whatsmeow/store/store.go +20 -0
- slidge_whatsapp/vendor/go.mau.fi/whatsmeow/types/call.go +6 -5
- slidge_whatsapp/vendor/go.mau.fi/whatsmeow/types/message.go +7 -1
- slidge_whatsapp/vendor/go.mau.fi/whatsmeow/types/user.go +3 -0
- slidge_whatsapp/vendor/go.mau.fi/whatsmeow/user.go +31 -2
- slidge_whatsapp/vendor/google.golang.org/protobuf/internal/filedesc/desc.go +35 -17
- slidge_whatsapp/vendor/google.golang.org/protobuf/internal/filedesc/desc_init.go +14 -0
- slidge_whatsapp/vendor/google.golang.org/protobuf/internal/filedesc/desc_lazy.go +20 -0
- slidge_whatsapp/vendor/google.golang.org/protobuf/internal/version/version.go +1 -1
- slidge_whatsapp/vendor/modules.txt +8 -6
- {slidge_whatsapp-0.3.0.dist-info → slidge_whatsapp-0.3.1.dist-info}/METADATA +1 -1
- {slidge_whatsapp-0.3.0.dist-info → slidge_whatsapp-0.3.1.dist-info}/RECORD +130 -106
- slidge_whatsapp/vendor/go.mau.fi/whatsmeow/proto/waBotMetadata/WABotMetadata.pb.go +0 -5156
- {slidge_whatsapp-0.3.0.dist-info → slidge_whatsapp-0.3.1.dist-info}/WHEEL +0 -0
- {slidge_whatsapp-0.3.0.dist-info → slidge_whatsapp-0.3.1.dist-info}/entry_points.txt +0 -0
- {slidge_whatsapp-0.3.0.dist-info → slidge_whatsapp-0.3.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -307,6 +307,9 @@ message WebMessageInfo {
|
|
|
307
307
|
repeated string statusMentionSources = 71;
|
|
308
308
|
repeated Citation supportAiCitations = 72;
|
|
309
309
|
optional string botTargetID = 73;
|
|
310
|
+
optional GroupHistoryIndividualMessageInfo groupHistoryIndividualMessageInfo = 74;
|
|
311
|
+
optional GroupHistoryBundleInfo groupHistoryBundleInfo = 75;
|
|
312
|
+
optional InteractiveMessageAdditionalMetadata interactiveMessageAdditionalMetadata = 76;
|
|
310
313
|
}
|
|
311
314
|
|
|
312
315
|
message PaymentInfo {
|
|
@@ -468,6 +471,17 @@ message MessageAddOn {
|
|
|
468
471
|
optional LegacyMessage legacyMessage = 8;
|
|
469
472
|
}
|
|
470
473
|
|
|
474
|
+
message GroupHistoryBundleInfo {
|
|
475
|
+
enum ProcessState {
|
|
476
|
+
NOT_DOWNLOADED = 0;
|
|
477
|
+
DOWNLOADED = 1;
|
|
478
|
+
DOWNLOAD_FAILED = 2;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
optional WAWebProtobufsE2E.MessageHistoryBundle deprecatedMessageHistoryBundle = 1;
|
|
482
|
+
optional ProcessState processState = 2;
|
|
483
|
+
}
|
|
484
|
+
|
|
471
485
|
message CommentMetadata {
|
|
472
486
|
optional WACommon.MessageKey commentParentKey = 1;
|
|
473
487
|
optional uint32 replyCount = 2;
|
|
@@ -535,6 +549,10 @@ message PollAdditionalMetadata {
|
|
|
535
549
|
optional bool pollInvalidated = 1;
|
|
536
550
|
}
|
|
537
551
|
|
|
552
|
+
message InteractiveMessageAdditionalMetadata {
|
|
553
|
+
optional bool isGalaxyFlowCompleted = 1;
|
|
554
|
+
}
|
|
555
|
+
|
|
538
556
|
message EventAdditionalMetadata {
|
|
539
557
|
optional bool isStale = 1;
|
|
540
558
|
}
|
|
@@ -579,3 +597,7 @@ message Citation {
|
|
|
579
597
|
required string cmsID = 3;
|
|
580
598
|
required string imageURL = 4;
|
|
581
599
|
}
|
|
600
|
+
|
|
601
|
+
message GroupHistoryIndividualMessageInfo {
|
|
602
|
+
optional WACommon.MessageKey bundleMessageKey = 1;
|
|
603
|
+
}
|
|
@@ -239,6 +239,8 @@ func (cli *Client) sendMessageReceipt(info *types.MessageInfo) {
|
|
|
239
239
|
}
|
|
240
240
|
if info.IsFromMe {
|
|
241
241
|
attrs["type"] = string(types.ReceiptTypeSender)
|
|
242
|
+
} else if info.Type == "peer_msg" {
|
|
243
|
+
attrs["type"] = string(types.ReceiptTypePeerMsg)
|
|
242
244
|
} else if cli.sendActiveReceipts.Load() == 0 {
|
|
243
245
|
attrs["type"] = string(types.ReceiptTypeInactive)
|
|
244
246
|
}
|
|
@@ -98,6 +98,7 @@ type infoQuery struct {
|
|
|
98
98
|
To types.JID
|
|
99
99
|
Target types.JID
|
|
100
100
|
ID string
|
|
101
|
+
SMaxID string
|
|
101
102
|
Content interface{}
|
|
102
103
|
|
|
103
104
|
Timeout time.Duration
|
|
@@ -118,6 +119,9 @@ func (cli *Client) sendIQAsyncAndGetData(query *infoQuery) (<-chan *waBinary.Nod
|
|
|
118
119
|
"xmlns": query.Namespace,
|
|
119
120
|
"type": string(query.Type),
|
|
120
121
|
}
|
|
122
|
+
if query.SMaxID != "" {
|
|
123
|
+
attrs["smax_id"] = query.SMaxID
|
|
124
|
+
}
|
|
121
125
|
if !query.To.IsEmpty() {
|
|
122
126
|
attrs["to"] = query.To
|
|
123
127
|
}
|
|
@@ -157,8 +157,7 @@ func (cli *Client) handleRetryReceipt(ctx context.Context, receipt *events.Recei
|
|
|
157
157
|
signalSKDMessage, err := builder.Create(ctx, senderKeyName)
|
|
158
158
|
if err != nil {
|
|
159
159
|
cli.Log.Warnf("Failed to create sender key distribution message to include in retry of %s in %s to %s: %v", messageID, receipt.Chat, receipt.Sender, err)
|
|
160
|
-
}
|
|
161
|
-
if msg.wa != nil {
|
|
160
|
+
} else if msg.wa != nil {
|
|
162
161
|
msg.wa.SenderKeyDistributionMessage = &waE2E.SenderKeyDistributionMessage{
|
|
163
162
|
GroupID: proto.String(receipt.Chat.String()),
|
|
164
163
|
AxolotlSenderKeyDistributionMessage: signalSKDMessage.Serialize(),
|
|
@@ -252,7 +251,7 @@ func (cli *Client) handleRetryReceipt(ctx context.Context, receipt *events.Recei
|
|
|
252
251
|
encryptionIdentity = lidForPN
|
|
253
252
|
}
|
|
254
253
|
}
|
|
255
|
-
encrypted, includeDeviceIdentity, err = cli.encryptMessageForDevice(ctx, plaintext, encryptionIdentity, bundle, encAttrs)
|
|
254
|
+
encrypted, includeDeviceIdentity, err = cli.encryptMessageForDevice(ctx, plaintext, encryptionIdentity, bundle, encAttrs, nil)
|
|
256
255
|
} else {
|
|
257
256
|
encrypted, err = cli.encryptMessageForDeviceV3(ctx, &waMsgTransport.MessageTransport_Payload{
|
|
258
257
|
ApplicationPayload: &waCommon.SubProtocol{
|
|
@@ -29,7 +29,7 @@ import (
|
|
|
29
29
|
"google.golang.org/protobuf/proto"
|
|
30
30
|
|
|
31
31
|
waBinary "go.mau.fi/whatsmeow/binary"
|
|
32
|
-
"go.mau.fi/whatsmeow/proto/
|
|
32
|
+
"go.mau.fi/whatsmeow/proto/waAICommon"
|
|
33
33
|
"go.mau.fi/whatsmeow/proto/waCommon"
|
|
34
34
|
"go.mau.fi/whatsmeow/proto/waE2E"
|
|
35
35
|
"go.mau.fi/whatsmeow/types"
|
|
@@ -74,7 +74,8 @@ func GenerateMessageID() types.MessageID {
|
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
type MessageDebugTimings struct {
|
|
77
|
-
|
|
77
|
+
LIDFetch time.Duration
|
|
78
|
+
Queue time.Duration
|
|
78
79
|
|
|
79
80
|
Marshal time.Duration
|
|
80
81
|
GetParticipants time.Duration
|
|
@@ -88,6 +89,9 @@ type MessageDebugTimings struct {
|
|
|
88
89
|
}
|
|
89
90
|
|
|
90
91
|
func (mdt MessageDebugTimings) MarshalZerologObject(evt *zerolog.Event) {
|
|
92
|
+
if mdt.LIDFetch != 0 {
|
|
93
|
+
evt.Dur("lid_fetch", mdt.LIDFetch)
|
|
94
|
+
}
|
|
91
95
|
evt.Dur("queue", mdt.Queue)
|
|
92
96
|
evt.Dur("marshal", mdt.Marshal)
|
|
93
97
|
if mdt.GetParticipants != 0 {
|
|
@@ -149,6 +153,8 @@ type SendRequestExtra struct {
|
|
|
149
153
|
MediaHandle string
|
|
150
154
|
|
|
151
155
|
Meta *types.MsgMetaInfo
|
|
156
|
+
// use this only if you know what you are doing
|
|
157
|
+
AdditionalNodes *[]waBinary.Node
|
|
152
158
|
}
|
|
153
159
|
|
|
154
160
|
// SendMessage sends the given message.
|
|
@@ -238,7 +244,7 @@ func (cli *Client) SendMessage(ctx context.Context, to types.JID, message *waE2E
|
|
|
238
244
|
|
|
239
245
|
if isBotMode {
|
|
240
246
|
if message.MessageContextInfo.BotMetadata == nil {
|
|
241
|
-
message.MessageContextInfo.BotMetadata = &
|
|
247
|
+
message.MessageContextInfo.BotMetadata = &waAICommon.BotMetadata{
|
|
242
248
|
PersonaID: proto.String("867051314767696$760019659443059"),
|
|
243
249
|
}
|
|
244
250
|
}
|
|
@@ -272,7 +278,11 @@ func (cli *Client) SendMessage(ctx context.Context, to types.JID, message *waE2E
|
|
|
272
278
|
return
|
|
273
279
|
}
|
|
274
280
|
|
|
275
|
-
participantNodes
|
|
281
|
+
var participantNodes []waBinary.Node
|
|
282
|
+
participantNodes, _, err = cli.encryptMessageForDevices(ctx, []types.JID{req.InlineBotJID}, resp.ID, messagePlaintext, nil, waBinary.Attrs{})
|
|
283
|
+
if err != nil {
|
|
284
|
+
return
|
|
285
|
+
}
|
|
276
286
|
extraParams.botNode = &waBinary.Node{
|
|
277
287
|
Tag: "bot",
|
|
278
288
|
Attrs: nil,
|
|
@@ -311,6 +321,28 @@ func (cli *Client) SendMessage(ctx context.Context, to types.JID, message *waE2E
|
|
|
311
321
|
resp.DebugTimings.GetParticipants = time.Since(start)
|
|
312
322
|
} else if to.Server == types.HiddenUserServer {
|
|
313
323
|
ownID = cli.getOwnLID()
|
|
324
|
+
} else if to.Server == types.DefaultUserServer && cli.Store.LIDMigrationTimestamp > 0 {
|
|
325
|
+
start := time.Now()
|
|
326
|
+
var toLID types.JID
|
|
327
|
+
toLID, err = cli.Store.LIDs.GetLIDForPN(ctx, to)
|
|
328
|
+
if err != nil {
|
|
329
|
+
err = fmt.Errorf("failed to get LID for PN %s: %w", to, err)
|
|
330
|
+
return
|
|
331
|
+
} else if toLID.IsEmpty() {
|
|
332
|
+
var info map[types.JID]types.UserInfo
|
|
333
|
+
info, err = cli.GetUserInfo([]types.JID{to})
|
|
334
|
+
if err != nil {
|
|
335
|
+
err = fmt.Errorf("failed to get user info for %s to fill LID cache: %w", to, err)
|
|
336
|
+
return
|
|
337
|
+
} else if toLID = info[to].LID; toLID.IsEmpty() {
|
|
338
|
+
err = fmt.Errorf("no LID found for %s from server", to)
|
|
339
|
+
return
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
resp.DebugTimings.LIDFetch = time.Since(start)
|
|
343
|
+
cli.Log.Debugf("Replacing SendMessage destination with LID as migration timestamp is set %s -> %s", to, toLID)
|
|
344
|
+
to = toLID
|
|
345
|
+
ownID = cli.getOwnLID()
|
|
314
346
|
}
|
|
315
347
|
if req.Meta != nil {
|
|
316
348
|
extraParams.metaNode = &waBinary.Node{
|
|
@@ -326,6 +358,10 @@ func (cli *Client) SendMessage(ctx context.Context, to types.JID, message *waE2E
|
|
|
326
358
|
}
|
|
327
359
|
}
|
|
328
360
|
|
|
361
|
+
if req.AdditionalNodes != nil {
|
|
362
|
+
extraParams.additionalNodes = req.AdditionalNodes
|
|
363
|
+
}
|
|
364
|
+
|
|
329
365
|
resp.Sender = ownID
|
|
330
366
|
|
|
331
367
|
start := time.Now()
|
|
@@ -582,6 +618,9 @@ func ParseDisappearingTimerString(val string) (time.Duration, bool) {
|
|
|
582
618
|
func (cli *Client) SetDisappearingTimer(chat types.JID, timer time.Duration, settingTS time.Time) (err error) {
|
|
583
619
|
switch chat.Server {
|
|
584
620
|
case types.DefaultUserServer, types.HiddenUserServer:
|
|
621
|
+
if settingTS.IsZero() {
|
|
622
|
+
settingTS = time.Now()
|
|
623
|
+
}
|
|
585
624
|
_, err = cli.SendMessage(context.TODO(), chat, &waE2E.Message{
|
|
586
625
|
ProtocolMessage: &waE2E.ProtocolMessage{
|
|
587
626
|
Type: waE2E.ProtocolMessage_EPHEMERAL_SETTING.Enum(),
|
|
@@ -673,9 +712,10 @@ func (cli *Client) sendNewsletter(
|
|
|
673
712
|
}
|
|
674
713
|
|
|
675
714
|
type nodeExtraParams struct {
|
|
676
|
-
botNode
|
|
677
|
-
metaNode
|
|
678
|
-
|
|
715
|
+
botNode *waBinary.Node
|
|
716
|
+
metaNode *waBinary.Node
|
|
717
|
+
additionalNodes *[]waBinary.Node
|
|
718
|
+
addressingMode types.AddressingMode
|
|
679
719
|
}
|
|
680
720
|
|
|
681
721
|
func (cli *Client) sendGroup(
|
|
@@ -1001,7 +1041,7 @@ func (cli *Client) preparePeerMessageNode(
|
|
|
1001
1041
|
}
|
|
1002
1042
|
}
|
|
1003
1043
|
start = time.Now()
|
|
1004
|
-
encrypted, isPreKey, err := cli.encryptMessageForDevice(ctx, plaintext, encryptionIdentity, nil, nil)
|
|
1044
|
+
encrypted, isPreKey, err := cli.encryptMessageForDevice(ctx, plaintext, encryptionIdentity, nil, nil, nil)
|
|
1005
1045
|
timings.PeerEncrypt = time.Since(start)
|
|
1006
1046
|
if err != nil {
|
|
1007
1047
|
return nil, fmt.Errorf("failed to encrypt peer message for %s: %v", to, err)
|
|
@@ -1047,6 +1087,9 @@ func (cli *Client) getMessageContent(
|
|
|
1047
1087
|
if extraParams.metaNode != nil {
|
|
1048
1088
|
content = append(content, *extraParams.metaNode)
|
|
1049
1089
|
}
|
|
1090
|
+
if extraParams.additionalNodes != nil {
|
|
1091
|
+
content = append(content, *extraParams.additionalNodes...)
|
|
1092
|
+
}
|
|
1050
1093
|
|
|
1051
1094
|
if buttonType := getButtonTypeFromMessage(message); buttonType != "" {
|
|
1052
1095
|
content = append(content, waBinary.Node{
|
|
@@ -1101,10 +1144,13 @@ func (cli *Client) prepareMessageNode(
|
|
|
1101
1144
|
}
|
|
1102
1145
|
|
|
1103
1146
|
start = time.Now()
|
|
1104
|
-
participantNodes, includeIdentity := cli.encryptMessageForDevices(
|
|
1147
|
+
participantNodes, includeIdentity, err := cli.encryptMessageForDevices(
|
|
1105
1148
|
ctx, allDevices, id, plaintext, dsmPlaintext, encAttrs,
|
|
1106
1149
|
)
|
|
1107
1150
|
timings.PeerEncrypt = time.Since(start)
|
|
1151
|
+
if err != nil {
|
|
1152
|
+
return nil, nil, err
|
|
1153
|
+
}
|
|
1108
1154
|
participantNode := waBinary.Node{
|
|
1109
1155
|
Tag: "participants",
|
|
1110
1156
|
Content: participantNodes,
|
|
@@ -1162,11 +1208,43 @@ func (cli *Client) encryptMessageForDevices(
|
|
|
1162
1208
|
id string,
|
|
1163
1209
|
msgPlaintext, dsmPlaintext []byte,
|
|
1164
1210
|
encAttrs waBinary.Attrs,
|
|
1165
|
-
) ([]waBinary.Node, bool) {
|
|
1211
|
+
) ([]waBinary.Node, bool, error) {
|
|
1166
1212
|
ownJID := cli.getOwnID()
|
|
1167
1213
|
ownLID := cli.getOwnLID()
|
|
1168
1214
|
includeIdentity := false
|
|
1169
1215
|
participantNodes := make([]waBinary.Node, 0, len(allDevices))
|
|
1216
|
+
|
|
1217
|
+
var pnDevices []types.JID
|
|
1218
|
+
for _, jid := range allDevices {
|
|
1219
|
+
if jid.Server == types.DefaultUserServer {
|
|
1220
|
+
pnDevices = append(pnDevices, jid)
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
1223
|
+
lidMappings, err := cli.Store.LIDs.GetManyLIDsForPNs(ctx, pnDevices)
|
|
1224
|
+
if err != nil {
|
|
1225
|
+
return nil, false, fmt.Errorf("failed to fetch LID mappings: %w", err)
|
|
1226
|
+
}
|
|
1227
|
+
|
|
1228
|
+
encryptionIdentities := make(map[types.JID]types.JID, len(allDevices))
|
|
1229
|
+
var sessionAddresses []string
|
|
1230
|
+
for _, jid := range allDevices {
|
|
1231
|
+
encryptionIdentity := jid
|
|
1232
|
+
if jid.Server == types.DefaultUserServer {
|
|
1233
|
+
// TODO query LID from server for missing entries
|
|
1234
|
+
if lidForPN, ok := lidMappings[jid]; ok && !lidForPN.IsEmpty() {
|
|
1235
|
+
cli.migrateSessionStore(ctx, jid, lidForPN)
|
|
1236
|
+
encryptionIdentity = lidForPN
|
|
1237
|
+
}
|
|
1238
|
+
}
|
|
1239
|
+
encryptionIdentities[jid] = encryptionIdentity
|
|
1240
|
+
sessionAddresses = append(sessionAddresses, encryptionIdentity.SignalAddress().String())
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1243
|
+
existingSessions, err := cli.Store.Sessions.HasManySessions(ctx, sessionAddresses)
|
|
1244
|
+
if err != nil {
|
|
1245
|
+
return nil, false, fmt.Errorf("failed to check which sessions exist: %w", err)
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1170
1248
|
var retryDevices, retryEncryptionIdentities []types.JID
|
|
1171
1249
|
for _, jid := range allDevices {
|
|
1172
1250
|
plaintext := msgPlaintext
|
|
@@ -1176,19 +1254,10 @@ func (cli *Client) encryptMessageForDevices(
|
|
|
1176
1254
|
}
|
|
1177
1255
|
plaintext = dsmPlaintext
|
|
1178
1256
|
}
|
|
1179
|
-
encryptionIdentity := jid
|
|
1180
|
-
if jid.Server == types.DefaultUserServer {
|
|
1181
|
-
lidForPN, err := cli.Store.LIDs.GetLIDForPN(ctx, jid)
|
|
1182
|
-
if err != nil {
|
|
1183
|
-
cli.Log.Warnf("Failed to get LID for %s: %v", jid, err)
|
|
1184
|
-
} else if !lidForPN.IsEmpty() {
|
|
1185
|
-
cli.migrateSessionStore(ctx, jid, lidForPN)
|
|
1186
|
-
encryptionIdentity = lidForPN
|
|
1187
|
-
}
|
|
1188
|
-
}
|
|
1257
|
+
encryptionIdentity := encryptionIdentities[jid]
|
|
1189
1258
|
|
|
1190
1259
|
encrypted, isPreKey, err := cli.encryptMessageForDeviceAndWrap(
|
|
1191
|
-
ctx, plaintext, jid, encryptionIdentity, nil, encAttrs,
|
|
1260
|
+
ctx, plaintext, jid, encryptionIdentity, nil, encAttrs, existingSessions,
|
|
1192
1261
|
)
|
|
1193
1262
|
if errors.Is(err, ErrNoSession) {
|
|
1194
1263
|
retryDevices = append(retryDevices, jid)
|
|
@@ -1197,6 +1266,9 @@ func (cli *Client) encryptMessageForDevices(
|
|
|
1197
1266
|
} else if err != nil {
|
|
1198
1267
|
// TODO return these errors if it's a fatal one (like context cancellation or database)
|
|
1199
1268
|
cli.Log.Warnf("Failed to encrypt %s for %s: %v", id, jid, err)
|
|
1269
|
+
if ctx.Err() != nil {
|
|
1270
|
+
return nil, false, err
|
|
1271
|
+
}
|
|
1200
1272
|
continue
|
|
1201
1273
|
}
|
|
1202
1274
|
|
|
@@ -1221,7 +1293,7 @@ func (cli *Client) encryptMessageForDevices(
|
|
|
1221
1293
|
plaintext = dsmPlaintext
|
|
1222
1294
|
}
|
|
1223
1295
|
encrypted, isPreKey, err := cli.encryptMessageForDeviceAndWrap(
|
|
1224
|
-
ctx, plaintext, jid, retryEncryptionIdentities[i], resp.bundle, encAttrs,
|
|
1296
|
+
ctx, plaintext, jid, retryEncryptionIdentities[i], resp.bundle, encAttrs, nil,
|
|
1225
1297
|
)
|
|
1226
1298
|
if err != nil {
|
|
1227
1299
|
// TODO return these errors if it's a fatal one (like context cancellation or database)
|
|
@@ -1235,7 +1307,7 @@ func (cli *Client) encryptMessageForDevices(
|
|
|
1235
1307
|
}
|
|
1236
1308
|
}
|
|
1237
1309
|
}
|
|
1238
|
-
return participantNodes, includeIdentity
|
|
1310
|
+
return participantNodes, includeIdentity, nil
|
|
1239
1311
|
}
|
|
1240
1312
|
|
|
1241
1313
|
func (cli *Client) encryptMessageForDeviceAndWrap(
|
|
@@ -1245,9 +1317,10 @@ func (cli *Client) encryptMessageForDeviceAndWrap(
|
|
|
1245
1317
|
encryptionIdentity types.JID,
|
|
1246
1318
|
bundle *prekey.Bundle,
|
|
1247
1319
|
encAttrs waBinary.Attrs,
|
|
1320
|
+
existingSessions map[string]bool,
|
|
1248
1321
|
) (*waBinary.Node, bool, error) {
|
|
1249
1322
|
node, includeDeviceIdentity, err := cli.encryptMessageForDevice(
|
|
1250
|
-
ctx, plaintext, encryptionIdentity, bundle, encAttrs,
|
|
1323
|
+
ctx, plaintext, encryptionIdentity, bundle, encAttrs, existingSessions,
|
|
1251
1324
|
)
|
|
1252
1325
|
if err != nil {
|
|
1253
1326
|
return nil, false, err
|
|
@@ -1271,6 +1344,7 @@ func (cli *Client) encryptMessageForDevice(
|
|
|
1271
1344
|
to types.JID,
|
|
1272
1345
|
bundle *prekey.Bundle,
|
|
1273
1346
|
extraAttrs waBinary.Attrs,
|
|
1347
|
+
existingSessions map[string]bool,
|
|
1274
1348
|
) (*waBinary.Node, bool, error) {
|
|
1275
1349
|
builder := session.NewBuilderFromSignal(cli.Store, to.SignalAddress(), pbSerializer)
|
|
1276
1350
|
if bundle != nil {
|
|
@@ -1287,10 +1361,18 @@ func (cli *Client) encryptMessageForDevice(
|
|
|
1287
1361
|
if err != nil {
|
|
1288
1362
|
return nil, false, fmt.Errorf("failed to process prekey bundle: %w", err)
|
|
1289
1363
|
}
|
|
1290
|
-
} else
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1364
|
+
} else {
|
|
1365
|
+
sessionExists, checked := existingSessions[to.SignalAddress().String()]
|
|
1366
|
+
if !checked {
|
|
1367
|
+
var err error
|
|
1368
|
+
sessionExists, err = cli.Store.ContainsSession(ctx, to.SignalAddress())
|
|
1369
|
+
if err != nil {
|
|
1370
|
+
return nil, false, err
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
1373
|
+
if !sessionExists {
|
|
1374
|
+
return nil, false, fmt.Errorf("%w with %s", ErrNoSession, to.SignalAddress().String())
|
|
1375
|
+
}
|
|
1294
1376
|
}
|
|
1295
1377
|
cipher := session.NewCipher(builder, to.SignalAddress())
|
|
1296
1378
|
ciphertext, err := cipher.Encrypt(ctx, padMessage(plaintext))
|
|
@@ -76,7 +76,7 @@ func (vc WAVersionContainer) ProtoAppVersion() *waWa6.ClientPayload_UserAgent_Ap
|
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
// waVersion is the WhatsApp web client version
|
|
79
|
-
var waVersion = WAVersionContainer{2, 3000,
|
|
79
|
+
var waVersion = WAVersionContainer{2, 3000, 1027949008}
|
|
80
80
|
|
|
81
81
|
// waVersionHash is the md5 hash of a dot-separated waVersion
|
|
82
82
|
var waVersionHash [16]byte
|
|
@@ -68,6 +68,10 @@ func (n *NoopStore) HasSession(ctx context.Context, address string) (bool, error
|
|
|
68
68
|
return false, n.Error
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
+
func (n *NoopStore) HasManySessions(ctx context.Context, addresses []string) (map[string]bool, error) {
|
|
72
|
+
return nil, n.Error
|
|
73
|
+
}
|
|
74
|
+
|
|
71
75
|
func (n *NoopStore) PutSession(ctx context.Context, address string, session []byte) error {
|
|
72
76
|
return n.Error
|
|
73
77
|
}
|
|
@@ -168,6 +172,10 @@ func (n *NoopStore) PutAllContactNames(ctx context.Context, contacts []ContactEn
|
|
|
168
172
|
return n.Error
|
|
169
173
|
}
|
|
170
174
|
|
|
175
|
+
func (n *NoopStore) PutManyRedactedPhones(ctx context.Context, entries []RedactedPhoneEntry) error {
|
|
176
|
+
return n.Error
|
|
177
|
+
}
|
|
178
|
+
|
|
171
179
|
func (n *NoopStore) GetContact(ctx context.Context, user types.JID) (types.ContactInfo, error) {
|
|
172
180
|
return types.ContactInfo{}, n.Error
|
|
173
181
|
}
|
|
@@ -244,6 +252,10 @@ func (n *NoopStore) GetLIDForPN(ctx context.Context, pn types.JID) (types.JID, e
|
|
|
244
252
|
return types.JID{}, n.Error
|
|
245
253
|
}
|
|
246
254
|
|
|
255
|
+
func (n *NoopStore) GetManyLIDsForPNs(ctx context.Context, pns []types.JID) (map[types.JID]types.JID, error) {
|
|
256
|
+
return nil, n.Error
|
|
257
|
+
}
|
|
258
|
+
|
|
247
259
|
func (n *NoopStore) GetPNForLID(ctx context.Context, lid types.JID) (types.JID, error) {
|
|
248
260
|
return types.JID{}, n.Error
|
|
249
261
|
}
|
|
@@ -13,10 +13,12 @@ import (
|
|
|
13
13
|
"errors"
|
|
14
14
|
"fmt"
|
|
15
15
|
"slices"
|
|
16
|
+
"strings"
|
|
16
17
|
"sync"
|
|
17
18
|
|
|
18
19
|
"github.com/rs/zerolog"
|
|
19
20
|
"go.mau.fi/util/dbutil"
|
|
21
|
+
"go.mau.fi/util/exslices"
|
|
20
22
|
|
|
21
23
|
"go.mau.fi/whatsmeow/store"
|
|
22
24
|
"go.mau.fi/whatsmeow/types"
|
|
@@ -61,18 +63,30 @@ func (s *CachedLIDMap) FillCache(ctx context.Context) error {
|
|
|
61
63
|
if err != nil {
|
|
62
64
|
return err
|
|
63
65
|
}
|
|
64
|
-
|
|
66
|
+
err = s.scanManyLids(rows, nil)
|
|
67
|
+
s.cacheFilled = err == nil
|
|
68
|
+
return err
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
func (s *CachedLIDMap) scanManyLids(rows dbutil.Rows, fn func(lid, pn string)) error {
|
|
72
|
+
if fn == nil {
|
|
73
|
+
fn = func(lid, pn string) {}
|
|
74
|
+
}
|
|
65
75
|
for rows.Next() {
|
|
66
76
|
var lid, pn string
|
|
67
|
-
err
|
|
77
|
+
err := rows.Scan(&lid, &pn)
|
|
68
78
|
if err != nil {
|
|
69
79
|
return err
|
|
70
80
|
}
|
|
71
81
|
s.pnToLIDCache[pn] = lid
|
|
72
82
|
s.lidToPNCache[lid] = pn
|
|
83
|
+
fn(lid, pn)
|
|
73
84
|
}
|
|
74
|
-
|
|
75
|
-
|
|
85
|
+
err := rows.Close()
|
|
86
|
+
if err != nil {
|
|
87
|
+
return err
|
|
88
|
+
}
|
|
89
|
+
return rows.Err()
|
|
76
90
|
}
|
|
77
91
|
|
|
78
92
|
func (s *CachedLIDMap) getLIDMapping(ctx context.Context, source types.JID, targetServer, query string, sourceToTarget, targetToSource map[string]string) (types.JID, error) {
|
|
@@ -122,6 +136,69 @@ func (s *CachedLIDMap) GetPNForLID(ctx context.Context, lid types.JID) (types.JI
|
|
|
122
136
|
)
|
|
123
137
|
}
|
|
124
138
|
|
|
139
|
+
func (s *CachedLIDMap) GetManyLIDsForPNs(ctx context.Context, pns []types.JID) (map[types.JID]types.JID, error) {
|
|
140
|
+
if len(pns) == 0 {
|
|
141
|
+
return nil, nil
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
result := make(map[types.JID]types.JID, len(pns))
|
|
145
|
+
|
|
146
|
+
s.lidCacheLock.RLock()
|
|
147
|
+
missingPNs := make([]string, 0, len(pns))
|
|
148
|
+
missingPNDevices := make(map[string][]types.JID)
|
|
149
|
+
for _, pn := range pns {
|
|
150
|
+
if pn.Server != types.DefaultUserServer {
|
|
151
|
+
continue
|
|
152
|
+
}
|
|
153
|
+
if lidUser, ok := s.pnToLIDCache[pn.User]; ok && lidUser != "" {
|
|
154
|
+
result[pn] = types.JID{User: lidUser, Device: pn.Device, Server: types.HiddenUserServer}
|
|
155
|
+
} else if !s.cacheFilled {
|
|
156
|
+
missingPNs = append(missingPNs, pn.User)
|
|
157
|
+
missingPNDevices[pn.User] = append(missingPNDevices[pn.User], pn)
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
s.lidCacheLock.RUnlock()
|
|
161
|
+
|
|
162
|
+
if len(missingPNs) == 0 {
|
|
163
|
+
return result, nil
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
s.lidCacheLock.Lock()
|
|
167
|
+
defer s.lidCacheLock.Unlock()
|
|
168
|
+
|
|
169
|
+
var rows dbutil.Rows
|
|
170
|
+
var err error
|
|
171
|
+
if s.db.Dialect == dbutil.Postgres && PostgresArrayWrapper != nil {
|
|
172
|
+
rows, err = s.db.Query(
|
|
173
|
+
ctx,
|
|
174
|
+
`SELECT lid, pn FROM whatsmeow_lid_map WHERE pn = ANY($1)`,
|
|
175
|
+
PostgresArrayWrapper(missingPNs),
|
|
176
|
+
)
|
|
177
|
+
} else {
|
|
178
|
+
placeholders := make([]string, len(missingPNs))
|
|
179
|
+
for i := range missingPNs {
|
|
180
|
+
placeholders[i] = fmt.Sprintf("$%d", i+1)
|
|
181
|
+
}
|
|
182
|
+
rows, err = s.db.Query(
|
|
183
|
+
ctx,
|
|
184
|
+
fmt.Sprintf(`SELECT lid, pn FROM whatsmeow_lid_map WHERE pn IN (%s)`, strings.Join(placeholders, ",")),
|
|
185
|
+
exslices.CastToAny(missingPNs)...,
|
|
186
|
+
)
|
|
187
|
+
}
|
|
188
|
+
if err != nil {
|
|
189
|
+
return nil, err
|
|
190
|
+
}
|
|
191
|
+
err = s.scanManyLids(rows, func(lid, pn string) {
|
|
192
|
+
for _, dev := range missingPNDevices[pn] {
|
|
193
|
+
lidDev := dev
|
|
194
|
+
lidDev.Server = types.HiddenUserServer
|
|
195
|
+
lidDev.User = lid
|
|
196
|
+
result[dev] = lidDev.ToNonAD()
|
|
197
|
+
}
|
|
198
|
+
})
|
|
199
|
+
return result, err
|
|
200
|
+
}
|
|
201
|
+
|
|
125
202
|
func (s *CachedLIDMap) PutLIDMapping(ctx context.Context, lid, pn types.JID) error {
|
|
126
203
|
if lid.Server != types.HiddenUserServer || pn.Server != types.DefaultUserServer {
|
|
127
204
|
return fmt.Errorf("invalid PutLIDMapping call %s/%s", lid, pn)
|
|
@@ -154,6 +231,7 @@ func (s *CachedLIDMap) PutManyLIDMappings(ctx context.Context, mappings []store.
|
|
|
154
231
|
}
|
|
155
232
|
return false
|
|
156
233
|
})
|
|
234
|
+
mappings = exslices.DeduplicateUnsortedOverwrite(mappings)
|
|
157
235
|
if len(mappings) == 0 {
|
|
158
236
|
return nil
|
|
159
237
|
}
|