Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added ability to send images. #124

Merged
merged 8 commits into from
Jul 19, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
29 changes: 24 additions & 5 deletions kik_unofficial/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,20 @@ def send_chat_message(self, peer_jid: str, message: str, bot_mention_jid=None):
log.info("[+] Sending chat message '{}' to user '{}'...".format(message, peer_jid))
return self._send_xmpp_element(chatting.OutgoingChatMessage(peer_jid, message, False, bot_mention_jid))

def send_chat_image(self, peer_jid: str, file, forward=True):
"""
Sends an image chat message to another person or a group with the given JID/username.
:param peer_jid: The Jabber ID for which to send the message (looks like username_ejs@talk.kik.com)
If you don't know the JID of someone, you can also specify a kik username here.
:param file: Path of the file to send.
"""
if self.is_group_jid(peer_jid):
log.info("[+] Sending chat image to group '{}'...".format(peer_jid))
return self._send_xmpp_element(chatting.OutgoingGroupChatImage(peer_jid, file, forward))
else:
log.info("[+] Sending chat image to user '{}'...".format(peer_jid))
return self._send_xmpp_element(chatting.OutgoingChatImage(peer_jid, file, False, forward))

def send_read_receipt(self, peer_jid: str, receipt_message_id: str, group_jid=None):
"""
Sends a read receipt for a previously sent message, to a specific user or group.
Expand Down Expand Up @@ -438,19 +452,24 @@ def disconnect(self):
# Internal methods
# -----------------

def _send_xmpp_element(self, xmpp_element: XMPPElement):
def _send_xmpp_element(self, message: XMPPElement):
"""
Serializes and sends the given XMPP element to kik servers

:param xmpp_element: The XMPP element to send
:return: The UUID of the element that was sent
"""
while not self.connected:
log.debug("[!] Waiting for connection.")
time.sleep(0.1)

self.loop.call_soon_threadsafe(self.connection.send_raw_data, (xmpp_element.serialize()))
return xmpp_element.message_id
if type(message.serialize()) is list:
log.debug("[!] Sending multi packet data.")
packets = message.serialize()
for p in packets:
self.loop.call_soon_threadsafe(self.connection.send_raw_data, p)
return message.message_id
else:
self.loop.call_soon_threadsafe(self.connection.send_raw_data, message.serialize())
return message.message_id

def _on_new_data_received(self, data: bytes):
"""
Expand Down
1 change: 1 addition & 0 deletions kik_unofficial/datatypes/xmpp/base_elements.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
class XMPPElement:
def __init__(self):
self.message_id = CryptographicUtils.make_kik_uuid()
self.content_id = CryptographicUtils.make_kik_uuid() #Creating a seprate uuid for content as kik does the same.

def serialize(self) -> bytes:
raise NotImplementedError
Expand Down
48 changes: 47 additions & 1 deletion kik_unofficial/datatypes/xmpp/chatting.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"""

import time

import os
from bs4 import BeautifulSoup
from kik_unofficial.datatypes.peers import Group
from kik_unofficial.datatypes.xmpp.base_elements import XMPPElement, XMPPResponse
Expand Down Expand Up @@ -50,6 +50,52 @@ def __init__(self, group_jid, body, bot_mention_jid):
super().__init__(group_jid, body, is_group=True, bot_mention_jid=bot_mention_jid)


class OutgoingChatImage(XMPPElement):
"""
Represents an outgoing image chat message to another kik entity (member or group)
"""
def __init__(self, peer_jid, file_location, is_group=False, forward=True):
super().__init__()
self.peer_jid = peer_jid
self.file_location = ParsingUtilities.parse_image(file_location)
self.allow_forward = forward
self.file_size = os.path.getsize(file_location)
self.base64 = ParsingUtilities.read_file_as_base64(self.file_location)
self.is_group = is_group

def serialize(self):
timestamp = str(int(round(time.time() * 1000)))
message_type = "chat" if not self.is_group else "groupchat"
data = ('<message to="{0}" id="{1}" cts="{2}" type="{3}">'
'<kik timestamp="{2}" qos="true" push="true" />'
'<request xmlns="kik:message:receipt" d="true" r="true" />'
'<content id="{4}" v="2" app-id="com.kik.ext.gallery">'
'<strings>'
'<app-name>Gallery</app-name>'
'<file-size>{5}</file-size>'
'<allow-forward>{6}</allow-forward>'
'<file-name>{4}.jpg</file-name>'
'</strings>'
'<images>'
'<preview>{7}</preview>'
'</images>'
'</content>'
'</message>'
).format(self.peer_jid, self.message_id, timestamp, message_type, self.content_id, self.file_size,
str(self.allow_forward).lower(), self.base64)

packets = [data[s:s+16384].encode() for s in range(0,len(data), 16384)]
return list(packets)


class OutgoingGroupChatImage(OutgoingChatImage):
"""
Represents an outgoing image chat message to a group
"""
def __init__(self, group_jid, file_location, forward):
super().__init__(group_jid, file_location, is_group=True, forward=forward)


class IncomingChatMessage(XMPPResponse):
"""
Represents an incoming text chat message from another user
Expand Down
2 changes: 1 addition & 1 deletion kik_unofficial/datatypes/xmpp/login.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ def __init__(self, node, username, password, device_id_override=None):
def serialize(self):
jid = self.node + "@talk.kik.com"
jid_with_resource = jid + "/CAN" + (self.device_id_override if self.device_id_override else device_id)
timestamp = CryptographicUtils.make_kik_timestamp()
timestamp = str(CryptographicUtils.make_kik_timestamp())
sid = CryptographicUtils.make_kik_uuid()

# some super secret cryptographic stuff
Expand Down
27 changes: 27 additions & 0 deletions kik_unofficial/utilities/parsing_utilities.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import base64
from PIL import Image
import math
import pathlib


class ParsingUtilities:
Expand All @@ -21,6 +24,30 @@ def decode_base64(data):
data += b'=' * (4 - missing_padding)
return base64.decodebytes(data)

@staticmethod
def read_file_as_base64(file_location):
with open(file_location, "rb") as file:
data = base64.b64encode(file.read())
return data.decode()

@staticmethod
def parse_image(file_location):
'''
Converts images to .jpg and compresses/upscales them so that large image files can be sent after compression.
'''
file_name = pathlib.PurePath(file_location).name
img = Image.open(file_location)
image_out = file_name.split('.')[0] + "_send.jpg"
width, height = img.size
if len(img.split()) == 4:
r, g, b, a = img.split()
img = Image.merge("RGB", (r, g, b))
larger_dim = height if height > width else width
ratio = larger_dim/900
image = img.resize((math.ceil(width / ratio), math.ceil(height / ratio)))
image.save(image_out)
return image_out

@staticmethod
def fix_base64_padding(data):
return data + '=' * (-len(data) % 4)
Expand Down