@akredit/acik-mcp 0.1.0 → 0.5.0
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/README.md +94 -8
- package/dist/apps/acik-mcp/src/araclar.d.ts +2 -28
- package/dist/apps/acik-mcp/src/araclar.js +29 -114
- package/dist/apps/acik-mcp/src/bologna.d.ts +291 -0
- package/dist/apps/acik-mcp/src/bologna.js +1693 -0
- package/dist/apps/acik-mcp/src/genel-ayristir.d.ts +92 -0
- package/dist/apps/acik-mcp/src/genel-ayristir.js +489 -0
- package/dist/apps/acik-mcp/src/http.d.ts +31 -3
- package/dist/apps/acik-mcp/src/http.js +90 -17
- package/dist/apps/acik-mcp/src/index.d.ts +7 -0
- package/dist/apps/acik-mcp/src/index.js +7 -0
- package/dist/apps/acik-mcp/src/kaynak-dogrula.d.ts +94 -0
- package/dist/apps/acik-mcp/src/kaynak-dogrula.js +460 -0
- package/dist/apps/acik-mcp/src/kaynak.d.ts +72 -0
- package/dist/apps/acik-mcp/src/kaynak.js +155 -0
- package/dist/apps/acik-mcp/src/kisisel-veri.d.ts +16 -2
- package/dist/apps/acik-mcp/src/kisisel-veri.js +78 -15
- package/dist/apps/acik-mcp/src/not-tablosu.d.ts +39 -0
- package/dist/apps/acik-mcp/src/not-tablosu.js +268 -0
- package/dist/apps/acik-mcp/src/promptlar.d.ts +48 -0
- package/dist/apps/acik-mcp/src/promptlar.js +323 -0
- package/dist/apps/acik-mcp/src/sunucu.d.ts +12 -4
- package/dist/apps/acik-mcp/src/sunucu.js +108 -24
- package/dist/apps/acik-mcp/src/temel.d.ts +59 -0
- package/dist/apps/acik-mcp/src/temel.js +10 -0
- package/dist/apps/acik-mcp/src/yardim.d.ts +56 -0
- package/dist/apps/acik-mcp/src/yardim.js +381 -0
- package/dist/packages/baglayicilar/src/bologna-proliz/ayristir.d.ts +29 -0
- package/dist/packages/baglayicilar/src/bologna-proliz/ayristir.js +200 -0
- package/dist/packages/cekirdek/src/bologna/taslak.d.ts +19 -3
- package/dist/packages/cekirdek/src/bologna/taslak.js +70 -9
- package/dist/packages/cekirdek/src/turler.d.ts +8 -0
- package/package.json +5 -3
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export interface Adim {
|
|
3
|
+
no: number;
|
|
4
|
+
yap: string;
|
|
5
|
+
/** Adımdan sonra ekranda ne görüleceği. */
|
|
6
|
+
goreceginiz?: string;
|
|
7
|
+
/** Menü ya da düğme adı sürüme göre değişebilir, doğrulanmadı. */
|
|
8
|
+
degisebilir?: boolean;
|
|
9
|
+
}
|
|
10
|
+
/** Her rehberin başında hocaya söylenecek giriş kuralı. */
|
|
11
|
+
export declare const GIRIS_KURALI_HOCAYA = "Kullan\u0131c\u0131 ad\u0131n\u0131z\u0131 ve parolan\u0131z\u0131 yaln\u0131z OBS'nin kendi giri\u015F ekran\u0131na, kendiniz yaz\u0131n. Bana, sohbete ya da Akredit'e asla yazmay\u0131n. Do\u011Frulama kodu (SMS, e-posta) ve CAPTCHA da yaln\u0131z sizindir.";
|
|
12
|
+
export declare const MCP_ADRESI = "https://akredit.org/mcp";
|
|
13
|
+
export declare const ISTEMCILER: readonly ["claude_web", "claude_masaustu", "claude_chrome", "chatgpt", "cursor", "vscode", "claude_code"];
|
|
14
|
+
export type Istemci = (typeof ISTEMCILER)[number];
|
|
15
|
+
/** Bağlayıcı ekleme adımları; site (akredit.org ana sayfası) aynı adımları buradan okur. */
|
|
16
|
+
export declare function baglayiciAdimlari(istemci: Istemci): Adim[];
|
|
17
|
+
export declare const ENGELLER: readonly ["tarayici_araci_yok", "site_acilmiyor", "giris_duvari", "captcha", "cerceve", "oturum_dustu", "sayfa_okunamiyor", "kaydet_basilamiyor", "akredit_gorunmuyor"];
|
|
18
|
+
export type Engel = (typeof ENGELLER)[number];
|
|
19
|
+
export declare const kurulumYardimiAraci: import("./temel.ts").AcikArac<z.ZodObject<{
|
|
20
|
+
istemci: z.ZodEnum<{
|
|
21
|
+
claude_web: "claude_web";
|
|
22
|
+
claude_masaustu: "claude_masaustu";
|
|
23
|
+
claude_chrome: "claude_chrome";
|
|
24
|
+
chatgpt: "chatgpt";
|
|
25
|
+
cursor: "cursor";
|
|
26
|
+
vscode: "vscode";
|
|
27
|
+
claude_code: "claude_code";
|
|
28
|
+
}>;
|
|
29
|
+
konu: z.ZodDefault<z.ZodEnum<{
|
|
30
|
+
baglayici: "baglayici";
|
|
31
|
+
hepsi: "hepsi";
|
|
32
|
+
tarayici: "tarayici";
|
|
33
|
+
}>>;
|
|
34
|
+
}, z.core.$strip>>;
|
|
35
|
+
export declare const engelCozAraci: import("./temel.ts").AcikArac<z.ZodObject<{
|
|
36
|
+
engel: z.ZodEnum<{
|
|
37
|
+
cerceve: "cerceve";
|
|
38
|
+
tarayici_araci_yok: "tarayici_araci_yok";
|
|
39
|
+
site_acilmiyor: "site_acilmiyor";
|
|
40
|
+
giris_duvari: "giris_duvari";
|
|
41
|
+
captcha: "captcha";
|
|
42
|
+
oturum_dustu: "oturum_dustu";
|
|
43
|
+
sayfa_okunamiyor: "sayfa_okunamiyor";
|
|
44
|
+
kaydet_basilamiyor: "kaydet_basilamiyor";
|
|
45
|
+
akredit_gorunmuyor: "akredit_gorunmuyor";
|
|
46
|
+
}>;
|
|
47
|
+
istemci: z.ZodOptional<z.ZodEnum<{
|
|
48
|
+
claude_web: "claude_web";
|
|
49
|
+
claude_masaustu: "claude_masaustu";
|
|
50
|
+
claude_chrome: "claude_chrome";
|
|
51
|
+
chatgpt: "chatgpt";
|
|
52
|
+
cursor: "cursor";
|
|
53
|
+
vscode: "vscode";
|
|
54
|
+
claude_code: "claude_code";
|
|
55
|
+
}>>;
|
|
56
|
+
}, z.core.$strip>>;
|
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
// Kurulum ve engel çözme rehberi. Metinler sabittir, model üretmez. Hocanın kendi OBS'sine tarayıcıyla girmesi
|
|
2
|
+
// için adımlar verir. Değişmez kural: kullanıcı adı, parola ve doğrulama kodu yalnız OBS'nin kendi ekranına,
|
|
3
|
+
// hocanın kendisi tarafından yazılır; asistana ve Akredit'e hiç yazılmaz. CAPTCHA'yı hoca çözer. OBS'de Kaydet'e
|
|
4
|
+
// hoca ya da hocanın açık izniyle asistan basar; onaya Gönder düğmesine hoca basar.
|
|
5
|
+
//
|
|
6
|
+
// Menü ve düğme adları istemcinin sürümüne göre değişir. Doğrulanmamış adlar `degisebilir: true` ile işaretlidir.
|
|
7
|
+
import { z } from "zod";
|
|
8
|
+
import { arac, KOD_KAYNAGI } from "./temel.js";
|
|
9
|
+
const numarala = (xs) => xs.map(([yap, goreceginiz, degisebilir], i) => ({
|
|
10
|
+
no: i + 1,
|
|
11
|
+
yap,
|
|
12
|
+
...(goreceginiz ? { goreceginiz } : {}),
|
|
13
|
+
...(degisebilir ? { degisebilir: true } : {}),
|
|
14
|
+
}));
|
|
15
|
+
/** Her rehberin başında hocaya söylenecek giriş kuralı. */
|
|
16
|
+
export const GIRIS_KURALI_HOCAYA = "Kullanıcı adınızı ve parolanızı yalnız OBS'nin kendi giriş ekranına, kendiniz yazın. Bana, sohbete ya da Akredit'e asla yazmayın. Doğrulama kodu (SMS, e-posta) ve CAPTCHA da yalnız sizindir.";
|
|
17
|
+
export const MCP_ADRESI = "https://akredit.org/mcp";
|
|
18
|
+
export const ISTEMCILER = [
|
|
19
|
+
"claude_web",
|
|
20
|
+
"claude_masaustu",
|
|
21
|
+
"claude_chrome",
|
|
22
|
+
"chatgpt",
|
|
23
|
+
"cursor",
|
|
24
|
+
"vscode",
|
|
25
|
+
"claude_code",
|
|
26
|
+
];
|
|
27
|
+
const ISTEMCI_ADI = {
|
|
28
|
+
claude_web: "Claude (claude.ai, web)",
|
|
29
|
+
claude_masaustu: "Claude masaüstü uygulaması",
|
|
30
|
+
claude_chrome: "Claude in Chrome (Chrome eklentisi)",
|
|
31
|
+
chatgpt: "ChatGPT",
|
|
32
|
+
cursor: "Cursor",
|
|
33
|
+
vscode: "VS Code",
|
|
34
|
+
claude_code: "Claude Code",
|
|
35
|
+
};
|
|
36
|
+
const BAGLAYICI = {
|
|
37
|
+
// Anthropic yardım belgesi "Get started with custom connectors using remote MCP" (erişim 2026-11-05).
|
|
38
|
+
claude_web: [
|
|
39
|
+
["claude.ai adresine girin ve oturum açın.", "Sol tarafta sohbet listesi, ortada yazı kutusu."],
|
|
40
|
+
["Customize (Özelleştir), ardından Connectors (Bağlayıcılar) bölümünü açın.", "Bağlı araçların listesi."],
|
|
41
|
+
["+ Add düğmesine, ardından Add custom connector seçeneğine basın.", "Ad ve adres soran pencere."],
|
|
42
|
+
[`Ad olarak Akredit, adres olarak ${MCP_ADRESI} yazın ve Continue deyin.`, "Kimlik doğrulama seçenekleri."],
|
|
43
|
+
["Authentication altında No sign in seçin ve Add deyin.", "Listede Akredit görünür."],
|
|
44
|
+
[
|
|
45
|
+
"Ücretsiz planda tek özel bağlayıcı eklenebilir. Team ve Enterprise planlarında bağlayıcıyı kuruluş sahibi ekler; siz Connectors listesinde Akredit'i bulup Connect dersiniz.",
|
|
46
|
+
undefined,
|
|
47
|
+
],
|
|
48
|
+
[
|
|
49
|
+
"Yeni sohbet açın. Yazı kutusunun altındaki + düğmesinden Akredit'in açık olduğunu denetleyin.",
|
|
50
|
+
"Akredit'in yanında açık anahtar.",
|
|
51
|
+
true,
|
|
52
|
+
],
|
|
53
|
+
[
|
|
54
|
+
"akredit yazın. Menü gelmezse Akredit araçlarıyla Bologna paketimi denetle yazın.",
|
|
55
|
+
"Odak menüsü: numaralı seçenekler.",
|
|
56
|
+
],
|
|
57
|
+
],
|
|
58
|
+
claude_masaustu: [
|
|
59
|
+
["Claude masaüstü uygulamasını açın ve oturum açın.", "claude.ai ile aynı görünüm."],
|
|
60
|
+
["Customize (Özelleştir), ardından Connectors (Bağlayıcılar) bölümünü açın.", "Bağlayıcı listesi."],
|
|
61
|
+
[
|
|
62
|
+
`+ Add, Add custom connector ile ad Akredit, adres ${MCP_ADRESI} yazın; Authentication altında No sign in seçip Add deyin.`,
|
|
63
|
+
"Listede Akredit görünür.",
|
|
64
|
+
],
|
|
65
|
+
["Yeni sohbette akredit yazın. Menü gelmezse Akredit araçlarıyla Bologna paketimi denetle yazın.", "Odak menüsü."],
|
|
66
|
+
],
|
|
67
|
+
claude_chrome: [
|
|
68
|
+
["Önce Claude'a Akredit bağlayıcısını ekleyin (Claude web adımları).", "Sohbette Akredit açık."],
|
|
69
|
+
[
|
|
70
|
+
"Chrome tarayıcısını açın. Chrome Web Mağazası'nda Claude eklentisini arayın (Claude in Chrome).",
|
|
71
|
+
"Eklentinin sayfası.",
|
|
72
|
+
true,
|
|
73
|
+
],
|
|
74
|
+
["Chrome'a ekle düğmesine basın, izin penceresinde onaylayın.", "Sağ üstte Claude simgesi.", true],
|
|
75
|
+
["Yapboz simgesinden Claude'u sabitleyin (iğne simgesi).", "Claude simgesi araç çubuğunda kalır.", true],
|
|
76
|
+
["Simgeye tıklayın, Claude hesabınızla oturum açın.", "Tarayıcının kenarında Claude paneli açılır.", true],
|
|
77
|
+
[
|
|
78
|
+
"OBS sayfanızı aynı pencerede açın; panelde akredit yazın.",
|
|
79
|
+
"Asistan OBS sayfasını okumak için izin ister; siz onaylarsınız.",
|
|
80
|
+
],
|
|
81
|
+
[
|
|
82
|
+
"Eklenti bazı planlarda ya da kurum hesaplarında kapalı olabilir; o durumda kopyala-yapıştır yedeğini kullanın.",
|
|
83
|
+
undefined,
|
|
84
|
+
true,
|
|
85
|
+
],
|
|
86
|
+
],
|
|
87
|
+
// OpenAI geliştirici belgesi "Custom MCP server" (erişim 2026-11-05). Hangi planlarda açık olduğu belgede yazmıyor.
|
|
88
|
+
chatgpt: [
|
|
89
|
+
["chatgpt.com adresinde, web tarayıcısında oturum açın.", "Sohbet ekranı."],
|
|
90
|
+
["chatgpt.com/plugins adresini açın; + düğmesine basıp Add custom MCP server seçin.", "Ad ve bağlantı soran form."],
|
|
91
|
+
[
|
|
92
|
+
`Ad olarak Akredit yazın; Connection altında Server URL olarak ${MCP_ADRESI} girin; kimlik doğrulama olarak no authentication seçin.`,
|
|
93
|
+
undefined,
|
|
94
|
+
true,
|
|
95
|
+
],
|
|
96
|
+
[
|
|
97
|
+
"Uyarıyı okuyup I understand and want to continue, ardından Create as a plugin deyin; sonra eklentiyi kurun.",
|
|
98
|
+
"Akredit eklentilerinizde görünür.",
|
|
99
|
+
true,
|
|
100
|
+
],
|
|
101
|
+
[
|
|
102
|
+
"Bu özelliğin açık olup olmadığı planınıza göre değişebilir. Kurum hesabında çalışma alanı izinleri yöneticiye bağlıdır.",
|
|
103
|
+
undefined,
|
|
104
|
+
true,
|
|
105
|
+
],
|
|
106
|
+
["Yeni sohbette @ yazıp Akredit'i seçin, sonra akredit yazın.", "Odak menüsü."],
|
|
107
|
+
],
|
|
108
|
+
cursor: [
|
|
109
|
+
["Cursor'da Settings (Ayarlar), ardından MCP bölümünü açın.", "MCP sunucu listesi.", true],
|
|
110
|
+
[
|
|
111
|
+
`Add new MCP server ile ad akredit, adres ${MCP_ADRESI} girin. Ya da .cursor/mcp.json dosyasına ekleyin.`,
|
|
112
|
+
"Akredit yeşil noktayla görünür.",
|
|
113
|
+
true,
|
|
114
|
+
],
|
|
115
|
+
["Sohbet panelinde eğik çizgi (/) yazın ve akredit seçin.", "Odak menüsü."],
|
|
116
|
+
],
|
|
117
|
+
vscode: [
|
|
118
|
+
["Komut paletini açın (Ctrl+Shift+P, Mac'te Cmd+Shift+P).", "Üstte arama kutusu."],
|
|
119
|
+
["MCP: Add Server yazıp seçin, HTTP türünü seçin.", "Adres soran kutu.", true],
|
|
120
|
+
[`Adres olarak ${MCP_ADRESI}, ad olarak akredit yazın.`, "Akredit sunucu listesinde."],
|
|
121
|
+
["Copilot sohbetinde ajan (Agent) kipini seçin, eğik çizgiyle akredit komutunu çalıştırın.", "Odak menüsü.", true],
|
|
122
|
+
],
|
|
123
|
+
claude_code: [
|
|
124
|
+
[
|
|
125
|
+
`Uçbirimde şunu çalıştırın: claude mcp add --transport http akredit ${MCP_ADRESI}`,
|
|
126
|
+
"Added HTTP MCP server iletisi.",
|
|
127
|
+
],
|
|
128
|
+
["claude komutuyla oturumu açın, /mcp ile Akredit'in bağlı olduğunu denetleyin.", "akredit: connected.", true],
|
|
129
|
+
["Eğik çizgiyle akredit komutunu seçin.", "Odak menüsü."],
|
|
130
|
+
],
|
|
131
|
+
};
|
|
132
|
+
/** Bağlayıcı ekleme adımları; site (akredit.org ana sayfası) aynı adımları buradan okur. */
|
|
133
|
+
export function baglayiciAdimlari(istemci) {
|
|
134
|
+
return numarala(BAGLAYICI[istemci]);
|
|
135
|
+
}
|
|
136
|
+
const TARAYICI = {
|
|
137
|
+
claude_web: [
|
|
138
|
+
[
|
|
139
|
+
"claude.ai web sürümü kendi başına OBS'nize giremez. Tarayıcı için Claude in Chrome eklentisini ya da masaüstü uygulamasını kullanın.",
|
|
140
|
+
undefined,
|
|
141
|
+
],
|
|
142
|
+
[
|
|
143
|
+
"Bunlar yoksa kopyala-yapıştır yedeği: OBS'de ders sayfasını açın, Ctrl+A ile hepsini seçin, Ctrl+C ile kopyalayın, sohbete Ctrl+V ile yapıştırın.",
|
|
144
|
+
"Asistan sayfayı ayrıştırır.",
|
|
145
|
+
],
|
|
146
|
+
],
|
|
147
|
+
claude_masaustu: [
|
|
148
|
+
[
|
|
149
|
+
"Masaüstü uygulamasında sohbetin yanında tarayıcı paneli açılabilir. Panelin düğmesi ya da menüsü sürüme göre değişir.",
|
|
150
|
+
"Sohbetin yanında küçük bir tarayıcı.",
|
|
151
|
+
true,
|
|
152
|
+
],
|
|
153
|
+
["Asistandan OBS adresinizi açmasını isteyin ya da adresi panele siz yazın.", "OBS giriş ekranı."],
|
|
154
|
+
[
|
|
155
|
+
"Kullanıcı adınızı ve parolanızı panelde OBS'nin kendi kutularına kendiniz yazın. Sohbete yazmayın.",
|
|
156
|
+
"OBS ana sayfası.",
|
|
157
|
+
],
|
|
158
|
+
[
|
|
159
|
+
"Asistana 'girdim' yazın. Asistan ders listesini ve Bologna sayfasını okur.",
|
|
160
|
+
"Ders listesi sohbette numaralı görünür.",
|
|
161
|
+
],
|
|
162
|
+
],
|
|
163
|
+
claude_chrome: [
|
|
164
|
+
["Chrome'da yeni sekmede OBS adresinizi açın.", "OBS giriş ekranı."],
|
|
165
|
+
[
|
|
166
|
+
"Kullanıcı adınızı ve parolanızı OBS'nin kendi kutularına kendiniz yazın; varsa doğrulama kodunu ve CAPTCHA'yı siz girin.",
|
|
167
|
+
"OBS ana sayfası.",
|
|
168
|
+
],
|
|
169
|
+
[
|
|
170
|
+
"Claude simgesine tıklayıp paneli açın, 'OBS'ye girdim' yazın.",
|
|
171
|
+
"Asistan sayfayı okumak için izin isteyebilir; onaylayın.",
|
|
172
|
+
true,
|
|
173
|
+
],
|
|
174
|
+
["Asistan ders listesini okur, dersinizi numarasıyla seçersiniz.", "Ders bilgi paketi alanları."],
|
|
175
|
+
[
|
|
176
|
+
"Asistan formu doldurursa her alanı gözle denetleyin. Kaydet'e siz basarsınız; isterseniz açık izninizle asistan basar. Onaya Gönder düğmesine siz basarsınız.",
|
|
177
|
+
"OBS'nin kayıt iletisi.",
|
|
178
|
+
],
|
|
179
|
+
],
|
|
180
|
+
chatgpt: [
|
|
181
|
+
[
|
|
182
|
+
"Yazı kutusundaki Araçlar (Tools) menüsünden Ajan modunu (Agent mode) seçin.",
|
|
183
|
+
"ChatGPT kendi tarayıcısını açar.",
|
|
184
|
+
true,
|
|
185
|
+
],
|
|
186
|
+
["OBS adresinizi açmasını isteyin.", "OBS giriş ekranı ajanın tarayıcısında."],
|
|
187
|
+
[
|
|
188
|
+
"Giriş ekranında Devral (Take over) düğmesine basın; kullanıcı adı ve parolayı kendiniz yazın. Sohbete yazmayın.",
|
|
189
|
+
"OBS ana sayfası.",
|
|
190
|
+
true,
|
|
191
|
+
],
|
|
192
|
+
["Denetimi asistana geri verin.", "Asistan sayfayı okur."],
|
|
193
|
+
["Kaydet'e siz basarsınız ya da açık izninizle asistan basar. Onaya Gönder düğmesine siz basarsınız.", undefined],
|
|
194
|
+
],
|
|
195
|
+
cursor: [
|
|
196
|
+
["Cursor'un tarayıcı aracı sürüme göre değişir. Varsa asistandan OBS adresini açmasını isteyin.", undefined, true],
|
|
197
|
+
["Yoksa kopyala-yapıştır yedeği: sayfada Ctrl+A, Ctrl+C, sohbete Ctrl+V.", "Asistan sayfayı ayrıştırır."],
|
|
198
|
+
],
|
|
199
|
+
vscode: [
|
|
200
|
+
["VS Code'da OBS'ye tarayıcıyla girmek için ek araç gerekir; bu en zor yoldur.", undefined, true],
|
|
201
|
+
[
|
|
202
|
+
"En kolayı kopyala-yapıştır: tarayıcınızda OBS sayfasında Ctrl+A, Ctrl+C, sohbete Ctrl+V.",
|
|
203
|
+
"Asistan sayfayı ayrıştırır.",
|
|
204
|
+
],
|
|
205
|
+
],
|
|
206
|
+
claude_code: [
|
|
207
|
+
[
|
|
208
|
+
"Claude Code'da tarayıcı için Claude in Chrome eklentisi bağlanabilir; adımlar sürüme göre değişir.",
|
|
209
|
+
undefined,
|
|
210
|
+
true,
|
|
211
|
+
],
|
|
212
|
+
["Yoksa kopyala-yapıştır yedeği: sayfada Ctrl+A, Ctrl+C, uçbirime yapıştırın.", "Asistan sayfayı ayrıştırır."],
|
|
213
|
+
],
|
|
214
|
+
};
|
|
215
|
+
export const ENGELLER = [
|
|
216
|
+
"tarayici_araci_yok",
|
|
217
|
+
"site_acilmiyor",
|
|
218
|
+
"giris_duvari",
|
|
219
|
+
"captcha",
|
|
220
|
+
"cerceve",
|
|
221
|
+
"oturum_dustu",
|
|
222
|
+
"sayfa_okunamiyor",
|
|
223
|
+
"kaydet_basilamiyor",
|
|
224
|
+
"akredit_gorunmuyor",
|
|
225
|
+
];
|
|
226
|
+
const KOPYALA_YAPISTIR = [
|
|
227
|
+
["Yedek yol: OBS'de ders bilgi paketi sayfasını kendiniz açın.", "Ders sayfası."],
|
|
228
|
+
["Sayfanın boş bir yerine tıklayın, Ctrl+A ile hepsini seçin (Mac'te Cmd+A).", "Sayfa mavi seçilir."],
|
|
229
|
+
["Ctrl+C ile kopyalayın (Mac'te Cmd+C).", undefined],
|
|
230
|
+
[
|
|
231
|
+
"Sohbete dönüp Ctrl+V ile yapıştırın ve gönderin.",
|
|
232
|
+
"Asistan alanları ayrıştırıp bulunan ve bulunamayan alanları söyler.",
|
|
233
|
+
],
|
|
234
|
+
];
|
|
235
|
+
const ENGEL_ADIMLARI = {
|
|
236
|
+
tarayici_araci_yok: {
|
|
237
|
+
ad: "Asistanın tarayıcı aracı yok",
|
|
238
|
+
adimlar: [
|
|
239
|
+
[
|
|
240
|
+
"Claude kullanıyorsanız Claude in Chrome eklentisini kurun (kurulum_yardimi, istemci: claude_chrome).",
|
|
241
|
+
undefined,
|
|
242
|
+
],
|
|
243
|
+
["Claude masaüstü uygulamasında sohbetin yanındaki tarayıcı panelini açmayı deneyin.", undefined, true],
|
|
244
|
+
["ChatGPT'de Araçlar menüsünden Ajan modunu seçin.", undefined, true],
|
|
245
|
+
],
|
|
246
|
+
yedek: true,
|
|
247
|
+
},
|
|
248
|
+
site_acilmiyor: {
|
|
249
|
+
ad: "OBS sitesi açılmıyor",
|
|
250
|
+
adimlar: [
|
|
251
|
+
["Adresi kendi tarayıcınızda açıp deneyin. Açılıyorsa adresi asistana olduğu gibi verin.", undefined],
|
|
252
|
+
[
|
|
253
|
+
"Kurum dışından erişim kapalı olabilir (VPN ya da kampüs ağı). Kurumunuzun bilgi işlem sayfasına bakın.",
|
|
254
|
+
undefined,
|
|
255
|
+
],
|
|
256
|
+
["Site bakımda olabilir; birkaç dakika sonra yeniden deneyin.", undefined],
|
|
257
|
+
],
|
|
258
|
+
yedek: true,
|
|
259
|
+
},
|
|
260
|
+
giris_duvari: {
|
|
261
|
+
ad: "Giriş ekranı",
|
|
262
|
+
adimlar: [
|
|
263
|
+
["Bu normaldir. Kullanıcı adınızı ve parolanızı OBS'nin kendi kutularına kendiniz yazın.", "OBS ana sayfası."],
|
|
264
|
+
[
|
|
265
|
+
"Asistana yalnız 'girdim' yazın. Parolayı sohbete yazmayın; yazdıysanız silin ve parolanızı değiştirin.",
|
|
266
|
+
undefined,
|
|
267
|
+
],
|
|
268
|
+
["e-Devlet ya da kurum tek oturum açma ekranı gelirse onu da kendiniz tamamlayın.", undefined],
|
|
269
|
+
],
|
|
270
|
+
yedek: false,
|
|
271
|
+
},
|
|
272
|
+
captcha: {
|
|
273
|
+
ad: "Ben robot değilim (CAPTCHA) kutusu",
|
|
274
|
+
adimlar: [
|
|
275
|
+
["CAPTCHA'yı siz çözün; asistan çözmez.", undefined],
|
|
276
|
+
["Çözdükten sonra asistana 'devam' yazın.", "Asistan kaldığı yerden sürer."],
|
|
277
|
+
],
|
|
278
|
+
yedek: false,
|
|
279
|
+
},
|
|
280
|
+
cerceve: {
|
|
281
|
+
ad: "Sayfa çerçeve (iframe) içinde, asistan içeriği göremiyor",
|
|
282
|
+
adimlar: [
|
|
283
|
+
[
|
|
284
|
+
"Ders sayfasındaki bağlantıya sağ tıklayın, Bağlantıyı yeni sekmede aç seçin.",
|
|
285
|
+
"Sayfa tek başına açılır.",
|
|
286
|
+
true,
|
|
287
|
+
],
|
|
288
|
+
["Asistandan bu yeni sekmeyi okumasını isteyin.", undefined],
|
|
289
|
+
],
|
|
290
|
+
yedek: true,
|
|
291
|
+
},
|
|
292
|
+
oturum_dustu: {
|
|
293
|
+
ad: "Oturum düştü ya da süre doldu",
|
|
294
|
+
adimlar: [
|
|
295
|
+
["OBS'ye yeniden kendiniz girin (kullanıcı adı ve parolayı yalnız OBS ekranına).", "OBS ana sayfası."],
|
|
296
|
+
[
|
|
297
|
+
"Kaydedilmemiş alanlar silinmiş olabilir. Asistandan son seçilen metni yeniden istemek için 'kopya metnini ver' yazın.",
|
|
298
|
+
undefined,
|
|
299
|
+
],
|
|
300
|
+
["Uzun formlarda her sekmeyi doldurduktan sonra Kaydet'e basın.", undefined],
|
|
301
|
+
],
|
|
302
|
+
yedek: false,
|
|
303
|
+
},
|
|
304
|
+
sayfa_okunamiyor: {
|
|
305
|
+
ad: "Asistan sayfayı okuyamıyor ya da alanları bulamıyor",
|
|
306
|
+
adimlar: [
|
|
307
|
+
["Sayfanın tamamen yüklendiğini bekleyin ve asistandan yeniden okumasını isteyin.", undefined],
|
|
308
|
+
["Bologna sayfasında dersin Ders Bilgileri sekmesinde olduğunuzu denetleyin.", undefined, true],
|
|
309
|
+
],
|
|
310
|
+
yedek: true,
|
|
311
|
+
},
|
|
312
|
+
kaydet_basilamiyor: {
|
|
313
|
+
ad: "Kaydet ya da Gönder",
|
|
314
|
+
adimlar: [
|
|
315
|
+
[
|
|
316
|
+
"Kaydet'e siz basarsınız; isterseniz asistan sizin açık izninizle (evet ya da bir kez hepsini kaydet) basar. Onaya Gönder düğmesine yalnız siz basarsınız.",
|
|
317
|
+
undefined,
|
|
318
|
+
],
|
|
319
|
+
["Basmadan önce alanları gözle denetleyin.", undefined],
|
|
320
|
+
["Düğme pasifse zorunlu bir alan boş olabilir; OBS'nin kırmızı uyarısına bakın ve asistana söyleyin.", undefined],
|
|
321
|
+
],
|
|
322
|
+
yedek: false,
|
|
323
|
+
},
|
|
324
|
+
akredit_gorunmuyor: {
|
|
325
|
+
ad: "Akredit araçları ya da akredit komutu görünmüyor",
|
|
326
|
+
adimlar: [
|
|
327
|
+
["Yeni bir sohbet açın; bağlayıcı eski sohbette görünmeyebilir.", undefined],
|
|
328
|
+
["Yazı kutusunun altındaki + ya da araç menüsünden Akredit'in açık olduğunu denetleyin.", undefined, true],
|
|
329
|
+
["Komut görünmüyorsa 'Akredit ile başlayalım' yazmanız yeter.", "Asistan odak menüsünü gösterir."],
|
|
330
|
+
[`Bağlayıcı adresinin tam olarak ${MCP_ADRESI} olduğunu denetleyin.`, undefined],
|
|
331
|
+
],
|
|
332
|
+
yedek: false,
|
|
333
|
+
},
|
|
334
|
+
};
|
|
335
|
+
export const kurulumYardimiAraci = arac({
|
|
336
|
+
ad: "kurulum_yardimi",
|
|
337
|
+
baslik: "Kurulum yardımı",
|
|
338
|
+
aciklama: "Akredit bağlayıcısını eklemek ve OBS'ye tarayıcıyla girmek için istemciye göre adım adım rehber verir: Claude web ve masaüstü, Claude in Chrome, ChatGPT, Cursor, VS Code, Claude Code. Her adımda ekranda ne görüleceğini söyler.",
|
|
339
|
+
sonrakiAdim: "Adımları hocaya tek tek gösterin: bir adım verin, 'oldu' demesini bekleyin, sonra sıradakine geçin. 'değişebilir' işaretli menü adını 'sizde biraz farklı olabilir' diye söyleyin. Giriş kuralını hocaya bir kez açıkça söyleyin.",
|
|
340
|
+
girdi: z.object({
|
|
341
|
+
istemci: z.enum(ISTEMCILER).describe("Hocanın kullandığı asistan"),
|
|
342
|
+
konu: z
|
|
343
|
+
.enum(["baglayici", "tarayici", "hepsi"])
|
|
344
|
+
.default("hepsi")
|
|
345
|
+
.describe("Bağlayıcı ekleme, OBS'ye tarayıcıyla giriş ya da ikisi"),
|
|
346
|
+
}),
|
|
347
|
+
calistir: ({ istemci, konu }) => ({
|
|
348
|
+
sonuc: {
|
|
349
|
+
istemci: ISTEMCI_ADI[istemci],
|
|
350
|
+
girisKurali: GIRIS_KURALI_HOCAYA,
|
|
351
|
+
...(konu !== "tarayici" ? { baglayiciAdimlari: numarala(BAGLAYICI[istemci]) } : {}),
|
|
352
|
+
...(konu !== "baglayici" ? { tarayiciAdimlari: numarala(TARAYICI[istemci]) } : {}),
|
|
353
|
+
not: "Menü adları istemcinin sürümüne ve diline göre değişebilir. 'degisebilir' işaretli adlar doğrulanmadı.",
|
|
354
|
+
},
|
|
355
|
+
kaynak: [KOD_KAYNAGI("apps/acik-mcp/src/yardim.ts", "Akredit kurulum rehberi")],
|
|
356
|
+
}),
|
|
357
|
+
});
|
|
358
|
+
export const engelCozAraci = arac({
|
|
359
|
+
ad: "engel_coz",
|
|
360
|
+
baslik: "Engel çöz",
|
|
361
|
+
aciklama: "OBS'ye girerken ya da sayfayı okurken çıkan engel için numaralı çözüm adımları verir: tarayıcı aracı yok, site açılmıyor, giriş ekranı, CAPTCHA, çerçeve, oturum düşmesi, sayfa okunamıyor, Kaydet düğmesi, Akredit görünmüyor. Gerekirse kopyala-yapıştır yedeğini de verir.",
|
|
362
|
+
sonrakiAdim: "Adımları hocaya tek tek gösterin. Hiçbiri olmazsa kopyala-yapıştır yedeğine geçin. Parola, doğrulama kodu ya da çerez istemeyin.",
|
|
363
|
+
girdi: z.object({
|
|
364
|
+
engel: z.enum(ENGELLER),
|
|
365
|
+
istemci: z.enum(ISTEMCILER).optional(),
|
|
366
|
+
}),
|
|
367
|
+
calistir: ({ engel, istemci }) => {
|
|
368
|
+
const e = ENGEL_ADIMLARI[engel];
|
|
369
|
+
const ozel = engel === "tarayici_araci_yok" && istemci ? TARAYICI[istemci] : [];
|
|
370
|
+
return {
|
|
371
|
+
sonuc: {
|
|
372
|
+
engel: e.ad,
|
|
373
|
+
girisKurali: GIRIS_KURALI_HOCAYA,
|
|
374
|
+
adimlar: numarala([...ozel, ...e.adimlar]),
|
|
375
|
+
...(e.yedek ? { yedekYol: numarala(KOPYALA_YAPISTIR) } : {}),
|
|
376
|
+
obsyeYazmaYedegi: "Tarayıcı aracı yoksa asistan bologna_kopya_metni ile alan alan kopyalanacak metni verir; siz OBS'de ilgili kutuya yapıştırıp Kaydet'e basarsınız.",
|
|
377
|
+
},
|
|
378
|
+
kaynak: [KOD_KAYNAGI("apps/acik-mcp/src/yardim.ts", "Akredit engel çözme rehberi")],
|
|
379
|
+
};
|
|
380
|
+
},
|
|
381
|
+
});
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { BolognaDersSayfasi, BolognaProgramCiktisi, BolognaProgramMatrisi } from "../../../cekirdek/src/index.js";
|
|
2
|
+
/** HTML parçasını düz metne çevirir. */
|
|
3
|
+
export declare function metin(html: string): string;
|
|
4
|
+
export type HtmlTablosu = string[][];
|
|
5
|
+
/** Sayfadaki en içteki tabloları satır ve hücre dizisine çevirir. */
|
|
6
|
+
export declare function tablolar(html: string): HtmlTablosu[];
|
|
7
|
+
/** Metindeki ilk sayıyı alır ("% 40" → 40, "2,5" → 2.5). */
|
|
8
|
+
export declare const sayi: (s: unknown) => number | null;
|
|
9
|
+
export interface BolognaAdresi {
|
|
10
|
+
taban: string;
|
|
11
|
+
sunit: string;
|
|
12
|
+
dil: string;
|
|
13
|
+
}
|
|
14
|
+
/** Program adresinden taban adresi ve program kimliğini çıkarır. */
|
|
15
|
+
export declare function adresCoz(adres: string): BolognaAdresi;
|
|
16
|
+
export declare function programCiktilari(html: string): BolognaProgramCiktisi[];
|
|
17
|
+
export declare const EPOSTA_YERINE = "[e-posta ay\u0131kland\u0131]";
|
|
18
|
+
/** Nesnedeki bütün metinlerden e-posta adreslerini ayıklar. */
|
|
19
|
+
export declare function epostaAyikla<T>(x: T): T;
|
|
20
|
+
export declare function programMatrisi(html: string): BolognaProgramMatrisi;
|
|
21
|
+
/** Ders listesi sayfasından ders kodu → Proliz ders kimliği eşlemesi. */
|
|
22
|
+
export declare function dersKimlikleri(html: string): Record<string, string>;
|
|
23
|
+
/** Bir ders bilgi sayfasını ayrıştırır. E-posta adresleri çıktıdan ayıklanır (KVKK: en az kişisel veri). */
|
|
24
|
+
export declare function dersSayfasiCoz(html: string): BolognaDersSayfasi;
|
|
25
|
+
/**
|
|
26
|
+
* TYYÇ matrisinde program çıktısı sütunlarındaki dolu hücreleri sayar.
|
|
27
|
+
* Satırlar "etiket, alt no, P1..Pn, alt no, etiket" ya da etiketsiz "alt no, P1..Pn, alt no" biçimindedir.
|
|
28
|
+
*/
|
|
29
|
+
export declare function tyycIsaretSayisi(html: string, pSayisi: number): number | null;
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
// Bologna Bilgi Paketi (Proliz OBS) sayfa ayrıştırıcıları.
|
|
2
|
+
// apps/cli/lib/bologna.mjs içindeki işlevlerin davranışı birebir korunarak TypeScript'e taşınmış hâlidir.
|
|
3
|
+
const VARLIKLAR = { nbsp: " ", amp: "&", lt: "<", gt: ">", quot: '"', apos: "'" };
|
|
4
|
+
const ETIKET = /<(?:[^>"']|"[^"]*"|'[^']*')*>/g;
|
|
5
|
+
const BETIK = /<(script|style)\b(?:[^>"']|"[^"]*"|'[^']*')*>[\s\S]*?<\/\1>/gi;
|
|
6
|
+
/** HTML parçasını düz metne çevirir. */
|
|
7
|
+
export function metin(html) {
|
|
8
|
+
return html
|
|
9
|
+
.replace(BETIK, "")
|
|
10
|
+
.replace(/<br\s*\/?>/gi, " ")
|
|
11
|
+
.replace(ETIKET, " ")
|
|
12
|
+
.replace(/&#(\d+);/g, (_, n) => String.fromCodePoint(Number(n)))
|
|
13
|
+
.replace(/&#x([0-9a-f]+);/gi, (_, n) => String.fromCodePoint(Number.parseInt(n, 16)))
|
|
14
|
+
.replace(/&([a-z]+);/gi, (m, a) => VARLIKLAR[a.toLowerCase()] ?? m)
|
|
15
|
+
.replace(/\s+/g, " ")
|
|
16
|
+
.trim();
|
|
17
|
+
}
|
|
18
|
+
/** Sayfadaki en içteki tabloları satır ve hücre dizisine çevirir. */
|
|
19
|
+
export function tablolar(html) {
|
|
20
|
+
const temiz = html.replace(BETIK, "");
|
|
21
|
+
const sonuc = [];
|
|
22
|
+
const re = /<table\b(?:[^>"']|"[^"]*"|'[^']*')*>((?:(?!<table\b)[\s\S])*?)<\/table>/gi;
|
|
23
|
+
for (let m = re.exec(temiz); m; m = re.exec(temiz)) {
|
|
24
|
+
const satirlar = [...(m[1] ?? "").matchAll(/<tr\b(?:[^>"']|"[^"]*"|'[^']*')*>([\s\S]*?)<\/tr>/gi)].map((r) => [...(r[1] ?? "").matchAll(/<t[dh]\b(?:[^>"']|"[^"]*"|'[^']*')*>([\s\S]*?)<\/t[dh]>/gi)].map((c) => metin(c[1] ?? "")));
|
|
25
|
+
if (satirlar.some((s) => s.length))
|
|
26
|
+
sonuc.push(satirlar);
|
|
27
|
+
}
|
|
28
|
+
return sonuc;
|
|
29
|
+
}
|
|
30
|
+
/** Metindeki ilk sayıyı alır ("% 40" → 40, "2,5" → 2.5). */
|
|
31
|
+
export const sayi = (s) => {
|
|
32
|
+
const m = String(s ?? "")
|
|
33
|
+
.replace(",", ".")
|
|
34
|
+
.match(/-?\d+(\.\d+)?/);
|
|
35
|
+
return m ? Number(m[0]) : null;
|
|
36
|
+
};
|
|
37
|
+
/** Program adresinden taban adresi ve program kimliğini çıkarır. */
|
|
38
|
+
export function adresCoz(adres) {
|
|
39
|
+
const u = new URL(adres);
|
|
40
|
+
const sunit = u.searchParams.get("curSunit");
|
|
41
|
+
const i = u.pathname.toLowerCase().indexOf("/bologna/");
|
|
42
|
+
if (!sunit || i < 0)
|
|
43
|
+
throw new Error("Bologna program adresi bekleniyor (içinde /bologna/ ve curSunit olmalı).");
|
|
44
|
+
return { taban: `${u.origin}${u.pathname.slice(0, i + 9)}`, sunit, dil: u.searchParams.get("lang") || "tr" };
|
|
45
|
+
}
|
|
46
|
+
export function programCiktilari(html) {
|
|
47
|
+
const t = tablolar(html).find((t) => /Program Öğrenme Çıktıları|Program Outcomes/i.test(t[0]?.join(" ") ?? ""));
|
|
48
|
+
if (!t)
|
|
49
|
+
return [];
|
|
50
|
+
return t
|
|
51
|
+
.slice(1)
|
|
52
|
+
.filter((r) => r.length >= 2 && sayi(r[0]) !== null)
|
|
53
|
+
.map((r) => ({ kod: `P${sayi(r[0])}`, metin: r[1] ?? "" }));
|
|
54
|
+
}
|
|
55
|
+
/** Ders kodu: harf önekli ("HUK101", "HUK 101") ya da sayısal ("1000.020105", EBYÜ canlı yapısı). */
|
|
56
|
+
const DERS_KODU = /^(?:[A-ZÇĞİÖŞÜ]{2,}\s*\d|\d{3,}\.\d{3,})/;
|
|
57
|
+
const DERS_KODU_ARA = /\b([A-ZÇĞİÖŞÜ]{2,}\s?\d{3}[A-Z]?|\d{3,}\.\d{3,})\b/;
|
|
58
|
+
const YUZDE = /^%\s*\d/;
|
|
59
|
+
const EPOSTA = /[\p{L}\d._%+-]+@[\p{L}\d-]+(?:\.[\p{L}\d-]+)+/gu;
|
|
60
|
+
export const EPOSTA_YERINE = "[e-posta ayıklandı]";
|
|
61
|
+
/** Nesnedeki bütün metinlerden e-posta adreslerini ayıklar. */
|
|
62
|
+
export function epostaAyikla(x) {
|
|
63
|
+
if (typeof x === "string")
|
|
64
|
+
return x.replace(EPOSTA, EPOSTA_YERINE);
|
|
65
|
+
if (Array.isArray(x))
|
|
66
|
+
return x.map(epostaAyikla);
|
|
67
|
+
if (x && typeof x === "object")
|
|
68
|
+
return Object.fromEntries(Object.entries(x).map(([k, v]) => [k, epostaAyikla(v)]));
|
|
69
|
+
return x;
|
|
70
|
+
}
|
|
71
|
+
export function programMatrisi(html) {
|
|
72
|
+
const satirlar = tablolar(html).flat();
|
|
73
|
+
const baslik = satirlar.find((r) => r.includes("Ders Kodu") && r.some((h) => /^P\d+$/.test(h)));
|
|
74
|
+
if (!baslik)
|
|
75
|
+
return { pkodlar: [], dersler: [] };
|
|
76
|
+
const pBas = baslik.findIndex((h) => /^P\d+$/.test(h));
|
|
77
|
+
const pkodlar = baslik.slice(pBas);
|
|
78
|
+
let yariyil = null;
|
|
79
|
+
const dersler = [];
|
|
80
|
+
for (const r of satirlar) {
|
|
81
|
+
const ilk = r[0] ?? "";
|
|
82
|
+
if (r.length === 1 && /Yarıyıl|Semester/i.test(ilk)) {
|
|
83
|
+
yariyil = sayi(ilk);
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
if (r.length < pBas + 1 || r === baslik || ilk === "Ders Kodu")
|
|
87
|
+
continue;
|
|
88
|
+
const kod = ilk.replace(/^\[G\]\s*/, "");
|
|
89
|
+
if (!DERS_KODU.test(kod))
|
|
90
|
+
continue;
|
|
91
|
+
dersler.push({
|
|
92
|
+
kod,
|
|
93
|
+
ad: r[1] ?? "",
|
|
94
|
+
zorunlu: /Zorunlu|Compulsory/i.test(r[2] ?? ""),
|
|
95
|
+
grup: r[3] || null,
|
|
96
|
+
yariyil,
|
|
97
|
+
grupBasligi: ilk.startsWith("[G]"),
|
|
98
|
+
degerler: Object.fromEntries(pkodlar.map((p, i) => [p, r[pBas + i] ?? ""])),
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
return { pkodlar, dersler };
|
|
102
|
+
}
|
|
103
|
+
/** Ders listesi sayfasından ders kodu → Proliz ders kimliği eşlemesi. */
|
|
104
|
+
export function dersKimlikleri(html) {
|
|
105
|
+
const kimlik = {};
|
|
106
|
+
for (const tr of html.split(/<tr\b/i).slice(1)) {
|
|
107
|
+
const id = tr.match(/prolizOpenCourseDetails\('?(\d+)/);
|
|
108
|
+
const kod = metin(tr).match(DERS_KODU_ARA);
|
|
109
|
+
if (id?.[1] && kod?.[1] && !kimlik[kod[1]])
|
|
110
|
+
kimlik[kod[1]] = id[1];
|
|
111
|
+
}
|
|
112
|
+
return kimlik;
|
|
113
|
+
}
|
|
114
|
+
/** Ders sayfasının ham ayrıştırması (e-posta ayıklanmadan). */
|
|
115
|
+
function dersSayfasiCozHam(html) {
|
|
116
|
+
const t = tablolar(html);
|
|
117
|
+
const bul = (re) => t.find((x) => re.test((x[0] ?? []).join(" ")));
|
|
118
|
+
const ust = bul(/Yarıyıl.*Kodu|Semester.*Code/i);
|
|
119
|
+
const [yariyil, kod, ad, tul, kredi, akts, guncelleme] = ust?.[1] ?? [];
|
|
120
|
+
const bilgiler = Object.fromEntries((bul(/Dersin Dili|Language/i) ?? []).filter((r) => r.length >= 2).map((r) => [r[0] ?? "", r[1] ?? ""]));
|
|
121
|
+
const degerlendirme = (bul(/Yarıyıl Çalışmaları|Term Studies/i) ?? [])
|
|
122
|
+
.slice(1)
|
|
123
|
+
.filter((r) => r.length >= 3 && !/Toplam|Total/i.test(r[0] ?? ""))
|
|
124
|
+
.map((r) => ({ ad: r[0] ?? "", sayi: sayi(r[1]), katki: sayi(r[2]) }));
|
|
125
|
+
const isyukuTablosu = bul(/İş Yükü|Workload/i) ?? [];
|
|
126
|
+
const isyuku = isyukuTablosu
|
|
127
|
+
.slice(1)
|
|
128
|
+
.filter((r) => r.length >= 4 && sayi(r[3]) !== null && !/Toplam|Total|AKTS|ECTS/i.test(r[0] ?? ""))
|
|
129
|
+
.map((r) => ({ ad: r[0] ?? "", sayi: sayi(r[1]), sure: sayi(r[2]), toplam: sayi(r[3]) }));
|
|
130
|
+
// Canlı Proliz sayfasında (EBYÜ, 2026-10-27) alan yüzdesi tablosu başlıksızdır: ["Sosyal Bilimler", "%100", ""].
|
|
131
|
+
const dersYapisi = (t.find((x) => x.some((r) => /Alan Bilgisi|Field/i.test(r[0] ?? ""))) ??
|
|
132
|
+
t.find((x) => x.length > 0 && x.every((r) => r.length >= 2 && YUZDE.test(r[1] ?? ""))) ??
|
|
133
|
+
[])
|
|
134
|
+
.filter((r) => r.length >= 2 && /%/.test(r[1] ?? ""))
|
|
135
|
+
.map((r) => ({ alan: r[0] ?? "", yuzde: sayi(r[1]) }));
|
|
136
|
+
const ciktiTablosu = bul(/Sıra No|No.*A[cç]ıklama|Description/i) ?? [];
|
|
137
|
+
const ciktilar = ciktiTablosu
|
|
138
|
+
.slice(1)
|
|
139
|
+
.filter((r) => r.length >= 2 && sayi(r[0]) !== null && r[1])
|
|
140
|
+
.map((r) => ({ kod: `Ö${sayi(r[0])}`, metin: r[1] ?? "" }));
|
|
141
|
+
const haftalar = (bul(/Hafta|Week/i) ?? []).slice(1).filter((r) => sayi(r[0]) !== null && r[1]).length;
|
|
142
|
+
// Katkı satırları "Ö3" ya da çıktının türüne göre "Bi3" (bilgi), "Be3" (beceri), "Ye3" (yetkinlik) diye adlandırılır.
|
|
143
|
+
const katkiTablosu = t.find((x) => (x[0] ?? []).some((h) => /^P\d+$/.test(h)) &&
|
|
144
|
+
x.some((r) => /^(Tüm|All|Ö\d+|Bi\d+|Be\d+|Ye\d+)$/.test(r[0] ?? "")));
|
|
145
|
+
const katki = {};
|
|
146
|
+
if (katkiTablosu) {
|
|
147
|
+
const pk = (katkiTablosu[0] ?? []).slice(1);
|
|
148
|
+
for (const r of katkiTablosu.slice(1)) {
|
|
149
|
+
if (!r[0])
|
|
150
|
+
continue;
|
|
151
|
+
const ad = /^(Ö|Bi|Be|Ye)\d+$/.test(r[0]) ? `Ö${sayi(r[0])}` : r[0];
|
|
152
|
+
katki[ad] = Object.fromEntries(pk.map((p, i) => [p, r[i + 1] ?? ""]));
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return {
|
|
156
|
+
yariyil: sayi(yariyil),
|
|
157
|
+
...(kod !== undefined ? { kod } : {}),
|
|
158
|
+
...(ad !== undefined ? { ad } : {}),
|
|
159
|
+
...(tul !== undefined ? { tul } : {}),
|
|
160
|
+
kredi: sayi(kredi),
|
|
161
|
+
akts: sayi(akts),
|
|
162
|
+
...(guncelleme !== undefined ? { guncelleme } : {}),
|
|
163
|
+
bilgiler,
|
|
164
|
+
degerlendirme,
|
|
165
|
+
isyuku,
|
|
166
|
+
dersYapisi,
|
|
167
|
+
ciktilar,
|
|
168
|
+
haftalar,
|
|
169
|
+
katki,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
/** Bir ders bilgi sayfasını ayrıştırır. E-posta adresleri çıktıdan ayıklanır (KVKK: en az kişisel veri). */
|
|
173
|
+
export function dersSayfasiCoz(html) {
|
|
174
|
+
return epostaAyikla(dersSayfasiCozHam(html));
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* TYYÇ matrisinde program çıktısı sütunlarındaki dolu hücreleri sayar.
|
|
178
|
+
* Satırlar "etiket, alt no, P1..Pn, alt no, etiket" ya da etiketsiz "alt no, P1..Pn, alt no" biçimindedir.
|
|
179
|
+
*/
|
|
180
|
+
export function tyycIsaretSayisi(html, pSayisi) {
|
|
181
|
+
if (!pSayisi)
|
|
182
|
+
return null;
|
|
183
|
+
// Canlı yapı (EBYÜ, 2026-10-27): 14 hücreli satır "alan, alt no, P1..Pn, alt no, alan"; rowspan yüzünden
|
|
184
|
+
// sonraki satırlar 12 hücreli "alt no, P1..Pn, alt no". Ayırıcı boş satırlar (alt no yok) sayılmaz.
|
|
185
|
+
// Hiç P hücresi okunamadıysa null (veri yok), okunup hepsi boşsa 0 döner.
|
|
186
|
+
let dolu = 0;
|
|
187
|
+
let okunan = 0;
|
|
188
|
+
for (const r of tablolar(html).flat()) {
|
|
189
|
+
const ic = r.length === pSayisi + 4 && /^\d+$/.test(r[1] ?? "")
|
|
190
|
+
? r.slice(2, 2 + pSayisi)
|
|
191
|
+
: r.length === pSayisi + 2 && /^\d+$/.test(r[0] ?? "")
|
|
192
|
+
? r.slice(1, 1 + pSayisi)
|
|
193
|
+
: null;
|
|
194
|
+
if (!ic)
|
|
195
|
+
continue;
|
|
196
|
+
okunan += ic.length;
|
|
197
|
+
dolu += ic.filter((h) => h && h !== "-").length;
|
|
198
|
+
}
|
|
199
|
+
return okunan ? dolu : null;
|
|
200
|
+
}
|