4
4
5
5
import autogui
6
6
import requests
7
+ from bs4 import BeautifulSoup
7
8
8
9
from typing import List
9
10
10
11
12
+ # noinspection PyBroadException
11
13
class Client :
14
+ """
15
+ A class to manage the IMVU client
16
+
17
+ Attributes:
18
+ cwd (str): The current working directory
19
+ version (str): The version of IMVU to manipulate
20
+ """
21
+
12
22
BASE_URL = 'https://static-akm.imvu.com/imvufiles/installers/InstallIMVU_{}.exe'
23
+ VERSION_URL = 'https://www.imvu.com/download.php'
13
24
CLIENT_PATH = '{}/IMVUClient' .format (os .getenv ('APPDATA' ))
14
25
15
- def __init__ (self , cwd , version ):
16
- self .cwd = cwd
17
- self .version = version
26
+ def __init__ (self , version = None , cwd = None ):
27
+ """
28
+ :param str|None version: The version of IMVU to manipulate, if None, the latest version is used
29
+ :param str|None cwd: The current working directory
30
+ """
31
+ self .cwd = cwd or os .getcwd ()
32
+ self .version = version or self ._latest_version ()
18
33
19
34
def __enter__ (self ):
20
35
return self
@@ -23,27 +38,58 @@ def __exit__(self, exc_type, exc_val, exc_tb):
23
38
self .cleanup ()
24
39
25
40
def download (self ):
41
+ """
42
+ Download the IMVU client executable if it does not exist
43
+
44
+ :return: True if the executable was downloaded, False otherwise
45
+ :rtype: bool
46
+ """
26
47
print ('DOWNLOADING: IMVU {}' .format (self .version ))
27
48
if os .path .isfile (os .path .join (self .cwd , 'InstallIMVU_%s.exe' % self .version )):
28
- return
49
+ return False
29
50
30
51
r = requests .get (Client .BASE_URL .format (self .version ))
31
52
32
53
with open (os .path .join (self .cwd , 'InstallIMVU_%s.exe' % self .version ), 'wb' ) as f :
33
54
f .write (r .content )
34
55
35
- def install (self ):
56
+ return True
57
+
58
+ def install (self , download = False ):
59
+ """
60
+ Install the IMVU client using the downloaded executable
61
+ :param bool download: Download the executable if it does not exist
62
+ """
63
+ if not os .path .isfile (os .path .join (self .cwd , 'InstallIMVU_%s.exe' % self .version )):
64
+ if download :
65
+ self .download ()
66
+ else :
67
+ raise IOError ('Could not find InstallIMVU_{}.exe' .format (self .version ))
68
+
36
69
print ('INSTALLING: IMVU {}' .format (self .version ))
37
70
autogui .open (os .path .join (self .cwd , 'InstallIMVU_%s.exe' % self .version ), setActive = False )
38
- autogui .setWindow ('IMVU Setup' )
39
- autogui .click ('Install' , 0 , 4 )
71
+ attempts = 0
72
+ while attempts < 5 :
73
+ attempts += 1
74
+ try :
75
+ autogui .setWindow ('IMVU Setup' )
76
+ autogui .click ('Install' , 0 , 4 )
77
+ break
78
+ except Exception :
79
+ if attempts > 5 :
80
+ raise RuntimeError ('Could not automate IMVU install! Failed to find Setup window.' )
81
+ print ('SLEEPING: 5 SECONDS...' )
82
+ time .sleep (5 )
40
83
print ('SLEEPING: 15 SECONDS...' )
41
84
time .sleep (15 )
42
85
43
86
attempts = 0
44
87
while attempts < 5 :
45
88
attempts += 1
46
89
try :
90
+ if autogui .exists ('Update Available' ):
91
+ autogui .setWindow ('Update Available' )
92
+ autogui .click ('Close' , 0 , 4 )
47
93
autogui .setWindow ('IMVU Login' )
48
94
autogui .click ('Close' , 0 , 4 )
49
95
break
@@ -68,10 +114,13 @@ def copy(self):
68
114
69
115
def patch (self , patchers , dry_run = False ):
70
116
"""
71
- :param List patchers:
72
- :param dry_run:
73
- :return:
117
+ Patch the IMVU client
118
+ :param List patchers: A list of patchers to run
119
+ :param bool dry_run: Do not apply patches, only simulate
74
120
"""
121
+ if not os .path .isdir (os .path .join (self .cwd , 'IMVUClient' )):
122
+ raise IOError ('Could not find IMVUClient directory' )
123
+
75
124
for Patcher in patchers :
76
125
patcher = Patcher (self .cwd )
77
126
patcher .setup ()
@@ -83,3 +132,71 @@ def cleanup(self):
83
132
executable = os .path .join (self .cwd , 'InstallIMVU_%s.exe' % self .version )
84
133
if os .path .isfile (executable ):
85
134
os .remove (executable )
135
+
136
+ def diff (self , generators , version = None ):
137
+ """
138
+ :param List[constructor] generators: A list of diff generators
139
+ :param str version: The version to diff against, if None, the latest version is used
140
+ """
141
+ if version is None :
142
+ version = self ._latest_version ()
143
+
144
+ if self .version == version :
145
+ print ('SKIPPED: IMVU {} and IMVU {} are the same' .format (self .version , version ))
146
+ return
147
+
148
+ if os .path .isdir (os .path .join (self .cwd , 'IMVUClient-{}' .format (self .version ))):
149
+ shutil .rmtree (os .path .join (self .cwd , 'IMVUClient-{}' .format (self .version )))
150
+
151
+ print ('DIFFING: IMVU {} and IMVU {}' .format (self .version , version ))
152
+
153
+ # Download the previous version first
154
+ self .download ()
155
+ self .install ()
156
+ self .copy ()
157
+
158
+ # Rename the previous version to IMVUClient-{version}
159
+ if os .path .isdir (os .path .join (self .cwd , 'IMVUClient-{}' .format (self .version ))):
160
+ shutil .rmtree (os .path .join (self .cwd , 'IMVUClient-{}' .format (self .version )))
161
+
162
+ shutil .move (os .path .join (self .cwd , 'IMVUClient' ), os .path .join (self .cwd , 'IMVUClient-{}' .format (self .version )))
163
+
164
+ # Download the current version
165
+ current_version = self .version
166
+ self .version = version
167
+
168
+ self .download ()
169
+ self .install ()
170
+ self .copy ()
171
+
172
+ # Rename the current version to IMVUClient-{current}
173
+ if os .path .isdir (os .path .join (self .cwd , 'IMVUClient-{}' .format (version ))):
174
+ shutil .rmtree (os .path .join (self .cwd , 'IMVUClient-{}' .format (version )))
175
+
176
+ shutil .move (os .path .join (self .cwd , 'IMVUClient' ), os .path .join (self .cwd , 'IMVUClient-{}' .format (version )))
177
+
178
+ self .version = current_version
179
+
180
+ previous_cwd = os .path .join (self .cwd , 'IMVUClient-{}' .format (self .version ))
181
+ current_cwd = os .path .join (self .cwd , 'IMVUClient-{}' .format (version ))
182
+
183
+ for Generator in generators :
184
+ print ('PROCESSING DIFF GENERATOR: {}' .format (Generator .__name__ ))
185
+ with Generator (self .cwd , self .version , version , previous_cwd , current_cwd ) as generator :
186
+ generator .diff ()
187
+
188
+ executable = os .path .join (self .cwd , 'InstallIMVU_%s.exe' % version )
189
+ if os .path .isfile (executable ):
190
+ os .remove (executable )
191
+
192
+ def _latest_version (self ):
193
+ """
194
+ Get the latest version of IMVU
195
+ :return: The latest version of IMVU
196
+ :rtype: str
197
+ """
198
+ response = requests .get (Client .VERSION_URL )
199
+ soup = BeautifulSoup (response .content , 'html.parser' )
200
+ h2 = soup .find ('h2' , text = 'IMVU Classic:' )
201
+ b = h2 .find_next ('b' )
202
+ return b .text .strip (':' )
0 commit comments