mirror of
https://github.com/cqwu-ehall/cqwu-ehall.git
synced 2024-11-22 02:55:33 +00:00
✨ Support get score
Co-authored-by: brian <brian@xtaolabs.com>
This commit is contained in:
parent
499fd90264
commit
60c32861ea
@ -1,7 +1,6 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
from typing import Coroutine, Optional
|
from typing import Coroutine, Optional
|
||||||
from httpx import AsyncClient, Cookies
|
from httpx import AsyncClient, Cookies
|
||||||
from urllib.parse import urlparse
|
|
||||||
|
|
||||||
from cqwu.methods import Methods
|
from cqwu.methods import Methods
|
||||||
from cqwu.types import User
|
from cqwu.types import User
|
||||||
@ -28,19 +27,8 @@ class Client(Methods):
|
|||||||
else:
|
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.headers = {
|
|
||||||
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
|
|
||||||
"Accept-Encoding": "gzip, deflate",
|
|
||||||
"Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8",
|
|
||||||
"Host": urlparse(self.host).netloc,
|
|
||||||
"Origin": self.host,
|
|
||||||
"User-Agent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36"
|
|
||||||
}
|
|
||||||
self.cookies = Cookies()
|
self.cookies = Cookies()
|
||||||
self.sub_cookies = Cookies()
|
self.request = AsyncClient()
|
||||||
self.init_sub_web = []
|
|
||||||
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
|
||||||
|
|
||||||
|
@ -1 +0,0 @@
|
|||||||
from .order import OrderStatus
|
|
@ -1,9 +0,0 @@
|
|||||||
from enum import Enum
|
|
||||||
|
|
||||||
|
|
||||||
class OrderStatus(Enum):
|
|
||||||
NO_PAY = 0
|
|
||||||
NO_PAY_RE = 1
|
|
||||||
SUCCESS = 2
|
|
||||||
FAILURE = 3
|
|
||||||
EXPIRED = 4
|
|
@ -1,6 +1,19 @@
|
|||||||
class UsernameOrPasswordError(Exception):
|
from .base import CQWUEhallError
|
||||||
|
|
||||||
|
|
||||||
|
class AuthError(CQWUEhallError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class CookieError(Exception):
|
class UsernameOrPasswordError(AuthError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class CookieError(AuthError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class NeedCaptchaError(AuthError):
|
||||||
|
""" 需要验证码才能登录 """
|
||||||
|
def __init__(self, captcha: bytes):
|
||||||
|
self.captcha = captcha
|
||||||
|
3
cqwu/errors/base.py
Normal file
3
cqwu/errors/base.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
class CQWUEhallError(Exception):
|
||||||
|
"""Base class for exceptions in this module."""
|
||||||
|
pass
|
@ -1,11 +1,13 @@
|
|||||||
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 .xg import XG
|
||||||
|
|
||||||
|
|
||||||
class Methods(
|
class Methods(
|
||||||
Auth,
|
Auth,
|
||||||
EPay,
|
EPay,
|
||||||
Users
|
Users,
|
||||||
|
XG,
|
||||||
):
|
):
|
||||||
pass
|
pass
|
||||||
|
@ -1,15 +1,19 @@
|
|||||||
from .login_with_password import LoginWithPassword
|
from .check_captcha import CheckCaptcha
|
||||||
|
from .export_cookie_to_file import ExportCookieToFile
|
||||||
|
from .login import Login
|
||||||
from .login_with_cookie import LoginWithCookie
|
from .login_with_cookie import LoginWithCookie
|
||||||
from .login_with_cookie_file import LoginWithCookieFile
|
from .login_with_cookie_file import LoginWithCookieFile
|
||||||
from .export_cookie_to_file import ExportCookieToFile
|
from .login_with_password import LoginWithPassword
|
||||||
from .oauth import Oauth
|
from .oauth import Oauth
|
||||||
|
|
||||||
|
|
||||||
class Auth(
|
class Auth(
|
||||||
LoginWithPassword,
|
CheckCaptcha,
|
||||||
|
ExportCookieToFile,
|
||||||
|
Login,
|
||||||
LoginWithCookie,
|
LoginWithCookie,
|
||||||
LoginWithCookieFile,
|
LoginWithCookieFile,
|
||||||
ExportCookieToFile,
|
LoginWithPassword,
|
||||||
Oauth
|
Oauth
|
||||||
):
|
):
|
||||||
pass
|
pass
|
||||||
|
34
cqwu/methods/auth/check_captcha.py
Normal file
34
cqwu/methods/auth/check_captcha.py
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import time
|
||||||
|
|
||||||
|
import cqwu
|
||||||
|
from cqwu.errors.auth import NeedCaptchaError
|
||||||
|
|
||||||
|
|
||||||
|
class CheckCaptcha:
|
||||||
|
async def check_captcha(
|
||||||
|
self: "cqwu.Client",
|
||||||
|
username: int = None,
|
||||||
|
show_qrcode: bool = True,
|
||||||
|
):
|
||||||
|
""" 检查是否需要验证码 """
|
||||||
|
username = username or self.username
|
||||||
|
params = {
|
||||||
|
"username": username,
|
||||||
|
"pwdEncrypt2": "pwdEncryptSalt",
|
||||||
|
"_": str(round(time.time() * 1000))
|
||||||
|
}
|
||||||
|
url = f"{self.auth_host}/authserver/needCaptcha.html"
|
||||||
|
captcha_html = await self.request.get(url, params=params, follow_redirects=False)
|
||||||
|
if captcha_html.text == 'true':
|
||||||
|
params = {
|
||||||
|
"ts": str(round(time.time()))
|
||||||
|
}
|
||||||
|
captcha_url = f"{self.auth_host}/authserver/captcha.html"
|
||||||
|
res = await self.request.get(captcha_url, params=params, follow_redirects=False)
|
||||||
|
if not show_qrcode:
|
||||||
|
raise NeedCaptchaError(res.content)
|
||||||
|
with open("captcha.jpg", mode="wb") as f:
|
||||||
|
f.write(res.content)
|
||||||
|
print("验证码已保存在当前目录下的 captcha.jpg 文件中。")
|
||||||
|
return self.get_input("验证码")
|
||||||
|
return False
|
22
cqwu/methods/auth/login.py
Normal file
22
cqwu/methods/auth/login.py
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import contextlib
|
||||||
|
from os.path import exists
|
||||||
|
|
||||||
|
import cqwu
|
||||||
|
from cqwu.errors.auth import CookieError
|
||||||
|
|
||||||
|
|
||||||
|
class Login:
|
||||||
|
async def login(
|
||||||
|
self: "cqwu.Client",
|
||||||
|
):
|
||||||
|
""" 登录 """
|
||||||
|
with contextlib.suppress(CookieError):
|
||||||
|
if self.cookie:
|
||||||
|
await self.login_with_cookie()
|
||||||
|
elif exists(self.cookie_file_path):
|
||||||
|
await self.login_with_cookie_file()
|
||||||
|
return
|
||||||
|
if self.username and self.password:
|
||||||
|
await self.login_with_password()
|
||||||
|
else:
|
||||||
|
raise CookieError()
|
@ -5,13 +5,15 @@ from cqwu.errors.auth import CookieError
|
|||||||
class LoginWithCookie:
|
class LoginWithCookie:
|
||||||
async def login_with_cookie(
|
async def login_with_cookie(
|
||||||
self: "cqwu.Client",
|
self: "cqwu.Client",
|
||||||
|
cookie: str = None,
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
使用 cookie 登录
|
使用 cookie 登录
|
||||||
"""
|
"""
|
||||||
if not self.cookie:
|
cookie = cookie or self.cookie
|
||||||
|
if not cookie:
|
||||||
raise CookieError()
|
raise CookieError()
|
||||||
|
self.cookie = cookie # noqa
|
||||||
try:
|
try:
|
||||||
data = self.cookie.split(";")
|
data = self.cookie.split(";")
|
||||||
for cookie in data:
|
for cookie in data:
|
||||||
@ -19,7 +21,7 @@ class LoginWithCookie:
|
|||||||
continue
|
continue
|
||||||
key, value = cookie.split("=")
|
key, value = cookie.split("=")
|
||||||
self.cookies.set(key, value)
|
self.cookies.set(key, value)
|
||||||
self.sub_cookies.set(key, value)
|
self.request.cookies.set(key, value)
|
||||||
self.me = await self.get_me() # noqa
|
self.me = await self.get_me() # noqa
|
||||||
except:
|
except Exception as e:
|
||||||
raise CookieError()
|
raise CookieError() from e
|
||||||
|
@ -18,5 +18,5 @@ class LoginWithCookieFile:
|
|||||||
with open(self.cookie_file_path, "r") as f:
|
with open(self.cookie_file_path, "r") as f:
|
||||||
self.cookie = f.read() # noqa
|
self.cookie = f.read() # noqa
|
||||||
await self.login_with_cookie()
|
await self.login_with_cookie()
|
||||||
except:
|
except Exception as e:
|
||||||
raise CookieError()
|
raise CookieError() from e
|
||||||
|
@ -1,7 +1,3 @@
|
|||||||
import time
|
|
||||||
|
|
||||||
from datetime import datetime
|
|
||||||
from urllib.parse import urlencode, urlparse
|
|
||||||
from lxml import etree
|
from lxml import etree
|
||||||
|
|
||||||
import cqwu
|
import cqwu
|
||||||
@ -11,25 +7,22 @@ from cqwu.utils.auth import encode_password
|
|||||||
|
|
||||||
class LoginWithPassword:
|
class LoginWithPassword:
|
||||||
async def login_with_password(
|
async def login_with_password(
|
||||||
self: "cqwu.Client",
|
self: "cqwu.Client",
|
||||||
|
captcha_code: str = None,
|
||||||
|
show_qrcode: bool = True,
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
使用学号加密码登录
|
使用学号加密码登录
|
||||||
"""
|
"""
|
||||||
headers = {
|
headers = {
|
||||||
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
|
'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',
|
||||||
'Cache-Control': 'max-age=0',
|
'Cache-Control': 'max-age=0',
|
||||||
'Connection': 'keep-alive',
|
'Connection': 'keep-alive',
|
||||||
'DNT': '1',
|
|
||||||
'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/102.0.5005.167 Safari/537.36',
|
'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',
|
||||||
"Referer": f"{self.auth_host}/authserver/login",
|
|
||||||
"Origin": self.auth_host,
|
|
||||||
"Host": urlparse(self.auth_host).netloc
|
|
||||||
}
|
}
|
||||||
session = self.request(headers=self.headers, follow_redirects=True)
|
html = await self.request.get(f"{self.auth_host}/authserver/login", headers=headers, follow_redirects=True)
|
||||||
html = await session.get(f"{self.host}/authserver/login")
|
|
||||||
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]
|
||||||
@ -42,33 +35,27 @@ class LoginWithPassword:
|
|||||||
'_eventId': tree.xpath('//*[@id="casLoginForm"]/input[4]/@value')[0],
|
'_eventId': tree.xpath('//*[@id="casLoginForm"]/input[4]/@value')[0],
|
||||||
'rmShown': tree.xpath('//*[@id="casLoginForm"]/input[5]/@value')[0]
|
'rmShown': tree.xpath('//*[@id="casLoginForm"]/input[5]/@value')[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
# 是否需要验证码
|
# 是否需要验证码
|
||||||
params = {
|
if not captcha_code:
|
||||||
"username": self.username,
|
form_data['captchaResponse'] = await self.check_captcha(show_qrcode=show_qrcode)
|
||||||
"pwdEncrypt2": "pwdEncryptSalt",
|
|
||||||
"_": str(round(time.time() * 1000))
|
|
||||||
}
|
|
||||||
|
|
||||||
need_captcha_url = f"{self.auth_host}/authserver/needCaptcha.html?{urlencode(params)}"
|
|
||||||
async with self.request() as client:
|
|
||||||
html = await client.get(need_captcha_url, follow_redirects=False)
|
|
||||||
if html.text == 'true':
|
|
||||||
ts = round(datetime.now().microsecond / 1000) # get milliseconds
|
|
||||||
captcha_url = f"{self.auth_host}/authserver/captcha.html?" + urlencode({"ts": ts})
|
|
||||||
async with self.request() as client:
|
|
||||||
res = await client.get(captcha_url, follow_redirects=False)
|
|
||||||
with open("captcha.jpg", mode="wb") as f:
|
|
||||||
f.write(res.content)
|
|
||||||
print("验证码已保存在当前目录下的 captcha.jpg 文件中。")
|
|
||||||
code = self.get_input("验证码")
|
|
||||||
form_data['captchaResponse'] = code
|
|
||||||
|
|
||||||
# 登录
|
# 登录
|
||||||
async with self.request(headers=headers, cookies=self.cookies) as client:
|
headers = {
|
||||||
html = await client.post(f"{self.auth_host}/authserver/login", data=form_data, follow_redirects=False)
|
'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',
|
||||||
|
'Origin': self.auth_host,
|
||||||
|
'Referer': f'{self.auth_host}/authserver/login',
|
||||||
|
'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',
|
||||||
|
}
|
||||||
|
html = await self.request.post(
|
||||||
|
f"{self.auth_host}/authserver/login",
|
||||||
|
headers=headers,
|
||||||
|
data=form_data,
|
||||||
|
follow_redirects=False,
|
||||||
|
)
|
||||||
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.sub_cookies.update(html.cookies)
|
|
||||||
self.me = await self.get_me() # noqa
|
self.me = await self.get_me() # noqa
|
||||||
|
@ -13,10 +13,5 @@ class Oauth:
|
|||||||
使用 统一身份认证平台 登录子系统,并且保存 cookie
|
使用 统一身份认证平台 登录子系统,并且保存 cookie
|
||||||
"""
|
"""
|
||||||
host = host or urlparse(url).hostname
|
host = host or urlparse(url).hostname
|
||||||
async with self.request(cookies=self.sub_cookies, follow_redirects=True) as client:
|
html = await self.request.get(url, follow_redirects=True)
|
||||||
html = await client.get(url)
|
return None if html.url.host != host else html
|
||||||
for history in html.history:
|
|
||||||
self.sub_cookies.update(history.cookies)
|
|
||||||
if host not in self.init_sub_web:
|
|
||||||
self.init_sub_web.append(host)
|
|
||||||
return None if html.url.host != host else html
|
|
||||||
|
@ -1,11 +1,9 @@
|
|||||||
from .gen_pay_qrcode import GenPayQrcode
|
from .gen_pay_qrcode import GenPayQrcode
|
||||||
from .get_balance import GetBalance
|
from .get_balance import GetBalance
|
||||||
from .get_orders import GetOrders
|
|
||||||
|
|
||||||
|
|
||||||
class EPay(
|
class EPay(
|
||||||
GenPayQrcode,
|
GenPayQrcode,
|
||||||
GetBalance,
|
GetBalance,
|
||||||
GetOrders
|
|
||||||
):
|
):
|
||||||
pass
|
pass
|
||||||
|
@ -2,6 +2,7 @@ import qrcode
|
|||||||
from bs4 import BeautifulSoup
|
from bs4 import BeautifulSoup
|
||||||
|
|
||||||
import cqwu
|
import cqwu
|
||||||
|
from cqwu.errors.auth import CookieError
|
||||||
|
|
||||||
|
|
||||||
class GenPayQrcode:
|
class GenPayQrcode:
|
||||||
@ -11,9 +12,12 @@ class GenPayQrcode:
|
|||||||
"""
|
"""
|
||||||
生成支付二维码
|
生成支付二维码
|
||||||
"""
|
"""
|
||||||
html = await self.oauth("http://218.194.176.214:8382/epay/thirdconsume/qrcode")
|
url = "http://218.194.176.214:8382/epay/thirdconsume/qrcode"
|
||||||
|
html = await self.oauth(url)
|
||||||
if not html:
|
if not html:
|
||||||
return
|
raise CookieError()
|
||||||
|
if html.url != url:
|
||||||
|
raise CookieError()
|
||||||
soup = BeautifulSoup(html.text, "lxml")
|
soup = BeautifulSoup(html.text, "lxml")
|
||||||
try:
|
try:
|
||||||
data = soup.find("input", attrs={"id": "myText"})["value"]
|
data = soup.find("input", attrs={"id": "myText"})["value"]
|
||||||
|
@ -2,6 +2,7 @@ from typing import Optional
|
|||||||
from bs4 import BeautifulSoup
|
from bs4 import BeautifulSoup
|
||||||
|
|
||||||
import cqwu
|
import cqwu
|
||||||
|
from cqwu.errors.auth import CookieError
|
||||||
|
|
||||||
|
|
||||||
class GetBalance:
|
class GetBalance:
|
||||||
@ -14,9 +15,12 @@ class GetBalance:
|
|||||||
Returns:
|
Returns:
|
||||||
str: 余额
|
str: 余额
|
||||||
"""
|
"""
|
||||||
html = await self.oauth("http://218.194.176.214:8382/epay/thirdapp/balance")
|
url = "http://218.194.176.214:8382/epay/thirdapp/balance"
|
||||||
|
html = await self.oauth(url)
|
||||||
if not html:
|
if not html:
|
||||||
return ""
|
raise CookieError()
|
||||||
|
if html.url != url:
|
||||||
|
raise CookieError()
|
||||||
soup = BeautifulSoup(html.text, "lxml")
|
soup = BeautifulSoup(html.text, "lxml")
|
||||||
try:
|
try:
|
||||||
return soup.find_all("div", "weui-cell__ft")[2].next
|
return soup.find_all("div", "weui-cell__ft")[2].next
|
||||||
|
@ -1,39 +0,0 @@
|
|||||||
from typing import List
|
|
||||||
|
|
||||||
import cqwu
|
|
||||||
from cqwu.types.order import Order
|
|
||||||
|
|
||||||
|
|
||||||
class GetOrders:
|
|
||||||
async def get_orders(
|
|
||||||
self: "cqwu.Client",
|
|
||||||
page: int = 1,
|
|
||||||
page_size: int = 10,
|
|
||||||
search: str = ""
|
|
||||||
) -> List[Order]:
|
|
||||||
"""
|
|
||||||
获取历史订单
|
|
||||||
|
|
||||||
:param page: 页码
|
|
||||||
:param page_size: 每页数量
|
|
||||||
:param search: 搜索关键字
|
|
||||||
|
|
||||||
:return: 订单列表
|
|
||||||
"""
|
|
||||||
url = "http://pay.cqwu.edu.cn/queryOrderList"
|
|
||||||
params = {
|
|
||||||
"orderno": search,
|
|
||||||
"page": page,
|
|
||||||
"pagesize": page_size,
|
|
||||||
}
|
|
||||||
if "pay.cqwu.edu.cn" not in self.init_sub_web:
|
|
||||||
await self.oauth(
|
|
||||||
"http://authserver.cqwu.edu.cn/authserver/login?service="
|
|
||||||
"http%3A%2F%2Fpay.cqwu.edu.cn%2FsignAuthentication%3Furl%3DopenPortal")
|
|
||||||
async with self.request(cookies=self.sub_cookies, follow_redirects=True) as client:
|
|
||||||
html = await client.get(url, params=params)
|
|
||||||
try:
|
|
||||||
data = html.json()["payOrderList"]
|
|
||||||
except KeyError:
|
|
||||||
return []
|
|
||||||
return [Order(**order) for order in data]
|
|
@ -3,6 +3,7 @@ from bs4 import BeautifulSoup
|
|||||||
|
|
||||||
import cqwu
|
import cqwu
|
||||||
from cqwu import types
|
from cqwu import types
|
||||||
|
from cqwu.errors.auth import CookieError
|
||||||
|
|
||||||
|
|
||||||
def get_value_from_soup(soup: BeautifulSoup, attr_id: str) -> Union[type(None), str, int]:
|
def get_value_from_soup(soup: BeautifulSoup, attr_id: str) -> Union[type(None), str, int]:
|
||||||
@ -26,10 +27,14 @@ class GetMe:
|
|||||||
Returns:
|
Returns:
|
||||||
types.User: 个人信息
|
types.User: 个人信息
|
||||||
"""
|
"""
|
||||||
html = await self.oauth(
|
url = "http://218.194.176.8/prizepunishnv/studentInfoManageStudentNV!forwardStudentInfo.action"
|
||||||
"http://218.194.176.8/prizepunishnv/studentInfoManageStudentNV!forwardStudentInfo.action")
|
html = await self.oauth(url)
|
||||||
if not html:
|
if not html:
|
||||||
return None
|
raise CookieError()
|
||||||
|
if html.url != url:
|
||||||
|
html = await self.request.get(url)
|
||||||
|
if html.url != url:
|
||||||
|
raise CookieError()
|
||||||
soup = BeautifulSoup(html.text, "lxml")
|
soup = BeautifulSoup(html.text, "lxml")
|
||||||
data = {
|
data = {
|
||||||
"username": "detail_xh",
|
"username": "detail_xh",
|
||||||
|
7
cqwu/methods/xg/__init__.py
Normal file
7
cqwu/methods/xg/__init__.py
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
from .get_score import GetScore
|
||||||
|
|
||||||
|
|
||||||
|
class XG(
|
||||||
|
GetScore,
|
||||||
|
):
|
||||||
|
pass
|
65
cqwu/methods/xg/get_score.py
Normal file
65
cqwu/methods/xg/get_score.py
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
import json
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
import cqwu
|
||||||
|
from cqwu import types
|
||||||
|
from cqwu.errors.auth import CookieError
|
||||||
|
|
||||||
|
|
||||||
|
class GetScore:
|
||||||
|
async def get_score(
|
||||||
|
self: "cqwu.Client",
|
||||||
|
year: int = 2022,
|
||||||
|
semester: int = 1,
|
||||||
|
) -> List["types.Score"]:
|
||||||
|
"""
|
||||||
|
获取期末成绩
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
List[types.Score]: 成绩列表
|
||||||
|
"""
|
||||||
|
url = "http://xg.cqwu.edu.cn/xsfw/sys/zhcptybbapp/*default/index.do#/cjcx"
|
||||||
|
html = await self.oauth(url)
|
||||||
|
if not html:
|
||||||
|
raise CookieError()
|
||||||
|
if html.url != url:
|
||||||
|
raise CookieError()
|
||||||
|
await self.request.get(
|
||||||
|
"http://xg.cqwu.edu.cn/xsfw/sys/swpubapp/indexmenu/getAppConfig.do?appId=5275772372599202&appName=zhcptybbapp&v=046351851777942055")
|
||||||
|
query_url = "http://xg.cqwu.edu.cn/xsfw/sys/zhcptybbapp/modules/cjcx/cjcxbgdz.do"
|
||||||
|
headers = {
|
||||||
|
'Accept': 'application/json, text/javascript, */*; q=0.01',
|
||||||
|
'Accept-Language': 'zh-CN,zh;q=0.9',
|
||||||
|
'Connection': 'keep-alive',
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
|
||||||
|
'Origin': 'http://xg.cqwu.edu.cn',
|
||||||
|
'Referer': 'http://xg.cqwu.edu.cn/xsfw/sys/zhcptybbapp/*default/index.do',
|
||||||
|
'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',
|
||||||
|
'X-Requested-With': 'XMLHttpRequest',
|
||||||
|
}
|
||||||
|
query_data = [
|
||||||
|
{
|
||||||
|
"name": "XN",
|
||||||
|
"caption": "学年",
|
||||||
|
"linkOpt": "AND",
|
||||||
|
"builderList": "cbl_m_List",
|
||||||
|
"builder": "m_value_equal",
|
||||||
|
"value": str(year),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "XQ",
|
||||||
|
"caption": "学期",
|
||||||
|
"linkOpt": "AND",
|
||||||
|
"builderList": "cbl_m_List",
|
||||||
|
"builder": "m_value_equal",
|
||||||
|
"value": str(semester),
|
||||||
|
}
|
||||||
|
]
|
||||||
|
data = {
|
||||||
|
'querySetting': json.dumps(query_data),
|
||||||
|
'pageSize': '100',
|
||||||
|
'pageNumber': '1',
|
||||||
|
}
|
||||||
|
html = await self.request.post(query_url, headers=headers, data=data)
|
||||||
|
data = [types.Score(**i) for i in html.json()["datas"]["cjcxbgdz"]["rows"]]
|
||||||
|
return data
|
@ -1 +1,2 @@
|
|||||||
|
from .score import Score
|
||||||
from .user import User
|
from .user import User
|
||||||
|
@ -1,27 +0,0 @@
|
|||||||
import cqwu
|
|
||||||
|
|
||||||
|
|
||||||
class Order:
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
orderno: str = None,
|
|
||||||
payproname: str = None,
|
|
||||||
orderamt: float = None,
|
|
||||||
createtime: str = None,
|
|
||||||
payflag: str = "",
|
|
||||||
**_
|
|
||||||
):
|
|
||||||
"""
|
|
||||||
订单
|
|
||||||
|
|
||||||
:param orderno: 订单编号
|
|
||||||
:param payproname: 缴费项目
|
|
||||||
:param orderamt: 缴费金额
|
|
||||||
:param createtime: 订单生成时间
|
|
||||||
:param payflag: 缴费状态
|
|
||||||
"""
|
|
||||||
self.order_id = orderno
|
|
||||||
self.project = payproname
|
|
||||||
self.amount = orderamt / 100
|
|
||||||
self.create_time = createtime
|
|
||||||
self.status = cqwu.enums.OrderStatus(int(payflag))
|
|
43
cqwu/types/score.py
Normal file
43
cqwu/types/score.py
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
|
||||||
|
class Score(BaseModel):
|
||||||
|
""" 成绩类 """
|
||||||
|
KCMC: str
|
||||||
|
XF: float
|
||||||
|
ZCJ: float
|
||||||
|
JD: str
|
||||||
|
XN: str
|
||||||
|
XN_DISPLAY: str
|
||||||
|
XQ: str
|
||||||
|
XQ_DISPLAY: str
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self) -> str:
|
||||||
|
""" 课程名称 """
|
||||||
|
return self.KCMC
|
||||||
|
|
||||||
|
@property
|
||||||
|
def credit(self) -> float:
|
||||||
|
""" 学分 """
|
||||||
|
return self.XF
|
||||||
|
|
||||||
|
@property
|
||||||
|
def score(self) -> float:
|
||||||
|
""" 成绩 """
|
||||||
|
return self.ZCJ
|
||||||
|
|
||||||
|
@property
|
||||||
|
def grade_point(self) -> float:
|
||||||
|
""" 绩点 """
|
||||||
|
return float(self.JD)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def year(self) -> int:
|
||||||
|
""" 学年 """
|
||||||
|
return int(self.XN)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def semester(self) -> int:
|
||||||
|
""" 学期 """
|
||||||
|
return int(self.XQ)
|
@ -1,6 +1,7 @@
|
|||||||
httpx
|
httpx==0.23.3
|
||||||
lxml==4.9.1
|
lxml==4.9.1
|
||||||
PyExecJS==1.5.1
|
PyExecJS2==1.6.1
|
||||||
beautifulsoup4==4.11.1
|
beautifulsoup4==4.11.2
|
||||||
qrcode==7.3.1
|
qrcode==7.4.2
|
||||||
pillow
|
pillow
|
||||||
|
pydantic==1.10.5
|
||||||
|
Loading…
Reference in New Issue
Block a user