parse-comcigan 0.0.1

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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Pro203S
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.
package/README.md ADDED
@@ -0,0 +1,118 @@
1
+ # parse-comcigan
2
+
3
+ 컴시간을 파싱하는 라이브러리입니다.
4
+ 안드로이드 컴시간알리미 앱에서 파싱합니다.
5
+ 아직 학생 시간표밖에 지원하지 않습니다.
6
+
7
+ ## 경고
8
+ 본 라이브러리는 **비공식적**으로 컴시간알리미의 데이터를 파싱합니다.
9
+ 상업적으로 사용하다 문제가 발생할경우, **제작자는 책임을 지지 않습니다.**
10
+
11
+ ## 설치
12
+ ```npm install parse-comcigan```
13
+
14
+ ## 개발
15
+
16
+ ### Comcigan
17
+
18
+ Comcigan 클래스의 인스턴스를 생성하여 사용합니다.
19
+ 인스턴스를 생성할 때 옵션을 지정할 수 있습니다.
20
+
21
+ |옵션|타입|기본값|설명|
22
+ |------|------|------|-----------|
23
+ |cacheMs|number|0|캐시될 시간(밀리초)입니다. 0일때 캐시를 비활성화합니다.|
24
+ |doNotThrow|boolean|true|[Axios](https://axios-http.com/)에서 validateStatus를 항상 true로 반환할 지에 대한 여부입니다.|
25
+ |debug|(msg: string) => any|undefined|디버그 메시지를 처리하는 콜백입니다. 이 콜백은 주로 컴시간과 통신할 때 호출됩니다.|
26
+
27
+ ```typescript
28
+ import Comcigan from 'parse-comcigan';
29
+
30
+ const comci = new Comcigan({
31
+ "cacheMs": 10000, // 10초동안 캐시됩니다.
32
+ "doNotThrow": true, // true일때, status가 4 또는 5로 시작하면 throw 됩니다.
33
+ "debug": (msg) => console.log(msg) // 모듈에서 디버그 메시지를 보내면 콘솔에 출력합니다.
34
+ });
35
+ ```
36
+ ---
37
+ ### SearchSchool
38
+
39
+ 학교를 검색합니다.
40
+ 마지막 "없으면 추가 검색 하세요." 문구는 자동으로 빠져서 나옵니다.
41
+ 당연하지만 컴시간을 사용하는 학교만 검색됩니다.
42
+
43
+ |파라메터|타입|필수|설명|
44
+ |------|------|------|-----------|
45
+ |query|string|Yes|검색어|
46
+
47
+ ```typescript
48
+ comci.SearchSchool("서울").then(data => {
49
+ console.log(data);
50
+ });
51
+ ```
52
+
53
+ 다음을 Promise 형태로 리턴합니다.
54
+ |키|타입|설명|
55
+ |-----|----|-----|
56
+ |region|string|학교의 지역|
57
+ |name|string|학교 이름|
58
+ |code|number|학교 코드|
59
+
60
+ ---
61
+ ### GetTimetable
62
+
63
+ 시간표를 가져옵니다.
64
+
65
+ |파라메터|타입|필수|설명|
66
+ |------|------|------|-----------|
67
+ |schoolCode|number|Yes|학교 코드|
68
+ |criteria|"weekday" or "period"|Yes|리스트 반환을 어떻게 할지 여부입니다.|
69
+ |grade|number|Yes|학년|
70
+ |classN|number|Yes|반|
71
+
72
+ ```typescript
73
+ comci.GetTimetable(96211, "weekday", 3, 5).then(data => {
74
+ console.log(data[0][5]); // [요일][교시] 월요일 5교시
75
+ });
76
+ comci.GetTimetable(96211, "period", 3, 5).then(data => {
77
+ console.log(data[5][0]); // [교시][요일] 월요일 5교시
78
+ });
79
+ ```
80
+
81
+ 다음을 Promise 형태로 리턴합니다.
82
+ |키|타입|설명|
83
+ |-----|----|-----|
84
+ |latestVersion|string|컴시간 앱 최신버전|
85
+ |changed|string|수정된 날짜 시간|
86
+ |timetable|[ComciganTimetableObject](#ComciganTimetableObject)[][]|시간표|
87
+
88
+ ---
89
+ ### GetClassList
90
+
91
+ 학급 리스트를 가져옵니다.
92
+
93
+ |파라메터|타입|필수|설명|
94
+ |------|------|------|-----------|
95
+ |schoolCode|number|Yes|학교 코드|
96
+
97
+ ```typescript
98
+ comci.GetClassList(96211).then(data => {
99
+ console.log(data[0]); // 1-1
100
+ });
101
+ ```
102
+
103
+ 리턴 타입: ```Promise<string>```
104
+
105
+ ## 타입
106
+
107
+ ### ComciganTimetableObject
108
+
109
+ |키|타입|설명|필수|
110
+ |-----|----|-----|-----|
111
+ |subject|string|과목|Yes|
112
+ |changed|string|교사|Yes|
113
+ |original|여기서 original만 빠진 오브젝트|원래 시간표|No|
114
+
115
+ ## 업데이트 로그
116
+
117
+ ### 0.0.1
118
+ - 첫번째 릴리즈
@@ -0,0 +1,106 @@
1
+ export interface ComciganInitializeType {
2
+ /**
3
+ * 이 기간동안 컴시간과 통신한 데이터가 캐시됩니다.
4
+ *
5
+ * 기본값은 0ms이며, 0ms일때는 캐시를 하지 않습니다.
6
+ */
7
+ cacheMs?: number;
8
+ /**
9
+ * axios에서 요청을 보내고 status가 오류 status일 때 throw할 지 여부입니다.
10
+ * 만약 false일 때 요청에 status가 오류 status면 그 타입에서 빈 값을 리턴합니다.
11
+ *
12
+ * 기본값은 false입니다.
13
+ */
14
+ doNotThrow?: boolean;
15
+ /**
16
+ * 디버그 메시지를 파라메터로 받습니다.
17
+ *
18
+ * 기본값은 없습니다.
19
+ */
20
+ debug?: (msg: string) => any;
21
+ }
22
+ export type ComciganSearched = {
23
+ /**
24
+ * 학교의 지역입니다.
25
+ */
26
+ region: string;
27
+ /**
28
+ * 학교의 이름입니다.
29
+ */
30
+ name: string;
31
+ /**
32
+ * 학교의 코드입니다.
33
+ */
34
+ code: number;
35
+ };
36
+ export type ComciganTimetableObject = {
37
+ /**
38
+ * 과목입니다.
39
+ */
40
+ subject: string;
41
+ /**
42
+ * 교사 성명입니다.
43
+ */
44
+ teacher: string;
45
+ /**
46
+ * 만약 변경되었을때 이 값이 주어집니다.
47
+ */
48
+ replacedFrom?: {
49
+ /**
50
+ * 과목입니다.
51
+ */
52
+ subject: string;
53
+ /**
54
+ * 교사 성명입니다.
55
+ */
56
+ teacher: string;
57
+ };
58
+ };
59
+ export type ComciganTimetable = {
60
+ /**
61
+ * 컴시간알리미 앱의 최신 버전입니다.
62
+ */
63
+ latestVersion: string;
64
+ /**
65
+ * 수정일입니다.
66
+ */
67
+ changedWhen: string;
68
+ /**
69
+ * 시간표입니다.
70
+ */
71
+ timetable: ComciganTimetableObject[][];
72
+ };
73
+ export default class Comcigan {
74
+ private _options;
75
+ private _cache;
76
+ private mSb;
77
+ private mTh;
78
+ private request;
79
+ private EUC_KR_encodeURI;
80
+ /**
81
+ * 파서를 초기화 합니다.
82
+ * @param options 파서의 옵션입니다.
83
+ */
84
+ constructor(options?: ComciganInitializeType);
85
+ /**
86
+ * 학교를 검색합니다.
87
+ * 마지막 "없으면 추가 검색 하세요" 문구는 빠져서 나옵니다.
88
+ * @param query 검색어
89
+ * @returns 검색된 학교들
90
+ */
91
+ SearchSchool(query: string): Promise<ComciganSearched[]>;
92
+ /**
93
+ * 시간표를 가져옵니다.
94
+ *
95
+ * @param schoolCode
96
+ * @param criteria
97
+ */
98
+ GetTimetable(schoolCode: number, criteria: "weekday" | "period", grade: number, classN: number): Promise<ComciganTimetable>;
99
+ /**
100
+ * 학급 목록을 가져옵니다.
101
+ *
102
+ * @param schoolCode 학교 코드입니다.
103
+ * @returns (학년)-(반) 문자열이 모인 배열
104
+ */
105
+ GetClassList(schoolCode: number): Promise<string[]>;
106
+ }
package/dist/index.js ADDED
@@ -0,0 +1,229 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import axios from 'axios';
11
+ import iconv from 'iconv-lite';
12
+ class Cache {
13
+ constructor() {
14
+ this._cacheStorage = {};
15
+ }
16
+ register(id, data, duration) {
17
+ this._cacheStorage[id] = {
18
+ "timeout": setTimeout(() => {
19
+ delete this._cacheStorage[id];
20
+ }, duration),
21
+ "data": data
22
+ };
23
+ }
24
+ delete(id) {
25
+ clearTimeout(this._cacheStorage[id].timeout);
26
+ delete this._cacheStorage[id];
27
+ }
28
+ clear() {
29
+ const caches = Object.keys(this._cacheStorage);
30
+ for (let cache of caches) {
31
+ clearTimeout(this._cacheStorage[cache].timeout);
32
+ delete this._cacheStorage[cache];
33
+ }
34
+ }
35
+ has(id) {
36
+ return Boolean(this._cacheStorage[id]);
37
+ }
38
+ get(id) {
39
+ var _a;
40
+ return (_a = this._cacheStorage[id]) === null || _a === void 0 ? void 0 : _a.data;
41
+ }
42
+ }
43
+ //#endregion
44
+ export default class Comcigan {
45
+ mSb(mm, m2) { if (m2 == 100) {
46
+ return mm % m2;
47
+ } return Math.floor(mm / m2); }
48
+ mTh(mm, m2) { if (m2 == 100) {
49
+ return Math.floor(mm / m2);
50
+ } return mm % m2; }
51
+ request(route, doNotParse) {
52
+ return __awaiter(this, void 0, void 0, function* () {
53
+ if (this._cache.has(route)) {
54
+ this._options.debug ? this._options.debug(`[request] Return cache with id registered as ${route}`) : 0;
55
+ return this._cache.get(route);
56
+ }
57
+ this._options.debug ? this._options.debug(`[request] Requesting to http://comci.kr:4081/${route}`) : 0;
58
+ const r = yield axios.get(`http://comci.kr:4081/${route}`, Object.assign({}, (this._options.doNotThrow && { "validateStatus": () => true })));
59
+ if (doNotParse)
60
+ return r.data;
61
+ const data = JSON.parse(r.data.slice(0, r.data.indexOf("}") + 1));
62
+ if (this._options.cacheMs && this._options.cacheMs !== 0) {
63
+ this._options.debug ? this._options.debug(`[request] Registered cache with id ${route}`) : 0;
64
+ this._cache.register(route, data, this._options.cacheMs);
65
+ }
66
+ return data;
67
+ });
68
+ }
69
+ EUC_KR_encodeURI(str) {
70
+ const buffer = iconv.encode(str, 'euc-kr');
71
+ return Array.from(buffer)
72
+ .map((byte) => '%' + byte.toString(16).toUpperCase().padStart(2, '0'))
73
+ .join('');
74
+ }
75
+ /**
76
+ * 파서를 초기화 합니다.
77
+ * @param options 파서의 옵션입니다.
78
+ */
79
+ constructor(options) {
80
+ this._options = {
81
+ "cacheMs": 0,
82
+ "doNotThrow": false
83
+ };
84
+ this._cache = new Cache();
85
+ this._options = Object.assign(Object.assign({}, this._options), options);
86
+ }
87
+ /**
88
+ * 학교를 검색합니다.
89
+ * 마지막 "없으면 추가 검색 하세요" 문구는 빠져서 나옵니다.
90
+ * @param query 검색어
91
+ * @returns 검색된 학교들
92
+ */
93
+ SearchSchool(query) {
94
+ return __awaiter(this, void 0, void 0, function* () {
95
+ const r = yield this.request(`sc_${this.EUC_KR_encodeURI(query)}`);
96
+ return r.학교검색.map(v => ({
97
+ "region": v[1],
98
+ "name": v[2],
99
+ "code": v[3]
100
+ })).filter(v => v.code !== 0);
101
+ });
102
+ }
103
+ /**
104
+ * 시간표를 가져옵니다.
105
+ *
106
+ * @param schoolCode
107
+ * @param criteria
108
+ */
109
+ GetTimetable(schoolCode, criteria, grade, classN) {
110
+ return __awaiter(this, void 0, void 0, function* () {
111
+ var _a;
112
+ const str = `36174_${schoolCode}_1_4_0_3_1.00`;
113
+ const route = (str.substring(9) + str.substring(0, 9)).split("").reverse().join("");
114
+ const data = yield this.request(`7813?${route}`, true);
115
+ const [latestVersion, step1] = data.split("^");
116
+ const edited = step1.substring(0, step1.indexOf("{"));
117
+ const step2 = step1.slice(step1.indexOf("{"));
118
+ const json = JSON.parse(step2.slice(0, step2.indexOf("}") + 1));
119
+ const WEEKDAY = ["월", "화", "수", "목", "금"];
120
+ const sep = (_a = json.분리) !== null && _a !== void 0 ? _a : 100;
121
+ // 시간표[학년][반][요일][교시]
122
+ const origin = json.시간표[grade][classN];
123
+ const today = json.학급시간표[grade][classN];
124
+ const toReturn = [];
125
+ if (criteria === "period") {
126
+ for (let weekday = 1; weekday < WEEKDAY.length + 1; weekday++) {
127
+ const weekdayList = [];
128
+ for (let period = 1; period < 9; period++) {
129
+ const o = origin[weekday][period];
130
+ const t = today[weekday][period];
131
+ if (!o || !t) {
132
+ weekdayList.push({
133
+ "subject": "-",
134
+ "teacher": "-"
135
+ });
136
+ continue;
137
+ }
138
+ const originSubject = json.과목명[this.mSb(o, sep) % sep];
139
+ const originTeacher = json.성명[this.mTh(o, sep) % sep];
140
+ if (o === t) {
141
+ weekdayList.push({
142
+ "subject": originSubject,
143
+ "teacher": originTeacher
144
+ });
145
+ continue;
146
+ }
147
+ const changedSubject = json.과목명[this.mSb(t, sep) % sep];
148
+ const changedTeacher = json.성명[this.mTh(t, sep) % sep];
149
+ weekdayList.push({
150
+ "subject": originSubject,
151
+ "teacher": originTeacher,
152
+ "replacedFrom": {
153
+ "subject": changedSubject,
154
+ "teacher": changedTeacher
155
+ }
156
+ });
157
+ }
158
+ toReturn.push(weekdayList);
159
+ }
160
+ }
161
+ else {
162
+ for (let period = 1; period < 9; period++) {
163
+ const periodList = [];
164
+ for (let weekday = 1; weekday < WEEKDAY.length + 1; weekday++) {
165
+ const o = origin[weekday][period];
166
+ const t = today[weekday][period];
167
+ if (!o || !t) {
168
+ periodList.push({
169
+ "subject": "-",
170
+ "teacher": "-"
171
+ });
172
+ continue;
173
+ }
174
+ const originSubject = json.과목명[this.mSb(o, sep) % sep];
175
+ const originTeacher = json.성명[this.mTh(o, sep) % sep];
176
+ if (o === t) {
177
+ periodList.push({
178
+ "subject": originSubject,
179
+ "teacher": originTeacher
180
+ });
181
+ continue;
182
+ }
183
+ const changedSubject = json.과목명[this.mSb(t, sep) % sep];
184
+ const changedTeacher = json.성명[this.mTh(t, sep) % sep];
185
+ periodList.push({
186
+ "subject": originSubject,
187
+ "teacher": originTeacher,
188
+ "replacedFrom": {
189
+ "subject": changedSubject,
190
+ "teacher": changedTeacher
191
+ }
192
+ });
193
+ }
194
+ toReturn.push(periodList);
195
+ }
196
+ }
197
+ return {
198
+ "changedWhen": edited,
199
+ latestVersion,
200
+ "timetable": toReturn
201
+ };
202
+ });
203
+ }
204
+ /**
205
+ * 학급 목록을 가져옵니다.
206
+ *
207
+ * @param schoolCode 학교 코드입니다.
208
+ * @returns (학년)-(반) 문자열이 모인 배열
209
+ */
210
+ GetClassList(schoolCode) {
211
+ return __awaiter(this, void 0, void 0, function* () {
212
+ const str = `36174_${schoolCode}_1_4_0_3_1.00`;
213
+ const route = (str.substring(9) + str.substring(0, 9)).split("").reverse().join("");
214
+ const data = yield this.request(`7813?${route}`, true);
215
+ const step1 = data.split("^")[1];
216
+ const step2 = step1.slice(step1.indexOf("{"));
217
+ const json = JSON.parse(step2.slice(0, step2.indexOf("}") + 1));
218
+ const rawClasses = json.학급수;
219
+ const toReturn = [];
220
+ for (let g = 1; g < rawClasses.length; g++) {
221
+ for (let c = 1; c < rawClasses[g] + 1; c++) {
222
+ toReturn.push(`${g}-${c}`);
223
+ }
224
+ }
225
+ return toReturn;
226
+ });
227
+ }
228
+ }
229
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,KAAK,MAAM,YAAY,CAAC;AAE/B,MAAM,KAAK;IAAX;QACY,kBAAa,GAKjB,EAAE,CAAC;IA2BX,CAAC;IAzBU,QAAQ,CAAC,EAAU,EAAE,IAAS,EAAE,QAAgB;QACnD,IAAI,CAAC,aAAa,CAAC,EAAE,CAAC,GAAG;YACrB,SAAS,EAAE,UAAU,CAAC,GAAG,EAAE;gBACvB,OAAO,IAAI,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC;YAClC,CAAC,EAAE,QAAQ,CAAC;YACZ,MAAM,EAAE,IAAI;SACf,CAAC;IACN,CAAC;IACM,MAAM,CAAC,EAAU;QACpB,YAAY,CAAC,IAAI,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC;QAC7C,OAAO,IAAI,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC;IAClC,CAAC;IACM,KAAK;QACR,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QAC/C,KAAK,IAAI,KAAK,IAAI,MAAM,EAAE,CAAC;YACvB,YAAY,CAAC,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,CAAC;YAChD,OAAO,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;QACrC,CAAC;IACL,CAAC;IACM,GAAG,CAAC,EAAU;QACjB,OAAO,OAAO,CAAC,IAAI,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC,CAAC;IAC3C,CAAC;IACM,GAAG,CAAC,EAAU;;QACjB,OAAO,MAAA,IAAI,CAAC,aAAa,CAAC,EAAE,CAAC,0CAAE,IAAI,CAAC;IACxC,CAAC;CACJ;AAqHD,YAAY;AAEZ,MAAM,CAAC,OAAO,OAAO,QAAQ;IAOjB,GAAG,CAAC,EAAU,EAAE,EAAU,IAAI,IAAI,EAAE,IAAI,GAAG,EAAE,CAAC;QAAC,OAAO,EAAE,GAAG,EAAE,CAAC;IAAC,CAAC,CAAC,OAAO,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC;IAC9F,GAAG,CAAC,EAAU,EAAE,EAAU,IAAI,IAAI,EAAE,IAAI,GAAG,EAAE,CAAC;QAAC,OAAO,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC;IAAC,CAAC,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC;IAExF,OAAO,CAAU,KAAa,EAAE,UAAoB;;YAC9D,IAAI,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;gBACzB,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,gDAAgD,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;gBACvG,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;YAClC,CAAC;YAED,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,gDAAgD,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YACvG,MAAM,CAAC,GAAG,MAAM,KAAK,CAAC,GAAG,CAAM,wBAAwB,KAAK,EAAE,oBACvD,CAAC,IAAI,CAAC,QAAQ,CAAC,UAAU,IAAI,EAAE,gBAAgB,EAAE,GAAG,EAAE,CAAC,IAAI,EAAE,CAAC,EACnE,CAAC;YAEH,IAAI,UAAU;gBAAE,OAAO,CAAC,CAAC,IAAI,CAAC;YAE9B,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;YAClE,IAAI,IAAI,CAAC,QAAQ,CAAC,OAAO,IAAI,IAAI,CAAC,QAAQ,CAAC,OAAO,KAAK,CAAC,EAAE,CAAC;gBACvD,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,sCAAsC,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;gBAC7F,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;YAC7D,CAAC;YACD,OAAO,IAAI,CAAC;QAChB,CAAC;KAAA;IAEO,gBAAgB,CAAC,GAAW;QAChC,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;QAC3C,OAAO,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC;aACpB,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;aACrE,IAAI,CAAC,EAAE,CAAC,CAAC;IAClB,CAAC;IAED;;;OAGG;IACH,YAAY,OAAgC;QAzCpC,aAAQ,GAA2B;YACvC,SAAS,EAAE,CAAC;YACZ,YAAY,EAAE,KAAK;SACtB,CAAC;QACM,WAAM,GAAG,IAAI,KAAK,EAAE,CAAC;QAsCzB,IAAI,CAAC,QAAQ,mCACN,IAAI,CAAC,QAAQ,GACb,OAAO,CACb,CAAC;IACN,CAAC;IAED;;;;;OAKG;IACU,YAAY,CAAC,KAAa;;YACnC,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC,OAAO,CAAsB,MAAM,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;YAExF,OAAO,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;gBACpB,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC;gBACd,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC;gBACZ,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC;aACf,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC;QAClC,CAAC;KAAA;IAED;;;;;OAKG;IACU,YAAY,CAAC,UAAkB,EAAE,QAA8B,EAAE,KAAa,EAAE,MAAc;;;YACvG,MAAM,GAAG,GAAG,SAAS,UAAU,eAAe,CAAC;YAC/C,MAAM,KAAK,GAAG,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACpF,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,OAAO,CAAS,QAAQ,KAAK,EAAE,EAAE,IAAI,CAAC,CAAC;YAE/D,MAAM,CAAC,aAAa,EAAE,KAAK,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YAC/C,MAAM,MAAM,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,EAAE,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC;YACtD,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC;YAC9C,MAAM,IAAI,GAAyB,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;YAEtF,MAAM,OAAO,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;YAC1C,MAAM,GAAG,GAAG,MAAA,IAAI,CAAC,EAAE,mCAAI,GAAG,CAAC;YAE3B,qBAAqB;YACrB,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC;YACvC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC;YAExC,MAAM,QAAQ,GAAgC,EAAE,CAAC;YAEjD,IAAI,QAAQ,KAAK,QAAQ,EAAE,CAAC;gBACxB,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,OAAO,EAAE,EAAE,CAAC;oBAC5D,MAAM,WAAW,GAA8B,EAAE,CAAC;oBAClD,KAAK,IAAI,MAAM,GAAG,CAAC,EAAE,MAAM,GAAG,CAAC,EAAE,MAAM,EAAE,EAAE,CAAC;wBACxC,MAAM,CAAC,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC;wBAClC,MAAM,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC;wBAEjC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;4BACX,WAAW,CAAC,IAAI,CAAC;gCACb,SAAS,EAAE,GAAG;gCACd,SAAS,EAAE,GAAG;6BACjB,CAAC,CAAC;4BACH,SAAS;wBACb,CAAC;wBAED,MAAM,aAAa,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,GAAG,CAAC,CAAC;wBACvD,MAAM,aAAa,GAAG,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,GAAG,CAAC,CAAC;wBAEtD,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;4BACV,WAAW,CAAC,IAAI,CAAC;gCACb,SAAS,EAAE,aAAa;gCACxB,SAAS,EAAE,aAAa;6BAC3B,CAAC,CAAC;4BACH,SAAS;wBACb,CAAC;wBAED,MAAM,cAAc,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,GAAG,CAAC,CAAC;wBACxD,MAAM,cAAc,GAAG,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,GAAG,CAAC,CAAC;wBAEvD,WAAW,CAAC,IAAI,CAAC;4BACb,SAAS,EAAE,aAAa;4BACxB,SAAS,EAAE,aAAa;4BACxB,cAAc,EAAE;gCACZ,SAAS,EAAE,cAAc;gCACzB,SAAS,EAAE,cAAc;6BAC5B;yBACJ,CAAC,CAAC;oBACP,CAAC;oBACD,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;gBAC/B,CAAC;YACL,CAAC;iBAAM,CAAC;gBACJ,KAAK,IAAI,MAAM,GAAG,CAAC,EAAE,MAAM,GAAG,CAAC,EAAE,MAAM,EAAE,EAAE,CAAC;oBACxC,MAAM,UAAU,GAA8B,EAAE,CAAC;oBACjD,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,OAAO,EAAE,EAAE,CAAC;wBAC5D,MAAM,CAAC,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC;wBAClC,MAAM,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC;wBAEjC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;4BACX,UAAU,CAAC,IAAI,CAAC;gCACZ,SAAS,EAAE,GAAG;gCACd,SAAS,EAAE,GAAG;6BACjB,CAAC,CAAC;4BACH,SAAS;wBACb,CAAC;wBAED,MAAM,aAAa,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,GAAG,CAAC,CAAC;wBACvD,MAAM,aAAa,GAAG,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,GAAG,CAAC,CAAC;wBAEtD,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;4BACV,UAAU,CAAC,IAAI,CAAC;gCACZ,SAAS,EAAE,aAAa;gCACxB,SAAS,EAAE,aAAa;6BAC3B,CAAC,CAAC;4BACH,SAAS;wBACb,CAAC;wBAED,MAAM,cAAc,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,GAAG,CAAC,CAAC;wBACxD,MAAM,cAAc,GAAG,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,GAAG,CAAC,CAAC;wBAEvD,UAAU,CAAC,IAAI,CAAC;4BACZ,SAAS,EAAE,aAAa;4BACxB,SAAS,EAAE,aAAa;4BACxB,cAAc,EAAE;gCACZ,SAAS,EAAE,cAAc;gCACzB,SAAS,EAAE,cAAc;6BAC5B;yBACJ,CAAC,CAAC;oBACP,CAAC;oBACD,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;gBAC9B,CAAC;YACL,CAAC;YAED,OAAO;gBACH,aAAa,EAAE,MAAM;gBACrB,aAAa;gBACb,WAAW,EAAE,QAAQ;aACxB,CAAC;QACN,CAAC;KAAA;IAED;;;;;OAKG;IACU,YAAY,CAAC,UAAkB;;YACxC,MAAM,GAAG,GAAG,SAAS,UAAU,eAAe,CAAC;YAC/C,MAAM,KAAK,GAAG,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACpF,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,OAAO,CAAS,QAAQ,KAAK,EAAE,EAAE,IAAI,CAAC,CAAC;YAE/D,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;YACjC,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC;YAC9C,MAAM,IAAI,GAAyB,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;YACtF,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC;YAE5B,MAAM,QAAQ,GAAa,EAAE,CAAC;YAE9B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,UAAU,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;gBACzC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;oBACzC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;gBAC/B,CAAC;YACL,CAAC;YAED,OAAO,QAAQ,CAAC;QACpB,CAAC;KAAA;CACJ"}
package/package.json ADDED
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "parse-comcigan",
3
+ "version": "0.0.1",
4
+ "description": "컴시간 알리미에서 시간표를 가져오는 라이브러리입니다.",
5
+ "main": "./dist/index.js",
6
+ "types": "./dist/index.d.ts",
7
+ "author": "Pro203S",
8
+ "license": "MIT",
9
+ "type": "module",
10
+ "scripts": {
11
+ "pack": "npm run clean && npm run build",
12
+ "build": "tsc",
13
+ "clean": "tsc --build --clean",
14
+ "dev": "nodemon --exec ts-node src/index.ts"
15
+ },
16
+ "devDependencies": {
17
+ "@types/node": "^22.14.1",
18
+ "nodemon": "^3.1.9",
19
+ "ts-node": "^10.9.2",
20
+ "typescript": "^5.8.3"
21
+ },
22
+ "dependencies": {
23
+ "axios": "^1.8.4",
24
+ "iconv-lite": "^0.6.3"
25
+ }
26
+ }
package/src/index.ts ADDED
@@ -0,0 +1,367 @@
1
+ import axios from 'axios';
2
+ import iconv from 'iconv-lite';
3
+
4
+ class Cache {
5
+ private _cacheStorage: {
6
+ [id: string]: {
7
+ "timeout": NodeJS.Timeout;
8
+ "data": any;
9
+ }
10
+ } = {};
11
+
12
+ public register(id: string, data: any, duration: number) {
13
+ this._cacheStorage[id] = {
14
+ "timeout": setTimeout(() => {
15
+ delete this._cacheStorage[id];
16
+ }, duration),
17
+ "data": data
18
+ };
19
+ }
20
+ public delete(id: string) {
21
+ clearTimeout(this._cacheStorage[id].timeout);
22
+ delete this._cacheStorage[id];
23
+ }
24
+ public clear() {
25
+ const caches = Object.keys(this._cacheStorage);
26
+ for (let cache of caches) {
27
+ clearTimeout(this._cacheStorage[cache].timeout);
28
+ delete this._cacheStorage[cache];
29
+ }
30
+ }
31
+ public has(id: string) {
32
+ return Boolean(this._cacheStorage[id]);
33
+ }
34
+ public get(id: string) {
35
+ return this._cacheStorage[id]?.data;
36
+ }
37
+ }
38
+
39
+ //#region 내부 타입
40
+ type RawComciganSearched = {
41
+ "학교검색": [
42
+ number,
43
+ string,
44
+ string,
45
+ number
46
+ ][];
47
+ };
48
+ type RawComciganTimetable = {
49
+ 교사수: number;
50
+ 성명: string[];
51
+ 학급수: number[];
52
+ 요일별시수: number[][];
53
+ 과목명: any[];
54
+ 시간표: any;
55
+ 전일제: number[];
56
+ 버젼: string;
57
+ 동시수업수: number;
58
+ 담임: number[][];
59
+ 가상학급수: number[];
60
+ 특별실수: number;
61
+ 열람제한일: string;
62
+ 저장일: string;
63
+ 학기시작일자: string;
64
+ 학교명: string;
65
+ 지역명: string;
66
+ 학년도: number;
67
+ 분리?: number;
68
+ 강의실: number;
69
+ 시작일: string;
70
+ 일과시간: string[];
71
+ 일자자료: [number, string][];
72
+ 오늘r: number;
73
+ 학급시간표: any;
74
+ 교사시간표: any[];
75
+ 강의실시간표: any;
76
+ 동시그룹: number[][];
77
+ 컴시간?: string;
78
+ };
79
+ //#endregion
80
+
81
+ //#region 외부 타입
82
+ export interface ComciganInitializeType {
83
+ /**
84
+ * 이 기간동안 컴시간과 통신한 데이터가 캐시됩니다.
85
+ *
86
+ * 기본값은 0ms이며, 0ms일때는 캐시를 하지 않습니다.
87
+ */
88
+ cacheMs?: number;
89
+ /**
90
+ * axios에서 요청을 보내고 status가 오류 status일 때 throw할 지 여부입니다.
91
+ * 만약 false일 때 요청에 status가 오류 status면 그 타입에서 빈 값을 리턴합니다.
92
+ *
93
+ * 기본값은 false입니다.
94
+ */
95
+ doNotThrow?: boolean;
96
+ /**
97
+ * 디버그 메시지를 파라메터로 받습니다.
98
+ *
99
+ * 기본값은 없습니다.
100
+ */
101
+ debug?: (msg: string) => any;
102
+ }
103
+ export type ComciganSearched = {
104
+ /**
105
+ * 학교의 지역입니다.
106
+ */
107
+ region: string;
108
+ /**
109
+ * 학교의 이름입니다.
110
+ */
111
+ name: string;
112
+ /**
113
+ * 학교의 코드입니다.
114
+ */
115
+ code: number;
116
+ };
117
+ export type ComciganTimetableObject = {
118
+ /**
119
+ * 과목입니다.
120
+ */
121
+ subject: string;
122
+ /**
123
+ * 교사 성명입니다.
124
+ */
125
+ teacher: string;
126
+ /**
127
+ * 만약 변경되었을때 이 값이 주어집니다.
128
+ */
129
+ original?: {
130
+ /**
131
+ * 과목입니다.
132
+ */
133
+ subject: string;
134
+ /**
135
+ * 교사 성명입니다.
136
+ */
137
+ teacher: string;
138
+ }
139
+ };
140
+ export type ComciganTimetable = {
141
+ /**
142
+ * 컴시간알리미 앱의 최신 버전입니다.
143
+ */
144
+ latestVersion: string;
145
+ /**
146
+ * 수정일입니다.
147
+ */
148
+ changed: string;
149
+ /**
150
+ * 시간표입니다.
151
+ */
152
+ timetable: ComciganTimetableObject[][];
153
+ };
154
+ //#endregion
155
+
156
+ export default class Comcigan {
157
+ private _options: ComciganInitializeType = {
158
+ "cacheMs": 0,
159
+ "doNotThrow": false
160
+ };
161
+ private _cache = new Cache();
162
+
163
+ private mSb(mm: number, m2: number) { if (m2 == 100) { return mm % m2; } return Math.floor(mm / m2); }
164
+ private mTh(mm: number, m2: number) { if (m2 == 100) { return Math.floor(mm / m2); } return mm % m2; }
165
+
166
+ private async request<T = any>(route: string, doNotParse?: boolean): Promise<T> {
167
+ if (this._cache.has(route)) {
168
+ this._options.debug ? this._options.debug(`[request] Return cache with id registered as ${route}`) : 0;
169
+ return this._cache.get(route);
170
+ }
171
+
172
+ this._options.debug ? this._options.debug(`[request] Requesting to http://comci.kr:4081/${route}`) : 0;
173
+ const r = await axios.get<any>(`http://comci.kr:4081/${route}`, {
174
+ ...(this._options.doNotThrow && { "validateStatus": () => true })
175
+ });
176
+
177
+ if (doNotParse) {
178
+ if (this._options.cacheMs && this._options.cacheMs !== 0) {
179
+ this._options.debug ? this._options.debug(`[request] Registered cache with id ${route}`) : 0;
180
+ this._cache.register(route, r.data, this._options.cacheMs);
181
+ }
182
+ return r.data;
183
+ }
184
+
185
+ const data = JSON.parse(r.data.slice(0, r.data.indexOf("}") + 1));
186
+ if (this._options.cacheMs && this._options.cacheMs !== 0) {
187
+ this._options.debug ? this._options.debug(`[request] Registered cache with id ${route}`) : 0;
188
+ this._cache.register(route, data, this._options.cacheMs);
189
+ }
190
+ return data;
191
+ }
192
+
193
+ private EUC_KR_encodeURI(str: string): string {
194
+ const buffer = iconv.encode(str, 'euc-kr');
195
+ return Array.from(buffer)
196
+ .map((byte) => '%' + byte.toString(16).toUpperCase().padStart(2, '0'))
197
+ .join('');
198
+ }
199
+
200
+ /**
201
+ * 파서를 초기화 합니다.
202
+ * @param options 파서의 옵션입니다.
203
+ */
204
+ constructor(options?: ComciganInitializeType) {
205
+ this._options = {
206
+ ...this._options,
207
+ ...options
208
+ };
209
+ }
210
+
211
+ /**
212
+ * 학교를 검색합니다.
213
+ * 마지막 "없으면 추가 검색 하세요" 문구는 빠져서 나옵니다.
214
+ * @param query 검색어
215
+ * @returns 검색된 학교들
216
+ */
217
+ public async SearchSchool(query: string): Promise<ComciganSearched[]> {
218
+ const r = await this.request<RawComciganSearched>(`sc_${this.EUC_KR_encodeURI(query)}`);
219
+
220
+ return r.학교검색.map(v => ({
221
+ "region": v[1],
222
+ "name": v[2],
223
+ "code": v[3]
224
+ })).filter(v => v.code !== 0);
225
+ }
226
+
227
+ /**
228
+ * 시간표를 가져옵니다.
229
+ *
230
+ * @param schoolCode
231
+ * @param criteria
232
+ */
233
+ public async GetTimetable(schoolCode: number, criteria: "weekday" | "period", grade: number, classN: number): Promise<ComciganTimetable> {
234
+ const str = `36174_${schoolCode}_1_4_0_3_1.00`;
235
+ const route = (str.substring(9) + str.substring(0, 9)).split("").reverse().join("");
236
+ const data = await this.request<string>(`7813?${route}`, true);
237
+
238
+ const [latestVersion, step1] = data.split("^");
239
+ const edited = step1.substring(0, step1.indexOf("{"));
240
+ const step2 = step1.slice(step1.indexOf("{"));
241
+ const json: RawComciganTimetable = JSON.parse(step2.slice(0, step2.indexOf("}") + 1));
242
+
243
+ const WEEKDAY = ["월", "화", "수", "목", "금"];
244
+ const sep = json.분리 ?? 100;
245
+
246
+ // 시간표[학년][반][요일][교시]
247
+ const origin = json.시간표[grade][classN];
248
+ const today = json.학급시간표[grade][classN];
249
+
250
+ const toReturn: ComciganTimetableObject[][] = [];
251
+
252
+ if (criteria === "period") {
253
+ for (let weekday = 1; weekday < WEEKDAY.length + 1; weekday++) {
254
+ const weekdayList: ComciganTimetableObject[] = [];
255
+ for (let period = 1; period < 9; period++) {
256
+ const o = origin[weekday][period];
257
+ const t = today[weekday][period];
258
+
259
+ if (!o || !t) {
260
+ weekdayList.push({
261
+ "subject": "-",
262
+ "teacher": "-"
263
+ });
264
+ continue;
265
+ }
266
+
267
+ const originSubject = json.과목명[this.mSb(o, sep) % sep];
268
+ const originTeacher = json.성명[this.mTh(o, sep) % sep];
269
+
270
+ if (o === t) {
271
+ weekdayList.push({
272
+ "subject": originSubject,
273
+ "teacher": originTeacher
274
+ });
275
+ continue;
276
+ }
277
+
278
+ const changedSubject = json.과목명[this.mSb(t, sep) % sep];
279
+ const changedTeacher = json.성명[this.mTh(t, sep) % sep];
280
+
281
+ weekdayList.push({
282
+ "subject": originSubject,
283
+ "teacher": originTeacher,
284
+ "original": {
285
+ "subject": changedSubject,
286
+ "teacher": changedTeacher
287
+ }
288
+ });
289
+ }
290
+ toReturn.push(weekdayList);
291
+ }
292
+ } else {
293
+ for (let period = 1; period < 9; period++) {
294
+ const periodList: ComciganTimetableObject[] = [];
295
+ for (let weekday = 1; weekday < WEEKDAY.length + 1; weekday++) {
296
+ const o = origin[weekday][period];
297
+ const t = today[weekday][period];
298
+
299
+ if (!o || !t) {
300
+ periodList.push({
301
+ "subject": "-",
302
+ "teacher": "-"
303
+ });
304
+ continue;
305
+ }
306
+
307
+ const originSubject = json.과목명[this.mSb(o, sep) % sep];
308
+ const originTeacher = json.성명[this.mTh(o, sep) % sep];
309
+
310
+ if (o === t) {
311
+ periodList.push({
312
+ "subject": originSubject,
313
+ "teacher": originTeacher
314
+ });
315
+ continue;
316
+ }
317
+
318
+ const changedSubject = json.과목명[this.mSb(t, sep) % sep];
319
+ const changedTeacher = json.성명[this.mTh(t, sep) % sep];
320
+
321
+ periodList.push({
322
+ "subject": originSubject,
323
+ "teacher": originTeacher,
324
+ "original": {
325
+ "subject": changedSubject,
326
+ "teacher": changedTeacher
327
+ }
328
+ });
329
+ }
330
+ toReturn.push(periodList);
331
+ }
332
+ }
333
+
334
+ return {
335
+ "changed": edited,
336
+ latestVersion,
337
+ "timetable": toReturn
338
+ };
339
+ }
340
+
341
+ /**
342
+ * 학급 목록을 가져옵니다.
343
+ *
344
+ * @param schoolCode 학교 코드입니다.
345
+ * @returns (학년)-(반) 문자열이 모인 배열
346
+ */
347
+ public async GetClassList(schoolCode: number): Promise<string[]> {
348
+ const str = `36174_${schoolCode}_1_4_0_3_1.00`;
349
+ const route = (str.substring(9) + str.substring(0, 9)).split("").reverse().join("");
350
+ const data = await this.request<string>(`7813?${route}`, true);
351
+
352
+ const step1 = data.split("^")[1];
353
+ const step2 = step1.slice(step1.indexOf("{"));
354
+ const json: RawComciganTimetable = JSON.parse(step2.slice(0, step2.indexOf("}") + 1));
355
+ const rawClasses = json.학급수;
356
+
357
+ const toReturn: string[] = [];
358
+
359
+ for (let g = 1; g < rawClasses.length; g++) {
360
+ for (let c = 1; c < rawClasses[g] + 1; c++) {
361
+ toReturn.push(`${g}-${c}`);
362
+ }
363
+ }
364
+
365
+ return toReturn;
366
+ }
367
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,19 @@
1
+ {
2
+ "compilerOptions": {
3
+ "outDir": "dist",
4
+ "target": "ES6",
5
+ "module": "ESNext",
6
+ "lib": [
7
+ "ES6",
8
+ "DOM"
9
+ ],
10
+ "moduleResolution": "Node",
11
+ "esModuleInterop": true,
12
+ "declaration": true,
13
+ "strict": true,
14
+ "sourceMap": true
15
+ },
16
+ "include": [
17
+ "src/**/*"
18
+ ]
19
+ }