turkiyem 1.0.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 +129 -0
- package/package.json +45 -0
- package/src/commands/deprem.js +67 -0
- package/src/commands/hat.js +70 -0
- package/src/commands/sehir.js +34 -0
- package/src/commands/temizle.js +13 -0
- package/src/index.js +91 -0
- package/src/services/afadService.js +110 -0
- package/src/services/egoService.js +124 -0
- package/src/services/iettService.js +153 -0
- package/src/utils/banner.js +31 -0
- package/src/utils/cache.js +21 -0
- package/src/utils/config.js +54 -0
- package/src/utils/display.js +103 -0
package/README.md
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# turkiyem
|
|
2
|
+
|
|
3
|
+
Turkiye Toplu Tasima ve Deprem CLI araci.
|
|
4
|
+
|
|
5
|
+
AFAD deprem verileri, EGO (Ankara) otobus saatleri ve IETT (Istanbul) GTFS hat bilgilerini terminalden sorgulayabilirsiniz.
|
|
6
|
+
|
|
7
|
+
## Kurulum
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install -g turkiyem
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Veya yerel olarak:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
git clone <repo-url>
|
|
17
|
+
cd turkiyemCLI
|
|
18
|
+
npm install
|
|
19
|
+
npm link
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Gereksinimler
|
|
23
|
+
|
|
24
|
+
- Node.js 20+
|
|
25
|
+
|
|
26
|
+
## Kullanim
|
|
27
|
+
|
|
28
|
+
### Banner ve Yardim
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
turkiyem
|
|
32
|
+
turkiyem help
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Sehir Secimi
|
|
36
|
+
|
|
37
|
+
Hat sorgulama icin once sehir secmelisiniz:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
turkiyem sehir ankara
|
|
41
|
+
turkiyem sehir istanbul
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Hat Sorgulama
|
|
45
|
+
|
|
46
|
+
Secili sehre gore hat bilgilerini sorgular:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
# Ankara (EGO)
|
|
50
|
+
turkiyem sehir ankara
|
|
51
|
+
turkiyem hat 340
|
|
52
|
+
|
|
53
|
+
# Istanbul (IETT)
|
|
54
|
+
turkiyem sehir istanbul
|
|
55
|
+
turkiyem hat 34AS
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Ankara icin EGO web sitesinden sefer saatleri cekilir.
|
|
59
|
+
Istanbul icin IETT GTFS verilerinden hat bilgileri (durak sayisi, ilk/son durak) gosterilir.
|
|
60
|
+
|
|
61
|
+
### Deprem Sorgulama
|
|
62
|
+
|
|
63
|
+
AFAD API uzerinden gercek zamanli deprem verileri:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
# Son 24 saat
|
|
67
|
+
turkiyem deprem son24
|
|
68
|
+
|
|
69
|
+
# Son 7 gun
|
|
70
|
+
turkiyem deprem 7gun
|
|
71
|
+
|
|
72
|
+
# Buyukluge gore filtrele (ornegin >= 4.0)
|
|
73
|
+
turkiyem deprem buyukluk 4.0
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Buyuklugu 4.0 ve ustu olan depremler kirmizi ile vurgulanir.
|
|
77
|
+
|
|
78
|
+
### Temizleme
|
|
79
|
+
|
|
80
|
+
Cache ve yapilandirmayi sifirlar:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
turkiyem temizle
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Versiyon
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
turkiyem --version
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Yapilandirma
|
|
93
|
+
|
|
94
|
+
Secili sehir `~/.turkiyem/config.json` dosyasinda saklanir. Bu dosya otomatik olusturulur.
|
|
95
|
+
|
|
96
|
+
## Veri Kaynaklari
|
|
97
|
+
|
|
98
|
+
| Kaynak | Aciklama |
|
|
99
|
+
|--------|----------|
|
|
100
|
+
| AFAD | Deprem verileri (deprem.afad.gov.tr) |
|
|
101
|
+
| EGO | Ankara otobus sefer saatleri (ego.gov.tr) |
|
|
102
|
+
| IETT | Istanbul GTFS hat verileri (data.ibb.gov.tr) |
|
|
103
|
+
|
|
104
|
+
## npm Publish
|
|
105
|
+
|
|
106
|
+
1. npm hesabiniza giris yapin:
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
npm login
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
2. package.json icindeki `name`, `version`, `author` alanlarini duzenleyin.
|
|
113
|
+
|
|
114
|
+
3. Yayinlayin:
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
npm publish
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
4. Guncelleme icin versiyonu artirin:
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
npm version patch
|
|
124
|
+
npm publish
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Lisans
|
|
128
|
+
|
|
129
|
+
MIT
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "turkiyem",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Türkiye Toplu Taşıma ve Deprem CLI aracı - AFAD deprem verileri, EGO/IETT hat bilgileri",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./src/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"turkiyem": "./src/index.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"start": "node src/index.js",
|
|
12
|
+
"test": "echo \"No tests yet\""
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"turkiye",
|
|
16
|
+
"deprem",
|
|
17
|
+
"afad",
|
|
18
|
+
"iett",
|
|
19
|
+
"ego",
|
|
20
|
+
"toplu-tasima",
|
|
21
|
+
"cli",
|
|
22
|
+
"earthquake",
|
|
23
|
+
"transit"
|
|
24
|
+
],
|
|
25
|
+
"author": "",
|
|
26
|
+
"license": "MIT",
|
|
27
|
+
"engines": {
|
|
28
|
+
"node": ">=20.0.0"
|
|
29
|
+
},
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"axios": "^1.7.9",
|
|
32
|
+
"chalk": "^5.4.1",
|
|
33
|
+
"cheerio": "^1.0.0",
|
|
34
|
+
"cli-table3": "^0.6.5",
|
|
35
|
+
"commander": "^13.1.0",
|
|
36
|
+
"csv-parse": "^5.6.0",
|
|
37
|
+
"dotenv": "^16.4.7",
|
|
38
|
+
"node-cache": "^5.1.2",
|
|
39
|
+
"ora": "^8.2.0"
|
|
40
|
+
},
|
|
41
|
+
"files": [
|
|
42
|
+
"src/**/*",
|
|
43
|
+
"README.md"
|
|
44
|
+
]
|
|
45
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import ora from 'ora';
|
|
3
|
+
import { fetchEarthquakes, fetchByMagnitude } from '../services/afadService.js';
|
|
4
|
+
import { createEarthquakeTable } from '../utils/display.js';
|
|
5
|
+
|
|
6
|
+
export async function depremSon24() {
|
|
7
|
+
const spinner = ora('AFAD verileri alınıyor (son 24 saat)...').start();
|
|
8
|
+
|
|
9
|
+
try {
|
|
10
|
+
const earthquakes = await fetchEarthquakes('son24');
|
|
11
|
+
|
|
12
|
+
if (!earthquakes || earthquakes.length === 0) {
|
|
13
|
+
spinner.info('Son 24 saatte kayıtlı deprem bulunamadı.');
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
spinner.succeed(`${earthquakes.length} deprem bulundu (son 24 saat)`);
|
|
18
|
+
console.log('');
|
|
19
|
+
console.log(createEarthquakeTable(earthquakes));
|
|
20
|
+
} catch (err) {
|
|
21
|
+
spinner.fail(chalk.red(err.message));
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export async function deprem7Gun() {
|
|
26
|
+
const spinner = ora('AFAD verileri alınıyor (son 7 gün)...').start();
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
const earthquakes = await fetchEarthquakes('7gun');
|
|
30
|
+
|
|
31
|
+
if (!earthquakes || earthquakes.length === 0) {
|
|
32
|
+
spinner.info('Son 7 günde kayıtlı deprem bulunamadı.');
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
spinner.succeed(`${earthquakes.length} deprem bulundu (son 7 gün)`);
|
|
37
|
+
console.log('');
|
|
38
|
+
console.log(createEarthquakeTable(earthquakes));
|
|
39
|
+
} catch (err) {
|
|
40
|
+
spinner.fail(chalk.red(err.message));
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export async function depremBuyukluk(value) {
|
|
45
|
+
const min = parseFloat(value);
|
|
46
|
+
if (isNaN(min)) {
|
|
47
|
+
console.log(chalk.red('Geçerli bir büyüklük değeri girin. Örnek: turkiyem deprem buyukluk 4.0'));
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const spinner = ora(`Büyüklüğü >= ${min} olan depremler aranıyor...`).start();
|
|
52
|
+
|
|
53
|
+
try {
|
|
54
|
+
const earthquakes = await fetchByMagnitude(min);
|
|
55
|
+
|
|
56
|
+
if (!earthquakes || earthquakes.length === 0) {
|
|
57
|
+
spinner.info(`Büyüklüğü >= ${min} olan deprem bulunamadı (son 7 gün).`);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
spinner.succeed(`${earthquakes.length} deprem bulundu (büyüklük >= ${min})`);
|
|
62
|
+
console.log('');
|
|
63
|
+
console.log(createEarthquakeTable(earthquakes));
|
|
64
|
+
} catch (err) {
|
|
65
|
+
spinner.fail(chalk.red(err.message));
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import ora from 'ora';
|
|
3
|
+
import { getCity } from '../utils/config.js';
|
|
4
|
+
import { fetchEgoSchedule } from '../services/egoService.js';
|
|
5
|
+
import { fetchIettRoute } from '../services/iettService.js';
|
|
6
|
+
import { createEgoInfoTable, createEgoScheduleTable, createRouteTable } from '../utils/display.js';
|
|
7
|
+
|
|
8
|
+
export async function hatSorgula(hatNo) {
|
|
9
|
+
if (!hatNo) {
|
|
10
|
+
console.log(chalk.red('Hat numarası belirtmelisiniz. Örnek: turkiyem hat 340'));
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const city = getCity();
|
|
15
|
+
|
|
16
|
+
if (!city) {
|
|
17
|
+
console.log(chalk.yellow('Henüz şehir seçmediniz. Önce şehir seçin:'));
|
|
18
|
+
console.log(chalk.cyan(' turkiyem sehir ankara'));
|
|
19
|
+
console.log(chalk.cyan(' turkiyem sehir istanbul'));
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (city === 'ankara') {
|
|
24
|
+
await queryEgo(hatNo);
|
|
25
|
+
} else if (city === 'istanbul') {
|
|
26
|
+
await queryIett(hatNo);
|
|
27
|
+
} else {
|
|
28
|
+
console.log(chalk.red(`Desteklenmeyen şehir: ${city}. ankara veya istanbul seçin.`));
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async function queryEgo(hatNo) {
|
|
33
|
+
const spinner = ora(`EGO hat ${hatNo} bilgileri alınıyor...`).start();
|
|
34
|
+
|
|
35
|
+
try {
|
|
36
|
+
const result = await fetchEgoSchedule(hatNo);
|
|
37
|
+
|
|
38
|
+
spinner.succeed(`Hat ${hatNo} bilgileri alındı`);
|
|
39
|
+
console.log('');
|
|
40
|
+
|
|
41
|
+
console.log(chalk.white.bold(' Hat Bilgileri'));
|
|
42
|
+
console.log(createEgoInfoTable(result.info));
|
|
43
|
+
console.log('');
|
|
44
|
+
|
|
45
|
+
if (result.schedule && result.schedule.length > 0) {
|
|
46
|
+
console.log(chalk.white.bold(' Sefer Saatleri'));
|
|
47
|
+
console.log(createEgoScheduleTable(result.schedule));
|
|
48
|
+
} else {
|
|
49
|
+
console.log(chalk.yellow(' Sefer saati bilgisi bulunamadı.'));
|
|
50
|
+
}
|
|
51
|
+
} catch (err) {
|
|
52
|
+
spinner.fail(chalk.red(err.message));
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async function queryIett(hatNo) {
|
|
57
|
+
const spinner = ora(`IETT hat ${hatNo} bilgileri alınıyor (GTFS verileri indiriliyor)...`).start();
|
|
58
|
+
|
|
59
|
+
try {
|
|
60
|
+
const route = await fetchIettRoute(hatNo);
|
|
61
|
+
|
|
62
|
+
spinner.succeed(`Hat ${hatNo} bilgileri alındı`);
|
|
63
|
+
console.log('');
|
|
64
|
+
|
|
65
|
+
console.log(chalk.white.bold(' Hat Bilgileri (IETT)'));
|
|
66
|
+
console.log(createRouteTable(route));
|
|
67
|
+
} catch (err) {
|
|
68
|
+
spinner.fail(chalk.red(err.message));
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { setCity, getCity } from '../utils/config.js';
|
|
3
|
+
|
|
4
|
+
const SUPPORTED_CITIES = ['ankara', 'istanbul'];
|
|
5
|
+
|
|
6
|
+
export function sehirSec(city) {
|
|
7
|
+
if (!city) {
|
|
8
|
+
const current = getCity();
|
|
9
|
+
if (current) {
|
|
10
|
+
console.log(chalk.green(`Şu an seçili şehir: ${chalk.bold(current)}`));
|
|
11
|
+
} else {
|
|
12
|
+
console.log(chalk.yellow('Henüz şehir seçilmemiş.'));
|
|
13
|
+
}
|
|
14
|
+
console.log('');
|
|
15
|
+
console.log(chalk.white('Kullanım:'));
|
|
16
|
+
console.log(chalk.cyan(' turkiyem sehir ankara'));
|
|
17
|
+
console.log(chalk.cyan(' turkiyem sehir istanbul'));
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const normalized = city.toLowerCase().trim();
|
|
22
|
+
|
|
23
|
+
if (!SUPPORTED_CITIES.includes(normalized)) {
|
|
24
|
+
console.log(chalk.red(`"${city}" desteklenmiyor.`));
|
|
25
|
+
console.log(chalk.white('Desteklenen şehirler:'));
|
|
26
|
+
SUPPORTED_CITIES.forEach((c) => {
|
|
27
|
+
console.log(chalk.cyan(` - ${c}`));
|
|
28
|
+
});
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
setCity(normalized);
|
|
33
|
+
console.log(chalk.green(`Şehir "${chalk.bold(normalized)}" olarak ayarlandı.`));
|
|
34
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { flushCache } from '../utils/cache.js';
|
|
3
|
+
import { resetConfig } from '../utils/config.js';
|
|
4
|
+
|
|
5
|
+
export function temizle() {
|
|
6
|
+
try {
|
|
7
|
+
flushCache();
|
|
8
|
+
resetConfig();
|
|
9
|
+
console.log(chalk.green('Cache ve yapılandırma başarıyla temizlendi.'));
|
|
10
|
+
} catch (err) {
|
|
11
|
+
console.log(chalk.red(`Temizleme sırasında hata oluştu: ${err.message}`));
|
|
12
|
+
}
|
|
13
|
+
}
|
package/src/index.js
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import 'dotenv/config';
|
|
4
|
+
import { Command } from 'commander';
|
|
5
|
+
import { createRequire } from 'node:module';
|
|
6
|
+
import { printBanner, printHelp } from './utils/banner.js';
|
|
7
|
+
import { sehirSec } from './commands/sehir.js';
|
|
8
|
+
import { hatSorgula } from './commands/hat.js';
|
|
9
|
+
import { depremSon24, deprem7Gun, depremBuyukluk } from './commands/deprem.js';
|
|
10
|
+
import { temizle } from './commands/temizle.js';
|
|
11
|
+
|
|
12
|
+
const require = createRequire(import.meta.url);
|
|
13
|
+
const pkg = require('../package.json');
|
|
14
|
+
|
|
15
|
+
process.on('unhandledRejection', (err) => {
|
|
16
|
+
console.error('Beklenmeyen hata:', err?.message || err);
|
|
17
|
+
process.exit(1);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
process.on('uncaughtException', (err) => {
|
|
21
|
+
console.error('Kritik hata:', err?.message || err);
|
|
22
|
+
process.exit(1);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
const program = new Command();
|
|
26
|
+
|
|
27
|
+
program
|
|
28
|
+
.name('turkiyem')
|
|
29
|
+
.description('Türkiye Toplu Taşıma ve Deprem CLI')
|
|
30
|
+
.version(pkg.version, '-v, --version', 'Versiyon göster')
|
|
31
|
+
.helpOption('-h, --help', 'Yardım göster')
|
|
32
|
+
.addHelpCommand(false);
|
|
33
|
+
|
|
34
|
+
program
|
|
35
|
+
.command('sehir [sehir]')
|
|
36
|
+
.description('Şehir seç (ankara veya istanbul)')
|
|
37
|
+
.action((sehir) => {
|
|
38
|
+
sehirSec(sehir);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
program
|
|
42
|
+
.command('hat <numara>')
|
|
43
|
+
.description('Hat bilgilerini sorgula')
|
|
44
|
+
.action(async (numara) => {
|
|
45
|
+
await hatSorgula(numara);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
const depremCmd = program
|
|
49
|
+
.command('deprem')
|
|
50
|
+
.description('Deprem verileri sorgula');
|
|
51
|
+
|
|
52
|
+
depremCmd
|
|
53
|
+
.command('son24')
|
|
54
|
+
.description('Son 24 saat depremler')
|
|
55
|
+
.action(async () => {
|
|
56
|
+
await depremSon24();
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
depremCmd
|
|
60
|
+
.command('7gun')
|
|
61
|
+
.description('Son 7 gün depremler')
|
|
62
|
+
.action(async () => {
|
|
63
|
+
await deprem7Gun();
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
depremCmd
|
|
67
|
+
.command('buyukluk <deger>')
|
|
68
|
+
.description('Büyüklüğe göre filtrele (ör: 4.0)')
|
|
69
|
+
.action(async (deger) => {
|
|
70
|
+
await depremBuyukluk(deger);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
program
|
|
74
|
+
.command('temizle')
|
|
75
|
+
.description('Cache ve yapılandırmayı temizle')
|
|
76
|
+
.action(() => {
|
|
77
|
+
temizle();
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
program
|
|
81
|
+
.command('help')
|
|
82
|
+
.description('Yardım göster')
|
|
83
|
+
.action(() => {
|
|
84
|
+
printHelp();
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
if (process.argv.length <= 2) {
|
|
88
|
+
printHelp();
|
|
89
|
+
} else {
|
|
90
|
+
program.parse(process.argv);
|
|
91
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
import { getCached, setCached } from '../utils/cache.js';
|
|
3
|
+
|
|
4
|
+
const AFAD_URL = 'https://deprem.afad.gov.tr/apiv2/event/filter';
|
|
5
|
+
|
|
6
|
+
function formatISODate(date) {
|
|
7
|
+
return date.toISOString().replace(/\.\d{3}Z$/, '');
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function computeTimeRange(period) {
|
|
11
|
+
const end = new Date();
|
|
12
|
+
const start = new Date();
|
|
13
|
+
|
|
14
|
+
if (period === 'son24') {
|
|
15
|
+
start.setHours(start.getHours() - 24);
|
|
16
|
+
} else if (period === '7gun') {
|
|
17
|
+
start.setDate(start.getDate() - 7);
|
|
18
|
+
} else {
|
|
19
|
+
start.setDate(start.getDate() - 1);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return { start: formatISODate(start), end: formatISODate(end) };
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function normalizeQuake(raw) {
|
|
26
|
+
return {
|
|
27
|
+
magnitude: raw.magnitude ?? raw.mag ?? raw.Magnitude ?? '-',
|
|
28
|
+
depth: raw.depth ?? raw.Depth ?? raw.derinlik ?? '-',
|
|
29
|
+
location: raw.location ?? raw.Location ?? raw.title ?? raw.yer ?? '-',
|
|
30
|
+
date: raw.date ?? raw.Date ?? raw.eventDate ?? '-',
|
|
31
|
+
latitude: raw.latitude ?? raw.Latitude ?? '-',
|
|
32
|
+
longitude: raw.longitude ?? raw.Longitude ?? '-',
|
|
33
|
+
type: raw.type ?? raw.Type ?? '-',
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export async function fetchEarthquakes(period, limit = 20) {
|
|
38
|
+
const cacheKey = `afad_${period}_${limit}`;
|
|
39
|
+
const cached = getCached(cacheKey);
|
|
40
|
+
if (cached) return cached;
|
|
41
|
+
|
|
42
|
+
const { start, end } = computeTimeRange(period);
|
|
43
|
+
|
|
44
|
+
const params = {
|
|
45
|
+
start,
|
|
46
|
+
end,
|
|
47
|
+
format: 'json',
|
|
48
|
+
limit,
|
|
49
|
+
orderby: 'timedesc',
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
try {
|
|
53
|
+
const response = await axios.get(AFAD_URL, {
|
|
54
|
+
params,
|
|
55
|
+
timeout: 15000,
|
|
56
|
+
maxRedirects: 5,
|
|
57
|
+
headers: {
|
|
58
|
+
'User-Agent': 'turkiyem-cli/1.0',
|
|
59
|
+
'Accept': 'application/json',
|
|
60
|
+
},
|
|
61
|
+
validateStatus(status) {
|
|
62
|
+
return status >= 200 && status < 600;
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
if (response.status === 500) {
|
|
67
|
+
const body = response.data;
|
|
68
|
+
if (body && body.status === 400) {
|
|
69
|
+
throw new Error(`AFAD API hata döndürdü: ${body.message || 'Geçersiz istek parametreleri'}`);
|
|
70
|
+
}
|
|
71
|
+
throw new Error(`AFAD API sunucu hatası (HTTP 500)`);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (response.status < 200 || response.status >= 300) {
|
|
75
|
+
throw new Error(`AFAD API HTTP ${response.status} hatası döndürdü`);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const data = response.data;
|
|
79
|
+
|
|
80
|
+
if (!Array.isArray(data)) {
|
|
81
|
+
throw new Error('AFAD API beklenmeyen yanıt formatı döndürdü');
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const earthquakes = data.map(normalizeQuake);
|
|
85
|
+
setCached(cacheKey, earthquakes);
|
|
86
|
+
return earthquakes;
|
|
87
|
+
} catch (err) {
|
|
88
|
+
if (err.code === 'ECONNABORTED') {
|
|
89
|
+
throw new Error('AFAD API isteği zaman aşımına uğradı. Lütfen tekrar deneyin.');
|
|
90
|
+
}
|
|
91
|
+
if (err.code === 'ENOTFOUND' || err.code === 'ECONNREFUSED') {
|
|
92
|
+
throw new Error('AFAD API sunucusuna bağlanılamıyor. İnternet bağlantınızı kontrol edin.');
|
|
93
|
+
}
|
|
94
|
+
throw err;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export async function fetchByMagnitude(minMagnitude) {
|
|
99
|
+
const cacheKey = `afad_mag_${minMagnitude}`;
|
|
100
|
+
const cached = getCached(cacheKey);
|
|
101
|
+
if (cached) return cached;
|
|
102
|
+
|
|
103
|
+
const earthquakes = await fetchEarthquakes('7gun', 100);
|
|
104
|
+
const filtered = earthquakes.filter(
|
|
105
|
+
(eq) => parseFloat(eq.magnitude) >= parseFloat(minMagnitude)
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
setCached(cacheKey, filtered);
|
|
109
|
+
return filtered;
|
|
110
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
import * as cheerio from 'cheerio';
|
|
3
|
+
import { getCached, setCached } from '../utils/cache.js';
|
|
4
|
+
|
|
5
|
+
const EGO_URL = 'https://www.ego.gov.tr/hareketsaatleri';
|
|
6
|
+
|
|
7
|
+
function parseInfoTable($) {
|
|
8
|
+
const info = {
|
|
9
|
+
hatNo: '',
|
|
10
|
+
hatAdi: '',
|
|
11
|
+
kalkis: '',
|
|
12
|
+
varis: '',
|
|
13
|
+
mesafe: '',
|
|
14
|
+
sure: '',
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const infoTable = $('.hatbilgileri table');
|
|
18
|
+
if (!infoTable.length) return info;
|
|
19
|
+
|
|
20
|
+
infoTable.find('tr').each((_, row) => {
|
|
21
|
+
const cells = $(row).find('td');
|
|
22
|
+
if (cells.length >= 3) {
|
|
23
|
+
const label = $(cells[0]).text().trim().toLowerCase();
|
|
24
|
+
const value = $(cells[2]).text().trim();
|
|
25
|
+
|
|
26
|
+
if (label.includes('hat no')) info.hatNo = value;
|
|
27
|
+
else if (label.includes('hat adı') || label.includes('hat ismi')) info.hatAdi = value;
|
|
28
|
+
else if (label.includes('kalkış')) info.kalkis = value;
|
|
29
|
+
else if (label.includes('varış')) info.varis = value;
|
|
30
|
+
else if (label.includes('mesafe')) info.mesafe = value;
|
|
31
|
+
else if (label.includes('süre')) info.sure = value;
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
return info;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function parseScheduleTable($, hatNo) {
|
|
39
|
+
const schedule = [];
|
|
40
|
+
const scheduleTable = $('.hareket-saatleri table');
|
|
41
|
+
if (!scheduleTable.length) return schedule;
|
|
42
|
+
|
|
43
|
+
const headers = [];
|
|
44
|
+
scheduleTable.find('th').each((_, th) => {
|
|
45
|
+
headers.push($(th).text().trim());
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
const dataRow = scheduleTable.find('tr').last();
|
|
49
|
+
const cells = dataRow.find('td');
|
|
50
|
+
|
|
51
|
+
cells.each((i, cell) => {
|
|
52
|
+
const raw = $(cell).html() || '';
|
|
53
|
+
const times = raw
|
|
54
|
+
.split(/<br\s*\/?>/i)
|
|
55
|
+
.map((t) => t.replace(/<[^>]*>/g, '').trim())
|
|
56
|
+
.filter((t) => t.length > 0)
|
|
57
|
+
.map((t) => t.replace(/\s*-\s*$/, '').trim())
|
|
58
|
+
.filter((t) => t.length > 0);
|
|
59
|
+
|
|
60
|
+
if (times.length > 0) {
|
|
61
|
+
const gunTipi = headers[i] || ['Hafta içi', 'Cumartesi', 'Pazar'][i] || `Sütun ${i + 1}`;
|
|
62
|
+
schedule.push({
|
|
63
|
+
hat: String(hatNo),
|
|
64
|
+
gunTipi,
|
|
65
|
+
saatler: times.join(', '),
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
return schedule;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export async function fetchEgoSchedule(hatNo) {
|
|
74
|
+
const cacheKey = `ego_${hatNo}`;
|
|
75
|
+
const cached = getCached(cacheKey);
|
|
76
|
+
if (cached) return cached;
|
|
77
|
+
|
|
78
|
+
try {
|
|
79
|
+
const response = await axios.get(EGO_URL, {
|
|
80
|
+
params: { hat_no: hatNo },
|
|
81
|
+
timeout: 15000,
|
|
82
|
+
maxRedirects: 5,
|
|
83
|
+
headers: {
|
|
84
|
+
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
|
|
85
|
+
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
|
|
86
|
+
'Accept-Language': 'tr-TR,tr;q=0.9,en-US;q=0.8,en;q=0.7',
|
|
87
|
+
},
|
|
88
|
+
responseType: 'text',
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
if (response.status !== 200) {
|
|
92
|
+
throw new Error(`EGO sunucusu HTTP ${response.status} döndürdü`);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const html = response.data;
|
|
96
|
+
if (!html || html.length < 100) {
|
|
97
|
+
throw new Error('EGO sunucusundan geçerli yanıt alınamadı');
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const $ = cheerio.load(html);
|
|
101
|
+
const info = parseInfoTable($);
|
|
102
|
+
|
|
103
|
+
if (!info.hatNo) {
|
|
104
|
+
info.hatNo = String(hatNo);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const schedule = parseScheduleTable($, hatNo);
|
|
108
|
+
|
|
109
|
+
const result = { info, schedule };
|
|
110
|
+
setCached(cacheKey, result);
|
|
111
|
+
return result;
|
|
112
|
+
} catch (err) {
|
|
113
|
+
if (err.code === 'ECONNABORTED') {
|
|
114
|
+
throw new Error('EGO sunucusu isteği zaman aşımına uğradı. Lütfen tekrar deneyin.');
|
|
115
|
+
}
|
|
116
|
+
if (err.code === 'ENOTFOUND' || err.code === 'ECONNREFUSED') {
|
|
117
|
+
throw new Error('EGO sunucusuna bağlanılamıyor. İnternet bağlantınızı kontrol edin.');
|
|
118
|
+
}
|
|
119
|
+
if (err.response && err.response.status === 404) {
|
|
120
|
+
throw new Error(`Hat numarası "${hatNo}" bulunamadı.`);
|
|
121
|
+
}
|
|
122
|
+
throw err;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
import { getCached, setCached } from '../utils/cache.js';
|
|
3
|
+
|
|
4
|
+
const DATASTORE_SQL_URL = 'https://data.ibb.gov.tr/api/3/action/datastore_search_sql';
|
|
5
|
+
const DATASTORE_SEARCH_URL = 'https://data.ibb.gov.tr/api/3/action/datastore_search';
|
|
6
|
+
|
|
7
|
+
const RESOURCE_IDS = {
|
|
8
|
+
routes: '46dbe388-c8c2-45c4-ac72-c06953de56a2',
|
|
9
|
+
trips: '7ff49bdd-b0d2-4a6e-9392-b598f77f5070',
|
|
10
|
+
stopTimes: '23778613-16fe-4d30-b8b8-8ca934ed2978',
|
|
11
|
+
stops: '2299bc82-983b-4bdf-8520-5cef8c555e29',
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const API_TIMEOUT = 30000;
|
|
15
|
+
const API_HEADERS = { 'User-Agent': 'turkiyem-cli/1.0' };
|
|
16
|
+
|
|
17
|
+
function escapeSQL(str) {
|
|
18
|
+
return str.replace(/'/g, "''");
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async function datastoreSQL(sql) {
|
|
22
|
+
try {
|
|
23
|
+
const response = await axios.get(DATASTORE_SQL_URL, {
|
|
24
|
+
params: { sql },
|
|
25
|
+
timeout: API_TIMEOUT,
|
|
26
|
+
headers: API_HEADERS,
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
if (!response.data?.success) {
|
|
30
|
+
throw new Error('IETT SQL sorgusu başarısız');
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return response.data.result.records;
|
|
34
|
+
} catch (err) {
|
|
35
|
+
if (err.response?.status === 403) {
|
|
36
|
+
throw new Error('IETT veri kaynağı erişim engeli (403 Forbidden). Lütfen daha sonra tekrar deneyin.');
|
|
37
|
+
}
|
|
38
|
+
if (err.code === 'ECONNABORTED') {
|
|
39
|
+
throw new Error('IETT veri kaynağı zaman aşımına uğradı. Lütfen tekrar deneyin.');
|
|
40
|
+
}
|
|
41
|
+
throw err;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async function datastoreSearch(resourceId, options = {}) {
|
|
46
|
+
const params = {
|
|
47
|
+
resource_id: resourceId,
|
|
48
|
+
limit: options.limit || 100,
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
if (options.filters) params.filters = JSON.stringify(options.filters);
|
|
52
|
+
if (options.sort) params.sort = options.sort;
|
|
53
|
+
|
|
54
|
+
try {
|
|
55
|
+
const response = await axios.get(DATASTORE_SEARCH_URL, {
|
|
56
|
+
params,
|
|
57
|
+
timeout: API_TIMEOUT,
|
|
58
|
+
headers: API_HEADERS,
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
if (!response.data?.success) {
|
|
62
|
+
throw new Error('IETT veri kaynağı başarısız yanıt döndürdü');
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return response.data.result;
|
|
66
|
+
} catch (err) {
|
|
67
|
+
if (err.response?.status === 403) {
|
|
68
|
+
throw new Error('IETT veri kaynağı erişim engeli (403 Forbidden). Lütfen daha sonra tekrar deneyin.');
|
|
69
|
+
}
|
|
70
|
+
if (err.code === 'ECONNABORTED') {
|
|
71
|
+
throw new Error('IETT veri kaynağı zaman aşımına uğradı. Lütfen tekrar deneyin.');
|
|
72
|
+
}
|
|
73
|
+
throw err;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function extractStopsFromName(longName) {
|
|
78
|
+
if (!longName || longName === '-') return { first: '-', last: '-' };
|
|
79
|
+
|
|
80
|
+
const separators = [' - ', ' – ', '-'];
|
|
81
|
+
for (const sep of separators) {
|
|
82
|
+
const parts = longName.split(sep).map((p) => p.trim()).filter(Boolean);
|
|
83
|
+
if (parts.length >= 2) {
|
|
84
|
+
return { first: parts[0], last: parts[parts.length - 1] };
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return { first: longName, last: '-' };
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async function tryGetStopCount(routeId) {
|
|
92
|
+
try {
|
|
93
|
+
const tripsResult = await datastoreSearch(RESOURCE_IDS.trips, {
|
|
94
|
+
filters: { route_id: routeId },
|
|
95
|
+
limit: 10,
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
const trips = tripsResult.records;
|
|
99
|
+
if (trips.length === 0) return 0;
|
|
100
|
+
|
|
101
|
+
for (const trip of trips) {
|
|
102
|
+
const stResult = await datastoreSearch(RESOURCE_IDS.stopTimes, {
|
|
103
|
+
filters: { trip_id: trip.trip_id },
|
|
104
|
+
limit: 1,
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
if (stResult.total > 0) {
|
|
108
|
+
return stResult.total;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return 0;
|
|
113
|
+
} catch {
|
|
114
|
+
return 0;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export async function fetchIettRoute(routeCode) {
|
|
119
|
+
const cacheKey = `iett_route_${routeCode.toUpperCase()}`;
|
|
120
|
+
const cached = getCached(cacheKey);
|
|
121
|
+
if (cached) return cached;
|
|
122
|
+
|
|
123
|
+
const code = routeCode.toUpperCase();
|
|
124
|
+
const sql = `SELECT * FROM "${RESOURCE_IDS.routes}" WHERE UPPER(route_short_name)='${escapeSQL(code)}' LIMIT 20`;
|
|
125
|
+
const routes = await datastoreSQL(sql);
|
|
126
|
+
|
|
127
|
+
if (!routes || routes.length === 0) {
|
|
128
|
+
throw new Error(`"${routeCode}" hat numarası IETT verilerinde bulunamadı.`);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const route = routes[0];
|
|
132
|
+
const { first, last } = extractStopsFromName(route.route_long_name);
|
|
133
|
+
|
|
134
|
+
const routeCount = routes.length;
|
|
135
|
+
const directions = routes
|
|
136
|
+
.map((r) => r.route_long_name)
|
|
137
|
+
.filter((v, i, a) => a.indexOf(v) === i);
|
|
138
|
+
|
|
139
|
+
const stopCount = await tryGetStopCount(route.route_id);
|
|
140
|
+
|
|
141
|
+
const result = {
|
|
142
|
+
routeShortName: route.route_short_name || code,
|
|
143
|
+
routeLongName: route.route_long_name || '-',
|
|
144
|
+
stopCount,
|
|
145
|
+
firstStop: first,
|
|
146
|
+
lastStop: last,
|
|
147
|
+
routeVariants: routeCount,
|
|
148
|
+
directions,
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
setCached(cacheKey, result);
|
|
152
|
+
return result;
|
|
153
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
|
|
3
|
+
const BANNER = `
|
|
4
|
+
${chalk.red.bold(' ████████╗██╗ ██╗██████╗ ██╗ ██╗██╗██╗ ██╗███████╗███╗ ███╗')}
|
|
5
|
+
${chalk.red.bold(' ╚══██╔══╝██║ ██║██╔══██╗██║ ██╔╝██║╚██╗ ██╔╝██╔════╝████╗ ████║')}
|
|
6
|
+
${chalk.red.bold(' ██║ ██║ ██║██████╔╝█████╔╝ ██║ ╚████╔╝ █████╗ ██╔████╔██║')}
|
|
7
|
+
${chalk.red.bold(' ██║ ██║ ██║██╔══██╗██╔═██╗ ██║ ╚██╔╝ ██╔══╝ ██║╚██╔╝██║')}
|
|
8
|
+
${chalk.red.bold(' ██║ ╚██████╔╝██║ ██║██║ ██╗██║ ██║ ███████╗██║ ╚═╝ ██║')}
|
|
9
|
+
${chalk.red.bold(' ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚══════╝╚═╝ ╚═╝')}
|
|
10
|
+
|
|
11
|
+
${chalk.white.bold('☾ ★')}
|
|
12
|
+
|
|
13
|
+
${chalk.gray('Türkiye Toplu Taşıma ve Deprem CLI')}
|
|
14
|
+
`;
|
|
15
|
+
|
|
16
|
+
export function printBanner() {
|
|
17
|
+
console.log(BANNER);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function printHelp() {
|
|
21
|
+
printBanner();
|
|
22
|
+
console.log(chalk.white.bold(' Komutlar:\n'));
|
|
23
|
+
console.log(chalk.cyan(' turkiyem sehir <ankara|istanbul>') + chalk.gray(' Şehir seç'));
|
|
24
|
+
console.log(chalk.cyan(' turkiyem hat <numara>') + chalk.gray(' Hat sorgula'));
|
|
25
|
+
console.log(chalk.cyan(' turkiyem deprem son24') + chalk.gray(' Son 24 saat depremler'));
|
|
26
|
+
console.log(chalk.cyan(' turkiyem deprem 7gun') + chalk.gray(' Son 7 gün depremler'));
|
|
27
|
+
console.log(chalk.cyan(' turkiyem deprem buyukluk <deger>') + chalk.gray(' Büyüklüğe göre filtrele'));
|
|
28
|
+
console.log(chalk.cyan(' turkiyem temizle') + chalk.gray(' Cache ve config temizle'));
|
|
29
|
+
console.log(chalk.cyan(' turkiyem --version') + chalk.gray(' Versiyon göster'));
|
|
30
|
+
console.log('');
|
|
31
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import NodeCache from 'node-cache';
|
|
2
|
+
|
|
3
|
+
const cache = new NodeCache({ stdTTL: 300, checkperiod: 60 });
|
|
4
|
+
|
|
5
|
+
export function getCached(key) {
|
|
6
|
+
return cache.get(key) || null;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function setCached(key, value, ttl) {
|
|
10
|
+
if (ttl !== undefined) {
|
|
11
|
+
cache.set(key, value, ttl);
|
|
12
|
+
} else {
|
|
13
|
+
cache.set(key, value);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function flushCache() {
|
|
18
|
+
cache.flushAll();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export default cache;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
|
|
5
|
+
const CONFIG_DIR = path.join(os.homedir(), '.turkiyem');
|
|
6
|
+
const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
|
|
7
|
+
|
|
8
|
+
const DEFAULT_CONFIG = { city: null };
|
|
9
|
+
|
|
10
|
+
function ensureDir() {
|
|
11
|
+
fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function readConfig() {
|
|
15
|
+
try {
|
|
16
|
+
ensureDir();
|
|
17
|
+
if (!fs.existsSync(CONFIG_FILE)) {
|
|
18
|
+
writeConfig(DEFAULT_CONFIG);
|
|
19
|
+
return { ...DEFAULT_CONFIG };
|
|
20
|
+
}
|
|
21
|
+
const raw = fs.readFileSync(CONFIG_FILE, 'utf-8');
|
|
22
|
+
const parsed = JSON.parse(raw);
|
|
23
|
+
if (typeof parsed !== 'object' || parsed === null) {
|
|
24
|
+
writeConfig(DEFAULT_CONFIG);
|
|
25
|
+
return { ...DEFAULT_CONFIG };
|
|
26
|
+
}
|
|
27
|
+
return parsed;
|
|
28
|
+
} catch {
|
|
29
|
+
writeConfig(DEFAULT_CONFIG);
|
|
30
|
+
return { ...DEFAULT_CONFIG };
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function writeConfig(config) {
|
|
35
|
+
ensureDir();
|
|
36
|
+
fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2), 'utf-8');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function getCity() {
|
|
40
|
+
const config = readConfig();
|
|
41
|
+
return config.city || null;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function setCity(city) {
|
|
45
|
+
const config = readConfig();
|
|
46
|
+
config.city = city;
|
|
47
|
+
writeConfig(config);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function resetConfig() {
|
|
51
|
+
writeConfig({ ...DEFAULT_CONFIG });
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export { CONFIG_DIR, CONFIG_FILE };
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import Table from 'cli-table3';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
|
|
4
|
+
export function createEarthquakeTable(earthquakes) {
|
|
5
|
+
const table = new Table({
|
|
6
|
+
head: [
|
|
7
|
+
chalk.white.bold('Tarih'),
|
|
8
|
+
chalk.white.bold('Büyüklük'),
|
|
9
|
+
chalk.white.bold('Derinlik (km)'),
|
|
10
|
+
chalk.white.bold('Konum'),
|
|
11
|
+
],
|
|
12
|
+
colWidths: [22, 12, 15, 45],
|
|
13
|
+
style: { head: [], border: ['gray'] },
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
for (const eq of earthquakes) {
|
|
17
|
+
const mag = parseFloat(eq.magnitude);
|
|
18
|
+
const magStr = mag >= 4.0
|
|
19
|
+
? chalk.red.bold(mag.toFixed(1))
|
|
20
|
+
: chalk.yellow(mag.toFixed(1));
|
|
21
|
+
|
|
22
|
+
const row = [
|
|
23
|
+
eq.date,
|
|
24
|
+
magStr,
|
|
25
|
+
eq.depth,
|
|
26
|
+
eq.location,
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
table.push(row);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return table.toString();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function createEgoScheduleTable(schedule) {
|
|
36
|
+
const table = new Table({
|
|
37
|
+
head: [
|
|
38
|
+
chalk.white.bold('Hat'),
|
|
39
|
+
chalk.white.bold('Gün Tipi'),
|
|
40
|
+
chalk.white.bold('Kalkış Saatleri'),
|
|
41
|
+
],
|
|
42
|
+
colWidths: [10, 14, 70],
|
|
43
|
+
style: { head: [], border: ['gray'] },
|
|
44
|
+
wordWrap: true,
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
for (const row of schedule) {
|
|
48
|
+
table.push([row.hat, row.gunTipi, row.saatler]);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return table.toString();
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function createEgoInfoTable(info) {
|
|
55
|
+
const table = new Table({
|
|
56
|
+
style: { head: [], border: ['gray'] },
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
table.push(
|
|
60
|
+
{ [chalk.cyan('Hat No')]: info.hatNo || '-' },
|
|
61
|
+
{ [chalk.cyan('Hat Adı')]: info.hatAdi || '-' },
|
|
62
|
+
{ [chalk.cyan('Kalkış')]: info.kalkis || '-' },
|
|
63
|
+
{ [chalk.cyan('Varış')]: info.varis || '-' },
|
|
64
|
+
{ [chalk.cyan('Mesafe')]: info.mesafe || '-' },
|
|
65
|
+
{ [chalk.cyan('Süre')]: info.sure || '-' },
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
return table.toString();
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function createRouteTable(route) {
|
|
72
|
+
const table = new Table({
|
|
73
|
+
style: { head: [], border: ['gray'] },
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
table.push(
|
|
77
|
+
{ [chalk.cyan('Hat Kısa Adı')]: route.routeShortName || '-' },
|
|
78
|
+
{ [chalk.cyan('Hat Uzun Adı')]: route.routeLongName || '-' },
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
if (route.stopCount > 0) {
|
|
82
|
+
table.push({ [chalk.cyan('Durak Sayısı')]: String(route.stopCount) });
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
table.push(
|
|
86
|
+
{ [chalk.cyan('İlk Durak')]: route.firstStop || '-' },
|
|
87
|
+
{ [chalk.cyan('Son Durak')]: route.lastStop || '-' },
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
if (route.directions && route.directions.length > 1) {
|
|
91
|
+
table.push({
|
|
92
|
+
[chalk.cyan('Güzergahlar')]: route.directions.join('\n'),
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (route.routeVariants && route.routeVariants > 1) {
|
|
97
|
+
table.push({
|
|
98
|
+
[chalk.cyan('Varyant Sayısı')]: String(route.routeVariants),
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return table.toString();
|
|
103
|
+
}
|