mirror of
https://github.com/Sneed-Group/pypush-plus-plus
synced 2025-01-09 17:33:47 +00:00
preparing some stuff
This commit is contained in:
parent
da483b995b
commit
3904bf9e3f
2 changed files with 108 additions and 43 deletions
25
demo.py
25
demo.py
|
@ -168,6 +168,7 @@ def fixup_handle(handle):
|
||||||
|
|
||||||
|
|
||||||
current_participants = []
|
current_participants = []
|
||||||
|
sms = False
|
||||||
current_effect = None
|
current_effect = None
|
||||||
while True:
|
while True:
|
||||||
im.activate_sms() # We must call this always since SMS could be turned off and on again, and it might have been on before this.
|
im.activate_sms() # We must call this always since SMS could be turned off and on again, and it might have been on before this.
|
||||||
|
@ -228,6 +229,13 @@ while True:
|
||||||
if len(msg) < 2 or msg[1] == "":
|
if len(msg) < 2 or msg[1] == "":
|
||||||
print("filter [recipients]")
|
print("filter [recipients]")
|
||||||
else:
|
else:
|
||||||
|
if msg[1] == "sms":
|
||||||
|
print("Filtering to SMS")
|
||||||
|
msg = msg[1:]
|
||||||
|
sms = True
|
||||||
|
else:
|
||||||
|
sms = False
|
||||||
|
|
||||||
print(f"Filtering to {[fixup_handle(h) for h in msg[1:]]}")
|
print(f"Filtering to {[fixup_handle(h) for h in msg[1:]]}")
|
||||||
current_participants = [fixup_handle(h) for h in msg[1:]]
|
current_participants = [fixup_handle(h) for h in msg[1:]]
|
||||||
im._cache_keys(
|
im._cache_keys(
|
||||||
|
@ -256,15 +264,16 @@ while True:
|
||||||
if msg.startswith("\\"):
|
if msg.startswith("\\"):
|
||||||
msg = msg[1:]
|
msg = msg[1:]
|
||||||
|
|
||||||
imsg = imessage.iMessage.create(
|
if sms:
|
||||||
im,
|
import uuid
|
||||||
msg,
|
m = imessage.SMSReflectedMessage(
|
||||||
current_participants,
|
msg, user.current_handle, current_participants, uuid.uuid4()
|
||||||
)
|
)
|
||||||
|
else:
|
||||||
|
m = imessage.iMessage.create(im, msg, current_participants)
|
||||||
|
m.effect = current_effect
|
||||||
|
|
||||||
imsg.effect = current_effect
|
im.send(m)
|
||||||
|
|
||||||
im.send(imsg)
|
|
||||||
current_effect = None
|
current_effect = None
|
||||||
else:
|
else:
|
||||||
print("No chat selected, use help for help")
|
print("No chat selected, use help for help")
|
||||||
|
|
126
imessage.py
126
imessage.py
|
@ -147,7 +147,7 @@ class Message:
|
||||||
@dataclass
|
@dataclass
|
||||||
class SMSReflectedMessage(Message):
|
class SMSReflectedMessage(Message):
|
||||||
def from_raw(message: bytes, sender: str | None = None) -> "SMSReflectedMessage":
|
def from_raw(message: bytes, sender: str | None = None) -> "SMSReflectedMessage":
|
||||||
"""Create a `SMSIncomingMessage` from raw message bytes"""
|
"""Create a `SMSReflectedMessage` from raw message bytes"""
|
||||||
|
|
||||||
# Decompress the message
|
# Decompress the message
|
||||||
try:
|
try:
|
||||||
|
@ -158,7 +158,7 @@ class SMSReflectedMessage(Message):
|
||||||
|
|
||||||
message = plistlib.loads(message)
|
message = plistlib.loads(message)
|
||||||
|
|
||||||
logger.debug(f"Decoding SMSReflectedMessage: {message}")
|
logger.info(f"Decoding SMSReflectedMessage: {message}")
|
||||||
|
|
||||||
return SMSReflectedMessage(
|
return SMSReflectedMessage(
|
||||||
text=message["mD"]["plain-body"],
|
text=message["mD"]["plain-body"],
|
||||||
|
@ -168,7 +168,42 @@ class SMSReflectedMessage(Message):
|
||||||
_raw=message,
|
_raw=message,
|
||||||
_compressed=compressed,
|
_compressed=compressed,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def to_raw(self) -> bytes:
|
||||||
|
# {'re': [{'id': '+14155086773', 'uID': '4155086773', 'n': 'us'}], 'ic': 0, 'mD': {'handle': '+14155086773', 'guid': imessage.py:201
|
||||||
|
# '35694E24-E265-4D5C-8CA7-9499E35D0402', 'replyToGUID': '4F9BC76B-B09C-2A60-B312-9029D529706B', 'plain-body': 'Test sms', 'service':
|
||||||
|
# 'SMS', 'sV': '1'}, 'fR': True, 'chat-style': 'im'}
|
||||||
|
#pass
|
||||||
|
# Strip tel: from participants, making sure they are all phone numbers
|
||||||
|
#participants = [p.replace("tel:", "") for p in self.participants]
|
||||||
|
|
||||||
|
d = {
|
||||||
|
"re": [{"id": p} for p in self.participants],
|
||||||
|
"ic": 0,
|
||||||
|
"mD": {
|
||||||
|
"handle": self.participants[0] if len(self.participants) == 1 else None,
|
||||||
|
#"handle": self.sender,
|
||||||
|
"guid": str(self.id).upper(),
|
||||||
|
#"replyToGUID": "3B4B465F-F419-40FD-A8EF-94A110518E9F",
|
||||||
|
#"replyToGUID": str(self.id).upper(),
|
||||||
|
"xhtml": f"<html><body>{self.text}</body></html>",
|
||||||
|
"plain-body": self.text,
|
||||||
|
"service": "SMS",
|
||||||
|
"sV": "1",
|
||||||
|
},
|
||||||
|
#"fR": True,
|
||||||
|
"chat-style": "im" if len(self.participants) == 1 else "chat"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Dump as plist
|
||||||
|
d = plistlib.dumps(d, fmt=plistlib.FMT_BINARY)
|
||||||
|
|
||||||
|
# Compress
|
||||||
|
if self._compressed:
|
||||||
|
d = gzip.compress(d, mtime=0)
|
||||||
|
|
||||||
|
return d
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"[SMS {self.sender}] '{self.text}'"
|
return f"[SMS {self.sender}] '{self.text}'"
|
||||||
|
|
||||||
|
@ -279,6 +314,22 @@ class iMessage(Message):
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"[iMessage {self.sender}] '{self.text}'"
|
return f"[iMessage {self.sender}] '{self.text}'"
|
||||||
|
|
||||||
|
MESSAGE_TYPES = {
|
||||||
|
100: ("com.apple.madrid", iMessage),
|
||||||
|
140: ("com.apple.private.alloy.sms", SMSIncomingMessage),
|
||||||
|
141: ("com.apple.private.alloy.sms", SMSIncomingImage),
|
||||||
|
143: ("com.apple.private.alloy.sms", SMSReflectedMessage),
|
||||||
|
144: ("com.apple.private.alloy.sms", SMSReflectedMessage),
|
||||||
|
}
|
||||||
|
|
||||||
|
def maybe_decompress(message: bytes) -> bytes:
|
||||||
|
"""Decompresses a message if it is compressed, otherwise returns the original message"""
|
||||||
|
try:
|
||||||
|
message = gzip.decompress(message)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
return message
|
||||||
|
|
||||||
class iMessageUser:
|
class iMessageUser:
|
||||||
"""Represents a logged in and connected iMessage user.
|
"""Represents a logged in and connected iMessage user.
|
||||||
This abstraction should probably be reworked into IDS some time..."""
|
This abstraction should probably be reworked into IDS some time..."""
|
||||||
|
@ -434,27 +485,12 @@ class iMessageUser:
|
||||||
"""
|
"""
|
||||||
Will return the next iMessage in the queue, or None if there are no messages
|
Will return the next iMessage in the queue, or None if there are no messages
|
||||||
"""
|
"""
|
||||||
|
for type, (topic, cls) in MESSAGE_TYPES.items():
|
||||||
# Check for iMessages
|
body = self._receive_raw(type, topic)
|
||||||
body = self._receive_raw(100, "com.apple.madrid")
|
if body is not None:
|
||||||
t = iMessage
|
t = cls
|
||||||
if body is None:
|
break
|
||||||
# Check for SMS messages
|
else:
|
||||||
body = self._receive_raw(143, "com.apple.private.alloy.sms")
|
|
||||||
t = SMSReflectedMessage
|
|
||||||
if body is None:
|
|
||||||
# Check for SMS reflected images
|
|
||||||
body = self._receive_raw(144, "com.apple.private.alloy.sms")
|
|
||||||
t = SMSReflectedMessage
|
|
||||||
if body is None:
|
|
||||||
# Check for SMS incoming messages
|
|
||||||
body = self._receive_raw(140, "com.apple.private.alloy.sms")
|
|
||||||
t = SMSIncomingMessage
|
|
||||||
if body is None:
|
|
||||||
# Incoming images
|
|
||||||
body = self._receive_raw(141, "com.apple.private.alloy.sms")
|
|
||||||
t = SMSIncomingImage
|
|
||||||
if body is None:
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
@ -485,8 +521,9 @@ class iMessageUser:
|
||||||
self.USER_CACHE = {}
|
self.USER_CACHE = {}
|
||||||
|
|
||||||
# Check to see if we have cached the keys for all of the participants
|
# Check to see if we have cached the keys for all of the participants
|
||||||
if all([p in self.USER_CACHE for p in participants]):
|
#if all([p in self.USER_CACHE for p in participants]):
|
||||||
return
|
# return
|
||||||
|
# TODO: This doesn't work since it doesn't check if they are cached for all topics
|
||||||
|
|
||||||
# Look up the public keys for the participants, and cache a token : public key mapping
|
# Look up the public keys for the participants, and cache a token : public key mapping
|
||||||
lookup = self.user.lookup(participants, topic=topic)
|
lookup = self.user.lookup(participants, topic=topic)
|
||||||
|
@ -625,6 +662,16 @@ class iMessageUser:
|
||||||
if act_message is None:
|
if act_message is None:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
logger.info(f"Received SMS activation message : {act_message}")
|
||||||
|
# Decrypt the payload
|
||||||
|
act_message = self._decrypt_payload(act_message["P"])
|
||||||
|
act_message = plistlib.loads(maybe_decompress(act_message))
|
||||||
|
|
||||||
|
if act_message == {'wc': False, 'ar': True}:
|
||||||
|
logger.info("SMS forwarding activated, sending response")
|
||||||
|
else:
|
||||||
|
logger.info("SMS forwarding de-activated, sending response")
|
||||||
|
|
||||||
self._send_raw(
|
self._send_raw(
|
||||||
147,
|
147,
|
||||||
[self.user.current_handle],
|
[self.user.current_handle],
|
||||||
|
@ -634,19 +681,28 @@ class iMessageUser:
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
def send(self, message: "iMessage"):
|
def send(self, message: Message):
|
||||||
self._cache_keys(message.participants, "com.apple.madrid")
|
# Check what type of message we are sending
|
||||||
|
for t, (topic, cls) in MESSAGE_TYPES.items():
|
||||||
|
if isinstance(message, cls):
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
raise Exception("Unknown message type")
|
||||||
|
|
||||||
|
send_to = message.participants if isinstance(message, iMessage) else [self.user.current_handle]
|
||||||
|
|
||||||
|
self._cache_keys(send_to, topic)
|
||||||
|
|
||||||
self._send_raw(
|
self._send_raw(
|
||||||
100,
|
t,
|
||||||
message.participants,
|
send_to,
|
||||||
"com.apple.madrid",
|
topic,
|
||||||
message.to_raw(),
|
message.to_raw(),
|
||||||
message.id,
|
message.id,
|
||||||
{
|
{
|
||||||
"E": "pair",
|
"E": "pair", # TODO: Do we need the nr field for SMS?
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
# Check for delivery
|
# Check for delivery
|
||||||
count = 0
|
count = 0
|
||||||
|
@ -655,14 +711,14 @@ class iMessageUser:
|
||||||
import time
|
import time
|
||||||
start = time.time()
|
start = time.time()
|
||||||
|
|
||||||
for p in message.participants:
|
for p in send_to:
|
||||||
for t in self.USER_CACHE[p]:
|
for t in self.USER_CACHE[p]:
|
||||||
if t == self.connection.token:
|
if t == self.connection.token:
|
||||||
continue
|
continue
|
||||||
total += 1
|
total += 1
|
||||||
|
|
||||||
while count < total and time.time() - start < 2:
|
while count < total and time.time() - start < 2:
|
||||||
resp = self._receive_raw(255, "com.apple.madrid")
|
resp = self._receive_raw(255, topic)
|
||||||
if resp is None:
|
if resp is None:
|
||||||
continue
|
continue
|
||||||
count += 1
|
count += 1
|
||||||
|
|
Loading…
Reference in a new issue