slidge-whatsapp 0.3.0__cp313-cp313-manylinux_2_36_aarch64.whl → 0.3.1__cp313-cp313-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.
Files changed (131) hide show
  1. slidge_whatsapp/event.go +33 -9
  2. slidge_whatsapp/generated/_whatsapp.cpython-313-aarch64-linux-gnu.h +179 -179
  3. slidge_whatsapp/generated/_whatsapp.cpython-313-aarch64-linux-gnu.so +0 -0
  4. slidge_whatsapp/generated/build.py +145 -145
  5. slidge_whatsapp/generated/whatsapp.c +1650 -1650
  6. slidge_whatsapp/generated/whatsapp.go +1032 -1032
  7. slidge_whatsapp/generated/whatsapp.py +1274 -1274
  8. slidge_whatsapp/generated/whatsapp_go.h +179 -179
  9. slidge_whatsapp/go.mod +5 -5
  10. slidge_whatsapp/go.sum +14 -14
  11. slidge_whatsapp/session.go +3 -3
  12. slidge_whatsapp/vendor/github.com/ebitengine/purego/README.md +21 -5
  13. slidge_whatsapp/vendor/github.com/ebitengine/purego/abi_loong64.h +60 -0
  14. slidge_whatsapp/vendor/github.com/ebitengine/purego/cgo.go +1 -1
  15. slidge_whatsapp/vendor/github.com/ebitengine/purego/dlerror.go +1 -1
  16. slidge_whatsapp/vendor/github.com/ebitengine/purego/dlfcn.go +1 -1
  17. slidge_whatsapp/vendor/github.com/ebitengine/purego/dlfcn_netbsd.go +15 -0
  18. slidge_whatsapp/vendor/github.com/ebitengine/purego/dlfcn_nocgo_netbsd.go +9 -0
  19. slidge_whatsapp/vendor/github.com/ebitengine/purego/dlfcn_stubs.s +1 -1
  20. slidge_whatsapp/vendor/github.com/ebitengine/purego/func.go +113 -60
  21. slidge_whatsapp/vendor/github.com/ebitengine/purego/gen.go +6 -0
  22. slidge_whatsapp/vendor/github.com/ebitengine/purego/go_runtime.go +1 -1
  23. slidge_whatsapp/vendor/github.com/ebitengine/purego/internal/cgo/dlfcn_cgo_unix.go +2 -2
  24. slidge_whatsapp/vendor/github.com/ebitengine/purego/internal/cgo/syscall_cgo_unix.go +2 -2
  25. slidge_whatsapp/vendor/github.com/ebitengine/purego/internal/fakecgo/abi_loong64.h +60 -0
  26. slidge_whatsapp/vendor/github.com/ebitengine/purego/internal/fakecgo/asm_loong64.s +40 -0
  27. slidge_whatsapp/vendor/github.com/ebitengine/purego/internal/fakecgo/callbacks.go +1 -1
  28. slidge_whatsapp/vendor/github.com/ebitengine/purego/internal/fakecgo/doc.go +1 -1
  29. slidge_whatsapp/vendor/github.com/ebitengine/purego/internal/fakecgo/go_libinit.go +1 -1
  30. slidge_whatsapp/vendor/github.com/ebitengine/purego/internal/fakecgo/go_linux_loong64.go +92 -0
  31. slidge_whatsapp/vendor/github.com/ebitengine/purego/internal/fakecgo/go_netbsd.go +106 -0
  32. slidge_whatsapp/vendor/github.com/ebitengine/purego/internal/fakecgo/go_setenv.go +1 -1
  33. slidge_whatsapp/vendor/github.com/ebitengine/purego/internal/fakecgo/go_util.go +1 -1
  34. slidge_whatsapp/vendor/github.com/ebitengine/purego/internal/fakecgo/iscgo.go +1 -1
  35. slidge_whatsapp/vendor/github.com/ebitengine/purego/internal/fakecgo/libcgo.go +1 -1
  36. slidge_whatsapp/vendor/github.com/ebitengine/purego/internal/fakecgo/libcgo_darwin.go +4 -0
  37. slidge_whatsapp/vendor/github.com/ebitengine/purego/internal/fakecgo/libcgo_freebsd.go +4 -0
  38. slidge_whatsapp/vendor/github.com/ebitengine/purego/internal/fakecgo/libcgo_linux.go +4 -0
  39. slidge_whatsapp/vendor/github.com/ebitengine/purego/internal/fakecgo/libcgo_netbsd.go +26 -0
  40. slidge_whatsapp/vendor/github.com/ebitengine/purego/internal/fakecgo/netbsd.go +23 -0
  41. slidge_whatsapp/vendor/github.com/ebitengine/purego/internal/fakecgo/setenv.go +1 -1
  42. slidge_whatsapp/vendor/github.com/ebitengine/purego/internal/fakecgo/symbols.go +11 -1
  43. slidge_whatsapp/vendor/github.com/ebitengine/purego/internal/fakecgo/symbols_darwin.go +1 -0
  44. slidge_whatsapp/vendor/github.com/ebitengine/purego/internal/fakecgo/symbols_freebsd.go +1 -0
  45. slidge_whatsapp/vendor/github.com/ebitengine/purego/internal/fakecgo/symbols_linux.go +1 -0
  46. slidge_whatsapp/vendor/github.com/ebitengine/purego/internal/fakecgo/symbols_netbsd.go +30 -0
  47. slidge_whatsapp/vendor/github.com/ebitengine/purego/internal/fakecgo/trampolines_loong64.s +71 -0
  48. slidge_whatsapp/vendor/github.com/ebitengine/purego/internal/fakecgo/trampolines_stubs.s +5 -1
  49. slidge_whatsapp/vendor/github.com/ebitengine/purego/nocgo.go +1 -1
  50. slidge_whatsapp/vendor/github.com/ebitengine/purego/struct_amd64.go +8 -4
  51. slidge_whatsapp/vendor/github.com/ebitengine/purego/struct_arm64.go +16 -6
  52. slidge_whatsapp/vendor/github.com/ebitengine/purego/struct_loong64.go +190 -0
  53. slidge_whatsapp/vendor/github.com/ebitengine/purego/struct_other.go +6 -2
  54. slidge_whatsapp/vendor/github.com/ebitengine/purego/sys_amd64.s +1 -1
  55. slidge_whatsapp/vendor/github.com/ebitengine/purego/sys_arm64.s +1 -1
  56. slidge_whatsapp/vendor/github.com/ebitengine/purego/sys_loong64.s +96 -0
  57. slidge_whatsapp/vendor/github.com/ebitengine/purego/sys_unix_arm64.s +1 -1
  58. slidge_whatsapp/vendor/github.com/ebitengine/purego/sys_unix_loong64.s +75 -0
  59. slidge_whatsapp/vendor/github.com/ebitengine/purego/syscall.go +6 -3
  60. slidge_whatsapp/vendor/github.com/ebitengine/purego/syscall_cgo_linux.go +3 -3
  61. slidge_whatsapp/vendor/github.com/ebitengine/purego/syscall_sysv.go +13 -10
  62. slidge_whatsapp/vendor/github.com/ebitengine/purego/syscall_windows.go +1 -1
  63. slidge_whatsapp/vendor/github.com/ebitengine/purego/zcallback_amd64.s +2002 -2002
  64. slidge_whatsapp/vendor/github.com/ebitengine/purego/zcallback_arm64.s +4002 -4002
  65. slidge_whatsapp/vendor/github.com/ebitengine/purego/zcallback_loong64.s +4014 -0
  66. slidge_whatsapp/vendor/go.mau.fi/util/dbutil/log.go +1 -0
  67. slidge_whatsapp/vendor/go.mau.fi/util/dbutil/module.go +118 -0
  68. slidge_whatsapp/vendor/go.mau.fi/util/dbutil/upgradetable.go +0 -34
  69. slidge_whatsapp/vendor/go.mau.fi/util/exbytes/string.go +20 -0
  70. slidge_whatsapp/vendor/go.mau.fi/util/exbytes/writer.go +78 -0
  71. slidge_whatsapp/vendor/go.mau.fi/util/exslices/cast.go +42 -0
  72. slidge_whatsapp/vendor/go.mau.fi/util/exslices/chunk.go +28 -0
  73. slidge_whatsapp/vendor/go.mau.fi/util/exslices/deduplicate.go +67 -0
  74. slidge_whatsapp/vendor/go.mau.fi/util/exslices/diff.go +63 -0
  75. slidge_whatsapp/vendor/go.mau.fi/util/exsync/event.go +15 -1
  76. slidge_whatsapp/vendor/go.mau.fi/util/random/string.go +47 -7
  77. slidge_whatsapp/vendor/go.mau.fi/whatsmeow/appstate/decode.go +1 -0
  78. slidge_whatsapp/vendor/go.mau.fi/whatsmeow/appstate/encode.go +34 -0
  79. slidge_whatsapp/vendor/go.mau.fi/whatsmeow/appstate/hash.go +1 -0
  80. slidge_whatsapp/vendor/go.mau.fi/whatsmeow/appstate.go +3 -0
  81. slidge_whatsapp/vendor/go.mau.fi/whatsmeow/armadillomessage.go +1 -2
  82. slidge_whatsapp/vendor/go.mau.fi/whatsmeow/call.go +6 -0
  83. slidge_whatsapp/vendor/go.mau.fi/whatsmeow/errors.go +1 -0
  84. slidge_whatsapp/vendor/go.mau.fi/whatsmeow/group.go +63 -42
  85. slidge_whatsapp/vendor/go.mau.fi/whatsmeow/internals.go +14 -10
  86. slidge_whatsapp/vendor/go.mau.fi/whatsmeow/message.go +45 -18
  87. slidge_whatsapp/vendor/go.mau.fi/whatsmeow/msgsecret.go +23 -0
  88. slidge_whatsapp/vendor/go.mau.fi/whatsmeow/notification.go +5 -1
  89. slidge_whatsapp/vendor/go.mau.fi/whatsmeow/pair.go +3 -7
  90. slidge_whatsapp/vendor/go.mau.fi/whatsmeow/proto/waAICommon/WAAICommon.pb.go +7747 -0
  91. slidge_whatsapp/vendor/go.mau.fi/whatsmeow/proto/{waBotMetadata/WABotMetadata.proto → waAICommon/WAAICommon.proto} +269 -9
  92. slidge_whatsapp/vendor/go.mau.fi/whatsmeow/proto/waDeviceCapabilities/WAProtobufsDeviceCapabilities.pb.go +128 -14
  93. slidge_whatsapp/vendor/go.mau.fi/whatsmeow/proto/waDeviceCapabilities/WAProtobufsDeviceCapabilities.proto +10 -0
  94. slidge_whatsapp/vendor/go.mau.fi/whatsmeow/proto/waE2E/WAWebProtobufsE2E.pb.go +3236 -4732
  95. slidge_whatsapp/vendor/go.mau.fi/whatsmeow/proto/waE2E/WAWebProtobufsE2E.proto +125 -273
  96. slidge_whatsapp/vendor/go.mau.fi/whatsmeow/proto/waHistorySync/WAWebProtobufsHistorySync.pb.go +11 -2
  97. slidge_whatsapp/vendor/go.mau.fi/whatsmeow/proto/waHistorySync/WAWebProtobufsHistorySync.proto +1 -0
  98. slidge_whatsapp/vendor/go.mau.fi/whatsmeow/proto/waStatusAttributions/WAStatusAttributions.pb.go +220 -81
  99. slidge_whatsapp/vendor/go.mau.fi/whatsmeow/proto/waStatusAttributions/WAStatusAttributions.proto +13 -0
  100. slidge_whatsapp/vendor/go.mau.fi/whatsmeow/proto/waSyncAction/WASyncAction.pb.go +705 -449
  101. slidge_whatsapp/vendor/go.mau.fi/whatsmeow/proto/waSyncAction/WASyncAction.proto +23 -0
  102. slidge_whatsapp/vendor/go.mau.fi/whatsmeow/proto/waWa6/WAWebProtobufsWa6.pb.go +78 -24
  103. slidge_whatsapp/vendor/go.mau.fi/whatsmeow/proto/waWa6/WAWebProtobufsWa6.proto +6 -0
  104. slidge_whatsapp/vendor/go.mau.fi/whatsmeow/proto/waWeb/WAWebProtobufsWeb.pb.go +516 -267
  105. slidge_whatsapp/vendor/go.mau.fi/whatsmeow/proto/waWeb/WAWebProtobufsWeb.proto +22 -0
  106. slidge_whatsapp/vendor/go.mau.fi/whatsmeow/receipt.go +2 -0
  107. slidge_whatsapp/vendor/go.mau.fi/whatsmeow/request.go +4 -0
  108. slidge_whatsapp/vendor/go.mau.fi/whatsmeow/retry.go +2 -3
  109. slidge_whatsapp/vendor/go.mau.fi/whatsmeow/send.go +110 -28
  110. slidge_whatsapp/vendor/go.mau.fi/whatsmeow/store/clientpayload.go +1 -1
  111. slidge_whatsapp/vendor/go.mau.fi/whatsmeow/store/noop.go +12 -0
  112. slidge_whatsapp/vendor/go.mau.fi/whatsmeow/store/sqlstore/lidmap.go +82 -4
  113. slidge_whatsapp/vendor/go.mau.fi/whatsmeow/store/sqlstore/store.go +112 -55
  114. slidge_whatsapp/vendor/go.mau.fi/whatsmeow/store/sqlstore/upgrades/00-latest-schema.sql +8 -7
  115. slidge_whatsapp/vendor/go.mau.fi/whatsmeow/store/sqlstore/upgrades/11-redacted-phone-contacts.sql +2 -0
  116. slidge_whatsapp/vendor/go.mau.fi/whatsmeow/store/store.go +20 -0
  117. slidge_whatsapp/vendor/go.mau.fi/whatsmeow/types/call.go +6 -5
  118. slidge_whatsapp/vendor/go.mau.fi/whatsmeow/types/message.go +7 -1
  119. slidge_whatsapp/vendor/go.mau.fi/whatsmeow/types/user.go +3 -0
  120. slidge_whatsapp/vendor/go.mau.fi/whatsmeow/user.go +31 -2
  121. slidge_whatsapp/vendor/google.golang.org/protobuf/internal/filedesc/desc.go +35 -17
  122. slidge_whatsapp/vendor/google.golang.org/protobuf/internal/filedesc/desc_init.go +14 -0
  123. slidge_whatsapp/vendor/google.golang.org/protobuf/internal/filedesc/desc_lazy.go +20 -0
  124. slidge_whatsapp/vendor/google.golang.org/protobuf/internal/version/version.go +1 -1
  125. slidge_whatsapp/vendor/modules.txt +8 -6
  126. {slidge_whatsapp-0.3.0.dist-info → slidge_whatsapp-0.3.1.dist-info}/METADATA +1 -1
  127. {slidge_whatsapp-0.3.0.dist-info → slidge_whatsapp-0.3.1.dist-info}/RECORD +130 -106
  128. slidge_whatsapp/vendor/go.mau.fi/whatsmeow/proto/waBotMetadata/WABotMetadata.pb.go +0 -5156
  129. {slidge_whatsapp-0.3.0.dist-info → slidge_whatsapp-0.3.1.dist-info}/WHEEL +0 -0
  130. {slidge_whatsapp-0.3.0.dist-info → slidge_whatsapp-0.3.1.dist-info}/entry_points.txt +0 -0
  131. {slidge_whatsapp-0.3.0.dist-info → slidge_whatsapp-0.3.1.dist-info}/licenses/LICENSE +0 -0
@@ -115,6 +115,7 @@ func (z zeroLogger) QueryTiming(ctx context.Context, method, query string, args
115
115
  if duration >= 1*time.Second {
116
116
  evt := log.Warn().
117
117
  Float64("duration_seconds", duration.Seconds()).
118
+ AnErr("result_error", err).
118
119
  Str("method", method).
119
120
  Str("query", query)
120
121
  if z.Caller {
@@ -0,0 +1,118 @@
1
+ // Package dbutil provides a simple framework for in-process database
2
+ // migrations. You provide the SQL files and they are run to upgrade
3
+ // the database. A versions table is automatically created in the
4
+ // database to track which migrations have been applied. There is
5
+ // support for multiple migration pathways, for example v0->v2 versus
6
+ // v0->v1->v2, and the shorter one is prioritized if both are
7
+ // provided.
8
+ //
9
+ // Example usage from Go:
10
+ //
11
+ // package main
12
+ //
13
+ // import (
14
+ // "context"
15
+ // "database/sql"
16
+ // "embed"
17
+ //
18
+ // "go.mau.fi/util/dbutil"
19
+ // )
20
+ //
21
+ // //go:embed *.sql
22
+ // var upgrades embed.FS
23
+ //
24
+ // func mainE() error {
25
+ // ctx := context.Background()
26
+ // rawDB, err := sql.Open("sqlite3", "./hotdogs.db")
27
+ // if err != nil {
28
+ // return err
29
+ // }
30
+ // db, err := dbutil.NewWithDB(rawDB, "sqlite3")
31
+ // if err != nil {
32
+ // return err
33
+ // }
34
+ // table := dbutil.UpgradeTable{}
35
+ // table.RegisterFS(upgrades)
36
+ // err = db.Upgrade(ctx)
37
+ // if err != nil {
38
+ // return err
39
+ // }
40
+ // // db has been upgraded to latest version
41
+ // return nil
42
+ // }
43
+ //
44
+ // In dbutil, the database is understood to have a monotonic integer
45
+ // sequence of versions starting at v0, v1, v2, etc. By providing
46
+ // migrations you define a directed acyclic graph (DAG) that allows
47
+ // dbutil to find a path from the current recorded database version to
48
+ // the latest version available.
49
+ //
50
+ // Each SQL migration file has a mandatory comment header that
51
+ // identifies which database versions it upgrades between. For example
52
+ // this is a migration that upgrades from v0 to v2:
53
+ //
54
+ // -- v0 -> v2: Do some things
55
+ //
56
+ // You can omit the first version for the common case of upgrading to
57
+ // a version from the previous version. For example this is a
58
+ // migration that upgrades from v1 to v2:
59
+ //
60
+ // -- v2: Do fewer things
61
+ //
62
+ // By providing "v1" and "v2" migrations, a v0 database would be
63
+ // upgraded to v1 and then v2, while by providing an additional "v0 ->
64
+ // v2" migration a v0 database would be upgraded directly to v2 as it
65
+ // is a more direct path. With that migration provided the "v1"
66
+ // migration is no longer needed.
67
+ //
68
+ // By default, when running migrations, if a more recent database
69
+ // version is live than the current code knows about (for example,
70
+ // from running a previous version of the application), dbutil will
71
+ // error out. However, many database migrations are backwards
72
+ // compatible. You can therefore indicate this when writing a
73
+ // migration, and previous versions of the application will accept a
74
+ // database with that migration applied, even if they are unaware of
75
+ // its contents. For example, if the migration from v1 to v2 was
76
+ // backwards compatible, you could provide this migration:
77
+ //
78
+ // -- v2 (compatible with v1+): Do fewer things
79
+ //
80
+ // When applying the migration, the compatibility level (v1) is saved
81
+ // to the versions table in the database, so that older versions of
82
+ // the application which only know about v1 will see that v2 of the
83
+ // database is still OK to use. If the compatibility level is not set,
84
+ // then it defaults to the same as the target version for the
85
+ // migration, which achieves the default behavior described in the
86
+ // previous paragraph.
87
+ //
88
+ // You can provide additional flags immediately following the header
89
+ // line. To disable wrapping the upgrade in a single transaction, put
90
+ // "transaction: off" on the second line.
91
+ //
92
+ // -- v5: Upgrade without transaction
93
+ // -- transaction: off
94
+ // // do dangerous stuff
95
+ //
96
+ // Within migrations, there is special syntax that can be used to
97
+ // filter parts of the SQL to apply only with specific dialects. To
98
+ // limit the next line to one dialect:
99
+ //
100
+ // -- only: postgres
101
+ //
102
+ // To limit the next N lines:
103
+ //
104
+ // -- only: sqlite for next 123 lines
105
+ //
106
+ // To limit a block of code, fenced by another directive:
107
+ //
108
+ // -- only: sqlite until "end only"
109
+ // QUERY;
110
+ // ANOTHER QUERY;
111
+ // -- end only sqlite
112
+ //
113
+ // If the single-line limit is on the second line of the file, the
114
+ // whole file is limited to that dialect.
115
+ //
116
+ // If the filter ends with `(lines commented)`, then ALL lines chosen
117
+ // by the filter will be uncommented.
118
+ package dbutil
@@ -60,24 +60,8 @@ func (ut *UpgradeTable) Register(from, to, compat int, message string, txn TxnMo
60
60
  (*ut)[from] = upg
61
61
  }
62
62
 
63
- // Syntax is either
64
- //
65
- // -- v0 -> v1: Message
66
- //
67
- // or
68
- //
69
- // -- v1: Message
70
- //
71
- // Both syntaxes may also have a compatibility notice before the colon:
72
- //
73
- // -- v5 (compatible with v3+): Upgrade with backwards compatibility
74
63
  var upgradeHeaderRegex = regexp.MustCompile(`^-- (?:v(\d+) -> )?v(\d+)(?: \(compatible with v(\d+)\+\))?: (.+)$`)
75
64
 
76
- // To disable wrapping the upgrade in a single transaction, put `--transaction: off` on the second line.
77
- //
78
- // -- v5: Upgrade without transaction
79
- // -- transaction: off
80
- // // do dangerous stuff
81
65
  var transactionDisableRegex = regexp.MustCompile(`^-- transaction: ([a-z-]*)`)
82
66
 
83
67
  func parseFileHeader(file []byte) (from, to, compat int, message string, txn TxnMode, lines [][]byte, err error) {
@@ -123,24 +107,6 @@ func parseFileHeader(file []byte) (from, to, compat int, message string, txn Txn
123
107
  return
124
108
  }
125
109
 
126
- // To limit the next line to one dialect:
127
- //
128
- // -- only: postgres
129
- //
130
- // To limit the next N lines:
131
- //
132
- // -- only: sqlite for next 123 lines
133
- //
134
- // To limit a block of code, fenced by another directive:
135
- //
136
- // -- only: sqlite until "end only"
137
- // QUERY;
138
- // ANOTHER QUERY;
139
- // -- end only sqlite
140
- //
141
- // If the single-line limit is on the second line of the file, the whole file is limited to that dialect.
142
- //
143
- // If the filter ends with `(lines commented)`, then ALL lines chosen by the filter will be uncommented.
144
110
  var dialectLineFilter = regexp.MustCompile(`^\s*-- only: (postgres|sqlite)(?: for next (\d+) lines| until "(end) only")?(?: \(lines? (commented)\))?`)
145
111
 
146
112
  // Constants used to make parseDialectFilter clearer
@@ -0,0 +1,20 @@
1
+ // Copyright (c) 2025 Tulir Asokan
2
+ //
3
+ // This Source Code Form is subject to the terms of the Mozilla Public
4
+ // License, v. 2.0. If a copy of the MPL was not distributed with this
5
+ // file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
+
7
+ package exbytes
8
+
9
+ import (
10
+ "unsafe"
11
+ )
12
+
13
+ // UnsafeString returns a string that points to the same memory as the input byte slice.
14
+ //
15
+ // The input byte slice must not be modified after this function is called.
16
+ //
17
+ // See [go.mau.fi/util/exstrings.UnsafeBytes] for the reverse operation.
18
+ func UnsafeString(b []byte) string {
19
+ return unsafe.String(unsafe.SliceData(b), len(b))
20
+ }
@@ -0,0 +1,78 @@
1
+ // Copyright (c) 2024 Tulir Asokan
2
+ //
3
+ // This Source Code Form is subject to the terms of the Mozilla Public
4
+ // License, v. 2.0. If a copy of the MPL was not distributed with this
5
+ // file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
+
7
+ package exbytes
8
+
9
+ import (
10
+ "errors"
11
+ "fmt"
12
+ "io"
13
+ )
14
+
15
+ // Writer is a simple byte writer that does not allow extending the buffer.
16
+ //
17
+ // Writes always go after the current len() and will fail if the slice capacity is too low.
18
+ //
19
+ // The correct way to use this is to create a slice with sufficient capacity and zero length:
20
+ //
21
+ // x := make([]byte, 0, 11)
22
+ // w := (*Writer)(&x)
23
+ // w.Write([]byte("hello"))
24
+ // w.WriteByte(' ')
25
+ // w.WriteString("world")
26
+ // fmt.Println(string(x)) // "hello world"
27
+ type Writer []byte
28
+
29
+ var ErrWriterBufferFull = errors.New("exbytes.Writer: buffer full")
30
+
31
+ var (
32
+ _ io.Writer = (*Writer)(nil)
33
+ _ io.StringWriter = (*Writer)(nil)
34
+ _ io.ByteWriter = (*Writer)(nil)
35
+ )
36
+
37
+ func (w *Writer) extendLen(n int) (int, error) {
38
+ ptr := len(*w)
39
+ if ptr+n > cap(*w) {
40
+ return 0, fmt.Errorf("%w (%d + %d > %d)", ErrWriterBufferFull, ptr, n, cap(*w))
41
+ }
42
+ *w = (*w)[:ptr+n]
43
+ return ptr, nil
44
+ }
45
+
46
+ func (w *Writer) Write(b []byte) (n int, err error) {
47
+ ptr, err := w.extendLen(len(b))
48
+ if err != nil {
49
+ return 0, err
50
+ }
51
+ copy((*w)[ptr:], b)
52
+ return len(b), nil
53
+ }
54
+
55
+ func (w *Writer) WriteString(s string) (n int, err error) {
56
+ ptr, err := w.extendLen(len(s))
57
+ if err != nil {
58
+ return 0, err
59
+ }
60
+ copy((*w)[ptr:], s)
61
+ return len(s), nil
62
+ }
63
+
64
+ func (w *Writer) WriteByte(r byte) error {
65
+ ptr, err := w.extendLen(1)
66
+ if err != nil {
67
+ return err
68
+ }
69
+ (*w)[ptr] = r
70
+ return nil
71
+ }
72
+
73
+ func (w *Writer) String() string {
74
+ if w == nil {
75
+ return "<nil>"
76
+ }
77
+ return string(*w)
78
+ }
@@ -0,0 +1,42 @@
1
+ // Copyright (c) 2024 Tulir Asokan
2
+ //
3
+ // This Source Code Form is subject to the terms of the Mozilla Public
4
+ // License, v. 2.0. If a copy of the MPL was not distributed with this
5
+ // file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
+
7
+ package exslices
8
+
9
+ func CastFunc[To, From any](source []From, conv func(From) To) []To {
10
+ result := make([]To, len(source))
11
+ for i, v := range source {
12
+ result[i] = conv(v)
13
+ }
14
+ return result
15
+ }
16
+
17
+ func CastFuncFilter[To, From any](source []From, conv func(From) (To, bool)) []To {
18
+ result := make([]To, 0, len(source))
19
+ for _, v := range source {
20
+ res, ok := conv(v)
21
+ if ok {
22
+ result = append(result, res)
23
+ }
24
+ }
25
+ return result
26
+ }
27
+
28
+ func CastToString[To, From ~string](source []From) []To {
29
+ result := make([]To, len(source))
30
+ for i, v := range source {
31
+ result[i] = To(v)
32
+ }
33
+ return result
34
+ }
35
+
36
+ func CastToAny[From any](source []From) []any {
37
+ result := make([]any, len(source))
38
+ for i, v := range source {
39
+ result[i] = v
40
+ }
41
+ return result
42
+ }
@@ -0,0 +1,28 @@
1
+ // Copyright (c) 2024 Tulir Asokan
2
+ //
3
+ // This Source Code Form is subject to the terms of the Mozilla Public
4
+ // License, v. 2.0. If a copy of the MPL was not distributed with this
5
+ // file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
+
7
+ package exslices
8
+
9
+ // Chunk splits a slice into chunks of the given size.
10
+ //
11
+ // From https://github.com/golang/go/issues/53987#issuecomment-1224367139
12
+ //
13
+ // TODO remove this after slices.Chunk can be used (it'll probably be added in Go 1.23, so it can be used after 1.22 is EOL)
14
+ func Chunk[T any](slice []T, size int) (chunks [][]T) {
15
+ if size < 1 {
16
+ panic("chunk size cannot be less than 1")
17
+ }
18
+ for i := 0; ; i++ {
19
+ next := i * size
20
+ if len(slice[next:]) > size {
21
+ end := next + size
22
+ chunks = append(chunks, slice[next:end:end])
23
+ } else {
24
+ chunks = append(chunks, slice[i*size:])
25
+ return
26
+ }
27
+ }
28
+ }
@@ -0,0 +1,67 @@
1
+ // Copyright (c) 2024 Tulir Asokan
2
+ //
3
+ // This Source Code Form is subject to the terms of the Mozilla Public
4
+ // License, v. 2.0. If a copy of the MPL was not distributed with this
5
+ // file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
+
7
+ package exslices
8
+
9
+ // DeduplicateUnsorted removes duplicates from the given slice without requiring that the input slice is sorted.
10
+ // The order of the output will be the same as the input. The input slice will not be modified.
11
+ //
12
+ // If you don't care about the order of the output, it's recommended to sort the list and then use [slices.Compact].
13
+ func DeduplicateUnsorted[T comparable](s []T) []T {
14
+ return deduplicateUnsortedInto(s, make([]T, 0, len(s)))
15
+ }
16
+
17
+ // DeduplicateUnsortedOverwrite removes duplicates from the given slice without requiring that the input slice is sorted.
18
+ // The input slice will be modified and used as the output slice to avoid extra allocations.
19
+ //
20
+ // If you don't care about the order of the output, it's recommended to sort the list and then use [slices.Compact].
21
+ func DeduplicateUnsortedOverwrite[T comparable](s []T) []T {
22
+ out := deduplicateUnsortedInto(s, s[:0])
23
+ clear(s[len(out):])
24
+ return out
25
+ }
26
+
27
+ func deduplicateUnsortedInto[T comparable](s, result []T) []T {
28
+ seen := make(map[T]struct{}, len(s))
29
+ for _, item := range s {
30
+ if _, ok := seen[item]; !ok {
31
+ seen[item] = struct{}{}
32
+ result = append(result, item)
33
+ }
34
+ }
35
+ return result
36
+ }
37
+
38
+ // DeduplicateUnsortedFunc removes duplicates from the given slice using the given key function without requiring
39
+ // that the input slice is sorted. The order of the output will be the same as the input.
40
+ //
41
+ // If you don't care about the order of the output, it's recommended to sort the list and then use [slices.CompactFunc].
42
+ func DeduplicateUnsortedFunc[T any, K comparable](s []T, getKey func(T) K) []T {
43
+ return deduplicateUnsortedFuncInto(s, make([]T, 0, len(s)), getKey)
44
+ }
45
+
46
+ // DeduplicateUnsortedOverwriteFunc removes duplicates from the given slice using the given key function
47
+ // without requiring that the input slice is sorted. The order of the output will be the same as the input.
48
+ // The input slice will be modified and used as the output slice to avoid extra allocations.
49
+ //
50
+ // If you don't care about the order of the output, it's recommended to sort the list and then use [slices.CompactFunc].
51
+ func DeduplicateUnsortedOverwriteFunc[T any, K comparable](s []T, getKey func(T) K) []T {
52
+ out := deduplicateUnsortedFuncInto(s, s[:0], getKey)
53
+ clear(s[len(out):])
54
+ return out
55
+ }
56
+
57
+ func deduplicateUnsortedFuncInto[T any, K comparable](s, result []T, getKey func(T) K) []T {
58
+ seen := make(map[K]struct{}, len(s))
59
+ for _, item := range s {
60
+ key := getKey(item)
61
+ if _, ok := seen[key]; !ok {
62
+ seen[key] = struct{}{}
63
+ result = append(result, item)
64
+ }
65
+ }
66
+ return result
67
+ }
@@ -0,0 +1,63 @@
1
+ // Copyright (c) 2023 Tulir Asokan
2
+ //
3
+ // This Source Code Form is subject to the terms of the Mozilla Public
4
+ // License, v. 2.0. If a copy of the MPL was not distributed with this
5
+ // file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
+
7
+ package exslices
8
+
9
+ // SortedDiff returns the difference between two already-sorted slices, with the help of the given comparison function.
10
+ // The output will be in the same order as the input, which means it'll be sorted.
11
+ func SortedDiff[T any](a, b []T, compare func(a, b T) int) (uniqueToA, uniqueToB []T) {
12
+ uniqueToA = make([]T, 0, len(a))
13
+ uniqueToB = make([]T, 0, len(b))
14
+
15
+ var i, j int
16
+ for {
17
+ if j >= len(b) {
18
+ uniqueToA = append(uniqueToA, a[i:]...)
19
+ break
20
+ } else if i >= len(a) {
21
+ uniqueToB = append(uniqueToB, b[j:]...)
22
+ break
23
+ }
24
+ c := compare(a[i], b[j])
25
+ if c < 0 {
26
+ uniqueToA = append(uniqueToA, a[i])
27
+ i++
28
+ } else if c > 0 {
29
+ uniqueToB = append(uniqueToB, b[j])
30
+ j++
31
+ } else {
32
+ i++
33
+ j++
34
+ }
35
+ }
36
+ return
37
+ }
38
+
39
+ // Diff returns the difference between two slices. The slices may contain duplicates and don't need to be sorted.
40
+ // The output will not be sorted, but is guaranteed to not contain any duplicates.
41
+ func Diff[T comparable](a, b []T) (uniqueToA, uniqueToB []T) {
42
+ maxLen := len(a)
43
+ if len(b) > maxLen {
44
+ maxLen = len(b)
45
+ }
46
+ collector := make(map[T]uint8, maxLen)
47
+ for _, item := range a {
48
+ collector[item] |= 0b01
49
+ }
50
+ for _, item := range b {
51
+ collector[item] |= 0b10
52
+ }
53
+ uniqueToA = make([]T, 0, maxLen)
54
+ uniqueToB = make([]T, 0, maxLen)
55
+ for item, mask := range collector {
56
+ if mask == 0b01 {
57
+ uniqueToA = append(uniqueToA, item)
58
+ } else if mask == 0b10 {
59
+ uniqueToB = append(uniqueToB, item)
60
+ }
61
+ }
62
+ return
63
+ }
@@ -8,6 +8,7 @@ package exsync
8
8
 
9
9
  import (
10
10
  "context"
11
+ "fmt"
11
12
  "sync"
12
13
  "time"
13
14
  )
@@ -50,7 +51,7 @@ func (e *Event) Wait(ctx context.Context) error {
50
51
  }
51
52
  }
52
53
 
53
- // WaitTimeout waits for either the event to happen within the given timeout.
54
+ // WaitTimeout waits for the event to happen within the given timeout.
54
55
  // If the timeout expires first, the return value is false, otherwise it's true.
55
56
  func (e *Event) WaitTimeout(timeout time.Duration) bool {
56
57
  select {
@@ -61,6 +62,19 @@ func (e *Event) WaitTimeout(timeout time.Duration) bool {
61
62
  }
62
63
  }
63
64
 
65
+ // WaitTimeoutCtx waits for the event to happen, the timeout to expire, or the given context to be done.
66
+ // If the context or timeout is done first, an error is returned, otherwise the return value is nil.
67
+ func (e *Event) WaitTimeoutCtx(ctx context.Context, timeout time.Duration) error {
68
+ select {
69
+ case <-e.GetChan():
70
+ return nil
71
+ case <-ctx.Done():
72
+ return ctx.Err()
73
+ case <-time.After(timeout):
74
+ return fmt.Errorf("exsync.Event: wait timeout")
75
+ }
76
+ }
77
+
64
78
  // IsSet returns true if the event has been set.
65
79
  func (e *Event) IsSet() bool {
66
80
  e.l.RLock()
@@ -1,4 +1,4 @@
1
- // Copyright (c) 2023 Tulir Asokan
1
+ // Copyright (c) 2025 Tulir Asokan
2
2
  //
3
3
  // This Source Code Form is subject to the terms of the Mozilla Public
4
4
  // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -10,20 +10,52 @@ import (
10
10
  "encoding/binary"
11
11
  "hash/crc32"
12
12
  "strings"
13
- "unsafe"
13
+
14
+ "go.mau.fi/util/exbytes"
14
15
  )
15
16
 
16
17
  const letters = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
17
18
 
18
19
  // StringBytes generates a random string of the given length and returns it as a byte array.
19
20
  func StringBytes(n int) []byte {
21
+ return StringBytesCharset(n, letters)
22
+ }
23
+
24
+ // AppendSequence generates a random sequence of the given length using the given character set
25
+ // and appends it to the given output slice.
26
+ func AppendSequence[T any](n int, charset, output []T) []T {
27
+ if n <= 0 {
28
+ return output
29
+ }
30
+ if output == nil {
31
+ output = make([]T, 0, n)
32
+ }
33
+ // If risk of modulo bias is too high, use 32-bit integers as source instead of 16-bit.
34
+ if 65536%len(charset) < 200 {
35
+ input := Bytes(n * 2)
36
+ for i := 0; i < n; i++ {
37
+ output = append(output, charset[binary.BigEndian.Uint16(input[i*2:])%uint16(len(charset))])
38
+ }
39
+ } else {
40
+ input := Bytes(n * 4)
41
+ for i := 0; i < n; i++ {
42
+ output = append(output, charset[binary.BigEndian.Uint32(input[i*4:])%uint32(len(charset))])
43
+ }
44
+ }
45
+ return output
46
+ }
47
+
48
+ // StringBytesCharset generates a random string of the given length using the given character set and returns it as a byte array.
49
+ // Note that the character set must be ASCII. For arbitrary Unicode, use [AppendSequence] with a `[]rune`.
50
+ func StringBytesCharset(n int, charset string) []byte {
20
51
  if n <= 0 {
21
52
  return []byte{}
22
53
  }
23
54
  input := Bytes(n * 2)
24
55
  for i := 0; i < n; i++ {
25
- // Risk of modulo bias is only 2 in 65535, values between 0 and 65533 are uniformly distributed
26
- input[i] = letters[binary.BigEndian.Uint16(input[i*2:])%uint16(len(letters))]
56
+ // The risk of modulo bias is (65536 % len(charset)) / 65536.
57
+ // For the default charset, that's 2 in 65536 or 0.003 %.
58
+ input[i] = charset[binary.BigEndian.Uint16(input[i*2:])%uint16(len(charset))]
27
59
  }
28
60
  input = input[:n]
29
61
  return input
@@ -34,8 +66,16 @@ func String(n int) string {
34
66
  if n <= 0 {
35
67
  return ""
36
68
  }
37
- str := StringBytes(n)
38
- return *(*string)(unsafe.Pointer(&str))
69
+ return exbytes.UnsafeString(StringBytes(n))
70
+ }
71
+
72
+ // StringCharset generates a random string of the given length using the given character set.
73
+ // Note that the character set must be ASCII. For arbitrary Unicode, use [AppendSequence] with a `[]rune`.
74
+ func StringCharset(n int, charset string) string {
75
+ if n <= 0 {
76
+ return ""
77
+ }
78
+ return exbytes.UnsafeString(StringBytesCharset(n, charset))
39
79
  }
40
80
 
41
81
  func base62Encode(val uint32, minWidth int) []byte {
@@ -65,7 +105,7 @@ func Token(namespace string, randomLength int) string {
65
105
  token[len(namespace)+randomLength+1] = '_'
66
106
  checksum := base62Encode(crc32.ChecksumIEEE(token[:len(token)-7]), 6)
67
107
  copy(token[len(token)-6:], checksum)
68
- return *(*string)(unsafe.Pointer(&token))
108
+ return exbytes.UnsafeString(token)
69
109
  }
70
110
 
71
111
  // GetTokenPrefix parses the given token generated with Token, validates the checksum and returns the prefix namespace.
@@ -166,6 +166,7 @@ func (proc *Processor) decodeMutations(ctx context.Context, mutations []*waServe
166
166
  out.Mutations = append(out.Mutations, Mutation{
167
167
  Operation: mutation.GetOperation(),
168
168
  Action: syncAction.GetValue(),
169
+ Version: syncAction.GetVersion(),
169
170
  Index: index,
170
171
  IndexMAC: indexMAC,
171
172
  ValueMAC: valueMAC,
@@ -45,7 +45,11 @@ func BuildMute(target types.JID, mute bool, muteDuration time.Duration) PatchInf
45
45
  if muteDuration > 0 {
46
46
  muteEndTimestamp = proto.Int64(time.Now().Add(muteDuration).UnixMilli())
47
47
  }
48
+ return BuildMuteAbs(target, mute, muteEndTimestamp)
49
+ }
48
50
 
51
+ // BuildMuteAbs builds an app state patch for muting or unmuting a chat with an absolute timestamp.
52
+ func BuildMuteAbs(target types.JID, mute bool, muteEndTimestamp *int64) PatchInfo {
49
53
  return PatchInfo{
50
54
  Type: WAPatchRegularHigh,
51
55
  Mutations: []MutationInfo{{
@@ -126,6 +130,36 @@ func BuildArchive(target types.JID, archive bool, lastMessageTimestamp time.Time
126
130
  return result
127
131
  }
128
132
 
133
+ // BuildMarkChatAsRead builds an app state patch for marking a chat as read or unread.
134
+ func BuildMarkChatAsRead(target types.JID, read bool, lastMessageTimestamp time.Time, lastMessageKey *waCommon.MessageKey) PatchInfo {
135
+ if lastMessageTimestamp.IsZero() {
136
+ lastMessageTimestamp = time.Now()
137
+ }
138
+ action := &waSyncAction.MarkChatAsReadAction{
139
+ Read: proto.Bool(read),
140
+ MessageRange: &waSyncAction.SyncActionMessageRange{
141
+ LastMessageTimestamp: proto.Int64(lastMessageTimestamp.Unix()),
142
+ },
143
+ }
144
+ if lastMessageKey != nil {
145
+ action.MessageRange.Messages = []*waSyncAction.SyncActionMessage{{
146
+ Key: lastMessageKey,
147
+ Timestamp: proto.Int64(lastMessageTimestamp.Unix()),
148
+ }}
149
+ }
150
+
151
+ return PatchInfo{
152
+ Type: WAPatchRegularLow,
153
+ Mutations: []MutationInfo{{
154
+ Index: []string{IndexMarkChatAsRead, target.String()},
155
+ Version: 3,
156
+ Value: &waSyncAction.SyncActionValue{
157
+ MarkChatAsReadAction: action,
158
+ },
159
+ }},
160
+ }
161
+ }
162
+
129
163
  func newLabelChatMutation(target types.JID, labelID string, labeled bool) MutationInfo {
130
164
  return MutationInfo{
131
165
  Index: []string{IndexLabelAssociationChat, labelID, target.String()},
@@ -22,6 +22,7 @@ import (
22
22
  type Mutation struct {
23
23
  Operation waServerSync.SyncdMutation_SyncdOperation
24
24
  Action *waSyncAction.SyncActionValue
25
+ Version int32
25
26
  Index []string
26
27
  IndexMAC []byte
27
28
  ValueMAC []byte