slidge-whatsapp 0.2.2__tar.gz → 0.2.4__tar.gz

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.

Files changed (22) hide show
  1. {slidge_whatsapp-0.2.2 → slidge_whatsapp-0.2.4}/PKG-INFO +6 -7
  2. {slidge_whatsapp-0.2.2 → slidge_whatsapp-0.2.4}/README.md +3 -3
  3. {slidge_whatsapp-0.2.2 → slidge_whatsapp-0.2.4}/pyproject.toml +8 -6
  4. {slidge_whatsapp-0.2.2 → slidge_whatsapp-0.2.4}/slidge_whatsapp/__init__.py +1 -1
  5. {slidge_whatsapp-0.2.2 → slidge_whatsapp-0.2.4}/slidge_whatsapp/contact.py +1 -1
  6. {slidge_whatsapp-0.2.2 → slidge_whatsapp-0.2.4}/slidge_whatsapp/event.go +56 -0
  7. {slidge_whatsapp-0.2.2 → slidge_whatsapp-0.2.4}/slidge_whatsapp/go.mod +11 -9
  8. {slidge_whatsapp-0.2.2 → slidge_whatsapp-0.2.4}/slidge_whatsapp/go.sum +16 -16
  9. {slidge_whatsapp-0.2.2 → slidge_whatsapp-0.2.4}/slidge_whatsapp/group.py +7 -2
  10. {slidge_whatsapp-0.2.2 → slidge_whatsapp-0.2.4}/slidge_whatsapp/media/media.go +8 -3
  11. {slidge_whatsapp-0.2.2 → slidge_whatsapp-0.2.4}/slidge_whatsapp/session.go +4 -5
  12. {slidge_whatsapp-0.2.2 → slidge_whatsapp-0.2.4}/slidge_whatsapp/session.py +53 -31
  13. {slidge_whatsapp-0.2.2 → slidge_whatsapp-0.2.4}/LICENSE +0 -0
  14. {slidge_whatsapp-0.2.2 → slidge_whatsapp-0.2.4}/build.py +0 -0
  15. {slidge_whatsapp-0.2.2 → slidge_whatsapp-0.2.4}/slidge_whatsapp/__main__.py +0 -0
  16. {slidge_whatsapp-0.2.2 → slidge_whatsapp-0.2.4}/slidge_whatsapp/command.py +0 -0
  17. {slidge_whatsapp-0.2.2 → slidge_whatsapp-0.2.4}/slidge_whatsapp/config.py +0 -0
  18. {slidge_whatsapp-0.2.2 → slidge_whatsapp-0.2.4}/slidge_whatsapp/gateway.go +0 -0
  19. {slidge_whatsapp-0.2.2 → slidge_whatsapp-0.2.4}/slidge_whatsapp/gateway.py +0 -0
  20. {slidge_whatsapp-0.2.2 → slidge_whatsapp-0.2.4}/slidge_whatsapp/media/ffmpeg.go +0 -0
  21. {slidge_whatsapp-0.2.2 → slidge_whatsapp-0.2.4}/slidge_whatsapp/media/mupdf.go +0 -0
  22. {slidge_whatsapp-0.2.2 → slidge_whatsapp-0.2.4}/slidge_whatsapp/media/stub.go +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: slidge-whatsapp
3
- Version: 0.2.2
3
+ Version: 0.2.4
4
4
  Summary: A Whatsapp/XMPP gateway.
5
5
  License: GNU AFFERO GENERAL PUBLIC LICENSE
6
6
  Version 3, 19 November 2007
@@ -669,10 +669,9 @@ Author-email: nicoco@nicoco.fr
669
669
  Requires-Python: >= 3.11
670
670
  Classifier: Topic :: Internet :: XMPP
671
671
  Requires-Dist: linkpreview (>=0.11.0)
672
- Requires-Dist: pybindgen (>=0.22.1)
673
- Requires-Dist: slidge (>=0.2.4)
672
+ Requires-Dist: slidge (>=0.2.4,<0.3)
674
673
  Project-URL: Chat room, https://conference.nicoco.fr:5281/muc_log/slidge/
675
- Project-URL: Documentation, https://slidge.codeberg.page/docs/slidge-whatsapp/main
674
+ Project-URL: Documentation, https://slidge.im/docs/slidge-whatsapp/main
676
675
  Project-URL: Homepage, https://codeberg.org/slidge/slidge-whatsapp
677
676
  Project-URL: Issues, https://codeberg.org/slidge/slidge-whatsapp/issues
678
677
  Project-URL: Repository, https://codeberg.org/slidge/slidge-whatsapp
@@ -694,7 +693,7 @@ A
694
693
 
695
694
  ## Installation
696
695
 
697
- Refer to the [slidge admin documentation](https://slidge.im/core/admin/)
696
+ Refer to the [slidge admin documentation](https://slidge.im/docs/slidge/main/admin/)
698
697
  for general info on how to set up an XMPP server component.
699
698
 
700
699
  ### Containers
@@ -702,8 +701,8 @@ for general info on how to set up an XMPP server component.
702
701
  From [the codeberg package registry](https://codeberg.org/slidge/-/packages?q=&type=container)
703
702
 
704
703
  ```sh
705
- # use ravermeister/slidge-whatsapp for arm64 (thanks raver! <3)
706
- docker run docker.io/nicocool84/slidge-whatsapp
704
+ # use docker.io/ravermeister/slidge-whatsapp for arm64 (thanks raver! <3)
705
+ docker run codeberg.org/slidge/slidge-whatsapp
707
706
  ```
708
707
 
709
708
  ### Python package
@@ -14,7 +14,7 @@ A
14
14
 
15
15
  ## Installation
16
16
 
17
- Refer to the [slidge admin documentation](https://slidge.im/core/admin/)
17
+ Refer to the [slidge admin documentation](https://slidge.im/docs/slidge/main/admin/)
18
18
  for general info on how to set up an XMPP server component.
19
19
 
20
20
  ### Containers
@@ -22,8 +22,8 @@ for general info on how to set up an XMPP server component.
22
22
  From [the codeberg package registry](https://codeberg.org/slidge/-/packages?q=&type=container)
23
23
 
24
24
  ```sh
25
- # use ravermeister/slidge-whatsapp for arm64 (thanks raver! <3)
26
- docker run docker.io/nicocool84/slidge-whatsapp
25
+ # use docker.io/ravermeister/slidge-whatsapp for arm64 (thanks raver! <3)
26
+ docker run codeberg.org/slidge/slidge-whatsapp
27
27
  ```
28
28
 
29
29
  ### Python package
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "slidge-whatsapp"
3
- version = "v0.2.2"
3
+ version = "v0.2.4"
4
4
  description = "A Whatsapp/XMPP gateway."
5
5
  authors = [
6
6
  {name = "nicoco", email = "nicoco@nicoco.fr"},
@@ -15,8 +15,7 @@ requires-python = ">= 3.11"
15
15
  keywords = ["xmpp", "gateway", "bridge", "instant messaging", "whatsapp"]
16
16
  dependencies = [
17
17
  "linkpreview>=0.11.0",
18
- "pybindgen>=0.22.1",
19
- "slidge>=0.2.4",
18
+ "slidge>=0.2.4,<0.3",
20
19
  ]
21
20
 
22
21
  [project.urls]
@@ -24,7 +23,7 @@ Homepage = "https://codeberg.org/slidge/slidge-whatsapp"
24
23
  Issues = "https://codeberg.org/slidge/slidge-whatsapp/issues"
25
24
  Repository = "https://codeberg.org/slidge/slidge-whatsapp"
26
25
  "Chat room" = "https://conference.nicoco.fr:5281/muc_log/slidge/"
27
- Documentation = "https://slidge.codeberg.page/docs/slidge-whatsapp/main"
26
+ Documentation = "https://slidge.im/docs/slidge-whatsapp/main"
28
27
 
29
28
  [project.scripts]
30
29
  slidge-whatsapp = "slidge_whatsapp:main"
@@ -108,8 +107,9 @@ body = """
108
107
  {% for commit in commits %}
109
108
  - {% if commit.scope %}*({{ commit.scope }})* {% endif %}\
110
109
  {% if commit.breaking %}[**breaking**] {% endif %}\
111
- {{ commit.message | upper_first }}\
112
- {% if commit.remote.username %} by @{{ commit.remote.username }}{%- endif %}\
110
+ {{ commit.message | split(pat="\n") | first | upper_first | trim }}\
111
+ {% if commit.remote.username %} by @{{ commit.remote.username }}{%- endif %} \
112
+ [`{{ commit.id | truncate(length=7, end="") }}`](./commit/{{ commit.id }})\
113
113
  {% endfor %}
114
114
  {% endfor %}\n
115
115
  """
@@ -118,6 +118,8 @@ footer = """
118
118
  """
119
119
 
120
120
  [tool.git-cliff.git]
121
+ conventional_commits = true
122
+ filter_unconventional = false
121
123
  commit_parsers = [
122
124
  { message = "^feat", group = "<!-- 0 -->🚀 Features" },
123
125
  { message = "^fix", group = "<!-- 1 -->🐛 Bug Fixes" },
@@ -13,5 +13,5 @@ def main():
13
13
  entrypoint("slidge_whatsapp")
14
14
 
15
15
 
16
- __version__ = "v0.2.2"
16
+ __version__ = "v0.2.4"
17
17
  __all__ = "Gateway", "session", "command", "contact", "config", "group", "main"
@@ -57,7 +57,7 @@ class Roster(LegacyRoster[str, Contact]):
57
57
  avatar = self.session.whatsapp.GetAvatar(data.JID, contact.avatar or "")
58
58
  if avatar.URL and contact.avatar != avatar.ID:
59
59
  await contact.set_avatar(avatar.URL, avatar.ID)
60
- elif avatar.URL == "":
60
+ elif avatar.URL == "" and avatar.ID == "":
61
61
  await contact.set_avatar(None)
62
62
  except RuntimeError as err:
63
63
  self.session.log.error(
@@ -145,6 +145,7 @@ const (
145
145
  MessageRevoke
146
146
  MessageReaction
147
147
  MessageAttachment
148
+ MessagePoll
148
149
  )
149
150
 
150
151
  // A Message represents one of many kinds of bidirectional communication payloads, for example, a
@@ -165,6 +166,8 @@ type Message struct {
165
166
  Attachments []Attachment // The list of file (image, video, etc.) attachments contained in this message.
166
167
  Preview Preview // A short description for the URL provided in the message body, if any.
167
168
  Location Location // The location metadata for messages, if any.
169
+ Poll Poll // The multiple-choice poll contained in the message, if any.
170
+ Album Album // The image album message, if any.
168
171
  MentionJIDs []string // A list of JIDs mentioned in this message, if any.
169
172
  Receipts []Receipt // The receipt statuses for the message, typically provided alongside historical messages.
170
173
  Reactions []Message // Reactions attached to message, typically provided alongside historical messages.
@@ -228,6 +231,25 @@ type Location struct {
228
231
  URL string
229
232
  }
230
233
 
234
+ // A Poll represents a multiple-choice question, on which each choice might be voted for one or more
235
+ // times.
236
+ type Poll struct {
237
+ Title string // The human-readable name of this poll.
238
+ Options []PollOption // The list of choices to vote on in the poll.
239
+ }
240
+
241
+ // A PollOption represents an individual choice within a broader poll.
242
+ type PollOption struct {
243
+ Title string // The human-readable name for the poll option.
244
+ }
245
+
246
+ // A Album message represents a collection of media files, typically images and videos.
247
+ type Album struct {
248
+ IsAlbum bool // Whether or not the message is an album, regardless of calculated media counts.
249
+ ImageCount int // The calculated amount of images in the album, might not be accurate.
250
+ VideoCount int // The calculated amount of videos in the album, might not be accurate.
251
+ }
252
+
231
253
  // NewMessageEvent returns event data meant for [Session.propagateEvent] for the primive message
232
254
  // event given. Unknown or invalid messages will return an [EventUnknown] event with nil data.
233
255
  func newMessageEvent(client *whatsmeow.Client, evt *events.Message) (EventKind, *EventPayload) {
@@ -306,6 +328,35 @@ func newMessageEvent(client *whatsmeow.Client, evt *events.Message) (EventKind,
306
328
  return EventMessage, &EventPayload{Message: message}
307
329
  }
308
330
 
331
+ // Handle poll messages.
332
+ for _, p := range []*waE2E.PollCreationMessage{
333
+ evt.Message.GetPollCreationMessageV3(),
334
+ evt.Message.GetPollCreationMessageV2(),
335
+ evt.Message.GetPollCreationMessage(),
336
+ } {
337
+ if p == nil {
338
+ continue
339
+ }
340
+ message.Kind = MessagePoll
341
+ message.Poll = Poll{Title: p.GetName()}
342
+ for _, o := range p.GetOptions() {
343
+ message.Poll.Options = append(message.Poll.Options, PollOption{
344
+ Title: o.GetOptionName(),
345
+ })
346
+ }
347
+ return EventMessage, &EventPayload{Message: message}
348
+ }
349
+
350
+ // Handle "album" messages, denoting a grouping of media messages to follow.
351
+ if a := evt.Message.GetAlbumMessage(); a != nil {
352
+ message.Album = Album{
353
+ IsAlbum: true,
354
+ ImageCount: int(a.GetExpectedImageCount()),
355
+ VideoCount: int(a.GetExpectedVideoCount()),
356
+ }
357
+ return EventMessage, &EventPayload{Message: message}
358
+ }
359
+
309
360
  // Handle message attachments, if any.
310
361
  if attach, context, err := getMessageAttachments(client, evt.Message); err != nil {
311
362
  client.Log.Errorf("Failed getting message attachments: %s", err)
@@ -578,6 +629,7 @@ func convertAttachment(attach *Attachment) error {
578
629
  // these are some times misdetected as such.
579
630
  if s.VideoWidth == 0 && s.VideoHeight == 0 && s.AudioSampleRate > 0 && s.Duration > 0 {
580
631
  spec = voiceMessageSpec
632
+ spec.SourceMIME = media.TypeM4A
581
633
  }
582
634
  }
583
635
  default:
@@ -586,6 +638,10 @@ func convertAttachment(attach *Attachment) error {
586
638
  }
587
639
 
588
640
  // Convert attachment between file-types, if source MIME matches the known list of convertable types.
641
+ if spec.SourceMIME == "" {
642
+ spec.SourceMIME = detectedMIME
643
+ }
644
+
589
645
  data, err := media.Convert(ctx, attach.Data, &spec)
590
646
  if err != nil {
591
647
  return fmt.Errorf("failed converting attachment: %w", err)
@@ -1,15 +1,17 @@
1
1
  module codeberg.org/slidge/slidge-whatsapp/slidge_whatsapp
2
2
 
3
- go 1.22.0
3
+ go 1.23.0
4
+
5
+ toolchain go1.24.1
4
6
 
5
7
  require (
6
8
  github.com/gen2brain/go-fitz v1.24.14
7
9
  github.com/go-python/gopy v0.4.11-0.20241206185020-5f285b890023
8
10
  github.com/h2non/filetype v1.1.3
9
11
  github.com/mattn/go-sqlite3 v1.14.24
10
- go.mau.fi/libsignal v0.1.1
11
- go.mau.fi/whatsmeow v0.0.0-20250104105216-918c879fcd19
12
- golang.org/x/image v0.23.0
12
+ go.mau.fi/libsignal v0.1.2
13
+ go.mau.fi/whatsmeow v0.0.0-20250307203951-daf102be9698
14
+ golang.org/x/image v0.25.0
13
15
  )
14
16
 
15
17
  require (
@@ -21,9 +23,9 @@ require (
21
23
  github.com/mattn/go-colorable v0.1.14 // indirect
22
24
  github.com/mattn/go-isatty v0.0.20 // indirect
23
25
  github.com/rs/zerolog v1.33.0 // indirect
24
- go.mau.fi/util v0.8.4 // indirect
25
- golang.org/x/crypto v0.32.0 // indirect
26
- golang.org/x/net v0.34.0 // indirect
27
- golang.org/x/sys v0.29.0 // indirect
28
- google.golang.org/protobuf v1.36.4 // indirect
26
+ go.mau.fi/util v0.8.5 // indirect
27
+ golang.org/x/crypto v0.36.0 // indirect
28
+ golang.org/x/net v0.37.0 // indirect
29
+ golang.org/x/sys v0.31.0 // indirect
30
+ google.golang.org/protobuf v1.36.5 // indirect
29
31
  )
@@ -37,26 +37,26 @@ github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8=
37
37
  github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
38
38
  github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
39
39
  github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
40
- go.mau.fi/libsignal v0.1.1 h1:m/0PGBh4QKP/I1MQ44ti4C0fMbLMuHb95cmDw01FIpI=
41
- go.mau.fi/libsignal v0.1.1/go.mod h1:QLs89F/OA3ThdSL2Wz2p+o+fi8uuQUz0e1BRa6ExdBw=
42
- go.mau.fi/util v0.8.4 h1:mVKlJcXWfVo8ZW3f4vqtjGpqtZqJvX4ETekxawt2vnQ=
43
- go.mau.fi/util v0.8.4/go.mod h1:MOfGTs1CBuK6ERTcSL4lb5YU7/ujz09eOPVEDckuazY=
44
- go.mau.fi/whatsmeow v0.0.0-20250104105216-918c879fcd19 h1:uVS+Zct5fF8rSXV9lfs87zoXdge0JXTzVGNkjmZ61UU=
45
- go.mau.fi/whatsmeow v0.0.0-20250104105216-918c879fcd19/go.mod h1:TLzm2XkwgufONEmiVAsFny+9uBqyEZnUoPrQAfMyuSU=
46
- golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
47
- golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
48
- golang.org/x/image v0.23.0 h1:HseQ7c2OpPKTPVzNjG5fwJsOTCiiwS4QdsYi5XU6H68=
49
- golang.org/x/image v0.23.0/go.mod h1:wJJBTdLfCCf3tiHa1fNxpZmUI4mmoZvwMCPP0ddoNKY=
50
- golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
51
- golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
40
+ go.mau.fi/libsignal v0.1.2 h1:Vs16DXWxSKyzVtI+EEXLCSy5pVWzzCzp/2eqFGvLyP0=
41
+ go.mau.fi/libsignal v0.1.2/go.mod h1:JpnLSSJptn/s1sv7I56uEMywvz8x4YzxeF5OzdPb6PE=
42
+ go.mau.fi/util v0.8.5 h1:PwCAAtcfK0XxZ4sdErJyfBMkTEWoQU33aB7QqDDzQRI=
43
+ go.mau.fi/util v0.8.5/go.mod h1:Ycug9mrbztlahHPEJ6H5r8Nu/xqZaWbE5vPHVWmfz6M=
44
+ go.mau.fi/whatsmeow v0.0.0-20250307203951-daf102be9698 h1:JRng1Qa5ZyOx59Cprle+DNf8LN0MAT2WJZis38hwuHQ=
45
+ go.mau.fi/whatsmeow v0.0.0-20250307203951-daf102be9698/go.mod h1:6hRrUtDWI2wTRClOd6m17GwrFE2a8/p5R4pjJsIVn+U=
46
+ golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
47
+ golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
48
+ golang.org/x/image v0.25.0 h1:Y6uW6rH1y5y/LK1J8BPWZtr6yZ7hrsy6hFrXjgsc2fQ=
49
+ golang.org/x/image v0.25.0/go.mod h1:tCAmOEGthTtkalusGp1g3xa2gke8J6c2N565dTyl9Rs=
50
+ golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c=
51
+ golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
52
52
  golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
53
53
  golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
54
54
  golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
55
- golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
56
- golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
55
+ golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
56
+ golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
57
57
  golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
58
58
  golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
59
- google.golang.org/protobuf v1.36.4 h1:6A3ZDJHn/eNqc1i+IdefRzy/9PokBTPvcqMySR7NNIM=
60
- google.golang.org/protobuf v1.36.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
59
+ google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
60
+ google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
61
61
  gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
62
62
  gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
@@ -25,14 +25,13 @@ class MUC(LegacyMUC[str, str, Participant, str]):
25
25
 
26
26
  HAS_DESCRIPTION = False
27
27
  REACTIONS_SINGLE_EMOJI = True
28
- _ALL_INFO_FILLED_ON_STARTUP = True
29
28
 
30
29
  async def update_info(self):
31
30
  try:
32
31
  avatar = self.session.whatsapp.GetAvatar(self.legacy_id, self.avatar or "")
33
32
  if avatar.URL and self.avatar != avatar.ID:
34
33
  await self.set_avatar(avatar.URL, avatar.ID)
35
- elif avatar.URL == "":
34
+ elif avatar.URL == "" and avatar.ID == "":
36
35
  await self.set_avatar(None)
37
36
  except RuntimeError as err:
38
37
  self.session.log.error(
@@ -95,6 +94,12 @@ class MUC(LegacyMUC[str, str, Participant, str]):
95
94
  if name := participant.nickname:
96
95
  self.subject_setter = name
97
96
  self.session.whatsapp_participants[self.legacy_id] = info.Participants
97
+ self.n_participants = len(info.Participants)
98
+ # Since whatsmeow does always emit a whatsapp.Group event even for participant changes,
99
+ # we need to do that to actually update the participant list.
100
+ if self._participants_filled:
101
+ async for _ in self.fill_participants():
102
+ pass
98
103
 
99
104
  async def fill_participants(self) -> AsyncIterator[Participant]:
100
105
  await self.session.bookmarks.ready
@@ -107,6 +107,8 @@ type Spec struct {
107
107
  VideoHeight int // The height of the video stream, in pixels.
108
108
  VideoFilter string // A complex filter to apply to the video stream.
109
109
 
110
+ Duration time.Duration // The duration of the audio or video stream.
111
+
110
112
  ImageWidth int // The width of the image, in pixels.
111
113
  ImageHeight int // The height of the image, in pixels.
112
114
  ImageQuality int // Image quality for lossy image formats, typically a value from 1 to 100.
@@ -114,8 +116,8 @@ type Spec struct {
114
116
 
115
117
  DocumentPage int // The number of pages for the document.
116
118
 
117
- Duration time.Duration // The duration of the audio or video stream.
118
- StripMetadata bool // Whether or not to remove any container-level metadata present in the stream.
119
+ SourceMIME MIMEType // The MIME type for the source data. If not set, this is derived from the source data.
120
+ StripMetadata bool // Whether or not to remove any container-level metadata present in the stream.
119
121
  }
120
122
 
121
123
  // CommandLineArgs returns the current [Spec] as a list of command-line arguments meant for FFMPEG
@@ -216,7 +218,10 @@ func (s Spec) commandLineArgs() ([]string, error) {
216
218
  // specification given. For information on how these definitions affect media conversions, see the
217
219
  // documentation for the [Spec] type.
218
220
  func Convert(ctx context.Context, data []byte, spec *Spec) ([]byte, error) {
219
- var from, to = DetectMIMEType(data), spec.MIME.BaseMediaType()
221
+ var from, to = spec.SourceMIME, spec.MIME.BaseMediaType()
222
+ if from == "" {
223
+ from = DetectMIMEType(data)
224
+ }
220
225
  switch from {
221
226
  case TypeOgg, TypeM4A:
222
227
  switch to {
@@ -305,7 +305,6 @@ func (s *Session) getMessagePayload(message Message) *waE2E.Message {
305
305
  }
306
306
 
307
307
  payload.ExtendedTextMessage.MatchedText = &message.Preview.URL
308
- payload.ExtendedTextMessage.CanonicalURL = &message.Preview.URL
309
308
  payload.ExtendedTextMessage.Title = &message.Preview.Title
310
309
  payload.ExtendedTextMessage.Description = &message.Preview.Description
311
310
 
@@ -535,15 +534,15 @@ func (s *Session) GetAvatar(resourceID, avatarID string) (Avatar, error) {
535
534
  }
536
535
 
537
536
  p, err := s.client.GetProfilePictureInfo(jid, &whatsmeow.GetProfilePictureParams{ExistingID: avatarID})
538
- if err != nil &&
539
- !errors.Is(err, whatsmeow.ErrProfilePictureNotSet) &&
540
- !errors.Is(err, whatsmeow.ErrProfilePictureUnauthorized) {
537
+ if errors.Is(err, whatsmeow.ErrProfilePictureNotSet) || errors.Is(err, whatsmeow.ErrProfilePictureUnauthorized) {
538
+ return Avatar{}, nil
539
+ } else if err != nil {
541
540
  return Avatar{}, fmt.Errorf("Could not get avatar: %s", err)
542
541
  } else if p != nil {
543
542
  return Avatar{ID: p.ID, URL: p.URL}, nil
544
543
  }
545
544
 
546
- return Avatar{}, nil
545
+ return Avatar{ID: avatarID}, nil
547
546
  }
548
547
 
549
548
  // SetAvatar updates the profile picture for the Contact or Group JID given; it can also update the
@@ -138,12 +138,12 @@ class Session(BaseSession[str, Recipient]):
138
138
  # not updated.
139
139
  if self.__connected.done():
140
140
  if data.Connect.Error != "":
141
+ self.send_gateway_status("Connection error", show="dnd")
142
+ self.send_gateway_message(data.Connect.Error)
143
+ else:
141
144
  self.send_gateway_status(
142
145
  self.__get_connected_status_message(), show="chat"
143
146
  )
144
- else:
145
- self.send_gateway_status("Connection error", show="dnd")
146
- self.send_gateway_message(data.Connect.Error)
147
147
  elif data.Connect.Error != "":
148
148
  self.xmpp.loop.call_soon_threadsafe(
149
149
  self.__connected.set_exception,
@@ -160,7 +160,9 @@ class Session(BaseSession[str, Recipient]):
160
160
  self.send_gateway_message(MESSAGE_LOGGED_OUT)
161
161
  self.send_gateway_status("Logged out", show="away")
162
162
  elif event == whatsapp.EventContact:
163
- await self.contacts.add_whatsapp_contact(data.Contact)
163
+ contact = await self.contacts.add_whatsapp_contact(data.Contact)
164
+ if contact is not None:
165
+ await contact.add_to_roster()
164
166
  elif event == whatsapp.EventGroup:
165
167
  await self.bookmarks.add_whatsapp_group(data.Group)
166
168
  elif event == whatsapp.EventPresence:
@@ -263,6 +265,17 @@ class Session(BaseSession[str, Recipient]):
263
265
  contact.react(
264
266
  legacy_msg_id=message.ID, emojis=emojis, carbon=message.IsCarbon
265
267
  )
268
+ elif message.Kind == whatsapp.MessagePoll:
269
+ body = "🗳 %s" % message.Poll.Title
270
+ for option in message.Poll.Options:
271
+ body = body + "\n☐ %s" % option.Title
272
+ contact.send_text(
273
+ body=body,
274
+ legacy_msg_id=message.ID,
275
+ when=message_timestamp,
276
+ reply_to=reply_to,
277
+ carbon=message.IsCarbon,
278
+ )
266
279
  for receipt in message.Receipts:
267
280
  await self.handle_receipt(receipt)
268
281
  for reaction in message.Reactions:
@@ -560,6 +573,13 @@ class Session(BaseSession[str, Recipient]):
560
573
  body = body + ";u=%d" % message.Location.Accuracy
561
574
  if message.IsForwarded:
562
575
  body = "↱ Forwarded message:\n " + add_quote_prefix(body)
576
+ if message.Album.IsAlbum:
577
+ body = body + "Album: "
578
+ if message.Album.ImageCount > 0:
579
+ body = body + "%d photos, " % message.Album.ImageCount
580
+ if message.Album.VideoCount > 0:
581
+ body = body + "%d videos" % message.Album.VideoCount
582
+ body = body.rstrip(" ,:")
563
583
  return body
564
584
 
565
585
  async def __get_reply_to(
@@ -590,30 +610,32 @@ class Session(BaseSession[str, Recipient]):
590
610
  if not match:
591
611
  return None
592
612
  url = match.group("url")
593
- async with self.http.get(url) as resp:
594
- if resp.status != 200:
595
- self.log.debug(
596
- "Could not generate a preview for %s because response status was %s",
597
- url,
598
- resp.status,
599
- )
600
- return None
601
- if resp.content_type != "text/html":
602
- self.log.debug(
603
- "Could not generate a preview for %s because content type is %s",
604
- url,
605
- resp.content_type,
606
- )
607
- return None
608
- try:
609
- html = await resp.text()
610
- except Exception as e:
611
- self.log.debug("Could not generate a preview for %s", url, exc_info=e)
612
- return None
613
- preview = LinkPreview(Link(url, html))
614
- if not preview.title:
615
- return None
616
- try:
613
+ try:
614
+ async with self.http.get(url) as resp:
615
+ if resp.status != 200:
616
+ self.log.debug(
617
+ "Could not generate a preview for %s because response status was %s",
618
+ url,
619
+ resp.status,
620
+ )
621
+ return None
622
+ if resp.content_type != "text/html":
623
+ self.log.debug(
624
+ "Could not generate a preview for %s because content type is %s",
625
+ url,
626
+ resp.content_type,
627
+ )
628
+ return None
629
+ try:
630
+ html = await resp.text()
631
+ except Exception as e:
632
+ self.log.debug(
633
+ "Could not generate a preview for %s", url, exc_info=e
634
+ )
635
+ return None
636
+ preview = LinkPreview(Link(url, html))
637
+ if not preview.title:
638
+ return None
617
639
  thumbnail = (
618
640
  await get_url_bytes(self.http, preview.image)
619
641
  if preview.image
@@ -635,9 +657,9 @@ class Session(BaseSession[str, Recipient]):
635
657
  else go.Slice_byte()
636
658
  ),
637
659
  )
638
- except Exception as e:
639
- self.log.debug("Could not generate a preview for %s", url, exc_info=e)
640
- return None
660
+ except Exception as e:
661
+ self.log.debug("Could not generate a preview for %s", url, exc_info=e)
662
+ return None
641
663
 
642
664
  async def __get_location(self, text: str) -> Optional[whatsapp.Location]:
643
665
  match = search(GEO_URI_SEARCH_REGEX, text)
File without changes