mirror of
https://github.com/cqwu-ehall/cqwu-ehall.git
synced 2024-11-22 11:01:20 +00:00
✨ Support get calendar
Co-authored-by: brian <brian@xtaolabs.com>
This commit is contained in:
parent
e2a43b1c5d
commit
ca794ea619
@ -15,22 +15,21 @@ class Client(Methods):
|
|||||||
password: str = None,
|
password: str = None,
|
||||||
cookie: str = None,
|
cookie: str = None,
|
||||||
cookie_file_path: str = "cookie.txt",
|
cookie_file_path: str = "cookie.txt",
|
||||||
client_vpn: bool = False,
|
|
||||||
):
|
):
|
||||||
self.username = username
|
self.username = username
|
||||||
self.password = password
|
self.password = password
|
||||||
self.cookie = cookie
|
self.cookie = cookie
|
||||||
self.cookie_file_path = cookie_file_path
|
self.cookie_file_path = cookie_file_path
|
||||||
if client_vpn:
|
|
||||||
self.host = "https://clientvpn.cqwu.edu.cn:10443/http/webvpn507e990968de07079b0f10d16c49bdb1cb8d3ca3a4d14f557999e92cbdf19fcd"
|
|
||||||
self.auth_host = self.host
|
|
||||||
else:
|
|
||||||
self.host = "http://ehall.cqwu.edu.cn"
|
self.host = "http://ehall.cqwu.edu.cn"
|
||||||
self.auth_host = "http://authserver.cqwu.edu.cn"
|
self.auth_host = "http://authserver.cqwu.edu.cn"
|
||||||
|
self.web_ehall_path = ""
|
||||||
self.cookies = Cookies()
|
self.cookies = Cookies()
|
||||||
self.request = AsyncClient()
|
self.request = AsyncClient()
|
||||||
self.loop = asyncio.get_event_loop()
|
self.loop = asyncio.get_event_loop()
|
||||||
self.me: Optional[User] = None
|
self.me: Optional[User] = None
|
||||||
|
self._use_password_login = False
|
||||||
|
self.xue_nian = 2022 # 学年
|
||||||
|
self.xue_qi = 1 # 学期 0 为第一学期, 1 为第二学期
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_input(word: str = "", is_int: bool = False):
|
def get_input(word: str = "", is_int: bool = False):
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
from .auth import Auth
|
from .auth import Auth
|
||||||
from .epay import EPay
|
from .epay import EPay
|
||||||
from .users import Users
|
from .users import Users
|
||||||
|
from .webvpn import WebVPN
|
||||||
from .xg import XG
|
from .xg import XG
|
||||||
|
|
||||||
|
|
||||||
@ -8,6 +9,7 @@ class Methods(
|
|||||||
Auth,
|
Auth,
|
||||||
EPay,
|
EPay,
|
||||||
Users,
|
Users,
|
||||||
|
WebVPN,
|
||||||
XG,
|
XG,
|
||||||
):
|
):
|
||||||
pass
|
pass
|
||||||
|
@ -10,10 +10,12 @@ class LoginWithPassword:
|
|||||||
self: "cqwu.Client",
|
self: "cqwu.Client",
|
||||||
captcha_code: str = None,
|
captcha_code: str = None,
|
||||||
show_qrcode: bool = True,
|
show_qrcode: bool = True,
|
||||||
|
auth_host: str = None,
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
使用学号加密码登录
|
使用学号加密码登录
|
||||||
"""
|
"""
|
||||||
|
auth_host = auth_host or self.auth_host
|
||||||
headers = {
|
headers = {
|
||||||
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
|
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
|
||||||
'Accept-Language': 'zh-CN,zh;q=0.9',
|
'Accept-Language': 'zh-CN,zh;q=0.9',
|
||||||
@ -22,7 +24,7 @@ class LoginWithPassword:
|
|||||||
'Upgrade-Insecure-Requests': '1',
|
'Upgrade-Insecure-Requests': '1',
|
||||||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36 Edg/110.0.1587.63',
|
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36 Edg/110.0.1587.63',
|
||||||
}
|
}
|
||||||
html = await self.request.get(f"{self.auth_host}/authserver/login", headers=headers, follow_redirects=True)
|
html = await self.request.get(f"{auth_host}/authserver/login", headers=headers, follow_redirects=True)
|
||||||
self.cookies.update(html.cookies)
|
self.cookies.update(html.cookies)
|
||||||
tree = etree.HTML(html.text)
|
tree = etree.HTML(html.text)
|
||||||
pwd_default_encrypt_salt = tree.xpath('//*[@id="pwdDefaultEncryptSalt"]/@value')[0]
|
pwd_default_encrypt_salt = tree.xpath('//*[@id="pwdDefaultEncryptSalt"]/@value')[0]
|
||||||
@ -46,18 +48,20 @@ class LoginWithPassword:
|
|||||||
'Accept-Language': 'zh-CN,zh;q=0.9',
|
'Accept-Language': 'zh-CN,zh;q=0.9',
|
||||||
'Cache-Control': 'max-age=0',
|
'Cache-Control': 'max-age=0',
|
||||||
'Connection': 'keep-alive',
|
'Connection': 'keep-alive',
|
||||||
'Origin': self.auth_host,
|
'Origin': auth_host,
|
||||||
'Referer': f'{self.auth_host}/authserver/login',
|
'Referer': f'{auth_host}/authserver/login',
|
||||||
'Upgrade-Insecure-Requests': '1',
|
'Upgrade-Insecure-Requests': '1',
|
||||||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36 Edg/110.0.1587.63',
|
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36 Edg/110.0.1587.63',
|
||||||
}
|
}
|
||||||
html = await self.request.post(
|
html = await self.request.post(
|
||||||
f"{self.auth_host}/authserver/login",
|
f"{auth_host}/authserver/login",
|
||||||
headers=headers,
|
headers=headers,
|
||||||
data=form_data,
|
data=form_data,
|
||||||
follow_redirects=False,
|
follow_redirects=False,
|
||||||
)
|
)
|
||||||
|
if auth_host == self.auth_host:
|
||||||
if 'CASTGC' not in html.cookies.keys():
|
if 'CASTGC' not in html.cookies.keys():
|
||||||
raise UsernameOrPasswordError
|
raise UsernameOrPasswordError
|
||||||
self.cookies.update(html.cookies)
|
self.cookies.update(html.cookies)
|
||||||
|
self._use_password_login = True # noqa
|
||||||
self.me = await self.get_me() # noqa
|
self.me = await self.get_me() # noqa
|
||||||
|
22
cqwu/methods/webvpn/__init__.py
Normal file
22
cqwu/methods/webvpn/__init__.py
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
from httpx import URL
|
||||||
|
|
||||||
|
from .get_calendar import GetCalendar
|
||||||
|
from .get_calendar_change import GetCalendarChange
|
||||||
|
from .login_webvpn import LoginWebVPN
|
||||||
|
|
||||||
|
|
||||||
|
class WebVPN(
|
||||||
|
GetCalendar,
|
||||||
|
GetCalendarChange,
|
||||||
|
LoginWebVPN,
|
||||||
|
):
|
||||||
|
@staticmethod
|
||||||
|
def get_web_vpn_host(url: URL, https: bool = False) -> str:
|
||||||
|
return next(
|
||||||
|
(
|
||||||
|
f"https://clientvpn.cqwu.edu.cn/{'https' if https else 'http'}/{i}"
|
||||||
|
for i in str(url).split("/")
|
||||||
|
if i.startswith("webvpn")
|
||||||
|
),
|
||||||
|
None,
|
||||||
|
)
|
42
cqwu/methods/webvpn/get_calendar.py
Normal file
42
cqwu/methods/webvpn/get_calendar.py
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
import base64
|
||||||
|
import cqwu
|
||||||
|
from cqwu.errors import CookieError
|
||||||
|
|
||||||
|
|
||||||
|
class GetCalendar:
|
||||||
|
async def get_calendar(
|
||||||
|
self: "cqwu.Client",
|
||||||
|
xue_nian: int = None,
|
||||||
|
xue_qi: int = None,
|
||||||
|
) -> str:
|
||||||
|
""" 获取课程表 """
|
||||||
|
xue_nian = xue_nian or self.xue_nian
|
||||||
|
xue_qi = xue_qi or self.xue_qi
|
||||||
|
jw_html = await self.request.get(
|
||||||
|
f"{self.web_ehall_path}/appShow?appId=5299144291521305", follow_redirects=True
|
||||||
|
)
|
||||||
|
if "教学管理服务平台" not in jw_html.text:
|
||||||
|
raise CookieError
|
||||||
|
jw_host = self.get_web_vpn_host(jw_html.url)
|
||||||
|
jw_url = f"{jw_host}/cqwljw/student/wsxk.xskcb10319.jsp"
|
||||||
|
params = {
|
||||||
|
"params": base64.b64encode(f"xn={xue_nian}&xq={xue_qi}".encode()).decode(),
|
||||||
|
}
|
||||||
|
headers = {
|
||||||
|
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
|
||||||
|
'Accept-Language': 'zh-CN,zh;q=0.9',
|
||||||
|
'Connection': 'keep-alive',
|
||||||
|
'Referer': f'{jw_host}/cqwljw/student/xkjg.wdkb.jsp?menucode=S20301',
|
||||||
|
'Sec-Fetch-Dest': 'iframe',
|
||||||
|
'Sec-Fetch-Mode': 'navigate',
|
||||||
|
'Sec-Fetch-Site': 'same-origin',
|
||||||
|
'Sec-Fetch-User': '?1',
|
||||||
|
'Upgrade-Insecure-Requests': '1',
|
||||||
|
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36 Edg/111.0.1661.41',
|
||||||
|
'sec-ch-ua': '"Microsoft Edge";v="111", "Not(A:Brand";v="8", "Chromium";v="111"',
|
||||||
|
'sec-ch-ua-mobile': '?0',
|
||||||
|
'sec-ch-ua-platform': '"Windows"',
|
||||||
|
}
|
||||||
|
jw_html = await self.request.get(jw_url, params=params, headers=headers, timeout=60, follow_redirects=True)
|
||||||
|
jw_html = jw_html.text.replace("""<script type="text/javascript" src="//clientvpn.cqwu.edu.cn/webvpn/bundle.debug.js" charset="utf-8"></script>""", "")
|
||||||
|
return jw_html.replace("<title></title>", '<meta charset="UTF-8">')
|
73
cqwu/methods/webvpn/get_calendar_change.py
Normal file
73
cqwu/methods/webvpn/get_calendar_change.py
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
import cqwu
|
||||||
|
from cqwu.errors import CookieError
|
||||||
|
|
||||||
|
|
||||||
|
def get_in_middle(text: str, start: str, end: str) -> str:
|
||||||
|
return text.split(start, 1)[1].split(end, 1)[0]
|
||||||
|
|
||||||
|
|
||||||
|
class GetCalendarChange:
|
||||||
|
async def get_calendar_change(
|
||||||
|
self: "cqwu.Client",
|
||||||
|
xue_nian: int = None,
|
||||||
|
xue_qi: int = None,
|
||||||
|
) -> str:
|
||||||
|
""" 获取课程表 """
|
||||||
|
xue_nian = xue_nian or self.xue_nian
|
||||||
|
xue_qi = xue_qi or self.xue_qi
|
||||||
|
jw_html = await self.request.get(
|
||||||
|
f"{self.web_ehall_path}/appShow?appId=5299144291521305", follow_redirects=True
|
||||||
|
)
|
||||||
|
if "教学管理服务平台" not in jw_html.text:
|
||||||
|
raise CookieError
|
||||||
|
jw_host = self.get_web_vpn_host(jw_html.url, https=True)
|
||||||
|
jw_url = f"{jw_host}/cqwljw/student/jxap.jxaptzxx_rpt.jsp"
|
||||||
|
jw_sg_url = f"{jw_host}/cqwljw/STU_DynamicInitDataAction.do"
|
||||||
|
headers = {
|
||||||
|
'Accept': '*/*',
|
||||||
|
'Accept-Language': 'zh-CN,zh;q=0.9',
|
||||||
|
'Connection': 'keep-alive',
|
||||||
|
'Origin': 'https://clientvpn.cqwu.edu.cn',
|
||||||
|
'Referer': f'{jw_host}/cqwljw/student/jxap.jxaptzxx.html?menucode=S20302',
|
||||||
|
'Sec-Fetch-Dest': 'empty',
|
||||||
|
'Sec-Fetch-Mode': 'cors',
|
||||||
|
'Sec-Fetch-Site': 'same-origin',
|
||||||
|
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36 Edg/111.0.1661.41',
|
||||||
|
'content-type': 'application/x-www-form-urlencoded',
|
||||||
|
'sec-ch-ua': '"Microsoft Edge";v="111", "Not(A:Brand";v="8", "Chromium";v="111"',
|
||||||
|
'sec-ch-ua-mobile': '?0',
|
||||||
|
'sec-ch-ua-platform': '"Windows"',
|
||||||
|
}
|
||||||
|
params = {
|
||||||
|
"classPath": "C73E288D0DEA8D7F772BBD7F8FDC7E66F44C9E3992261989ECBAC5A3D722B306C6354658E0F25121E24CED075326C19885F263F369E5CD668E2EEE7CFB7EB5788F202FC6FD7DB0C96FB6995C1DD96ADE84BE3E72CFFBE9EC74FA044498BD2D21EA0439F9DC625F0EF61B7159924C542D577F814848F27128"
|
||||||
|
}
|
||||||
|
res = await self.request.post(jw_sg_url, params=params, headers=headers, timeout=60, follow_redirects=True)
|
||||||
|
data = {
|
||||||
|
'xh': get_in_middle(res.text, '<xh>', '</xh>'),
|
||||||
|
'xn': str(xue_nian),
|
||||||
|
'xq': str(xue_qi),
|
||||||
|
'xnxq': f'{xue_nian},{xue_qi}',
|
||||||
|
'menucode_current': 'S20302',
|
||||||
|
}
|
||||||
|
headers = {
|
||||||
|
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
|
||||||
|
'Accept-Language': 'zh-CN,zh;q=0.9',
|
||||||
|
'Cache-Control': 'max-age=0',
|
||||||
|
'Connection': 'keep-alive',
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded',
|
||||||
|
'Origin': 'https://clientvpn.cqwu.edu.cn',
|
||||||
|
'Referer': f'{jw_host}/cqwljw/student/jxap.jxaptzxx.html?menucode=S20302',
|
||||||
|
'Sec-Fetch-Dest': 'iframe',
|
||||||
|
'Sec-Fetch-Mode': 'navigate',
|
||||||
|
'Sec-Fetch-Site': 'same-origin',
|
||||||
|
'Sec-Fetch-User': '?1',
|
||||||
|
'Upgrade-Insecure-Requests': '1',
|
||||||
|
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36 Edg/111.0.1661.41',
|
||||||
|
'sec-ch-ua': '"Microsoft Edge";v="111", "Not(A:Brand";v="8", "Chromium";v="111"',
|
||||||
|
'sec-ch-ua-mobile': '?0',
|
||||||
|
'sec-ch-ua-platform': '"Windows"',
|
||||||
|
}
|
||||||
|
jw_html = await self.request.post(jw_url, data=data, headers=headers, timeout=60, follow_redirects=True)
|
||||||
|
jw_html = jw_html.text.replace("""<script type="text/javascript" src="//clientvpn.cqwu.edu.cn/webvpn/bundle.debug.js" charset="utf-8"></script>""", "")
|
||||||
|
jw_html = jw_html.replace("""<script language='javascript' type='text/javascript' src='../js/Print.js'></script>""", "")
|
||||||
|
return jw_html.replace("<title></title>", '<meta charset="UTF-8">')
|
21
cqwu/methods/webvpn/login_webvpn.py
Normal file
21
cqwu/methods/webvpn/login_webvpn.py
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import cqwu
|
||||||
|
|
||||||
|
|
||||||
|
class LoginWebVPN:
|
||||||
|
async def login_web_vpn(
|
||||||
|
self: "cqwu.Client",
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
登录 WebVPN
|
||||||
|
"""
|
||||||
|
if not self._use_password_login:
|
||||||
|
return
|
||||||
|
url = "https://webvpn.cqwu.edu.cn"
|
||||||
|
ehall_html = await self.request.get(url, follow_redirects=True)
|
||||||
|
self.web_ehall_path = self.get_web_vpn_host(ehall_html.url) # noqa
|
||||||
|
await self.oauth("http://authserver.cqwu.edu.cn/authserver/login?service=https://clientvpn.cqwu.edu.cn/enlink/api/client/callback/cas")
|
||||||
|
auth_html = await self.request.get(
|
||||||
|
f"{self.web_ehall_path}/login", follow_redirects=True
|
||||||
|
)
|
||||||
|
if web_auth_path := self.get_web_vpn_host(auth_html.url):
|
||||||
|
await self.login_with_password(auth_host=web_auth_path)
|
@ -9,8 +9,8 @@ from cqwu.errors.auth import CookieError
|
|||||||
class GetScore:
|
class GetScore:
|
||||||
async def get_score(
|
async def get_score(
|
||||||
self: "cqwu.Client",
|
self: "cqwu.Client",
|
||||||
year: int = 2022,
|
year: int = None,
|
||||||
semester: int = 1,
|
semester: int = None,
|
||||||
) -> List["types.Score"]:
|
) -> List["types.Score"]:
|
||||||
"""
|
"""
|
||||||
获取期末成绩
|
获取期末成绩
|
||||||
@ -18,6 +18,8 @@ class GetScore:
|
|||||||
Returns:
|
Returns:
|
||||||
List[types.Score]: 成绩列表
|
List[types.Score]: 成绩列表
|
||||||
"""
|
"""
|
||||||
|
year = year or self.xue_nian
|
||||||
|
semester = semester or self.xue_qi
|
||||||
url = "http://xg.cqwu.edu.cn/xsfw/sys/zhcptybbapp/*default/index.do#/cjcx"
|
url = "http://xg.cqwu.edu.cn/xsfw/sys/zhcptybbapp/*default/index.do#/cjcx"
|
||||||
html = await self.oauth(url)
|
html = await self.oauth(url)
|
||||||
if not html:
|
if not html:
|
||||||
|
4
setup.py
4
setup.py
@ -5,7 +5,7 @@ with open("README.md", "r", encoding="utf-8") as fh:
|
|||||||
|
|
||||||
setuptools.setup(
|
setuptools.setup(
|
||||||
name="cqwu", # 用自己的名替换其中的YOUR_USERNAME_
|
name="cqwu", # 用自己的名替换其中的YOUR_USERNAME_
|
||||||
version="0.0.3", # 包版本号,便于维护版本
|
version="0.0.4", # 包版本号,便于维护版本
|
||||||
author="omg-xtao", # 作者,可以写自己的姓名
|
author="omg-xtao", # 作者,可以写自己的姓名
|
||||||
author_email="xtao@xtaolink.cn", # 作者联系方式,可写自己的邮箱地址
|
author_email="xtao@xtaolink.cn", # 作者联系方式,可写自己的邮箱地址
|
||||||
description="A cqwu ehall client.", # 包的简述
|
description="A cqwu ehall client.", # 包的简述
|
||||||
@ -18,7 +18,7 @@ setuptools.setup(
|
|||||||
"License :: OSI Approved :: MIT License",
|
"License :: OSI Approved :: MIT License",
|
||||||
"Operating System :: OS Independent",
|
"Operating System :: OS Independent",
|
||||||
],
|
],
|
||||||
python_requires='>=3.6', # 对python的最低版本要求
|
python_requires='>=3.8', # 对python的最低版本要求
|
||||||
install_requires=[
|
install_requires=[
|
||||||
"httpx",
|
"httpx",
|
||||||
"lxml",
|
"lxml",
|
||||||
|
Loading…
Reference in New Issue
Block a user