slidge-whatsapp 0.2.0a0__cp311-cp311-manylinux_2_36_x86_64.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/__init__.py +11 -0
- slidge_whatsapp/__main__.py +9 -0
- slidge_whatsapp/attachment.go +386 -0
- slidge_whatsapp/command.py +143 -0
- slidge_whatsapp/config.py +38 -0
- slidge_whatsapp/contact.py +75 -0
- slidge_whatsapp/event.go +856 -0
- slidge_whatsapp/gateway.go +175 -0
- slidge_whatsapp/gateway.py +97 -0
- slidge_whatsapp/generated/__init__.py +0 -0
- slidge_whatsapp/generated/_whatsapp.cpython-311-x86_64-linux-gnu.so +0 -0
- slidge_whatsapp/generated/build.py +378 -0
- slidge_whatsapp/generated/go.py +1720 -0
- slidge_whatsapp/generated/whatsapp.py +2797 -0
- slidge_whatsapp/go.mod +28 -0
- slidge_whatsapp/go.sum +55 -0
- slidge_whatsapp/group.py +240 -0
- slidge_whatsapp/session.go +783 -0
- slidge_whatsapp/session.py +663 -0
- slidge_whatsapp/util.py +12 -0
- slidge_whatsapp-0.2.0a0.dist-info/LICENSE +661 -0
- slidge_whatsapp-0.2.0a0.dist-info/METADATA +81 -0
- slidge_whatsapp-0.2.0a0.dist-info/RECORD +25 -0
- slidge_whatsapp-0.2.0a0.dist-info/WHEEL +4 -0
- slidge_whatsapp-0.2.0a0.dist-info/entry_points.txt +3 -0
slidge_whatsapp/go.mod
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
module git.sr.ht/~nicoco/slidge-whatsapp/slidge_whatsapp
|
|
2
|
+
|
|
3
|
+
go 1.21
|
|
4
|
+
|
|
5
|
+
toolchain go1.21.5
|
|
6
|
+
|
|
7
|
+
require (
|
|
8
|
+
github.com/go-python/gopy v0.4.10
|
|
9
|
+
github.com/h2non/filetype v1.1.3
|
|
10
|
+
github.com/mattn/go-sqlite3 v1.14.22
|
|
11
|
+
go.mau.fi/libsignal v0.1.1-0.20240705162345-47e713a595ab
|
|
12
|
+
go.mau.fi/whatsmeow v0.0.0-20240710112833-d732338c041f
|
|
13
|
+
golang.org/x/image v0.15.0
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
require (
|
|
17
|
+
filippo.io/edwards25519 v1.1.0 // indirect
|
|
18
|
+
github.com/google/uuid v1.6.0 // indirect
|
|
19
|
+
github.com/gorilla/websocket v1.5.1 // indirect
|
|
20
|
+
github.com/mattn/go-colorable v0.1.13 // indirect
|
|
21
|
+
github.com/mattn/go-isatty v0.0.20 // indirect
|
|
22
|
+
github.com/rs/zerolog v1.33.0 // indirect
|
|
23
|
+
go.mau.fi/util v0.5.0 // indirect
|
|
24
|
+
golang.org/x/crypto v0.25.0 // indirect
|
|
25
|
+
golang.org/x/net v0.27.0 // indirect
|
|
26
|
+
golang.org/x/sys v0.22.0 // indirect
|
|
27
|
+
google.golang.org/protobuf v1.34.2 // indirect
|
|
28
|
+
)
|
slidge_whatsapp/go.sum
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
|
2
|
+
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
|
3
|
+
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
|
4
|
+
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
|
5
|
+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
|
6
|
+
github.com/go-python/gopy v0.4.10 h1:Ec3x+NTSzLsw9f6FTdDLwQCQlmlNmJIu4J6nSnyugqE=
|
|
7
|
+
github.com/go-python/gopy v0.4.10/go.mod h1:zMV/gSSYa9u/8Zp0WYR+L/z+kOIqIUtMg/a1/GRy5uw=
|
|
8
|
+
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
|
9
|
+
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
|
10
|
+
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
|
11
|
+
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
|
12
|
+
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
|
13
|
+
github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
|
|
14
|
+
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
|
|
15
|
+
github.com/h2non/filetype v1.1.3 h1:FKkx9QbD7HR/zjK1Ia5XiBsq9zdLi5Kf3zGyFTAFkGg=
|
|
16
|
+
github.com/h2non/filetype v1.1.3/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY=
|
|
17
|
+
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
|
18
|
+
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
|
19
|
+
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
|
20
|
+
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
|
21
|
+
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
|
22
|
+
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
|
23
|
+
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
|
|
24
|
+
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
|
25
|
+
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
|
26
|
+
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
|
27
|
+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
|
28
|
+
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
|
29
|
+
github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8=
|
|
30
|
+
github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
|
|
31
|
+
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
|
32
|
+
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
|
33
|
+
go.mau.fi/libsignal v0.1.1-0.20240705162345-47e713a595ab h1:/tnRxsaaG/xBGjXTb6tzTr+XY4T5fQlrGHE1p9ir/wM=
|
|
34
|
+
go.mau.fi/libsignal v0.1.1-0.20240705162345-47e713a595ab/go.mod h1:R8ovrTezxtUNzCQE5PH30StOQWWeBskBsWE55vMfY9I=
|
|
35
|
+
go.mau.fi/util v0.5.0 h1:8yELAl+1CDRrwGe9NUmREgVclSs26Z68pTWePHVxuDo=
|
|
36
|
+
go.mau.fi/util v0.5.0/go.mod h1:DsJzUrJAG53lCZnnYvq9/mOyLuPScWwYhvETiTrpdP4=
|
|
37
|
+
go.mau.fi/whatsmeow v0.0.0-20240710112833-d732338c041f h1:ni8K5zVngwOWrrZ1HzwEmvAovj0+p0cq464l9g0/dt0=
|
|
38
|
+
go.mau.fi/whatsmeow v0.0.0-20240710112833-d732338c041f/go.mod h1:lMW+LxRTakgyNasZwYNB+2uqjKox75GcEfeUXSJhe8I=
|
|
39
|
+
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
|
|
40
|
+
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
|
|
41
|
+
golang.org/x/image v0.15.0 h1:kOELfmgrmJlw4Cdb7g/QGuB3CvDrXbqEIww/pNtNBm8=
|
|
42
|
+
golang.org/x/image v0.15.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE=
|
|
43
|
+
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
|
|
44
|
+
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
|
|
45
|
+
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
46
|
+
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
47
|
+
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
48
|
+
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
|
|
49
|
+
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
|
50
|
+
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
|
51
|
+
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
52
|
+
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
|
53
|
+
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
|
54
|
+
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
|
55
|
+
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
slidge_whatsapp/group.py
ADDED
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
import re
|
|
2
|
+
from datetime import datetime, timezone
|
|
3
|
+
from typing import TYPE_CHECKING, Optional
|
|
4
|
+
|
|
5
|
+
from slidge.group import LegacyBookmarks, LegacyMUC, LegacyParticipant, MucType
|
|
6
|
+
from slidge.util.archive_msg import HistoryMessage
|
|
7
|
+
from slidge.util.types import Hat, HoleBound, Mention, MucAffiliation
|
|
8
|
+
from slixmpp.exceptions import XMPPError
|
|
9
|
+
|
|
10
|
+
from .generated import whatsapp
|
|
11
|
+
from .util import get_bytes_temp
|
|
12
|
+
|
|
13
|
+
if TYPE_CHECKING:
|
|
14
|
+
from .contact import Contact
|
|
15
|
+
from .session import Session
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class Participant(LegacyParticipant):
|
|
19
|
+
contact: "Contact"
|
|
20
|
+
muc: "MUC"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class MUC(LegacyMUC[str, str, Participant, str]):
|
|
24
|
+
session: "Session"
|
|
25
|
+
type = MucType.GROUP
|
|
26
|
+
|
|
27
|
+
REACTIONS_SINGLE_EMOJI = True
|
|
28
|
+
_ALL_INFO_FILLED_ON_STARTUP = True
|
|
29
|
+
|
|
30
|
+
HAS_DESCRIPTION = False
|
|
31
|
+
|
|
32
|
+
async def update_info(self):
|
|
33
|
+
try:
|
|
34
|
+
avatar = self.session.whatsapp.GetAvatar(self.legacy_id, self.avatar or "")
|
|
35
|
+
except RuntimeError:
|
|
36
|
+
# no avatar
|
|
37
|
+
await self.set_avatar(None)
|
|
38
|
+
else:
|
|
39
|
+
if avatar.URL:
|
|
40
|
+
await self.set_avatar(avatar.URL, avatar.ID)
|
|
41
|
+
|
|
42
|
+
async def backfill(
|
|
43
|
+
self,
|
|
44
|
+
after: HoleBound | None = None,
|
|
45
|
+
before: HoleBound | None = None,
|
|
46
|
+
):
|
|
47
|
+
"""
|
|
48
|
+
Request history for messages older than the oldest message given by ID and date.
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
if before is None:
|
|
52
|
+
return
|
|
53
|
+
# WhatsApp requires a full reference to the last seen message in performing on-demand sync.
|
|
54
|
+
|
|
55
|
+
assert isinstance(before.id, str)
|
|
56
|
+
oldest_message = whatsapp.Message(
|
|
57
|
+
ID=before.id,
|
|
58
|
+
IsCarbon=self.session.message_is_carbon(self, before.id),
|
|
59
|
+
Timestamp=int(before.timestamp.timestamp()),
|
|
60
|
+
)
|
|
61
|
+
self.session.whatsapp.RequestMessageHistory(self.legacy_id, oldest_message)
|
|
62
|
+
|
|
63
|
+
def get_message_sender(self, legacy_msg_id: str):
|
|
64
|
+
assert self.pk is not None
|
|
65
|
+
stored = self.xmpp.store.mam.get_by_legacy_id(self.pk, legacy_msg_id)
|
|
66
|
+
if stored is None:
|
|
67
|
+
raise XMPPError("internal-server-error", "Unable to find message sender")
|
|
68
|
+
msg = HistoryMessage(stored.stanza)
|
|
69
|
+
occupant_id = msg.stanza["occupant-id"]["id"]
|
|
70
|
+
if occupant_id == "slidge-user":
|
|
71
|
+
return self.session.contacts.user_legacy_id
|
|
72
|
+
if "@" in occupant_id:
|
|
73
|
+
jid_username = occupant_id.split("@")[0]
|
|
74
|
+
return jid_username.removeprefix("+") + "@" + whatsapp.DefaultUserServer
|
|
75
|
+
raise XMPPError("internal-server-error", "Unable to find message sender")
|
|
76
|
+
|
|
77
|
+
async def update_whatsapp_info(self, info: whatsapp.Group):
|
|
78
|
+
"""
|
|
79
|
+
Set MUC information based on WhatsApp group information, which may or may not be partial in
|
|
80
|
+
case of updates to existing MUCs.
|
|
81
|
+
"""
|
|
82
|
+
if info.Nickname:
|
|
83
|
+
self.user_nick = info.Nickname
|
|
84
|
+
if info.Name:
|
|
85
|
+
self.name = info.Name
|
|
86
|
+
if info.Subject.Subject:
|
|
87
|
+
self.subject = info.Subject.Subject
|
|
88
|
+
if info.Subject.SetAt:
|
|
89
|
+
set_at = datetime.fromtimestamp(info.Subject.SetAt, tz=timezone.utc)
|
|
90
|
+
self.subject_date = set_at
|
|
91
|
+
if info.Subject.SetByJID:
|
|
92
|
+
participant = await self.get_participant_by_legacy_id(
|
|
93
|
+
info.Subject.SetByJID
|
|
94
|
+
)
|
|
95
|
+
if name := participant.nickname:
|
|
96
|
+
self.subject_setter = name
|
|
97
|
+
for data in info.Participants:
|
|
98
|
+
participant = await self.get_participant_by_legacy_id(data.JID)
|
|
99
|
+
if data.Action == whatsapp.GroupParticipantActionRemove:
|
|
100
|
+
self.remove_participant(participant)
|
|
101
|
+
else:
|
|
102
|
+
if data.Affiliation == whatsapp.GroupAffiliationAdmin:
|
|
103
|
+
# Only owners can change the group name according to
|
|
104
|
+
# XEP-0045, so we make all "WA admins" "XMPP owners"
|
|
105
|
+
participant.affiliation = "owner"
|
|
106
|
+
participant.role = "moderator"
|
|
107
|
+
elif data.Affiliation == whatsapp.GroupAffiliationOwner:
|
|
108
|
+
# The WA owner is in fact the person who created the room
|
|
109
|
+
participant.set_hats(
|
|
110
|
+
[Hat("https://slidge.im/hats/slidge-whatsapp/owner", "Owner")]
|
|
111
|
+
)
|
|
112
|
+
participant.affiliation = "owner"
|
|
113
|
+
participant.role = "moderator"
|
|
114
|
+
else:
|
|
115
|
+
participant.affiliation = "member"
|
|
116
|
+
participant.role = "participant"
|
|
117
|
+
|
|
118
|
+
async def replace_mentions(self, t: str):
|
|
119
|
+
return replace_whatsapp_mentions(
|
|
120
|
+
t,
|
|
121
|
+
participants=(
|
|
122
|
+
{
|
|
123
|
+
p.contact.jid_username: p.nickname
|
|
124
|
+
async for p in self.get_participants()
|
|
125
|
+
if p.contact is not None # should not happen
|
|
126
|
+
}
|
|
127
|
+
| {self.session.user_phone: self.user_nick}
|
|
128
|
+
if self.session.user_phone # user_phone *should* be set at this point,
|
|
129
|
+
else {} # but better safe than sorry
|
|
130
|
+
),
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
async def on_avatar(self, data: Optional[bytes], mime: Optional[str]) -> None:
|
|
134
|
+
return self.session.whatsapp.SetAvatar(
|
|
135
|
+
self.legacy_id, await get_bytes_temp(data) if data else ""
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
async def on_set_config(
|
|
139
|
+
self,
|
|
140
|
+
name: Optional[str],
|
|
141
|
+
description: Optional[str],
|
|
142
|
+
):
|
|
143
|
+
# there are no group descriptions in WA, but topics=subjects
|
|
144
|
+
if self.name != name:
|
|
145
|
+
self.session.whatsapp.SetGroupName(self.legacy_id, name)
|
|
146
|
+
|
|
147
|
+
async def on_set_subject(self, subject: str):
|
|
148
|
+
if self.subject != subject:
|
|
149
|
+
self.session.whatsapp.SetGroupTopic(self.legacy_id, subject)
|
|
150
|
+
|
|
151
|
+
async def on_set_affiliation(
|
|
152
|
+
self,
|
|
153
|
+
contact: "Contact", # type:ignore
|
|
154
|
+
affiliation: MucAffiliation,
|
|
155
|
+
reason: Optional[str],
|
|
156
|
+
nickname: Optional[str],
|
|
157
|
+
):
|
|
158
|
+
assert contact.contact_pk is not None
|
|
159
|
+
assert self.pk is not None
|
|
160
|
+
if affiliation == "member":
|
|
161
|
+
if (
|
|
162
|
+
self.xmpp.store.participants.get_by_contact(self.pk, contact.contact_pk)
|
|
163
|
+
is not None
|
|
164
|
+
):
|
|
165
|
+
change = "demote"
|
|
166
|
+
else:
|
|
167
|
+
change = "add"
|
|
168
|
+
elif affiliation == "admin":
|
|
169
|
+
change = "promote"
|
|
170
|
+
elif affiliation == "outcast" or affiliation == "none":
|
|
171
|
+
change = "remove"
|
|
172
|
+
else:
|
|
173
|
+
raise XMPPError(
|
|
174
|
+
"bad-request",
|
|
175
|
+
f"You can't make a participant '{affiliation}' in whatsapp",
|
|
176
|
+
)
|
|
177
|
+
self.session.whatsapp.SetAffiliation(self.legacy_id, contact.legacy_id, change)
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
class Bookmarks(LegacyBookmarks[str, MUC]):
|
|
181
|
+
session: "Session"
|
|
182
|
+
|
|
183
|
+
def __init__(self, session: "Session"):
|
|
184
|
+
super().__init__(session)
|
|
185
|
+
self.__filled = False
|
|
186
|
+
|
|
187
|
+
async def fill(self):
|
|
188
|
+
groups = self.session.whatsapp.GetGroups()
|
|
189
|
+
for group in groups:
|
|
190
|
+
await self.add_whatsapp_group(group)
|
|
191
|
+
self.__filled = True
|
|
192
|
+
|
|
193
|
+
async def add_whatsapp_group(self, data: whatsapp.Group):
|
|
194
|
+
muc = await self.by_legacy_id(data.JID)
|
|
195
|
+
await muc.update_whatsapp_info(data)
|
|
196
|
+
await muc.add_to_bookmarks()
|
|
197
|
+
|
|
198
|
+
async def legacy_id_to_jid_local_part(self, legacy_id: str):
|
|
199
|
+
return "#" + legacy_id[: legacy_id.find("@")]
|
|
200
|
+
|
|
201
|
+
async def jid_local_part_to_legacy_id(self, local_part: str):
|
|
202
|
+
if not local_part.startswith("#"):
|
|
203
|
+
raise XMPPError("bad-request", "Invalid group ID, expected '#' prefix")
|
|
204
|
+
|
|
205
|
+
if not self.__filled:
|
|
206
|
+
raise XMPPError(
|
|
207
|
+
"recipient-unavailable", "Still fetching group info, please retry later"
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
whatsapp_group_id = (
|
|
211
|
+
local_part.removeprefix("#") + "@" + whatsapp.DefaultGroupServer
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
if (
|
|
215
|
+
self.xmpp.store.rooms.get_by_legacy_id(
|
|
216
|
+
self.session.user_pk, whatsapp_group_id
|
|
217
|
+
)
|
|
218
|
+
is None
|
|
219
|
+
):
|
|
220
|
+
raise XMPPError("item-not-found", f"No group found for {whatsapp_group_id}")
|
|
221
|
+
|
|
222
|
+
return whatsapp_group_id
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def replace_xmpp_mentions(text: str, mentions: list[Mention]):
|
|
226
|
+
offset: int = 0
|
|
227
|
+
result: str = ""
|
|
228
|
+
for m in mentions:
|
|
229
|
+
legacy_id = "@" + m.contact.legacy_id[: m.contact.legacy_id.find("@")]
|
|
230
|
+
result = result + text[offset : m.start] + legacy_id
|
|
231
|
+
offset = m.end
|
|
232
|
+
return result + text[offset:] if offset > 0 else text
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
def replace_whatsapp_mentions(text: str, participants: dict[str, str]):
|
|
236
|
+
def match(m: re.Match):
|
|
237
|
+
group = m.group(0)
|
|
238
|
+
return participants.get(group.replace("@", "+"), group)
|
|
239
|
+
|
|
240
|
+
return re.sub(r"@\d+", match, text)
|