Source code for yowsup_celery.layer

# -*- coding: utf-8 -*-
from yowsup.layers.interface import YowInterfaceLayer, ProtocolEntityCallback
from yowsup.layers import EventCallback
from yowsup.layers.protocol_messages.protocolentities import \
    TextMessageProtocolEntity
from yowsup.layers.protocol_media.protocolentities import RequestUploadIqProtocolEntity
from yowsup.layers.protocol_media.protocolentities.message_media_downloadable_image import \
    ImageDownloadableMediaMessageProtocolEntity
from yowsup.layers.protocol_media.protocolentities.message_media_downloadable_audio import \
    AudioDownloadableMediaMessageProtocolEntity
from yowsup.layers.protocol_media.protocolentities.message_media_location import LocationMediaMessageProtocolEntity
from yowsup.layers.protocol_media.protocolentities.message_media_vcard import VCardMediaMessageProtocolEntity
import logging
from yowsup.layers.network import YowNetworkLayer
from yowsup.layers import YowLayerEvent
from functools import wraps
import sys
import os
from yowsup_celery.layer_interface import CeleryLayerInterface
from yowsup_celery.exceptions import ConnectionError
from yowsup.layers.protocol_media.mediauploader import MediaUploader

logger = logging.getLogger(__name__)

[docs]def connection_required(f): @wraps(f) def decorated_function(self, *args, **kwargs): if not self.connected: raise ConnectionError("%s needs to be connected" % f.__name__) return f(self, *args, **kwargs) return decorated_function
[docs]class CeleryLayer(YowInterfaceLayer): """ Layer to be on the top of the Yowsup Stack. :ivar bool connected: connected or not connected to whatsapp :ivar YowLayerInterface interface: layer interface """ def __init__(self): super(CeleryLayer, self).__init__() self.connected = False self.interface = CeleryLayerInterface(self)
[docs] def normalize_jid(self, number): if '@' in number: return number elif "-" in number: return "%s@g.us" % number return "%s@s.whatsapp.net" % number
[docs] def do_send_image(self, file_path, url, to, ip=None, caption=None): entity = ImageDownloadableMediaMessageProtocolEntity.fromFilePath(file_path, url, ip, to, caption=caption) self.toLower(entity)
[docs] def do_send_audio(self, file_path, url, to, ip=None, caption=None): entity = AudioDownloadableMediaMessageProtocolEntity.fromFilePath(file_path, url, ip, to) self.toLower(entity)
@ProtocolEntityCallback("success")
[docs] def on_success(self, success_protocol_entity): """ Callback when there is a successful connection to whatsapp server """ logger.info("Logged in") self.connected = True
@ProtocolEntityCallback("failure")
[docs] def on_failure(self, entity): """ Callback function when there is a failure in a connection to whatsapp server """ logger.error("Login failed, reason: %s" % entity.getReason()) self.connected = False
@ProtocolEntityCallback("ack") @connection_required
[docs] def on_ack(self, entity): """ Callback function when receiving an ack for a sent message from whatsapp """ logger.info("Ack id %s received" % entity.getId())
@ProtocolEntityCallback("message") @connection_required
[docs] def on_message(self, message_protocol_entity): """ Callback function when receiving message from whatsapp server """ logger.info("Message id %s received" % message_protocol_entity.getId()) # answer with receipt self.toLower(message_protocol_entity.ack())
@ProtocolEntityCallback("receipt") @connection_required
[docs] def on_receipt(self, receipt_protocol_entity): """ Callback function when receiving receipt message from whatsapp """ self.toLower(receipt_protocol_entity.ack())
@EventCallback(YowNetworkLayer.EVENT_STATE_DISCONNECTED) @connection_required
[docs] def on_disconnected(self, yowLayerEvent): """ Callback function when receiving a disconnection event """ logger.info("On disconnected") self.connected = False
[docs] def on_request_upload_result(self, jid, path, result_request_upload_iq_protocol_entity, request_upload_iq_protocol_entity, caption=None): if request_upload_iq_protocol_entity.mediaType == RequestUploadIqProtocolEntity.MEDIA_TYPE_AUDIO: do_send_fn = self.do_send_audio else: do_send_fn = self.do_send_image if result_request_upload_iq_protocol_entity.isDuplicate(): do_send_fn(path, result_request_upload_iq_protocol_entity.getUrl(), jid, result_request_upload_iq_protocol_entity.getIp(), caption) else: success_fn = lambda filePath, jid, url: do_send_fn(filePath, url, jid, result_request_upload_iq_protocol_entity.getIp(), caption) mediaUploader = MediaUploader(jid, self.getOwnJid(), path, result_request_upload_iq_protocol_entity.getUrl(), result_request_upload_iq_protocol_entity.getResumeOffset(), success_fn, self.on_upload_error, self.on_upload_progress, async=True) mediaUploader.start()
[docs] def on_upload_error(self, file_path, jid, url): logger.error("Upload file %s to %s for %s failed!" % (file_path, url, jid))
[docs] def on_upload_progress(self, file_path, jid, url, progress): logger.info("%s => %s, %d%% \r" % (os.path.basename(file_path), jid, progress))
@connection_required
[docs] def send_message(self, number, content): """ Send message :param str number: phone number with cc (country code) :param str content: body text of the message """ outgoing_message = TextMessageProtocolEntity(content.encode("utf-8") if sys.version_info >= (3, 0) else content, to=self.normalize_jid(number)) self.toLower(outgoing_message) return outgoing_message
[docs] def connect(self): if self.connected: logger.warning("Already connected, disconnect first") return False self.getLayerInterface(YowNetworkLayer).connect() return True
@connection_required
[docs] def disconnect(self): self.broadcastEvent(YowLayerEvent(YowNetworkLayer.EVENT_STATE_DISCONNECT)) return True
def _send_media_path(self, number, path, type, caption=None): jid = self.normalize_jid(number) entity = RequestUploadIqProtocolEntity(type, filePath=path) success_fn = lambda success_entity, original_entity: self.on_request_upload_result(jid, path, success_entity, original_entity, caption) error_fn = lambda error_entity, original_entity: self.on_request_upload_error(jid, path, error_entity, original_entity) self._sendIq(entity, success_fn, error_fn) @connection_required
[docs] def send_image(self, number, path, caption=None): """ Send image message :param str number: phone number with cc (country code) :param str path: image file path """ return self._send_media_path(number, path, RequestUploadIqProtocolEntity.MEDIA_TYPE_IMAGE, caption)
@connection_required
[docs] def send_audio(self, number, path): """ Send audio message :param str number: phone number with cc (country code) :param str path: audio file path """ return self._send_media_path(number, path, RequestUploadIqProtocolEntity.MEDIA_TYPE_AUDIO)
@connection_required
[docs] def send_location(self, number, name, url, latitude, longitude): """ Send location message :param str number: phone number with cc (country code) :param str name: indentifier for the location :param str url: location url :param str longitude: location longitude :param str latitude: location latitude """ location_message = LocationMediaMessageProtocolEntity(latitude, longitude, name, url, encoding="raw", to=self.normalize_jid(number)) self.toLower(location_message) return location_message
@connection_required
[docs] def send_vcard(self, number, name, data): """ Send location message :param str number: phone number with cc (country code) :param str name: indentifier for the location :param str data: vcard format i.e. BEGIN:VCARD VERSION:3.0 N:;Home;;; FN:Home item1.TEL:+34 911234567 item1.X-ABLabel:Home END:VCARD """ vcard_message = VCardMediaMessageProtocolEntity(name, data, to=self.normalize_jid(number)) self.toLower(vcard_message) return vcard_message