slidge-whatsapp 0.2.0a0__tar.gz → 0.2.0b0__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.
Files changed (21) hide show
  1. {slidge_whatsapp-0.2.0a0 → slidge_whatsapp-0.2.0b0}/PKG-INFO +7 -3
  2. {slidge_whatsapp-0.2.0a0 → slidge_whatsapp-0.2.0b0}/README.md +5 -1
  3. {slidge_whatsapp-0.2.0a0 → slidge_whatsapp-0.2.0b0}/pyproject.toml +3 -3
  4. {slidge_whatsapp-0.2.0a0 → slidge_whatsapp-0.2.0b0}/slidge_whatsapp/__init__.py +8 -2
  5. slidge_whatsapp-0.2.0b0/slidge_whatsapp/__main__.py +3 -0
  6. {slidge_whatsapp-0.2.0a0 → slidge_whatsapp-0.2.0b0}/slidge_whatsapp/event.go +18 -1
  7. {slidge_whatsapp-0.2.0a0 → slidge_whatsapp-0.2.0b0}/slidge_whatsapp/group.py +12 -2
  8. {slidge_whatsapp-0.2.0a0 → slidge_whatsapp-0.2.0b0}/slidge_whatsapp/session.go +6 -5
  9. {slidge_whatsapp-0.2.0a0 → slidge_whatsapp-0.2.0b0}/slidge_whatsapp/session.py +19 -10
  10. slidge_whatsapp-0.2.0a0/slidge_whatsapp/__main__.py +0 -9
  11. {slidge_whatsapp-0.2.0a0 → slidge_whatsapp-0.2.0b0}/LICENSE +0 -0
  12. {slidge_whatsapp-0.2.0a0 → slidge_whatsapp-0.2.0b0}/build.py +0 -0
  13. {slidge_whatsapp-0.2.0a0 → slidge_whatsapp-0.2.0b0}/slidge_whatsapp/attachment.go +0 -0
  14. {slidge_whatsapp-0.2.0a0 → slidge_whatsapp-0.2.0b0}/slidge_whatsapp/command.py +0 -0
  15. {slidge_whatsapp-0.2.0a0 → slidge_whatsapp-0.2.0b0}/slidge_whatsapp/config.py +0 -0
  16. {slidge_whatsapp-0.2.0a0 → slidge_whatsapp-0.2.0b0}/slidge_whatsapp/contact.py +0 -0
  17. {slidge_whatsapp-0.2.0a0 → slidge_whatsapp-0.2.0b0}/slidge_whatsapp/gateway.go +0 -0
  18. {slidge_whatsapp-0.2.0a0 → slidge_whatsapp-0.2.0b0}/slidge_whatsapp/gateway.py +0 -0
  19. {slidge_whatsapp-0.2.0a0 → slidge_whatsapp-0.2.0b0}/slidge_whatsapp/go.mod +0 -0
  20. {slidge_whatsapp-0.2.0a0 → slidge_whatsapp-0.2.0b0}/slidge_whatsapp/go.sum +0 -0
  21. {slidge_whatsapp-0.2.0a0 → slidge_whatsapp-0.2.0b0}/slidge_whatsapp/util.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: slidge-whatsapp
3
- Version: 0.2.0a0
3
+ Version: 0.2.0b0
4
4
  Summary: A Whatsapp/XMPP gateway.
5
5
  Author: deuill
6
6
  Author-email: alex@deuill.org
@@ -10,7 +10,7 @@ Classifier: Programming Language :: Python :: 3.11
10
10
  Classifier: Programming Language :: Python :: 3.12
11
11
  Requires-Dist: linkpreview (>=0.6.5,<0.7.0)
12
12
  Requires-Dist: pybindgen (>=0.22.1,<0.23.0)
13
- Requires-Dist: slidge (>=0.2.0alpha4,<0.3.0)
13
+ Requires-Dist: slidge (>=0.2.0beta0,<0.3.0)
14
14
  Description-Content-Type: text/markdown
15
15
 
16
16
  # slidge-whatsapp
@@ -57,9 +57,13 @@ With [pipx](https://pypa.github.io/pipx/):
57
57
  pipx install slidge-whatsapp
58
58
 
59
59
  # for the bleeding edge
60
- pipx install slidge-whatsapp \
60
+ pipx install slidge-whatsapp==0.0.0.dev0 \
61
61
  --pip-args='--extra-index-url https://slidge.im/repo'
62
62
 
63
+ # to update bleeding edge installs
64
+ pipx install slidge-whatsapp==0.0.0.dev0 \
65
+ --pip-args='--extra-index-url https://slidge.im/repo' --force
66
+
63
67
  slidge-whatsapp --help
64
68
  ```
65
69
 
@@ -42,9 +42,13 @@ With [pipx](https://pypa.github.io/pipx/):
42
42
  pipx install slidge-whatsapp
43
43
 
44
44
  # for the bleeding edge
45
- pipx install slidge-whatsapp \
45
+ pipx install slidge-whatsapp==0.0.0.dev0 \
46
46
  --pip-args='--extra-index-url https://slidge.im/repo'
47
47
 
48
+ # to update bleeding edge installs
49
+ pipx install slidge-whatsapp==0.0.0.dev0 \
50
+ --pip-args='--extra-index-url https://slidge.im/repo' --force
51
+
48
52
  slidge-whatsapp --help
49
53
  ```
50
54
 
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "slidge-whatsapp"
3
- version = "0.2.0alpha0"
3
+ version = "0.2.0beta"
4
4
  description = "A Whatsapp/XMPP gateway."
5
5
  authors = ["deuill <alex@deuill.org>", "Nicoco <nicoco@nicoco.fr>"]
6
6
  readme = "README.md"
@@ -23,7 +23,7 @@ script = "build.py"
23
23
  python = "^3.11"
24
24
  linkpreview = "^0.6.5"
25
25
  pybindgen = "^0.22.1"
26
- slidge = "^0.2.0alpha4"
26
+ slidge = "^0.2.0beta0"
27
27
 
28
28
  [tool.poetry.group.dev.dependencies]
29
29
  pytest-asyncio = "^0.21.0"
@@ -48,7 +48,7 @@ requires = ["poetry-core", "pybindgen"]
48
48
  build-backend = "poetry.core.masonry.api"
49
49
 
50
50
  [tool.poetry.scripts]
51
- slidge-whatsapp = 'slidge_whatsapp.__main__:main'
51
+ slidge-whatsapp = 'slidge_whatsapp:main'
52
52
 
53
53
  [tool.mypy]
54
54
  files = ["slidge_whatsapp"]
@@ -2,10 +2,16 @@
2
2
  WhatsApp gateway using the multi-device API.
3
3
  """
4
4
 
5
+ from slidge import entrypoint
5
6
  from slidge.util.util import get_version # noqa: F401
6
7
 
7
8
  from . import command, config, contact, group, session
8
9
  from .gateway import Gateway
9
10
 
10
- __version__ = "0.2.0alpha0"
11
- __all__ = "Gateway", "session", "command", "contact", "config", "group"
11
+
12
+ def main():
13
+ entrypoint("slidge_whatsapp")
14
+
15
+
16
+ __version__ = "0.2.0beta"
17
+ __all__ = "Gateway", "session", "command", "contact", "config", "group", "main"
@@ -0,0 +1,3 @@
1
+ from slidge_whatsapp import main
2
+
3
+ main()
@@ -834,9 +834,22 @@ type CallState int
834
834
 
835
835
  // The call states handled by the overarching session event handler.
836
836
  const (
837
- CallMissed CallState = 1 + iota
837
+ CallUnknown CallState = iota
838
+ CallIncoming
839
+ CallMissed
838
840
  )
839
841
 
842
+ // CallStateFromReason converts the given (internal) reason string to a public [CallState]. Calls
843
+ // given invalid or unknown reasons will return the [CallUnknown] state.
844
+ func callStateFromReason(reason string) CallState {
845
+ switch reason {
846
+ case "", "timeout":
847
+ return CallMissed
848
+ default:
849
+ return CallUnknown
850
+ }
851
+ }
852
+
840
853
  // A Call represents an incoming or outgoing voice/video call made over WhatsApp. Full support for
841
854
  // calls is currently not implemented, and this structure contains the bare minimum data required
842
855
  // for notifying on missed calls.
@@ -848,6 +861,10 @@ type Call struct {
848
861
 
849
862
  // NewCallEvent returns event data meant for [Session.propagateEvent] for the call metadata given.
850
863
  func newCallEvent(state CallState, meta types.BasicCallMeta) (EventKind, *EventPayload) {
864
+ if state == CallUnknown || meta.From.IsEmpty() {
865
+ return EventUnknown, nil
866
+ }
867
+
851
868
  return EventCall, &EventPayload{Call: Call{
852
869
  State: state,
853
870
  JID: meta.From.ToNonAD().String(),
@@ -1,6 +1,6 @@
1
1
  import re
2
2
  from datetime import datetime, timezone
3
- from typing import TYPE_CHECKING, Optional
3
+ from typing import TYPE_CHECKING, AsyncIterator, Optional
4
4
 
5
5
  from slidge.group import LegacyBookmarks, LegacyMUC, LegacyParticipant, MucType
6
6
  from slidge.util.archive_msg import HistoryMessage
@@ -94,7 +94,16 @@ class MUC(LegacyMUC[str, str, Participant, str]):
94
94
  )
95
95
  if name := participant.nickname:
96
96
  self.subject_setter = name
97
- for data in info.Participants:
97
+ self.session.wa_participants[self.legacy_id] = info.Participants
98
+
99
+ async def fill_participants(self) -> AsyncIterator[Participant]:
100
+ await self.session.bookmarks.ready
101
+ try:
102
+ participants = self.session.wa_participants.pop(self.legacy_id)
103
+ except KeyError:
104
+ self.log.warning("No participants!")
105
+ return
106
+ for data in participants:
98
107
  participant = await self.get_participant_by_legacy_id(data.JID)
99
108
  if data.Action == whatsapp.GroupParticipantActionRemove:
100
109
  self.remove_participant(participant)
@@ -114,6 +123,7 @@ class MUC(LegacyMUC[str, str, Participant, str]):
114
123
  else:
115
124
  participant.affiliation = "member"
116
125
  participant.role = "participant"
126
+ yield participant
117
127
 
118
128
  async def replace_mentions(self, t: str):
119
129
  return replace_whatsapp_mentions(
@@ -15,8 +15,8 @@ import (
15
15
  "go.mau.fi/whatsmeow"
16
16
  "go.mau.fi/whatsmeow/appstate"
17
17
  "go.mau.fi/whatsmeow/proto/waCommon"
18
- "go.mau.fi/whatsmeow/proto/waHistorySync"
19
18
  "go.mau.fi/whatsmeow/proto/waE2E"
19
+ "go.mau.fi/whatsmeow/proto/waHistorySync"
20
20
  "go.mau.fi/whatsmeow/store"
21
21
  "go.mau.fi/whatsmeow/types"
22
22
  "go.mau.fi/whatsmeow/types/events"
@@ -574,6 +574,7 @@ func (s *Session) SetGroupTopic(resourceID, topic string) error {
574
574
  }
575
575
 
576
576
  return s.client.SetGroupTopic(jid, "", "", topic)
577
+
577
578
  }
578
579
 
579
580
  func (s *Session) SetAffiliation(groupID, participantID string, change whatsmeow.ParticipantChange) ([]types.GroupParticipant, error) {
@@ -699,7 +700,7 @@ func (s *Session) handleEvent(evt interface{}) {
699
700
  switch evt.Data.GetSyncType() {
700
701
  case waHistorySync.HistorySync_PUSH_NAME:
701
702
  for _, n := range evt.Data.GetPushnames() {
702
- jid, err := types.ParseJID(n.GetId())
703
+ jid, err := types.ParseJID(n.GetID())
703
704
  if err != nil {
704
705
  continue
705
706
  }
@@ -729,10 +730,10 @@ func (s *Session) handleEvent(evt interface{}) {
729
730
  s.propagateEvent(newGroupEvent(evt))
730
731
  case *events.ChatPresence:
731
732
  s.propagateEvent(newChatStateEvent(evt))
733
+ case *events.CallOffer:
734
+ s.propagateEvent(newCallEvent(CallIncoming, evt.BasicCallMeta))
732
735
  case *events.CallTerminate:
733
- if evt.Reason == "timeout" {
734
- s.propagateEvent(newCallEvent(CallMissed, evt.BasicCallMeta))
735
- }
736
+ s.propagateEvent(newCallEvent(callStateFromReason(evt.Reason), evt.BasicCallMeta))
736
737
  case *events.LoggedOut:
737
738
  s.client.Disconnect()
738
739
  if err := s.client.Store.Delete(); err != nil {
@@ -1,3 +1,4 @@
1
+ import asyncio
1
2
  from asyncio import iscoroutine, run_coroutine_threadsafe
2
3
  from datetime import datetime, timezone
3
4
  from functools import wraps
@@ -71,10 +72,11 @@ class Session(BaseSession[str, Recipient]):
71
72
  self.whatsapp = self.xmpp.whatsapp.NewSession(device)
72
73
  self._handle_event = make_sync(self.handle_event, self.xmpp.loop)
73
74
  self.whatsapp.SetEventHandler(self._handle_event)
74
- self._connected = self.xmpp.loop.create_future()
75
+ self.__reset_connected()
75
76
  self.user_phone: Optional[str] = None
76
77
  self._presence_status: str = ""
77
78
  self._lock = Lock()
79
+ self.wa_participants = dict[str, list[whatsapp.GroupParticipant]]()
78
80
 
79
81
  def migrate(self):
80
82
  user_shelf_path = (
@@ -96,14 +98,20 @@ class Session(BaseSession[str, Recipient]):
96
98
  self.legacy_module_data_set({"device_id": device_id})
97
99
  user_shelf_path.unlink()
98
100
 
101
+ def __reset_connected(self):
102
+ if hasattr(self, "_connected"):
103
+ if not self._connected.done():
104
+ self.xmpp.loop.call_soon_threadsafe(self._connected.cancel)
105
+ self._connected: asyncio.Future[str] = self.xmpp.loop.create_future()
106
+
99
107
  async def login(self):
100
108
  """
101
109
  Initiate login process and connect session to WhatsApp. Depending on existing state, login
102
110
  might either return having initiated the Linked Device registration process in the background,
103
111
  or will re-connect to a previously existing Linked Device session.
104
112
  """
113
+ self.__reset_connected()
105
114
  self.whatsapp.Login()
106
- self._connected = self.xmpp.loop.create_future()
107
115
  return await self._connected
108
116
 
109
117
  async def logout(self):
@@ -137,7 +145,9 @@ class Session(BaseSession[str, Recipient]):
137
145
  else:
138
146
  self.contacts.user_legacy_id = data.ConnectedJID
139
147
  self.user_phone = "+" + data.ConnectedJID.split("@")[0]
140
- self._connected.set_result(self.__get_connected_status_message())
148
+ self.xmpp.loop.call_soon_threadsafe(
149
+ self._connected.set_result, self.__get_connected_status_message()
150
+ )
141
151
  elif event == whatsapp.EventLoggedOut:
142
152
  self.logged = False
143
153
  self.send_gateway_message(MESSAGE_LOGGED_OUT)
@@ -180,14 +190,13 @@ class Session(BaseSession[str, Recipient]):
180
190
 
181
191
  async def handle_call(self, call: whatsapp.Call):
182
192
  contact = await self.contacts.by_legacy_id(call.JID)
183
- if call.State == whatsapp.CallMissed:
184
- text = "Missed call"
193
+ text = f"from {contact.name or 'tel:' + str(contact.jid.local)} (xmpp:{contact.jid.bare})"
194
+ if call.State == whatsapp.CallIncoming:
195
+ text = "Incoming call " + text
196
+ elif call.State == whatsapp.CallMissed:
197
+ text = "Missed call " + text
185
198
  else:
186
- text = "Call"
187
- text = (
188
- text
189
- + f" from {contact.name or 'tel:' + str(contact.jid.local)} (xmpp:{contact.jid.bare})"
190
- )
199
+ text = "Call " + text
191
200
  if call.Timestamp > 0:
192
201
  call_at = datetime.fromtimestamp(call.Timestamp, tz=timezone.utc)
193
202
  text = text + f" at {call_at}"
@@ -1,9 +0,0 @@
1
- from slidge import entrypoint
2
-
3
-
4
- def main():
5
- entrypoint("slidge_whatsapp")
6
-
7
-
8
- if __name__ == "__main__":
9
- main()