myfap 0.1.0__tar.gz

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.
myfap-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 WindowScary321
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
myfap-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,214 @@
1
+ Metadata-Version: 2.4
2
+ Name: myfap
3
+ Version: 0.1.0
4
+ Summary: CLI Tool dựa trên MyFAP API dành cho Sinh Viên FPTU
5
+ Author-email: Author <author@example.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/WindowScary321/myfap-api-cli
8
+ Requires-Python: >=3.8
9
+ Description-Content-Type: text/markdown
10
+ License-File: LICENSE
11
+ Requires-Dist: typer>=0.9.0
12
+ Requires-Dist: rich>=13.0.0
13
+ Requires-Dist: playwright>=1.40.0
14
+ Requires-Dist: requests>=2.31.0
15
+ Requires-Dist: pkce>=1.0.3
16
+ Requires-Dist: beautifulsoup4>=4.12.0
17
+ Requires-Dist: ics>=0.7.2
18
+ Requires-Dist: pytz>=2023.3
19
+ Dynamic: license-file
20
+
21
+ # MyFAP API CLI
22
+
23
+ ## Giới thiệu
24
+
25
+ Chào mứng đến với dự án đầu tiên của kế hoạch lật đổ FBTU. Cli này tương tác giống hoàn toàn với APP MYFAP trên điện thoại. Bạn có thể xem lịch học, lịch thi, điểm số (sự thất vọng của gia đình), thông tin cá nhân, v.v. 1 cách thuận tiện, không bị gò bó trong giao diện app. <br>
26
+ - Lưu ý: repo này được thực hiện cùng với sự hỗ trợ của Ai (Gemini 3.1 Pro Preview)
27
+
28
+ ## Tính năng chính
29
+ - Hỗ trợ đa nền tảng.
30
+ - Login thông qua FEID hoặc Google OAuth.
31
+ - Xem lịch học (từng tuần hoặc cả Kỳ).
32
+ - Xem điểm (từng môn hoặc cả Kỳ).
33
+ - Check điểm danh (từng môn hoặc cả Kỳ).
34
+ - Check tình trạng đơn từ.
35
+ - Xem thông báo của trường.
36
+ - Check Info của sinh viên hoặc phòng dvsv.
37
+ - Convert lịch học Kỳ sang **ICS**, dễ dàng import lịch học vào Google Calendar hoặc các dịch vụ tương tự.
38
+ - Và nhiều hơn nữa ...
39
+
40
+ ## Cách sử dụng
41
+
42
+ Do dự án đang trong quá trình phát triển nên chưa thể đem lên pip. Bạn có thể tải sauce về và chạy:
43
+
44
+ - Tạo venv.
45
+
46
+ ```
47
+ python -m venv .venv && .\.venv\Scripts\activate
48
+ ```
49
+
50
+ - Cài đặt dependency.
51
+
52
+ ```
53
+ pip install .
54
+ ```
55
+
56
+ - Cài đặt playwright (nếu bạn ko dùng chrome hoặc edge).
57
+ ```
58
+ playwright install
59
+ ```
60
+
61
+ - Lấy danh sách cơ sở.
62
+
63
+ ```
64
+ myfap campuses
65
+ ```
66
+
67
+ - Login vào feid (chỉ cần làm 1 lần, mặc định chọn cơ sở Hola).
68
+ ```
69
+ myfap login
70
+ ```
71
+ Sau khi hoàn thành login, phiên đăng nhập sẽ dc lưu ở ``~/.myfap-api-cli/session.json`` <br>
72
+ - Login cho cơ sở Xavalo.
73
+ ```
74
+ myfap login -c HCM
75
+ ```
76
+
77
+ - Lấy Lịch học (tự động lấy kỳ mới nhất).
78
+
79
+ ```
80
+ myfap schedule
81
+ ```
82
+
83
+ ## Các Options
84
+ |Option|Chú thích|
85
+ |:-|:-|
86
+ |--help|Hiển thị bảng Commands và Options|
87
+ |-c, --campus <cơ sở>|Mã cơ sở (VD: APHL, HCM, DN) [default: APHL]|
88
+ |-s, --semester <kỳ học>|Tên kỳ học (VD: Summer2026)|
89
+ |-cid, --courseid <mã môn>|Chọn mã môn, chỉ có ở command ``marks``. (VD: 82934, lấy từ bảng điểm của kỳ, ko phải mã môn như PRO192, SWE201c, ...)|
90
+ |-w, --week <số tuần>|Chọn tuần, chỉ có ở command ``week-timetable``|
91
+ |-y, --year <số năm>|Chọn năm học, chỉ có ở command ``week-timetable``|
92
+ |-d, --date \<YYYY-MM-DD\>|Chỉ định ngày chính xác, chỉ có ở command ``week-timetable``|
93
+ |-sc, --subjectcode <mã môn>|Chọn môn học, chỉ có ở command ``attendance``|
94
+ |-cn, --classname <mã lớp>|Chọn lớp học, chỉ có ở command ``attendance``|
95
+ |--ics|Convert Sang **ICS**, dễ dàng import lịch học vào Google Calendar hoặc các dịch vụ tương tự, chỉ có ở command ``schedule``)|
96
+ |-p, --pretty|Format lại bảng điểm gốc của trường sang dạng json, chỉ có ở command ``marks``)|
97
+ |-r, --refresh|Refresh thủ công ``authen_key`` trong file ``session.json``, chỉ có ở command ``login``|
98
+ |-f, --force|Buộc đăng nhập lại, bỏ qua file ``session.json``, chỉ có ở command ``login``|
99
+
100
+ ## Các ví dụ sử dụng
101
+ - Lưu ý:
102
+ - Do hầu hết các command đểu trả về json, ``*`` đánh dấu những command trả output ra màn hình.
103
+ - Nếu ko thêm flag _thời gian_ hoặc _kỳ_, ``myfap`` sẽ tự động lấy thời gian mới nhất.
104
+ - ``+`` nghĩa là bạn sẽ ko cần thêm flag ``--semester`` hoặc ``-s`` nếu gọi api vào đúng thời gian trong kỳ. Để xem thông tin của kỳ trước, bạn phải thêm flag này.
105
+ - Xem tất cả command ``*``
106
+ ```
107
+ (.venv) PS D:\git\myfap-api-cli> myfap --help
108
+ Usage: myfap [OPTIONS] COMMAND [ARGS]...
109
+
110
+ CLI Tool dựa trên MyFAP dành cho Sinh Viên FPTU
111
+
112
+ Options:
113
+ -c, --campus TEXT Mã cơ sở (VD: APHL, HCM, DN) [default: APHL]
114
+ --help Show this message and exit.
115
+
116
+ Commands:
117
+ login Đăng nhập vào hệ thống FEID
118
+ campuses Xem danh sách các cơ sở (Campus)
119
+ semesters Xem danh sách kỳ học
120
+ schedule Xem lịch học (xuất JSON)
121
+ marks Xem bảng điểm (xuất JSON)
122
+ exams Xem lịch thi (xuất JSON)
123
+ week-timetable Xem lịch học theo tuần (xuất JSON)
124
+ attendance Xem thông tin điểm danh (xuất JSON)
125
+ applications Xem danh sách đơn từ đã gửi cho trường (xuất JSON)
126
+ news Xem 10 thông báo gần nhất (xuất JSON)
127
+ info Xem thông tin chung (sinh viên, campus)
128
+ other Các chức năng phụ trợ khác (survey, feedback, fee...)
129
+ ```
130
+ - Xem danh sách kỳ ``*``
131
+ ```
132
+ myfap semesters
133
+ ```
134
+ - Xem lịch học của Kỳ bất kỳ
135
+ ```
136
+ myfap schedule -s Summer2026
137
+ ```
138
+ - Convert lịch học sang **ICS** (import vào GG Calendar)
139
+ ```
140
+ myfap schedule -s Summer2026 --ics
141
+ ```
142
+ - Xem lịch thi của Kỳ bất kỳ
143
+ ```
144
+ myfap exams -s Summer2026
145
+ ```
146
+ - Xem lịch từng tuần được chỉ định ``+``
147
+ ```
148
+ myfap week-timetable --week 22
149
+ ```
150
+ - Xem lịch từng tuần bằng thời gian ngày ``+``
151
+ ```
152
+ myfap week-timetable --date 2026-6-7
153
+ ```
154
+ - Xem danh sách điểm danh của Kỳ bất kỳ
155
+ ```
156
+ myfap attendance -s Spring2026
157
+ ```
158
+ - Xem danh sách điểm danh của môn bất kỳ trong Kỳ ``+``
159
+ ```
160
+ myfap attendance -sc CSD201 -cn SE2026 -s Spring2026
161
+ ```
162
+ - Xem ~~sự thất vọng của gia đinh~~ điểm số của Kỳ bất kỳ
163
+ ```
164
+ myfap marks -s Fall2025
165
+ ```
166
+ - Xem điểm của môn bất kỳ (ví dụ: môn MAD101, xem mã môn ở command trên)
167
+ ```
168
+ myfap marks -cid 95619
169
+ ```
170
+ - Xem điểm của môn bất kỳ (thêm ``-p`` hoặc ``--pretty`` cho đẹp)
171
+ ```
172
+ myfap marks -cid 95619 -p
173
+ ```
174
+ - Xem tình trạng gửi đơn của Fap Web
175
+ ```
176
+ myfap applications
177
+ ```
178
+ - Xem thông báo gửi toàn trường
179
+ ```
180
+ myfap news
181
+ ```
182
+ - Xem thông tin sinh viên
183
+ ```
184
+ myfap info student
185
+ ```
186
+ - Xem thông tin phòng dvsv
187
+ ```
188
+ myfap info campus
189
+ ```
190
+ - Xem thông tin thêm ``*``
191
+ ```bash
192
+ myfap other --help
193
+ Usage: myfap other [OPTIONS] COMMAND [ARGS]...
194
+
195
+ Các chức năng phụ trợ khác (survey, feedback, fee...)
196
+
197
+ Options:
198
+ --help Show this message and exit.
199
+
200
+ Commands:
201
+ survey Kiểm tra các survey chưa thực hiện (xuất JSON)
202
+ feedback Kiểm tra Feedback (xuất JSON)
203
+ profile Kiểm tra Update Profile (xuất JSON)
204
+ notification Xem thông báo qua MSSV (xuất JSON)
205
+ fee Lấy danh sách học phí chưa thanh toán (xuất JSON)
206
+ ```
207
+ - Refresh thủ công ``authen_key`` trong trường hợp cli bị lỗi
208
+ ```
209
+ myfap login -r
210
+ ```
211
+ - Đăng nhập lại, bỏ qua file ``session.json``
212
+ ```
213
+ myfap login -f
214
+ ```
myfap-0.1.0/README.md ADDED
@@ -0,0 +1,194 @@
1
+ # MyFAP API CLI
2
+
3
+ ## Giới thiệu
4
+
5
+ Chào mứng đến với dự án đầu tiên của kế hoạch lật đổ FBTU. Cli này tương tác giống hoàn toàn với APP MYFAP trên điện thoại. Bạn có thể xem lịch học, lịch thi, điểm số (sự thất vọng của gia đình), thông tin cá nhân, v.v. 1 cách thuận tiện, không bị gò bó trong giao diện app. <br>
6
+ - Lưu ý: repo này được thực hiện cùng với sự hỗ trợ của Ai (Gemini 3.1 Pro Preview)
7
+
8
+ ## Tính năng chính
9
+ - Hỗ trợ đa nền tảng.
10
+ - Login thông qua FEID hoặc Google OAuth.
11
+ - Xem lịch học (từng tuần hoặc cả Kỳ).
12
+ - Xem điểm (từng môn hoặc cả Kỳ).
13
+ - Check điểm danh (từng môn hoặc cả Kỳ).
14
+ - Check tình trạng đơn từ.
15
+ - Xem thông báo của trường.
16
+ - Check Info của sinh viên hoặc phòng dvsv.
17
+ - Convert lịch học Kỳ sang **ICS**, dễ dàng import lịch học vào Google Calendar hoặc các dịch vụ tương tự.
18
+ - Và nhiều hơn nữa ...
19
+
20
+ ## Cách sử dụng
21
+
22
+ Do dự án đang trong quá trình phát triển nên chưa thể đem lên pip. Bạn có thể tải sauce về và chạy:
23
+
24
+ - Tạo venv.
25
+
26
+ ```
27
+ python -m venv .venv && .\.venv\Scripts\activate
28
+ ```
29
+
30
+ - Cài đặt dependency.
31
+
32
+ ```
33
+ pip install .
34
+ ```
35
+
36
+ - Cài đặt playwright (nếu bạn ko dùng chrome hoặc edge).
37
+ ```
38
+ playwright install
39
+ ```
40
+
41
+ - Lấy danh sách cơ sở.
42
+
43
+ ```
44
+ myfap campuses
45
+ ```
46
+
47
+ - Login vào feid (chỉ cần làm 1 lần, mặc định chọn cơ sở Hola).
48
+ ```
49
+ myfap login
50
+ ```
51
+ Sau khi hoàn thành login, phiên đăng nhập sẽ dc lưu ở ``~/.myfap-api-cli/session.json`` <br>
52
+ - Login cho cơ sở Xavalo.
53
+ ```
54
+ myfap login -c HCM
55
+ ```
56
+
57
+ - Lấy Lịch học (tự động lấy kỳ mới nhất).
58
+
59
+ ```
60
+ myfap schedule
61
+ ```
62
+
63
+ ## Các Options
64
+ |Option|Chú thích|
65
+ |:-|:-|
66
+ |--help|Hiển thị bảng Commands và Options|
67
+ |-c, --campus <cơ sở>|Mã cơ sở (VD: APHL, HCM, DN) [default: APHL]|
68
+ |-s, --semester <kỳ học>|Tên kỳ học (VD: Summer2026)|
69
+ |-cid, --courseid <mã môn>|Chọn mã môn, chỉ có ở command ``marks``. (VD: 82934, lấy từ bảng điểm của kỳ, ko phải mã môn như PRO192, SWE201c, ...)|
70
+ |-w, --week <số tuần>|Chọn tuần, chỉ có ở command ``week-timetable``|
71
+ |-y, --year <số năm>|Chọn năm học, chỉ có ở command ``week-timetable``|
72
+ |-d, --date \<YYYY-MM-DD\>|Chỉ định ngày chính xác, chỉ có ở command ``week-timetable``|
73
+ |-sc, --subjectcode <mã môn>|Chọn môn học, chỉ có ở command ``attendance``|
74
+ |-cn, --classname <mã lớp>|Chọn lớp học, chỉ có ở command ``attendance``|
75
+ |--ics|Convert Sang **ICS**, dễ dàng import lịch học vào Google Calendar hoặc các dịch vụ tương tự, chỉ có ở command ``schedule``)|
76
+ |-p, --pretty|Format lại bảng điểm gốc của trường sang dạng json, chỉ có ở command ``marks``)|
77
+ |-r, --refresh|Refresh thủ công ``authen_key`` trong file ``session.json``, chỉ có ở command ``login``|
78
+ |-f, --force|Buộc đăng nhập lại, bỏ qua file ``session.json``, chỉ có ở command ``login``|
79
+
80
+ ## Các ví dụ sử dụng
81
+ - Lưu ý:
82
+ - Do hầu hết các command đểu trả về json, ``*`` đánh dấu những command trả output ra màn hình.
83
+ - Nếu ko thêm flag _thời gian_ hoặc _kỳ_, ``myfap`` sẽ tự động lấy thời gian mới nhất.
84
+ - ``+`` nghĩa là bạn sẽ ko cần thêm flag ``--semester`` hoặc ``-s`` nếu gọi api vào đúng thời gian trong kỳ. Để xem thông tin của kỳ trước, bạn phải thêm flag này.
85
+ - Xem tất cả command ``*``
86
+ ```
87
+ (.venv) PS D:\git\myfap-api-cli> myfap --help
88
+ Usage: myfap [OPTIONS] COMMAND [ARGS]...
89
+
90
+ CLI Tool dựa trên MyFAP dành cho Sinh Viên FPTU
91
+
92
+ Options:
93
+ -c, --campus TEXT Mã cơ sở (VD: APHL, HCM, DN) [default: APHL]
94
+ --help Show this message and exit.
95
+
96
+ Commands:
97
+ login Đăng nhập vào hệ thống FEID
98
+ campuses Xem danh sách các cơ sở (Campus)
99
+ semesters Xem danh sách kỳ học
100
+ schedule Xem lịch học (xuất JSON)
101
+ marks Xem bảng điểm (xuất JSON)
102
+ exams Xem lịch thi (xuất JSON)
103
+ week-timetable Xem lịch học theo tuần (xuất JSON)
104
+ attendance Xem thông tin điểm danh (xuất JSON)
105
+ applications Xem danh sách đơn từ đã gửi cho trường (xuất JSON)
106
+ news Xem 10 thông báo gần nhất (xuất JSON)
107
+ info Xem thông tin chung (sinh viên, campus)
108
+ other Các chức năng phụ trợ khác (survey, feedback, fee...)
109
+ ```
110
+ - Xem danh sách kỳ ``*``
111
+ ```
112
+ myfap semesters
113
+ ```
114
+ - Xem lịch học của Kỳ bất kỳ
115
+ ```
116
+ myfap schedule -s Summer2026
117
+ ```
118
+ - Convert lịch học sang **ICS** (import vào GG Calendar)
119
+ ```
120
+ myfap schedule -s Summer2026 --ics
121
+ ```
122
+ - Xem lịch thi của Kỳ bất kỳ
123
+ ```
124
+ myfap exams -s Summer2026
125
+ ```
126
+ - Xem lịch từng tuần được chỉ định ``+``
127
+ ```
128
+ myfap week-timetable --week 22
129
+ ```
130
+ - Xem lịch từng tuần bằng thời gian ngày ``+``
131
+ ```
132
+ myfap week-timetable --date 2026-6-7
133
+ ```
134
+ - Xem danh sách điểm danh của Kỳ bất kỳ
135
+ ```
136
+ myfap attendance -s Spring2026
137
+ ```
138
+ - Xem danh sách điểm danh của môn bất kỳ trong Kỳ ``+``
139
+ ```
140
+ myfap attendance -sc CSD201 -cn SE2026 -s Spring2026
141
+ ```
142
+ - Xem ~~sự thất vọng của gia đinh~~ điểm số của Kỳ bất kỳ
143
+ ```
144
+ myfap marks -s Fall2025
145
+ ```
146
+ - Xem điểm của môn bất kỳ (ví dụ: môn MAD101, xem mã môn ở command trên)
147
+ ```
148
+ myfap marks -cid 95619
149
+ ```
150
+ - Xem điểm của môn bất kỳ (thêm ``-p`` hoặc ``--pretty`` cho đẹp)
151
+ ```
152
+ myfap marks -cid 95619 -p
153
+ ```
154
+ - Xem tình trạng gửi đơn của Fap Web
155
+ ```
156
+ myfap applications
157
+ ```
158
+ - Xem thông báo gửi toàn trường
159
+ ```
160
+ myfap news
161
+ ```
162
+ - Xem thông tin sinh viên
163
+ ```
164
+ myfap info student
165
+ ```
166
+ - Xem thông tin phòng dvsv
167
+ ```
168
+ myfap info campus
169
+ ```
170
+ - Xem thông tin thêm ``*``
171
+ ```bash
172
+ myfap other --help
173
+ Usage: myfap other [OPTIONS] COMMAND [ARGS]...
174
+
175
+ Các chức năng phụ trợ khác (survey, feedback, fee...)
176
+
177
+ Options:
178
+ --help Show this message and exit.
179
+
180
+ Commands:
181
+ survey Kiểm tra các survey chưa thực hiện (xuất JSON)
182
+ feedback Kiểm tra Feedback (xuất JSON)
183
+ profile Kiểm tra Update Profile (xuất JSON)
184
+ notification Xem thông báo qua MSSV (xuất JSON)
185
+ fee Lấy danh sách học phí chưa thanh toán (xuất JSON)
186
+ ```
187
+ - Refresh thủ công ``authen_key`` trong trường hợp cli bị lỗi
188
+ ```
189
+ myfap login -r
190
+ ```
191
+ - Đăng nhập lại, bỏ qua file ``session.json``
192
+ ```
193
+ myfap login -f
194
+ ```
@@ -0,0 +1,3 @@
1
+ """
2
+ MyFAP API CLI - Khởi tạo module
3
+ """
@@ -0,0 +1,226 @@
1
+ import requests
2
+ import html
3
+ from .auth import FAP_HOST, MAGIC_ID, create_checksum
4
+
5
+ def check_api_error(data):
6
+ if isinstance(data, dict):
7
+ code = str(data.get("code", ""))
8
+ if code == "201":
9
+ msg = data.get("message", "Lỗi API (Mã 201)")
10
+ raise Exception(f"FAP API báo lỗi: {msg}")
11
+ if "data" in data and not data.get("data"): # Báo lỗi nếu data rỗng hoặc null
12
+ raise Exception("Không có dữ liệu (Data rỗng) hoặc bạn chưa hoặc truyền sai flag --semester")
13
+ return data
14
+
15
+ def clean_json(data):
16
+ """Giải mã các ký tự HTML Entity (&ocirc;, &aacute;, ...) do server ASP.NET trả về"""
17
+ if isinstance(data, dict):
18
+ return {k: clean_json(v) for k, v in data.items()}
19
+ elif isinstance(data, list):
20
+ return [clean_json(v) for v in data]
21
+ elif isinstance(data, str):
22
+ return html.unescape(data)
23
+ return data
24
+
25
+
26
+ class MyFapEssential:
27
+ def __init__(self, auth=None):
28
+ self.auth = auth
29
+ self.session = requests.Session()
30
+ self.session.headers.update({"User-Agent": "okhttp/4.9.2"})
31
+
32
+ def get_campuses(self):
33
+ """Lấy danh sách các cơ sở (không cần authenKey)"""
34
+ url = f"{FAP_HOST}/GetAllActiveCampus"
35
+ r = self.session.get(url)
36
+ if r.status_code == 200:
37
+ return clean_json(check_api_error(r.json())).get('data', [])
38
+ return []
39
+
40
+ def get_semesters(self, campus: str = None):
41
+ """Lấy danh sách các kỳ học. Yêu cầu đối tượng auth hợp lệ."""
42
+ if not self.auth:
43
+ raise Exception("Lỗi: Cần đối tượng auth để lấy danh sách kỳ học.")
44
+
45
+ target_campus = campus or self.auth.campus
46
+ checksum = create_checksum(MAGIC_ID, target_campus)
47
+ url = f"{FAP_HOST}/GetSemester?campusCode={target_campus}&Authen={self.auth.authen_key}&checksum={checksum}"
48
+ r = self.session.get(url)
49
+ if r.status_code == 200:
50
+ semesters = check_api_error(r.json()).get('data', [])
51
+ semesters.sort(key=lambda x: x['termID'], reverse=True)
52
+ return semesters
53
+ raise Exception(f"Lỗi lấy danh sách kỳ học: {r.text}")
54
+
55
+ def get_week(self, date_str: str):
56
+ """Lấy số tuần tương ứng với ngày (YYYY-MM-DD)"""
57
+ url = f"{FAP_HOST}/GetWeekByDate?date={date_str}"
58
+ r = self.session.get(url)
59
+ if r.status_code == 200:
60
+ return int(check_api_error(r.json()).get("data", 0))
61
+ raise Exception(f"Lỗi lấy tuần: {r.text}")
62
+
63
+ def get_news(self, news_type: int = 1):
64
+ """Lấy 10 thông báo gần nhất (gửi toàn trường)"""
65
+ if not self.auth:
66
+ raise Exception("Lỗi: Không tìm thấy session xác thực (auth).")
67
+ # Identifier cho API này chính là giá trị của 'type'
68
+ checksum = create_checksum(str(news_type), self.auth.campus)
69
+ url = f"{FAP_HOST}/GetTop10News?campusCode={self.auth.campus}&Authen={self.auth.authen_key}&type={news_type}&checksum={checksum}"
70
+ r = self.session.get(url)
71
+ if r.status_code == 200:
72
+ return clean_json(check_api_error(r.json()))
73
+ raise Exception(f"Lỗi lấy thông báo: {r.text}")
74
+
75
+ class MyFapOther:
76
+ def __init__(self, auth):
77
+ self.auth = auth
78
+ self.session = requests.Session()
79
+ self.session.headers.update({"User-Agent": "okhttp/4.9.2"})
80
+
81
+ def get_required_survey(self):
82
+ """Lấy các survey chưa thực hiện"""
83
+ if not getattr(self.auth, 'email', None):
84
+ raise Exception("Lỗi: Không tìm thấy email trong session. Vui lòng chạy 'myfap login' lại để lấy thông tin.")
85
+ # Identifier cho API này là email (username)
86
+ checksum = create_checksum(self.auth.email, self.auth.campus)
87
+ url = f"https://survey.fpt.edu.vn/API/myFAP/GetRequiredSurvey?username={self.auth.email}&checksum={checksum}"
88
+ r = self.session.get(url)
89
+ if r.status_code == 200:
90
+ return clean_json(check_api_error(r.json()))
91
+ raise Exception(f"Lỗi lấy Survey: {r.text}")
92
+
93
+ def check_open_feedback(self):
94
+ """Kiểm tra Feedback"""
95
+ checksum = create_checksum(self.auth.mssv, self.auth.campus)
96
+ url = f"{FAP_HOST}/CheckOpenFeedBack?campusCode={self.auth.campus}&rollNumber={self.auth.mssv}&Authen={self.auth.authen_key}&checksum={checksum}"
97
+ r = self.session.get(url)
98
+ if r.status_code == 200:
99
+ return clean_json(check_api_error(r.json()))
100
+ raise Exception(f"Lỗi lấy Feedback: {r.text}")
101
+
102
+ def check_update_profile(self):
103
+ """Kiểm tra Update Profile"""
104
+ checksum = create_checksum(self.auth.mssv, self.auth.campus)
105
+ url = f"{FAP_HOST}/CheckUpdateProfile?campusCode={self.auth.campus}&rollNumber={self.auth.mssv}&Authen={self.auth.authen_key}&checksum={checksum}"
106
+ r = self.session.get(url)
107
+ if r.status_code == 200:
108
+ return clean_json(check_api_error(r.json()))
109
+ raise Exception(f"Lỗi lấy Update Profile: {r.text}")
110
+
111
+ def get_notification(self):
112
+ """Lấy thông báo qua MSSV"""
113
+ checksum = create_checksum(self.auth.mssv, self.auth.campus)
114
+ url = f"{FAP_HOST}/GetNotificationByRoll?campusCode={self.auth.campus}&rollNumber={self.auth.mssv}&Authen={self.auth.authen_key}&checksum={checksum}"
115
+ r = self.session.get(url)
116
+ if r.status_code == 200:
117
+ return clean_json(check_api_error(r.json()))
118
+ raise Exception(f"Lỗi lấy Notification: {r.text}")
119
+
120
+ def get_fee(self):
121
+ """Lấy danh sách học phí chưa thanh toán"""
122
+ checksum = create_checksum(self.auth.mssv, self.auth.campus)
123
+ url = f"{FAP_HOST}/GetFeeByRoll?campusCode={self.auth.campus}&rollNumber={self.auth.mssv}&Authen={self.auth.authen_key}&checksum={checksum}"
124
+ r = self.session.get(url)
125
+ if r.status_code == 200:
126
+ return clean_json(check_api_error(r.json()))
127
+ if r.status_code == 404:
128
+ return {"status": "Không có khoản nợ học phí nào cần thanh toán."}
129
+ raise Exception(f"Lỗi lấy Fee: {r.text}")
130
+
131
+ class MyFapClient:
132
+ def __init__(self, auth):
133
+ self.auth = auth
134
+ self.session = requests.Session()
135
+ self.session.headers.update({"User-Agent": "okhttp/4.9.2"})
136
+
137
+ def get_schedule(self, semester: str):
138
+ """Lấy lịch học theo kỳ"""
139
+ checksum = create_checksum(self.auth.mssv, self.auth.campus)
140
+ url = f"{FAP_HOST}/GetActivityStudent?campusCode={self.auth.campus}&rollNumber={self.auth.mssv}&Semester={semester}&Authen={self.auth.authen_key}&checksum={checksum}"
141
+ r = self.session.get(url)
142
+ if r.status_code == 200:
143
+ return clean_json(check_api_error(r.json()))
144
+ raise Exception(f"Lỗi lấy lịch học: {r.text}")
145
+
146
+ def get_marks(self, semester: str):
147
+ """Lấy bảng điểm theo kỳ"""
148
+ checksum = create_checksum(self.auth.mssv, self.auth.campus)
149
+ url = f"{FAP_HOST}/GetStudentMark?campusCode={self.auth.campus}&rollNumber={self.auth.mssv}&Semester={semester}&Authen={self.auth.authen_key}&checksum={checksum}"
150
+ r = self.session.get(url)
151
+ if r.status_code == 200:
152
+ return clean_json(check_api_error(r.json()))
153
+ raise Exception(f"Lỗi lấy bảng điểm: {r.text}")
154
+
155
+ def get_mark_by_course(self, course_id: str):
156
+ """Lấy điểm chi tiết của một môn học"""
157
+ checksum = create_checksum(self.auth.mssv, self.auth.campus)
158
+ url = f"{FAP_HOST}/GetMarkByCourse?campusCode={self.auth.campus}&CourseId={course_id}&rollNumber={self.auth.mssv}&Authen={self.auth.authen_key}&checksum={checksum}"
159
+ r = self.session.get(url)
160
+ if r.status_code == 200:
161
+ return clean_json(check_api_error(r.json()))
162
+ raise Exception(f"Lỗi lấy điểm môn học: {r.text}")
163
+
164
+ def get_exams(self, semester: str):
165
+ """Lấy lịch thi theo kỳ"""
166
+ checksum = create_checksum(self.auth.mssv, self.auth.campus)
167
+ url = f"{FAP_HOST}/GetScheduleExam?campusCode={self.auth.campus}&rollNumber={self.auth.mssv}&Authen={self.auth.authen_key}&checksum={checksum}&Semester={semester}"
168
+ r = self.session.get(url)
169
+ if r.status_code == 200:
170
+ return clean_json(check_api_error(r.json()))
171
+ raise Exception(f"Lỗi lấy lịch thi: {r.text}")
172
+
173
+ def get_schedule_by_week(self, week: int, semester: str, year: int):
174
+ """Lấy lịch học theo tuần"""
175
+ checksum = create_checksum(self.auth.mssv, self.auth.campus)
176
+ url = f"{FAP_HOST}/GetActivityStudentByWeek?campusCode={self.auth.campus}&week={week}&rollNumber={self.auth.mssv}&Semester={semester}&year={year}&Authen={self.auth.authen_key}&checksum={checksum}"
177
+ r = self.session.get(url)
178
+ if r.status_code == 200:
179
+ return clean_json(check_api_error(r.json()))
180
+ raise Exception(f"Lỗi lấy lịch học tuần: {r.text}")
181
+
182
+ def get_attendances(self, semester: str):
183
+ """Lấy danh sách trạng thái điểm danh tổng quát của kỳ"""
184
+ checksum = create_checksum(self.auth.mssv, self.auth.campus)
185
+ url = f"{FAP_HOST}/GetStudentAttendances?campusCode={self.auth.campus}&Semester={semester}&rollNumber={self.auth.mssv}&Authen={self.auth.authen_key}&checksum={checksum}"
186
+ r = self.session.get(url)
187
+ if r.status_code == 200:
188
+ return clean_json(check_api_error(r.json()))
189
+ raise Exception(f"Lỗi lấy trạng thái điểm danh: {r.text}")
190
+
191
+ def get_course_attendance(self, semester: str, subject_code: str, class_name: str):
192
+ """Lấy chi tiết điểm danh của một môn học"""
193
+ checksum = create_checksum(self.auth.mssv, self.auth.campus)
194
+ url = f"{FAP_HOST}/getCourseAttendance?campusCode={self.auth.campus}&rollNumber={self.auth.mssv}&Semester={semester}&SubjectCode={subject_code}&ClassName={class_name}&Authen={self.auth.authen_key}&checksum={checksum}"
195
+ r = self.session.get(url)
196
+ if r.status_code == 200:
197
+ return clean_json(check_api_error(r.json()))
198
+ raise Exception(f"Lỗi lấy chi tiết điểm danh môn học: {r.text}")
199
+
200
+ def get_applications(self):
201
+ """Lấy danh sách các đơn từ đã gửi cho trường"""
202
+ checksum = create_checksum(self.auth.mssv, self.auth.campus)
203
+ url = f"{FAP_HOST}/GetApplication?campusCode={self.auth.campus}&rollNumber={self.auth.mssv}&Authen={self.auth.authen_key}&checksum={checksum}"
204
+ r = self.session.get(url)
205
+ if r.status_code == 200:
206
+ return clean_json(check_api_error(r.json()))
207
+ raise Exception(f"Lỗi lấy danh sách đơn từ: {r.text}")
208
+
209
+ def get_student_info(self):
210
+ """Lấy thông tin sinh viên"""
211
+ checksum = create_checksum(self.auth.mssv, self.auth.campus)
212
+ url = f"{FAP_HOST}/GetStudentById?campusCode={self.auth.campus}&rollNumber={self.auth.mssv}&Authen={self.auth.authen_key}&checksum={checksum}"
213
+ r = self.session.get(url)
214
+ if r.status_code == 200:
215
+ return clean_json(check_api_error(r.json()))
216
+ raise Exception(f"Lỗi lấy thông tin sinh viên: {r.text}")
217
+
218
+ def get_campus_info(self):
219
+ """Lấy thông tin phòng Dịch vụ sinh viên"""
220
+ checksum = create_checksum(self.auth.mssv, self.auth.campus)
221
+ url = f"{FAP_HOST}/GetCampusInfo?campusCode={self.auth.campus}&rollNumber={self.auth.mssv}&Authen={self.auth.authen_key}&checksum={checksum}"
222
+ r = self.session.get(url)
223
+ if r.status_code == 200:
224
+ return clean_json(check_api_error(r.json()))
225
+ raise Exception(f"Lỗi lấy thông tin campus: {r.text}")
226
+