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.
- {slidge_whatsapp-0.2.0a0 → slidge_whatsapp-0.2.0b0}/PKG-INFO +7 -3
- {slidge_whatsapp-0.2.0a0 → slidge_whatsapp-0.2.0b0}/README.md +5 -1
- {slidge_whatsapp-0.2.0a0 → slidge_whatsapp-0.2.0b0}/pyproject.toml +3 -3
- {slidge_whatsapp-0.2.0a0 → slidge_whatsapp-0.2.0b0}/slidge_whatsapp/__init__.py +8 -2
- slidge_whatsapp-0.2.0b0/slidge_whatsapp/__main__.py +3 -0
- {slidge_whatsapp-0.2.0a0 → slidge_whatsapp-0.2.0b0}/slidge_whatsapp/event.go +18 -1
- {slidge_whatsapp-0.2.0a0 → slidge_whatsapp-0.2.0b0}/slidge_whatsapp/group.py +12 -2
- {slidge_whatsapp-0.2.0a0 → slidge_whatsapp-0.2.0b0}/slidge_whatsapp/session.go +6 -5
- {slidge_whatsapp-0.2.0a0 → slidge_whatsapp-0.2.0b0}/slidge_whatsapp/session.py +19 -10
- slidge_whatsapp-0.2.0a0/slidge_whatsapp/__main__.py +0 -9
- {slidge_whatsapp-0.2.0a0 → slidge_whatsapp-0.2.0b0}/LICENSE +0 -0
- {slidge_whatsapp-0.2.0a0 → slidge_whatsapp-0.2.0b0}/build.py +0 -0
- {slidge_whatsapp-0.2.0a0 → slidge_whatsapp-0.2.0b0}/slidge_whatsapp/attachment.go +0 -0
- {slidge_whatsapp-0.2.0a0 → slidge_whatsapp-0.2.0b0}/slidge_whatsapp/command.py +0 -0
- {slidge_whatsapp-0.2.0a0 → slidge_whatsapp-0.2.0b0}/slidge_whatsapp/config.py +0 -0
- {slidge_whatsapp-0.2.0a0 → slidge_whatsapp-0.2.0b0}/slidge_whatsapp/contact.py +0 -0
- {slidge_whatsapp-0.2.0a0 → slidge_whatsapp-0.2.0b0}/slidge_whatsapp/gateway.go +0 -0
- {slidge_whatsapp-0.2.0a0 → slidge_whatsapp-0.2.0b0}/slidge_whatsapp/gateway.py +0 -0
- {slidge_whatsapp-0.2.0a0 → slidge_whatsapp-0.2.0b0}/slidge_whatsapp/go.mod +0 -0
- {slidge_whatsapp-0.2.0a0 → slidge_whatsapp-0.2.0b0}/slidge_whatsapp/go.sum +0 -0
- {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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
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
|
-
|
|
11
|
-
|
|
11
|
+
|
|
12
|
+
def main():
|
|
13
|
+
entrypoint("slidge_whatsapp")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
__version__ = "0.2.0beta"
|
|
17
|
+
__all__ = "Gateway", "session", "command", "contact", "config", "group", "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
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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
|
-
|
|
184
|
-
|
|
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}"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|