sondakika 2.0.6 → 2.0.7
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 +342 -324
- package/cli.js +555 -488
- package/package.json +25 -25
package/cli.js
CHANGED
|
@@ -1,488 +1,555 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
const blessed = require('blessed');
|
|
4
|
-
const RssParser = require('rss-parser');
|
|
5
|
-
const fs = require('fs');
|
|
6
|
-
const path = require('path');
|
|
7
|
-
const os = require('os');
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
fs.
|
|
34
|
-
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
console.
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
console.error(
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
start
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
tags: true,
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
const
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
};
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
};
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
if (
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const blessed = require('blessed');
|
|
4
|
+
const RssParser = require('rss-parser');
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
const path = require('path');
|
|
7
|
+
const os = require('os');
|
|
8
|
+
const { exec } = require('child_process');
|
|
9
|
+
|
|
10
|
+
const packagePath = path.join(__dirname, 'package.json');
|
|
11
|
+
const { version } = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
|
|
12
|
+
|
|
13
|
+
const STATE_DIR = path.join(os.homedir(), '.config', 'sondakika-cli');
|
|
14
|
+
const STATE_FILE = path.join(STATE_DIR, 'state.json');
|
|
15
|
+
|
|
16
|
+
const defaultCLIState = {
|
|
17
|
+
lastSource: 'trt',
|
|
18
|
+
itemsPerPage: 10
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
function loadCLIState() {
|
|
22
|
+
try {
|
|
23
|
+
if (fs.existsSync(STATE_FILE)) {
|
|
24
|
+
const data = JSON.parse(fs.readFileSync(STATE_FILE, 'utf8'));
|
|
25
|
+
return { ...defaultCLIState, ...data };
|
|
26
|
+
}
|
|
27
|
+
} catch (e) {}
|
|
28
|
+
return { ...defaultCLIState };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function saveCLIState(state) {
|
|
32
|
+
try {
|
|
33
|
+
if (!fs.existsSync(STATE_DIR)) fs.mkdirSync(STATE_DIR, { recursive: true });
|
|
34
|
+
fs.writeFileSync(STATE_FILE, JSON.stringify(state, null, 2), 'utf8');
|
|
35
|
+
} catch (e) {}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Sources with working URLs (mix of HTTP and HTTPS as per original root index.js)
|
|
39
|
+
const sources = {
|
|
40
|
+
cumhuriyet: {
|
|
41
|
+
name: 'Cumhuriyet',
|
|
42
|
+
url: 'https://www.cumhuriyet.com.tr/rss/son_dakika.xml',
|
|
43
|
+
isSondakika: true
|
|
44
|
+
},
|
|
45
|
+
trt: {
|
|
46
|
+
name: 'TRT Haber',
|
|
47
|
+
url: 'https://www.trthaber.com/sondakika.rss',
|
|
48
|
+
isSondakika: true
|
|
49
|
+
},
|
|
50
|
+
mynet: {
|
|
51
|
+
name: 'Mynet',
|
|
52
|
+
url: 'https://www.mynet.com/haber/rss/sondakika',
|
|
53
|
+
isSondakika: true
|
|
54
|
+
},
|
|
55
|
+
sabah: {
|
|
56
|
+
name: 'Sabah',
|
|
57
|
+
url: 'https://www.sabah.com.tr/rss/anasayfa.xml',
|
|
58
|
+
isSondakika: false
|
|
59
|
+
},
|
|
60
|
+
star: {
|
|
61
|
+
name: 'Star',
|
|
62
|
+
url: 'https://www.star.com.tr/rss/rss.asp?cid=124',
|
|
63
|
+
isSondakika: false
|
|
64
|
+
},
|
|
65
|
+
vatan: {
|
|
66
|
+
name: 'Gazete Vatan',
|
|
67
|
+
url: 'https://www.gazetevatan.com/rss/gundem.xml',
|
|
68
|
+
isSondakika: false
|
|
69
|
+
},
|
|
70
|
+
haberturk: {
|
|
71
|
+
name: 'Haberturk',
|
|
72
|
+
url: 'https://www.haberturk.com/rss',
|
|
73
|
+
isSondakika: false
|
|
74
|
+
},
|
|
75
|
+
cnnturk: {
|
|
76
|
+
name: 'CNN Turk',
|
|
77
|
+
url: 'https://cnnturk.com/feed/rss/turkiye',
|
|
78
|
+
isSondakika: false
|
|
79
|
+
},
|
|
80
|
+
yenisafak: {
|
|
81
|
+
name: 'Yeni Safak',
|
|
82
|
+
url: 'https://www.yenisafak.com/rss',
|
|
83
|
+
isSondakika: false
|
|
84
|
+
},
|
|
85
|
+
aa: {
|
|
86
|
+
name: 'Anadolu Ajansi',
|
|
87
|
+
url: 'https://www.aa.com.tr/en/rss',
|
|
88
|
+
isSondakika: false
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const parser = new RssParser({
|
|
93
|
+
timeout: 10000,
|
|
94
|
+
requestOptions: {
|
|
95
|
+
headers: {
|
|
96
|
+
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
maxRedirects: 5
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
const ITEMS_PER_PAGE = 10;
|
|
103
|
+
|
|
104
|
+
function formatDate(pubDate) {
|
|
105
|
+
if (!pubDate) return '';
|
|
106
|
+
const date = new Date(pubDate);
|
|
107
|
+
if (isNaN(date.getTime())) return '';
|
|
108
|
+
return date.toLocaleDateString('tr-TR', {
|
|
109
|
+
day: '2-digit',
|
|
110
|
+
month: '2-digit',
|
|
111
|
+
year: 'numeric',
|
|
112
|
+
hour: '2-digit',
|
|
113
|
+
minute: '2-digit'
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
async function fetchNews(sourceKey, count) {
|
|
118
|
+
const source = sources[sourceKey.toLowerCase()];
|
|
119
|
+
if (!source) {
|
|
120
|
+
console.error(`Unknown source: ${sourceKey}`);
|
|
121
|
+
console.log(`Available sources: ${Object.keys(sources).join(', ')}`);
|
|
122
|
+
process.exit(1);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
try {
|
|
126
|
+
const feed = await parser.parseURL(source.url);
|
|
127
|
+
|
|
128
|
+
if (!feed.items || feed.items.length === 0) {
|
|
129
|
+
console.error(`No items found in feed from ${source.name}`);
|
|
130
|
+
process.exit(1);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return feed.items.slice(0, count);
|
|
134
|
+
} catch (err) {
|
|
135
|
+
console.error('Error fetching news:', err.message);
|
|
136
|
+
console.error(`URL: ${source.url}`);
|
|
137
|
+
process.exit(1);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function wrapText(text, width) {
|
|
142
|
+
const lines = [];
|
|
143
|
+
let start = 0;
|
|
144
|
+
while (start < text.length) {
|
|
145
|
+
let end = Math.min(start + width, text.length);
|
|
146
|
+
if (end < text.length && end > start) {
|
|
147
|
+
const lastSpace = text.lastIndexOf(' ', end);
|
|
148
|
+
if (lastSpace > start && lastSpace > end - width / 2) {
|
|
149
|
+
end = lastSpace;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
lines.push(text.substring(start, end).trim());
|
|
153
|
+
start = end;
|
|
154
|
+
while (start < text.length && text[start] === ' ') {
|
|
155
|
+
start++;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return lines.length ? lines : [''];
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function createScreen() {
|
|
162
|
+
return blessed.screen({
|
|
163
|
+
smartCSR: true,
|
|
164
|
+
title: 'Sondakika Haberler'
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function createMainList(screen, items, sourceName) {
|
|
169
|
+
const list = blessed.list({
|
|
170
|
+
parent: screen,
|
|
171
|
+
top: 2,
|
|
172
|
+
left: 0,
|
|
173
|
+
width: '100%',
|
|
174
|
+
height: '100%-4',
|
|
175
|
+
tags: true,
|
|
176
|
+
keys: false,
|
|
177
|
+
mouse: false,
|
|
178
|
+
border: null,
|
|
179
|
+
style: {
|
|
180
|
+
selected: {
|
|
181
|
+
fg: 'white',
|
|
182
|
+
bg: 'blue'
|
|
183
|
+
}
|
|
184
|
+
},
|
|
185
|
+
scrollbar: {
|
|
186
|
+
ch: '█',
|
|
187
|
+
inverse: true
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
list.focus();
|
|
192
|
+
|
|
193
|
+
const header = blessed.box({
|
|
194
|
+
parent: screen,
|
|
195
|
+
top: 0,
|
|
196
|
+
left: 0,
|
|
197
|
+
width: '100%',
|
|
198
|
+
height: 2,
|
|
199
|
+
content: `{bold}📰 ${sourceName}{/bold} - {cyan-fg}▲/▼{/cyan-fg} Seç, {cyan-fg}◄/►{/cyan-fg} Sayfa, {cyan-fg}Enter{/cyan-fg} Görüntüle, {cyan-fg}Q{/cyan-fg} Çıkış`,
|
|
200
|
+
tags: true,
|
|
201
|
+
style: {
|
|
202
|
+
fg: 'white',
|
|
203
|
+
bg: 'blue'
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
const footer = blessed.box({
|
|
208
|
+
parent: screen,
|
|
209
|
+
bottom: 0,
|
|
210
|
+
left: 0,
|
|
211
|
+
width: '100%',
|
|
212
|
+
height: 2,
|
|
213
|
+
content: '{cyan-fg}▲{/cyan-fg} Yukarı {cyan-fg}▼{/cyan-fg} Aşağı {cyan-fg}◄{/cyan-fg} Önceki Sayfa {cyan-fg}►{/cyan-fg} Sonraki Sayfa {cyan-fg}Enter{/cyan-fg} Görüntüle {cyan-fg}Q{/cyan-fg} Çıkış',
|
|
214
|
+
tags: true,
|
|
215
|
+
style: {
|
|
216
|
+
fg: 'white',
|
|
217
|
+
bg: 'black'
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
return { list, header, footer };
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function createFullScreenView(screen) {
|
|
225
|
+
const overlay = blessed.box({
|
|
226
|
+
parent: screen,
|
|
227
|
+
top: 0,
|
|
228
|
+
left: 0,
|
|
229
|
+
width: '100%',
|
|
230
|
+
height: '100%-2',
|
|
231
|
+
tags: true,
|
|
232
|
+
border: {
|
|
233
|
+
type: 'line'
|
|
234
|
+
},
|
|
235
|
+
style: {
|
|
236
|
+
border: {
|
|
237
|
+
fg: 'cyan'
|
|
238
|
+
}
|
|
239
|
+
},
|
|
240
|
+
hidden: true,
|
|
241
|
+
scrollable: true,
|
|
242
|
+
alwaysScroll: true,
|
|
243
|
+
scrolloffset: 1
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
const linkBar = blessed.box({
|
|
247
|
+
parent: screen,
|
|
248
|
+
bottom: 1,
|
|
249
|
+
left: 1,
|
|
250
|
+
width: '100%-2',
|
|
251
|
+
height: 1,
|
|
252
|
+
tags: true,
|
|
253
|
+
hidden: true,
|
|
254
|
+
content: '',
|
|
255
|
+
style: {
|
|
256
|
+
fg: 'cyan',
|
|
257
|
+
bg: 'black'
|
|
258
|
+
}
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
const closeHint = blessed.box({
|
|
262
|
+
parent: screen,
|
|
263
|
+
bottom: 0,
|
|
264
|
+
left: 0,
|
|
265
|
+
width: '100%',
|
|
266
|
+
height: 1,
|
|
267
|
+
content: '{center}{cyan-fg}◄{/cyan-fg} Önceki {cyan-fg}►{/cyan-fg} Sonraki Don/Ac (Enter){/center}',
|
|
268
|
+
tags: true,
|
|
269
|
+
style: {
|
|
270
|
+
fg: 'black',
|
|
271
|
+
bg: 'cyan'
|
|
272
|
+
}
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
return { overlay, linkBar, closeHint };
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
function updateListDisplay(list, items, page, totalPages) {
|
|
279
|
+
const start = page * ITEMS_PER_PAGE;
|
|
280
|
+
const end = Math.min(start + ITEMS_PER_PAGE, items.length);
|
|
281
|
+
const pageItems = items.slice(start, end);
|
|
282
|
+
|
|
283
|
+
const lines = pageItems.map((item, idx) => {
|
|
284
|
+
const globalIdx = start + idx + 1;
|
|
285
|
+
const dateStr = formatDate(item.pubDate || item.isodate);
|
|
286
|
+
const titleLines = wrapText(item.title, 60);
|
|
287
|
+
let line = `{bold}${globalIdx}.{/bold} ${titleLines[0]}`;
|
|
288
|
+
if (dateStr) {
|
|
289
|
+
line += ` {gray-fg}[${dateStr}]{/gray-fg}`;
|
|
290
|
+
}
|
|
291
|
+
return line;
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
list.setItems(lines);
|
|
295
|
+
list.scrollTo(0);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
function showFullScreen(overlay, linkBar, item) {
|
|
299
|
+
const title = item.title || 'Başlık yok';
|
|
300
|
+
const dateStr = formatDate(item.pubDate || item.isodate);
|
|
301
|
+
const summary = item.summary || item.contentSnippet || item.content || 'İçerik yok';
|
|
302
|
+
const link = item.link || '';
|
|
303
|
+
|
|
304
|
+
const titleLines = wrapText(title, 70);
|
|
305
|
+
const summaryLines = wrapText(summary, 70);
|
|
306
|
+
|
|
307
|
+
let content = '{bold}' + titleLines.join('\n') + '{/bold}\n\n';
|
|
308
|
+
|
|
309
|
+
if (dateStr) {
|
|
310
|
+
content += `{gray-fg}📅 ${dateStr}{/gray-fg}\n\n`;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
content += summaryLines.join('\n');
|
|
314
|
+
|
|
315
|
+
overlay.setContent(content);
|
|
316
|
+
overlay.show();
|
|
317
|
+
|
|
318
|
+
if (link) {
|
|
319
|
+
linkBar.setContent(`{cyan-fg}🔗 ${link}{/cyan-fg}`);
|
|
320
|
+
} else {
|
|
321
|
+
linkBar.setContent('');
|
|
322
|
+
}
|
|
323
|
+
linkBar.show();
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
function hideFullScreen(overlay, linkBar) {
|
|
327
|
+
overlay.hide();
|
|
328
|
+
linkBar.hide();
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
async function main() {
|
|
332
|
+
const args = process.argv.slice(2);
|
|
333
|
+
const cliState = loadCLIState();
|
|
334
|
+
|
|
335
|
+
if (args.length === 0 || args[0] === '--help' || args[0] === '-h') {
|
|
336
|
+
const breakingSrc = Object.entries(sources)
|
|
337
|
+
.filter(([k, v]) => v.isSondakika)
|
|
338
|
+
.map(([k, v]) => ` ${k.padEnd(12)} ${v.name}`)
|
|
339
|
+
.join('\n');
|
|
340
|
+
const generalSrc = Object.entries(sources)
|
|
341
|
+
.filter(([k, v]) => !v.isSondakika)
|
|
342
|
+
.map(([k, v]) => ` ${k.padEnd(12)} ${v.name}`)
|
|
343
|
+
.join('\n');
|
|
344
|
+
console.log(`
|
|
345
|
+
📰 Sondakika v${version} - Türkçe Haber Okuyucu (CLI)
|
|
346
|
+
Tam özellikli terminal haber okuyucu
|
|
347
|
+
|
|
348
|
+
Kullanım:
|
|
349
|
+
sondakika <kaynak> [adet]
|
|
350
|
+
sondakika --version
|
|
351
|
+
sondakika --help
|
|
352
|
+
|
|
353
|
+
Kaynaklar (Son Dakika):
|
|
354
|
+
${breakingSrc}
|
|
355
|
+
|
|
356
|
+
Kaynaklar (Haberler):
|
|
357
|
+
${generalSrc}
|
|
358
|
+
|
|
359
|
+
Örnekler:
|
|
360
|
+
sondakika cumhuriyet # Cumhuriyet haberleri
|
|
361
|
+
sondakika trt 15 # TRT 15 haber
|
|
362
|
+
sondakika mynet 5 # Mynet 5 haber
|
|
363
|
+
sondakika sabah # Sabah haberleri
|
|
364
|
+
sondakika haberturk 5 # Habertürk 5 haber
|
|
365
|
+
sondakika cnnturk # CNN Türk haberleri
|
|
366
|
+
`);
|
|
367
|
+
process.exit(0);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
if (args[0] === '--version' || args[0] === '-v') {
|
|
371
|
+
console.log(`sondakika v${version}`);
|
|
372
|
+
process.exit(0);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
const sourceKey = args[0] ? args[0].toLowerCase() : cliState.lastSource;
|
|
376
|
+
const count = args[1] ? parseInt(args[1]) : 1000; // Default to max (fetch all available news)
|
|
377
|
+
|
|
378
|
+
const source = sources[sourceKey];
|
|
379
|
+
if (!source) {
|
|
380
|
+
console.error(`Unknown source: ${sourceKey}`);
|
|
381
|
+
console.log(`Available sources: ${Object.keys(sources).join(', ')}`);
|
|
382
|
+
process.exit(1);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
// Save the source and count for next time
|
|
386
|
+
cliState.lastSource = sourceKey;
|
|
387
|
+
cliState.itemsPerPage = count;
|
|
388
|
+
saveCLIState(cliState);
|
|
389
|
+
|
|
390
|
+
const sourceName = source.isSondakika ? `${source.name} (Son Dakika)` : source.name;
|
|
391
|
+
|
|
392
|
+
const screen = createScreen();
|
|
393
|
+
const { list, header, footer } = createMainList(screen, [], sourceName);
|
|
394
|
+
const { overlay, linkBar, closeHint } = createFullScreenView(screen);
|
|
395
|
+
|
|
396
|
+
let items = [];
|
|
397
|
+
let currentPage = 0;
|
|
398
|
+
let totalPages = 0;
|
|
399
|
+
let inFullScreen = false;
|
|
400
|
+
let currentItemIndex = 0;
|
|
401
|
+
|
|
402
|
+
const loadNews = async () => {
|
|
403
|
+
header.setContent(`{bold}📰 ${sourceName}{/bold} - Yükleniyor...`);
|
|
404
|
+
screen.render();
|
|
405
|
+
|
|
406
|
+
items = await fetchNews(sourceKey, count);
|
|
407
|
+
totalPages = Math.ceil(items.length / ITEMS_PER_PAGE);
|
|
408
|
+
currentPage = 0;
|
|
409
|
+
|
|
410
|
+
updateListDisplay(list, items, currentPage, totalPages);
|
|
411
|
+
list.select(0);
|
|
412
|
+
header.setContent(`{bold}📰 ${sourceName}{/bold} - {cyan-fg}▲/▼{/cyan-fg} Seç, {cyan-fg}◄/►{/cyan-fg} Sayfa, {cyan-fg}Enter{/cyan-fg} Görüntüle, {cyan-fg}Q{/cyan-fg} Çıkış`);
|
|
413
|
+
footer.setContent(`{cyan-fg}▲{/cyan-fg} Yukarı {cyan-fg}▼{/cyan-fg} Aşağı {cyan-fg}◄{/cyan-fg} Önceki Sayfa (${currentPage + 1}/${totalPages}) {cyan-fg}►{/cyan-fg} Sonraki Sayfa {cyan-fg}Enter{/cyan-fg} Görüntüle {cyan-fg}Q{/cyan-fg} Çıkış`);
|
|
414
|
+
screen.render();
|
|
415
|
+
};
|
|
416
|
+
|
|
417
|
+
const updateFooter = () => {
|
|
418
|
+
footer.setContent(`{cyan-fg}▲{/cyan-fg} Yukarı {cyan-fg}▼{/cyan-fg} Aşağı {cyan-fg}◄{/cyan-fg} Önceki Sayfa (${currentPage + 1}/${totalPages}) {cyan-fg}►{/cyan-fg} Sonraki Sayfa {cyan-fg}Enter{/cyan-fg} Görüntüle {cyan-fg}Q{/cyan-fg} Çıkış`);
|
|
419
|
+
};
|
|
420
|
+
|
|
421
|
+
const goToPrevPage = () => {
|
|
422
|
+
if (currentPage > 0) {
|
|
423
|
+
currentPage--;
|
|
424
|
+
updateListDisplay(list, items, currentPage, totalPages);
|
|
425
|
+
list.select(0);
|
|
426
|
+
updateFooter();
|
|
427
|
+
screen.render();
|
|
428
|
+
}
|
|
429
|
+
};
|
|
430
|
+
|
|
431
|
+
const goToNextPage = () => {
|
|
432
|
+
if (currentPage < totalPages - 1) {
|
|
433
|
+
currentPage++;
|
|
434
|
+
updateListDisplay(list, items, currentPage, totalPages);
|
|
435
|
+
list.select(0);
|
|
436
|
+
updateFooter();
|
|
437
|
+
screen.render();
|
|
438
|
+
}
|
|
439
|
+
};
|
|
440
|
+
|
|
441
|
+
const openFullScreen = () => {
|
|
442
|
+
const start = currentPage * ITEMS_PER_PAGE;
|
|
443
|
+
currentItemIndex = start + list.selected;
|
|
444
|
+
if (currentItemIndex < items.length) {
|
|
445
|
+
showFullScreen(overlay, linkBar, items[currentItemIndex]);
|
|
446
|
+
inFullScreen = true;
|
|
447
|
+
screen.render();
|
|
448
|
+
}
|
|
449
|
+
};
|
|
450
|
+
|
|
451
|
+
const closeFullScreen = () => {
|
|
452
|
+
hideFullScreen(overlay, linkBar);
|
|
453
|
+
inFullScreen = false;
|
|
454
|
+
screen.render();
|
|
455
|
+
};
|
|
456
|
+
|
|
457
|
+
const goToPrevItem = () => {
|
|
458
|
+
if (inFullScreen && currentItemIndex > 0) {
|
|
459
|
+
currentItemIndex--;
|
|
460
|
+
showFullScreen(overlay, linkBar, items[currentItemIndex]);
|
|
461
|
+
const newPage = Math.floor(currentItemIndex / ITEMS_PER_PAGE);
|
|
462
|
+
const newSelected = currentItemIndex % ITEMS_PER_PAGE;
|
|
463
|
+
if (newPage !== currentPage) {
|
|
464
|
+
currentPage = newPage;
|
|
465
|
+
updateListDisplay(list, items, currentPage, totalPages);
|
|
466
|
+
updateFooter();
|
|
467
|
+
}
|
|
468
|
+
list.select(newSelected);
|
|
469
|
+
screen.render();
|
|
470
|
+
}
|
|
471
|
+
};
|
|
472
|
+
|
|
473
|
+
const goToNextItem = () => {
|
|
474
|
+
if (inFullScreen && currentItemIndex < items.length - 1) {
|
|
475
|
+
currentItemIndex++;
|
|
476
|
+
showFullScreen(overlay, linkBar, items[currentItemIndex]);
|
|
477
|
+
const newPage = Math.floor(currentItemIndex / ITEMS_PER_PAGE);
|
|
478
|
+
const newSelected = currentItemIndex % ITEMS_PER_PAGE;
|
|
479
|
+
if (newPage !== currentPage) {
|
|
480
|
+
currentPage = newPage;
|
|
481
|
+
updateListDisplay(list, items, currentPage, totalPages);
|
|
482
|
+
updateFooter();
|
|
483
|
+
}
|
|
484
|
+
list.select(newSelected);
|
|
485
|
+
screen.render();
|
|
486
|
+
}
|
|
487
|
+
};
|
|
488
|
+
|
|
489
|
+
screen.key(['up'], () => {
|
|
490
|
+
if (!inFullScreen) {
|
|
491
|
+
list.up();
|
|
492
|
+
screen.render();
|
|
493
|
+
}
|
|
494
|
+
});
|
|
495
|
+
|
|
496
|
+
screen.key(['down'], () => {
|
|
497
|
+
if (!inFullScreen) {
|
|
498
|
+
list.down();
|
|
499
|
+
screen.render();
|
|
500
|
+
}
|
|
501
|
+
});
|
|
502
|
+
|
|
503
|
+
screen.key(['left'], () => {
|
|
504
|
+
if (inFullScreen) {
|
|
505
|
+
goToPrevItem();
|
|
506
|
+
} else {
|
|
507
|
+
goToPrevPage();
|
|
508
|
+
}
|
|
509
|
+
});
|
|
510
|
+
|
|
511
|
+
screen.key(['right'], () => {
|
|
512
|
+
if (inFullScreen) {
|
|
513
|
+
goToNextItem();
|
|
514
|
+
} else {
|
|
515
|
+
goToNextPage();
|
|
516
|
+
}
|
|
517
|
+
});
|
|
518
|
+
|
|
519
|
+
screen.key(['o', 'O'], () => {
|
|
520
|
+
if (inFullScreen && currentItemIndex < items.length) {
|
|
521
|
+
const link = items[currentItemIndex].link;
|
|
522
|
+
if (link) {
|
|
523
|
+
const cmd = process.platform === 'win32' ? 'start' : process.platform === 'darwin' ? 'open' : 'xdg-open';
|
|
524
|
+
exec(`${cmd} "${link}"`);
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
});
|
|
528
|
+
|
|
529
|
+
screen.key(['enter'], () => {
|
|
530
|
+
if (inFullScreen) {
|
|
531
|
+
closeFullScreen();
|
|
532
|
+
} else {
|
|
533
|
+
openFullScreen();
|
|
534
|
+
}
|
|
535
|
+
});
|
|
536
|
+
|
|
537
|
+
// Use Escape, Q, or Ctrl+C to quit
|
|
538
|
+
screen.unkey(['q', 'C-c']);
|
|
539
|
+
screen.key(['escape', 'q', 'Q', 'C-c'], () => {
|
|
540
|
+
process.exit(0);
|
|
541
|
+
});
|
|
542
|
+
|
|
543
|
+
list.on('select', (el, idx) => {
|
|
544
|
+
openFullScreen();
|
|
545
|
+
});
|
|
546
|
+
|
|
547
|
+
await loadNews();
|
|
548
|
+
|
|
549
|
+
screen.render();
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
main().catch(err => {
|
|
553
|
+
console.error('Error:', err.message);
|
|
554
|
+
process.exit(1);
|
|
555
|
+
});
|