PySPlus 0.3__py3-none-any.whl → 0.5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pysplus/Client.py +119 -37
- pysplus/async_sync.py +15 -0
- {pysplus-0.3.dist-info → pysplus-0.5.dist-info}/METADATA +3 -3
- pysplus-0.5.dist-info/RECORD +8 -0
- pysplus-0.3.dist-info/RECORD +0 -7
- {pysplus-0.3.dist-info → pysplus-0.5.dist-info}/WHEEL +0 -0
- {pysplus-0.3.dist-info → pysplus-0.5.dist-info}/top_level.txt +0 -0
pysplus/Client.py
CHANGED
@@ -7,7 +7,9 @@ from selenium.webdriver.chrome.options import Options
|
|
7
7
|
from webdriver_manager.chrome import ChromeDriverManager
|
8
8
|
from bs4 import BeautifulSoup
|
9
9
|
from .colors import *
|
10
|
-
from
|
10
|
+
from .async_sync import *
|
11
|
+
from typing import Optional,Literal
|
12
|
+
from traceback import format_exc
|
11
13
|
import time,os,json,asyncio,logging,pickle
|
12
14
|
|
13
15
|
logging.getLogger('selenium').setLevel(logging.WARNING)
|
@@ -35,7 +37,6 @@ class Client:
|
|
35
37
|
self.time_out = text_json_py_slpus_session["time_out"]
|
36
38
|
self.user_agent = text_json_py_slpus_session["user_agent"]
|
37
39
|
self.display_welcome = text_json_py_slpus_session["display_welcome"]
|
38
|
-
asyncio.run(self.login())
|
39
40
|
else:
|
40
41
|
if not number_phone:
|
41
42
|
number_phone = input("Enter your phone number : ")
|
@@ -46,21 +47,21 @@ class Client:
|
|
46
47
|
number_phone = input("Enter your phone number : ")
|
47
48
|
if number_phone.startswith("0"):
|
48
49
|
number_phone = number_phone[1:]
|
49
|
-
is_login =
|
50
|
+
is_login = self.login()
|
50
51
|
if not is_login:
|
51
52
|
print("Error Login !")
|
52
53
|
exit()
|
53
|
-
text_json_py_slpus_session = {
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
}
|
60
|
-
with open(name, "w", encoding="utf-8") as file:
|
61
|
-
|
62
|
-
|
63
|
-
|
54
|
+
# text_json_py_slpus_session = {
|
55
|
+
# "name_session": name_session,
|
56
|
+
# "number_phone":number_phone,
|
57
|
+
# "user_agent": user_agent,
|
58
|
+
# "time_out": time_out,
|
59
|
+
# "display_welcome": display_welcome,
|
60
|
+
# }
|
61
|
+
# with open(name, "w", encoding="utf-8") as file:
|
62
|
+
# json.dump(
|
63
|
+
# text_json_py_slpus_session, file, ensure_ascii=False, indent=4
|
64
|
+
# )
|
64
65
|
self.time_out = time_out
|
65
66
|
self.user_agent = user_agent
|
66
67
|
self.number_phone = number_phone
|
@@ -77,6 +78,7 @@ class Client:
|
|
77
78
|
if not number.startswith("9"):
|
78
79
|
return False
|
79
80
|
return True
|
81
|
+
@async_to_sync
|
80
82
|
async def login(self) -> bool:
|
81
83
|
"""لاگین / login"""
|
82
84
|
chrome_options = Options()
|
@@ -86,23 +88,23 @@ class Client:
|
|
86
88
|
chrome_options.add_argument("--lang=fa")
|
87
89
|
chrome_options.add_experimental_option("detach", True)
|
88
90
|
service = Service(ChromeDriverManager().install())
|
89
|
-
driver = webdriver.Chrome(service=service, options=chrome_options)
|
90
91
|
self.driver = webdriver.Chrome(service=service, options=chrome_options)
|
91
|
-
wait = WebDriverWait(driver,
|
92
|
+
wait = WebDriverWait(self.driver, 30)
|
92
93
|
try:
|
93
|
-
driver.get("https://web.splus.ir")
|
94
|
+
self.driver.get("https://web.splus.ir")
|
95
|
+
wait.until(lambda d: d.execute_script("return document.readyState") == "complete")
|
94
96
|
time.sleep(1)
|
95
97
|
is_open_cookies = False
|
96
98
|
if os.path.exists(self.name_cookies):
|
97
99
|
with open(self.name_cookies, 'rb') as file:
|
98
100
|
cookies = pickle.load(file)
|
99
101
|
for cookie in cookies:
|
100
|
-
driver.add_cookie(cookie)
|
102
|
+
self.driver.add_cookie(cookie)
|
101
103
|
is_open_cookies = True
|
102
104
|
if is_open_cookies:
|
103
|
-
driver.refresh()
|
105
|
+
self.driver.refresh()
|
104
106
|
try:
|
105
|
-
understand_button = WebDriverWait(driver, 3).until(
|
107
|
+
understand_button = WebDriverWait(self.driver, 3).until(
|
106
108
|
EC.element_to_be_clickable((By.XPATH, "//button[contains(text(), 'متوجه شدم')]"))
|
107
109
|
)
|
108
110
|
understand_button.click()
|
@@ -124,25 +126,47 @@ class Client:
|
|
124
126
|
code_input = wait.until(
|
125
127
|
EC.presence_of_element_located((By.CSS_SELECTOR, "input#sign-in-code"))
|
126
128
|
)
|
127
|
-
self.code_html = driver.page_source
|
129
|
+
self.code_html = self.driver.page_source
|
128
130
|
code_input.clear()
|
129
131
|
code_input.send_keys(verification_code)
|
130
132
|
time.sleep(5)
|
131
|
-
self.code_html = driver.page_source
|
133
|
+
self.code_html = self.driver.page_source
|
132
134
|
messages = await self.get_chat_ids()
|
133
135
|
while not messages:
|
134
136
|
time.sleep(1)
|
135
|
-
self.code_html = driver.page_source
|
137
|
+
self.code_html = self.driver.page_source
|
136
138
|
messages = await self.get_chat_ids()
|
137
139
|
with open(self.name_cookies, 'wb') as file:
|
138
|
-
pickle.dump(driver.get_cookies(), file)
|
140
|
+
pickle.dump(self.driver.get_cookies(), file)
|
139
141
|
return True
|
140
142
|
except Exception as e:
|
141
|
-
|
143
|
+
print("/*-+123456789*-+")
|
144
|
+
print(format_exc())
|
145
|
+
print("123456789*-+")
|
146
|
+
self.driver.save_screenshot("error_screenshot.png")
|
142
147
|
print("ERROR :")
|
143
148
|
print(e)
|
144
149
|
print("ERROR SAVED : error_screenshot.png")
|
145
150
|
return False
|
151
|
+
|
152
|
+
@async_to_sync
|
153
|
+
async def get_type_chat_id(
|
154
|
+
self,
|
155
|
+
chat_id:str
|
156
|
+
) -> Literal["Channel","Group","Bot","User",None]:
|
157
|
+
"""getting chat id type / گرفتن نوع چت آیدی"""
|
158
|
+
if chat_id.startswith("-"):
|
159
|
+
if len(chat_id) == 11:
|
160
|
+
return "Channel"
|
161
|
+
elif len(chat_id) == 12:
|
162
|
+
return "Group"
|
163
|
+
if len(chat_id) == 6:
|
164
|
+
return "User"
|
165
|
+
elif len(chat_id) == 8:
|
166
|
+
return "Bot"
|
167
|
+
return None
|
168
|
+
|
169
|
+
@async_to_sync
|
146
170
|
async def get_chat_ids(self) -> list:
|
147
171
|
"""گرفتن چت آیدی ها / getting chat ids"""
|
148
172
|
soup = BeautifulSoup(self.code_html, "html.parser")
|
@@ -162,24 +186,82 @@ class Client:
|
|
162
186
|
if a!=None:
|
163
187
|
chat = str(a["href"]).replace("#","")
|
164
188
|
chats.append(chat)
|
165
|
-
else:
|
166
|
-
print("❌ تگ ریشه پیدا نشد!")
|
167
189
|
return chats
|
168
|
-
|
190
|
+
|
191
|
+
@async_to_sync
|
192
|
+
async def get_chats(self) -> list:
|
193
|
+
"""گرفتن چت ها / getting chats"""
|
194
|
+
soup = BeautifulSoup(self.code_html, "html.parser")
|
195
|
+
root = soup.select_one(
|
196
|
+
"body > #UiLoader > div.Transition.full-height > "
|
197
|
+
"#Main.left-column-shown.left-column-open > "
|
198
|
+
"#LeftColumn > #LeftColumn-main > div.Transition > "
|
199
|
+
"div.ChatFolders.not-open.not-shown > div.Transition > "
|
200
|
+
"div.chat-list.custom-scroll > div[style*='position: relative']"
|
201
|
+
)
|
202
|
+
chats_ = []
|
203
|
+
chats = []
|
204
|
+
if root:
|
205
|
+
divs = root.find_all("div", recursive=True)
|
206
|
+
for div in divs:
|
207
|
+
anchors = div.find_all("a", href=True)
|
208
|
+
for a in anchors:
|
209
|
+
if a!=None:
|
210
|
+
chat = str(a["href"]).replace("#","")
|
211
|
+
chats_.append(chat)
|
212
|
+
for chat in chats_:
|
213
|
+
type_chat = await self.get_type_chat_id(chat)
|
214
|
+
chats.append({
|
215
|
+
"chat_id":chat,
|
216
|
+
"type_chat":type_chat
|
217
|
+
})
|
218
|
+
return chats
|
219
|
+
|
220
|
+
@async_to_sync
|
221
|
+
async def open_chat(self, chat_id: str) -> bool:
|
222
|
+
"""opening chat / باز کردن چت"""
|
223
|
+
try:
|
224
|
+
self.driver.get("https://web.splus.ir")
|
225
|
+
WebDriverWait(self.driver, 60).until(
|
226
|
+
EC.presence_of_element_located((By.CSS_SELECTOR, "div.chat-list, div[role='main']"))
|
227
|
+
)
|
228
|
+
chat_link = WebDriverWait(self.driver, 20).until(
|
229
|
+
EC.element_to_be_clickable((By.CSS_SELECTOR, f'a[href="#{chat_id}"]'))
|
230
|
+
)
|
231
|
+
chat_link.click()
|
232
|
+
print(f"✅ Chat {chat_id} opened.")
|
233
|
+
WebDriverWait(self.driver, 30).until(
|
234
|
+
EC.presence_of_element_located((By.CSS_SELECTOR, "div[contenteditable='true']"))
|
235
|
+
)
|
236
|
+
return True
|
237
|
+
except Exception as e:
|
238
|
+
print("❌ Error in open_chat : ", e)
|
239
|
+
self.driver.save_screenshot("open_chat_error.png")
|
240
|
+
return False
|
241
|
+
|
242
|
+
@async_to_sync
|
243
|
+
async def send_text(self, chat_id: str, text: str) -> bool:
|
169
244
|
"""ارسال متن / sending text"""
|
170
245
|
try:
|
171
|
-
self.
|
172
|
-
|
173
|
-
|
174
|
-
EC.presence_of_element_located((By.CSS_SELECTOR, "div.input-message-input"))
|
246
|
+
await self.open_chat(chat_id)
|
247
|
+
WebDriverWait(self.driver, 25).until(
|
248
|
+
EC.presence_of_element_located((By.CSS_SELECTOR, "div[contenteditable='true']"))
|
175
249
|
)
|
176
|
-
input_box.
|
177
|
-
|
178
|
-
|
179
|
-
|
250
|
+
input_box = self.driver.find_element(By.CSS_SELECTOR, "div[contenteditable='true']")
|
251
|
+
self.driver.execute_script("""
|
252
|
+
arguments[0].innerText = arguments[1];
|
253
|
+
arguments[0].dispatchEvent(new Event('input', { bubbles: true }));
|
254
|
+
""", input_box, text)
|
255
|
+
send_button = WebDriverWait(self.driver, 30).until(
|
256
|
+
EC.element_to_be_clickable((
|
257
|
+
By.CSS_SELECTOR,
|
258
|
+
"button.Button.send.main-button.default.secondary.round.click-allowed"
|
259
|
+
))
|
180
260
|
)
|
181
261
|
send_button.click()
|
262
|
+
print("✅ Message sent successfully.")
|
182
263
|
return True
|
183
264
|
except Exception as e:
|
184
|
-
print("Error in send_text:
|
265
|
+
print(f"❌ Error in send_text : {e}")
|
266
|
+
self.driver.save_screenshot("send_text_error.png")
|
185
267
|
return False
|
pysplus/async_sync.py
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
import asyncio
|
2
|
+
from functools import wraps
|
3
|
+
|
4
|
+
def async_to_sync(func):
|
5
|
+
"""دکوراتور برای تبدیل تابع async به sync"""
|
6
|
+
@wraps(func)
|
7
|
+
def wrapper(*args, **kwargs):
|
8
|
+
try:
|
9
|
+
loop = asyncio.get_running_loop()
|
10
|
+
async def coro_wrapper():
|
11
|
+
return await func(*args, **kwargs)
|
12
|
+
return coro_wrapper()
|
13
|
+
except RuntimeError:
|
14
|
+
return asyncio.run(func(*args, **kwargs))
|
15
|
+
return wrapper
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: PySPlus
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.5
|
4
4
|
Summary: the library SPlus platform for bots.
|
5
5
|
Home-page: https://github.com/OandONE/SPlus
|
6
6
|
Author: seyyed mohamad hosein moosavi raja(01)
|
@@ -21,11 +21,11 @@ Dynamic: requires-dist
|
|
21
21
|
Dynamic: requires-python
|
22
22
|
Dynamic: summary
|
23
23
|
|
24
|
-
#
|
24
|
+
# PySPlus
|
25
25
|
|
26
26
|
This Python library is for SPlus platform bots and is currently being updated.
|
27
27
|
|
28
|
-
##
|
28
|
+
## PySPlus
|
29
29
|
|
30
30
|
- 1 fast
|
31
31
|
- 2 simple syntax
|
@@ -0,0 +1,8 @@
|
|
1
|
+
pysplus/Client.py,sha256=QuNKFuDiSaysh93_0wfXKsY_PGCg1sEqYGdvuTLQRaU,11552
|
2
|
+
pysplus/__init__.py,sha256=D6b12F-lYPY38W7ymcNtCgUQb7tu79scbEcbDjIRR5c,97
|
3
|
+
pysplus/async_sync.py,sha256=d9VSE7_A7zgOI8BAlZoxfm8LdkdWxyZdpgAEOrRpQcg,489
|
4
|
+
pysplus/colors.py,sha256=SwcpovYEff7gDHMtWZAjYnVSbvhqSiHXlAoa4eSJ9RM,556
|
5
|
+
pysplus-0.5.dist-info/METADATA,sha256=yqXKWChLEIVeFXJR5-l4anNFJyA2z8pjHVmT2EVM0Sc,854
|
6
|
+
pysplus-0.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
7
|
+
pysplus-0.5.dist-info/top_level.txt,sha256=h2PKSbKoDxAHsNE8pxuQY8VllsI-4KQW_Udcd7DKQkA,8
|
8
|
+
pysplus-0.5.dist-info/RECORD,,
|
pysplus-0.3.dist-info/RECORD
DELETED
@@ -1,7 +0,0 @@
|
|
1
|
-
pysplus/Client.py,sha256=emZEP-Ct22GkmR_GsofohEUUBh-hMrbMTBPFlPFuiak,8256
|
2
|
-
pysplus/__init__.py,sha256=D6b12F-lYPY38W7ymcNtCgUQb7tu79scbEcbDjIRR5c,97
|
3
|
-
pysplus/colors.py,sha256=SwcpovYEff7gDHMtWZAjYnVSbvhqSiHXlAoa4eSJ9RM,556
|
4
|
-
pysplus-0.3.dist-info/METADATA,sha256=6kCTAaI-d-0XxjA94i_uilP1cGePT3gEoj0SSgeQx-Q,856
|
5
|
-
pysplus-0.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
6
|
-
pysplus-0.3.dist-info/top_level.txt,sha256=h2PKSbKoDxAHsNE8pxuQY8VllsI-4KQW_Udcd7DKQkA,8
|
7
|
-
pysplus-0.3.dist-info/RECORD,,
|
File without changes
|
File without changes
|