turkiyemonitor 1.1.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/LICENSE +21 -0
- package/README.md +54 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +58 -0
- package/dist/cli.js.map +1 -0
- package/dist/collectors/crypto.d.ts +3 -0
- package/dist/collectors/crypto.js +37 -0
- package/dist/collectors/crypto.js.map +1 -0
- package/dist/collectors/earthquakes.d.ts +3 -0
- package/dist/collectors/earthquakes.js +53 -0
- package/dist/collectors/earthquakes.js.map +1 -0
- package/dist/collectors/economic-news.d.ts +3 -0
- package/dist/collectors/economic-news.js +49 -0
- package/dist/collectors/economic-news.js.map +1 -0
- package/dist/collectors/eksi-sozluk.d.ts +3 -0
- package/dist/collectors/eksi-sozluk.js +38 -0
- package/dist/collectors/eksi-sozluk.js.map +1 -0
- package/dist/collectors/exchange-rates.d.ts +3 -0
- package/dist/collectors/exchange-rates.js +36 -0
- package/dist/collectors/exchange-rates.js.map +1 -0
- package/dist/collectors/fuel-prices.d.ts +3 -0
- package/dist/collectors/fuel-prices.js +70 -0
- package/dist/collectors/fuel-prices.js.map +1 -0
- package/dist/collectors/magnificent-seven-stocks.d.ts +3 -0
- package/dist/collectors/magnificent-seven-stocks.js +40 -0
- package/dist/collectors/magnificent-seven-stocks.js.map +1 -0
- package/dist/collectors/politics.d.ts +3 -0
- package/dist/collectors/politics.js +35 -0
- package/dist/collectors/politics.js.map +1 -0
- package/dist/collectors/stocks.d.ts +3 -0
- package/dist/collectors/stocks.js +76 -0
- package/dist/collectors/stocks.js.map +1 -0
- package/dist/collectors/tuik-stats.d.ts +3 -0
- package/dist/collectors/tuik-stats.js +36 -0
- package/dist/collectors/tuik-stats.js.map +1 -0
- package/dist/collectors/twitter-trends.d.ts +3 -0
- package/dist/collectors/twitter-trends.js +25 -0
- package/dist/collectors/twitter-trends.js.map +1 -0
- package/dist/collectors/weather.d.ts +3 -0
- package/dist/collectors/weather.js +67 -0
- package/dist/collectors/weather.js.map +1 -0
- package/dist/collectors/youtube-trends.d.ts +3 -0
- package/dist/collectors/youtube-trends.js +40 -0
- package/dist/collectors/youtube-trends.js.map +1 -0
- package/dist/core/formatter.d.ts +3 -0
- package/dist/core/formatter.js +315 -0
- package/dist/core/formatter.js.map +1 -0
- package/dist/core/i18n.d.ts +3 -0
- package/dist/core/i18n.js +100 -0
- package/dist/core/i18n.js.map +1 -0
- package/dist/core/runner.d.ts +4 -0
- package/dist/core/runner.js +90 -0
- package/dist/core/runner.js.map +1 -0
- package/dist/domain/agenda-result.d.ts +24 -0
- package/dist/domain/agenda-result.js +59 -0
- package/dist/domain/agenda-result.js.map +1 -0
- package/dist/domain/types.d.ts +94 -0
- package/dist/domain/types.js +3 -0
- package/dist/domain/types.js.map +1 -0
- package/dist/services/browser.d.ts +7 -0
- package/dist/services/browser.js +64 -0
- package/dist/services/browser.js.map +1 -0
- package/dist/services/http-client.d.ts +2 -0
- package/dist/services/http-client.js +20 -0
- package/dist/services/http-client.js.map +1 -0
- package/package.json +66 -0
- package/screenshot.png +0 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Barış Kısır
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# TurkiyeMonitor 🇹🇷
|
|
2
|
+
|
|
3
|
+
A professional CLI tool for monitoring Turkiye's agenda in real time. Get economic data, news, social trends, earthquakes, weather, and market snapshots from your terminal.
|
|
4
|
+
|
|
5
|
+
<img src="screenshot.png" alt="Screenshot" width="50%">
|
|
6
|
+
|
|
7
|
+
[](https://github.com/bariskisir/TurkiyeMonitor)
|
|
8
|
+
[](https://www.npmjs.com/package/turkiyemonitor)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install -g turkiyemonitor
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
## Usage
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
turkiyemonitor
|
|
22
|
+
turkiyemonitor --lang tr
|
|
23
|
+
turkiyemonitor --lang en
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
| Option | Description | Default |
|
|
27
|
+
|--------|-------------|---------|
|
|
28
|
+
| `--lang` | Output language (`tr` or `en`) | Auto-detect from OS |
|
|
29
|
+
| `--log` | Show application logs | - |
|
|
30
|
+
| `--version` | Show version | - |
|
|
31
|
+
| `--help` | Show help | - |
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
## Modules
|
|
35
|
+
|
|
36
|
+
| # | Module | Source |
|
|
37
|
+
|---|--------|--------|
|
|
38
|
+
| 1 | 💱 Exchange Rates | investing.com |
|
|
39
|
+
| 2 | 📈 BIST 100 Stocks | investing.com |
|
|
40
|
+
| 3 | 🪙 Crypto Assets | investing.com |
|
|
41
|
+
| 4 | ⛽ Fuel Prices | petrolofisi |
|
|
42
|
+
| 5 | 📊 TUIK Indicators | tuik |
|
|
43
|
+
| 6 | 📰 Economic News | bloomberght.com |
|
|
44
|
+
| 7 | 🐦 Twitter Trends | getdaytrends.com |
|
|
45
|
+
| 8 | 🏛️ Politics News | sondakika.com |
|
|
46
|
+
| 9 | 💬 Ekşi Sözlük Agenda | eksisozluk.com |
|
|
47
|
+
| 10 | 🌍 Earthquakes (4+) | kandilli |
|
|
48
|
+
| 11 | ▶️ YouTube Trends | yttrendz.com |
|
|
49
|
+
| 12 | 🌦️ Weather | mgm.gov.tr |
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
## License
|
|
53
|
+
|
|
54
|
+
MIT
|
package/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Provides the TurkiyeMonitor command-line entry point and option handling.
|
|
3
|
+
import { Command } from 'commander';
|
|
4
|
+
import fs from 'node:fs';
|
|
5
|
+
import os from 'node:os';
|
|
6
|
+
import path from 'node:path';
|
|
7
|
+
import { fileURLToPath } from 'node:url';
|
|
8
|
+
import { formatOutput } from './core/formatter.js';
|
|
9
|
+
import { runAgendaCollectors } from './core/runner.js';
|
|
10
|
+
const program = new Command();
|
|
11
|
+
const currentDirectory = path.dirname(fileURLToPath(import.meta.url));
|
|
12
|
+
const logFilePath = path.join(currentDirectory, '..', 'log.txt');
|
|
13
|
+
/** Detects the preferred output language from the runtime locale. */
|
|
14
|
+
function detectLanguage() {
|
|
15
|
+
const locale = Intl.DateTimeFormat().resolvedOptions().locale ||
|
|
16
|
+
process.env.LANG ||
|
|
17
|
+
process.env.LC_ALL ||
|
|
18
|
+
process.env.LC_MESSAGES ||
|
|
19
|
+
os.platform();
|
|
20
|
+
const language = locale.toLowerCase().slice(0, 2);
|
|
21
|
+
return language === 'tr' || language === 'en' ? language : 'en';
|
|
22
|
+
}
|
|
23
|
+
/** Normalizes user-supplied language values to supported language codes. */
|
|
24
|
+
function resolveLanguage(language) {
|
|
25
|
+
return language === 'tr' || language === 'en' ? language : 'en';
|
|
26
|
+
}
|
|
27
|
+
/** Prints the application log file when it exists. */
|
|
28
|
+
function printLogFile() {
|
|
29
|
+
if (fs.existsSync(logFilePath)) {
|
|
30
|
+
console.log(fs.readFileSync(logFilePath, 'utf8'));
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
console.log('No log file found.');
|
|
34
|
+
}
|
|
35
|
+
program
|
|
36
|
+
.name('turkiyemonitor')
|
|
37
|
+
.description('A CLI tool for monitoring Turkiye agenda data in real time.')
|
|
38
|
+
.version('1.1.0')
|
|
39
|
+
.option('--lang <language>', 'Output language (tr or en)', detectLanguage())
|
|
40
|
+
.option('--log', 'Show application logs')
|
|
41
|
+
.action(async (options) => {
|
|
42
|
+
if (options.log) {
|
|
43
|
+
printLogFile();
|
|
44
|
+
process.exit(0);
|
|
45
|
+
}
|
|
46
|
+
try {
|
|
47
|
+
const language = resolveLanguage(options.lang);
|
|
48
|
+
const result = await runAgendaCollectors(language);
|
|
49
|
+
console.log(formatOutput(result, language));
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
53
|
+
console.error(`Fatal error: ${message}`);
|
|
54
|
+
process.exit(1);
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
program.parse();
|
|
58
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// Collects leading cryptocurrency quotes from Investing.com.
|
|
2
|
+
import { scrape } from '../services/browser.js';
|
|
3
|
+
const excludedSymbols = new Set(['USDT', 'BNB']);
|
|
4
|
+
/** Fetches leading cryptocurrency prices for the crypto section. */
|
|
5
|
+
export async function fetch() {
|
|
6
|
+
const quotes = await scrape('https://www.investing.com/crypto/currencies', () => {
|
|
7
|
+
const data = [];
|
|
8
|
+
const table = document.querySelector('table');
|
|
9
|
+
if (!table) {
|
|
10
|
+
return data;
|
|
11
|
+
}
|
|
12
|
+
const rows = Array.from(table.querySelectorAll('tbody tr'));
|
|
13
|
+
for (const row of rows) {
|
|
14
|
+
const cells = Array.from(row.querySelectorAll('td')).map((cell) => cell.textContent?.trim() ?? '');
|
|
15
|
+
if (cells.length < 6) {
|
|
16
|
+
continue;
|
|
17
|
+
}
|
|
18
|
+
const nameText = cells[3];
|
|
19
|
+
const price = cells[4];
|
|
20
|
+
const changePercent = cells[5];
|
|
21
|
+
if (nameText && price.includes('$')) {
|
|
22
|
+
const lines = nameText.split('\n').map((line) => line.trim()).filter(Boolean);
|
|
23
|
+
data.push({ name: lines[1] || lines[0] || '', price, changePercent });
|
|
24
|
+
}
|
|
25
|
+
if (data.length >= 5) {
|
|
26
|
+
break;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return data;
|
|
30
|
+
}, 'table');
|
|
31
|
+
const filteredQuotes = quotes.filter((quote) => !excludedSymbols.has(quote.name));
|
|
32
|
+
if (filteredQuotes.length === 0) {
|
|
33
|
+
throw new Error('No crypto data found');
|
|
34
|
+
}
|
|
35
|
+
return filteredQuotes;
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=crypto.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crypto.js","sourceRoot":"","sources":["../../src/collectors/crypto.ts"],"names":[],"mappings":"AAAA,6DAA6D;AAG7D,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAEhD,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC;AAEjD,oEAAoE;AACpE,MAAM,CAAC,KAAK,UAAU,KAAK;IACzB,MAAM,MAAM,GAAG,MAAM,MAAM,CACzB,6CAA6C,EAC7C,GAAG,EAAE;QACH,MAAM,IAAI,GAAkE,EAAE,CAAC;QAC/E,MAAM,KAAK,GAAG,QAAQ,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;QAE9C,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,gBAAgB,CAAC,UAAU,CAAC,CAAC,CAAC;QAE5D,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAa,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;YAE5G,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACrB,SAAS;YACX,CAAC;YAED,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;YAC1B,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;YACvB,MAAM,aAAa,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;YAE/B,IAAI,QAAQ,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;gBACpC,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,IAAY,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;gBACtF,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,aAAa,EAAE,CAAC,CAAC;YACxE,CAAC;YAED,IAAI,IAAI,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;gBACrB,MAAM;YACR,CAAC;QACH,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC,EACD,OAAO,CACR,CAAC;IAEF,MAAM,cAAc,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,eAAe,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;IAElF,IAAI,cAAc,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAChC,MAAM,IAAI,KAAK,CAAC,sBAAsB,CAAC,CAAC;IAC1C,CAAC;IAED,OAAO,cAAc,CAAC;AACxB,CAAC"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// Collects recent magnitude 4+ earthquakes from KOERI XML data.
|
|
2
|
+
import { parseStringPromise } from 'xml2js';
|
|
3
|
+
import httpClient from '../services/http-client.js';
|
|
4
|
+
const koeriLast24HoursUrl = 'http://www.koeri.boun.edu.tr/sismo/zeqmap/xmlt/son24saat.xml';
|
|
5
|
+
/** Fetches recent magnitude 4+ earthquakes from KOERI. */
|
|
6
|
+
export async function fetch() {
|
|
7
|
+
const response = await httpClient.get(koeriLast24HoursUrl, {
|
|
8
|
+
responseType: 'text',
|
|
9
|
+
timeout: 10000,
|
|
10
|
+
});
|
|
11
|
+
const parsed = (await parseStringPromise(response.data, { explicitArray: false }));
|
|
12
|
+
const earthquakes = extractEarthquakeItems(parsed)
|
|
13
|
+
.map(normalizeEarthquake)
|
|
14
|
+
.filter((earthquake) => earthquake !== null && earthquake.magnitude >= 4)
|
|
15
|
+
.sort((left, right) => right.time.localeCompare(left.time));
|
|
16
|
+
return earthquakes.slice(0, 5);
|
|
17
|
+
}
|
|
18
|
+
/** Recursively extracts earthquake-like XML nodes from the parsed response. */
|
|
19
|
+
function extractEarthquakeItems(source) {
|
|
20
|
+
if (!source || typeof source !== 'object') {
|
|
21
|
+
return [];
|
|
22
|
+
}
|
|
23
|
+
const items = [];
|
|
24
|
+
for (const [key, value] of Object.entries(source)) {
|
|
25
|
+
if (key.toLowerCase().includes('earthquake') || key.toLowerCase().includes('earhquake')) {
|
|
26
|
+
if (Array.isArray(value)) {
|
|
27
|
+
items.push(...value);
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
items.push(value);
|
|
31
|
+
}
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
items.push(...extractEarthquakeItems(value));
|
|
35
|
+
}
|
|
36
|
+
return items;
|
|
37
|
+
}
|
|
38
|
+
/** Converts a KOERI XML node into the internal earthquake shape. */
|
|
39
|
+
function normalizeEarthquake(item) {
|
|
40
|
+
if (!item || typeof item !== 'object') {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
const source = '$' in item && typeof item.$ === 'object' && item.$ ? item.$ : item;
|
|
44
|
+
const attributes = source;
|
|
45
|
+
const magnitude = Number.parseFloat(attributes.mag || attributes.magnitude || attributes.ML || attributes.Ml || attributes.Mw || attributes.MD || attributes.Md || '0');
|
|
46
|
+
return {
|
|
47
|
+
magnitude,
|
|
48
|
+
location: attributes.lokasyon || attributes.location || '-',
|
|
49
|
+
depth: attributes.Depth || attributes.depth || attributes.derinlik || '-',
|
|
50
|
+
time: attributes.name || attributes.tarih || attributes.date || attributes.Time || attributes.time || '-',
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=earthquakes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"earthquakes.js","sourceRoot":"","sources":["../../src/collectors/earthquakes.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// Collects current economy headlines from Bloomberg HT.
|
|
2
|
+
import * as cheerio from 'cheerio';
|
|
3
|
+
import httpClient from '../services/http-client.js';
|
|
4
|
+
/** Fetches economy headlines from Bloomberg HT. */
|
|
5
|
+
export async function fetch() {
|
|
6
|
+
const response = await httpClient.get('https://www.bloomberght.com/haberler');
|
|
7
|
+
const $ = cheerio.load(response.data);
|
|
8
|
+
const headlines = [];
|
|
9
|
+
const seenTitles = new Set();
|
|
10
|
+
$('a').each((_, element) => {
|
|
11
|
+
if (headlines.length >= 6) {
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
const anchor = $(element);
|
|
15
|
+
const href = anchor.attr('href') || '';
|
|
16
|
+
const title = normalizeHeadline(anchor.text());
|
|
17
|
+
if (!isBloombergNewsUrl(href) || !isUsableHeadline(title) || seenTitles.has(title)) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
seenTitles.add(title);
|
|
21
|
+
headlines.push({
|
|
22
|
+
title,
|
|
23
|
+
url: href.startsWith('http') ? href : `https://www.bloomberght.com${href}`,
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
if (headlines.length === 0) {
|
|
27
|
+
throw new Error('No economic news found');
|
|
28
|
+
}
|
|
29
|
+
return headlines;
|
|
30
|
+
}
|
|
31
|
+
/** Removes page chrome text from a scraped headline. */
|
|
32
|
+
function normalizeHeadline(text) {
|
|
33
|
+
return text.replace(/^HABERLER\s*/i, '').replace(/^Haberler\s*/i, '').trim();
|
|
34
|
+
}
|
|
35
|
+
/** Checks whether a Bloomberg HT URL points to an article. */
|
|
36
|
+
function isBloombergNewsUrl(href) {
|
|
37
|
+
if (!href || href === '/' || href === '#' || href.includes('javascript')) {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
if (href.includes('/kategori') || href.includes('/etiket') || href === '/haberler') {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
return /^\/[a-z0-9-]+-\d{5,}$/.test(href) || (href.includes('bloomberght.com/') && /\d{5,}$/.test(href));
|
|
44
|
+
}
|
|
45
|
+
/** Checks whether a headline has enough content for display. */
|
|
46
|
+
function isUsableHeadline(title) {
|
|
47
|
+
return title.length >= 20;
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=economic-news.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"economic-news.js","sourceRoot":"","sources":["../../src/collectors/economic-news.ts"],"names":[],"mappings":"AAAA,wDAAwD;AAExD,OAAO,KAAK,OAAO,MAAM,SAAS,CAAC;AAEnC,OAAO,UAAU,MAAM,4BAA4B,CAAC;AAEpD,mDAAmD;AACnD,MAAM,CAAC,KAAK,UAAU,KAAK;IACzB,MAAM,QAAQ,GAAG,MAAM,UAAU,CAAC,GAAG,CAAS,sCAAsC,CAAC,CAAC;IACtF,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IACtC,MAAM,SAAS,GAAe,EAAE,CAAC;IACjC,MAAM,UAAU,GAAG,IAAI,GAAG,EAAU,CAAC;IAErC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,EAAE;QACzB,IAAI,SAAS,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;YAC1B,OAAO;QACT,CAAC;QAED,MAAM,MAAM,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC;QAC1B,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;QACvC,MAAM,KAAK,GAAG,iBAAiB,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;QAE/C,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,IAAI,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;YACnF,OAAO;QACT,CAAC;QAED,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACtB,SAAS,CAAC,IAAI,CAAC;YACb,KAAK;YACL,GAAG,EAAE,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,8BAA8B,IAAI,EAAE;SAC3E,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC3B,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC;IAC5C,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,wDAAwD;AACxD,SAAS,iBAAiB,CAAC,IAAY;IACrC,OAAO,IAAI,CAAC,OAAO,CAAC,eAAe,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,eAAe,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;AAC/E,CAAC;AAED,8DAA8D;AAC9D,SAAS,kBAAkB,CAAC,IAAY;IACtC,IAAI,CAAC,IAAI,IAAI,IAAI,KAAK,GAAG,IAAI,IAAI,KAAK,GAAG,IAAI,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC;QACzE,OAAO,KAAK,CAAC;IACf,CAAC;IAED,IAAI,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,IAAI,KAAK,WAAW,EAAE,CAAC;QACnF,OAAO,KAAK,CAAC;IACf,CAAC;IAED,OAAO,uBAAuB,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,kBAAkB,CAAC,IAAI,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;AAC3G,CAAC;AAED,gEAAgE;AAChE,SAAS,gBAAgB,CAAC,KAAa;IACrC,OAAO,KAAK,CAAC,MAAM,IAAI,EAAE,CAAC;AAC5B,CAAC"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// Collects the current Eksi Sozluk agenda topics.
|
|
2
|
+
import * as cheerio from 'cheerio';
|
|
3
|
+
import httpClient from '../services/http-client.js';
|
|
4
|
+
/** Fetches current agenda topics from Eksi Sozluk. */
|
|
5
|
+
export async function fetch() {
|
|
6
|
+
const response = await httpClient.get('https://eksisozluk.com/basliklar/gundem');
|
|
7
|
+
const $ = cheerio.load(response.data);
|
|
8
|
+
const topics = [];
|
|
9
|
+
$('ul.topic-list li a').each((_, element) => {
|
|
10
|
+
const anchor = $(element);
|
|
11
|
+
const fullText = anchor.text().trim();
|
|
12
|
+
const entries = anchor.find('small').text().trim();
|
|
13
|
+
const title = fullText.replace(entries, '').trim();
|
|
14
|
+
const href = anchor.attr('href') || '';
|
|
15
|
+
const topicId = parseTopicId(href);
|
|
16
|
+
if (title) {
|
|
17
|
+
topics.push({
|
|
18
|
+
title,
|
|
19
|
+
entries: entries || '-',
|
|
20
|
+
url: `https://eksisozluk.com${href}`,
|
|
21
|
+
topicId,
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
if (topics.length === 0) {
|
|
26
|
+
throw new Error('No Eksi Sozluk topics found');
|
|
27
|
+
}
|
|
28
|
+
return topics
|
|
29
|
+
.sort((left, right) => right.topicId - left.topicId)
|
|
30
|
+
.slice(0, 5)
|
|
31
|
+
.map(({ title, entries, url }) => ({ title, entries, url }));
|
|
32
|
+
}
|
|
33
|
+
/** Extracts the numeric Eksi Sozluk topic id from a topic URL. */
|
|
34
|
+
function parseTopicId(href) {
|
|
35
|
+
const match = href.match(/--(\d+)/);
|
|
36
|
+
return match ? Number.parseInt(match[1], 10) : 0;
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=eksi-sozluk.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"eksi-sozluk.js","sourceRoot":"","sources":["../../src/collectors/eksi-sozluk.ts"],"names":[],"mappings":"AAAA,kDAAkD;AAElD,OAAO,KAAK,OAAO,MAAM,SAAS,CAAC;AAEnC,OAAO,UAAU,MAAM,4BAA4B,CAAC;AAMpD,sDAAsD;AACtD,MAAM,CAAC,KAAK,UAAU,KAAK;IACzB,MAAM,QAAQ,GAAG,MAAM,UAAU,CAAC,GAAG,CAAS,yCAAyC,CAAC,CAAC;IACzF,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IACtC,MAAM,MAAM,GAAgB,EAAE,CAAC;IAE/B,CAAC,CAAC,oBAAoB,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,EAAE;QAC1C,MAAM,MAAM,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC;QAC1B,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC;QACtC,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC;QACnD,MAAM,KAAK,GAAG,QAAQ,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;QACnD,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;QACvC,MAAM,OAAO,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;QAEnC,IAAI,KAAK,EAAE,CAAC;YACV,MAAM,CAAC,IAAI,CAAC;gBACV,KAAK;gBACL,OAAO,EAAE,OAAO,IAAI,GAAG;gBACvB,GAAG,EAAE,yBAAyB,IAAI,EAAE;gBACpC,OAAO;aACR,CAAC,CAAC;QACL,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC;IACjD,CAAC;IAED,OAAO,MAAM;SACV,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC;SACnD,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC;SACX,GAAG,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC;AACjE,CAAC;AAED,kEAAkE;AAClE,SAAS,YAAY,CAAC,IAAY;IAChC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;IACpC,OAAO,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACnD,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// Collects key exchange-rate quotes from Investing.com.
|
|
2
|
+
import { scrape } from '../services/browser.js';
|
|
3
|
+
const preferredOrder = ['USD/TRY', 'EUR/TRY', 'GBP/TRY', 'GAU/TRY', 'EUR/USD'];
|
|
4
|
+
const pairs = [
|
|
5
|
+
{ name: 'USD/TRY', slug: 'usd-try' },
|
|
6
|
+
{ name: 'EUR/TRY', slug: 'eur-try' },
|
|
7
|
+
{ name: 'GBP/TRY', slug: 'gbp-try' },
|
|
8
|
+
{ name: 'GAU/TRY', slug: 'gau-try' },
|
|
9
|
+
{ name: 'EUR/USD', slug: 'eur-usd' },
|
|
10
|
+
];
|
|
11
|
+
/** Fetches exchange-rate quotes in a stable display order. */
|
|
12
|
+
export async function fetch() {
|
|
13
|
+
const quotes = await scrapeInvestingPairs();
|
|
14
|
+
if (quotes.length === 0) {
|
|
15
|
+
throw new Error('No exchange rate data found');
|
|
16
|
+
}
|
|
17
|
+
return quotes.sort((left, right) => resolvePairOrder(left.name) - resolvePairOrder(right.name));
|
|
18
|
+
}
|
|
19
|
+
/** Assigns each exchange pair to its preferred display position. */
|
|
20
|
+
function resolvePairOrder(pairName) {
|
|
21
|
+
const orderIndex = preferredOrder.indexOf(pairName);
|
|
22
|
+
return orderIndex === -1 ? Number.MAX_SAFE_INTEGER : orderIndex;
|
|
23
|
+
}
|
|
24
|
+
/** Scrapes the configured currency pair pages concurrently. */
|
|
25
|
+
async function scrapeInvestingPairs() {
|
|
26
|
+
const requests = pairs.map((pair) => scrape(`https://www.investing.com/currencies/${pair.slug}`, () => {
|
|
27
|
+
const last = document.querySelector('[data-test="instrument-price-last"]')?.textContent?.trim();
|
|
28
|
+
const changePercent = document.querySelector('[data-test="instrument-price-change-percent"]')?.textContent?.trim();
|
|
29
|
+
return last ? { last, changePercent: changePercent || '-' } : null;
|
|
30
|
+
}, '[data-test="instrument-price-last"]')
|
|
31
|
+
.then((quote) => (quote ? { name: pair.name, ...quote } : null))
|
|
32
|
+
.catch(() => null));
|
|
33
|
+
const results = await Promise.all(requests);
|
|
34
|
+
return results.filter((quote) => quote !== null);
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=exchange-rates.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exchange-rates.js","sourceRoot":"","sources":["../../src/collectors/exchange-rates.ts"],"names":[],"mappings":"AAAA,wDAAwD;AAGxD,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAEhD,MAAM,cAAc,GAAG,CAAC,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC;AAC/E,MAAM,KAAK,GAAG;IACZ,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE;IACpC,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE;IACpC,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE;IACpC,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE;IACpC,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE;CACrC,CAAC;AAEF,8DAA8D;AAC9D,MAAM,CAAC,KAAK,UAAU,KAAK;IACzB,MAAM,MAAM,GAAG,MAAM,oBAAoB,EAAE,CAAC;IAE5C,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC;IACjD,CAAC;IAED,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,gBAAgB,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;AAClG,CAAC;AAED,oEAAoE;AACpE,SAAS,gBAAgB,CAAC,QAAgB;IACxC,MAAM,UAAU,GAAG,cAAc,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IACpD,OAAO,UAAU,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC,CAAC,UAAU,CAAC;AAClE,CAAC;AAED,+DAA+D;AAC/D,KAAK,UAAU,oBAAoB;IACjC,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAClC,MAAM,CACJ,wCAAwC,IAAI,CAAC,IAAI,EAAE,EACnD,GAAG,EAAE;QACH,MAAM,IAAI,GAAG,QAAQ,CAAC,aAAa,CAAC,qCAAqC,CAAC,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;QAChG,MAAM,aAAa,GAAG,QAAQ,CAAC,aAAa,CAAC,+CAA+C,CAAC,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;QACnH,OAAO,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,aAAa,IAAI,GAAG,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IACrE,CAAC,EACD,qCAAqC,CACtC;SACE,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;SAC/D,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CACrB,CAAC;IAEF,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAC5C,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC,KAAK,EAAsC,EAAE,CAAC,KAAK,KAAK,IAAI,CAAC,CAAC;AACvF,CAAC"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// Collects Istanbul fuel prices from Petrol Ofisi.
|
|
2
|
+
import * as cheerio from 'cheerio';
|
|
3
|
+
import httpClient from '../services/http-client.js';
|
|
4
|
+
const wantedProducts = ['V/Max Kurşunsuz 95', 'V/Max Diesel', 'PO/gaz Otogaz'];
|
|
5
|
+
/** Fetches selected fuel prices for Istanbul Europe. */
|
|
6
|
+
export async function fetch() {
|
|
7
|
+
const response = await httpClient.get('https://www.petrolofisi.com.tr/akaryakit-fiyatlari');
|
|
8
|
+
const $ = cheerio.load(response.data);
|
|
9
|
+
const results = [...extractTablePrices($), ...extractListPrices($)];
|
|
10
|
+
if (results.length === 0) {
|
|
11
|
+
throw new Error('No fuel price data found');
|
|
12
|
+
}
|
|
13
|
+
return deduplicatePrices(results);
|
|
14
|
+
}
|
|
15
|
+
/** Extracts fuel prices from the desktop table layout. */
|
|
16
|
+
function extractTablePrices($) {
|
|
17
|
+
const headers = [];
|
|
18
|
+
const prices = [];
|
|
19
|
+
$('table.table-prices thead th').each((_, element) => {
|
|
20
|
+
headers.push($(element).text().trim());
|
|
21
|
+
});
|
|
22
|
+
const row = $('tr[data-disctrict-name="ISTANBUL (AVRUPA)"]').first();
|
|
23
|
+
if (!row.length || headers.length <= 1) {
|
|
24
|
+
return prices;
|
|
25
|
+
}
|
|
26
|
+
row.find('td').each((index, element) => {
|
|
27
|
+
const productName = headers[index] || '';
|
|
28
|
+
if (!wantedProducts.includes(productName)) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
const price = $(element).find('span.with-tax').text().trim();
|
|
32
|
+
if (price) {
|
|
33
|
+
prices.push({ productName, amount: Number.parseFloat(price.replace(',', '.')) });
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
return prices;
|
|
37
|
+
}
|
|
38
|
+
/** Extracts fuel prices from the responsive list layout. */
|
|
39
|
+
function extractListPrices($) {
|
|
40
|
+
const prices = [];
|
|
41
|
+
$('li').each((_, element) => {
|
|
42
|
+
const item = $(element);
|
|
43
|
+
if (!item.text().trim().startsWith('ISTANBUL (AVRUPA)')) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
item.find('div.mt-2').each((_, block) => {
|
|
47
|
+
const productName = $(block).find('.text-primary').text().trim();
|
|
48
|
+
if (!wantedProducts.includes(productName)) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
const priceMatch = $(block).text().match(/([\d.]+)\s*TL/);
|
|
52
|
+
if (priceMatch) {
|
|
53
|
+
prices.push({ productName, amount: Number.parseFloat(priceMatch[1].replace(',', '.')) });
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
return prices;
|
|
58
|
+
}
|
|
59
|
+
/** Removes duplicate prices when both page layouts are present. */
|
|
60
|
+
function deduplicatePrices(prices) {
|
|
61
|
+
const seen = new Set();
|
|
62
|
+
return prices.filter((price) => {
|
|
63
|
+
if (seen.has(price.productName)) {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
seen.add(price.productName);
|
|
67
|
+
return true;
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=fuel-prices.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fuel-prices.js","sourceRoot":"","sources":["../../src/collectors/fuel-prices.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// Collects the Magnificent Seven stock quotes from Investing.com.
|
|
2
|
+
import { scrape } from '../services/browser.js';
|
|
3
|
+
const magnificentSevenStocks = [
|
|
4
|
+
{ slug: 'nvidia-corp', ticker: 'NVDA' },
|
|
5
|
+
{ slug: 'apple-computer-inc', ticker: 'AAPL' },
|
|
6
|
+
{ slug: 'google-inc', ticker: 'GOOGL' },
|
|
7
|
+
{ slug: 'microsoft-corp', ticker: 'MSFT' },
|
|
8
|
+
{ slug: 'amazon-com-inc', ticker: 'AMZN' },
|
|
9
|
+
{ slug: 'facebook-inc', ticker: 'META' },
|
|
10
|
+
{ slug: 'tesla-motors', ticker: 'TSLA' },
|
|
11
|
+
];
|
|
12
|
+
/** Fetches Magnificent Seven stock quotes in display order. */
|
|
13
|
+
export async function fetch() {
|
|
14
|
+
const results = [];
|
|
15
|
+
for (const stock of magnificentSevenStocks) {
|
|
16
|
+
const quote = await scrapeStock(stock.slug, stock.ticker);
|
|
17
|
+
if (quote) {
|
|
18
|
+
results.push(quote);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
if (results.length === 0) {
|
|
22
|
+
throw new Error('No Magnificent Seven stock data found');
|
|
23
|
+
}
|
|
24
|
+
return results;
|
|
25
|
+
}
|
|
26
|
+
/** Scrapes a single stock quote page from Investing.com. */
|
|
27
|
+
async function scrapeStock(slug, ticker) {
|
|
28
|
+
try {
|
|
29
|
+
const quote = await scrape(`https://www.investing.com/equities/${slug}`, () => {
|
|
30
|
+
const price = document.querySelector('[data-test="instrument-price-last"]')?.textContent?.trim();
|
|
31
|
+
const changePercent = document.querySelector('[data-test="instrument-price-change-percent"]')?.textContent?.trim();
|
|
32
|
+
return price ? { price, changePercent: changePercent || '-' } : null;
|
|
33
|
+
}, '[data-test="instrument-price-last"]');
|
|
34
|
+
return quote ? { name: ticker, ticker, price: quote.price, changePercent: quote.changePercent } : null;
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=magnificent-seven-stocks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"magnificent-seven-stocks.js","sourceRoot":"","sources":["../../src/collectors/magnificent-seven-stocks.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAGlE,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAEhD,MAAM,sBAAsB,GAAG;IAC7B,EAAE,IAAI,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,EAAE;IACvC,EAAE,IAAI,EAAE,oBAAoB,EAAE,MAAM,EAAE,MAAM,EAAE;IAC9C,EAAE,IAAI,EAAE,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE;IACvC,EAAE,IAAI,EAAE,gBAAgB,EAAE,MAAM,EAAE,MAAM,EAAE;IAC1C,EAAE,IAAI,EAAE,gBAAgB,EAAE,MAAM,EAAE,MAAM,EAAE;IAC1C,EAAE,IAAI,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,EAAE;IACxC,EAAE,IAAI,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,EAAE;CACzC,CAAC;AAEF,+DAA+D;AAC/D,MAAM,CAAC,KAAK,UAAU,KAAK;IACzB,MAAM,OAAO,GAAkB,EAAE,CAAC;IAElC,KAAK,MAAM,KAAK,IAAI,sBAAsB,EAAE,CAAC;QAC3C,MAAM,KAAK,GAAG,MAAM,WAAW,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;QAE1D,IAAI,KAAK,EAAE,CAAC;YACV,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACtB,CAAC;IACH,CAAC;IAED,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAC;IAC3D,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,4DAA4D;AAC5D,KAAK,UAAU,WAAW,CAAC,IAAY,EAAE,MAAc;IACrD,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,MAAM,MAAM,CACxB,sCAAsC,IAAI,EAAE,EAC5C,GAAG,EAAE;YACH,MAAM,KAAK,GAAG,QAAQ,CAAC,aAAa,CAAC,qCAAqC,CAAC,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;YACjG,MAAM,aAAa,GAAG,QAAQ,CAAC,aAAa,CAAC,+CAA+C,CAAC,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;YACnH,OAAO,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,aAAa,EAAE,aAAa,IAAI,GAAG,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;QACvE,CAAC,EACD,qCAAqC,CACtC,CAAC;QAEF,OAAO,KAAK,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,aAAa,EAAE,KAAK,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IACzG,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// Collects current politics headlines from sondakika.com.
|
|
2
|
+
import * as cheerio from 'cheerio';
|
|
3
|
+
import httpClient from '../services/http-client.js';
|
|
4
|
+
/** Fetches politics headlines from sondakika.com. */
|
|
5
|
+
export async function fetch() {
|
|
6
|
+
const response = await httpClient.get('https://www.sondakika.com/siyaset/');
|
|
7
|
+
const $ = cheerio.load(response.data);
|
|
8
|
+
const headlines = [];
|
|
9
|
+
const seenTitles = new Set();
|
|
10
|
+
$('a').each((_, element) => {
|
|
11
|
+
if (headlines.length >= 5) {
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
const anchor = $(element);
|
|
15
|
+
const href = anchor.attr('href') || '';
|
|
16
|
+
const title = anchor.text().trim();
|
|
17
|
+
if (!isPoliticsNewsUrl(href) || title.length < 20 || seenTitles.has(title)) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
seenTitles.add(title);
|
|
21
|
+
headlines.push({
|
|
22
|
+
title,
|
|
23
|
+
url: href.startsWith('http') ? href : `https://www.sondakika.com${href}`,
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
if (headlines.length === 0) {
|
|
27
|
+
throw new Error('No politics news found');
|
|
28
|
+
}
|
|
29
|
+
return headlines;
|
|
30
|
+
}
|
|
31
|
+
/** Checks whether a URL points to a politics article. */
|
|
32
|
+
function isPoliticsNewsUrl(href) {
|
|
33
|
+
return href.includes('/haber/') || href.includes('/politika/');
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=politics.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"politics.js","sourceRoot":"","sources":["../../src/collectors/politics.ts"],"names":[],"mappings":"AAAA,0DAA0D;AAE1D,OAAO,KAAK,OAAO,MAAM,SAAS,CAAC;AAEnC,OAAO,UAAU,MAAM,4BAA4B,CAAC;AAEpD,qDAAqD;AACrD,MAAM,CAAC,KAAK,UAAU,KAAK;IACzB,MAAM,QAAQ,GAAG,MAAM,UAAU,CAAC,GAAG,CAAS,oCAAoC,CAAC,CAAC;IACpF,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IACtC,MAAM,SAAS,GAAe,EAAE,CAAC;IACjC,MAAM,UAAU,GAAG,IAAI,GAAG,EAAU,CAAC;IAErC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,EAAE;QACzB,IAAI,SAAS,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;YAC1B,OAAO;QACT,CAAC;QAED,MAAM,MAAM,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC;QAC1B,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;QACvC,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC;QAEnC,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,MAAM,GAAG,EAAE,IAAI,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;YAC3E,OAAO;QACT,CAAC;QAED,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACtB,SAAS,CAAC,IAAI,CAAC;YACb,KAAK;YACL,GAAG,EAAE,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,4BAA4B,IAAI,EAAE;SACzE,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC3B,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC;IAC5C,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,yDAAyD;AACzD,SAAS,iBAAiB,CAAC,IAAY;IACrC,OAAO,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC;AACjE,CAAC"}
|