Make Session use YowsupApp instead of SpectrumLayer

This commit is contained in:
moyamo 2015-08-30 20:51:44 +02:00
parent 8cc3c85145
commit 254465301b
2 changed files with 203 additions and 175 deletions

View file

@ -44,32 +44,32 @@ from yowsup.layers.protocol_receipts import YowReceiptProtocolLayer
from yowsup.layers.protocol_acks import YowAckProtocolLayer from yowsup.layers.protocol_acks import YowAckProtocolLayer
from yowsup.layers.logger import YowLoggerLayer from yowsup.layers.logger import YowLoggerLayer
from yowsup.common import YowConstants from yowsup.common import YowConstants
from yowsup.layers.protocol_receipts.protocolentities import * from yowsup.layers.protocol_receipts.protocolentities import *
from yowsup import env from yowsup import env
from yowsup.layers.protocol_presence import * from yowsup.layers.protocol_presence import *
from yowsup.layers.protocol_presence.protocolentities import * from yowsup.layers.protocol_presence.protocolentities import *
from yowsup.layers.protocol_messages.protocolentities import TextMessageProtocolEntity from yowsup.layers.protocol_messages.protocolentities import TextMessageProtocolEntity
from yowsup.layers.protocol_chatstate.protocolentities import * from yowsup.layers.protocol_chatstate.protocolentities import *
from yowsup.layers.protocol_acks.protocolentities import * from yowsup.layers.protocol_acks.protocolentities import *
from yowsup.layers import YowLayer from yowsup.layers import YowLayer
from yowsup.layers.auth import YowCryptLayer, YowAuthenticationProtocolLayer from yowsup.layers.auth import YowCryptLayer, YowAuthenticationProtocolLayer
from yowsup.layers.coder import YowCoderLayer from yowsup.layers.coder import YowCoderLayer
from yowsup.layers.logger import YowLoggerLayer from yowsup.layers.logger import YowLoggerLayer
from yowsup.layers.network import YowNetworkLayer from yowsup.layers.network import YowNetworkLayer
from yowsup.layers.protocol_messages import YowMessagesProtocolLayer from yowsup.layers.protocol_messages import YowMessagesProtocolLayer
from yowsup.layers.stanzaregulator import YowStanzaRegulator from yowsup.layers.stanzaregulator import YowStanzaRegulator
from yowsup.layers.protocol_media import YowMediaProtocolLayer from yowsup.layers.protocol_media import YowMediaProtocolLayer
from yowsup.layers.protocol_acks import YowAckProtocolLayer from yowsup.layers.protocol_acks import YowAckProtocolLayer
from yowsup.layers.protocol_receipts import YowReceiptProtocolLayer from yowsup.layers.protocol_receipts import YowReceiptProtocolLayer
from yowsup.layers.protocol_groups import YowGroupsProtocolLayer from yowsup.layers.protocol_groups import YowGroupsProtocolLayer
from yowsup.layers.protocol_presence import YowPresenceProtocolLayer from yowsup.layers.protocol_presence import YowPresenceProtocolLayer
from yowsup.layers.protocol_ib import YowIbProtocolLayer from yowsup.layers.protocol_ib import YowIbProtocolLayer
from yowsup.layers.protocol_notifications import YowNotificationsProtocolLayer from yowsup.layers.protocol_notifications import YowNotificationsProtocolLayer
from yowsup.layers.protocol_iq import YowIqProtocolLayer from yowsup.layers.protocol_iq import YowIqProtocolLayer
from yowsup.layers.protocol_contacts import YowContactsIqProtocolLayer from yowsup.layers.protocol_contacts import YowContactsIqProtocolLayer
from yowsup.layers.protocol_chatstate import YowChatstateProtocolLayer from yowsup.layers.protocol_chatstate import YowChatstateProtocolLayer
from yowsup.layers.protocol_privacy import YowPrivacyProtocolLayer from yowsup.layers.protocol_privacy import YowPrivacyProtocolLayer
from yowsup.layers.protocol_profiles import YowProfilesProtocolLayer from yowsup.layers.protocol_profiles import YowProfilesProtocolLayer
from yowsup.layers.protocol_calls import YowCallsProtocolLayer from yowsup.layers.protocol_calls import YowCallsProtocolLayer
from Spectrum2 import protocol_pb2 from Spectrum2 import protocol_pb2
@ -78,8 +78,9 @@ from threading import Timer
from group import Group from group import Group
from bot import Bot from bot import Bot
from constants import * from constants import *
from yowsupwrapper import YowsupApp
class Session(): class Session(YowsupApp):
def __init__(self, backend, user, legacyName, extra, db): def __init__(self, backend, user, legacyName, extra, db):
self.logger = logging.getLogger(self.__class__.__name__) self.logger = logging.getLogger(self.__class__.__name__)
@ -105,76 +106,22 @@ class Session():
self.bot = Bot(self) self.bot = Bot(self)
env.CURRENT_ENV = env.S40YowsupEnv()
layers = (SpectrumLayer,
YowParallelLayer((YowAuthenticationProtocolLayer,
YowMessagesProtocolLayer,
YowReceiptProtocolLayer,
YowAckProtocolLayer,
YowMediaProtocolLayer,
YowIbProtocolLayer,
YowIqProtocolLayer,
YowNotificationsProtocolLayer,
YowContactsIqProtocolLayer,
# YowChatstateProtocolLayer,
YowCallsProtocolLayer,
YowMediaProtocolLayer,
YowPrivacyProtocolLayer,
YowProfilesProtocolLayer,
YowGroupsProtocolLayer,
YowPresenceProtocolLayer)),
YowAxolotlLayer,
YowCoderLayer,
YowCryptLayer,
YowStanzaRegulator,
YowNetworkLayer
)
self.stack = YowStack(layers)
self.stack.broadcastEvent(
YowLayerEvent(SpectrumLayer.EVENT_START,
backend = self.backend,
user = self.user,
db = self.db,
legacyName = self.legacyName,
session = self
)
)
def __del__(self): # handleLogoutRequest def __del__(self): # handleLogoutRequest
self.logout() self.logout()
def call(self, method, **kwargs): def call(self, method, **kwargs):
self.logger.debug("%s(%s)", method, self.logger.debug("%s(%s)", method,
", ".join(str(k) + ': ' + str(v) for k, v in kwargs.items())) ", ".join(str(k) + ': ' + str(v) for k, v in kwargs.items()))
self.stack.broadcastEvent(YowLayerEvent(method, **kwargs)) ##self.stack.broadcastEvent(YowLayerEvent(method, **kwargs))
def logout(self): def logout(self):
self.loggedin = False self.loggedin = False
self.stack.broadcastEvent(YowLayerEvent(YowNetworkLayer.EVENT_STATE_DISCONNECT)) super(Session, self).logout()
def login(self, password): def login(self, password):
self.stack.setProp(YowAuthenticationProtocolLayer.PROP_CREDENTIALS,
(self.legacyName, password))
self.stack.setProp(YowNetworkLayer.PROP_ENDPOINT,
YowConstants.ENDPOINTS[0])
self.stack.setProp(YowCoderLayer.PROP_DOMAIN,
YowConstants.DOMAIN)
self.stack.setProp(YowCoderLayer.PROP_RESOURCE,
env.CURRENT_ENV.getResource())
self.stack.setProp(YowIqProtocolLayer.PROP_PING_INTERVAL, 5)
self.loggedin = True self.loggedin = True
self.password = password self.password = password
try: super(Session, self).login(self.legacyName, self.password)
self.stack.broadcastEvent(
YowLayerEvent(YowNetworkLayer.EVENT_STATE_CONNECT))
except TypeError as e: # Occurs when password is not correctly formated
self.logger.debug("Auth error -> user: %s; details: %s;",
self.user, e)
try:
self.stack.loop(timeout=0.5, discrete=0.5)
except AuthError as e: # For some reason Yowsup throws an exception
self.logger.debug("Auth error -> user: %s; details: %s;",
self.user, e)
def updateRoomList(self): def updateRoomList(self):
rooms = [] rooms = []
@ -183,6 +130,86 @@ class Session():
self.backend.handleRoomList(rooms) self.backend.handleRoomList(rooms)
def updateRoster(self):
self.logger.debug("Update roster")
old = self.buddies.keys()
self.buddies.load()
new = self.buddies.keys()
add = set(new) - set(old)
remove = set(old) - set(new)
self.logger.debug("Roster remove: %s", str(list(remove)))
self.logger.debug("Roster add: %s", str(list(add)))
for number in remove:
self.backend.handleBuddyChanged(self.user, number, "", [], protocol_pb2.STATUS_NONE)
self.backend.handleBuddyRemoved(self.user, number)
entity = UnsubscribePresenceProtocolEntity(number + "@s.whatsapp.net")
self.toLower(entity)
for number in add:
buddy = self.buddies[number]
entity = SubscribePresenceProtocolEntity(number + "@s.whatsapp.net")
self.toLower(entity)
# Called by superclass
def onAuthSuccess(self, status, kind, creation,
expiration, props, nonce, t):
self.logger.info("Auth success: %s", self.user)
self.backend.handleConnected(self.user)
self.backend.handleBuddyChanged(self.user, "bot", self.bot.name, ["Admin"], protocol_pb2.STATUS_ONLINE)
self.initialized = True
self.updateRoster()
# Called by superclass
def onAuthFailed(self, reason):
self.logger.info("Auth failed: %s (%s)", self.user, reason)
self.backend.handleDisconnected(self.user, 0, reason)
self.password = None
# Called by superclass
def onDisconnect(self):
self.backend.handleDisconnected(self.user, 0, 'Disconnected for unknown reasons')
self.loggedin = False
# Called by superclass
def onReceipt(self, _id, _from, timestamp, type, participant, offline, items):
self.logger.debug("received receipt, sending ack: " +
' '.join(map(str, [_id, _from, timestamp,
type, participant, offline, items]))
)
# Called by superclass
def onAck(self, _id,_class, _from, timestamp):
self.logger.debug('received ack ' +
' '.join(map(str, [_id, _class, _from,timestamp,]))
)
# Called by superclass
def onMessageReceived(self, messageEntity):
self.logger.debug(str(messageEntity))
buddy = messageEntity.getFrom().split('@')[0]
messageContent = utils.softToUni(messageEntity.getBody())
timestamp = messageEntity.getTimestamp()
self.sendReceipt(messageEntity.getId(), messageEntity.getFrom(), 'read',
messageEntity.getParticipant())
if messageEntity.isBroadcast():
self.logger.info("Broadcast received from %s to %s: %s (at ts=%s)",\
buddy, self.legacyName, messageContent, timestamp)
messageContent = "[Broadcast] " + messageContent
else:
self.logger.info("Message received from %s to %s: %s (at ts=%s)",
buddy, self.legacyName, messageContent, timestamp)
self.session.sendMessageToXMPP(buddy, messageContent, timestamp)
# if receiptRequested: self.call("message_ack", (jid, messageId))
# spectrum RequestMethods # spectrum RequestMethods
def sendTypingStarted(self, buddy): def sendTypingStarted(self, buddy):
if buddy != "bot": if buddy != "bot":
@ -203,21 +230,21 @@ class Session():
elif "-" in sender: # group msg elif "-" in sender: # group msg
if "/" in sender: if "/" in sender:
room, buddy = sender.split("/") room, buddy = sender.split("/")
self.call("message_send", to = buddy + "@s.whatsapp.net", self.sendTextMessage(buddy + '@s.whatsapp.net', message)
message = message)
else: else:
room = sender room = sender
group = self.groups[room] group = self.groups[room]
self.backend.handleMessage(self.user, room, message, group.nick) self.backend.handleMessage(self.user, room, message, group.nick)
self.call("message_send", to = room + "@g.us", message = message) self.sendTextMessage(room + '@g.us', message)
else: # private msg else: # private msg
buddy = sender buddy = sender
if message == "\\lastseen": if message == "\\lastseen":
self.presenceRequested.append(buddy) self.presenceRequested.append(buddy)
self.call("presence_request", buddy = (buddy + "@s.whatsapp.net",)) self.call("presence_request", buddy = (buddy + "@s.whatsapp.net",))
else: else:
self.call("message_send", to=buddy + "@s.whatsapp.net", message=message) self.sendTextMessage(buddy + '@s.whatsapp.net', message)
def sendMessageToXMPP(self, buddy, messageContent, timestamp = ""): def sendMessageToXMPP(self, buddy, messageContent, timestamp = ""):
if timestamp: if timestamp:
@ -448,12 +475,12 @@ class SpectrumLayer(YowInterfaceLayer):
entity = PresenceProtocolEntity(name = 'This status is non-empty') entity = PresenceProtocolEntity(name = 'This status is non-empty')
self.toLower(entity) self.toLower(entity)
retval = True retval = True
elif layerEvent.getName() == 'message_send': # elif layerEvent.getName() == 'message_send':
to = layerEvent.getArg('to') # to = layerEvent.getArg('to')
message = layerEvent.getArg('message') # message = layerEvent.getArg('message')
messageEntity = TextMessageProtocolEntity(message, to = to) # messageEntity = TextMessageProtocolEntity(message, to = to)
self.toLower(messageEntity) # self.toLower(messageEntity)
retval = True # retval = True
elif layerEvent.getName() == 'typing_send': elif layerEvent.getName() == 'typing_send':
buddy = layerEvent.getArg('buddy') buddy = layerEvent.getArg('buddy')
state = OutgoingChatstateProtocolEntity( state = OutgoingChatstateProtocolEntity(
@ -476,82 +503,6 @@ class SpectrumLayer(YowInterfaceLayer):
self.logger.debug("EVENT %s", layerEvent.getName()) self.logger.debug("EVENT %s", layerEvent.getName())
return retval return retval
@ProtocolEntityCallback("success")
def onAuthSuccess(self, entity):
self.logger.info("Auth success: %s", self.user)
self.backend.handleConnected(self.user)
self.backend.handleBuddyChanged(self.user, "bot", self.bot.name, ["Admin"], protocol_pb2.STATUS_ONLINE)
self.session.initialized = True
self.updateRoster()
@ProtocolEntityCallback("failure")
def onAuthFailed(self, entity):
self.logger.info("Auth failed: %s (%s)", self.user, entity.getReason())
self.backend.handleDisconnected(self.user, 0, entity.getReason())
self.session.password = None
@ProtocolEntityCallback("receipt")
def onReceipt(self, entity):
self.logger.debug("received receipt, sending ack: " + str(entity.ack()))
ack = OutgoingAckProtocolEntity(entity.getId(), "receipt", entity.getType(), entity.getFrom())
self.toLower(ack)
@ProtocolEntityCallback("ack")
def onAck(self, entity):
self.logger.debug('received ack ' + str(entity))
@ProtocolEntityCallback("notification")
def onNotification(self, notification):
self.toLower(notification.ack())
def updateRoster(self):
self.logger.debug("Update roster")
old = self.session.buddies.keys()
self.session.buddies.load()
new = self.session.buddies.keys()
add = set(new) - set(old)
remove = set(old) - set(new)
self.logger.debug("Roster remove: %s", str(list(remove)))
self.logger.debug("Roster add: %s", str(list(add)))
for number in remove:
self.backend.handleBuddyChanged(self.user, number, "", [], protocol_pb2.STATUS_NONE)
self.backend.handleBuddyRemoved(self.user, number)
entity = UnsubscribePresenceProtocolEntity(number + "@s.whatsapp.net")
self.toLower(entity)
for number in add:
buddy = self.session.buddies[number]
entity = SubscribePresenceProtocolEntity(number + "@s.whatsapp.net")
self.toLower(entity)
@ProtocolEntityCallback("message")
def onMessageReceived(self, messageEntity):
buddy = messageEntity.getFrom().split('@')[0]
messageContent = utils.softToUni(messageEntity.getBody())
timestamp = messageEntity.getTimestamp()
receipt = OutgoingReceiptProtocolEntity(messageEntity.getId(),messageEntity.getFrom(), 'read', messageEntity.getParticipant())
self.toLower(receipt)
if messageEntity.isBroadcast():
self.logger.info("Broadcast received from %s to %s: %s (at ts=%s)",\
buddy, self.legacyName, messageContent, timestamp)
messageContent = "[Broadcast] " + messageContent
else:
self.logger.info("Message received from %s to %s: %s (at ts=%s)",
buddy, self.legacyName, messageContent, timestamp)
self.session.sendMessageToXMPP(buddy, messageContent, timestamp)
# if receiptRequested: self.call("message_ack", (jid, messageId))
@ProtocolEntityCallback("presence") @ProtocolEntityCallback("presence")
def onPrecenceUpdated(self, presence): def onPrecenceUpdated(self, presence):
jid = presence.getFrom() jid = presence.getFrom()

View file

@ -1,6 +1,8 @@
from yowsup import env from yowsup import env
from yowsup.stacks import YowStack from yowsup.stacks import YowStack
from yowsup.common import YowConstants
from yowsup.layers import YowLayerEvent, YowParallelLayer
# Layers # Layers
from yowsup.layers.auth import YowCryptLayer, YowAuthenticationProtocolLayer from yowsup.layers.auth import YowCryptLayer, YowAuthenticationProtocolLayer
@ -23,10 +25,15 @@ from yowsup.layers.protocol_privacy import YowPrivacyProtocolLayer
from yowsup.layers.protocol_profiles import YowProfilesProtocolLayer from yowsup.layers.protocol_profiles import YowProfilesProtocolLayer
from yowsup.layers.protocol_calls import YowCallsProtocolLayer from yowsup.layers.protocol_calls import YowCallsProtocolLayer
# ProtocolEntities
from yowsup.layers.protocol_presence.protocolentities import *
from yowsup.layers.protocol_messages.protocolentities import TextMessageProtocolEntity
from yowsup.layers.protocol_chatstate.protocolentities import *
from yowsup.layers.protocol_acks.protocolentities import *
class YowsupApp: class YowsupApp:
def __init__(self): def __init__(self):
self.logged_in = False
env.CURRENT_ENV = env.S40YowsupEnv() env.CURRENT_ENV = env.S40YowsupEnv()
layers = (SpectrumLayer, layers = (SpectrumLayer,
@ -57,6 +64,67 @@ class YowsupApp:
YowLayerEvent(YowsupAppLayer.EVENT_START, caller = self) YowLayerEvent(YowsupAppLayer.EVENT_START, caller = self)
) )
def login(self, username, password):
"""Login to yowsup
Should result in onAuthSuccess or onAuthFailure to be called.
Args:
- username: (str) username in the form of 1239482382 (country code
and cellphone number)
- password: (str) base64 encoded password
"""
self.stack.setProp(YowAuthenticationProtocolLayer.PROP_CREDENTIALS,
(username, password))
self.stack.setProp(YowNetworkLayer.PROP_ENDPOINT,
YowConstants.ENDPOINTS[0])
self.stack.setProp(YowCoderLayer.PROP_DOMAIN,
YowConstants.DOMAIN)
self.stack.setProp(YowCoderLayer.PROP_RESOURCE,
env.CURRENT_ENV.getResource())
# self.stack.setProp(YowIqProtocolLayer.PROP_PING_INTERVAL, 5)
try:
self.stack.broadcastEvent(
YowLayerEvent(YowNetworkLayer.EVENT_STATE_CONNECT))
except TypeError as e: # Occurs when password is not correctly formated
self.onAuthFailure('password not base64 encoded')
try:
self.stack.loop(timeout=0.5, discrete=0.5)
except AuthError as e: # For some reason Yowsup throws an exception
self.onAuthFailure("%s" % e)
def logout(self):
"""
Logout from whatsapp
"""
self.stack.broadcastEvent(YowLayerEvent(YowNetworkLayer.EVENT_STATE_DISCONNECT))
def sendReceipt(self, _id, _from, read, participant):
"""
Send a receipt (delivered: double-tick, read: blue-ticks)
Args:
- _id: id of message received
- _from
- read: ('read' or something else)
- participant
"""
receipt = OutgoingReceiptProtocolEntity(_id, _from, read, participant)
self.stack.toLower(receipt)
def sendTextMessage(self, to, message):
"""
Sends a text message
Args:
- to: (xxxxxxxxxx@s.whatsapp.net) who to send the message to
- message: (str) the body of the message
"""
messageEntity = TextMessageProtocolEntity(message, to = to)
self.stack.toLower(messageEntity)
def onAuthSuccess(self, status, kind, creation, expiration, props, nonce, t): def onAuthSuccess(self, status, kind, creation, expiration, props, nonce, t):
""" """
Called when login is successful. Called when login is successful.
@ -109,6 +177,11 @@ class YowsupApp:
""" """
pass pass
def onDisconnect(self):
"""
Called when disconnected from whatsapp
"""
from yowsup.layers.interface import YowInterfaceLayer, ProtocolEntityCallback from yowsup.layers.interface import YowInterfaceLayer, ProtocolEntityCallback
class YowsupAppLayer(YowInterfaceLayer): class YowsupAppLayer(YowInterfaceLayer):
@ -121,17 +194,21 @@ class YowsupAppLayer(YowInterfaceLayer):
# return True otherwise # return True otherwise
if layerEvent.getName() == YowsupAppLayer.EVENT_START: if layerEvent.getName() == YowsupAppLayer.EVENT_START:
self.caller = layerEvent.getArg('caller') self.caller = layerEvent.getArg('caller')
return True
elif layerEvent.getName() == YowNetworkLayer.EVENT_STATE_DISCONNECTED:
self.caller.onDisconnect()
return True
@ProtocolEntityCallback('success') @ProtocolEntityCallback('success')
def onAuthSuccess(self, entity): def onAuthSuccess(self, entity):
# entity is SuccessProtocolEntity # entity is SuccessProtocolEntity
status = entity.status status = entity.status
kind = entity.kind kind = entity.kind
creation = entity.creation creation = entity.creation
expiration = entity.expiration expiration = entity.expiration
props = entity.props props = entity.props
nonce = entity.nonce nonce = entity.nonce
t = entity.t # I don't know what this is t = entity.t # I don't know what this is
self.caller.onAuthSuccess(status, kind, creation, expiration, props, nonce, t) self.caller.onAuthSuccess(status, kind, creation, expiration, props, nonce, t)
@ProtocolEntityCallback('failure') @ProtocolEntityCallback('failure')
@ -150,8 +227,8 @@ class YowsupAppLayer(YowInterfaceLayer):
_id = entity._id _id = entity._id
_from = entity._from _from = entity._from
timestamp = entity.timestamp timestamp = entity.timestamp
type = entity.type type = entity.type
participant = entity.participant participant = entity.participant
offline = entity.offline offline = entity.offline
items = entity.items items = entity.items
self.caller.onReceipt(_id, _from, timestamp, type, participant, offline, items) self.caller.onReceipt(_id, _from, timestamp, type, participant, offline, items)