Skip to content

Commit 2c010ab

Browse files
committed
Resolved problems with Python3; Removed prints and exits; Fix save firefox profile; Store WebStorage in firefox profile in order to keep session; Use local storage if it exists in profile
1 parent bfcebb5 commit 2c010ab

File tree

8 files changed

+150
-69
lines changed

8 files changed

+150
-69
lines changed

.env

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
webwhatsapi@2.0.1

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,5 @@ chrome_cache/
88
*.png
99
**/.DS_Store
1010
**/*.pyc
11+
profiles
12+
.idea

setup.py

+38-8
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,45 @@
55
https://github.com/pypa/sampleproject
66
"""
77

8-
# Always prefer setuptools over distutils
9-
from setuptools import setup, find_packages
8+
import ast
9+
1010
# To use a consistent encoding
1111
from codecs import open
1212
from os import path
13-
14-
here = path.abspath(path.dirname(__file__))
13+
# Always prefer setuptools over distutils
14+
from setuptools import setup
15+
16+
PACKAGE_NAME = 'webwhatsapi'
17+
18+
path = path.join(path.dirname(__file__), PACKAGE_NAME, '__init__.py')
19+
20+
with open(path, 'r') as file:
21+
t = compile(file.read(), path, 'exec', ast.PyCF_ONLY_AST)
22+
for node in (n for n in t.body if isinstance(n, ast.Assign)):
23+
if len(node.targets) != 1:
24+
continue
25+
26+
name = node.targets[0]
27+
if not isinstance(name, ast.Name) or \
28+
name.id not in ('__version__', '__version_info__', 'VERSION'):
29+
continue
30+
31+
v = node.value
32+
if isinstance(v, ast.Str):
33+
version = v.s
34+
break
35+
if isinstance(v, ast.Tuple):
36+
r = []
37+
for e in v.elts:
38+
if isinstance(e, ast.Str):
39+
r.append(e.s)
40+
elif isinstance(e, ast.Num):
41+
r.append(str(e.n))
42+
version = '.'.join(r)
43+
break
1544

1645
# Get the long description from the README file
17-
with open(path.join(here, 'README.rst'), encoding='utf-8') as f:
46+
with open(path.join(path.dirname(__file__), 'README.rst'), encoding='utf-8') as f:
1847
long_description = f.read()
1948

2049
setup(
@@ -23,14 +52,14 @@
2352
# Versions should comply with PEP440. For a discussion on single-sourcing
2453
# the version across setup.py and the project code, see
2554
# https://packaging.python.org/en/latest/single_source_version.html
26-
version='2.0.1',
55+
version=version,
2756

2857
description='A python interface for Whatsapp Web',
2958
long_description=long_description,
3059

3160
# The project's main homepage.
3261
url='https://github.com/mukulhase/WhatsAPI',
33-
download_url='https://github.com/mukulhase/WhatsAPI/archive/2.0.1.tar.gz',
62+
download_url='https://github.com/mukulhase/WhatsAPI/archive/{}.tar.gz'.format(version),
3463

3564
# Author details
3665
author='Mukul Hase',
@@ -58,14 +87,15 @@
5887
# Specify the Python versions you support here. In particular, ensure
5988
# that you indicate whether you support Python 2, Python 3 or both.
6089
'Programming Language :: Python :: 2.7',
90+
'Programming Language :: Python :: 3.6',
6191
],
6292

6393
# What does your project relate to?
6494
keywords='Whatsapp Chat Bot Chatbot Selenium Web Whatsapp API',
6595

6696
# You can just specify the packages manually here if your project is
6797
# simple. Or you can use find_packages().
68-
packages=find_packages(exclude=['contrib', 'docs', 'tests']),
98+
packages=PACKAGE_NAME,
6999
install_requires=[
70100
'python-dateutil>=2.6.0',
71101
'selenium>=3.4.3',

webwhatsapi/__init__.py

+78-36
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,27 @@
22
WhatsAPI module
33
"""
44

5+
import logging
6+
from json import dumps, loads
57

6-
from __future__ import print_function
7-
8-
import sys
9-
import datetime
10-
import time
118
import os
12-
import sys
13-
import logging
14-
import pickle
159
import tempfile
16-
10+
import shutil
1711
from selenium import webdriver
18-
from selenium.webdriver.chrome.options import Options
19-
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
12+
from selenium.common.exceptions import NoSuchElementException
2013
from selenium.webdriver.common.by import By
14+
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
15+
from selenium.webdriver.firefox.options import Options
2116
from selenium.webdriver.support import expected_conditions as EC
2217
from selenium.webdriver.support.ui import WebDriverWait
23-
from selenium.common.exceptions import NoSuchElementException
18+
19+
from .consts import Selectors, URL
20+
from .objects.chat import Chat, GroupChat, UserChat
21+
from .objects.contact import Contact
22+
from .objects.message import Message, MessageGroup
23+
from .wapi_js_wrapper import WapiJsWrapper
24+
25+
__version__ = '2.0.2'
2426

2527

2628
class WhatsAPIDriverStatus(object):
@@ -30,21 +32,18 @@ class WhatsAPIDriverStatus(object):
3032
NotLoggedIn = 'NotLoggedIn'
3133
LoggedIn = 'LoggedIn'
3234

33-
from consts import Selectors, URL
34-
from objects.chat import Chat, GroupChat, UserChat
35-
from objects.contact import Contact
36-
from objects.message import Message, MessageGroup
37-
from webwhatsapi.wapi_js_wrapper import WapiJsWrapper
3835

39-
if __debug__:
40-
import pprint
41-
pp = pprint.PrettyPrinter(indent=4)
36+
class WhatsAPIException(Exception):
37+
pass
38+
4239

4340
class WhatsAPIDriver(object):
4441
_PROXY = None
4542

4643
_URL = "https://web.whatsapp.com"
4744

45+
_LOCAL_STORAGE_FILE = 'localStorage.json'
46+
4847
_SELECTORS = {
4948
'firstrun': "#wrapper",
5049
'qrCode': "img[alt=\"Scan me!\"]",
@@ -78,13 +77,40 @@ class WhatsAPIDriver(object):
7877
# Do not alter this
7978
_profile = None
8079

81-
def save_firefox_profile(self):
80+
def get_local_storage(self):
81+
return self.driver.execute_script('return window.localStorage;')
82+
83+
def set_local_storage(self, data):
84+
self.driver.execute_script(''.join(["window.localStorage.setItem('{}', '{}');".format(k, v)
85+
for k, v in data.items()]))
86+
87+
def save_firefox_profile(self, remove_old=False):
8288
"Function to save the firefox profile to the permanant one"
8389
self.logger.info("Saving profile from %s to %s" % (self._profile.path, self._profile_path))
84-
os.system("cp -R " + self._profile.path + " "+ self._profile_path)
85-
cookie_file = os.path.join(self._profile_path, "cookies.pkl")
86-
if self.driver:
87-
pickle.dump(self.driver.get_cookies() , open(cookie_file,"wb"))
90+
91+
if remove_old:
92+
if os.path.exists(self._profile_path):
93+
try:
94+
shutil.rmtree(self._profile_path)
95+
except OSError:
96+
pass
97+
98+
shutil.copytree(os.path.join(self._profile.path), self._profile_path,
99+
ignore=shutil.ignore_patterns("parent.lock", "lock", ".parentlock"))
100+
else:
101+
for item in os.listdir(self._profile.path):
102+
if item in ["parent.lock", "lock", ".parentlock"]:
103+
continue
104+
s = os.path.join(self._profile.path, item)
105+
d = os.path.join(self._profile_path, item)
106+
if os.path.isdir(s):
107+
shutil.copytree(s, d,
108+
ignore=shutil.ignore_patterns("parent.lock", "lock", ".parentlock"))
109+
else:
110+
shutil.copy2(s, d)
111+
112+
with open(os.path.join(self._profile_path, self._LOCAL_STORAGE_FILE), 'w') as f:
113+
f.write(dumps(self.get_local_storage()))
88114

89115
def set_proxy(self, proxy):
90116
self.logger.info("Setting proxy to %s" % proxy)
@@ -95,7 +121,8 @@ def set_proxy(self, proxy):
95121
self._profile.set_preference("network.proxy.ssl", proxy_address)
96122
self._profile.set_preference("network.proxy.ssl_port", int(proxy_port))
97123

98-
def __init__(self, client="firefox", username="API", proxy=None, command_executor=None, loadstyles=False, profile=None):
124+
def __init__(self, client="firefox", username="API", proxy=None, command_executor=None, loadstyles=False,
125+
profile=None, headless=False):
99126
"Initialises the webdriver"
100127

101128
# Get the name of the config folder
@@ -105,8 +132,8 @@ def __init__(self, client="firefox", username="API", proxy=None, command_executo
105132
if not os.path.exists(self.config_dir):
106133
os.makedirs(self.config_dir)
107134
except OSError:
108-
print("Error: Could not create config dir")
109-
exit(-1)
135+
self.logger.critical("Error: Could not create config dir")
136+
raise WhatsAPIException("Error: Could not create config dir")
110137

111138
self.logger.setLevel(logging.DEBUG)
112139

@@ -119,9 +146,8 @@ def __init__(self, client="firefox", username="API", proxy=None, command_executo
119146
self._profile_path = profile
120147
self.logger.info("Checking for profile at %s" % self._profile_path)
121148
if not os.path.exists(self._profile_path):
122-
print("Could not find profile at %s" % profile)
123-
self.logger.error("Could not find profile at %s" % profile)
124-
exit(-1)
149+
self.logger.critical("Could not find profile at %s" % profile)
150+
raise WhatsAPIException("Could not find profile at %s" % profile)
125151
else:
126152
self._profile_path = None
127153

@@ -138,11 +164,22 @@ def __init__(self, client="firefox", username="API", proxy=None, command_executo
138164
self._profile.set_preference('permissions.default.image', 2)
139165
## Disable Flash
140166
self._profile.set_preference('dom.ipc.plugins.enabled.libflashplayer.so',
141-
'false')
167+
'false')
142168
if proxy is not None:
143169
self.set_proxy(proxy)
170+
171+
options = Options()
172+
173+
if headless:
174+
options.set_headless()
175+
176+
options.profile = self._profile
177+
178+
capabilities = DesiredCapabilities.FIREFOX.copy()
179+
capabilities['webStorageEnabled'] = True
180+
144181
self.logger.info("Starting webdriver")
145-
self.driver = webdriver.Firefox(self._profile)
182+
self.driver = webdriver.Firefox(capabilities=capabilities, options=options)
146183

147184
elif self.client == "chrome":
148185
self._profile = webdriver.chrome.options.Options()
@@ -161,14 +198,20 @@ def __init__(self, client="firefox", username="API", proxy=None, command_executo
161198

162199
else:
163200
self.logger.error("Invalid client: %s" % client)
164-
print("Enter a valid client name")
165201
self.username = username
166202
self.wapi_functions = WapiJsWrapper(self.driver)
167203

168204
self.driver.set_script_timeout(500)
169205
self.driver.implicitly_wait(10)
170206
self.driver.get(self._URL)
171207

208+
local_storage_file = os.path.join(self._profile.path, self._LOCAL_STORAGE_FILE)
209+
if os.path.exists(local_storage_file):
210+
with open(local_storage_file) as f:
211+
self.set_local_storage(loads(f.read()))
212+
213+
self.driver.refresh()
214+
172215
def wait_for_login(self):
173216
"""Waits for the QR to go away"""
174217
WebDriverWait(self.driver, 90).until(
@@ -182,7 +225,6 @@ def get_qr(self):
182225
qr = self.driver.find_element_by_css_selector(self._SELECTORS['qrCode'])
183226
fd, fn_png = tempfile.mkstemp(prefix=self.username, suffix='.png')
184227
self.logger.debug("QRcode image saved at %s" % fn_png)
185-
print(fn_png)
186228
qr.screenshot(fn_png)
187229
os.close(fd)
188230
return fn_png
@@ -288,7 +330,7 @@ def get_chat_from_phone_number(self, number):
288330
:return: Chat
289331
:rtype: Chat
290332
"""
291-
chats = filter(lambda chat:(type(chat) is UserChat), self.get_all_chats())
333+
chats = filter(lambda chat: (type(chat) is UserChat), self.get_all_chats())
292334
return next((contact for contact in chats if (number in contact.id)), None)
293335

294336
def reload_qr(self):

webwhatsapi/objects/chat.py

+8-6
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
from webwhatsapi.objects.whatsapp_object import WhatsappObject, driver_needed
2-
from webwhatsapi.helper import safe_str
3-
from webwhatsapi.objects.message import Message
1+
from six import with_metaclass
2+
3+
from .message import Message
4+
from .whatsapp_object import WhatsappObject, driver_needed
5+
from ..helper import safe_str
6+
47

5-
##TODO: Fix relative imports for Python3
68
class ChatMetaClass(type):
79
"""
810
Chat type factory
@@ -28,8 +30,7 @@ def __call__(cls, js_obj, driver=None):
2830
return type.__call__(UserChat, js_obj, driver)
2931

3032

31-
class Chat(WhatsappObject):
32-
__metaclass__ = ChatMetaClass
33+
class Chat(with_metaclass(ChatMetaClass, WhatsappObject)):
3334

3435
def __init__(self, js_obj, driver=None):
3536
super(Chat, self).__init__(js_obj, driver)
@@ -52,6 +53,7 @@ def load_earlier_messages(self):
5253
def load_all_earlier_messages(self):
5354
self.driver.wapi_functions.loadAllEarlierMessages(self.id)
5455

56+
5557
class UserChat(Chat):
5658
def __init__(self, js_obj, driver=None):
5759
super(UserChat, self).__init__(js_obj, driver)

webwhatsapi/objects/contact.py

+6-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1-
from webwhatsapi.objects.whatsapp_object import WhatsappObject, driver_needed
2-
from webwhatsapi.helper import safe_str
3-
import six
1+
from six import string_types
2+
3+
from .whatsapp_object import WhatsappObject, driver_needed
4+
from ..helper import safe_str
5+
46

57
class Contact(WhatsappObject):
68
def __init__(self, js_obj, driver=None):
@@ -19,7 +21,7 @@ def get_chat(self):
1921

2022
def get_safe_name(self):
2123
name = (self.name or self.push_name or self.formatted_name)
22-
if (isinstance(name, six.string_types)):
24+
if (isinstance(name, string_types)):
2325
safe_name = safe_str(name)
2426
else:
2527
safe_name = "Unknown"

0 commit comments

Comments
 (0)