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

@ -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,6 +194,10 @@ 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):