Browse Source

use spaces for indention as mandated by PEP-8

yowsup-3
Steffen Vogel 3 years ago
parent
commit
d022243f6c
  1. 100
      transWhat/bot.py
  2. 402
      transWhat/buddy.py
  3. 258
      transWhat/deferred.py
  4. 144
      transWhat/group.py
  5. 278
      transWhat/registersession.py
  6. 1624
      transWhat/session.py
  7. 24
      transWhat/threadutils.py
  8. 6
      transWhat/transwhat.py
  9. 272
      transWhat/whatsappbackend.py
  10. 1636
      transWhat/yowsupwrapper.py

100
transWhat/bot.py

@ -6,68 +6,68 @@ import time
import os
class Bot():
def __init__(self, session, name = "Bot"):
self.session = session
self.name = name
def __init__(self, session, name = "Bot"):
self.session = session
self.name = name
self.commands = {
"help": self._help,
"groups": self._groups,
"getgroups": self._getgroups
}
self.commands = {
"help": self._help,
"groups": self._groups,
"getgroups": self._getgroups
}
def parse(self, message):
args = message.strip().split(" ")
cmd = args.pop(0)
def parse(self, message):
args = message.strip().split(" ")
cmd = args.pop(0)
if len(cmd) > 0 and cmd[0] == '\\':
try:
self.call(cmd[1:], args)
except KeyError:
self.send("invalid command")
except TypeError:
self.send("invalid syntax")
else:
self.send("a valid command starts with a backslash")
if len(cmd) > 0 and cmd[0] == '\\':
try:
self.call(cmd[1:], args)
except KeyError:
self.send("invalid command")
except TypeError:
self.send("invalid syntax")
else:
self.send("a valid command starts with a backslash")
def call(self, cmd, args = []):
func = self.commands[cmd.lower()]
spec = inspect.getargspec(func)
maxs = len(spec.args) - 1
reqs = maxs - len(spec.defaults or [])
if (reqs > len(args)) or (len(args) > maxs):
raise TypeError()
def call(self, cmd, args = []):
func = self.commands[cmd.lower()]
spec = inspect.getargspec(func)
maxs = len(spec.args) - 1
reqs = maxs - len(spec.defaults or [])
if (reqs > len(args)) or (len(args) > maxs):
raise TypeError()
thread = threading.Thread(target=func, args=tuple(args))
thread.start()
thread = threading.Thread(target=func, args=tuple(args))
thread.start()
def send(self, message):
self.session.backend.handleMessage(self.session.user, self.name, message)
def send(self, message):
self.session.backend.handleMessage(self.session.user, self.name, message)
# commands
def _help(self):
self.send("""following bot commands are available:
\\help show this message
# commands
def _help(self):
self.send("""following bot commands are available:
\\help show this message
following user commands are available:
\\lastseen request last online timestamp from buddy
\\lastseen request last online timestamp from buddy
following group commands are available
\\leave permanently leave group chat
\\groups print all attended groups
\\getgroups get current groups from WA""")
\\leave permanently leave group chat
\\groups print all attended groups
\\getgroups get current groups from WA""")
def _groups(self):
for group in self.session.groups:
buddy = self.session.groups[group].owner
try:
nick = self.session.buddies[buddy].nick
except KeyError:
nick = buddy
def _groups(self):
for group in self.session.groups:
buddy = self.session.groups[group].owner
try:
nick = self.session.buddies[buddy].nick
except KeyError:
nick = buddy
self.send(self.session.groups[group].id + "@" + self.session.backend.spectrum_jid + " " + self.session.groups[group].subject + " Owner: " + nick )
self.send(self.session.groups[group].id + "@" + self.session.backend.spectrum_jid + " " + self.session.groups[group].subject + " Owner: " + nick )
def _getgroups(self):
#self.session.call("group_getGroups", ("participating",))
self.session.requestGroupsList(self.session._updateGroups)
def _getgroups(self):
#self.session.call("group_getGroups", ("participating",))
self.session.requestGroupsList(self.session._updateGroups)

402
transWhat/buddy.py

@ -7,207 +7,207 @@ import Spectrum2
from . import deferred
class Buddy():
def __init__(self, owner, number, nick, statusMsg, groups, image_hash):
self.nick = nick
self.owner = owner
self.number = "%s" % number
self.groups = groups
self.image_hash = image_hash if image_hash is not None else ""
self.statusMsg = u""
self.lastseen = 0
self.presence = 0
def update(self, nick, groups, image_hash):
self.nick = nick
self.groups = groups
if image_hash is not None:
self.image_hash = image_hash
def __str__(self):
# we must return str here
return str("%s (nick=%s)") % (self.number, self.nick)
def __init__(self, owner, number, nick, statusMsg, groups, image_hash):
self.nick = nick
self.owner = owner
self.number = "%s" % number
self.groups = groups
self.image_hash = image_hash if image_hash is not None else ""
self.statusMsg = u""
self.lastseen = 0
self.presence = 0
def update(self, nick, groups, image_hash):
self.nick = nick
self.groups = groups
if image_hash is not None:
self.image_hash = image_hash
def __str__(self):
# we must return str here
return str("%s (nick=%s)") % (self.number, self.nick)
class BuddyList(dict):
def __init__(self, owner, backend, user, session):
self.owner = owner
self.backend = backend
self.session = session
self.user = user
self.logger = logging.getLogger(self.__class__.__name__)
def _load(self, buddies):
for buddy in buddies:
number = buddy.buddyName
nick = buddy.alias
statusMsg = buddy.statusMessage
groups = [g for g in buddy.group]
image_hash = buddy.iconHash
self[number] = Buddy(self.owner, number, nick, statusMsg,
groups, image_hash)
self.logger.debug("Update roster")
contacts = self.keys()
contacts.remove('bot')
self.session.sendSync(contacts, delta=False, interactive=True,
success=self.onSync)
self.logger.debug("Roster add: %s" % list(contacts))
for number in contacts:
buddy = self[number]
self.updateSpectrum(buddy)
def onSync(self, existing, nonexisting, invalid):
"""We should only presence subscribe to existing numbers"""
for number in existing:
self.session.subscribePresence(number)
self.logger.debug("%s is requesting statuses of: %s" % (self.user, existing))
self.session.requestStatuses(existing, success = self.onStatus)
self.logger.debug("Removing nonexisting buddies %s" % nonexisting)
for number in nonexisting:
self.remove(number)
try: del self[number]
except KeyError: self.logger.warn("non-existing buddy really didn't exist: %s" % number)
self.logger.debug("Removing invalid buddies %s" % invalid)
for number in invalid:
self.remove(number)
try: del self[number]
except KeyError: self.logger.warn("non-existing buddy really didn't exist: %s" % number)
def onStatus(self, contacts):
self.logger.debug("%s received statuses of: %s" % (self.user, contacts))
for number, (status, time) in contacts.iteritems():
try: buddy = self[number]
except KeyError: self.logger.warn("received status of buddy not in list: %s" % number)
if status is None:
buddy.statusMsg = ""
else:
buddy.statusMsg = status
self.updateSpectrum(buddy)
def load(self, buddies):
if self.session.loggedIn:
self._load(buddies)
else:
self.session.loginQueue.append(lambda: self._load(buddies))
def update(self, number, nick, groups, image_hash):
if number in self:
buddy = self[number]
buddy.update(nick, groups, image_hash)
else:
buddy = Buddy(self.owner, number, nick, "", groups, image_hash)
self[number] = buddy
self.logger.debug("Roster add: %s" % buddy)
self.session.sendSync([number], delta = True, interactive = True)
self.session.subscribePresence(number)
self.session.requestStatuses([number], success = self.onStatus)
if image_hash == "" or image_hash is None:
self.requestVCard(number)
self.updateSpectrum(buddy)
return buddy
def updateSpectrum(self, buddy):
if buddy.presence == 0:
status = Spectrum2.protocol_pb2.STATUS_NONE
elif buddy.presence == 'unavailable':
status = Spectrum2.protocol_pb2.STATUS_AWAY
else:
status = Spectrum2.protocol_pb2.STATUS_ONLINE
statusmsg = buddy.statusMsg
if buddy.lastseen != 0:
timestamp = time.localtime(buddy.lastseen)
statusmsg += time.strftime("\n Last seen: %a, %d %b %Y %H:%M:%S", timestamp)
iconHash = buddy.image_hash if buddy.image_hash is not None else ""
self.logger.debug("Updating buddy %s (%s) in %s, image_hash = %s" %
(buddy.nick, buddy.number, buddy.groups, iconHash))
self.logger.debug("Status Message: %s" % statusmsg)
self.backend.handleBuddyChanged(self.user, buddy.number, buddy.nick,
buddy.groups, status, statusMessage=statusmsg, iconHash=iconHash)
def remove(self, number):
try:
buddy = self[number]
del self[number]
self.backend.handleBuddyChanged(self.user, number, "", [],
Spectrum2.protocol_pb2.STATUS_NONE)
self.backend.handleBuddyRemoved(self.user, number)
self.session.unsubscribePresence(number)
# TODO Sync remove
return buddy
except KeyError:
return None
def requestVCard(self, buddy, ID=None):
if "/" in buddy:
room, nick = buddy.split("/")
group = self.session.groups[room]
buddynr = None
for othernumber, othernick in group.participants.iteritems():
if othernick == nick:
buddynr = othernumber
break
if buddynr is None:
return
else:
buddynr = buddy
if buddynr == self.user or buddynr == self.user.split('@')[0]:
buddynr = self.session.legacyName
# Get profile picture
self.logger.debug('Requesting profile picture of %s' % buddynr)
response = deferred.Deferred()
# Error probably means image doesn't exist
error = deferred.Deferred()
self.session.requestProfilePicture(buddynr, onSuccess=response.run,
onFailure=error.run)
response = response.arg(0)
pictureData = response.pictureData()
# Send VCard
if ID != None:
deferred.call(self.logger.debug, 'Sending VCard (%s) with image id %s: %s' %
(ID, response.pictureId(), pictureData.then(base64.b64encode)))
deferred.call(self.backend.handleVCard, self.user, ID, buddy, "", "",
pictureData)
# If error
error.when(self.logger.debug, 'Sending VCard (%s) without image' % ID)
error.when(self.backend.handleVCard, self.user, ID, buddy, "", "", "")
# Send image hash
if not buddynr == self.session.legacyName:
try:
obuddy = self[buddynr]
nick = obuddy.nick
groups = obuddy.groups
except KeyError:
nick = ""
groups = []
def sha1hash(data):
hashlib.sha1(data).hexdigest()
image_hash = pictureData.then(sha1hash)
deferred.call(self.logger.debug, 'Image hash is %s' % image_hash)
deferred.call(self.update, buddynr, nick, groups, image_hash)
# No image
error.when(self.logger.debug, 'No image')
error.when(self.update, buddynr, nick, groups, '')
def refresh(self, number):
self.session.unsubscribePresence(number)
self.session.subscribePresence(number)
self.requestVCard(number)
self.session.requestStatuses([number], success = self.onStatus)
def __init__(self, owner, backend, user, session):
self.owner = owner
self.backend = backend
self.session = session
self.user = user
self.logger = logging.getLogger(self.__class__.__name__)
def _load(self, buddies):
for buddy in buddies:
number = buddy.buddyName
nick = buddy.alias
statusMsg = buddy.statusMessage
groups = [g for g in buddy.group]
image_hash = buddy.iconHash
self[number] = Buddy(self.owner, number, nick, statusMsg,
groups, image_hash)
self.logger.debug("Update roster")
contacts = self.keys()
contacts.remove('bot')
self.session.sendSync(contacts, delta=False, interactive=True,
success=self.onSync)
self.logger.debug("Roster add: %s" % list(contacts))
for number in contacts:
buddy = self[number]
self.updateSpectrum(buddy)
def onSync(self, existing, nonexisting, invalid):
"""We should only presence subscribe to existing numbers"""
for number in existing:
self.session.subscribePresence(number)
self.logger.debug("%s is requesting statuses of: %s" % (self.user, existing))
self.session.requestStatuses(existing, success = self.onStatus)
self.logger.debug("Removing nonexisting buddies %s" % nonexisting)
for number in nonexisting:
self.remove(number)
try: del self[number]
except KeyError: self.logger.warn("non-existing buddy really didn't exist: %s" % number)
self.logger.debug("Removing invalid buddies %s" % invalid)
for number in invalid:
self.remove(number)
try: del self[number]
except KeyError: self.logger.warn("non-existing buddy really didn't exist: %s" % number)
def onStatus(self, contacts):
self.logger.debug("%s received statuses of: %s" % (self.user, contacts))
for number, (status, time) in contacts.iteritems():
try: buddy = self[number]
except KeyError: self.logger.warn("received status of buddy not in list: %s" % number)
if status is None:
buddy.statusMsg = ""
else:
buddy.statusMsg = status
self.updateSpectrum(buddy)
def load(self, buddies):
if self.session.loggedIn:
self._load(buddies)
else:
self.session.loginQueue.append(lambda: self._load(buddies))
def update(self, number, nick, groups, image_hash):
if number in self:
buddy = self[number]
buddy.update(nick, groups, image_hash)
else:
buddy = Buddy(self.owner, number, nick, "", groups, image_hash)
self[number] = buddy
self.logger.debug("Roster add: %s" % buddy)
self.session.sendSync([number], delta = True, interactive = True)
self.session.subscribePresence(number)
self.session.requestStatuses([number], success = self.onStatus)
if image_hash == "" or image_hash is None:
self.requestVCard(number)
self.updateSpectrum(buddy)
return buddy
def updateSpectrum(self, buddy):
if buddy.presence == 0:
status = Spectrum2.protocol_pb2.STATUS_NONE
elif buddy.presence == 'unavailable':
status = Spectrum2.protocol_pb2.STATUS_AWAY
else:
status = Spectrum2.protocol_pb2.STATUS_ONLINE
statusmsg = buddy.statusMsg
if buddy.lastseen != 0:
timestamp = time.localtime(buddy.lastseen)
statusmsg += time.strftime("\n Last seen: %a, %d %b %Y %H:%M:%S", timestamp)
iconHash = buddy.image_hash if buddy.image_hash is not None else ""
self.logger.debug("Updating buddy %s (%s) in %s, image_hash = %s" %
(buddy.nick, buddy.number, buddy.groups, iconHash))
self.logger.debug("Status Message: %s" % statusmsg)
self.backend.handleBuddyChanged(self.user, buddy.number, buddy.nick,
buddy.groups, status, statusMessage=statusmsg, iconHash=iconHash)
def remove(self, number):
try:
buddy = self[number]
del self[number]
self.backend.handleBuddyChanged(self.user, number, "", [],
Spectrum2.protocol_pb2.STATUS_NONE)
self.backend.handleBuddyRemoved(self.user, number)
self.session.unsubscribePresence(number)
# TODO Sync remove
return buddy
except KeyError:
return None
def requestVCard(self, buddy, ID=None):
if "/" in buddy:
room, nick = buddy.split("/")
group = self.session.groups[room]
buddynr = None
for othernumber, othernick in group.participants.iteritems():
if othernick == nick:
buddynr = othernumber
break
if buddynr is None:
return
else:
buddynr = buddy
if buddynr == self.user or buddynr == self.user.split('@')[0]:
buddynr = self.session.legacyName
# Get profile picture
self.logger.debug('Requesting profile picture of %s' % buddynr)
response = deferred.Deferred()
# Error probably means image doesn't exist
error = deferred.Deferred()
self.session.requestProfilePicture(buddynr, onSuccess=response.run,
onFailure=error.run)
response = response.arg(0)
pictureData = response.pictureData()
# Send VCard
if ID != None:
deferred.call(self.logger.debug, 'Sending VCard (%s) with image id %s: %s' %
(ID, response.pictureId(), pictureData.then(base64.b64encode)))
deferred.call(self.backend.handleVCard, self.user, ID, buddy, "", "",
pictureData)
# If error
error.when(self.logger.debug, 'Sending VCard (%s) without image' % ID)
error.when(self.backend.handleVCard, self.user, ID, buddy, "", "", "")
# Send image hash
if not buddynr == self.session.legacyName:
try:
obuddy = self[buddynr]
nick = obuddy.nick
groups = obuddy.groups
except KeyError:
nick = ""
groups = []
def sha1hash(data):
hashlib.sha1(data).hexdigest()
image_hash = pictureData.then(sha1hash)
deferred.call(self.logger.debug, 'Image hash is %s' % image_hash)
deferred.call(self.update, buddynr, nick, groups, image_hash)
# No image
error.when(self.logger.debug, 'No image')
error.when(self.update, buddynr, nick, groups, '')
def refresh(self, number):
self.session.unsubscribePresence(number)
self.session.subscribePresence(number)
self.requestVCard(number)
self.session.requestStatuses([number], success = self.onStatus)

258
transWhat/deferred.py

@ -1,139 +1,139 @@
from functools import partial
class Deferred(object):
"""
Represents a delayed computation. This is a more elegant way to deal with
callbacks.
A Deferred object can be thought of as a computation whose value is yet to
be determined. We can manipulate the Deferred as if it where a regular
value by using the then method. Computations dependent on the Deferred will
only proceed when the run method is called.
Attributes of a Deferred can be accessed directly as methods. The result of
calling these functions will be Deferred.
Example:
image = Deferred()
getImageWithCallback(image.run)
image.then(displayFunc)
colors = Deferred()
colors.append('blue')
colors.then(print)
colors.run(['red', 'green']) #=> ['red', 'green', 'blue']
"""
def __init__(self):
self.subscribers = []
self.computed = False
self.args = None
self.kwargs = None
def run(self, *args, **kwargs):
"""
Give a value to the deferred. Calling this method more than once will
result in a DeferredHasValue exception to be raised.
"""
if self.computed:
raise DeferredHasValue("Deferred object already has a value.")
else:
self.args = args
self.kwargs = kwargs
for func, deferred in self.subscribers:
deferred.run(func(*args, **kwargs))
self.computed = True
def then(self, func):
"""
Apply func to Deferred value. Returns a Deferred whose value will be
the result of applying func.
"""
result = Deferred()
if self.computed:
result.run(func(*self.args, **self.kwargs))
else:
self.subscribers.append((func, result))
return result
def arg(self, n):
"""
Returns the nth positional argument of a deferred as a deferred
Args:
n - the index of the positional argument
"""
def helper(*args, **kwargs):
return args[n]
return self.then(helper)
def when(self, func, *args, **kwargs):
""" Calls when func(*args, **kwargs) when deferred gets a value """
def helper(*args2, **kwargs2):
func(*args, **kwargs)
return self.then(helper)
def __getattr__(self, method_name):
return getattr(Then(self), method_name)
"""
Represents a delayed computation. This is a more elegant way to deal with
callbacks.
A Deferred object can be thought of as a computation whose value is yet to
be determined. We can manipulate the Deferred as if it where a regular
value by using the then method. Computations dependent on the Deferred will
only proceed when the run method is called.
Attributes of a Deferred can be accessed directly as methods. The result of
calling these functions will be Deferred.
Example:
image = Deferred()
getImageWithCallback(image.run)
image.then(displayFunc)
colors = Deferred()
colors.append('blue')
colors.then(print)
colors.run(['red', 'green']) #=> ['red', 'green', 'blue']
"""
def __init__(self):
self.subscribers = []
self.computed = False
self.args = None
self.kwargs = None
def run(self, *args, **kwargs):
"""
Give a value to the deferred. Calling this method more than once will
result in a DeferredHasValue exception to be raised.
"""
if self.computed:
raise DeferredHasValue("Deferred object already has a value.")
else:
self.args = args
self.kwargs = kwargs
for func, deferred in self.subscribers:
deferred.run(func(*args, **kwargs))
self.computed = True
def then(self, func):
"""
Apply func to Deferred value. Returns a Deferred whose value will be
the result of applying func.
"""
result = Deferred()
if self.computed:
result.run(func(*self.args, **self.kwargs))
else:
self.subscribers.append((func, result))
return result
def arg(self, n):
"""
Returns the nth positional argument of a deferred as a deferred
Args:
n - the index of the positional argument
"""
def helper(*args, **kwargs):
return args[n]
return self.then(helper)
def when(self, func, *args, **kwargs):
""" Calls when func(*args, **kwargs) when deferred gets a value """
def helper(*args2, **kwargs2):
func(*args, **kwargs)
return self.then(helper)
def __getattr__(self, method_name):
return getattr(Then(self), method_name)
class Then(object):
"""
Allows you to call methods on a Deferred.
Example:
colors = Deferred()
Then(colors).append('blue')
colors.run(['red', 'green'])
colors.then(print) #=> ['red', 'green', 'blue']
"""
def __init__(self, deferred):
self.deferred = deferred
def __getattr__(self, name):
def tryCall(obj, *args, **kwargs):
if callable(obj):
return obj(*args, **kwargs)
else:
return obj
def helper(*args, **kwargs):
func = (lambda x: tryCall(getattr(x, name), *args, **kwargs))
return self.deferred.then(func)
return helper
"""
Allows you to call methods on a Deferred.
Example:
colors = Deferred()
Then(colors).append('blue')
colors.run(['red', 'green'])
colors.then(print) #=> ['red', 'green', 'blue']
"""
def __init__(self, deferred):
self.deferred = deferred
def __getattr__(self, name):
def tryCall(obj, *args, **kwargs):
if callable(obj):
return obj(*args, **kwargs)
else:
return obj
def helper(*args, **kwargs):
func = (lambda x: tryCall(getattr(x, name), *args, **kwargs))
return self.deferred.then(func)
return helper
def call(func, *args, **kwargs):
"""
Call a function with deferred arguments
Example:
colors = Deferred()
colors.append('blue')
colors.run(['red', 'green'])
call(print, colors) #=> ['red', 'green', 'blue']
call(print, 'hi', colors) #=> hi ['red', 'green', 'blue']
"""
for i, c in enumerate(args):
if isinstance(c, Deferred):
# Function without deferred arguments
normalfunc = partial(func, *args[:i])
# Function with deferred and possibly deferred arguments
def restfunc(*arg2, **kwarg2):
apply_deferred = partial(normalfunc, *arg2, **kwarg2)
return call(apply_deferred, *args[i + 1:], **kwargs)
return c.then(restfunc)
items = kwargs.items()
for i, (k, v) in enumerate(items):
if isinstance(v, Deferred):
# Function without deferred arguments
normalfunc = partial(func, *args, **dict(items[:i]))
# Function with deferred and possibly deferred arguments
def restfunc2(*arg2, **kwarg2):
apply_deferred = partial(normalfunc, *arg2, **kwarg2)
return call(apply_deferred, **dict(items[i + 1:]))
return v.then(restfunc2)
# No items deferred
return func(*args, **kwargs)
"""
Call a function with deferred arguments
Example:
colors = Deferred()
colors.append('blue')
colors.run(['red', 'green'])
call(print, colors) #=> ['red', 'green', 'blue']
call(print, 'hi', colors) #=> hi ['red', 'green', 'blue']
"""
for i, c in enumerate(args):
if isinstance(c, Deferred):
# Function without deferred arguments
normalfunc = partial(func, *args[:i])
# Function with deferred and possibly deferred arguments
def restfunc(*arg2, **kwarg2):
apply_deferred = partial(normalfunc, *arg2, **kwarg2)
return call(apply_deferred, *args[i + 1:], **kwargs)
return c.then(restfunc)
items = kwargs.items()
for i, (k, v) in enumerate(items):
if isinstance(v, Deferred):
# Function without deferred arguments
normalfunc = partial(func, *args, **dict(items[:i]))
# Function with deferred and possibly deferred arguments
def restfunc2(*arg2, **kwarg2):
apply_deferred = partial(normalfunc, *arg2, **kwarg2)
return call(apply_deferred, **dict(items[i + 1:]))
return v.then(restfunc2)
# No items deferred
return func(*args, **kwargs)
class DeferredHasValue(Exception):
def __init__(self, string):
super(DeferredHasValue, self).__init__(string)
def __init__(self, string):
super(DeferredHasValue, self).__init__(string)

144
transWhat/group.py

@ -2,83 +2,83 @@ import Spectrum2
class Group():
def __init__(self, id, owner, subject, subjectOwner, backend, user):
self.id = id
self.subject = subject
self.subjectOwner = subjectOwner
self.owner = owner
self.joined = False
self.backend = backend
self.user = user
def __init__(self, id, owner, subject, subjectOwner, backend, user):
self.id = id
self.subject = subject
self.subjectOwner = subjectOwner
self.owner = owner
self.joined = False
self.backend = backend
self.user = user
self.nick = "me"
# Participants is a number -> nickname dict
self.participants = {}
self.nick = "me"
# Participants is a number -> nickname dict
self.participants = {}
def addParticipants(self, participants, buddies, yourNumber):
"""
Adds participants to the group.
def addParticipants(self, participants, buddies, yourNumber):
"""
Adds participants to the group.
Args:
- participants: (Iterable) phone numbers of participants
- buddies: (dict) Used to get the nicknames of the participants
- yourNumber: The number you are using
"""
for jid in participants:
number = jid.split('@')[0]
try:
nick = buddies[number].nick
except KeyError:
nick = number
if number == yourNumber:
nick = self.nick
if nick == "":
nick = number
self.participants[number] = nick
Args:
- participants: (Iterable) phone numbers of participants
- buddies: (dict) Used to get the nicknames of the participants
- yourNumber: The number you are using
"""
for jid in participants:
number = jid.split('@')[0]
try:
nick = buddies[number].nick
except KeyError:
nick = number
if number == yourNumber:
nick = self.nick
if nick == "":
nick = number
self.participants[number] = nick
def sendParticipantsToSpectrum(self, yourNumber):
for number, nick in self.participants.iteritems():
if number == self.owner:
flags = Spectrum2.protocol_pb2.PARTICIPANT_FLAG_MODERATOR
else:
flags = Spectrum2.protocol_pb2.PARTICIPANT_FLAG_NONE
if number == yourNumber:
flags = flags | Spectrum2.protocol_pb2.PARTICIPANT_FLAG_ME
try:
self._updateParticipant(number, flags, Spectrum2.protocol_pb2.STATUS_ONLINE,
self.backend.sessions[self.user].buddies[number].image_hash)
except KeyError:
self._updateParticipant(number, flags, Spectrum2.protocol_pb2.STATUS_ONLINE)
def sendParticipantsToSpectrum(self, yourNumber):
for number, nick in self.participants.iteritems():
if number == self.owner:
flags = Spectrum2.protocol_pb2.PARTICIPANT_FLAG_MODERATOR
else:
flags = Spectrum2.protocol_pb2.PARTICIPANT_FLAG_NONE
if number == yourNumber:
flags = flags | Spectrum2.protocol_pb2.PARTICIPANT_FLAG_ME
try:
self._updateParticipant(number, flags, Spectrum2.protocol_pb2.STATUS_ONLINE,
self.backend.sessions[self.user].buddies[number].image_hash)
except KeyError:
self._updateParticipant(number, flags, Spectrum2.protocol_pb2.STATUS_ONLINE)
def removeParticipants(self, participants):
for jid in participants:
number = jid.split('@')[0]
nick = self.participants[number]
flags = Spectrum2.protocol_pb2.PARTICIPANT_FLAG_NONE
self._updateParticipant(number, flags, Spectrum2.protocol_pb2.STATUS_NONE)
del self.participants[number]
def removeParticipants(self, participants):
for jid in participants:
number = jid.split('@')[0]
nick = self.participants[number]
flags = Spectrum2.protocol_pb2.PARTICIPANT_FLAG_NONE
self._updateParticipant(number, flags, Spectrum2.protocol_pb2.STATUS_NONE)
del self.participants[number]
def leaveRoom(self):
for number in self.participants:
nick = self.participants[number]
flags = Spectrum2.protocol_pb2.PARTICIPANT_FLAG_ROOM_NOT_FOUND
self._updateParticipant(number, flags, Spectrum2.protocol_pb2.STATUS_NONE)
def leaveRoom(self):
for number in self.participants:
nick = self.participants[number]
flags = Spectrum2.protocol_pb2.PARTICIPANT_FLAG_ROOM_NOT_FOUND
self._updateParticipant(number, flags, Spectrum2.protocol_pb2.STATUS_NONE)
def changeNick(self, number, new_nick):
if self.participants[number] == new_nick:
return
if number == self.owner:
flags = Spectrum2.protocol_pb2.PARTICIPANT_FLAG_MODERATOR
else:
flags = Spectrum2.protocol_pb2.PARTICIPANT_FLAG_NONE
self._updateParticipant(number, flags, Spectrum2.protocol_pb2.STATUS_ONLINE, new_nick)
self.participants[number] = new_nick
def changeNick(self, number, new_nick):
if self.participants[number] == new_nick:
return
if number == self.owner:
flags = Spectrum2.protocol_pb2.PARTICIPANT_FLAG_MODERATOR
else:
flags = Spectrum2.protocol_pb2.PARTICIPANT_FLAG_NONE
self._updateParticipant(number, flags, Spectrum2.protocol_pb2.STATUS_ONLINE, new_nick)
self.participants[number] = new_nick
def _updateParticipant(self, number, flags, status, imageHash = "", newNick = ""):
nick = self.participants[number]
# Notice the status message is the buddy's number
if self.joined:
self.backend.handleParticipantChanged(
self.user, nick, self.id, flags,
status, number, newname = newNick, iconHash = imageHash)
def _updateParticipant(self, number, flags, status, imageHash = "", newNick = ""):
nick = self.participants[number]
# Notice the status message is the buddy's number
if self.joined:
self.backend.handleParticipantChanged(
self.user, nick, self.id, flags,
status, number, newname = newNick, iconHash = imageHash)

278
transWhat/registersession.py

@ -8,142 +8,142 @@ from . import threadutils
class RegisterSession(YowsupApp):
"""
A dummy Session object that is used to register a user to whatsapp
"""
WANT_CC = 0
WANT_SMS = 1
def __init__(self, backend, user, legacyName, extra):
self.user = user
self.number = legacyName
self.backend = backend
self.countryCode = ''
self.logger = logging.getLogger(self.__class__.__name__)
self.state = self.WANT_CC
def login(self, password=""):
self.backend.handleConnected(self.user)
self.backend.handleBuddyChanged(self.user, 'bot', 'bot',
['Admin'], Spectrum2.protocol_pb2.STATUS_ONLINE)
self.backend.handleMessage(self.user, 'bot',
'Please enter your country code')
def sendMessageToWA(self, buddy, message, ID='', xhtml=''):
if buddy == 'bot' and self.state == self.WANT_CC:
try:
country_code = int(message.strip())
except ValueError:
self.backend.handleMessage(self.user, 'bot',
'Country code must be a number')
else: # Succeded in decoding country code
country_code = "%s" % country_code
if country_code != self.number[:len(country_code)]:
self.backend.handleMessage(self.user,
'bot', 'Number does not start with provided country code')
else:
self.backend.handleMessage(self.user, 'bot', 'Requesting sms code')
self.logger.debug('Requesting SMS code for %s' % self.user)
self.countryCode = country_code
self._requestSMSCodeNonBlock()
elif buddy == 'bot' and self.state == self.WANT_SMS:
code = message.strip()
if self._checkSMSFormat(code):
self._requestPassword(code)
else:
self.backend.handleMessage(self.user,
'bot', 'Invalid code. Must be of the form XXX-XXX.')
else:
self.logger.warn('Unauthorised user (%s) attempting to send messages' %
self.user)
self.backend.handleMessage(self.user, buddy,
'You are not logged in yet. You can only send messages to bot.')
def _checkSMSFormat(self, sms):
splitting = sms.split('-')
if len(splitting) != 2:
return False
a, b = splitting
if len(a) != 3 and len(b) != 3:
return False
try:
int(a)
int(b)
except ValueError:
return False
return True
def _requestSMSCodeNonBlock(self):
number = self.number[len(self.countryCode):]
threadFunc = lambda: self.requestSMSCode(self.countryCode, number)
threadutils.runInThread(threadFunc, self._confirmation)
self.backend.handleMessage(self.user, 'bot', 'SMS Code Sent')
def _confirmation(self, result):
self.state = self.WANT_SMS
resultStr = self._resultToString(result)
self.backend.handleMessage(self.user, 'bot', 'Response:')
self.backend.handleMessage(self.user, 'bot', resultStr)
self.backend.handleMessage(self.user, 'bot', 'Please enter SMS Code')
def _requestPassword(self, smsCode):
cc = self.countryCode
number = self.number[len(cc):]
threadFunc = lambda: self.requestPassword(cc, number, smsCode)
threadutils.runInThread(threadFunc, self._gotPassword)
self.backend.handleMessage(self.user, 'bot', 'Getting Password')
def _gotPassword(self, result):
resultStr = self._resultToString(result)
self.backend.handleMessage(self.user, 'bot', 'Response:')
self.backend.handleMessage(self.user, 'bot', resultStr)
self.backend.handleMessage(self.user, 'bot', 'Logging you in')
password = result['pw']
self.backend.relogin(self.user, self.number, password, None)
def _resultToString(self, result):
unistr = str if sys.version_info >= (3, 0) else unicode
out = []
for k, v in result.items():
if v is None:
continue
out.append("%s: %s" % (k, v))
return "\n".join(out)
# Dummy methods. Whatsapp backend might call these, but they should have no
# effect
def logout(self):
pass
def joinRoom(self, room, nickname):
pass
def leaveRoom(self, room):
pass
def changeStatusMessage(self, statusMessage):
pass
def changeStatus(self, status):
pass
def loadBuddies(self, buddies):
pass
def updateBuddy(self, buddies):
pass
def removeBuddy(self, buddies):
pass
def sendTypingStarted(self, buddy):
pass
def sendTypingStopped(self, buddy):
pass
def requestVCard(self, buddy, ID):
pass
def setProfilePicture(self, previewPicture, fullPicture = None):
pass
"""
A dummy Session object that is used to register a user to whatsapp
"""
WANT_CC = 0
WANT_SMS = 1
def __init__(self, backend, user, legacyName, extra):
self.user = user
self.number = legacyName
self.backend = backend
self.countryCode = ''
self.logger = logging.getLogger(self.__class__.__name__)
self.state = self.WANT_CC
def login(self, password=""):
self.backend.handleConnected(self.user)
self.backend.handleBuddyChanged(self.user, 'bot', 'bot',
['Admin'], Spectrum2.protocol_pb2.STATUS_ONLINE)
self.backend.handleMessage(self.user, 'bot',
'Please enter your country code')
def sendMessageToWA(self, buddy, message, ID='', xhtml=''):
if buddy == 'bot' and self.state == self.WANT_CC:
try:
country_code = int(message.strip())
except ValueError:
self.backend.handleMessage(self.user, 'bot',
'Country code must be a number')
else: # Succeded in decoding country code
country_code = "%s" % country_code
if country_code != self.number[:len(country_code)]:
self.backend.handleMessage(self.user,
'bot', 'Number does not start with provided country code')
else:
self.backend.handleMessage(self.user, 'bot', 'Requesting sms code')
self.logger.debug('Requesting SMS code for %s' % self.user)
self.countryCode = country_code
self._requestSMSCodeNonBlock()
elif buddy == 'bot' and self.state == self.WANT_SMS:
code = message.strip()
if self._checkSMSFormat(code):
self._requestPassword(code)
else:
self.backend.handleMessage(self.user,
'bot', 'Invalid code. Must be of the form XXX-XXX.')
else:
self.logger.warn('Unauthorised user (%s) attempting to send messages' %
self.user)
self.backend.handleMessage(self.user, buddy,
'You are not logged in yet. You can only send messages to bot.')
def _checkSMSFormat(self, sms):
splitting = sms.split('-')
if len(splitting) != 2:
return False
a, b = splitting
if len(a) != 3 and len(b) != 3:
return False
try:
int(a)
int(b)
except ValueError:
return False
return True
def _requestSMSCodeNonBlock(self):
number = self.number[len(self.countryCode):]
threadFunc = lambda: self.requestSMSCode(self.countryCode, number)
threadutils.runInThread(threadFunc, self._confirmation)
self.backend.handleMessage(self.user, 'bot', 'SMS Code Sent')
def _confirmation(self, result):
self.state = self.WANT_SMS
resultStr = self._resultToString(result)
self.backend.handleMessage(self.user, 'bot', 'Response:')
self.backend.handleMessage(self.user, 'bot', resultStr)
self.backend.handleMessage(self.user, 'bot', 'Please enter SMS Code')
def _requestPassword(self, smsCode):
cc = self.countryCode
number = self.number[len(cc):]
threadFunc = lambda: self.requestPassword(cc, number, smsCode)
threadutils.runInThread(threadFunc, self._gotPassword)
self.backend.handleMessage(self.user, 'bot', 'Getting Password')
def _gotPassword(self, result):
resultStr = self._resultToString(result)
self.backend.handleMessage(self.user, 'bot', 'Response:')
self.backend.handleMessage(self.user, 'bot', resultStr)
self.backend.handleMessage(self.user, 'bot', 'Logging you in')
password = result['pw']
self.backend.relogin(self.user, self.number, password, None)
def _resultToString(self, result):
unistr = str if sys.version_info >= (3, 0) else unicode
out = []
for k, v in result.items():
if v is None:
continue
out.append("%s: %s" % (k, v))
return "\n".join(out)
# Dummy methods. Whatsapp backend might call these, but they should have no
# effect
def logout(self):
pass
def joinRoom(self, room, nickname):
pass
def leaveRoom(self, room):
pass
def changeStatusMessage(self, statusMessage):
pass
def changeStatus(self, status):
pass
def loadBuddies(self, buddies):
pass
def updateBuddy(self, buddies):
pass
def removeBuddy(self, buddies):
pass
def sendTypingStarted(self, buddy):
pass
def sendTypingStopped(self, buddy):
pass
def requestVCard(self, buddy, ID):
pass
def setProfilePicture(self, previewPicture, fullPicture = None):
pass

1624
transWhat/session.py

File diff suppressed because it is too large

24
transWhat/threadutils.py

@ -5,16 +5,16 @@ import threading
eventQueue = queue.Queue()
def runInThread(threadFunc, callback):
"""
Executes threadFunc in a new thread. The result of threadFunc will be
pass as the first argument to callback. callback will be called in the main
thread.
"""
def helper():
# Execute threadfunc in new thread
result = threadFunc()
# Queue callback to be call in main thread
eventQueue.put(lambda: callback(result))
"""
Executes threadFunc in a new thread. The result of threadFunc will be
pass as the first argument to callback. callback will be called in the main
thread.
"""
def helper():
# Execute threadfunc in new thread
result = threadFunc()
# Queue callback to be call in main thread
eventQueue.put(lambda: callback(result))
thread = threading.Thread(target=helper)
thread.start()
thread = threading.Thread(target=helper)
thread.start()

6
transWhat/transwhat.py

@ -21,8 +21,8 @@ parser.add_argument('--log', type=str)
parser.add_argument('--host', type=str, required=True)
parser.add_argument('--port', type=int, required=True)
parser.add_argument('--service.backend_id', metavar="ID", type=int, required=True)
parser.add_argument('config', type=str)
parser.add_argument('-j', type=str, metavar="JID", required=True)
parser.add_argument('config', type=str)
args, unknown = parser.parse_known_args()
@ -75,14 +75,16 @@ def main():
try:
asyncore.loop(timeout=1.0, count=10, use_poll = True)
try:
callback = YowStack._YowStack__detachedQueue.get(False) #doesn't block
callback = YowStack._YowStack__detachedQueue.get(False) # doesn't block
callback()
except queue.Empty:
pass
else:
break
if closed:
break
while True:
try:
callback = threadutils.eventQueue.get_nowait()

272
transWhat/whatsappbackend.py

@ -5,140 +5,140 @@ from .session import Session
from .registersession import RegisterSession
class WhatsAppBackend(Spectrum2.Backend):
def __init__(self, io, spectrum_jid, specConf):
Spectrum2.Backend.__init__(self)
self.logger = logging.getLogger(self.__class__.__name__)
self.io = io
self.specConf = specConf
self.sessions = { }
self.spectrum_jid = spectrum_jid
# Used to prevent duplicate messages
self.lastMsgId = {}
self.logger.debug("Backend started")
# RequestsHandlers
def handleLoginRequest(self, user, legacyName, password, extra):
self.logger.debug("handleLoginRequest(user=%s, legacyName=%s)" % (user, legacyName))
# Key word means we should register a new password
if password == 'register':
if user not in self.sessions:
self.sessions[user] = RegisterSession(self, user, legacyName, extra)
else:
if user not in self.sessions:
self.sessions[user] = Session(self, user, legacyName, extra)
self.sessions[user].login(password)
def handleLogoutRequest(self, user, legacyName):
self.logger.debug("handleLogoutRequest(user=%s, legacyName=%s)" % (user, legacyName))
if user in self.sessions:
self.sessions[user].logout()
del self.sessions[user]
def handleMessageSendRequest(self, user, buddy, message, xhtml="", ID=""):
self.logger.debug("handleMessageSendRequest(user=%s, buddy=%s, message=%s, xhtml=%s, ID=%s)" %
( user, buddy, message, xhtml, ID))
# For some reason spectrum occasionally sends to identical messages to
# a buddy, one to the bare jid and one to the /bot resource. This
# causes duplicate messages. Thus we should not send consecutive
# messages with the same id
if ID == '':
self.sessions[user].sendMessageToWA(buddy, message, ID, xhtml)
elif user not in self.lastMsgId or self.lastMsgId[user] != ID:
self.sessions[user].sendMessageToWA(buddy, message, ID, xhtml)
self.lastMsgId[user] = ID
def handleJoinRoomRequest(self, user, room, nickname, pasword):
self.logger.debug("handleJoinRoomRequest(user=%s, room=%s, nickname=%s)" % (user, room, nickname))
self.sessions[user].joinRoom(room, nickname)
def handleLeaveRoomRequest(self, user, room):
self.logger.debug("handleLeaveRoomRequest(user=%s, room=%s)" % (user, room))
self.sessions[user].leaveRoom(room)
def handleStatusChangeRequest(self, user, status, statusMessage):
self.logger.debug("handleStatusChangeRequest(user=%s, status=%d, statusMessage=%s)" % (user, status, statusMessage))
self.sessions[user].changeStatusMessage(statusMessage)
self.sessions[user].changeStatus(status)
def handleBuddies(self, buddies):
"""Called when user logs in. Used to initialize roster."""
self.logger.debug("handleBuddies(buddies=%s)" % buddies)
buddies = [b for b in buddies.buddy]
if len(buddies) > 0:
user = buddies[0].userName
self.sessions[user].loadBuddies(buddies)
def handleBuddyUpdatedRequest(self, user, buddy, nick, groups):
self.logger.debug("handleBuddyUpdatedRequest(user=%s, buddy=%s, nick=%s, groups=%s)" % (user, buddy, nick, groups))
self.sessions[user].updateBuddy(buddy, nick, groups)
def handleBuddyRemovedRequest(self, user, buddy, groups):
self.logger.debug("handleBuddyRemovedRequest(user=%s, buddy=%s, groups=%s)" % (user, buddy, groups))
self.sessions[user].removeBuddy(buddy)
def handleTypingRequest(self, user, buddy):
self.logger.debug("handleTypingRequest(user=%s, buddy=%s)" % (user, buddy))
self.sessions[user].sendTypingStarted(buddy)
def handleTypedRequest(self, user, buddy):
self.logger.debug("handleTypedRequest(user=%s, buddy=%s)" % (user, buddy))
self.sessions[user].sendTypingStopped(buddy)
def handleStoppedTypingRequest(self, user, buddy):
self.logger.debug("handleStoppedTypingRequest(user=%s, buddy=%s)" % (user, buddy))
self.sessions[user].sendTypingStopped(buddy)
def handleVCardRequest(self, user, buddy, ID):
self.logger.debug("handleVCardRequest(user=%s, buddy=%s, ID=%s)" % (user, buddy, ID))
self.sessions[user].requestVCard(buddy, ID)
def handleVCardUpdatedRequest(self, user, photo, nickname):
self.logger