2
2
WhatsAPI module
3
3
"""
4
4
5
+ import logging
6
+ from json import dumps , loads
5
7
6
- from __future__ import print_function
7
-
8
- import sys
9
- import datetime
10
- import time
11
8
import os
12
- import sys
13
- import logging
14
- import pickle
15
9
import tempfile
16
-
10
+ import shutil
17
11
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
20
13
from selenium .webdriver .common .by import By
14
+ from selenium .webdriver .common .desired_capabilities import DesiredCapabilities
15
+ from selenium .webdriver .firefox .options import Options
21
16
from selenium .webdriver .support import expected_conditions as EC
22
17
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'
24
26
25
27
26
28
class WhatsAPIDriverStatus (object ):
@@ -30,21 +32,18 @@ class WhatsAPIDriverStatus(object):
30
32
NotLoggedIn = 'NotLoggedIn'
31
33
LoggedIn = 'LoggedIn'
32
34
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
38
35
39
- if __debug__ :
40
- import pprint
41
- pp = pprint . PrettyPrinter ( indent = 4 )
36
+ class WhatsAPIException ( Exception ) :
37
+ pass
38
+
42
39
43
40
class WhatsAPIDriver (object ):
44
41
_PROXY = None
45
42
46
43
_URL = "https://web.whatsapp.com"
47
44
45
+ _LOCAL_STORAGE_FILE = 'localStorage.json'
46
+
48
47
_SELECTORS = {
49
48
'firstrun' : "#wrapper" ,
50
49
'qrCode' : "img[alt=\" Scan me!\" ]" ,
@@ -78,13 +77,40 @@ class WhatsAPIDriver(object):
78
77
# Do not alter this
79
78
_profile = None
80
79
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 ):
82
88
"Function to save the firefox profile to the permanant one"
83
89
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 ()))
88
114
89
115
def set_proxy (self , proxy ):
90
116
self .logger .info ("Setting proxy to %s" % proxy )
@@ -95,7 +121,8 @@ def set_proxy(self, proxy):
95
121
self ._profile .set_preference ("network.proxy.ssl" , proxy_address )
96
122
self ._profile .set_preference ("network.proxy.ssl_port" , int (proxy_port ))
97
123
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 ):
99
126
"Initialises the webdriver"
100
127
101
128
# Get the name of the config folder
@@ -105,8 +132,8 @@ def __init__(self, client="firefox", username="API", proxy=None, command_executo
105
132
if not os .path .exists (self .config_dir ):
106
133
os .makedirs (self .config_dir )
107
134
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" )
110
137
111
138
self .logger .setLevel (logging .DEBUG )
112
139
@@ -119,9 +146,8 @@ def __init__(self, client="firefox", username="API", proxy=None, command_executo
119
146
self ._profile_path = profile
120
147
self .logger .info ("Checking for profile at %s" % self ._profile_path )
121
148
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 )
125
151
else :
126
152
self ._profile_path = None
127
153
@@ -138,11 +164,22 @@ def __init__(self, client="firefox", username="API", proxy=None, command_executo
138
164
self ._profile .set_preference ('permissions.default.image' , 2 )
139
165
## Disable Flash
140
166
self ._profile .set_preference ('dom.ipc.plugins.enabled.libflashplayer.so' ,
141
- 'false' )
167
+ 'false' )
142
168
if proxy is not None :
143
169
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
+
144
181
self .logger .info ("Starting webdriver" )
145
- self .driver = webdriver .Firefox (self . _profile )
182
+ self .driver = webdriver .Firefox (capabilities = capabilities , options = options )
146
183
147
184
elif self .client == "chrome" :
148
185
self ._profile = webdriver .chrome .options .Options ()
@@ -161,14 +198,20 @@ def __init__(self, client="firefox", username="API", proxy=None, command_executo
161
198
162
199
else :
163
200
self .logger .error ("Invalid client: %s" % client )
164
- print ("Enter a valid client name" )
165
201
self .username = username
166
202
self .wapi_functions = WapiJsWrapper (self .driver )
167
203
168
204
self .driver .set_script_timeout (500 )
169
205
self .driver .implicitly_wait (10 )
170
206
self .driver .get (self ._URL )
171
207
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
+
172
215
def wait_for_login (self ):
173
216
"""Waits for the QR to go away"""
174
217
WebDriverWait (self .driver , 90 ).until (
@@ -182,7 +225,6 @@ def get_qr(self):
182
225
qr = self .driver .find_element_by_css_selector (self ._SELECTORS ['qrCode' ])
183
226
fd , fn_png = tempfile .mkstemp (prefix = self .username , suffix = '.png' )
184
227
self .logger .debug ("QRcode image saved at %s" % fn_png )
185
- print (fn_png )
186
228
qr .screenshot (fn_png )
187
229
os .close (fd )
188
230
return fn_png
@@ -288,7 +330,7 @@ def get_chat_from_phone_number(self, number):
288
330
:return: Chat
289
331
:rtype: Chat
290
332
"""
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 ())
292
334
return next ((contact for contact in chats if (number in contact .id )), None )
293
335
294
336
def reload_qr (self ):
0 commit comments