Merge branch 'yowsup-2' into setuptools

This commit is contained in:
Steffen Vogel 2017-02-11 16:15:04 -03:00
commit 766c40c790
15 changed files with 645 additions and 358 deletions

View File

@ -6,7 +6,6 @@ transWhat is a WhatsApp XMPP Gateway based on [Spectrum 2](http://www.spectrum.i
- [yowsup-1](http://github.com/stv0g/transwhat/tree/yowsup-1) My original version which is based on @tgalal first Yowsup version (**deprecated** and broken).
- [yowsup-2](http://github.com/stv0g/transwhat/tree/yowsup-2) Major rewrite from @moyamo for @tgalal's new Yowsup 2 (**recommended**).
- [develop](http://github.com/stv0g/transwhat/tree/develop) A develop branch with the latest fixes and improvements. This branch is based on `yowsup-2`.
For production, please use the `yowsup-2` branch.

View File

@ -1,3 +1,29 @@
__author__ = "Steffen Vogel"
__copyright__ = "Copyright 2015-2017, Steffen Vogel"
__license__ = "GPLv3"
__maintainer__ = "Steffen Vogel"
__email__ = "post@steffenvogel.de"
"""
This file is part of transWhat
transWhat is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
any later version.
transwhat is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with transWhat. If not, see <http://www.gnu.org/licenses/>.
"""
# use unicode encoding for all literals by default (for python2.x)
from __future__ import unicode_literals
import protocol_pb2
import socket
import struct
@ -11,7 +37,7 @@ import resource
def WRAP(MESSAGE, TYPE):
wrap = protocol_pb2.WrapperMessage()
wrap.type = TYPE
wrap.payload = MESSAGE
wrap.payload = bytes(MESSAGE)
return wrap.SerializeToString()
class SpectrumBackend:
@ -24,7 +50,7 @@ class SpectrumBackend:
def __init__(self):
self.m_pingReceived = False
self.m_data = ""
self.m_data = bytes("")
self.m_init_res = 0
self.logger = logging.getLogger(self.__class__.__name__)
@ -67,7 +93,7 @@ class SpectrumBackend:
vcard.id = ID
vcard.fullname = fullName
vcard.nickname = nickname
vcard.photo = photo
vcard.photo = bytes(photo)
message = WRAP(vcard.SerializeToString(), protocol_pb2.WrapperMessage.TYPE_VCARD)
self.send(message)
@ -220,7 +246,7 @@ class SpectrumBackend:
def handleFTData(self, ftID, data):
d = protocol_pb2.FileTransferData()
d.ftid = ftID
d.data = data
d.data = bytes(data)
message = WRAP(d.SerializeToString(), protocol_pb2.WrapperMessage.TYPE_FT_DATA);
self.send(message)
@ -277,13 +303,11 @@ class SpectrumBackend:
self.handleMessageSendRequest(payload.userName, payload.buddyName, payload.message, payload.xhtml, payload.id)
def handleConvMessageAckPayload(self, data):
payload = protocol_pb2.ConversationMessage()
if (payload.ParseFromString(data) == False):
#TODO: ERROR
return
self.handleMessageAckRequest(payload.userName, payload.buddyName, payload.id)
payload = protocol_pb2.ConversationMessage()
if (payload.ParseFromString(data) == False):
#TODO: ERROR
return
self.handleMessageAckRequest(payload.userName, payload.buddyName, payload.id)
def handleAttentionPayload(self, data):
payload = protocol_pb2.ConversationMessage()
@ -452,7 +476,7 @@ class SpectrumBackend:
elif wrapper.type == protocol_pb2.WrapperMessage.TYPE_EXIT:
self.handleExitRequest()
elif wrapper.type == protocol_pb2.WrapperMessage.TYPE_CONV_MESSAGE_ACK:
self.handleConvMessageAckPayload(wrapper.payload)
self.handleConvMessageAckPayload(wrapper.payload)
elif wrapper.type == protocol_pb2.WrapperMessage.TYPE_RAW_XML:
self.handleRawXmlRequest(wrapper.payload)
elif wrapper.type == protocol_pb2.WrapperMessage.TYPE_BUDDIES:
@ -543,14 +567,14 @@ class SpectrumBackend:
raise NotImplementedError, "Implement me"
def handleMessageAckRequest(self, user, legacyName, ID = 0):
"""
Called when XMPP user sends message to legacy network.
@param user: XMPP JID of user for which this event occurs.
@param legacyName: Legacy network name of buddy or room.
@param ID: message ID
"""
"""
Called when XMPP user sends message to legacy network.
@param user: XMPP JID of user for which this event occurs.
@param legacyName: Legacy network name of buddy or room.
@param ID: message ID
"""
# raise NotImplementedError, "Implement me"
# raise NotImplementedError, "Implement me"
pass

View File

@ -1,3 +1,29 @@
__author__ = "Steffen Vogel"
__copyright__ = "Copyright 2015-2017, Steffen Vogel"
__license__ = "GPLv3"
__maintainer__ = "Steffen Vogel"
__email__ = "post@steffenvogel.de"
"""
This file is part of transWhat
transWhat is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
any later version.
transwhat is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with transWhat. If not, see <http://www.gnu.org/licenses/>.
"""
# use unicode encoding for all literals by default (for python2.x)
from __future__ import unicode_literals
import asyncore, socket
import logging
import sys
@ -12,7 +38,7 @@ class IOChannel(asyncore.dispatcher):
self.callback = callback
self.closeCallback = closeCallback
self.buffer = ""
self.buffer = bytes("")
def sendData(self, data):
self.buffer += data

141
config.py Normal file
View File

@ -0,0 +1,141 @@
__author__ = "Steffen Vogel"
__copyright__ = "Copyright 2015-2017, Steffen Vogel"
__license__ = "GPLv3"
__maintainer__ = "Steffen Vogel"
__email__ = "post@steffenvogel.de"
"""
This file is part of transWhat
transWhat is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
any later version.
transwhat is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with transWhat. If not, see <http://www.gnu.org/licenses/>.
"""
# I'm guessing this is the format of the spectrum config file in BNF
# <config_file> ::= <line>*
# <line> ::= <space>* <expr> <space>* <newline> | <space*>
# <expr> ::= <section> | <assignment>
# <section> ::= [<identifier>*]
# <assignment> ::= <identifier> <space>* = <space>* <value>
class SpectrumConfig:
"""
Represents spectrum2 configuration options.
"""
def __init__(self, path_to_config_file):
"""
Initialises configuration file.
Args:
path_to_config_file: The absolute path to the configuration file.
"""
self.config_path = path_to_config_file
self.options = self.loadConfig(self.config_path)
# Load backend_logging information
self.options.update(self.loadConfig(self['logging.backend_config']))
def loadConfig(self, file_name):
section = {'a': ""} # Current section heading,
# It's a dictionary because variables in python closures can't be
# assigned to.
options = dict()
# Recursive descent parser
def consume_spaces(line):
i = 0
for c in line:
if c != ' ':
break
i += 1
return line[i:]
def read_identifier(line):
i = 0
for c in line:
if c == ' ' or c==']' or c=='[' or c=='=':
break
i += 1
# no identifier
if i == 0:
return (None, 'No identifier')
return (line[:i], line[i:])
def parse_section(line):
if len(line) == 0 or line[0] != '[':
return (None, 'expected [')
line = line[1:]
identifier, line = read_identifier(line)
if len(line) == 0 or line[0] != ']' or identifier is None:
return (None, line)
return (identifier, line[1:])
def parse_assignment(line):
key, line = read_identifier(line)
if key is None:
return (None, None, line)
line = consume_spaces(line)
if len(line) == 0 or line[0] != '=':
return (None, None, 'Expected =')
line = consume_spaces(line[1:])
value = line[:-1]
return (key, value, '\n')
def expr(line):
sec, newline = parse_section(line)
if sec is not None:
section['a'] = sec
else:
key, value, newline = parse_assignment(line)
if key is not None:
if section['a'] != '':
options[section['a'] + '.' + key] = value
else:
options[key] = value
else:
return (None, newline)
return (newline, None)
def parse_line(line, line_number):
line = consume_spaces(line)
if line == '\n':
return
newline, error = expr(line)
if newline is None:
raise ConfigParseError(str(line_number) + ': ' + error + ': ' + repr(line))
newline = consume_spaces(newline)
if newline != '\n':
raise ConfigParseError(str(line_number) + ': Expected newline got ' + repr(newline))
def strip_comments(line):
i = 0
for c in line:
if c == '#' or c == '\n':
break
i += 1
return line[:i] + '\n'
with open(file_name, 'r') as f:
i = 1
while True:
line = f.readline()
if line == '':
break
parse_line(strip_comments(line), i)
i += 1
return options
def __getitem__(self, key):
return self.options[key]
class ConfigParseError(Exception):
pass

View File

@ -1,5 +1,5 @@
__author__ = "Steffen Vogel"
__copyright__ = "Copyright 2015, Steffen Vogel"
__copyright__ = "Copyright 2015-2017, Steffen Vogel"
__license__ = "GPLv3"
__maintainer__ = "Steffen Vogel"
__email__ = "post@steffenvogel.de"
@ -21,6 +21,9 @@ __email__ = "post@steffenvogel.de"
along with transWhat. If not, see <http://www.gnu.org/licenses/>.
"""
# use unicode encoding for all literals by default (for python2.x)
from __future__ import unicode_literals
import threading
import inspect
import re
@ -37,7 +40,7 @@ class Bot():
self.commands = {
"help": self._help,
"prune": self._prune,
"groups": self._groups,
"groups": self._groups,
"getgroups": self._getgroups
}
@ -92,9 +95,9 @@ following group commands are available
for group in self.session.groups:
buddy = self.session.groups[group].owner
try:
nick = self.session.buddies[buddy].nick
except KeyError:
nick = buddy
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 )

View File

@ -1,5 +1,5 @@
__author__ = "Steffen Vogel"
__copyright__ = "Copyright 2015, Steffen Vogel"
__copyright__ = "Copyright 2015-2017, Steffen Vogel"
__license__ = "GPLv3"
__maintainer__ = "Steffen Vogel"
__email__ = "post@steffenvogel.de"
@ -21,6 +21,9 @@ __email__ = "post@steffenvogel.de"
along with transWhat. If not, see <http://www.gnu.org/licenses/>.
"""
# use unicode encoding for all literals by default (for python2.x)
from __future__ import unicode_literals
from Spectrum2 import protocol_pb2
import logging
@ -36,7 +39,7 @@ class Buddy():
def __init__(self, owner, number, nick, statusMsg, groups, image_hash):
self.nick = nick
self.owner = owner
self.number = number
self.number = "%s" % number
self.groups = groups
self.image_hash = image_hash if image_hash is not None else ""
self.statusMsg = u""
@ -50,7 +53,8 @@ class Buddy():
self.image_hash = image_hash
def __str__(self):
return "%s (nick=%s)" % (self.number, self.nick)
# we must return str here
return str("%s (nick=%s)") % (self.number, self.nick)
class BuddyList(dict):
@ -65,7 +69,7 @@ class BuddyList(dict):
for buddy in buddies:
number = buddy.buddyName
nick = buddy.alias
statusMsg = buddy.statusMessage.decode('utf-8')
statusMsg = buddy.statusMessage
groups = [g for g in buddy.group]
image_hash = buddy.iconHash
self[number] = Buddy(self.owner, number, nick, statusMsg,
@ -79,7 +83,7 @@ class BuddyList(dict):
self.session.sendSync(contacts, delta=False, interactive=True,
success=self.onSync)
self.logger.debug("Roster add: %s", str(list(contacts)))
self.logger.debug("Roster add: %s" % list(contacts))
for number in contacts:
buddy = self[number]
@ -90,23 +94,27 @@ class BuddyList(dict):
for number in existing:
self.session.subscribePresence(number)
self.logger.debug("%s is requesting statuses of: %s", self.user, existing)
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)
self.logger.debug("Removing nonexisting buddies %s" % nonexisting)
for number in nonexisting:
self.remove(number)
del self[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)
self.logger.debug("Removing invalid buddies %s" % invalid)
for number in invalid:
self.remove(number)
del self[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)
self.logger.debug("%s received statuses of: %s" % (self.user, contacts))
for number, (status, time) in contacts.iteritems():
buddy = self[number]
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:
@ -127,7 +135,7 @@ class BuddyList(dict):
else:
buddy = Buddy(self.owner, number, nick, "", groups, image_hash)
self[number] = buddy
self.logger.debug("Roster add: %s", 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)
@ -151,9 +159,9 @@ class BuddyList(dict):
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.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)
@ -190,7 +198,7 @@ class BuddyList(dict):
buddynr = self.session.legacyName
# Get profile picture
self.logger.debug('Requesting profile picture of %s', buddynr)
self.logger.debug('Requesting profile picture of %s' % buddynr)
response = deferred.Deferred()
# Error probably means image doesn't exist
error = deferred.Deferred()
@ -201,12 +209,12 @@ class BuddyList(dict):
pictureData = response.pictureData()
# Send VCard
if ID != None:
call(self.logger.debug, 'Sending VCard (%s) with image id %s: %s',
ID, response.pictureId(), pictureData.then(base64.b64encode))
call(self.logger.debug, 'Sending VCard (%s) with image id %s: %s' %
(ID, response.pictureId(), pictureData.then(base64.b64encode)))
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.logger.debug, 'Sending VCard (%s) without image' % ID)
error.when(self.backend.handleVCard, self.user, ID, buddy, "", "", "")
# Send image hash
@ -219,7 +227,7 @@ class BuddyList(dict):
nick = ""
groups = []
image_hash = pictureData.then(utils.sha1hash)
call(self.logger.debug, 'Image hash is %s', image_hash)
call(self.logger.debug, 'Image hash is %s' % image_hash)
call(self.update, buddynr, nick, groups, image_hash)
# No image
error.when(self.logger.debug, 'No image')

View File

@ -1,3 +1,29 @@
__author__ = "Steffen Vogel"
__copyright__ = "Copyright 2015-2017, Steffen Vogel"
__license__ = "GPLv3"
__maintainer__ = "Steffen Vogel"
__email__ = "post@steffenvogel.de"
"""
This file is part of transWhat
transWhat is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
any later version.
transwhat is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with transWhat. If not, see <http://www.gnu.org/licenses/>.
"""
# use unicode encoding for all literals by default (for python2.x)
from __future__ import unicode_literals
from functools import partial
class Deferred(object):

View File

@ -1,5 +1,5 @@
__author__ = "Steffen Vogel"
__copyright__ = "Copyright 2015, Steffen Vogel"
__copyright__ = "Copyright 2015-2017, Steffen Vogel"
__license__ = "GPLv3"
__maintainer__ = "Steffen Vogel"
__email__ = "post@steffenvogel.de"
@ -21,6 +21,9 @@ __email__ = "post@steffenvogel.de"
along with transWhat. If not, see <http://www.gnu.org/licenses/>.
"""
# use unicode encoding for all literals by default (for python2.x)
from __future__ import unicode_literals
from Spectrum2 import protocol_pb2
class Group():

View File

@ -1,3 +1,28 @@
__author__ = "Steffen Vogel"
__copyright__ = "Copyright 2015-2017, Steffen Vogel"
__license__ = "GPLv3"
__maintainer__ = "Steffen Vogel"
__email__ = "post@steffenvogel.de"
"""
This file is part of transWhat
transWhat is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
any later version.
transwhat is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with transWhat. If not, see <http://www.gnu.org/licenses/>.
"""
# use unicode encoding for all literals by default (for python2.x)
from __future__ import unicode_literals
from Spectrum2 import protocol_pb2
from yowsupwrapper import YowsupApp
@ -34,13 +59,13 @@ class RegisterSession(YowsupApp):
self.backend.handleMessage(self.user, 'bot',
'Country code must be a number')
else: # Succeded in decoding country code
country_code = str(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.logger.debug('Requesting SMS code for %s' % self.user)
self.countryCode = country_code
self._requestSMSCodeNonBlock()
elif buddy == 'bot' and self.state == self.WANT_SMS:
@ -51,7 +76,7 @@ class RegisterSession(YowsupApp):
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.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.')
@ -104,7 +129,7 @@ class RegisterSession(YowsupApp):
for k, v in result.items():
if v is None:
continue
out.append("%s: %s" %(k, v.encode("utf-8") if type(v) is unistr else v))
out.append("%s: %s" % (k, v))
return "\n".join(out)

View File

@ -1,5 +1,5 @@
__author__ = "Steffen Vogel"
__copyright__ = "Copyright 2015, Steffen Vogel"
__copyright__ = "Copyright 2015-2017, Steffen Vogel"
__license__ = "GPLv3"
__maintainer__ = "Steffen Vogel"
__email__ = "post@steffenvogel.de"
@ -21,12 +21,15 @@ __email__ = "post@steffenvogel.de"
along with transWhat. If not, see <http://www.gnu.org/licenses/>.
"""
# use unicode encoding for all literals by default (for python2.x)
from __future__ import unicode_literals
import utils
import logging
import urllib
import time
from PIL import Image
# from PIL import Image
import sys
import os
@ -45,21 +48,18 @@ from yowsupwrapper import YowsupApp
class MsgIDs:
def __init__(self, xmppId, waId):
self.xmppId = xmppId
self.waId = waId
self.cnt = 0
def __init__(self, xmppId, waId):
self.xmppId = xmppId
self.waId = waId
self.cnt = 0
class Session(YowsupApp):
broadcast_prefix = u'\U0001F4E2 '
broadcast_prefix = '\U0001F4E2 '
def __init__(self, backend, user, legacyName, extra):
super(Session, self).__init__()
self.logger = logging.getLogger(self.__class__.__name__)
self.logger.info("Created: %s", legacyName)
self.logger.info("Created: %s" % legacyName)
self.backend = backend
self.user = user
@ -99,12 +99,12 @@ class Session(YowsupApp):
self.logout()
def logout(self):
self.logger.info("%s logged out", self.user)
self.logger.info("%s logged out" % self.user)
super(Session, self).logout()
self.loggedIn = False
def login(self, password):
self.logger.info("%s attempting login", self.user)
self.logger.info("%s attempting login" % self.user)
self.password = password
self.shouldBeConncted = True
super(Session, self).login(self.legacyName, self.password)
@ -126,14 +126,14 @@ class Session(YowsupApp):
rooms.append([self._shortenGroupId(room), group.subject])
text.append(self._shortenGroupId(room) + '@' + self.backend.spectrum_jid + ' :' + group.subject)
self.logger.debug("Got rooms: %s", rooms)
self.logger.debug("Got rooms: %s" % rooms)
self.backend.handleRoomList(rooms)
message = "Note, you are a participant of the following groups:\n" +\
'\n'.join(text) + '\nIf you do not join them you will lose messages'
message = "Note, you are a participant of the following groups:\n" + \
"\n".join(text) + "\nIf you do not join them you will lose messages"
#self.bot.send(message)
def _updateGroups(self, response, request):
self.logger.debug('Received groups list %s', response)
self.logger.debug('Received groups list %s' % response)
groups = response.getGroups()
for group in groups:
room = group.getId()
@ -159,8 +159,8 @@ class Session(YowsupApp):
msg = self.groupOfflineQueue[room].pop(0)
self.backend.handleMessage(self.user, room, msg[1],
msg[0], "", msg[2])
self.logger.debug("Send queued group message to: %s %s %s",
msg[0],msg[1], msg[2])
self.logger.debug("Send queued group message to: %s %s %s" %
(msg[0],msg[1], msg[2]))
self.gotGroupList = True
for room, nick in self.joinRoomQueue:
self.joinRoom(room, nick)
@ -173,8 +173,8 @@ class Session(YowsupApp):
return
room = self._lengthenGroupId(room)
if room in self.groups:
self.logger.info("Joining room: %s room=%s, nick=%s",
self.legacyName, room, nick)
self.logger.info("Joining room: %s room=%s, nick=%s" %
(self.legacyName, room, nick))
group = self.groups[room]
group.joined = True
@ -188,43 +188,43 @@ class Session(YowsupApp):
group.sendParticipantsToSpectrum(self.legacyName)
self.backend.handleSubject(self.user, self._shortenGroupId(room),
group.subject, ownerNick)
self.logger.debug("Room subject: room=%s, subject=%s",
room, group.subject)
self.logger.debug("Room subject: room=%s, subject=%s" %
(room, group.subject))
self.backend.handleRoomNicknameChanged(
self.user, self._shortenGroupId(room), group.subject
)
else:
self.logger.warn("Room doesn't exist: %s", room)
self.logger.warn("Room doesn't exist: %s" % room)
def leaveRoom(self, room):
if room in self.groups:
self.logger.info("Leaving room: %s room=%s", self.legacyName, room)
self.logger.info("Leaving room: %s room=%s" % (self.legacyName, room))
group = self.groups[room]
group.joined = False
else:
self.logger.warn("Room doesn't exist: %s. Unable to leave.", room)
self.logger.warn("Room doesn't exist: %s. Unable to leave." % room)
def _lastSeen(self, number, seconds):
self.logger.debug("Last seen %s at %s seconds" % (number, str(seconds)))
self.logger.debug("Last seen %s at %s seconds" % (number, seconds))
if seconds < 60:
self.onPresenceAvailable(number)
else:
self.onPresenceUnavailable(number)
def sendReadReceipts(self, buddy):
for _id, _from, participant in self.recvMsgIDs:
for _id, _from, participant, t in self.recvMsgIDs:
if _from.split('@')[0] == buddy:
self.sendReceipt(_id, _from, 'read', participant)
self.recvMsgIDs.remove((_id, _from, participant))
self.recvMsgIDs.remove((_id, _from, participant, t))
self.logger.debug("Send read receipt to %s (ID: %s)", _from, _id)
# Called by superclass
def onAuthSuccess(self, status, kind, creation,
expiration, props, nonce, t):
self.logger.info("Auth success: %s", self.user)
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)
["Admin"], protocol_pb2.STATUS_ONLINE)
# Initialisation?
self.requestPrivacyList()
self.requestClientConfig()
@ -249,7 +249,7 @@ class Session(YowsupApp):
# Called by superclass
def onAuthFailed(self, reason):
self.logger.info("Auth failed: %s (%s)", self.user, reason)
self.logger.info("Auth failed: %s (%s)" % (self.user, reason))
self.backend.handleDisconnected(self.user, 0, reason)
self.password = None
self.loggedIn = False
@ -261,9 +261,8 @@ class Session(YowsupApp):
# 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]))
self.logger.debug("received receipt, sending ack: %s" %
[ _id, _from, timestamp, type, participant, offline, items ]
)
try:
number = _from.split('@')[0]
@ -272,29 +271,22 @@ class Session(YowsupApp):
if self.msgIDs[_id].cnt == 2:
del self.msgIDs[_id]
except KeyError:
self.logger.error("Message %s not found. Unable to send ack", _id)
self.logger.error("Message %s not found. Unable to send ack" % _id)
# Called by superclass
def onAck(self, _id, _class, _from, timestamp):
self.logger.debug('received ack ' +
' '.join(map(str, [_id, _class, _from,timestamp,]))
)
self.logger.debug('received ack: %s' % [ _id, _class, _from, timestamp ])
# Called by superclass
def onTextMessage(self, _id, _from, to, notify, timestamp, participant,
offline, retry, body):
self.logger.debug('received TextMessage' +
' '.join(map(str, [
_id, _from, to, notify, timestamp,
participant, offline, retry, body
]))
)
buddy = _from.split('@')[0]
messageContent = utils.softToUni(body)
self.sendReceipt(_id, _from, None, participant)
self.recvMsgIDs.append((_id, _from, participant))
self.logger.info("Message received from %s to %s: %s (at ts=%s)",
buddy, self.legacyName, messageContent, timestamp)
self.recvMsgIDs.append((_id, _from, participant, timestamp))
self.logger.info("Message received from %s to %s: %s (at ts=%s)" %
(buddy, self.legacyName, messageContent, timestamp))
if participant is not None: # Group message or broadcast
partname = participant.split('@')[0]
if _from.split('@')[1] == 'broadcast': # Broadcast message
@ -310,32 +302,48 @@ class Session(YowsupApp):
# Called by superclass
def onImage(self, image):
self.logger.debug('Received image message %s', str(image))
self.logger.debug('Received image message: %s' % image)
buddy = image._from.split('@')[0]
participant = image.participant
if image.caption is None:
image.caption = ''
if image.isEncrypted():
self.logger.debug('Received encrypted image message')
if self.backend.specConf is not None and self.backend.specConf.__getitem__("service.web_directory") is not None and self.backend.specConf.__getitem__("service.web_url") is not None :
ipath = "/" + str(image.timestamp) + image.getExtension()
with open(self.backend.specConf.__getitem__("service.web_directory") + ipath,"wb") as f:
f.write(image.getMediaContent())
url = self.backend.specConf.__getitem__("service.web_url") + ipath
else:
self.logger.warn('Received encrypted image: web storage not set in config!')
url = image.url
else:
url = image.url
if participant is not None: # Group message
partname = participant.split('@')[0]
if image._from.split('@')[1] == 'broadcast': # Broadcast message
self.sendMessageToXMPP(partname, self.broadcast_prefix, image.timestamp)
self.sendMessageToXMPP(partname, image.url, image.timestamp)
self.sendMessageToXMPP(partname, url, image.timestamp)
self.sendMessageToXMPP(partname, image.caption, image.timestamp)
else: # Group message
self.sendGroupMessageToXMPP(buddy, partname, image.url, image.timestamp)
self.sendGroupMessageToXMPP(buddy, partname, url, image.timestamp)
self.sendGroupMessageToXMPP(buddy, partname, image.caption, image.timestamp)
else:
self.sendMessageToXMPP(buddy, image.url, image.timestamp)
self.sendMessageToXMPP(buddy, url, image.timestamp)
self.sendMessageToXMPP(buddy, image.caption, image.timestamp)
self.sendReceipt(image._id, image._from, None, image.participant)
self.recvMsgIDs.append((image._id, image._from, image.participant))
self.recvMsgIDs.append((image._id, image._from, image.participant, image.timestamp))
# Called by superclass
def onAudio(self, audio):
self.logger.debug('Received audio message %s', str(audio))
self.logger.debug('Received audio message: %s' % audio)
buddy = audio._from.split('@')[0]
participant = audio.participant
participant = audio.participant
message = audio.url
if participant is not None: # Group message
partname = participant.split('@')[0]
@ -347,14 +355,14 @@ class Session(YowsupApp):
else:
self.sendMessageToXMPP(buddy, message, audio.timestamp)
self.sendReceipt(audio._id, audio._from, None, audio.participant)
self.recvMsgIDs.append((audio._id, audio._from, audio.participant))
self.recvMsgIDs.append((audio._id, audio._from, audio.participant, audio.timestamp))
# Called by superclass
def onVideo(self, video):
self.logger.debug('Received video message %s', str(video))
self.logger.debug('Received video message: %s' % video)
buddy = video._from.split('@')[0]
participant = video.participant
participant = video.participant
message = video.url
if participant is not None: # Group message
@ -367,7 +375,7 @@ class Session(YowsupApp):
else:
self.sendMessageToXMPP(buddy, message, video.timestamp)
self.sendReceipt(video._id, video._from, None, video.participant)
self.recvMsgIDs.append((video._id, video._from, video.participant))
self.recvMsgIDs.append((video._id, video._from, video.participant, video.timestamp))
def onLocation(self, location):
@ -375,12 +383,10 @@ class Session(YowsupApp):
latitude = location.getLatitude()
longitude = location.getLongitude()
url = location.getLocationURL()
participant = location.participant
participant = location.participant
latlong = 'geo:' + latitude + ',' + longitude
self.logger.debug("Location received from %s: %s, %s",
buddy, latitude, longitude)
self.logger.debug("Location received from %s: %s, %s", (buddy, latitude, longitude))
if participant is not None: # Group message
partname = participant.split('@')[0]
@ -398,16 +404,14 @@ class Session(YowsupApp):
self.sendMessageToXMPP(buddy, url, location.timestamp)
self.sendMessageToXMPP(buddy, latlong, location.timestamp)
self.sendReceipt(location._id, location._from, None, location.participant)
self.recvMsgIDs.append((loaction._id, location._from, location.participant))
self.recvMsgIDs.append((location._id, location._from, location.participant, location.timestamp))
# Called by superclass
def onVCard(self, _id, _from, name, card_data, to, notify, timestamp, participant):
self.logger.debug('received VCard' +
' '.join(map(str, [
_id, _from, name, card_data, to, notify, timestamp, participant
]))
self.logger.debug('received VCard: %s' %
[ _id, _from, name, card_data, to, notify, timestamp, participant ]
)
message = "Received VCard (not implemented yet)"
buddy = _from.split("@")[0]
@ -423,19 +427,19 @@ class Session(YowsupApp):
# self.sendMessageToXMPP(buddy, card_data)
#self.transferFile(buddy, str(name), card_data)
self.sendReceipt(_id, _from, None, participant)
self.recvMsgIDs.append((_id, _from, participant))
self.recvMsgIDs.append((_id, _from, participant, timestamp))
def transferFile(self, buddy, name, data):
# Not working
self.logger.debug('transfering file %s', name)
self.logger.debug('transfering file: %s' % name)
self.backend.handleFTStart(self.user, buddy, name, len(data))
self.backend.handleFTData(0, data)
self.backend.handleFTFinish(self.user, buddy, name, len(data), 0)
# Called by superclass
def onContactTyping(self, buddy):
self.logger.info("Started typing: %s", buddy)
self.logger.info("Started typing: %s" % buddy)
if buddy != 'bot':
self.sendPresence(True)
self.backend.handleBuddyTyping(self.user, buddy)
@ -445,7 +449,7 @@ class Session(YowsupApp):
# Called by superclass
def onContactPaused(self, buddy):
self.logger.info("Paused typing: %s", buddy)
self.logger.info("Paused typing: %s" % buddy)
if buddy != 'bot':
self.backend.handleBuddyTyped(self.user, buddy)
self.timer = Timer(3, self.backend.handleBuddyStoppedTyping,
@ -453,20 +457,20 @@ class Session(YowsupApp):
# Called by superclass
def onAddedToGroup(self, group):
self.logger.debug("Added to group: %s", group)
self.logger.debug("Added to group: %s" % group)
room = group.getGroupId()
owner = group.getCreatorJid(full = False)
subjectOwner = group.getSubjectOwnerJid(full = False)
subject = utils.softToUni(group.getSubject())
self.groups[room] = Group(room, owner, subject, subjectOwner, self.backend, self.user)
self.groups[room].addParticipants(group.getParticipants, self.buddies, self.legacyName)
self.groups[room].addParticipants(group.getParticipants(), self.buddies, self.legacyName)
self.bot.send("You have been added to group: %s@%s (%s)"
% (self._shortenGroupId(room), subject, self.backend.spectrum_jid))
# Called by superclass
def onParticipantsAddedToGroup(self, group):
self.logger.debug("Participants added to group: %s", group)
self.logger.debug("Participants added to group: %s" % group)
room = group.getGroupId().split('@')[0]
self.groups[room].addParticipants(group.getParticipants(), self.buddies, self.legacyName)
self.groups[room].sendParticipantsToSpectrum(self.legacyName)
@ -474,12 +478,13 @@ class Session(YowsupApp):
# Called by superclass
def onSubjectChanged(self, room, subject, subjectOwner, timestamp):
self.logger.debug(
"onSubjectChange(rrom=%s, subject=%s, subjectOwner=%s, timestamp=%s)",
room, subject, subjectOwner, timestamp)
"onSubjectChange(rrom=%s, subject=%s, subjectOwner=%s, timestamp=%s)" %
(room, subject, subjectOwner, timestamp)
)
try:
group = self.groups[room]
except KeyError:
self.logger.error("Subject of non-existant group (%s) changed", group)
self.logger.error("Subject of non-existant group (%s) changed" % group)
else:
group.subject = subject
group.subjectOwner = subjectOwner
@ -491,46 +496,46 @@ class Session(YowsupApp):
# Called by superclass
def onParticipantsRemovedFromGroup(self, room, participants):
self.logger.debug("Participants removed from group: %s, %s",
room, participants)
self.logger.debug("Participants removed from group: %s, %s" %
(room, participants))
self.groups[room].removeParticipants(participants)
# Called by superclass
def onContactStatusChanged(self, number, status):
self.logger.debug("%s changed their status to %s", number, status)
self.logger.debug("%s changed their status to %s" % (number, status))
try:
buddy = self.buddies[number]
buddy.statusMsg = status
self.buddies.updateSpectrum(buddy)
except KeyError:
self.logger.debug("%s not in buddy list", number)
self.logger.debug("%s not in buddy list" % number)
# Called by superclass
def onContactPictureChanged(self, number):
self.logger.debug("%s changed their profile picture", number)
self.logger.debug("%s changed their profile picture" % number)
self.buddies.requestVCard(number)
# Called by superclass
def onContactAdded(self, number, nick):
self.logger.debug("Adding new contact %s (%s)", nick, number)
self.logger.debug("Adding new contact %s (%s)" % (nick, number))
self.updateBuddy(number, nick, [])
# Called by superclass
def onContactRemoved(self, number):
self.logger.debug("Removing contact %s", number)
self.logger.debug("Removing contact %s" % number)
self.removeBuddy(number)
def onContactUpdated(self, oldnumber, newnumber):
self.logger.debug("Contact has changed number from %s to %s",
oldnumber, newnumber)
self.logger.debug("Contact has changed number from %s to %s" %
(oldnumber, newnumber))
if newnumber in self.buddies:
self.logger.warn("Contact %s exists, just updating", newnumber)
self.logger.warn("Contact %s exists, just updating" % newnumber)
self.buddies.refresh(newnumber)
try:
buddy = self.buddies[oldnumber]
except KeyError:
self.logger.warn("Old contact (%s) not found. Adding new contact (%s)",
oldnumber, newnumber)
self.logger.warn("Old contact (%s) not found. Adding new contact (%s)" %
(oldnumber, newnumber))
nick = ""
else:
self.removeBuddy(buddy.number)
@ -538,17 +543,17 @@ class Session(YowsupApp):
self.updateBuddy(newnumber, nick, [])
def onPresenceReceived(self, _type, name, jid, lastseen):
self.logger.info("Presence received: %s %s %s %s", _type, name, jid, lastseen)
self.logger.info("Presence received: %s %s %s %s" % (_type, name, jid, lastseen))
buddy = jid.split("@")[0]
try:
buddy = self.buddies[buddy]
except KeyError:
# Sometimes whatsapp send our own presence
if buddy != self.legacyName:
self.logger.error("Buddy not found: %s", buddy)
self.logger.error("Buddy not found: %s" % buddy)
return
if (lastseen == str(buddy.lastseen)) and (_type == buddy.presence):
if (lastseen == buddy.lastseen) and (_type == buddy.presence):
return
if ((lastseen != "deny") and (lastseen != None) and (lastseen != "none")):
@ -563,20 +568,18 @@ class Session(YowsupApp):
else:
self.onPresenceAvailable(buddy)
def onPresenceAvailable(self, buddy):
self.logger.info("Is available: %s", buddy)
self.logger.info("Is available: %s" % buddy)
self.buddies.updateSpectrum(buddy)
def onPresenceUnavailable(self, buddy):
self.logger.info("Is unavailable: %s", buddy)
self.logger.info("Is unavailable: %s" % buddy)
self.buddies.updateSpectrum(buddy)
# spectrum RequestMethods
def sendTypingStarted(self, buddy):
if buddy != "bot":
self.logger.info("Started typing: %s to %s", self.legacyName, buddy)
self.logger.info("Started typing: %s to %s" % (self.legacyName, buddy))
self.sendTyping(buddy, True)
self.sendReadReceipts(buddy)
# If he is typing he is present
@ -586,7 +589,7 @@ class Session(YowsupApp):
def sendTypingStopped(self, buddy):
if buddy != "bot":
self.logger.info("Stopped typing: %s to %s", self.legacyName, buddy)
self.logger.info("Stopped typing: %s to %s" % (self.legacyName, buddy))
self.sendTyping(buddy, False)
self.sendReadReceipts(buddy)
@ -602,7 +605,7 @@ class Session(YowsupApp):
# Success
path = success.arg(0)
call(self.logger.info, "Success: Image downloaded to %s", path)
call(self.logger.info, "Success: Image downloaded to %s" % path)
pathWithExt = path.then(lambda p: p + "." + imgType)
call(os.rename, path, pathWithExt)
pathJpg = path.then(lambda p: p + ".jpg")
@ -610,7 +613,7 @@ class Session(YowsupApp):
im = call(Image.open, pathWithExt)
call(im.save, pathJpg)
call(os.remove, pathWithExt)
call(self.logger.info, "Sending image to %s", to)
call(self.logger.info, "Sending image to %s" % to)
waId = deferred.Deferred()
call(super(Session, self).sendImage, to, pathJpg, onSuccess = waId.run)
call(self.setWaId, ID, waId)
@ -626,10 +629,9 @@ class Session(YowsupApp):
self.msgIDs[waId] = MsgIDs(XmppId, waId)
def sendMessageToWA(self, sender, message, ID, xhtml=""):
self.logger.info("Message sent from %s to %s: %s (xhtml=%s)",
self.legacyName, sender, message, xhtml)
self.logger.info("Message sent from %s to %s: %s (xhtml=%s)" %
(self.legacyName, sender, message, xhtml))
message = message.encode("utf-8")
self.sendReadReceipts(sender)
if sender == "bot":
@ -644,27 +646,27 @@ class Session(YowsupApp):
number = othernumber
break
if number is not None:
self.logger.debug("Private message sent from %s to %s", self.legacyName, number)
self.logger.debug("Private message sent from %s to %s" % (self.legacyName, number))
waId = self.sendTextMessage(number + '@s.whatsapp.net', message)
self.msgIDs[waId] = MsgIDs( ID, waId)
else:
self.logger.error("Attempted to send private message to non-existent user")
self.logger.debug("%s to %s in %s", self.legacyName, nick, room)
self.logger.debug("%s to %s in %s" % (self.legacyName, nick, room))
else:
room = sender
if message[0] == '\\' and message[:1] != '\\\\':
self.logger.debug("Executing command %s in %s", message, room)
self.logger.debug("Executing command %s in %s" % (message, room))
self.executeCommand(message, room)
else:
try:
group = self.groups[self._lengthenGroupId(room)]
self.logger.debug("Group Message from %s to %s Groups: %s",
group.nick , group , self.groups)
self.logger.debug("Group Message from %s to %s Groups: %s" %
(group.nick , group , self.groups))
self.backend.handleMessage(
self.user, room, message.decode('utf-8'), group.nick, xhtml=xhtml
self.user, room, message, group.nick, xhtml=xhtml
)
except KeyError:
self.logger.error('Group not found: %s', room)
self.logger.error('Group not found: %s' % room)
if (".jpg" in message.lower()) or (".webp" in message.lower()):
self.sendImage(message, ID, room + '@g.us')
@ -674,82 +676,68 @@ class Session(YowsupApp):
self.sendTextMessage(room + '@g.us', message)
else: # private msg
buddy = sender
# if message == "\\lastseen":
# self.call("presence_request", buddy = (buddy + "@s.whatsapp.net",))
# else:
if message.split(" ").pop(0) == "\\lastseen":
self.presenceRequested.append(buddy)
#self.call("presence_request", (buddy + "@s.whatsapp.net",))
self._requestLastSeen(buddy)
elif message.split(" ").pop(0) == "\\gpp":
self.logger.info("Get Profile Picture! ")
self.sendMessageToXMPP(buddy, "Fetching Profile Picture")
#self.call("contact_getProfilePicture", (buddy + "@s.whatsapp.net",))
self.requestVCard(buddy)
else:
if (".jpg" in message.lower()) or (".webp" in message.lower()):
self.sendImage(message, ID, buddy + "@s.whatsapp.net")
elif "geo:" in message.lower():
self._sendLocation(buddy + "@s.whatsapp.net", message, ID)
else:
waId = self.sendTextMessage(sender + '@s.whatsapp.net', message)
self.msgIDs[waId] = MsgIDs( ID, waId)
self.presenceRequested.append(buddy)
self._requestLastSeen(buddy)
elif message.split(" ").pop(0) == "\\gpp":
self.sendMessageToXMPP(buddy, "Fetching Profile Picture")
self.requestVCard(buddy)
elif (".jpg" in message.lower()) or (".webp" in message.lower()):
self.sendImage(message, ID, buddy + "@s.whatsapp.net")
elif "geo:" in message.lower():
self._sendLocation(buddy + "@s.whatsapp.net", message, ID)
else:
waId = self.sendTextMessage(sender + '@s.whatsapp.net', message)
self.msgIDs[waId] = MsgIDs(ID, waId)
self.logger.info("WA Message send to %s with ID %s", buddy, waId)
#self.sendTextMessage(sender + '@s.whatsapp.net', message)
self.logger.info("WA Message send to %s with ID %s", buddy, waId)
def executeCommand(self, command, room):
if command == '\\leave':
self.logger.debug("Leaving room %s", room)
# Leave group on whatsapp side
self.leaveGroup(room)
# Delete Room on spectrum side
self.leaveGroup(room) # Leave group on whatsapp side
group = self.groups[room]
group.leaveRoom()
group.leaveRoom() # Delete Room on spectrum side
del self.groups[room]
def _requestLastSeen(self, buddy):
def onSuccess(buddy, lastseen):
def onSuccess(buddy, lastseen):
timestamp = time.localtime(time.localtime()-lastseen)
timestring = time.strftime("%a, %d %b %Y %H:%M:%S", timestamp)
self.sendMessageToXMPP(buddy, "%s (%s) %s" % (timestring, utils.ago(lastseen),str(lastseen)))
def onError(errorIqEntity, originalIqEntity):
self.sendMessageToXMPP(errorIqEntity.getFrom(), "LastSeen Error")
timestring = time.strftime("%a, %d %b %Y %H:%M:%S", timestamp)
self.sendMessageToXMPP(buddy, "%s (%s) %s" % (timestring, utils.ago(lastseen), str(lastseen)))
def onError(errorIqEntity, originalIqEntity):
self.sendMessageToXMPP(errorIqEntity.getFrom(), "LastSeen Error")
self.requestLastSeen(buddy, onSuccess, onError)
def _sendLocation(self, buddy, message, ID):
#with open('/opt/transwhat/map.jpg', 'rb') as imageFile:
# raw = base64.b64encode(imageFile.read())
latitude,longitude = message.split(':')[1].split(',')
waId = self.sendLocation(buddy, float(latitude), float(longitude))
self.msgIDs[waId] = MsgIDs( ID, waId)
self.logger.info("WA Location Message send to %s with ID %s", buddy, waId)
latitude,longitude = message.split(':')[1].split(',')
waId = self.sendLocation(buddy, float(latitude), float(longitude))
self.msgIDs[waId] = MsgIDs(ID, waId)
self.logger.info("WA Location Message send to %s with ID %s", buddy, waId)
def sendMessageToXMPP(self, buddy, messageContent, timestamp = "", nickname = ""):
if timestamp:
timestamp = time.strftime("%Y%m%dT%H%M%S", time.gmtime(timestamp))
if self.initialized == False:
self.logger.debug("Message queued from %s to %s: %s",
buddy, self.legacyName, messageContent)
self.logger.debug("Message queued from %s to %s: %s" %
(buddy, self.legacyName, messageContent))
self.offlineQueue.append((buddy, messageContent, timestamp))
else:
self.logger.debug("Message sent from %s to %s: %s", buddy,
self.legacyName, messageContent)
self.logger.debug("Message sent from %s to %s: %s" % (
buddy, self.legacyName, messageContent))
self.backend.handleMessage(self.user, buddy, messageContent, "",
"", timestamp)
def sendGroupMessageToXMPP(self, room, number, messageContent, timestamp = u"", defaultname = u""):
def sendGroupMessageToXMPP(self, room, number, messageContent, timestamp = "", defaultname = ""):
if timestamp:
timestamp = time.strftime("%Y%m%dT%H%M%S", time.gmtime(timestamp))
if self.initialized == False:
self.logger.debug("Group message queued from %s to %s: %s",
number, room, messageContent)
self.logger.debug("Group message queued from %s to %s: %s" %
(number, room, messageContent))
if room not in self.groupOfflineQueue:
self.groupOfflineQueue[room] = [ ]
@ -758,8 +746,8 @@ class Session(YowsupApp):
(number, messageContent, timestamp)
)
else:
self.logger.debug("Group message sent from %s to %s: %s",
number, room, messageContent)
self.logger.debug("Group message sent from %s to %s: %s" %
(number, room, messageContent))
try:
group = self.groups[room]
# Update nickname
@ -787,7 +775,7 @@ class Session(YowsupApp):
def changeStatus(self, status):
if status != self.status:
self.logger.info("Status changed: %s", status)
self.logger.info("Status changed: %s" % status)
self.status = status
if status == protocol_pb2.STATUS_ONLINE \
@ -799,8 +787,8 @@ class Session(YowsupApp):
def changeStatusMessage(self, statusMessage):
if (statusMessage != self.statusMessage) or (self.initialized == False):
self.statusMessage = statusMessage
self.setStatus(statusMessage.encode('utf-8'))
self.logger.info("Status message changed: %s", statusMessage)
self.setStatus(statusMessage)
self.logger.info("Status message changed: %s" % statusMessage)
#if self.initialized == False:
# self.sendOfflineMessages()
@ -824,66 +812,67 @@ class Session(YowsupApp):
def removeBuddy(self, buddy):
if buddy != "bot":
self.logger.info("Buddy removed: %s", buddy)
self.logger.info("Buddy removed: %s" % buddy)
self.buddies.remove(buddy)
def requestVCard(self, buddy, ID=None):
self.buddies.requestVCard(buddy, ID)
def createThumb(self, size=100, raw=False):
img = Image.open(self.imgPath)
width, height = img.size
img_thumbnail = self.imgPath + '_thumbnail'
img = Image.open(self.imgPath)
width, height = img.size
img_thumbnail = self.imgPath + '_thumbnail'
if width > height:
nheight = float(height) / width * size
nwidth = size
else:
nwidth = float(width) / height * size
nheight = size
if width > height:
nheight = float(height) / width * size
nwidth = size
else:
nwidth = float(width) / height * size
nheight = size
img.thumbnail((nwidth, nheight), Image.ANTIALIAS)
img.save(img_thumbnail, 'JPEG')
img.thumbnail((nwidth, nheight), Image.ANTIALIAS)
img.save(img_thumbnail, 'JPEG')
with open(img_thumbnail, 'rb') as imageFile:
raw = base64.b64encode(imageFile.read())
with open(img_thumbnail, 'rb') as imageFile:
raw = base64.b64encode(imageFile.read())
return raw
return raw
# Not used
def onLocationReceived(self, messageId, jid, name, preview, latitude, longitude, receiptRequested, isBroadcast):
buddy = jid.split("@")[0]
self.logger.info("Location received from %s: %s, %s", buddy, latitude, longitude)
self.logger.info("Location received from %s: %s, %s" % (buddy, latitude, longitude))
url = "http://maps.google.de?%s" % urllib.urlencode({ "q": "%s %s" % (latitude, longitude) })
self.sendMessageToXMPP(buddy, utils.shorten(url))
if receiptRequested: self.call("message_ack", (jid, messageId))
if receiptRequested:
self.call("message_ack", (jid, messageId))
def onGroupSubjectReceived(self, messageId, gjid, jid, subject, timestamp, receiptRequested):
room = gjid.split("@")[0]
buddy = jid.split("@")[0]
self.backend.handleSubject(self.user, room, subject, buddy)
if receiptRequested: self.call("subject_ack", (gjid, messageId))
if receiptRequested:
self.call("subject_ack", (gjid, messageId))
# Yowsup Notifications
def onGroupParticipantRemoved(self, gjid, jid, author, timestamp, messageId, receiptRequested):
room = gjid.split("@")[0]
buddy = jid.split("@")[0]
self.logger.info("Removed %s from room %s", buddy, room)
self.logger.info("Removed %s from room %s" % (buddy, room))
self.backend.handleParticipantChanged(self.user, buddy, room, protocol_pb2.PARTICIPANT_FLAG_NONE, protocol_pb2.STATUS_NONE) # TODO
if receiptRequested: self.call("notification_ack", (gjid, messageId))
def onContactProfilePictureUpdated(self, jid, timestamp, messageId, pictureId, receiptRequested):
# TODO
if receiptRequested: self.call("notification_ack", (jid, messageId))
if receiptRequested:
self.call("notification_ack", (jid, messageId))
def onGroupPictureUpdated(self, jid, author, timestamp, messageId, pictureId, receiptRequested):
# TODO
if receiptRequested: self.call("notification_ack", (jid, messageId))
if receiptRequested:
self.call("notification_ack", (jid, messageId))

View File

@ -1,3 +1,29 @@
__author__ = "Steffen Vogel"
__copyright__ = "Copyright 2015-2017, Steffen Vogel"
__license__ = "GPLv3"
__maintainer__ = "Steffen Vogel"
__email__ = "post@steffenvogel.de"
"""
This file is part of transWhat
transWhat is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
any later version.
transwhat is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with transWhat. If not, see <http://www.gnu.org/licenses/>.
"""
# use unicode encoding for all literals by default (for python2.x)
from __future__ import unicode_literals
import Queue
import threading

View File

@ -1,7 +1,7 @@
#!/usr/bin/python
__author__ = "Steffen Vogel"
__copyright__ = "Copyright 2015, Steffen Vogel"
__copyright__ = "Copyright 2015-2017, Steffen Vogel"
__license__ = "GPLv3"
__maintainer__ = "Steffen Vogel"
__email__ = "post@steffenvogel.de"
@ -23,6 +23,9 @@ __email__ = "post@steffenvogel.de"
along with transWhat. If not, see <http://www.gnu.org/licenses/>.
"""
# use unicode encoding for all literals by default (for python2.x)
from __future__ import unicode_literals
import argparse
import traceback
import logging
@ -35,7 +38,7 @@ import threadutils
sys.path.insert(0, os.getcwd())
from Spectrum2.iochannel import IOChannel
from config import SpectrumConfig
from whatsappbackend import WhatsAppBackend
from yowsup.common import YowConstants
from yowsup.stacks import YowStack
@ -43,6 +46,7 @@ from yowsup.stacks import YowStack
# Arguments
parser = argparse.ArgumentParser()
parser.add_argument('--debug', action='store_true')
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)
@ -52,14 +56,22 @@ parser.add_argument('-j', type=str, metavar="JID", required=True)
args, unknown = parser.parse_known_args()
YowConstants.PATH_STORAGE='/var/lib/spectrum2/' + args.j
loggingfile = '/var/log/spectrum2/' + args.j + '/backends/backend.log'
if args.log is None:
args.log = '/var/log/spectrum2/' + args.j + '/backends/backend.log'
# Logging
logging.basicConfig( \
filename=loggingfile,\
format = "%(asctime)-15s %(levelname)s %(name)s: %(message)s", \
level = logging.DEBUG if args.debug else logging.INFO \
logging.basicConfig(
filename = args.log,
format = "%(asctime)-15s %(levelname)s %(name)s: %(message)s",
level = logging.DEBUG if args.debug else logging.INFO
)
if args.config is not None:
specConf = SpectrumConfig(args.config)
else:
specConf = None
# Handler
def handleTransportData(data):
try:
@ -80,7 +92,7 @@ def connectionClosed():
# Main
io = IOChannel(args.host, args.port, handleTransportData, connectionClosed)
plugin = WhatsAppBackend(io, args.j)
plugin = WhatsAppBackend(io, args.j, specConf)
plugin.handleBackendConfig({
'features': [

View File

@ -1,5 +1,5 @@
__author__ = "Steffen Vogel"
__copyright__ = "Copyright 2015, Steffen Vogel"
__copyright__ = "Copyright 2015-2017, Steffen Vogel"
__license__ = "GPLv3"
__maintainer__ = "Steffen Vogel"
__email__ = "post@steffenvogel.de"
@ -21,10 +21,14 @@ __email__ = "post@steffenvogel.de"
along with transWhat. If not, see <http://www.gnu.org/licenses/>.
"""
# use unicode encoding for all literals by default (for python2.x)
from __future__ import unicode_literals
import e4u
import base64
import hashlib
def ago(secs):
periods = ["second", "minute", "hour", "day", "week", "month", "year", "decade"]
lengths = [60, 60, 24, 7,4.35, 12, 10]
@ -43,11 +47,10 @@ def ago(secs):
return "%d %s ago" % (diff, period)
def softToUni(message):
message = message.decode("utf-8")
return e4u.translate(message, reverse=False, **e4u.SOFTBANK_TRANSLATE_PROFILE)
return e4u.translate(message.encode("utf-8"), reverse=False, **e4u.SOFTBANK_TRANSLATE_PROFILE)
def decodePassword(password):
return base64.b64decode(bytes(password.encode("utf-8")))
return base64.b64decode(bytes(password))
def sha1hash(data):
return hashlib.sha1(data).hexdigest()
return hashlib.sha1(data).hexdigest()

View File

@ -1,5 +1,5 @@
__author__ = "Steffen Vogel"
__copyright__ = "Copyright 2015, Steffen Vogel"
__copyright__ = "Copyright 2015-2017, Steffen Vogel"
__license__ = "GPLv3"
__maintainer__ = "Steffen Vogel"
__email__ = "post@steffenvogel.de"
@ -21,6 +21,9 @@ __email__ = "post@steffenvogel.de"
along with transWhat. If not, see <http://www.gnu.org/licenses/>.
"""
# use unicode encoding for all literals by default (for python2.x)
from __future__ import unicode_literals
from Spectrum2.backend import SpectrumBackend
from Spectrum2 import protocol_pb2
@ -29,11 +32,13 @@ from registersession import RegisterSession
import logging
class WhatsAppBackend(SpectrumBackend):
def __init__(self, io, spectrum_jid):
def __init__(self, io, spectrum_jid, specConf):
SpectrumBackend.__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
@ -43,7 +48,7 @@ class WhatsAppBackend(SpectrumBackend):
# RequestsHandlers
def handleLoginRequest(self, user, legacyName, password, extra):
self.logger.debug("handleLoginRequest(user=%s, legacyName=%s)", user, legacyName)
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:
@ -55,13 +60,14 @@ class WhatsAppBackend(SpectrumBackend):
self.sessions[user].login(password)
def handleLogoutRequest(self, user, legacyName):
self.logger.debug("handleLogoutRequest(user=%s, legacyName=%s)", 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)
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
@ -73,63 +79,63 @@ class WhatsAppBackend(SpectrumBackend):
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.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.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.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)
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, str(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, str(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.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.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.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.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.debug("handleVCardUpdatedRequest(user=%s, nickname=%s)", user, nickname)
self.logger.debug("handleVCardUpdatedRequest(user=%s, nickname=%s)" % (user, nickname))
self.sessions[user].setProfilePicture(photo)
def handleBuddyBlockToggled(self, user, buddy, blocked):
self.logger.debug("handleBuddyBlockedToggled(user=%s, buddy=%s, blocked=%s)", user, buddy, blocked)
self.logger.debug("handleBuddyBlockedToggled(user=%s, buddy=%s, blocked=%s)" % (user, buddy, blocked))
def relogin(self, user, legacyName, password, extra):
"""
Used to re-initialize the session object. Used when finished with
registration session and the user needs to login properly
"""
self.logger.debug("relogin(user=%s, legacyName=%s)", user, legacyName)
self.logger.debug("relogin(user=%s, legacyName=%s)" % (user, legacyName))
# Change password in spectrum database
self.handleQuery('register %s %s %s' % (user, legacyName, password))
# Key word means we should register a new password
@ -144,8 +150,8 @@ class WhatsAppBackend(SpectrumBackend):
pass
def handleFTStartRequest(self, user, buddy, fileName, size, ftID):
self.logger.debug('File send request %s, for user %s, from %s, size: %s',
fileName, user, buddy, size)
self.logger.debug('File send request %s, for user %s, from %s, size: %s' %
(fileName, user, buddy, size))
def handleFTFinishRequest(self, user, buddy, fileName, size, ftID):
pass
@ -160,7 +166,7 @@ class WhatsAppBackend(SpectrumBackend):
pass
def handleMessageAckRequest(self, user, legacyName, ID = 0):
self.logger.info("Meassage ACK request for %s !!",legacyName)
self.logger.info("Meassage ACK request for %s !!" % legacyName)
def sendData(self, data):
self.io.sendData(data)

View File

@ -1,13 +1,43 @@
__author__ = "Steffen Vogel"
__copyright__ = "Copyright 2015-2017, Steffen Vogel"
__license__ = "GPLv3"
__maintainer__ = "Steffen Vogel"
__email__ = "post@steffenvogel.de"
"""
This file is part of transWhat
transWhat is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
any later version.
transwhat is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with transWhat. If not, see <http://www.gnu.org/licenses/>.
"""
# use unicode encoding for all literals by default (for python2.x)
from __future__ import unicode_literals
import logging
from yowsup import env
from yowsup.env import S40YowsupEnv
from yowsup.stacks import YowStack
from yowsup.common import YowConstants
from yowsup.layers import YowLayerEvent, YowParallelLayer
from yowsup.layers.auth import AuthError
from yowsup.stacks import YowStack
from yowsup.stacks import YowStackBuilder
from yowsup.common import YowConstants
# Layers
from yowsup.layers.axolotl import YowAxolotlLayer
from yowsup.layers.axolotl import AxolotlSendLayer, AxolotlControlLayer, AxolotlReceivelayer
from yowsup.layers.auth import YowCryptLayer, YowAuthenticationProtocolLayer
from yowsup.layers.coder import YowCoderLayer
from yowsup.layers.logger import YowLoggerLayer
@ -53,40 +83,9 @@ from yowsup.registration import WARegRequest
from functools import partial
#from session import MsgIDs
# Temporarily work around yowsup padding bugs with new protocol
class UpdatedYowAxolotlLayer(YowAxolotlLayer):
def decodeInt7bit(self, string):
idx = 0
while ord(string[idx]) >= 128:
idx += 1
consumedBytes = idx + 1
value = 0
while idx >= 0:
value <<= 7
value += ord(string[idx]) % 128
idx -= 1
return value, consumedBytes
def unpadV2Plaintext(self, v2plaintext):
end = -ord(v2plaintext[-1]) # length of the left padding
length,consumed = self.decodeInt7bit(v2plaintext[1:])
return v2plaintext[1+consumed:end]
# Temporary env until yowsup updates
class UpdatedS40YowsupEnv(env.S40YowsupEnv):
_VERSION = "2.13.39"
_OS_NAME= "S40"
_OS_VERSION = "14.26"
_DEVICE_NAME = "302"
_MANUFACTURER = "Nokia"
_TOKEN_STRING = "PdA2DJyKoUrwLw1Bg6EIhzh502dF9noR9uFCllGk{phone}"
_AXOLOTL = True
class YowsupApp(object):
def __init__(self):
env.CURRENT_ENV = UpdatedS40YowsupEnv()
env.CURRENT_ENV = env.AndroidYowsupEnv()
layers = (YowsupAppLayer,
YowParallelLayer((YowAuthenticationProtocolLayer,
@ -104,14 +103,21 @@ class YowsupApp(object):
YowProfilesProtocolLayer,
YowGroupsProtocolLayer,
YowPresenceProtocolLayer)),
UpdatedYowAxolotlLayer,
AxolotlControlLayer,
YowParallelLayer((AxolotlSendLayer, AxolotlReceivelayer)),
YowCoderLayer,
YowCryptLayer,
YowStanzaRegulator,
YowNetworkLayer
)
self.logger = logging.getLogger(self.__class__.__name__)
self.stack = YowStack(layers)
stackBuilder = YowStackBuilder()
self.stack = stackBuilder \
.pushDefaultLayers(True) \
.push(YowsupAppLayer) \
.build()
self.stack.broadcastEvent(
YowLayerEvent(YowsupAppLayer.EVENT_START, caller = self)
)
@ -129,12 +135,6 @@ class YowsupApp(object):
"""
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:
@ -163,6 +163,7 @@ class YowsupApp(object):
- read: ('read' or None) None is just delivered, 'read' is read
- participant
"""
self.logger.debug(u'Sending receipt to whatsapp: %s', [_id, _from, read, participant])
receipt = OutgoingReceiptProtocolEntity(_id, _from, read, participant)
self.sendEntity(receipt)
@ -178,71 +179,66 @@ class YowsupApp(object):
- to: (xxxxxxxxxx@s.whatsapp.net) who to send the message to
- message: (str) the body of the message
"""
messageEntity = TextMessageProtocolEntity(message, to = to)
messageEntity = TextMessageProtocolEntity(message.encode('utf-8'), to = to)
self.sendEntity(messageEntity)
return messageEntity.getId()
def sendLocation(self, to, latitude, longitude):
messageEntity = LocationMediaMessageProtocolEntity(latitude,longitude, None, None, "raw", to = to)
self.sendEntity(messageEntity)
return messageEntity.getId()
return messageEntity.getId()
def sendImage(self, jid, path, caption = None, onSuccess = None, onFailure = None):
entity = RequestUploadIqProtocolEntity(RequestUploadIqProtocolEntity.MEDIA_TYPE_IMAGE, filePath=path)
successFn = lambda successEntity, originalEntity: self.onRequestUploadResult(jid, path, successEntity, originalEntity, caption, onSuccess, onFailure)
errorFn = lambda errorEntity, originalEntity: self.onRequestUploadError(jid, path, errorEntity, originalEntity)
errorFn = lambda errorEntity, originalEntity: self.onRequestUploadError(jid, path, errorEntity, originalEntity)
self.sendIq(entity, successFn, errorFn)
self.sendIq(entity, successFn, errorFn)
def onRequestUploadResult(self, jid, filePath, resultRequestUploadIqProtocolEntity, requestUploadIqProtocolEntity, caption = None, onSuccess=None, onFailure=None):
if requestUploadIqProtocolEntity.mediaType == RequestUploadIqProtocolEntity.MEDIA_TYPE_AUDIO:
doSendFn = self.doSendAudio
else:
doSendFn = self.doSendImage
if requestUploadIqProtocolEntity.mediaType == RequestUploadIqProtocolEntity.MEDIA_TYPE_AUDIO:
doSendFn = self.doSendAudio
else:
doSendFn = self.doSendImage
if resultRequestUploadIqProtocolEntity.isDuplicate():
doSendFn(filePath, resultRequestUploadIqProtocolEntity.getUrl(), jid,
resultRequestUploadIqProtocolEntity.getIp(), caption)
else:
successFn = lambda filePath, jid, url: doSendFn(filePath, url, jid, resultRequestUploadIqProtocolEntity.getIp(), caption, onSuccess, onFailure)
if resultRequestUploadIqProtocolEntity.isDuplicate():
doSendFn(filePath, resultRequestUploadIqProtocolEntity.getUrl(), jid,
resultRequestUploadIqProtocolEntity.getIp(), caption, onSuccess, onFailure)
else:
successFn = lambda filePath, jid, url: doSendFn(filePath, url.encode('ascii','ignore'), jid, resultRequestUploadIqProtocolEntity.getIp(), caption, onSuccess, onFailure)
ownNumber = self.stack.getLayerInterface(YowAuthenticationProtocolLayer).getUsername(full=False)
mediaUploader = MediaUploader(jid, ownNumber, filePath,
resultRequestUploadIqProtocolEntity.getUrl(),
resultRequestUploadIqProtocolEntity.getResumeOffset(),
successFn, self.onUploadError, self.onUploadProgress, async=False)
mediaUploader.start()
mediaUploader = MediaUploader(jid, ownNumber, filePath,
resultRequestUploadIqProtocolEntity.getUrl(),
resultRequestUploadIqProtocolEntity.getResumeOffset(),
successFn, self.onUploadError, self.onUploadProgress, async=False)
mediaUploader.start()
def onRequestUploadError(self, jid, path, errorRequestUploadIqProtocolEntity, requestUploadIqProtocolEntity):
self.logger.error("Request upload for file %s for %s failed" % (path, jid))
def onRequestUploadError(self, jid, path, errorRequestUploadIqProtocolEntity, requestUploadIqProtocolEntity):
self.logger.error("Request upload for file %s for %s failed" % (path, jid))
def onUploadError(self, filePath, jid, url):
#logger.error("Upload file %s to %s for %s failed!" % (filePath, url, jid))
self.logger.error("Upload Error!")
def onUploadError(self, filePath, jid, url):
self.logger.error("Upload file %s to %s for %s failed!" % (filePath, url, jid))
def onUploadProgress(self, filePath, jid, url, progress):
#sys.stdout.write("%s => %s, %d%% \r" % (os.path.basename(filePath), jid, progress))
#sys.stdout.flush()
def onUploadProgress(self, filePath, jid, url, progress):
self.logger.info("%s => %s, %d%% \r" % (os.path.basename(filePath), jid, progress))
pass
def doSendImage(self, filePath, url, to, ip = None, caption = None, onSuccess = None, onFailure = None):
entity = ImageDownloadableMediaMessageProtocolEntity.fromFilePath(filePath, url, ip, to, caption = caption)
self.sendEntity(entity)
#self.msgIDs[entity.getId()] = MsgIDs(self.imgMsgId, entity.getId())
if onSuccess is not None:
onSuccess(entity.getId())
return entity.getId()
def doSendAudio(self, filePath, url, to, ip = None, caption = None, onSuccess = None, onFailure = None):
entity = AudioDownloadableMediaMessageProtocolEntity.fromFilePath(filePath, url, ip, to)
self.sendEntity(entity)
entity = ImageDownloadableMediaMessageProtocolEntity.fromFilePath(filePath, url, ip, to, caption = caption)
self.sendEntity(entity)
#self.msgIDs[entity.getId()] = MsgIDs(self.imgMsgId, entity.getId())
if onSuccess is not None:
onSuccess(entity.getId())
return entity.getId()
def doSendAudio(self, filePath, url, to, ip = None, caption = None, onSuccess = None, onFailure = None):
entity = AudioDownloadableMediaMessageProtocolEntity.fromFilePath(filePath, url, ip, to)
self.sendEntity(entity)
#self.msgIDs[entity.getId()] = MsgIDs(self.imgMsgId, entity.getId())
if onSuccess is not None:
onSuccess(entity.getId())
return entity.getId()
def sendPresence(self, available):
"""
@ -264,7 +260,7 @@ class YowsupApp(object):
- phone_number: (str) The cellphone number of the person to
subscribe to
"""
self.logger.debug("Subscribing to Presence updates from %s", (phone_number))
self.logger.debug("Subscribing to Presence updates from %s" % phone_number)
jid = phone_number + '@s.whatsapp.net'
entity = SubscribePresenceProtocolEntity(jid)
self.sendEntity(entity)
@ -394,7 +390,7 @@ class YowsupApp(object):
iq = GetStatusesIqProtocolEntity([c + '@s.whatsapp.net' for c in contacts])
def onSuccess(response, request):
if success is not None:
self.logger.debug("Received Statuses %s", response)
self.logger.debug("Received Statuses %s" % response)
s = {}
for k, v in response.statuses.iteritems():
s[k.split('@')[0]] = v
@ -563,7 +559,7 @@ class YowsupApp(object):
"""
pass
def onTextMessage(self, _id, _from, to, notify, timestamp, participant, offline, retry, body):
def onTextMessage(self, _id, _from, to, notify, timestamp, participant, offline, retry, body):
"""
Called when text message is received
@ -798,7 +794,7 @@ class YowsupAppLayer(YowInterfaceLayer):
"""
Sends ack automatically
"""
self.logger.debug("Received notification (%s): %s", type(entity), entity)
self.logger.debug("Received notification (%s): %s" % (type(entity), entity))
self.toLower(entity.ack())
if isinstance(entity, CreateGroupsNotificationProtocolEntity):
self.caller.onAddedToGroup(entity)
@ -840,7 +836,7 @@ class YowsupAppLayer(YowInterfaceLayer):
@ProtocolEntityCallback('message')
def onMessageReceived(self, entity):
self.logger.debug("Received Message: %s", entity)
self.logger.debug("Received Message: %s" % unicode(entity))
if entity.getType() == MessageProtocolEntity.MESSAGE_TYPE_TEXT:
self.caller.onTextMessage(
entity._id,